vinext 0.1.2 → 0.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (199) hide show
  1. package/dist/build/prerender.d.ts +9 -1
  2. package/dist/build/prerender.js +41 -12
  3. package/dist/build/run-prerender.d.ts +10 -2
  4. package/dist/build/run-prerender.js +15 -1
  5. package/dist/client/app-nav-failure-handler.d.ts +8 -0
  6. package/dist/client/app-nav-failure-handler.js +44 -0
  7. package/dist/client/vinext-next-data.d.ts +18 -1
  8. package/dist/client/window-next.d.ts +2 -1
  9. package/dist/client/window-next.js +12 -1
  10. package/dist/cloudflare/src/cache/cdn-adapter.runtime.js +6 -1
  11. package/dist/config/config-matchers.js +73 -14
  12. package/dist/config/next-config.d.ts +46 -4
  13. package/dist/config/next-config.js +147 -48
  14. package/dist/deploy.d.ts +30 -11
  15. package/dist/deploy.js +180 -99
  16. package/dist/entries/app-browser-entry.d.ts +9 -3
  17. package/dist/entries/app-browser-entry.js +21 -3
  18. package/dist/entries/app-rsc-entry.d.ts +2 -0
  19. package/dist/entries/app-rsc-entry.js +64 -5
  20. package/dist/entries/app-rsc-manifest.js +2 -0
  21. package/dist/entries/app-ssr-entry.js +1 -1
  22. package/dist/entries/pages-client-entry.js +53 -8
  23. package/dist/entries/pages-server-entry.js +41 -5
  24. package/dist/index.js +200 -62
  25. package/dist/plugins/extensionless-dynamic-import.d.ts +6 -0
  26. package/dist/plugins/extensionless-dynamic-import.js +152 -0
  27. package/dist/plugins/optimize-imports.d.ts +2 -1
  28. package/dist/plugins/optimize-imports.js +11 -9
  29. package/dist/plugins/postcss.js +7 -7
  30. package/dist/plugins/typeof-window.d.ts +14 -0
  31. package/dist/plugins/typeof-window.js +150 -0
  32. package/dist/routing/app-route-graph.d.ts +2 -1
  33. package/dist/routing/app-route-graph.js +44 -14
  34. package/dist/routing/file-matcher.d.ts +10 -1
  35. package/dist/routing/file-matcher.js +22 -1
  36. package/dist/routing/pages-router.js +3 -3
  37. package/dist/routing/utils.d.ts +35 -6
  38. package/dist/routing/utils.js +59 -7
  39. package/dist/server/api-handler.d.ts +6 -1
  40. package/dist/server/api-handler.js +21 -15
  41. package/dist/server/app-browser-action-result.d.ts +19 -6
  42. package/dist/server/app-browser-action-result.js +19 -10
  43. package/dist/server/app-browser-entry.js +167 -90
  44. package/dist/server/app-browser-error.d.ts +10 -6
  45. package/dist/server/app-browser-error.js +43 -8
  46. package/dist/server/app-browser-hydration.d.ts +2 -0
  47. package/dist/server/app-browser-hydration.js +1 -0
  48. package/dist/server/app-browser-navigation-controller.d.ts +4 -2
  49. package/dist/server/app-browser-navigation-controller.js +23 -2
  50. package/dist/server/app-browser-server-action-navigation.d.ts +6 -0
  51. package/dist/server/app-browser-server-action-navigation.js +9 -0
  52. package/dist/server/app-browser-stream.js +86 -43
  53. package/dist/server/app-elements-wire.d.ts +6 -1
  54. package/dist/server/app-elements-wire.js +14 -4
  55. package/dist/server/app-elements.d.ts +2 -2
  56. package/dist/server/app-elements.js +2 -2
  57. package/dist/server/app-fallback-renderer.d.ts +1 -0
  58. package/dist/server/app-fallback-renderer.js +3 -1
  59. package/dist/server/app-optimistic-routing.js +2 -2
  60. package/dist/server/app-page-boundary-render.d.ts +1 -0
  61. package/dist/server/app-page-boundary-render.js +27 -14
  62. package/dist/server/app-page-cache-render.d.ts +53 -0
  63. package/dist/server/app-page-cache-render.js +91 -0
  64. package/dist/server/app-page-cache.d.ts +16 -2
  65. package/dist/server/app-page-cache.js +62 -1
  66. package/dist/server/app-page-dispatch.d.ts +26 -0
  67. package/dist/server/app-page-dispatch.js +149 -92
  68. package/dist/server/app-page-element-builder.d.ts +1 -0
  69. package/dist/server/app-page-element-builder.js +5 -2
  70. package/dist/server/app-page-execution.d.ts +6 -1
  71. package/dist/server/app-page-execution.js +21 -1
  72. package/dist/server/app-page-probe.d.ts +1 -0
  73. package/dist/server/app-page-probe.js +4 -0
  74. package/dist/server/app-page-render-observation.d.ts +3 -1
  75. package/dist/server/app-page-render-observation.js +17 -1
  76. package/dist/server/app-page-render.d.ts +12 -1
  77. package/dist/server/app-page-render.js +42 -4
  78. package/dist/server/app-page-request.d.ts +2 -0
  79. package/dist/server/app-page-request.js +2 -1
  80. package/dist/server/app-page-route-wiring.d.ts +3 -1
  81. package/dist/server/app-page-route-wiring.js +14 -5
  82. package/dist/server/app-page-stream.d.ts +15 -3
  83. package/dist/server/app-page-stream.js +11 -5
  84. package/dist/server/app-pages-bridge.d.ts +18 -0
  85. package/dist/server/app-pages-bridge.js +22 -5
  86. package/dist/server/app-ppr-fallback-shell-render.d.ts +17 -0
  87. package/dist/server/app-ppr-fallback-shell-render.js +26 -0
  88. package/dist/server/app-ppr-fallback-shell.d.ts +13 -1
  89. package/dist/server/app-ppr-fallback-shell.js +8 -1
  90. package/dist/server/app-route-handler-dispatch.js +9 -2
  91. package/dist/server/app-route-handler-policy.d.ts +1 -0
  92. package/dist/server/app-router-entry.js +5 -0
  93. package/dist/server/app-rsc-cache-busting.js +2 -0
  94. package/dist/server/app-rsc-handler.d.ts +25 -0
  95. package/dist/server/app-rsc-handler.js +154 -54
  96. package/dist/server/app-rsc-route-matching.d.ts +3 -0
  97. package/dist/server/app-rsc-route-matching.js +2 -0
  98. package/dist/server/app-segment-config.d.ts +9 -1
  99. package/dist/server/app-segment-config.js +12 -3
  100. package/dist/server/app-server-action-execution.d.ts +1 -0
  101. package/dist/server/app-server-action-execution.js +42 -13
  102. package/dist/server/app-ssr-entry.d.ts +2 -0
  103. package/dist/server/app-ssr-entry.js +83 -10
  104. package/dist/server/cache-control.js +4 -0
  105. package/dist/server/dev-server.d.ts +2 -2
  106. package/dist/server/dev-server.js +244 -51
  107. package/dist/server/hybrid-route-priority.d.ts +22 -0
  108. package/dist/server/hybrid-route-priority.js +33 -0
  109. package/dist/server/image-optimization.d.ts +18 -9
  110. package/dist/server/image-optimization.js +37 -23
  111. package/dist/server/implicit-tags.d.ts +2 -1
  112. package/dist/server/implicit-tags.js +4 -1
  113. package/dist/server/navigation-planner.d.ts +133 -30
  114. package/dist/server/navigation-planner.js +114 -0
  115. package/dist/server/navigation-trace.d.ts +8 -1
  116. package/dist/server/navigation-trace.js +8 -1
  117. package/dist/server/pages-api-route.d.ts +6 -0
  118. package/dist/server/pages-api-route.js +13 -2
  119. package/dist/server/pages-asset-tags.d.ts +2 -1
  120. package/dist/server/pages-asset-tags.js +6 -2
  121. package/dist/server/pages-data-route.d.ts +8 -1
  122. package/dist/server/pages-data-route.js +11 -2
  123. package/dist/server/pages-get-initial-props.d.ts +54 -4
  124. package/dist/server/pages-get-initial-props.js +43 -1
  125. package/dist/server/pages-node-compat.js +2 -2
  126. package/dist/server/pages-page-data.d.ts +11 -2
  127. package/dist/server/pages-page-data.js +204 -33
  128. package/dist/server/pages-page-handler.d.ts +4 -2
  129. package/dist/server/pages-page-handler.js +59 -22
  130. package/dist/server/pages-page-response.d.ts +2 -1
  131. package/dist/server/pages-page-response.js +7 -4
  132. package/dist/server/pages-request-pipeline.d.ts +1 -0
  133. package/dist/server/pages-request-pipeline.js +73 -36
  134. package/dist/server/pregenerated-concrete-paths.d.ts +1 -17
  135. package/dist/server/pregenerated-concrete-paths.js +2 -19
  136. package/dist/server/prerender-manifest.d.ts +33 -0
  137. package/dist/server/prerender-manifest.js +54 -0
  138. package/dist/server/prerender-route-params.d.ts +1 -2
  139. package/dist/server/prod-server.js +9 -3
  140. package/dist/server/request-pipeline.d.ts +3 -15
  141. package/dist/server/request-pipeline.js +58 -47
  142. package/dist/server/rsc-stream-hints.d.ts +5 -1
  143. package/dist/server/rsc-stream-hints.js +6 -1
  144. package/dist/server/seed-cache.js +10 -18
  145. package/dist/shims/app-router-scroll-state.d.ts +3 -1
  146. package/dist/shims/app-router-scroll-state.js +14 -2
  147. package/dist/shims/app-router-scroll.d.ts +3 -0
  148. package/dist/shims/app-router-scroll.js +28 -18
  149. package/dist/shims/cache-runtime.js +3 -2
  150. package/dist/shims/cache.d.ts +1 -0
  151. package/dist/shims/cache.js +1 -1
  152. package/dist/shims/cdn-cache.d.ts +5 -5
  153. package/dist/shims/dynamic-preload-chunks.js +6 -4
  154. package/dist/shims/error-boundary.d.ts +2 -0
  155. package/dist/shims/error-boundary.js +7 -0
  156. package/dist/shims/error.js +3 -2
  157. package/dist/shims/error.react-server.d.ts +9 -0
  158. package/dist/shims/error.react-server.js +6 -0
  159. package/dist/shims/fetch-cache.d.ts +3 -1
  160. package/dist/shims/fetch-cache.js +45 -20
  161. package/dist/shims/hash-scroll.js +6 -1
  162. package/dist/shims/headers.js +29 -4
  163. package/dist/shims/internal/als-registry.js +28 -1
  164. package/dist/shims/internal/app-route-detection.js +8 -17
  165. package/dist/shims/internal/hybrid-client-route-owner.d.ts +31 -0
  166. package/dist/shims/internal/hybrid-client-route-owner.js +143 -0
  167. package/dist/shims/internal/navigation-untracked.d.ts +35 -0
  168. package/dist/shims/internal/navigation-untracked.js +55 -0
  169. package/dist/shims/internal/pages-data-target.d.ts +7 -2
  170. package/dist/shims/internal/pages-data-target.js +17 -8
  171. package/dist/shims/internal/pages-router-accessor.d.ts +19 -0
  172. package/dist/shims/internal/pages-router-accessor.js +13 -0
  173. package/dist/shims/internal/router-context.d.ts +2 -1
  174. package/dist/shims/internal/router-context.js +3 -1
  175. package/dist/shims/link.js +12 -5
  176. package/dist/shims/navigation.d.ts +8 -2
  177. package/dist/shims/navigation.js +61 -31
  178. package/dist/shims/ppr-fallback-shell.d.ts +5 -1
  179. package/dist/shims/ppr-fallback-shell.js +28 -7
  180. package/dist/shims/router.d.ts +13 -2
  181. package/dist/shims/router.js +419 -128
  182. package/dist/shims/server.d.ts +16 -1
  183. package/dist/shims/server.js +44 -12
  184. package/dist/shims/unified-request-context.js +1 -0
  185. package/dist/utils/built-asset-url.d.ts +4 -0
  186. package/dist/utils/built-asset-url.js +11 -0
  187. package/dist/utils/commonjs-loader.d.ts +16 -0
  188. package/dist/utils/commonjs-loader.js +100 -0
  189. package/dist/utils/deployment-id.d.ts +8 -0
  190. package/dist/utils/deployment-id.js +22 -0
  191. package/dist/utils/html-limited-bots.d.ts +18 -1
  192. package/dist/utils/html-limited-bots.js +23 -1
  193. package/dist/utils/parse-cookie.d.ts +13 -0
  194. package/dist/utils/parse-cookie.js +52 -0
  195. package/dist/utils/path.d.ts +7 -1
  196. package/dist/utils/path.js +9 -1
  197. package/package.json +2 -2
  198. package/dist/shims/internal/parse-cookie-header.d.ts +0 -14
  199. package/dist/shims/internal/parse-cookie-header.js +0 -30
