vinext 0.0.33 → 0.0.35

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 (70) hide show
  1. package/README.md +1 -0
  2. package/dist/build/prerender.d.ts +9 -4
  3. package/dist/build/prerender.js +27 -9
  4. package/dist/build/prerender.js.map +1 -1
  5. package/dist/build/run-prerender.js +4 -1
  6. package/dist/build/run-prerender.js.map +1 -1
  7. package/dist/config/next-config.d.ts +7 -1
  8. package/dist/config/next-config.js +39 -26
  9. package/dist/config/next-config.js.map +1 -1
  10. package/dist/deploy.js +52 -4
  11. package/dist/deploy.js.map +1 -1
  12. package/dist/entries/app-rsc-entry.js +278 -740
  13. package/dist/entries/app-rsc-entry.js.map +1 -1
  14. package/dist/entries/pages-server-entry.js +60 -134
  15. package/dist/entries/pages-server-entry.js.map +1 -1
  16. package/dist/index.d.ts +9 -1
  17. package/dist/index.js +339 -26
  18. package/dist/index.js.map +1 -1
  19. package/dist/plugins/optimize-imports.js +28 -2
  20. package/dist/plugins/optimize-imports.js.map +1 -1
  21. package/dist/server/app-browser-entry.js +4 -5
  22. package/dist/server/app-browser-entry.js.map +1 -1
  23. package/dist/server/app-page-boundary-render.d.ts +63 -0
  24. package/dist/server/app-page-boundary-render.js +182 -0
  25. package/dist/server/app-page-boundary-render.js.map +1 -0
  26. package/dist/server/app-page-boundary.d.ts +57 -0
  27. package/dist/server/app-page-boundary.js +60 -0
  28. package/dist/server/app-page-boundary.js.map +1 -0
  29. package/dist/server/app-page-execution.d.ts +46 -0
  30. package/dist/server/app-page-execution.js +109 -0
  31. package/dist/server/app-page-execution.js.map +1 -0
  32. package/dist/server/app-page-probe.d.ts +17 -0
  33. package/dist/server/app-page-probe.js +35 -0
  34. package/dist/server/app-page-probe.js.map +1 -0
  35. package/dist/server/app-page-render.d.ts +59 -0
  36. package/dist/server/app-page-render.js +176 -0
  37. package/dist/server/app-page-render.js.map +1 -0
  38. package/dist/server/app-page-request.d.ts +58 -0
  39. package/dist/server/app-page-request.js +79 -0
  40. package/dist/server/app-page-request.js.map +1 -0
  41. package/dist/server/app-page-response.js +1 -1
  42. package/dist/server/app-page-response.js.map +1 -1
  43. package/dist/server/app-page-stream.d.ts +63 -0
  44. package/dist/server/app-page-stream.js +98 -0
  45. package/dist/server/app-page-stream.js.map +1 -0
  46. package/dist/server/app-ssr-stream.js +6 -4
  47. package/dist/server/app-ssr-stream.js.map +1 -1
  48. package/dist/server/pages-page-response.d.ts +54 -0
  49. package/dist/server/pages-page-response.js +140 -0
  50. package/dist/server/pages-page-response.js.map +1 -0
  51. package/dist/server/prod-server.d.ts +13 -1
  52. package/dist/server/prod-server.js +116 -19
  53. package/dist/server/prod-server.js.map +1 -1
  54. package/dist/server/seed-cache.d.ts +44 -0
  55. package/dist/server/seed-cache.js +127 -0
  56. package/dist/server/seed-cache.js.map +1 -0
  57. package/dist/server/worker-utils.d.ts +0 -6
  58. package/dist/server/worker-utils.js +41 -5
  59. package/dist/server/worker-utils.js.map +1 -1
  60. package/dist/shims/error-boundary.js +1 -1
  61. package/dist/shims/font-google-base.js +1 -1
  62. package/dist/shims/font-google-base.js.map +1 -1
  63. package/dist/shims/font-google.d.ts +2 -3
  64. package/dist/shims/font-google.js +2 -3
  65. package/dist/shims/image.js +4 -2
  66. package/dist/shims/image.js.map +1 -1
  67. package/package.json +1 -1
  68. package/dist/shims/font-google.generated.d.ts +0 -1929
  69. package/dist/shims/font-google.generated.js +0 -1929
  70. package/dist/shims/font-google.generated.js.map +0 -1
