vinext 1.3.1 → 1.4.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 (271) hide show
  1. package/README.md +25 -22
  2. package/THIRD_PARTY_LICENSES.md +47 -0
  3. package/dist/_virtual/_rolldown/runtime.js +3 -2
  4. package/dist/build/prerender.js +4 -3
  5. package/dist/build/report.d.ts +0 -14
  6. package/dist/build/report.js +1 -51
  7. package/dist/check.js +4 -7
  8. package/dist/cli.js +3 -0
  9. package/dist/client/dev-error-overlay-store.d.ts +1 -0
  10. package/dist/client/dev-error-overlay.d.ts +2 -0
  11. package/dist/client/dev-error-overlay.js +20 -4
  12. package/dist/client/navigation-runtime.d.ts +13 -1
  13. package/dist/client/navigation-runtime.js +1 -1
  14. package/dist/config/config-matchers.js +2 -0
  15. package/dist/config/destination-params.d.ts +2 -1
  16. package/dist/config/destination-params.js +52 -2
  17. package/dist/config/next-config.d.ts +25 -0
  18. package/dist/config/next-config.js +82 -4
  19. package/dist/deps/.pnpm/iconv-lite@0.4.24/deps/iconv-lite/encodings/dbcs-codec.js +327 -0
  20. package/dist/deps/.pnpm/iconv-lite@0.4.24/deps/iconv-lite/encodings/dbcs-data.js +123 -0
  21. package/dist/deps/.pnpm/iconv-lite@0.4.24/deps/iconv-lite/encodings/index.js +29 -0
  22. package/dist/deps/.pnpm/iconv-lite@0.4.24/deps/iconv-lite/encodings/internal.js +134 -0
  23. package/dist/deps/.pnpm/iconv-lite@0.4.24/deps/iconv-lite/encodings/sbcs-codec.js +50 -0
  24. package/dist/deps/.pnpm/iconv-lite@0.4.24/deps/iconv-lite/encodings/sbcs-data-generated.js +455 -0
  25. package/dist/deps/.pnpm/iconv-lite@0.4.24/deps/iconv-lite/encodings/sbcs-data.js +147 -0
  26. package/dist/deps/.pnpm/iconv-lite@0.4.24/deps/iconv-lite/encodings/tables/big5-added.json.js +169 -0
  27. package/dist/deps/.pnpm/iconv-lite@0.4.24/deps/iconv-lite/encodings/tables/cp936.json.js +2584 -0
  28. package/dist/deps/.pnpm/iconv-lite@0.4.24/deps/iconv-lite/encodings/tables/cp949.json.js +2220 -0
  29. package/dist/deps/.pnpm/iconv-lite@0.4.24/deps/iconv-lite/encodings/tables/cp950.json.js +227 -0
  30. package/dist/deps/.pnpm/iconv-lite@0.4.24/deps/iconv-lite/encodings/tables/eucjp.json.js +425 -0
  31. package/dist/deps/.pnpm/iconv-lite@0.4.24/deps/iconv-lite/encodings/tables/gb18030-ranges.json.js +427 -0
  32. package/dist/deps/.pnpm/iconv-lite@0.4.24/deps/iconv-lite/encodings/tables/gbk-added.json.js +229 -0
  33. package/dist/deps/.pnpm/iconv-lite@0.4.24/deps/iconv-lite/encodings/tables/shiftjis.json.js +292 -0
  34. package/dist/deps/.pnpm/iconv-lite@0.4.24/deps/iconv-lite/encodings/utf16.js +106 -0
  35. package/dist/deps/.pnpm/iconv-lite@0.4.24/deps/iconv-lite/encodings/utf7.js +182 -0
  36. package/dist/deps/.pnpm/iconv-lite@0.4.24/deps/iconv-lite/lib/bom-handling.js +42 -0
  37. package/dist/deps/.pnpm/iconv-lite@0.4.24/deps/iconv-lite/lib/extend-node.js +147 -0
  38. package/dist/deps/.pnpm/iconv-lite@0.4.24/deps/iconv-lite/lib/index.js +94 -0
  39. package/dist/deps/.pnpm/iconv-lite@0.4.24/deps/iconv-lite/lib/streams.js +95 -0
  40. package/dist/deps/.pnpm/safer-buffer@2.1.2/deps/safer-buffer/safer.js +45 -0
  41. package/dist/entries/app-rsc-entry.js +17 -49
  42. package/dist/entries/app-rsc-manifest.js +3 -4
  43. package/dist/entries/app-ssr-entry.js +1 -0
  44. package/dist/entries/pages-server-entry.js +36 -9
  45. package/dist/index.js +112 -90
  46. package/dist/init-cloudflare.d.ts +1 -3
  47. package/dist/init-cloudflare.js +10 -48
  48. package/dist/init-platform.d.ts +2 -0
  49. package/dist/init-platform.js +4 -0
  50. package/dist/plugins/extensionless-dynamic-import.js +6 -2
  51. package/dist/plugins/next-intl-catalog.d.ts +12 -0
  52. package/dist/plugins/next-intl-catalog.js +85 -0
  53. package/dist/plugins/pages-node-externals.d.ts +1 -0
  54. package/dist/plugins/pages-node-externals.js +1 -1
  55. package/dist/plugins/strip-server-exports.js +2 -1
  56. package/dist/plugins/use-cache-callable.d.ts +1 -1
  57. package/dist/plugins/use-cache-callable.js +136 -94
  58. package/dist/routing/app-route-graph.d.ts +6 -0
  59. package/dist/routing/app-route-graph.js +9 -1
  60. package/dist/server/api-handler.js +21 -5
  61. package/dist/server/app-bfcache-identity.js +1 -1
  62. package/dist/server/app-browser-entry.js +86 -66
  63. package/dist/server/app-browser-history-controller.d.ts +5 -4
  64. package/dist/server/app-browser-history-controller.js +93 -15
  65. package/dist/server/app-browser-navigation-controller.d.ts +2 -2
  66. package/dist/server/app-browser-navigation-controller.js +1 -2
  67. package/dist/server/app-browser-rsc-redirect.js +7 -7
  68. package/dist/server/app-browser-server-action-client.d.ts +2 -0
  69. package/dist/server/app-browser-server-action-client.js +5 -4
  70. package/dist/server/app-browser-state.d.ts +2 -4
  71. package/dist/server/app-browser-state.js +9 -4
  72. package/dist/server/app-browser-supplemental-refresh.js +16 -1
  73. package/dist/server/app-browser-visible-commit.d.ts +1 -1
  74. package/dist/server/app-browser-visible-commit.js +4 -14
  75. package/dist/server/app-elements-wire.d.ts +6 -38
  76. package/dist/server/app-elements-wire.js +4 -41
  77. package/dist/server/app-elements.d.ts +2 -2
  78. package/dist/server/app-elements.js +2 -2
  79. package/dist/server/app-history-state.d.ts +2 -2
  80. package/dist/server/app-history-state.js +5 -2
  81. package/dist/server/app-mounted-layouts.d.ts +21 -0
  82. package/dist/server/app-mounted-layouts.js +67 -0
  83. package/dist/server/app-optimistic-routing.d.ts +19 -1
  84. package/dist/server/app-optimistic-routing.js +43 -4
  85. package/dist/server/app-page-boundary-render.js +10 -14
  86. package/dist/server/app-page-boundary.js +2 -2
  87. package/dist/server/app-page-cache-finalizer.d.ts +12 -1
  88. package/dist/server/app-page-cache-finalizer.js +70 -29
  89. package/dist/server/app-page-cache.d.ts +13 -1
  90. package/dist/server/app-page-cache.js +31 -9
  91. package/dist/server/app-page-dispatch.d.ts +5 -11
  92. package/dist/server/app-page-dispatch.js +84 -155
  93. package/dist/server/app-page-element-builder.d.ts +2 -2
  94. package/dist/server/app-page-element-builder.js +13 -12
  95. package/dist/server/app-page-execution.d.ts +17 -52
  96. package/dist/server/app-page-execution.js +102 -95
  97. package/dist/server/app-page-head.d.ts +13 -0
  98. package/dist/server/app-page-head.js +44 -16
  99. package/dist/server/app-page-http-access-fallback-metadata.d.ts +1 -0
  100. package/dist/server/app-page-http-access-fallback-metadata.js +2 -2
  101. package/dist/server/app-page-layout-gate.d.ts +35 -0
  102. package/dist/server/app-page-layout-gate.js +50 -0
  103. package/dist/server/app-page-params.d.ts +0 -1
  104. package/dist/server/app-page-params.js +1 -14
  105. package/dist/server/app-page-probe.d.ts +3 -21
  106. package/dist/server/app-page-probe.js +4 -37
  107. package/dist/server/app-page-render.d.ts +2 -13
  108. package/dist/server/app-page-render.js +103 -234
  109. package/dist/server/app-page-request.d.ts +1 -3
  110. package/dist/server/app-page-request.js +1 -1
  111. package/dist/server/app-page-response.js +3 -3
  112. package/dist/server/app-page-route-wiring.d.ts +13 -6
  113. package/dist/server/app-page-route-wiring.js +86 -63
  114. package/dist/server/app-page-rsc-render-status.d.ts +27 -1
  115. package/dist/server/app-page-rsc-render-status.js +121 -13
  116. package/dist/server/app-page-stream.d.ts +12 -8
  117. package/dist/server/app-page-stream.js +4 -3
  118. package/dist/server/app-pages-bridge.d.ts +4 -0
  119. package/dist/server/app-pages-bridge.js +7 -3
  120. package/dist/server/app-render-dependency.d.ts +11 -4
  121. package/dist/server/app-render-dependency.js +86 -38
  122. package/dist/server/app-request-stage-dispatch.js +1 -1
  123. package/dist/server/app-request-stage-independent-entry.js +1 -1
  124. package/dist/server/app-route-handler-cache.js +2 -2
  125. package/dist/server/app-route-handler-dispatch.js +14 -9
  126. package/dist/server/app-route-handler-execution.js +11 -5
  127. package/dist/server/app-route-handler-policy.d.ts +1 -1
  128. package/dist/server/app-route-handler-policy.js +3 -3
  129. package/dist/server/app-route-handler-response.js +1 -1
  130. package/dist/server/app-route-handler-runtime.d.ts +1 -1
  131. package/dist/server/app-route-handler-runtime.js +28 -11
  132. package/dist/server/app-router-entry.js +1 -1
  133. package/dist/server/app-rsc-cache-busting.d.ts +12 -12
  134. package/dist/server/app-rsc-cache-busting.js +31 -72
  135. package/dist/server/app-rsc-error-handler.d.ts +2 -1
  136. package/dist/server/app-rsc-error-handler.js +10 -2
  137. package/dist/server/app-rsc-errors.d.ts +8 -0
  138. package/dist/server/app-rsc-errors.js +11 -1
  139. package/dist/server/app-rsc-handler.d.ts +5 -5
  140. package/dist/server/app-rsc-handler.js +80 -83
  141. package/dist/server/app-rsc-request-normalization.d.ts +10 -16
  142. package/dist/server/app-rsc-request-normalization.js +12 -18
  143. package/dist/server/app-rsc-response-finalizer.js +2 -2
  144. package/dist/server/app-rsc-response-stage.js +9 -15
  145. package/dist/server/app-server-action-execution.d.ts +3 -1
  146. package/dist/server/app-server-action-execution.js +15 -8
  147. package/dist/server/app-ssr-entry.d.ts +8 -0
  148. package/dist/server/app-ssr-entry.js +22 -16
  149. package/dist/server/app-ssr-stream.js +1 -1
  150. package/dist/server/app-visited-response-cache.d.ts +36 -1
  151. package/dist/server/app-visited-response-cache.js +46 -5
  152. package/dist/server/app-worker-stages.d.ts +37 -6
  153. package/dist/server/app-worker-stages.js +81 -9
  154. package/dist/server/artifact-compatibility.d.ts +0 -1
  155. package/dist/server/artifact-compatibility.js +1 -10
  156. package/dist/server/bfcache-identity.d.ts +7 -0
  157. package/dist/server/bfcache-identity.js +10 -1
  158. package/dist/server/cache-proof.d.ts +1 -115
  159. package/dist/server/cache-proof.js +1 -544
  160. package/dist/server/cacheability-manifest.js +1 -2
  161. package/dist/server/cacheability-request.js +13 -8
  162. package/dist/server/dev-origin-check.d.ts +2 -1
  163. package/dist/server/dev-origin-check.js +2 -1
  164. package/dist/server/dev-server.js +6 -4
  165. package/dist/server/headers.d.ts +6 -3
  166. package/dist/server/headers.js +9 -5
  167. package/dist/server/image-optimization.d.ts +5 -0
  168. package/dist/server/image-optimization.js +6 -1
  169. package/dist/server/isr-cache.d.ts +8 -2
  170. package/dist/server/isr-cache.js +30 -2
  171. package/dist/server/metadata-route-response.js +32 -15
  172. package/dist/server/middleware-matcher.js +19 -6
  173. package/dist/server/middleware-runtime.js +6 -2
  174. package/dist/server/multi-stage.d.ts +57 -12
  175. package/dist/server/multi-stage.js +13 -1
  176. package/dist/server/navigation-planner.js +1 -1
  177. package/dist/server/pages-api-route.js +28 -19
  178. package/dist/server/pages-media-type.d.ts +21 -1
  179. package/dist/server/pages-media-type.js +69 -5
  180. package/dist/server/pages-node-compat.d.ts +1 -1
  181. package/dist/server/pages-node-compat.js +54 -27
  182. package/dist/server/pages-page-data.js +2 -1
  183. package/dist/server/pages-page-handler.js +28 -26
  184. package/dist/server/pages-page-response.d.ts +9 -0
  185. package/dist/server/pages-page-response.js +18 -4
  186. package/dist/server/pages-request-pipeline.d.ts +8 -0
  187. package/dist/server/pages-request-pipeline.js +2 -2
  188. package/dist/server/pages-request-stage-entry.js +36 -14
  189. package/dist/server/pages-response-stage-entry.js +9 -3
  190. package/dist/server/pages-router-entry.js +3 -2
  191. package/dist/server/prod-server.d.ts +2 -2
  192. package/dist/server/prod-server.js +47 -9
  193. package/dist/server/request-error-log.d.ts +9 -0
  194. package/dist/server/request-error-log.js +13 -0
  195. package/dist/server/request-pipeline.d.ts +15 -4
  196. package/dist/server/request-pipeline.js +43 -15
  197. package/dist/server/rsc-completion-metadata.js +3 -0
  198. package/dist/server/shared-cache-identity.d.ts +23 -0
  199. package/dist/server/shared-cache-identity.js +36 -0
  200. package/dist/server/static-file-cache.d.ts +9 -0
  201. package/dist/server/static-file-cache.js +13 -2
  202. package/dist/server/worker-stages.d.ts +16 -3
  203. package/dist/server/worker-stages.js +23 -4
  204. package/dist/shims/cache-handler.js +2 -2
  205. package/dist/shims/cache-request-state.d.ts +23 -26
  206. package/dist/shims/cache-request-state.js +22 -57
  207. package/dist/shims/cache-runtime.d.ts +3 -1
  208. package/dist/shims/cache-runtime.js +94 -48
  209. package/dist/shims/cache.d.ts +16 -10
  210. package/dist/shims/cache.js +47 -36
  211. package/dist/shims/cacheability-classification.d.ts +2 -0
  212. package/dist/shims/cdn-cache.d.ts +22 -5
  213. package/dist/shims/client-hook-error.d.ts +2 -0
  214. package/dist/shims/client-hook-error.js +16 -1
  215. package/dist/shims/client-page-root.js +2 -2
  216. package/dist/shims/dynamic.js +9 -6
  217. package/dist/shims/error-boundary.js +2 -2
  218. package/dist/shims/fetch-cache-state.d.ts +5 -1
  219. package/dist/shims/fetch-cache-state.js +1 -3
  220. package/dist/shims/fetch-cache.d.ts +2 -1
  221. package/dist/shims/fetch-cache.js +39 -25
  222. package/dist/shims/form.js +2 -2
  223. package/dist/shims/headers.d.ts +10 -24
  224. package/dist/shims/headers.js +22 -70
  225. package/dist/shims/internal/cache-context-accessor.d.ts +1 -1
  226. package/dist/shims/internal/navigation-untracked.js +1 -1
  227. package/dist/shims/link.js +8 -11
  228. package/dist/shims/metadata.d.ts +23 -21
  229. package/dist/shims/metadata.js +62 -24
  230. package/dist/shims/navigation-server.js +1 -1
  231. package/dist/shims/navigation.d.ts +74 -7
  232. package/dist/shims/navigation.js +184 -31
  233. package/dist/shims/navigation.react-server.js +2 -2
  234. package/dist/shims/request-state-types.d.ts +3 -2
  235. package/dist/shims/slot.d.ts +16 -0
  236. package/dist/shims/slot.js +44 -9
  237. package/dist/shims/unified-request-context.d.ts +7 -1
  238. package/dist/shims/unified-request-context.js +3 -3
  239. package/dist/utils/cache-control-metadata.d.ts +5 -0
  240. package/dist/utils/cache-control-metadata.js +9 -1
  241. package/dist/utils/cache-life-profiles.d.ts +33 -0
  242. package/dist/utils/cache-life-profiles.js +111 -0
  243. package/dist/utils/middleware-request-headers.d.ts +14 -3
  244. package/dist/utils/middleware-request-headers.js +54 -7
  245. package/dist/utils/project.d.ts +2 -2
  246. package/dist/utils/project.js +3 -16
  247. package/dist/utils/regex.d.ts +5 -0
  248. package/dist/utils/regex.js +11 -1
  249. package/dist/utils/text-stream.d.ts +10 -5
  250. package/dist/utils/text-stream.js +6 -6
  251. package/package.json +2 -1
  252. package/dist/build/layout-classification-types.d.ts +0 -60
  253. package/dist/build/layout-classification-types.js +0 -1
  254. package/dist/build/layout-classification.d.ts +0 -62
  255. package/dist/build/layout-classification.js +0 -98
  256. package/dist/build/route-classification-injector.d.ts +0 -33
  257. package/dist/build/route-classification-injector.js +0 -59
  258. package/dist/build/route-classification-manifest.d.ts +0 -54
  259. package/dist/build/route-classification-manifest.js +0 -143
  260. package/dist/server/app-browser-client-reuse-manifest.d.ts +0 -11
  261. package/dist/server/app-browser-client-reuse-manifest.js +0 -101
  262. package/dist/server/app-layout-param-observation.d.ts +0 -42
  263. package/dist/server/app-layout-param-observation.js +0 -168
  264. package/dist/server/client-reuse-manifest.d.ts +0 -103
  265. package/dist/server/client-reuse-manifest.js +0 -209
  266. package/dist/server/skip-cache-proof.d.ts +0 -60
  267. package/dist/server/skip-cache-proof.js +0 -168
  268. package/dist/server/static-layout-client-reuse-proof.d.ts +0 -14
  269. package/dist/server/static-layout-client-reuse-proof.js +0 -33
  270. package/dist/utils/sorted-array.d.ts +0 -8
  271. package/dist/utils/sorted-array.js +0 -20
