akanjs 2.3.0 → 2.3.1-rc.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (180) hide show
  1. package/base/primitiveRegistry.ts +19 -12
  2. package/client/csrTypes.ts +16 -0
  3. package/constant/fieldInfo.ts +11 -9
  4. package/constant/getDefault.ts +1 -1
  5. package/fetch/requestStorage.ts +5 -0
  6. package/package.json +4 -4
  7. package/server/akanApp.ts +26 -3
  8. package/server/akanServer.ts +5 -1
  9. package/server/cachePolicy.ts +99 -5
  10. package/server/imageOptimizer.ts +14 -1
  11. package/server/metadata.tsx +117 -33
  12. package/server/resolver/database.resolver.ts +4 -4
  13. package/server/routeElementComposer.tsx +46 -14
  14. package/server/routeState.ts +379 -0
  15. package/server/routeTreeBuilder.ts +3 -2
  16. package/server/rscClient.tsx +316 -46
  17. package/server/rscClientFetch.ts +57 -0
  18. package/server/rscClientPatch.ts +157 -0
  19. package/server/rscHeadPatch.ts +80 -0
  20. package/server/rscNavigationState.ts +315 -0
  21. package/server/rscPartialCommit.ts +3 -0
  22. package/server/rscPatchSafety.ts +57 -0
  23. package/server/rscSegmentOutlet.tsx +69 -0
  24. package/server/rscSegmentOutletReference.ts +24 -0
  25. package/server/rscWorker.tsx +380 -53
  26. package/server/rscWorkerCache.ts +180 -0
  27. package/server/rscWorkerHost.ts +40 -12
  28. package/server/rscWorkerReplay.ts +11 -2
  29. package/server/ssrFromRscRenderer.tsx +15 -10
  30. package/server/ssrTypes.ts +18 -0
  31. package/server/types.tsx +4 -0
  32. package/server/webRouter.ts +198 -42
  33. package/service/predefinedAdaptor/database.adaptor.ts +72 -25
  34. package/signal/signalContext.ts +1 -1
  35. package/types/base/primitiveRegistry.d.ts +6 -6
  36. package/types/client/csrTypes.d.ts +16 -0
  37. package/types/constant/fieldInfo.d.ts +8 -7
  38. package/types/fetch/requestStorage.d.ts +2 -0
  39. package/types/server/cachePolicy.d.ts +36 -0
  40. package/types/server/metadata.d.ts +10 -1
  41. package/types/server/routeElementComposer.d.ts +9 -1
  42. package/types/server/routeState.d.ts +94 -0
  43. package/types/server/rscClient.d.ts +1 -0
  44. package/types/server/rscClientFetch.d.ts +24 -0
  45. package/types/server/rscClientPatch.d.ts +21 -0
  46. package/types/server/rscHeadPatch.d.ts +12 -0
  47. package/types/server/rscNavigationState.d.ts +78 -0
  48. package/types/server/rscPartialCommit.d.ts +1 -0
  49. package/types/server/rscPatchSafety.d.ts +8 -0
  50. package/types/server/rscSegmentOutlet.d.ts +17 -0
  51. package/types/server/rscSegmentOutletReference.d.ts +2 -0
  52. package/types/server/rscWorker.d.ts +5 -0
  53. package/types/server/rscWorkerCache.d.ts +63 -0
  54. package/types/server/rscWorkerHost.d.ts +8 -4
  55. package/types/server/rscWorkerReplay.d.ts +3 -0
  56. package/types/server/ssrFromRscRenderer.d.ts +1 -0
  57. package/types/server/ssrTypes.d.ts +17 -0
  58. package/types/server/types.d.ts +4 -0
  59. package/types/server/webRouter.d.ts +7 -3
  60. package/types/service/predefinedAdaptor/database.adaptor.d.ts +6 -0
  61. package/types/ui/Button.d.ts +1 -1
  62. package/types/ui/ClientSide.d.ts +1 -1
  63. package/types/ui/Constant/Doc.d.ts +6 -6
  64. package/types/ui/Constant/Mermaid.d.ts +1 -1
  65. package/types/ui/Constant/index.d.ts +1 -1
  66. package/types/ui/Constant/schemaDoc.d.ts +1 -1
  67. package/types/ui/Copy.d.ts +1 -1
  68. package/types/ui/CsrImage.d.ts +1 -1
  69. package/types/ui/Data/CardList.d.ts +1 -1
  70. package/types/ui/Data/Dashboard.d.ts +1 -1
  71. package/types/ui/Data/Insight.d.ts +1 -1
  72. package/types/ui/Data/Item.d.ts +6 -6
  73. package/types/ui/Data/ListContainer.d.ts +1 -1
  74. package/types/ui/Data/Pagination.d.ts +1 -1
  75. package/types/ui/Data/TableList.d.ts +1 -1
  76. package/types/ui/DatePicker.d.ts +3 -3
  77. package/types/ui/Dialog/Close.d.ts +1 -1
  78. package/types/ui/Dialog/Content.d.ts +1 -1
  79. package/types/ui/Dialog/Provider.d.ts +1 -1
  80. package/types/ui/Dialog/Trigger.d.ts +1 -1
  81. package/types/ui/Dialog/index.d.ts +3 -3
  82. package/types/ui/DragAction.d.ts +4 -4
  83. package/types/ui/DraggableList.d.ts +3 -3
  84. package/types/ui/Dropdown.d.ts +1 -1
  85. package/types/ui/Empty.d.ts +1 -1
  86. package/types/ui/Field.d.ts +22 -22
  87. package/types/ui/Image.d.ts +1 -1
  88. package/types/ui/InfiniteScroll.d.ts +1 -1
  89. package/types/ui/Input.d.ts +6 -6
  90. package/types/ui/KeyboardAvoiding.d.ts +1 -1
  91. package/types/ui/Layout/BottomAction.d.ts +1 -1
  92. package/types/ui/Layout/BottomInset.d.ts +1 -1
  93. package/types/ui/Layout/BottomTab.d.ts +1 -1
  94. package/types/ui/Layout/Header.d.ts +1 -1
  95. package/types/ui/Layout/LeftSider.d.ts +1 -1
  96. package/types/ui/Layout/Navbar.d.ts +1 -1
  97. package/types/ui/Layout/RightSider.d.ts +1 -1
  98. package/types/ui/Layout/Sider.d.ts +1 -1
  99. package/types/ui/Layout/Template.d.ts +1 -1
  100. package/types/ui/Layout/TopLeftAction.d.ts +1 -1
  101. package/types/ui/Layout/Unit.d.ts +1 -1
  102. package/types/ui/Layout/View.d.ts +1 -1
  103. package/types/ui/Layout/Zone.d.ts +1 -1
  104. package/types/ui/Layout/index.d.ts +12 -12
  105. package/types/ui/Link/Back.d.ts +1 -1
  106. package/types/ui/Link/Close.d.ts +1 -1
  107. package/types/ui/Link/CsrLink.d.ts +1 -1
  108. package/types/ui/Link/Lang.d.ts +1 -1
  109. package/types/ui/Link/SsrLink.d.ts +1 -1
  110. package/types/ui/Link/index.d.ts +1 -1
  111. package/types/ui/Load/Edit.d.ts +1 -1
  112. package/types/ui/Load/Edit_Client.d.ts +1 -1
  113. package/types/ui/Load/PageCSR.d.ts +1 -1
  114. package/types/ui/Load/Pagination.d.ts +1 -1
  115. package/types/ui/Load/Units.d.ts +1 -1
  116. package/types/ui/Load/View.d.ts +1 -1
  117. package/types/ui/Loading/Area.d.ts +1 -1
  118. package/types/ui/Loading/Button.d.ts +1 -1
  119. package/types/ui/Loading/Input.d.ts +1 -1
  120. package/types/ui/Loading/ProgressBar.d.ts +1 -1
  121. package/types/ui/Loading/Skeleton.d.ts +1 -1
  122. package/types/ui/Loading/Spin.d.ts +1 -1
  123. package/types/ui/Loading/index.d.ts +6 -6
  124. package/types/ui/Menu.d.ts +1 -1
  125. package/types/ui/Modal.d.ts +1 -1
  126. package/types/ui/Model/AdminPanel.d.ts +1 -1
  127. package/types/ui/Model/Edit.d.ts +1 -1
  128. package/types/ui/Model/EditModal.d.ts +1 -1
  129. package/types/ui/Model/EditWrapper.d.ts +1 -1
  130. package/types/ui/Model/LoadInit.d.ts +1 -1
  131. package/types/ui/Model/New.d.ts +1 -1
  132. package/types/ui/Model/NewWrapper.d.ts +1 -1
  133. package/types/ui/Model/NewWrapper_Client.d.ts +1 -1
  134. package/types/ui/Model/Remove.d.ts +1 -1
  135. package/types/ui/Model/RemoveWrapper.d.ts +1 -1
  136. package/types/ui/Model/SureToRemove.d.ts +1 -1
  137. package/types/ui/Model/View.d.ts +1 -1
  138. package/types/ui/Model/ViewEditModal.d.ts +1 -1
  139. package/types/ui/Model/ViewModal.d.ts +1 -1
  140. package/types/ui/Model/ViewWrapper.d.ts +1 -1
  141. package/types/ui/More.d.ts +1 -1
  142. package/types/ui/ObjectId.d.ts +1 -1
  143. package/types/ui/Popconfirm.d.ts +1 -1
  144. package/types/ui/Radio.d.ts +2 -2
  145. package/types/ui/RecentTime.d.ts +1 -1
  146. package/types/ui/Refresh.d.ts +1 -1
  147. package/types/ui/ScreenNavigator.d.ts +3 -3
  148. package/types/ui/Select.d.ts +1 -1
  149. package/types/ui/Signal/Arg.d.ts +13 -13
  150. package/types/ui/Signal/Doc.d.ts +6 -6
  151. package/types/ui/Signal/Listener.d.ts +2 -2
  152. package/types/ui/Signal/Message.d.ts +4 -4
  153. package/types/ui/Signal/Object.d.ts +4 -4
  154. package/types/ui/Signal/PubSub.d.ts +4 -4
  155. package/types/ui/Signal/Request.d.ts +2 -2
  156. package/types/ui/Signal/Response.d.ts +3 -3
  157. package/types/ui/Signal/RestApi.d.ts +5 -5
  158. package/types/ui/Signal/WebSocket.d.ts +2 -2
  159. package/types/ui/System/CSR.d.ts +5 -5
  160. package/types/ui/System/Client.d.ts +8 -8
  161. package/types/ui/System/Common.d.ts +2 -2
  162. package/types/ui/System/DevModeToggle.d.ts +1 -1
  163. package/types/ui/System/Gtag.d.ts +1 -1
  164. package/types/ui/System/Messages.d.ts +1 -1
  165. package/types/ui/System/Reconnect.d.ts +1 -1
  166. package/types/ui/System/Root.d.ts +1 -1
  167. package/types/ui/System/SSR.d.ts +4 -4
  168. package/types/ui/System/SelectLanguage.d.ts +1 -1
  169. package/types/ui/System/ThemeToggle.d.ts +1 -1
  170. package/types/ui/System/index.d.ts +7 -7
  171. package/types/ui/Tab/Menu.d.ts +1 -1
  172. package/types/ui/Tab/Menus.d.ts +1 -1
  173. package/types/ui/Tab/Panel.d.ts +1 -1
  174. package/types/ui/Tab/Provider.d.ts +1 -1
  175. package/types/ui/Tab/index.d.ts +4 -4
  176. package/types/ui/Table.d.ts +1 -1
  177. package/types/ui/ToggleSelect.d.ts +2 -2
  178. package/types/ui/Unauthorized.d.ts +1 -1
  179. package/ui/Constant/schemaDoc.ts +1 -1
  180. package/server/resolver/resolver.contract.fixture.ts +0 -222
