vinext 0.1.7 → 0.2.0

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 (279) hide show
  1. package/README.md +51 -30
  2. package/dist/build/inject-pregenerated-paths.d.ts +4 -0
  3. package/dist/build/inject-pregenerated-paths.js +18 -0
  4. package/dist/build/layout-classification.js +1 -1
  5. package/dist/build/nitro-route-rules.js +1 -1
  6. package/dist/build/pages-client-assets-module.d.ts +11 -0
  7. package/dist/build/pages-client-assets-module.js +27 -0
  8. package/dist/build/prerender.d.ts +2 -1
  9. package/dist/build/prerender.js +13 -6
  10. package/dist/build/report.d.ts +13 -2
  11. package/dist/build/report.js +99 -2
  12. package/dist/build/run-prerender.d.ts +7 -0
  13. package/dist/build/run-prerender.js +10 -1
  14. package/dist/build/standalone.js +2 -0
  15. package/dist/cache/cache-adapters-virtual.js +3 -3
  16. package/dist/check.d.ts +18 -0
  17. package/dist/check.js +77 -19
  18. package/dist/cli-args.d.ts +1 -2
  19. package/dist/cli-args.js +1 -1
  20. package/dist/cli-dev-config.d.ts +12 -0
  21. package/dist/cli-dev-config.js +23 -0
  22. package/dist/cli.js +113 -97
  23. package/dist/{server → client}/dev-error-overlay-store.d.ts +1 -1
  24. package/dist/{server → client}/dev-error-overlay-store.js +1 -1
  25. package/dist/{server → client}/dev-error-overlay.d.ts +1 -1
  26. package/dist/{server → client}/dev-error-overlay.js +2 -2
  27. package/dist/client/navigation-runtime.d.ts +2 -0
  28. package/dist/client/navigation-runtime.js +3 -1
  29. package/dist/client/window-next.d.ts +2 -1
  30. package/dist/cloudflare/index.d.ts +2 -2
  31. package/dist/cloudflare/index.js +2 -2
  32. package/dist/config/config-matchers.js +14 -10
  33. package/dist/config/next-config.d.ts +6 -3
  34. package/dist/config/next-config.js +47 -1
  35. package/dist/config/prerender.d.ts +34 -0
  36. package/dist/config/prerender.js +55 -0
  37. package/dist/config/server-external-packages.d.ts +4 -0
  38. package/dist/config/server-external-packages.js +91 -0
  39. package/dist/entries/app-rsc-entry.d.ts +2 -1
  40. package/dist/entries/app-rsc-entry.js +89 -15
  41. package/dist/entries/app-rsc-manifest.js +10 -0
  42. package/dist/entries/pages-client-entry.d.ts +3 -0
  43. package/dist/entries/pages-client-entry.js +16 -1
  44. package/dist/entries/pages-server-entry.js +6 -2
  45. package/dist/image/image-adapters-virtual.d.ts +59 -0
  46. package/dist/image/image-adapters-virtual.js +59 -0
  47. package/dist/index.d.ts +33 -0
  48. package/dist/index.js +211 -118
  49. package/dist/init-cloudflare.d.ts +43 -0
  50. package/dist/init-cloudflare.js +839 -0
  51. package/dist/init-platform.d.ts +40 -0
  52. package/dist/init-platform.js +195 -0
  53. package/dist/init.d.ts +16 -38
  54. package/dist/init.js +208 -97
  55. package/dist/node_modules/.pnpm/am-i-vibing@0.5.0/node_modules/am-i-vibing/dist/detector-1yx2Hoe0.js +294 -0
  56. package/dist/node_modules/.pnpm/process-ancestry@0.1.0/node_modules/process-ancestry/dist/index.js +94 -0
  57. package/dist/{cloudflare → packages/cloudflare}/src/cache/cdn-adapter.runtime.js +1 -1
  58. package/dist/{cloudflare → packages/cloudflare}/src/cache/kv-data-adapter.runtime.d.ts +2 -2
  59. package/dist/{cloudflare → packages/cloudflare}/src/cache/kv-data-adapter.runtime.js +1 -1
  60. package/dist/packages/cloudflare/src/deploy-config.js +150 -0
  61. package/dist/packages/cloudflare/src/deploy-help.js +55 -0
  62. package/dist/packages/cloudflare/src/deploy.js +276 -0
  63. package/dist/{cloudflare → packages/cloudflare/src}/tpr.d.ts +4 -41
  64. package/dist/{cloudflare → packages/cloudflare/src}/tpr.js +18 -15
  65. package/dist/plugins/ast-scope.d.ts +16 -0
  66. package/dist/plugins/ast-scope.js +62 -0
  67. package/dist/plugins/ast-utils.d.ts +27 -1
  68. package/dist/plugins/ast-utils.js +32 -1
  69. package/dist/plugins/css-module-imports.d.ts +14 -0
  70. package/dist/plugins/css-module-imports.js +59 -0
  71. package/dist/plugins/extensionless-dynamic-import.js +2 -2
  72. package/dist/plugins/ignore-dynamic-requests.d.ts +11 -0
  73. package/dist/plugins/ignore-dynamic-requests.js +530 -0
  74. package/dist/plugins/middleware-server-only.d.ts +8 -6
  75. package/dist/plugins/middleware-server-only.js +8 -7
  76. package/dist/plugins/optimize-imports.js +1 -1
  77. package/dist/plugins/typeof-window.d.ts +1 -1
  78. package/dist/plugins/typeof-window.js +28 -56
  79. package/dist/routing/app-route-graph.d.ts +29 -4
  80. package/dist/routing/app-route-graph.js +182 -40
  81. package/dist/routing/app-router.d.ts +5 -0
  82. package/dist/routing/app-router.js +5 -0
  83. package/dist/routing/file-matcher.d.ts +8 -0
  84. package/dist/routing/file-matcher.js +10 -1
  85. package/dist/routing/pages-router.js +2 -2
  86. package/dist/server/api-handler.js +9 -14
  87. package/dist/server/app-browser-action-result.d.ts +4 -1
  88. package/dist/server/app-browser-action-result.js +8 -1
  89. package/dist/server/app-browser-entry.js +194 -190
  90. package/dist/server/app-browser-history-controller.d.ts +2 -1
  91. package/dist/server/app-browser-history-controller.js +6 -2
  92. package/dist/server/app-browser-interception-context.d.ts +1 -0
  93. package/dist/server/app-browser-interception-context.js +4 -2
  94. package/dist/server/app-browser-navigation-controller.d.ts +2 -0
  95. package/dist/server/app-browser-navigation-controller.js +60 -31
  96. package/dist/server/app-browser-server-action-client.d.ts +32 -0
  97. package/dist/server/app-browser-server-action-client.js +127 -0
  98. package/dist/server/app-browser-state.d.ts +11 -1
  99. package/dist/server/app-browser-state.js +25 -8
  100. package/dist/server/app-browser-visible-commit.d.ts +6 -1
  101. package/dist/server/app-browser-visible-commit.js +43 -25
  102. package/dist/server/app-elements-wire.d.ts +9 -2
  103. package/dist/server/app-elements-wire.js +8 -1
  104. package/dist/server/app-elements.d.ts +2 -2
  105. package/dist/server/app-elements.js +4 -3
  106. package/dist/server/app-fallback-renderer.d.ts +5 -2
  107. package/dist/server/app-fallback-renderer.js +15 -7
  108. package/dist/server/app-hydration-cache-publication.d.ts +11 -0
  109. package/dist/server/app-hydration-cache-publication.js +43 -0
  110. package/dist/server/app-middleware.d.ts +4 -3
  111. package/dist/server/app-middleware.js +8 -3
  112. package/dist/server/app-optimistic-routing.js +11 -4
  113. package/dist/server/app-page-boundary-render.d.ts +3 -0
  114. package/dist/server/app-page-boundary-render.js +14 -3
  115. package/dist/server/app-page-cache-finalizer.d.ts +1 -0
  116. package/dist/server/app-page-cache-finalizer.js +10 -3
  117. package/dist/server/app-page-cache-render.d.ts +1 -0
  118. package/dist/server/app-page-cache-render.js +8 -4
  119. package/dist/server/app-page-cache.d.ts +1 -0
  120. package/dist/server/app-page-cache.js +5 -2
  121. package/dist/server/app-page-dispatch.d.ts +33 -4
  122. package/dist/server/app-page-dispatch.js +63 -90
  123. package/dist/server/app-page-element-builder.d.ts +9 -2
  124. package/dist/server/app-page-element-builder.js +114 -23
  125. package/dist/server/app-page-head.d.ts +16 -1
  126. package/dist/server/app-page-head.js +44 -22
  127. package/dist/server/app-page-params.d.ts +2 -1
  128. package/dist/server/app-page-params.js +8 -1
  129. package/dist/server/app-page-ppr-runtime.d.ts +7 -0
  130. package/dist/server/app-page-ppr-runtime.js +70 -0
  131. package/dist/server/app-page-probe.d.ts +1 -0
  132. package/dist/server/app-page-probe.js +6 -1
  133. package/dist/server/app-page-render-identity.d.ts +1 -0
  134. package/dist/server/app-page-render-identity.js +1 -1
  135. package/dist/server/app-page-render.d.ts +4 -1
  136. package/dist/server/app-page-render.js +26 -3
  137. package/dist/server/app-page-request.d.ts +22 -1
  138. package/dist/server/app-page-request.js +92 -15
  139. package/dist/server/app-page-route-wiring.d.ts +14 -2
  140. package/dist/server/app-page-route-wiring.js +61 -25
  141. package/dist/server/app-page-search-params-observation.d.ts +4 -2
  142. package/dist/server/app-page-search-params-observation.js +11 -7
  143. package/dist/server/app-page-segment-state.d.ts +2 -1
  144. package/dist/server/app-page-segment-state.js +3 -1
  145. package/dist/server/app-page-stream.d.ts +5 -0
  146. package/dist/server/app-page-stream.js +3 -1
  147. package/dist/server/app-ppr-fallback-shell-render.d.ts +4 -3
  148. package/dist/server/app-route-handler-dispatch.js +1 -0
  149. package/dist/server/app-route-handler-execution.js +7 -2
  150. package/dist/server/app-route-handler-response.js +1 -0
  151. package/dist/server/app-route-handler-runtime.js +1 -1
  152. package/dist/server/app-route-module-loader.d.ts +2 -0
  153. package/dist/server/app-route-module-loader.js +1 -0
  154. package/dist/server/app-router-entry.js +16 -8
  155. package/dist/server/app-rsc-errors.js +7 -1
  156. package/dist/server/app-rsc-handler.d.ts +3 -1
  157. package/dist/server/app-rsc-handler.js +67 -39
  158. package/dist/server/app-rsc-request-normalization.d.ts +3 -2
  159. package/dist/server/app-rsc-request-normalization.js +6 -3
  160. package/dist/server/app-rsc-route-matching.d.ts +7 -0
  161. package/dist/server/app-rsc-route-matching.js +36 -3
  162. package/dist/server/app-segment-config.d.ts +12 -0
  163. package/dist/server/app-segment-config.js +91 -5
  164. package/dist/server/app-server-action-execution.d.ts +5 -0
  165. package/dist/server/app-server-action-execution.js +94 -33
  166. package/dist/server/app-ssr-entry.d.ts +3 -0
  167. package/dist/server/app-ssr-entry.js +19 -8
  168. package/dist/server/app-ssr-stream.d.ts +7 -3
  169. package/dist/server/app-ssr-stream.js +10 -6
  170. package/dist/server/app-static-generation.d.ts +1 -0
  171. package/dist/server/app-static-generation.js +1 -0
  172. package/dist/server/app-visited-response-cache.d.ts +6 -0
  173. package/dist/server/app-visited-response-cache.js +3 -1
  174. package/dist/server/client-trace-metadata.js +26 -0
  175. package/dist/server/default-global-not-found-module.d.ts +14 -0
  176. package/dist/server/default-global-not-found-module.js +14 -0
  177. package/dist/server/dev-route-files.js +4 -0
  178. package/dist/server/dev-server.js +19 -18
  179. package/dist/server/dev-stack-sourcemap.d.ts +1 -1
  180. package/dist/server/dev-stack-sourcemap.js +1 -1
  181. package/dist/server/fetch-handler.d.ts +2 -0
  182. package/dist/server/fetch-handler.js +18 -0
  183. package/dist/server/headers.d.ts +5 -15
  184. package/dist/server/headers.js +4 -15
  185. package/dist/server/image-optimization.d.ts +51 -1
  186. package/dist/server/image-optimization.js +52 -2
  187. package/dist/server/implicit-tags.js +1 -1
  188. package/dist/server/isr-cache.d.ts +1 -1
  189. package/dist/server/isr-cache.js +2 -2
  190. package/dist/server/metadata-routes.d.ts +3 -1
  191. package/dist/server/metadata-routes.js +12 -2
  192. package/dist/server/middleware-runtime.js +6 -1
  193. package/dist/server/navigation-planner.d.ts +1 -0
  194. package/dist/server/navigation-planner.js +14 -3
  195. package/dist/server/pages-api-route.js +2 -2
  196. package/dist/server/pages-asset-tags.d.ts +4 -6
  197. package/dist/server/pages-asset-tags.js +12 -12
  198. package/dist/server/pages-client-assets.d.ts +12 -0
  199. package/dist/server/pages-client-assets.js +10 -0
  200. package/dist/server/pages-node-compat.d.ts +8 -1
  201. package/dist/server/pages-node-compat.js +32 -4
  202. package/dist/server/pages-page-data.d.ts +24 -1
  203. package/dist/server/pages-page-data.js +45 -24
  204. package/dist/server/pages-page-handler.d.ts +2 -1
  205. package/dist/server/pages-page-handler.js +19 -12
  206. package/dist/server/pages-page-response.d.ts +7 -0
  207. package/dist/server/pages-page-response.js +4 -1
  208. package/dist/server/pages-request-pipeline.d.ts +2 -0
  209. package/dist/server/pages-request-pipeline.js +25 -1
  210. package/dist/server/pages-router-entry.d.ts +30 -0
  211. package/dist/server/pages-router-entry.js +108 -0
  212. package/dist/server/prerender-manifest.d.ts +3 -1
  213. package/dist/server/prerender-route-params.js +1 -1
  214. package/dist/server/prod-server.d.ts +1 -1
  215. package/dist/server/prod-server.js +48 -26
  216. package/dist/server/request-pipeline.js +1 -0
  217. package/dist/server/seed-cache.js +4 -4
  218. package/dist/server/worker-utils.d.ts +2 -1
  219. package/dist/server/worker-utils.js +7 -1
  220. package/dist/shims/app-router-scroll-state.d.ts +1 -0
  221. package/dist/shims/app-router-scroll-state.js +1 -0
  222. package/dist/shims/app-router-scroll.js +2 -1
  223. package/dist/shims/cache.js +19 -15
  224. package/dist/shims/cdn-cache.js +1 -1
  225. package/dist/shims/dynamic-preload-chunks.js +2 -1
  226. package/dist/shims/error-boundary.d.ts +19 -1
  227. package/dist/shims/error-boundary.js +11 -1
  228. package/dist/shims/form.d.ts +3 -1
  229. package/dist/shims/form.js +37 -43
  230. package/dist/shims/headers.d.ts +9 -1
  231. package/dist/shims/headers.js +31 -6
  232. package/dist/shims/image.js +75 -14
  233. package/dist/shims/internal/app-route-detection.d.ts +2 -17
  234. package/dist/shims/internal/app-route-detection.js +4 -17
  235. package/dist/shims/internal/hybrid-client-route-owner-direct.d.ts +23 -0
  236. package/dist/shims/internal/hybrid-client-route-owner-direct.js +51 -0
  237. package/dist/shims/internal/hybrid-client-route-owner.d.ts +2 -15
  238. package/dist/shims/internal/hybrid-client-route-owner.js +9 -60
  239. package/dist/shims/internal/pages-data-fetch-dedup.d.ts +9 -5
  240. package/dist/shims/internal/pages-data-fetch-dedup.js +73 -14
  241. package/dist/shims/internal/pages-data-target.d.ts +10 -9
  242. package/dist/shims/internal/pages-data-target.js +91 -12
  243. package/dist/shims/internal/pages-router-components.d.ts +7 -0
  244. package/dist/shims/internal/pages-router-components.js +13 -0
  245. package/dist/shims/layout-segment-context.d.ts +4 -1
  246. package/dist/shims/layout-segment-context.js +18 -4
  247. package/dist/shims/link.js +70 -37
  248. package/dist/shims/metadata.d.ts +3 -2
  249. package/dist/shims/metadata.js +8 -4
  250. package/dist/shims/navigation.d.ts +5 -1
  251. package/dist/shims/navigation.js +45 -10
  252. package/dist/shims/navigation.react-server.js +1 -1
  253. package/dist/shims/root-params.d.ts +15 -1
  254. package/dist/shims/root-params.js +21 -1
  255. package/dist/shims/router.d.ts +5 -7
  256. package/dist/shims/router.js +374 -94
  257. package/dist/shims/server.js +3 -2
  258. package/dist/shims/slot.js +3 -6
  259. package/dist/typegen.js +6 -5
  260. package/dist/utils/client-runtime-metadata.d.ts +2 -18
  261. package/dist/utils/client-runtime-metadata.js +31 -22
  262. package/dist/utils/dev-stack-sourcemap-endpoint.d.ts +4 -0
  263. package/dist/{server → utils}/dev-stack-sourcemap-endpoint.js +1 -1
  264. package/dist/utils/domain-locale.d.ts +6 -3
  265. package/dist/{server → utils}/middleware-request-headers.d.ts +1 -1
  266. package/dist/{server → utils}/middleware-request-headers.js +2 -2
  267. package/dist/utils/path.d.ts +2 -1
  268. package/dist/utils/path.js +1 -1
  269. package/dist/utils/project.d.ts +52 -2
  270. package/dist/utils/project.js +230 -5
  271. package/dist/utils/protocol-headers.d.ts +17 -0
  272. package/dist/utils/protocol-headers.js +17 -0
  273. package/dist/utils/react-version.d.ts +4 -0
  274. package/dist/utils/react-version.js +44 -0
  275. package/package.json +53 -4
  276. package/dist/deploy.d.ts +0 -190
  277. package/dist/deploy.js +0 -1033
  278. package/dist/server/dev-stack-sourcemap-endpoint.d.ts +0 -4
  279. /package/dist/{cloudflare → packages/cloudflare}/src/utils/cache-control-metadata.js +0 -0
