vinext 1.0.0-beta.1 → 1.0.0-beta.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +10 -10
- package/dist/build/client-build-config.js +8 -1
- package/dist/build/report.d.ts +8 -1
- package/dist/build/report.js +12 -2
- package/dist/check.js +19 -2
- package/dist/cli.js +3 -2
- package/dist/config/config-matchers.d.ts +13 -37
- package/dist/config/config-matchers.js +35 -122
- package/dist/config/next-config.d.ts +15 -5
- package/dist/config/next-config.js +7 -1
- package/dist/config/request-context.d.ts +14 -0
- package/dist/config/request-context.js +26 -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 +30 -9
- package/dist/entries/app-rsc-manifest.js +36 -0
- package/dist/entries/pages-client-entry.js +0 -1
- package/dist/entries/pages-server-entry.js +13 -5
- package/dist/index.js +194 -101
- package/dist/init-cloudflare.d.ts +3 -1
- package/dist/init-cloudflare.js +59 -5
- package/dist/init.d.ts +6 -4
- package/dist/init.js +20 -22
- 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/og-assets.js +1 -1
- package/dist/plugins/require-context.js +20 -15
- 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 +27 -11
- package/dist/routing/app-route-graph.js +158 -22
- package/dist/routing/app-router.js +12 -6
- package/dist/routing/file-matcher.d.ts +11 -22
- package/dist/routing/file-matcher.js +21 -33
- package/dist/routing/pages-router.js +23 -29
- package/dist/routing/route-matching.d.ts +14 -1
- package/dist/routing/route-matching.js +13 -1
- package/dist/routing/route-pattern.d.ts +2 -1
- package/dist/routing/route-pattern.js +7 -4
- package/dist/routing/route-trie.d.ts +5 -1
- package/dist/routing/route-trie.js +5 -2
- package/dist/server/api-handler.d.ts +2 -0
- package/dist/server/api-handler.js +3 -3
- package/dist/server/app-browser-entry.js +110 -37
- package/dist/server/app-browser-history-controller.d.ts +1 -1
- package/dist/server/app-browser-history-controller.js +3 -3
- package/dist/server/app-browser-navigation-controller.d.ts +23 -21
- package/dist/server/app-browser-navigation-controller.js +2 -2
- package/dist/server/app-browser-state.js +1 -1
- package/dist/server/app-browser-visible-commit.js +1 -1
- package/dist/server/app-fallback-renderer.d.ts +3 -0
- package/dist/server/app-fallback-renderer.js +4 -2
- package/dist/server/app-middleware.js +2 -1
- package/dist/server/app-page-boundary-render.d.ts +12 -1
- package/dist/server/app-page-boundary-render.js +84 -2
- package/dist/server/app-page-cache-finalizer.d.ts +1 -1
- package/dist/server/app-page-cache-finalizer.js +1 -1
- package/dist/server/app-page-cache.d.ts +2 -2
- package/dist/server/app-page-cache.js +16 -2
- package/dist/server/app-page-dispatch.d.ts +19 -2
- package/dist/server/app-page-dispatch.js +45 -23
- package/dist/server/app-page-element-builder.d.ts +9 -2
- package/dist/server/app-page-element-builder.js +144 -33
- package/dist/server/app-page-head.d.ts +56 -4
- package/dist/server/app-page-head.js +216 -85
- package/dist/server/app-page-http-access-fallback-metadata.d.ts +29 -0
- package/dist/server/app-page-http-access-fallback-metadata.js +114 -0
- 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 +2 -2
- package/dist/server/app-page-render.js +1 -1
- package/dist/server/app-page-request.d.ts +15 -0
- package/dist/server/app-page-request.js +134 -14
- package/dist/server/app-page-response.js +1 -1
- package/dist/server/app-page-route-wiring.d.ts +16 -0
- package/dist/server/app-page-route-wiring.js +254 -52
- package/dist/server/app-pages-bridge.d.ts +1 -1
- package/dist/server/app-pages-bridge.js +2 -1
- package/dist/server/app-post-middleware-context.d.ts +1 -1
- package/dist/server/app-post-middleware-context.js +1 -1
- package/dist/server/app-route-handler-cache.js +4 -0
- package/dist/server/app-route-handler-dispatch.js +40 -20
- package/dist/server/app-route-handler-execution.d.ts +6 -1
- package/dist/server/app-route-handler-execution.js +42 -16
- package/dist/server/app-route-handler-policy.d.ts +2 -0
- package/dist/server/app-route-handler-policy.js +3 -3
- package/dist/server/app-route-handler-response.js +1 -1
- package/dist/server/app-route-module-loader.d.ts +9 -1
- package/dist/server/app-route-module-loader.js +4 -0
- package/dist/server/app-router-entry.js +6 -3
- package/dist/server/app-rsc-cache-busting.d.ts +1 -1
- package/dist/server/app-rsc-errors.d.ts +1 -1
- package/dist/server/app-rsc-errors.js +22 -2
- package/dist/server/app-rsc-handler.d.ts +10 -5
- package/dist/server/app-rsc-handler.js +73 -33
- package/dist/server/app-rsc-request-normalization.d.ts +3 -2
- package/dist/server/app-rsc-request-normalization.js +8 -2
- package/dist/server/app-rsc-response-finalizer.d.ts +2 -2
- package/dist/server/app-rsc-response-finalizer.js +5 -11
- package/dist/server/app-rsc-route-matching.d.ts +29 -0
- package/dist/server/app-rsc-route-matching.js +93 -11
- package/dist/server/app-segment-config.js +13 -3
- package/dist/server/app-server-action-execution.d.ts +3 -1
- package/dist/server/app-server-action-execution.js +9 -7
- package/dist/server/app-ssr-entry.js +6 -6
- package/dist/server/app-ssr-stream.d.ts +1 -7
- package/dist/server/app-ssr-stream.js +2 -10
- package/dist/server/cache-control.d.ts +2 -2
- package/dist/server/cache-control.js +2 -1
- package/dist/server/config-headers.d.ts +24 -0
- package/dist/server/config-headers.js +52 -0
- package/dist/server/dev-initial-server-error.d.ts +1 -1
- package/dist/server/dev-server.d.ts +3 -1
- package/dist/server/dev-server.js +170 -321
- package/dist/server/headers.d.ts +2 -2
- package/dist/server/headers.js +4 -3
- package/dist/server/isr-cache.d.ts +11 -34
- package/dist/server/isr-cache.js +33 -43
- package/dist/server/isr-decision.d.ts +2 -2
- package/dist/server/middleware-matcher-pattern.d.ts +22 -0
- package/dist/server/middleware-matcher-pattern.js +219 -0
- package/dist/server/middleware-matcher.d.ts +2 -7
- package/dist/server/middleware-matcher.js +13 -60
- package/dist/server/middleware-path-to-regexp.d.ts +14 -0
- package/dist/server/middleware-path-to-regexp.js +228 -0
- package/dist/server/middleware-runtime.js +4 -3
- package/dist/server/middleware.d.ts +1 -1
- package/dist/server/middleware.js +2 -1
- package/dist/server/pages-api-route.d.ts +2 -0
- package/dist/server/pages-api-route.js +4 -1
- package/dist/server/pages-data-route.d.ts +11 -1
- package/dist/server/pages-data-route.js +16 -1
- package/dist/server/pages-dev-hydration.js +0 -1
- package/dist/server/pages-get-initial-props.d.ts +16 -3
- package/dist/server/pages-get-initial-props.js +17 -10
- 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 +7 -3
- package/dist/server/pages-page-data.d.ts +64 -10
- package/dist/server/pages-page-data.js +349 -80
- package/dist/server/pages-page-handler.d.ts +9 -3
- package/dist/server/pages-page-handler.js +131 -36
- package/dist/server/pages-page-response.d.ts +5 -3
- package/dist/server/pages-page-response.js +24 -27
- package/dist/server/pages-readiness.d.ts +3 -3
- package/dist/server/pages-request-pipeline.d.ts +8 -0
- package/dist/server/pages-request-pipeline.js +30 -12
- 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 +16 -4
- package/dist/server/prerender-route-params.js +2 -2
- package/dist/server/prod-server.d.ts +3 -5
- package/dist/server/prod-server.js +72 -35
- package/dist/server/request-pipeline.d.ts +15 -29
- package/dist/server/request-pipeline.js +24 -59
- package/dist/server/revalidation-host.d.ts +11 -0
- package/dist/server/revalidation-host.js +19 -0
- package/dist/server/rsc-stream-hints.js +122 -18
- package/dist/server/worker-revalidation-context.d.ts +13 -0
- package/dist/server/worker-revalidation-context.js +29 -0
- package/dist/shims/app.d.ts +1 -1
- package/dist/shims/cache-handler.d.ts +1 -1
- package/dist/shims/cache-handler.js +10 -7
- package/dist/shims/cache-request-state.d.ts +27 -1
- package/dist/shims/cache-request-state.js +60 -1
- package/dist/shims/cache-runtime.d.ts +18 -1
- package/dist/shims/cache-runtime.js +26 -8
- package/dist/shims/cache.d.ts +5 -5
- package/dist/shims/cache.js +27 -11
- package/dist/shims/document.d.ts +42 -83
- package/dist/shims/document.js +62 -53
- package/dist/shims/dynamic.d.ts +5 -25
- package/dist/shims/dynamic.js +29 -8
- package/dist/shims/error.d.ts +1 -1
- package/dist/shims/error.js +2 -2
- package/dist/shims/fetch-cache.js +13 -4
- package/dist/shims/font-google-base.d.ts +11 -4
- package/dist/shims/font-google-base.js +17 -8
- package/dist/shims/font-local.d.ts +10 -3
- package/dist/shims/font-local.js +41 -10
- package/dist/shims/head.d.ts +13 -2
- package/dist/shims/head.js +3 -3
- package/dist/shims/headers.d.ts +37 -43
- package/dist/shims/headers.js +183 -74
- package/dist/shims/image.d.ts +27 -37
- package/dist/shims/image.js +21 -14
- package/dist/shims/internal/cookie-serialize.d.ts +12 -9
- package/dist/shims/internal/cookie-serialize.js +10 -7
- package/dist/shims/internal/hybrid-client-route-owner.js +3 -1
- package/dist/shims/internal/interpolate-as.d.ts +15 -1
- package/dist/shims/internal/interpolate-as.js +27 -2
- package/dist/shims/internal/utils.d.ts +3 -3
- package/dist/shims/legacy-image.d.ts +3 -37
- package/dist/shims/legacy-image.js +6 -16
- package/dist/shims/link.d.ts +15 -22
- package/dist/shims/link.js +34 -13
- package/dist/shims/metadata.d.ts +7 -17
- package/dist/shims/metadata.js +74 -24
- package/dist/shims/navigation-context-state.d.ts +1 -1
- package/dist/shims/navigation.d.ts +7 -3
- package/dist/shims/navigation.js +25 -14
- package/dist/shims/navigation.react-server.js +1 -1
- package/dist/shims/next-shims-augmentations.d.ts +1 -0
- package/dist/shims/next-shims-public.d.ts +1 -0
- package/dist/shims/public-shim-map.json.js +103 -0
- package/dist/shims/readonly-url-search-params.d.ts +3 -3
- package/dist/shims/readonly-url-search-params.js +3 -3
- package/dist/shims/request-context.d.ts +4 -1
- package/dist/shims/request-state-types.d.ts +1 -1
- package/dist/shims/router.d.ts +55 -21
- package/dist/shims/router.js +180 -44
- package/dist/shims/script.d.ts +6 -10
- package/dist/shims/script.js +7 -4
- package/dist/shims/server.d.ts +50 -26
- package/dist/shims/server.js +193 -55
- package/dist/shims/unified-request-context.d.ts +22 -3
- package/dist/shims/unified-request-context.js +84 -1
- package/dist/shims/url-safety.d.ts +1 -1
- package/dist/shims/url-safety.js +2 -2
- package/dist/typegen.d.ts +8 -2
- package/dist/typegen.js +76 -15
- package/dist/utils/cache-control-metadata.d.ts +2 -1
- package/dist/utils/cache-control-metadata.js +5 -1
- package/dist/utils/domain-locale.d.ts +2 -2
- package/dist/utils/external-url.d.ts +5 -0
- package/dist/utils/external-url.js +7 -0
- 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/dist/utils/regex-safety.d.ts +8 -0
- package/dist/utils/regex-safety.js +737 -0
- package/package.json +13 -2
|
@@ -1,10 +1,15 @@
|
|
|
1
1
|
import { addBasePathToPathname, hasBasePath } from "../utils/base-path.js";
|
|
2
2
|
import { patternToNextFormat } from "../routing/route-validation.js";
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
3
|
+
import { PRERENDER_REVALIDATE_HEADER } from "../utils/protocol-headers.js";
|
|
4
|
+
import { requestContextFromRequest } from "../config/request-context.js";
|
|
5
|
+
import { isExternalUrl } from "../utils/external-url.js";
|
|
6
|
+
import { applyMiddlewareRequestHeaders, matchRedirect, matchRewrite, preserveRedirectDestinationQuery, proxyExternalRequest, sanitizeDestination } from "../config/config-matchers.js";
|
|
7
|
+
import { cloneRequestWithUrl, normalizeTrailingSlash } from "./request-pipeline.js";
|
|
8
|
+
import { isOnDemandRevalidateRequest } from "./isr-cache.js";
|
|
5
9
|
import { mergeRewriteQuery } from "../utils/query.js";
|
|
6
10
|
import { normalizeDefaultLocalePathname, stripI18nLocaleForApiRoute } from "./pages-i18n.js";
|
|
7
11
|
import { buildMiddlewarePrefetchSkipResponse } from "./pages-data-route.js";
|
|
12
|
+
import { applyConfigHeadersToHeaderRecord } from "./config-headers.js";
|
|
8
13
|
import { mergeHeaders } from "./worker-utils.js";
|
|
9
14
|
//#region src/server/pages-request-pipeline.ts
|
|
10
15
|
async function fetchWorkerFilesystemRoute(request, requestPathname, phase, fetchAsset) {
|
|
@@ -53,6 +58,9 @@ async function runPagesRequest(request, deps) {
|
|
|
53
58
|
const url = new URL(request.url);
|
|
54
59
|
let pathname = url.pathname;
|
|
55
60
|
const search = url.search;
|
|
61
|
+
const revalidateHeader = request.headers.get(PRERENDER_REVALIDATE_HEADER);
|
|
62
|
+
const isOnDemandRevalidate = deps.authorizeOnDemandRevalidate ? deps.authorizeOnDemandRevalidate(revalidateHeader) : isOnDemandRevalidateRequest(revalidateHeader);
|
|
63
|
+
const requestConfigPathname = deps.configMatchPathname ?? pathname;
|
|
56
64
|
const basePathState = {
|
|
57
65
|
basePath,
|
|
58
66
|
hadBasePath
|
|
@@ -66,9 +74,9 @@ async function runPagesRequest(request, deps) {
|
|
|
66
74
|
}
|
|
67
75
|
const reqCtx = requestContextFromRequest(request);
|
|
68
76
|
const requestHostname = i18nConfig ? url.hostname : "";
|
|
69
|
-
const
|
|
77
|
+
const requestConfigMatchPathname = i18nConfig ? normalizeDefaultLocalePathname(requestConfigPathname, i18nConfig, { hostname: requestHostname }) : requestConfigPathname;
|
|
70
78
|
if (configRedirects.length) {
|
|
71
|
-
const redirect = matchRedirect(
|
|
79
|
+
const redirect = matchRedirect(requestConfigMatchPathname, configRedirects, reqCtx, basePathState);
|
|
72
80
|
if (redirect) {
|
|
73
81
|
const location = preserveRedirectDestinationQuery(sanitizeDestination(basePath && hadBasePath && !isExternalUrl(redirect.destination) && !hasBasePath(redirect.destination, basePath) ? basePath + redirect.destination : redirect.destination), deps.rawSearch ?? search);
|
|
74
82
|
return {
|
|
@@ -82,6 +90,7 @@ async function runPagesRequest(request, deps) {
|
|
|
82
90
|
}
|
|
83
91
|
const originalResolvedUrl = pathname + search;
|
|
84
92
|
let resolvedUrl = originalResolvedUrl;
|
|
93
|
+
let resolvedPathnameIsRequestPathname = true;
|
|
85
94
|
const middlewareHeaders = {};
|
|
86
95
|
let middlewareStatus;
|
|
87
96
|
const serveFilesystemRoute = async (requestPathname, phase) => {
|
|
@@ -93,7 +102,7 @@ async function runPagesRequest(request, deps) {
|
|
|
93
102
|
};
|
|
94
103
|
return served ? { type: "handled" } : null;
|
|
95
104
|
};
|
|
96
|
-
if (typeof deps.runMiddleware === "function") {
|
|
105
|
+
if (!isOnDemandRevalidate && typeof deps.runMiddleware === "function") {
|
|
97
106
|
const result = await deps.runMiddleware(request, deps.ctx ?? null, { isDataRequest });
|
|
98
107
|
if (result.waitUntilPromises && result.waitUntilPromises.length > 0) {
|
|
99
108
|
const ctx = deps.ctx;
|
|
@@ -131,7 +140,10 @@ async function runPagesRequest(request, deps) {
|
|
|
131
140
|
else if (existing) middlewareHeaders[key] = [existing, value];
|
|
132
141
|
else middlewareHeaders[key] = [value];
|
|
133
142
|
} else middlewareHeaders[key] = value;
|
|
134
|
-
if (result.rewriteUrl)
|
|
143
|
+
if (result.rewriteUrl) {
|
|
144
|
+
resolvedUrl = result.rewriteUrl;
|
|
145
|
+
resolvedPathnameIsRequestPathname = false;
|
|
146
|
+
}
|
|
135
147
|
middlewareStatus = result.status ?? result.rewriteStatus;
|
|
136
148
|
}
|
|
137
149
|
const { postMwReqCtx, request: postMwReq } = applyMiddlewareRequestHeaders(middlewareHeaders, request, { preserveCredentialHeaders: isExternalUrl(resolvedUrl) });
|
|
@@ -143,6 +155,7 @@ async function runPagesRequest(request, deps) {
|
|
|
143
155
|
});
|
|
144
156
|
let resolvedPathname = pathnameForResolvedUrl(resolvedUrl);
|
|
145
157
|
const matchResolvedPathname = (p) => i18nConfig ? normalizeDefaultLocalePathname(p, i18nConfig, { hostname: requestHostname }) : p;
|
|
158
|
+
const configSourcePathname = () => resolvedPathnameIsRequestPathname ? requestConfigMatchPathname : matchResolvedPathname(resolvedPathname);
|
|
146
159
|
const matchedPathnameForRoute = (routePattern) => {
|
|
147
160
|
const matchedPathname = routePattern ? patternToNextFormat(routePattern) : resolvedPathname;
|
|
148
161
|
if (!i18nConfig) return matchedPathname;
|
|
@@ -161,7 +174,7 @@ async function runPagesRequest(request, deps) {
|
|
|
161
174
|
};
|
|
162
175
|
if (configHeaders.length) applyConfigHeadersToHeaderRecord(middlewareHeaders, {
|
|
163
176
|
configHeaders,
|
|
164
|
-
pathname:
|
|
177
|
+
pathname: requestConfigMatchPathname,
|
|
165
178
|
requestContext: reqCtx,
|
|
166
179
|
basePathState
|
|
167
180
|
});
|
|
@@ -173,7 +186,7 @@ async function runPagesRequest(request, deps) {
|
|
|
173
186
|
if (directFilesystemResult) return directFilesystemResult;
|
|
174
187
|
let configRewriteFired = false;
|
|
175
188
|
for (const rewrite of configRewrites.beforeFiles ?? []) {
|
|
176
|
-
const rewritten = matchRewrite(
|
|
189
|
+
const rewritten = matchRewrite(configSourcePathname(), [rewrite], rewriteRequestContext(), basePathState);
|
|
177
190
|
if (rewritten) {
|
|
178
191
|
if (isExternalUrl(rewritten)) return {
|
|
179
192
|
type: "response",
|
|
@@ -181,6 +194,7 @@ async function runPagesRequest(request, deps) {
|
|
|
181
194
|
};
|
|
182
195
|
resolvedUrl = mergeRewriteQuery(resolvedUrl, rewritten);
|
|
183
196
|
resolvedPathname = pathnameForResolvedUrl(resolvedUrl);
|
|
197
|
+
resolvedPathnameIsRequestPathname = false;
|
|
184
198
|
configRewriteFired = true;
|
|
185
199
|
}
|
|
186
200
|
}
|
|
@@ -227,7 +241,7 @@ async function runPagesRequest(request, deps) {
|
|
|
227
241
|
let pageMatch = !isOutsideBasePathUnclaimed() && deps.matchPageRoute ? deps.matchPageRoute(resolvedPathname, request) : null;
|
|
228
242
|
let resolvedPathnameChanged = false;
|
|
229
243
|
if (!pageMatch || pageMatch.route.isDynamic) for (const rewrite of configRewrites.afterFiles ?? []) {
|
|
230
|
-
const rewritten = matchRewrite(
|
|
244
|
+
const rewritten = matchRewrite(configSourcePathname(), [rewrite], rewriteRequestContext(), basePathState);
|
|
231
245
|
if (rewritten) {
|
|
232
246
|
if (isExternalUrl(rewritten)) return {
|
|
233
247
|
type: "response",
|
|
@@ -235,6 +249,7 @@ async function runPagesRequest(request, deps) {
|
|
|
235
249
|
};
|
|
236
250
|
resolvedUrl = mergeRewriteQuery(resolvedUrl, rewritten);
|
|
237
251
|
resolvedPathname = pathnameForResolvedUrl(resolvedUrl);
|
|
252
|
+
resolvedPathnameIsRequestPathname = false;
|
|
238
253
|
configRewriteFired = true;
|
|
239
254
|
resolvedPathnameChanged = true;
|
|
240
255
|
const afterFilesFilesystemResult = await serveFilesystemRoute(resolvedPathname, "afterFiles");
|
|
@@ -253,7 +268,7 @@ async function runPagesRequest(request, deps) {
|
|
|
253
268
|
if (typeof deps.renderPage === "function") {
|
|
254
269
|
let renderPageMatch = pageMatch;
|
|
255
270
|
if ((isOutsideBasePathUnclaimed() || isDataReq || isDataRequest) && !renderPageMatch && configRewrites.fallback?.length) for (const rewrite of configRewrites.fallback) {
|
|
256
|
-
const fallbackRewrite = matchRewrite(
|
|
271
|
+
const fallbackRewrite = matchRewrite(configSourcePathname(), [rewrite], rewriteRequestContext(), basePathState);
|
|
257
272
|
if (!fallbackRewrite) continue;
|
|
258
273
|
if (isExternalUrl(fallbackRewrite)) return {
|
|
259
274
|
type: "response",
|
|
@@ -261,6 +276,7 @@ async function runPagesRequest(request, deps) {
|
|
|
261
276
|
};
|
|
262
277
|
resolvedUrl = mergeRewriteQuery(resolvedUrl, fallbackRewrite);
|
|
263
278
|
resolvedPathname = pathnameForResolvedUrl(resolvedUrl);
|
|
279
|
+
resolvedPathnameIsRequestPathname = false;
|
|
264
280
|
configRewriteFired = true;
|
|
265
281
|
const fallbackFilesystemResult = await serveFilesystemRoute(resolvedPathname, "fallback");
|
|
266
282
|
if (fallbackFilesystemResult) return fallbackFilesystemResult;
|
|
@@ -281,7 +297,7 @@ async function runPagesRequest(request, deps) {
|
|
|
281
297
|
let response = await deps.renderPage(request, resolvedUrl, initialRenderOptions, stagedHeaders);
|
|
282
298
|
let matchedFallbackRewrite = false;
|
|
283
299
|
if (response.status === 404 && shouldDeferErrorPageOnMiss && configRewrites.fallback?.length) for (const rewrite of configRewrites.fallback) {
|
|
284
|
-
const fallbackRewrite = matchRewrite(
|
|
300
|
+
const fallbackRewrite = matchRewrite(configSourcePathname(), [rewrite], rewriteRequestContext(), basePathState);
|
|
285
301
|
if (!fallbackRewrite) continue;
|
|
286
302
|
if (isExternalUrl(fallbackRewrite)) return {
|
|
287
303
|
type: "response",
|
|
@@ -289,6 +305,7 @@ async function runPagesRequest(request, deps) {
|
|
|
289
305
|
};
|
|
290
306
|
resolvedUrl = mergeRewriteQuery(resolvedUrl, fallbackRewrite);
|
|
291
307
|
resolvedPathname = pathnameForResolvedUrl(resolvedUrl);
|
|
308
|
+
resolvedPathnameIsRequestPathname = false;
|
|
292
309
|
configRewriteFired = true;
|
|
293
310
|
const fallbackFilesystemResult = await serveFilesystemRoute(resolvedPathname, "fallback");
|
|
294
311
|
if (fallbackFilesystemResult) return fallbackFilesystemResult;
|
|
@@ -325,7 +342,7 @@ async function runPagesRequest(request, deps) {
|
|
|
325
342
|
}
|
|
326
343
|
let devPageMatch = isOutsideBasePathUnclaimed() ? null : resolvedPathnameChanged ? deps.matchPageRoute ? deps.matchPageRoute(resolvedPathname, request) : null : pageMatch;
|
|
327
344
|
if (!devPageMatch && configRewrites.fallback?.length) for (const rewrite of configRewrites.fallback) {
|
|
328
|
-
const fallbackRewrite = matchRewrite(
|
|
345
|
+
const fallbackRewrite = matchRewrite(configSourcePathname(), [rewrite], rewriteRequestContext(), basePathState);
|
|
329
346
|
if (!fallbackRewrite) continue;
|
|
330
347
|
if (isExternalUrl(fallbackRewrite)) return {
|
|
331
348
|
type: "response",
|
|
@@ -333,6 +350,7 @@ async function runPagesRequest(request, deps) {
|
|
|
333
350
|
};
|
|
334
351
|
resolvedUrl = mergeRewriteQuery(resolvedUrl, fallbackRewrite);
|
|
335
352
|
resolvedPathname = pathnameForResolvedUrl(resolvedUrl);
|
|
353
|
+
resolvedPathnameIsRequestPathname = false;
|
|
336
354
|
configRewriteFired = true;
|
|
337
355
|
const fallbackFilesystemResult = await serveFilesystemRoute(resolvedPathname, "fallback");
|
|
338
356
|
if (fallbackFilesystemResult) return fallbackFilesystemResult;
|
|
@@ -10,6 +10,6 @@ type RevalidateOptions = {
|
|
|
10
10
|
*/
|
|
11
11
|
unstable_onlyGenerated?: boolean;
|
|
12
12
|
};
|
|
13
|
-
declare function performOnDemandRevalidate(source: IncomingMessage | Headers, urlPath: string, opts?: RevalidateOptions): Promise<void>;
|
|
13
|
+
declare function performOnDemandRevalidate(source: IncomingMessage | Headers, urlPath: string, opts?: RevalidateOptions, trustedOrigin?: string, allowedRevalidateHeaderKeys?: readonly string[], dev?: boolean): Promise<void>;
|
|
14
14
|
//#endregion
|
|
15
15
|
export { RevalidateOptions, performOnDemandRevalidate };
|
|
@@ -1,19 +1,65 @@
|
|
|
1
|
+
import { getRequestExecutionContext } from "../shims/request-context.js";
|
|
2
|
+
import { PRERENDER_REVALIDATE_HEADER, PRERENDER_REVALIDATE_ONLY_GENERATED_HEADER, VINEXT_REVALIDATE_HOST_HEADER } from "../utils/protocol-headers.js";
|
|
1
3
|
import "./headers.js";
|
|
2
|
-
import {
|
|
4
|
+
import { getRevalidateSecret, isOnDemandRevalidateRequest } from "./isr-cache.js";
|
|
5
|
+
import { normalizeDomainHostname } from "../utils/domain-locale.js";
|
|
3
6
|
import { resolveRequestHost, resolveRequestProtocol } from "./proxy-trust.js";
|
|
4
7
|
//#region src/server/pages-revalidate.ts
|
|
5
|
-
async function performOnDemandRevalidate(source, urlPath, opts = {}) {
|
|
8
|
+
async function performOnDemandRevalidate(source, urlPath, opts = {}, trustedOrigin, allowedRevalidateHeaderKeys = [], dev = false) {
|
|
6
9
|
if (typeof urlPath !== "string" || !urlPath.startsWith("/")) throw new Error(`Invalid urlPath provided to revalidate(), must be a path e.g. /blog/post-1, received ${urlPath}`);
|
|
7
|
-
|
|
8
|
-
const
|
|
9
|
-
const
|
|
10
|
+
if (isSourceRevalidationRequest(source)) throw new Error(`Cannot revalidate ${urlPath} from an internal revalidation request`);
|
|
11
|
+
const executionContext = getRequestExecutionContext();
|
|
12
|
+
const dispatchRevalidate = executionContext?.dispatchPagesRevalidate;
|
|
13
|
+
const target = createRevalidateTarget(source, urlPath, trustedOrigin);
|
|
10
14
|
const headers = { [PRERENDER_REVALIDATE_HEADER]: getRevalidateSecret() };
|
|
15
|
+
if (trustedOrigin && !dispatchRevalidate) {
|
|
16
|
+
const logicalHostname = normalizeDomainHostname(resolveRequestHost(source, "localhost"));
|
|
17
|
+
if (logicalHostname) headers[VINEXT_REVALIDATE_HOST_HEADER] = logicalHostname;
|
|
18
|
+
}
|
|
11
19
|
if (opts.unstable_onlyGenerated) headers[PRERENDER_REVALIDATE_ONLY_GENERATED_HEADER] = "1";
|
|
12
|
-
const
|
|
20
|
+
const allowedHeaders = new Set(allowedRevalidateHeaderKeys.map((key) => key.toLowerCase()));
|
|
21
|
+
if (dev) allowedHeaders.add("cookie");
|
|
22
|
+
for (const key of allowedHeaders) {
|
|
23
|
+
const value = readSourceHeader(source, key);
|
|
24
|
+
if (value !== void 0) headers[key] = value;
|
|
25
|
+
}
|
|
26
|
+
if (dispatchRevalidate && executionContext.isInternalPagesRevalidation) throw new Error(`Cannot revalidate ${urlPath} from an internal revalidation request`);
|
|
27
|
+
const res = await fetchRevalidateTarget(target, headers, dispatchRevalidate);
|
|
28
|
+
if (!((res.headers.get("x-vercel-cache") ?? res.headers.get("x-nextjs-cache"))?.toUpperCase() === "REVALIDATED" || res.status === 200 || res.status === 404 && opts.unstable_onlyGenerated === true)) throw new Error(`Failed to revalidate ${urlPath}: ${res.status}`);
|
|
29
|
+
}
|
|
30
|
+
function readSourceHeader(source, key) {
|
|
31
|
+
if (isWebHeaders(source)) return source.get(key) ?? void 0;
|
|
32
|
+
const value = source.headers[key];
|
|
33
|
+
return Array.isArray(value) ? value.join(", ") : value;
|
|
34
|
+
}
|
|
35
|
+
function isSourceRevalidationRequest(source) {
|
|
36
|
+
return isOnDemandRevalidateRequest(isWebHeaders(source) ? source.get(PRERENDER_REVALIDATE_HEADER) : source.headers[PRERENDER_REVALIDATE_HEADER]);
|
|
37
|
+
}
|
|
38
|
+
function isWebHeaders(source) {
|
|
39
|
+
return typeof source.get === "function";
|
|
40
|
+
}
|
|
41
|
+
async function fetchRevalidateTarget(initialTarget, headers, dispatchRevalidate) {
|
|
42
|
+
const request = new Request(initialTarget, {
|
|
13
43
|
method: "HEAD",
|
|
14
|
-
headers
|
|
44
|
+
headers,
|
|
45
|
+
redirect: "manual"
|
|
15
46
|
});
|
|
16
|
-
|
|
47
|
+
return dispatchRevalidate ? await dispatchRevalidate(request) : await fetch(request);
|
|
48
|
+
}
|
|
49
|
+
function createRevalidateTarget(source, urlPath, trustedOrigin) {
|
|
50
|
+
const origin = resolveRevalidateOrigin(source, trustedOrigin);
|
|
51
|
+
const target = new URL(`${origin}${urlPath}`);
|
|
52
|
+
if (target.origin !== origin) throw new Error(`Invalid urlPath provided to revalidate(), resolved outside application origin`);
|
|
53
|
+
return target;
|
|
54
|
+
}
|
|
55
|
+
function resolveRevalidateOrigin(source, trustedOrigin) {
|
|
56
|
+
if (trustedOrigin) return normalizeRevalidateOrigin(trustedOrigin);
|
|
57
|
+
return normalizeRevalidateOrigin(`${resolveRequestProtocol(source)}://${resolveRequestHost(source, "localhost")}`);
|
|
58
|
+
}
|
|
59
|
+
function normalizeRevalidateOrigin(origin) {
|
|
60
|
+
const parsed = new URL(origin);
|
|
61
|
+
if (parsed.protocol !== "http:" && parsed.protocol !== "https:") throw new Error(`Invalid revalidate origin protocol: ${parsed.protocol}`);
|
|
62
|
+
return parsed.origin;
|
|
17
63
|
}
|
|
18
64
|
//#endregion
|
|
19
65
|
export { performOnDemandRevalidate };
|
|
@@ -22,6 +22,7 @@ type PagesWorkerEnv = {
|
|
|
22
22
|
type PagesWorkerExecutionContext = {
|
|
23
23
|
waitUntil?(promise: Promise<unknown>): void;
|
|
24
24
|
passThroughOnException?(): void;
|
|
25
|
+
cache?: unknown;
|
|
25
26
|
};
|
|
26
27
|
declare const _default: {
|
|
27
28
|
fetch(request: Request, env?: PagesWorkerEnv, ctx?: PagesWorkerExecutionContext): Promise<Response>;
|
|
@@ -1,4 +1,6 @@
|
|
|
1
|
+
import { normalizePathnameForRouteMatchStrict } from "../routing/utils.js";
|
|
1
2
|
import { hasBasePath, stripBasePath } from "../utils/base-path.js";
|
|
3
|
+
import { VINEXT_REVALIDATE_HOST_HEADER } from "../utils/protocol-headers.js";
|
|
2
4
|
import { notFoundStaticAssetResponse } from "./http-error-responses.js";
|
|
3
5
|
import { isOpenRedirectShaped } from "./open-redirect.js";
|
|
4
6
|
import { cloneRequestWithHeaders, cloneRequestWithUrl, filterInternalHeaders } from "./request-pipeline.js";
|
|
@@ -6,6 +8,7 @@ import { assetPrefixPathname, isNextStaticPath } from "../utils/asset-prefix.js"
|
|
|
6
8
|
import { DEFAULT_DEVICE_SIZES, DEFAULT_IMAGE_SIZES, handleConfiguredImageOptimization, isImageOptimizationPath } from "./image-optimization.js";
|
|
7
9
|
import { finalizeMissingStaticAssetResponse } from "./worker-utils.js";
|
|
8
10
|
import { fetchWorkerFilesystemRoute, runPagesRequest, wrapMiddlewareWithBasePath } from "./pages-request-pipeline.js";
|
|
11
|
+
import { createWorkerRevalidationContext } from "./worker-revalidation-context.js";
|
|
9
12
|
import { registerConfiguredCacheAdapters } from "virtual:vinext-cache-adapters";
|
|
10
13
|
import { registerConfiguredImageOptimizer } from "virtual:vinext-image-adapters";
|
|
11
14
|
import * as pagesEntry from "virtual:vinext-server-entry";
|
|
@@ -24,7 +27,7 @@ import * as pagesEntry from "virtual:vinext-server-entry";
|
|
|
24
27
|
* import handler from "vinext/server/pages-router-entry";
|
|
25
28
|
* return handler.fetch(request, env, ctx);
|
|
26
29
|
*/
|
|
27
|
-
const { handleApiRoute, hasMiddleware, matchPageRoute, normalizeDataRequest, renderPage, runMiddleware, vinextConfig } = pagesEntry;
|
|
30
|
+
const { authorizeOnDemandRevalidate, handleApiRoute, hasMiddleware, matchPageRoute, normalizeDataRequest, renderPage, runMiddleware, vinextConfig } = pagesEntry;
|
|
28
31
|
const basePath = vinextConfig?.basePath ?? "";
|
|
29
32
|
const assetPathPrefix = assetPrefixPathname(vinextConfig?.assetPrefix ?? "");
|
|
30
33
|
const trailingSlash = vinextConfig?.trailingSlash ?? false;
|
|
@@ -46,14 +49,22 @@ const imageConfig = vinextConfig?.images ? {
|
|
|
46
49
|
var pages_router_entry_default = { async fetch(request, env, ctx) {
|
|
47
50
|
return handleRequest(request, env, ctx);
|
|
48
51
|
} };
|
|
49
|
-
async function handleRequest(request, env,
|
|
52
|
+
async function handleRequest(request, env, platformCtx) {
|
|
53
|
+
const ctx = createWorkerRevalidationContext(platformCtx, (internalRequest, internalCtx) => handleRequest(internalRequest, env, internalCtx));
|
|
50
54
|
registerConfiguredCacheAdapters(env);
|
|
51
55
|
registerConfiguredImageOptimizer(env);
|
|
52
56
|
try {
|
|
53
57
|
let pathname = new URL(request.url).pathname;
|
|
54
58
|
if (isOpenRedirectShaped(pathname)) return new Response("This page could not be found", { status: 404 });
|
|
59
|
+
try {
|
|
60
|
+
normalizePathnameForRouteMatchStrict(pathname);
|
|
61
|
+
} catch {
|
|
62
|
+
return new Response("Bad Request", { status: 400 });
|
|
63
|
+
}
|
|
55
64
|
const missingBuildAsset = isNextStaticPath(pathname, basePath, assetPathPrefix);
|
|
56
|
-
|
|
65
|
+
const filteredHeaders = ctx.isInternalPagesRevalidation ? new Headers(request.headers) : filterInternalHeaders(request.headers);
|
|
66
|
+
filteredHeaders.delete(VINEXT_REVALIDATE_HOST_HEADER);
|
|
67
|
+
request = cloneRequestWithHeaders(request, filteredHeaders);
|
|
57
68
|
const hadBasePath = !basePath || hasBasePath(pathname, basePath);
|
|
58
69
|
{
|
|
59
70
|
const stripped = stripBasePath(pathname, basePath);
|
|
@@ -87,10 +98,11 @@ async function handleRequest(request, env, ctx) {
|
|
|
87
98
|
isDataRequest: isDataReq,
|
|
88
99
|
hasMiddleware,
|
|
89
100
|
ctx,
|
|
101
|
+
authorizeOnDemandRevalidate: typeof authorizeOnDemandRevalidate === "function" ? authorizeOnDemandRevalidate : void 0,
|
|
90
102
|
matchPageRoute: typeof matchPageRoute === "function" ? matchPageRoute : null,
|
|
91
103
|
runMiddleware: typeof runMiddleware === "function" ? wrapMiddlewareWithBasePath(runMiddleware, basePath, hadBasePath) : null,
|
|
92
104
|
renderPage: typeof renderPage === "function" ? (req, resolvedUrl, options, stagedHeaders) => renderPage(req, resolvedUrl, null, ctx, stagedHeaders, options) : null,
|
|
93
|
-
handleApi: typeof handleApiRoute === "function" ? (req, apiUrl) => handleApiRoute(req, apiUrl, ctx) : null,
|
|
105
|
+
handleApi: typeof handleApiRoute === "function" ? (req, apiUrl) => handleApiRoute(req, apiUrl, ctx, new URL(req.url).origin) : null,
|
|
94
106
|
serveFilesystemRoute: async (requestPathname, _stagedHeaders, phase) => {
|
|
95
107
|
if (!env?.ASSETS) return false;
|
|
96
108
|
return fetchWorkerFilesystemRoute(request, requestPathname, phase, (assetRequest) => Promise.resolve(env.ASSETS.fetch(assetRequest)));
|
|
@@ -58,11 +58,11 @@ function decodedPrerenderRouteParamEquals(prerenderValue, matchedValue) {
|
|
|
58
58
|
if (prerenderValue.length !== matchedValue.length) return false;
|
|
59
59
|
return prerenderValue.every((item, index) => {
|
|
60
60
|
const decoded = decodePrerenderRouteParam(item);
|
|
61
|
-
return decoded !== null && decoded === matchedValue[index];
|
|
61
|
+
return item === matchedValue[index] || decoded !== null && decoded === matchedValue[index];
|
|
62
62
|
});
|
|
63
63
|
}
|
|
64
64
|
const decoded = decodePrerenderRouteParam(prerenderValue);
|
|
65
|
-
return decoded !== null && decoded === matchedValue;
|
|
65
|
+
return prerenderValue === matchedValue || decoded !== null && decoded === matchedValue;
|
|
66
66
|
}
|
|
67
67
|
function prerenderRouteParamsPayloadMatchesRoute(payload, routePattern, params) {
|
|
68
68
|
return matchPrerenderRouteParamsPayload(payload, routePattern, params)?.kind === "exact";
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { NextI18nConfig } from "../config/next-config.js";
|
|
1
2
|
import { negotiateEncoding } from "./accept-encoding.js";
|
|
2
3
|
import { StaticFileCache } from "./static-file-cache.js";
|
|
3
4
|
import { resolveRequestHost, trustProxy, trustedHosts } from "./proxy-trust.js";
|
|
@@ -101,12 +102,9 @@ declare function tryServeStatic(req: IncomingMessage, res: ServerResponse, clien
|
|
|
101
102
|
* Convert a Node.js IncomingMessage to a Web Request object.
|
|
102
103
|
*
|
|
103
104
|
* When `urlOverride` is provided, it is used as the path + query string
|
|
104
|
-
* instead of `req.url`.
|
|
105
|
-
* caller has already decoded and normalized the pathname (e.g. the App
|
|
106
|
-
* Router prod server normalizes before static-asset lookup, and can pass
|
|
107
|
-
* the result here so the downstream RSC handler doesn't re-normalize).
|
|
105
|
+
* instead of `req.url`.
|
|
108
106
|
*/
|
|
109
|
-
declare function nodeToWebRequest(req: IncomingMessage, urlOverride?: string, prerenderSecret?: string): Request;
|
|
107
|
+
declare function nodeToWebRequest(req: IncomingMessage, urlOverride?: string, prerenderSecret?: string, i18nConfig?: NextI18nConfig | null, authorizeOnDemandRevalidate?: (headerValue: string | null) => boolean): Request;
|
|
110
108
|
/**
|
|
111
109
|
* Stream a Web Response back to a Node.js ServerResponse.
|
|
112
110
|
* Supports streaming compression for SSR responses.
|
|
@@ -6,9 +6,9 @@ import { VINEXT_STATIC_FILE_HEADER } from "./headers.js";
|
|
|
6
6
|
import { normalizePath } from "./normalize-path.js";
|
|
7
7
|
import { notFoundResponse } from "./http-error-responses.js";
|
|
8
8
|
import { isOpenRedirectShaped } from "./open-redirect.js";
|
|
9
|
-
import { filterInternalHeaders } from "./request-pipeline.js";
|
|
9
|
+
import { canonicalizeRequestPathname, filterInternalHeaders } from "./request-pipeline.js";
|
|
10
10
|
import { isUnknownRecord } from "../utils/record.js";
|
|
11
|
-
import { buildNextDataNotFoundResponse, isNextDataPathname, normalizeNextDataPagePathname, parseNextDataPathname } from "./pages-data-route.js";
|
|
11
|
+
import { buildNextDataNotFoundResponse, encodeUrlParserIgnoredCharacters, isNextDataPathname, normalizeNextDataPagePathname, parseNextDataPathname, urlParserCreatesPagesDataPath } from "./pages-data-route.js";
|
|
12
12
|
import { ASSET_PREFIX_URL_DIR, assetPrefixPathname, isAbsoluteAssetPrefix } from "../utils/asset-prefix.js";
|
|
13
13
|
import { setPagesClientAssets } from "./pages-client-assets.js";
|
|
14
14
|
import { resolveRequestHost, resolveRequestProtocol, trustProxy, trustedHosts } from "./proxy-trust.js";
|
|
@@ -16,6 +16,7 @@ import { DEFAULT_DEVICE_SIZES, DEFAULT_IMAGE_SIZES, isImageOptimizationPath, isS
|
|
|
16
16
|
import { installSocketErrorBackstop } from "./socket-error-backstop.js";
|
|
17
17
|
import { CONTENT_TYPES, StaticFileCache, etagFromFilenameHash } from "./static-file-cache.js";
|
|
18
18
|
import { collectInlineCssManifest } from "../build/inline-css.js";
|
|
19
|
+
import { readTrustedRevalidationHostname } from "./revalidation-host.js";
|
|
19
20
|
import { mergeHeaders } from "./worker-utils.js";
|
|
20
21
|
import { runPagesRequest, wrapMiddlewareWithBasePath } from "./pages-request-pipeline.js";
|
|
21
22
|
import { computeClientRuntimeMetadata } from "../utils/client-runtime-metadata.js";
|
|
@@ -555,18 +556,18 @@ async function statIfFile(filePath) {
|
|
|
555
556
|
* Convert a Node.js IncomingMessage to a Web Request object.
|
|
556
557
|
*
|
|
557
558
|
* When `urlOverride` is provided, it is used as the path + query string
|
|
558
|
-
* instead of `req.url`.
|
|
559
|
-
* caller has already decoded and normalized the pathname (e.g. the App
|
|
560
|
-
* Router prod server normalizes before static-asset lookup, and can pass
|
|
561
|
-
* the result here so the downstream RSC handler doesn't re-normalize).
|
|
559
|
+
* instead of `req.url`.
|
|
562
560
|
*/
|
|
563
|
-
function nodeToWebRequest(req, urlOverride, prerenderSecret) {
|
|
564
|
-
const
|
|
565
|
-
const url = new URL(urlOverride ?? req.url ?? "/", origin);
|
|
561
|
+
function nodeToWebRequest(req, urlOverride, prerenderSecret, i18nConfig, authorizeOnDemandRevalidate) {
|
|
562
|
+
const proto = resolveRequestProtocol(req);
|
|
566
563
|
const rawHeaders = nodeHeadersToWebHeaders(req.headers);
|
|
564
|
+
const revalidationHostname = readTrustedRevalidationHostname(rawHeaders, i18nConfig, authorizeOnDemandRevalidate);
|
|
565
|
+
const origin = `${proto}://${revalidationHostname ?? resolveRequestHost(req, "localhost")}`;
|
|
566
|
+
const url = new URL(urlOverride ?? req.url ?? "/", origin);
|
|
567
567
|
const prerenderRouteParamsPayload = readTrustedPrerenderRouteParamsFromHeaders(rawHeaders, prerenderSecret);
|
|
568
568
|
const isTrustedSpeculativePrerender = process.env.VINEXT_PRERENDER === "1" && prerenderSecret !== void 0 && rawHeaders.get("x-vinext-prerender-secret") === prerenderSecret && rawHeaders.get("x-vinext-prerender-speculative") === "1";
|
|
569
569
|
const headers = filterInternalHeaders(rawHeaders);
|
|
570
|
+
if (revalidationHostname) headers.set("host", revalidationHostname);
|
|
570
571
|
const prerenderRouteParamsHeader = serializePrerenderRouteParamsHeader(prerenderRouteParamsPayload);
|
|
571
572
|
if (prerenderRouteParamsHeader !== null) headers.set(VINEXT_PRERENDER_ROUTE_PARAMS_HEADER, prerenderRouteParamsHeader);
|
|
572
573
|
if (isTrustedSpeculativePrerender) headers.set(VINEXT_PRERENDER_SPECULATIVE_HEADER, "1");
|
|
@@ -663,19 +664,32 @@ async function startProdServer(options = {}) {
|
|
|
663
664
|
silent
|
|
664
665
|
});
|
|
665
666
|
}
|
|
666
|
-
function createNodeExecutionContext() {
|
|
667
|
+
function createNodeExecutionContext(trustedRevalidateOrigin) {
|
|
667
668
|
return {
|
|
668
669
|
waitUntil(promise) {
|
|
669
670
|
Promise.resolve(promise).catch(() => {});
|
|
670
671
|
},
|
|
671
|
-
passThroughOnException() {}
|
|
672
|
+
passThroughOnException() {},
|
|
673
|
+
trustedRevalidateOrigin
|
|
672
674
|
};
|
|
673
675
|
}
|
|
676
|
+
function resolveTrustedNodeRevalidateOrigin(req, configuredHost, configuredPort) {
|
|
677
|
+
const port = req.socket.localPort ?? configuredPort;
|
|
678
|
+
return `http://${normalizeInternalFetchHost(configuredHost)}:${port}`;
|
|
679
|
+
}
|
|
680
|
+
function normalizeInternalFetchHost(host) {
|
|
681
|
+
if (!host || host === "0.0.0.0" || host === "::") return "localhost";
|
|
682
|
+
if (host.includes(":") && !host.startsWith("[")) return `[${host}]`;
|
|
683
|
+
return host;
|
|
684
|
+
}
|
|
674
685
|
function resolveAppRouterHandler(entry) {
|
|
675
|
-
if (typeof entry === "function")
|
|
686
|
+
if (typeof entry === "function") {
|
|
687
|
+
const handler = entry;
|
|
688
|
+
return (request, ctx) => Promise.resolve(handler(request, ctx));
|
|
689
|
+
}
|
|
676
690
|
if (entry && typeof entry === "object" && "fetch" in entry) {
|
|
677
691
|
const workerEntry = entry;
|
|
678
|
-
if (typeof workerEntry.fetch === "function") return (request) => Promise.resolve(workerEntry.fetch(request, void 0,
|
|
692
|
+
if (typeof workerEntry.fetch === "function") return (request, ctx) => Promise.resolve(workerEntry.fetch(request, void 0, ctx));
|
|
679
693
|
}
|
|
680
694
|
console.error("[vinext] App Router entry must export either a default handler function or a Worker-style default export with fetch()");
|
|
681
695
|
process.exit(1);
|
|
@@ -796,6 +810,8 @@ async function startAppRouterServer(options) {
|
|
|
796
810
|
const appRouterBasePath = typeof rscModule.__basePath === "string" ? rscModule.__basePath : "";
|
|
797
811
|
const appRouterInlineCss = rscModule.__inlineCss === true;
|
|
798
812
|
const appRouterHasPagesDir = rscModule.__hasPagesDir === true;
|
|
813
|
+
const appRouterI18nConfig = rscModule.__i18nConfig ?? null;
|
|
814
|
+
const appRouterAuthorizeOnDemandRevalidate = typeof rscModule.authorizeOnDemandRevalidate === "function" ? rscModule.authorizeOnDemandRevalidate : void 0;
|
|
799
815
|
const appImageAllowedWidths = Array.isArray(rscModule.__imageAllowedWidths) ? rscModule.__imageAllowedWidths : [...DEFAULT_DEVICE_SIZES, ...DEFAULT_IMAGE_SIZES];
|
|
800
816
|
let imageConfig = typeof rscModule.__imageConfig === "object" && rscModule.__imageConfig !== null ? rscModule.__imageConfig : void 0;
|
|
801
817
|
if (imageConfig === void 0) {
|
|
@@ -873,9 +889,14 @@ async function startAppRouterServer(options) {
|
|
|
873
889
|
return;
|
|
874
890
|
}
|
|
875
891
|
try {
|
|
876
|
-
const
|
|
877
|
-
const
|
|
878
|
-
|
|
892
|
+
const request = nodeToWebRequest(req, rawUrl, prerenderSecret, appRouterI18nConfig, appRouterAuthorizeOnDemandRevalidate);
|
|
893
|
+
const response = await rscHandler(request, createNodeExecutionContext(resolveTrustedNodeRevalidateOrigin(req, host, port)));
|
|
894
|
+
if (missingBuildAsset && response.status === 404) {
|
|
895
|
+
cancelResponseBody(response);
|
|
896
|
+
res.writeHead(404, { "Content-Type": "text/plain; charset=utf-8" });
|
|
897
|
+
res.end("Not Found");
|
|
898
|
+
return;
|
|
899
|
+
}
|
|
879
900
|
const staticFileSignal = response.headers.get(VINEXT_STATIC_FILE_HEADER);
|
|
880
901
|
if (staticFileSignal) {
|
|
881
902
|
let staticFilePath = "/";
|
|
@@ -891,12 +912,6 @@ async function startAppRouterServer(options) {
|
|
|
891
912
|
await sendWebResponse(notFoundResponse({ headers: toWebHeaders(staticResponseHeaders) }), req, res, compress);
|
|
892
913
|
return;
|
|
893
914
|
}
|
|
894
|
-
if (missingBuildAsset && response.status === 404) {
|
|
895
|
-
cancelResponseBody(response);
|
|
896
|
-
res.writeHead(404, { "Content-Type": "text/plain; charset=utf-8" });
|
|
897
|
-
res.end("Not Found");
|
|
898
|
-
return;
|
|
899
|
-
}
|
|
900
915
|
await sendWebResponse(response, req, res, compress);
|
|
901
916
|
} catch (e) {
|
|
902
917
|
console.error("[vinext] Server error:", e);
|
|
@@ -939,14 +954,16 @@ function readPagesServerEntryPageRoutes(value) {
|
|
|
939
954
|
*
|
|
940
955
|
* Uses the server entry (dist/server/entry.js) which exports:
|
|
941
956
|
* - renderPage(request, url, manifest, ctx?, middlewareHeaders?) — SSR rendering (Web Request → Response)
|
|
942
|
-
* - handleApiRoute(request, url, ctx?) — API route handling
|
|
957
|
+
* - handleApiRoute(request, url, ctx?, trustedRevalidateOrigin?) — API route handling
|
|
958
|
+
* (ctx optional; pass for ctx.waitUntil() on Workers)
|
|
959
|
+
* - authorizeOnDemandRevalidate(header) — validates against the bundled build secret
|
|
943
960
|
* - runMiddleware(request, ctx?) — middleware execution (ctx optional; pass for ctx.waitUntil() on Workers)
|
|
944
961
|
* - vinextConfig — embedded next.config.js settings
|
|
945
962
|
*/
|
|
946
963
|
async function startPagesRouterServer(options) {
|
|
947
964
|
const { port, host, clientDir, serverEntryPath, compress, purpose, silent } = options;
|
|
948
965
|
const serverEntry = await importServerEntryModule(serverEntryPath);
|
|
949
|
-
const { renderPage, handleApiRoute: handleApi, runMiddleware, vinextConfig, buildId: pagesBuildId } = serverEntry;
|
|
966
|
+
const { renderPage, handleApiRoute: handleApi, authorizeOnDemandRevalidate, runMiddleware, vinextConfig, buildId: pagesBuildId } = serverEntry;
|
|
950
967
|
const matchPageRoute = typeof serverEntry.matchPageRoute === "function" ? serverEntry.matchPageRoute : void 0;
|
|
951
968
|
const hasMiddleware = serverEntry.hasMiddleware === true;
|
|
952
969
|
const pageRoutes = readPagesServerEntryPageRoutes(serverEntry.pageRoutes);
|
|
@@ -987,8 +1004,9 @@ async function startPagesRouterServer(options) {
|
|
|
987
1004
|
res.end("This page could not be found");
|
|
988
1005
|
return;
|
|
989
1006
|
}
|
|
990
|
-
const rawPagesPathname = rawPagesPathnameBeforeNormalize.replaceAll("\\", "/");
|
|
1007
|
+
const rawPagesPathname = canonicalizeRequestPathname(rawPagesPathnameBeforeNormalize.replaceAll("\\", "/"));
|
|
991
1008
|
const rawQs = rawUrl.includes("?") ? rawUrl.slice(rawUrl.indexOf("?")) : "";
|
|
1009
|
+
let requestPathname = normalizePath(rawPagesPathname);
|
|
992
1010
|
let pathname;
|
|
993
1011
|
try {
|
|
994
1012
|
pathname = normalizePath(normalizePathnameForRouteMatchStrict(rawPagesPathname));
|
|
@@ -997,7 +1015,7 @@ async function startPagesRouterServer(options) {
|
|
|
997
1015
|
res.end("Bad Request");
|
|
998
1016
|
return;
|
|
999
1017
|
}
|
|
1000
|
-
let url =
|
|
1018
|
+
let url = requestPathname + rawQs;
|
|
1001
1019
|
if (pathname === "/__vinext/prerender/pages-static-paths") {
|
|
1002
1020
|
const secret = req.headers[VINEXT_PRERENDER_SECRET_HEADER];
|
|
1003
1021
|
if (!prerenderSecret || secret !== prerenderSecret) {
|
|
@@ -1058,18 +1076,31 @@ async function startPagesRouterServer(options) {
|
|
|
1058
1076
|
return;
|
|
1059
1077
|
}
|
|
1060
1078
|
try {
|
|
1061
|
-
const hadBasePath = !basePath || hasBasePath(
|
|
1079
|
+
const hadBasePath = !basePath || hasBasePath(requestPathname, basePath);
|
|
1080
|
+
let configMatchPathname = stripBasePath(requestPathname, basePath);
|
|
1062
1081
|
{
|
|
1063
|
-
const
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1082
|
+
const strippedPathname = stripBasePath(pathname, basePath);
|
|
1083
|
+
const strippedRequestPathname = stripBasePath(requestPathname, basePath);
|
|
1084
|
+
pathname = strippedPathname;
|
|
1085
|
+
if (strippedRequestPathname !== requestPathname) {
|
|
1086
|
+
requestPathname = strippedRequestPathname;
|
|
1087
|
+
url = requestPathname + rawQs;
|
|
1067
1088
|
}
|
|
1068
1089
|
}
|
|
1090
|
+
if (urlParserCreatesPagesDataPath(pathname)) {
|
|
1091
|
+
res.writeHead(404);
|
|
1092
|
+
res.end("This page could not be found");
|
|
1093
|
+
return;
|
|
1094
|
+
}
|
|
1095
|
+
requestPathname = encodeUrlParserIgnoredCharacters(requestPathname);
|
|
1096
|
+
{
|
|
1097
|
+
const qs = url.includes("?") ? url.slice(url.indexOf("?")) : "";
|
|
1098
|
+
url = requestPathname + qs;
|
|
1099
|
+
}
|
|
1069
1100
|
let isDataReq = false;
|
|
1070
1101
|
const originalRenderUrl = url;
|
|
1071
|
-
if (isNextDataPathname(
|
|
1072
|
-
const dataMatch = pagesBuildId ? parseNextDataPathname(
|
|
1102
|
+
if (isNextDataPathname(requestPathname)) {
|
|
1103
|
+
const dataMatch = pagesBuildId ? parseNextDataPathname(requestPathname, pagesBuildId) : null;
|
|
1073
1104
|
if (!dataMatch) {
|
|
1074
1105
|
await sendWebResponse(buildNextDataNotFoundResponse(), req, res, compress);
|
|
1075
1106
|
return;
|
|
@@ -1078,16 +1109,20 @@ async function startPagesRouterServer(options) {
|
|
|
1078
1109
|
const qs = url.includes("?") ? url.slice(url.indexOf("?")) : "";
|
|
1079
1110
|
const pagePathname = normalizeNextDataPagePathname(dataMatch.pagePathname, hasMiddleware && trailingSlash);
|
|
1080
1111
|
url = pagePathname + qs;
|
|
1112
|
+
requestPathname = pagePathname;
|
|
1081
1113
|
pathname = pagePathname;
|
|
1114
|
+
configMatchPathname = pagePathname;
|
|
1082
1115
|
}
|
|
1083
1116
|
const protocol = resolveRequestProtocol(req);
|
|
1084
1117
|
const hostHeader = resolveRequestHost(req, `${host}:${port}`);
|
|
1085
1118
|
const rawReqHeaders = nodeHeadersToWebHeaders(req.headers);
|
|
1119
|
+
const revalidationHostname = readTrustedRevalidationHostname(rawReqHeaders, i18nConfig, typeof authorizeOnDemandRevalidate === "function" ? authorizeOnDemandRevalidate : void 0);
|
|
1086
1120
|
const isDataRequest = isDataReq;
|
|
1087
1121
|
const reqHeaders = filterInternalHeaders(rawReqHeaders);
|
|
1122
|
+
if (revalidationHostname) reqHeaders.set("host", revalidationHostname);
|
|
1088
1123
|
const method = req.method ?? "GET";
|
|
1089
1124
|
const hasBody = method !== "GET" && method !== "HEAD";
|
|
1090
|
-
const result = await runPagesRequest(new Request(`${protocol}://${hostHeader}${url}`, {
|
|
1125
|
+
const result = await runPagesRequest(new Request(`${protocol}://${revalidationHostname ?? hostHeader}${url}`, {
|
|
1091
1126
|
method,
|
|
1092
1127
|
headers: reqHeaders,
|
|
1093
1128
|
body: hasBody ? readNodeStream(req) : void 0,
|
|
@@ -1105,13 +1140,15 @@ async function startPagesRouterServer(options) {
|
|
|
1105
1140
|
hasMiddleware,
|
|
1106
1141
|
ctx: void 0,
|
|
1107
1142
|
rawSearch: rawQs,
|
|
1143
|
+
authorizeOnDemandRevalidate: typeof authorizeOnDemandRevalidate === "function" ? authorizeOnDemandRevalidate : void 0,
|
|
1144
|
+
configMatchPathname,
|
|
1108
1145
|
matchPageRoute: matchPageRoute ?? null,
|
|
1109
1146
|
runMiddleware: typeof runMiddleware === "function" ? wrapMiddlewareWithBasePath(runMiddleware, basePath, hadBasePath) : null,
|
|
1110
1147
|
renderPage: typeof renderPage === "function" ? (request, resolvedUrl, options, stagedHeaders) => renderPage(request, resolvedUrl, ssrManifest, void 0, stagedHeaders, {
|
|
1111
1148
|
...options,
|
|
1112
1149
|
originalUrl: originalRenderUrl
|
|
1113
1150
|
}) : null,
|
|
1114
|
-
handleApi: typeof handleApi === "function" ? (request, apiUrl) => handleApi(request, apiUrl, createNodeExecutionContext()) : null,
|
|
1151
|
+
handleApi: typeof handleApi === "function" ? (request, apiUrl) => handleApi(request, apiUrl, createNodeExecutionContext(), resolveTrustedNodeRevalidateOrigin(req, host, port)) : null,
|
|
1115
1152
|
serveFilesystemRoute: async (requestPathname, stagedHeaders, phase) => {
|
|
1116
1153
|
if (req.method !== "GET" && req.method !== "HEAD" || requestPathname === "/" || requestPathname === "/api" || requestPathname.startsWith("/api/") || phase === "direct" && requestPathname.startsWith(`/_next/static/`)) return false;
|
|
1117
1154
|
return tryServeStatic(req, res, clientDir, requestPathname, compress, staticCache, stagedHeaders);
|