vinext 0.0.41 → 0.0.43

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 (166) hide show
  1. package/README.md +0 -1
  2. package/dist/build/client-build-config.d.ts +119 -0
  3. package/dist/build/client-build-config.js +149 -0
  4. package/dist/build/client-build-config.js.map +1 -0
  5. package/dist/build/layout-classification-types.d.ts +62 -0
  6. package/dist/build/layout-classification-types.js +1 -0
  7. package/dist/build/layout-classification.d.ts +60 -0
  8. package/dist/build/layout-classification.js +98 -0
  9. package/dist/build/layout-classification.js.map +1 -0
  10. package/dist/build/report.d.ts +15 -1
  11. package/dist/build/report.js +50 -1
  12. package/dist/build/report.js.map +1 -1
  13. package/dist/build/route-classification-manifest.d.ts +53 -0
  14. package/dist/build/route-classification-manifest.js +145 -0
  15. package/dist/build/route-classification-manifest.js.map +1 -0
  16. package/dist/build/run-prerender.js +1 -1
  17. package/dist/build/ssr-manifest.d.ts +19 -0
  18. package/dist/build/ssr-manifest.js +71 -0
  19. package/dist/build/ssr-manifest.js.map +1 -0
  20. package/dist/check.js +2 -2
  21. package/dist/check.js.map +1 -1
  22. package/dist/cli.js +1 -1
  23. package/dist/client/entry.js +1 -1
  24. package/dist/config/config-matchers.js +1 -0
  25. package/dist/config/config-matchers.js.map +1 -1
  26. package/dist/entries/app-rsc-entry.js +315 -101
  27. package/dist/entries/app-rsc-entry.js.map +1 -1
  28. package/dist/index.d.ts +1 -169
  29. package/dist/index.js +112 -432
  30. package/dist/index.js.map +1 -1
  31. package/dist/plugins/fonts.d.ts +49 -1
  32. package/dist/plugins/fonts.js +96 -3
  33. package/dist/plugins/fonts.js.map +1 -1
  34. package/dist/plugins/postcss.d.ts +27 -0
  35. package/dist/plugins/postcss.js +94 -0
  36. package/dist/plugins/postcss.js.map +1 -0
  37. package/dist/plugins/strip-server-exports.d.ts +14 -0
  38. package/dist/plugins/strip-server-exports.js +73 -0
  39. package/dist/plugins/strip-server-exports.js.map +1 -0
  40. package/dist/routing/app-router.d.ts +6 -4
  41. package/dist/routing/app-router.js +44 -25
  42. package/dist/routing/app-router.js.map +1 -1
  43. package/dist/server/app-browser-entry.js +307 -100
  44. package/dist/server/app-browser-entry.js.map +1 -1
  45. package/dist/server/app-browser-error.d.ts +8 -0
  46. package/dist/server/app-browser-error.js +9 -0
  47. package/dist/server/app-browser-error.js.map +1 -0
  48. package/dist/server/app-browser-state.d.ts +93 -0
  49. package/dist/server/app-browser-state.js +132 -0
  50. package/dist/server/app-browser-state.js.map +1 -0
  51. package/dist/server/app-elements.d.ts +92 -0
  52. package/dist/server/app-elements.js +122 -0
  53. package/dist/server/app-elements.js.map +1 -0
  54. package/dist/server/app-page-boundary-render.d.ts +2 -1
  55. package/dist/server/app-page-boundary-render.js +40 -1
  56. package/dist/server/app-page-boundary-render.js.map +1 -1
  57. package/dist/server/app-page-cache.d.ts +6 -3
  58. package/dist/server/app-page-cache.js +14 -8
  59. package/dist/server/app-page-cache.js.map +1 -1
  60. package/dist/server/app-page-execution.d.ts +36 -3
  61. package/dist/server/app-page-execution.js +50 -10
  62. package/dist/server/app-page-execution.js.map +1 -1
  63. package/dist/server/app-page-probe.d.ts +10 -4
  64. package/dist/server/app-page-probe.js +24 -15
  65. package/dist/server/app-page-probe.js.map +1 -1
  66. package/dist/server/app-page-render.d.ts +7 -4
  67. package/dist/server/app-page-render.js +13 -4
  68. package/dist/server/app-page-render.js.map +1 -1
  69. package/dist/server/app-page-request.d.ts +52 -4
  70. package/dist/server/app-page-request.js +86 -16
  71. package/dist/server/app-page-request.js.map +1 -1
  72. package/dist/server/app-page-response.d.ts +1 -0
  73. package/dist/server/app-page-response.js +1 -0
  74. package/dist/server/app-page-response.js.map +1 -1
  75. package/dist/server/app-page-route-wiring.d.ts +22 -8
  76. package/dist/server/app-page-route-wiring.js +219 -83
  77. package/dist/server/app-page-route-wiring.js.map +1 -1
  78. package/dist/server/app-render-dependency.d.ts +13 -0
  79. package/dist/server/app-render-dependency.js +35 -0
  80. package/dist/server/app-render-dependency.js.map +1 -0
  81. package/dist/server/app-route-handler-execution.d.ts +1 -0
  82. package/dist/server/app-route-handler-execution.js +1 -0
  83. package/dist/server/app-route-handler-execution.js.map +1 -1
  84. package/dist/server/app-route-handler-policy.js +5 -3
  85. package/dist/server/app-route-handler-policy.js.map +1 -1
  86. package/dist/server/app-route-handler-response.js +2 -0
  87. package/dist/server/app-route-handler-response.js.map +1 -1
  88. package/dist/server/app-route-handler-runtime.d.ts +1 -0
  89. package/dist/server/app-route-handler-runtime.js +26 -1
  90. package/dist/server/app-route-handler-runtime.js.map +1 -1
  91. package/dist/server/app-ssr-entry.js +6 -2
  92. package/dist/server/app-ssr-entry.js.map +1 -1
  93. package/dist/server/dev-server.js +2 -4
  94. package/dist/server/dev-server.js.map +1 -1
  95. package/dist/server/middleware.js +1 -5
  96. package/dist/server/middleware.js.map +1 -1
  97. package/dist/server/prod-server.d.ts +3 -3
  98. package/dist/server/prod-server.js +1 -1
  99. package/dist/server/prod-server.js.map +1 -1
  100. package/dist/server/request-pipeline.d.ts +2 -1
  101. package/dist/server/request-pipeline.js +34 -5
  102. package/dist/server/request-pipeline.js.map +1 -1
  103. package/dist/shims/cache-runtime.d.ts +1 -0
  104. package/dist/shims/cache-runtime.js +0 -5
  105. package/dist/shims/cache-runtime.js.map +1 -1
  106. package/dist/shims/cache.d.ts +1 -0
  107. package/dist/shims/cache.js +1 -8
  108. package/dist/shims/cache.js.map +1 -1
  109. package/dist/shims/client-hook-error.d.ts +14 -0
  110. package/dist/shims/client-hook-error.js +19 -0
  111. package/dist/shims/client-hook-error.js.map +1 -0
  112. package/dist/shims/constants.d.ts +3 -3
  113. package/dist/shims/constants.js +3 -3
  114. package/dist/shims/constants.js.map +1 -1
  115. package/dist/shims/document.d.ts +6 -6
  116. package/dist/shims/error-boundary.d.ts +4 -4
  117. package/dist/shims/error-boundary.js +1 -1
  118. package/dist/shims/error-boundary.js.map +1 -1
  119. package/dist/shims/form.d.ts +3 -3
  120. package/dist/shims/head-state.d.ts +1 -0
  121. package/dist/shims/head-state.js +0 -5
  122. package/dist/shims/head-state.js.map +1 -1
  123. package/dist/shims/headers.d.ts +11 -0
  124. package/dist/shims/headers.js +13 -10
  125. package/dist/shims/headers.js.map +1 -1
  126. package/dist/shims/i18n-state.d.ts +1 -0
  127. package/dist/shims/i18n-state.js +0 -4
  128. package/dist/shims/i18n-state.js.map +1 -1
  129. package/dist/shims/internal/app-router-context.d.ts +6 -6
  130. package/dist/shims/internal/router-context.d.ts +2 -2
  131. package/dist/shims/layout-segment-context.d.ts +2 -2
  132. package/dist/shims/link.js +19 -11
  133. package/dist/shims/link.js.map +1 -1
  134. package/dist/shims/metadata.d.ts +3 -3
  135. package/dist/shims/navigation-state.d.ts +2 -0
  136. package/dist/shims/navigation-state.js +0 -13
  137. package/dist/shims/navigation-state.js.map +1 -1
  138. package/dist/shims/navigation.d.ts +56 -9
  139. package/dist/shims/navigation.js +112 -28
  140. package/dist/shims/navigation.js.map +1 -1
  141. package/dist/shims/navigation.react-server.d.ts +14 -0
  142. package/dist/shims/navigation.react-server.js +29 -0
  143. package/dist/shims/navigation.react-server.js.map +1 -0
  144. package/dist/shims/request-context.d.ts +1 -0
  145. package/dist/shims/request-context.js +0 -9
  146. package/dist/shims/request-context.js.map +1 -1
  147. package/dist/shims/request-state-types.d.ts +1 -1
  148. package/dist/shims/router-state.d.ts +1 -0
  149. package/dist/shims/router-state.js +0 -5
  150. package/dist/shims/router-state.js.map +1 -1
  151. package/dist/shims/slot.d.ts +11 -7
  152. package/dist/shims/slot.js +28 -19
  153. package/dist/shims/slot.js.map +1 -1
  154. package/dist/shims/unified-request-context.d.ts +2 -0
  155. package/dist/shims/unified-request-context.js +0 -14
  156. package/dist/shims/unified-request-context.js.map +1 -1
  157. package/dist/utils/mdx-scan.d.ts +10 -0
  158. package/dist/utils/mdx-scan.js +36 -0
  159. package/dist/utils/mdx-scan.js.map +1 -0
  160. package/dist/utils/public-routes.d.ts +5 -0
  161. package/dist/utils/public-routes.js +50 -0
  162. package/dist/utils/public-routes.js.map +1 -0
  163. package/package.json +3 -3
  164. package/dist/plugins/fix-use-server-closure-collision.d.ts +0 -29
  165. package/dist/plugins/fix-use-server-closure-collision.js +0 -204
  166. package/dist/plugins/fix-use-server-closure-collision.js.map +0 -1