@@ -1,8 +1,8 @@
1
1
  import { normalizeAppElementsSlotBindings } from "./app-elements-wire.js";
2
2
  import "./app-elements.js";
3
3
  import { mergeElements } from "../shims/slot.js";
4
- import { NavigationTraceReasonCodes, NavigationTraceTransactionCodes, createNavigationTrace, prependNavigationTraceEntry } from "./navigation-trace.js";
5
4
  import { preserveBfcacheIdsForMergedElements } from "./app-bfcache-identity.js";
5
+ import { NavigationTraceReasonCodes, NavigationTraceTransactionCodes, createNavigationTrace, prependNavigationTraceEntry } from "./navigation-trace.js";
6
6
  import { createPendingNavigationCommit, resolvePendingNavigationCommitDispositionDecision } from "./app-browser-state.js";
7
7
  //#region src/server/app-browser-visible-commit.ts
8
8
  const approvedVisibleCommitBrand = Symbol("ApprovedVisibleCommit");
@@ -17,6 +17,8 @@ function commitOperationRecord(operation, visibleCommitVersion) {
17
17
  return {
18
18
  id: operation.id,
19
19
  lane: operation.lane,
20
+ ...operation.navigationCommitKind !== void 0 ? { navigationCommitKind: operation.navigationCommitKind } : {},
21
+ ...operation.navigationId !== void 0 ? { navigationId: operation.navigationId } : {},
20
22
  startedVisibleCommitVersion: operation.startedVisibleCommitVersion,
21
23
  state: "committed",
22
24
  visibleCommitVersion
@@ -56,9 +58,23 @@ function reduceApprovedVisibleCommitState(state, commit) {
56
58
  const { action } = commit;
57
59
  switch (action.type) {
58
60
  case "traverse":
59
- case "navigate": {
60
- const preserveElementIds = action.reuseCurrentBfcacheIds ? commit.decision.preserveElementIds : [];
61
- const preservePreviousSlotIds = action.reuseCurrentBfcacheIds ? commit.decision.preservePreviousSlotIds : [];
61
+ case "navigate":
62
+ case "replace": {
63
+ const bfcacheCompatiblePreserveElementIds = action.reuseCurrentBfcacheIds && action.operation.lane !== "refresh" ? commit.decision.preserveElementIds.filter((id) => {
64
+ const previousBfcacheId = state.bfcacheIds[id];
65
+ return previousBfcacheId !== void 0 && action.bfcacheIds[id] === previousBfcacheId;
66
+ }) : [];
67
+ const preservedSlotOwnerElementIdSet = new Set(bfcacheCompatiblePreserveElementIds);
68
+ const preservePreviousSlotIds = action.reuseCurrentBfcacheIds ? commit.decision.preservePreviousSlotIds.filter((slotId) => {
69
+ const targetBinding = action.slotBindings.find((binding) => binding.slotId === slotId);
70
+ return targetBinding?.ownerLayoutId !== null && targetBinding?.ownerLayoutId !== void 0 && preservedSlotOwnerElementIdSet.has(targetBinding.ownerLayoutId);
71
+ }) : [];
72
+ const hmrPreservedSlotOwnerLayoutIds = action.operation.lane === "hmr" ? bfcacheCompatiblePreserveElementIds.filter((id) => preservePreviousSlotIds.some((slotId) => {
73
+ return action.slotBindings.find((binding) => binding.slotId === slotId)?.ownerLayoutId === id;
74
+ })) : [];
75
+ const hmrPreserveElementIds = action.operation.lane === "hmr" && state.routeId === action.routeId && Object.hasOwn(state.elements, state.routeId) ? [state.routeId, ...hmrPreservedSlotOwnerLayoutIds] : hmrPreservedSlotOwnerLayoutIds;
76
+ const hmrUniquePreserveElementIds = hmrPreserveElementIds.length > 1 ? [...new Set(hmrPreserveElementIds)] : hmrPreserveElementIds;
77
+ const preserveElementIds = action.operation.lane === "hmr" ? hmrUniquePreserveElementIds : bfcacheCompatiblePreserveElementIds;
62
78
  const mergedElements = mergeElements(state.elements, action.elements, {
63
79
  clearAbsentSlots: action.type === "traverse" || !action.reuseCurrentBfcacheIds,
64
80
  preserveAbsentSlots: action.reuseCurrentBfcacheIds && commit.decision.preserveAbsentSlots,
@@ -84,20 +100,6 @@ function reduceApprovedVisibleCommitState(state, commit) {
84
100
  slotBindings: mergeSlotBindings(state.slotBindings, action.slotBindings, action.layoutIds, preservePreviousSlotIds)
85
101
  }, action.operation);
86
102
  }
87
- case "replace": return commitVisibleRouterState(state, {
88
- bfcacheIds: action.bfcacheIds,
89
- elements: action.elements,
90
- interception: action.interception,
91
- interceptionContext: action.interceptionContext,
92
- layoutFlags: action.layoutFlags,
93
- layoutIds: action.layoutIds,
94
- navigationSnapshot: action.navigationSnapshot,
95
- previousNextUrl: action.previousNextUrl,
96
- renderId: action.renderId,
97
- rootLayoutTreePath: action.rootLayoutTreePath,
98
- routeId: action.routeId,
99
- slotBindings: action.slotBindings
100
- }, action.operation);
101
103
  default: {
102
104
  const _exhaustive = action.type;
103
105
  throw new Error("[vinext] Unknown router action: " + String(_exhaustive));
@@ -176,14 +178,28 @@ function addCommitTransactionTrace(decision, pending) {
176
178
  default: throw new Error("[vinext] Unknown commit decision: " + String(decision));
177
179
  }
178
180
  }
179
- function approveHmrVisibleCommit(pending) {
181
+ function approveHmrVisibleCommit(options) {
182
+ const { currentState, pending } = options;
180
183
  if (pending.action.operation.lane !== "hmr") throw new Error("[vinext] HMR visible commit approval requires an HMR pending operation");
181
- const decision = addCommitTransactionTrace(createVisibleCommitDecision(), pending);
182
- if (decision.disposition !== "commit") throw new Error("[vinext] HMR visible commit approval did not produce a commit decision");
183
- return createApprovedVisibleCommit({
184
- decision,
185
- pending
186
- });
184
+ const tracedDecision = addCommitTransactionTrace(resolvePendingNavigationCommitDecision({
185
+ activeNavigationId: pending.action.operation.id,
186
+ currentState,
187
+ pending,
188
+ routeManifest: options.routeManifest,
189
+ startedNavigationId: pending.action.operation.id,
190
+ targetHref: options.targetHref
191
+ }), pending);
192
+ if (tracedDecision.disposition === "commit") return {
193
+ approvedCommit: createApprovedVisibleCommit({
194
+ decision: tracedDecision,
195
+ pending
196
+ }),
197
+ decision: tracedDecision
198
+ };
199
+ return {
200
+ approvedCommit: null,
201
+ decision: tracedDecision
202
+ };
187
203
  }
188
204
  function approvePendingNavigationCommit(options) {
189
205
  const decision = addCommitTransactionTrace(resolvePendingNavigationCommitDecision({
@@ -213,6 +229,8 @@ function approvePendingNavigationCommit(options) {
213
229
  async function resolveAndClassifyNavigationCommit(options) {
214
230
  const pending = await createPendingNavigationCommit({
215
231
  currentState: options.currentState,
232
+ navigationCommitKind: void 0,
233
+ navigationId: options.startedNavigationId,
216
234
  nextElements: options.nextElements,
217
235
  navigationSnapshot: options.navigationSnapshot,
218
236
  operationLane: options.operationLane,
@@ -6,6 +6,7 @@ import { ReactNode } from "react";
6
6
  //#region src/server/app-elements-wire.d.ts
7
7
  declare const APP_ARTIFACT_COMPATIBILITY_KEY = "__artifactCompatibility";
8
8
  declare const APP_CACHE_ENTRY_REUSE_PROOF_KEY = "__cacheEntryReuseProof";
9
+ declare const APP_DYNAMIC_STALE_TIME_KEY = "__dynamicStaleTime";
9
10
  declare const APP_INTERCEPTION_KEY = "__interception";
10
11
  declare const APP_INTERCEPTION_CONTEXT_KEY = "__interceptionContext";
11
12
  declare const APP_LAYOUT_IDS_KEY = "__layoutIds";
@@ -70,6 +71,7 @@ type LayoutFlags = Readonly<Record<string, "s" | "d">>;
70
71
  type AppElementsMetadata = {
71
72
  artifactCompatibility: ArtifactCompatibilityEnvelope;
72
73
  cacheEntryReuseProof?: CacheEntryReuseProof;
74
+ dynamicStaleTimeSeconds?: number;
73
75
  interception: AppElementsInterception | null;
74
76
  interceptionContext: string | null;
75
77
  layoutIds: readonly string[];
@@ -100,6 +102,7 @@ type AppElementsWireElementKey = {
100
102
  treePath: string;
101
103
  };
102
104
  type AppElementsWireMetadataInput = {
105
+ dynamicStaleTimeSeconds?: number;
103
106
  interception?: AppElementsInterception | null;
104
107
  interceptionContext: string | null;
105
108
  layoutIds?: readonly string[];
@@ -109,6 +112,7 @@ type AppElementsWireMetadataInput = {
109
112
  sourcePage?: string | null;
110
113
  };
111
114
  type AppElementsWireMetadataEntries = Readonly<{
115
+ [APP_DYNAMIC_STALE_TIME_KEY]?: number;
112
116
  [APP_ROUTE_KEY]: string;
113
117
  [APP_INTERCEPTION_KEY]?: AppElementsInterception;
114
118
  [APP_INTERCEPTION_CONTEXT_KEY]: string | null;
@@ -123,10 +127,11 @@ type AppElementsWireMetadataEntries = Readonly<{
123
127
  * known keys (e.g. __layoutFlags). Distinct from AppElements / AppWireElements
124
128
  * which only carry render-time values.
125
129
  */
126
- type AppOutgoingElements = Readonly<Record<string, ReactNode | LayoutFlags | ArtifactCompatibilityEnvelope | CacheEntryReuseProof | AppElementsInterception | RenderObservation | readonly string[] | readonly AppElementsSlotBinding[]>>;
130
+ type AppOutgoingElements = Readonly<Record<string, ReactNode | LayoutFlags | ArtifactCompatibilityEnvelope | CacheEntryReuseProof | AppElementsInterception | RenderObservation | number | readonly string[] | readonly AppElementsSlotBinding[]>>;
127
131
  type AppElementsWireKeys = {
128
132
  readonly artifactCompatibility: typeof APP_ARTIFACT_COMPATIBILITY_KEY;
129
133
  readonly cacheEntryReuseProof: typeof APP_CACHE_ENTRY_REUSE_PROOF_KEY;
134
+ readonly dynamicStaleTime: typeof APP_DYNAMIC_STALE_TIME_KEY;
130
135
  readonly interception: typeof APP_INTERCEPTION_KEY;
131
136
  readonly interceptionContext: typeof APP_INTERCEPTION_CONTEXT_KEY;
132
137
  readonly layoutIds: typeof APP_LAYOUT_IDS_KEY;
@@ -149,6 +154,7 @@ type AppElementsWireCodec = {
149
154
  element: ReactNode | AppElements;
150
155
  artifactCompatibility?: ArtifactCompatibilityEnvelope;
151
156
  cacheEntryReuseProof?: CacheEntryReuseProof;
157
+ dynamicStaleTimeSeconds?: number;
152
158
  layoutFlags: LayoutFlags;
153
159
  renderObservation?: RenderObservation;
154
160
  skipDisposition?: ClientReuseManifestSkipDisposition;
@@ -182,6 +188,7 @@ declare function buildOutgoingAppPayload(input: {
182
188
  element: ReactNode | AppElements;
183
189
  artifactCompatibility?: ArtifactCompatibilityEnvelope;
184
190
  cacheEntryReuseProof?: CacheEntryReuseProof;
191
+ dynamicStaleTimeSeconds?: number;
185
192
  layoutFlags: LayoutFlags;
186
193
  renderObservation?: RenderObservation;
187
194
  skipDisposition?: ClientReuseManifestSkipDisposition;
@@ -189,4 +196,4 @@ declare function buildOutgoingAppPayload(input: {
189
196
  declare function readAppElementsMetadata(elements: Readonly<Record<string, unknown>>): AppElementsMetadata;
190
197
  declare const AppElementsWire: AppElementsWireCodec;
191
198
  //#endregion
192
- export { APP_ARTIFACT_COMPATIBILITY_KEY, APP_CACHE_ENTRY_REUSE_PROOF_KEY, APP_INTERCEPTION_CONTEXT_KEY, APP_INTERCEPTION_KEY, APP_LAYOUT_FLAGS_KEY, APP_LAYOUT_IDS_KEY, APP_RENDER_OBSERVATION_KEY, APP_ROOT_LAYOUT_KEY, APP_ROUTE_KEY, APP_SKIPPED_LAYOUT_IDS_KEY, APP_SLOT_BINDINGS_KEY, APP_SOURCE_PAGE_KEY, APP_STATIC_SIBLINGS_KEY, APP_UNMATCHED_SLOT_WIRE_VALUE, AppElementValue, AppElements, AppElementsInterception, AppElementsSlotBinding, AppElementsWire, AppOutgoingElements, AppWireElements, LayoutFlags, UNMATCHED_SLOT, buildOutgoingAppPayload, compareAppElementsSlotIds, isAppElementsRecord, normalizeAppElements, normalizeAppElementsSlotBindings, readAppElementsMetadata, withLayoutFlags };
199
+ export { APP_ARTIFACT_COMPATIBILITY_KEY, APP_CACHE_ENTRY_REUSE_PROOF_KEY, APP_DYNAMIC_STALE_TIME_KEY, APP_INTERCEPTION_CONTEXT_KEY, APP_INTERCEPTION_KEY, APP_LAYOUT_FLAGS_KEY, APP_LAYOUT_IDS_KEY, APP_RENDER_OBSERVATION_KEY, APP_ROOT_LAYOUT_KEY, APP_ROUTE_KEY, APP_SKIPPED_LAYOUT_IDS_KEY, APP_SLOT_BINDINGS_KEY, APP_SOURCE_PAGE_KEY, APP_STATIC_SIBLINGS_KEY, APP_UNMATCHED_SLOT_WIRE_VALUE, AppElementValue, AppElements, AppElementsInterception, AppElementsSlotBinding, AppElementsWire, AppOutgoingElements, AppWireElements, LayoutFlags, UNMATCHED_SLOT, buildOutgoingAppPayload, compareAppElementsSlotIds, isAppElementsRecord, normalizeAppElements, normalizeAppElementsSlotBindings, readAppElementsMetadata, withLayoutFlags };
@@ -8,6 +8,7 @@ import { isValidElement } from "react";
8
8
  const APP_INTERCEPTION_SEPARATOR = "\0";
9
9
  const APP_ARTIFACT_COMPATIBILITY_KEY = "__artifactCompatibility";
10
10
  const APP_CACHE_ENTRY_REUSE_PROOF_KEY = "__cacheEntryReuseProof";
11
+ const APP_DYNAMIC_STALE_TIME_KEY = "__dynamicStaleTime";
11
12
  const APP_INTERCEPTION_KEY = "__interception";
12
13
  const APP_INTERCEPTION_CONTEXT_KEY = "__interceptionContext";
13
14
  const APP_LAYOUT_IDS_KEY = "__layoutIds";
@@ -177,6 +178,7 @@ function createAppElementsWireMetadataEntries(input) {
177
178
  [APP_INTERCEPTION_CONTEXT_KEY]: input.interceptionContext,
178
179
  [APP_LAYOUT_IDS_KEY]: layoutIds,
179
180
  [APP_ROOT_LAYOUT_KEY]: input.rootLayoutTreePath,
181
+ ...input.dynamicStaleTimeSeconds === void 0 ? {} : { [APP_DYNAMIC_STALE_TIME_KEY]: input.dynamicStaleTimeSeconds },
180
182
  ...input.sourcePage === null || input.sourcePage === void 0 ? {} : { [APP_SOURCE_PAGE_KEY]: input.sourcePage }
181
183
  };
182
184
  const entriesWithInterception = input.interception ? {
@@ -319,6 +321,7 @@ function buildOutgoingAppPayload(input) {
319
321
  if (skippedLayoutIds.size > 0) payload[APP_SKIPPED_LAYOUT_IDS_KEY] = [...skippedLayoutIds];
320
322
  payload[APP_ARTIFACT_COMPATIBILITY_KEY] = input.artifactCompatibility ?? createArtifactCompatibilityEnvelope();
321
323
  if (input.cacheEntryReuseProof) payload[APP_CACHE_ENTRY_REUSE_PROOF_KEY] = input.cacheEntryReuseProof;
324
+ if (input.dynamicStaleTimeSeconds !== void 0) payload[APP_DYNAMIC_STALE_TIME_KEY] = input.dynamicStaleTimeSeconds;
322
325
  if (input.renderObservation) payload[APP_RENDER_OBSERVATION_KEY] = input.renderObservation;
323
326
  return payload;
324
327
  }
@@ -394,10 +397,13 @@ function readAppElementsMetadata(elements) {
394
397
  const interception = parseInterceptionMetadata(elements[APP_INTERCEPTION_KEY]);
395
398
  const artifactCompatibility = readArtifactCompatibilityMetadata(elements[APP_ARTIFACT_COMPATIBILITY_KEY]);
396
399
  const cacheEntryReuseProof = parseCacheEntryReuseProofMetadata(elements[APP_CACHE_ENTRY_REUSE_PROOF_KEY]);
400
+ const dynamicStaleTime = elements[APP_DYNAMIC_STALE_TIME_KEY];
401
+ const dynamicStaleTimeSeconds = typeof dynamicStaleTime === "number" && Number.isFinite(dynamicStaleTime) && dynamicStaleTime >= 0 ? dynamicStaleTime : void 0;
397
402
  const sourcePage = readSourcePageMetadata(elements[APP_SOURCE_PAGE_KEY]);
398
403
  return {
399
404
  artifactCompatibility,
400
405
  ...cacheEntryReuseProof ? { cacheEntryReuseProof } : {},
406
+ ...dynamicStaleTimeSeconds === void 0 ? {} : { dynamicStaleTimeSeconds },
401
407
  interception,
402
408
  interceptionContext: interceptionContext ?? null,
403
409
  layoutIds,
@@ -413,6 +419,7 @@ const AppElementsWire = {
413
419
  keys: {
414
420
  artifactCompatibility: APP_ARTIFACT_COMPATIBILITY_KEY,
415
421
  cacheEntryReuseProof: APP_CACHE_ENTRY_REUSE_PROOF_KEY,
422
+ dynamicStaleTime: APP_DYNAMIC_STALE_TIME_KEY,
416
423
  interception: APP_INTERCEPTION_KEY,
417
424
  interceptionContext: APP_INTERCEPTION_CONTEXT_KEY,
418
425
  layoutIds: APP_LAYOUT_IDS_KEY,
@@ -440,4 +447,4 @@ const AppElementsWire = {
440
447
  withLayoutFlags
441
448
  };
442
449
  //#endregion
443
- export { APP_ARTIFACT_COMPATIBILITY_KEY, APP_CACHE_ENTRY_REUSE_PROOF_KEY, APP_INTERCEPTION_CONTEXT_KEY, APP_INTERCEPTION_KEY, APP_LAYOUT_FLAGS_KEY, APP_LAYOUT_IDS_KEY, APP_RENDER_OBSERVATION_KEY, APP_ROOT_LAYOUT_KEY, APP_ROUTE_KEY, APP_SKIPPED_LAYOUT_IDS_KEY, APP_SLOT_BINDINGS_KEY, APP_SOURCE_PAGE_KEY, APP_STATIC_SIBLINGS_KEY, APP_UNMATCHED_SLOT_WIRE_VALUE, AppElementsWire, UNMATCHED_SLOT, buildOutgoingAppPayload, compareAppElementsSlotIds, isAppElementsRecord, normalizeAppElements, normalizeAppElementsSlotBindings, readAppElementsMetadata, withLayoutFlags };
450
+ export { APP_ARTIFACT_COMPATIBILITY_KEY, APP_CACHE_ENTRY_REUSE_PROOF_KEY, APP_DYNAMIC_STALE_TIME_KEY, APP_INTERCEPTION_CONTEXT_KEY, APP_INTERCEPTION_KEY, APP_LAYOUT_FLAGS_KEY, APP_LAYOUT_IDS_KEY, APP_RENDER_OBSERVATION_KEY, APP_ROOT_LAYOUT_KEY, APP_ROUTE_KEY, APP_SKIPPED_LAYOUT_IDS_KEY, APP_SLOT_BINDINGS_KEY, APP_SOURCE_PAGE_KEY, APP_STATIC_SIBLINGS_KEY, APP_UNMATCHED_SLOT_WIRE_VALUE, AppElementsWire, UNMATCHED_SLOT, buildOutgoingAppPayload, compareAppElementsSlotIds, isAppElementsRecord, normalizeAppElements, normalizeAppElementsSlotBindings, readAppElementsMetadata, withLayoutFlags };
@@ -1,4 +1,4 @@
1
- import { APP_ARTIFACT_COMPATIBILITY_KEY, APP_CACHE_ENTRY_REUSE_PROOF_KEY, APP_INTERCEPTION_CONTEXT_KEY, APP_INTERCEPTION_KEY, APP_LAYOUT_FLAGS_KEY, APP_LAYOUT_IDS_KEY, APP_RENDER_OBSERVATION_KEY, APP_ROOT_LAYOUT_KEY, APP_ROUTE_KEY, APP_SKIPPED_LAYOUT_IDS_KEY, APP_SLOT_BINDINGS_KEY, APP_SOURCE_PAGE_KEY, APP_STATIC_SIBLINGS_KEY, APP_UNMATCHED_SLOT_WIRE_VALUE, AppElementValue, AppElements, AppElementsInterception, AppElementsSlotBinding, AppElementsWire, AppOutgoingElements, AppWireElements, LayoutFlags, UNMATCHED_SLOT, buildOutgoingAppPayload, compareAppElementsSlotIds, isAppElementsRecord, normalizeAppElements, normalizeAppElementsSlotBindings, readAppElementsMetadata, withLayoutFlags } from "./app-elements-wire.js";
1
+ import { APP_ARTIFACT_COMPATIBILITY_KEY, APP_CACHE_ENTRY_REUSE_PROOF_KEY, APP_DYNAMIC_STALE_TIME_KEY, APP_INTERCEPTION_CONTEXT_KEY, APP_INTERCEPTION_KEY, APP_LAYOUT_FLAGS_KEY, APP_LAYOUT_IDS_KEY, APP_RENDER_OBSERVATION_KEY, APP_ROOT_LAYOUT_KEY, APP_ROUTE_KEY, APP_SKIPPED_LAYOUT_IDS_KEY, APP_SLOT_BINDINGS_KEY, APP_SOURCE_PAGE_KEY, APP_STATIC_SIBLINGS_KEY, APP_UNMATCHED_SLOT_WIRE_VALUE, AppElementValue, AppElements, AppElementsInterception, AppElementsSlotBinding, AppElementsWire, AppOutgoingElements, AppWireElements, LayoutFlags, UNMATCHED_SLOT, buildOutgoingAppPayload, compareAppElementsSlotIds, isAppElementsRecord, normalizeAppElements, normalizeAppElementsSlotBindings, readAppElementsMetadata, withLayoutFlags } from "./app-elements-wire.js";
2
2
 
3
3
  //#region src/server/app-elements.d.ts
4
4
  declare const APP_PREFETCH_LOADING_SHELL_MARKER_KEY = "__prefetchLoadingShell";
@@ -6,4 +6,4 @@ declare function getMountedSlotIds(elements: AppElements): string[];
6
6
  declare function getMountedSlotIdsHeader(elements: AppElements): string | null;
7
7
  declare function resolveVisitedResponseInterceptionContext(requestInterceptionContext: string | null, payloadInterceptionContext: string | null): string | null;
8
8
  //#endregion
9
- export { APP_ARTIFACT_COMPATIBILITY_KEY, APP_CACHE_ENTRY_REUSE_PROOF_KEY, APP_INTERCEPTION_CONTEXT_KEY, APP_INTERCEPTION_KEY, APP_LAYOUT_FLAGS_KEY, APP_LAYOUT_IDS_KEY, APP_PREFETCH_LOADING_SHELL_MARKER_KEY, APP_RENDER_OBSERVATION_KEY, APP_ROOT_LAYOUT_KEY, APP_ROUTE_KEY, APP_SKIPPED_LAYOUT_IDS_KEY, APP_SLOT_BINDINGS_KEY, APP_SOURCE_PAGE_KEY, APP_STATIC_SIBLINGS_KEY, APP_UNMATCHED_SLOT_WIRE_VALUE, type AppElementValue, type AppElements, type AppElementsInterception, type AppElementsSlotBinding, AppElementsWire, type AppOutgoingElements, type AppWireElements, type LayoutFlags, UNMATCHED_SLOT, buildOutgoingAppPayload, compareAppElementsSlotIds, getMountedSlotIds, getMountedSlotIdsHeader, isAppElementsRecord, normalizeAppElements, normalizeAppElementsSlotBindings, readAppElementsMetadata, resolveVisitedResponseInterceptionContext, withLayoutFlags };
9
+ export { APP_ARTIFACT_COMPATIBILITY_KEY, APP_CACHE_ENTRY_REUSE_PROOF_KEY, APP_DYNAMIC_STALE_TIME_KEY, APP_INTERCEPTION_CONTEXT_KEY, APP_INTERCEPTION_KEY, APP_LAYOUT_FLAGS_KEY, APP_LAYOUT_IDS_KEY, APP_PREFETCH_LOADING_SHELL_MARKER_KEY, APP_RENDER_OBSERVATION_KEY, APP_ROOT_LAYOUT_KEY, APP_ROUTE_KEY, APP_SKIPPED_LAYOUT_IDS_KEY, APP_SLOT_BINDINGS_KEY, APP_SOURCE_PAGE_KEY, APP_STATIC_SIBLINGS_KEY, APP_UNMATCHED_SLOT_WIRE_VALUE, type AppElementValue, type AppElements, type AppElementsInterception, type AppElementsSlotBinding, AppElementsWire, type AppOutgoingElements, type AppWireElements, type LayoutFlags, UNMATCHED_SLOT, buildOutgoingAppPayload, compareAppElementsSlotIds, getMountedSlotIds, getMountedSlotIdsHeader, isAppElementsRecord, normalizeAppElements, normalizeAppElementsSlotBindings, readAppElementsMetadata, resolveVisitedResponseInterceptionContext, withLayoutFlags };
@@ -1,11 +1,12 @@
1
- import { APP_ARTIFACT_COMPATIBILITY_KEY, APP_CACHE_ENTRY_REUSE_PROOF_KEY, APP_INTERCEPTION_CONTEXT_KEY, APP_INTERCEPTION_KEY, APP_LAYOUT_FLAGS_KEY, APP_LAYOUT_IDS_KEY, APP_RENDER_OBSERVATION_KEY, APP_ROOT_LAYOUT_KEY, APP_ROUTE_KEY, APP_SKIPPED_LAYOUT_IDS_KEY, APP_SLOT_BINDINGS_KEY, APP_SOURCE_PAGE_KEY, APP_STATIC_SIBLINGS_KEY, APP_UNMATCHED_SLOT_WIRE_VALUE, AppElementsWire, UNMATCHED_SLOT, buildOutgoingAppPayload, compareAppElementsSlotIds, isAppElementsRecord, normalizeAppElements, normalizeAppElementsSlotBindings, readAppElementsMetadata, withLayoutFlags } from "./app-elements-wire.js";
1
+ import { APP_ARTIFACT_COMPATIBILITY_KEY, APP_CACHE_ENTRY_REUSE_PROOF_KEY, APP_DYNAMIC_STALE_TIME_KEY, APP_INTERCEPTION_CONTEXT_KEY, APP_INTERCEPTION_KEY, APP_LAYOUT_FLAGS_KEY, APP_LAYOUT_IDS_KEY, APP_RENDER_OBSERVATION_KEY, APP_ROOT_LAYOUT_KEY, APP_ROUTE_KEY, APP_SKIPPED_LAYOUT_IDS_KEY, APP_SLOT_BINDINGS_KEY, APP_SOURCE_PAGE_KEY, APP_STATIC_SIBLINGS_KEY, APP_UNMATCHED_SLOT_WIRE_VALUE, AppElementsWire, UNMATCHED_SLOT, buildOutgoingAppPayload, compareAppElementsSlotIds, isAppElementsRecord, normalizeAppElements, normalizeAppElementsSlotBindings, readAppElementsMetadata, withLayoutFlags } from "./app-elements-wire.js";
2
2
  import { normalizeMountedSlotsHeader } from "./app-mounted-slots-header.js";
3
3
  //#region src/server/app-elements.ts
4
4
  const APP_PREFETCH_LOADING_SHELL_MARKER_KEY = "__prefetchLoadingShell";
5
5
  function getMountedSlotIds(elements) {
6
6
  return Object.keys(elements).filter((key) => {
7
7
  const value = elements[key];
8
- return AppElementsWire.isSlotId(key) && value !== null && value !== void 0 && value !== UNMATCHED_SLOT;
8
+ const parsed = AppElementsWire.parseElementKey(key);
9
+ return parsed?.kind === "slot" && parsed.name !== "children" && value !== null && value !== void 0 && value !== UNMATCHED_SLOT;
9
10
  }).sort();
10
11
  }
11
12
  function getMountedSlotIdsHeader(elements) {
@@ -15,4 +16,4 @@ function resolveVisitedResponseInterceptionContext(requestInterceptionContext, p
15
16
  return payloadInterceptionContext ?? requestInterceptionContext;
16
17
  }
17
18
  //#endregion
18
- export { APP_ARTIFACT_COMPATIBILITY_KEY, APP_CACHE_ENTRY_REUSE_PROOF_KEY, APP_INTERCEPTION_CONTEXT_KEY, APP_INTERCEPTION_KEY, APP_LAYOUT_FLAGS_KEY, APP_LAYOUT_IDS_KEY, APP_PREFETCH_LOADING_SHELL_MARKER_KEY, APP_RENDER_OBSERVATION_KEY, APP_ROOT_LAYOUT_KEY, APP_ROUTE_KEY, APP_SKIPPED_LAYOUT_IDS_KEY, APP_SLOT_BINDINGS_KEY, APP_SOURCE_PAGE_KEY, APP_STATIC_SIBLINGS_KEY, APP_UNMATCHED_SLOT_WIRE_VALUE, AppElementsWire, UNMATCHED_SLOT, buildOutgoingAppPayload, compareAppElementsSlotIds, getMountedSlotIds, getMountedSlotIdsHeader, isAppElementsRecord, normalizeAppElements, normalizeAppElementsSlotBindings, readAppElementsMetadata, resolveVisitedResponseInterceptionContext, withLayoutFlags };
19
+ export { APP_ARTIFACT_COMPATIBILITY_KEY, APP_CACHE_ENTRY_REUSE_PROOF_KEY, APP_DYNAMIC_STALE_TIME_KEY, APP_INTERCEPTION_CONTEXT_KEY, APP_INTERCEPTION_KEY, APP_LAYOUT_FLAGS_KEY, APP_LAYOUT_IDS_KEY, APP_PREFETCH_LOADING_SHELL_MARKER_KEY, APP_RENDER_OBSERVATION_KEY, APP_ROOT_LAYOUT_KEY, APP_ROUTE_KEY, APP_SKIPPED_LAYOUT_IDS_KEY, APP_SLOT_BINDINGS_KEY, APP_SOURCE_PAGE_KEY, APP_STATIC_SIBLINGS_KEY, APP_UNMATCHED_SLOT_WIRE_VALUE, AppElementsWire, UNMATCHED_SLOT, buildOutgoingAppPayload, compareAppElementsSlotIds, getMountedSlotIds, getMountedSlotIdsHeader, isAppElementsRecord, normalizeAppElements, normalizeAppElementsSlotBindings, readAppElementsMetadata, resolveVisitedResponseInterceptionContext, withLayoutFlags };
@@ -4,6 +4,7 @@ import { NavigationContext } from "../shims/navigation-context-state.js";
4
4
  import { AppPageParams } from "./app-page-boundary.js";
5
5
  import { AppPageFontPreload } from "./app-page-execution.js";
6
6
  import { AppPageMiddlewareContext } from "./app-page-response.js";
7
+ import { ApplyAppPageFileBasedMetadata } from "./app-page-head.js";
7
8
  import { AppPageSsrHandler } from "./app-page-stream.js";
8
9
  import { AppPageBoundaryRoute } from "./app-page-boundary-render.js";
9
10
  import { ReactNode } from "react";
@@ -27,11 +28,13 @@ type AppFallbackRendererFontProviders = {
27
28
  getFontStyles: () => string[];
28
29
  };
29
30
  type AppFallbackRendererOptions<TModule extends AppPageModule = AppPageModule> = {
31
+ applyFileBasedMetadata?: ApplyAppPageFileBasedMetadata;
30
32
  clearRequestContext: () => void;
31
33
  createRscOnErrorHandler: (request: Request, pathname: string, routePath: string) => AppPageBoundaryOnError;
32
34
  fontProviders: AppFallbackRendererFontProviders;
33
35
  getNavigationContext: () => NavigationContext | null;
34
- globalErrorModule?: TModule | null;
36
+ globalErrorModule?: TModule | null; /** Whether experimental.globalNotFound is enabled for route-miss 404s. */
37
+ globalNotFoundEnabled?: boolean;
35
38
  /**
36
39
  * Loader for the user's `app/global-not-found.tsx` module. When provided,
37
40
  * route-miss 404s render this module as a standalone document (skipping the
@@ -73,7 +76,7 @@ type AppFallbackRendererCallContext = {
73
76
  sourcePageSegments?: readonly string[] | null;
74
77
  };
75
78
  type AppFallbackRenderer<TModule extends AppPageModule = AppPageModule> = {
76
- renderErrorBoundary: (route: AppPageBoundaryRoute<TModule> | null, error: unknown, isRscRequest: boolean, request: Request, matchedParams: AppPageParams | undefined, scriptNonce: string | undefined, middlewareContext: AppPageMiddlewareContext, callContext?: AppFallbackRendererCallContext) => Promise<Response | null>;
79
+ renderErrorBoundary: (route: AppPageBoundaryRoute<TModule> | null, error: unknown, isRscRequest: boolean, request: Request, matchedParams: AppPageParams | undefined, scriptNonce: string | undefined, middlewareContext: AppPageMiddlewareContext, callContext?: AppFallbackRendererCallContext, errorOrigin?: "rsc" | "ssr") => Promise<Response | null>;
77
80
  renderHttpAccessFallback: (route: AppPageBoundaryRoute<TModule> | null, statusCode: number, isRscRequest: boolean, request: Request, opts: {
78
81
  boundaryComponent?: AppPageComponent | null;
79
82
  boundaryModule?: TModule | null;
@@ -1,5 +1,6 @@
1
1
  import { renderAppPageErrorBoundary, renderAppPageHttpAccessFallback } from "./app-page-boundary-render.js";
2
2
  import { DEFAULT_GLOBAL_ERROR_MODULE } from "./default-global-error-module.js";
3
+ import { DEFAULT_GLOBAL_NOT_FOUND_MODULE } from "./default-global-not-found-module.js";
3
4
  import { DEFAULT_NOT_FOUND_MODULE } from "./default-not-found-module.js";
4
5
  //#region src/server/app-fallback-renderer.ts
5
6
  const EMPTY_MW_CTX = {
@@ -7,7 +8,7 @@ const EMPTY_MW_CTX = {
7
8
  status: null
8
9
  };
9
10
  function createAppFallbackRenderer(options) {
10
- const { basePath = "", clearRequestContext, createRscOnErrorHandler: buildRscOnErrorHandler, fontProviders, getNavigationContext, globalErrorModule, loadGlobalNotFoundModule, makeThenableParams, metadataRoutes, resolveChildSegments, rootBoundaries, rscRenderer, sanitizer, ssrLoader, trailingSlash } = options;
11
+ const { applyFileBasedMetadata, basePath = "", clearRequestContext, createRscOnErrorHandler: buildRscOnErrorHandler, fontProviders, getNavigationContext, globalErrorModule, globalNotFoundEnabled = false, loadGlobalNotFoundModule, makeThenableParams, metadataRoutes, resolveChildSegments, rootBoundaries, rscRenderer, sanitizer, ssrLoader, trailingSlash } = options;
11
12
  const { rootForbiddenModule, rootLayouts, rootNotFoundModule, rootUnauthorizedModule } = rootBoundaries;
12
13
  const effectiveGlobalErrorModule = globalErrorModule ?? DEFAULT_GLOBAL_ERROR_MODULE;
13
14
  const effectiveRootNotFoundModule = rootNotFoundModule ?? DEFAULT_NOT_FOUND_MODULE;
@@ -19,10 +20,12 @@ function createAppFallbackRenderer(options) {
19
20
  }
20
21
  return {
21
22
  async renderHttpAccessFallback(route, statusCode, isRscRequest, request, opts, scriptNonce, middlewareContext, callContext) {
22
- if (statusCode === 404 && !!loadGlobalNotFoundModule && !route && !opts?.boundaryComponent) {
23
+ const useGlobalNotFound = statusCode === 404 && globalNotFoundEnabled && !route && !opts?.boundaryComponent;
24
+ if (useGlobalNotFound && loadGlobalNotFoundModule) {
23
25
  const globalNotFoundModule = await resolveGlobalNotFoundModule();
24
26
  const globalNotFoundComponent = globalNotFoundModule?.default ?? null;
25
27
  if (globalNotFoundComponent) return renderAppPageHttpAccessFallback({
28
+ applyFileBasedMetadata,
26
29
  boundaryComponent: globalNotFoundComponent,
27
30
  boundaryModule: globalNotFoundModule ?? null,
28
31
  buildFontLinkHeader: fontProviders.buildFontLinkHeader,
@@ -56,7 +59,9 @@ function createAppFallbackRenderer(options) {
56
59
  statusCode
57
60
  });
58
61
  }
62
+ const routeMissRootNotFoundModule = useGlobalNotFound ? DEFAULT_GLOBAL_NOT_FOUND_MODULE : effectiveRootNotFoundModule;
59
63
  return renderAppPageHttpAccessFallback({
64
+ applyFileBasedMetadata,
60
65
  basePath,
61
66
  trailingSlash,
62
67
  boundaryComponent: opts?.boundaryComponent ?? null,
@@ -73,7 +78,7 @@ function createAppFallbackRenderer(options) {
73
78
  globalErrorModule: effectiveGlobalErrorModule,
74
79
  isEdgeRuntime: callContext?.isEdgeRuntime,
75
80
  isRscRequest,
76
- layoutModules: opts?.layouts ?? null,
81
+ layoutModules: useGlobalNotFound ? [] : opts?.layouts ?? null,
77
82
  loadSsrHandler: ssrLoader,
78
83
  makeThenableParams,
79
84
  matchedParams: opts?.matchedParams ?? route?.params ?? {},
@@ -82,12 +87,13 @@ function createAppFallbackRenderer(options) {
82
87
  requestUrl: request.url,
83
88
  resolveChildSegments,
84
89
  rootForbiddenModule,
85
- rootLayouts,
86
- rootNotFoundModule: effectiveRootNotFoundModule,
90
+ rootLayouts: useGlobalNotFound ? [] : rootLayouts,
91
+ rootNotFoundModule: routeMissRootNotFoundModule,
87
92
  rootUnauthorizedModule,
88
- route,
93
+ route: useGlobalNotFound ? null : route,
89
94
  renderToReadableStream: rscRenderer,
90
95
  scriptNonce,
96
+ skipLayoutWrapping: useGlobalNotFound,
91
97
  sourcePageSegments: callContext?.sourcePageSegments,
92
98
  statusCode
93
99
  });
@@ -95,8 +101,9 @@ function createAppFallbackRenderer(options) {
95
101
  renderNotFound(route, isRscRequest, request, matchedParams, scriptNonce, middlewareContext, callContext) {
96
102
  return this.renderHttpAccessFallback(route, 404, isRscRequest, request, { matchedParams }, scriptNonce, middlewareContext, callContext);
97
103
  },
98
- renderErrorBoundary(route, error, isRscRequest, request, matchedParams, scriptNonce, middlewareContext, callContext) {
104
+ renderErrorBoundary(route, error, isRscRequest, request, matchedParams, scriptNonce, middlewareContext, callContext, errorOrigin = "rsc") {
99
105
  return renderAppPageErrorBoundary({
106
+ applyFileBasedMetadata,
100
107
  basePath,
101
108
  trailingSlash,
102
109
  buildFontLinkHeader: fontProviders.buildFontLinkHeader,
@@ -105,6 +112,7 @@ function createAppFallbackRenderer(options) {
105
112
  return buildRscOnErrorHandler(request, pathname, routePath);
106
113
  },
107
114
  error,
115
+ errorOrigin,
108
116
  getFontLinks: fontProviders.getFontLinks,
109
117
  getFontPreloads: fontProviders.getFontPreloads,
110
118
  getFontStyles: fontProviders.getFontStyles,
@@ -0,0 +1,11 @@
1
+ //#region src/server/app-hydration-cache-publication.d.ts
2
+ type HydrationCachePublication = {
3
+ commit(): void;
4
+ complete(): void;
5
+ fail(): void;
6
+ invalidate(): void;
7
+ publish(publishCandidate: () => () => void): void;
8
+ };
9
+ declare function createHydrationCachePublication(): HydrationCachePublication;
10
+ //#endregion
11
+ export { HydrationCachePublication, createHydrationCachePublication };
@@ -0,0 +1,43 @@
1
+ //#region src/server/app-hydration-cache-publication.ts
2
+ function createHydrationCachePublication() {
3
+ let state = "pending";
4
+ let pendingPublication = null;
5
+ let invalidatePublishedCandidate = null;
6
+ const publishPendingCandidate = () => {
7
+ if (state !== "committed" && state !== "complete" || pendingPublication === null) return;
8
+ const publishCandidate = pendingPublication;
9
+ pendingPublication = null;
10
+ invalidatePublishedCandidate = publishCandidate();
11
+ };
12
+ return {
13
+ commit() {
14
+ if (state !== "pending") return;
15
+ state = "committed";
16
+ publishPendingCandidate();
17
+ },
18
+ complete() {
19
+ if (state === "committed") state = "complete";
20
+ },
21
+ fail() {
22
+ if (state === "complete" || state === "invalidated") return;
23
+ state = "invalidated";
24
+ pendingPublication = null;
25
+ invalidatePublishedCandidate?.();
26
+ invalidatePublishedCandidate = null;
27
+ },
28
+ invalidate() {
29
+ if (state === "invalidated") return;
30
+ state = "invalidated";
31
+ pendingPublication = null;
32
+ invalidatePublishedCandidate?.();
33
+ invalidatePublishedCandidate = null;
34
+ },
35
+ publish(publishCandidate) {
36
+ if (state === "invalidated") return;
37
+ pendingPublication = publishCandidate;
38
+ publishPendingCandidate();
39
+ }
40
+ };
41
+ }
42
+ //#endregion
43
+ export { createHydrationCachePublication };
@@ -12,11 +12,11 @@ type ApplyAppMiddlewareOptions = {
12
12
  basePath?: string;
13
13
  cleanPathname: string;
14
14
  context: AppMiddlewareContext;
15
+ hadBasePath?: boolean;
15
16
  i18nConfig?: NextI18nConfig | null;
16
17
  /**
17
- * Whether the inbound request was a `_next/data` fetch. Captured from the
18
- * raw incoming headers by the caller, because `x-nextjs-data` is in
19
- * INTERNAL_HEADERS and is stripped before this function runs.
18
+ * Whether the inbound request was recognized as a `_next/data` fetch from
19
+ * trusted URL normalization before internal headers were stripped.
20
20
  */
21
21
  isDataRequest?: boolean;
22
22
  filePath?: string;
@@ -34,6 +34,7 @@ type ApplyAppMiddlewareOptions = {
34
34
  type ApplyAppMiddlewareResult = {
35
35
  kind: "continue";
36
36
  cleanPathname: string;
37
+ rewritten: boolean;
37
38
  search: string | null;
38
39
  } | {
39
40
  kind: "response";
@@ -1,5 +1,6 @@
1
- import { FLIGHT_HEADERS, VINEXT_MW_CTX_HEADER } from "./headers.js";
2
- import { buildRequestHeadersFromMiddlewareResponse } from "./middleware-request-headers.js";
1
+ import { VINEXT_MW_CTX_HEADER } from "../utils/protocol-headers.js";
2
+ import { FLIGHT_HEADERS } from "./headers.js";
3
+ import { buildRequestHeadersFromMiddlewareResponse } from "../utils/middleware-request-headers.js";
3
4
  import { isExternalUrl, proxyExternalRequest } from "../config/config-matchers.js";
4
5
  import { internalServerErrorResponse } from "./http-error-responses.js";
5
6
  import { cloneRequestWithHeaders, processMiddlewareHeaders } from "./request-pipeline.js";
@@ -100,6 +101,7 @@ async function applyAppMiddleware(options) {
100
101
  const forwarded = applyForwardedMiddlewareContext(options.request, options.context);
101
102
  const middlewareRequest = requestWithoutFlightHeaders(options.request);
102
103
  let cleanPathname = options.cleanPathname;
104
+ let rewritten = false;
103
105
  let search = null;
104
106
  if (forwarded.rewriteUrl) try {
105
107
  if (isExternalMiddlewareRewrite(forwarded.rewriteUrl, middlewareRequest)) return {
@@ -108,6 +110,7 @@ async function applyAppMiddleware(options) {
108
110
  };
109
111
  const rewriteParsed = new URL(forwarded.rewriteUrl, middlewareRequest.url);
110
112
  cleanPathname = rewriteParsed.pathname;
113
+ rewritten = true;
111
114
  search = rewriteParsed.search;
112
115
  } catch (e) {
113
116
  console.error("[vinext] Failed to apply forwarded middleware rewrite:", e);
@@ -116,7 +119,7 @@ async function applyAppMiddleware(options) {
116
119
  if (!forwarded.applied) {
117
120
  const result = await executeMiddleware({
118
121
  basePath: options.basePath,
119
- hadBasePath: true,
122
+ hadBasePath: options.hadBasePath ?? true,
120
123
  filePath: options.filePath,
121
124
  i18nConfig: options.i18nConfig,
122
125
  isDataRequest: options.isDataRequest,
@@ -150,6 +153,7 @@ async function applyAppMiddleware(options) {
150
153
  };
151
154
  const rewriteParsed = new URL(result.rewriteUrl, middlewareRequest.url);
152
155
  cleanPathname = rewriteParsed.pathname;
156
+ rewritten = true;
153
157
  search = rewriteParsed.search;
154
158
  }
155
159
  }
@@ -161,6 +165,7 @@ async function applyAppMiddleware(options) {
161
165
  return {
162
166
  kind: "continue",
163
167
  cleanPathname,
168
+ rewritten,
164
169
  search
165
170
  };
166
171
  }
@@ -1,9 +1,9 @@
1
1
  import { buildParams, decodeMatchedParams, splitPathnameForRouteMatch } from "../routing/utils.js";
2
2
  import { stripBasePath } from "../utils/base-path.js";
3
- import { matchRoutePattern } from "../routing/route-pattern.js";
4
3
  import { isUnknownRecord } from "../utils/record.js";
5
4
  import { AppElementsWire } from "./app-elements-wire.js";
6
5
  import "./app-elements.js";
6
+ import { matchRoutePattern } from "../routing/route-pattern.js";
7
7
  import { stripRscCacheBustingSearchParam, stripRscSuffix } from "./app-rsc-cache-busting.js";
8
8
  import { Suspense, createElement, isValidElement } from "react";
9
9
  //#region src/server/app-optimistic-routing.ts
@@ -162,8 +162,15 @@ function elementHasSuspenseFallback(value, depth = 0) {
162
162
  if (!isUnknownRecord(props)) return false;
163
163
  return elementHasSuspenseFallback(Reflect.get(props, "children"), depth + 1);
164
164
  }
165
- function getPageElementIds(elements) {
166
- return Object.keys(elements).filter((key) => AppElementsWire.parseElementKey(key)?.kind === "page").sort();
165
+ function getPageElementIds(elements, route) {
166
+ const pageElementIds = /* @__PURE__ */ new Set();
167
+ if (route.pageId && Object.hasOwn(elements, route.pageId)) pageElementIds.add(route.pageId);
168
+ for (const slotId of route.slotIds) {
169
+ const parsed = AppElementsWire.parseElementKey(slotId);
170
+ if (parsed?.kind === "slot" && parsed.name === "children" && Object.hasOwn(elements, slotId)) pageElementIds.add(slotId);
171
+ }
172
+ for (const key of Object.keys(elements)) if (AppElementsWire.parseElementKey(key)?.kind === "page") pageElementIds.add(key);
173
+ return Array.from(pageElementIds).sort();
167
174
  }
168
175
  function OptimisticRouteSegment() {
169
176
  throw OPTIMISTIC_ROUTE_SEGMENT_SUSPENSE_TRIGGER;
@@ -182,7 +189,7 @@ function createOptimisticRouteTemplate(options) {
182
189
  if (!options.allowLoadingShell && !elementHasSuspenseFallback(routeElement)) return null;
183
190
  if (options.allowLoadingShell && options.elements["__prefetchLoadingShell"] !== "LoadingBoundary") return null;
184
191
  if (options.allowLoadingShell && (routeElement === void 0 || routeElement === null)) return null;
185
- const pageElementIds = getPageElementIds(options.elements);
192
+ const pageElementIds = getPageElementIds(options.elements, match.route);
186
193
  if (pageElementIds.length === 0) return null;
187
194
  return {
188
195
  elements: options.elements,
@@ -4,6 +4,7 @@ import { NavigationContext } from "../shims/navigation-context-state.js";
4
4
  import { AppPageParams } from "./app-page-boundary.js";
5
5
  import { AppPageFontPreload } from "./app-page-execution.js";
6
6
  import { AppPageMiddlewareContext } from "./app-page-response.js";
7
+ import { ApplyAppPageFileBasedMetadata } from "./app-page-head.js";
7
8
  import { AppPageSsrHandler } from "./app-page-stream.js";
8
9
  import { ComponentType, ReactNode } from "react";
9
10
 
@@ -27,6 +28,7 @@ type AppPageBoundaryRoute<TModule extends AppPageModule = AppPageModule> = {
27
28
  unauthorized?: TModule | null;
28
29
  };
29
30
  type AppPageBoundaryRenderCommonOptions<TModule extends AppPageModule = AppPageModule> = {
31
+ applyFileBasedMetadata?: ApplyAppPageFileBasedMetadata;
30
32
  buildFontLinkHeader: (preloads: readonly AppPageFontPreload[] | null | undefined) => string;
31
33
  clearRequestContext: () => void;
32
34
  createRscOnErrorHandler: (pathname: string, routePath: string) => AppPageBoundaryOnError;
@@ -73,6 +75,7 @@ type RenderAppPageHttpAccessFallbackOptions<TModule extends AppPageModule = AppP
73
75
  } & AppPageBoundaryRenderCommonOptions<TModule>;
74
76
  type RenderAppPageErrorBoundaryOptions<TModule extends AppPageModule = AppPageModule> = {
75
77
  error: unknown;
78
+ errorOrigin?: "rsc" | "ssr";
76
79
  matchedParams?: AppPageParams | null;
77
80
  route?: AppPageBoundaryRoute<TModule> | null;
78
81
  sanitizeErrorForClient: (error: Error) => Error;
@@ -3,7 +3,7 @@ import { AppElementsWire } from "./app-elements-wire.js";
3
3
  import "./app-elements.js";
4
4
  import DefaultGlobalError from "../shims/default-global-error.js";
5
5
  import { isNavigationSignalError } from "../utils/navigation-signal.js";
6
- import { ErrorBoundary, GlobalErrorBoundary } from "../shims/error-boundary.js";
6
+ import { ErrorBoundary, GlobalErrorBoundary, SerializedErrorBoundary } from "../shims/error-boundary.js";
7
7
  import { LayoutSegmentProvider } from "../shims/layout-segment-context.js";
8
8
  import { MetadataHead, ViewportHead } from "../shims/metadata.js";
9
9
  import { resolveAppPageSpecialError } from "./app-page-execution.js";
@@ -152,6 +152,7 @@ async function renderAppPageHttpAccessFallback(options) {
152
152
  const pathname = new URL(options.requestUrl).pathname;
153
153
  const routeSegments = resolveHttpAccessFallbackHeadRouteSegments(options.route, layoutModules);
154
154
  const { metadata, viewport } = await resolveAppPageHead({
155
+ applyFileBasedMetadata: options.applyFileBasedMetadata,
155
156
  basePath: options.basePath ?? "",
156
157
  layoutModules,
157
158
  layoutTreePositions: resolveHttpAccessFallbackHeadLayoutTreePositions(options.route, layoutModules),
@@ -214,7 +215,7 @@ async function renderAppPageErrorBoundary(options) {
214
215
  if (!errorBoundary.component) return null;
215
216
  const rawError = options.error instanceof Error ? options.error : new Error(String(options.error));
216
217
  rewriteClientHookError(rawError);
217
- const errorObject = options.sanitizeErrorForClient(rawError);
218
+ const errorObject = options.errorOrigin === "ssr" ? rawError : options.sanitizeErrorForClient(rawError);
218
219
  const matchedParams = options.matchedParams ?? options.route?.params ?? {};
219
220
  const layoutModules = options.route?.layouts ?? options.rootLayouts;
220
221
  const pathname = new URL(options.requestUrl).pathname;
@@ -224,6 +225,7 @@ async function renderAppPageErrorBoundary(options) {
224
225
  })];
225
226
  if (!errorBoundary.isGlobalError) try {
226
227
  const { metadata, viewport } = await resolveAppPageHead({
228
+ applyFileBasedMetadata: options.applyFileBasedMetadata,
227
229
  basePath: options.basePath ?? "",
228
230
  fallbackOnFileMetadataError: true,
229
231
  layoutModules,
@@ -247,7 +249,16 @@ async function renderAppPageErrorBoundary(options) {
247
249
  console.error(`[vinext] App page error boundary head resolution failed for ${options.route?.pattern ?? pathname}:`, error);
248
250
  }
249
251
  const buildElement = (BoundaryComponent) => {
250
- const boundaryElement = createElement(BoundaryComponent, { error: errorObject });
252
+ const serializedError = {
253
+ digest: "digest" in errorObject ? String(errorObject.digest) : void 0,
254
+ message: errorObject.message,
255
+ name: errorObject.name,
256
+ stack: process.env.NODE_ENV !== "production" ? errorObject.stack : void 0
257
+ };
258
+ const boundaryElement = errorBoundary.isGlobalError && BoundaryComponent !== DEFAULT_GLOBAL_ERROR_COMPONENT ? createElement(SerializedErrorBoundary, {
259
+ error: serializedError,
260
+ fallback: BoundaryComponent
261
+ }) : createElement(BoundaryComponent, { error: errorObject });
251
262
  return wrapRenderedBoundaryElement({
252
263
  element: createElement(Fragment, null, ...headElements, errorBoundary.isGlobalError ? createElement(GlobalErrorBoundary, {
253
264
  fallback: DEFAULT_GLOBAL_ERROR_COMPONENT,
@@ -30,6 +30,7 @@ type FinalizeAppPageHtmlCacheResponseOptions = {
30
30
  isrRscKey: AppPageRscCacheKeyBuilder;
31
31
  isrSet: AppPageCacheSetter;
32
32
  interceptionContext?: string | null;
33
+ omitPendingDynamicCacheState?: boolean;
33
34
  preserveClientResponseHeaders?: boolean;
34
35
  expireSeconds?: number;
35
36
  revalidateSeconds: number | null;