vinext 1.0.0 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (263) hide show
  1. package/README.md +72 -1
  2. package/THIRD_PARTY_LICENSES.md +26 -0
  3. package/dist/build/app-page-static-eligibility.d.ts +15 -0
  4. package/dist/build/app-page-static-eligibility.js +82 -0
  5. package/dist/build/lifecycle.js +10 -10
  6. package/dist/build/pages-client-assets-module.js +6 -1
  7. package/dist/build/precompress.js +1 -1
  8. package/dist/build/prerender-paths.js +6 -85
  9. package/dist/build/prerender.d.ts +8 -1
  10. package/dist/build/prerender.js +167 -46
  11. package/dist/build/run-prerender.js +4 -3
  12. package/dist/build/server-entry.d.ts +2 -0
  13. package/dist/build/server-entry.js +7 -1
  14. package/dist/build/server-manifest.d.ts +7 -0
  15. package/dist/build/server-manifest.js +11 -1
  16. package/dist/cache/cache-adapters-virtual.d.ts +7 -1
  17. package/dist/cache/cache-adapters-virtual.js +3 -3
  18. package/dist/client/browser-globals.d.ts +4 -0
  19. package/dist/client/browser-globals.js +5 -0
  20. package/dist/client/navigation-runtime.d.ts +11 -1
  21. package/dist/client/navigation-runtime.js +3 -3
  22. package/dist/config/config-matchers.js +3 -1
  23. package/dist/config/dotenv.js +14 -3
  24. package/dist/config/next-config.d.ts +7 -0
  25. package/dist/config/next-config.js +18 -3
  26. package/dist/entries/app-rsc-entry.d.ts +2 -0
  27. package/dist/entries/app-rsc-entry.js +67 -45
  28. package/dist/index.d.ts +4 -1
  29. package/dist/index.js +200 -100
  30. package/dist/init-cloudflare.d.ts +1 -1
  31. package/dist/init-cloudflare.js +6 -6
  32. package/dist/init-platform.js +1 -1
  33. package/dist/init.js +4 -2
  34. package/dist/plugins/cache-flight-codec.d.ts +19 -0
  35. package/dist/plugins/cache-flight-codec.js +68 -0
  36. package/dist/plugins/commonjs-esm-facade.d.ts +35 -0
  37. package/dist/plugins/commonjs-esm-facade.js +80 -0
  38. package/dist/plugins/commonjs-syntax.d.ts +16 -0
  39. package/dist/plugins/commonjs-syntax.js +22 -0
  40. package/dist/plugins/fonts.d.ts +8 -11
  41. package/dist/plugins/fonts.js +71 -34
  42. package/dist/plugins/ignore-dynamic-requests.d.ts +11 -0
  43. package/dist/plugins/ignore-dynamic-requests.js +54 -3
  44. package/dist/plugins/import-meta-url.js +6 -6
  45. package/dist/plugins/og-asset-ownership.d.ts +2 -0
  46. package/dist/plugins/og-asset-ownership.js +24 -13
  47. package/dist/plugins/og-assets.d.ts +18 -0
  48. package/dist/plugins/og-assets.js +50 -8
  49. package/dist/plugins/og-harfbuzz.js +38 -27
  50. package/dist/plugins/optimize-imports.js +2 -1
  51. package/dist/plugins/require-context.d.ts +1 -0
  52. package/dist/plugins/require-context.js +28 -1
  53. package/dist/plugins/scan-build-css.d.ts +12 -0
  54. package/dist/plugins/scan-build-css.js +39 -0
  55. package/dist/plugins/strip-server-exports.d.ts +2 -0
  56. package/dist/plugins/strip-server-exports.js +3 -1
  57. package/dist/plugins/transform-result.d.ts +18 -0
  58. package/dist/plugins/transform-result.js +23 -2
  59. package/dist/plugins/typeof-window.d.ts +10 -0
  60. package/dist/plugins/typeof-window.js +33 -9
  61. package/dist/routing/app-route-graph.d.ts +0 -1
  62. package/dist/routing/app-route-graph.js +2 -4
  63. package/dist/routing/app-route-ids.d.ts +3 -0
  64. package/dist/routing/app-route-ids.js +6 -0
  65. package/dist/routing/file-matcher.d.ts +1 -0
  66. package/dist/routing/file-matcher.js +1 -1
  67. package/dist/server/api-handler.js +17 -7
  68. package/dist/server/app-bfcache-id.d.ts +1 -0
  69. package/dist/server/app-bfcache-id.js +7 -1
  70. package/dist/server/app-bfcache-identity.js +1 -2
  71. package/dist/server/app-browser-entry.js +15 -9
  72. package/dist/server/app-browser-history-controller.d.ts +2 -0
  73. package/dist/server/app-browser-history-controller.js +8 -0
  74. package/dist/server/app-browser-hydration.d.ts +1 -1
  75. package/dist/server/app-browser-hydration.js +2 -2
  76. package/dist/server/app-browser-interception-context.js +26 -2
  77. package/dist/server/app-browser-navigation-controller.d.ts +7 -1
  78. package/dist/server/app-browser-navigation-controller.js +94 -11
  79. package/dist/server/app-browser-state.js +1 -1
  80. package/dist/server/app-browser-stream.d.ts +1 -1
  81. package/dist/server/app-fallback-renderer.d.ts +2 -0
  82. package/dist/server/app-fallback-renderer.js +1 -0
  83. package/dist/server/app-history-state.d.ts +3 -1
  84. package/dist/server/app-history-state.js +9 -5
  85. package/dist/server/app-middleware.js +2 -1
  86. package/dist/server/app-optimistic-routing.js +1 -1
  87. package/dist/server/app-page-boundary-render.d.ts +5 -0
  88. package/dist/server/app-page-boundary-render.js +6 -4
  89. package/dist/server/app-page-cache-finalizer.d.ts +11 -0
  90. package/dist/server/app-page-cache-finalizer.js +41 -7
  91. package/dist/server/app-page-cache-render.d.ts +18 -0
  92. package/dist/server/app-page-cache-render.js +65 -44
  93. package/dist/server/app-page-cache.d.ts +5 -0
  94. package/dist/server/app-page-cache.js +27 -7
  95. package/dist/server/app-page-dispatch.d.ts +9 -1
  96. package/dist/server/app-page-dispatch.js +47 -30
  97. package/dist/server/app-page-element-builder.d.ts +7 -0
  98. package/dist/server/app-page-element-builder.js +27 -42
  99. package/dist/server/app-page-execution.d.ts +15 -12
  100. package/dist/server/app-page-execution.js +30 -40
  101. package/dist/server/app-page-head.d.ts +5 -0
  102. package/dist/server/app-page-head.js +9 -3
  103. package/dist/server/app-page-probe.d.ts +0 -109
  104. package/dist/server/app-page-probe.js +3 -136
  105. package/dist/server/app-page-render-identity.d.ts +0 -1
  106. package/dist/server/app-page-render-identity.js +1 -1
  107. package/dist/server/app-page-render-observation.js +2 -2
  108. package/dist/server/app-page-render.d.ts +13 -5
  109. package/dist/server/app-page-render.js +219 -118
  110. package/dist/server/app-page-request.d.ts +0 -1
  111. package/dist/server/app-page-request.js +1 -2
  112. package/dist/server/app-page-response.d.ts +4 -3
  113. package/dist/server/app-page-response.js +4 -3
  114. package/dist/server/app-page-route-wiring.d.ts +17 -5
  115. package/dist/server/app-page-route-wiring.js +55 -33
  116. package/dist/server/app-page-rsc-render-status.d.ts +48 -0
  117. package/dist/server/app-page-rsc-render-status.js +54 -0
  118. package/dist/server/app-page-search-params-observation.js +1 -1
  119. package/dist/server/app-page-stream.d.ts +15 -8
  120. package/dist/server/app-page-stream.js +7 -1
  121. package/dist/server/app-page-tracing.js +2 -1
  122. package/dist/server/app-pages-bridge.js +3 -2
  123. package/dist/server/app-render-abort-error.d.ts +3 -0
  124. package/dist/server/app-render-abort-error.js +8 -0
  125. package/dist/server/app-request-stage-dispatch.js +1 -1
  126. package/dist/server/app-request-stage-independent-entry.js +8 -11
  127. package/dist/server/app-response-header-provenance.d.ts +2 -0
  128. package/dist/server/app-response-header-provenance.js +10 -4
  129. package/dist/server/app-route-handler-cache.d.ts +2 -1
  130. package/dist/server/app-route-handler-cache.js +11 -7
  131. package/dist/server/app-route-handler-dispatch.js +11 -4
  132. package/dist/server/app-route-handler-execution.d.ts +9 -1
  133. package/dist/server/app-route-handler-execution.js +28 -13
  134. package/dist/server/app-route-handler-policy.js +2 -2
  135. package/dist/server/app-route-handler-response.d.ts +1 -1
  136. package/dist/server/app-route-handler-response.js +14 -5
  137. package/dist/server/app-router-entry.d.ts +2 -1
  138. package/dist/server/app-router-entry.js +8 -12
  139. package/dist/server/app-rsc-errors.d.ts +0 -1
  140. package/dist/server/app-rsc-errors.js +2 -6
  141. package/dist/server/app-rsc-handler.d.ts +5 -0
  142. package/dist/server/app-rsc-handler.js +229 -102
  143. package/dist/server/app-rsc-request-normalization.d.ts +4 -2
  144. package/dist/server/app-rsc-request-normalization.js +7 -4
  145. package/dist/server/app-rsc-response-finalizer.js +4 -1
  146. package/dist/server/app-rsc-response-stage.d.ts +1 -1
  147. package/dist/server/app-rsc-response-stage.js +6 -4
  148. package/dist/server/app-rsc-route-matching.d.ts +9 -1
  149. package/dist/server/app-rsc-route-matching.js +37 -20
  150. package/dist/server/app-server-action-execution.d.ts +2 -1
  151. package/dist/server/app-server-action-execution.js +1 -1
  152. package/dist/server/app-ssr-entry.d.ts +3 -2
  153. package/dist/server/app-ssr-entry.js +9 -7
  154. package/dist/server/app-ssr-search-params-gate.js +1 -1
  155. package/dist/server/app-ssr-stream.d.ts +6 -1
  156. package/dist/server/app-ssr-stream.js +35 -16
  157. package/dist/server/app-worker-stages.d.ts +2 -2
  158. package/dist/server/app-worker-stages.js +3 -3
  159. package/dist/server/cache-control.js +3 -11
  160. package/dist/server/cacheability-request.js +56 -52
  161. package/dist/server/config-headers.js +3 -1
  162. package/dist/server/dev-server.js +23 -5
  163. package/dist/server/headers.d.ts +16 -0
  164. package/dist/server/headers.js +19 -1
  165. package/dist/server/http-error-responses.js +1 -0
  166. package/dist/server/image-optimization.d.ts +16 -0
  167. package/dist/server/image-optimization.js +26 -6
  168. package/dist/server/isr-cache.d.ts +2 -0
  169. package/dist/server/isr-cache.js +8 -3
  170. package/dist/server/metadata-route-response.js +94 -37
  171. package/dist/server/middleware-matcher-pattern.js +8 -4
  172. package/dist/server/middleware-response-headers.js +2 -0
  173. package/dist/server/multi-stage.d.ts +8 -0
  174. package/dist/server/navigation-planner.js +1 -1
  175. package/dist/server/node-response-signal.d.ts +16 -0
  176. package/dist/server/node-response-signal.js +26 -0
  177. package/dist/server/open-redirect.d.ts +47 -0
  178. package/dist/server/open-redirect.js +74 -1
  179. package/dist/server/pages-api-route.js +8 -2
  180. package/dist/server/pages-asset-tags.d.ts +1 -0
  181. package/dist/server/pages-asset-tags.js +73 -22
  182. package/dist/server/pages-client-assets.d.ts +2 -0
  183. package/dist/server/pages-page-data.d.ts +6 -1
  184. package/dist/server/pages-page-data.js +71 -65
  185. package/dist/server/pages-page-handler.js +50 -15
  186. package/dist/server/pages-page-response.js +3 -1
  187. package/dist/server/pages-request-pipeline.d.ts +2 -1
  188. package/dist/server/pages-request-pipeline.js +20 -5
  189. package/dist/server/pages-request-stage-entry.js +14 -8
  190. package/dist/server/prerender-manifest.d.ts +6 -0
  191. package/dist/server/prerender-manifest.js +1 -1
  192. package/dist/server/prod-server.d.ts +8 -7
  193. package/dist/server/prod-server.js +152 -78
  194. package/dist/server/request-pipeline.d.ts +20 -8
  195. package/dist/server/request-pipeline.js +21 -7
  196. package/dist/server/response-compression.d.ts +43 -0
  197. package/dist/server/response-compression.js +111 -0
  198. package/dist/server/response-stage-cacheability.js +1 -1
  199. package/dist/server/seed-cache.js +10 -7
  200. package/dist/server/socket-error-backstop.d.ts +14 -0
  201. package/dist/server/socket-error-backstop.js +27 -4
  202. package/dist/server/worker-revalidation-context.d.ts +2 -1
  203. package/dist/server/worker-revalidation-context.js +1 -0
  204. package/dist/server/workers-tracing.d.ts +17 -0
  205. package/dist/server/workers-tracing.js +48 -8
  206. package/dist/shims/cache-adapter-instantiate.d.ts +50 -0
  207. package/dist/shims/cache-adapter-instantiate.js +118 -0
  208. package/dist/shims/cache-callable-runtime.d.ts +2 -40
  209. package/dist/shims/cache-callable-runtime.js +50 -168
  210. package/dist/shims/cache-flight-arguments.d.ts +22 -0
  211. package/dist/shims/cache-flight-arguments.js +56 -0
  212. package/dist/shims/cache-request-state.d.ts +18 -14
  213. package/dist/shims/cache-request-state.js +33 -21
  214. package/dist/shims/cache-runtime.d.ts +6 -19
  215. package/dist/shims/cache-runtime.js +168 -226
  216. package/dist/shims/cache.d.ts +3 -2
  217. package/dist/shims/cache.js +8 -5
  218. package/dist/shims/cacheability-classification.d.ts +8 -1
  219. package/dist/shims/cacheability-classification.js +2 -2
  220. package/dist/shims/cdn-cache.d.ts +10 -1
  221. package/dist/shims/cdn-cache.js +16 -9
  222. package/dist/shims/error-boundary-navigation.d.ts +7 -0
  223. package/dist/shims/error-boundary-navigation.js +19 -1
  224. package/dist/shims/error-boundary.d.ts +10 -10
  225. package/dist/shims/error-boundary.js +26 -7
  226. package/dist/shims/error.d.ts +4 -18
  227. package/dist/shims/error.js +17 -10
  228. package/dist/shims/error.react-server.d.ts +4 -6
  229. package/dist/shims/error.react-server.js +4 -1
  230. package/dist/shims/fetch-cache.d.ts +6 -0
  231. package/dist/shims/fetch-cache.js +23 -4
  232. package/dist/shims/form.js +1 -1
  233. package/dist/shims/headers.d.ts +19 -69
  234. package/dist/shims/headers.js +82 -183
  235. package/dist/shims/internal/app-page-props-cache-key.d.ts +5 -2
  236. package/dist/shims/internal/app-page-props-cache-key.js +24 -15
  237. package/dist/shims/internal/cache-context-accessor.d.ts +16 -0
  238. package/dist/shims/internal/cache-context-accessor.js +15 -0
  239. package/dist/shims/internal/headers-state.d.ts +73 -0
  240. package/dist/shims/internal/headers-state.js +157 -0
  241. package/dist/shims/metadata.js +3 -3
  242. package/dist/shims/navigation-errors.d.ts +6 -0
  243. package/dist/shims/navigation-errors.js +11 -1
  244. package/dist/shims/navigation.d.ts +7 -0
  245. package/dist/shims/navigation.js +16 -6
  246. package/dist/shims/request-context.d.ts +9 -0
  247. package/dist/shims/request-state-types.d.ts +2 -1
  248. package/dist/shims/root-params.js +1 -1
  249. package/dist/shims/server.js +52 -2
  250. package/dist/shims/thenable-params.d.ts +4 -0
  251. package/dist/shims/thenable-params.js +25 -3
  252. package/dist/shims/unified-request-context.d.ts +1 -1
  253. package/dist/shims/unified-request-context.js +1 -0
  254. package/dist/utils/encode-cache-tag.d.ts +1 -0
  255. package/dist/utils/encode-cache-tag.js +11 -1
  256. package/dist/utils/mdx-scan.d.ts +5 -0
  257. package/dist/utils/mdx-scan.js +8 -1
  258. package/dist/utils/regex-safety.d.ts +10 -0
  259. package/dist/utils/regex-safety.js +70 -18
  260. package/dist/utils/vite-cli-invocation.js +8 -1
  261. package/package.json +8 -4
  262. package/dist/server/metadata-route-cache-policy.d.ts +0 -5
  263. package/dist/server/metadata-route-cache-policy.js +0 -6