@@ -15,6 +15,7 @@ import fs from "node:fs";
15
15
  */
16
16
  const configMatchersPath = resolveEntryPath("../config/config-matchers.js", import.meta.url);
17
17
  const requestPipelinePath = resolveEntryPath("../server/request-pipeline.js", import.meta.url);
18
+ const middlewareRequestHeadersPath = resolveEntryPath("../server/middleware-request-headers.js", import.meta.url);
18
19
  const requestContextShimPath = resolveEntryPath("../shims/request-context.js", import.meta.url);
19
20
  const normalizePathModulePath = resolveEntryPath("../server/normalize-path.js", import.meta.url);
20
21
  const appRouteHandlerRuntimePath = resolveEntryPath("../server/app-route-handler-runtime.js", import.meta.url);
@@ -24,6 +25,7 @@ const appRouteHandlerCachePath = resolveEntryPath("../server/app-route-handler-c
24
25
  const appPageCachePath = resolveEntryPath("../server/app-page-cache.js", import.meta.url);
25
26
  const appPageExecutionPath = resolveEntryPath("../server/app-page-execution.js", import.meta.url);
26
27
  const appPageBoundaryRenderPath = resolveEntryPath("../server/app-page-boundary-render.js", import.meta.url);
28
+ const appElementsPath = resolveEntryPath("../server/app-elements.js", import.meta.url);
27
29
  const appPageRouteWiringPath = resolveEntryPath("../server/app-page-route-wiring.js", import.meta.url);
28
30
  const appPageRenderPath = resolveEntryPath("../server/app-page-render.js", import.meta.url);
29
31
  const appPageResponsePath = resolveEntryPath("../server/app-page-response.js", import.meta.url);
@@ -69,7 +71,7 @@ function generateRscEntry(appDir, routes, middlewarePath, metadataRoutes, global
69
71
  if (route.pagePath) getImportVar(route.pagePath);
70
72
  if (route.routePath) getImportVar(route.routePath);
71
73
  for (const layout of route.layouts) getImportVar(layout);
72
- for (const tmpl of route.templates) if (tmpl) getImportVar(tmpl);
74
+ for (const tmpl of route.templates) getImportVar(tmpl);
73
75
  if (route.loadingPath) getImportVar(route.loadingPath);
74
76
  if (route.errorPath) getImportVar(route.errorPath);
75
77
  if (route.layoutErrorPaths) {
@@ -88,9 +90,9 @@ function generateRscEntry(appDir, routes, middlewarePath, metadataRoutes, global
88
90
  for (const ir of slot.interceptingRoutes) getImportVar(ir.pagePath);
89
91
  }
90
92
  }
91
- const routeEntries = routes.map((route) => {
93
+ const routeEntries = routes.map((route, routeIdx) => {
92
94
  const layoutVars = route.layouts.map((l) => getImportVar(l));
93
- const templateVars = route.templates.map((t) => t ? getImportVar(t) : "null");
95
+ const templateVars = route.templates.map((t) => getImportVar(t));
94
96
  const notFoundVars = (route.notFoundPaths || []).map((nf) => nf ? getImportVar(nf) : "null");
95
97
  const slotEntries = route.parallelSlots.map((slot) => {
96
98
  const interceptEntries = slot.interceptingRoutes.map((ir) => ` {
@@ -99,7 +101,8 @@ function generateRscEntry(appDir, routes, middlewarePath, metadataRoutes, global
99
101
  page: ${getImportVar(ir.pagePath)},
100
102
  params: ${JSON.stringify(ir.params)},
101
103
  }`);
102
- return ` ${JSON.stringify(slot.name)}: {
104
+ return ` ${JSON.stringify(slot.key)}: {
105
+ name: ${JSON.stringify(slot.name)},
103
106
  page: ${slot.pagePath ? getImportVar(slot.pagePath) : "null"},
104
107
  default: ${slot.defaultPath ? getImportVar(slot.defaultPath) : "null"},
105
108
  layout: ${slot.layoutPath ? getImportVar(slot.layoutPath) : "null"},
@@ -114,6 +117,8 @@ ${interceptEntries.join(",\n")}
114
117
  });
115
118
  const layoutErrorVars = (route.layoutErrorPaths || []).map((ep) => ep ? getImportVar(ep) : "null");
116
119
  return ` {
120
+ __buildTimeClassifications: __VINEXT_CLASS(${routeIdx}), // evaluated once at module load
121
+ __buildTimeReasons: __classDebug ? __VINEXT_CLASS_REASONS(${routeIdx}) : null,
117
122
  pattern: ${JSON.stringify(route.pattern)},
118
123
  patternParts: ${JSON.stringify(route.patternParts)},
119
124
  isDynamic: ${route.isDynamic},
@@ -122,6 +127,7 @@ ${interceptEntries.join(",\n")}
122
127
  routeHandler: ${route.routePath ? getImportVar(route.routePath) : "null"},
123
128
  layouts: [${layoutVars.join(", ")}],
124
129
  routeSegments: ${JSON.stringify(route.routeSegments)},
130
+ templateTreePositions: ${JSON.stringify(route.templateTreePositions)},
125
131
  layoutTreePositions: ${JSON.stringify(route.layoutTreePositions)},
126
132
  templates: [${templateVars.join(", ")}],
127
133
  errors: [${layoutErrorVars.join(", ")}],
@@ -219,6 +225,7 @@ ${instrumentationPath ? `import * as _instrumentation from ${JSON.stringify(inst
219
225
  ${effectiveMetaRoutes.length > 0 ? `import { sitemapToXml, robotsToText, manifestToJson } from ${JSON.stringify(metadataRoutesPath)};` : ""}
220
226
  import { requestContextFromRequest, normalizeHost, matchRedirect, matchRewrite, matchHeaders, isExternalUrl, proxyExternalRequest, sanitizeDestination } from ${JSON.stringify(configMatchersPath)};
221
227
  import { decodePathParams as __decodePathParams } from ${JSON.stringify(normalizePathModulePath)};
228
+ import { buildRequestHeadersFromMiddlewareResponse as __buildRequestHeadersFromMiddlewareResponse } from ${JSON.stringify(middlewareRequestHeadersPath)};
222
229
  import { validateCsrfOrigin, validateServerActionPayload, validateImageUrl, guardProtocolRelativeUrl, hasBasePath, stripBasePath, normalizeTrailingSlash, processMiddlewareHeaders } from ${JSON.stringify(requestPipelinePath)};
223
230
  import {
224
231
  isKnownDynamicAppRoute as __isKnownDynamicAppRoute,
@@ -246,7 +253,12 @@ import {
246
253
  renderAppPageHttpAccessFallback as __renderAppPageHttpAccessFallback,
247
254
  } from ${JSON.stringify(appPageBoundaryRenderPath)};
248
255
  import {
249
- buildAppPageRouteElement as __buildAppPageRouteElement,
256
+ APP_INTERCEPTION_CONTEXT_KEY as __APP_INTERCEPTION_CONTEXT_KEY,
257
+ createAppPayloadRouteId as __createAppPayloadRouteId,
258
+ } from ${JSON.stringify(appElementsPath)};
259
+ import {
260
+ buildAppPageElements as __buildAppPageElements,
261
+ createAppPageTreePath as __createAppPageTreePath,
250
262
  resolveAppPageChildSegments as __resolveAppPageChildSegments,
251
263
  } from ${JSON.stringify(appPageRouteWiringPath)};
252
264
  import {
@@ -257,6 +269,7 @@ import {
257
269
  } from ${JSON.stringify(appPageResponsePath)};
258
270
  import { getScriptNonceFromHeaderSources as __getScriptNonceFromHeaderSources } from ${JSON.stringify(cspPath)};
259
271
  import {
272
+ resolveAppPageActionRerenderTarget as __resolveAppPageActionRerenderTarget,
260
273
  buildAppPageElement as __buildAppPageElement,
261
274
  resolveAppPageIntercept as __resolveAppPageIntercept,
262
275
  validateAppPageDynamicParams as __validateAppPageDynamicParams,
@@ -397,7 +410,21 @@ function __isrCacheKey(pathname, suffix) {
397
410
  return prefix + ":__hash:" + __isrFnv1a64(normalized) + ":" + suffix;
398
411
  }
399
412
  function __isrHtmlKey(pathname) { return __isrCacheKey(pathname, "html"); }
400
- function __isrRscKey(pathname) { return __isrCacheKey(pathname, "rsc"); }
413
+ function __isrRscKey(pathname, mountedSlotsHeader) {
414
+ if (!mountedSlotsHeader) return __isrCacheKey(pathname, "rsc");
415
+ return __isrCacheKey(pathname, "rsc:" + __isrFnv1a64(mountedSlotsHeader));
416
+ }
417
+ function __normalizeMountedSlotsHeader(raw) {
418
+ if (!raw) return null;
419
+ const normalized = Array.from(
420
+ new Set(
421
+ raw
422
+ .split(/\\s+/)
423
+ .filter(Boolean),
424
+ ),
425
+ ).sort().join(" ");
426
+ return normalized || null;
427
+ }
401
428
  function __isrRouteKey(pathname) { return __isrCacheKey(pathname, "route"); }
402
429
  // Verbose cache logging — opt in with NEXT_PRIVATE_DEBUG_CACHE=1.
403
430
  // Matches the env var Next.js uses for its own cache debug output so operators
@@ -406,6 +433,16 @@ const __isrDebug = process.env.NEXT_PRIVATE_DEBUG_CACHE
406
433
  ? console.debug.bind(console, "[vinext] ISR:")
407
434
  : undefined;
408
435
 
436
+ // Classification debug — opt in with VINEXT_DEBUG_CLASSIFICATION=1. Gated on
437
+ // the env var so the hot path pays no overhead unless an operator is actively
438
+ // tracing why a layout was flagged static or dynamic. The reason payload is
439
+ // carried by __VINEXT_CLASS_REASONS and consumed inside probeAppPageLayouts.
440
+ const __classDebug = process.env.VINEXT_DEBUG_CLASSIFICATION
441
+ ? function(layoutId, reason) {
442
+ console.debug("[vinext] CLS:", layoutId, reason);
443
+ }
444
+ : undefined;
445
+
409
446
  // Normalize null-prototype objects from matchPattern() into thenable objects
410
447
  // that work both as Promises (for Next.js 15+ async params) and as plain
411
448
  // objects with synchronous property access (for pre-15 code like params.id).
@@ -574,6 +611,24 @@ async function __ensureInstrumentation() {
574
611
  return __instrumentationInitPromise;
575
612
  }` : ""}
576
613
 
614
+ // Build-time layout classification dispatch. Replaced in generateBundle
615
+ // with a switch statement that returns a pre-computed per-layout
616
+ // Map<layoutIndex, "static" | "dynamic"> for each route. Until the
617
+ // plugin patches this stub, every route falls back to the Layer 3
618
+ // runtime probe, which is the current (slow) behaviour.
619
+ function __VINEXT_CLASS(routeIdx) {
620
+ return null;
621
+ }
622
+
623
+ // Build-time layout classification reasons dispatch. Sibling of
624
+ // __VINEXT_CLASS, returning a per-route Map<layoutIndex, ClassificationReason>
625
+ // that feeds the debug channel when VINEXT_DEBUG_CLASSIFICATION is active.
626
+ // Replaced in generateBundle with a real dispatch table; the stub returns
627
+ // null so the hot path never allocates reason maps when debug is off.
628
+ function __VINEXT_CLASS_REASONS(routeIdx) {
629
+ return null;
630
+ }
631
+
577
632
  const routes = [
578
633
  ${routeEntries.join(",\n")}
579
634
  ];
@@ -730,18 +785,22 @@ function matchPattern(urlParts, patternParts) {
730
785
  return params;
731
786
  }
732
787
 
788
+ function mergeMatchedParams(sourceParams, targetParams) {
789
+ return Object.assign(Object.create(null), sourceParams, targetParams);
790
+ }
791
+
733
792
  // Build a global intercepting route lookup for RSC navigation.
734
- // Maps target URL patterns to { sourceRouteIndex, slotName, interceptPage, params }.
793
+ // Maps target URL patterns to { sourceRouteIndex, slotKey, interceptPage, params }.
735
794
  const interceptLookup = [];
736
795
  for (let ri = 0; ri < routes.length; ri++) {
737
796
  const r = routes[ri];
738
797
  if (!r.slots) continue;
739
- for (const [slotName, slotMod] of Object.entries(r.slots)) {
798
+ for (const [slotKey, slotMod] of Object.entries(r.slots)) {
740
799
  if (!slotMod.intercepts) continue;
741
800
  for (const intercept of slotMod.intercepts) {
742
801
  interceptLookup.push({
743
802
  sourceRouteIndex: ri,
744
- slotName,
803
+ slotKey,
745
804
  targetPattern: intercept.targetPattern,
746
805
  targetPatternParts: intercept.targetPattern.split("/").filter(Boolean),
747
806
  page: intercept.page,
@@ -755,21 +814,54 @@ for (let ri = 0; ri < routes.length; ri++) {
755
814
  * Check if a pathname matches any intercepting route.
756
815
  * Returns the match info or null.
757
816
  */
758
- function findIntercept(pathname) {
817
+ function findIntercept(pathname, sourcePathname = null) {
759
818
  const urlParts = pathname.split("/").filter(Boolean);
760
819
  for (const entry of interceptLookup) {
761
820
  const params = matchPattern(urlParts, entry.targetPatternParts);
762
821
  if (params !== null) {
763
- return { ...entry, matchedParams: params };
822
+ let sourceParams = Object.create(null);
823
+ if (sourcePathname !== null) {
824
+ const sourceRoute = routes[entry.sourceRouteIndex];
825
+ const sourceParts = sourcePathname.split("/").filter(Boolean);
826
+ const matchedSourceParams = sourceRoute
827
+ ? matchPattern(sourceParts, sourceRoute.patternParts)
828
+ : null;
829
+ if (matchedSourceParams !== null) {
830
+ sourceParams = matchedSourceParams;
831
+ }
832
+ }
833
+ return { ...entry, matchedParams: mergeMatchedParams(sourceParams, params) };
764
834
  }
765
835
  }
766
836
  return null;
767
837
  }
768
838
 
769
- async function buildPageElement(route, params, opts, searchParams) {
839
+ async function buildPageElements(route, params, routePath, pageRequest) {
840
+ const {
841
+ opts,
842
+ searchParams,
843
+ isRscRequest,
844
+ request,
845
+ mountedSlotsHeader,
846
+ } = pageRequest;
847
+ const hasPageModule = !!route.page;
770
848
  const PageComponent = route.page?.default;
771
- if (!PageComponent) {
772
- return createElement("div", null, "Page has no default export");
849
+ if (hasPageModule && !PageComponent) {
850
+ const _interceptionContext = opts?.interceptionContext ?? null;
851
+ const _noExportRouteId = __createAppPayloadRouteId(routePath, _interceptionContext);
852
+ let _noExportRootLayout = null;
853
+ if (route.layouts?.length > 0) {
854
+ // Compute the root layout tree path for this error payload using the
855
+ // canonical helper so it stays aligned with buildAppPageElements().
856
+ const _tp = route.layoutTreePositions?.[0] ?? 0;
857
+ _noExportRootLayout = __createAppPageTreePath(route.routeSegments, _tp);
858
+ }
859
+ return {
860
+ [__APP_INTERCEPTION_CONTEXT_KEY]: _interceptionContext,
861
+ __route: _noExportRouteId,
862
+ __rootLayout: _noExportRootLayout,
863
+ [_noExportRouteId]: createElement("div", null, "Page has no default export"),
864
+ };
773
865
  }
774
866
 
775
867
  // Resolve metadata and viewport from layouts and page.
@@ -798,6 +890,24 @@ async function buildPageElement(route, params, opts, searchParams) {
798
890
  // route it to the nearest error.tsx boundary (or global-error.tsx).
799
891
  const layoutMods = route.layouts.filter(Boolean);
800
892
 
893
+ // Convert URLSearchParams → plain object for page generateMetadata() and
894
+ // pageProps.searchParams. Built before the layout loop so the page metadata
895
+ // call (below) and pageProps can reference the same object.
896
+ // NOTE: Layouts do NOT receive searchParams in generateMetadata() — only
897
+ // pages do. This matches Next.js behavior (resolve-metadata.ts:777).
898
+ const spObj = Object.create(null);
899
+ let hasSearchParams = false;
900
+ if (searchParams && searchParams.forEach) {
901
+ searchParams.forEach(function(v, k) {
902
+ hasSearchParams = true;
903
+ if (k in spObj) {
904
+ spObj[k] = Array.isArray(spObj[k]) ? spObj[k].concat(v) : [spObj[k], v];
905
+ } else {
906
+ spObj[k] = v;
907
+ }
908
+ });
909
+ }
910
+
801
911
  // Build the parent promise chain and kick off metadata resolution in one pass.
802
912
  // Each layout module is called exactly once. layoutMetaPromises[i] is the
803
913
  // promise for layout[i]'s own metadata result.
@@ -820,22 +930,6 @@ async function buildPageElement(route, params, opts, searchParams) {
820
930
  // Page's parent is the fully-accumulated layout metadata.
821
931
  const pageParentPromise = accumulatedMetaPromise;
822
932
 
823
- // Convert URLSearchParams → plain object so we can pass it to
824
- // resolveModuleMetadata (which expects Record<string, string | string[]>).
825
- // This same object is reused for pageProps.searchParams below.
826
- const spObj = {};
827
- let hasSearchParams = false;
828
- if (searchParams && searchParams.forEach) {
829
- searchParams.forEach(function(v, k) {
830
- hasSearchParams = true;
831
- if (k in spObj) {
832
- spObj[k] = Array.isArray(spObj[k]) ? spObj[k].concat(v) : [spObj[k], v];
833
- } else {
834
- spObj[k] = v;
835
- }
836
- });
837
- }
838
-
839
933
  const [layoutMetaResults, layoutVpResults, pageMeta, pageVp] = await Promise.all([
840
934
  Promise.all(layoutMetaPromises),
841
935
  Promise.all(layoutMods.map((mod) => resolveModuleViewport(mod, params).catch((err) => { console.error("[vinext] Layout generateViewport() failed:", err); return null; }))),
@@ -867,19 +961,31 @@ async function buildPageElement(route, params, opts, searchParams) {
867
961
  // dynamic, and this avoids false positives from React internals.
868
962
  if (hasSearchParams) markDynamicUsage();
869
963
  }
870
- return __buildAppPageRouteElement({
871
- element: createElement(PageComponent, pageProps),
964
+ // mountedSlotsHeader is threaded through from the handler scope so every
965
+ // call site shares one source of truth for request-derived values. Reading
966
+ // the same header in two places invites silent drift when a future refactor
967
+ // changes only one of them.
968
+ const mountedSlotIds = mountedSlotsHeader
969
+ ? new Set(mountedSlotsHeader.split(" "))
970
+ : null;
971
+
972
+ return __buildAppPageElements({
973
+ element: PageComponent ? createElement(PageComponent, pageProps) : null,
872
974
  globalErrorModule: ${globalErrorVar ? globalErrorVar : "null"},
975
+ isRscRequest,
976
+ mountedSlotIds,
873
977
  makeThenableParams,
874
978
  matchedParams: params,
875
979
  resolvedMetadata,
876
980
  resolvedViewport,
981
+ interceptionContext: opts?.interceptionContext ?? null,
982
+ routePath,
877
983
  rootNotFoundModule: ${rootNotFoundVar ? rootNotFoundVar : "null"},
878
984
  route,
879
985
  slotOverrides:
880
- opts && opts.interceptSlot && opts.interceptPage
986
+ opts && opts.interceptSlotKey && opts.interceptPage
881
987
  ? {
882
- [opts.interceptSlot]: {
988
+ [opts.interceptSlotKey]: {
883
989
  pageModule: opts.interceptPage,
884
990
  params: opts.interceptParams || params,
885
991
  },
@@ -1034,7 +1140,7 @@ export default async function handler(request, ctx) {
1034
1140
  // Per-request container for middleware state. Passed into
1035
1141
  // _handleRequest which fills in .headers and .status;
1036
1142
  // avoids module-level variables that race on Workers.
1037
- const _mwCtx = { headers: null, status: null };
1143
+ const _mwCtx = { headers: null, requestHeaders: null, status: null };
1038
1144
  const response = await _handleRequest(request, __reqCtx, _mwCtx);
1039
1145
  // Apply custom headers from next.config.js to non-redirect responses.
1040
1146
  // Skip redirects (3xx) because Response.redirect() creates immutable headers,
@@ -1198,6 +1304,14 @@ async function _handleRequest(request, __reqCtx, _mwCtx) {
1198
1304
  }
1199
1305
 
1200
1306
  const isRscRequest = pathname.endsWith(".rsc") || request.headers.get("accept")?.includes("text/x-component");
1307
+ // Read mounted-slots header once at the handler scope and thread it through
1308
+ // every buildPageElements call site. Previously both the handler and
1309
+ // buildPageElements read and normalized it independently, which invited
1310
+ // silent drift if a future refactor changed only one path.
1311
+ const __mountedSlotsHeader = __normalizeMountedSlotsHeader(
1312
+ request.headers.get("x-vinext-mounted-slots"),
1313
+ );
1314
+ const interceptionContextHeader = request.headers.get("X-Vinext-Interception-Context")?.replaceAll("\0", "") || null;
1201
1315
  let cleanPathname = pathname.replace(/\\.rsc$/, "");
1202
1316
 
1203
1317
  // Middleware response headers and custom rewrite status are stored in
@@ -1336,6 +1450,9 @@ async function _handleRequest(request, __reqCtx, _mwCtx) {
1336
1450
  // be merged into the outgoing HTTP response — this prefix is reserved for
1337
1451
  // internal routing signals and must never reach clients.
1338
1452
  if (_mwCtx.headers) {
1453
+ // Preserve the pre-strip header set so route handlers can reconstruct
1454
+ // a request object with middleware header overrides applied.
1455
+ _mwCtx.requestHeaders = new Headers(_mwCtx.headers);
1339
1456
  applyMiddlewareRequestHeaders(_mwCtx.headers);
1340
1457
  processMiddlewareHeaders(_mwCtx.headers);
1341
1458
  }
@@ -1520,10 +1637,7 @@ async function _handleRequest(request, __reqCtx, _mwCtx) {
1520
1637
  returnValue = { ok: true, data };
1521
1638
  } catch (e) {
1522
1639
  // Detect redirect() / permanentRedirect() called inside the action.
1523
- // These throw errors with digest "NEXT_REDIRECT;<type>;<url>[;<status>]".
1524
- // The type field is empty when redirect() was called without an explicit
1525
- // type argument. In Server Action context, Next.js defaults to "push" so
1526
- // the Back button works after form submissions.
1640
+ // These throw errors with digest "NEXT_REDIRECT;replace;url[;status]".
1527
1641
  // The URL is encodeURIComponent-encoded to prevent semicolons in the URL
1528
1642
  // from corrupting the delimiter-based digest format.
1529
1643
  if (e && typeof e === "object" && "digest" in e) {
@@ -1532,7 +1646,7 @@ async function _handleRequest(request, __reqCtx, _mwCtx) {
1532
1646
  const parts = digest.split(";");
1533
1647
  actionRedirect = {
1534
1648
  url: decodeURIComponent(parts[2]),
1535
- type: parts[1] || "push", // Server Action → default "push"
1649
+ type: parts[1] || "push", // "push" or "replace"
1536
1650
  status: parts[3] ? parseInt(parts[3], 10) : 307,
1537
1651
  };
1538
1652
  returnValue = { ok: true, data: undefined };
@@ -1569,9 +1683,6 @@ async function _handleRequest(request, __reqCtx, _mwCtx) {
1569
1683
  "Content-Type": "text/x-component; charset=utf-8",
1570
1684
  "Vary": "RSC, Accept",
1571
1685
  });
1572
- // Merge middleware headers first so the framework's own redirect control
1573
- // headers below are always authoritative and cannot be clobbered by
1574
- // middleware that happens to set x-action-redirect* keys.
1575
1686
  __mergeMiddlewareResponseHeaders(redirectHeaders, _mwCtx.headers);
1576
1687
  redirectHeaders.set("x-action-redirect", actionRedirect.url);
1577
1688
  redirectHeaders.set("x-action-redirect-type", actionRedirect.type);
@@ -1586,24 +1697,73 @@ async function _handleRequest(request, __reqCtx, _mwCtx) {
1586
1697
 
1587
1698
  // After the action, re-render the current page so the client
1588
1699
  // gets an updated React tree reflecting any mutations.
1700
+ //
1701
+ // When the original request came from inside an intercepted modal
1702
+ // (X-Vinext-Interception-Context present + source route still
1703
+ // matches), rebuild the intercepted tree — otherwise the modal would
1704
+ // unmount and the direct route would render in its place. Mirrors
1705
+ // the interception resolution used by the GET path.
1589
1706
  const match = matchRoute(cleanPathname);
1590
1707
  let element;
1708
+ let errorPattern = match ? match.route.pattern : cleanPathname;
1591
1709
  if (match) {
1592
1710
  const { route: actionRoute, params: actionParams } = match;
1711
+ const __actionRerenderTarget = __resolveAppPageActionRerenderTarget({
1712
+ cleanPathname,
1713
+ currentParams: actionParams,
1714
+ currentRoute: actionRoute,
1715
+ findIntercept(pathname) {
1716
+ return findIntercept(pathname, interceptionContextHeader);
1717
+ },
1718
+ getRouteParamNames(sourceRoute) {
1719
+ return sourceRoute.params;
1720
+ },
1721
+ getSourceRoute(sourceRouteIndex) {
1722
+ return routes[sourceRouteIndex];
1723
+ },
1724
+ isRscRequest,
1725
+ toInterceptOpts(intercept) {
1726
+ return {
1727
+ interceptionContext: interceptionContextHeader,
1728
+ interceptSlotKey: intercept.slotKey,
1729
+ interceptPage: intercept.page,
1730
+ interceptParams: intercept.matchedParams,
1731
+ };
1732
+ },
1733
+ });
1734
+
1593
1735
  setNavigationContext({
1594
1736
  pathname: cleanPathname,
1595
1737
  searchParams: url.searchParams,
1596
- params: actionParams,
1738
+ params: __actionRerenderTarget.navigationParams,
1597
1739
  });
1598
- element = await buildPageElement(actionRoute, actionParams, undefined, url.searchParams);
1740
+ element = buildPageElements(
1741
+ __actionRerenderTarget.route,
1742
+ __actionRerenderTarget.params,
1743
+ cleanPathname,
1744
+ {
1745
+ opts: __actionRerenderTarget.interceptOpts,
1746
+ searchParams: url.searchParams,
1747
+ isRscRequest,
1748
+ request,
1749
+ mountedSlotsHeader: __mountedSlotsHeader,
1750
+ },
1751
+ );
1752
+ errorPattern = __actionRerenderTarget.route.pattern;
1599
1753
  } else {
1600
- element = createElement("div", null, "Page not found");
1754
+ const _actionRouteId = __createAppPayloadRouteId(cleanPathname, null);
1755
+ element = {
1756
+ [__APP_INTERCEPTION_CONTEXT_KEY]: null,
1757
+ __route: _actionRouteId,
1758
+ __rootLayout: null,
1759
+ [_actionRouteId]: createElement("div", null, "Page not found"),
1760
+ };
1601
1761
  }
1602
1762
 
1603
1763
  const onRenderError = createRscOnErrorHandler(
1604
1764
  request,
1605
1765
  cleanPathname,
1606
- match ? match.route.pattern : cleanPathname,
1766
+ errorPattern,
1607
1767
  );
1608
1768
  const rscStream = renderToReadableStream(
1609
1769
  { root: element, returnValue },
@@ -1618,15 +1778,22 @@ async function _handleRequest(request, __reqCtx, _mwCtx) {
1618
1778
  const actionPendingCookies = getAndClearPendingCookies();
1619
1779
  const actionDraftCookie = getDraftModeCookieHeader();
1620
1780
 
1621
- const actionHeaders = new Headers({ "Content-Type": "text/x-component; charset=utf-8", "Vary": "RSC, Accept" });
1781
+ const actionHeaders = new Headers({
1782
+ "Content-Type": "text/x-component; charset=utf-8",
1783
+ "Vary": "RSC, Accept",
1784
+ });
1622
1785
  __mergeMiddlewareResponseHeaders(actionHeaders, _mwCtx.headers);
1786
+ const actionResponse = new Response(rscStream, {
1787
+ status: _mwCtx.status ?? 200,
1788
+ headers: actionHeaders,
1789
+ });
1623
1790
  if (actionPendingCookies.length > 0 || actionDraftCookie) {
1624
1791
  for (const cookie of actionPendingCookies) {
1625
- actionHeaders.append("Set-Cookie", cookie);
1792
+ actionResponse.headers.append("Set-Cookie", cookie);
1626
1793
  }
1627
- if (actionDraftCookie) actionHeaders.append("Set-Cookie", actionDraftCookie);
1794
+ if (actionDraftCookie) actionResponse.headers.append("Set-Cookie", actionDraftCookie);
1628
1795
  }
1629
- return new Response(rscStream, { status: _mwCtx.status ?? 200, headers: actionHeaders });
1796
+ return actionResponse;
1630
1797
  } catch (err) {
1631
1798
  getAndClearPendingCookies(); // Clear pending cookies on error
1632
1799
  console.error("[vinext] Server action error:", err);
@@ -1686,12 +1853,24 @@ async function _handleRequest(request, __reqCtx, _mwCtx) {
1686
1853
  if (!isRscRequest) {
1687
1854
  const __pagesEntry = await import.meta.viteRsc.loadModule("ssr", "index");
1688
1855
  if (typeof __pagesEntry.renderPage === "function") {
1856
+ const __pagesRequestHeaders = _mwCtx.requestHeaders
1857
+ ? __buildRequestHeadersFromMiddlewareResponse(request.headers, _mwCtx.requestHeaders)
1858
+ : null;
1859
+ const __pagesRequest = __pagesRequestHeaders
1860
+ ? new Request(request.url, { method: request.method, headers: __pagesRequestHeaders })
1861
+ : request;
1689
1862
  // Use segment-wise decoding to preserve encoded path delimiters (%2F).
1690
1863
  // decodeURIComponent would turn /admin%2Fpanel into /admin/panel,
1691
1864
  // changing the path structure and bypassing middleware matchers.
1692
1865
  // Ported from Next.js: packages/next/src/server/lib/router-utils/decode-path-params.ts
1693
1866
  // https://github.com/vercel/next.js/blob/canary/packages/next/src/server/lib/router-utils/decode-path-params.ts
1694
- const __pagesRes = await __pagesEntry.renderPage(request, __decodePathParams(url.pathname) + (url.search || ""), {});
1867
+ const __pagesRes = await __pagesEntry.renderPage(
1868
+ __pagesRequest,
1869
+ __decodePathParams(url.pathname) + (url.search || ""),
1870
+ {},
1871
+ undefined,
1872
+ _mwCtx.requestHeaders,
1873
+ );
1695
1874
  // Only return the Pages Router response if it matched a route
1696
1875
  // (non-404). A 404 means the path isn't a Pages route either,
1697
1876
  // so fall through to the App Router not-found page below.
@@ -1834,6 +2013,7 @@ async function _handleRequest(request, __reqCtx, _mwCtx) {
1834
2013
  markDynamicUsage,
1835
2014
  method,
1836
2015
  middlewareContext: _mwCtx,
2016
+ middlewareRequestHeaders: _mwCtx.requestHeaders,
1837
2017
  params: makeThenableParams(params),
1838
2018
  reportRequestError: _reportRequestError,
1839
2019
  request,
@@ -1853,8 +2033,9 @@ async function _handleRequest(request, __reqCtx, _mwCtx) {
1853
2033
  }
1854
2034
 
1855
2035
  // Build the component tree: layouts wrapping the page
2036
+ const hasPageModule = !!route.page;
1856
2037
  const PageComponent = route.page?.default;
1857
- if (!PageComponent) {
2038
+ if (hasPageModule && !PageComponent) {
1858
2039
  setHeadersContext(null);
1859
2040
  setNavigationContext(null);
1860
2041
  return new Response("Page has no default export", { status: 500 });
@@ -1932,6 +2113,7 @@ async function _handleRequest(request, __reqCtx, _mwCtx) {
1932
2113
  isrHtmlKey: __isrHtmlKey,
1933
2114
  isrRscKey: __isrRscKey,
1934
2115
  isrSet: __isrSet,
2116
+ mountedSlotsHeader: __mountedSlotsHeader,
1935
2117
  revalidateSeconds,
1936
2118
  renderFreshPageForCache: async function() {
1937
2119
  // Re-render the page to produce fresh HTML + RSC data for the cache
@@ -1946,7 +2128,21 @@ async function _handleRequest(request, __reqCtx, _mwCtx) {
1946
2128
  return _runWithUnifiedCtx(__revalUCtx, async () => {
1947
2129
  _ensureFetchPatch();
1948
2130
  setNavigationContext({ pathname: cleanPathname, searchParams: new URLSearchParams(), params });
1949
- const __revalElement = await buildPageElement(route, params, undefined, new URLSearchParams());
2131
+ // Slot context (X-Vinext-Mounted-Slots) is inherited from the
2132
+ // triggering request so the regen result is cached under the
2133
+ // correct slot-variant key.
2134
+ const __revalElement = await buildPageElements(
2135
+ route,
2136
+ params,
2137
+ cleanPathname,
2138
+ {
2139
+ opts: undefined,
2140
+ searchParams: new URLSearchParams(),
2141
+ isRscRequest,
2142
+ request,
2143
+ mountedSlotsHeader: __mountedSlotsHeader,
2144
+ },
2145
+ );
1950
2146
  const __revalOnError = createRscOnErrorHandler(request, cleanPathname, route.pattern);
1951
2147
  const __revalRscStream = renderToReadableStream(__revalElement, { onError: __revalOnError });
1952
2148
  const __revalRscCapture = __teeAppPageRscStreamForCapture(__revalRscStream, true);
@@ -1995,44 +2191,32 @@ async function _handleRequest(request, __reqCtx, _mwCtx) {
1995
2191
  // If the target URL matches an intercepting route in a parallel slot,
1996
2192
  // render the source route with the intercepting page in the slot.
1997
2193
  const __interceptResult = await __resolveAppPageIntercept({
1998
- buildPageElement,
2194
+ buildPageElement(interceptRoute, interceptParams, interceptOpts, interceptSearchParams) {
2195
+ return buildPageElements(
2196
+ interceptRoute,
2197
+ interceptParams,
2198
+ cleanPathname,
2199
+ {
2200
+ opts: interceptOpts,
2201
+ searchParams: interceptSearchParams,
2202
+ isRscRequest,
2203
+ request,
2204
+ mountedSlotsHeader: __mountedSlotsHeader,
2205
+ },
2206
+ );
2207
+ },
1999
2208
  cleanPathname,
2000
2209
  currentRoute: route,
2001
- findIntercept,
2002
- getRoutePattern(sourceRoute) {
2003
- return sourceRoute.pattern;
2210
+ findIntercept(pathname) {
2211
+ return findIntercept(pathname, interceptionContextHeader);
2212
+ },
2213
+ getRouteParamNames(sourceRoute) {
2214
+ return sourceRoute.params;
2004
2215
  },
2005
2216
  getSourceRoute(sourceRouteIndex) {
2006
2217
  return routes[sourceRouteIndex];
2007
2218
  },
2008
2219
  isRscRequest,
2009
- matchSourceRouteParams(pattern) {
2010
- // Extract actual URL param values by prefix-matching the request pathname
2011
- // against the source route's pattern. This handles all interception conventions:
2012
- // (.) same-level, (..) one-level-up, and (...) root — the source pattern's
2013
- // dynamic segments that align with the URL get their real values extracted.
2014
- // We must NOT use matchRoute(pattern) here: the trie would match the literal
2015
- // ":param" strings as dynamic segment values, returning e.g. {id: ":id"}.
2016
- const patternParts = pattern.split("/").filter(Boolean);
2017
- const urlParts = cleanPathname.split("/").filter(Boolean);
2018
- const params = Object.create(null);
2019
- for (let i = 0; i < patternParts.length; i++) {
2020
- const pp = patternParts[i];
2021
- if (pp.endsWith("+") || pp.endsWith("*")) {
2022
- // urlParts.slice(i) safely returns [] when i >= urlParts.length,
2023
- // which is the correct value for optional catch-all with zero segments.
2024
- params[pp.slice(1, -1)] = urlParts.slice(i);
2025
- break;
2026
- }
2027
- if (i >= urlParts.length) break;
2028
- if (pp.startsWith(":")) {
2029
- params[pp.slice(1)] = urlParts[i];
2030
- } else if (pp !== urlParts[i]) {
2031
- break;
2032
- }
2033
- }
2034
- return params;
2035
- },
2036
2220
  renderInterceptResponse(sourceRoute, interceptElement) {
2037
2221
  const interceptOnError = createRscOnErrorHandler(
2038
2222
  request,
@@ -2046,7 +2230,10 @@ async function _handleRequest(request, __reqCtx, _mwCtx) {
2046
2230
  // by the client, and async server components that run during consumption need the
2047
2231
  // context to still be live. The AsyncLocalStorage scope from runWithRequestContext
2048
2232
  // handles cleanup naturally when all async continuations complete.
2049
- const interceptHeaders = new Headers({ "Content-Type": "text/x-component; charset=utf-8", "Vary": "RSC, Accept" });
2233
+ const interceptHeaders = new Headers({
2234
+ "Content-Type": "text/x-component; charset=utf-8",
2235
+ "Vary": "RSC, Accept",
2236
+ });
2050
2237
  __mergeMiddlewareResponseHeaders(interceptHeaders, _mwCtx.headers);
2051
2238
  return new Response(interceptStream, {
2052
2239
  status: _mwCtx.status ?? 200,
@@ -2057,7 +2244,8 @@ async function _handleRequest(request, __reqCtx, _mwCtx) {
2057
2244
  setNavigationContext,
2058
2245
  toInterceptOpts(intercept) {
2059
2246
  return {
2060
- interceptSlot: intercept.slotName,
2247
+ interceptionContext: interceptionContextHeader,
2248
+ interceptSlotKey: intercept.slotKey,
2061
2249
  interceptPage: intercept.page,
2062
2250
  interceptParams: intercept.matchedParams,
2063
2251
  };
@@ -2070,7 +2258,13 @@ async function _handleRequest(request, __reqCtx, _mwCtx) {
2070
2258
 
2071
2259
  const __pageBuildResult = await __buildAppPageElement({
2072
2260
  buildPageElement() {
2073
- return buildPageElement(route, params, interceptOpts, url.searchParams);
2261
+ return buildPageElements(route, params, cleanPathname, {
2262
+ opts: interceptOpts,
2263
+ searchParams: url.searchParams,
2264
+ isRscRequest,
2265
+ request,
2266
+ mountedSlotsHeader: __mountedSlotsHeader,
2267
+ });
2074
2268
  },
2075
2269
  renderErrorBoundaryPage(buildErr) {
2076
2270
  return renderErrorBoundaryPage(route, buildErr, isRscRequest, request, params, _scriptNonce);
@@ -2108,19 +2302,6 @@ async function _handleRequest(request, __reqCtx, _mwCtx) {
2108
2302
  // rscCssTransform — no manual loadCss() call needed.
2109
2303
  const _hasLoadingBoundary = !!(route.loading && route.loading.default);
2110
2304
  const _asyncLayoutParams = makeThenableParams(params);
2111
- // Convert URLSearchParams to a plain object then wrap in makeThenableParams()
2112
- // so probePage() passes the same shape that buildPageElement() gives to the
2113
- // real render. Without this, pages that destructure await-ed searchParams
2114
- // throw TypeError during probe.
2115
- const _probeSearchObj = {};
2116
- url.searchParams.forEach(function(v, k) {
2117
- if (k in _probeSearchObj) {
2118
- _probeSearchObj[k] = Array.isArray(_probeSearchObj[k]) ? _probeSearchObj[k].concat(v) : [_probeSearchObj[k], v];
2119
- } else {
2120
- _probeSearchObj[k] = v;
2121
- }
2122
- });
2123
- const _asyncSearchParams = makeThenableParams(_probeSearchObj);
2124
2305
  return __renderAppPageLifecycle({
2125
2306
  cleanPathname,
2126
2307
  clearRequestContext() {
@@ -2166,9 +2347,42 @@ async function _handleRequest(request, __reqCtx, _mwCtx) {
2166
2347
  return LayoutComp({ params: _asyncLayoutParams, children: null });
2167
2348
  },
2168
2349
  probePage() {
2350
+ if (!PageComponent) return null;
2351
+ const _probeSearchObj = {};
2352
+ url.searchParams.forEach(function(v, k) {
2353
+ if (k in _probeSearchObj) {
2354
+ _probeSearchObj[k] = Array.isArray(_probeSearchObj[k])
2355
+ ? _probeSearchObj[k].concat(v)
2356
+ : [_probeSearchObj[k], v];
2357
+ } else {
2358
+ _probeSearchObj[k] = v;
2359
+ }
2360
+ });
2361
+ const _asyncSearchParams = makeThenableParams(_probeSearchObj);
2169
2362
  return PageComponent({ params: _asyncLayoutParams, searchParams: _asyncSearchParams });
2170
2363
  },
2364
+ classification: {
2365
+ getLayoutId(index) {
2366
+ const tp = route.layoutTreePositions?.[index] ?? 0;
2367
+ return "layout:" + __createAppPageTreePath(route.routeSegments, tp);
2368
+ },
2369
+ buildTimeClassifications: route.__buildTimeClassifications,
2370
+ buildTimeReasons: route.__buildTimeReasons,
2371
+ debugClassification: __classDebug,
2372
+ async runWithIsolatedDynamicScope(fn) {
2373
+ const priorDynamic = consumeDynamicUsage();
2374
+ try {
2375
+ const result = await fn();
2376
+ const dynamicDetected = consumeDynamicUsage();
2377
+ return { result, dynamicDetected };
2378
+ } finally {
2379
+ consumeDynamicUsage();
2380
+ if (priorDynamic) markDynamicUsage();
2381
+ }
2382
+ },
2383
+ },
2171
2384
  revalidateSeconds,
2385
+ mountedSlotsHeader: __mountedSlotsHeader,
2172
2386
  renderErrorBoundaryResponse(renderErr) {
2173
2387
  return renderErrorBoundaryPage(route, renderErr, isRscRequest, request, params, _scriptNonce);
2174
2388
  },