vinext 1.0.0-beta.2 → 1.0.0-beta.4
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 +6 -9
- package/dist/build/client-build-config.js +7 -1
- package/dist/build/precompress.js +1 -1
- package/dist/build/prerender.d.ts +2 -1
- package/dist/build/prerender.js +12 -2
- package/dist/check.js +4 -0
- package/dist/cli-args.d.ts +2 -1
- package/dist/cli-args.js +10 -0
- package/dist/cli.js +14 -8
- package/dist/client/navigation-runtime.d.ts +6 -0
- package/dist/client/navigation-runtime.js +1 -1
- package/dist/config/config-matchers.js +4 -1
- package/dist/config/dotenv.d.ts +3 -4
- package/dist/config/dotenv.js +30 -22
- package/dist/config/next-config.d.ts +16 -5
- package/dist/config/next-config.js +7 -0
- package/dist/entries/app-browser-entry.d.ts +4 -10
- package/dist/entries/app-browser-entry.js +20 -4
- package/dist/entries/app-rsc-entry.js +21 -3
- package/dist/entries/app-rsc-manifest.js +19 -0
- package/dist/entries/pages-client-entry.js +0 -1
- package/dist/entries/pages-server-entry.js +13 -3
- package/dist/index.js +122 -49
- package/dist/init-cloudflare.d.ts +3 -1
- package/dist/init-cloudflare.js +59 -5
- package/dist/plugins/client-reference-dedup.js +7 -5
- package/dist/plugins/extensionless-dynamic-import.js +23 -18
- package/dist/plugins/ignore-dynamic-requests.js +3 -1
- package/dist/plugins/import-meta-url.js +17 -6
- package/dist/plugins/require-context.js +20 -15
- package/dist/plugins/sass.js +3 -4
- package/dist/plugins/transform-cache.d.ts +19 -0
- package/dist/plugins/transform-cache.js +36 -0
- package/dist/routing/app-route-graph.d.ts +14 -6
- package/dist/routing/app-route-graph.js +106 -16
- package/dist/server/api-handler.d.ts +2 -0
- package/dist/server/api-handler.js +7 -6
- package/dist/server/app-bfcache-identity.js +28 -2
- package/dist/server/app-browser-entry.js +175 -75
- package/dist/server/app-browser-navigation-controller.d.ts +23 -21
- package/dist/server/app-browser-navigation-controller.js +30 -5
- package/dist/server/app-browser-prefetch-response.d.ts +9 -0
- package/dist/server/app-browser-prefetch-response.js +32 -0
- package/dist/server/app-browser-state.d.ts +8 -3
- package/dist/server/app-browser-state.js +9 -3
- package/dist/server/app-page-cache-finalizer.js +1 -1
- package/dist/server/app-page-cache.d.ts +1 -1
- package/dist/server/app-page-cache.js +16 -2
- package/dist/server/app-page-dispatch.d.ts +9 -1
- package/dist/server/app-page-dispatch.js +37 -8
- package/dist/server/app-page-element-builder.d.ts +4 -1
- package/dist/server/app-page-element-builder.js +34 -7
- package/dist/server/app-page-head.d.ts +3 -3
- package/dist/server/app-page-head.js +58 -13
- package/dist/server/app-page-http-access-fallback-metadata.d.ts +2 -2
- package/dist/server/app-page-http-access-fallback-metadata.js +9 -1
- package/dist/server/app-page-probe.d.ts +4 -0
- package/dist/server/app-page-probe.js +6 -2
- package/dist/server/app-page-render.d.ts +1 -1
- package/dist/server/app-page-render.js +13 -3
- package/dist/server/app-page-request.d.ts +3 -1
- package/dist/server/app-page-request.js +4 -13
- package/dist/server/app-page-response.d.ts +2 -0
- package/dist/server/app-page-response.js +7 -2
- package/dist/server/app-page-route-wiring.d.ts +10 -2
- package/dist/server/app-page-route-wiring.js +269 -59
- package/dist/server/app-pages-bridge.d.ts +2 -1
- package/dist/server/app-pages-bridge.js +3 -1
- package/dist/server/app-route-handler-cache.js +4 -0
- package/dist/server/app-route-handler-dispatch.js +9 -3
- package/dist/server/app-route-handler-response.js +1 -1
- package/dist/server/app-route-module-loader.d.ts +6 -0
- package/dist/server/app-route-module-loader.js +3 -0
- package/dist/server/app-router-entry.js +6 -3
- package/dist/server/app-rsc-cache-busting.d.ts +7 -0
- package/dist/server/app-rsc-cache-busting.js +6 -0
- package/dist/server/app-rsc-errors.js +10 -0
- package/dist/server/app-rsc-handler.d.ts +2 -0
- package/dist/server/app-rsc-handler.js +30 -13
- package/dist/server/app-rsc-render-mode.d.ts +3 -2
- package/dist/server/app-rsc-render-mode.js +4 -1
- package/dist/server/app-rsc-request-normalization.js +65 -4
- package/dist/server/app-rsc-route-matching.d.ts +9 -0
- package/dist/server/app-rsc-route-matching.js +6 -0
- package/dist/server/app-server-action-execution.d.ts +1 -1
- package/dist/server/app-ssr-entry.js +4 -4
- package/dist/server/app-visited-response-cache.d.ts +6 -1
- package/dist/server/app-visited-response-cache.js +49 -1
- package/dist/server/cache-control.d.ts +2 -2
- package/dist/server/cache-control.js +2 -1
- package/dist/server/dev-server.d.ts +3 -1
- package/dist/server/dev-server.js +204 -348
- package/dist/server/edge-api-runtime.d.ts +10 -1
- package/dist/server/edge-api-runtime.js +29 -1
- package/dist/server/headers.d.ts +4 -2
- package/dist/server/headers.js +6 -3
- package/dist/server/isr-cache.d.ts +10 -33
- package/dist/server/isr-cache.js +33 -43
- package/dist/server/isr-decision.d.ts +2 -2
- package/dist/server/pages-api-route.d.ts +4 -0
- package/dist/server/pages-api-route.js +6 -3
- package/dist/server/pages-asset-tags.d.ts +1 -0
- package/dist/server/pages-asset-tags.js +6 -4
- package/dist/server/pages-dev-hydration.js +0 -1
- package/dist/server/pages-document-asset-props.d.ts +26 -0
- package/dist/server/pages-document-asset-props.js +69 -0
- package/dist/server/pages-get-initial-props.d.ts +1 -1
- package/dist/server/pages-get-initial-props.js +1 -4
- package/dist/server/pages-i18n.js +64 -16
- package/dist/server/pages-node-compat.d.ts +2 -0
- package/dist/server/pages-node-compat.js +10 -6
- package/dist/server/pages-page-data.d.ts +61 -9
- package/dist/server/pages-page-data.js +334 -74
- package/dist/server/pages-page-handler.d.ts +6 -1
- package/dist/server/pages-page-handler.js +119 -20
- package/dist/server/pages-page-response.d.ts +7 -3
- package/dist/server/pages-page-response.js +41 -33
- package/dist/server/pages-request-pipeline.d.ts +7 -0
- package/dist/server/pages-request-pipeline.js +5 -1
- package/dist/server/pages-revalidate.d.ts +1 -1
- package/dist/server/pages-revalidate.js +54 -8
- package/dist/server/pages-router-entry.d.ts +1 -0
- package/dist/server/pages-router-entry.js +10 -4
- package/dist/server/prerender-manifest.d.ts +1 -0
- package/dist/server/prod-server.d.ts +2 -1
- package/dist/server/prod-server.js +38 -13
- package/dist/server/revalidation-host.d.ts +11 -0
- package/dist/server/revalidation-host.js +19 -0
- package/dist/server/seed-cache.js +1 -1
- package/dist/server/worker-revalidation-context.d.ts +13 -0
- package/dist/server/worker-revalidation-context.js +30 -0
- package/dist/shims/app-router-scroll-state.d.ts +2 -1
- package/dist/shims/app-router-scroll-state.js +7 -2
- package/dist/shims/cache-handler.d.ts +1 -1
- package/dist/shims/cache-handler.js +10 -7
- package/dist/shims/cache-runtime.d.ts +6 -0
- package/dist/shims/cache-runtime.js +4 -1
- package/dist/shims/cache.js +1 -1
- package/dist/shims/document.js +15 -2
- package/dist/shims/error.js +1 -1
- package/dist/shims/fetch-cache.js +1 -1
- package/dist/shims/font-google-base.js +17 -8
- package/dist/shims/font-local.js +41 -10
- package/dist/shims/hash-scroll.d.ts +1 -1
- package/dist/shims/hash-scroll.js +2 -1
- package/dist/shims/headers.d.ts +10 -13
- package/dist/shims/headers.js +85 -36
- package/dist/shims/internal/cookie-serialize.d.ts +10 -9
- package/dist/shims/internal/cookie-serialize.js +9 -8
- package/dist/shims/internal/interpolate-as.d.ts +15 -1
- package/dist/shims/internal/interpolate-as.js +27 -2
- package/dist/shims/link.d.ts +2 -0
- package/dist/shims/link.js +77 -24
- package/dist/shims/metadata.d.ts +18 -34
- package/dist/shims/metadata.js +98 -56
- package/dist/shims/navigation.d.ts +18 -2
- package/dist/shims/navigation.js +70 -21
- package/dist/shims/request-context.d.ts +6 -2
- package/dist/shims/router.js +18 -7
- package/dist/shims/script.js +1 -1
- package/dist/shims/server.d.ts +7 -6
- package/dist/shims/server.js +156 -57
- package/dist/shims/slot.d.ts +2 -1
- package/dist/shims/slot.js +6 -2
- package/dist/shims/streamed-icons.d.ts +9 -0
- package/dist/shims/streamed-icons.js +33 -0
- package/dist/shims/unified-request-context.d.ts +21 -2
- package/dist/shims/unified-request-context.js +82 -1
- package/dist/utils/cache-control-metadata.d.ts +2 -1
- package/dist/utils/cache-control-metadata.js +5 -1
- package/dist/utils/html-limited-bots.js +1 -1
- package/dist/utils/protocol-headers.d.ts +7 -1
- package/dist/utils/protocol-headers.js +7 -1
- package/package.json +1 -1
|
@@ -20,6 +20,7 @@ function loadAppInterceptLayouts(intercept) {
|
|
|
20
20
|
if (loadState?.interceptLayoutsLoading) return loadState.interceptLayoutsLoading;
|
|
21
21
|
const loads = [];
|
|
22
22
|
pushArrayLoads(loads, intercept.interceptLayouts, intercept.__loadInterceptLayouts);
|
|
23
|
+
pushArrayLoads(loads, intercept.interceptLoadings, intercept.__loadInterceptLoadings);
|
|
23
24
|
if (loads.length === 0) return Promise.resolve(intercept.interceptLayouts ?? []);
|
|
24
25
|
const loading = Promise.all(loads).then(() => {
|
|
25
26
|
if (loadState) loadState.interceptLayoutsLoading = null;
|
|
@@ -52,6 +53,7 @@ function ensureAppRouteModulesLoaded(route) {
|
|
|
52
53
|
pushFieldLoad(loads, route, "unauthorized", route.__loadUnauthorized);
|
|
53
54
|
pushArrayLoads(loads, route.layouts, route.__loadLayouts);
|
|
54
55
|
pushArrayLoads(loads, route.templates, route.__loadTemplates);
|
|
56
|
+
pushArrayLoads(loads, route.loadings, route.__loadLoadings);
|
|
55
57
|
pushArrayLoads(loads, route.errors, route.__loadErrors);
|
|
56
58
|
pushArrayLoads(loads, route.errorPaths, route.__loadErrorPaths);
|
|
57
59
|
pushArrayLoads(loads, route.notFounds, route.__loadNotFounds);
|
|
@@ -63,6 +65,7 @@ function ensureAppRouteModulesLoaded(route) {
|
|
|
63
65
|
pushFieldLoad(loads, slot, "layout", slot.__loadLayout);
|
|
64
66
|
pushArrayLoads(loads, slot.configLayouts, slot.__loadConfigLayouts);
|
|
65
67
|
pushFieldLoad(loads, slot, "loading", slot.__loadLoading);
|
|
68
|
+
pushArrayLoads(loads, slot.loadings, slot.__loadLoadings);
|
|
66
69
|
pushFieldLoad(loads, slot, "error", slot.__loadError);
|
|
67
70
|
pushFieldLoad(loads, slot, "notFound", slot.__loadNotFound);
|
|
68
71
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import "./server-globals.js";
|
|
2
2
|
import { runWithExecutionContext } from "../shims/request-context.js";
|
|
3
|
-
import { VINEXT_PRERENDER_ROUTE_PARAMS_HEADER } from "../utils/protocol-headers.js";
|
|
3
|
+
import { VINEXT_PRERENDER_ROUTE_PARAMS_HEADER, VINEXT_REVALIDATE_HOST_HEADER } from "../utils/protocol-headers.js";
|
|
4
4
|
import { badRequestResponse, notFoundResponse, notFoundStaticAssetResponse } from "./http-error-responses.js";
|
|
5
5
|
import { isOpenRedirectShaped } from "./open-redirect.js";
|
|
6
6
|
import { cloneRequestWithHeaders, filterInternalHeaders } from "./request-pipeline.js";
|
|
@@ -8,6 +8,7 @@ import { assetPrefixPathname, isNextStaticPath } from "../utils/asset-prefix.js"
|
|
|
8
8
|
import { getImageOptimizer, handleConfiguredImageOptimization, isImageOptimizationPath } from "./image-optimization.js";
|
|
9
9
|
import { finalizeMissingStaticAssetResponse, resolveStaticAssetSignal } from "./worker-utils.js";
|
|
10
10
|
import { readTrustedPrerenderRouteParams, serializePrerenderRouteParamsHeader } from "./prerender-route-params.js";
|
|
11
|
+
import { createWorkerRevalidationContext } from "./worker-revalidation-context.js";
|
|
11
12
|
import rscHandler, { __assetPrefix, __basePath, __imageAllowedWidths, __imageConfig } from "virtual:vinext-rsc-entry";
|
|
12
13
|
import { registerConfiguredCacheAdapters } from "virtual:vinext-cache-adapters";
|
|
13
14
|
import { registerConfiguredImageOptimizer } from "virtual:vinext-image-adapters";
|
|
@@ -17,7 +18,8 @@ const __workerAssetPathPrefix = assetPrefixPathname(typeof __assetPrefix === "st
|
|
|
17
18
|
var app_router_entry_default = { async fetch(request, env, ctx) {
|
|
18
19
|
return handleRequest(request, env, ctx);
|
|
19
20
|
} };
|
|
20
|
-
async function handleRequest(request, env,
|
|
21
|
+
async function handleRequest(request, env, platformCtx) {
|
|
22
|
+
const ctx = platformCtx?.trustedRevalidateOrigin ? platformCtx : createWorkerRevalidationContext(platformCtx, (internalRequest, internalCtx) => handleRequest(internalRequest, env, internalCtx));
|
|
21
23
|
registerConfiguredCacheAdapters(env);
|
|
22
24
|
registerConfiguredImageOptimizer(env);
|
|
23
25
|
const url = new URL(request.url);
|
|
@@ -34,7 +36,8 @@ async function handleRequest(request, env, ctx) {
|
|
|
34
36
|
const missingBuildAsset = isNextStaticPath(url.pathname, __workerBasePath, __workerAssetPathPrefix);
|
|
35
37
|
{
|
|
36
38
|
const prerenderRouteParamsPayload = readTrustedPrerenderRouteParams(request);
|
|
37
|
-
const filteredHeaders = filterInternalHeaders(request.headers);
|
|
39
|
+
const filteredHeaders = ctx.isInternalPagesRevalidation ? new Headers(request.headers) : filterInternalHeaders(request.headers);
|
|
40
|
+
filteredHeaders.delete(VINEXT_REVALIDATE_HOST_HEADER);
|
|
38
41
|
const prerenderRouteParamsHeader = serializePrerenderRouteParamsHeader(prerenderRouteParamsPayload);
|
|
39
42
|
if (prerenderRouteParamsHeader !== null) filteredHeaders.set(VINEXT_PRERENDER_ROUTE_PARAMS_HEADER, prerenderRouteParamsHeader);
|
|
40
43
|
request = cloneRequestWithHeaders(request, filteredHeaders);
|
|
@@ -16,7 +16,14 @@ type CreateRscRequestHeadersOptions = {
|
|
|
16
16
|
clientReuseManifestHeader?: string | null;
|
|
17
17
|
interceptionContext?: string | null;
|
|
18
18
|
mountedSlotsHeader?: string | null;
|
|
19
|
+
includePrefetchHeader?: boolean;
|
|
19
20
|
renderMode?: AppRscRenderMode;
|
|
21
|
+
fetchPriority?: "auto" | "high" | "low";
|
|
22
|
+
nextUrl?: string | null;
|
|
23
|
+
prefetchRouterState?: {
|
|
24
|
+
pathAndSearch: string;
|
|
25
|
+
routeId: string;
|
|
26
|
+
} | null;
|
|
20
27
|
};
|
|
21
28
|
type ResolveInvalidRscCacheBustingRequestOptions = {
|
|
22
29
|
isRscRequest: boolean;
|
|
@@ -154,6 +154,12 @@ function createRscRequestHeaders(options = {}) {
|
|
|
154
154
|
["RSC"]: "1"
|
|
155
155
|
});
|
|
156
156
|
applyDeploymentIdHeader(headers);
|
|
157
|
+
if (options.prefetchRouterState) {
|
|
158
|
+
if (options.includePrefetchHeader !== false) headers.set(NEXT_ROUTER_PREFETCH_HEADER, "1");
|
|
159
|
+
headers.set(NEXT_ROUTER_STATE_TREE_HEADER, encodeURIComponent(JSON.stringify(options.prefetchRouterState)));
|
|
160
|
+
}
|
|
161
|
+
if (options.nextUrl) headers.set(NEXT_URL_HEADER, options.nextUrl);
|
|
162
|
+
if (process.env.__NEXT_TEST_MODE && options.fetchPriority) headers.set("Next-Test-Fetch-Priority", options.fetchPriority);
|
|
157
163
|
if (options.interceptionContext !== void 0 && options.interceptionContext !== null) headers.set(VINEXT_INTERCEPTION_CONTEXT_HEADER, options.interceptionContext);
|
|
158
164
|
if (options.mountedSlotsHeader !== void 0 && options.mountedSlotsHeader !== null) headers.set(VINEXT_MOUNTED_SLOTS_HEADER, options.mountedSlotsHeader);
|
|
159
165
|
if (options.clientReuseManifestHeader !== void 0 && options.clientReuseManifestHeader !== null) headers.set(VINEXT_CLIENT_REUSE_MANIFEST_HEADER, options.clientReuseManifestHeader);
|
|
@@ -7,6 +7,11 @@ function hasDigest(error) {
|
|
|
7
7
|
}
|
|
8
8
|
const BAILOUT_TO_CSR_DIGEST = "BAILOUT_TO_CLIENT_SIDE_RENDERING";
|
|
9
9
|
const DYNAMIC_SERVER_USAGE_DIGEST = "DYNAMIC_SERVER_USAGE";
|
|
10
|
+
function isAbortError(error) {
|
|
11
|
+
if (!error || typeof error !== "object") return false;
|
|
12
|
+
const name = Reflect.get(error, "name");
|
|
13
|
+
return name === "AbortError" || name === "ResponseAborted";
|
|
14
|
+
}
|
|
10
15
|
/**
|
|
11
16
|
* vinext's mirror of Next.js's `getDigestForWellKnownError`: returns the digest
|
|
12
17
|
* string only when the error is a genuine control-flow signal — a redirect,
|
|
@@ -52,6 +57,7 @@ function sanitizeErrorForClient(error, nodeEnv = process.env.NODE_ENV) {
|
|
|
52
57
|
function createRscOnErrorHandler(options) {
|
|
53
58
|
return (error) => {
|
|
54
59
|
const nodeEnv = options.nodeEnv ?? process.env.NODE_ENV;
|
|
60
|
+
if (isAbortError(error)) return;
|
|
55
61
|
const wellKnownDigest = getDigestForWellKnownError(error);
|
|
56
62
|
if (wellKnownDigest !== void 0) return wellKnownDigest;
|
|
57
63
|
if (nodeEnv !== "production" && error instanceof Error && error.message.includes("Only plain objects, and a few built-ins, can be passed to Client Components")) {
|
|
@@ -62,6 +68,10 @@ function createRscOnErrorHandler(options) {
|
|
|
62
68
|
const reportableError = typeof error === "object" && ORIGINAL_SERVER_ERROR in error ? Reflect.get(error, ORIGINAL_SERVER_ERROR) : error;
|
|
63
69
|
options.reportRequestError(reportableError instanceof Error ? reportableError : new Error(getThrownValueMessage(reportableError)), options.requestInfo, options.errorContext);
|
|
64
70
|
}
|
|
71
|
+
if (nodeEnv !== "production" && error && !hasDigest(error)) {
|
|
72
|
+
const loggableError = typeof error === "object" && ORIGINAL_SERVER_ERROR in error ? Reflect.get(error, ORIGINAL_SERVER_ERROR) : error;
|
|
73
|
+
console.error("[vinext] Server render error:", loggableError);
|
|
74
|
+
}
|
|
65
75
|
if (hasDigest(error)) return String(error.digest);
|
|
66
76
|
if (error) {
|
|
67
77
|
const digest = errorDigest(getThrownValueMessage(error) + getThrownValueStack(error));
|
|
@@ -125,6 +125,7 @@ type HandleServerActionRequestOptions<TRoute> = {
|
|
|
125
125
|
middlewareContext: AppRscMiddlewareContext;
|
|
126
126
|
mountedSlotsHeader: string | null;
|
|
127
127
|
request: Request;
|
|
128
|
+
scriptNonce?: string;
|
|
128
129
|
routeMatch: AppRscRouteMatch<TRoute> | null;
|
|
129
130
|
routePathname: string;
|
|
130
131
|
searchParams: URLSearchParams;
|
|
@@ -195,6 +196,7 @@ type CreateAppRscHandlerOptions<TRoute extends AppRscHandlerRoute> = {
|
|
|
195
196
|
imageConfig?: ImageConfig;
|
|
196
197
|
isDev: boolean;
|
|
197
198
|
loadPrerenderPagesRoutes?: () => Promise<unknown>;
|
|
199
|
+
matchInterceptRoute?: (pathname: string, sourcePathname: string) => AppRscRouteMatch<TRoute> | null;
|
|
198
200
|
matchRoute: (pathname: string) => AppRscRouteMatch<TRoute> | null;
|
|
199
201
|
matchRequestRoute?: (pathname: string) => AppRscRouteMatch<TRoute> | null;
|
|
200
202
|
runMiddleware?: (options: RunAppMiddlewareOptions) => Promise<ApplyAppMiddlewareResult>;
|
|
@@ -1,12 +1,13 @@
|
|
|
1
|
-
import { createRequestContext, runWithRequestContext } from "../shims/unified-request-context.js";
|
|
1
|
+
import { closeAfterResponse, closeAfterResponseWithBody, createRequestContext, runWithRequestContext } from "../shims/unified-request-context.js";
|
|
2
2
|
import { addBasePathToPathname, hasBasePath, stripBasePath } from "../utils/base-path.js";
|
|
3
3
|
import { getRequestExecutionContext } from "../shims/request-context.js";
|
|
4
|
-
import { VINEXT_MW_CTX_HEADER, VINEXT_PRERENDER_ROUTE_PARAMS_HEADER, VINEXT_PRERENDER_SPECULATIVE_HEADER } from "../utils/protocol-headers.js";
|
|
4
|
+
import { VINEXT_MW_CTX_HEADER, VINEXT_PRERENDER_ROUTE_PARAMS_HEADER, VINEXT_PRERENDER_SPECULATIVE_HEADER, VINEXT_REVALIDATE_HOST_HEADER } from "../utils/protocol-headers.js";
|
|
5
5
|
import { ACTION_REVALIDATED_HEADER } from "./headers.js";
|
|
6
6
|
import { requestContextFromRequest } from "../config/request-context.js";
|
|
7
7
|
import { isExternalUrl } from "../utils/external-url.js";
|
|
8
8
|
import { notFoundResponse } from "./http-error-responses.js";
|
|
9
9
|
import { cloneRequestWithHeaders, cloneRequestWithUrl, filterInternalHeaders, normalizeTrailingSlash, resolvePublicFileRoute } from "./request-pipeline.js";
|
|
10
|
+
import { isOnDemandRevalidateRequest } from "./isr-cache.js";
|
|
10
11
|
import { headersContextFromRequest } from "../shims/headers.js";
|
|
11
12
|
import { ensureFetchPatch, setCurrentFetchSoftTags } from "../shims/fetch-cache.js";
|
|
12
13
|
import { mergeRewriteQuery } from "../utils/query.js";
|
|
@@ -190,8 +191,9 @@ async function handleAppRscRequest(options, request, preMiddlewareRequestContext
|
|
|
190
191
|
};
|
|
191
192
|
let didMiddlewareRewrite = false;
|
|
192
193
|
let didMiddlewareRewritePathname = false;
|
|
193
|
-
|
|
194
|
-
|
|
194
|
+
const runMiddleware = isOnDemandRevalidateRequest(request.headers.get("x-prerender-revalidate")) ? void 0 : options.runMiddleware;
|
|
195
|
+
if (runMiddleware) {
|
|
196
|
+
const middlewareResult = await runMiddleware({
|
|
195
197
|
cleanPathname,
|
|
196
198
|
context: middlewareContext,
|
|
197
199
|
hadBasePath,
|
|
@@ -326,7 +328,11 @@ async function handleAppRscRequest(options, request, preMiddlewareRequestContext
|
|
|
326
328
|
searchParams: getResolvedSearchParams(),
|
|
327
329
|
params: {}
|
|
328
330
|
});
|
|
329
|
-
const
|
|
331
|
+
const directPreActionMatch = filesystemRouteEligible ? matchCleanPathname() : null;
|
|
332
|
+
const preActionRoutePathname = cleanPathnameIsRequestPathname ? requestCleanPathname : cleanPathname;
|
|
333
|
+
const interceptionPreActionMatch = filesystemRouteEligible && directPreActionMatch === null && isRscRequest && interceptionContextHeader !== null ? options.matchInterceptRoute?.(preActionRoutePathname, interceptionContextHeader) ?? null : null;
|
|
334
|
+
const preActionMatch = directPreActionMatch ?? interceptionPreActionMatch;
|
|
335
|
+
const isInterceptionMatch = interceptionPreActionMatch !== null;
|
|
330
336
|
if (preActionMatch) setRootParams(pickRootParams(preActionMatch.params, preActionMatch.route.rootParamNames));
|
|
331
337
|
if (pagesDataRequest && didMiddlewareRewritePathname && preActionMatch) {
|
|
332
338
|
const headers = new Headers();
|
|
@@ -372,8 +378,9 @@ async function handleAppRscRequest(options, request, preMiddlewareRequestContext
|
|
|
372
378
|
middlewareContext,
|
|
373
379
|
mountedSlotsHeader,
|
|
374
380
|
request,
|
|
381
|
+
scriptNonce,
|
|
375
382
|
routeMatch: preActionMatch,
|
|
376
|
-
routePathname:
|
|
383
|
+
routePathname: preActionRoutePathname,
|
|
377
384
|
searchParams: getResolvedSearchParams()
|
|
378
385
|
}) : null;
|
|
379
386
|
if (serverActionResponse) return serverActionResponse;
|
|
@@ -381,7 +388,7 @@ async function handleAppRscRequest(options, request, preMiddlewareRequestContext
|
|
|
381
388
|
let match = preActionMatch;
|
|
382
389
|
const renderPagesForMatchKind = async (matchKind) => {
|
|
383
390
|
if (!filesystemRouteEligible) return null;
|
|
384
|
-
const response = match === null || match.route.isDynamic ? await options.renderPagesFallback?.({
|
|
391
|
+
const response = !isInterceptionMatch && (match === null || match.route.isDynamic) ? await options.renderPagesFallback?.({
|
|
385
392
|
appRouteMatch: match ?? null,
|
|
386
393
|
allowRscDocumentFallback: didMiddlewareRewritePathname,
|
|
387
394
|
isDataRequest,
|
|
@@ -407,7 +414,7 @@ async function handleAppRscRequest(options, request, preMiddlewareRequestContext
|
|
|
407
414
|
options.clearRequestContext();
|
|
408
415
|
return staticPagesFallbackResponse;
|
|
409
416
|
}
|
|
410
|
-
if (!resolvedLateRewritesForAction && (!match || match.route.isDynamic)) for (const rewrite of options.configRewrites.afterFiles) {
|
|
417
|
+
if (!isInterceptionMatch && !resolvedLateRewritesForAction && (!match || match.route.isDynamic)) for (const rewrite of options.configRewrites.afterFiles) {
|
|
411
418
|
const afterFilesRewrite = await applyRewrite({
|
|
412
419
|
basePathState,
|
|
413
420
|
clearRequestContext: options.clearRequestContext,
|
|
@@ -480,7 +487,7 @@ async function handleAppRscRequest(options, request, preMiddlewareRequestContext
|
|
|
480
487
|
}
|
|
481
488
|
if (pagesDataRequest) {
|
|
482
489
|
options.clearRequestContext();
|
|
483
|
-
if (
|
|
490
|
+
if (runMiddleware && (middlewareContext.status === null || middlewareContext.status === 200 || middlewareContext.status === 404)) {
|
|
484
491
|
const response = buildNextDataNotFoundResponse();
|
|
485
492
|
const headers = new Headers(response.headers);
|
|
486
493
|
headers.set("x-nextjs-matched-path", matchPathname(canonicalPathname));
|
|
@@ -632,9 +639,11 @@ function createAppRscHandler(options) {
|
|
|
632
639
|
const pagesDataNormalization = options.renderPagesFallback && pagesDataCandidate ? normalizePagesDataRequest(pagesDataCandidate, options.buildId, "", typeof options.runMiddleware === "function" && options.trailingSlash) : null;
|
|
633
640
|
if (pagesDataNormalization?.notFoundResponse) return pagesDataNormalization.notFoundResponse;
|
|
634
641
|
const isPagesDataRequest = pagesDataNormalization?.isDataReq === true;
|
|
642
|
+
const executionContext = isExecutionContextLike(ctx) ? ctx : getRequestExecutionContext() ?? null;
|
|
635
643
|
const prerenderRouteParamsPayload = readTrustedPrerenderRouteParams(rawRequest);
|
|
636
644
|
const isTrustedSpeculativePrerender = process.env.VINEXT_PRERENDER === "1" && rawRequest.headers.get("x-vinext-prerender-secret") !== null && rawRequest.headers.get("x-vinext-prerender-speculative") === "1";
|
|
637
|
-
const filteredHeaders = filterInternalHeaders(rawRequest.headers);
|
|
645
|
+
const filteredHeaders = executionContext?.isInternalPagesRevalidation ? new Headers(rawRequest.headers) : filterInternalHeaders(rawRequest.headers);
|
|
646
|
+
filteredHeaders.delete(VINEXT_REVALIDATE_HOST_HEADER);
|
|
638
647
|
if (mwCtx !== null) filteredHeaders.set(VINEXT_MW_CTX_HEADER, mwCtx);
|
|
639
648
|
const prerenderRouteParamsHeader = serializePrerenderRouteParamsHeader(prerenderRouteParamsPayload);
|
|
640
649
|
if (prerenderRouteParamsHeader !== null) filteredHeaders.set(VINEXT_PRERENDER_ROUTE_PARAMS_HEADER, prerenderRouteParamsHeader);
|
|
@@ -647,12 +656,12 @@ function createAppRscHandler(options) {
|
|
|
647
656
|
}
|
|
648
657
|
const request = cloneRequestWithHeaders(appRequest, filteredHeaders);
|
|
649
658
|
const pagesDataRequest = pagesDataNormalization?.isDataReq ? cloneRequestWithHeaders(pagesDataCandidate, filteredHeaders) : null;
|
|
650
|
-
const
|
|
651
|
-
return runWithRequestContext(createRequestContext({
|
|
659
|
+
const requestContext = createRequestContext({
|
|
652
660
|
headersContext: headersContextFromRequest(request, { draftModeSecret: options.draftModeSecret }),
|
|
653
661
|
executionContext,
|
|
654
662
|
unstableCacheRevalidation: "background"
|
|
655
|
-
})
|
|
663
|
+
});
|
|
664
|
+
const responsePromise = runWithRequestContext(requestContext, () => runWithPrerenderWorkUnit(async () => {
|
|
656
665
|
ensureFetchPatch();
|
|
657
666
|
const preMiddlewareRequestContext = requestContextFromRequest(request);
|
|
658
667
|
let response;
|
|
@@ -669,6 +678,14 @@ function createAppRscHandler(options) {
|
|
|
669
678
|
requestContext: preMiddlewareRequestContext
|
|
670
679
|
});
|
|
671
680
|
}, { route: () => new URL(request.url).pathname }));
|
|
681
|
+
let response;
|
|
682
|
+
try {
|
|
683
|
+
response = await responsePromise;
|
|
684
|
+
} catch (error) {
|
|
685
|
+
await closeAfterResponse(requestContext);
|
|
686
|
+
throw error;
|
|
687
|
+
}
|
|
688
|
+
return closeAfterResponseWithBody(response, requestContext);
|
|
672
689
|
};
|
|
673
690
|
}
|
|
674
691
|
//#endregion
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
//#region src/server/app-rsc-render-mode.d.ts
|
|
2
|
-
type AppRscRenderMode = "navigation" | "prefetch-dynamic-shell" | "prefetch-loading-shell";
|
|
2
|
+
type AppRscRenderMode = "navigation" | "prefetch-empty" | "prefetch-dynamic-shell" | "prefetch-loading-shell";
|
|
3
3
|
declare const APP_RSC_RENDER_MODE_NAVIGATION = "navigation";
|
|
4
|
+
declare const APP_RSC_RENDER_MODE_PREFETCH_EMPTY = "prefetch-empty";
|
|
4
5
|
declare const APP_RSC_RENDER_MODE_PREFETCH_DYNAMIC_SHELL = "prefetch-dynamic-shell";
|
|
5
6
|
declare const APP_RSC_RENDER_MODE_PREFETCH_LOADING_SHELL = "prefetch-loading-shell";
|
|
6
7
|
declare function getRscRenderModeCacheVariant(mode: AppRscRenderMode): string | null;
|
|
7
8
|
declare function parseAppRscRenderMode(value: string | null): AppRscRenderMode;
|
|
8
9
|
//#endregion
|
|
9
|
-
export { APP_RSC_RENDER_MODE_NAVIGATION, APP_RSC_RENDER_MODE_PREFETCH_DYNAMIC_SHELL, APP_RSC_RENDER_MODE_PREFETCH_LOADING_SHELL, AppRscRenderMode, getRscRenderModeCacheVariant, parseAppRscRenderMode };
|
|
10
|
+
export { APP_RSC_RENDER_MODE_NAVIGATION, APP_RSC_RENDER_MODE_PREFETCH_DYNAMIC_SHELL, APP_RSC_RENDER_MODE_PREFETCH_EMPTY, APP_RSC_RENDER_MODE_PREFETCH_LOADING_SHELL, AppRscRenderMode, getRscRenderModeCacheVariant, parseAppRscRenderMode };
|
|
@@ -1,18 +1,21 @@
|
|
|
1
1
|
//#region src/server/app-rsc-render-mode.ts
|
|
2
2
|
const APP_RSC_RENDER_MODE_NAVIGATION = "navigation";
|
|
3
|
+
const APP_RSC_RENDER_MODE_PREFETCH_EMPTY = "prefetch-empty";
|
|
3
4
|
const APP_RSC_RENDER_MODE_PREFETCH_DYNAMIC_SHELL = "prefetch-dynamic-shell";
|
|
4
5
|
const APP_RSC_RENDER_MODE_PREFETCH_LOADING_SHELL = "prefetch-loading-shell";
|
|
5
6
|
function getRscRenderModeCacheVariant(mode) {
|
|
7
|
+
if (mode === "prefetch-empty") return "prefetch-empty";
|
|
6
8
|
if (mode === "prefetch-dynamic-shell") return "prefetch-dynamic-shell";
|
|
7
9
|
if (mode === "prefetch-loading-shell") return "prefetch-loading-shell";
|
|
8
10
|
return null;
|
|
9
11
|
}
|
|
10
12
|
function parseAppRscRenderMode(value) {
|
|
11
13
|
switch (value) {
|
|
14
|
+
case APP_RSC_RENDER_MODE_PREFETCH_EMPTY: return APP_RSC_RENDER_MODE_PREFETCH_EMPTY;
|
|
12
15
|
case APP_RSC_RENDER_MODE_PREFETCH_DYNAMIC_SHELL: return APP_RSC_RENDER_MODE_PREFETCH_DYNAMIC_SHELL;
|
|
13
16
|
case APP_RSC_RENDER_MODE_PREFETCH_LOADING_SHELL: return APP_RSC_RENDER_MODE_PREFETCH_LOADING_SHELL;
|
|
14
17
|
default: return APP_RSC_RENDER_MODE_NAVIGATION;
|
|
15
18
|
}
|
|
16
19
|
}
|
|
17
20
|
//#endregion
|
|
18
|
-
export { APP_RSC_RENDER_MODE_NAVIGATION, APP_RSC_RENDER_MODE_PREFETCH_DYNAMIC_SHELL, APP_RSC_RENDER_MODE_PREFETCH_LOADING_SHELL, getRscRenderModeCacheVariant, parseAppRscRenderMode };
|
|
21
|
+
export { APP_RSC_RENDER_MODE_NAVIGATION, APP_RSC_RENDER_MODE_PREFETCH_DYNAMIC_SHELL, APP_RSC_RENDER_MODE_PREFETCH_EMPTY, APP_RSC_RENDER_MODE_PREFETCH_LOADING_SHELL, getRscRenderModeCacheVariant, parseAppRscRenderMode };
|
|
@@ -1,15 +1,63 @@
|
|
|
1
1
|
import { normalizePathnameForRouteMatchStrict } from "../routing/utils.js";
|
|
2
2
|
import { hasBasePath, stripBasePath } from "../utils/base-path.js";
|
|
3
|
-
import { VINEXT_CLIENT_REUSE_MANIFEST_HEADER, VINEXT_INTERCEPTION_CONTEXT_HEADER, VINEXT_MOUNTED_SLOTS_HEADER, VINEXT_RSC_RENDER_MODE_HEADER } from "./headers.js";
|
|
3
|
+
import { NEXT_ROUTER_STATE_TREE_HEADER, NEXT_URL_HEADER, VINEXT_CLIENT_REUSE_MANIFEST_HEADER, VINEXT_INTERCEPTION_CONTEXT_HEADER, VINEXT_MOUNTED_SLOTS_HEADER, VINEXT_RSC_RENDER_MODE_HEADER } from "./headers.js";
|
|
4
4
|
import { normalizePath } from "./normalize-path.js";
|
|
5
5
|
import { badRequestResponse, notFoundResponse } from "./http-error-responses.js";
|
|
6
6
|
import { guardProtocolRelativeUrl } from "./request-pipeline.js";
|
|
7
7
|
import { normalizeMountedSlotsHeader } from "./app-mounted-slots-header.js";
|
|
8
|
-
import { APP_RSC_RENDER_MODE_NAVIGATION, parseAppRscRenderMode } from "./app-rsc-render-mode.js";
|
|
9
|
-
import { stripRscSuffix } from "./app-rsc-cache-busting.js";
|
|
8
|
+
import { APP_RSC_RENDER_MODE_NAVIGATION, APP_RSC_RENDER_MODE_PREFETCH_EMPTY, APP_RSC_RENDER_MODE_PREFETCH_LOADING_SHELL, parseAppRscRenderMode } from "./app-rsc-render-mode.js";
|
|
9
|
+
import { VINEXT_RSC_CACHE_BUSTING_SEARCH_PARAM, stripRscSuffix } from "./app-rsc-cache-busting.js";
|
|
10
10
|
import { parseClientReuseManifestHeader } from "./client-reuse-manifest.js";
|
|
11
11
|
import { normalizeInterceptionContextHeader } from "./app-interception-context-header.js";
|
|
12
12
|
//#region src/server/app-rsc-request-normalization.ts
|
|
13
|
+
function extractFlightRouterStatePath(value, depth = 0) {
|
|
14
|
+
if (!Array.isArray(value) || value.length < 2 || depth > 64) return null;
|
|
15
|
+
const rawSegment = value[0];
|
|
16
|
+
const segment = Array.isArray(rawSegment) ? rawSegment[1] : rawSegment;
|
|
17
|
+
if (typeof segment !== "string") return null;
|
|
18
|
+
if (segment === "__DEFAULT__" || /^(?:\(\.\)|\(\.\.\)|\(\.\.\.\))/.test(segment)) return null;
|
|
19
|
+
const parallelRoutes = value[1];
|
|
20
|
+
if (!parallelRoutes || typeof parallelRoutes !== "object" || Array.isArray(parallelRoutes)) return null;
|
|
21
|
+
let childPath = null;
|
|
22
|
+
const children = Reflect.get(parallelRoutes, "children");
|
|
23
|
+
if (children !== void 0) childPath = extractFlightRouterStatePath(children, depth + 1);
|
|
24
|
+
if (childPath === null) for (const [key, child] of Object.entries(parallelRoutes)) {
|
|
25
|
+
if (key === "children") continue;
|
|
26
|
+
childPath = extractFlightRouterStatePath(child, depth + 1);
|
|
27
|
+
if (childPath !== null) break;
|
|
28
|
+
}
|
|
29
|
+
return `/${[segment === "" || segment === "children" || segment.startsWith("__PAGE__") || segment.startsWith("(") && segment.endsWith(")") ? "" : segment.replace(/^\/+/, ""), ...childPath === null ? [] : childPath.split("/")].filter(Boolean).join("/")}`;
|
|
30
|
+
}
|
|
31
|
+
function parsePrefetchRouterState(value) {
|
|
32
|
+
if (!value) return null;
|
|
33
|
+
try {
|
|
34
|
+
const parsed = JSON.parse(decodeURIComponent(value));
|
|
35
|
+
if (Array.isArray(parsed)) {
|
|
36
|
+
const pathAndSearch = extractFlightRouterStatePath(parsed);
|
|
37
|
+
return pathAndSearch === null ? null : { pathAndSearch };
|
|
38
|
+
}
|
|
39
|
+
if (!parsed || typeof parsed !== "object") return null;
|
|
40
|
+
const pathAndSearch = Reflect.get(parsed, "pathAndSearch");
|
|
41
|
+
const routeId = Reflect.get(parsed, "routeId");
|
|
42
|
+
if (typeof pathAndSearch !== "string" || !pathAndSearch.startsWith("/") || typeof routeId !== "string" || routeId.length === 0) return null;
|
|
43
|
+
return { pathAndSearch };
|
|
44
|
+
} catch {
|
|
45
|
+
return null;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
function normalizeComparablePathAndSearch(value, basePath, baseUrl) {
|
|
49
|
+
const parsed = new URL(value, baseUrl);
|
|
50
|
+
const pathname = basePath && hasBasePath(parsed.pathname, basePath) ? stripBasePath(parsed.pathname, basePath) : parsed.pathname;
|
|
51
|
+
const search = parsed.searchParams.toString();
|
|
52
|
+
return `${pathname}${search ? `?${search}` : ""}`;
|
|
53
|
+
}
|
|
54
|
+
function tryNormalizeComparablePathAndSearch(value, basePath, baseUrl) {
|
|
55
|
+
try {
|
|
56
|
+
return normalizeComparablePathAndSearch(value, basePath, baseUrl);
|
|
57
|
+
} catch {
|
|
58
|
+
return null;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
13
61
|
/**
|
|
14
62
|
* Normalize an App Router RSC request.
|
|
15
63
|
*
|
|
@@ -65,7 +113,20 @@ function normalizeRscRequest(request, basePath, allowOutsideBasePath = false) {
|
|
|
65
113
|
const requestCleanPathname = stripRscSuffix(requestPathname);
|
|
66
114
|
const interceptionContextHeader = normalizeInterceptionContextHeader(request.headers.get(VINEXT_INTERCEPTION_CONTEXT_HEADER));
|
|
67
115
|
const mountedSlotsHeader = normalizeMountedSlotsHeader(request.headers.get(VINEXT_MOUNTED_SLOTS_HEADER));
|
|
68
|
-
|
|
116
|
+
let renderMode = isRscRequest ? parseAppRscRenderMode(request.headers.get(VINEXT_RSC_RENDER_MODE_HEADER)) : APP_RSC_RENDER_MODE_NAVIGATION;
|
|
117
|
+
if (isRscRequest && renderMode === "navigation" && request.headers.get("Next-Router-Prefetch") === "1" && request.headers.get("Next-Router-Segment-Prefetch") === null) {
|
|
118
|
+
const nextUrl = request.headers.get(NEXT_URL_HEADER);
|
|
119
|
+
const routerState = parsePrefetchRouterState(request.headers.get(NEXT_ROUTER_STATE_TREE_HEADER));
|
|
120
|
+
if (nextUrl && routerState) {
|
|
121
|
+
const targetUrl = new URL(url);
|
|
122
|
+
targetUrl.pathname = cleanPathname;
|
|
123
|
+
targetUrl.searchParams.delete(VINEXT_RSC_CACHE_BUSTING_SEARCH_PARAM);
|
|
124
|
+
const routerPathAndSearch = tryNormalizeComparablePathAndSearch(routerState.pathAndSearch, basePath, url);
|
|
125
|
+
const nextPathAndSearch = tryNormalizeComparablePathAndSearch(nextUrl, basePath, url);
|
|
126
|
+
const targetPathAndSearch = normalizeComparablePathAndSearch(targetUrl.href, basePath, url);
|
|
127
|
+
if (routerPathAndSearch !== null && nextPathAndSearch !== null) renderMode = routerPathAndSearch === nextPathAndSearch && routerPathAndSearch === targetPathAndSearch ? APP_RSC_RENDER_MODE_PREFETCH_EMPTY : APP_RSC_RENDER_MODE_PREFETCH_LOADING_SHELL;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
69
130
|
return {
|
|
70
131
|
clientReuseManifest: isRscRequest ? parseClientReuseManifestHeader(request.headers.get(VINEXT_CLIENT_REUSE_MANIFEST_HEADER)) : { kind: "absent" },
|
|
71
132
|
hadBasePath,
|
|
@@ -33,8 +33,11 @@ type AppRscInterceptForMatching = {
|
|
|
33
33
|
interceptLayouts: readonly unknown[];
|
|
34
34
|
interceptLayoutSegments?: readonly (readonly string[])[];
|
|
35
35
|
interceptBranchSegments?: readonly string[];
|
|
36
|
+
interceptLoadings?: readonly unknown[];
|
|
37
|
+
interceptLoadingTreePositions?: readonly number[];
|
|
36
38
|
interceptNotFoundBranchSegments?: readonly string[];
|
|
37
39
|
__loadInterceptLayouts?: readonly (() => Promise<unknown>)[] | null;
|
|
40
|
+
__loadInterceptLoadings?: readonly (() => Promise<unknown>)[] | null;
|
|
38
41
|
page: unknown;
|
|
39
42
|
__pageLoader?: (() => Promise<unknown>) | null;
|
|
40
43
|
notFound?: unknown;
|
|
@@ -54,8 +57,11 @@ type AppRscSiblingInterceptForMatching = {
|
|
|
54
57
|
interceptLayouts: readonly unknown[];
|
|
55
58
|
interceptLayoutSegments?: readonly (readonly string[])[];
|
|
56
59
|
interceptBranchSegments?: readonly string[];
|
|
60
|
+
interceptLoadings?: readonly unknown[];
|
|
61
|
+
interceptLoadingTreePositions?: readonly number[];
|
|
57
62
|
interceptNotFoundBranchSegments?: readonly string[];
|
|
58
63
|
__loadInterceptLayouts?: readonly (() => Promise<unknown>)[] | null;
|
|
64
|
+
__loadInterceptLoadings?: readonly (() => Promise<unknown>)[] | null;
|
|
59
65
|
page: unknown;
|
|
60
66
|
__pageLoader?: (() => Promise<unknown>) | null;
|
|
61
67
|
notFound?: unknown;
|
|
@@ -93,8 +99,11 @@ type AppRscInterceptLookupEntry = {
|
|
|
93
99
|
interceptLayouts: readonly unknown[];
|
|
94
100
|
interceptLayoutSegments?: readonly (readonly string[])[];
|
|
95
101
|
interceptBranchSegments?: readonly string[];
|
|
102
|
+
interceptLoadings?: readonly unknown[];
|
|
103
|
+
interceptLoadingTreePositions?: readonly number[];
|
|
96
104
|
interceptNotFoundBranchSegments?: readonly string[];
|
|
97
105
|
__loadInterceptLayouts?: readonly (() => Promise<unknown>)[] | null;
|
|
106
|
+
__loadInterceptLoadings?: readonly (() => Promise<unknown>)[] | null;
|
|
98
107
|
page: unknown;
|
|
99
108
|
__pageLoader?: (() => Promise<unknown>) | null;
|
|
100
109
|
notFound: unknown;
|
|
@@ -170,8 +170,11 @@ function createInterceptLookup(routes) {
|
|
|
170
170
|
interceptLayouts: intercept.interceptLayouts,
|
|
171
171
|
interceptLayoutSegments: intercept.interceptLayoutSegments,
|
|
172
172
|
interceptBranchSegments: intercept.interceptBranchSegments,
|
|
173
|
+
interceptLoadings: intercept.interceptLoadings,
|
|
174
|
+
interceptLoadingTreePositions: intercept.interceptLoadingTreePositions,
|
|
173
175
|
interceptNotFoundBranchSegments: intercept.interceptNotFoundBranchSegments,
|
|
174
176
|
__loadInterceptLayouts: intercept.__loadInterceptLayouts,
|
|
177
|
+
__loadInterceptLoadings: intercept.__loadInterceptLoadings,
|
|
175
178
|
page: intercept.page,
|
|
176
179
|
__pageLoader: intercept.__pageLoader,
|
|
177
180
|
notFound: intercept.notFound,
|
|
@@ -203,8 +206,11 @@ function createInterceptLookup(routes) {
|
|
|
203
206
|
interceptLayouts: intercept.interceptLayouts,
|
|
204
207
|
interceptLayoutSegments: intercept.interceptLayoutSegments,
|
|
205
208
|
interceptBranchSegments: intercept.interceptBranchSegments,
|
|
209
|
+
interceptLoadings: intercept.interceptLoadings,
|
|
210
|
+
interceptLoadingTreePositions: intercept.interceptLoadingTreePositions,
|
|
206
211
|
interceptNotFoundBranchSegments: intercept.interceptNotFoundBranchSegments,
|
|
207
212
|
__loadInterceptLayouts: intercept.__loadInterceptLayouts,
|
|
213
|
+
__loadInterceptLoadings: intercept.__loadInterceptLoadings,
|
|
208
214
|
page: intercept.page,
|
|
209
215
|
__pageLoader: intercept.__pageLoader,
|
|
210
216
|
notFound: intercept.notFound,
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { ActionRevalidationKind } from "../shims/cache-request-state.js";
|
|
2
1
|
import { AppRscRenderMode } from "./app-rsc-render-mode.js";
|
|
2
|
+
import { ActionRevalidationKind } from "../shims/cache-request-state.js";
|
|
3
3
|
import { FetchCacheMode } from "../shims/fetch-cache.js";
|
|
4
4
|
import { HeadersAccessPhase } from "../shims/headers.js";
|
|
5
5
|
import { ReactFormState } from "react-dom/client";
|
|
@@ -6,13 +6,12 @@ import "./app-elements.js";
|
|
|
6
6
|
import { isPprFallbackShellAbortError } from "../shims/ppr-fallback-shell.js";
|
|
7
7
|
import { AppRouterContext } from "../shims/internal/app-router-context.js";
|
|
8
8
|
import { appendAssetDeploymentIdQuery } from "../utils/deployment-id.js";
|
|
9
|
-
import { ServerInsertedHTMLContext, clearServerInsertedHTML, getBfcacheIdMapContext, registerServerInsertedHTMLCallback, renderServerInsertedHTML, setNavigationContext } from "../shims/navigation-context-state.js";
|
|
10
|
-
import "../shims/navigation-server.js";
|
|
11
|
-
import { runWithNavigationContext } from "../shims/navigation-state.js";
|
|
12
9
|
import { withScriptNonce } from "../shims/script-nonce-context.js";
|
|
13
10
|
import { createInlineScriptTag, createNonceAttribute, escapeHtmlAttr, safeJsonStringify } from "./html.js";
|
|
14
11
|
import { getClientTraceMetadataHTML } from "./client-trace-metadata.js";
|
|
15
12
|
import { setPagesClientAssets } from "./pages-client-assets.js";
|
|
13
|
+
import { ServerInsertedHTMLContext, clearServerInsertedHTML, getBfcacheIdMapContext, registerServerInsertedHTMLCallback, renderServerInsertedHTML, setNavigationContext } from "../shims/navigation-context-state.js";
|
|
14
|
+
import "../shims/navigation-server.js";
|
|
16
15
|
import DefaultGlobalError from "../shims/default-global-error.js";
|
|
17
16
|
import { BfcacheStateKeyMapContext, ElementsContext, Slot } from "../shims/slot.js";
|
|
18
17
|
import { createSsrErrorMetaRenderer } from "./app-ssr-error-meta.js";
|
|
@@ -22,6 +21,7 @@ import { RSC_FORM_STATE_GLOBAL } from "./app-browser-hydration.js";
|
|
|
22
21
|
import { createClientReferencePreloader } from "./app-client-reference-preloader.js";
|
|
23
22
|
import { deferUntilStreamConsumed } from "./defer-until-stream-consumed.js";
|
|
24
23
|
import { runWithRootParamsScope } from "../shims/root-params.js";
|
|
24
|
+
import { runWithNavigationContext } from "../shims/navigation-state.js";
|
|
25
25
|
import { BeforeInteractiveContext } from "../shims/before-interactive-context.js";
|
|
26
26
|
import { renderBeforeInteractiveInlineScripts } from "./before-interactive-head.js";
|
|
27
27
|
import { createInitialDevServerErrorScript } from "./dev-initial-server-error.js";
|
|
@@ -123,7 +123,7 @@ function renderFontHtml(fontData, nonce, options = {}) {
|
|
|
123
123
|
const nonceAttr = createNonceAttribute(nonce);
|
|
124
124
|
const includeStyles = options.includeStyles ?? true;
|
|
125
125
|
for (const url of fontData.links ?? []) fontHTML += `<link rel="stylesheet"${nonceAttr} href="${escapeHtmlAttr(appendAssetDeploymentIdQuery(url))}" />\n`;
|
|
126
|
-
for (const preload of fontData.preloads ?? []) fontHTML += `<link rel="preload"${nonceAttr} href="${escapeHtmlAttr(
|
|
126
|
+
for (const preload of fontData.preloads ?? []) fontHTML += `<link rel="preload"${nonceAttr} href="${escapeHtmlAttr(preload.href)}" as="font" type="${escapeHtmlAttr(preload.type)}" crossorigin />\n`;
|
|
127
127
|
if (includeStyles && fontData.styles && fontData.styles.length > 0) fontHTML += `<style data-vinext-fonts${nonceAttr}>${fontData.styles.join("\n")}</style>\n`;
|
|
128
128
|
return fontHTML;
|
|
129
129
|
}
|
|
@@ -25,5 +25,10 @@ declare function isVisitedResponseCacheEntryFresh(entry: VisitedResponseCacheEnt
|
|
|
25
25
|
navigationKind: VisitedResponseCacheNavigationKind;
|
|
26
26
|
now: number;
|
|
27
27
|
}): boolean;
|
|
28
|
+
declare function findVisitedResponseCacheEntry(cache: Map<string, VisitedResponseCacheEntry>, rscUrl: string, interceptionContext: string | null): {
|
|
29
|
+
cacheKey: string;
|
|
30
|
+
entry: VisitedResponseCacheEntry;
|
|
31
|
+
} | null;
|
|
32
|
+
declare function deleteVisitedResponseCacheEntry(cache: Map<string, VisitedResponseCacheEntry>, rscUrl: string, interceptionContext: string | null): boolean;
|
|
28
33
|
//#endregion
|
|
29
|
-
export { MAX_TRAVERSAL_CACHE_TTL, VISITED_RESPONSE_CACHE_TTL, VisitedResponseCacheEntry, createVisitedResponseCacheEntry, isVisitedResponseCacheEntryFresh };
|
|
34
|
+
export { MAX_TRAVERSAL_CACHE_TTL, VISITED_RESPONSE_CACHE_TTL, VisitedResponseCacheEntry, createVisitedResponseCacheEntry, deleteVisitedResponseCacheEntry, findVisitedResponseCacheEntry, isVisitedResponseCacheEntryFresh };
|
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import { AppElementsWire } from "./app-elements-wire.js";
|
|
2
|
+
import "./app-elements.js";
|
|
3
|
+
import { stripRscCacheBustingSearchParam } from "./app-rsc-cache-busting.js";
|
|
1
4
|
import { resolveCachedRscResponseExpiresAt } from "../shims/navigation.js";
|
|
2
5
|
//#region src/server/app-visited-response-cache.ts
|
|
3
6
|
const VISITED_RESPONSE_CACHE_TTL = 5 * 6e4;
|
|
@@ -17,5 +20,50 @@ function isVisitedResponseCacheEntryFresh(entry, options) {
|
|
|
17
20
|
if (options.navigationKind === "traverse") return options.now - entry.createdAt < MAX_TRAVERSAL_CACHE_TTL;
|
|
18
21
|
return entry.expiresAt > options.now;
|
|
19
22
|
}
|
|
23
|
+
function normalizeVisitedResponseCacheLookupUrl(rscUrl) {
|
|
24
|
+
try {
|
|
25
|
+
const url = new URL(rscUrl, "http://vinext.local");
|
|
26
|
+
stripRscCacheBustingSearchParam(url);
|
|
27
|
+
return `${url.pathname}${url.search}`;
|
|
28
|
+
} catch {
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
function parseVisitedResponseCacheKey(cacheKey) {
|
|
33
|
+
const separatorIndex = cacheKey.indexOf("\0");
|
|
34
|
+
if (separatorIndex === -1) return {
|
|
35
|
+
interceptionContext: null,
|
|
36
|
+
rscUrl: cacheKey
|
|
37
|
+
};
|
|
38
|
+
return {
|
|
39
|
+
interceptionContext: cacheKey.slice(separatorIndex + 1),
|
|
40
|
+
rscUrl: cacheKey.slice(0, separatorIndex)
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
function findVisitedResponseCacheEntry(cache, rscUrl, interceptionContext) {
|
|
44
|
+
const exactCacheKey = AppElementsWire.encodeCacheKey(rscUrl, interceptionContext);
|
|
45
|
+
const exactEntry = cache.get(exactCacheKey);
|
|
46
|
+
if (exactEntry) return {
|
|
47
|
+
cacheKey: exactCacheKey,
|
|
48
|
+
entry: exactEntry
|
|
49
|
+
};
|
|
50
|
+
const normalizedTarget = normalizeVisitedResponseCacheLookupUrl(rscUrl);
|
|
51
|
+
if (normalizedTarget === null) return null;
|
|
52
|
+
for (const [cacheKey, entry] of cache) {
|
|
53
|
+
const source = parseVisitedResponseCacheKey(cacheKey);
|
|
54
|
+
if (source.interceptionContext !== interceptionContext) continue;
|
|
55
|
+
if (normalizeVisitedResponseCacheLookupUrl(source.rscUrl) !== normalizedTarget) continue;
|
|
56
|
+
return {
|
|
57
|
+
cacheKey,
|
|
58
|
+
entry
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
return null;
|
|
62
|
+
}
|
|
63
|
+
function deleteVisitedResponseCacheEntry(cache, rscUrl, interceptionContext) {
|
|
64
|
+
const match = findVisitedResponseCacheEntry(cache, rscUrl, interceptionContext);
|
|
65
|
+
if (!match) return false;
|
|
66
|
+
return cache.delete(match.cacheKey);
|
|
67
|
+
}
|
|
20
68
|
//#endregion
|
|
21
|
-
export { MAX_TRAVERSAL_CACHE_TTL, VISITED_RESPONSE_CACHE_TTL, createVisitedResponseCacheEntry, isVisitedResponseCacheEntryFresh };
|
|
69
|
+
export { MAX_TRAVERSAL_CACHE_TTL, VISITED_RESPONSE_CACHE_TTL, createVisitedResponseCacheEntry, deleteVisitedResponseCacheEntry, findVisitedResponseCacheEntry, isVisitedResponseCacheEntryFresh };
|
|
@@ -26,7 +26,7 @@ declare function applyCdnResponseHeaders(headers: Headers, input: CdnCacheableHe
|
|
|
26
26
|
* Next.js source:
|
|
27
27
|
* https://github.com/vercel/next.js/blob/canary/packages/next/src/server/lib/cache-control.ts
|
|
28
28
|
*/
|
|
29
|
-
declare function buildRevalidateCacheControl(revalidateSeconds: number, expireSeconds?: number): string;
|
|
29
|
+
declare function buildRevalidateCacheControl(revalidateSeconds: number | false, expireSeconds?: number): string;
|
|
30
30
|
/**
|
|
31
31
|
* Builds Cache-Control for ISR cache reads. HIT responses and STALE responses
|
|
32
32
|
* with stored expire metadata use the same route policy because Next.js derives
|
|
@@ -34,6 +34,6 @@ declare function buildRevalidateCacheControl(revalidateSeconds: number, expireSe
|
|
|
34
34
|
* STALE entries without expire metadata keep vinext's legacy `s-maxage=0`
|
|
35
35
|
* fallback so older cache entries are not treated as newly fresh downstream.
|
|
36
36
|
*/
|
|
37
|
-
declare function buildCachedRevalidateCacheControl(cacheState: "HIT" | "STALE", revalidateSeconds: number, expireSeconds?: number): string;
|
|
37
|
+
declare function buildCachedRevalidateCacheControl(cacheState: "HIT" | "STALE", revalidateSeconds: number | false, expireSeconds?: number): string;
|
|
38
38
|
//#endregion
|
|
39
39
|
export { BROWSER_REVALIDATE_CACHE_CONTROL, NEVER_CACHE_CONTROL, NO_STORE_CACHE_CONTROL, STATIC_CACHE_CONTROL, applyCdnResponseHeaders, buildCachedRevalidateCacheControl, buildRevalidateCacheControl, shouldUseNextDeployCacheControl };
|
|
@@ -48,6 +48,7 @@ function applyCdnResponseHeaders(headers, input) {
|
|
|
48
48
|
* https://github.com/vercel/next.js/blob/canary/packages/next/src/server/lib/cache-control.ts
|
|
49
49
|
*/
|
|
50
50
|
function buildRevalidateCacheControl(revalidateSeconds, expireSeconds) {
|
|
51
|
+
if (revalidateSeconds === false) return STATIC_CACHE_CONTROL;
|
|
51
52
|
if (expireSeconds === void 0) return `s-maxage=${revalidateSeconds}, stale-while-revalidate`;
|
|
52
53
|
if (revalidateSeconds >= expireSeconds) return `s-maxage=${revalidateSeconds}`;
|
|
53
54
|
return `s-maxage=${revalidateSeconds}, stale-while-revalidate=${expireSeconds - revalidateSeconds}`;
|
|
@@ -60,7 +61,7 @@ function buildRevalidateCacheControl(revalidateSeconds, expireSeconds) {
|
|
|
60
61
|
* fallback so older cache entries are not treated as newly fresh downstream.
|
|
61
62
|
*/
|
|
62
63
|
function buildCachedRevalidateCacheControl(cacheState, revalidateSeconds, expireSeconds) {
|
|
63
|
-
if (revalidateSeconds === Infinity) return STATIC_CACHE_CONTROL;
|
|
64
|
+
if (revalidateSeconds === false || revalidateSeconds === Infinity) return STATIC_CACHE_CONTROL;
|
|
64
65
|
if (cacheState === "STALE" && expireSeconds === void 0) return STALE_REVALIDATE_CACHE_CONTROL;
|
|
65
66
|
return buildRevalidateCacheControl(revalidateSeconds, expireSeconds);
|
|
66
67
|
}
|
|
@@ -53,7 +53,9 @@ clientTraceMetadata?: readonly string[], htmlLimitedBots?: string,
|
|
|
53
53
|
* `nextConfig?.reactStrictMode === true`.
|
|
54
54
|
*/
|
|
55
55
|
|
|
56
|
-
reactStrictMode?: boolean
|
|
56
|
+
reactStrictMode?: boolean, /** Next.js `expireTime`, used when formatting terminal GSP responses. */
|
|
57
|
+
|
|
58
|
+
expireTime?: number, crossOrigin?: string): (req: IncomingMessage, res: ServerResponse, url: string, /** Status code override — propagated from middleware rewrite status. */
|
|
57
59
|
|
|
58
60
|
statusCode?: number,
|
|
59
61
|
/**
|