vinext 1.4.1 → 1.4.3

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 (143) hide show
  1. package/README.md +5 -2
  2. package/dist/build/action-owner-manifest.js +1 -0
  3. package/dist/build/prerender-paths.d.ts +11 -1
  4. package/dist/build/prerender-paths.js +36 -10
  5. package/dist/check.js +1 -1
  6. package/dist/cli.js +1 -1
  7. package/dist/client/dev-error-overlay.d.ts +2 -1
  8. package/dist/client/dev-error-overlay.js +8 -3
  9. package/dist/config/config-matchers.js +8 -4
  10. package/dist/config/destination-params.js +5 -2
  11. package/dist/config/next-config.d.ts +25 -1
  12. package/dist/config/next-config.js +31 -1
  13. package/dist/entries/app-rsc-entry.js +65 -30
  14. package/dist/entries/app-rsc-manifest.js +4 -0
  15. package/dist/entries/pages-server-entry.js +19 -4
  16. package/dist/index.js +37 -37
  17. package/dist/init-cloudflare.js +18 -2
  18. package/dist/init-platform.js +2 -2
  19. package/dist/init.js +42 -5
  20. package/dist/plugins/css-target.d.ts +18 -0
  21. package/dist/plugins/css-target.js +93 -0
  22. package/dist/routing/app-route-graph.d.ts +10 -0
  23. package/dist/routing/app-route-graph.js +15 -1
  24. package/dist/server/api-handler.d.ts +2 -0
  25. package/dist/server/api-handler.js +10 -8
  26. package/dist/server/app-action-forwarding.js +3 -1
  27. package/dist/server/app-browser-entry.js +37 -41
  28. package/dist/server/app-browser-error.d.ts +1 -1
  29. package/dist/server/app-browser-error.js +13 -4
  30. package/dist/server/app-browser-supplemental-refresh.d.ts +13 -0
  31. package/dist/server/app-browser-supplemental-refresh.js +36 -4
  32. package/dist/server/app-elements.d.ts +3 -0
  33. package/dist/server/app-elements.js +14 -1
  34. package/dist/server/app-fallback-renderer.js +2 -2
  35. package/dist/server/app-middleware.d.ts +2 -0
  36. package/dist/server/app-middleware.js +1 -0
  37. package/dist/server/app-optimistic-routing.d.ts +12 -1
  38. package/dist/server/app-optimistic-routing.js +38 -16
  39. package/dist/server/app-page-boundary-render.js +4 -2
  40. package/dist/server/app-page-cache-render.js +1 -1
  41. package/dist/server/app-page-dispatch.d.ts +11 -1
  42. package/dist/server/app-page-dispatch.js +78 -31
  43. package/dist/server/app-page-element-builder.d.ts +11 -20
  44. package/dist/server/app-page-element-builder.js +39 -50
  45. package/dist/server/app-page-intercept-segment-config.d.ts +56 -0
  46. package/dist/server/app-page-intercept-segment-config.js +90 -0
  47. package/dist/server/app-page-navigation-params.d.ts +23 -0
  48. package/dist/server/app-page-navigation-params.js +49 -0
  49. package/dist/server/app-page-render.d.ts +5 -2
  50. package/dist/server/app-page-render.js +17 -3
  51. package/dist/server/app-page-request.d.ts +24 -2
  52. package/dist/server/app-page-request.js +10 -10
  53. package/dist/server/app-page-route-wiring.d.ts +17 -5
  54. package/dist/server/app-page-route-wiring.js +19 -13
  55. package/dist/server/app-page-segment-plan.js +9 -1
  56. package/dist/server/app-page-segment-state.d.ts +9 -0
  57. package/dist/server/app-page-segment-state.js +54 -1
  58. package/dist/server/app-pages-bridge.d.ts +6 -0
  59. package/dist/server/app-pages-bridge.js +8 -3
  60. package/dist/server/app-request-stage-independent-entry.js +2 -3
  61. package/dist/server/app-response-stage-entry.js +1 -0
  62. package/dist/server/app-route-module-loader.d.ts +2 -0
  63. package/dist/server/app-route-module-loader.js +1 -0
  64. package/dist/server/app-router-entry.js +2 -3
  65. package/dist/server/app-rsc-cache-busting.d.ts +8 -0
  66. package/dist/server/app-rsc-cache-busting.js +13 -1
  67. package/dist/server/app-rsc-combined-handler.js +2 -1
  68. package/dist/server/app-rsc-handler.d.ts +14 -1
  69. package/dist/server/app-rsc-handler.js +111 -62
  70. package/dist/server/app-rsc-response-finalizer.d.ts +18 -3
  71. package/dist/server/app-rsc-response-finalizer.js +61 -7
  72. package/dist/server/app-rsc-route-matching.d.ts +14 -0
  73. package/dist/server/app-rsc-route-matching.js +42 -18
  74. package/dist/server/app-server-action-execution.js +16 -10
  75. package/dist/server/app-ssr-entry.js +8 -0
  76. package/dist/server/cache-control.d.ts +2 -0
  77. package/dist/server/cache-control.js +3 -1
  78. package/dist/server/config-headers.d.ts +7 -0
  79. package/dist/server/config-headers.js +20 -1
  80. package/dist/server/image-animation.d.ts +9 -0
  81. package/dist/server/image-animation.js +118 -0
  82. package/dist/server/image-local-patterns.d.ts +21 -0
  83. package/dist/server/image-local-patterns.js +59 -0
  84. package/dist/server/image-optimization.d.ts +48 -27
  85. package/dist/server/image-optimization.js +316 -22
  86. package/dist/server/image-source-reader.d.ts +34 -0
  87. package/dist/server/image-source-reader.js +126 -0
  88. package/dist/server/metadata-route-response.d.ts +6 -1
  89. package/dist/server/metadata-route-response.js +46 -10
  90. package/dist/server/middleware-matcher.d.ts +1 -0
  91. package/dist/server/middleware-matcher.js +1 -1
  92. package/dist/server/middleware-runtime.d.ts +5 -0
  93. package/dist/server/middleware-runtime.js +48 -15
  94. package/dist/server/middleware.d.ts +1 -1
  95. package/dist/server/middleware.js +2 -1
  96. package/dist/server/nitro-public-files-plugin.js +20 -8
  97. package/dist/server/nitro-request-target-plugin.d.ts +17 -0
  98. package/dist/server/nitro-request-target-plugin.js +22 -0
  99. package/dist/server/open-redirect.d.ts +2 -1
  100. package/dist/server/open-redirect.js +2 -1
  101. package/dist/server/pages-client-assets.d.ts +1 -0
  102. package/dist/server/pages-data-route.d.ts +5 -1
  103. package/dist/server/pages-data-route.js +14 -6
  104. package/dist/server/pages-i18n.d.ts +7 -0
  105. package/dist/server/pages-i18n.js +14 -1
  106. package/dist/server/pages-node-compat.js +7 -3
  107. package/dist/server/pages-page-handler.js +4 -4
  108. package/dist/server/pages-request-pipeline.d.ts +9 -0
  109. package/dist/server/pages-request-pipeline.js +35 -15
  110. package/dist/server/pages-request-stage-entry.js +8 -2
  111. package/dist/server/pages-router-entry.js +3 -0
  112. package/dist/server/prod-server.d.ts +1 -1
  113. package/dist/server/prod-server.js +64 -62
  114. package/dist/server/request-pipeline.d.ts +5 -0
  115. package/dist/server/request-pipeline.js +11 -1
  116. package/dist/server/response-stage-cacheability.d.ts +7 -0
  117. package/dist/server/response-stage-cacheability.js +6 -5
  118. package/dist/server/srvx-middleware-origin.d.ts +13 -0
  119. package/dist/server/srvx-middleware-origin.js +19 -0
  120. package/dist/server/srvx-request-target.d.ts +12 -0
  121. package/dist/server/srvx-request-target.js +20 -0
  122. package/dist/server/static-file-cache.js +10 -22
  123. package/dist/server/worker-image-optimization.d.ts +10 -3
  124. package/dist/server/worker-image-optimization.js +43 -9
  125. package/dist/server/worker-revalidation-context.js +1 -0
  126. package/dist/server/worker-utils.d.ts +8 -0
  127. package/dist/server/worker-utils.js +19 -2
  128. package/dist/shims/error-boundary.d.ts +6 -2
  129. package/dist/shims/error-boundary.js +4 -2
  130. package/dist/shims/image-props.d.ts +6 -0
  131. package/dist/shims/image-props.js +39 -3
  132. package/dist/shims/image.js +3 -3
  133. package/dist/shims/request-context.d.ts +3 -1
  134. package/dist/shims/slot.d.ts +3 -0
  135. package/dist/shims/slot.js +67 -9
  136. package/dist/shims/url-utils.js +6 -1
  137. package/dist/utils/base-path.d.ts +7 -0
  138. package/dist/utils/base-path.js +11 -1
  139. package/dist/utils/client-entry-manifest.d.ts +5 -0
  140. package/dist/utils/client-entry-manifest.js +1 -0
  141. package/dist/utils/client-runtime-metadata.d.ts +5 -0
  142. package/dist/utils/client-runtime-metadata.js +33 -7
  143. package/package.json +1 -1