@@ -3,7 +3,6 @@ import { compareStrings } from "../utils/compare.js";
3
3
  import { isInterceptionMatchedUrlPath } from "./normalize-path.js";
4
4
  import { createAppElementsWireSlotId, isAppElementsWireSlotId, parseAppElementsWireElementKey } from "./app-elements-wire-key.js";
5
5
  import { createArtifactCompatibilityEnvelope, parseArtifactCompatibilityEnvelope } from "./artifact-compatibility.js";
6
- import { releaseAppElementRenderDependency } from "./app-render-dependency.js";
7
6
  import { isValidElement } from "react";
8
7
  //#region src/server/app-elements-wire.ts
9
8
  const APP_INTERCEPTION_SEPARATOR = "\0";
@@ -14,7 +13,6 @@ const APP_DYNAMIC_STALE_TIME_KEY = "__dynamicStaleTime";
14
13
  const APP_INTERCEPTION_KEY = "__interception";
15
14
  const APP_INTERCEPTION_CONTEXT_KEY = "__interceptionContext";
16
15
  const APP_LAYOUT_IDS_KEY = "__layoutIds";
17
- const APP_LAYOUT_FLAGS_KEY = "__layoutFlags";
18
16
  const APP_ROUTE_KEY = "__route";
19
17
  const APP_ROOT_LAYOUT_KEY = "__rootLayout";
