vinext 0.1.3 → 0.1.5
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/dist/build/client-build-config.d.ts +11 -2
- package/dist/build/client-build-config.js +17 -6
- package/dist/build/css-url-assets.d.ts +1 -1
- package/dist/build/css-url-assets.js +9 -7
- package/dist/build/prerender.js +3 -1
- package/dist/cache/cache-adapters-virtual.js +1 -1
- package/dist/client/pages-router-link-navigation.d.ts +33 -7
- package/dist/client/pages-router-link-navigation.js +32 -2
- package/dist/client/vinext-next-data.js +2 -0
- package/dist/cloudflare/src/cache/kv-data-adapter.runtime.d.ts +1 -1
- package/dist/config/config-matchers.d.ts +11 -1
- package/dist/config/config-matchers.js +14 -2
- package/dist/config/tsconfig-paths.js +14 -1
- package/dist/deploy.js +20 -13
- package/dist/entries/app-rsc-entry.js +27 -22
- package/dist/entries/pages-client-entry.js +14 -13
- package/dist/entries/pages-server-entry.js +8 -27
- package/dist/index.js +365 -147
- package/dist/plugins/css-data-url.js +30 -26
- package/dist/plugins/dynamic-preload-metadata.js +2 -4
- package/dist/plugins/extensionless-dynamic-import.js +27 -24
- package/dist/plugins/fonts.js +5 -4
- package/dist/plugins/import-meta-url.js +21 -15
- package/dist/plugins/instrumentation-client.js +1 -1
- package/dist/plugins/middleware-server-only.js +7 -6
- package/dist/plugins/og-assets.js +48 -46
- package/dist/plugins/optimize-imports.js +9 -3
- package/dist/plugins/remove-console.d.ts +7 -1
- package/dist/plugins/remove-console.js +4 -1
- package/dist/plugins/require-context.js +21 -20
- package/dist/plugins/strip-server-exports.d.ts +16 -8
- package/dist/plugins/strip-server-exports.js +496 -46
- package/dist/routing/app-route-graph.js +2 -2
- package/dist/server/app-bfcache-identity.d.ts +26 -0
- package/dist/server/app-bfcache-identity.js +127 -0
- package/dist/server/app-browser-action-result.js +1 -1
- package/dist/server/app-browser-entry.js +22 -12
- package/dist/server/app-browser-navigation-controller.d.ts +1 -1
- package/dist/server/app-browser-navigation-controller.js +1 -1
- package/dist/server/app-browser-state.d.ts +3 -22
- package/dist/server/app-browser-state.js +23 -139
- package/dist/server/app-browser-stream.js +1 -1
- package/dist/server/app-browser-visible-commit.d.ts +1 -1
- package/dist/server/app-browser-visible-commit.js +3 -2
- package/dist/server/app-fallback-renderer.d.ts +1 -1
- package/dist/server/app-layout-param-observation.d.ts +1 -1
- package/dist/server/app-layout-param-observation.js +1 -1
- package/dist/server/app-middleware.js +2 -1
- package/dist/server/app-page-boundary-render.d.ts +1 -1
- package/dist/server/app-page-boundary.js +1 -1
- package/dist/server/app-page-cache-finalizer.d.ts +62 -0
- package/dist/server/app-page-cache-finalizer.js +122 -0
- package/dist/server/app-page-cache-render.d.ts +2 -2
- package/dist/server/app-page-cache-render.js +1 -1
- package/dist/server/app-page-cache.d.ts +2 -53
- package/dist/server/app-page-cache.js +5 -131
- package/dist/server/app-page-dispatch.d.ts +2 -2
- package/dist/server/app-page-dispatch.js +10 -8
- package/dist/server/app-page-probe.js +3 -2
- package/dist/server/app-page-render-observation.js +2 -2
- package/dist/server/app-page-render.d.ts +3 -3
- package/dist/server/app-page-render.js +3 -2
- package/dist/server/app-page-stream.d.ts +2 -9
- package/dist/server/app-page-stream.js +1 -35
- package/dist/server/app-pages-bridge.d.ts +5 -1
- package/dist/server/app-pages-bridge.js +5 -13
- package/dist/server/app-request-context.d.ts +1 -2
- package/dist/server/app-request-context.js +2 -1
- package/dist/server/app-route-handler-dispatch.js +3 -2
- package/dist/server/app-route-handler-execution.d.ts +1 -1
- package/dist/server/app-route-handler-execution.js +1 -1
- package/dist/server/app-route-handler-response.d.ts +1 -1
- package/dist/server/app-router-entry.js +2 -1
- package/dist/server/app-rsc-handler.d.ts +3 -0
- package/dist/server/app-rsc-handler.js +73 -31
- package/dist/server/app-rsc-response-finalizer.js +1 -1
- package/dist/server/app-rsc-route-matching.js +6 -2
- package/dist/server/app-server-action-execution.d.ts +1 -1
- package/dist/server/app-server-action-execution.js +10 -6
- package/dist/server/app-ssr-entry.d.ts +1 -1
- package/dist/server/app-ssr-entry.js +12 -38
- package/dist/server/app-ssr-router-instance.d.ts +6 -0
- package/dist/server/app-ssr-router-instance.js +24 -0
- package/dist/server/app-ssr-stream.js +1 -1
- package/dist/server/artifact-compatibility.js +1 -1
- package/dist/server/before-interactive-head.d.ts +17 -0
- package/dist/server/before-interactive-head.js +35 -0
- package/dist/server/client-reuse-manifest.js +1 -1
- package/dist/server/csp.js +1 -4
- package/dist/server/defer-until-stream-consumed.d.ts +7 -0
- package/dist/server/defer-until-stream-consumed.js +34 -0
- package/dist/server/dev-server.js +82 -37
- package/dist/server/instrumentation.js +1 -1
- package/dist/server/isr-cache.d.ts +1 -1
- package/dist/server/isr-cache.js +1 -1
- package/dist/server/isr-decision.d.ts +1 -1
- package/dist/server/middleware-matcher.js +20 -9
- package/dist/server/middleware-runtime.d.ts +3 -4
- package/dist/server/middleware-runtime.js +4 -2
- package/dist/server/navigation-planner.d.ts +3 -12
- package/dist/server/navigation-planner.js +24 -0
- package/dist/server/navigation-trace.d.ts +2 -1
- package/dist/server/navigation-trace.js +1 -0
- package/dist/server/open-redirect.d.ts +12 -0
- package/dist/server/open-redirect.js +21 -0
- package/dist/server/operation-token.d.ts +40 -0
- package/dist/server/operation-token.js +85 -0
- package/dist/server/pages-data-route.d.ts +1 -1
- package/dist/server/pages-data-route.js +7 -4
- package/dist/server/pages-dev-module-url.d.ts +4 -0
- package/dist/server/pages-dev-module-url.js +15 -0
- package/dist/server/pages-document-initial-props.d.ts +4 -15
- package/dist/server/pages-document-initial-props.js +27 -56
- package/dist/server/pages-i18n.js +2 -2
- package/dist/server/pages-page-data.d.ts +1 -1
- package/dist/server/pages-page-data.js +3 -1
- package/dist/server/pages-page-handler.js +3 -1
- package/dist/server/pages-page-response.d.ts +3 -1
- package/dist/server/pages-page-response.js +6 -6
- package/dist/server/pages-readiness.js +1 -1
- package/dist/server/pages-request-pipeline.d.ts +7 -7
- package/dist/server/pages-request-pipeline.js +63 -21
- package/dist/server/prod-server.d.ts +3 -1
- package/dist/server/prod-server.js +43 -11
- package/dist/server/request-pipeline.d.ts +1 -24
- package/dist/server/request-pipeline.js +1 -33
- package/dist/server/seed-cache.d.ts +1 -1
- package/dist/server/static-file-cache.js +16 -4
- package/dist/shims/before-interactive-context.d.ts +14 -3
- package/dist/shims/cache-handler.d.ts +106 -0
- package/dist/shims/cache-handler.js +176 -0
- package/dist/shims/cache-request-state.d.ts +47 -0
- package/dist/shims/cache-request-state.js +126 -0
- package/dist/shims/cache-runtime.d.ts +2 -2
- package/dist/shims/cache-runtime.js +3 -14
- package/dist/shims/cache.d.ts +3 -231
- package/dist/shims/cache.js +17 -383
- package/dist/shims/cdn-cache.d.ts +1 -1
- package/dist/shims/cdn-cache.js +1 -1
- package/dist/shims/document.d.ts +15 -20
- package/dist/shims/document.js +5 -8
- package/dist/shims/error-boundary-navigation.d.ts +7 -0
- package/dist/shims/error-boundary-navigation.js +44 -0
- package/dist/shims/error-boundary.js +10 -8
- package/dist/shims/error.js +2 -1
- package/dist/shims/fetch-cache.js +1 -1
- package/dist/shims/form.js +1 -1
- package/dist/shims/image.js +74 -9
- package/dist/shims/internal/app-page-props-cache-key.d.ts +5 -0
- package/dist/shims/internal/app-page-props-cache-key.js +16 -0
- package/dist/shims/internal/navigation-untracked.js +2 -1
- package/dist/shims/internal/pages-data-fetch-dedup.d.ts +6 -7
- package/dist/shims/internal/pages-data-fetch-dedup.js +67 -14
- package/dist/shims/internal/pages-data-target.js +1 -1
- package/dist/shims/layout-segment-context.d.ts +1 -1
- package/dist/shims/layout-segment-context.js +2 -1
- package/dist/shims/link.js +38 -17
- package/dist/shims/metadata.js +4 -4
- package/dist/shims/navigation-context-state.d.ts +40 -0
- package/dist/shims/navigation-context-state.js +116 -0
- package/dist/shims/navigation-errors.d.ts +55 -0
- package/dist/shims/navigation-errors.js +110 -0
- package/dist/shims/navigation-server.d.ts +3 -0
- package/dist/shims/navigation-server.js +3 -0
- package/dist/shims/navigation-state.d.ts +1 -2
- package/dist/shims/navigation-state.js +2 -1
- package/dist/shims/navigation.d.ts +3 -291
- package/dist/shims/navigation.js +16 -445
- package/dist/shims/navigation.react-server.d.ts +2 -2
- package/dist/shims/navigation.react-server.js +3 -1
- package/dist/shims/request-state-types.d.ts +3 -3
- package/dist/shims/router.d.ts +6 -2
- package/dist/shims/router.js +99 -20
- package/dist/shims/script.js +9 -5
- package/dist/shims/slot.js +3 -1
- package/dist/shims/unified-request-context.d.ts +2 -2
- package/dist/utils/has-trailing-comma.d.ts +24 -0
- package/dist/utils/has-trailing-comma.js +62 -0
- package/dist/utils/text-stream.d.ts +1 -1
- package/dist/utils/text-stream.js +2 -2
- package/dist/utils/virtual-module.d.ts +5 -0
- package/dist/utils/virtual-module.js +0 -0
- package/dist/utils/vite-version.d.ts +12 -1
- package/dist/utils/vite-version.js +9 -1
- package/package.json +5 -1
|
@@ -5,6 +5,14 @@ import { mergeRewriteQuery } from "../utils/query.js";
|
|
|
5
5
|
import { normalizeDefaultLocalePathname, stripI18nLocaleForApiRoute } from "./pages-i18n.js";
|
|
6
6
|
import { mergeHeaders } from "./worker-utils.js";
|
|
7
7
|
//#region src/server/pages-request-pipeline.ts
|
|
8
|
+
async function fetchWorkerFilesystemRoute(request, requestPathname, phase, fetchAsset) {
|
|
9
|
+
if (phase === "direct" || request.method !== "GET" && request.method !== "HEAD" || requestPathname === "/api" || requestPathname.startsWith("/api/")) return false;
|
|
10
|
+
const assetUrl = new URL(request.url);
|
|
11
|
+
assetUrl.pathname = requestPathname;
|
|
12
|
+
assetUrl.search = "";
|
|
13
|
+
const response = await fetchAsset(new Request(assetUrl, request));
|
|
14
|
+
return response.status === 404 ? false : response;
|
|
15
|
+
}
|
|
8
16
|
/**
|
|
9
17
|
* Wrap an adapter's `runMiddleware` callback so middleware receives the original
|
|
10
18
|
* (pre-basePath-stripping) URL. Adapters strip the basePath before handing the
|
|
@@ -72,6 +80,15 @@ async function runPagesRequest(request, deps) {
|
|
|
72
80
|
let resolvedUrl = originalResolvedUrl;
|
|
73
81
|
const middlewareHeaders = {};
|
|
74
82
|
let middlewareStatus;
|
|
83
|
+
const serveFilesystemRoute = async (requestPathname, phase) => {
|
|
84
|
+
if (!deps.serveFilesystemRoute) return null;
|
|
85
|
+
const served = await deps.serveFilesystemRoute(requestPathname, middlewareHeaders, phase);
|
|
86
|
+
if (served instanceof Response) return {
|
|
87
|
+
type: "response",
|
|
88
|
+
response: mergeHeaders(served, middlewareHeaders, middlewareStatus)
|
|
89
|
+
};
|
|
90
|
+
return served ? { type: "handled" } : null;
|
|
91
|
+
};
|
|
75
92
|
if (typeof deps.runMiddleware === "function") {
|
|
76
93
|
const result = await deps.runMiddleware(request, deps.ctx ?? null, { isDataRequest });
|
|
77
94
|
if (result.waitUntilPromises && result.waitUntilPromises.length > 0) {
|
|
@@ -132,9 +149,8 @@ async function runPagesRequest(request, deps) {
|
|
|
132
149
|
type: "response",
|
|
133
150
|
response: mergeHeaders(await proxyExternal(request, resolvedUrl), middlewareHeaders, void 0)
|
|
134
151
|
};
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
}
|
|
152
|
+
const directFilesystemResult = await serveFilesystemRoute(pathname, "direct");
|
|
153
|
+
if (directFilesystemResult) return directFilesystemResult;
|
|
138
154
|
let configRewriteFired = false;
|
|
139
155
|
for (const rewrite of configRewrites.beforeFiles ?? []) {
|
|
140
156
|
const rewritten = matchRewrite(matchResolvedPathname(resolvedPathname), [rewrite], rewriteRequestContext(), basePathState);
|
|
@@ -148,6 +164,10 @@ async function runPagesRequest(request, deps) {
|
|
|
148
164
|
configRewriteFired = true;
|
|
149
165
|
}
|
|
150
166
|
}
|
|
167
|
+
if (configRewriteFired) {
|
|
168
|
+
const beforeFilesResult = await serveFilesystemRoute(resolvedPathname, "beforeFiles");
|
|
169
|
+
if (beforeFilesResult) return beforeFilesResult;
|
|
170
|
+
}
|
|
151
171
|
if (basePath && !hadBasePath && !configRewriteFired) return {
|
|
152
172
|
type: "response",
|
|
153
173
|
response: new Response("This page could not be found", {
|
|
@@ -155,27 +175,33 @@ async function runPagesRequest(request, deps) {
|
|
|
155
175
|
headers: { "Content-Type": "text/html; charset=utf-8" }
|
|
156
176
|
})
|
|
157
177
|
};
|
|
158
|
-
const
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
if (
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
178
|
+
const handleResolvedApiRoute = async () => {
|
|
179
|
+
const apiLookupUrl = stripI18nLocaleForApiRoute(resolvedUrl, i18nConfig);
|
|
180
|
+
const apiLookupPathname = apiLookupUrl.split("?")[0];
|
|
181
|
+
if (!apiLookupPathname.startsWith("/api/") && apiLookupPathname !== "/api") return null;
|
|
182
|
+
if (typeof deps.handleApi === "function") {
|
|
183
|
+
let apiRequest = request;
|
|
184
|
+
if (basePath && hadBasePath) {
|
|
185
|
+
const apiRequestUrl = new URL(request.url);
|
|
186
|
+
apiRequestUrl.pathname = addBasePathToPathname(apiRequestUrl.pathname, basePath);
|
|
187
|
+
apiRequest = cloneRequestWithUrl(request, apiRequestUrl.toString());
|
|
188
|
+
}
|
|
189
|
+
return {
|
|
190
|
+
type: "response",
|
|
191
|
+
defaultContentType: "application/octet-stream",
|
|
192
|
+
response: mergeHeaders(await deps.handleApi(apiRequest, apiLookupUrl, deps.ctx ?? null), middlewareHeaders, middlewareStatus)
|
|
193
|
+
};
|
|
166
194
|
}
|
|
167
195
|
return {
|
|
168
|
-
type: "
|
|
169
|
-
|
|
170
|
-
|
|
196
|
+
type: "api",
|
|
197
|
+
apiUrl: apiLookupUrl,
|
|
198
|
+
stagedHeaders: middlewareHeaders,
|
|
199
|
+
requestHeaders: request.headers,
|
|
200
|
+
middlewareStatus
|
|
171
201
|
};
|
|
172
|
-
} else return {
|
|
173
|
-
type: "api",
|
|
174
|
-
apiUrl: apiLookupUrl,
|
|
175
|
-
stagedHeaders: middlewareHeaders,
|
|
176
|
-
requestHeaders: request.headers,
|
|
177
|
-
middlewareStatus
|
|
178
202
|
};
|
|
203
|
+
const apiResult = await handleResolvedApiRoute();
|
|
204
|
+
if (apiResult) return apiResult;
|
|
179
205
|
let pageMatch = deps.matchPageRoute ? deps.matchPageRoute(resolvedPathname, request) : null;
|
|
180
206
|
let resolvedPathnameChanged = false;
|
|
181
207
|
if (!pageMatch || pageMatch.route.isDynamic) for (const rewrite of configRewrites.afterFiles ?? []) {
|
|
@@ -188,6 +214,10 @@ async function runPagesRequest(request, deps) {
|
|
|
188
214
|
resolvedUrl = mergeRewriteQuery(resolvedUrl, rewritten);
|
|
189
215
|
resolvedPathname = pathnameForResolvedUrl(resolvedUrl);
|
|
190
216
|
resolvedPathnameChanged = true;
|
|
217
|
+
const afterFilesFilesystemResult = await serveFilesystemRoute(resolvedPathname, "afterFiles");
|
|
218
|
+
if (afterFilesFilesystemResult) return afterFilesFilesystemResult;
|
|
219
|
+
const afterFilesApiResult = await handleResolvedApiRoute();
|
|
220
|
+
if (afterFilesApiResult) return afterFilesApiResult;
|
|
191
221
|
pageMatch = deps.matchPageRoute ? deps.matchPageRoute(resolvedPathname, request) : null;
|
|
192
222
|
if (pageMatch) break;
|
|
193
223
|
}
|
|
@@ -208,6 +238,10 @@ async function runPagesRequest(request, deps) {
|
|
|
208
238
|
};
|
|
209
239
|
resolvedUrl = mergeRewriteQuery(resolvedUrl, fallbackRewrite);
|
|
210
240
|
resolvedPathname = pathnameForResolvedUrl(resolvedUrl);
|
|
241
|
+
const fallbackFilesystemResult = await serveFilesystemRoute(resolvedPathname, "fallback");
|
|
242
|
+
if (fallbackFilesystemResult) return fallbackFilesystemResult;
|
|
243
|
+
const fallbackApiResult = await handleResolvedApiRoute();
|
|
244
|
+
if (fallbackApiResult) return fallbackApiResult;
|
|
211
245
|
renderPageMatch = deps.matchPageRoute ? deps.matchPageRoute(resolvedPathname, request) : null;
|
|
212
246
|
refreshDataRewriteHeader();
|
|
213
247
|
if (renderPageMatch) break;
|
|
@@ -228,6 +262,10 @@ async function runPagesRequest(request, deps) {
|
|
|
228
262
|
};
|
|
229
263
|
resolvedUrl = mergeRewriteQuery(resolvedUrl, fallbackRewrite);
|
|
230
264
|
resolvedPathname = pathnameForResolvedUrl(resolvedUrl);
|
|
265
|
+
const fallbackFilesystemResult = await serveFilesystemRoute(resolvedPathname, "fallback");
|
|
266
|
+
if (fallbackFilesystemResult) return fallbackFilesystemResult;
|
|
267
|
+
const fallbackApiResult = await handleResolvedApiRoute();
|
|
268
|
+
if (fallbackApiResult) return fallbackApiResult;
|
|
231
269
|
response = await deps.renderPage(request, resolvedUrl, void 0, stagedHeaders);
|
|
232
270
|
matchedFallbackRewrite = true;
|
|
233
271
|
if (response.status !== 404) break;
|
|
@@ -250,6 +288,10 @@ async function runPagesRequest(request, deps) {
|
|
|
250
288
|
};
|
|
251
289
|
resolvedUrl = mergeRewriteQuery(resolvedUrl, fallbackRewrite);
|
|
252
290
|
resolvedPathname = pathnameForResolvedUrl(resolvedUrl);
|
|
291
|
+
const fallbackFilesystemResult = await serveFilesystemRoute(resolvedPathname, "fallback");
|
|
292
|
+
if (fallbackFilesystemResult) return fallbackFilesystemResult;
|
|
293
|
+
const fallbackApiResult = await handleResolvedApiRoute();
|
|
294
|
+
if (fallbackApiResult) return fallbackApiResult;
|
|
253
295
|
if (deps.matchPageRoute?.(resolvedPathname, request)) break;
|
|
254
296
|
}
|
|
255
297
|
refreshDataRewriteHeader();
|
|
@@ -264,4 +306,4 @@ async function runPagesRequest(request, deps) {
|
|
|
264
306
|
};
|
|
265
307
|
}
|
|
266
308
|
//#endregion
|
|
267
|
-
export { runPagesRequest, wrapMiddlewareWithBasePath };
|
|
309
|
+
export { fetchWorkerFilesystemRoute, runPagesRequest, wrapMiddlewareWithBasePath };
|
|
@@ -41,6 +41,8 @@ import { IncomingMessage, ServerResponse } from "node:http";
|
|
|
41
41
|
*/
|
|
42
42
|
declare function resolveServerEntryImportUrl(entryPath: string): string;
|
|
43
43
|
declare function importServerEntryModule(entryPath: string): Promise<any>;
|
|
44
|
+
/** Convert a Node.js IncomingMessage into a ReadableStream for Web Request body. */
|
|
45
|
+
declare function readNodeStream(req: IncomingMessage): ReadableStream<Uint8Array>;
|
|
44
46
|
type ProdServerOptions = {
|
|
45
47
|
/** Port to listen on */port?: number; /** Host to bind to */
|
|
46
48
|
host?: string; /** Path to the build output directory */
|
|
@@ -143,4 +145,4 @@ declare function resolveAppRouterPrerenderSeeder(entryModule: unknown): AppRoute
|
|
|
143
145
|
*/
|
|
144
146
|
declare function resolveAppRouterAssetPath(pathname: string, assetPathPrefix: string, assetPrefix: string): string | null;
|
|
145
147
|
//#endregion
|
|
146
|
-
export { COMPRESSIBLE_TYPES, COMPRESS_THRESHOLD, ProdServerOptions, importServerEntryModule, mergeResponseHeaders, mergeWebResponse, negotiateEncoding, nodeToWebRequest, resolveAppRouterAssetPath, resolveAppRouterPrerenderSeeder, resolveRequestHost as resolveHost, resolveServerEntryImportUrl, sendCompressed, sendWebResponse, startProdServer, trustProxy, trustedHosts, tryServeStatic };
|
|
148
|
+
export { COMPRESSIBLE_TYPES, COMPRESS_THRESHOLD, ProdServerOptions, importServerEntryModule, mergeResponseHeaders, mergeWebResponse, negotiateEncoding, nodeToWebRequest, readNodeStream, resolveAppRouterAssetPath, resolveAppRouterPrerenderSeeder, resolveRequestHost as resolveHost, resolveServerEntryImportUrl, sendCompressed, sendWebResponse, startProdServer, trustProxy, trustedHosts, tryServeStatic };
|
|
@@ -3,7 +3,8 @@ import { hasBasePath, stripBasePath } from "../utils/base-path.js";
|
|
|
3
3
|
import { VINEXT_PRERENDER_ROUTE_PARAMS_HEADER, VINEXT_PRERENDER_SECRET_HEADER, VINEXT_STATIC_FILE_HEADER } from "./headers.js";
|
|
4
4
|
import { normalizePath } from "./normalize-path.js";
|
|
5
5
|
import { notFoundResponse } from "./http-error-responses.js";
|
|
6
|
-
import {
|
|
6
|
+
import { isOpenRedirectShaped } from "./open-redirect.js";
|
|
7
|
+
import { filterInternalHeaders } from "./request-pipeline.js";
|
|
7
8
|
import { isUnknownRecord } from "../utils/record.js";
|
|
8
9
|
import { resolveRequestHost, resolveRequestProtocol, trustProxy, trustedHosts } from "./proxy-trust.js";
|
|
9
10
|
import { DEFAULT_DEVICE_SIZES, DEFAULT_IMAGE_SIZES, isImageOptimizationPath, isSafeImageContentType, parseImageParams } from "./image-optimization.js";
|
|
@@ -109,11 +110,42 @@ async function importServerEntryModule(entryPath) {
|
|
|
109
110
|
}
|
|
110
111
|
/** Convert a Node.js IncomingMessage into a ReadableStream for Web Request body. */
|
|
111
112
|
function readNodeStream(req) {
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
113
|
+
let cancelled = false;
|
|
114
|
+
let cleanup = () => {};
|
|
115
|
+
return new ReadableStream({
|
|
116
|
+
start(controller) {
|
|
117
|
+
cleanup = () => {
|
|
118
|
+
req.off("data", onData);
|
|
119
|
+
req.off("end", onEnd);
|
|
120
|
+
req.off("error", onError);
|
|
121
|
+
};
|
|
122
|
+
const onData = (chunk) => {
|
|
123
|
+
if (cancelled) return;
|
|
124
|
+
controller.enqueue(new Uint8Array(chunk));
|
|
125
|
+
if ((controller.desiredSize ?? 0) <= 0) req.pause();
|
|
126
|
+
};
|
|
127
|
+
const onEnd = () => {
|
|
128
|
+
cleanup();
|
|
129
|
+
if (!cancelled) controller.close();
|
|
130
|
+
};
|
|
131
|
+
const onError = (error) => {
|
|
132
|
+
cleanup();
|
|
133
|
+
if (!cancelled) controller.error(error);
|
|
134
|
+
};
|
|
135
|
+
req.on("data", onData);
|
|
136
|
+
req.on("end", onEnd);
|
|
137
|
+
req.on("error", onError);
|
|
138
|
+
req.pause();
|
|
139
|
+
},
|
|
140
|
+
pull() {
|
|
141
|
+
if (!cancelled) req.resume();
|
|
142
|
+
},
|
|
143
|
+
cancel() {
|
|
144
|
+
cancelled = true;
|
|
145
|
+
cleanup();
|
|
146
|
+
req.resume();
|
|
147
|
+
}
|
|
148
|
+
});
|
|
117
149
|
}
|
|
118
150
|
/** Content types that benefit from compression. */
|
|
119
151
|
const COMPRESSIBLE_TYPES = new Set([
|
|
@@ -514,7 +546,7 @@ function nodeToWebRequest(req, urlOverride, prerenderSecret) {
|
|
|
514
546
|
headers
|
|
515
547
|
};
|
|
516
548
|
if (hasBody) {
|
|
517
|
-
init.body =
|
|
549
|
+
init.body = readNodeStream(req);
|
|
518
550
|
init.duplex = "half";
|
|
519
551
|
}
|
|
520
552
|
return new Request(url, init);
|
|
@@ -1008,7 +1040,7 @@ async function startPagesRouterServer(options) {
|
|
|
1008
1040
|
const protocol = resolveRequestProtocol(req);
|
|
1009
1041
|
const hostHeader = resolveRequestHost(req, `${host}:${port}`);
|
|
1010
1042
|
const rawReqHeaders = nodeHeadersToWebHeaders(req.headers);
|
|
1011
|
-
const isDataRequest =
|
|
1043
|
+
const isDataRequest = isDataReq;
|
|
1012
1044
|
const reqHeaders = filterInternalHeaders(rawReqHeaders);
|
|
1013
1045
|
const method = req.method ?? "GET";
|
|
1014
1046
|
const hasBody = method !== "GET" && method !== "HEAD";
|
|
@@ -1036,8 +1068,8 @@ async function startPagesRouterServer(options) {
|
|
|
1036
1068
|
originalUrl: originalRenderUrl
|
|
1037
1069
|
}) : null,
|
|
1038
1070
|
handleApi: typeof handleApi === "function" ? (request, apiUrl) => handleApi(request, apiUrl, createNodeExecutionContext()) : null,
|
|
1039
|
-
|
|
1040
|
-
if (requestPathname === "/" || requestPathname.startsWith("/api/") || requestPathname.startsWith(`/_next/static/`)) return false;
|
|
1071
|
+
serveFilesystemRoute: async (requestPathname, stagedHeaders, phase) => {
|
|
1072
|
+
if (req.method !== "GET" && req.method !== "HEAD" || requestPathname === "/" || requestPathname === "/api" || requestPathname.startsWith("/api/") || phase === "direct" && requestPathname.startsWith(`/_next/static/`)) return false;
|
|
1041
1073
|
return tryServeStatic(req, res, clientDir, requestPathname, compress, staticCache, stagedHeaders);
|
|
1042
1074
|
}
|
|
1043
1075
|
});
|
|
@@ -1088,4 +1120,4 @@ async function startPagesRouterServer(options) {
|
|
|
1088
1120
|
};
|
|
1089
1121
|
}
|
|
1090
1122
|
//#endregion
|
|
1091
|
-
export { COMPRESSIBLE_TYPES, COMPRESS_THRESHOLD, importServerEntryModule, mergeResponseHeaders, mergeWebResponse, negotiateEncoding, nodeToWebRequest, resolveAppRouterAssetPath, resolveAppRouterPrerenderSeeder, resolveRequestHost as resolveHost, resolveServerEntryImportUrl, sendCompressed, sendWebResponse, startProdServer, trustProxy, trustedHosts, tryServeStatic };
|
|
1123
|
+
export { COMPRESSIBLE_TYPES, COMPRESS_THRESHOLD, importServerEntryModule, mergeResponseHeaders, mergeWebResponse, negotiateEncoding, nodeToWebRequest, readNodeStream, resolveAppRouterAssetPath, resolveAppRouterPrerenderSeeder, resolveRequestHost as resolveHost, resolveServerEntryImportUrl, sendCompressed, sendWebResponse, startProdServer, trustProxy, trustedHosts, tryServeStatic };
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { NextHeader } from "../config/next-config.js";
|
|
2
2
|
import { BasePathMatchState, RequestContext } from "../config/config-matchers.js";
|
|
3
3
|
import { INTERNAL_HEADERS, VINEXT_INTERNAL_HEADERS } from "./headers.js";
|
|
4
|
+
import { isOpenRedirectShaped } from "./open-redirect.js";
|
|
4
5
|
import { hasBasePath, stripBasePath } from "../utils/base-path.js";
|
|
5
6
|
|
|
6
7
|
//#region src/server/request-pipeline.d.ts
|
|
@@ -42,30 +43,6 @@ import { hasBasePath, stripBasePath } from "../utils/base-path.js";
|
|
|
42
43
|
* @returns A 404 Response if the path is protocol-relative, or null to continue
|
|
43
44
|
*/
|
|
44
45
|
declare function guardProtocolRelativeUrl(rawPathname: string): Response | null;
|
|
45
|
-
/**
|
|
46
|
-
* Returns true if a request pathname looks like a protocol-relative open
|
|
47
|
-
* redirect, in either literal or percent-encoded form.
|
|
48
|
-
*
|
|
49
|
-
* Exported for call sites that need to replicate the guard inline (Pages
|
|
50
|
-
* Router worker codegen, Node production server) and for defense-in-depth
|
|
51
|
-
* checks inside redirect emitters.
|
|
52
|
-
*
|
|
53
|
-
* A pathname is considered "open redirect shaped" when its first segment,
|
|
54
|
-
* after decoding backslashes and encoded delimiters, would cause a browser
|
|
55
|
-
* to resolve a `Location` containing the pathname as protocol-relative:
|
|
56
|
-
*
|
|
57
|
-
* - literal `//evil.com`
|
|
58
|
-
* - literal `/\evil.com` (browsers normalize `\` to `/`)
|
|
59
|
-
* - encoded `/%5Cevil.com` (`%5C` decodes to `\` in Location)
|
|
60
|
-
* - encoded `/%2F/evil.com` (`%2F` decodes to `/` → `//`)
|
|
61
|
-
* - mixed `/%5C%2F`, `/%5C%5C` (and other combinations)
|
|
62
|
-
*
|
|
63
|
-
* We explicitly do not require a valid percent sequence elsewhere in the
|
|
64
|
-
* pathname — we only examine the leading bytes (up to the second real or
|
|
65
|
-
* encoded delimiter) so malformed suffixes can still reach the normal
|
|
66
|
-
* "400 Bad Request" decode path instead of being masked as "404".
|
|
67
|
-
*/
|
|
68
|
-
declare function isOpenRedirectShaped(rawPathname: string): boolean;
|
|
69
46
|
type HeaderRecord = Record<string, string | string[]>;
|
|
70
47
|
type ApplyConfigHeadersOptions = {
|
|
71
48
|
configHeaders: NextHeader[];
|
|
@@ -2,6 +2,7 @@ import { hasBasePath, removeTrailingSlash, stripBasePath } from "../utils/base-p
|
|
|
2
2
|
import { INTERNAL_HEADERS, VINEXT_INTERNAL_HEADERS, VINEXT_STATIC_FILE_HEADER } from "./headers.js";
|
|
3
3
|
import { matchHeaders } from "../config/config-matchers.js";
|
|
4
4
|
import { forbiddenResponse, notFoundResponse } from "./http-error-responses.js";
|
|
5
|
+
import { isOpenRedirectShaped } from "./open-redirect.js";
|
|
5
6
|
//#region src/server/request-pipeline.ts
|
|
6
7
|
/**
|
|
7
8
|
* Shared request pipeline utilities.
|
|
@@ -44,39 +45,6 @@ function guardProtocolRelativeUrl(rawPathname) {
|
|
|
44
45
|
if (isOpenRedirectShaped(rawPathname)) return notFoundResponse();
|
|
45
46
|
return null;
|
|
46
47
|
}
|
|
47
|
-
/**
|
|
48
|
-
* Returns true if a request pathname looks like a protocol-relative open
|
|
49
|
-
* redirect, in either literal or percent-encoded form.
|
|
50
|
-
*
|
|
51
|
-
* Exported for call sites that need to replicate the guard inline (Pages
|
|
52
|
-
* Router worker codegen, Node production server) and for defense-in-depth
|
|
53
|
-
* checks inside redirect emitters.
|
|
54
|
-
*
|
|
55
|
-
* A pathname is considered "open redirect shaped" when its first segment,
|
|
56
|
-
* after decoding backslashes and encoded delimiters, would cause a browser
|
|
57
|
-
* to resolve a `Location` containing the pathname as protocol-relative:
|
|
58
|
-
*
|
|
59
|
-
* - literal `//evil.com`
|
|
60
|
-
* - literal `/\evil.com` (browsers normalize `\` to `/`)
|
|
61
|
-
* - encoded `/%5Cevil.com` (`%5C` decodes to `\` in Location)
|
|
62
|
-
* - encoded `/%2F/evil.com` (`%2F` decodes to `/` → `//`)
|
|
63
|
-
* - mixed `/%5C%2F`, `/%5C%5C` (and other combinations)
|
|
64
|
-
*
|
|
65
|
-
* We explicitly do not require a valid percent sequence elsewhere in the
|
|
66
|
-
* pathname — we only examine the leading bytes (up to the second real or
|
|
67
|
-
* encoded delimiter) so malformed suffixes can still reach the normal
|
|
68
|
-
* "400 Bad Request" decode path instead of being masked as "404".
|
|
69
|
-
*/
|
|
70
|
-
function isOpenRedirectShaped(rawPathname) {
|
|
71
|
-
if (!rawPathname.startsWith("/")) return false;
|
|
72
|
-
const afterSlash = rawPathname.slice(1);
|
|
73
|
-
if (afterSlash.startsWith("/") || afterSlash.startsWith("\\")) return true;
|
|
74
|
-
if (afterSlash.length >= 3 && afterSlash[0] === "%") {
|
|
75
|
-
const encoded = afterSlash.slice(0, 3).toLowerCase();
|
|
76
|
-
if (encoded === "%5c" || encoded === "%2f") return true;
|
|
77
|
-
}
|
|
78
|
-
return false;
|
|
79
|
-
}
|
|
80
48
|
const FILE_LIKE_PATHNAME_RE = /\.[^/]+\/?$/;
|
|
81
49
|
function isWellKnownPathname(pathname) {
|
|
82
50
|
return pathname === "/.well-known" || pathname.startsWith("/.well-known/");
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { normalizePathSeparators } from "../utils/path.js";
|
|
2
|
-
import "../utils/asset-prefix.js";
|
|
2
|
+
import { ASSET_PREFIX_URL_DIR } from "../utils/asset-prefix.js";
|
|
3
3
|
import path from "node:path";
|
|
4
4
|
import fs from "node:fs/promises";
|
|
5
5
|
//#region src/server/static-file-cache.ts
|
|
@@ -23,6 +23,7 @@ const CONTENT_TYPES = {
|
|
|
23
23
|
".css": "text/css",
|
|
24
24
|
".html": "text/html",
|
|
25
25
|
".json": "application/json",
|
|
26
|
+
".txt": "text/plain; charset=utf-8",
|
|
26
27
|
".png": "image/png",
|
|
27
28
|
".jpg": "image/jpeg",
|
|
28
29
|
".jpeg": "image/jpeg",
|
|
@@ -165,9 +166,20 @@ var StaticFileCache = class StaticFileCache {
|
|
|
165
166
|
function etagFromFilenameHash(relativePath, ext) {
|
|
166
167
|
const basename = path.basename(relativePath, ext);
|
|
167
168
|
const lastDash = basename.lastIndexOf("-");
|
|
168
|
-
if (lastDash
|
|
169
|
-
|
|
170
|
-
|
|
169
|
+
if (lastDash !== -1 && lastDash !== basename.length - 1) {
|
|
170
|
+
const suffix = basename.slice(lastDash + 1);
|
|
171
|
+
if (suffix.length >= 6 && suffix.length <= 12 && /^[A-Za-z0-9_-]+$/.test(suffix)) return `W/"${suffix}"`;
|
|
172
|
+
}
|
|
173
|
+
const normalizedPath = normalizePathSeparators(relativePath);
|
|
174
|
+
const managedMediaSegment = `${ASSET_PREFIX_URL_DIR}/media/`;
|
|
175
|
+
if (normalizedPath.startsWith(managedMediaSegment) || normalizedPath.includes(`/${managedMediaSegment}`)) {
|
|
176
|
+
const lastDot = basename.lastIndexOf(".");
|
|
177
|
+
if (lastDot !== -1) {
|
|
178
|
+
const suffix = basename.slice(lastDot + 1);
|
|
179
|
+
if (/^[0-9a-f]{8}$/.test(suffix)) return `W/"${suffix}"`;
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
return null;
|
|
171
183
|
}
|
|
172
184
|
function buildVariant(info, baseHeaders, encoding) {
|
|
173
185
|
return {
|
|
@@ -2,17 +2,28 @@ import React from "react";
|
|
|
2
2
|
|
|
3
3
|
//#region src/shims/before-interactive-context.d.ts
|
|
4
4
|
/**
|
|
5
|
-
*
|
|
5
|
+
* A `<Script strategy="beforeInteractive">` captured during SSR.
|
|
6
6
|
*
|
|
7
7
|
* The Script shim hands these records to the SSR pipeline through
|
|
8
8
|
* `BeforeInteractiveContext` instead of rendering the `<script>` tag inline.
|
|
9
9
|
* The pipeline then emits the captured tag immediately after `<head>` opens,
|
|
10
10
|
* so the script runs before any React-hoisted stylesheets or modulepreload
|
|
11
11
|
* links. Matches the standard no-flash dark-mode pattern.
|
|
12
|
+
*
|
|
13
|
+
* Both inline (`children`/`dangerouslySetInnerHTML`) and external (`src`)
|
|
14
|
+
* beforeInteractive scripts flow through here, mirroring Next.js which registers
|
|
15
|
+
* inline and src beforeInteractive scripts equally in the App Router runtime
|
|
16
|
+
* (`(self.__next_s=...).push(...)`). An entry has `src` set for external
|
|
17
|
+
* scripts and `innerHTML` set for inline scripts (never both).
|
|
12
18
|
*/
|
|
13
19
|
type BeforeInteractiveInlineScript = {
|
|
14
|
-
/** Optional id attribute. */id?: string; /**
|
|
15
|
-
|
|
20
|
+
/** Optional id attribute. */id?: string; /** External script URL. Set for src beforeInteractive scripts. */
|
|
21
|
+
src?: string;
|
|
22
|
+
/**
|
|
23
|
+
* Pre-escaped inline content (already passed through `escapeInlineContent`).
|
|
24
|
+
* Set for inline beforeInteractive scripts; omitted for src scripts.
|
|
25
|
+
*/
|
|
26
|
+
innerHTML?: string; /** Nonce to emit on the `<script>` tag, when CSP is enabled. */
|
|
16
27
|
nonce?: string;
|
|
17
28
|
/**
|
|
18
29
|
* Additional HTML attributes to emit on the tag. Booleans render as the
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import { RenderObservation } from "../server/cache-proof.js";
|
|
2
|
+
|
|
3
|
+
//#region src/shims/cache-handler.d.ts
|
|
4
|
+
type CacheHandlerValue = {
|
|
5
|
+
lastModified: number;
|
|
6
|
+
age?: number;
|
|
7
|
+
cacheState?: string;
|
|
8
|
+
cacheControl?: CacheControlMetadata;
|
|
9
|
+
value: IncrementalCacheValue | null;
|
|
10
|
+
};
|
|
11
|
+
type CacheControlMetadata = {
|
|
12
|
+
revalidate: number;
|
|
13
|
+
expire?: number;
|
|
14
|
+
};
|
|
15
|
+
type IncrementalCacheValue = CachedFetchValue | CachedAppPageValue | CachedPagesValue | CachedRouteValue | CachedRedirectValue | CachedImageValue;
|
|
16
|
+
type CachedFetchValue = {
|
|
17
|
+
kind: "FETCH";
|
|
18
|
+
data: {
|
|
19
|
+
headers: Record<string, string>;
|
|
20
|
+
body: string;
|
|
21
|
+
url: string;
|
|
22
|
+
status?: number;
|
|
23
|
+
};
|
|
24
|
+
tags?: string[];
|
|
25
|
+
revalidate: number | false;
|
|
26
|
+
};
|
|
27
|
+
type CachedAppPageValue = {
|
|
28
|
+
kind: "APP_PAGE";
|
|
29
|
+
html: string;
|
|
30
|
+
rscData: ArrayBuffer | undefined;
|
|
31
|
+
headers: Record<string, string | string[]> | undefined;
|
|
32
|
+
postponed: string | undefined;
|
|
33
|
+
renderObservation?: RenderObservation;
|
|
34
|
+
status: number | undefined;
|
|
35
|
+
};
|
|
36
|
+
type CachedPagesValue = {
|
|
37
|
+
kind: "PAGES";
|
|
38
|
+
html: string;
|
|
39
|
+
pageData: object;
|
|
40
|
+
generatedFromDataRequest?: boolean;
|
|
41
|
+
headers: Record<string, string | string[]> | undefined;
|
|
42
|
+
status: number | undefined;
|
|
43
|
+
};
|
|
44
|
+
type CachedRouteValue = {
|
|
45
|
+
kind: "APP_ROUTE";
|
|
46
|
+
body: ArrayBuffer;
|
|
47
|
+
status: number;
|
|
48
|
+
headers: Record<string, string | string[]>;
|
|
49
|
+
};
|
|
50
|
+
type CachedRedirectValue = {
|
|
51
|
+
kind: "REDIRECT";
|
|
52
|
+
props: object;
|
|
53
|
+
};
|
|
54
|
+
type CachedImageValue = {
|
|
55
|
+
kind: "IMAGE";
|
|
56
|
+
etag: string;
|
|
57
|
+
buffer: ArrayBuffer;
|
|
58
|
+
extension: string;
|
|
59
|
+
revalidate?: number;
|
|
60
|
+
};
|
|
61
|
+
type CacheHandlerContext = {
|
|
62
|
+
dev?: boolean;
|
|
63
|
+
maxMemoryCacheSize?: number;
|
|
64
|
+
revalidatedTags?: string[];
|
|
65
|
+
[key: string]: unknown;
|
|
66
|
+
};
|
|
67
|
+
type CacheHandler = {
|
|
68
|
+
get(key: string, ctx?: Record<string, unknown>): Promise<CacheHandlerValue | null>;
|
|
69
|
+
set(key: string, data: IncrementalCacheValue | null, ctx?: Record<string, unknown>): Promise<void>;
|
|
70
|
+
revalidateTag(tags: string | string[], durations?: {
|
|
71
|
+
expire?: number;
|
|
72
|
+
}): Promise<void>;
|
|
73
|
+
resetRequestCache?(): void;
|
|
74
|
+
};
|
|
75
|
+
declare class NoOpCacheHandler implements CacheHandler {
|
|
76
|
+
get(_key: string, _ctx?: Record<string, unknown>): Promise<CacheHandlerValue | null>;
|
|
77
|
+
set(_key: string, _data: IncrementalCacheValue | null, _ctx?: Record<string, unknown>): Promise<void>;
|
|
78
|
+
revalidateTag(_tags: string | string[], _durations?: {
|
|
79
|
+
expire?: number;
|
|
80
|
+
}): Promise<void>;
|
|
81
|
+
}
|
|
82
|
+
type MemoryCacheHandlerOptions = Pick<CacheHandlerContext, "maxMemoryCacheSize"> & {
|
|
83
|
+
cacheMaxMemorySize?: number;
|
|
84
|
+
};
|
|
85
|
+
declare class MemoryCacheHandler implements CacheHandler {
|
|
86
|
+
private store;
|
|
87
|
+
private tagRevalidatedAt;
|
|
88
|
+
private readonly maxMemoryCacheSize;
|
|
89
|
+
private currentMemoryCacheSize;
|
|
90
|
+
constructor(options?: number | MemoryCacheHandlerOptions);
|
|
91
|
+
private estimateEntrySize;
|
|
92
|
+
private deleteEntry;
|
|
93
|
+
private touchEntry;
|
|
94
|
+
private evictLeastRecentlyUsed;
|
|
95
|
+
get(key: string, ctx?: Record<string, unknown>): Promise<CacheHandlerValue | null>;
|
|
96
|
+
set(key: string, data: IncrementalCacheValue | null, ctx?: Record<string, unknown>): Promise<void>;
|
|
97
|
+
revalidateTag(tags: string | string[]): Promise<void>;
|
|
98
|
+
resetRequestCache(): void;
|
|
99
|
+
}
|
|
100
|
+
declare function configureMemoryCacheHandler(options?: MemoryCacheHandlerOptions): void;
|
|
101
|
+
declare function setDataCacheHandler(handler: CacheHandler): void;
|
|
102
|
+
declare function getDataCacheHandler(): CacheHandler;
|
|
103
|
+
declare function setCacheHandler(handler: CacheHandler): void;
|
|
104
|
+
declare function getCacheHandler(): CacheHandler;
|
|
105
|
+
//#endregion
|
|
106
|
+
export { CacheControlMetadata, CacheHandler, CacheHandlerContext, CacheHandlerValue, CachedAppPageValue, CachedFetchValue, CachedImageValue, CachedPagesValue, CachedRedirectValue, CachedRouteValue, IncrementalCacheValue, MemoryCacheHandler, NoOpCacheHandler, configureMemoryCacheHandler, getCacheHandler, getDataCacheHandler, setCacheHandler, setDataCacheHandler };
|