@@ -21,7 +21,10 @@ const appRouteHandlerPolicyPath = fileURLToPath(new URL("../server/app-route-han
21
21
  const appRouteHandlerExecutionPath = fileURLToPath(new URL("../server/app-route-handler-execution.js", import.meta.url)).replace(/\\/g, "/");
22
22
  const appRouteHandlerCachePath = fileURLToPath(new URL("../server/app-route-handler-cache.js", import.meta.url)).replace(/\\/g, "/");
23
23
  const appPageCachePath = fileURLToPath(new URL("../server/app-page-cache.js", import.meta.url)).replace(/\\/g, "/");
24
- const appPageResponsePath = fileURLToPath(new URL("../server/app-page-response.js", import.meta.url)).replace(/\\/g, "/");
24
+ const appPageExecutionPath = fileURLToPath(new URL("../server/app-page-execution.js", import.meta.url)).replace(/\\/g, "/");
25
+ const appPageBoundaryRenderPath = fileURLToPath(new URL("../server/app-page-boundary-render.js", import.meta.url)).replace(/\\/g, "/");
26
+ const appPageRenderPath = fileURLToPath(new URL("../server/app-page-render.js", import.meta.url)).replace(/\\/g, "/");
27
+ const appPageRequestPath = fileURLToPath(new URL("../server/app-page-request.js", import.meta.url)).replace(/\\/g, "/");
25
28
  const appRouteHandlerResponsePath = fileURLToPath(new URL("../server/app-route-handler-response.js", import.meta.url)).replace(/\\/g, "/");
26
29
  const routeTriePath = fileURLToPath(new URL("../routing/route-trie.js", import.meta.url)).replace(/\\/g, "/");
27
30
  /**
@@ -179,7 +182,7 @@ import { AsyncLocalStorage } from "node:async_hooks";
179
182
  // Flight lines are newline-delimited, so we buffer partial lines across chunks
180
183
  // to guarantee the regex never sees a split hint.
181
184
  function renderToReadableStream(model, options) {
182
- const _hlFixRe = /(\\d+:HL\\[.*?),"stylesheet"(\\]|,)/g;
185
+ const _hlFixRe = /(\\d*:HL\\[.*?),"stylesheet"(\\]|,)/g;
183
186
  const stream = _renderToReadableStream(model, options);
184
187
  const decoder = new TextDecoder();
185
188
  const encoder = new TextEncoder();
@@ -226,17 +229,26 @@ import {
226
229
  executeAppRouteHandler as __executeAppRouteHandler,
227
230
  } from ${JSON.stringify(appRouteHandlerExecutionPath)};
228
231
  import { readAppRouteHandlerCacheResponse as __readAppRouteHandlerCacheResponse } from ${JSON.stringify(appRouteHandlerCachePath)};
232
+ import { readAppPageCacheResponse as __readAppPageCacheResponse } from ${JSON.stringify(appPageCachePath)};
229
233
  import {
230
- finalizeAppPageHtmlCacheResponse as __finalizeAppPageHtmlCacheResponse,
231
- readAppPageCacheResponse as __readAppPageCacheResponse,
232
- scheduleAppPageRscCacheWrite as __scheduleAppPageRscCacheWrite,
233
- } from ${JSON.stringify(appPageCachePath)};
234
+ buildAppPageFontLinkHeader as __buildAppPageFontLinkHeader,
235
+ buildAppPageSpecialErrorResponse as __buildAppPageSpecialErrorResponse,
236
+ readAppPageTextStream as __readAppPageTextStream,
237
+ resolveAppPageSpecialError as __resolveAppPageSpecialError,
238
+ teeAppPageRscStreamForCapture as __teeAppPageRscStreamForCapture,
239
+ } from ${JSON.stringify(appPageExecutionPath)};
234
240
  import {
235
- buildAppPageHtmlResponse as __buildAppPageHtmlResponse,
236
- buildAppPageRscResponse as __buildAppPageRscResponse,
237
- resolveAppPageHtmlResponsePolicy as __resolveAppPageHtmlResponsePolicy,
238
- resolveAppPageRscResponsePolicy as __resolveAppPageRscResponsePolicy,
239
- } from ${JSON.stringify(appPageResponsePath)};
241
+ renderAppPageErrorBoundary as __renderAppPageErrorBoundary,
242
+ renderAppPageHttpAccessFallback as __renderAppPageHttpAccessFallback,
243
+ } from ${JSON.stringify(appPageBoundaryRenderPath)};
244
+ import {
245
+ renderAppPageLifecycle as __renderAppPageLifecycle,
246
+ } from ${JSON.stringify(appPageRenderPath)};
247
+ import {
248
+ buildAppPageElement as __buildAppPageElement,
249
+ resolveAppPageIntercept as __resolveAppPageIntercept,
250
+ validateAppPageDynamicParams as __validateAppPageDynamicParams,
251
+ } from ${JSON.stringify(appPageRequestPath)};
240
252
  import {
241
253
  applyRouteHandlerMiddlewareContext as __applyRouteHandlerMiddlewareContext,
242
254
  } from ${JSON.stringify(appRouteHandlerResponsePath)};
@@ -445,15 +457,8 @@ function __errorDigest(str) {
445
457
  // unchanged since their digests are used for client-side routing.
446
458
  function __sanitizeErrorForClient(error) {
447
459
  // Navigation errors must pass through with their digest intact
448
- if (error && typeof error === "object" && "digest" in error) {
449
- const digest = String(error.digest);
450
- if (
451
- digest.startsWith("NEXT_REDIRECT;") ||
452
- digest === "NEXT_NOT_FOUND" ||
453
- digest.startsWith("NEXT_HTTP_ERROR_FALLBACK;")
454
- ) {
455
- return error;
456
- }
460
+ if (__resolveAppPageSpecialError(error)) {
461
+ return error;
457
462
  }
458
463
  // In development, pass through the original error for debugging
459
464
  if (process.env.NODE_ENV !== "production") {
@@ -611,139 +616,37 @@ const rootLayouts = [${rootLayoutVars.join(", ")}];
611
616
  * @param opts.layouts - Override the layouts to wrap with (for layout-level notFound, excludes the throwing layout)
612
617
  */
613
618
  async function renderHTTPAccessFallbackPage(route, statusCode, isRscRequest, request, opts) {
614
- // Determine which boundary component to use based on status code
615
- let BoundaryComponent = opts?.boundaryComponent ?? null;
616
- if (!BoundaryComponent) {
617
- let boundaryModule;
618
- if (statusCode === 403) {
619
- boundaryModule = route?.forbidden ?? rootForbiddenModule;
620
- } else if (statusCode === 401) {
621
- boundaryModule = route?.unauthorized ?? rootUnauthorizedModule;
622
- } else {
623
- boundaryModule = route?.notFound ?? rootNotFoundModule;
624
- }
625
- BoundaryComponent = boundaryModule?.default ?? null;
626
- }
627
- const layouts = opts?.layouts ?? route?.layouts ?? rootLayouts;
628
- if (!BoundaryComponent) return null;
629
-
630
- // Resolve metadata and viewport from parent layouts so that not-found/error
631
- // pages inherit title, description, OG tags etc. — matching Next.js behavior.
632
- // Build the serial parent chain for layout metadata (same as buildPageElement).
633
- const _filteredLayouts = layouts.filter(Boolean);
634
- const _fallbackParams = opts?.matchedParams ?? route?.params ?? {};
635
- const _layoutMetaPromises = [];
636
- let _accumulatedMeta = Promise.resolve({});
637
- for (let _i = 0; _i < _filteredLayouts.length; _i++) {
638
- const _parentForLayout = _accumulatedMeta;
639
- const _metaP = resolveModuleMetadata(_filteredLayouts[_i], _fallbackParams, undefined, _parentForLayout)
640
- .catch((err) => { console.error("[vinext] Layout generateMetadata() failed:", err); return null; });
641
- _layoutMetaPromises.push(_metaP);
642
- _accumulatedMeta = _metaP.then(async (_r) =>
643
- _r ? mergeMetadata([await _parentForLayout, _r]) : await _parentForLayout
644
- );
645
- }
646
- const [_metaResults, _vpResults] = await Promise.all([
647
- Promise.all(_layoutMetaPromises),
648
- Promise.all(_filteredLayouts.map((mod) => resolveModuleViewport(mod, _fallbackParams).catch((err) => { console.error("[vinext] Layout generateViewport() failed:", err); return null; }))),
649
- ]);
650
- const metadataList = _metaResults.filter(Boolean);
651
- const viewportList = _vpResults.filter(Boolean);
652
- const resolvedMetadata = metadataList.length > 0 ? mergeMetadata(metadataList) : null;
653
- const resolvedViewport = mergeViewport(viewportList);
654
-
655
- // Build element: metadata head + noindex meta + boundary component wrapped in layouts
656
- // Always include charset and default viewport for parity with Next.js.
657
- const charsetMeta = createElement("meta", { charSet: "utf-8" });
658
- const noindexMeta = createElement("meta", { name: "robots", content: "noindex" });
659
- const headElements = [charsetMeta, noindexMeta];
660
- if (resolvedMetadata) headElements.push(createElement(MetadataHead, { metadata: resolvedMetadata }));
661
- headElements.push(createElement(ViewportHead, { viewport: resolvedViewport }));
662
- let element = createElement(Fragment, null, ...headElements, createElement(BoundaryComponent));
663
- if (isRscRequest) {
664
- // For RSC requests (client-side navigation), wrap the element with the same
665
- // component wrappers that buildPageElement() uses. Without these wrappers,
666
- // React's reconciliation would see a mismatched tree structure between the
667
- // old fiber tree (ErrorBoundary > LayoutSegmentProvider > html > body > NotFoundBoundary > ...)
668
- // and the new tree (html > body > ...), causing it to destroy and recreate
669
- // the entire DOM tree, resulting in a blank white page.
670
- //
671
- // We wrap each layout with LayoutSegmentProvider and add GlobalErrorBoundary
672
- // to match the wrapping order in buildPageElement(), ensuring smooth
673
- // client-side tree reconciliation.
674
- const _treePositions = route?.layoutTreePositions;
675
- const _routeSegs = route?.routeSegments || [];
676
- const _fallbackParams = opts?.matchedParams ?? route?.params ?? {};
677
- const _asyncFallbackParams = makeThenableParams(_fallbackParams);
678
- for (let i = layouts.length - 1; i >= 0; i--) {
679
- const LayoutComponent = layouts[i]?.default;
680
- if (LayoutComponent) {
681
- element = createElement(LayoutComponent, { children: element, params: _asyncFallbackParams });
682
- const _tp = _treePositions ? _treePositions[i] : 0;
683
- const _cs = __resolveChildSegments(_routeSegs, _tp, _fallbackParams);
684
- element = createElement(LayoutSegmentProvider, { childSegments: _cs }, element);
685
- }
686
- }
687
- ${globalErrorVar ? `
688
- const _GlobalErrorComponent = ${globalErrorVar}.default;
689
- if (_GlobalErrorComponent) {
690
- element = createElement(ErrorBoundary, {
691
- fallback: _GlobalErrorComponent,
692
- children: element,
693
- });
694
- }
695
- ` : ""}
696
- const _pathname = new URL(request.url).pathname;
697
- const onRenderError = createRscOnErrorHandler(
698
- request,
699
- _pathname,
700
- route?.pattern ?? _pathname,
701
- );
702
- const rscStream = renderToReadableStream(element, { onError: onRenderError });
703
- // Do NOT clear context here — the RSC stream is consumed lazily by the client.
704
- // Clearing context now would cause async server components (e.g. NextIntlClientProviderServer)
705
- // that run during stream consumption to see null headers/navigation context and throw,
706
- // resulting in missing provider context on the client (e.g. next-intl useTranslations fails
707
- // with "context from NextIntlClientProvider was not found").
708
- // Context is cleared naturally when the ALS scope from runWithRequestContext unwinds.
709
- return new Response(rscStream, {
710
- status: statusCode,
711
- headers: { "Content-Type": "text/x-component; charset=utf-8", "Vary": "RSC, Accept" },
712
- });
713
- }
714
- // For HTML (full page load) responses, wrap with layouts only (no client-side
715
- // wrappers needed since SSR generates the complete HTML document).
716
- const _fallbackParamsHtml = opts?.matchedParams ?? route?.params ?? {};
717
- const _asyncFallbackParamsHtml = makeThenableParams(_fallbackParamsHtml);
718
- for (let i = layouts.length - 1; i >= 0; i--) {
719
- const LayoutComponent = layouts[i]?.default;
720
- if (LayoutComponent) {
721
- element = createElement(LayoutComponent, { children: element, params: _asyncFallbackParamsHtml });
722
- }
723
- }
724
- const _pathname = new URL(request.url).pathname;
725
- const onRenderError = createRscOnErrorHandler(
726
- request,
727
- _pathname,
728
- route?.pattern ?? _pathname,
729
- );
730
- const rscStream = renderToReadableStream(element, { onError: onRenderError });
731
- // Collect font data from RSC environment
732
- const fontData = {
733
- links: _getSSRFontLinks(),
734
- styles: _getSSRFontStyles(),
735
- preloads: _getSSRFontPreloads(),
736
- };
737
- const ssrEntry = await import.meta.viteRsc.loadModule("ssr", "index");
738
- const htmlStream = await ssrEntry.handleSsr(rscStream, _getNavigationContext(), fontData);
739
- setHeadersContext(null);
740
- setNavigationContext(null);
741
- const _respHeaders = { "Content-Type": "text/html; charset=utf-8", "Vary": "RSC, Accept" };
742
- const _linkParts = (fontData.preloads || []).map(function(p) { return "<" + p.href + ">; rel=preload; as=font; type=" + p.type + "; crossorigin"; });
743
- if (_linkParts.length > 0) _respHeaders["Link"] = _linkParts.join(", ");
744
- return new Response(htmlStream, {
745
- status: statusCode,
746
- headers: _respHeaders,
619
+ return __renderAppPageHttpAccessFallback({
620
+ boundaryComponent: opts?.boundaryComponent ?? null,
621
+ buildFontLinkHeader: __buildAppPageFontLinkHeader,
622
+ clearRequestContext() {
623
+ setHeadersContext(null);
624
+ setNavigationContext(null);
625
+ },
626
+ createRscOnErrorHandler(pathname, routePath) {
627
+ return createRscOnErrorHandler(request, pathname, routePath);
628
+ },
629
+ getFontLinks: _getSSRFontLinks,
630
+ getFontPreloads: _getSSRFontPreloads,
631
+ getFontStyles: _getSSRFontStyles,
632
+ getNavigationContext: _getNavigationContext,
633
+ globalErrorModule: ${globalErrorVar ? globalErrorVar : "null"},
634
+ isRscRequest,
635
+ layoutModules: opts?.layouts ?? null,
636
+ loadSsrHandler() {
637
+ return import.meta.viteRsc.loadModule("ssr", "index");
638
+ },
639
+ makeThenableParams,
640
+ matchedParams: opts?.matchedParams ?? route?.params ?? {},
641
+ requestUrl: request.url,
642
+ resolveChildSegments: __resolveChildSegments,
643
+ rootForbiddenModule: rootForbiddenModule,
644
+ rootLayouts: rootLayouts,
645
+ rootNotFoundModule: rootNotFoundModule,
646
+ rootUnauthorizedModule: rootUnauthorizedModule,
647
+ route,
648
+ renderToReadableStream,
649
+ statusCode,
747
650
  });
748
651
  }
749
652
 
@@ -760,122 +663,33 @@ async function renderNotFoundPage(route, isRscRequest, request, matchedParams) {
760
663
  * by the boundary). This matches that behavior intentionally.
761
664
  */
762
665
  async function renderErrorBoundaryPage(route, error, isRscRequest, request, matchedParams) {
763
- // Resolve the error boundary component: leaf error.tsx first, then walk per-layout
764
- // errors from innermost to outermost (matching ancestor inheritance), then global-error.tsx.
765
- let ErrorComponent = route?.error?.default ?? null;
766
- let _isGlobalError = false;
767
- if (!ErrorComponent && route?.errors) {
768
- for (let i = route.errors.length - 1; i >= 0; i--) {
769
- if (route.errors[i]?.default) {
770
- ErrorComponent = route.errors[i].default;
771
- break;
772
- }
773
- }
774
- }
775
- ${globalErrorVar ? `
776
- if (!ErrorComponent) {
777
- ErrorComponent = ${globalErrorVar}?.default ?? null;
778
- _isGlobalError = !!ErrorComponent;
779
- }
780
- ` : ""}
781
- if (!ErrorComponent) return null;
782
-
783
- const rawError = error instanceof Error ? error : new Error(String(error));
784
- // Sanitize the error in production to avoid leaking internal details
785
- // (database errors, file paths, stack traces) through error.tsx to the client.
786
- // In development, pass the original error for debugging.
787
- const errorObj = __sanitizeErrorForClient(rawError);
788
- // Only pass error — reset is a client-side concern (re-renders the segment) and
789
- // can't be serialized through RSC. The error.tsx component will receive reset=undefined
790
- // during SSR, which is fine — onClick={undefined} is harmless, and the real reset
791
- // function is only meaningful after hydration.
792
- let element = createElement(ErrorComponent, {
793
- error: errorObj,
794
- });
795
-
796
- // global-error.tsx provides its own <html> and <body> (it replaces the root
797
- // layout). Skip layout wrapping when rendering it to avoid double <html> tags.
798
- if (!_isGlobalError) {
799
- const layouts = route?.layouts ?? rootLayouts;
800
- if (isRscRequest) {
801
- // For RSC requests (client-side navigation), wrap with the same component
802
- // wrappers that buildPageElement() uses (LayoutSegmentProvider, GlobalErrorBoundary).
803
- // This ensures React can reconcile the tree without destroying the DOM.
804
- // Same rationale as renderHTTPAccessFallbackPage — see comment there.
805
- const _errTreePositions = route?.layoutTreePositions;
806
- const _errRouteSegs = route?.routeSegments || [];
807
- const _errParams = matchedParams ?? route?.params ?? {};
808
- const _asyncErrParams = makeThenableParams(_errParams);
809
- for (let i = layouts.length - 1; i >= 0; i--) {
810
- const LayoutComponent = layouts[i]?.default;
811
- if (LayoutComponent) {
812
- element = createElement(LayoutComponent, { children: element, params: _asyncErrParams });
813
- const _etp = _errTreePositions ? _errTreePositions[i] : 0;
814
- const _ecs = __resolveChildSegments(_errRouteSegs, _etp, _errParams);
815
- element = createElement(LayoutSegmentProvider, { childSegments: _ecs }, element);
816
- }
817
- }
818
- ${globalErrorVar ? `
819
- const _ErrGlobalComponent = ${globalErrorVar}.default;
820
- if (_ErrGlobalComponent) {
821
- element = createElement(ErrorBoundary, {
822
- fallback: _ErrGlobalComponent,
823
- children: element,
824
- });
825
- }
826
- ` : ""}
827
- } else {
828
- // For HTML (full page load) responses, wrap with layouts only.
829
- const _errParamsHtml = matchedParams ?? route?.params ?? {};
830
- const _asyncErrParamsHtml = makeThenableParams(_errParamsHtml);
831
- for (let i = layouts.length - 1; i >= 0; i--) {
832
- const LayoutComponent = layouts[i]?.default;
833
- if (LayoutComponent) {
834
- element = createElement(LayoutComponent, { children: element, params: _asyncErrParamsHtml });
835
- }
836
- }
837
- }
838
- }
839
-
840
- const _pathname = new URL(request.url).pathname;
841
- const onRenderError = createRscOnErrorHandler(
842
- request,
843
- _pathname,
844
- route?.pattern ?? _pathname,
845
- );
846
-
847
- if (isRscRequest) {
848
- const rscStream = renderToReadableStream(element, { onError: onRenderError });
849
- // Do NOT clear context here — the RSC stream is consumed lazily by the client.
850
- // Clearing context now would cause async server components (e.g. NextIntlClientProviderServer)
851
- // that run during stream consumption to see null headers/navigation context and throw,
852
- // resulting in missing provider context on the client (e.g. next-intl useTranslations fails
853
- // with "context from NextIntlClientProvider was not found").
854
- // Context is cleared naturally when the ALS scope from runWithRequestContext unwinds.
855
- return new Response(rscStream, {
856
- status: 200,
857
- headers: { "Content-Type": "text/x-component; charset=utf-8", "Vary": "RSC, Accept" },
858
- });
859
- }
860
-
861
- // HTML (full page load) response — render through RSC → SSR pipeline
862
- const rscStream = renderToReadableStream(element, { onError: onRenderError });
863
- // Collect font data from RSC environment so error pages include font styles
864
- const fontData = {
865
- links: _getSSRFontLinks(),
866
- styles: _getSSRFontStyles(),
867
- preloads: _getSSRFontPreloads(),
868
- };
869
- const ssrEntry = await import.meta.viteRsc.loadModule("ssr", "index");
870
- const htmlStream = await ssrEntry.handleSsr(rscStream, _getNavigationContext(), fontData);
871
- setHeadersContext(null);
872
- setNavigationContext(null);
873
- const _errHeaders = { "Content-Type": "text/html; charset=utf-8", "Vary": "RSC, Accept" };
874
- const _errLinkParts = (fontData.preloads || []).map(function(p) { return "<" + p.href + ">; rel=preload; as=font; type=" + p.type + "; crossorigin"; });
875
- if (_errLinkParts.length > 0) _errHeaders["Link"] = _errLinkParts.join(", ");
876
- return new Response(htmlStream, {
877
- status: 200,
878
- headers: _errHeaders,
666
+ return __renderAppPageErrorBoundary({
667
+ buildFontLinkHeader: __buildAppPageFontLinkHeader,
668
+ clearRequestContext() {
669
+ setHeadersContext(null);
670
+ setNavigationContext(null);
671
+ },
672
+ createRscOnErrorHandler(pathname, routePath) {
673
+ return createRscOnErrorHandler(request, pathname, routePath);
674
+ },
675
+ error,
676
+ getFontLinks: _getSSRFontLinks,
677
+ getFontPreloads: _getSSRFontPreloads,
678
+ getFontStyles: _getSSRFontStyles,
679
+ getNavigationContext: _getNavigationContext,
680
+ globalErrorModule: ${globalErrorVar ? globalErrorVar : "null"},
681
+ isRscRequest,
682
+ loadSsrHandler() {
683
+ return import.meta.viteRsc.loadModule("ssr", "index");
684
+ },
685
+ makeThenableParams,
686
+ matchedParams: matchedParams ?? route?.params ?? {},
687
+ requestUrl: request.url,
688
+ resolveChildSegments: __resolveChildSegments,
689
+ rootLayouts: rootLayouts,
690
+ route,
691
+ renderToReadableStream,
692
+ sanitizeErrorForClient: __sanitizeErrorForClient,
879
693
  });
880
694
  }
881
695
 
@@ -1425,8 +1239,6 @@ export default async function handler(request, ctx) {
1425
1239
 
1426
1240
  async function _handleRequest(request, __reqCtx, _mwCtx) {
1427
1241
  const __reqStart = process.env.NODE_ENV !== "production" ? performance.now() : 0;
1428
- let __compileEnd;
1429
- let __renderEnd;
1430
1242
  // __reqStart is included in the timing header so the Node logging middleware
1431
1243
  // can compute true compile time as: handlerStart - middlewareStart.
1432
1244
  // Format: "handlerStart,compileMs,renderMs" - all as integers (ms). Dev-only.
@@ -2268,41 +2080,18 @@ async function _handleRequest(request, __reqCtx, _mwCtx) {
2268
2080
  const __revalElement = await buildPageElement(route, params, undefined, new URLSearchParams());
2269
2081
  const __revalOnError = createRscOnErrorHandler(request, cleanPathname, route.pattern);
2270
2082
  const __revalRscStream = renderToReadableStream(__revalElement, { onError: __revalOnError });
2271
- // Tee RSC stream: one for SSR, one to capture rscData
2272
- const [__revalRscForSsr, __revalRscForCapture] = __revalRscStream.tee();
2273
- // Capture rscData bytes in parallel with SSR
2274
- const __rscDataPromise = (async () => {
2275
- const __rscReader = __revalRscForCapture.getReader();
2276
- const __rscChunks = [];
2277
- let __rscTotal = 0;
2278
- for (;;) {
2279
- const { done, value } = await __rscReader.read();
2280
- if (done) break;
2281
- __rscChunks.push(value);
2282
- __rscTotal += value.byteLength;
2283
- }
2284
- const __rscBuf = new Uint8Array(__rscTotal);
2285
- let __rscOff = 0;
2286
- for (const c of __rscChunks) { __rscBuf.set(c, __rscOff); __rscOff += c.byteLength; }
2287
- return __rscBuf.buffer;
2288
- })();
2083
+ const __revalRscCapture = __teeAppPageRscStreamForCapture(__revalRscStream, true);
2289
2084
  const __revalFontData = { links: _getSSRFontLinks(), styles: _getSSRFontStyles(), preloads: _getSSRFontPreloads() };
2290
2085
  const __revalSsrEntry = await import.meta.viteRsc.loadModule("ssr", "index");
2291
- const __revalHtmlStream = await __revalSsrEntry.handleSsr(__revalRscForSsr, _getNavigationContext(), __revalFontData);
2086
+ const __revalHtmlStream = await __revalSsrEntry.handleSsr(
2087
+ __revalRscCapture.responseStream,
2088
+ _getNavigationContext(),
2089
+ __revalFontData,
2090
+ );
2292
2091
  setHeadersContext(null);
2293
2092
  setNavigationContext(null);
2294
- // Collect the full HTML string from the stream
2295
- const __revalReader = __revalHtmlStream.getReader();
2296
- const __revalDecoder = new TextDecoder();
2297
- const __revalChunks = [];
2298
- for (;;) {
2299
- const { done, value } = await __revalReader.read();
2300
- if (done) break;
2301
- __revalChunks.push(__revalDecoder.decode(value, { stream: true }));
2302
- }
2303
- __revalChunks.push(__revalDecoder.decode());
2304
- const __freshHtml = __revalChunks.join("");
2305
- const __freshRscData = await __rscDataPromise;
2093
+ const __freshHtml = await __readAppPageTextStream(__revalHtmlStream);
2094
+ const __freshRscData = await __revalRscCapture.capturedRscDataPromise;
2306
2095
  const __pageTags = __pageCacheTags(cleanPathname, getCollectedFetchTags());
2307
2096
  return { html: __freshHtml, rscData: __freshRscData, tags: __pageTags };
2308
2097
  });
@@ -2316,466 +2105,215 @@ async function _handleRequest(request, __reqCtx, _mwCtx) {
2316
2105
 
2317
2106
  // dynamicParams = false: only params from generateStaticParams are allowed.
2318
2107
  // This runs AFTER the ISR cache read so that a cache hit skips this work entirely.
2319
- if (dynamicParamsConfig === false && route.isDynamic && typeof route.page?.generateStaticParams === "function") {
2320
- try {
2321
- // Pass parent params to generateStaticParams (Next.js top-down params passing).
2322
- // Parent params = all matched params that DON'T belong to the leaf page's own dynamic segments.
2323
- // We pass the full matched params; the function uses only what it needs.
2324
- const staticParams = await route.page.generateStaticParams({ params });
2325
- if (Array.isArray(staticParams)) {
2326
- const paramKeys = Object.keys(params);
2327
- const isAllowed = staticParams.some(sp =>
2328
- paramKeys.every(key => {
2329
- const val = params[key];
2330
- const staticVal = sp[key];
2331
- // Allow parent params to not be in the returned set (they're inherited)
2332
- if (staticVal === undefined) return true;
2333
- if (Array.isArray(val)) return JSON.stringify(val) === JSON.stringify(staticVal);
2334
- return String(val) === String(staticVal);
2335
- })
2336
- );
2337
- if (!isAllowed) {
2338
- setHeadersContext(null);
2339
- setNavigationContext(null);
2340
- return new Response("Not Found", { status: 404 });
2341
- }
2342
- }
2343
- } catch (err) {
2108
+ const __dynamicParamsResponse = await __validateAppPageDynamicParams({
2109
+ clearRequestContext() {
2110
+ setHeadersContext(null);
2111
+ setNavigationContext(null);
2112
+ },
2113
+ enforceStaticParamsOnly: dynamicParamsConfig === false,
2114
+ generateStaticParams: route.page?.generateStaticParams,
2115
+ isDynamicRoute: route.isDynamic,
2116
+ logGenerateStaticParamsError(err) {
2344
2117
  console.error("[vinext] generateStaticParams error:", err);
2345
- }
2118
+ },
2119
+ params,
2120
+ });
2121
+ if (__dynamicParamsResponse) {
2122
+ return __dynamicParamsResponse;
2346
2123
  }
2347
2124
 
2348
2125
  // Check for intercepting routes on RSC requests (client-side navigation).
2349
2126
  // If the target URL matches an intercepting route in a parallel slot,
2350
2127
  // render the source route with the intercepting page in the slot.
2351
- let interceptOpts = undefined;
2352
- if (isRscRequest) {
2353
- const intercept = findIntercept(cleanPathname);
2354
- if (intercept) {
2355
- const sourceRoute = routes[intercept.sourceRouteIndex];
2356
- if (sourceRoute && sourceRoute !== route) {
2357
- // Render the source route (e.g. /feed) with the intercepting page in the slot
2358
- const sourceMatch = matchRoute(sourceRoute.pattern);
2359
- const sourceParams = sourceMatch ? sourceMatch.params : {};
2360
- setNavigationContext({
2361
- pathname: cleanPathname,
2362
- searchParams: url.searchParams,
2363
- params: intercept.matchedParams,
2364
- });
2365
- const interceptElement = await buildPageElement(sourceRoute, sourceParams, {
2366
- interceptSlot: intercept.slotName,
2367
- interceptPage: intercept.page,
2368
- interceptParams: intercept.matchedParams,
2369
- }, url.searchParams);
2370
- const interceptOnError = createRscOnErrorHandler(
2371
- request,
2372
- cleanPathname,
2373
- sourceRoute.pattern,
2374
- );
2375
- const interceptStream = renderToReadableStream(interceptElement, { onError: interceptOnError });
2376
- // Do NOT clear headers/navigation context here the RSC stream is consumed lazily
2377
- // by the client, and async server components that run during consumption need the
2378
- // context to still be live. The AsyncLocalStorage scope from runWithRequestContext
2379
- // handles cleanup naturally when all async continuations complete.
2380
- return new Response(interceptStream, {
2381
- headers: { "Content-Type": "text/x-component; charset=utf-8", "Vary": "RSC, Accept" },
2382
- });
2383
- }
2384
- // If sourceRoute === route, apply intercept opts to the normal render
2385
- interceptOpts = {
2128
+ const __interceptResult = await __resolveAppPageIntercept({
2129
+ buildPageElement,
2130
+ cleanPathname,
2131
+ currentRoute: route,
2132
+ findIntercept,
2133
+ getRoutePattern(sourceRoute) {
2134
+ return sourceRoute.pattern;
2135
+ },
2136
+ getSourceRoute(sourceRouteIndex) {
2137
+ return routes[sourceRouteIndex];
2138
+ },
2139
+ isRscRequest,
2140
+ matchSourceRouteParams(pattern) {
2141
+ return matchRoute(pattern)?.params ?? {};
2142
+ },
2143
+ renderInterceptResponse(sourceRoute, interceptElement) {
2144
+ const interceptOnError = createRscOnErrorHandler(
2145
+ request,
2146
+ cleanPathname,
2147
+ sourceRoute.pattern,
2148
+ );
2149
+ const interceptStream = renderToReadableStream(interceptElement, {
2150
+ onError: interceptOnError,
2151
+ });
2152
+ // Do NOT clear headers/navigation context here — the RSC stream is consumed lazily
2153
+ // by the client, and async server components that run during consumption need the
2154
+ // context to still be live. The AsyncLocalStorage scope from runWithRequestContext
2155
+ // handles cleanup naturally when all async continuations complete.
2156
+ return new Response(interceptStream, {
2157
+ headers: { "Content-Type": "text/x-component; charset=utf-8", "Vary": "RSC, Accept" },
2158
+ });
2159
+ },
2160
+ searchParams: url.searchParams,
2161
+ setNavigationContext,
2162
+ toInterceptOpts(intercept) {
2163
+ return {
2386
2164
  interceptSlot: intercept.slotName,
2387
2165
  interceptPage: intercept.page,
2388
2166
  interceptParams: intercept.matchedParams,
2389
2167
  };
2390
- }
2168
+ },
2169
+ });
2170
+ if (__interceptResult.response) {
2171
+ return __interceptResult.response;
2391
2172
  }
2173
+ const interceptOpts = __interceptResult.interceptOpts;
2392
2174
 
2393
- let element;
2394
- try {
2395
- element = await buildPageElement(route, params, interceptOpts, url.searchParams);
2396
- } catch (buildErr) {
2397
- // Check for redirect/notFound/forbidden/unauthorized thrown during metadata resolution or async components
2398
- if (buildErr && typeof buildErr === "object" && "digest" in buildErr) {
2399
- const digest = String(buildErr.digest);
2400
- if (digest.startsWith("NEXT_REDIRECT;")) {
2401
- const parts = digest.split(";");
2402
- const redirectUrl = decodeURIComponent(parts[2]);
2403
- const statusCode = parts[3] ? parseInt(parts[3], 10) : 307;
2404
- setHeadersContext(null);
2405
- setNavigationContext(null);
2406
- return Response.redirect(new URL(redirectUrl, request.url), statusCode);
2407
- }
2408
- if (digest === "NEXT_NOT_FOUND" || digest.startsWith("NEXT_HTTP_ERROR_FALLBACK;")) {
2409
- const statusCode = digest === "NEXT_NOT_FOUND" ? 404 : parseInt(digest.split(";")[1], 10);
2410
- const fallbackResp = await renderHTTPAccessFallbackPage(route, statusCode, isRscRequest, request, { matchedParams: params });
2411
- if (fallbackResp) return fallbackResp;
2412
- setHeadersContext(null);
2413
- setNavigationContext(null);
2414
- const statusText = statusCode === 403 ? "Forbidden" : statusCode === 401 ? "Unauthorized" : "Not Found";
2415
- return new Response(statusText, { status: statusCode });
2416
- }
2417
- }
2418
- // Non-special error (e.g. generateMetadata() threw) — render error.tsx if available
2419
- const errorBoundaryResp = await renderErrorBoundaryPage(route, buildErr, isRscRequest, request, params);
2420
- if (errorBoundaryResp) return errorBoundaryResp;
2421
- throw buildErr;
2175
+ const __pageBuildResult = await __buildAppPageElement({
2176
+ buildPageElement() {
2177
+ return buildPageElement(route, params, interceptOpts, url.searchParams);
2178
+ },
2179
+ renderErrorBoundaryPage(buildErr) {
2180
+ return renderErrorBoundaryPage(route, buildErr, isRscRequest, request, params);
2181
+ },
2182
+ renderSpecialError(__buildSpecialError) {
2183
+ return __buildAppPageSpecialErrorResponse({
2184
+ clearRequestContext() {
2185
+ setHeadersContext(null);
2186
+ setNavigationContext(null);
2187
+ },
2188
+ renderFallbackPage(statusCode) {
2189
+ return renderHTTPAccessFallbackPage(route, statusCode, isRscRequest, request, {
2190
+ matchedParams: params,
2191
+ });
2192
+ },
2193
+ requestUrl: request.url,
2194
+ specialError: __buildSpecialError,
2195
+ });
2196
+ },
2197
+ resolveSpecialError: __resolveAppPageSpecialError,
2198
+ });
2199
+ if (__pageBuildResult.response) {
2200
+ return __pageBuildResult.response;
2422
2201
  }
2202
+ const element = __pageBuildResult.element;
2423
2203
 
2424
2204
  // Note: CSS is automatically injected by @vitejs/plugin-rsc's
2425
2205
  // rscCssTransform — no manual loadCss() call needed.
2426
-
2427
- // Helper: check if an error is a redirect/notFound/forbidden/unauthorized thrown by the navigation shim
2428
- async function handleRenderError(err) {
2429
- if (err && typeof err === "object" && "digest" in err) {
2430
- const digest = String(err.digest);
2431
- if (digest.startsWith("NEXT_REDIRECT;")) {
2432
- const parts = digest.split(";");
2433
- const redirectUrl = decodeURIComponent(parts[2]);
2434
- const statusCode = parts[3] ? parseInt(parts[3], 10) : 307;
2435
- setHeadersContext(null);
2436
- setNavigationContext(null);
2437
- return Response.redirect(new URL(redirectUrl, request.url), statusCode);
2438
- }
2439
- if (digest === "NEXT_NOT_FOUND" || digest.startsWith("NEXT_HTTP_ERROR_FALLBACK;")) {
2440
- const statusCode = digest === "NEXT_NOT_FOUND" ? 404 : parseInt(digest.split(";")[1], 10);
2441
- const fallbackResp = await renderHTTPAccessFallbackPage(route, statusCode, isRscRequest, request, { matchedParams: params });
2442
- if (fallbackResp) return fallbackResp;
2443
- setHeadersContext(null);
2444
- setNavigationContext(null);
2445
- const statusText = statusCode === 403 ? "Forbidden" : statusCode === 401 ? "Unauthorized" : "Not Found";
2446
- return new Response(statusText, { status: statusCode });
2447
- }
2448
- }
2449
- return null;
2450
- }
2451
-
2452
- // Pre-render layout components to catch notFound()/redirect() thrown from layouts.
2453
- // In Next.js, each layout level has its own NotFoundBoundary. When a layout throws
2454
- // notFound(), the parent layout's boundary catches it and renders the parent's
2455
- // not-found.tsx. Since React Flight doesn't activate client error boundaries during
2456
- // RSC rendering, we catch layout-level throws here and render the appropriate
2457
- // fallback page with only the layouts above the throwing one.
2458
- //
2459
- // IMPORTANT: Layout pre-render runs BEFORE page pre-render. In Next.js, layouts
2460
- // render before their children — if a layout throws notFound(), the page never
2461
- // executes. By checking layouts first, we avoid a bug where the page's notFound()
2462
- // triggers renderHTTPAccessFallbackPage with ALL route layouts, but one of those
2463
- // layouts itself throws notFound() during the fallback rendering (causing a 500).
2464
- if (route.layouts && route.layouts.length > 0) {
2465
- const asyncParams = makeThenableParams(params);
2466
- // Run inside ALS context so the module-level console.error patch suppresses
2467
- // "Invalid hook call" only for this request's probe — concurrent requests
2468
- // each have their own ALS store and are unaffected.
2469
- const _layoutProbeResult = await _suppressHookWarningAls.run(true, async () => {
2470
- for (let li = route.layouts.length - 1; li >= 0; li--) {
2471
- const LayoutComp = route.layouts[li]?.default;
2472
- if (!LayoutComp) continue;
2473
- try {
2474
- const lr = LayoutComp({ params: asyncParams, children: null });
2475
- if (lr && typeof lr === "object" && typeof lr.then === "function") await lr;
2476
- } catch (layoutErr) {
2477
- if (layoutErr && typeof layoutErr === "object" && "digest" in layoutErr) {
2478
- const digest = String(layoutErr.digest);
2479
- if (digest.startsWith("NEXT_REDIRECT;")) {
2480
- const parts = digest.split(";");
2481
- const redirectUrl = decodeURIComponent(parts[2]);
2482
- const statusCode = parts[3] ? parseInt(parts[3], 10) : 307;
2483
- setHeadersContext(null);
2484
- setNavigationContext(null);
2485
- return Response.redirect(new URL(redirectUrl, request.url), statusCode);
2486
- }
2487
- if (digest === "NEXT_NOT_FOUND" || digest.startsWith("NEXT_HTTP_ERROR_FALLBACK;")) {
2488
- const statusCode = digest === "NEXT_NOT_FOUND" ? 404 : parseInt(digest.split(";")[1], 10);
2489
- // Find the not-found component from the parent level (the boundary that
2490
- // would catch this in Next.js). Walk up from the throwing layout to find
2491
- // the nearest not-found at a parent layout's directory.
2492
- let parentNotFound = null;
2493
- if (route.notFounds) {
2494
- for (let pi = li - 1; pi >= 0; pi--) {
2495
- if (route.notFounds[pi]?.default) {
2496
- parentNotFound = route.notFounds[pi].default;
2497
- break;
2498
- }
2499
- }
2500
- }
2501
- if (!parentNotFound) parentNotFound = ${rootNotFoundVar ? `${rootNotFoundVar}?.default` : "null"};
2502
- // Wrap in only the layouts above the throwing one
2503
- const parentLayouts = route.layouts.slice(0, li);
2504
- const fallbackResp = await renderHTTPAccessFallbackPage(
2505
- route, statusCode, isRscRequest, request,
2506
- { boundaryComponent: parentNotFound, layouts: parentLayouts, matchedParams: params }
2507
- );
2508
- if (fallbackResp) return fallbackResp;
2509
- setHeadersContext(null);
2510
- setNavigationContext(null);
2511
- const statusText = statusCode === 403 ? "Forbidden" : statusCode === 401 ? "Unauthorized" : "Not Found";
2512
- return new Response(statusText, { status: statusCode });
2513
- }
2514
- }
2515
- // Not a special error — let it propagate through normal RSC rendering
2516
- }
2517
- }
2518
- return null;
2519
- });
2520
- if (_layoutProbeResult instanceof Response) return _layoutProbeResult;
2521
- }
2522
-
2523
- // Pre-render the page component to catch redirect()/notFound() thrown synchronously.
2524
- // Server Components are just functions — we can call PageComponent directly to detect
2525
- // these special throws before starting the RSC stream.
2526
- //
2527
- // For routes with a loading.tsx Suspense boundary, we skip awaiting async components.
2528
- // The Suspense boundary + rscOnError will handle redirect/notFound thrown during
2529
- // streaming, and blocking here would defeat streaming (the slow component's delay
2530
- // would be hit before the RSC stream even starts).
2531
- //
2532
- // Because this calls the component outside React's render cycle, hooks like use()
2533
- // trigger "Invalid hook call" console.error in dev. The module-level ALS patch
2534
- // suppresses the warning only within this request's execution context.
2535
2206
  const _hasLoadingBoundary = !!(route.loading && route.loading.default);
2536
- const _pageProbeResult = await _suppressHookWarningAls.run(true, async () => {
2537
- try {
2538
- const testResult = PageComponent({ params });
2539
- // If it's a promise (async component), only await if there's no loading boundary.
2540
- // With a loading boundary, the Suspense streaming pipeline handles async resolution
2541
- // and any redirect/notFound errors via rscOnError.
2542
- if (testResult && typeof testResult === "object" && typeof testResult.then === "function") {
2543
- if (!_hasLoadingBoundary) {
2544
- await testResult;
2545
- } else {
2546
- // Suppress unhandled promise rejection — with a loading boundary,
2547
- // redirect/notFound errors are handled by rscOnError during streaming.
2548
- testResult.catch(() => {});
2549
- }
2550
- }
2551
- } catch (preRenderErr) {
2552
- const specialResponse = await handleRenderError(preRenderErr);
2553
- if (specialResponse) return specialResponse;
2554
- // Non-special errors from the pre-render test are expected (e.g. use() hook
2555
- // fails outside React's render cycle, client references can't execute on server).
2556
- // Only redirect/notFound/forbidden/unauthorized are actionable here — other
2557
- // errors will be properly caught during actual RSC/SSR rendering below.
2558
- }
2559
- return null;
2560
- });
2561
- if (_pageProbeResult instanceof Response) return _pageProbeResult;
2562
-
2563
- // Mark end of compile phase: route matching, middleware, tree building are done.
2564
- if (process.env.NODE_ENV !== "production") __compileEnd = performance.now();
2565
-
2566
- // Render to RSC stream.
2567
- // Track non-navigation RSC errors so we can detect when the in-tree global
2568
- // ErrorBoundary catches during SSR (producing double <html>/<body>) and
2569
- // re-render with renderErrorBoundaryPage (which skips layouts for global-error).
2570
- let _rscErrorForRerender = null;
2571
- const _baseOnError = createRscOnErrorHandler(request, cleanPathname, route.pattern);
2572
- const onRenderError = function(error, requestInfo, errorContext) {
2573
- if (!(error && typeof error === "object" && "digest" in error)) {
2574
- _rscErrorForRerender = error;
2575
- }
2576
- return _baseOnError(error, requestInfo, errorContext);
2577
- };
2578
- const rscStream = renderToReadableStream(element, { onError: onRenderError });
2579
-
2580
- // For ISR pages in production: tee the RSC stream immediately after creation so we
2581
- // can capture rscData for BOTH RSC requests (client-side nav/prefetch) and HTML
2582
- // requests. The tee must happen here — before the isRscRequest branch — so both
2583
- // paths can use the captured bytes when writing to the ISR cache.
2584
- // __rscForResponse → sent to the client (RSC response) or to SSR (HTML response)
2585
- // __isrRscDataPromise → resolves to ArrayBuffer of captured RSC wire bytes
2586
- let __rscForResponse = rscStream;
2587
- let __isrRscDataPromise = null;
2588
- if (process.env.NODE_ENV === "production" && revalidateSeconds !== null && revalidateSeconds > 0 && revalidateSeconds !== Infinity && !isForceDynamic) {
2589
- const [__rscA, __rscB] = rscStream.tee();
2590
- __rscForResponse = __rscA;
2591
- __isrRscDataPromise = (async () => {
2592
- const __rscReader = __rscB.getReader();
2593
- const __rscChunks = [];
2594
- let __rscTotal = 0;
2595
- for (;;) {
2596
- const { done, value } = await __rscReader.read();
2597
- if (done) break;
2598
- __rscChunks.push(value);
2599
- __rscTotal += value.byteLength;
2600
- }
2601
- const __rscBuf = new Uint8Array(__rscTotal);
2602
- let __rscOff = 0;
2603
- for (const c of __rscChunks) { __rscBuf.set(c, __rscOff); __rscOff += c.byteLength; }
2604
- return __rscBuf.buffer;
2605
- })();
2606
- }
2607
-
2608
- if (isRscRequest) {
2609
- // Direct RSC stream response (for client-side navigation)
2610
- // NOTE: Do NOT clear headers/navigation context here!
2611
- // The RSC stream is consumed lazily - components render when chunks are read.
2612
- // If we clear context now, headers()/cookies() will fail during rendering.
2613
- // Context will be cleared when the next request starts (via runWithRequestContext).
2614
- const __dynamicUsedInRsc = consumeDynamicUsage();
2615
- const __rscResponsePolicy = __resolveAppPageRscResponsePolicy({
2616
- dynamicUsedDuringBuild: __dynamicUsedInRsc,
2617
- isDynamicError,
2618
- isForceDynamic,
2619
- isForceStatic,
2620
- isProduction: process.env.NODE_ENV === "production",
2621
- revalidateSeconds,
2622
- });
2623
- const __rscResponse = __buildAppPageRscResponse(__rscForResponse, {
2624
- middlewareContext: _mwCtx,
2625
- params,
2626
- policy: __rscResponsePolicy,
2627
- timing: process.env.NODE_ENV !== "production"
2628
- ? {
2629
- compileEnd: __compileEnd,
2630
- handlerStart: __reqStart,
2631
- responseKind: "rsc",
2632
- }
2633
- : undefined,
2634
- });
2635
- // For ISR-eligible RSC requests in production: write rscData to its own key.
2636
- // HTML is stored under a separate key (written by the HTML path below) so
2637
- // these writes never race or clobber each other.
2638
- __scheduleAppPageRscCacheWrite({
2639
- capturedRscDataPromise: process.env.NODE_ENV === "production" ? __isrRscDataPromise : null,
2640
- cleanPathname,
2641
- consumeDynamicUsage,
2642
- dynamicUsedDuringBuild: __dynamicUsedInRsc,
2643
- getPageTags() {
2644
- return __pageCacheTags(cleanPathname, getCollectedFetchTags());
2645
- },
2646
- isrDebug: __isrDebug,
2647
- isrRscKey: __isrRscKey,
2648
- isrSet: __isrSet,
2649
- revalidateSeconds,
2650
- waitUntil(promise) {
2651
- _getRequestExecutionContext()?.waitUntil(promise);
2652
- },
2653
- });
2654
- return __rscResponse;
2655
- }
2656
-
2657
- // Collect font data from RSC environment before passing to SSR
2658
- // (Fonts are loaded during RSC rendering when layout.tsx calls Geist() etc.)
2659
- const fontData = {
2660
- links: _getSSRFontLinks(),
2661
- styles: _getSSRFontStyles(),
2662
- preloads: _getSSRFontPreloads(),
2663
- };
2664
-
2665
- // Build HTTP Link header for font preloading.
2666
- // This lets the browser (and CDN) start fetching font files before parsing HTML,
2667
- // eliminating the CSS → woff2 download waterfall.
2668
- const fontPreloads = fontData.preloads || [];
2669
- const fontLinkHeaderParts = [];
2670
- for (const preload of fontPreloads) {
2671
- fontLinkHeaderParts.push("<" + preload.href + ">; rel=preload; as=font; type=" + preload.type + "; crossorigin");
2672
- }
2673
- const fontLinkHeader = fontLinkHeaderParts.length > 0 ? fontLinkHeaderParts.join(", ") : "";
2674
-
2675
- // __rscForResponse was already teed above (before isRscRequest) for ISR pages in
2676
- // production. For non-ISR or dev, __rscForResponse === rscStream (no tee).
2677
- // __isrRscDataPromise resolves to rscData bytes used by the RSC write path above;
2678
- // the HTML write path below uses its own separate key and does not need rscData.
2679
-
2680
- // Delegate to SSR environment for HTML rendering
2681
- let htmlStream;
2682
- try {
2683
- const ssrEntry = await import.meta.viteRsc.loadModule("ssr", "index");
2684
- htmlStream = await ssrEntry.handleSsr(__rscForResponse, _getNavigationContext(), fontData);
2685
- // Shell render complete; Suspense boundaries stream asynchronously
2686
- if (process.env.NODE_ENV !== "production") __renderEnd = performance.now();
2687
- } catch (ssrErr) {
2688
- const specialResponse = await handleRenderError(ssrErr);
2689
- if (specialResponse) return specialResponse;
2690
- // Non-special error during SSR — render error.tsx if available
2691
- const errorBoundaryResp = await renderErrorBoundaryPage(route, ssrErr, isRscRequest, request, params);
2692
- if (errorBoundaryResp) return errorBoundaryResp;
2693
- throw ssrErr;
2694
- }
2695
-
2696
- // If an RSC error was caught by the in-tree global ErrorBoundary during SSR,
2697
- // the HTML output has double <html>/<body> (root layout + global-error.tsx).
2698
- // Discard it and re-render using renderErrorBoundaryPage which skips layouts
2699
- // when the error falls through to global-error.tsx.
2700
- ${globalErrorVar ? `
2701
- if (_rscErrorForRerender && !isRscRequest) {
2702
- const _hasLocalBoundary = !!(route?.error?.default) || !!(route?.errors && route.errors.some(function(e) { return e?.default; }));
2703
- if (!_hasLocalBoundary) {
2704
- const cleanResp = await renderErrorBoundaryPage(route, _rscErrorForRerender, false, request, params);
2705
- if (cleanResp) return cleanResp;
2706
- }
2707
- }
2708
- ` : ""}
2709
-
2710
- // Check for draftMode Set-Cookie header (from draftMode().enable()/disable())
2711
- const draftCookie = getDraftModeCookieHeader();
2712
-
2713
- setHeadersContext(null);
2714
- setNavigationContext(null);
2715
-
2716
- // Check if any component called connection(), cookies(), headers(), or noStore()
2717
- // during rendering. If so, treat as dynamic (skip ISR, set no-store).
2718
- const dynamicUsedDuringRender = consumeDynamicUsage();
2719
-
2720
- // Check if cacheLife() was called during rendering (e.g., page with file-level "use cache").
2721
- // If so, use its revalidation period for the Cache-Control header.
2722
- const requestCacheLife = _consumeRequestScopedCacheLife();
2723
- if (requestCacheLife && requestCacheLife.revalidate !== undefined && revalidateSeconds === null) {
2724
- revalidateSeconds = requestCacheLife.revalidate;
2725
- }
2726
- const __htmlResponsePolicy = __resolveAppPageHtmlResponsePolicy({
2727
- dynamicUsedDuringRender,
2207
+ const _asyncLayoutParams = makeThenableParams(params);
2208
+ return __renderAppPageLifecycle({
2209
+ cleanPathname,
2210
+ clearRequestContext() {
2211
+ setHeadersContext(null);
2212
+ setNavigationContext(null);
2213
+ },
2214
+ consumeDynamicUsage,
2215
+ createRscOnErrorHandler(pathname, routePath) {
2216
+ return createRscOnErrorHandler(request, pathname, routePath);
2217
+ },
2218
+ element,
2219
+ getDraftModeCookieHeader,
2220
+ getFontLinks: _getSSRFontLinks,
2221
+ getFontPreloads: _getSSRFontPreloads,
2222
+ getFontStyles: _getSSRFontStyles,
2223
+ getNavigationContext: _getNavigationContext,
2224
+ getPageTags() {
2225
+ return __pageCacheTags(cleanPathname, getCollectedFetchTags());
2226
+ },
2227
+ getRequestCacheLife() {
2228
+ return _consumeRequestScopedCacheLife();
2229
+ },
2230
+ handlerStart: __reqStart,
2231
+ hasLoadingBoundary: _hasLoadingBoundary,
2728
2232
  isDynamicError,
2729
2233
  isForceDynamic,
2730
2234
  isForceStatic,
2731
2235
  isProduction: process.env.NODE_ENV === "production",
2732
- revalidateSeconds,
2733
- });
2734
- const __htmlResponseTiming = process.env.NODE_ENV !== "production"
2735
- ? {
2736
- compileEnd: __compileEnd,
2737
- handlerStart: __reqStart,
2738
- renderEnd: __renderEnd,
2739
- responseKind: "html",
2740
- }
2741
- : undefined;
2742
-
2743
- // Emit Cache-Control for ISR pages and write to ISR cache on MISS (production only).
2744
- // revalidate=Infinity means "cache forever" (no periodic revalidation) — treated as
2745
- // static here so we emit s-maxage=31536000 but skip ISR cache management.
2746
- if (__htmlResponsePolicy.shouldWriteToCache) {
2747
- const __isrResponseProd = __buildAppPageHtmlResponse(htmlStream, {
2748
- draftCookie,
2749
- fontLinkHeader,
2750
- middlewareContext: _mwCtx,
2751
- policy: __htmlResponsePolicy,
2752
- timing: __htmlResponseTiming,
2753
- });
2754
- return __finalizeAppPageHtmlCacheResponse(__isrResponseProd, {
2755
- capturedRscDataPromise: __isrRscDataPromise,
2756
- cleanPathname,
2757
- getPageTags: function() {
2758
- return __pageCacheTags(cleanPathname, getCollectedFetchTags());
2759
- },
2760
- isrDebug: __isrDebug,
2761
- isrHtmlKey: __isrHtmlKey,
2762
- isrRscKey: __isrRscKey,
2763
- isrSet: __isrSet,
2764
- revalidateSeconds,
2765
- waitUntil: function(__cachePromise) {
2766
- // Register with ExecutionContext (from ALS) so the Workers runtime keeps
2767
- // the isolate alive until the cache write finishes, even after the response is sent.
2768
- _getRequestExecutionContext()?.waitUntil(__cachePromise);
2769
- },
2770
- });
2771
- }
2772
-
2773
- return __buildAppPageHtmlResponse(htmlStream, {
2774
- draftCookie,
2775
- fontLinkHeader,
2236
+ isRscRequest,
2237
+ isrDebug: __isrDebug,
2238
+ isrHtmlKey: __isrHtmlKey,
2239
+ isrRscKey: __isrRscKey,
2240
+ isrSet: __isrSet,
2241
+ layoutCount: route.layouts?.length ?? 0,
2242
+ loadSsrHandler() {
2243
+ return import.meta.viteRsc.loadModule("ssr", "index");
2244
+ },
2776
2245
  middlewareContext: _mwCtx,
2777
- policy: __htmlResponsePolicy,
2778
- timing: __htmlResponseTiming,
2246
+ params,
2247
+ probeLayoutAt(li) {
2248
+ const LayoutComp = route.layouts[li]?.default;
2249
+ if (!LayoutComp) return null;
2250
+ return LayoutComp({ params: _asyncLayoutParams, children: null });
2251
+ },
2252
+ probePage() {
2253
+ return PageComponent({ params });
2254
+ },
2255
+ revalidateSeconds,
2256
+ renderErrorBoundaryResponse(renderErr) {
2257
+ return renderErrorBoundaryPage(route, renderErr, isRscRequest, request, params);
2258
+ },
2259
+ async renderLayoutSpecialError(__layoutSpecialError, li) {
2260
+ return __buildAppPageSpecialErrorResponse({
2261
+ clearRequestContext() {
2262
+ setHeadersContext(null);
2263
+ setNavigationContext(null);
2264
+ },
2265
+ renderFallbackPage(statusCode) {
2266
+ // Find the not-found component from the parent level (the boundary that
2267
+ // would catch this in Next.js). Walk up from the throwing layout to find
2268
+ // the nearest not-found at a parent layout's directory.
2269
+ let parentNotFound = null;
2270
+ if (route.notFounds) {
2271
+ for (let pi = li - 1; pi >= 0; pi--) {
2272
+ if (route.notFounds[pi]?.default) {
2273
+ parentNotFound = route.notFounds[pi].default;
2274
+ break;
2275
+ }
2276
+ }
2277
+ }
2278
+ if (!parentNotFound) parentNotFound = ${rootNotFoundVar ? `${rootNotFoundVar}?.default` : "null"};
2279
+ const parentLayouts = route.layouts.slice(0, li);
2280
+ return renderHTTPAccessFallbackPage(route, statusCode, isRscRequest, request, {
2281
+ boundaryComponent: parentNotFound,
2282
+ layouts: parentLayouts,
2283
+ matchedParams: params,
2284
+ });
2285
+ },
2286
+ requestUrl: request.url,
2287
+ specialError: __layoutSpecialError,
2288
+ });
2289
+ },
2290
+ async renderPageSpecialError(specialError) {
2291
+ return __buildAppPageSpecialErrorResponse({
2292
+ clearRequestContext() {
2293
+ setHeadersContext(null);
2294
+ setNavigationContext(null);
2295
+ },
2296
+ renderFallbackPage(statusCode) {
2297
+ return renderHTTPAccessFallbackPage(route, statusCode, isRscRequest, request, {
2298
+ matchedParams: params,
2299
+ });
2300
+ },
2301
+ requestUrl: request.url,
2302
+ specialError,
2303
+ });
2304
+ },
2305
+ renderToReadableStream,
2306
+ routeHasLocalBoundary: !!(route?.error?.default) || !!(route?.errors && route.errors.some(function(e) { return e?.default; })),
2307
+ routePattern: route.pattern,
2308
+ runWithSuppressedHookWarning(probe) {
2309
+ // Run inside ALS context so the module-level console.error patch suppresses
2310
+ // "Invalid hook call" only for this request's probe — concurrent requests
2311
+ // each have their own ALS store and are unaffected.
2312
+ return _suppressHookWarningAls.run(true, probe);
2313
+ },
2314
+ waitUntil(__cachePromise) {
2315
+ _getRequestExecutionContext()?.waitUntil(__cachePromise);
2316
+ },
2779
2317
  });
2780
2318
  }
2781
2319