vinext 0.1.1 → 0.1.2
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 +2 -5
- package/dist/build/client-build-config.d.ts +7 -1
- package/dist/build/client-build-config.js +9 -1
- package/dist/check.js +4 -3
- package/dist/client/navigation-runtime.d.ts +3 -2
- package/dist/client/window-next.d.ts +6 -4
- package/dist/config/config-matchers.d.ts +11 -4
- package/dist/config/config-matchers.js +15 -2
- package/dist/config/next-config.d.ts +13 -0
- package/dist/config/next-config.js +2 -0
- package/dist/deploy.js +9 -2
- package/dist/entries/app-rsc-entry.js +7 -1
- package/dist/entries/pages-client-entry.js +1 -1
- package/dist/entries/pages-server-entry.js +7 -6
- package/dist/index.d.ts +0 -2
- package/dist/index.js +86 -78
- package/dist/plugins/dynamic-preload-metadata.d.ts +13 -0
- package/dist/plugins/dynamic-preload-metadata.js +415 -0
- package/dist/plugins/og-assets.js +2 -2
- package/dist/plugins/optimize-imports.d.ts +8 -4
- package/dist/plugins/optimize-imports.js +16 -12
- package/dist/plugins/sass.d.ts +53 -24
- package/dist/plugins/sass.js +249 -1
- package/dist/plugins/wasm-module-import.d.ts +15 -0
- package/dist/plugins/wasm-module-import.js +50 -0
- package/dist/routing/app-route-graph.d.ts +23 -1
- package/dist/routing/app-route-graph.js +47 -8
- package/dist/routing/file-matcher.js +1 -1
- package/dist/server/app-browser-entry.js +108 -213
- package/dist/server/app-browser-error.d.ts +4 -1
- package/dist/server/app-browser-error.js +7 -1
- package/dist/server/app-browser-history-controller.d.ts +104 -0
- package/dist/server/app-browser-history-controller.js +210 -0
- package/dist/server/app-browser-navigation-controller.d.ts +3 -2
- package/dist/server/app-browser-navigation-controller.js +10 -7
- package/dist/server/app-browser-rsc-redirect.d.ts +11 -2
- package/dist/server/app-browser-rsc-redirect.js +30 -8
- package/dist/server/app-browser-state.js +4 -7
- package/dist/server/app-browser-visible-commit.js +1 -1
- package/dist/server/app-fallback-renderer.d.ts +2 -1
- package/dist/server/app-fallback-renderer.js +3 -1
- package/dist/server/app-middleware.js +1 -0
- package/dist/server/app-optimistic-routing.js +22 -1
- package/dist/server/app-page-boundary-render.d.ts +2 -1
- package/dist/server/app-page-boundary-render.js +4 -2
- package/dist/server/app-page-cache.js +9 -7
- package/dist/server/app-page-dispatch.d.ts +8 -0
- package/dist/server/app-page-dispatch.js +18 -5
- package/dist/server/app-page-element-builder.d.ts +22 -2
- package/dist/server/app-page-element-builder.js +37 -8
- package/dist/server/app-page-execution.d.ts +1 -1
- package/dist/server/app-page-execution.js +32 -17
- package/dist/server/app-page-render.d.ts +1 -1
- package/dist/server/app-page-render.js +7 -14
- package/dist/server/app-page-request.d.ts +1 -0
- package/dist/server/app-page-request.js +3 -2
- package/dist/server/app-page-response.js +1 -1
- package/dist/server/app-page-route-wiring.d.ts +3 -1
- package/dist/server/app-page-route-wiring.js +8 -7
- package/dist/server/app-page-stream.d.ts +1 -6
- package/dist/server/app-page-stream.js +1 -4
- package/dist/server/app-route-handler-response.js +11 -10
- package/dist/server/app-route-handler-runtime.js +12 -1
- package/dist/server/app-rsc-handler.js +1 -1
- package/dist/server/app-rsc-response-finalizer.js +1 -1
- package/dist/server/app-server-action-execution.d.ts +11 -0
- package/dist/server/app-server-action-execution.js +5 -2
- package/dist/server/app-ssr-entry.js +2 -2
- package/dist/server/app-ssr-stream.js +9 -1
- package/dist/server/dev-lockfile.js +2 -1
- package/dist/server/dev-server.js +43 -12
- package/dist/server/headers.d.ts +8 -1
- package/dist/server/headers.js +8 -1
- package/dist/server/instrumentation-runtime.d.ts +6 -0
- package/dist/server/instrumentation-runtime.js +8 -0
- package/dist/server/isr-decision.d.ts +79 -0
- package/dist/server/isr-decision.js +70 -0
- package/dist/server/metadata-route-response.js +5 -3
- package/dist/server/middleware-runtime.d.ts +13 -0
- package/dist/server/middleware-runtime.js +11 -7
- package/dist/server/middleware.js +1 -0
- package/dist/server/navigation-planner.d.ts +62 -1
- package/dist/server/navigation-planner.js +188 -0
- package/dist/server/navigation-trace.d.ts +11 -1
- package/dist/server/navigation-trace.js +11 -1
- package/dist/server/normalize-path.d.ts +0 -8
- package/dist/server/normalize-path.js +3 -1
- package/dist/server/otel-tracer-extension.d.ts +45 -0
- package/dist/server/otel-tracer-extension.js +89 -0
- package/dist/server/pages-api-route.d.ts +14 -3
- package/dist/server/pages-api-route.js +6 -1
- package/dist/server/pages-asset-tags.d.ts +15 -4
- package/dist/server/pages-asset-tags.js +18 -12
- package/dist/server/pages-data-route.js +5 -1
- package/dist/server/pages-node-compat.d.ts +3 -11
- package/dist/server/pages-node-compat.js +174 -121
- package/dist/server/pages-page-data.d.ts +28 -0
- package/dist/server/pages-page-data.js +61 -17
- package/dist/server/pages-page-handler.d.ts +1 -0
- package/dist/server/pages-page-handler.js +22 -6
- package/dist/server/pages-page-response.d.ts +45 -1
- package/dist/server/pages-page-response.js +66 -5
- package/dist/server/pages-readiness.d.ts +1 -1
- package/dist/server/pages-request-pipeline.d.ts +15 -1
- package/dist/server/pages-request-pipeline.js +23 -2
- package/dist/server/prod-server.d.ts +39 -1
- package/dist/server/prod-server.js +98 -34
- package/dist/shims/cache-runtime.js +9 -2
- package/dist/shims/dynamic-preload-chunks.d.ts +8 -0
- package/dist/shims/dynamic-preload-chunks.js +77 -0
- package/dist/shims/dynamic.d.ts +4 -0
- package/dist/shims/dynamic.js +4 -2
- package/dist/shims/error-boundary.d.ts +4 -4
- package/dist/shims/error.js +37 -11
- package/dist/shims/fetch-cache.d.ts +9 -1
- package/dist/shims/fetch-cache.js +11 -1
- package/dist/shims/head.js +6 -1
- package/dist/shims/headers.d.ts +16 -2
- package/dist/shims/headers.js +37 -1
- package/dist/shims/image-config.js +7 -1
- package/dist/shims/internal/app-route-detection.d.ts +6 -3
- package/dist/shims/internal/app-route-detection.js +10 -6
- package/dist/shims/internal/app-router-context.d.ts +5 -0
- package/dist/shims/metadata.d.ts +6 -2
- package/dist/shims/metadata.js +32 -14
- package/dist/shims/navigation.d.ts +7 -16
- package/dist/shims/navigation.js +33 -16
- package/dist/shims/router.js +28 -1
- package/dist/shims/script-nonce-context.d.ts +1 -1
- package/dist/shims/script-nonce-context.js +11 -3
- package/dist/shims/server.d.ts +17 -1
- package/dist/shims/server.js +31 -6
- package/dist/shims/slot.js +1 -1
- package/dist/shims/unified-request-context.js +1 -0
- package/dist/typegen.js +1 -0
- package/dist/utils/client-build-manifest.js +15 -5
- package/dist/utils/client-runtime-metadata.d.ts +45 -0
- package/dist/utils/client-runtime-metadata.js +63 -0
- package/dist/utils/hash.d.ts +17 -1
- package/dist/utils/hash.js +36 -1
- package/dist/utils/lazy-chunks.d.ts +27 -1
- package/dist/utils/lazy-chunks.js +65 -1
- package/dist/utils/manifest-paths.d.ts +20 -2
- package/dist/utils/manifest-paths.js +38 -3
- package/dist/utils/path.d.ts +2 -1
- package/dist/utils/path.js +5 -1
- package/package.json +2 -2
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { addBasePathToPathname } from "../utils/base-path.js";
|
|
1
2
|
import { buildRequestHeadersFromMiddlewareResponse } from "./middleware-request-headers.js";
|
|
2
3
|
import { NextRequest, RequestCookies, sealRequestCookies, sealRequestHeaders } from "../shims/server.js";
|
|
3
4
|
//#region src/server/app-route-handler-runtime.ts
|
|
@@ -156,7 +157,17 @@ function createTrackedAppRouteRequest(request, options = {}) {
|
|
|
156
157
|
}
|
|
157
158
|
} });
|
|
158
159
|
};
|
|
159
|
-
const wrapRequest = (
|
|
160
|
+
const wrapRequest = (rawInput) => {
|
|
161
|
+
let input = rawInput;
|
|
162
|
+
if (options.basePath) {
|
|
163
|
+
const inputUrl = new URL(rawInput.url);
|
|
164
|
+
const prefixedPathname = addBasePathToPathname(inputUrl.pathname, options.basePath);
|
|
165
|
+
if (prefixedPathname !== inputUrl.pathname) {
|
|
166
|
+
inputUrl.pathname = prefixedPathname;
|
|
167
|
+
const bodySource = rawInput.body && !rawInput.bodyUsed ? rawInput.clone() : rawInput;
|
|
168
|
+
input = new Request(inputUrl, bodySource);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
160
171
|
const requestHeaders = options.middlewareHeaders ? buildRequestHeadersFromMiddlewareResponse(input.headers, options.middlewareHeaders) : null;
|
|
161
172
|
const requestWithOverrides = requestHeaders ? rebuildRequestWithHeaders(input, requestHeaders) : input;
|
|
162
173
|
const nextRequest = requestWithOverrides instanceof NextRequest ? requestWithOverrides : new NextRequest(requestWithOverrides, { nextConfig: nextConfig ?? void 0 });
|
|
@@ -14,10 +14,10 @@ import { isImageOptimizationPath } from "./image-optimization.js";
|
|
|
14
14
|
import { mergeMiddlewareResponseHeaders } from "./middleware-response-headers.js";
|
|
15
15
|
import "./app-page-response.js";
|
|
16
16
|
import { prerenderRouteParamsPayloadMatchesRoute, readTrustedPrerenderRouteParams, serializePrerenderRouteParamsHeader } from "./prerender-route-params.js";
|
|
17
|
-
import { pickRootParams, setRootParams } from "../shims/root-params.js";
|
|
18
17
|
import { applyAppMiddleware } from "./app-middleware.js";
|
|
19
18
|
import { buildPageCacheTags } from "./implicit-tags.js";
|
|
20
19
|
import { buildPostMwRequestContext } from "./app-post-middleware-context.js";
|
|
20
|
+
import { pickRootParams, setRootParams } from "../shims/root-params.js";
|
|
21
21
|
import { handleAppPrerenderEndpoint } from "./app-prerender-endpoints.js";
|
|
22
22
|
import { flattenErrorCauses } from "../utils/error-cause.js";
|
|
23
23
|
import { finalizeAppRscResponse } from "./app-rsc-response-finalizer.js";
|
|
@@ -3,9 +3,9 @@ import { hasBasePath, stripBasePath } from "../utils/base-path.js";
|
|
|
3
3
|
import "./headers.js";
|
|
4
4
|
import { normalizePath } from "./normalize-path.js";
|
|
5
5
|
import { applyConfigHeadersToResponse } from "./request-pipeline.js";
|
|
6
|
+
import { applyCdnResponseHeaders } from "./cache-control.js";
|
|
6
7
|
import { VINEXT_RSC_VARY_HEADER } from "./app-rsc-cache-busting.js";
|
|
7
8
|
import { normalizeDefaultLocalePathname } from "./pages-i18n.js";
|
|
8
|
-
import { applyCdnResponseHeaders } from "./cache-control.js";
|
|
9
9
|
import { mergeVaryHeader } from "./middleware-response-headers.js";
|
|
10
10
|
//#region src/server/app-rsc-response-finalizer.ts
|
|
11
11
|
/**
|
|
@@ -31,6 +31,17 @@ type AppServerActionRoute = {
|
|
|
31
31
|
pattern: string;
|
|
32
32
|
routeHandler?: unknown;
|
|
33
33
|
routeSegments?: readonly string[];
|
|
34
|
+
params?: readonly string[] | null;
|
|
35
|
+
slots?: Readonly<Record<string, {
|
|
36
|
+
default?: {
|
|
37
|
+
default?: unknown;
|
|
38
|
+
} | null;
|
|
39
|
+
page?: {
|
|
40
|
+
default?: unknown;
|
|
41
|
+
} | null;
|
|
42
|
+
slotPatternParts?: readonly string[] | null;
|
|
43
|
+
slotParamNames?: readonly string[] | null;
|
|
44
|
+
}>> | null;
|
|
34
45
|
};
|
|
35
46
|
/**
|
|
36
47
|
* Side-effect headers captured during a progressive (no-JS) server action's
|
|
@@ -15,6 +15,7 @@ import { applyEdgeRuntimeHeader } from "./app-page-response.js";
|
|
|
15
15
|
import { getNextErrorDigest, parseNextHttpErrorDigest, parseNextRedirectDigest } from "./next-error-digest.js";
|
|
16
16
|
import { createServerActionNotFoundResponse, getServerActionNotFoundMessage, isServerActionNotFoundError } from "./server-action-not-found.js";
|
|
17
17
|
import { deferUntilStreamConsumed } from "./app-page-stream.js";
|
|
18
|
+
import { resolveAppPageNavigationParams } from "./app-page-element-builder.js";
|
|
18
19
|
import { resolveAppPageActionRerenderTarget } from "./app-page-request.js";
|
|
19
20
|
import { buildPageCacheTags } from "./implicit-tags.js";
|
|
20
21
|
import { getSetCookieName } from "./cookie-utils.js";
|
|
@@ -591,10 +592,11 @@ async function handleServerActionRscRequest(options) {
|
|
|
591
592
|
url: redirectTarget
|
|
592
593
|
});
|
|
593
594
|
setHeadersContext(headersContextFromRequest(redirectRenderRequest));
|
|
595
|
+
const redirectNavigationParams = resolveAppPageNavigationParams(targetMatch.route, targetMatch.params, targetPathname, null);
|
|
594
596
|
options.setNavigationContext({
|
|
595
597
|
pathname: targetPathname,
|
|
596
598
|
searchParams: redirectTarget.searchParams,
|
|
597
|
-
params:
|
|
599
|
+
params: redirectNavigationParams
|
|
598
600
|
});
|
|
599
601
|
setCurrentFetchCacheMode(options.resolveRouteFetchCacheMode?.(targetMatch.route) ?? null);
|
|
600
602
|
setCurrentFetchSoftTags(buildServerActionPageTags(targetMatch.route, targetPathname));
|
|
@@ -667,10 +669,11 @@ async function handleServerActionRscRequest(options) {
|
|
|
667
669
|
isRscRequest: options.isRscRequest,
|
|
668
670
|
toInterceptOpts: options.toInterceptOpts
|
|
669
671
|
});
|
|
672
|
+
const resolvedActionNavigationParams = resolveAppPageNavigationParams(actionRerenderTarget.route, actionRerenderTarget.navigationParams, options.cleanPathname, actionRerenderTarget.interceptOpts);
|
|
670
673
|
options.setNavigationContext({
|
|
671
674
|
pathname: options.cleanPathname,
|
|
672
675
|
searchParams: options.searchParams,
|
|
673
|
-
params:
|
|
676
|
+
params: resolvedActionNavigationParams
|
|
674
677
|
});
|
|
675
678
|
await options.ensureRouteLoaded?.(actionRerenderTarget.route);
|
|
676
679
|
setCurrentFetchCacheMode(options.resolveRouteFetchCacheMode?.(actionRerenderTarget.route) ?? null);
|
|
@@ -12,12 +12,12 @@ import { getClientTraceMetadataHTML } from "./client-trace-metadata.js";
|
|
|
12
12
|
import { BfcacheStateKeyMapContext, ElementsContext, Slot } from "../shims/slot.js";
|
|
13
13
|
import { createSsrErrorMetaRenderer } from "./app-ssr-error-meta.js";
|
|
14
14
|
import { createNavigationRuntimeRscMetadataScript, createRscEmbedTransform, createTickBufferedTransform } from "./app-ssr-stream.js";
|
|
15
|
-
import { BeforeInteractiveContext } from "../shims/before-interactive-context.js";
|
|
16
|
-
import { runWithRootParamsScope } from "../shims/root-params.js";
|
|
17
15
|
import { createBfcacheSegmentStateKeyMap, createInitialBfcacheIdMap } from "./app-browser-state.js";
|
|
18
16
|
import { RSC_FORM_STATE_GLOBAL } from "./app-browser-hydration.js";
|
|
19
17
|
import { createClientReferencePreloader } from "./app-client-reference-preloader.js";
|
|
20
18
|
import { deferUntilStreamConsumed } from "./app-page-stream.js";
|
|
19
|
+
import { runWithRootParamsScope } from "../shims/root-params.js";
|
|
20
|
+
import { BeforeInteractiveContext } from "../shims/before-interactive-context.js";
|
|
21
21
|
import { createInitialDevServerErrorScript } from "./dev-initial-server-error.js";
|
|
22
22
|
import { Fragment, createElement, use } from "react";
|
|
23
23
|
import { renderToReadableStream, renderToStaticMarkup } from "react-dom/server.edge";
|
|
@@ -89,6 +89,12 @@ function fixPreloadAs(html) {
|
|
|
89
89
|
const LINK_TAG_RE = /<link\b[^>]*>/gi;
|
|
90
90
|
const HTML_REWRITE_EXCLUDED_REGION_RE = /<!--[\s\S]*?-->|<(script|style|textarea|title)\b[^>]*>[\s\S]*?<\/\1\s*>/gi;
|
|
91
91
|
const HTML_REWRITE_EXCLUDED_REGION_START_RE = /<!--|<(script|style|textarea|title)\b[^>]*>/gi;
|
|
92
|
+
const CLOSE_TAG_RES = {
|
|
93
|
+
script: /<\/script\s*>/i,
|
|
94
|
+
style: /<\/style\s*>/i,
|
|
95
|
+
textarea: /<\/textarea\s*>/i,
|
|
96
|
+
title: /<\/title\s*>/i
|
|
97
|
+
};
|
|
92
98
|
function getHtmlAttribute(tag, name) {
|
|
93
99
|
const attrRe = /\s([^\s"'=<>`]+)(?:\s*=\s*(?:"([^"]*)"|'([^']*)'|([^\s"'=<>`]+)))?/g;
|
|
94
100
|
let match;
|
|
@@ -141,7 +147,9 @@ function findTrailingOpenHtmlRewriteExcludedRegionStart(html) {
|
|
|
141
147
|
}
|
|
142
148
|
const tagName = match[1]?.toLowerCase();
|
|
143
149
|
if (!tagName) continue;
|
|
144
|
-
const
|
|
150
|
+
const closeTagRe = CLOSE_TAG_RES[tagName];
|
|
151
|
+
if (!closeTagRe) continue;
|
|
152
|
+
const close = closeTagRe.exec(html.slice(HTML_REWRITE_EXCLUDED_REGION_START_RE.lastIndex));
|
|
145
153
|
if (!close) return start;
|
|
146
154
|
HTML_REWRITE_EXCLUDED_REGION_START_RE.lastIndex += close.index + close[0].length;
|
|
147
155
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { normalizePathSeparators } from "../utils/path.js";
|
|
1
2
|
import fs from "node:fs";
|
|
2
3
|
import path from "node:path";
|
|
3
4
|
//#region src/server/dev-lockfile.ts
|
|
@@ -98,7 +99,7 @@ function formatAlreadyRunningError(opts) {
|
|
|
98
99
|
if (!existing) return [
|
|
99
100
|
"Another vinext dev server appears to be running in this directory.",
|
|
100
101
|
"",
|
|
101
|
-
`Stale lock file: ${path.relative(cwd, lockfilePath)}`,
|
|
102
|
+
`Stale lock file: ${normalizePathSeparators(path.relative(cwd, lockfilePath))}`,
|
|
102
103
|
"Remove it manually if no server is running, then re-run `vinext dev`."
|
|
103
104
|
].join("\n");
|
|
104
105
|
const killCommand = process.platform === "win32" ? `taskkill /PID ${existing.pid} /F` : `kill ${existing.pid}`;
|
|
@@ -2,10 +2,13 @@ import { createRequestContext, runWithRequestContext } from "../shims/unified-re
|
|
|
2
2
|
import { createValidFileMatcher, findFileWithExtensions } from "../routing/file-matcher.js";
|
|
3
3
|
import { patternToNextFormat } from "../routing/route-validation.js";
|
|
4
4
|
import { matchRoute } from "../routing/pages-router.js";
|
|
5
|
+
import { NEXTJS_DEPLOYMENT_ID_HEADER } from "./headers.js";
|
|
5
6
|
import { normalizeStaticPathname } from "../routing/route-pattern.js";
|
|
6
7
|
import { importModule, reportRequestError } from "./instrumentation.js";
|
|
7
8
|
import { buildCacheStateHeaders } from "./cache-headers.js";
|
|
8
9
|
import { _runWithCacheState } from "../shims/cache.js";
|
|
10
|
+
import { NEVER_CACHE_CONTROL, NO_STORE_CACHE_CONTROL } from "./cache-control.js";
|
|
11
|
+
import { buildMissIsrCacheControl, decideIsr } from "./isr-decision.js";
|
|
9
12
|
import { PRERENDER_REVALIDATE_HEADER, buildPagesCacheValue, getRevalidateDuration, isOnDemandRevalidateRequest, isrCacheKey, isrGet, isrSet, setRevalidateDuration, triggerBackgroundRegeneration } from "./isr-cache.js";
|
|
10
13
|
import { ensureFetchPatch, runWithFetchCache } from "../shims/fetch-cache.js";
|
|
11
14
|
import { runWithPrivateCache } from "../shims/cache-runtime.js";
|
|
@@ -62,7 +65,10 @@ function writeGsspRedirect(res, redirect, isDataReq) {
|
|
|
62
65
|
let dest = redirect.destination;
|
|
63
66
|
if (!dest.startsWith("http://") && !dest.startsWith("https://")) dest = dest.replace(/^[\\/]+/, "/");
|
|
64
67
|
if (isDataReq) {
|
|
65
|
-
|
|
68
|
+
const deploymentId = process.env.__VINEXT_DEPLOYMENT_ID || process.env.NEXT_DEPLOYMENT_ID;
|
|
69
|
+
const dataHeaders = { "Content-Type": "application/json" };
|
|
70
|
+
if (deploymentId) dataHeaders[NEXTJS_DEPLOYMENT_ID_HEADER] = deploymentId;
|
|
71
|
+
res.writeHead(200, dataHeaders);
|
|
66
72
|
res.end(JSON.stringify({ pageProps: {
|
|
67
73
|
__N_REDIRECT: dest,
|
|
68
74
|
__N_REDIRECT_STATUS: status
|
|
@@ -228,7 +234,10 @@ function createSSRHandler(server, runner, routes, pagesDir, i18nConfig, fileMatc
|
|
|
228
234
|
const match = matchRoute(localeStrippedUrl, routes);
|
|
229
235
|
if (!match) {
|
|
230
236
|
if (isDataReq) {
|
|
231
|
-
|
|
237
|
+
const deploymentId = process.env.__VINEXT_DEPLOYMENT_ID || process.env.NEXT_DEPLOYMENT_ID;
|
|
238
|
+
const notFoundHeaders = { "Content-Type": "application/json" };
|
|
239
|
+
if (deploymentId) notFoundHeaders[NEXTJS_DEPLOYMENT_ID_HEADER] = deploymentId;
|
|
240
|
+
res.writeHead(404, notFoundHeaders);
|
|
232
241
|
res.end("{}");
|
|
233
242
|
return;
|
|
234
243
|
}
|
|
@@ -325,7 +334,10 @@ function createSSRHandler(server, runner, routes, pagesDir, i18nConfig, fileMatc
|
|
|
325
334
|
});
|
|
326
335
|
if (fallback === false && !isValidPath) {
|
|
327
336
|
if (isDataReq) {
|
|
328
|
-
|
|
337
|
+
const deploymentId = process.env.__VINEXT_DEPLOYMENT_ID || process.env.NEXT_DEPLOYMENT_ID;
|
|
338
|
+
const notFoundHeaders = { "Content-Type": "application/json" };
|
|
339
|
+
if (deploymentId) notFoundHeaders[NEXTJS_DEPLOYMENT_ID_HEADER] = deploymentId;
|
|
340
|
+
res.writeHead(404, notFoundHeaders);
|
|
329
341
|
res.end("{}");
|
|
330
342
|
return;
|
|
331
343
|
}
|
|
@@ -369,7 +381,10 @@ function createSSRHandler(server, runner, routes, pagesDir, i18nConfig, fileMatc
|
|
|
369
381
|
}
|
|
370
382
|
if (result && "notFound" in result && result.notFound) {
|
|
371
383
|
if (isDataReq) {
|
|
372
|
-
|
|
384
|
+
const deploymentId = process.env.__VINEXT_DEPLOYMENT_ID || process.env.NEXT_DEPLOYMENT_ID;
|
|
385
|
+
const notFoundHeaders = { "Content-Type": "application/json" };
|
|
386
|
+
if (deploymentId) notFoundHeaders[NEXTJS_DEPLOYMENT_ID_HEADER] = deploymentId;
|
|
387
|
+
res.writeHead(404, notFoundHeaders);
|
|
373
388
|
res.end("{}");
|
|
374
389
|
return;
|
|
375
390
|
}
|
|
@@ -385,7 +400,7 @@ function createSSRHandler(server, runner, routes, pagesDir, i18nConfig, fileMatc
|
|
|
385
400
|
if (Array.isArray(val)) gsspExtraHeaders[key] = val.map(String);
|
|
386
401
|
else gsspExtraHeaders[key] = String(val);
|
|
387
402
|
}
|
|
388
|
-
if (!Object.keys(gsspExtraHeaders).some((k) => k.toLowerCase() === "cache-control")) gsspExtraHeaders["Cache-Control"] =
|
|
403
|
+
if (!Object.keys(gsspExtraHeaders).some((k) => k.toLowerCase() === "cache-control")) gsspExtraHeaders["Cache-Control"] = NEVER_CACHE_CONTROL;
|
|
389
404
|
}
|
|
390
405
|
const responseHeaders = typeof res.getHeaders === "function" ? res.getHeaders() : void 0;
|
|
391
406
|
const scriptNonce = getScriptNonceFromNodeHeaderSources(req.headers, responseHeaders);
|
|
@@ -405,11 +420,15 @@ function createSSRHandler(server, runner, routes, pagesDir, i18nConfig, fileMatc
|
|
|
405
420
|
if (!isOnDemandRevalidate && cached && !cached.isStale && cached.value.value?.kind === "PAGES" && !scriptNonce && !isDataReq) {
|
|
406
421
|
const cachedHtml = cached.value.value.html;
|
|
407
422
|
const transformedHtml = await server.transformIndexHtml(url, cachedHtml);
|
|
408
|
-
const
|
|
423
|
+
const { cacheControl: hitCacheControl } = decideIsr({
|
|
424
|
+
cacheState: "HIT",
|
|
425
|
+
kind: "dev",
|
|
426
|
+
revalidateSeconds: getRevalidateDuration(cacheKey) ?? 60
|
|
427
|
+
});
|
|
409
428
|
const hitHeaders = {
|
|
410
429
|
"Content-Type": "text/html",
|
|
411
430
|
...buildCacheStateHeaders("HIT"),
|
|
412
|
-
"Cache-Control":
|
|
431
|
+
"Cache-Control": hitCacheControl
|
|
413
432
|
};
|
|
414
433
|
if (earlyFontLinkHeader) hitHeaders["Link"] = earlyFontLinkHeader;
|
|
415
434
|
res.writeHead(200, hitHeaders);
|
|
@@ -498,11 +517,15 @@ function createSSRHandler(server, runner, routes, pagesDir, i18nConfig, fileMatc
|
|
|
498
517
|
routePath: route.pattern,
|
|
499
518
|
routeType: "render"
|
|
500
519
|
});
|
|
501
|
-
const
|
|
520
|
+
const { cacheControl: staleCacheControl } = decideIsr({
|
|
521
|
+
cacheState: "STALE",
|
|
522
|
+
kind: "dev",
|
|
523
|
+
revalidateSeconds: getRevalidateDuration(cacheKey) ?? 60
|
|
524
|
+
});
|
|
502
525
|
const staleHeaders = {
|
|
503
526
|
"Content-Type": "text/html",
|
|
504
527
|
...buildCacheStateHeaders("STALE"),
|
|
505
|
-
"Cache-Control":
|
|
528
|
+
"Cache-Control": staleCacheControl
|
|
506
529
|
};
|
|
507
530
|
if (earlyFontLinkHeader) staleHeaders["Link"] = earlyFontLinkHeader;
|
|
508
531
|
res.writeHead(200, staleHeaders);
|
|
@@ -524,7 +547,10 @@ function createSSRHandler(server, runner, routes, pagesDir, i18nConfig, fileMatc
|
|
|
524
547
|
}
|
|
525
548
|
if (result && "notFound" in result && result.notFound) {
|
|
526
549
|
if (isDataReq) {
|
|
527
|
-
|
|
550
|
+
const deploymentId = process.env.__VINEXT_DEPLOYMENT_ID || process.env.NEXT_DEPLOYMENT_ID;
|
|
551
|
+
const notFoundHeaders = { "Content-Type": "application/json" };
|
|
552
|
+
if (deploymentId) notFoundHeaders[NEXTJS_DEPLOYMENT_ID_HEADER] = deploymentId;
|
|
553
|
+
res.writeHead(404, notFoundHeaders);
|
|
528
554
|
res.end("{}");
|
|
529
555
|
return;
|
|
530
556
|
}
|
|
@@ -554,6 +580,11 @@ function createSSRHandler(server, runner, routes, pagesDir, i18nConfig, fileMatc
|
|
|
554
580
|
if (isDataReq) {
|
|
555
581
|
const dataHeaders = { "Content-Type": "application/json" };
|
|
556
582
|
if (gsspExtraHeaders) for (const [k, v] of Object.entries(gsspExtraHeaders)) dataHeaders[k] = v;
|
|
583
|
+
const dataRoutePattern = patternToNextFormat(route.pattern);
|
|
584
|
+
if (dataRoutePattern !== "/_error" && dataRoutePattern !== "/500") {
|
|
585
|
+
const deploymentId = process.env.__VINEXT_DEPLOYMENT_ID || process.env.NEXT_DEPLOYMENT_ID;
|
|
586
|
+
if (deploymentId) dataHeaders[NEXTJS_DEPLOYMENT_ID_HEADER] = deploymentId;
|
|
587
|
+
}
|
|
557
588
|
res.writeHead(statusCode ?? 200, dataHeaders);
|
|
558
589
|
res.end(JSON.stringify({ pageProps }));
|
|
559
590
|
_renderEnd = now();
|
|
@@ -677,9 +708,9 @@ hydrate();
|
|
|
677
708
|
} catch {}
|
|
678
709
|
const allScripts = `${nextDataScript}\n ${createInlineScriptTag(`window.__VINEXT_PAGE_PATTERNS__=${safeJsonStringify(pagePatterns)}`, scriptNonce)}\n ${hydrationScript}`;
|
|
679
710
|
const extraHeaders = { ...gsspExtraHeaders };
|
|
680
|
-
if (isrRevalidateSeconds) if (scriptNonce) extraHeaders["Cache-Control"] =
|
|
711
|
+
if (isrRevalidateSeconds) if (scriptNonce) extraHeaders["Cache-Control"] = NO_STORE_CACHE_CONTROL;
|
|
681
712
|
else {
|
|
682
|
-
extraHeaders["Cache-Control"] =
|
|
713
|
+
extraHeaders["Cache-Control"] = buildMissIsrCacheControl(isrRevalidateSeconds);
|
|
683
714
|
Object.assign(extraHeaders, buildCacheStateHeaders("MISS"));
|
|
684
715
|
}
|
|
685
716
|
if (allFontPreloads.length > 0) extraHeaders["Link"] = allFontPreloads.map((p) => `<${p.href}>; rel=preload; as=font; type=${p.type}; crossorigin`).join(", ");
|
package/dist/server/headers.d.ts
CHANGED
|
@@ -60,6 +60,13 @@ declare const RSC_ACTION_HEADER = "x-rsc-action";
|
|
|
60
60
|
declare const NEXT_ACTION_HEADER = "next-action";
|
|
61
61
|
/** Next.js action-not-found indicator (value "1"). */
|
|
62
62
|
declare const NEXTJS_ACTION_NOT_FOUND_HEADER = "x-nextjs-action-not-found";
|
|
63
|
+
/**
|
|
64
|
+
* Deployment ID header used by the Pages Router for deployment-skew
|
|
65
|
+
* protection. Set on every `/_next/data/` response so the client can detect
|
|
66
|
+
* when a new deployment has been rolled out and trigger a hard navigation.
|
|
67
|
+
* Mirrors `NEXT_NAV_DEPLOYMENT_ID_HEADER` from Next.js `lib/constants.ts`.
|
|
68
|
+
*/
|
|
69
|
+
declare const NEXTJS_DEPLOYMENT_ID_HEADER = "x-nextjs-deployment-id";
|
|
63
70
|
/** Forwarded action marker — set when a request has already been forwarded between workers. */
|
|
64
71
|
declare const ACTION_FORWARDED_HEADER = "x-action-forwarded";
|
|
65
72
|
/** Indicates revalidation occurred — value is JSON kind (1 = path/tag, 2 = dynamic-only). */
|
|
@@ -100,4 +107,4 @@ declare const INTERNAL_HEADERS: string[];
|
|
|
100
107
|
/** Vinext-only internal headers stripped alongside Next.js protocol internals. */
|
|
101
108
|
declare const VINEXT_INTERNAL_HEADERS: string[];
|
|
102
109
|
//#endregion
|
|
103
|
-
export { ACTION_FORWARDED_HEADER, ACTION_REDIRECT_HEADER, ACTION_REDIRECT_STATUS_HEADER, ACTION_REDIRECT_TYPE_HEADER, ACTION_REVALIDATED_HEADER, FLIGHT_HEADERS, INTERNAL_HEADERS, MIDDLEWARE_HEADER_PREFIX, MIDDLEWARE_NEXT_HEADER, MIDDLEWARE_OVERRIDE_HEADERS, MIDDLEWARE_REQUEST_HEADER_PREFIX, MIDDLEWARE_REWRITE_HEADER, MIDDLEWARE_SET_COOKIE_HEADER, NEXTJS_ACTION_NOT_FOUND_HEADER, NEXTJS_CACHE_HEADER, NEXT_ACTION_HEADER, NEXT_ROUTER_PREFETCH_HEADER, NEXT_ROUTER_SEGMENT_PREFETCH_HEADER, NEXT_ROUTER_STATE_TREE_HEADER, NEXT_URL_HEADER, RSC_ACTION_HEADER, RSC_HEADER, VINEXT_CACHE_HEADER, VINEXT_CLIENT_REUSE_MANIFEST_HEADER, VINEXT_DYNAMIC_STALE_TIME_HEADER, VINEXT_INTERCEPTION_CONTEXT_HEADER, VINEXT_INTERNAL_HEADERS, VINEXT_MOUNTED_SLOTS_HEADER, VINEXT_MW_CTX_HEADER, VINEXT_PARAMS_HEADER, VINEXT_PRERENDER_ROUTE_PARAMS_HEADER, VINEXT_PRERENDER_SECRET_HEADER, VINEXT_REVALIDATE_HEADER, VINEXT_RSC_MARKER_HEADER, VINEXT_RSC_REDIRECT_HEADER, VINEXT_RSC_RENDER_MODE_HEADER, VINEXT_STATIC_FILE_HEADER, VINEXT_TIMING_HEADER };
|
|
110
|
+
export { ACTION_FORWARDED_HEADER, ACTION_REDIRECT_HEADER, ACTION_REDIRECT_STATUS_HEADER, ACTION_REDIRECT_TYPE_HEADER, ACTION_REVALIDATED_HEADER, FLIGHT_HEADERS, INTERNAL_HEADERS, MIDDLEWARE_HEADER_PREFIX, MIDDLEWARE_NEXT_HEADER, MIDDLEWARE_OVERRIDE_HEADERS, MIDDLEWARE_REQUEST_HEADER_PREFIX, MIDDLEWARE_REWRITE_HEADER, MIDDLEWARE_SET_COOKIE_HEADER, NEXTJS_ACTION_NOT_FOUND_HEADER, NEXTJS_CACHE_HEADER, NEXTJS_DEPLOYMENT_ID_HEADER, NEXT_ACTION_HEADER, NEXT_ROUTER_PREFETCH_HEADER, NEXT_ROUTER_SEGMENT_PREFETCH_HEADER, NEXT_ROUTER_STATE_TREE_HEADER, NEXT_URL_HEADER, RSC_ACTION_HEADER, RSC_HEADER, VINEXT_CACHE_HEADER, VINEXT_CLIENT_REUSE_MANIFEST_HEADER, VINEXT_DYNAMIC_STALE_TIME_HEADER, VINEXT_INTERCEPTION_CONTEXT_HEADER, VINEXT_INTERNAL_HEADERS, VINEXT_MOUNTED_SLOTS_HEADER, VINEXT_MW_CTX_HEADER, VINEXT_PARAMS_HEADER, VINEXT_PRERENDER_ROUTE_PARAMS_HEADER, VINEXT_PRERENDER_SECRET_HEADER, VINEXT_REVALIDATE_HEADER, VINEXT_RSC_MARKER_HEADER, VINEXT_RSC_REDIRECT_HEADER, VINEXT_RSC_RENDER_MODE_HEADER, VINEXT_STATIC_FILE_HEADER, VINEXT_TIMING_HEADER };
|
package/dist/server/headers.js
CHANGED
|
@@ -60,6 +60,13 @@ const RSC_ACTION_HEADER = "x-rsc-action";
|
|
|
60
60
|
const NEXT_ACTION_HEADER = "next-action";
|
|
61
61
|
/** Next.js action-not-found indicator (value "1"). */
|
|
62
62
|
const NEXTJS_ACTION_NOT_FOUND_HEADER = "x-nextjs-action-not-found";
|
|
63
|
+
/**
|
|
64
|
+
* Deployment ID header used by the Pages Router for deployment-skew
|
|
65
|
+
* protection. Set on every `/_next/data/` response so the client can detect
|
|
66
|
+
* when a new deployment has been rolled out and trigger a hard navigation.
|
|
67
|
+
* Mirrors `NEXT_NAV_DEPLOYMENT_ID_HEADER` from Next.js `lib/constants.ts`.
|
|
68
|
+
*/
|
|
69
|
+
const NEXTJS_DEPLOYMENT_ID_HEADER = "x-nextjs-deployment-id";
|
|
63
70
|
/** Forwarded action marker — set when a request has already been forwarded between workers. */
|
|
64
71
|
const ACTION_FORWARDED_HEADER = "x-action-forwarded";
|
|
65
72
|
/** Indicates revalidation occurred — value is JSON kind (1 = path/tag, 2 = dynamic-only). */
|
|
@@ -122,4 +129,4 @@ const INTERNAL_HEADERS = [
|
|
|
122
129
|
/** Vinext-only internal headers stripped alongside Next.js protocol internals. */
|
|
123
130
|
const VINEXT_INTERNAL_HEADERS = [VINEXT_PRERENDER_ROUTE_PARAMS_HEADER];
|
|
124
131
|
//#endregion
|
|
125
|
-
export { ACTION_FORWARDED_HEADER, ACTION_REDIRECT_HEADER, ACTION_REDIRECT_STATUS_HEADER, ACTION_REDIRECT_TYPE_HEADER, ACTION_REVALIDATED_HEADER, FLIGHT_HEADERS, INTERNAL_HEADERS, MIDDLEWARE_HEADER_PREFIX, MIDDLEWARE_NEXT_HEADER, MIDDLEWARE_OVERRIDE_HEADERS, MIDDLEWARE_REQUEST_HEADER_PREFIX, MIDDLEWARE_REWRITE_HEADER, MIDDLEWARE_SET_COOKIE_HEADER, NEXTJS_ACTION_NOT_FOUND_HEADER, NEXTJS_CACHE_HEADER, NEXT_ACTION_HEADER, NEXT_ROUTER_PREFETCH_HEADER, NEXT_ROUTER_SEGMENT_PREFETCH_HEADER, NEXT_ROUTER_STATE_TREE_HEADER, NEXT_URL_HEADER, RSC_ACTION_HEADER, RSC_HEADER, VINEXT_CACHE_HEADER, VINEXT_CLIENT_REUSE_MANIFEST_HEADER, VINEXT_DYNAMIC_STALE_TIME_HEADER, VINEXT_INTERCEPTION_CONTEXT_HEADER, VINEXT_INTERNAL_HEADERS, VINEXT_MOUNTED_SLOTS_HEADER, VINEXT_MW_CTX_HEADER, VINEXT_PARAMS_HEADER, VINEXT_PRERENDER_ROUTE_PARAMS_HEADER, VINEXT_PRERENDER_SECRET_HEADER, VINEXT_REVALIDATE_HEADER, VINEXT_RSC_MARKER_HEADER, VINEXT_RSC_REDIRECT_HEADER, VINEXT_RSC_RENDER_MODE_HEADER, VINEXT_STATIC_FILE_HEADER, VINEXT_TIMING_HEADER };
|
|
132
|
+
export { ACTION_FORWARDED_HEADER, ACTION_REDIRECT_HEADER, ACTION_REDIRECT_STATUS_HEADER, ACTION_REDIRECT_TYPE_HEADER, ACTION_REVALIDATED_HEADER, FLIGHT_HEADERS, INTERNAL_HEADERS, MIDDLEWARE_HEADER_PREFIX, MIDDLEWARE_NEXT_HEADER, MIDDLEWARE_OVERRIDE_HEADERS, MIDDLEWARE_REQUEST_HEADER_PREFIX, MIDDLEWARE_REWRITE_HEADER, MIDDLEWARE_SET_COOKIE_HEADER, NEXTJS_ACTION_NOT_FOUND_HEADER, NEXTJS_CACHE_HEADER, NEXTJS_DEPLOYMENT_ID_HEADER, NEXT_ACTION_HEADER, NEXT_ROUTER_PREFETCH_HEADER, NEXT_ROUTER_SEGMENT_PREFETCH_HEADER, NEXT_ROUTER_STATE_TREE_HEADER, NEXT_URL_HEADER, RSC_ACTION_HEADER, RSC_HEADER, VINEXT_CACHE_HEADER, VINEXT_CLIENT_REUSE_MANIFEST_HEADER, VINEXT_DYNAMIC_STALE_TIME_HEADER, VINEXT_INTERCEPTION_CONTEXT_HEADER, VINEXT_INTERNAL_HEADERS, VINEXT_MOUNTED_SLOTS_HEADER, VINEXT_MW_CTX_HEADER, VINEXT_PARAMS_HEADER, VINEXT_PRERENDER_ROUTE_PARAMS_HEADER, VINEXT_PRERENDER_SECRET_HEADER, VINEXT_REVALIDATE_HEADER, VINEXT_RSC_MARKER_HEADER, VINEXT_RSC_REDIRECT_HEADER, VINEXT_RSC_RENDER_MODE_HEADER, VINEXT_STATIC_FILE_HEADER, VINEXT_TIMING_HEADER };
|
|
@@ -34,6 +34,12 @@
|
|
|
34
34
|
* Ensure the instrumentation module's `register()` and `onRequestError`
|
|
35
35
|
* hooks have been applied exactly once.
|
|
36
36
|
*
|
|
37
|
+
* After `register()` runs, we extend the OTel tracer provider so that spans
|
|
38
|
+
* created inside Cache Component renders (warmup / fallback-resume phases) use
|
|
39
|
+
* a fresh OTel context rather than inheriting the prerender work unit store.
|
|
40
|
+
* This mirrors Next.js's `afterRegistration()` call in
|
|
41
|
+
* `instrumentation-node-extensions.ts`.
|
|
42
|
+
*
|
|
37
43
|
* @param instrumentationModule - The imported `instrumentation.ts` module.
|
|
38
44
|
* Passed as an argument so the generated entry can import it normally
|
|
39
45
|
* without this helper needing to know the module path.
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { extendTracerProviderForCacheComponents } from "./otel-tracer-extension.js";
|
|
1
2
|
//#region src/server/instrumentation-runtime.ts
|
|
2
3
|
let initialized = false;
|
|
3
4
|
let initPromise = null;
|
|
@@ -8,6 +9,12 @@ function isOnRequestErrorHandler(value) {
|
|
|
8
9
|
* Ensure the instrumentation module's `register()` and `onRequestError`
|
|
9
10
|
* hooks have been applied exactly once.
|
|
10
11
|
*
|
|
12
|
+
* After `register()` runs, we extend the OTel tracer provider so that spans
|
|
13
|
+
* created inside Cache Component renders (warmup / fallback-resume phases) use
|
|
14
|
+
* a fresh OTel context rather than inheriting the prerender work unit store.
|
|
15
|
+
* This mirrors Next.js's `afterRegistration()` call in
|
|
16
|
+
* `instrumentation-node-extensions.ts`.
|
|
17
|
+
*
|
|
11
18
|
* @param instrumentationModule - The imported `instrumentation.ts` module.
|
|
12
19
|
* Passed as an argument so the generated entry can import it normally
|
|
13
20
|
* without this helper needing to know the module path.
|
|
@@ -18,6 +25,7 @@ async function ensureInstrumentationRegistered(instrumentationModule) {
|
|
|
18
25
|
if (initPromise) return initPromise;
|
|
19
26
|
initPromise = (async () => {
|
|
20
27
|
if (typeof instrumentationModule.register === "function") await instrumentationModule.register();
|
|
28
|
+
extendTracerProviderForCacheComponents();
|
|
21
29
|
if (isOnRequestErrorHandler(instrumentationModule.onRequestError)) globalThis.__VINEXT_onRequestErrorHandler__ = instrumentationModule.onRequestError;
|
|
22
30
|
initialized = true;
|
|
23
31
|
})();
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { CacheControlMetadata } from "../shims/cache.js";
|
|
2
|
+
import { NEVER_CACHE_CONTROL, NO_STORE_CACHE_CONTROL } from "./cache-control.js";
|
|
3
|
+
|
|
4
|
+
//#region src/server/isr-decision.d.ts
|
|
5
|
+
type IsrDisposition = "HIT" | "STALE" | "MISS";
|
|
6
|
+
type IsrDecision = {
|
|
7
|
+
disposition: IsrDisposition; /** True when the caller must schedule a background regeneration. */
|
|
8
|
+
scheduleRegeneration: boolean; /** The `Cache-Control` string to stamp on the response. */
|
|
9
|
+
cacheControl: string;
|
|
10
|
+
};
|
|
11
|
+
/**
|
|
12
|
+
* Per-router special-case policies for `Cache-Control`.
|
|
13
|
+
*
|
|
14
|
+
* - `"app-page"` / `"pages"`: `buildCachedRevalidateCacheControl` for HIT/STALE.
|
|
15
|
+
* - `"app-route"`: same, but `revalidateSeconds=0` forces `NEVER_CACHE_CONTROL`
|
|
16
|
+
* and `revalidateSeconds=Infinity` forces `STATIC_CACHE_CONTROL`.
|
|
17
|
+
* - `"dev"`: like `"pages"`, but `revalidate=0`/`Infinity` guards are absent
|
|
18
|
+
* (dev never caches when revalidate=0 and never has Infinity entries in practice).
|
|
19
|
+
*/
|
|
20
|
+
type IsrPolicyKind = "app-page" | "app-route" | "pages" | "dev";
|
|
21
|
+
type DecideIsrOptions = {
|
|
22
|
+
/**
|
|
23
|
+
* The cache state. Content guards (kind-mismatch, empty body,
|
|
24
|
+
* query-variant-unproven) must have already passed before passing
|
|
25
|
+
* `"HIT"` or `"STALE"` here.
|
|
26
|
+
*/
|
|
27
|
+
cacheState: "HIT" | "STALE" | "MISS"; /** Which router is making the decision. */
|
|
28
|
+
kind: IsrPolicyKind;
|
|
29
|
+
/**
|
|
30
|
+
* The route's configured revalidate window in seconds. Used as the fallback
|
|
31
|
+
* when `cacheControlMeta` is absent.
|
|
32
|
+
*
|
|
33
|
+
* For `"dev"` call sites this is the only source of the revalidate value —
|
|
34
|
+
* dev never has metadata attached to a cache entry.
|
|
35
|
+
*/
|
|
36
|
+
revalidateSeconds: number;
|
|
37
|
+
/**
|
|
38
|
+
* The expire ceiling (seconds from epoch) read from the route config.
|
|
39
|
+
* Absent when the route pre-dates expire metadata support.
|
|
40
|
+
*/
|
|
41
|
+
expireSeconds?: number;
|
|
42
|
+
/**
|
|
43
|
+
* Optional per-entry metadata written alongside the cache value.
|
|
44
|
+
* When present its `revalidate`/`expire` fields override the route defaults,
|
|
45
|
+
* exactly as the call sites do today with `cacheControl?.revalidate ?? revalidateSeconds`.
|
|
46
|
+
*/
|
|
47
|
+
cacheControlMeta?: CacheControlMetadata;
|
|
48
|
+
};
|
|
49
|
+
/**
|
|
50
|
+
* Derive the `Cache-Control` string for an ISR response.
|
|
51
|
+
*
|
|
52
|
+
* Content guards (kind mismatch, query-variant-unproven, empty body) are the
|
|
53
|
+
* caller's responsibility and must happen *before* this call. `cacheState`
|
|
54
|
+
* must only be `"HIT"` or `"STALE"` when those guards have already passed.
|
|
55
|
+
*/
|
|
56
|
+
declare function decideIsr(options: DecideIsrOptions): IsrDecision;
|
|
57
|
+
/**
|
|
58
|
+
* Build the `Cache-Control` string for a fresh MISS response whose ISR policy
|
|
59
|
+
* is known (i.e. revalidate is set and > 0). Uses the unbounded SWR form when
|
|
60
|
+
* no expire ceiling is available, exactly as `buildRevalidateCacheControl` does.
|
|
61
|
+
*
|
|
62
|
+
* Separate from `decideIsr` because a MISS doesn't read a cache entry and
|
|
63
|
+
* therefore never has `cacheControlMeta`. `expireSeconds` here is the route
|
|
64
|
+
* config ceiling passed directly from the caller (not a per-entry fallback).
|
|
65
|
+
*/
|
|
66
|
+
declare function buildMissIsrCacheControl(revalidateSeconds: number, expireSeconds?: number): string;
|
|
67
|
+
/**
|
|
68
|
+
* Build the `Cache-Control` string for a fresh (MISS) app-route response.
|
|
69
|
+
*
|
|
70
|
+
* Applies the same `revalidateSeconds=0`→NEVER and `Infinity`→STATIC gates
|
|
71
|
+
* that `decideIsr` uses for app-route cached responses. `expireSeconds` is
|
|
72
|
+
* the route config ceiling passed directly (not per-entry metadata fallback).
|
|
73
|
+
*
|
|
74
|
+
* Used by `applyRouteHandlerRevalidateHeader` which operates on a fresh
|
|
75
|
+
* response that has no per-entry cache metadata.
|
|
76
|
+
*/
|
|
77
|
+
declare function buildAppRouteMissIsrCacheControl(revalidateSeconds: number, expireSeconds?: number): string;
|
|
78
|
+
//#endregion
|
|
79
|
+
export { NEVER_CACHE_CONTROL as ISR_NEVER_CACHE_CONTROL, NO_STORE_CACHE_CONTROL as ISR_NO_STORE_CACHE_CONTROL, buildAppRouteMissIsrCacheControl, buildMissIsrCacheControl, decideIsr };
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { NEVER_CACHE_CONTROL, NO_STORE_CACHE_CONTROL, STATIC_CACHE_CONTROL, buildCachedRevalidateCacheControl, buildRevalidateCacheControl } from "./cache-control.js";
|
|
2
|
+
//#region src/server/isr-decision.ts
|
|
3
|
+
/** Resolve effective revalidate/expire, preferring per-entry metadata. */
|
|
4
|
+
function resolveRevalidate(options) {
|
|
5
|
+
return {
|
|
6
|
+
effectiveRevalidate: options.cacheControlMeta?.revalidate ?? options.revalidateSeconds,
|
|
7
|
+
effectiveExpire: options.cacheControlMeta === void 0 ? void 0 : options.cacheControlMeta.expire ?? options.expireSeconds
|
|
8
|
+
};
|
|
9
|
+
}
|
|
10
|
+
function buildCacheControl(disposition, kind, revalidate, expire) {
|
|
11
|
+
if (kind === "app-route") {
|
|
12
|
+
if (revalidate === 0) return NEVER_CACHE_CONTROL;
|
|
13
|
+
if (revalidate === Infinity) return STATIC_CACHE_CONTROL;
|
|
14
|
+
}
|
|
15
|
+
return buildCachedRevalidateCacheControl(disposition, revalidate, expire);
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Derive the `Cache-Control` string for an ISR response.
|
|
19
|
+
*
|
|
20
|
+
* Content guards (kind mismatch, query-variant-unproven, empty body) are the
|
|
21
|
+
* caller's responsibility and must happen *before* this call. `cacheState`
|
|
22
|
+
* must only be `"HIT"` or `"STALE"` when those guards have already passed.
|
|
23
|
+
*/
|
|
24
|
+
function decideIsr(options) {
|
|
25
|
+
if (options.cacheState === "MISS") return {
|
|
26
|
+
disposition: "MISS",
|
|
27
|
+
scheduleRegeneration: false,
|
|
28
|
+
cacheControl: ""
|
|
29
|
+
};
|
|
30
|
+
const { effectiveRevalidate, effectiveExpire } = resolveRevalidate(options);
|
|
31
|
+
if (options.cacheState === "HIT") return {
|
|
32
|
+
disposition: "HIT",
|
|
33
|
+
scheduleRegeneration: false,
|
|
34
|
+
cacheControl: buildCacheControl("HIT", options.kind, effectiveRevalidate, effectiveExpire)
|
|
35
|
+
};
|
|
36
|
+
return {
|
|
37
|
+
disposition: "STALE",
|
|
38
|
+
scheduleRegeneration: true,
|
|
39
|
+
cacheControl: buildCacheControl("STALE", options.kind, effectiveRevalidate, effectiveExpire)
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Build the `Cache-Control` string for a fresh MISS response whose ISR policy
|
|
44
|
+
* is known (i.e. revalidate is set and > 0). Uses the unbounded SWR form when
|
|
45
|
+
* no expire ceiling is available, exactly as `buildRevalidateCacheControl` does.
|
|
46
|
+
*
|
|
47
|
+
* Separate from `decideIsr` because a MISS doesn't read a cache entry and
|
|
48
|
+
* therefore never has `cacheControlMeta`. `expireSeconds` here is the route
|
|
49
|
+
* config ceiling passed directly from the caller (not a per-entry fallback).
|
|
50
|
+
*/
|
|
51
|
+
function buildMissIsrCacheControl(revalidateSeconds, expireSeconds) {
|
|
52
|
+
return buildRevalidateCacheControl(revalidateSeconds, expireSeconds);
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Build the `Cache-Control` string for a fresh (MISS) app-route response.
|
|
56
|
+
*
|
|
57
|
+
* Applies the same `revalidateSeconds=0`→NEVER and `Infinity`→STATIC gates
|
|
58
|
+
* that `decideIsr` uses for app-route cached responses. `expireSeconds` is
|
|
59
|
+
* the route config ceiling passed directly (not per-entry metadata fallback).
|
|
60
|
+
*
|
|
61
|
+
* Used by `applyRouteHandlerRevalidateHeader` which operates on a fresh
|
|
62
|
+
* response that has no per-entry cache metadata.
|
|
63
|
+
*/
|
|
64
|
+
function buildAppRouteMissIsrCacheControl(revalidateSeconds, expireSeconds) {
|
|
65
|
+
if (revalidateSeconds === 0) return NEVER_CACHE_CONTROL;
|
|
66
|
+
if (revalidateSeconds === Infinity) return STATIC_CACHE_CONTROL;
|
|
67
|
+
return buildRevalidateCacheControl(revalidateSeconds, expireSeconds);
|
|
68
|
+
}
|
|
69
|
+
//#endregion
|
|
70
|
+
export { NEVER_CACHE_CONTROL as ISR_NEVER_CACHE_CONTROL, NO_STORE_CACHE_CONTROL as ISR_NO_STORE_CACHE_CONTROL, buildAppRouteMissIsrCacheControl, buildMissIsrCacheControl, decideIsr };
|
|
@@ -53,9 +53,9 @@ function getMetadataRouteFunctions(route) {
|
|
|
53
53
|
routeFunctionCache.set(route, functions);
|
|
54
54
|
return functions;
|
|
55
55
|
}
|
|
56
|
-
function matchMetadataRoute(route, cleanPathname, functions) {
|
|
56
|
+
function matchMetadataRoute(route, cleanPathname, functions, getUrlParts) {
|
|
57
57
|
if (route.patternParts) {
|
|
58
|
-
const urlParts =
|
|
58
|
+
const urlParts = getUrlParts();
|
|
59
59
|
if (functions.hasGeneratedImageMetadata && urlParts.length > 0) {
|
|
60
60
|
const params = matchMetadataRoutePattern(urlParts.slice(0, -1), route.patternParts);
|
|
61
61
|
if (params) return {
|
|
@@ -184,6 +184,8 @@ function serveStaticMetadataRoute(route) {
|
|
|
184
184
|
}
|
|
185
185
|
}
|
|
186
186
|
async function handleMetadataRouteRequest(options) {
|
|
187
|
+
let urlParts;
|
|
188
|
+
const getUrlParts = () => urlParts ??= options.cleanPathname.split("/").filter(Boolean);
|
|
187
189
|
for (const route of options.metadataRoutes) {
|
|
188
190
|
const functions = getMetadataRouteFunctions(route);
|
|
189
191
|
if (route.type === "sitemap" && route.isDynamic) {
|
|
@@ -193,7 +195,7 @@ async function handleMetadataRouteRequest(options) {
|
|
|
193
195
|
continue;
|
|
194
196
|
}
|
|
195
197
|
}
|
|
196
|
-
const match = matchMetadataRoute(route, options.cleanPathname, functions);
|
|
198
|
+
const match = matchMetadataRoute(route, options.cleanPathname, functions, getUrlParts);
|
|
197
199
|
if (!match) continue;
|
|
198
200
|
return route.isDynamic ? callDynamicMetadataRoute(route, match, options.makeThenableParams, functions) : serveStaticMetadataRoute(route);
|
|
199
201
|
}
|
|
@@ -19,6 +19,19 @@ type MiddlewareHandler = (request: NextRequest, event: NextFetchEvent) => Respon
|
|
|
19
19
|
type ExecuteMiddlewareOptions = {
|
|
20
20
|
basePath?: string;
|
|
21
21
|
filePath?: string;
|
|
22
|
+
/**
|
|
23
|
+
* Whether the incoming request was inside the configured basePath. Drives
|
|
24
|
+
* the `nextUrl.basePath` the middleware observes: in-basePath requests are
|
|
25
|
+
* re-prefixed so NextURL reports the configured basePath, while
|
|
26
|
+
* out-of-basePath ("absolute path") requests stay un-prefixed so middleware
|
|
27
|
+
* sees `nextUrl.basePath === ""` (Next.js `getNextPathnameInfo` semantics —
|
|
28
|
+
* see test/e2e/middleware-base-path "should execute from absolute paths").
|
|
29
|
+
* When omitted it is derived from the request URL, which is correct for the
|
|
30
|
+
* Pages prod/deploy adapters because they pass the original (un-stripped)
|
|
31
|
+
* URL. Callers that pass an already-stripped URL (dev server, App Router)
|
|
32
|
+
* must set this explicitly.
|
|
33
|
+
*/
|
|
34
|
+
hadBasePath?: boolean;
|
|
22
35
|
i18nConfig?: NextI18nConfig | null;
|
|
23
36
|
includeErrorDetails?: boolean;
|
|
24
37
|
/**
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { normalizePathnameForRouteMatchStrict } from "../routing/utils.js";
|
|
2
|
+
import { addBasePathToPathname, hasBasePath, stripBasePath } from "../utils/base-path.js";
|
|
2
3
|
import "./server-globals.js";
|
|
3
4
|
import { getRequestExecutionContext, runWithExecutionContext } from "../shims/request-context.js";
|
|
4
5
|
import { MIDDLEWARE_REWRITE_HEADER } from "./headers.js";
|
|
@@ -99,12 +100,13 @@ function resolveMiddlewarePathname(request) {
|
|
|
99
100
|
return badRequestResponse();
|
|
100
101
|
}
|
|
101
102
|
}
|
|
102
|
-
function createNextRequest(request, normalizedPathname, i18nConfig, basePath, trailingSlash) {
|
|
103
|
+
function createNextRequest(request, normalizedPathname, i18nConfig, basePath, trailingSlash, hadBasePath) {
|
|
103
104
|
const url = new URL(request.url);
|
|
104
105
|
let mwRequest = request.body && !request.bodyUsed ? request.clone() : request;
|
|
105
|
-
|
|
106
|
+
const mwPathname = basePath && hadBasePath ? addBasePathToPathname(normalizedPathname, basePath) : normalizedPathname;
|
|
107
|
+
if (mwPathname !== url.pathname) {
|
|
106
108
|
const mwUrl = new URL(url);
|
|
107
|
-
mwUrl.pathname =
|
|
109
|
+
mwUrl.pathname = mwPathname;
|
|
108
110
|
mwRequest = new Request(mwUrl, mwRequest);
|
|
109
111
|
}
|
|
110
112
|
const nextConfig = basePath || i18nConfig || trailingSlash ? {
|
|
@@ -124,9 +126,11 @@ async function executeMiddleware(options) {
|
|
|
124
126
|
continue: false,
|
|
125
127
|
response: normalizedPathname
|
|
126
128
|
};
|
|
127
|
-
|
|
128
|
-
const
|
|
129
|
-
|
|
129
|
+
const hadBasePath = options.hadBasePath ?? (!options.basePath || hasBasePath(new URL(options.request.url).pathname, options.basePath));
|
|
130
|
+
const matchPathname = options.basePath ? stripBasePath(normalizedPathname, options.basePath) : normalizedPathname;
|
|
131
|
+
if (!matchesMiddleware(matchPathname, middlewareMatcher(options.module), options.request, options.i18nConfig)) return { continue: true };
|
|
132
|
+
const nextRequest = createNextRequest(options.request, normalizedPathname, options.i18nConfig, options.basePath, options.trailingSlash, hadBasePath);
|
|
133
|
+
const fetchEvent = new NextFetchEvent({ page: matchPathname });
|
|
130
134
|
let response;
|
|
131
135
|
try {
|
|
132
136
|
response = await middlewareFn(nextRequest, fetchEvent);
|
|
@@ -195,7 +199,7 @@ async function executeMiddleware(options) {
|
|
|
195
199
|
try {
|
|
196
200
|
const rewriteParsed = new URL(rewriteUrl, options.request.url);
|
|
197
201
|
const requestOrigin = new URL(options.request.url).origin;
|
|
198
|
-
if (rewriteParsed.origin === requestOrigin) rewritePath = rewriteParsed.pathname + rewriteParsed.search;
|
|
202
|
+
if (rewriteParsed.origin === requestOrigin) rewritePath = (options.basePath ? stripBasePath(rewriteParsed.pathname, options.basePath) : rewriteParsed.pathname) + rewriteParsed.search;
|
|
199
203
|
else rewritePath = rewriteParsed.href;
|
|
200
204
|
} catch {
|
|
201
205
|
rewritePath = rewriteUrl;
|