@@ -1,27 +1,27 @@
1
1
  import { isPromiseLike } from "../utils/promise.js";
2
2
  import { APP_RSC_RENDER_MODE_NAVIGATION } from "./app-rsc-render-mode.js";
3
+ import { matchRoutePattern } from "../routing/route-pattern.js";
3
4
  import { createAppPageRenderDependency, invokeAppComponent, isAppClientReference, isAppRenderSuspension, isReactOwnedAppComponent, renderAfterAppDependencies } from "./app-render-dependency.js";
4
5
  import { AppElementsWire } from "./app-elements-wire.js";
5
6
  import "./app-elements.js";
6
- import { createAppPageRenderIdentity } from "./app-page-render-identity.js";
7
- import { matchRoutePattern } from "../routing/route-pattern.js";
7
+ import { withUseCacheLayoutMarker, withUseCachePageMarker } from "../shims/internal/app-page-props-cache-key.js";
8
8
  import { makeThenableParams } from "../shims/thenable-params.js";
9
- import { withUseCachePageMarker } from "../shims/internal/app-page-props-cache-key.js";
10
9
  import { resolveAppPageBranchParams, resolveAppPageSegmentParams } from "./app-page-params.js";
11
10
  import { resolveAppPageSpecialError } from "./app-page-execution.js";