@@ -0,0 +1,90 @@
1
+ import { collectAppPageStaticGenerationRuntimes, collectAppPageStaticParamsWalkSegments, hasAppPageGenerateStaticParamsAtLastDynamicSegment, isAppPageStaticEligible, isEdgeRuntime, resolveAppPageSegmentConfig, resolveAppPageStaticGenerationRuntime } from "./app-segment-config.js";
2
+ import { SIBLING_PAGE_INTERCEPT_SLOT_KEY } from "./app-rsc-route-matching.js";
3
+ //#region src/server/app-page-intercept-segment-config.ts
4
+ /**
5
+ * Next.js renders an interception as its own route, whose loader tree holds
6
+ * the owner's layouts and the intercepting page, never the source's page, so
7
+ * its segment config comes from those modules alone. A sibling intercept's
8
+ * branch takes the children position below its owner folder; a slot
9
+ * intercept's branch takes its slot, beside the other slots of the owner's
10
+ * layouts, and children renders the owner's default.
11
+ */
12
+ function resolveAppPageInterceptSegmentConfig(route, interceptOpts) {
13
+ const routeLayouts = route.layouts ?? [];
14
+ const routeSegments = route.routeSegments ?? [];
15
+ const isSiblingIntercept = interceptOpts.interceptSlotKey === SIBLING_PAGE_INTERCEPT_SLOT_KEY;
16
+ const interceptedSlot = isSiblingIntercept ? void 0 : route.slots?.[interceptOpts.interceptSlotKey];
17
+ const branchSegments = interceptOpts.interceptBranchSegments ?? [];
18
+ const interceptLayouts = interceptOpts.interceptLayouts ?? [];
19
+ const interceptLayoutPositions = (interceptOpts.interceptLayoutSegments ?? []).map((segments) => segments.length);
20
+ const ownerPosition = isSiblingIntercept ? interceptOpts.interceptSourcePageSegments ? interceptOpts.interceptSourcePageSegments.length - branchSegments.length : routeSegments.length : interceptedSlot?.ownerTreePosition ?? route.layoutTreePositions?.[interceptedSlot?.layoutIndex ?? -1] ?? routeSegments.length;
21
+ const ownerLayoutIndexes = routeLayouts.flatMap((_, index) => (route.layoutTreePositions?.[index] ?? 0) <= ownerPosition ? [index] : []);
22
+ const ownerLayouts = ownerLayoutIndexes.map((index) => routeLayouts[index]);
23
+ const ownerLayoutPositions = ownerLayoutIndexes.map((index) => route.layoutTreePositions?.[index] ?? 0);
24
+ const ownerSegments = routeSegments.slice(0, ownerPosition);
25
+ const page = interceptOpts.interceptPage;
26
+ const retainedBranches = Object.entries(route.slots ?? {}).flatMap(([slotKey, slot]) => slotKey !== interceptOpts.interceptSlotKey && (slot.ownerTreePosition ?? route.layoutTreePositions?.[slot.layoutIndex ?? 0] ?? 0) <= ownerPosition ? [{
27
+ configLayouts: slot.page ? slot.configLayouts : null,
28
+ configLayoutTreePositions: slot.page ? slot.configLayoutTreePositions : null,
29
+ isDefault: !slot.page,
30
+ layout: slot.page ? slot.layout : null,
31
+ name: slot.name,
32
+ ownerTreePosition: slot.ownerTreePosition,
33
+ page: slot.page ?? slot.default,
34
+ routeSegments: slot.routeSegments
35
+ }] : []);
36
+ const tree = isSiblingIntercept ? {
37
+ layouts: [...ownerLayouts, ...interceptLayouts],
38
+ layoutTreePositions: [...ownerLayoutPositions, ...interceptLayoutPositions.map((position) => ownerPosition + position)],
39
+ page,
40
+ parallelBranches: retainedBranches,
41
+ routeSegments: [...ownerSegments, ...branchSegments]
42
+ } : {
43
+ layouts: ownerLayouts,
44
+ layoutTreePositions: ownerLayoutPositions,
45
+ page: null,
46
+ parallelBranches: [...retainedBranches, {
47
+ configLayouts: interceptLayouts,
48
+ configLayoutTreePositions: interceptLayoutPositions,
49
+ layout: interceptedSlot?.layout,
50
+ name: interceptedSlot?.name,
51
+ ownerTreePosition: ownerPosition,
52
+ page,
53
+ routeSegments: branchSegments
54
+ }],
55
+ routeSegments: ownerSegments
56
+ };
57
+ const config = resolveAppPageSegmentConfig({
58
+ layouts: [
59
+ ...ownerLayouts,
60
+ ...isSiblingIntercept ? [] : [interceptedSlot?.layout],
61
+ ...interceptLayouts
62
+ ],
63
+ page,
64
+ parallelBranches: retainedBranches,
65
+ parallelPages: retainedBranches.map((branch) => branch.page)
66
+ });
67
+ const walkSegments = collectAppPageStaticParamsWalkSegments(tree);
68
+ const hasGenerateStaticParams = hasAppPageGenerateStaticParamsAtLastDynamicSegment(tree);
69
+ const isDynamicRoute = walkSegments.some((segment) => segment.dynamic);
70
+ const isStaticGenerationEdgeRuntime = isEdgeRuntime(resolveAppPageStaticGenerationRuntime(collectAppPageStaticGenerationRuntimes(tree)));
71
+ return {
72
+ dynamicConfig: config.dynamicConfig,
73
+ dynamicStaleTimeSeconds: config.dynamicStaleTimeSeconds,
74
+ fetchCacheMode: config.fetchCache ?? null,
75
+ hasAnyGenerateStaticParams: walkSegments.some((segment) => segment.generateStaticParams),
76
+ hasGenerateStaticParams,
77
+ isEdgeRuntime: isEdgeRuntime(config.runtime),
78
+ isStaticEligible: isAppPageStaticEligible({
79
+ dynamicConfig: config.dynamicConfig,
80
+ hasGenerateStaticParams,
81
+ isDynamicRoute,
82
+ isStaticGenerationEdgeRuntime,
83
+ revalidateSeconds: config.revalidateSeconds
84
+ }),
85
+ isStaticGenerationEdgeRuntime,
86
+ revalidateSeconds: config.revalidateSeconds
87
+ };
88
+ }
89
+ //#endregion
90
+ export { resolveAppPageInterceptSegmentConfig };
@@ -0,0 +1,23 @@
1
+ import { AppPageParams } from "./app-page-boundary.js";
2
+ //#region src/server/app-page-navigation-params.d.ts
3
+ export type AppPageNavigationParamSlot = {
4
+ __loadDefault?: unknown;
5
+ __loadPage?: unknown;
6
+ default?: unknown;
7
+ page?: unknown;
8
+ slotPatternParts?: readonly string[] | null;
9
+ slotParamNames?: readonly string[] | null;
10
+ };
11
+ type AppPageNavigationParamRoute = {
12
+ params?: readonly string[] | null;
13
+ slots?: Readonly<Record<string, AppPageNavigationParamSlot>> | null;
14
+ };
15
+ type AppPageNavigationParamInterceptOptions = {
16
+ interceptPage?: unknown;
17
+ interceptParams?: AppPageParams | null;
18
+ interceptSlotKey?: string | null;
19
+ };
20
+ export declare function resolveSlotParamOverrides(route: AppPageNavigationParamRoute, routePath: string): Readonly<Record<string, AppPageParams>> | null;
21
+ export declare function resolveAppPageNavigationParams(route: AppPageNavigationParamRoute, routeParams: AppPageParams, routePath: string, opts?: AppPageNavigationParamInterceptOptions | null): AppPageParams;
22
+ //#endregion
23
+ export {};
@@ -0,0 +1,49 @@
1
+ import { matchRoutePattern } from "../routing/route-pattern.js";
2
+ import "./app-rsc-route-matching.js";
3
+ //#region src/server/app-page-navigation-params.ts
4
+ function resolveSlotParamOverrides(route, routePath) {
5
+ const overrides = {};
6
+ const slots = route.slots;
7
+ if (slots) {
8
+ let urlParts = null;
9
+ const routeParamSet = collectParamNameSet(route.params);
10
+ for (const [slotKey, slot] of Object.entries(slots)) {
11
+ const patternParts = slot.slotPatternParts;
12
+ const paramNames = slot.slotParamNames;
13
+ if (!patternParts || patternParts.length === 0) continue;
14
+ if (paramNames && paramNames.every((name) => routeParamSet.has(name))) continue;
15
+ if (urlParts === null) urlParts = routePath.split("/").filter(Boolean);
16
+ const matched = matchRoutePattern(urlParts, patternParts);
17
+ if (!matched) continue;
18
+ overrides[slotKey] = matched;
19
+ }
20
+ }
21
+ return Object.keys(overrides).length > 0 ? overrides : null;
22
+ }
23
+ function mergeAppPageParams(target, source) {
24
+ for (const [key, value] of Object.entries(source)) target[key] = value;
25
+ }
26
+ function isDefaultExportModule(module) {
27
+ return typeof module === "object" && module !== null;
28
+ }
29
+ function hasDefaultExport(module) {
30
+ if (!isDefaultExportModule(module)) return false;
31
+ return module?.default !== null && module?.default !== void 0;
32
+ }
33
+ function resolveAppPageNavigationParams(route, routeParams, routePath, opts) {
34
+ const navigationParams = { ...routeParams };
35
+ const slotParamOverrides = resolveSlotParamOverrides(route, routePath);
36
+ for (const [slotKey, slot] of Object.entries(route.slots ?? {})) {
37
+ const isInterceptedSlot = opts?.interceptSlotKey === slotKey && opts.interceptSlotKey !== "__vinext_page_intercept" && hasDefaultExport(opts.interceptPage);
38
+ if (!isInterceptedSlot && !hasDefaultExport(slot.page) && !hasDefaultExport(slot.default) && !slot.__loadPage && !slot.__loadDefault) continue;
39
+ mergeAppPageParams(navigationParams, isInterceptedSlot ? opts?.interceptParams ?? routeParams : slotParamOverrides?.[slotKey] ?? routeParams);
40
+ }
41
+ return navigationParams;
42
+ }
43
+ function collectParamNameSet(params) {
44
+ const set = /* @__PURE__ */ new Set();
45
+ if (params) for (const name of params) set.add(name);
46
+ return set;
47
+ }
48
+ //#endregion
49
+ export { resolveAppPageNavigationParams, resolveSlotParamOverrides };
@@ -150,9 +150,12 @@ type RenderAppPageLifecycleOptions = RenderAppPageLifecycleOptionsBase & ({
150
150
  * static, draft mode, `force-dynamic`, `revalidate = 0`, or a dynamic API read
151
151
  * before the response left the render. Responses that leave the render before
152
152
  * its response policy, such as error boundaries and special errors, get the
153
- * same header as the normal render.
153
+ * same header as the normal render. One not yet known dynamic is never stored
154
+ * either, and its body can still reach a dynamic API after its headers leave,
155
+ * so it is sent like a dynamic render (see resolveUnstoredEarlyResponseCacheControl
156
+ * for the responses that aren't).
154
157
  */
155
- export declare function applyIneligibleRouteCachePolicy(response: Response, options: Pick<RenderAppPageLifecycleOptions, "dynamicStaleTimeSeconds" | "isDraftMode" | "isDynamicError" | "isForceDynamic" | "isForceStatic" | "isProduction" | "isProgressiveActionRender" | "isRscRequest" | "isStaticEligible" | "middlewareContext" | "peekDynamicUsage" | "revalidateSeconds" | "scriptNonce">, requestCacheLife?: AppPageRequestCacheLife | null): Response;
158
+ export declare function applyIneligibleRouteCachePolicy(response: Response, options: Pick<RenderAppPageLifecycleOptions, "dynamicStaleTimeSeconds" | "isDraftMode" | "isDynamicError" | "isForceDynamic" | "isForceStatic" | "isPrerender" | "isProduction" | "isProgressiveActionRender" | "isRscRequest" | "isStaticEligible" | "middlewareContext" | "peekDynamicUsage" | "revalidateSeconds" | "scriptNonce">, requestCacheLife?: AppPageRequestCacheLife | null): Response;
156
159
  export declare function renderAppPageLifecycle(options: RenderAppPageLifecycleOptions): Promise<Response>;
157
160
  //#endregion
158
161
  export {};
@@ -24,10 +24,10 @@ import { appendRscCompletionMetadata } from "./rsc-completion-metadata.js";
24
24
  import { isBailoutToCSRError } from "../shims/navigation-errors.js";
25
25
  import { applyClientStaleTimeHeader, applyEdgeRuntimeHeader, buildAppPageHtmlResponse, buildAppPageRscResponse, resolveAppPageHtmlResponsePolicy, resolveAppPageRscResponsePolicy, resolveUncacheableCacheControl } from "./app-page-response.js";
26
26
  import { buildAppPageFontLinkHeader, readAppPageBinaryStream, resolveAppPageCaughtNotFoundBoundaries, resolveAppPageShellSpecialError, resolveAppPageSpecialError, resolveAppPageSpecialErrorStoredHeaders, teeAppPageRscStreamForCapture } from "./app-page-execution.js";
27
+ import { buildAppPageHtmlDocumentResponse, buildAppPageLinkHeader, createAppPageFontData, createAppPageRscErrorTracker, createAppPageSsrErrorHandler, renderAppPageHtmlStream, renderAppPageHtmlStreamWithRecovery } from "./app-page-stream.js";
27
28
  import { getAppPageLayoutGate } from "./app-page-layout-gate.js";
28
29
  import { hasDigest } from "./app-rsc-errors.js";
29
30
  import { recordAppPageRenderError, traceAppPageRender } from "./app-page-tracing.js";
30
- import { buildAppPageHtmlDocumentResponse, buildAppPageLinkHeader, createAppPageFontData, createAppPageRscErrorTracker, createAppPageSsrErrorHandler, renderAppPageHtmlStream, renderAppPageHtmlStreamWithRecovery } from "./app-page-stream.js";
31
31
  import { createAppPageHtmlOutputScope, createAppPageRenderObservation, createAppPageRscOutputScope } from "./app-page-render-observation.js";
32
32
  import { createAppPageRscRenderStatusResolver } from "./app-page-rsc-render-status.js";
33
33
  import { finalizeAppPageCacheabilityEvaluationResponse, finalizeAppPageHtmlCacheResponse, finalizeAppPageRscCacheResponse } from "./app-page-cache-finalizer.js";
@@ -70,11 +70,14 @@ function applyRequestCacheLife(options) {
70
70
  * static, draft mode, `force-dynamic`, `revalidate = 0`, or a dynamic API read
71
71
  * before the response left the render. Responses that leave the render before
72
72
  * its response policy, such as error boundaries and special errors, get the
73
- * same header as the normal render.
73
+ * same header as the normal render. One not yet known dynamic is never stored
74
+ * either, and its body can still reach a dynamic API after its headers leave,
75
+ * so it is sent like a dynamic render (see resolveUnstoredEarlyResponseCacheControl
76
+ * for the responses that aren't).
74
77
  */
75
78
  function applyIneligibleRouteCachePolicy(response, options, requestCacheLife = null) {
76
- const cacheControl = resolveEarlyResponseCacheControl(options);
77
79
  const isRscRedirect = options.isRscRequest && response.headers.has("X-Vinext-Rsc-Redirect");
80
+ const cacheControl = resolveEarlyResponseCacheControl(options) ?? (isRscRedirect ? null : resolveUnstoredEarlyResponseCacheControl(response, options));
78
81
  if (!cacheControl) {
79
82
  const staleTimeSeconds = isRscRedirect ? resolveClientStaleTimeSeconds(requestCacheLife) : void 0;
80
83
  if (staleTimeSeconds === void 0) return response;
@@ -108,6 +111,17 @@ function resolveEarlyResponseCacheControl(options) {
108
111
  const ignoresDynamicUsage = (options.isForceStatic || options.isDynamicError) && (options.revalidateSeconds === null || options.revalidateSeconds === Infinity);
109
112
  return options.revalidateSeconds === 0 || !ignoresDynamicUsage && (options.peekDynamicUsage?.() ?? peekDynamicUsage()) ? uncacheable : null;
110
113
  }
114
+ /**
115
+ * The normal render can still become a cache entry, so it sends its pending
116
+ * policy until the stream proves it static. An early response never does, and
117
+ * nothing checks it again, so one with a body takes the never-cache header
118
+ * unless it already carries a policy, such as middleware's. A force-static or
119
+ * dynamic = "error" render can't read the request, and dev and the build's
120
+ * prerender, which reads `no-store` as a dynamic render, keep no policy.
121
+ */
122
+ function resolveUnstoredEarlyResponseCacheControl(response, options) {
123
+ return options.isProduction && options.isPrerender !== true && !options.isForceStatic && !options.isDynamicError && response.body !== null && !response.headers.has("cache-control") ? NEVER_CACHE_CONTROL : null;
124
+ }
111
125
  function resolveAppPageCacheWriteRevalidateSeconds(options) {
112
126
  if (!options.isStaticEligible) return null;
113
127
  if (options.revalidateSeconds === null && (options.isForceStatic || options.isDynamicError)) return Infinity;
@@ -100,6 +100,25 @@ type AppPageInterceptMatch<TPage = unknown> = {
100
100
  targetPatternParts?: readonly string[];
101
101
  targetRouteGraphId?: string | null;
102
102
  };
103
+ export type AppPageInterceptState<TRoute, TPage> = {
104
+ kind: "none";
105
+ } | {
106
+ kind: "current-route";
107
+ intercept: AppPageInterceptMatch<TPage>;
108
+ } | {
109
+ kind: "source-route";
110
+ intercept: AppPageInterceptMatch<TPage>;
111
+ sourceRoute: TRoute;
112
+ };
113
+ type AppPageInterceptStateLookup<TRoute, TPage> = {
114
+ cleanPathname: string;
115
+ currentRoute: TRoute;
116
+ findIntercept: (pathname: string) => AppPageInterceptMatch<TPage> | null;
117
+ getSourceRoute: (sourceRouteIndex: number) => Awaitable<TRoute | undefined>;
118
+ /** The state this request already resolved, which is then reused. */
119
+ interceptState?: AppPageInterceptState<TRoute, TPage>;
120
+ isRscRequest: boolean;
121
+ };
103
122
  type ResolveAppPageInterceptionRerenderTargetOptions<TRoute, TPage, TInterceptOpts> = {
104
123
  cleanPathname: string;
105
124
  currentParams: AppPageParams;
@@ -107,6 +126,7 @@ type ResolveAppPageInterceptionRerenderTargetOptions<TRoute, TPage, TInterceptOp
107
126
  findIntercept: (pathname: string) => AppPageInterceptMatch<TPage> | null;
108
127
  getRouteParamNames: (route: TRoute) => readonly string[];
109
128
  getSourceRoute: (sourceRouteIndex: number) => Awaitable<TRoute | undefined>;
129
+ interceptState?: AppPageInterceptState<TRoute, TPage>;
110
130
  isRscRequest: boolean;
111
131
  toInterceptOpts: (intercept: AppPageInterceptMatch<TPage>) => TInterceptOpts;
112
132
  };
@@ -128,10 +148,11 @@ type ResolveAppPageInterceptOptions<TRoute, TPage, TInterceptOpts, TElement> = {
128
148
  findIntercept: (pathname: string) => AppPageInterceptMatch<TPage> | null;
129
149
  getRouteParamNames: (route: TRoute) => readonly string[];
130
150
  getSourceRoute: (sourceRouteIndex: number) => Awaitable<TRoute | undefined>;
151
+ interceptState?: AppPageInterceptState<TRoute, TPage>;
131
152
  isRscRequest: boolean;
132
153
  resolveNavigationParams: (route: TRoute, params: AppPageParams, pathname: string, interceptOpts: TInterceptOpts) => AppPageParams;
133
- renderInterceptResponse: (route: TRoute, element: TElement) => Promise<Response> | Response;
134
- resolveSearchParams?: (route: TRoute, searchParams: URLSearchParams) => Awaitable<URLSearchParams>;
154
+ renderInterceptResponse: (route: TRoute, element: TElement, navigationParams: AppPageParams) => Promise<Response> | Response;
155
+ resolveSearchParams?: (route: TRoute, searchParams: URLSearchParams, interceptOpts: TInterceptOpts) => Awaitable<URLSearchParams>;
135
156
  searchParams: URLSearchParams;
136
157
  setNavigationContext: (context: {
137
158
  params: AppPageParams;
@@ -146,6 +167,7 @@ type ResolveAppPageInterceptResult<TInterceptOpts> = {
146
167
  };
147
168
  export declare function resolveAppPageGenerateStaticParamsSources(options: ResolveAppPageGenerateStaticParamsSourcesOptions): GenerateStaticParamsSource[];
148
169
  export declare function validateAppPageDynamicParams(options: ValidateAppPageDynamicParamsOptions): Promise<Response | null>;
170
+ export declare function resolveAppPageInterceptState<TRoute, TPage>(options: AppPageInterceptStateLookup<TRoute, TPage>): Promise<AppPageInterceptState<TRoute, TPage>>;
149
171
  export declare function resolveAppPageInterceptionRerenderTarget<TRoute, TPage, TInterceptOpts>(options: ResolveAppPageInterceptionRerenderTargetOptions<TRoute, TPage, TInterceptOpts>): Promise<ResolveAppPageInterceptionRerenderTargetResult<TRoute, TInterceptOpts>>;
150
172
  export declare function resolveAppPageActionRerenderTarget<TRoute, TPage, TInterceptOpts>(options: ResolveAppPageActionRerenderTargetOptions<TRoute, TPage, TInterceptOpts>): Promise<ResolveAppPageActionRerenderTargetResult<TRoute, TInterceptOpts>>;
151
173
  export declare function resolveAppPageIntercept<TRoute, TPage, TInterceptOpts, TElement>(options: ResolveAppPageInterceptOptions<TRoute, TPage, TInterceptOpts, TElement>): Promise<ResolveAppPageInterceptResult<TInterceptOpts>>;
@@ -278,6 +278,7 @@ async function validateAppPageDynamicParams(options) {
278
278
  return null;
279
279
  }
280
280
  async function resolveAppPageInterceptState(options) {
281
+ if (options.interceptState) return options.interceptState;
281
282
  if (!options.isRscRequest) return { kind: "none" };
282
283
  const intercept = options.findIntercept(options.cleanPathname);
283
284
  if (!intercept) return { kind: "none" };
@@ -301,10 +302,9 @@ async function resolveAppPageInterceptionRerenderTarget(options) {
301
302
  cleanPathname: options.cleanPathname,
302
303
  currentRoute: options.currentRoute,
303
304
  findIntercept: options.findIntercept,
304
- getRouteParamNames: options.getRouteParamNames,
305
305
  getSourceRoute: options.getSourceRoute,
306
- isRscRequest: options.isRscRequest,
307
- toInterceptOpts: options.toInterceptOpts
306
+ interceptState: options.interceptState,
307
+ isRscRequest: options.isRscRequest
308
308
  });
309
309
  if (interceptState.kind === "source-route") {
310
310
  const sourceMatchedParams = interceptState.intercept.sourceMatchedParams ?? interceptState.intercept.matchedParams;
@@ -333,10 +333,9 @@ async function resolveAppPageIntercept(options) {
333
333
  cleanPathname: options.cleanPathname,
334
334
  currentRoute: options.currentRoute,
335
335
  findIntercept: options.findIntercept,
336
- getRouteParamNames: options.getRouteParamNames,
337
336
  getSourceRoute: options.getSourceRoute,
338
- isRscRequest: options.isRscRequest,
339
- toInterceptOpts: options.toInterceptOpts
337
+ interceptState: options.interceptState,
338
+ isRscRequest: options.isRscRequest
340
339
  });
341
340
  if (interceptState.kind === "source-route") {
342
341
  const renderRoute = interceptState.sourceRoute;
@@ -346,17 +345,18 @@ async function resolveAppPageIntercept(options) {
346
345
  ...sourceMatchedParams,
347
346
  ...interceptState.intercept.matchedParams
348
347
  };
349
- const renderSearchParams = options.resolveSearchParams ? await options.resolveSearchParams(renderRoute, options.searchParams) : options.searchParams;
348
+ const renderSearchParams = options.resolveSearchParams ? await options.resolveSearchParams(renderRoute, options.searchParams, interceptOpts) : options.searchParams;
350
349
  const renderParams = pickRouteParams(sourceMatchedParams, options.getRouteParamNames(interceptState.sourceRoute));
350
+ const resolvedNavigationParams = options.resolveNavigationParams(renderRoute, navigationParams, options.cleanPathname, interceptOpts);
351
351
  options.setNavigationContext({
352
- params: options.resolveNavigationParams(renderRoute, navigationParams, options.cleanPathname, interceptOpts),
352
+ params: resolvedNavigationParams,
353
353
  pathname: options.cleanPathname,
354
354
  searchParams: renderSearchParams
355
355
  });
356
356
  const interceptElement = await options.buildPageElement(renderRoute, renderParams, interceptOpts, renderSearchParams);
357
357
  return {
358
358
  interceptOpts: void 0,
359
- response: await options.renderInterceptResponse(renderRoute, interceptElement)
359
+ response: await options.renderInterceptResponse(renderRoute, interceptElement, resolvedNavigationParams)
360
360
  };
361
361
  }
362
362
  return {
@@ -385,4 +385,4 @@ async function buildAppPageElement(options) {
385
385
  }
386
386
  }
387
387
  //#endregion
388
- export { buildAppPageElement, resolveAppPageActionRerenderTarget, resolveAppPageGenerateStaticParamsSources, resolveAppPageIntercept, resolveAppPageInterceptionRerenderTarget, validateAppPageDynamicParams };
388
+ export { buildAppPageElement, resolveAppPageActionRerenderTarget, resolveAppPageGenerateStaticParamsSources, resolveAppPageIntercept, resolveAppPageInterceptState, resolveAppPageInterceptionRerenderTarget, validateAppPageDynamicParams };
@@ -35,6 +35,8 @@ export type AppPageRouteWiringSlot<TModule extends AppPageModule = AppPageModule
35
35
  configLayouts?: readonly (TModule | null | undefined)[] | null;
36
36
  configLayoutTreePositions?: readonly number[] | null;
37
37
  error?: TErrorModule | null;
38
+ errors?: readonly (TErrorModule | null | undefined)[] | null;
39
+ errorTreePositions?: readonly number[] | null;
38
40
  layout?: TModule | null;
39
41
  layoutIndex: number;
40
42
  loading?: TModule | null;
@@ -99,6 +101,8 @@ export type AppPageRouteWiringRoute<TModule extends AppPageModule = AppPageModul
99
101
  id: string;
100
102
  ownerTreePath: string;
101
103
  state: AppElementsSlotBinding["state"];
104
+ /** A slot sub-route whose children its route segments don't describe. */
105
+ synthetic?: boolean;
102
106
  } | null;
103
107
  /**
104
108
  * Static sibling segment names at each dynamic URL level for this route. Used
@@ -216,11 +220,7 @@ type BuildAppPageElementsOptions<TModule extends AppPageModule = AppPageModule,
216
220
  isRscRequest?: boolean;
217
221
  /**
218
222
  * The render can never be stored and, in production, is never sent with
219
- * static cache headers (see app-page-dispatch.ts), so a loading-shell
220
- * prefetch without a loading boundary may skip its layouts. A render that
221
- * may be stored keeps them, so a layout's dynamic API read still keeps it
222
- * out of the cache. An explicit CDN cache policy on a dynamic route can
223
- * still store the route tree, as a CDN would store Next.js's.
223
+ * static cache headers (see app-page-dispatch.ts).
224
224
  */
225
225
  isUncacheableRender?: boolean;
226
226
  /**
@@ -232,6 +232,12 @@ type BuildAppPageElementsOptions<TModule extends AppPageModule = AppPageModule,
232
232
  mountedLayouts?: ReadonlySet<string> | null;
233
233
  mountedSlotIds?: ReadonlySet<string> | null;
234
234
  renderIdentity?: AppPageRenderIdentity;
235
+ /**
236
+ * The render has no PPR (see app-page-dispatch.ts), so a loading-shell
237
+ * prefetch without a loading boundary sends only the route tree. Without it,
238
+ * the layouts render, as Next.js renders them with isRoutePPREnabled.
239
+ */
240
+ routeTreeOnlyPrefetchShell?: boolean;
235
241
  renderMode?: AppRscRenderMode;
236
242
  routePath: string;
237
243
  semanticPageIdentity?: Readonly<{
@@ -241,6 +247,12 @@ type BuildAppPageElementsOptions<TModule extends AppPageModule = AppPageModule,
241
247
  sourceRouteGraphId: string;
242
248
  }> | null;
243
249
  semanticInterceptionTargetRouteId?: string | null;
250
+ /**
251
+ * A sibling intercept's segment state keys, app root first, down to the
252
+ * segment its page element renders. Its BFCache segment levels follow these
253
+ * rather than the source route's segments.
254
+ */
255
+ siblingInterceptSegmentKeys?: readonly string[] | null;
244
256
  sourcePageSegments?: readonly string[] | null;
245
257
  };
246
258
  export type AppPageTemplateEntry<TModule extends AppPageModule = AppPageModule> = {
@@ -514,7 +514,7 @@ function buildAppPageElements(options) {
514
514
  [pageElementId]: null,
515
515
  [routeId]: null
516
516
  };
517
- if (isPrefetchLoadingShell && !shouldRenderPrefetchLoadingShell && options.isUncacheableRender === true) return {
517
+ if (isPrefetchLoadingShell && !shouldRenderPrefetchLoadingShell && options.routeTreeOnlyPrefetchShell === true) return {
518
518
  ...metadataEntries,
519
519
  ...options.route.staticSiblings && options.route.staticSiblings.length > 0 ? { [APP_STATIC_SIBLINGS_KEY]: options.route.staticSiblings } : {},
520
520
  [pageElementId]: null,
@@ -669,9 +669,11 @@ function buildAppPageElements(options) {
669
669
  branchAccessBoundaries.set(treePosition, entries);
670
670
  }
671
671
  const branchErrors = /* @__PURE__ */ new Map();
672
- if (overrideOrPageComponent && hasSlotTreeOverride) for (const [index, errorModule] of (slotOverride?.errorModules ?? []).entries()) {
672
+ const slotErrorModules = !overrideOrPageComponent ? void 0 : hasSlotTreeOverride ? slotOverride?.errorModules : slot.errors;
673
+ const slotErrorTreePositions = hasSlotTreeOverride ? slotOverride?.errorTreePositions : slot.errorTreePositions;
674
+ for (const [index, errorModule] of (slotErrorModules ?? []).entries()) {
673
675
  const component = getErrorBoundaryExport(errorModule);
674
- const treePosition = slotOverride?.errorTreePositions?.[index];
676
+ const treePosition = slotErrorTreePositions?.[index];
675
677
  if (component && treePosition !== void 0) branchErrors.set(treePosition, component);
676
678
  }
677
679
  const resolveSlotNotFoundBoundary = (treePosition) => {
@@ -753,12 +755,6 @@ function buildAppPageElements(options) {
753
755
  ])).filter((treePosition) => !prefetchSlotLoadingEntry || !isOwnedAtRoutePrefetchCutoff && treePosition <= prefetchSlotLoadingEntry.treePosition).sort((left, right) => left - right);
754
756
  for (let index = branchTreePositions.length - 1; index >= 0; index--) {
755
757
  const treePosition = branchTreePositions[index];
756
- const nestedBfcacheSegment = nestedBfcacheSegments.find((segment) => segment.treePosition === treePosition);
757
- if (nestedBfcacheSegment) slotElement = /* @__PURE__ */ jsx(BfcacheSegmentBoundary, {
758
- id: nestedBfcacheSegment.id,
759
- stateKey: nestedBfcacheSegment.stateKey,
760
- children: slotElement
761
- });
762
758
  const loadingComponents = prefetchSlotLoadingEntry ? [] : branchLoadings.get(treePosition) ?? [];
763
759
  for (let loadingIndex = loadingComponents.length - 1; loadingIndex >= 0; loadingIndex--) {
764
760
  const LoadingComponent = loadingComponents[loadingIndex];
@@ -786,6 +782,12 @@ function buildAppPageElements(options) {
786
782
  fallback: SegmentErrorComponent,
787
783
  children: slotElement
788
784
  });
785
+ const nestedBfcacheSegment = nestedBfcacheSegments.find((segment) => segment.treePosition === treePosition);
786
+ if (nestedBfcacheSegment) slotElement = /* @__PURE__ */ jsx(BfcacheSegmentBoundary, {
787
+ id: nestedBfcacheSegment.id,
788
+ stateKey: nestedBfcacheSegment.stateKey,
789
+ children: slotElement
790
+ });
789
791
  const layoutEntriesAtPosition = branchLayouts.get(treePosition) ?? [];
790
792
  for (let layoutIndex = layoutEntriesAtPosition.length - 1; layoutIndex >= 0; layoutIndex--) {
791
793
  const layoutEntry = layoutEntriesAtPosition[layoutIndex];
@@ -878,17 +880,19 @@ function buildAppPageElements(options) {
878
880
  children: routeChildren
879
881
  });
880
882
  const firstLayoutWithElementTreePosition = layoutEntries.find((entry) => getDefaultExport(entry.layoutModule) !== null)?.treePosition;
881
- const keepsSegmentLevelLists = String(process.env.__NEXT_CACHE_COMPONENTS) === "true" && firstLayoutWithElementTreePosition !== void 0 && !options.route.childrenSlot && options.semanticPageIdentity === void 0;
882
- const spineTreePositions = keepsSegmentLevelLists ? Array.from(/* @__PURE__ */ new Set([...orderedTreePositions, ...Array.from({ length: Math.max(0, routeSegments.length - firstLayoutWithElementTreePosition) }, (_, offset) => firstLayoutWithElementTreePosition + 1 + offset)])).sort((left, right) => left - right) : orderedTreePositions;
883
+ const siblingInterceptSegmentKeys = options.siblingInterceptSegmentKeys ?? null;
884
+ const keepsSegmentLevelLists = String(process.env.__NEXT_CACHE_COMPONENTS) === "true" && firstLayoutWithElementTreePosition !== void 0 && !options.route.childrenSlot?.synthetic && (options.semanticPageIdentity === void 0 || siblingInterceptSegmentKeys !== null);
885
+ const spineSegmentCount = siblingInterceptSegmentKeys?.length ?? routeSegments.length;
886
+ const spineTreePositions = keepsSegmentLevelLists ? Array.from(/* @__PURE__ */ new Set([...orderedTreePositions, ...Array.from({ length: Math.max(0, spineSegmentCount - firstLayoutWithElementTreePosition) }, (_, offset) => firstLayoutWithElementTreePosition + 1 + offset)])).sort((left, right) => left - right) : orderedTreePositions;
883
887
  const renderedTreePositions = isPrefetchLoadingShell ? spineTreePositions.filter(includesPrefetchTreePosition) : spineTreePositions;
884
888
  const firstLayoutBelowShell = keepsSegmentLevelLists && isPrefetchLoadingShell ? layoutEntries.find((entry) => !includesPrefetchTreePosition(entry.treePosition) && getDefaultExport(entry.layoutModule) !== null) : void 0;
885
889
  let childSegmentElementId = firstLayoutBelowShell?.id ?? pageElementId;
886
- let childSegmentTreePosition = firstLayoutBelowShell?.treePosition ?? routeSegments.length + 1;
890
+ let childSegmentTreePosition = firstLayoutBelowShell?.treePosition ?? spineSegmentCount + 1;
887
891
  const retainChildSegment = (treePosition, children) => treePosition === childSegmentTreePosition - 1 ? /* @__PURE__ */ jsx(BfcacheChildrenBoundary, {
888
892
  id: childSegmentElementId,
889
893
  children
890
894
  }) : /* @__PURE__ */ jsx(BfcacheChildrenBoundary, {
891
- stateKey: createBfcacheChildSegmentStateKey(resolveAppPageTemplateStateKey(routeSegments, treePosition, options.matchedParams)),
895
+ stateKey: createBfcacheChildSegmentStateKey(siblingInterceptSegmentKeys ? JSON.stringify(siblingInterceptSegmentKeys.slice(0, treePosition + 1)) : resolveAppPageTemplateStateKey(routeSegments, treePosition, options.matchedParams)),
892
896
  children
893
897
  });
894
898
  for (let index = renderedTreePositions.length - 1; index >= 0; index--) {
@@ -977,8 +981,10 @@ function buildAppPageElements(options) {
977
981
  const globalErrorComponent = getErrorBoundaryExport(options.globalErrorModule);
978
982
  routeChildren = /* @__PURE__ */ jsx(GlobalErrorBoundary, {
979
983
  fallback: DEFAULT_GLOBAL_ERROR_COMPONENT,
984
+ wrapsGlobalErrorBoundary: globalErrorComponent !== null,
980
985
  children: globalErrorComponent ? /* @__PURE__ */ jsx(ErrorBoundary, {
981
986
  fallback: globalErrorComponent,
987
+ isGlobalErrorBoundary: true,
982
988
  children: routeChildren
983
989
  }) : routeChildren
984
990
  });
@@ -1,7 +1,7 @@
1
1
  import { AppElementsWire } from "./app-elements-wire.js";
2
2
  import "./app-elements.js";
3
3
  import { createNestedBfcacheSlotSegmentId, deriveBfcacheSegmentIdentity } from "./bfcache-identity.js";
4
- import { resolveAppPageRouteStateKey, resolveAppPageSemanticSegmentStateKey, resolveAppPageTemplateStateKey } from "./app-page-segment-state.js";
4
+ import { APP_PAGE_DEFAULT_SEGMENT_KEY, APP_PAGE_SEGMENT_KEY, resolveAppPageRouteStateKey, resolveAppPageSemanticSegmentStateKey, resolveAppPageTemplateStateKey } from "./app-page-segment-state.js";
5
5
  //#region src/server/app-page-segment-plan.ts
6
6
  function requireGraphSequenceId(ids, index, kind) {
7
7
  const id = ids[index];
@@ -118,6 +118,14 @@ function createAppPageSegmentPlan(options) {
118
118
  treePosition
119
119
  });
120
120
  }
121
+ if (semanticSegments.length > 0) {
122
+ const leafKey = slotBinding.state === "default" ? APP_PAGE_DEFAULT_SEGMENT_KEY : APP_PAGE_SEGMENT_KEY;
123
+ semanticSegments.push({
124
+ identityKey: JSON.stringify([...boundSegmentKeys, leafKey]),
125
+ stateKey: leafKey,
126
+ treePosition: branchSegments.length
127
+ });
128
+ }
121
129
  }
122
130
  const includedInPayload = options.includeSlot(ownerTreePosition, targetTreePosition);
123
131
  const graphId = graphIds ? graphIds.slots[slotKey] : slot.id ?? slotId;
@@ -1,8 +1,17 @@
1
1
  import { AppPageParams } from "./app-page-boundary.js";
2
2
  //#region src/server/app-page-segment-state.d.ts
3
3
  export declare const APP_PAGE_SEGMENT_KEY = "__PAGE__";
4
+ export declare const APP_PAGE_DEFAULT_SEGMENT_KEY = "__DEFAULT__";
4
5
  export declare function isAppPageRouteGroupSegment(segment: string): boolean;
5
6
  export declare function canonicalizeAppPageParams(params: AppPageParams): void;
7
+ /**
8
+ * Rebinds segment state keys and BFCache identities resolved with one set of
9
+ * params to another. Keys are JSON arrays of segment keys, nested to any
10
+ * depth, so each whole segment key is compared with the param tokens
11
+ * `resolveSingleSegmentStateKey` produced for the learned params; only those
12
+ * whose value differs are replaced. Returns null when no token changes.
13
+ */
14
+ export declare function createAppPageSegmentStateKeyRebinder(fromParams: AppPageParams, toParams: AppPageParams): ((stateKey: string) => string) | null;
6
15
  export declare function resolveAppPageChildSegments(routeSegments: readonly string[], treePosition: number, params: AppPageParams): string[];
7
16
  export declare function resolveAppPageSegmentStateKey(routeSegments: readonly string[], treePosition: number, params: AppPageParams): string;
8
17
  /**
@@ -1,6 +1,7 @@
1
1
  import { isCatchAllSegment, isOptionalCatchAllSegment } from "../routing/utils.js";
2
2
  //#region src/server/app-page-segment-state.ts
3
3
  const APP_PAGE_SEGMENT_KEY = "__PAGE__";
4
+ const APP_PAGE_DEFAULT_SEGMENT_KEY = "__DEFAULT__";
4
5
  function isDynamicSegment(segment) {
5
6
  return segment.startsWith("[") && segment.endsWith("]") && !segment.includes(".");
6
7
  }
@@ -49,6 +50,58 @@ function resolveSingleSegmentStateKey(segment, params) {
49
50
  if (!param) return segment;
50
51
  return `${param.name}|${formatSegmentStateParamValue(param, params, segment)}|${param.type}`;
51
52
  }
53
+ const SEGMENT_PARAM_TYPES = [
54
+ "d",
55
+ "c",
56
+ "oc"
57
+ ];
58
+ function formatSegmentParamPattern(param) {
59
+ if (param.type === "oc") return `[[...${param.name}]]`;
60
+ if (param.type === "c") return `[...${param.name}]`;
61
+ return `[${param.name}]`;
62
+ }
63
+ /**
64
+ * Rebinds segment state keys and BFCache identities resolved with one set of
65
+ * params to another. Keys are JSON arrays of segment keys, nested to any
66
+ * depth, so each whole segment key is compared with the param tokens
67
+ * `resolveSingleSegmentStateKey` produced for the learned params; only those
68
+ * whose value differs are replaced. Returns null when no token changes.
69
+ */
70
+ function createAppPageSegmentStateKeyRebinder(fromParams, toParams) {
71
+ const tokens = /* @__PURE__ */ new Map();
72
+ for (const name of /* @__PURE__ */ new Set([...Object.keys(fromParams), ...Object.keys(toParams)])) for (const type of SEGMENT_PARAM_TYPES) {
73
+ const param = {
74
+ name,
75
+ type
76
+ };
77
+ const fallbackSegment = formatSegmentParamPattern(param);
78
+ const from = formatSegmentStateParamValue(param, fromParams, fallbackSegment);
79
+ const to = formatSegmentStateParamValue(param, toParams, fallbackSegment);
80
+ if (from !== to) tokens.set(`${name}|${from}|${type}`, `${name}|${to}|${type}`);
81
+ }
82
+ if (tokens.size === 0) return null;
83
+ const rebind = (stateKey) => {
84
+ const token = tokens.get(stateKey);
85
+ if (token !== void 0) return token;
86
+ if (!stateKey.startsWith("[")) return stateKey;
87
+ let parts;
88
+ try {
89
+ parts = JSON.parse(stateKey);
90
+ } catch {
91
+ return stateKey;
92
+ }
93
+ if (!Array.isArray(parts)) return stateKey;
94
+ let changed = false;
95
+ const rebound = parts.map((part) => {
96
+ if (typeof part !== "string") return part;
97
+ const next = rebind(part);
98
+ if (next !== part) changed = true;
99
+ return next;
100
+ });
101
+ return changed ? JSON.stringify(rebound) : stateKey;
102
+ };
103
+ return rebind;
104
+ }
52
105
  function resolveAppPageChildSegments(routeSegments, treePosition, params) {
53
106
  const rawSegments = routeSegments.slice(treePosition);
54
107
  const resolvedSegments = [];
@@ -104,4 +157,4 @@ function resolveAppPageTemplateStateKey(routeSegments, treePosition, params) {
104
157
  return statePath.length > 0 ? JSON.stringify(statePath) : "";
105
158
  }
106
159
  //#endregion
107
- export { APP_PAGE_SEGMENT_KEY, canonicalizeAppPageParams, isAppPageRouteGroupSegment, resolveAppPageChildSegments, resolveAppPagePatternStateKey, resolveAppPageRouteStateKey, resolveAppPageSegmentStateKey, resolveAppPageSemanticSegmentStateKey, resolveAppPageTemplateStateKey };
160
+ export { APP_PAGE_DEFAULT_SEGMENT_KEY, APP_PAGE_SEGMENT_KEY, canonicalizeAppPageParams, createAppPageSegmentStateKeyRebinder, isAppPageRouteGroupSegment, resolveAppPageChildSegments, resolveAppPagePatternStateKey, resolveAppPageRouteStateKey, resolveAppPageSegmentStateKey, resolveAppPageSemanticSegmentStateKey, resolveAppPageTemplateStateKey };
@@ -57,6 +57,12 @@ type RenderPagesFallbackOptions = {
57
57
  pathname?: string;
58
58
  pagesDataRequest?: Request | null;
59
59
  request: Request;
60
+ /**
61
+ * The basePath `request.url` still carries. The page renderer receives the
62
+ * URL without it, as in the Pages Router pipeline, so `req.url` and `asPath`
63
+ * leave it out like Next.js.
64
+ */
65
+ requestBasePath?: string;
60
66
  /** The shared cache entry answers every host; a request-time render is not stored. */
61
67
  sharedAcrossHosts?: boolean;
62
68
  url: URL;