20
18
  const APP_SKIPPED_LAYOUT_IDS_KEY = "__skippedLayoutIds";
@@ -33,7 +31,6 @@ const APP_BFCACHE_SEGMENT_IDENTITIES_KEY = "__bfcacheSegmentIdentities";
33
31
  const APP_STATIC_SIBLINGS_KEY = "__staticSiblings";
34
32
  const APP_UNMATCHED_SLOT_WIRE_VALUE = "__VINEXT_UNMATCHED_SLOT__";
35
33
  const UNMATCHED_SLOT = Symbol.for("vinext.unmatchedSlot");
36
- const EMPTY_SKIPPED_LAYOUT_IDS = /* @__PURE__ */ new Set();
37
34
  function createCacheProofRejectionCodeSet(codes) {
38
35
  return new Set(codes);
39
36
  }
@@ -147,15 +144,6 @@ function normalizeAppElements(elements) {
147
144
  for (const [key, value] of Object.entries(elements)) normalized[key] = isAppElementsWireSlotId(key) && value === "__VINEXT_UNMATCHED_SLOT__" ? UNMATCHED_SLOT : value;
148
145
  return normalized;
149
146
  }
150
- function isLayoutFlagsRecord(value) {
151
- if (typeof value !== "object" || value === null || Array.isArray(value)) return false;
152
- for (const v of Object.values(value)) if (v !== "s" && v !== "d") return false;
153
- return true;
154
- }
155
- function parseLayoutFlags(value) {
156
- if (isLayoutFlagsRecord(value)) return value;
157
- return {};
158
- }
159
147
  function parseLayoutIdList(value, fieldName) {
160
148
  if (value === void 0) return [];
161
149
  if (!Array.isArray(value)) throw new Error(`[vinext] Invalid ${fieldName} in App Router payload: expected layout id string[]`);
@@ -242,7 +230,7 @@ function parseInterceptionMetadata(value) {
242
230
  * Used to distinguish the App Router payload object from bare React elements at
243
231
  * the render boundary. Narrows to `Readonly<Record<string, unknown>>` because
244
232
  * the outgoing payload carries heterogeneous values (ReactNodes for the rendered
245
- * tree, plus metadata like `__layoutFlags` which is a plain object). Delegates
233
+ * tree, plus metadata like `__artifactCompatibility`, a plain object). Delegates
246
234
  * to React's canonical `isValidElement` so we don't depend on React's internal
247
235
  * `$$typeof` marker scheme.
248
236
  */
@@ -252,36 +240,15 @@ function isAppElementsRecord(value) {
252
240
  if (isValidElement(value)) return false;
253
241
  return true;
254
242
  }
255
- function withLayoutFlags(elements, layoutFlags) {
256
- return {
257
- ...elements,
258
- [APP_LAYOUT_FLAGS_KEY]: layoutFlags
259
- };
260
- }
261
243
  function buildOutgoingAppPayload(input) {
262
244
  if (!isAppElementsRecord(input.element)) return input.element;
263
- const skippedLayoutIds = createSkippedLayoutIds(input.skipDisposition);
264
245
  const payload = {};
265
- for (const [key, value] of Object.entries(input.element)) {
266
- if (skippedLayoutIds.has(key)) {
267
- releaseAppElementRenderDependency(input.element, key);
268
- continue;
269
- }
270
- payload[key] = value === UNMATCHED_SLOT ? APP_UNMATCHED_SLOT_WIRE_VALUE : value;
271
- }
272
- payload[APP_LAYOUT_FLAGS_KEY] = input.layoutFlags;
273
- if (skippedLayoutIds.size > 0) payload[APP_SKIPPED_LAYOUT_IDS_KEY] = [...skippedLayoutIds];
246
+ for (const [key, value] of Object.entries(input.element)) payload[key] = value === UNMATCHED_SLOT ? APP_UNMATCHED_SLOT_WIRE_VALUE : value;
274
247
  payload[APP_ARTIFACT_COMPATIBILITY_KEY] = input.artifactCompatibility ?? createArtifactCompatibilityEnvelope();
275
248
  if (input.cacheEntryReuseProof) payload[APP_CACHE_ENTRY_REUSE_PROOF_KEY] = input.cacheEntryReuseProof;
276
249
  if (input.dynamicStaleTimeSeconds !== void 0) payload[APP_DYNAMIC_STALE_TIME_KEY] = input.dynamicStaleTimeSeconds;
277
250
  return payload;
278
251
  }
279
- function createSkippedLayoutIds(skipDisposition) {
280
- if (skipDisposition?.enabled !== true) return EMPTY_SKIPPED_LAYOUT_IDS;
281
- const skippedLayoutIds = /* @__PURE__ */ new Set();
282
- for (const id of skipDisposition.skippedEntryIds) if (parseAppElementsWireElementKey(id)?.kind === "layout") skippedLayoutIds.add(id);
283
- return skippedLayoutIds;
284
- }
285
252
  function readArtifactCompatibilityMetadata(value) {
286
253
  if (value === void 0) return createArtifactCompatibilityEnvelope();
287
254
  return parseArtifactCompatibilityEnvelope(value) ?? createArtifactCompatibilityEnvelope();
@@ -351,7 +318,6 @@ function readAppElementsMetadata(elements) {
351
318
  const rootLayoutTreePath = elements[APP_ROOT_LAYOUT_KEY];
352
319
  if (rootLayoutTreePath === void 0) throw new Error("[vinext] Missing __rootLayout key in App Router payload");
353
320
  if (rootLayoutTreePath !== null && typeof rootLayoutTreePath !== "string") throw new Error("[vinext] Invalid __rootLayout in App Router payload: expected string or null");
354
- const layoutFlags = parseLayoutFlags(elements[APP_LAYOUT_FLAGS_KEY]);
355
321
  const layoutIds = parseLayoutIds(elements[APP_LAYOUT_IDS_KEY]);
356
322
  const skippedLayoutIds = parseSkippedLayoutIds(elements[APP_SKIPPED_LAYOUT_IDS_KEY]);
357
323
  const slotBindings = parseSlotBindings(elements[APP_SLOT_BINDINGS_KEY], { layoutIds });
@@ -369,7 +335,6 @@ function readAppElementsMetadata(elements) {
369
335
  interception,
370
336
  interceptionContext: interceptionContext ?? null,
371
337
  layoutIds,
372
- layoutFlags,
373
338
  routeId,
374
339
  rootLayoutTreePath,
375
340
  bfcacheSegmentIdentities,
@@ -386,7 +351,6 @@ const AppElementsWire = {
386
351
  interception: APP_INTERCEPTION_KEY,
387
352
  interceptionContext: APP_INTERCEPTION_CONTEXT_KEY,
388
353
  layoutIds: APP_LAYOUT_IDS_KEY,
389
- layoutFlags: APP_LAYOUT_FLAGS_KEY,
390
354
  rootLayout: APP_ROOT_LAYOUT_KEY,
391
355
  route: APP_ROUTE_KEY,
392
356
  bfcacheSegmentIdentities: APP_BFCACHE_SEGMENT_IDENTITIES_KEY,
@@ -406,8 +370,7 @@ const AppElementsWire = {
406
370
  encodeTemplateId: createAppPayloadTemplateId,
407
371
  isSlotId: isAppElementsWireSlotId,
408
372
  parseElementKey: parseAppElementsWireElementKey,
409
- readMetadata: readAppElementsMetadata,
410
- withLayoutFlags
373
+ readMetadata: readAppElementsMetadata
411
374
  };
412
375
  //#endregion
413
- export { APP_ARTIFACT_COMPATIBILITY_KEY, APP_BFCACHE_SEGMENT_IDENTITIES_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_ROOT_LAYOUT_KEY, APP_ROUTE_KEY, APP_SKIPPED_LAYOUT_IDS_KEY, APP_SLOT_BINDINGS_KEY, APP_SOURCE_PAGE_SEGMENTS_KEY, APP_STATIC_SIBLINGS_KEY, APP_UNMATCHED_SLOT_WIRE_VALUE, AppElementsWire, UNMATCHED_SLOT, buildOutgoingAppPayload, compareAppElementsSlotIds, isAppElementsRecord, normalizeAppElements, normalizeAppElementsSlotBindings, readAppElementsMetadata, withLayoutFlags };
376
+ export { APP_ARTIFACT_COMPATIBILITY_KEY, APP_BFCACHE_SEGMENT_IDENTITIES_KEY, APP_CACHE_ENTRY_REUSE_PROOF_KEY, APP_DYNAMIC_STALE_TIME_KEY, APP_INTERCEPTION_CONTEXT_KEY, APP_INTERCEPTION_KEY, APP_LAYOUT_IDS_KEY, APP_ROOT_LAYOUT_KEY, APP_ROUTE_KEY, APP_SKIPPED_LAYOUT_IDS_KEY, APP_SLOT_BINDINGS_KEY, APP_SOURCE_PAGE_SEGMENTS_KEY, APP_STATIC_SIBLINGS_KEY, APP_UNMATCHED_SLOT_WIRE_VALUE, AppElementsWire, UNMATCHED_SLOT, buildOutgoingAppPayload, compareAppElementsSlotIds, isAppElementsRecord, normalizeAppElements, normalizeAppElementsSlotBindings, readAppElementsMetadata };
@@ -1,4 +1,4 @@
1
- import { APP_ARTIFACT_COMPATIBILITY_KEY, APP_BFCACHE_SEGMENT_IDENTITIES_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_ROOT_LAYOUT_KEY, APP_ROUTE_KEY, APP_SKIPPED_LAYOUT_IDS_KEY, APP_SLOT_BINDINGS_KEY, APP_SOURCE_PAGE_SEGMENTS_KEY, APP_STATIC_SIBLINGS_KEY, APP_UNMATCHED_SLOT_WIRE_VALUE, AppElementValue, AppElements, AppElementsBfcacheSegmentIdentities, 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_BFCACHE_SEGMENT_IDENTITIES_KEY, APP_CACHE_ENTRY_REUSE_PROOF_KEY, APP_DYNAMIC_STALE_TIME_KEY, APP_INTERCEPTION_CONTEXT_KEY, APP_INTERCEPTION_KEY, APP_LAYOUT_IDS_KEY, APP_ROOT_LAYOUT_KEY, APP_ROUTE_KEY, APP_SKIPPED_LAYOUT_IDS_KEY, APP_SLOT_BINDINGS_KEY, APP_SOURCE_PAGE_SEGMENTS_KEY, APP_STATIC_SIBLINGS_KEY, APP_UNMATCHED_SLOT_WIRE_VALUE, AppElementValue, AppElements, AppElementsBfcacheSegmentIdentities, AppElementsInterception, AppElementsSlotBinding, AppElementsWire, AppOutgoingElements, AppWireElements, UNMATCHED_SLOT, buildOutgoingAppPayload, compareAppElementsSlotIds, isAppElementsRecord, normalizeAppElements, normalizeAppElementsSlotBindings, readAppElementsMetadata } from "./app-elements-wire.js";
2
2
  //#region src/server/app-elements.d.ts
3
3
  export declare const APP_PREFETCH_LOADING_SHELL_MARKER_KEY = "__prefetchLoadingShell";
4
4
  /** Prefix of the element that streams a route's generateMetadata() tags in the body. */
@@ -7,4 +7,4 @@ export declare function getMountedSlotIds(elements: AppElements): string[];
7
7
  export declare function getMountedSlotIdsHeader(elements: AppElements): string | null;
8
8
  export declare function resolveVisitedResponseInterceptionContext(requestInterceptionContext: string | null, payloadInterceptionContext: string | null): string | null;
9
9
  //#endregion
10
- export { APP_ARTIFACT_COMPATIBILITY_KEY, APP_BFCACHE_SEGMENT_IDENTITIES_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_ROOT_LAYOUT_KEY, APP_ROUTE_KEY, APP_SKIPPED_LAYOUT_IDS_KEY, APP_SLOT_BINDINGS_KEY, APP_SOURCE_PAGE_SEGMENTS_KEY, APP_STATIC_SIBLINGS_KEY, APP_UNMATCHED_SLOT_WIRE_VALUE, type AppElementValue, type AppElements, type AppElementsBfcacheSegmentIdentities, type AppElementsInterception, type AppElementsSlotBinding, AppElementsWire, type AppOutgoingElements, type AppWireElements, type LayoutFlags, UNMATCHED_SLOT, buildOutgoingAppPayload, compareAppElementsSlotIds, isAppElementsRecord, normalizeAppElements, normalizeAppElementsSlotBindings, readAppElementsMetadata, withLayoutFlags };
10
+ export { APP_ARTIFACT_COMPATIBILITY_KEY, APP_BFCACHE_SEGMENT_IDENTITIES_KEY, APP_CACHE_ENTRY_REUSE_PROOF_KEY, APP_DYNAMIC_STALE_TIME_KEY, APP_INTERCEPTION_CONTEXT_KEY, APP_INTERCEPTION_KEY, APP_LAYOUT_IDS_KEY, APP_ROOT_LAYOUT_KEY, APP_ROUTE_KEY, APP_SKIPPED_LAYOUT_IDS_KEY, APP_SLOT_BINDINGS_KEY, APP_SOURCE_PAGE_SEGMENTS_KEY, APP_STATIC_SIBLINGS_KEY, APP_UNMATCHED_SLOT_WIRE_VALUE, type AppElementValue, type AppElements, type AppElementsBfcacheSegmentIdentities, type AppElementsInterception, type AppElementsSlotBinding, AppElementsWire, type AppOutgoingElements, type AppWireElements, UNMATCHED_SLOT, buildOutgoingAppPayload, compareAppElementsSlotIds, isAppElementsRecord, normalizeAppElements, normalizeAppElementsSlotBindings, readAppElementsMetadata };
@@ -1,5 +1,5 @@
1
1
  import { normalizeMountedSlotsHeader } from "./app-mounted-slots-header.js";
2
- import { APP_ARTIFACT_COMPATIBILITY_KEY, APP_BFCACHE_SEGMENT_IDENTITIES_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_ROOT_LAYOUT_KEY, APP_ROUTE_KEY, APP_SKIPPED_LAYOUT_IDS_KEY, APP_SLOT_BINDINGS_KEY, APP_SOURCE_PAGE_SEGMENTS_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
+ import { APP_ARTIFACT_COMPATIBILITY_KEY, APP_BFCACHE_SEGMENT_IDENTITIES_KEY, APP_CACHE_ENTRY_REUSE_PROOF_KEY, APP_DYNAMIC_STALE_TIME_KEY, APP_INTERCEPTION_CONTEXT_KEY, APP_INTERCEPTION_KEY, APP_LAYOUT_IDS_KEY, APP_ROOT_LAYOUT_KEY, APP_ROUTE_KEY, APP_SKIPPED_LAYOUT_IDS_KEY, APP_SLOT_BINDINGS_KEY, APP_SOURCE_PAGE_SEGMENTS_KEY, APP_STATIC_SIBLINGS_KEY, APP_UNMATCHED_SLOT_WIRE_VALUE, AppElementsWire, UNMATCHED_SLOT, buildOutgoingAppPayload, compareAppElementsSlotIds, isAppElementsRecord, normalizeAppElements, normalizeAppElementsSlotBindings, readAppElementsMetadata } from "./app-elements-wire.js";
3
3
  //#region src/server/app-elements.ts
4
4
  const APP_PREFETCH_LOADING_SHELL_MARKER_KEY = "__prefetchLoadingShell";
5
5
  /** Prefix of the element that streams a route's generateMetadata() tags in the body. */
@@ -18,4 +18,4 @@ function resolveVisitedResponseInterceptionContext(requestInterceptionContext, p
18
18
  return payloadInterceptionContext ?? requestInterceptionContext;
19
19
  }
20
20
  //#endregion
21
- export { APP_ARTIFACT_COMPATIBILITY_KEY, APP_BFCACHE_SEGMENT_IDENTITIES_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_ROOT_LAYOUT_KEY, APP_ROUTE_KEY, APP_SKIPPED_LAYOUT_IDS_KEY, APP_SLOT_BINDINGS_KEY, APP_SOURCE_PAGE_SEGMENTS_KEY, APP_STATIC_SIBLINGS_KEY, APP_STREAMING_METADATA_BODY_ID_PREFIX, APP_UNMATCHED_SLOT_WIRE_VALUE, AppElementsWire, UNMATCHED_SLOT, buildOutgoingAppPayload, compareAppElementsSlotIds, getMountedSlotIds, getMountedSlotIdsHeader, isAppElementsRecord, normalizeAppElements, normalizeAppElementsSlotBindings, readAppElementsMetadata, resolveVisitedResponseInterceptionContext, withLayoutFlags };
21
+ export { APP_ARTIFACT_COMPATIBILITY_KEY, APP_BFCACHE_SEGMENT_IDENTITIES_KEY, APP_CACHE_ENTRY_REUSE_PROOF_KEY, APP_DYNAMIC_STALE_TIME_KEY, APP_INTERCEPTION_CONTEXT_KEY, APP_INTERCEPTION_KEY, APP_LAYOUT_IDS_KEY, APP_PREFETCH_LOADING_SHELL_MARKER_KEY, APP_ROOT_LAYOUT_KEY, APP_ROUTE_KEY, APP_SKIPPED_LAYOUT_IDS_KEY, APP_SLOT_BINDINGS_KEY, APP_SOURCE_PAGE_SEGMENTS_KEY, APP_STATIC_SIBLINGS_KEY, APP_STREAMING_METADATA_BODY_ID_PREFIX, APP_UNMATCHED_SLOT_WIRE_VALUE, AppElementsWire, UNMATCHED_SLOT, buildOutgoingAppPayload, compareAppElementsSlotIds, getMountedSlotIds, getMountedSlotIdsHeader, isAppElementsRecord, normalizeAppElements, normalizeAppElementsSlotBindings, readAppElementsMetadata, resolveVisitedResponseInterceptionContext };
@@ -75,11 +75,11 @@ export declare function isExternalHistoryState(state: unknown): boolean;
75
75
  */
76
76
  export declare function isAppOwnedHistoryState(state: unknown): boolean;
77
77
  export declare function createAppOwnedHistoryState(state: unknown): unknown;
78
- export declare function createHistoryStateWithTreeSnapshotId(state: unknown, treeSnapshotId: number | null): unknown;
78
+ export declare function createHistoryStateWithTreeSnapshotId(state: unknown, treeSnapshotId: string | null): unknown;
79
79
  export declare function createHistoryStateWithTreeSnapshotClaim(state: unknown, claimed: boolean): unknown;
80
80
  export declare function isHistoryStateTreeSnapshotClaimed(state: unknown): boolean;
81
81
  export declare function clearHistoryStateTreeSnapshotId(state: unknown): unknown;
82
- export declare function readHistoryStateTreeSnapshotId(state: unknown): number | null;
82
+ export declare function readHistoryStateTreeSnapshotId(state: unknown): string | null;
83
83
  export declare function readHistoryStateActiveRoutePaths(state: unknown): readonly string[] | null;
84
84
  export declare function resolveActiveRoutePaths(slotBindings: readonly {
85
85
  activeRouteId?: string | null;
@@ -210,7 +210,7 @@ function createAppOwnedHistoryState(state) {
210
210
  function createHistoryStateWithTreeSnapshotId(state, treeSnapshotId) {
211
211
  const nextState = cloneHistoryState(state);
212
212
  const previousTreeSnapshotId = readHistoryStateTreeSnapshotId(nextState);
213
- if (isNonNegativeSafeInteger(treeSnapshotId)) {
213
+ if (isTreeSnapshotId(treeSnapshotId)) {
214
214
  nextState[VINEXT_TREE_SNAPSHOT_ID_HISTORY_STATE_KEY] = treeSnapshotId;
215
215
  if (previousTreeSnapshotId !== treeSnapshotId) delete nextState[VINEXT_TREE_SNAPSHOT_CLAIMED_HISTORY_STATE_KEY];
216
216
  } else {
@@ -233,7 +233,10 @@ function clearHistoryStateTreeSnapshotId(state) {
233
233
  }
234
234
  function readHistoryStateTreeSnapshotId(state) {
235
235
  const value = readHistoryStateRecord(state)?.[VINEXT_TREE_SNAPSHOT_ID_HISTORY_STATE_KEY];
236
- return isNonNegativeSafeInteger(value) ? value : null;
236
+ return isTreeSnapshotId(value) ? value : null;
237
+ }
238
+ function isTreeSnapshotId(value) {
239
+ return typeof value === "string" && value.length > 0;
237
240
  }
238
241
  function readHistoryStateActiveRoutePaths(state) {
239
242
  const value = readHistoryStateRecord(state)?.[VINEXT_ACTIVE_ROUTE_PATHS_HISTORY_STATE_KEY];
@@ -0,0 +1,21 @@
1
+ import { AppElements } from "./app-elements-wire.js";
2
+ import "./app-elements.js";
3
+ //#region src/server/app-mounted-layouts.d.ts
4
+ export declare function hashMountedLayoutIdentity(identity: string): string;
5
+ export declare function parseMountedLayoutsHeader(value: string | null): ReadonlySet<string> | null;
6
+ /**
7
+ * The header for the outermost layouts a client state holds. Only that prefix
8
+ * can be left out of a render, as a layout below one that re-renders renders
9
+ * inside it.
10
+ */
11
+ export declare function createMountedLayoutsHeader(elements: AppElements, layoutIds: readonly string[]): string | null;
12
+ /**
13
+ * Whether a client state still holds, unchanged, every layout a payload left
14
+ * out. A navigation that started from another state, or a payload stored for
15
+ * another client, can name layouts this one doesn't hold.
16
+ */
17
+ export declare function holdsSkippedLayouts(current: {
18
+ elements: AppElements;
19
+ layoutIds: readonly string[];
20
+ }, next: AppElements): boolean;
21
+ //#endregion
@@ -0,0 +1,67 @@
1
+ import { fnv1a64 } from "../utils/hash.js";
2
+ import { AppElementsWire } from "./app-elements-wire.js";
3
+ import "./app-elements.js";
4
+ //#region src/server/app-mounted-layouts.ts
5
+ /**
6
+ * A client navigation names the layouts it already holds, so a render that's
7
+ * never stored can leave them out, as Next.js leaves out the segments above
8
+ * where a navigation diverges from its router state tree.
9
+ *
10
+ * Each entry hashes a layout's segment identity, which binds the layout to its
11
+ * route prefix and that prefix's params, so a layout whose params changed never
12
+ * matches.
13
+ */
14
+ const MAX_MOUNTED_LAYOUTS = 64;
15
+ const MOUNTED_LAYOUT_HASH = /^[0-9a-f]{16}$/;
16
+ function hashMountedLayoutIdentity(identity) {
17
+ return fnv1a64(identity);
18
+ }
19
+ function serializeMountedLayouts(identities) {
20
+ if (identities.length === 0) return null;
21
+ return identities.slice(0, MAX_MOUNTED_LAYOUTS).map(hashMountedLayoutIdentity).join(",");
22
+ }
23
+ function parseMountedLayoutsHeader(value) {
24
+ if (!value) return null;
25
+ const hashes = /* @__PURE__ */ new Set();
26
+ for (const entry of value.split(",", 65)) {
27
+ const hash = entry.trim();
28
+ if (!MOUNTED_LAYOUT_HASH.test(hash)) return null;
29
+ hashes.add(hash);
30
+ }
31
+ if (hashes.size === 0 || hashes.size > MAX_MOUNTED_LAYOUTS) return null;
32
+ return hashes;
33
+ }
34
+ /**
35
+ * The header for the outermost layouts a client state holds. Only that prefix
36
+ * can be left out of a render, as a layout below one that re-renders renders
37
+ * inside it.
38
+ */
39
+ function createMountedLayoutsHeader(elements, layoutIds) {
40
+ const identities = AppElementsWire.readMetadata(elements).bfcacheSegmentIdentities;
41
+ const held = [];
42
+ for (const layoutId of layoutIds) {
43
+ const identity = identities[layoutId];
44
+ if (identity === void 0 || !Object.hasOwn(elements, layoutId)) break;
45
+ held.push(identity);
46
+ }
47
+ return serializeMountedLayouts(held);
48
+ }
49
+ /**
50
+ * Whether a client state still holds, unchanged, every layout a payload left
51
+ * out. A navigation that started from another state, or a payload stored for
52
+ * another client, can name layouts this one doesn't hold.
53
+ */
54
+ function holdsSkippedLayouts(current, next) {
55
+ const nextMetadata = AppElementsWire.readMetadata(next);
56
+ if (nextMetadata.skippedLayoutIds.length === 0) return true;
57
+ const currentIdentities = AppElementsWire.readMetadata(current.elements).bfcacheSegmentIdentities;
58
+ const currentLayoutIds = new Set(current.layoutIds);
59
+ for (const layoutId of nextMetadata.skippedLayoutIds) {
60
+ if (!currentLayoutIds.has(layoutId) || !Object.hasOwn(current.elements, layoutId)) return false;
61
+ const identity = nextMetadata.bfcacheSegmentIdentities[layoutId];
62
+ if (identity === void 0 || identity !== currentIdentities[layoutId]) return false;
63
+ }
64
+ return true;
65
+ }
66
+ //#endregion
67
+ export { createMountedLayoutsHeader, hashMountedLayoutIdentity, holdsSkippedLayouts, parseMountedLayoutsHeader };
@@ -1,7 +1,7 @@
1
1
  import { BrowserRouteManifest, BrowserRouteManifestRoute } from "../routing/browser-route-manifest.js";
2
- import { OptimisticRouteTrieMatch } from "../routing/optimistic-route-trie.js";
3
2
  import { AppElements } from "./app-elements-wire.js";
4
3
  import "./app-elements.js";
4
+ import { OptimisticRouteTrieMatch } from "../routing/optimistic-route-trie.js";
5
5
  //#region src/server/app-optimistic-routing.d.ts
6
6
  type OptimisticRouteMatch = OptimisticRouteTrieMatch<BrowserRouteManifestRoute>;
7
7
  export type OptimisticRouteTemplate = {
@@ -90,5 +90,23 @@ export declare function resolveOptimisticNavigationPayload(options: {
90
90
  routeManifest: BrowserRouteManifest;
91
91
  templates: ReadonlyMap<string, OptimisticRouteTemplate>;
92
92
  }): OptimisticNavigationPayload | null;
93
+ export type LearnedOptimisticRouteTemplate = {
94
+ key: string;
95
+ template: OptimisticRouteTemplate;
96
+ };
97
+ /**
98
+ * Optimistic route templates and the prefetch sources they were learned from.
99
+ * `clear()` retires every template, so none can outlive the prefetch payload
100
+ * it was derived from, and drops any result a learning started before the
101
+ * clear resolves to. Sources that are still cached are learned again on the
102
+ * next optimistic navigation.
103
+ */
104
+ export declare function createOptimisticRouteTemplateStore(): {
105
+ templates: ReadonlyMap<string, OptimisticRouteTemplate>;
106
+ clear(): void;
107
+ hasSource(sourceKey: string): boolean;
108
+ learn(sourceKey: string, learnTemplate: () => Promise<LearnedOptimisticRouteTemplate | null>): Promise<void>;
109
+ pendingLearning(): Promise<void>[];
110
+ };
93
111
  //#endregion
94
112
  export {};
@@ -3,10 +3,10 @@ import { stripBasePath } from "../utils/base-path.js";
3
3
  import { decodeMatchedParams, splitPathnameForRouteMatch } from "../routing/utils.js";
4
4
  import { extractRawRoutePatternParams, matchRoutePattern } from "../routing/route-pattern.js";
5
5
  import { buildOptimisticRouteTrie, matchOptimisticRouteTrie } from "../routing/optimistic-route-trie.js";
6
+ import { stripRscCacheBustingSearchParam } from "./app-rsc-cache-busting.js";
6
7
  import { AppElementsWire } from "./app-elements-wire.js";
7
8
  import "./app-elements.js";
8
9
  import { createNestedBfcacheSlotSegmentId, deriveBfcacheSegmentIdentity, isNestedBfcacheSlotSegmentId } from "./bfcache-identity.js";
9
- import { stripRscCacheBustingSearchParam, stripRscSuffix } from "./app-rsc-cache-busting.js";
10
10
  import { canonicalizeAppPageParams, resolveAppPagePatternStateKey, resolveAppPageSemanticSegmentStateKey } from "./app-page-segment-state.js";
11
11
  import { Suspense, createElement, isValidElement } from "react";
12
12
  //#region src/server/app-optimistic-routing.ts
@@ -36,8 +36,7 @@ function hrefToRouteParts(href, basePath) {
36
36
  return null;
37
37
  }
38
38
  stripRscCacheBustingSearchParam(url);
39
- const withoutRscSuffix = stripRscSuffix(url.pathname);
40
- const appPathname = stripBasePath(withoutRscSuffix, basePath);
39
+ const appPathname = stripBasePath(url.pathname, basePath);
41
40
  const pathname = appPathname === "" ? "/" : appPathname;
42
41
  return {
43
42
  normalized: splitPathnameForRouteMatch(pathname),
@@ -310,5 +309,45 @@ function resolveOptimisticNavigationPayload(options) {
310
309
  urlParts: urlParts.normalized
311
310
  };
312
311
  }
312
+ /**
313
+ * Optimistic route templates and the prefetch sources they were learned from.
314
+ * `clear()` retires every template, so none can outlive the prefetch payload
315
+ * it was derived from, and drops any result a learning started before the
316
+ * clear resolves to. Sources that are still cached are learned again on the
317
+ * next optimistic navigation.
318
+ */
319
+ function createOptimisticRouteTemplateStore() {
320
+ const templates = /* @__PURE__ */ new Map();
321
+ const learnedSources = /* @__PURE__ */ new Set();
322
+ const learning = /* @__PURE__ */ new Map();
323
+ let generation = 0;
324
+ return {
325
+ templates,
326
+ clear() {
327
+ generation += 1;
328
+ templates.clear();
329
+ learnedSources.clear();
330
+ learning.clear();
331
+ },
332
+ hasSource(sourceKey) {
333
+ return learnedSources.has(sourceKey) || learning.has(sourceKey);
334
+ },
335
+ learn(sourceKey, learnTemplate) {
336
+ const learnedGeneration = generation;
337
+ const promise = learnTemplate().then((learned) => {
338
+ if (learned === null || learnedGeneration !== generation) return;
339
+ templates.set(learned.key, learned.template);
340
+ learnedSources.add(sourceKey);
341
+ }).finally(() => {
342
+ if (learning.get(sourceKey) === promise) learning.delete(sourceKey);
343
+ });
344
+ learning.set(sourceKey, promise);
345
+ return promise;
346
+ },
347
+ pendingLearning() {
348
+ return [...learning.values()];
349
+ }
350
+ };
351
+ }
313
352
  //#endregion
314
- export { canCommitOptimisticRouteTemplate, createOptimisticRouteElements, createOptimisticRouteTemplate, getOptimisticPrefetchSourceKey, getOptimisticRouteTemplateKey, keepOptimisticStreamingMetadata, matchOptimisticRouteManifestRoute, resolveOptimisticNavigationParamsForHref, resolveOptimisticNavigationPayload };
353
+ export { canCommitOptimisticRouteTemplate, createOptimisticRouteElements, createOptimisticRouteTemplate, createOptimisticRouteTemplateStore, getOptimisticPrefetchSourceKey, getOptimisticRouteTemplateKey, keepOptimisticStreamingMetadata, matchOptimisticRouteManifestRoute, resolveOptimisticNavigationParamsForHref, resolveOptimisticNavigationPayload };
@@ -2,26 +2,25 @@ import { stripBasePath } from "../utils/base-path.js";
2
2
  import { isNavigationSignalError } from "../utils/navigation-signal.js";
3
3
  import { NEVER_CACHE_CONTROL, applyCdnResponseHeaders } from "./cache-control.js";
4
4
  import { isRenderDynamicLatched, isStaticGenerationAccessError } from "../shims/internal/headers-state.js";
5
+ import { copyLinkHeaderProvenance } from "./app-response-header-provenance.js";
5
6
  import { createArtifactCompatibilityEnvelope } from "./artifact-compatibility.js";
6
7
  import { AppElementsWire } from "./app-elements-wire.js";
7
8
  import "./app-elements.js";
8
9
  import DefaultGlobalError from "../shims/default-global-error.js";
9
10
  import { ErrorBoundary, GlobalErrorBoundary, SerializedErrorBoundary } from "../shims/error-boundary.js";
11
+ import { rewriteClientHookError } from "../shims/client-hook-error.js";
10
12
  import { LayoutSegmentProvider } from "../shims/layout-segment-context.js";
11
13
  import { withUseCacheLayoutMarker } from "../shims/internal/app-page-props-cache-key.js";
12
14
  import { MetadataHead, ViewportHead } from "../shims/metadata.js";
13
- import { stripRscSuffix } from "./app-rsc-cache-busting.js";
14
- import { copyLinkHeaderProvenance } from "./app-response-header-provenance.js";
15
15
  import { buildRscRedirectFlightStream } from "./app-rsc-redirect-flight.js";
16
16
  import { bufferAppPageBinaryStream, buildAppPageSpecialErrorResponse, resolveAppPageSpecialError } from "./app-page-execution.js";
17
- import { resolveAppPageBranchParams, resolveAppPageSegmentParams } from "./app-page-params.js";
18
- import { createAppPageLayoutEntries, createAppPageRouteBodyMetadata, createAppPageSourcePage } from "./app-page-route-wiring.js";
19
- import { buildClientHookErrorMessage } from "../shims/client-hook-error.js";
20
17
  import { createAppPageMetadataSearchParamsObserver } from "./app-page-search-params-observation.js";
21
- import { hasAppPageDynamicMetadata, prepareAppPageHead, resolveActiveParallelRouteHeadInputs, resolveAppPageHead, slotInterceptKeepsSourceHead } from "./app-page-head.js";
18
+ import { resolveAppPageBranchParams, resolveAppPageSegmentParams } from "./app-page-params.js";
19
+ import { hasAppPageDynamicMetadata, prepareAppPageHead, resolveActiveParallelRouteHeadInputs, resolveAppPageHead, resolveInterceptLayoutsBesidePage, slotInterceptKeepsSourceHead } from "./app-page-head.js";
22
20
  import { renderAppPageBoundaryResponse, resolveAppPageErrorBoundary, resolveAppPageHttpAccessBoundaryModule, wrapAppPageBoundaryElement } from "./app-page-boundary.js";
23
21
  import { createRouteNotFoundHeadPlanOptions, recoverAppPageRouteHead, resolveHttpAccessFallbackMetadata, resolveHttpAccessFallbackViewport } from "./app-page-http-access-fallback-metadata.js";
24
22
  import "./app-rsc-route-matching.js";
23
+ import { createAppPageLayoutEntries, createAppPageRouteBodyMetadata, createAppPageSourcePage } from "./app-page-route-wiring.js";
25
24
  import { resolveSlotParamOverrides } from "./app-page-element-builder.js";
26
25
  import { createAppPageFontData, createAppPageRscErrorTracker, createAppPageSsrErrorHandler, renderAppPageHtmlResponse } from "./app-page-stream.js";
27
26
  import { Fragment, Suspense, createElement } from "react";
@@ -258,7 +257,7 @@ async function renderAppPageHttpAccessFallback(options) {
258
257
  if (!boundaryComponent) return null;
259
258
  const layoutModules = options.layoutModules ?? options.route?.layouts ?? options.rootLayouts;
260
259
  const pathname = new URL(options.requestUrl).pathname;
261
- const routePathname = options.routePathname ?? stripRscSuffix(stripBasePath(pathname, options.basePath ?? ""));
260
+ const routePathname = options.routePathname ?? stripBasePath(pathname, options.basePath ?? "");
262
261
  const headBoundaryModule = options.headBoundaryModule ?? boundaryModule;
263
262
  const headLayoutModules = options.headLayoutModules ?? layoutModules;
264
263
  const routeSegments = resolveHttpAccessFallbackHeadRouteSegments(options.route, headLayoutModules);
@@ -301,6 +300,7 @@ async function renderAppPageHttpAccessFallback(options) {
301
300
  head: {
302
301
  layoutModules: intercept?.interceptLayouts ?? [],
303
302
  layoutParams: (intercept?.interceptLayoutSegments ?? []).map((layoutSegments) => resolveAppPageBranchParams(intercept?.interceptBranchSegments ?? layoutSegments, layoutSegments.length, effectiveParams, layoutSegments)),
303
+ layoutsBesidePage: resolveInterceptLayoutsBesidePage(intercept?.interceptLayoutSegments, intercept?.interceptBranchSegments),
304
304
  pageModule: intercept?.interceptPage ?? null,
305
305
  params: effectiveParams,
306
306
  routeSegments: intercept?.interceptSourcePageSegments ?? segments
@@ -611,6 +611,7 @@ async function renderAppPageErrorBoundary(options) {
611
611
  skipLayoutWrapping: errorBoundary.isGlobalError
612
612
  });
613
613
  };
614
+ const isErrorStatus = errorBoundary.isGlobalError || !options.isRscRequest;
614
615
  const renderWith = async (BoundaryComponent) => {
615
616
  const response = await renderAppPageBoundaryElementResponse({
616
617
  ...options,
@@ -620,9 +621,9 @@ async function renderAppPageErrorBoundary(options) {
620
621
  navigationParams: matchedParams,
621
622
  route: options.route,
622
623
  routePattern: options.route?.pattern,
623
- status: errorBoundary.isGlobalError ? 500 : 200
624
+ status: isErrorStatus ? 500 : 200
624
625
  });
625
- if (errorBoundary.isGlobalError) applyCdnResponseHeaders(response.headers, { cacheControl: NEVER_CACHE_CONTROL });
626
+ if (isErrorStatus) applyCdnResponseHeaders(response.headers, { cacheControl: NEVER_CACHE_CONTROL });
626
627
  return response;
627
628
  };
628
629
  try {
@@ -635,10 +636,5 @@ async function renderAppPageErrorBoundary(options) {
635
636
  throw renderError;
636
637
  }
637
638
  }
638
- const _clientHookPattern = /\b(useState|useEffect|useReducer|useRef|useContext|useLayoutEffect|useInsertionEffect|useSyncExternalStore|useTransition|useImperativeHandle|useDeferredValue|useActionState|useOptimistic|useEffectEvent)\b.*is not a function/;
639
- function rewriteClientHookError(error) {
640
- const match = error.message.match(_clientHookPattern);
641
- if (match) error.message = buildClientHookErrorMessage(`${match[1]}()`);
642
- }
643
639
  //#endregion
644
640
  export { renderAppPageErrorBoundary, renderAppPageHttpAccessFallback };
@@ -1,10 +1,10 @@
1
1
  import { VINEXT_RSC_VARY_HEADER } from "./app-rsc-vary.js";
2
2
  import { runWithFetchDedupe } from "../shims/fetch-cache.js";
3
- import { VINEXT_RSC_CONTENT_TYPE, applyRscCompatibilityIdHeader, applyRscDeploymentIdHeader } from "./app-rsc-cache-busting.js";
4
3
  import { mergeMiddlewareResponseHeaders } from "./middleware-response-headers.js";
4
+ import { VINEXT_RSC_CONTENT_TYPE, applyRscCompatibilityIdHeader, applyRscDeploymentIdHeader } from "./app-rsc-cache-busting.js";
5
+ import { isAppPageRouteGroupSegment } from "./app-page-segment-state.js";
5
6
  import { applyEdgeRuntimeHeader } from "./app-page-response.js";
6
7
  import { resolveAppPageSegmentParams } from "./app-page-params.js";
7
- import { isAppPageRouteGroupSegment } from "./app-page-segment-state.js";
8
8
  //#region src/server/app-page-boundary.ts
9
9
  function resolveAppPageHttpAccessBoundaryComponent(options) {
10
10
  return options.getDefaultExport(resolveAppPageHttpAccessBoundaryModule({
@@ -2,6 +2,7 @@ import { RenderObservation } from "./cache-proof.js";
2
2
  import { AppRscRenderMode } from "./app-rsc-render-mode.js";
3
3
  import { AppPageRenderObservationState } from "./app-page-render-observation.js";
4
4
  import { AppPageCacheSetter } from "./isr-cache.js";
5
+ import { CdnAppPageDocument } from "../shims/cdn-cache.js";
5
6
  import { AppPageRscRenderStatus } from "./app-page-rsc-render-status.js";
6
7
  //#region src/server/app-page-cache-finalizer.d.ts
7
8
  type AppPageDebugLogger = (event: string, detail: string) => void;
@@ -30,6 +31,8 @@ type FinalizeAppPageCacheabilityEvaluationOptions = {
30
31
  /** An RSC render's status, from its document's shell. */
31
32
  resolveRscRenderStatus?: () => Promise<AppPageRscRenderStatus>;
32
33
  revalidateSeconds: number | null;
34
+ /** Hands the CDN adapter the page's document to store beside the response. */
35
+ captureAdmittedDocument?: (document: CdnAppPageDocument | null) => void;
33
36
  };
34
37
  type FinalizeAppPageHtmlCacheResponseOptions = {
35
38
  bypassInterceptionContextCache?: boolean;
@@ -66,15 +69,20 @@ type FinalizeAppPageHtmlCacheResponseOptions = {
66
69
  };
67
70
  type ScheduleAppPageRscCacheWriteOptions = {
68
71
  bypassInterceptionContextCache?: boolean;
72
+ /** Hands the CDN adapter the page's document to store beside the response. */
73
+ captureAdmittedDocument?: (document: CdnAppPageDocument | null) => void;
69
74
  capturedRscDataPromise: Promise<ArrayBuffer> | null;
70
75
  cleanPathname: string;
71
76
  consumeDynamicUsage: () => boolean;
72
77
  consumeRenderObservationState?: () => AppPageRenderObservationState;
78
+ /** Observes the document the render's status resolution rendered, if any. */
79
+ createHtmlRenderObservation?: BuildAppPageCacheRenderObservation;
73
80
  createRscRenderObservation: BuildAppPageCacheRenderObservation;
74
81
  dynamicUsedDuringBuild: boolean;
75
82
  getPageTags: () => string[];
76
83
  getRequestCacheLife?: () => AppPageRequestCacheLife | null;
77
84
  isrDebug?: AppPageDebugLogger;
85
+ isrHtmlKey?: (pathname: string) => string;
78
86
  isrRscKey: AppPageRscCacheKeyBuilder;
79
87
  isrSet: AppPageCacheSetter;
80
88
  interceptionContext?: string | null;
@@ -82,7 +90,10 @@ type ScheduleAppPageRscCacheWriteOptions = {
82
90
  mountedSlotsHeader?: string | null;
83
91
  omitPendingDynamicCacheState?: boolean;
84
92
  renderMode?: AppRscRenderMode;
85
- /** The render's status, from its document's shell. Without it the render is a 200. */
93
+ /**
94
+ * The render's status, from its document's shell, and the page's document
95
+ * when it rendered one to store. Without it the render is a 200.
96
+ */
86
97
  resolveRscRenderStatus?: () => Promise<AppPageRscRenderStatus>;
87
98
  preserveClientResponseHeaders?: boolean;
88
99
  expireSeconds?: number;