@@ -1,5 +1,5 @@
1
1
  import type { AkanMetadata, Head, ResolvedHead, ResolveHeadResult } from "akanjs/client";
2
- import type { ReactNode } from "react";
2
+ import { AKAN_RSC_HEAD_SNAPSHOT_VERSION, type AkanHeadSnapshotNode, type AkanHeadSnapshotV1 } from "./routeState";
3
3
 
4
4
  function isRecord(value: unknown): value is Record<string, unknown> {
5
5
  return Boolean(value && typeof value === "object" && !Array.isArray(value));
@@ -10,44 +10,70 @@ function normalizeStringArray(value: string | string[] | undefined): string[] {
10
10
  return Array.isArray(value) ? value : [value];
11
11
  }
12
12
 
13
- function renderOpenGraph(metadata: AkanMetadata): ReactNode[] {
13
+ function createMetaNode(attrs: Record<string, string | undefined>): AkanHeadSnapshotNode | null {
14
+ if (attrs.content === undefined || attrs.content === "") return null;
15
+ const normalizedAttrs = Object.fromEntries(
16
+ Object.entries(attrs).filter((entry): entry is [string, string] => typeof entry[1] === "string" && entry[1] !== ""),
17
+ );
18
+ return Object.keys(normalizedAttrs).length > 0 ? { tag: "meta", attrs: normalizedAttrs } : null;
19
+ }
20
+
21
+ function createLinkNode(attrs: Record<string, string | undefined>): AkanHeadSnapshotNode | null {
22
+ if (attrs.href === undefined || attrs.href === "") return null;
23
+ const normalizedAttrs = Object.fromEntries(
24
+ Object.entries(attrs).filter((entry): entry is [string, string] => typeof entry[1] === "string" && entry[1] !== ""),
25
+ );
26
+ return Object.keys(normalizedAttrs).length > 0 ? { tag: "link", attrs: normalizedAttrs } : null;
27
+ }
28
+
29
+ function createOpenGraphHeadSnapshotNodes(metadata: AkanMetadata): AkanHeadSnapshotNode[] {
14
30
  const openGraph = metadata.openGraph;
15
31
  if (!openGraph) return [];
16
- const nodes: ReactNode[] = [];
17
- if (openGraph.title) nodes.push(<meta key="og:title" property="og:title" content={openGraph.title} />);
18
- if (openGraph.description)
19
- nodes.push(<meta key="og:description" property="og:description" content={openGraph.description} />);
20
- if (openGraph.type) nodes.push(<meta key="og:type" property="og:type" content={openGraph.type} />);
21
- if (openGraph.url) nodes.push(<meta key="og:url" property="og:url" content={openGraph.url} />);
22
- if (openGraph.siteName) nodes.push(<meta key="og:site_name" property="og:site_name" content={openGraph.siteName} />);
23
- for (const [index, image] of normalizeStringArray(openGraph.images).entries()) {
24
- nodes.push(<meta key={`og:image:${index}`} property="og:image" content={image} />);
32
+ const nodes: AkanHeadSnapshotNode[] = [];
33
+ const pushMeta = (property: string, content: string | undefined) => {
34
+ const node = createMetaNode({ property, content });
35
+ if (node) nodes.push(node);
36
+ };
37
+ pushMeta("og:title", openGraph.title);
38
+ pushMeta("og:description", openGraph.description);
39
+ pushMeta("og:type", openGraph.type);
40
+ pushMeta("og:url", openGraph.url);
41
+ pushMeta("og:site_name", openGraph.siteName);
42
+ for (const image of normalizeStringArray(openGraph.images)) {
43
+ const node = createMetaNode({ property: "og:image", content: image });
44
+ if (node) nodes.push(node);
25
45
  }
26
46
  return nodes;
27
47
  }
28
48
 
29
- function renderTwitter(metadata: AkanMetadata): ReactNode[] {
49
+ function createTwitterHeadSnapshotNodes(metadata: AkanMetadata): AkanHeadSnapshotNode[] {
30
50
  const twitter = metadata.twitter;
31
51
  if (!twitter) return [];
32
- const nodes: ReactNode[] = [];
33
- if (twitter.card) nodes.push(<meta key="twitter:card" name="twitter:card" content={twitter.card} />);
34
- if (twitter.title) nodes.push(<meta key="twitter:title" name="twitter:title" content={twitter.title} />);
35
- if (twitter.description)
36
- nodes.push(<meta key="twitter:description" name="twitter:description" content={twitter.description} />);
37
- for (const [index, image] of normalizeStringArray(twitter.images).entries()) {
38
- nodes.push(<meta key={`twitter:image:${index}`} name="twitter:image" content={image} />);
52
+ const nodes: AkanHeadSnapshotNode[] = [];
53
+ const pushMeta = (name: string, content: string | undefined) => {
54
+ const node = createMetaNode({ name, content });
55
+ if (node) nodes.push(node);
56
+ };
57
+ pushMeta("twitter:card", twitter.card);
58
+ pushMeta("twitter:title", twitter.title);
59
+ pushMeta("twitter:description", twitter.description);
60
+ for (const image of normalizeStringArray(twitter.images)) {
61
+ const node = createMetaNode({ name: "twitter:image", content: image });
62
+ if (node) nodes.push(node);
39
63
  }
40
64
  return nodes;
41
65
  }
42
66
 
43
- function renderAlternates(metadata: AkanMetadata): ReactNode[] {
67
+ function createAlternateHeadSnapshotNodes(metadata: AkanMetadata): AkanHeadSnapshotNode[] {
44
68
  const alternates = metadata.alternates;
45
69
  if (!alternates) return [];
46
- const nodes: ReactNode[] = [];
47
- if (alternates.canonical) nodes.push(<link key="canonical" rel="canonical" href={alternates.canonical} />);
70
+ const nodes: AkanHeadSnapshotNode[] = [];
71
+ const canonical = createLinkNode({ rel: "canonical", href: alternates.canonical });
72
+ if (canonical) nodes.push(canonical);
48
73
  if (alternates.languages) {
49
74
  for (const [lang, href] of Object.entries(alternates.languages)) {
50
- nodes.push(<link key={`metadata:alternate:${lang}`} rel="alternate" hrefLang={lang} href={href} />);
75
+ const alternate = createLinkNode({ rel: "alternate", hrefLang: lang, href });
76
+ if (alternate) nodes.push(alternate);
51
77
  }
52
78
  }
53
79
  return nodes;
@@ -65,19 +91,67 @@ export function isAkanMetadata(value: unknown): value is AkanMetadata {
65
91
  );
66
92
  }
67
93
 
68
- export function renderMetadata(metadata: AkanMetadata): Head {
94
+ export function createAkanMetadataHeadSnapshot(metadata: AkanMetadata): AkanHeadSnapshotV1 {
95
+ const nodes: AkanHeadSnapshotNode[] = [];
96
+ if (metadata.title) nodes.push({ tag: "title", text: metadata.title });
97
+ const description = createMetaNode({ name: "description", content: metadata.description });
98
+ if (description) nodes.push(description);
99
+ const robots = createMetaNode({ name: "robots", content: metadata.robots });
100
+ if (robots) nodes.push(robots);
101
+ nodes.push(...createOpenGraphHeadSnapshotNodes(metadata));
102
+ nodes.push(...createTwitterHeadSnapshotNodes(metadata));
103
+ nodes.push(...createAlternateHeadSnapshotNodes(metadata));
104
+ return { version: AKAN_RSC_HEAD_SNAPSHOT_VERSION, nodes };
105
+ }
106
+
107
+ export function createAkanLocaleAlternateHeadSnapshot(languages: Record<string, string>): AkanHeadSnapshotV1 {
108
+ return {
109
+ version: AKAN_RSC_HEAD_SNAPSHOT_VERSION,
110
+ nodes: Object.entries(languages).map(([lang, href]) => ({
111
+ tag: "link",
112
+ attrs: { rel: "alternate", hrefLang: lang, href },
113
+ })),
114
+ };
115
+ }
116
+
117
+ export function mergeAkanHeadSnapshots(
118
+ ...snapshots: Array<AkanHeadSnapshotV1 | null | undefined>
119
+ ): AkanHeadSnapshotV1 | undefined {
120
+ const nodes = snapshots.flatMap((snapshot) => snapshot?.nodes ?? []);
121
+ return snapshots.some(Boolean) ? { version: AKAN_RSC_HEAD_SNAPSHOT_VERSION, nodes } : undefined;
122
+ }
123
+
124
+ export function renderAkanHeadSnapshot(snapshot: AkanHeadSnapshotV1, options: { markRouteOwned?: boolean } = {}): Head {
125
+ const markRouteOwned = options.markRouteOwned ?? true;
69
126
  return (
70
127
  <>
71
- {metadata.title ? <title>{metadata.title}</title> : null}
72
- {metadata.description ? <meta name="description" content={metadata.description} /> : null}
73
- {metadata.robots ? <meta name="robots" content={metadata.robots} /> : null}
74
- {renderOpenGraph(metadata)}
75
- {renderTwitter(metadata)}
76
- {renderAlternates(metadata)}
128
+ {snapshot.nodes.map((node, index) => {
129
+ const marker = markRouteOwned
130
+ ? {
131
+ "data-akan-head": "route",
132
+ "data-akan-head-key": `${node.tag}:${index}`,
133
+ }
134
+ : {};
135
+ if (node.tag === "title") {
136
+ return (
137
+ <title key={`${node.tag}:${index}`} {...marker}>
138
+ {node.text ?? ""}
139
+ </title>
140
+ );
141
+ }
142
+ if (node.tag === "meta") {
143
+ return <meta key={`${node.tag}:${index}`} {...node.attrs} {...marker} />;
144
+ }
145
+ return <link key={`${node.tag}:${index}`} {...node.attrs} {...marker} />;
146
+ })}
77
147
  </>
78
148
  );
79
149
  }
80
150
 
151
+ export function renderMetadata(metadata: AkanMetadata): Head {
152
+ return renderAkanHeadSnapshot(createAkanMetadataHeadSnapshot(metadata));
153
+ }
154
+
81
155
  export function hasExplicitLanguageAlternates(metadata: AkanMetadata | null | undefined): boolean {
82
156
  return Boolean(metadata?.alternates?.languages && Object.keys(metadata.alternates.languages).length > 0);
83
157
  }
@@ -94,14 +168,24 @@ export function isResolvedHead(value: unknown): value is ResolvedHead {
94
168
  }
95
169
 
96
170
  export function resolveMetadataHead(metadata: AkanMetadata): ResolvedHead {
171
+ const headSnapshot = createAkanMetadataHeadSnapshot(metadata);
97
172
  return {
98
- node: renderMetadata(metadata),
173
+ node: renderAkanHeadSnapshot(headSnapshot),
99
174
  hasExplicitLanguageAlternates: hasExplicitLanguageAlternates(metadata),
175
+ headSnapshot,
100
176
  };
101
177
  }
102
178
 
103
- export function resolveHeadExport(value: Head | AkanMetadata | null | undefined): ResolvedHead {
104
- return isAkanMetadata(value) ? resolveMetadataHead(value) : { node: value, hasExplicitLanguageAlternates: false };
179
+ export function resolveHeadExport(
180
+ value: Head | AkanMetadata | null | undefined,
181
+ options: { includeHeadSnapshot?: boolean } = {},
182
+ ): ResolvedHead {
183
+ if (!isAkanMetadata(value)) return { node: value, hasExplicitLanguageAlternates: false };
184
+ if (options.includeHeadSnapshot !== false) return resolveMetadataHead(value);
185
+ return {
186
+ node: renderAkanHeadSnapshot(createAkanMetadataHeadSnapshot(value), { markRouteOwned: false }),
187
+ hasExplicitLanguageAlternates: hasExplicitLanguageAlternates(value),
188
+ };
105
189
  }
106
190
 
107
191
  export function resolveHeadResult(value: ResolveHeadResult): ResolvedHead {
@@ -246,16 +246,16 @@ export class DatabaseResolver {
246
246
  return await timedQuery(() => this.__store.findIds(find, { sort, skip, limit, sample }));
247
247
  }
248
248
  async __find(query?: QueryOf<any>, queryOption?: FindQueryOption): Promise<any | null> {
249
- const { find, sort, skip, sample } = getFindQuery(query, queryOption);
250
- return await timedQuery(() => this.__store.findOne(find, { sort, skip, sample }));
249
+ const { find, sort, skip, sample, select } = getFindQuery(query, queryOption);
250
+ return await timedQuery(() => this.__store.findOne(find, { sort, skip, sample, select }));
251
251
  }
252
252
  async __findId(query?: QueryOf<any>, queryOption?: FindQueryOption): Promise<string | null> {
253
253
  const { find, sort, skip, sample } = getFindQuery(query, queryOption);
254
254
  return await timedQuery(() => this.__store.findId(find, { sort, skip, sample }));
255
255
  }
256
256
  async __pick(query?: QueryOf<any>, queryOption?: FindQueryOption): Promise<any> {
257
- const { find, sort, skip, sample } = getFindQuery(query, queryOption);
258
- return await this.__store.pickOne(find, { sort, skip, sample });
257
+ const { find, sort, skip, sample, select } = getFindQuery(query, queryOption);
258
+ return await this.__store.pickOne(find, { sort, skip, sample, select });
259
259
  }
260
260
  async __pickId(query?: QueryOf<any>, queryOption?: FindQueryOption): Promise<string> {
261
261
  const { find, sort, skip, sample } = getFindQuery(query, queryOption);
@@ -9,6 +9,9 @@ import type {
9
9
  } from "akanjs/client";
10
10
  import { Children, cloneElement, isValidElement, type ReactElement, type ReactNode, Suspense } from "react";
11
11
  import { resolveHeadResult } from "./metadata";
12
+ import { type AkanRouteSegmentState, createAkanRouteSegments, createAkanSegmentOutletKey } from "./routeState";
13
+ import { isAkanRscPartialCommitEnabled } from "./rscPartialCommit";
14
+ import { AkanSegmentOutletReference } from "./rscSegmentOutletReference";
12
15
 
13
16
  export class RouteElementComposer {
14
17
  static compose({
@@ -20,20 +23,32 @@ export class RouteElementComposer {
20
23
  params: Record<string, string>;
21
24
  searchParams: Record<string, string | string[]>;
22
25
  }): ReactNode {
23
- const renders = [...pathRoute.renderRootLayouts, ...pathRoute.renderLayouts, pathRoute.renderPage];
24
- let element: ReactNode = null;
25
- for (let i = renders.length - 1; i >= 0; i--) {
26
- const routeRender = renders[i];
27
- if (!routeRender) continue;
28
- element = (
29
- <Suspense fallback={RouteElementComposer.#composeLoadingFallback(renders.slice(i), params)}>
30
- <RouteElementComposer.AsyncRender routeRender={routeRender} params={params} searchParams={searchParams}>
31
- {element}
32
- </RouteElementComposer.AsyncRender>
33
- </Suspense>
34
- );
35
- }
36
- return element;
26
+ return RouteElementComposer.composeRenders({
27
+ renders: RouteElementComposer.#getRenderStack(pathRoute),
28
+ segments: isAkanRscPartialCommitEnabled() ? createAkanRouteSegments(pathRoute) : undefined,
29
+ params,
30
+ searchParams,
31
+ });
32
+ }
33
+
34
+ static composeSuffix({
35
+ pathRoute,
36
+ params,
37
+ searchParams,
38
+ patchStartIndex,
39
+ }: {
40
+ pathRoute: PathRoute;
41
+ params: Record<string, string>;
42
+ searchParams: Record<string, string | string[]>;
43
+ patchStartIndex: number;
44
+ }): ReactNode | null {
45
+ const renders = RouteElementComposer.#getRenderStack(pathRoute);
46
+ if (!Number.isInteger(patchStartIndex) || patchStartIndex < 0 || patchStartIndex >= renders.length) return null;
47
+ return RouteElementComposer.composeRenders({
48
+ renders: renders.slice(patchStartIndex),
49
+ params,
50
+ searchParams,
51
+ });
37
52
  }
38
53
 
39
54
  static async resolveHead({
@@ -109,10 +124,12 @@ export class RouteElementComposer {
109
124
 
110
125
  static composeRenders({
111
126
  renders,
127
+ segments,
112
128
  params,
113
129
  searchParams,
114
130
  }: {
115
131
  renders: RouteRender[];
132
+ segments?: AkanRouteSegmentState[];
116
133
  params: Record<string, string>;
117
134
  searchParams: Record<string, string | string[]>;
118
135
  }): ReactNode {
@@ -127,6 +144,17 @@ export class RouteElementComposer {
127
144
  </RouteElementComposer.AsyncRender>
128
145
  </Suspense>
129
146
  );
147
+ const segment = segments?.[i];
148
+ if (segment?.kind === "page") {
149
+ const routeSegments = segments;
150
+ if (!routeSegments) continue;
151
+ const outletKey =
152
+ createAkanSegmentOutletKey(
153
+ routeSegments.slice(0, i + 1).map((item) => item.key),
154
+ i,
155
+ ) ?? segment.key;
156
+ element = <AkanSegmentOutletReference segmentKey={outletKey}>{element}</AkanSegmentOutletReference>;
157
+ }
130
158
  }
131
159
  return element;
132
160
  }
@@ -199,6 +227,10 @@ export class RouteElementComposer {
199
227
  return RouteElementComposer.#normalizeReactNode(children);
200
228
  }
201
229
 
230
+ static #getRenderStack(pathRoute: PathRoute): RouteRender[] {
231
+ return [...pathRoute.renderRootLayouts, ...pathRoute.renderLayouts, pathRoute.renderPage];
232
+ }
233
+
202
234
  static #composeLoadingFallback(renders: RouteRender[], params: Record<string, string>): ReactNode {
203
235
  let element: ReactNode = null;
204
236
  for (let i = renders.length - 1; i >= 0; i--) {