@@ -24,6 +24,7 @@ type AppPageInterceptOptions<TModule extends AppPageModule = AppPageModule> = {
24
24
  interceptSlotId?: string | null;
25
25
  interceptSlotKey?: string | null;
26
26
  interceptSourceMatchedUrl?: string | null;
27
+ interceptSourcePageSegments?: readonly string[] | null;
27
28
  };
28
29
  type AppPagePageRequest<TModule extends AppPageModule = AppPageModule> = {
29
30
  /** Interception context from current-route navigation (null for direct visits). */opts?: AppPageInterceptOptions<TModule> | null; /** URL search params from the incoming request (null when unavailable). */
@@ -6,7 +6,7 @@ import { createAppPageRenderIdentity } from "./app-page-render-identity.js";
6
6
  import { makeThenableParams } from "../shims/thenable-params.js";
7
7
  import { resolveActiveParallelRouteHeadInputs, resolveAppPageHead } from "./app-page-head.js";
8
8
  import { makeObservedAppPageSearchParamsThenable } from "./app-page-search-params-observation.js";
9
- import { buildAppPageElements, createAppPageTreePath } from "./app-page-route-wiring.js";
9
+ import { buildAppPageElements, createAppPageSourcePage, createAppPageTreePath } from "./app-page-route-wiring.js";
10
10
  import { DEFAULT_GLOBAL_ERROR_MODULE } from "./default-global-error-module.js";
11
11
  import "./app-rsc-route-matching.js";
12
12
  import { shouldServeStreamingMetadata } from "./streaming-metadata.js";
@@ -36,6 +36,7 @@ async function buildPageElements(options) {
36
36
  const effectivePageModule = isSiblingIntercept ? opts.interceptPage : pageModule;
37
37
  const EffectivePageComponent = effectivePageModule?.default;
38
38
  const effectiveParams = isSiblingIntercept ? opts.interceptParams ?? params : params;
39
+ const sourcePageSegments = isSiblingIntercept ? opts?.interceptSourcePageSegments : route.routeSegments;
39
40
  const hasPageModule = !!pageModule;
40
41
  const renderIdentity = createAppPageRenderIdentity({
41
42
  displayPathname,
@@ -56,7 +57,8 @@ async function buildPageElements(options) {
56
57
  interceptionContext: renderIdentity.interceptionContext,
57
58
  layoutIds: noExportLayoutIds,
58
59
  rootLayoutTreePath: noExportRootLayout,
59
- routeId: renderIdentity.routeId
60
+ routeId: renderIdentity.routeId,
61
+ sourcePage: createAppPageSourcePage(sourcePageSegments)
60
62
  }),
61
63
  [renderIdentity.routeId]: createElement("div", null, "Page has no default export")
62
64
  };
@@ -112,6 +114,7 @@ async function buildPageElements(options) {
112
114
  resolvedViewport,
113
115
  renderIdentity,
114
116
  routePath,
117
+ sourcePageSegments,
115
118
  rootNotFoundModule: rootNotFoundModule ?? null,
116
119
  rootForbiddenModule: rootForbiddenModule ?? null,
117
120
  rootUnauthorizedModule: rootUnauthorizedModule ?? null,
@@ -121,13 +121,18 @@ type ProbeAppPageComponentOptions = {
121
121
  probePage: () => unknown;
122
122
  runWithSuppressedHookWarning<T>(probe: () => Promise<T>): Promise<T>;
123
123
  };
124
+ type ProbeAppPageThrownErrorOptions = {
125
+ probePage: () => unknown;
126
+ runWithSuppressedHookWarning<T>(probe: () => Promise<T>): Promise<T>;
127
+ };
124
128
  declare function resolveAppPageSpecialError(error: unknown): AppPageSpecialError | null;
125
129
  declare function buildAppPageSpecialErrorResponse(options: BuildAppPageSpecialErrorResponseOptions): Promise<Response>;
126
130
  /** See `LayoutFlags` type docblock in app-elements.ts for lifecycle. */
127
131
  declare function probeAppPageLayouts(options: ProbeAppPageLayoutsOptions): Promise<ProbeAppPageLayoutsResult>;
128
132
  declare function probeAppPageComponent(options: ProbeAppPageComponentOptions): Promise<Response | null>;
133
+ declare function probeAppPageThrownError(options: ProbeAppPageThrownErrorOptions): Promise<unknown>;
129
134
  declare function readAppPageBinaryStream(stream: ReadableStream<Uint8Array>): Promise<ArrayBuffer>;
130
135
  declare function teeAppPageRscStreamForCapture(stream: ReadableStream<Uint8Array>, shouldCapture: boolean): AppPageRscStreamCapture;
131
136
  declare function buildAppPageFontLinkHeader(preloads: readonly AppPageFontPreload[] | null | undefined): string;
132
137
  //#endregion
133
- export { AppPageFontPreload, AppPageSpecialError, LayoutClassificationOptions, type LayoutFlags, buildAppPageFontLinkHeader, buildAppPageSpecialErrorResponse, probeAppPageComponent, probeAppPageLayouts, readAppPageBinaryStream, resolveAppPageSpecialError, tagAppPageMetadataError, teeAppPageRscStreamForCapture };
138
+ export { AppPageFontPreload, AppPageSpecialError, LayoutClassificationOptions, type LayoutFlags, buildAppPageFontLinkHeader, buildAppPageSpecialErrorResponse, probeAppPageComponent, probeAppPageLayouts, probeAppPageThrownError, readAppPageBinaryStream, resolveAppPageSpecialError, tagAppPageMetadataError, teeAppPageRscStreamForCapture };
@@ -274,6 +274,26 @@ async function probeAppPageComponent(options) {
274
274
  return outcome.completed ? outcome.result : null;
275
275
  });
276
276
  }
277
+ async function probeAppPageThrownError(options) {
278
+ return options.runWithSuppressedHookWarning(async () => {
279
+ const outcome = await runWithConnectionProbe(async () => {
280
+ try {
281
+ const pageResult = options.probePage();
282
+ if (isPromiseLike(pageResult)) await pageResult;
283
+ } catch (error) {
284
+ return {
285
+ error,
286
+ thrown: true
287
+ };
288
+ }
289
+ return {
290
+ error: null,
291
+ thrown: false
292
+ };
293
+ });
294
+ return outcome.completed && outcome.result.thrown ? outcome.result.error : null;
295
+ });
296
+ }
277
297
  async function readAppPageBinaryStream(stream) {
278
298
  const reader = stream.getReader();
279
299
  const chunks = [];
@@ -305,4 +325,4 @@ function buildAppPageFontLinkHeader(preloads) {
305
325
  return preloads.map((preload) => `<${preload.href}>; rel=preload; as=font; type=${preload.type}; crossorigin`).join(", ");
306
326
  }
307
327
  //#endregion
308
- export { buildAppPageFontLinkHeader, buildAppPageSpecialErrorResponse, probeAppPageComponent, probeAppPageLayouts, readAppPageBinaryStream, resolveAppPageSpecialError, tagAppPageMetadataError, teeAppPageRscStreamForCapture };
328
+ export { buildAppPageFontLinkHeader, buildAppPageSpecialErrorResponse, probeAppPageComponent, probeAppPageLayouts, probeAppPageThrownError, readAppPageBinaryStream, resolveAppPageSpecialError, tagAppPageMetadataError, teeAppPageRscStreamForCapture };
@@ -119,6 +119,7 @@ type ProbeAppPageBeforeRenderResult = {
119
119
  };
120
120
  type ProbeAppPageBeforeRenderOptions = {
121
121
  hasLoadingBoundary: boolean;
122
+ skipProbes?: boolean;
122
123
  layoutCount: number;
123
124
  probeLayoutAt: (layoutIndex: number) => unknown;
124
125
  probePage: () => unknown;
@@ -227,6 +227,10 @@ function buildAppPageProbes(options) {
227
227
  }
228
228
  async function probeAppPageBeforeRender(options) {
229
229
  let layoutFlags = {};
230
+ if (options.skipProbes) return {
231
+ response: null,
232
+ layoutFlags
233
+ };
230
234
  if (options.layoutCount > 0) {
231
235
  const layoutProbeResult = await probeAppPageLayouts({
232
236
  layoutCount: options.layoutCount,
@@ -7,6 +7,8 @@ type AppPageRenderObservationState = Readonly<{
7
7
  requestApis: readonly RenderRequestApiKind[];
8
8
  }>;
9
9
  declare function createEmptyAppPageRenderObservationState(): AppPageRenderObservationState;
10
+ declare function consumeAppPageRenderObservationState(): AppPageRenderObservationState;
11
+ declare function discardAppPageRenderState(): void;
10
12
  declare function createAppPageRenderObservation(options: {
11
13
  boundaryOutcome: BoundaryOutcome;
12
14
  cacheTags: readonly string[];
@@ -31,4 +33,4 @@ declare function createAppPageHtmlOutputScope(options: {
31
33
  routePattern: string;
32
34
  }): CacheProofOutputScope;
33
35
  //#endregion
34
- export { AppPageRenderObservationState, createAppPageHtmlOutputScope, createAppPageRenderObservation, createAppPageRscOutputScope, createEmptyAppPageRenderObservationState };
36
+ export { AppPageRenderObservationState, consumeAppPageRenderObservationState, createAppPageHtmlOutputScope, createAppPageRenderObservation, createAppPageRscOutputScope, createEmptyAppPageRenderObservationState, discardAppPageRenderState };
@@ -1,7 +1,10 @@
1
+ import { consumeDynamicUsage, consumeInvalidDynamicUsageError, consumeRenderRequestApiUsage } from "../shims/headers.js";
1
2
  import { fnv1a64 } from "../utils/hash.js";
3
+ import { _consumeRequestScopedCacheLife } from "../shims/cache.js";
2
4
  import { AppElementsWire, isAppElementsRecord } from "./app-elements-wire.js";
3
5
  import { normalizeMountedSlotsHeader } from "./app-mounted-slots-header.js";
4
6
  import "./app-elements.js";
7
+ import { consumeDynamicFetchObservations } from "../shims/fetch-cache.js";
5
8
  import { buildRenderObservation, buildRenderRequestApiObservations } from "./cache-proof.js";
6
9
  //#region src/server/app-page-render-observation.ts
7
10
  function readRootBoundaryId(element) {
@@ -30,6 +33,19 @@ function createEmptyAppPageRenderObservationState() {
30
33
  requestApis: []
31
34
  };
32
35
  }
36
+ function consumeAppPageRenderObservationState() {
37
+ return {
38
+ dynamicFetches: consumeDynamicFetchObservations(),
39
+ requestApis: consumeRenderRequestApiUsage()
40
+ };
41
+ }
42
+ function discardAppPageRenderState() {
43
+ _consumeRequestScopedCacheLife();
44
+ consumeDynamicFetchObservations();
45
+ consumeRenderRequestApiUsage();
46
+ consumeInvalidDynamicUsageError();
47
+ consumeDynamicUsage();
48
+ }
33
49
  function createAppPageRenderObservation(options) {
34
50
  return buildRenderObservation({
35
51
  boundaryOutcome: options.boundaryOutcome,
@@ -63,4 +79,4 @@ function createAppPageHtmlOutputScope(options) {
63
79
  };
64
80
  }
65
81
  //#endregion
66
- export { createAppPageHtmlOutputScope, createAppPageRenderObservation, createAppPageRscOutputScope, createEmptyAppPageRenderObservationState };
82
+ export { consumeAppPageRenderObservationState, createAppPageHtmlOutputScope, createAppPageRenderObservation, createAppPageRscOutputScope, createEmptyAppPageRenderObservationState, discardAppPageRenderState };
@@ -7,8 +7,8 @@ import { AppPageMiddlewareContext } from "./app-page-response.js";
7
7
  import { RootParams } from "../shims/root-params.js";
8
8
  import { AppPageSsrHandler } from "./app-page-stream.js";
9
9
  import { AppLayoutParamAccessTracker } from "./app-layout-param-observation.js";
10
- import { AppRscRenderMode } from "./app-rsc-render-mode.js";
11
10
  import { AppPageRenderObservationState } from "./app-page-render-observation.js";
11
+ import { AppRscRenderMode } from "./app-rsc-render-mode.js";
12
12
  import { ReactNode } from "react";
13
13
  import { ReactFormState } from "react-dom/client";
14
14
 
@@ -49,6 +49,7 @@ type RenderAppPageLifecycleOptions = {
49
49
  peekRequestCacheLife?: () => AppPageRequestCacheLife | null;
50
50
  getDraftModeCookieHeader: () => string | null | undefined;
51
51
  handlerStart: number;
52
+ hasCustomGlobalError?: boolean;
52
53
  hasLoadingBoundary: boolean;
53
54
  dynamicStaleTimeSeconds?: number;
54
55
  isDynamicError: boolean;
@@ -70,6 +71,9 @@ type RenderAppPageLifecycleOptions = {
70
71
  middlewareContext: AppPageMiddlewareContext;
71
72
  navigationParams: Record<string, unknown>;
72
73
  params: Record<string, unknown>;
74
+ pprFallbackShellSignal?: AbortSignal;
75
+ pprFallbackShellReactSignal?: AbortSignal;
76
+ abortPprFallbackShell?: () => void;
73
77
  rootParams?: RootParams;
74
78
  peekRenderObservationState?: () => AppPageRenderObservationState;
75
79
  probeLayoutAt: (layoutIndex: number) => unknown;
@@ -82,7 +86,14 @@ type RenderAppPageLifecycleOptions = {
82
86
  renderPageSpecialError: (specialError: AppPageSpecialError) => Promise<Response>;
83
87
  renderToReadableStream: (element: ReactNode | AppOutgoingElements, options: {
84
88
  onError: AppPageBoundaryOnError;
89
+ signal?: AbortSignal;
85
90
  }) => ReadableStream<Uint8Array>;
91
+ prerenderToReadableStream?: (element: ReactNode | AppOutgoingElements, options: {
92
+ onError: AppPageBoundaryOnError;
93
+ signal?: AbortSignal;
94
+ }) => Promise<{
95
+ prelude: ReadableStream<Uint8Array>;
96
+ }>;
86
97
  routePattern: string;
87
98
  runWithSuppressedHookWarning<T>(probe: () => Promise<T>): Promise<T>;
88
99
  scriptNonce?: string;
@@ -1,4 +1,4 @@
1
- import { NO_STORE_CACHE_CONTROL } from "./cache-control.js";
1
+ import { NEVER_CACHE_CONTROL, NO_STORE_CACHE_CONTROL, applyCdnResponseHeaders } from "./cache-control.js";
2
2
  import { createArtifactCompatibilityEnvelope, createArtifactCompatibilityGraphVersion } from "./artifact-compatibility.js";
3
3
  import { AppElementsWire, isAppElementsRecord } from "./app-elements-wire.js";
4
4
  import "./app-elements.js";
@@ -249,6 +249,7 @@ function wrapRscResponseForDevErrorReporting(response, consumeInvalidDynamicUsag
249
249
  async function renderAppPageLifecycle(options) {
250
250
  const preRenderResult = await probeAppPageBeforeRender({
251
251
  hasLoadingBoundary: options.hasLoadingBoundary,
252
+ skipProbes: options.pprFallbackShellSignal !== void 0,
252
253
  layoutCount: options.layoutCount,
253
254
  probeLayoutAt(layoutIndex) {
254
255
  return options.probeLayoutAt(layoutIndex);
@@ -315,11 +316,31 @@ async function renderAppPageLifecycle(options) {
315
316
  });
316
317
  const compileEnd = options.isProduction ? void 0 : performance.now();
317
318
  const rscErrorTracker = createAppPageRscErrorTracker(options.createRscOnErrorHandler(options.cleanPathname, options.routePattern));
318
- const rscStream = runWithFetchDedupe(() => options.renderToReadableStream(outgoingElement, { onError: rscErrorTracker.onRenderError }));
319
+ let rscStream = await runWithFetchDedupe(async () => {
320
+ if (options.pprFallbackShellSignal && options.prerenderToReadableStream) {
321
+ const reactSignal = options.pprFallbackShellReactSignal ?? options.pprFallbackShellSignal;
322
+ const pendingResult = options.prerenderToReadableStream(outgoingElement, {
323
+ onError: rscErrorTracker.onRenderError,
324
+ signal: reactSignal
325
+ });
326
+ if (options.abortPprFallbackShell) setTimeout(options.abortPprFallbackShell, 0);
327
+ return (await pendingResult).prelude;
328
+ }
329
+ return options.renderToReadableStream(outgoingElement, { onError: rscErrorTracker.onRenderError });
330
+ });
331
+ let pprFallbackShellRsc = null;
332
+ if (options.pprFallbackShellSignal) pprFallbackShellRsc = new Uint8Array(await readAppPageBinaryStream(rscStream));
319
333
  let revalidateSeconds = options.revalidateSeconds;
320
334
  let expireSeconds = options.expireSeconds;
321
335
  const shouldCaptureRscForCacheMetadata = options.isProgressiveActionRender !== true && (options.isProduction || options.isPrerender === true) && (revalidateSeconds === null || revalidateSeconds > 0 && revalidateSeconds !== Infinity) && !options.isDraftMode && !options.isForceDynamic && !shouldBypassRscCacheForSkipTransport;
322
- const rscCapture = teeAppPageRscStreamForCapture(rscStream, shouldCaptureRscForCacheMetadata);
336
+ const createBufferedRscStream = (close) => new ReadableStream({ start(controller) {
337
+ if (pprFallbackShellRsc) controller.enqueue(pprFallbackShellRsc);
338
+ if (close) controller.close();
339
+ } });
340
+ const rscCapture = pprFallbackShellRsc ? {
341
+ ssrStream: createBufferedRscStream(false),
342
+ ...shouldCaptureRscForCacheMetadata ? { sideStream: createBufferedRscStream(true) } : {}
343
+ } : teeAppPageRscStreamForCapture(rscStream, shouldCaptureRscForCacheMetadata);
323
344
  const rscForResponse = rscCapture.ssrStream;
324
345
  const capturedRscDataRef = { value: null };
325
346
  if (rscCapture.sideStream && options.isRscRequest) capturedRscDataRef.value = readAppPageBinaryStream(rscCapture.sideStream);
@@ -415,17 +436,19 @@ async function renderAppPageLifecycle(options) {
415
436
  return renderAppPageHtmlStream({
416
437
  capturedRscDataRef,
417
438
  fontData,
439
+ hasCustomGlobalError: options.hasCustomGlobalError,
418
440
  navigationContext: options.getNavigationContext(),
419
441
  basePath: options.basePath,
420
442
  clientTraceMetadata: options.clientTraceMetadata,
421
443
  reactMaxHeadersLength: options.reactMaxHeadersLength,
422
444
  rootParams: options.rootParams,
445
+ pprFallbackShellSignal: options.pprFallbackShellSignal,
423
446
  formState: options.formState ?? null,
424
447
  rscStream: rscForResponse,
425
448
  scriptNonce: options.scriptNonce,
426
449
  sideStream: rscCapture.sideStream,
427
450
  ssrHandler,
428
- waitForAllReady: options.isPrerender
451
+ waitForAllReady: options.isPrerender === true
429
452
  });
430
453
  },
431
454
  renderSpecialErrorResponse(specialError) {
@@ -482,6 +505,21 @@ async function renderAppPageLifecycle(options) {
482
505
  renderEnd,
483
506
  responseKind: "html"
484
507
  });
508
+ if (htmlRender.shellErrorRecovered) {
509
+ const response = buildAppPageHtmlResponse(safeHtmlStream, {
510
+ draftCookie,
511
+ linkHeader,
512
+ isEdgeRuntime: options.isEdgeRuntime,
513
+ middlewareContext: {
514
+ headers: options.middlewareContext.headers,
515
+ status: 500
516
+ },
517
+ policy: { cacheControl: NEVER_CACHE_CONTROL },
518
+ timing: htmlResponseTiming
519
+ });
520
+ applyCdnResponseHeaders(response.headers, { cacheControl: NEVER_CACHE_CONTROL });
521
+ return response;
522
+ }
485
523
  const shouldSpeculativelyWriteCache = options.isProduction && shouldCaptureRscForCacheMetadata && revalidateSeconds === null && !options.isDynamicError && !options.isForceStatic && !options.scriptNonce && options.isProgressiveActionRender !== true && !dynamicUsedDuringRender;
486
524
  if (htmlResponsePolicy.shouldWriteToCache || shouldSpeculativelyWriteCache) {
487
525
  const isrResponse = buildAppPageHtmlResponse(safeHtmlStream, {
@@ -29,6 +29,7 @@ type ResolveAppPageGenerateStaticParamsSourcesOptions = {
29
29
  };
30
30
  type BuildAppPageElementOptions<TElement> = {
31
31
  buildPageElement: () => Promise<TElement>;
32
+ probePageSpecialError?: () => Promise<AppPageSpecialError | null>;
32
33
  renderErrorBoundaryPage: (error: unknown) => Promise<Response | null>;
33
34
  renderSpecialError: (specialError: AppPageSpecialError) => Promise<Response>;
34
35
  resolveSpecialError: (error: unknown) => AppPageSpecialError | null;
@@ -44,6 +45,7 @@ type AppPageInterceptMatch<TPage = unknown> = {
44
45
  slotId?: string | null;
45
46
  slotKey: string;
46
47
  sourceRouteIndex: number;
48
+ sourcePageSegments?: readonly string[] | null;
47
49
  };
48
50
  type ResolveAppPageInterceptMatchOptions<TRoute, TPage, TInterceptOpts> = {
49
51
  cleanPathname: string;
@@ -180,7 +180,8 @@ async function buildAppPageElement(options) {
180
180
  response: null
181
181
  };
182
182
  } catch (error) {
183
- const specialError = options.resolveSpecialError(error);
183
+ const buildSpecialError = options.resolveSpecialError(error);
184
+ const specialError = (buildSpecialError ? await options.probePageSpecialError?.() : null) ?? buildSpecialError;
184
185
  if (specialError) return {
185
186
  element: null,
186
187
  response: await options.renderSpecialError(specialError)
@@ -130,6 +130,7 @@ type BuildAppPageElementsOptions<TModule extends AppPageModule = AppPageModule,
130
130
  renderIdentity?: AppPageRenderIdentity;
131
131
  renderMode?: AppRscRenderMode;
132
132
  routePath: string;
133
+ sourcePageSegments?: readonly string[] | null;
133
134
  };
134
135
  declare function createAppPageTreePath(routeSegments: readonly string[] | null | undefined, treePosition: number): string;
135
136
  declare function probeAppPageLayoutWithTracking<TModule extends AppPageModule>(options: {
@@ -143,7 +144,8 @@ declare function createAppPageLayoutEntries<TModule extends AppPageModule, TErro
143
144
  forbiddens?: readonly (TModule | null | undefined)[] | null;
144
145
  unauthorizeds?: readonly (TModule | null | undefined)[] | null;
145
146
  }): AppPageLayoutEntry<TModule, TErrorModule>[];
147
+ declare function createAppPageSourcePage(routeSegments: readonly string[] | null | undefined): string;
146
148
  declare function createAppPageRouteBodyMetadata(metadata: Metadata | null, pathname: string, metadataPlacement: "body" | "head", trailingSlash?: boolean): ReactNode;
147
149
  declare function buildAppPageElements<TModule extends AppPageModule, TErrorModule extends AppPageErrorModule>(options: BuildAppPageElementsOptions<TModule, TErrorModule>): AppElements;
148
150
  //#endregion
149
- export { AppPageErrorModule, AppPageModule, AppPageRouteWiringRoute, AppPageSlotOverride, buildAppPageElements, createAppPageLayoutEntries, createAppPageRouteBodyMetadata, createAppPageTreePath, probeAppPageLayoutWithTracking, resolveAppPageChildSegments };
151
+ export { AppPageErrorModule, AppPageModule, AppPageRouteWiringRoute, AppPageSlotOverride, buildAppPageElements, createAppPageLayoutEntries, createAppPageRouteBodyMetadata, createAppPageSourcePage, createAppPageTreePath, probeAppPageLayoutWithTracking, resolveAppPageChildSegments };
@@ -2,7 +2,8 @@ import { createAppRenderDependency, registerAppElementRenderDependencies, render
2
2
  import { APP_STATIC_SIBLINGS_KEY, AppElementsWire, normalizeAppElementsSlotBindings } from "./app-elements-wire.js";
3
3
  import { APP_RSC_RENDER_MODE_PREFETCH_LOADING_SHELL, shouldSuppressLoadingBoundaries } from "./app-rsc-render-mode.js";
4
4
  import { APP_PREFETCH_LOADING_SHELL_MARKER_KEY } from "./app-elements.js";
5
- import { ErrorBoundary, ForbiddenBoundary, NotFoundBoundary, RedirectBoundary, UnauthorizedBoundary } from "../shims/error-boundary.js";
5
+ import DefaultGlobalError from "../shims/default-global-error.js";
6
+ import { ErrorBoundary, ForbiddenBoundary, GlobalErrorBoundary, NotFoundBoundary, RedirectBoundary, UnauthorizedBoundary } from "../shims/error-boundary.js";
6
7
  import { AppRouterScrollTarget } from "../shims/app-router-scroll.js";
7
8
  import { LayoutSegmentProvider } from "../shims/layout-segment-context.js";
8
9
  import { MetadataHead, ViewportHead, renderMetadataToHtml } from "../shims/metadata.js";
@@ -14,6 +15,7 @@ import { Fragment, Suspense } from "react";
14
15
  import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
15
16
  //#region src/server/app-page-route-wiring.tsx
16
17
  const APP_PAGE_LAYOUT_PROBE_CHILD = /* @__PURE__ */ jsx(Fragment, {});
18
+ const DEFAULT_GLOBAL_ERROR_COMPONENT = DefaultGlobalError;
17
19
  function getDefaultExport(module) {
18
20
  return module?.default ?? null;
19
21
  }
@@ -85,6 +87,9 @@ function createAppPageTemplateEntries(route) {
85
87
  };
86
88
  });
87
89
  }
90
+ function createAppPageSourcePage(routeSegments) {
91
+ return `/${[...routeSegments ?? [], "page"].join("/")}`;
92
+ }
88
93
  function createAppPageErrorEntries(route) {
89
94
  return (route.errorPaths ?? route.errors ?? []).flatMap((errorModule, index) => {
90
95
  if (!errorModule) return [];
@@ -206,6 +211,7 @@ function buildAppPageElements(options) {
206
211
  layoutIds: options.route.ids?.layouts ?? layoutEntries.map((entry) => entry.id),
207
212
  rootLayoutTreePath,
208
213
  routeId,
214
+ sourcePage: createAppPageSourcePage(options.sourcePageSegments ?? routeSegments),
209
215
  slotBindings: createAppPageSlotBindings(options.route, layoutEntries, resolveSlotOverride, {
210
216
  interception: renderIdentity?.interception ?? options.interception ?? null,
211
217
  interceptionContext,
@@ -427,9 +433,12 @@ function buildAppPageElements(options) {
427
433
  });
428
434
  }
429
435
  const globalErrorComponent = getErrorBoundaryExport(options.globalErrorModule);
430
- if (globalErrorComponent) routeChildren = /* @__PURE__ */ jsx(ErrorBoundary, {
431
- fallback: globalErrorComponent,
432
- children: routeChildren
436
+ routeChildren = /* @__PURE__ */ jsx(GlobalErrorBoundary, {
437
+ fallback: DEFAULT_GLOBAL_ERROR_COMPONENT,
438
+ children: globalErrorComponent ? /* @__PURE__ */ jsx(ErrorBoundary, {
439
+ fallback: globalErrorComponent,
440
+ children: routeChildren
441
+ }) : routeChildren
433
442
  });
434
443
  elements[routeId] = /* @__PURE__ */ jsxs(Fragment$1, { children: [
435
444
  createAppPageRouteHead(options.resolvedMetadata, options.resolvedViewport, options.resolvedMetadataPathname ?? options.routePath, metadataPlacement, options.trailingSlash),
@@ -440,4 +449,4 @@ function buildAppPageElements(options) {
440
449
  return elements;
441
450
  }
442
451
  //#endregion
443
- export { buildAppPageElements, createAppPageLayoutEntries, createAppPageRouteBodyMetadata, createAppPageTreePath, probeAppPageLayoutWithTracking, resolveAppPageChildSegments };
452
+ export { buildAppPageElements, createAppPageLayoutEntries, createAppPageRouteBodyMetadata, createAppPageSourcePage, createAppPageTreePath, probeAppPageLayoutWithTracking, resolveAppPageChildSegments };
@@ -18,6 +18,7 @@ type AppSsrRenderResult = {
18
18
  htmlStream: ReadableStream<Uint8Array>;
19
19
  metadataReady: Promise<void>;
20
20
  capturedRscData: Promise<ArrayBuffer> | null;
21
+ shellErrorRecovered?: boolean;
21
22
  /**
22
23
  * Preload `Link` header value emitted by React during SSR (via `onHeaders`),
23
24
  * already capped to `reactMaxHeadersLength`. Empty/undefined when React
@@ -62,9 +63,14 @@ type AppPageSsrHandler = {
62
63
  sideStream?: ReadableStream<Uint8Array>;
63
64
  capturedRscDataRef?: {
64
65
  value: Promise<ArrayBuffer> | null;
65
- }; /** When true, wait for the full React tree before emitting bytes. */
66
+ }; /** Abort signal for a build-time PPR fallback-shell static render. */
67
+ pprFallbackShellSignal?: AbortSignal; /** When true, wait for the full React tree before emitting bytes. */
66
68
  waitForAllReady?: boolean; /** Dev-only: original server error to surface in the browser overlay. */
67
69
  initialDevServerError?: unknown;
70
+ /** When true, an SSR-phase-only shell render error resolves to the
71
+ * default `__next_error__` error-document shell (with the original
72
+ * flight payload and bootstrap) instead of rejecting. See handleSsr. */
73
+ fallbackToErrorDocumentOnShellError?: boolean;
68
74
  }) => Promise<ReadableStream<Uint8Array> | AppSsrRenderResult>;
69
75
  };
70
76
  type RenderAppPageHtmlStreamOptions = {
@@ -93,9 +99,14 @@ type RenderAppPageHtmlStreamOptions = {
93
99
  sideStream?: ReadableStream<Uint8Array>; /** Out-parameter filled with accumulated raw RSC bytes after stream consumption. */
94
100
  capturedRscDataRef?: {
95
101
  value: Promise<ArrayBuffer> | null;
96
- }; /** When true, wait for the full React tree before emitting bytes. */
102
+ }; /** Abort signal for a build-time PPR fallback-shell static render. */
103
+ pprFallbackShellSignal?: AbortSignal; /** When true, wait for the full React tree before emitting bytes. */
97
104
  waitForAllReady?: boolean; /** Dev-only: original server error to surface in the browser overlay. */
98
105
  initialDevServerError?: unknown;
106
+ /** True when the app supplies a custom global-error.tsx. Disables the
107
+ * default error-document shell fallback so SSR shell errors keep driving
108
+ * the server-rendered global-error boundary re-render. */
109
+ hasCustomGlobalError?: boolean;
99
110
  };
100
111
  type RenderAppPageHtmlResponseOptions = {
101
112
  clearRequestContext: () => void;
@@ -108,7 +119,8 @@ type AppPageHtmlStreamRecoveryResult = {
108
119
  htmlStream: ReadableStream<Uint8Array> | null;
109
120
  response: Response | null;
110
121
  metadataReady: Promise<void>;
111
- capturedRscData: Promise<ArrayBuffer> | null; /** React-emitted preload `Link` header (already capped). */
122
+ capturedRscData: Promise<ArrayBuffer> | null;
123
+ shellErrorRecovered: boolean; /** React-emitted preload `Link` header (already capped). */
112
124
  linkHeader?: string;
113
125
  };
114
126
  type RenderAppPageHtmlStreamWithRecoveryOptions<TSpecialError> = {
@@ -11,7 +11,8 @@ function normalizeAppSsrRenderResult(raw, fallbackCapturedRscData = null) {
11
11
  return {
12
12
  htmlStream: raw,
13
13
  metadataReady: resolvedMetadataReady,
14
- capturedRscData: fallbackCapturedRscData
14
+ capturedRscData: fallbackCapturedRscData,
15
+ shellErrorRecovered: false
15
16
  };
16
17
  }
17
18
  /**
@@ -62,8 +63,10 @@ async function renderAppPageHtmlStream(options) {
62
63
  rootParams: options.rootParams,
63
64
  sideStream: options.sideStream,
64
65
  capturedRscDataRef: options.capturedRscDataRef,
66
+ pprFallbackShellSignal: options.pprFallbackShellSignal,
65
67
  waitForAllReady: options.waitForAllReady,
66
- initialDevServerError: options.initialDevServerError
68
+ initialDevServerError: options.initialDevServerError,
69
+ fallbackToErrorDocumentOnShellError: options.waitForAllReady !== true && options.hasCustomGlobalError === false
67
70
  };
68
71
  return normalizeAppSsrRenderResult(await options.ssrHandler.handleSsr(options.rscStream, options.navigationContext, options.fontData, ssrOptions), options.capturedRscDataRef?.value ?? null);
69
72
  }
@@ -121,13 +124,14 @@ async function renderAppPageHtmlResponse(options) {
121
124
  }
122
125
  async function renderAppPageHtmlStreamWithRecovery(options) {
123
126
  try {
124
- const { htmlStream, metadataReady, capturedRscData, linkHeader } = normalizeAppSsrRenderResult(await options.renderHtmlStream());
127
+ const { htmlStream, metadataReady, capturedRscData, linkHeader, shellErrorRecovered } = normalizeAppSsrRenderResult(await options.renderHtmlStream());
125
128
  options.onShellRendered?.();
126
129
  return {
127
130
  htmlStream,
128
131
  response: null,
129
132
  metadataReady,
130
133
  capturedRscData,
134
+ shellErrorRecovered: shellErrorRecovered === true,
131
135
  linkHeader
132
136
  };
133
137
  } catch (error) {
@@ -136,14 +140,16 @@ async function renderAppPageHtmlStreamWithRecovery(options) {
136
140
  htmlStream: null,
137
141
  response: await options.renderSpecialErrorResponse(specialError),
138
142
  metadataReady: resolvedMetadataReady,
139
- capturedRscData: null
143
+ capturedRscData: null,
144
+ shellErrorRecovered: false
140
145
  };
141
146
  const boundaryResponse = await options.renderErrorBoundaryResponse(error);
142
147
  if (boundaryResponse) return {
143
148
  htmlStream: null,
144
149
  response: boundaryResponse,
145
150
  metadataReady: resolvedMetadataReady,
146
- capturedRscData: null
151
+ capturedRscData: null,
152
+ shellErrorRecovered: false
147
153
  };
148
154
  throw error;
149
155
  }
@@ -3,8 +3,22 @@ import { AppMiddlewareContext } from "./app-middleware.js";
3
3
  //#region src/server/app-pages-bridge.d.ts
4
4
  type PagesEntry = {
5
5
  handleApiRoute?: (request: Request, url: string) => Promise<Response> | Response;
6
+ matchApiRoute?: (url: string, request: Request) => PagesRouteMatch | null;
7
+ matchPageRoute?: (url: string, request: Request) => PagesRouteMatch | null;
6
8
  renderPage?: (request: Request, url: string, query: Record<string, unknown>, parsedUrl: unknown, middlewareRequestHeaders?: Headers | null) => Promise<Response> | Response;
7
9
  };
10
+ type PagesRouteMatch = {
11
+ route: {
12
+ isDynamic: boolean;
13
+ pattern: string;
14
+ };
15
+ };
16
+ type AppRouteMatch = {
17
+ route: {
18
+ isDynamic: boolean;
19
+ pattern: string;
20
+ };
21
+ };
8
22
  type RenderPagesFallbackDependencies = {
9
23
  loadPagesEntry: () => Promise<PagesEntry> | PagesEntry;
10
24
  buildRequestHeaders: (requestHeaders: Headers, middlewareRequestHeaders: Headers) => Headers | null;
@@ -28,8 +42,12 @@ type RenderPagesFallbackDependencies = {
28
42
  getDraftModeCookieHeader: () => string | null | undefined;
29
43
  };
30
44
  type RenderPagesFallbackOptions = {
45
+ allowRscDocumentFallback?: boolean;
46
+ appRouteMatch?: AppRouteMatch | null;
31
47
  isRscRequest: boolean;
48
+ matchKind?: "dynamic" | "static";
32
49
  middlewareContext: AppMiddlewareContext;
50
+ pathname?: string;
33
51
  request: Request;
34
52
  url: URL;
35
53
  };
@@ -1,11 +1,12 @@
1
+ import { pagesRouteHasPriorityOverAppRoute } from "./hybrid-route-priority.js";
1
2
  //#region src/server/app-pages-bridge.ts
2
3
  /**
3
4
  * Fallback handler to route App Router requests to the Pages Router when no App Router route matches.
4
5
  */
5
6
  async function renderPagesFallback(options, dependencies) {
6
- const { isRscRequest, middlewareContext, request, url } = options;
7
+ const { allowRscDocumentFallback = false, appRouteMatch = null, isRscRequest, matchKind, middlewareContext, pathname = options.url.pathname, request, url } = options;
7
8
  const { loadPagesEntry, buildRequestHeaders, decodePathParams, applyRouteHandlerMiddlewareContext, getDraftModeCookieHeader } = dependencies;
8
- if (isRscRequest) return null;
9
+ if (isRscRequest && !allowRscDocumentFallback) return null;
9
10
  const pagesEntry = await loadPagesEntry();
10
11
  const pagesRequestHeaders = middlewareContext.requestHeaders ? buildRequestHeaders(request.headers, middlewareContext.requestHeaders) : null;
11
12
  let pagesRequest = request;
@@ -20,17 +21,33 @@ async function renderPagesFallback(options, dependencies) {
20
21
  }
21
22
  pagesRequest = new Request(request.url, pagesRequestInit);
22
23
  }
23
- const pagesUrl = decodePathParams(url.pathname) + (url.search || "");
24
- const pagesPathname = url.pathname;
24
+ const queryIndex = pathname.indexOf("?");
25
+ const pagesPathname = queryIndex === -1 ? pathname : pathname.slice(0, queryIndex);
26
+ const pagesSearch = queryIndex === -1 ? url.search || "" : pathname.slice(queryIndex);
27
+ const pagesUrl = decodePathParams(pagesPathname) + pagesSearch;
25
28
  if (pagesPathname.startsWith("/api/") || pagesPathname === "/api") {
26
29
  if (typeof pagesEntry.handleApiRoute !== "function") return null;
30
+ const hasApiMatcher = typeof pagesEntry.matchApiRoute === "function";
31
+ const apiMatch = hasApiMatcher ? pagesEntry.matchApiRoute?.(pagesUrl, pagesRequest) ?? null : null;
32
+ if (hasApiMatcher && apiMatch === null) return null;
33
+ if (apiMatch !== null && matchKind === "static" && apiMatch.route.isDynamic) return null;
34
+ if (apiMatch !== null && matchKind === "dynamic" && !apiMatch.route.isDynamic) return null;
35
+ if (appRouteMatch !== null) {
36
+ if (apiMatch === null || !pagesRouteHasPriorityOverAppRoute(apiMatch.route, appRouteMatch.route)) return null;
37
+ }
27
38
  const pagesApiResponse = await pagesEntry.handleApiRoute(pagesRequest, pagesUrl);
28
39
  const draftCookie = getDraftModeCookieHeader();
29
40
  return applyDraftModeCookie(applyRouteHandlerMiddlewareContext(pagesApiResponse, middlewareContext), draftCookie);
30
41
  }
31
42
  if (typeof pagesEntry.renderPage !== "function") return null;
43
+ const hasPageMatcher = typeof pagesEntry.matchPageRoute === "function";
44
+ const pageMatch = hasPageMatcher ? pagesEntry.matchPageRoute?.(pagesUrl, pagesRequest) ?? null : null;
45
+ if (hasPageMatcher && pageMatch === null) return null;
46
+ if (pageMatch !== null && matchKind === "static" && pageMatch.route.isDynamic) return null;
47
+ if (pageMatch !== null && matchKind === "dynamic" && !pageMatch.route.isDynamic) return null;
48
+ if (appRouteMatch !== null && (pageMatch === null || !pagesRouteHasPriorityOverAppRoute(pageMatch.route, appRouteMatch.route))) return null;
32
49
  const pagesRes = await pagesEntry.renderPage(pagesRequest, pagesUrl, {}, void 0, middlewareContext.requestHeaders);
33
- if (pagesRes.status === 404) return null;
50
+ if (pagesRes.status === 404 && pageMatch === null) return null;
34
51
  return applyDraftModeCookie(pagesRes, getDraftModeCookieHeader());
35
52
  }
36
53
  /**
@@ -0,0 +1,17 @@
1
+ import { PprFallbackShellState } from "../shims/ppr-fallback-shell.js";
2
+ import { ReactNode } from "react";
3
+
4
+ //#region src/server/app-ppr-fallback-shell-render.d.ts
5
+ type AppPageBoundaryOnError = (error: unknown, requestInfo: unknown, errorContext: unknown) => unknown;
6
+ type AppPageRenderableElement = ReactNode | Record<string, ReactNode>;
7
+ declare function warmPprFallbackShellCaches(options: {
8
+ element: AppPageRenderableElement;
9
+ onError: AppPageBoundaryOnError;
10
+ renderToReadableStream: (element: AppPageRenderableElement, options: {
11
+ onError: AppPageBoundaryOnError;
12
+ signal?: AbortSignal;
13
+ }) => ReadableStream<Uint8Array>;
14
+ state: PprFallbackShellState;
15
+ }): Promise<void>;
16
+ //#endregion
17
+ export { warmPprFallbackShellCaches };