12
- import { prepareAppPageHead, resolveActiveParallelRouteHeadInputs } from "./app-page-head.js";
13
- import { createAppPageSearchParamsObserver, makeObservedAppPageSearchParamsThenable } from "./app-page-search-params-observation.js";
14
11
  import { APP_PAGE_SEGMENT_KEY, isAppPageRouteGroupSegment, resolveAppPagePatternStateKey, resolveAppPageRouteStateKey } from "./app-page-segment-state.js";
15
12
  import { sanitizeErrorForClient } from "./app-rsc-errors.js";
16
13
  import { resolveAppPageModuleTraceSegment, traceCreateComponentTree, traceGetLayoutOrPageModule } from "./app-page-tracing.js";
17
14
  import { buildAppPageElements, createAppPageSourcePage, createAppPageTreePath, resolveAppPageLoadingModuleAtOrAbove } from "./app-page-route-wiring.js";
15
+ import { collectAppPageSearchParams, hasAppPageDynamicMetadata, prepareAppPageHead, resolveActiveParallelRouteHeadInputs } from "./app-page-head.js";
18
16
  import { resolveHttpAccessFallbackMetadata, resolveHttpAccessFallbackViewport } from "./app-page-http-access-fallback-metadata.js";
19
17
  import { ClientPageRoot } from "../shims/client-page-root.js";
20
18
  import "./app-rsc-route-matching.js";
21
19
  import { resolveAppPageParentHttpAccessBoundary } from "./app-page-boundary.js";
22
20
  import { DEFAULT_GLOBAL_ERROR_MODULE } from "./default-global-error-module.js";
21
+ import { createAppPageRenderIdentity } from "./app-page-render-identity.js";
22
+ import { createAppPageSearchParamsObserver, makeObservedAppPageSearchParamsThenable } from "./app-page-search-params-observation.js";
23
23
  import { shouldServeStreamingMetadata } from "./streaming-metadata.js";
24
- import { Suspense, createElement } from "react";
24
+ import { Suspense, cache, createElement } from "react";
25
25
  //#region src/server/app-page-element-builder.ts
