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
@@ -23,6 +23,13 @@ type AppPageRequestCacheLife = {
23
23
  stale?: number;
24
24
  };
25
25
  type AppPageRenderableElement = ReactNode | Readonly<Record<string, ReactNode>>;
26
+ type AppPageSpecialErrorRenderOptions = {
27
+ /**
28
+ * The document may be stored in place of the page's, so like a cache
29
+ * candidate's render it must not carry the request's query.
30
+ */
31
+ isCacheCandidate?: boolean;
32
+ };
26
33
  type PreparedAppPageElement = {
27
34
  element: AppPageRenderableElement;
28
35
  response?: never;
@@ -63,7 +70,6 @@ type RenderAppPageLifecycleOptionsBase = {
63
70
  getDraftModeCookieHeader: () => string | null | undefined;
64
71
  handlerStart: number;
65
72
  hasCustomGlobalError?: boolean;
66
- hasLoadingBoundary: boolean;
67
73
  dynamicStaleTimeSeconds?: number;
68
74
  isDynamicError: boolean;
69
75
  isDraftMode: boolean;
@@ -83,9 +89,12 @@ type RenderAppPageLifecycleOptionsBase = {
83
89
  isCacheCandidate?: boolean;
84
90
  isProgressiveActionRender?: boolean;
85
91
  isPrerender?: boolean;
92
+ /** PPR routes send every RSC response as a 200. */
93
+ isRoutePPREnabled?: boolean;
86
94
  isSpeculativePrerender?: boolean;
95
+ /** Wait for the whole document before its first byte, as a prerender does. */
96
+ renderWholeDocument?: boolean;
87
97
  isProduction: boolean;
88
- probePageBeforeRender?: boolean;
89
98
  omitPendingDynamicCacheState?: boolean;
90
99
  isRscRequest: boolean;
91
100
  traceOperation?: "prerender" | "render";
@@ -107,13 +116,12 @@ type RenderAppPageLifecycleOptionsBase = {
107
116
  rootParams?: RootParams;
108
117
  peekRenderObservationState?: () => AppPageRenderObservationState;
109
118
  probeLayoutAt: (layoutIndex: number) => unknown;
110
- probePage: () => unknown;
111
119
  expireSeconds?: number;
112
120
  formState?: ReactFormState | null;
113
121
  revalidateSeconds: number | null;
114
122
  renderErrorBoundaryResponse: (error: unknown, errorOrigin: "rsc" | "ssr") => Promise<Response | null>;
115
123
  renderLayoutSpecialError: (specialError: AppPageSpecialError, layoutIndex: number) => Promise<Response>;
116
- renderPageSpecialError: (specialError: AppPageSpecialError) => Promise<Response>;
124
+ renderPageSpecialError: (specialError: AppPageSpecialError, renderOptions?: AppPageSpecialErrorRenderOptions) => Promise<Response>;
117
125
  renderToReadableStream: (element: ReactNode | AppOutgoingElements, options: {
118
126
  onError: AppPageBoundaryOnError;
119
127
  signal?: AbortSignal;
@@ -150,6 +158,6 @@ type RenderAppPageLifecycleOptions = RenderAppPageLifecycleOptionsBase & ({
150
158
  * its response policy, such as error boundaries and special errors, get the
151
159
  * same header as the normal render.
152
160
  */
153
- export declare function applyIneligibleRouteCachePolicy(response: Response, options: Pick<RenderAppPageLifecycleOptions, "isDraftMode" | "isDynamicError" | "isForceDynamic" | "isForceStatic" | "isProduction" | "isProgressiveActionRender" | "isRscRequest" | "isStaticEligible" | "middlewareContext" | "peekDynamicUsage" | "revalidateSeconds" | "scriptNonce">): Response;
161
+ export declare function applyIneligibleRouteCachePolicy(response: Response, options: Pick<RenderAppPageLifecycleOptions, "dynamicStaleTimeSeconds" | "isDraftMode" | "isDynamicError" | "isForceDynamic" | "isForceStatic" | "isProduction" | "isProgressiveActionRender" | "isRscRequest" | "isStaticEligible" | "middlewareContext" | "peekDynamicUsage" | "revalidateSeconds" | "scriptNonce">, probeCacheLife?: AppPageRequestCacheLife | null): Response;
154
162
  export declare function renderAppPageLifecycle(options: RenderAppPageLifecycleOptions): Promise<Response>;
155
163
  //#endregion
@@ -1,31 +1,36 @@
1
1
  import { preserveFullyBufferedBodyMetadata } from "./fully-buffered-response.js";
2
2
  import { bindRequestContext } from "../shims/unified-request-context.js";
3
- import { VINEXT_RSC_COMPLETION_METADATA_HEADER } from "./headers.js";
3
+ import { getRequestExecutionContext } from "../shims/request-context.js";
4
+ import { VINEXT_DYNAMIC_STALE_TIME_HEADER, VINEXT_RSC_COMPLETION_METADATA_HEADER } from "./headers.js";
4
5
  import { internalServerErrorResponse } from "./http-error-responses.js";
5
6
  import { isRouteCacheabilityEvaluation, recordRouteCacheabilityClientTraceMetadataMarker } from "../shims/cacheability-classification.js";
6
7
  import { NEVER_CACHE_CONTROL, NO_STORE_CACHE_CONTROL, applyCdnResponseHeaders, isCdnResponsePolicyHeader } from "./cache-control.js";
7
- import { createArtifactCompatibilityEnvelope, createArtifactCompatibilityGraphVersion } from "./artifact-compatibility.js";
8
- import { AppElementsWire, isAppElementsRecord } from "./app-elements-wire.js";
9
- import "./app-elements.js";
10
8
  import { resolveClientStaleTimeSeconds } from "../utils/cache-control-metadata.js";
11
- import { isRenderDynamicLatched, peekDynamicUsage } from "../shims/headers.js";
9
+ import { isRenderDynamicLatched, markDynamicUsage } from "../shims/internal/headers-state.js";
10
+ import { peekDynamicUsage, runWithDetachedDynamicUsage } from "../shims/headers.js";
12
11
  import { runWithFetchDedupe } from "../shims/fetch-cache.js";
12
+ import { setCacheStateHeaders } from "./cache-headers.js";
13
13
  import { deferUntilStreamConsumed } from "./defer-until-stream-consumed.js";
14
+ import { createArtifactCompatibilityEnvelope, createArtifactCompatibilityGraphVersion } from "./artifact-compatibility.js";
15
+ import { AppElementsWire, isAppElementsRecord } from "./app-elements-wire.js";
16
+ import "./app-elements.js";
14
17
  import { isBailoutToCSRError } from "../shims/navigation-errors.js";
15
18
  import { mergeMiddlewareResponseHeaders } from "./middleware-response-headers.js";
16
- import { copyLinkHeaderProvenance } from "./app-response-header-provenance.js";
17
- import { applyEdgeRuntimeHeader, buildAppPageHtmlResponse, buildAppPageRscResponse, resolveAppPageHtmlResponsePolicy, resolveAppPageRscResponsePolicy, resolveUncacheableCacheControl } from "./app-page-response.js";
18
- import { buildAppPageFontLinkHeader, readAppPageBinaryStream, resolveAppPageSpecialError, teeAppPageRscStreamForCapture } from "./app-page-execution.js";
19
+ import { applyPrerenderCacheLifeHeader, applyPrerenderCacheTagsHeader } from "./prerender-cache-life-header.js";
20
+ import { copyLinkHeaderProvenance, getFrameworkLinkHeader } from "./app-response-header-provenance.js";
21
+ import { applyClientStaleTimeHeader, applyEdgeRuntimeHeader, buildAppPageHtmlResponse, buildAppPageRscResponse, resolveAppPageHtmlResponsePolicy, resolveAppPageRscResponsePolicy, resolveUncacheableCacheControl } from "./app-page-response.js";
22
+ import { buildAppPageFontLinkHeader, readAppPageBinaryStream, resolveAppPageShellSpecialError, resolveAppPageSpecialError, resolveAppPageSpecialErrorStoredHeaders, teeAppPageRscStreamForCapture } from "./app-page-execution.js";
19
23
  import { probeAppPageBeforeRender } from "./app-page-probe.js";
20
24
  import { hasDigest } from "./app-rsc-errors.js";
21
25
  import { recordAppPageRenderError, traceAppPageRender } from "./app-page-tracing.js";
22
26
  import { traceResponseStartWithCompletion } from "./response-start-tracing.js";
23
27
  import { DEFAULT_CACHE_VARIANT_BUDGET, buildCacheVariantWithRouteBudget, buildRenderObservation, buildRenderRequestApiObservations, createStaticLayoutArtifactReuseDecision } from "./cache-proof.js";
24
28
  import { createAppPageHtmlOutputScope, createAppPageRenderObservation, createAppPageRscOutputScope } from "./app-page-render-observation.js";
29
+ import { buildAppPageLinkHeader, createAppPageFontData, createAppPageRscErrorTracker, createAppPageSsrErrorHandler, renderAppPageHtmlStream, renderAppPageHtmlStreamWithRecovery } from "./app-page-stream.js";
30
+ import { createAppPageRscRenderStatusResolver } from "./app-page-rsc-render-status.js";
25
31
  import { finalizeAppPageCacheabilityEvaluationResponse, finalizeAppPageHtmlCacheResponse, finalizeAppPageRscCacheResponse } from "./app-page-cache-finalizer.js";
26
32
  import { createStaticLayoutClientReuseArtifactCompatibility, createStaticLayoutClientReusePayloadHash, createStaticLayoutClientReuseRouteId } from "./static-layout-client-reuse-proof.js";
27
33
  import { appendRscCompletionMetadata } from "./rsc-completion-metadata.js";
28
- import { buildAppPageLinkHeader, createAppPageFontData, createAppPageRscErrorTracker, createAppPageSsrErrorHandler, renderAppPageHtmlStream, renderAppPageHtmlStreamWithRecovery } from "./app-page-stream.js";
29
34
  import { getStaticLayoutObservationSkipRejection } from "./app-layout-param-observation.js";
30
35
  import { createClientReuseSkipTransportPlan, crossCheckClientReuseManifestEntryWithCache } from "./skip-cache-proof.js";
31
36
  //#region src/server/app-page-render.ts
@@ -69,15 +74,32 @@ function applyRequestCacheLife(options) {
69
74
  * its response policy, such as error boundaries and special errors, get the
70
75
  * same header as the normal render.
71
76
  */
72
- function applyIneligibleRouteCachePolicy(response, options) {
77
+ function applyIneligibleRouteCachePolicy(response, options, probeCacheLife = null) {
73
78
  const cacheControl = resolveEarlyResponseCacheControl(options);
74
- if (!cacheControl) return response;
79
+ const isRscRedirect = options.isRscRequest && response.headers.has("X-Vinext-Rsc-Redirect");
80
+ if (!cacheControl) {
81
+ const staleTimeSeconds = isRscRedirect ? resolveClientStaleTimeSeconds(probeCacheLife) : void 0;
82
+ if (staleTimeSeconds === void 0) return response;
83
+ const stamped = cloneForStamping(response);
84
+ applyClientStaleTimeHeader(stamped.headers, staleTimeSeconds);
85
+ return stamped;
86
+ }
87
+ const dynamicStaleTimeSeconds = isRscRedirect ? options.dynamicStaleTimeSeconds ?? resolveConfiguredDynamicStaleTimeSeconds() : void 0;
75
88
  const middlewarePolicy = [...options.middlewareContext.headers ?? []].filter(([name, value]) => isCdnResponsePolicyHeader(name) && response.headers.get(name) === value);
76
- if (middlewarePolicy.some(([name]) => name === "cache-control")) return response;
89
+ const keepsMiddlewareCacheControl = middlewarePolicy.some(([name]) => name === "cache-control");
90
+ if (keepsMiddlewareCacheControl && dynamicStaleTimeSeconds === void 0) return response;
91
+ const stamped = cloneForStamping(response);
92
+ if (!keepsMiddlewareCacheControl) {
93
+ applyCdnResponseHeaders(stamped.headers, { cacheControl });
94
+ for (const [name, value] of middlewarePolicy) stamped.headers.set(name, value);
95
+ }
96
+ if (dynamicStaleTimeSeconds !== void 0) stamped.headers.set(VINEXT_DYNAMIC_STALE_TIME_HEADER, String(dynamicStaleTimeSeconds));
97
+ return stamped;
98
+ }
99
+ /** Some early responses have immutable headers, so stamp a copy. */
100
+ function cloneForStamping(response) {
77
101
  const stamped = preserveFullyBufferedBodyMetadata(response, new Response(response.body, response));
78
102
  copyLinkHeaderProvenance(response.headers, stamped.headers);
79
- applyCdnResponseHeaders(stamped.headers, { cacheControl });
80
- for (const [name, value] of middlewarePolicy) stamped.headers.set(name, value);
81
103
  return stamped;
82
104
  }
83
105
  /** The known-dynamic branches of the RSC and HTML response policies, in their order. */
@@ -367,32 +389,26 @@ async function renderAppPageLifecycleImpl(options, renderSpan) {
367
389
  }
368
390
  return dynamicUsageObserved;
369
391
  };
370
- const configuredProbePageBeforeRender = options.probePageBeforeRender ?? options.isRscRequest;
371
- const probePageBeforeRender = options.isRscRequest || configuredProbePageBeforeRender && !(options.peekDynamicUsage?.() ?? false);
372
- const preRenderResult = await probeAppPageBeforeRender({
373
- hasLoadingBoundary: options.hasLoadingBoundary,
374
- probePageBeforeRender,
392
+ const probeOutcome = await runWithDetachedDynamicUsage(() => probeAppPageBeforeRender({
375
393
  skipProbes: options.pprFallbackShellSignal !== void 0,
376
394
  layoutCount: options.layoutCount,
377
395
  probeLayoutAt(layoutIndex) {
378
396
  return options.probeLayoutAt(layoutIndex);
379
397
  },
380
- probePage() {
381
- return options.probePage();
382
- },
383
398
  renderLayoutSpecialError(specialError, layoutIndex) {
384
399
  return options.renderLayoutSpecialError(specialError, layoutIndex);
385
400
  },
386
- renderPageSpecialError(specialError) {
387
- return options.renderPageSpecialError(specialError);
388
- },
389
401
  resolveSpecialError: resolveAppPageSpecialError,
390
402
  runWithSuppressedHookWarning(probe) {
391
403
  return options.runWithSuppressedHookWarning(probe);
392
404
  },
393
405
  classification: options.classification
394
- });
395
- if (preRenderResult.response) return applyIneligibleRouteCachePolicy(preRenderResult.response, options);
406
+ }));
407
+ const preRenderResult = probeOutcome.result;
408
+ if (preRenderResult.response) {
409
+ if (probeOutcome.dynamicDetected) markDynamicUsage();
410
+ return applyIneligibleRouteCachePolicy(preRenderResult.response, options, probeOutcome.cacheLife);
411
+ }
396
412
  const layoutFlags = preRenderResult.layoutFlags;
397
413
  const artifactCompatibility = createAppPageArtifactCompatibility(options.element, options.routePattern);
398
414
  const rootBoundaryId = artifactCompatibility?.rootBoundaryId ?? null;
@@ -457,7 +473,7 @@ async function renderAppPageLifecycleImpl(options, renderSpan) {
457
473
  if (options.pprFallbackShellSignal) pprFallbackShellRsc = new Uint8Array(await readAppPageBinaryStream(rscStream));
458
474
  let revalidateSeconds = options.revalidateSeconds;
459
475
  let expireSeconds = options.expireSeconds;
460
- const shouldWaitForAllReady = options.isPrerender === true && options.isSpeculativePrerender !== true;
476
+ const shouldWaitForAllReady = options.isPrerender === true && options.isSpeculativePrerender !== true || options.renderWholeDocument === true;
461
477
  const shouldReadRequestCacheLifeForPrerender = options.isPrerender === true;
462
478
  const mayResolveCacheLifeAfterHeaders = options.isProgressiveActionRender !== true && (revalidateSeconds === null || revalidateSeconds > 0) && !options.isDraftMode && !options.isForceDynamic && !shouldBypassRscCache;
463
479
  const shouldCaptureRscForCacheMetadata = (options.isProduction || options.isPrerender === true) && mayResolveCacheLifeAfterHeaders && options.isStaticEligible;
@@ -473,6 +489,16 @@ async function renderAppPageLifecycleImpl(options, renderSpan) {
473
489
  const capturedRscDataRef = { value: null };
474
490
  if (rscCapture.sideStream && options.isRscRequest) capturedRscDataRef.value = readAppPageBinaryStream(rscCapture.sideStream);
475
491
  if (options.isRscRequest) {
492
+ const resolveRscRenderStatus = options.isProduction && shouldCaptureRscForCacheMetadata && capturedRscDataRef.value !== null && options.isPrerender !== true && options.isRoutePPREnabled !== true ? createAppPageRscRenderStatusResolver({
493
+ basePath: options.basePath,
494
+ capturedRscData: capturedRscDataRef.value,
495
+ getCapturedSpecialError: rscErrorTracker.getCapturedSpecialError,
496
+ getCapturedSpecialErrors: rscErrorTracker.getCapturedSpecialErrors,
497
+ isForceStatic: options.isForceStatic,
498
+ loadSsrHandler: options.loadSsrHandler,
499
+ navigationContext: options.getNavigationContext(),
500
+ rootParams: options.rootParams
501
+ }) : void 0;
476
502
  let requestCacheLifeForPrerender = null;
477
503
  if (shouldWaitForAllReady) await settleCapturedRscRenderForCacheMetadata(capturedRscDataRef.value);
478
504
  if (shouldReadRequestCacheLifeForPrerender) {
@@ -565,6 +591,7 @@ async function renderAppPageLifecycleImpl(options, renderSpan) {
565
591
  mountedSlotsHeader: options.mountedSlotsHeader,
566
592
  omitPendingDynamicCacheState: options.omitPendingDynamicCacheState,
567
593
  renderMode: options.renderMode,
594
+ resolveRscRenderStatus,
568
595
  preserveClientResponseHeaders: rscResponsePolicy.cacheState !== "MISS",
569
596
  expireSeconds,
570
597
  isStaticEligible: options.isStaticEligible,
@@ -590,6 +617,145 @@ async function renderAppPageLifecycleImpl(options, renderSpan) {
590
617
  let requestCacheLifeForPrerender = null;
591
618
  let dynamicUsedDuringHtmlRender = false;
592
619
  let renderEnd;
620
+ let shellSpecialError = null;
621
+ const resolveHtmlCacheWrite = (dynamicUsedDuringRender) => {
622
+ const htmlResponsePolicy = resolveAppPageHtmlResponsePolicy({
623
+ dynamicUsedDuringRender,
624
+ isProgressiveActionRender: options.isProgressiveActionRender === true,
625
+ hasScriptNonce: Boolean(options.scriptNonce),
626
+ isDraftMode: options.isDraftMode,
627
+ isDynamicError: options.isDynamicError,
628
+ isForceDynamic: options.isForceDynamic,
629
+ isForceStatic: options.isForceStatic,
630
+ isProduction: options.isProduction,
631
+ isStaticEligible: options.isStaticEligible,
632
+ expireSeconds,
633
+ revalidateSeconds
634
+ });
635
+ const shouldSpeculativelyWriteCache = options.isProduction && shouldCaptureRscForCacheMetadata && !options.isEdgeRuntime && revalidateSeconds === null && !options.isDynamicError && !options.isForceStatic && !options.scriptNonce && options.isProgressiveActionRender !== true && !dynamicUsedDuringRender;
636
+ return {
637
+ htmlResponsePolicy,
638
+ shouldWriteHtmlCache: htmlResponsePolicy.shouldWriteToCache || shouldSpeculativelyWriteCache
639
+ };
640
+ };
641
+ const finalizeHtmlCacheWrite = (response, write) => finalizeAppPageHtmlCacheResponse(response, {
642
+ bypassInterceptionContextCache: options.bypassInterceptionContextCache,
643
+ capturedDynamicUsageBeforeContextCleanup: write.capturedDynamicUsageBeforeContextCleanup,
644
+ capturedRscDataPromise: capturedRscDataRef.value,
645
+ cleanPathname: options.cleanPathname,
646
+ clientTraceMetadataMarker,
647
+ consumeDynamicUsage: consumeRenderDynamicUsage,
648
+ consumeRenderObservationState,
649
+ createHtmlRenderObservation(input) {
650
+ return createAppPageRenderObservation({
651
+ boundaryOutcome: { kind: "success" },
652
+ cacheability: "public",
653
+ cacheTags: input.cacheTags,
654
+ cleanPathname: options.cleanPathname,
655
+ completeness: "complete",
656
+ output: htmlOutputScope,
657
+ params: options.navigationParams,
658
+ state: input.state
659
+ });
660
+ },
661
+ createRscRenderObservation(input) {
662
+ return createAppPageRenderObservation({
663
+ boundaryOutcome: { kind: "success" },
664
+ cacheability: "public",
665
+ cacheTags: input.cacheTags,
666
+ cleanPathname: options.cleanPathname,
667
+ completeness: "complete",
668
+ output: rscOutputScope,
669
+ params: options.navigationParams,
670
+ state: input.state
671
+ });
672
+ },
673
+ getPageTags() {
674
+ return options.getPageTags();
675
+ },
676
+ getRequestCacheLife() {
677
+ return readRequestCacheLifeForCachePolicy(options);
678
+ },
679
+ headers: write.headers,
680
+ isrDebug: options.isrDebug,
681
+ isrHtmlKey: options.isrHtmlKey,
682
+ isrRscKey: options.isrRscKey,
683
+ isrSet: options.isrSet,
684
+ interceptionContext: options.interceptionContext,
685
+ interceptionId: options.interceptionId,
686
+ omitPendingDynamicCacheState: options.omitPendingDynamicCacheState,
687
+ preserveClientResponseHeaders: !write.htmlResponsePolicy.shouldWriteToCache,
688
+ expireSeconds,
689
+ isStaticEligible: options.isStaticEligible,
690
+ revalidateSeconds: resolveAppPageCacheWriteRevalidateSeconds({
691
+ isDynamicError: options.isDynamicError,
692
+ isForceStatic: options.isForceStatic,
693
+ isStaticEligible: options.isStaticEligible,
694
+ revalidateSeconds
695
+ }),
696
+ linkHeader: write.linkHeader,
697
+ status: write.status,
698
+ waitUntil(cachePromise) {
699
+ options.waitUntil?.(cachePromise);
700
+ }
701
+ });
702
+ const mayStoreShellSpecialError = () => options.isProduction && shouldCaptureRscForCacheMetadata && options.isPrerender !== true && capturedRscDataRef.value !== null && resolveEarlyResponseCacheControl(options) === null;
703
+ const mayPrerenderShellSpecialError = () => options.isPrerender === true && options.isSpeculativePrerender !== true && options.pprFallbackShellSignal === void 0 && shouldCaptureRscForCacheMetadata && capturedRscDataRef.value !== null && resolveEarlyResponseCacheControl(options) === null;
704
+ const readPrerenderCacheMetadata = () => ({
705
+ cacheTags: options.getPageTags(),
706
+ requestCacheLife: readRequestCacheLifeForPrerender(options)
707
+ });
708
+ let prerenderedSpecialError = null;
709
+ const finalizePrerenderedShellSpecialErrorResponse = async (response, specialError, prerendered) => {
710
+ if (response.status !== specialError.statusCode) return applyIneligibleRouteCachePolicy(response, options);
711
+ const body = await response.arrayBuffer();
712
+ const { cacheTags, requestCacheLife } = prerendered.redirectMetadata ?? readPrerenderCacheMetadata();
713
+ ({expireSeconds, revalidateSeconds} = applyRequestCacheLife({
714
+ expireSeconds,
715
+ requestCacheLife,
716
+ revalidateSeconds
717
+ }));
718
+ const { htmlResponsePolicy } = resolveHtmlCacheWrite(prerendered.dynamicUsed || consumeRenderDynamicUsage());
719
+ const headers = new Headers(response.headers);
720
+ if (htmlResponsePolicy.cacheControl && !headers.has("cache-control")) headers.set("Cache-Control", htmlResponsePolicy.cacheControl);
721
+ applyPrerenderCacheLifeHeader(headers, requestCacheLife);
722
+ applyPrerenderCacheTagsHeader(headers, cacheTags);
723
+ getRequestExecutionContext()?.recordPrerenderSpecialError?.({
724
+ headers: resolveAppPageSpecialErrorStoredHeaders(specialError, options.basePath) ?? {},
725
+ status: response.status
726
+ });
727
+ const prerenderResponse = new Response(body, {
728
+ headers,
729
+ status: response.status,
730
+ statusText: response.statusText
731
+ });
732
+ copyLinkHeaderProvenance(response.headers, prerenderResponse.headers);
733
+ return prerenderResponse;
734
+ };
735
+ const finalizeShellSpecialErrorResponse = (response, specialError) => {
736
+ if (response.status !== specialError.statusCode) return applyIneligibleRouteCachePolicy(response, options);
737
+ const dynamicUsedDuringRender = consumeRenderDynamicUsage();
738
+ const { htmlResponsePolicy, shouldWriteHtmlCache } = resolveHtmlCacheWrite(dynamicUsedDuringRender);
739
+ const headers = new Headers(response.headers);
740
+ if (htmlResponsePolicy.cacheControl && !headers.has("cache-control")) headers.set("Cache-Control", htmlResponsePolicy.cacheControl);
741
+ if (htmlResponsePolicy.cacheState) setCacheStateHeaders(headers, htmlResponsePolicy.cacheState);
742
+ const policyResponse = new Response(response.body, {
743
+ headers,
744
+ status: response.status,
745
+ statusText: response.statusText
746
+ });
747
+ copyLinkHeaderProvenance(response.headers, policyResponse.headers);
748
+ if (!shouldWriteHtmlCache) return policyResponse;
749
+ const clientResponse = finalizeHtmlCacheWrite(policyResponse, {
750
+ capturedDynamicUsageBeforeContextCleanup: () => dynamicUsedDuringRender,
751
+ headers: resolveAppPageSpecialErrorStoredHeaders(specialError, options.basePath),
752
+ htmlResponsePolicy,
753
+ linkHeader: getFrameworkLinkHeader(response.headers),
754
+ status: response.status
755
+ });
756
+ copyLinkHeaderProvenance(response.headers, clientResponse.headers);
757
+ return clientResponse;
758
+ };
593
759
  const htmlRender = await renderAppPageHtmlStreamWithRecovery({
594
760
  onShellRendered() {
595
761
  if (!options.isProduction) renderEnd = performance.now();
@@ -656,30 +822,34 @@ async function renderAppPageLifecycleImpl(options, renderSpan) {
656
822
  isStaticGeneration: options.isPrerender === true,
657
823
  isForceStatic: options.isForceStatic,
658
824
  isCacheCandidate: isCacheCandidateHtmlRender,
659
- onSsrError: createAppPageSsrErrorHandler(onSsrError, rscErrorTracker.isCapturedError)
825
+ onSsrError: createAppPageSsrErrorHandler(onSsrError, rscErrorTracker.isCapturedError),
826
+ mirrorNextFlight: () => rscErrorTracker.getCapturedSpecialError() !== null
660
827
  });
661
828
  },
662
- renderSpecialErrorResponse(specialError) {
663
- return options.renderPageSpecialError(specialError);
829
+ async renderSpecialErrorResponse(specialError) {
830
+ shellSpecialError = specialError;
831
+ const mayPrerender = mayPrerenderShellSpecialError();
832
+ if (mayStoreShellSpecialError() || mayPrerender) await settleCapturedRscRenderForCacheMetadata(capturedRscDataRef.value, peekRenderDynamicUsage);
833
+ if (mayPrerender) prerenderedSpecialError = {
834
+ dynamicUsed: consumeRenderDynamicUsage(),
835
+ redirectMetadata: specialError.kind === "redirect" ? readPrerenderCacheMetadata() : null
836
+ };
837
+ return options.renderPageSpecialError(specialError, { isCacheCandidate: isCacheCandidateHtmlRender });
664
838
  },
665
- resolveSpecialError: resolveAppPageSpecialError
839
+ resolveSpecialError(error) {
840
+ return resolveAppPageShellSpecialError(error, rscErrorTracker.getCapturedSpecialErrors(error));
841
+ }
666
842
  });
667
843
  options.onRenderComplete?.(htmlRender.renderComplete);
668
- if (htmlRender.response) return applyIneligibleRouteCachePolicy(htmlRender.response, options);
844
+ if (htmlRender.response) {
845
+ if (shellSpecialError && options.isProduction && options.isPrerender !== true && resolveEarlyResponseCacheControl(options) === null) return finalizeShellSpecialErrorResponse(htmlRender.response, shellSpecialError);
846
+ if (shellSpecialError && prerenderedSpecialError) return finalizePrerenderedShellSpecialErrorResponse(htmlRender.response, shellSpecialError, prerenderedSpecialError);
847
+ return applyIneligibleRouteCachePolicy(htmlRender.response, options);
848
+ }
669
849
  let htmlStream = htmlRender.htmlStream;
670
850
  if (!htmlStream) throw new Error("[vinext] Expected an HTML stream when no fallback response was returned");
671
851
  const linkHeader = buildAppPageLinkHeader(htmlRender.linkHeader, fontLinkHeader, options.reactMaxHeadersLength);
672
852
  if (options.isPrerender === true) await htmlRender.metadataReady;
673
- if (options.hasLoadingBoundary || !probePageBeforeRender) {
674
- const captured = rscErrorTracker.getCapturedSpecialError();
675
- if (captured) {
676
- const specialError = resolveAppPageSpecialError(captured);
677
- if (specialError) {
678
- htmlStream.cancel().catch(() => {});
679
- return applyIneligibleRouteCachePolicy(await options.renderPageSpecialError(specialError), options);
680
- }
681
- }
682
- }
683
853
  let dynamicUsedDuringRender = consumeRenderDynamicUsage();
684
854
  dynamicUsedDuringHtmlRender = dynamicUsedDuringRender;
685
855
  const stopSpeculativeMetadataWaitOnDynamicUsage = options.isSpeculativePrerender === true && shouldReadRequestCacheLifeForPrerender ? () => {
@@ -709,19 +879,7 @@ async function renderAppPageLifecycleImpl(options, renderSpan) {
709
879
  dynamicUsedDuringHtmlRender = dynamicUsedBeforeContextCleanup;
710
880
  options.clearRequestContext();
711
881
  });
712
- const htmlResponsePolicy = resolveAppPageHtmlResponsePolicy({
713
- dynamicUsedDuringRender,
714
- isProgressiveActionRender: options.isProgressiveActionRender === true,
715
- hasScriptNonce: Boolean(options.scriptNonce),
716
- isDraftMode: options.isDraftMode,
717
- isDynamicError: options.isDynamicError,
718
- isForceDynamic: options.isForceDynamic,
719
- isForceStatic: options.isForceStatic,
720
- isProduction: options.isProduction,
721
- isStaticEligible: options.isStaticEligible,
722
- expireSeconds,
723
- revalidateSeconds
724
- });
882
+ const { htmlResponsePolicy, shouldWriteHtmlCache } = resolveHtmlCacheWrite(dynamicUsedDuringRender);
725
883
  const htmlResponseTiming = buildResponseTiming({
726
884
  compileEnd,
727
885
  handlerStart: options.handlerStart,
@@ -746,8 +904,7 @@ async function renderAppPageLifecycleImpl(options, renderSpan) {
746
904
  applyCdnResponseHeaders(response.headers, { cacheControl: NEVER_CACHE_CONTROL });
747
905
  return response;
748
906
  }
749
- const shouldSpeculativelyWriteCache = options.isProduction && shouldCaptureRscForCacheMetadata && !options.isEdgeRuntime && revalidateSeconds === null && !options.isDynamicError && !options.isForceStatic && !options.scriptNonce && options.isProgressiveActionRender !== true && !dynamicUsedDuringRender;
750
- if (htmlResponsePolicy.shouldWriteToCache || shouldSpeculativelyWriteCache) {
907
+ if (shouldWriteHtmlCache) {
751
908
  const isrResponse = buildAppPageHtmlResponse(safeHtmlStream, {
752
909
  cacheTags: options.isPrerender === true ? options.getPageTags() : void 0,
753
910
  draftCookie,
@@ -759,66 +916,10 @@ async function renderAppPageLifecycleImpl(options, renderSpan) {
759
916
  timing: htmlResponseTiming
760
917
  });
761
918
  if (options.isPrerender === true) return isrResponse;
762
- return finalizeAppPageHtmlCacheResponse(isrResponse, {
763
- bypassInterceptionContextCache: options.bypassInterceptionContextCache,
764
- capturedDynamicUsageBeforeContextCleanup() {
765
- return dynamicUsedBeforeContextCleanup;
766
- },
767
- capturedRscDataPromise: capturedRscDataRef.value,
768
- cleanPathname: options.cleanPathname,
769
- clientTraceMetadataMarker,
770
- consumeDynamicUsage: consumeRenderDynamicUsage,
771
- consumeRenderObservationState,
772
- createHtmlRenderObservation(input) {
773
- return createAppPageRenderObservation({
774
- boundaryOutcome: { kind: "success" },
775
- cacheability: "public",
776
- cacheTags: input.cacheTags,
777
- cleanPathname: options.cleanPathname,
778
- completeness: "complete",
779
- output: htmlOutputScope,
780
- params: options.navigationParams,
781
- state: input.state
782
- });
783
- },
784
- createRscRenderObservation(input) {
785
- return createAppPageRenderObservation({
786
- boundaryOutcome: { kind: "success" },
787
- cacheability: "public",
788
- cacheTags: input.cacheTags,
789
- cleanPathname: options.cleanPathname,
790
- completeness: "complete",
791
- output: rscOutputScope,
792
- params: options.navigationParams,
793
- state: input.state
794
- });
795
- },
796
- getPageTags() {
797
- return options.getPageTags();
798
- },
799
- getRequestCacheLife() {
800
- return readRequestCacheLifeForCachePolicy(options);
801
- },
802
- isrDebug: options.isrDebug,
803
- isrHtmlKey: options.isrHtmlKey,
804
- isrRscKey: options.isrRscKey,
805
- isrSet: options.isrSet,
806
- interceptionContext: options.interceptionContext,
807
- interceptionId: options.interceptionId,
808
- omitPendingDynamicCacheState: options.omitPendingDynamicCacheState,
809
- preserveClientResponseHeaders: !htmlResponsePolicy.shouldWriteToCache,
810
- expireSeconds,
811
- isStaticEligible: options.isStaticEligible,
812
- revalidateSeconds: resolveAppPageCacheWriteRevalidateSeconds({
813
- isDynamicError: options.isDynamicError,
814
- isForceStatic: options.isForceStatic,
815
- isStaticEligible: options.isStaticEligible,
816
- revalidateSeconds
817
- }),
818
- linkHeader: linkHeader ?? null,
819
- waitUntil(cachePromise) {
820
- options.waitUntil?.(cachePromise);
821
- }
919
+ return finalizeHtmlCacheWrite(isrResponse, {
920
+ capturedDynamicUsageBeforeContextCleanup: () => dynamicUsedBeforeContextCleanup,
921
+ htmlResponsePolicy,
922
+ linkHeader: linkHeader ?? null
822
923
  });
823
924
  }
824
925
  const response = buildAppPageHtmlResponse(safeHtmlStream, {
@@ -49,7 +49,6 @@ type ResolveAppPageGenerateStaticParamsSourcesOptions = {
49
49
  };
50
50
  type BuildAppPageElementOptions<TElement> = {
51
51
  buildPageElement: () => Promise<TElement>;
52
- probePageSpecialError?: () => Promise<AppPageSpecialError | null>;
53
52
  renderErrorBoundaryPage: (error: unknown) => Promise<Response | null>;
54
53
  renderSpecialError: (specialError: AppPageSpecialError) => Promise<Response>;
55
54
  resolveSpecialError: (error: unknown) => AppPageSpecialError | null;
@@ -333,8 +333,7 @@ async function buildAppPageElement(options) {
333
333
  response: null
334
334
  };
335
335
  } catch (error) {
336
- const buildSpecialError = options.resolveSpecialError(error);
337
- const specialError = (buildSpecialError ? await options.probePageSpecialError?.() : null) ?? buildSpecialError;
336
+ const specialError = options.resolveSpecialError(error);
338
337
  if (specialError) return {
339
338
  element: null,
340
339
  response: await options.renderSpecialError(specialError)
@@ -71,9 +71,10 @@ type BuildAppPageHtmlResponseOptions = {
71
71
  timing?: AppPageResponseTiming;
72
72
  };
73
73
  /**
74
- * Only ever set from a *completed* render's cacheLife (cache replay or
75
- * prerender seed) — `use cache` scopes keep resolving after headers commit,
76
- * so a streaming response can never carry it.
74
+ * Only ever set from a *completed* render's cacheLife (cache replay,
75
+ * prerender seed, or a layout probe's early redirect) — `use cache` scopes
76
+ * keep resolving after headers commit, so a streaming response can never
77
+ * carry it.
77
78
  */
78
79
  export declare function applyClientStaleTimeHeader(headers: Headers, staleTimeSeconds: number | undefined): void;
79
80
  /**
@@ -18,9 +18,10 @@ function applyDynamicStaleTimeHeader(headers, dynamicStaleTimeSeconds) {
18
18
  if (dynamicStaleTimeSeconds !== void 0 && Number.isInteger(dynamicStaleTimeSeconds) && dynamicStaleTimeSeconds >= 0) headers.set(VINEXT_DYNAMIC_STALE_TIME_HEADER, String(dynamicStaleTimeSeconds));
19
19
  }
20
20
  /**
21
- * Only ever set from a *completed* render's cacheLife (cache replay or
22
- * prerender seed) — `use cache` scopes keep resolving after headers commit,
23
- * so a streaming response can never carry it.
21
+ * Only ever set from a *completed* render's cacheLife (cache replay,
22
+ * prerender seed, or a layout probe's early redirect) — `use cache` scopes
23
+ * keep resolving after headers commit, so a streaming response can never
24
+ * carry it.
24
25
  */
25
26
  function applyClientStaleTimeHeader(headers, staleTimeSeconds) {
26
27
  if (staleTimeSeconds === void 0) return;
@@ -140,14 +140,15 @@ type BuildAppPageRouteElementOptions<TModule extends AppPageModule = AppPageModu
140
140
  makeThenableParams: MakeThenableParams;
141
141
  matchedParams: AppPageParams;
142
142
  metadataPlacement?: "body" | "head";
143
- resolvedMetadata: Metadata | null;
143
+ /**
144
+ * Starts resolving the page's head. The head, its streamed body tags and its
145
+ * error outlet each call this while Flight renders them, so callers memoise
146
+ * it per render with React's `cache()`. Without it the head has no metadata.
147
+ */
148
+ resolveHead?: (() => AppPageRouteHead) | null;
144
149
  resolvedMetadataPathname?: string;
145
- resolvedViewport: Viewport;
146
150
  scriptNonce?: string;
147
- streamingMetadata?: Promise<Metadata | null> | null;
148
- streamingMetadataOutlet?: Promise<unknown> | null;
149
151
  streamingMetadataOutletSuspended?: boolean;
150
- streamingMetadataTags?: Promise<Metadata | null> | null;
151
152
  trailingSlash?: boolean;
152
153
  rootForbiddenModule?: TModule | null;
153
154
  rootNotFoundModule?: TModule | null;
@@ -158,6 +159,17 @@ type BuildAppPageRouteElementOptions<TModule extends AppPageModule = AppPageModu
158
159
  searchParams?: unknown;
159
160
  slotOverrides?: Readonly<Record<string, AppPageSlotOverride<TModule>>> | null;
160
161
  };
162
+ /**
163
+ * A page's head as Flight renders it. `metadata` and `viewport` hold the tags
164
+ * to render, including the HTTP access fallback's when resolution failed, so
165
+ * they never reject. `outlet` rejects with the resolution error so the route's
166
+ * boundaries can handle it.
167
+ */
168
+ export type AppPageRouteHead = {
169
+ metadata: Promise<Metadata | null>;
170
+ outlet: Promise<null>;
171
+ viewport: Promise<Viewport>;
172
+ };
161
173
  type MakeThenableParams = (params: AppPageParams, observer?: ThenableParamsObserver) => unknown;
162
174
  type BuildAppPageElementsOptions<TModule extends AppPageModule = AppPageModule, TErrorModule extends AppPageErrorModule = AppPageErrorModule> = BuildAppPageRouteElementOptions<TModule, TErrorModule> & {
163
175
  interception?: AppElementsInterception | null;