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.
- package/README.md +1 -0
- package/dist/build/prerender.d.ts +9 -4
- package/dist/build/prerender.js +27 -9
- package/dist/build/prerender.js.map +1 -1
- package/dist/build/run-prerender.js +4 -1
- package/dist/build/run-prerender.js.map +1 -1
- package/dist/config/next-config.d.ts +7 -1
- package/dist/config/next-config.js +39 -26
- package/dist/config/next-config.js.map +1 -1
- package/dist/deploy.js +52 -4
- package/dist/deploy.js.map +1 -1
- package/dist/entries/app-rsc-entry.js +278 -740
- package/dist/entries/app-rsc-entry.js.map +1 -1
- package/dist/entries/pages-server-entry.js +60 -134
- package/dist/entries/pages-server-entry.js.map +1 -1
- package/dist/index.d.ts +9 -1
- package/dist/index.js +339 -26
- package/dist/index.js.map +1 -1
- package/dist/plugins/optimize-imports.js +28 -2
- package/dist/plugins/optimize-imports.js.map +1 -1
- package/dist/server/app-browser-entry.js +4 -5
- package/dist/server/app-browser-entry.js.map +1 -1
- package/dist/server/app-page-boundary-render.d.ts +63 -0
- package/dist/server/app-page-boundary-render.js +182 -0
- package/dist/server/app-page-boundary-render.js.map +1 -0
- package/dist/server/app-page-boundary.d.ts +57 -0
- package/dist/server/app-page-boundary.js +60 -0
- package/dist/server/app-page-boundary.js.map +1 -0
- package/dist/server/app-page-execution.d.ts +46 -0
- package/dist/server/app-page-execution.js +109 -0
- package/dist/server/app-page-execution.js.map +1 -0
- package/dist/server/app-page-probe.d.ts +17 -0
- package/dist/server/app-page-probe.js +35 -0
- package/dist/server/app-page-probe.js.map +1 -0
- package/dist/server/app-page-render.d.ts +59 -0
- package/dist/server/app-page-render.js +176 -0
- package/dist/server/app-page-render.js.map +1 -0
- package/dist/server/app-page-request.d.ts +58 -0
- package/dist/server/app-page-request.js +79 -0
- package/dist/server/app-page-request.js.map +1 -0
- package/dist/server/app-page-response.js +1 -1
- package/dist/server/app-page-response.js.map +1 -1
- package/dist/server/app-page-stream.d.ts +63 -0
- package/dist/server/app-page-stream.js +98 -0
- package/dist/server/app-page-stream.js.map +1 -0
- package/dist/server/app-ssr-stream.js +6 -4
- package/dist/server/app-ssr-stream.js.map +1 -1
- package/dist/server/pages-page-response.d.ts +54 -0
- package/dist/server/pages-page-response.js +140 -0
- package/dist/server/pages-page-response.js.map +1 -0
- package/dist/server/prod-server.d.ts +13 -1
- package/dist/server/prod-server.js +116 -19
- package/dist/server/prod-server.js.map +1 -1
- package/dist/server/seed-cache.d.ts +44 -0
- package/dist/server/seed-cache.js +127 -0
- package/dist/server/seed-cache.js.map +1 -0
- package/dist/server/worker-utils.d.ts +0 -6
- package/dist/server/worker-utils.js +41 -5
- package/dist/server/worker-utils.js.map +1 -1
- package/dist/shims/error-boundary.js +1 -1
- package/dist/shims/font-google-base.js +1 -1
- package/dist/shims/font-google-base.js.map +1 -1
- package/dist/shims/font-google.d.ts +2 -3
- package/dist/shims/font-google.js +2 -3
- package/dist/shims/image.js +4 -2
- package/dist/shims/image.js.map +1 -1
- package/package.json +1 -1
- package/dist/shims/font-google.generated.d.ts +0 -1929
- package/dist/shims/font-google.generated.js +0 -1929
- 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
|
|
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
|
|
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
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
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
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
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
|
|
449
|
-
|
|
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
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
}
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
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
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
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
|
-
|
|
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(
|
|
2086
|
+
const __revalHtmlStream = await __revalSsrEntry.handleSsr(
|
|
2087
|
+
__revalRscCapture.responseStream,
|
|
2088
|
+
_getNavigationContext(),
|
|
2089
|
+
__revalFontData,
|
|
2090
|
+
);
|
|
2292
2091
|
setHeadersContext(null);
|
|
2293
2092
|
setNavigationContext(null);
|
|
2294
|
-
|
|
2295
|
-
const
|
|
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
|
-
|
|
2320
|
-
|
|
2321
|
-
|
|
2322
|
-
|
|
2323
|
-
|
|
2324
|
-
|
|
2325
|
-
|
|
2326
|
-
|
|
2327
|
-
|
|
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
|
-
|
|
2352
|
-
|
|
2353
|
-
|
|
2354
|
-
|
|
2355
|
-
|
|
2356
|
-
|
|
2357
|
-
|
|
2358
|
-
|
|
2359
|
-
|
|
2360
|
-
|
|
2361
|
-
|
|
2362
|
-
|
|
2363
|
-
|
|
2364
|
-
|
|
2365
|
-
|
|
2366
|
-
|
|
2367
|
-
|
|
2368
|
-
|
|
2369
|
-
|
|
2370
|
-
|
|
2371
|
-
|
|
2372
|
-
|
|
2373
|
-
|
|
2374
|
-
|
|
2375
|
-
|
|
2376
|
-
|
|
2377
|
-
|
|
2378
|
-
|
|
2379
|
-
|
|
2380
|
-
|
|
2381
|
-
|
|
2382
|
-
|
|
2383
|
-
|
|
2384
|
-
|
|
2385
|
-
|
|
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
|
-
|
|
2394
|
-
|
|
2395
|
-
|
|
2396
|
-
|
|
2397
|
-
|
|
2398
|
-
|
|
2399
|
-
|
|
2400
|
-
|
|
2401
|
-
|
|
2402
|
-
|
|
2403
|
-
|
|
2404
|
-
|
|
2405
|
-
|
|
2406
|
-
|
|
2407
|
-
|
|
2408
|
-
|
|
2409
|
-
|
|
2410
|
-
|
|
2411
|
-
|
|
2412
|
-
|
|
2413
|
-
|
|
2414
|
-
|
|
2415
|
-
|
|
2416
|
-
|
|
2417
|
-
|
|
2418
|
-
|
|
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
|
|
2537
|
-
|
|
2538
|
-
|
|
2539
|
-
|
|
2540
|
-
|
|
2541
|
-
|
|
2542
|
-
|
|
2543
|
-
|
|
2544
|
-
|
|
2545
|
-
|
|
2546
|
-
|
|
2547
|
-
|
|
2548
|
-
|
|
2549
|
-
|
|
2550
|
-
|
|
2551
|
-
|
|
2552
|
-
|
|
2553
|
-
|
|
2554
|
-
|
|
2555
|
-
|
|
2556
|
-
|
|
2557
|
-
|
|
2558
|
-
}
|
|
2559
|
-
|
|
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
|
-
|
|
2733
|
-
|
|
2734
|
-
|
|
2735
|
-
|
|
2736
|
-
|
|
2737
|
-
|
|
2738
|
-
|
|
2739
|
-
|
|
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
|
-
|
|
2778
|
-
|
|
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
|
|