26
26
  function resolveInterceptLayoutParams(branchSegments, layoutSegments, params) {
27
27
  return resolveAppPageBranchParams(branchSegments, layoutSegments.length, params, layoutSegments);
@@ -60,7 +60,7 @@ function traceAppPageInterceptLayoutModules(modules, branchSegments, layoutSegme
60
60
  async function buildPageElements(options) {
61
61
  const { route, params, routePath, displayPathname = routePath, pageRequest, globalErrorModule, rootNotFoundModule, rootForbiddenModule, rootUnauthorizedModule, metadataRoutes } = options;
62
62
  const slotParamOverrides = resolveSlotParamOverrides(route, routePath);
63
- const { opts, searchParams, isRscRequest, mountedSlotsHeader, renderMode = APP_RSC_RENDER_MODE_NAVIGATION, observeMetadataSearchParamsAccess = false, observePageSearchParamsAccess = false, isForceStatic = false, serveStreamingMetadata, isProduction = process.env.NODE_ENV === "production" } = pageRequest;
63
+ const { opts, searchParams, isRscRequest, mountedSlotsHeader, renderMode = APP_RSC_RENDER_MODE_NAVIGATION, observeMetadataSearchParamsAccess = false, observePageSearchParamsAccess = false, isForceStatic = false, serveStreamingMetadata, placeStreamedMetadataInHead = false, isProduction = process.env.NODE_ENV === "production" } = pageRequest;
64
64
  const pageModule = route.page;
65
65
  const isSiblingIntercept = opts?.interceptSlotKey === "__vinext_page_intercept" && !!opts?.interceptPage;
66
66
  const effectivePageModule = isSiblingIntercept ? opts.interceptPage : pageModule;
@@ -140,7 +140,7 @@ async function buildPageElements(options) {
140
140
  } : null;
141
141
  const parallelRoutes = [...primaryParallelRouteHeadInput ? [primaryParallelRouteHeadInput.head] : [], ...activeParallelRouteHeadInputs.map((input) => input.head)];
142
142
  const metadataSearchParamsObserver = observeMetadataSearchParamsAccess ? createAppPageSearchParamsObserver() : void 0;
143
- const preparedHead = prepareAppPageHead({
143
+ const headOptions = {
144
144
  applyFileBasedMetadata: options.applyFileBasedMetadata,
145
145
  basePath: options.basePath ?? "",
146
146
  layoutModules: route.layouts,
@@ -153,15 +153,11 @@ async function buildPageElements(options) {
153
153
  routeSegments: route.routeSegments ?? null,
154
154
  searchParams,
155
155
  searchParamsObserver: metadataSearchParamsObserver
156
- });
157
- const { hasDynamicMetadata, pageSearchParams } = preparedHead;
156
+ };
157
+ const hasDynamicMetadata = hasAppPageDynamicMetadata(headOptions);
158
+ const { pageSearchParams } = collectAppPageSearchParams(searchParams);
158
159
  const streamGeneratedHead = serveStreamingMetadata ?? shouldServeStreamingMetadata(pageRequest.request.headers.get("user-agent") ?? "", options.htmlLimitedBots);
159
- const metadataPlacement = hasDynamicMetadata && streamGeneratedHead ? "body" : "head";
160
- const shouldDeferMetadata = metadataPlacement === "body";
161
- const streamingMetadata = shouldDeferMetadata ? isProduction ? preparedHead.metadata.catch((error) => {
162
- throw sanitizeErrorForClient(error, "production");
163
- }) : preparedHead.metadata : null;
164
- streamingMetadata?.catch(() => null);
160
+ const metadataPlacement = hasDynamicMetadata && streamGeneratedHead && !placeStreamedMetadataInHead ? "body" : "head";
165
161
  const resolveNotFoundFallbackPlanOptions = () => {
166
162
  const routeBoundaryModule = route.notFound;
167
163
  const parentBoundary = resolveAppPageParentHttpAccessBoundary({
@@ -189,8 +185,6 @@ async function buildPageElements(options) {
189
185
  routeSegments: route.routeSegments ?? null
190
186
  };
191
187
  };
192
- let viewportErrorOutlet = null;
193
- let metadataErrorOutlet = null;
194
188
  const resolveMetadataErrorTags = async (error) => {
195
189
  if (resolveAppPageSpecialError(error)?.kind !== "http-access-fallback") return null;
196
190
  return resolveHttpAccessFallbackMetadata({
@@ -201,24 +195,19 @@ async function buildPageElements(options) {
201
195
  routePath: route.pattern
202
196
  }).catch(() => null);
203
197
  };
204
- const [resolvedMetadata, resolvedViewport] = await Promise.all([shouldDeferMetadata ? Promise.resolve(null) : preparedHead.metadata.catch((error) => {
205
- metadataErrorOutlet = Promise.reject(isProduction ? sanitizeErrorForClient(error, "production") : error);
206
- metadataErrorOutlet.catch(() => null);
207
- return resolveMetadataErrorTags(error);
208
- }), preparedHead.viewport.catch(async (error) => {
209
- const specialError = resolveAppPageSpecialError(error);
210
- viewportErrorOutlet = Promise.reject(isProduction ? sanitizeErrorForClient(error, "production") : error);
211
- viewportErrorOutlet.catch(() => null);
212
- return specialError?.kind === "http-access-fallback" ? resolveHttpAccessFallbackViewport(resolveNotFoundFallbackPlanOptions()).catch(() => ({})) : {};
213
- })]);
214
- const streamingMetadataTags = shouldDeferMetadata ? preparedHead.metadata.catch(resolveMetadataErrorTags) : null;
215
- const streamingMetadataOutletInputs = [
216
- streamingMetadata,
217
- metadataErrorOutlet,
218
- viewportErrorOutlet
219
- ].filter((promise) => promise !== null).map((promise) => Promise.resolve(promise));
220
- const streamingMetadataOutlet = streamingMetadataOutletInputs.length > 0 ? Promise.all(streamingMetadataOutletInputs).then(() => null) : null;
221
- streamingMetadataOutlet?.catch(() => null);
198
+ const toClientError = (error) => isProduction ? sanitizeErrorForClient(error, "production") : error;
199
+ const resolveHead = cache(() => {
200
+ const preparedHead = prepareAppPageHead(headOptions);
201
+ const outlet = Promise.all([preparedHead.metadata, preparedHead.viewport]).then(() => null, (error) => {
202
+ throw toClientError(error);
203
+ });
204
+ outlet.catch(() => null);
205
+ return {
206
+ metadata: preparedHead.metadata.catch(resolveMetadataErrorTags),
207
+ outlet,
208
+ viewport: preparedHead.viewport.catch((error) => resolveAppPageSpecialError(error)?.kind === "http-access-fallback" ? resolveHttpAccessFallbackViewport(resolveNotFoundFallbackPlanOptions()).catch(() => ({})) : {})
209
+ };
210
+ });
222
211
  const pageProps = { params: makeThenableParams(effectiveParams) };
223
212
  const pageTreePosition = (sourcePageSegments ?? route.routeSegments ?? []).length;
224
213
  const hasPageLoadingBoundary = resolveAppPageLoadingModuleAtOrAbove(route, pageTreePosition) !== null || isSiblingIntercept && resolveAppPageLoadingModuleAtOrAbove({
@@ -299,7 +288,7 @@ async function buildPageElements(options) {
299
288
  const LayoutComponent = traceGetLayoutOrPageModule(resolveInterceptLayoutTraceSegment(opts?.interceptBranchSegments, interceptLayoutSegments), () => layoutModule.default);
300
289
  if (!LayoutComponent) continue;
301
290
  const interceptLayoutParams = resolveInterceptLayoutParams(opts?.interceptBranchSegments ?? interceptLayoutSegments, interceptLayoutSegments, effectiveParams);
302
- element = createElement(LayoutComponent, { params: makeThenableParams(interceptLayoutParams) }, element);
291
+ element = createElement(LayoutComponent, withUseCacheLayoutMarker(LayoutComponent, { params: makeThenableParams(interceptLayoutParams) }), element);
303
292
  }
304
293
  }
305
294
  return element;
@@ -319,14 +308,10 @@ async function buildPageElements(options) {
319
308
  matchedParams: params,
320
309
  pageRenderDependency,
321
310
  metadataPlacement,
322
- resolvedMetadata,
311
+ resolveHead,
323
312
  resolvedMetadataPathname: routePath,
324
- resolvedViewport,
325
313
  scriptNonce: options.scriptNonce,
326
- streamingMetadata,
327
- streamingMetadataOutlet,
328
314
  streamingMetadataOutletSuspended: streamGeneratedHead,
329
- streamingMetadataTags,
330
315
  renderIdentity,
331
316
  routePath,
332
317
  semanticPageIdentity,
@@ -123,22 +123,25 @@ type ProbeAppPageLayoutsOptions = {
123
123
  /** When provided, enables per-layout static/dynamic classification. */
124
124
  classification?: LayoutClassificationOptions | null;
125
125
  };
126
- type ProbeAppPageComponentOptions = {
127
- awaitAsyncResult: boolean;
128
- onError: (error: unknown) => Promise<Response | null>;
129
- probePage: () => unknown;
130
- runWithSuppressedHookWarning<T>(probe: () => Promise<T>): Promise<T>;
131
- };
132
- type ProbeAppPageThrownErrorOptions = {
133
- probePage: () => unknown;
134
- runWithSuppressedHookWarning<T>(probe: () => Promise<T>): Promise<T>;
135
- };
136
126
  export declare function resolveAppPageSpecialError(error: unknown): AppPageSpecialError | null;
127
+ /**
128
+ * Resolve the special error that rejected an SSR shell. An error from the RSC
129
+ * render reaches the shell decoded from its Flight digest, so whether
130
+ * generateMetadata() threw it comes from the errors the RSC render threw with
131
+ * that digest. When the page threw one too, the page's decides. An error a
132
+ * client component threw during SSR is the page's, since generateMetadata()
133
+ * runs only in the RSC render.
134
+ */
135
+ export declare function resolveAppPageShellSpecialError(error: unknown, renderedSpecialErrors: readonly unknown[]): AppPageSpecialError | null;
136
+ /**
137
+ * The response headers stored with a page's special-error entries: a
138
+ * redirect's own `location`, not the response's, which middleware may have
139
+ * replaced for this request.
140
+ */
141
+ export declare function resolveAppPageSpecialErrorStoredHeaders(specialError: AppPageSpecialError, basePath: string | undefined): Record<string, string> | undefined;
137
142
  export declare function buildAppPageSpecialErrorResponse(options: BuildAppPageSpecialErrorResponseOptions): Promise<Response>;
138
143
  /** See `LayoutFlags` type docblock in app-elements.ts for lifecycle. */
139
144
  export declare function probeAppPageLayouts(options: ProbeAppPageLayoutsOptions): Promise<ProbeAppPageLayoutsResult>;
140
- export declare function probeAppPageComponent(options: ProbeAppPageComponentOptions): Promise<Response | null>;
141
- export declare function probeAppPageThrownError(options: ProbeAppPageThrownErrorOptions): Promise<unknown>;
142
145
  export declare function readAppPageBinaryStream(stream: ReadableStream<Uint8Array>): Promise<ArrayBuffer>;
143
146
  export declare function bufferAppPageBinaryStream(stream: ReadableStream<Uint8Array>): Promise<ReadableStream<Uint8Array>>;
144
147
  export declare function teeAppPageRscStreamForCapture(stream: ReadableStream<Uint8Array>, shouldCapture: boolean): AppPageRscStreamCapture;
@@ -1,9 +1,10 @@
1
1
  import { VINEXT_RSC_REDIRECT_HEADER, VINEXT_RSC_REDIRECT_TYPE_HEADER } from "./headers.js";
2
2
  import { isPromiseLike } from "../utils/promise.js";
3
3
  import { runWithConnectionProbe } from "../shims/headers.js";
4
+ import { isThrownNavigationError } from "../shims/navigation-errors.js";
4
5
  import { VINEXT_RSC_CONTENT_TYPE, applyRscCompatibilityIdHeader, applyRscDeploymentIdHeader, createRscRedirectLocation } from "./app-rsc-cache-busting.js";
5
6
  import { mergeMiddlewareResponseHeaders } from "./middleware-response-headers.js";
6
- import { copyLinkHeaderProvenance, hasFrameworkLinkHeaders, markFrameworkLinkHeaders } from "./app-response-header-provenance.js";
7
+ import { copyLinkHeaderProvenance, getFrameworkLinkHeader, markFrameworkLinkHeaders } from "./app-response-header-provenance.js";
7
8
  import { applyEdgeRuntimeHeader } from "./app-page-response.js";
8
9
  import { parseNextHttpErrorDigest, parseNextRedirectDigest } from "./next-error-digest.js";
9
10
  import { renderSsrErrorMetaTags } from "./app-ssr-error-meta.js";
@@ -43,7 +44,7 @@ function getAppPageStatusText(statusCode) {
43
44
  return statusCode === 403 ? "Forbidden" : statusCode === 401 ? "Unauthorized" : "Not Found";
44
45
  }
45
46
  function mergeAppPageSpecialErrorHeaders(response, middlewareContext) {
46
- const frameworkLink = hasFrameworkLinkHeaders(response.headers) ? response.headers.get("link") : null;
47
+ const frameworkLink = getFrameworkLinkHeader(response.headers);
47
48
  const headers = new Headers(response.headers);
48
49
  if (frameworkLink) headers.delete("link");
49
50
  mergeMiddlewareResponseHeaders(headers, middlewareContext?.headers ?? null);
@@ -77,6 +78,23 @@ function resolveAppPageSpecialError(error) {
77
78
  return null;
78
79
  }
79
80
  /**
81
+ * Resolve the special error that rejected an SSR shell. An error from the RSC
82
+ * render reaches the shell decoded from its Flight digest, so whether
83
+ * generateMetadata() threw it comes from the errors the RSC render threw with
84
+ * that digest. When the page threw one too, the page's decides. An error a
85
+ * client component threw during SSR is the page's, since generateMetadata()
86
+ * runs only in the RSC render.
87
+ */
88
+ function resolveAppPageShellSpecialError(error, renderedSpecialErrors) {
89
+ const specialError = resolveAppPageSpecialError(error);
90
+ if (!specialError || specialError.fromMetadata === true) return specialError;
91
+ if (isThrownNavigationError(error)) return specialError;
92
+ return renderedSpecialErrors.length > 0 && renderedSpecialErrors.every((rendered) => resolveAppPageSpecialError(rendered)?.fromMetadata === true) ? {
93
+ ...specialError,
94
+ fromMetadata: true
95
+ } : specialError;
96
+ }
97
+ /**
80
98
  * Resolves a redirect() target against the request URL and prepends the
81
99
  * configured basePath when the target is an app-internal absolute path.
82
100
  *
@@ -95,6 +113,15 @@ function applyAppPageRedirectBasePath(location, basePath) {
95
113
  return `${basePath}${suffixIndex === -1 ? location : location.slice(0, suffixIndex)}${suffixIndex === -1 ? "" : location.slice(suffixIndex)}`;
96
114
  }
97
115
  /**
116
+ * The response headers stored with a page's special-error entries: a
117
+ * redirect's own `location`, not the response's, which middleware may have
118
+ * replaced for this request.
119
+ */
120
+ function resolveAppPageSpecialErrorStoredHeaders(specialError, basePath) {
121
+ if (specialError.kind !== "redirect") return void 0;
122
+ return { location: applyAppPageRedirectBasePath(specialError.location, basePath) };
123
+ }
124
+ /**
98
125
  * Returns a path-relative form (`/foo?bar`) of an absolute URL when it shares
99
126
  * the request's origin; otherwise returns the URL verbatim. Used so the digest
100
127
  * we embed in the flight payload matches Next.js's convention — the digest
@@ -254,43 +281,6 @@ async function probeLayoutForErrors(options, layoutIndex) {
254
281
  });
255
282
  return outcome.completed ? outcome.result : null;
256
283
  }
257
- async function probeAppPageComponent(options) {
258
- return options.runWithSuppressedHookWarning(async () => {
259
- const outcome = await runWithConnectionProbe(async () => {
260
- try {
261
- const pageResult = options.probePage();
262
- if (isPromiseLike(pageResult)) {
263
- if (options.awaitAsyncResult) await pageResult;
264
- else Promise.resolve(pageResult).catch(() => {});
265
- }
266
- } catch (error) {
267
- return options.onError(error);
268
- }
269
- return null;
270
- });
271
- return outcome.completed ? outcome.result : null;
272
- });
273
- }
274
- async function probeAppPageThrownError(options) {
275
- return options.runWithSuppressedHookWarning(async () => {
276
- const outcome = await runWithConnectionProbe(async () => {
277
- try {
278
- const pageResult = options.probePage();
279
- if (isPromiseLike(pageResult)) await pageResult;
280
- } catch (error) {
281
- return {
282
- error,
283
- thrown: true
284
- };
285
- }
286
- return {
287
- error: null,
288
- thrown: false
289
- };
290
- });
291
- return outcome.completed && outcome.result.thrown ? outcome.result.error : null;
292
- });
293
- }
294
284
  async function readAppPageBinaryStream(stream) {
295
285
  const reader = stream.getReader();
296
286
  const chunks = [];
@@ -335,4 +325,4 @@ function buildAppPageFontLinkHeader(preloads) {
335
325
  return preloads.map((preload) => `<${preload.href}>; rel=preload; as=font; type=${preload.type}; crossorigin`).join(", ");
336
326
  }
337
327
  //#endregion
338
- export { bufferAppPageBinaryStream, buildAppPageFontLinkHeader, buildAppPageSpecialErrorResponse, probeAppPageComponent, probeAppPageLayouts, probeAppPageThrownError, readAppPageBinaryStream, resolveAppPageSpecialError, tagAppPageMetadataError, teeAppPageRscStreamForCapture };
328
+ export { bufferAppPageBinaryStream, buildAppPageFontLinkHeader, buildAppPageSpecialErrorResponse, probeAppPageLayouts, readAppPageBinaryStream, resolveAppPageShellSpecialError, resolveAppPageSpecialError, resolveAppPageSpecialErrorStoredHeaders, tagAppPageMetadataError, teeAppPageRscStreamForCapture };
@@ -121,6 +121,11 @@ export declare function collectAppPageSearchParams(searchParams: URLSearchParams
121
121
  */
122
122
  export declare function resolveOrderedAppPageMetadata<TModule extends AppPageHeadModule>(options: ResolveOrderedAppPageMetadataOptions<TModule>): Promise<Metadata | null>;
123
123
  export declare function resolveAppPageHead<TModule extends AppPageHeadModule>(options: ResolveAppPageHeadOptions<TModule>): Promise<ResolveAppPageHeadResult>;
124
+ /**
125
+ * Whether any segment of the page's head exports `generateMetadata()`. Known
126
+ * from the modules alone, so callers can place the head before resolving it.
127
+ */
128
+ export declare function hasAppPageDynamicMetadata<TModule extends AppPageHeadModule>(options: Pick<ResolveAppPageHeadOptions<TModule>, "layoutModules" | "pageModule" | "parallelRoutes">): boolean;
124
129
  /**
125
130
  * Start metadata and viewport resolution without coupling their completion.
126
131
  *
@@ -294,6 +294,13 @@ async function resolveAppPageHead(options) {
294
294
  };
295
295
  }
296
296
  /**
297
+ * Whether any segment of the page's head exports `generateMetadata()`. Known
298
+ * from the modules alone, so callers can place the head before resolving it.
299
+ */
300
+ function hasAppPageDynamicMetadata(options) {
301
+ return options.layoutModules.some(hasGenerateMetadata) || hasGenerateMetadata(options.pageModule) || (options.parallelRoutes ?? []).some(parallelRouteHasDynamicMetadata);
302
+ }
303
+ /**
297
304
  * Start metadata and viewport resolution without coupling their completion.
298
305
  *
299
306
  * Live document renders can place the metadata promise behind Suspense while
@@ -308,7 +315,6 @@ function prepareAppPageHeadInner(options) {
308
315
  const layoutTreePositions = options.layoutTreePositions ?? [];
309
316
  const layoutInputs = createLayoutInputs(options.layoutModules, layoutTreePositions);
310
317
  const layoutSourcePositions = layoutInputs.map((input) => input.treePosition);
311
- const primaryHasDynamicMetadata = layoutInputs.some((input) => hasGenerateMetadata(input.module)) || hasGenerateMetadata(options.pageModule);
312
318
  const { hasSearchParams, pageSearchParams } = collectAppPageSearchParams(options.searchParams);
313
319
  const layoutMetadataPromise = resolveLayoutMetadata(layoutInputs, options.params, routeSegments);
314
320
  const layoutViewport = resolveLayoutViewport(layoutInputs, options.params, routeSegments);
@@ -334,7 +340,7 @@ function prepareAppPageHeadInner(options) {
334
340
  accumulatedViewport = parallelViewport.resolvedViewport;
335
341
  }
336
342
  const parallelRouteViewportPromise = Promise.all(parallelRouteViewportPromises);
337
- const hasDynamicMetadata = primaryHasDynamicMetadata || parallelRoutes.some(parallelRouteHasDynamicMetadata);
343
+ const hasDynamicMetadata = hasAppPageDynamicMetadata(options);
338
344
  const metadata = Promise.all([
339
345
  layoutMetadataPromise,
340
346
  pageMetadataPromise,
@@ -379,4 +385,4 @@ function prepareAppPageHeadInner(options) {
379
385
  };
380
386
  }
381
387
  //#endregion
382
- export { collectAppPageSearchParams, prepareAppPageHead, resolveActiveParallelRouteHeadInputs, resolveAppPageHead, resolveOrderedAppPageMetadata };
388
+ export { collectAppPageSearchParams, hasAppPageDynamicMetadata, prepareAppPageHead, resolveActiveParallelRouteHeadInputs, resolveAppPageHead, resolveOrderedAppPageMetadata };
@@ -12,124 +12,15 @@ type ProbeReactServerSubtreeOptions = Readonly<{
12
12
  * failures only make static-layout skip fall back to render-and-send.
13
13
  */
14
14
  export declare function probeReactServerSubtree(node: unknown, options?: ProbeReactServerSubtreeOptions): Promise<void>;
15
- /**
16
- * Build a probePage() invocation for the App Router request lifecycle.
17
- *
18
- * The generated RSC entry calls this once per request after route matching to
19
- * eagerly invoke the page component. Surfacing redirect()/notFound() throws
20
- * here lets the probe lifecycle turn them into proper HTTP responses before
21
- * RSC streaming begins (see `probeAppPageBeforeRender`).
22
- *
23
- * The helper exists to keep the generated entry thin (a single delegation
24
- * call) and to make the search-params wiring directly unit-testable. A bug
25
- * here previously slipped through because the entry hand-rolled the call and
26
- * read a non-existent key off `collectAppPageSearchParams`'s return value
27
- * (see https://github.com/cloudflare/vinext/issues/1235).
28
- *
29
- * Returns `null` when the route has no page component (eg. interception-only
30
- * routes), matching the caller contract on `probePage`.
31
- */
32
- export declare function probeAppPage(options: {
33
- pageComponent: unknown;
34
- asyncRouteParams: unknown;
35
- searchParams: URLSearchParams | null | undefined;
36
- }): unknown;
37
- type AppPageProbeModule = Readonly<{
38
- default?: unknown;
39
- }> | null | undefined;
40
- type AppPageProbeSlot = Readonly<{
41
- page?: AppPageProbeModule;
42
- loading?: AppPageProbeModule;
43
- loadings?: readonly AppPageProbeModule[] | null;
44
- loadingTreePositions?: readonly number[] | null;
45
- }> | null | undefined;
46
- type AppPageProbeRoute = Readonly<{
47
- slots?: Readonly<Record<string, AppPageProbeSlot>> | null;
48
- }>;
49
- type AppPageProbeIntercept = Readonly<{
50
- page?: AppPageProbeModule;
51
- interceptLoadings?: readonly AppPageProbeModule[] | null;
52
- matchedParams?: unknown;
53
- /**
54
- * Key of the parallel-route slot this interception overrides. At render
55
- * time the matched route's `slots[slotKey].page` is replaced by the
56
- * interception page (see app-page-element-builder.ts), so the slot's own
57
- * page never renders for this request.
58
- */
59
- slotKey?: string | null;
60
- }> | null | undefined;
61
- /**
62
- * Fan out the per-request page probes for the App Router dispatch lifecycle.
63
- *
64
- * A single request can render more than one page component: the matched page,
65
- * each active parallel-route slot page, and an interception page when one
66
- * matches. Each must be probed so searchParams access anywhere in the rendered
67
- * tree bails the request out of the query-invariant static cache.
68
- *
69
- * Extracted out of the generated RSC entry so the fan-out is directly
70
- * unit-testable and the entry stays codegen glue (see AGENTS.md "Generated
71
- * Entry Modules Should Stay Thin"). Returns a list of resolved promises so the
72
- * caller can `Promise.all` them.
73
- *
74
- * The fan-out is scoped to the page components that render for this request:
75
- *
76
- * - **Interception override:** when an interception matches it replaces the
77
- * page of the slot named by `intercept.slotKey` (the element builder sets
78
- * `overrides[slotKey].pageModule` to the interception page, which wins over
79
- * `slot.page` in `app-page-route-wiring.tsx`). We probe the interception page
80
- * in place of that slot's own page rather than probing both — probing the
81
- * overridden slot page would mark an otherwise-static request dynamic for a
82
- * component that never renders.
83
- * - **Non-overridden slots:** `slot.page?.default` is exactly what renders.
84
- * `app-page-route-wiring.tsx` resolves a slot to `overrideOrPageComponent ??
85
- * defaultComponent`, so whenever a slot has a `page.tsx` that page renders.
86
- * When a slot has only a `default.tsx` (including the soft-nav case at
87
- * `app-page-route-wiring.tsx:741` that skips an already-mounted slot), there
88
- * is no `slot.page?.default`, so `probeAppPage` short-circuits to `null` and
89
- * probes nothing — a no-op, not an over-bail.
90
- *
91
- * Interception only fires for RSC navigations (`resolveAppPageInterceptState`
92
- * returns `kind: "none"` when `!isRscRequest`, app-page-request.ts:324), so the
93
- * interception handling here is gated on `isRscRequest`. For non-RSC (HTML)
94
- * requests the matched route renders normally, so we probe every slot's own
95
- * page and skip the interception probe entirely. The remaining "source-route"
96
- * interception case (where a *different* route renders, app-page-request.ts:342)
97
- * never reaches this probe: `dispatchAppPage` returns the intercepted response
98
- * before calling `probePage`, so by the time this runs any matched interception
99
- * is the current-route override case above.
100
- *
101
- * A `default.tsx` that itself awaits `searchParams` is not probed here, but the
102
- * real render still observes that access and skips the query-invariant cache
103
- * write (the same loading.tsx backstop), so this cannot under-bail.
104
- */
105
- export declare function buildAppPageProbes(options: {
106
- route: AppPageProbeRoute;
107
- pageComponent: unknown;
108
- asyncRouteParams: unknown;
109
- searchParams: URLSearchParams | null | undefined;
110
- intercept?: AppPageProbeIntercept;
111
- /**
112
- * Whether this is an RSC navigation. Interception only fires for RSC
113
- * requests, so the interception probe is ignored when this is false.
114
- */
115
- isRscRequest: boolean;
116
- /** Fallback raw params used when an interception match omits its own. */
117
- matchedParams: unknown;
118
- makeThenableParams: (params: unknown) => unknown;
119
- }): Promise<unknown>[];
120
15
  type ProbeAppPageBeforeRenderResult = {
121
16
  response: Response | null;
122
17
  layoutFlags: LayoutFlags;
123
18
  };
124
19
  type ProbeAppPageBeforeRenderOptions = {
125
- hasLoadingBoundary: boolean;
126
- probePageBeforeRender?: boolean;
127
20
  skipProbes?: boolean;
128
21
  layoutCount: number;
129
22
  probeLayoutAt: (layoutIndex: number) => unknown;
130
- probePage: () => unknown;
131
23
  renderLayoutSpecialError: (specialError: AppPageSpecialError, layoutIndex: number) => Promise<Response>;
132
- renderPageSpecialError: (specialError: AppPageSpecialError) => Promise<Response>;
133
24
  resolveSpecialError: (error: unknown) => AppPageSpecialError | null;
134
25
  runWithSuppressedHookWarning<T>(probe: () => Promise<T>): Promise<T>;
135
26
  /** When provided, enables per-layout static/dynamic classification. */
@@ -1,10 +1,5 @@
1
1
  import { isPromiseLike } from "../utils/promise.js";
2
- import { isNextRouterError } from "../shims/navigation-errors.js";
3
- import "../shims/navigation-server.js";
4
- import { markAppPagePropsForUseCache, withUseCachePageMarker } from "../shims/internal/app-page-props-cache-key.js";
5
- import { probeAppPageComponent, probeAppPageLayouts } from "./app-page-execution.js";
6
- import { collectAppPageSearchParams } from "./app-page-head.js";
7
- import { makeObservedAppPageSearchParamsThenable } from "./app-page-search-params-observation.js";
2
+ import { probeAppPageLayouts } from "./app-page-execution.js";
8
3
  import { Fragment, isValidElement } from "react";
9
4
  //#region src/server/app-page-probe.ts
10
5
  const DEFAULT_SUBTREE_PROBE_MAX_DEPTH = 32;
@@ -123,119 +118,6 @@ async function probeReactServerSubtree(node, options = {}) {
123
118
  };
124
119
  await visit(node, 0);
125
120
  }
126
- async function probeReactServerSubtreeForDynamicUsage(node) {
127
- try {
128
- await probeReactServerSubtree(node);
129
- } catch (error) {
130
- if (isNextRouterError(error)) return;
131
- throw error;
132
- }
133
- }
134
- /**
135
- * Build a probePage() invocation for the App Router request lifecycle.
136
- *
137
- * The generated RSC entry calls this once per request after route matching to
138
- * eagerly invoke the page component. Surfacing redirect()/notFound() throws
139
- * here lets the probe lifecycle turn them into proper HTTP responses before
140
- * RSC streaming begins (see `probeAppPageBeforeRender`).
141
- *
142
- * The helper exists to keep the generated entry thin (a single delegation
143
- * call) and to make the search-params wiring directly unit-testable. A bug
144
- * here previously slipped through because the entry hand-rolled the call and
145
- * read a non-existent key off `collectAppPageSearchParams`'s return value
146
- * (see https://github.com/cloudflare/vinext/issues/1235).
147
- *
148
- * Returns `null` when the route has no page component (eg. interception-only
149
- * routes), matching the caller contract on `probePage`.
150
- */
151
- function probeAppPage(options) {
152
- const { pageComponent, asyncRouteParams, searchParams } = options;
153
- if (typeof pageComponent !== "function") return null;
154
- const { pageSearchParams } = collectAppPageSearchParams(searchParams);
155
- const asyncSearchParams = makeObservedAppPageSearchParamsThenable(pageSearchParams, { observeReactPromiseStatus: true });
156
- const result = pageComponent(markAppPagePropsForUseCache(withUseCachePageMarker(pageComponent, {
157
- params: asyncRouteParams,
158
- searchParams: asyncSearchParams
159
- })));
160
- if (isPromiseLike(result)) return result.then(async (resolved) => {
161
- await probeReactServerSubtreeForDynamicUsage(resolved);
162
- return resolved;
163
- });
164
- if (isValidElement(result) || Array.isArray(result)) return probeReactServerSubtreeForDynamicUsage(result).then(() => result);
165
- return result;
166
- }
167
- /**
168
- * Fan out the per-request page probes for the App Router dispatch lifecycle.
169
- *
170
- * A single request can render more than one page component: the matched page,
171
- * each active parallel-route slot page, and an interception page when one
172
- * matches. Each must be probed so searchParams access anywhere in the rendered
173
- * tree bails the request out of the query-invariant static cache.
174
- *
175
- * Extracted out of the generated RSC entry so the fan-out is directly
176
- * unit-testable and the entry stays codegen glue (see AGENTS.md "Generated
177
- * Entry Modules Should Stay Thin"). Returns a list of resolved promises so the
178
- * caller can `Promise.all` them.
179
- *
180
- * The fan-out is scoped to the page components that render for this request:
181
- *
182
- * - **Interception override:** when an interception matches it replaces the
183
- * page of the slot named by `intercept.slotKey` (the element builder sets
184
- * `overrides[slotKey].pageModule` to the interception page, which wins over
185
- * `slot.page` in `app-page-route-wiring.tsx`). We probe the interception page
186
- * in place of that slot's own page rather than probing both — probing the
187
- * overridden slot page would mark an otherwise-static request dynamic for a
188
- * component that never renders.
189
- * - **Non-overridden slots:** `slot.page?.default` is exactly what renders.
190
- * `app-page-route-wiring.tsx` resolves a slot to `overrideOrPageComponent ??
191
- * defaultComponent`, so whenever a slot has a `page.tsx` that page renders.
192
- * When a slot has only a `default.tsx` (including the soft-nav case at
193
- * `app-page-route-wiring.tsx:741` that skips an already-mounted slot), there
194
- * is no `slot.page?.default`, so `probeAppPage` short-circuits to `null` and
195
- * probes nothing — a no-op, not an over-bail.
196
- *
197
- * Interception only fires for RSC navigations (`resolveAppPageInterceptState`
198
- * returns `kind: "none"` when `!isRscRequest`, app-page-request.ts:324), so the
199
- * interception handling here is gated on `isRscRequest`. For non-RSC (HTML)
200
- * requests the matched route renders normally, so we probe every slot's own
201
- * page and skip the interception probe entirely. The remaining "source-route"
202
- * interception case (where a *different* route renders, app-page-request.ts:342)
203
- * never reaches this probe: `dispatchAppPage` returns the intercepted response
204
- * before calling `probePage`, so by the time this runs any matched interception
205
- * is the current-route override case above.
206
- *
207
- * A `default.tsx` that itself awaits `searchParams` is not probed here, but the
208
- * real render still observes that access and skips the query-invariant cache
209
- * write (the same loading.tsx backstop), so this cannot under-bail.
210
- */
211
- function buildAppPageProbes(options) {
212
- const { route, pageComponent, asyncRouteParams, searchParams, matchedParams } = options;
213
- const intercept = options.isRscRequest ? options.intercept : null;
214
- const probes = [probeAppPage({
215
- pageComponent,
216
- asyncRouteParams,
217
- searchParams
218
- })];
219
- const overriddenSlotKey = intercept?.slotKey ?? null;
220
- for (const [slotKey, slot] of Object.entries(route.slots ?? {})) {
221
- if (overriddenSlotKey !== null && slotKey === overriddenSlotKey) continue;
222
- if (slot?.loading?.default || slot?.loadings?.some((loading) => loading?.default)) continue;
223
- probes.push(probeAppPage({
224
- pageComponent: slot?.page?.default,
225
- asyncRouteParams,
226
- searchParams
227
- }));
228
- }
229
- const interceptedSlot = intercept?.slotKey ? route.slots?.[intercept.slotKey] : null;
230
- const interceptedSlotHasRootLoading = Boolean(interceptedSlot?.loading?.default || interceptedSlot?.loadings?.some((loading, index) => loading?.default && interceptedSlot.loadingTreePositions?.[index] === 0));
231
- const interceptHasLoadingBoundary = Boolean(intercept?.interceptLoadings?.some((loading) => loading?.default) || interceptedSlotHasRootLoading);
232
- if (intercept && !interceptHasLoadingBoundary) probes.push(probeAppPage({
233
- pageComponent: intercept.page?.default,
234
- asyncRouteParams: options.makeThenableParams(intercept.matchedParams ?? matchedParams),
235
- searchParams
236
- }));
237
- return probes.map((probe) => Promise.resolve(probe));
238
- }
239
121
  async function probeAppPageBeforeRender(options) {
240
122
  let layoutFlags = {};
241
123
  if (options.skipProbes) return {
@@ -262,25 +144,10 @@ async function probeAppPageBeforeRender(options) {
262
144
  layoutFlags
263
145
  };
264
146
  }
265
- if (options.hasLoadingBoundary || options.probePageBeforeRender === false) return {
266
- response: null,
267
- layoutFlags
268
- };
269
147
  return {
270
- response: await probeAppPageComponent({
271
- awaitAsyncResult: true,
272
- async onError(pageError) {
273
- const specialError = options.resolveSpecialError(pageError);
274
- if (specialError) return options.renderPageSpecialError(specialError);
275
- return null;
276
- },
277
- probePage: options.probePage,
278
- runWithSuppressedHookWarning(probe) {
279
- return options.runWithSuppressedHookWarning(probe);
280
- }
281
- }),
148
+ response: null,
282
149
  layoutFlags
283
150
  };
284
151
  }
285
152
  //#endregion
286
- export { buildAppPageProbes, probeAppPage, probeAppPageBeforeRender, probeReactServerSubtree };
153
+ export { probeAppPageBeforeRender, probeReactServerSubtree };
@@ -18,6 +18,5 @@ export type AppPageRenderIdentity = {
18
18
  routeId: string;
19
19
  targetMatchedPathname: string;
20
20
  };
21
- export declare function normalizeAppPageInterceptionProofPathname(pathname: string | null): string | null;
22
21
  export declare function createAppPageRenderIdentity(input: AppPageRenderIdentityInput): AppPageRenderIdentity;
23
22
  //#endregion
@@ -38,4 +38,4 @@ function createAppPageRenderIdentity(input) {
38
38
  };
39
39
  }
40
40
  //#endregion
41
- export { createAppPageRenderIdentity, normalizeAppPageInterceptionProofPathname };
41
+ export { createAppPageRenderIdentity };
@@ -1,10 +1,10 @@
1
1
  import { fnv1a64 } from "../utils/hash.js";
2
2
  import { normalizeMountedSlotsHeader } from "./app-mounted-slots-header.js";
3
- import { AppElementsWire, isAppElementsRecord } from "./app-elements-wire.js";
4
- import "./app-elements.js";
5
3
  import { consumeDynamicUsage, consumeInvalidDynamicUsageError, consumeRenderRequestApiUsage } from "../shims/headers.js";
6
4
  import { _consumeRequestScopedCacheLife } from "../shims/cache-request-state.js";
7
5
  import { consumeDynamicFetchObservations } from "../shims/fetch-cache.js";
6
+ import { AppElementsWire, isAppElementsRecord } from "./app-elements-wire.js";
7
+ import "./app-elements.js";
8
8
  import { buildRenderObservation, buildRenderRequestApiObservations } from "./cache-proof.js";
9
9
  //#region src/server/app-page-render-observation.ts
10
10
  function readRootBoundaryId(element) {