vinext 1.4.1 → 1.4.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 +5 -2
- package/dist/build/action-owner-manifest.js +1 -0
- package/dist/build/prerender-paths.d.ts +11 -1
- package/dist/build/prerender-paths.js +36 -10
- package/dist/check.js +1 -1
- package/dist/cli.js +1 -1
- package/dist/client/dev-error-overlay.d.ts +2 -1
- package/dist/client/dev-error-overlay.js +8 -3
- package/dist/config/config-matchers.js +8 -4
- package/dist/config/destination-params.js +5 -2
- package/dist/config/next-config.d.ts +25 -1
- package/dist/config/next-config.js +31 -1
- package/dist/entries/app-rsc-entry.js +65 -30
- package/dist/entries/app-rsc-manifest.js +4 -0
- package/dist/entries/pages-server-entry.js +19 -4
- package/dist/index.js +37 -37
- package/dist/init-cloudflare.js +18 -2
- package/dist/init-platform.js +2 -2
- package/dist/init.js +42 -5
- package/dist/plugins/css-target.d.ts +18 -0
- package/dist/plugins/css-target.js +93 -0
- package/dist/routing/app-route-graph.d.ts +10 -0
- package/dist/routing/app-route-graph.js +15 -1
- package/dist/server/api-handler.d.ts +2 -0
- package/dist/server/api-handler.js +10 -8
- package/dist/server/app-action-forwarding.js +3 -1
- package/dist/server/app-browser-entry.js +37 -41
- package/dist/server/app-browser-error.d.ts +1 -1
- package/dist/server/app-browser-error.js +13 -4
- package/dist/server/app-browser-supplemental-refresh.d.ts +13 -0
- package/dist/server/app-browser-supplemental-refresh.js +36 -4
- package/dist/server/app-elements.d.ts +3 -0
- package/dist/server/app-elements.js +14 -1
- package/dist/server/app-fallback-renderer.js +2 -2
- package/dist/server/app-middleware.d.ts +2 -0
- package/dist/server/app-middleware.js +1 -0
- package/dist/server/app-optimistic-routing.d.ts +12 -1
- package/dist/server/app-optimistic-routing.js +38 -16
- package/dist/server/app-page-boundary-render.js +4 -2
- package/dist/server/app-page-cache-render.js +1 -1
- package/dist/server/app-page-dispatch.d.ts +11 -1
- package/dist/server/app-page-dispatch.js +78 -31
- package/dist/server/app-page-element-builder.d.ts +11 -20
- package/dist/server/app-page-element-builder.js +39 -50
- package/dist/server/app-page-intercept-segment-config.d.ts +56 -0
- package/dist/server/app-page-intercept-segment-config.js +90 -0
- package/dist/server/app-page-navigation-params.d.ts +23 -0
- package/dist/server/app-page-navigation-params.js +49 -0
- package/dist/server/app-page-render.d.ts +5 -2
- package/dist/server/app-page-render.js +17 -3
- package/dist/server/app-page-request.d.ts +24 -2
- package/dist/server/app-page-request.js +10 -10
- package/dist/server/app-page-route-wiring.d.ts +17 -5
- package/dist/server/app-page-route-wiring.js +19 -13
- package/dist/server/app-page-segment-plan.js +9 -1
- package/dist/server/app-page-segment-state.d.ts +9 -0
- package/dist/server/app-page-segment-state.js +54 -1
- package/dist/server/app-pages-bridge.d.ts +6 -0
- package/dist/server/app-pages-bridge.js +8 -3
- package/dist/server/app-request-stage-independent-entry.js +2 -3
- package/dist/server/app-response-stage-entry.js +1 -0
- package/dist/server/app-route-module-loader.d.ts +2 -0
- package/dist/server/app-route-module-loader.js +1 -0
- package/dist/server/app-router-entry.js +2 -3
- package/dist/server/app-rsc-cache-busting.d.ts +8 -0
- package/dist/server/app-rsc-cache-busting.js +13 -1
- package/dist/server/app-rsc-combined-handler.js +2 -1
- package/dist/server/app-rsc-handler.d.ts +14 -1
- package/dist/server/app-rsc-handler.js +111 -62
- package/dist/server/app-rsc-response-finalizer.d.ts +18 -3
- package/dist/server/app-rsc-response-finalizer.js +61 -7
- package/dist/server/app-rsc-route-matching.d.ts +14 -0
- package/dist/server/app-rsc-route-matching.js +42 -18
- package/dist/server/app-server-action-execution.js +16 -10
- package/dist/server/app-ssr-entry.js +8 -0
- package/dist/server/cache-control.d.ts +2 -0
- package/dist/server/cache-control.js +3 -1
- package/dist/server/config-headers.d.ts +7 -0
- package/dist/server/config-headers.js +20 -1
- package/dist/server/image-animation.d.ts +9 -0
- package/dist/server/image-animation.js +118 -0
- package/dist/server/image-local-patterns.d.ts +21 -0
- package/dist/server/image-local-patterns.js +59 -0
- package/dist/server/image-optimization.d.ts +48 -27
- package/dist/server/image-optimization.js +316 -22
- package/dist/server/image-source-reader.d.ts +34 -0
- package/dist/server/image-source-reader.js +126 -0
- package/dist/server/metadata-route-response.d.ts +6 -1
- package/dist/server/metadata-route-response.js +46 -10
- package/dist/server/middleware-matcher.d.ts +1 -0
- package/dist/server/middleware-matcher.js +1 -1
- package/dist/server/middleware-runtime.d.ts +5 -0
- package/dist/server/middleware-runtime.js +48 -15
- package/dist/server/middleware.d.ts +1 -1
- package/dist/server/middleware.js +2 -1
- package/dist/server/nitro-public-files-plugin.js +20 -8
- package/dist/server/nitro-request-target-plugin.d.ts +17 -0
- package/dist/server/nitro-request-target-plugin.js +22 -0
- package/dist/server/open-redirect.d.ts +2 -1
- package/dist/server/open-redirect.js +2 -1
- package/dist/server/pages-client-assets.d.ts +1 -0
- package/dist/server/pages-data-route.d.ts +5 -1
- package/dist/server/pages-data-route.js +14 -6
- package/dist/server/pages-i18n.d.ts +7 -0
- package/dist/server/pages-i18n.js +14 -1
- package/dist/server/pages-node-compat.js +7 -3
- package/dist/server/pages-page-handler.js +4 -4
- package/dist/server/pages-request-pipeline.d.ts +9 -0
- package/dist/server/pages-request-pipeline.js +35 -15
- package/dist/server/pages-request-stage-entry.js +8 -2
- package/dist/server/pages-router-entry.js +3 -0
- package/dist/server/prod-server.d.ts +1 -1
- package/dist/server/prod-server.js +64 -62
- package/dist/server/request-pipeline.d.ts +5 -0
- package/dist/server/request-pipeline.js +11 -1
- package/dist/server/response-stage-cacheability.d.ts +7 -0
- package/dist/server/response-stage-cacheability.js +6 -5
- package/dist/server/srvx-middleware-origin.d.ts +13 -0
- package/dist/server/srvx-middleware-origin.js +19 -0
- package/dist/server/srvx-request-target.d.ts +12 -0
- package/dist/server/srvx-request-target.js +20 -0
- package/dist/server/static-file-cache.js +10 -22
- package/dist/server/worker-image-optimization.d.ts +10 -3
- package/dist/server/worker-image-optimization.js +43 -9
- package/dist/server/worker-revalidation-context.js +1 -0
- package/dist/server/worker-utils.d.ts +8 -0
- package/dist/server/worker-utils.js +19 -2
- package/dist/shims/error-boundary.d.ts +6 -2
- package/dist/shims/error-boundary.js +4 -2
- package/dist/shims/image-props.d.ts +6 -0
- package/dist/shims/image-props.js +39 -3
- package/dist/shims/image.js +3 -3
- package/dist/shims/request-context.d.ts +3 -1
- package/dist/shims/slot.d.ts +3 -0
- package/dist/shims/slot.js +67 -9
- package/dist/shims/url-utils.js +6 -1
- package/dist/utils/base-path.d.ts +7 -0
- package/dist/utils/base-path.js +11 -1
- package/dist/utils/client-entry-manifest.d.ts +5 -0
- package/dist/utils/client-entry-manifest.js +1 -0
- package/dist/utils/client-runtime-metadata.d.ts +5 -0
- package/dist/utils/client-runtime-metadata.js +33 -7
- package/package.json +1 -1
|
@@ -8,6 +8,19 @@ function readHeader(headers, name) {
|
|
|
8
8
|
return direct;
|
|
9
9
|
}
|
|
10
10
|
const normalizeHostname = normalizeDomainHostname;
|
|
11
|
+
const LOCALE_REDIRECT = Symbol.for("vinext.pages-locale-redirect");
|
|
12
|
+
/**
|
|
13
|
+
* Mark a locale-detection redirect. Next.js answers it before next.config
|
|
14
|
+
* headers are staged, so it goes out without them. The global symbol crosses
|
|
15
|
+
* the App Router / Pages Router module graph boundary.
|
|
16
|
+
*/
|
|
17
|
+
function markLocaleRedirect(response) {
|
|
18
|
+
if (!isLocaleRedirect(response)) Object.defineProperty(response, LOCALE_REDIRECT, { value: true });
|
|
19
|
+
return response;
|
|
20
|
+
}
|
|
21
|
+
function isLocaleRedirect(response) {
|
|
22
|
+
return LOCALE_REDIRECT in response;
|
|
23
|
+
}
|
|
11
24
|
/**
|
|
12
25
|
* Return the configured locale that the first pathname segment names, compared
|
|
13
26
|
* case-insensitively (`/EN/about` -> `"en"`), or `undefined` when there is none.
|
|
@@ -259,4 +272,4 @@ function resolvePagesI18nRequest(url, i18nConfig, headers, hostname, basePath =
|
|
|
259
272
|
};
|
|
260
273
|
}
|
|
261
274
|
//#endregion
|
|
262
|
-
export { detectDomainLocale, detectLocaleFromAcceptLanguage, detectPathnameLocale, extractLocaleFromUrl, getLocaleRedirect, normalizeDefaultLocalePathname, parseCookieLocaleFromHeader, resolvePagesI18nRequest, shouldRestoreLocaleRootTrailingSlash, stripI18nLocaleForApiRoute };
|
|
275
|
+
export { detectDomainLocale, detectLocaleFromAcceptLanguage, detectPathnameLocale, extractLocaleFromUrl, getLocaleRedirect, isLocaleRedirect, markLocaleRedirect, normalizeDefaultLocalePathname, parseCookieLocaleFromHeader, resolvePagesI18nRequest, shouldRestoreLocaleRootTrailingSlash, stripI18nLocaleForApiRoute };
|
|
@@ -201,10 +201,14 @@ var PagesResponseStream = class extends Writable {
|
|
|
201
201
|
return this;
|
|
202
202
|
}
|
|
203
203
|
json(data) {
|
|
204
|
-
this.resHeaders["content-type"] = "application/json";
|
|
204
|
+
this.resHeaders["content-type"] = "application/json; charset=utf-8";
|
|
205
205
|
this.end(JSON.stringify(data));
|
|
206
206
|
}
|
|
207
207
|
send(data) {
|
|
208
|
+
if (data === null || data === void 0) {
|
|
209
|
+
this.end();
|
|
210
|
+
return;
|
|
211
|
+
}
|
|
208
212
|
if (NO_BODY_RESPONSE_STATUSES.has(this.resStatusCode)) {
|
|
209
213
|
this.end();
|
|
210
214
|
return;
|
|
@@ -220,8 +224,8 @@ var PagesResponseStream = class extends Writable {
|
|
|
220
224
|
this.end(data);
|
|
221
225
|
return;
|
|
222
226
|
}
|
|
223
|
-
if (typeof data === "object"
|
|
224
|
-
this.resHeaders["content-type"] = "application/json";
|
|
227
|
+
if (typeof data === "object" || typeof data === "number" || typeof data === "boolean") {
|
|
228
|
+
this.resHeaders["content-type"] = "application/json; charset=utf-8";
|
|
225
229
|
this.end(JSON.stringify(data));
|
|
226
230
|
return;
|
|
227
231
|
}
|
|
@@ -5,7 +5,7 @@ import { NEXTJS_CACHE_HEADER, NEXTJS_DEPLOYMENT_ID_HEADER, VINEXT_CACHE_HEADER,
|
|
|
5
5
|
import { normalizePathnameForRouteMatch } from "../routing/utils.js";
|
|
6
6
|
import { patternToNextFormat } from "../routing/route-validation.js";
|
|
7
7
|
import { getRequestExecutionContext } from "../shims/request-context.js";
|
|
8
|
-
import { extractLocaleFromUrl, resolvePagesI18nRequest } from "./pages-i18n.js";
|
|
8
|
+
import { extractLocaleFromUrl, markLocaleRedirect, resolvePagesI18nRequest } from "./pages-i18n.js";
|
|
9
9
|
import { buildNextDataNotFoundResponse, buildNextDataPropsJsonResponse, normalizePagesDataRequest, parseNextDataPathname } from "./pages-data-route.js";
|
|
10
10
|
import { reportRequestError } from "./instrumentation.js";
|
|
11
11
|
import { logRequestError } from "./request-error-log.js";
|
|
@@ -154,7 +154,7 @@ function createPagesPageHandler(opts) {
|
|
|
154
154
|
const originalRequestPathAndSearch = originalRequestUrl.pathname + originalRequestUrl.search;
|
|
155
155
|
let dataRequestPathname = null;
|
|
156
156
|
let dataRequestSearch = "";
|
|
157
|
-
const initialDataNorm = normalizePagesDataRequest(request, buildId,
|
|
157
|
+
const initialDataNorm = normalizePagesDataRequest(request, buildId, "", hasMiddleware && vinextConfig.trailingSlash);
|
|
158
158
|
if (!isDataReq) {
|
|
159
159
|
if (initialDataNorm.notFoundResponse) return initialDataNorm.notFoundResponse;
|
|
160
160
|
if (initialDataNorm.isDataReq) {
|
|
@@ -195,10 +195,10 @@ function createPagesPageHandler(opts) {
|
|
|
195
195
|
const currentDefaultLocale = i18nConfig ? localeInfo.domainLocale ? localeInfo.domainLocale.defaultLocale : i18nConfig.defaultLocale : void 0;
|
|
196
196
|
const domainLocales = i18nConfig ? i18nConfig.domains : void 0;
|
|
197
197
|
const pageIsrCacheKey = isrCacheKeyForRequest(i18nConfig ? localeInfo.domainLocale ? JSON.stringify([String(localeInfo.domainLocale.domain).toLowerCase(), locale]) : "locale:" + String(locale) : null);
|
|
198
|
-
if (localeInfo.redirectUrl) return new Response(null, {
|
|
198
|
+
if (localeInfo.redirectUrl) return markLocaleRedirect(new Response(null, {
|
|
199
199
|
status: 307,
|
|
200
200
|
headers: { Location: localeInfo.redirectUrl }
|
|
201
|
-
});
|
|
201
|
+
}));
|
|
202
202
|
let match = options && options.__forcedRoute ? {
|
|
203
203
|
route: options.__forcedRoute,
|
|
204
204
|
params: {}
|
|
@@ -15,6 +15,14 @@ type PageRouteMatch = {
|
|
|
15
15
|
dataKind?: PagesRouteDataKind;
|
|
16
16
|
};
|
|
17
17
|
};
|
|
18
|
+
/**
|
|
19
|
+
* Whether a filesystem route may serve `requestPathname` from the client
|
|
20
|
+
* build directory. Like Next.js, only a public file or a build asset resolves
|
|
21
|
+
* there: the directory also holds build metadata and private artifacts. A
|
|
22
|
+
* direct GET/HEAD reaches this route only for a public file, since servers
|
|
23
|
+
* answer existing build assets before middleware.
|
|
24
|
+
*/
|
|
25
|
+
export declare function isServableFilesystemRoute(requestPathname: string, phase: FilesystemRoutePhase, method: string, publicFiles: ReadonlySet<string>, basePath?: string, assetPathPrefix?: string, pathnameHasBasePath?: boolean): boolean;
|
|
18
26
|
export declare function fetchWorkerFilesystemRoute(request: Request, requestPathname: string, phase: FilesystemRoutePhase, fetchAsset: (request: Request) => Promise<Response>, publicFiles: ReadonlySet<string>, basePath?: string, assetPathPrefix?: string, pathnameHasBasePath?: boolean): Promise<Response | false>;
|
|
19
27
|
export type MiddlewareResult = {
|
|
20
28
|
continue: boolean;
|
|
@@ -77,6 +85,7 @@ export type PagesPipelineDeps = {
|
|
|
77
85
|
runMiddleware?: ((request: Request, ctx: unknown, opts: {
|
|
78
86
|
isDataRequest: boolean;
|
|
79
87
|
isStaleDataRequest?: boolean;
|
|
88
|
+
restoreLocaleRootTrailingSlash?: boolean;
|
|
80
89
|
}) => Promise<MiddlewareResult>) | null;
|
|
81
90
|
/** Original URL presented to middleware when URL normalization is disabled. */
|
|
82
91
|
middlewareRequest?: Request;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { PRERENDER_REVALIDATE_HEADER } from "../utils/protocol-headers.js";
|
|
2
2
|
import { requestContextFromRequest } from "../config/request-context.js";
|
|
3
3
|
import { isExternalUrl } from "../utils/external-url.js";
|
|
4
|
-
import {
|
|
4
|
+
import { restoreBasePath } from "../utils/base-path.js";
|
|
5
5
|
import { VINEXT_PRERENDER_REWRITTEN_HEADER } from "./headers.js";
|
|
6
6
|
import { methodNotAllowedResponse, notFoundStaticAssetResponse, sanitizeMethodNotAllowedHeaders } from "./http-error-responses.js";
|
|
7
7
|
import { cloneRequestWithUrl, normalizeTrailingSlash } from "./request-pipeline.js";
|
|
@@ -14,10 +14,12 @@ import { buildMiddlewarePrefetchSkipResponse } from "./pages-data-route.js";
|
|
|
14
14
|
import { markRouteCacheabilityDynamic } from "../shims/cacheability-classification.js";
|
|
15
15
|
import { isOnDemandRevalidateRequest } from "./revalidation-request.js";
|
|
16
16
|
import { mergeRewriteQuery } from "../utils/query.js";
|
|
17
|
+
import { isImageOptimizationPath } from "./image-optimization.js";
|
|
17
18
|
import { setFrameworkRequestRoute } from "./request-tracing.js";
|
|
18
19
|
import { withExternalRewriteResponseHeaders } from "./middleware-response-headers.js";
|
|
19
20
|
import { applyConfigHeadersToHeaderRecord } from "./config-headers.js";
|
|
20
|
-
import { finalizeMissingStaticAssetResponse, mergeHeaders } from "./worker-utils.js";
|
|
21
|
+
import { finalizeMissingStaticAssetResponse, mergeHeaders, mergeStaticFileHeaders } from "./worker-utils.js";
|
|
22
|
+
import { publicFileRouteKey } from "../utils/public-file-path.js";
|
|
21
23
|
//#region src/server/pages-request-pipeline.ts
|
|
22
24
|
function ruleUsesUnkeyedRequestCondition(rule) {
|
|
23
25
|
return [...rule.has ?? [], ...rule.missing ?? []].some((condition) => condition.type === "header" || condition.type === "cookie" || condition.type === "host");
|
|
@@ -34,24 +36,41 @@ function headersFromRecord(record) {
|
|
|
34
36
|
else headers.set(name, value);
|
|
35
37
|
return headers;
|
|
36
38
|
}
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
39
|
+
/**
|
|
40
|
+
* Whether a filesystem route may serve `requestPathname` from the client
|
|
41
|
+
* build directory. Like Next.js, only a public file or a build asset resolves
|
|
42
|
+
* there: the directory also holds build metadata and private artifacts. A
|
|
43
|
+
* direct GET/HEAD reaches this route only for a public file, since servers
|
|
44
|
+
* answer existing build assets before middleware.
|
|
45
|
+
*/
|
|
46
|
+
function isServableFilesystemRoute(requestPathname, phase, method, publicFiles, basePath = "", assetPathPrefix = "", pathnameHasBasePath = true) {
|
|
47
|
+
const assetUrl = new URL("http://localhost");
|
|
40
48
|
assetUrl.pathname = requestPathname;
|
|
41
|
-
assetUrl.search = "";
|
|
42
49
|
const decodedAssetUrl = new URL(assetUrl);
|
|
43
50
|
let publicFileKey;
|
|
51
|
+
let servedPathname;
|
|
52
|
+
let servedFileKey;
|
|
44
53
|
try {
|
|
45
54
|
decodedAssetUrl.pathname = decodeURIComponent(assetUrl.pathname);
|
|
46
55
|
publicFileKey = normalizePathnameForRouteMatchStrict(assetUrl.pathname);
|
|
56
|
+
servedPathname = decodeURIComponent(requestPathname);
|
|
57
|
+
servedFileKey = publicFileRouteKey(servedPathname);
|
|
47
58
|
} catch {
|
|
48
59
|
return false;
|
|
49
60
|
}
|
|
50
|
-
|
|
51
|
-
|
|
61
|
+
const decodedPathname = decodedAssetUrl.pathname;
|
|
62
|
+
if (!pathnameHasBasePath && !isAssetPrefixBuildAsset(decodedPathname, assetPathPrefix)) return false;
|
|
63
|
+
const isPublicFile = pathnameHasBasePath && publicFiles.has(publicFileKey) && publicFiles.has(servedFileKey);
|
|
52
64
|
if (!isPublicFile && (requestPathname === "/api" || requestPathname.startsWith("/api/"))) return false;
|
|
53
|
-
if (phase === "direct" &&
|
|
54
|
-
|
|
65
|
+
if (phase === "direct" && (method === "GET" || method === "HEAD") && !isPublicFile) return false;
|
|
66
|
+
return isPublicFile || isNextStaticPath(decodedPathname, basePath, assetPathPrefix) && isNextStaticPath(servedPathname, basePath, assetPathPrefix);
|
|
67
|
+
}
|
|
68
|
+
async function fetchWorkerFilesystemRoute(request, requestPathname, phase, fetchAsset, publicFiles, basePath = "", assetPathPrefix = "", pathnameHasBasePath = true) {
|
|
69
|
+
const isRetrievalMethod = request.method === "GET" || request.method === "HEAD";
|
|
70
|
+
const assetUrl = new URL(request.url);
|
|
71
|
+
assetUrl.pathname = requestPathname;
|
|
72
|
+
assetUrl.search = "";
|
|
73
|
+
if (!isServableFilesystemRoute(requestPathname, phase, request.method, publicFiles, basePath, assetPathPrefix, pathnameHasBasePath)) return false;
|
|
55
74
|
const response = await fetchAsset(isRetrievalMethod ? new Request(assetUrl, request) : new Request(assetUrl, {
|
|
56
75
|
method: "HEAD",
|
|
57
76
|
headers: request.headers
|
|
@@ -80,7 +99,7 @@ function wrapMiddlewareWithBasePath(runMiddleware, basePath, hadBasePath) {
|
|
|
80
99
|
if (!hadBasePath || !basePath) return runMiddleware;
|
|
81
100
|
return (request, ctx, opts) => {
|
|
82
101
|
const mwUrl = new URL(request.url);
|
|
83
|
-
mwUrl.pathname =
|
|
102
|
+
mwUrl.pathname = restoreBasePath(mwUrl.pathname, basePath);
|
|
84
103
|
return runMiddleware(cloneRequestWithUrl(request, mwUrl.toString()), ctx, opts);
|
|
85
104
|
};
|
|
86
105
|
}
|
|
@@ -166,7 +185,7 @@ async function runPagesRequest(request, deps) {
|
|
|
166
185
|
const served = await deps.serveFilesystemRoute(requestPathname, middlewareHeaders, phase, resolvedUrl, request, resolvedPathnameHasBasePath);
|
|
167
186
|
if (served instanceof Response) {
|
|
168
187
|
const isStaticMethodNotAllowed = served.status === 405 && served.headers.get("allow") === "GET, HEAD";
|
|
169
|
-
const response = mergeHeaders(served, middlewareHeaders, isStaticMethodNotAllowed || !served.ok || served.status === 206 ? void 0 : middlewareStatus);
|
|
188
|
+
const response = (resolvedPathnameHasBasePath && isImageOptimizationPath(requestPathname) ? mergeHeaders : mergeStaticFileHeaders)(served, middlewareHeaders, isStaticMethodNotAllowed || !served.ok || served.status === 206 ? void 0 : middlewareStatus);
|
|
170
189
|
if (isStaticMethodNotAllowed) sanitizeMethodNotAllowedHeaders(response.headers, "GET, HEAD");
|
|
171
190
|
return {
|
|
172
191
|
type: "response",
|
|
@@ -178,7 +197,8 @@ async function runPagesRequest(request, deps) {
|
|
|
178
197
|
if (!isOnDemandRevalidate && typeof deps.runMiddleware === "function") {
|
|
179
198
|
const result = await deps.runMiddleware(deps.middlewareRequest ?? request, deps.ctx ?? null, {
|
|
180
199
|
isDataRequest,
|
|
181
|
-
isStaleDataRequest: isDataRequest && !!deps.dataNotFoundResponse
|
|
200
|
+
isStaleDataRequest: isDataRequest && !!deps.dataNotFoundResponse,
|
|
201
|
+
restoreLocaleRootTrailingSlash: deps.restoreLocaleRootTrailingSlash
|
|
182
202
|
});
|
|
183
203
|
if (deps.recordCacheability !== false && result.pathnameEligible) markRouteCacheabilityDynamic("middleware can match this pathname");
|
|
184
204
|
if (result.waitUntilPromises && result.waitUntilPromises.length > 0) {
|
|
@@ -306,7 +326,7 @@ async function runPagesRequest(request, deps) {
|
|
|
306
326
|
let apiRequest = request;
|
|
307
327
|
if (basePath && hadBasePath) {
|
|
308
328
|
const apiRequestUrl = new URL(request.url);
|
|
309
|
-
apiRequestUrl.pathname =
|
|
329
|
+
apiRequestUrl.pathname = restoreBasePath(apiRequestUrl.pathname, basePath);
|
|
310
330
|
apiRequest = cloneRequestWithUrl(request, apiRequestUrl.toString());
|
|
311
331
|
}
|
|
312
332
|
const response = await deps.handleApi(apiRequest, apiLookupUrl, deps.ctx ?? null, headersFromRecord(middlewareHeaders));
|
|
@@ -464,4 +484,4 @@ async function runPagesRequest(request, deps) {
|
|
|
464
484
|
};
|
|
465
485
|
}
|
|
466
486
|
//#endregion
|
|
467
|
-
export { fetchWorkerFilesystemRoute, runPagesRequest, wrapMiddlewareWithBasePath };
|
|
487
|
+
export { fetchWorkerFilesystemRoute, isServableFilesystemRoute, runPagesRequest, wrapMiddlewareWithBasePath };
|
|
@@ -69,10 +69,13 @@ const configRewrites = vinextConfig?.rewrites ?? {
|
|
|
69
69
|
const configHeaders = vinextConfig?.headers ?? [];
|
|
70
70
|
const imageConfig = vinextConfig?.images ? {
|
|
71
71
|
qualities: vinextConfig.images.qualities,
|
|
72
|
+
localPatterns: vinextConfig.images.localPatterns,
|
|
72
73
|
dangerouslyAllowSVG: vinextConfig.images.dangerouslyAllowSVG,
|
|
73
74
|
dangerouslyAllowLocalIP: vinextConfig.images.dangerouslyAllowLocalIP,
|
|
74
75
|
contentDispositionType: vinextConfig.images.contentDispositionType,
|
|
75
|
-
contentSecurityPolicy: vinextConfig.images.contentSecurityPolicy
|
|
76
|
+
contentSecurityPolicy: vinextConfig.images.contentSecurityPolicy,
|
|
77
|
+
minimumCacheTTL: vinextConfig.images.minimumCacheTTL,
|
|
78
|
+
maximumResponseBody: vinextConfig.images.maximumResponseBody
|
|
76
79
|
} : void 0;
|
|
77
80
|
/**
|
|
78
81
|
* Whether the request stage serves this request as a client build file
|
|
@@ -383,7 +386,10 @@ async function handleRequestImpl(request, env, platformCtx, dispatchResponseStag
|
|
|
383
386
|
});
|
|
384
387
|
if (sourceResult.type !== "response") return null;
|
|
385
388
|
await sourceResult.response.body?.cancel().catch(() => {});
|
|
386
|
-
if (isImageSourceBodyStatus(sourceResult.response.status)
|
|
389
|
+
if (isImageSourceBodyStatus(sourceResult.response.status) && sourceAsset !== null) {
|
|
390
|
+
const { withStagedCacheControl } = await import("./worker-image-optimization.js");
|
|
391
|
+
return withStagedCacheControl(sourceAsset, sourceResult.response.headers.get("Cache-Control"));
|
|
392
|
+
}
|
|
387
393
|
await sourceAsset?.body?.cancel().catch(() => {});
|
|
388
394
|
return null;
|
|
389
395
|
};
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import { VINEXT_CACHEABILITY_PROBE_HEADER, VINEXT_CACHEABILITY_PROBE_QUERY_PARAM } from "./headers.js";
|
|
2
|
+
import { notFoundResponse } from "./http-error-responses.js";
|
|
2
3
|
import { cloneRequestWithHeaders, cloneRequestWithUrl, stripRequestUrlFragment } from "./request-pipeline.js";
|
|
3
4
|
import { runWithExecutionContext } from "../shims/request-context.js";
|
|
4
5
|
import { getCdnCacheAdapter } from "../shims/cdn-cache.js";
|
|
5
6
|
import { validateCdnRequest } from "./cache-control.js";
|
|
6
7
|
import { createWorkerRevalidationContext } from "./worker-revalidation-context.js";
|
|
7
8
|
import { createWorkerPrerenderDiscoveryContext, createWorkerPrerenderReadinessResponse } from "./worker-prerender-discovery.js";
|
|
9
|
+
import { isSrvxAbsoluteFormTarget } from "./srvx-request-target.js";
|
|
8
10
|
import { handleRequestStageLocally, isPagesClientBuildFileRequest, pagesRequestStageBuildId, pagesRequestStagePrerenderSecret } from "./pages-request-stage-entry.js";
|
|
9
11
|
import { renderPagesResponse } from "./pages-response-stage-entry.js";
|
|
10
12
|
import { registerConfiguredCacheAdapters } from "virtual:vinext-cache-adapters";
|
|
@@ -52,6 +54,7 @@ async function handleSingleStageRequest(request, env, platformCtx) {
|
|
|
52
54
|
return finalize ? finalize(response, ctx) : response;
|
|
53
55
|
}
|
|
54
56
|
var pages_router_entry_default = { fetch(request, env, ctx) {
|
|
57
|
+
if (isSrvxAbsoluteFormTarget(request)) return Promise.resolve(notFoundResponse());
|
|
55
58
|
const strippedRequest = stripRequestUrlFragment(request);
|
|
56
59
|
const handle = () => handleSingleStageRequest(strippedRequest, env, ctx);
|
|
57
60
|
return ctx && typeof ctx.waitUntil === "function" ? runWithExecutionContext(ctx, handle) : handle();
|
|
@@ -90,7 +90,7 @@ declare function mergeWebResponse(middlewareHeaders: Record<string, string | str
|
|
|
90
90
|
* Send a compressed response if the content type is compressible and the
|
|
91
91
|
* client supports compression. Otherwise send uncompressed.
|
|
92
92
|
*/
|
|
93
|
-
declare function sendCompressed(req: IncomingMessage, res: ServerResponse, body: string | Buffer, contentType: string, statusCode: number, extraHeaders?: Record<string, string | string[]>, compress?: boolean, statusText?: string): void;
|
|
93
|
+
declare function sendCompressed(req: IncomingMessage, res: ServerResponse, body: string | Buffer, contentType: string | undefined, statusCode: number, extraHeaders?: Record<string, string | string[]>, compress?: boolean, statusText?: string): void;
|
|
94
94
|
/**
|
|
95
95
|
* Try to serve a static file from the client build directory.
|
|
96
96
|
*
|
|
@@ -7,7 +7,7 @@ import { notFoundResponse } from "./http-error-responses.js";
|
|
|
7
7
|
import { getRepeatedSlashRedirect, isOpenRedirectShaped, sendRepeatedSlashRedirect } from "./open-redirect.js";
|
|
8
8
|
import { readStaticFileSignal } from "./static-file-signal.js";
|
|
9
9
|
import { canonicalizeRequestPathname, filterInternalHeaders } from "./request-pipeline.js";
|
|
10
|
-
import { ASSET_PREFIX_URL_DIR, assetPrefixPathname, isAbsoluteAssetPrefix
|
|
10
|
+
import { ASSET_PREFIX_URL_DIR, assetPrefixPathname, isAbsoluteAssetPrefix } from "../utils/asset-prefix.js";
|
|
11
11
|
import { StaticFileCache, contentTypeForPath, etagFromFilenameHash, staticFileCacheControl } from "./static-file-cache.js";
|
|
12
12
|
import { normalizePathnameForRouteMatchStrict } from "../routing/utils.js";
|
|
13
13
|
import { resolveRequestHost, resolveRequestProtocol, resolveTrustedForwardedHost, trustProxy, trustedHosts } from "./proxy-trust.js";
|
|
@@ -19,13 +19,13 @@ import { parseHttpDate } from "./http-date.js";
|
|
|
19
19
|
import { evaluateStaticPreconditions } from "./http-conditional.js";
|
|
20
20
|
import { setPagesClientAssets } from "./pages-client-assets.js";
|
|
21
21
|
import { signalFromNodeResponse } from "./node-response-signal.js";
|
|
22
|
-
import { DEFAULT_DEVICE_SIZES, DEFAULT_IMAGE_SIZES, IMAGE_RESPONSE_CACHE_STATE,
|
|
22
|
+
import { DEFAULT_DEVICE_SIZES, DEFAULT_IMAGE_SIZES, IMAGE_RESPONSE_CACHE_STATE, IMAGE_TYPE_NOT_ALLOWED_MESSAGE, INVALID_IMAGE_SOURCE_MESSAGE, isImageOptimizationPath, isImageSourceBodyStatus, isSafeImageContentType, parseImageParams, readImageOptimizationSignal, sourceImageCacheControl } from "./image-optimization.js";
|
|
23
23
|
import { installSocketErrorBackstop } from "./socket-error-backstop.js";
|
|
24
24
|
import { collectInlineCssManifest } from "../build/inline-css.js";
|
|
25
25
|
import { readTrustedRevalidationHostname } from "./revalidation-host.js";
|
|
26
26
|
import { traceFrameworkRequest } from "./request-tracing.js";
|
|
27
27
|
import { mergeHeaders } from "./worker-utils.js";
|
|
28
|
-
import { runPagesRequest, wrapMiddlewareWithBasePath } from "./pages-request-pipeline.js";
|
|
28
|
+
import { isServableFilesystemRoute, runPagesRequest, wrapMiddlewareWithBasePath } from "./pages-request-pipeline.js";
|
|
29
29
|
import { computeClientRuntimeMetadata } from "../utils/client-runtime-metadata.js";
|
|
30
30
|
import { readPrerenderSecret, readServerCompress } from "../build/server-manifest.js";
|
|
31
31
|
import { readTrustedPrerenderRouteParamsFromHeaders, serializePrerenderRouteParamsHeader } from "./prerender-route-params.js";
|
|
@@ -270,6 +270,7 @@ const OMIT_STATIC_RESPONSE_HEADERS = /* @__PURE__ */ new Set([
|
|
|
270
270
|
"content-type",
|
|
271
271
|
VINEXT_PRERENDER_SPECIAL_ERROR_HEADER
|
|
272
272
|
]);
|
|
273
|
+
const IMAGE_ERROR_SIGNAL_HEADERS = ["content-type", "content-encoding"];
|
|
273
274
|
function omitHeadersCaseInsensitive(headersRecord, targets) {
|
|
274
275
|
const filtered = {};
|
|
275
276
|
for (const [key, value] of Object.entries(headersRecord)) {
|
|
@@ -338,6 +339,7 @@ function installClientBuildManifestGlobals(clientDir, assetBase, assetPrefix, cr
|
|
|
338
339
|
});
|
|
339
340
|
setPagesClientAssets({
|
|
340
341
|
appBootstrapPreinitModules: metadata.appBootstrapPreinitModules,
|
|
342
|
+
appBootstrapPreloadModules: metadata.appBootstrapPreloadModules,
|
|
341
343
|
appRouteManifestModule: metadata.appRouteManifestModule,
|
|
342
344
|
lazyChunks: metadata.lazyChunks,
|
|
343
345
|
dynamicPreloads: metadata.dynamicPreloads,
|
|
@@ -443,7 +445,7 @@ function sendCompressed(req, res, body, contentType, statusCode, extraHeaders =
|
|
|
443
445
|
if (encoding !== "identity") {
|
|
444
446
|
writeHead(mergeVaryHeader({
|
|
445
447
|
...headersWithoutBodyHeaders,
|
|
446
|
-
"Content-Type": contentType,
|
|
448
|
+
...contentType !== void 0 && { "Content-Type": contentType },
|
|
447
449
|
"Content-Encoding": encoding
|
|
448
450
|
}, "Accept-Encoding"));
|
|
449
451
|
const compressor = createCompressor(encoding);
|
|
@@ -452,7 +454,7 @@ function sendCompressed(req, res, body, contentType, statusCode, extraHeaders =
|
|
|
452
454
|
} else {
|
|
453
455
|
const identityHeaders = {
|
|
454
456
|
...headersWithoutBodyHeaders,
|
|
455
|
-
"Content-Type": contentType,
|
|
457
|
+
...contentType !== void 0 && { "Content-Type": contentType },
|
|
456
458
|
"Content-Length": String(buf.length)
|
|
457
459
|
};
|
|
458
460
|
writeHead(varyByEncoding ? mergeVaryHeader(identityHeaders, "Accept-Encoding") : identityHeaders);
|
|
@@ -476,20 +478,18 @@ function staticCacheLookupPath(pathname) {
|
|
|
476
478
|
}
|
|
477
479
|
/**
|
|
478
480
|
* Give a Node image passthrough Next.js's image Cache-Control instead of the
|
|
479
|
-
* public-file default: a
|
|
480
|
-
*
|
|
481
|
+
* public-file default: a build asset keeps its immutable default, and a public
|
|
482
|
+
* file, whatever its path, is revalidated after `images.minimumCacheTTL`, or
|
|
483
|
+
* after the max-age a direct request for it gets (from middleware or
|
|
484
|
+
* `headers()`, else the public-file default) when that is longer.
|
|
481
485
|
*/
|
|
482
|
-
function withImageSourceCacheControl(headers,
|
|
483
|
-
if (
|
|
486
|
+
function withImageSourceCacheControl(headers, publicFileCacheControl, config) {
|
|
487
|
+
if (publicFileCacheControl === void 0) return headers;
|
|
484
488
|
return {
|
|
485
489
|
...headers,
|
|
486
|
-
"Cache-Control":
|
|
490
|
+
"Cache-Control": sourceImageCacheControl(false, publicFileCacheControl ?? staticFileCacheControl(false), config)
|
|
487
491
|
};
|
|
488
492
|
}
|
|
489
|
-
/** Pathnames the Pages Router serves from routes rather than the client directory. */
|
|
490
|
-
function isPagesFilesystemExcludedPath(pathname, phase) {
|
|
491
|
-
return pathname === "/" || pathname === "/api" || pathname.startsWith("/api/") || phase === "direct" && pathname.startsWith(`/_next/static/`);
|
|
492
|
-
}
|
|
493
493
|
/**
|
|
494
494
|
* Try to serve a static file from the client build directory.
|
|
495
495
|
*
|
|
@@ -782,32 +782,14 @@ function pipeStaticFileRange(filePath, range, res) {
|
|
|
782
782
|
}
|
|
783
783
|
});
|
|
784
784
|
}
|
|
785
|
-
/**
|
|
786
|
-
* Resolve the actual file to serve, trying extension-less HTML fallbacks.
|
|
787
|
-
* Returns the resolved path + size + mtime, or null if not found.
|
|
788
|
-
*/
|
|
785
|
+
/** Returns the file's path + size + mtime, or null if it is not a regular file. */
|
|
789
786
|
async function resolveStaticFile(staticFile) {
|
|
790
787
|
const stat = await statIfFile(staticFile);
|
|
791
|
-
|
|
788
|
+
return stat ? {
|
|
792
789
|
path: staticFile,
|
|
793
790
|
size: stat.size,
|
|
794
791
|
mtimeMs: stat.mtimeMs
|
|
795
|
-
};
|
|
796
|
-
const htmlFallback = staticFile + ".html";
|
|
797
|
-
const htmlStat = await statIfFile(htmlFallback);
|
|
798
|
-
if (htmlStat) return {
|
|
799
|
-
path: htmlFallback,
|
|
800
|
-
size: htmlStat.size,
|
|
801
|
-
mtimeMs: htmlStat.mtimeMs
|
|
802
|
-
};
|
|
803
|
-
const indexFallback = path.join(staticFile, "index.html");
|
|
804
|
-
const indexStat = await statIfFile(indexFallback);
|
|
805
|
-
if (indexStat) return {
|
|
806
|
-
path: indexFallback,
|
|
807
|
-
size: indexStat.size,
|
|
808
|
-
mtimeMs: indexStat.mtimeMs
|
|
809
|
-
};
|
|
810
|
-
return null;
|
|
792
|
+
} : null;
|
|
811
793
|
}
|
|
812
794
|
async function statIfFile(filePath) {
|
|
813
795
|
try {
|
|
@@ -1128,6 +1110,7 @@ function installPagesClientAssets(options) {
|
|
|
1128
1110
|
setPagesClientAssets({
|
|
1129
1111
|
clientEntry: metadata.clientEntryFile,
|
|
1130
1112
|
appBootstrapPreinitModules: metadata.appBootstrapPreinitModules,
|
|
1113
|
+
appBootstrapPreloadModules: metadata.appBootstrapPreloadModules,
|
|
1131
1114
|
appRouteManifestModule: metadata.appRouteManifestModule,
|
|
1132
1115
|
ssrManifest: Object.keys(ssrManifest).length > 0 ? ssrManifest : void 0,
|
|
1133
1116
|
sharedChunks: metadata.sharedChunks,
|
|
@@ -1195,18 +1178,25 @@ async function startAppRouterServer(options) {
|
|
|
1195
1178
|
if (seededRoutes > 0) console.log(`[vinext] Seeded ${seededRoutes} pre-rendered route${seededRoutes !== 1 ? "s" : ""} into memory cache`);
|
|
1196
1179
|
const staticCache = await StaticFileCache.create(clientDir);
|
|
1197
1180
|
const serveImageOptimization = async (req, res, request, signal) => {
|
|
1198
|
-
const
|
|
1181
|
+
const signalHeaders = mergeResponseHeaders({}, new Response(null, { headers: signal.middlewareHeaders }));
|
|
1182
|
+
const stagedHeaders = omitHeadersCaseInsensitive(signalHeaders, OMIT_STATIC_RESPONSE_HEADERS);
|
|
1199
1183
|
for (const [name, value] of Object.entries(stagedHeaders)) res.setHeader(name, value);
|
|
1184
|
+
const sendBadRequest = (body) => {
|
|
1185
|
+
for (const name of IMAGE_ERROR_SIGNAL_HEADERS) {
|
|
1186
|
+
const value = signalHeaders[name];
|
|
1187
|
+
if (value !== void 0) res.setHeader(name, value);
|
|
1188
|
+
}
|
|
1189
|
+
res.writeHead(400);
|
|
1190
|
+
res.end(body);
|
|
1191
|
+
};
|
|
1200
1192
|
const parsedUrl = new URL(`/${signal.search}`, "http://localhost");
|
|
1201
|
-
const params = parseImageParams(parsedUrl, appImageAllowedWidths, imageConfig?.qualities);
|
|
1193
|
+
const params = parseImageParams(parsedUrl, appImageAllowedWidths, imageConfig?.qualities, { localPatterns: imageConfig?.localPatterns });
|
|
1202
1194
|
if (!params) {
|
|
1203
|
-
|
|
1204
|
-
res.end("Bad Request");
|
|
1195
|
+
sendBadRequest("Bad Request");
|
|
1205
1196
|
return;
|
|
1206
1197
|
}
|
|
1207
1198
|
const sendUnsafeContentType = () => {
|
|
1208
|
-
|
|
1209
|
-
res.end("The requested resource is not an allowed image type");
|
|
1199
|
+
sendBadRequest(IMAGE_TYPE_NOT_ALLOWED_MESSAGE);
|
|
1210
1200
|
};
|
|
1211
1201
|
const isSafeSourcePath = (sourcePath) => isSafeImageContentType(contentTypeForPath(sourcePath), imageConfig?.dangerouslyAllowSVG);
|
|
1212
1202
|
const imageSecurityHeaders = {
|
|
@@ -1214,7 +1204,7 @@ async function startAppRouterServer(options) {
|
|
|
1214
1204
|
"X-Content-Type-Options": "nosniff",
|
|
1215
1205
|
"Content-Disposition": imageConfig?.contentDispositionType === "attachment" ? "attachment" : "inline"
|
|
1216
1206
|
};
|
|
1217
|
-
const serveSource = (sourcePath) => tryServeStatic(req, res, clientDir, sourcePath, false, staticCache, withImageSourceCacheControl(imageSecurityHeaders,
|
|
1207
|
+
const serveSource = (sourcePath, publicFileCacheControl) => tryServeStatic(req, res, clientDir, sourcePath, false, staticCache, withImageSourceCacheControl(imageSecurityHeaders, publicFileCacheControl, imageConfig), void 0, IMAGE_REPRESENTATION_HEADERS);
|
|
1218
1208
|
const sourceUrl = new URL(params.imageUrl, request.url);
|
|
1219
1209
|
let sourcePathname = null;
|
|
1220
1210
|
try {
|
|
@@ -1240,11 +1230,10 @@ async function startAppRouterServer(options) {
|
|
|
1240
1230
|
sendUnsafeContentType();
|
|
1241
1231
|
return;
|
|
1242
1232
|
}
|
|
1243
|
-
if (await serveSource(sourcePath)) return;
|
|
1233
|
+
if (await serveSource(sourcePath, sourceResponse.headers.get("Cache-Control"))) return;
|
|
1244
1234
|
}
|
|
1245
1235
|
}
|
|
1246
|
-
|
|
1247
|
-
res.end(INVALID_IMAGE_SOURCE_MESSAGE);
|
|
1236
|
+
sendBadRequest(INVALID_IMAGE_SOURCE_MESSAGE);
|
|
1248
1237
|
};
|
|
1249
1238
|
const clientBuildFiles = new Set(Array.isArray(rscModule.__clientBuildFiles) ? rscModule.__clientBuildFiles : []);
|
|
1250
1239
|
const handleRequestImpl = async (req, res) => {
|
|
@@ -1433,6 +1422,8 @@ async function createPagesRouterRequestHandler(options) {
|
|
|
1433
1422
|
const ensureInstrumentation = typeof serverEntry.__ensureInstrumentation === "function" ? () => serverEntry.__ensureInstrumentation() : () => void 0;
|
|
1434
1423
|
const hasMiddleware = serverEntry.hasMiddleware === true;
|
|
1435
1424
|
const pageRoutes = readPagesServerEntryPageRoutes(serverEntry.pageRoutes);
|
|
1425
|
+
const publicFiles = serverEntry.publicFiles instanceof Set ? serverEntry.publicFiles : /* @__PURE__ */ new Set();
|
|
1426
|
+
const clientBuildFiles = serverEntry.clientBuildFiles instanceof Set ? serverEntry.clientBuildFiles : /* @__PURE__ */ new Set();
|
|
1436
1427
|
const prerenderSecret = readPrerenderSecret(path.dirname(serverEntryPath));
|
|
1437
1428
|
const compress = compressOption && readServerCompress(serverDir);
|
|
1438
1429
|
const basePath = vinextConfig?.basePath ?? "";
|
|
@@ -1455,8 +1446,11 @@ async function createPagesRouterRequestHandler(options) {
|
|
|
1455
1446
|
dangerouslyAllowSVG: vinextConfig.images.dangerouslyAllowSVG,
|
|
1456
1447
|
dangerouslyAllowLocalIP: vinextConfig.images.dangerouslyAllowLocalIP,
|
|
1457
1448
|
qualities: vinextConfig.images.qualities,
|
|
1449
|
+
localPatterns: vinextConfig.images.localPatterns,
|
|
1458
1450
|
contentDispositionType: vinextConfig.images.contentDispositionType,
|
|
1459
|
-
contentSecurityPolicy: vinextConfig.images.contentSecurityPolicy
|
|
1451
|
+
contentSecurityPolicy: vinextConfig.images.contentSecurityPolicy,
|
|
1452
|
+
minimumCacheTTL: vinextConfig.images.minimumCacheTTL,
|
|
1453
|
+
maximumResponseBody: vinextConfig.images.maximumResponseBody
|
|
1460
1454
|
} : void 0;
|
|
1461
1455
|
const ssrManifest = installPagesClientAssets({
|
|
1462
1456
|
clientDir,
|
|
@@ -1546,6 +1540,10 @@ async function createPagesRouterRequestHandler(options) {
|
|
|
1546
1540
|
if (pagesAssetLookup) {
|
|
1547
1541
|
if (await tryServeStatic(req, res, clientDir, pagesAssetLookup, compress, staticCache)) return;
|
|
1548
1542
|
}
|
|
1543
|
+
if (req.method === "GET" || req.method === "HEAD") {
|
|
1544
|
+
const clientBuildFile = resolveClientBuildFile(rawPagesPathname, basePath, clientBuildFiles);
|
|
1545
|
+
if (clientBuildFile && await tryServeStatic(req, res, clientDir, clientBuildFile, compress, staticCache)) return;
|
|
1546
|
+
}
|
|
1549
1547
|
try {
|
|
1550
1548
|
const hadBasePath = !basePath || hasBasePath(requestPathname, basePath);
|
|
1551
1549
|
const restoreLocaleRootTrailingSlash = shouldRestoreLocaleRootTrailingSlash(requestPathname, {
|
|
@@ -1616,6 +1614,7 @@ async function createPagesRouterRequestHandler(options) {
|
|
|
1616
1614
|
const middlewareRequest = isDataReq && skipProxyUrlNormalize ? new Request(new URL(originalRenderUrl, webRequest.url), webRequest) : void 0;
|
|
1617
1615
|
const middlewareCtx = createNodeExecutionContext(void 0, { middlewareOrigin: resolveNodeMiddlewareOrigin(req, webRequest.url, host, port) });
|
|
1618
1616
|
const runNodeMiddleware = typeof runMiddleware === "function" ? (request, _ctx, opts) => runMiddleware(request, middlewareCtx, opts) : null;
|
|
1617
|
+
/** The public file a source resolves to, with the Cache-Control staged for it. */
|
|
1619
1618
|
const resolvePublicImageSource = async (sourceUrl) => {
|
|
1620
1619
|
const rawSourcePathname = sourceUrl.pathname;
|
|
1621
1620
|
if (getRepeatedSlashRedirect(rawSourcePathname) || isOpenRedirectShaped(rawSourcePathname)) return null;
|
|
@@ -1623,7 +1622,7 @@ async function createPagesRouterRequestHandler(options) {
|
|
|
1623
1622
|
const sourceHadBasePath = !basePath || hasBasePath(sourceRequestPathname, basePath);
|
|
1624
1623
|
const strippedSourcePathname = stripBasePath(sourceRequestPathname, basePath);
|
|
1625
1624
|
if (isNextDataPathname(strippedSourcePathname)) return null;
|
|
1626
|
-
let
|
|
1625
|
+
let source = null;
|
|
1627
1626
|
const result = await runPagesRequest(new Request(new URL(strippedSourcePathname + sourceUrl.search, sourceUrl), { signal: webRequest.signal }), {
|
|
1628
1627
|
...deps,
|
|
1629
1628
|
hadBasePath: sourceHadBasePath,
|
|
@@ -1641,17 +1640,20 @@ async function createPagesRouterRequestHandler(options) {
|
|
|
1641
1640
|
runMiddleware: runNodeMiddleware ? wrapMiddlewareWithBasePath(runNodeMiddleware, basePath, sourceHadBasePath) : null,
|
|
1642
1641
|
renderPage: null,
|
|
1643
1642
|
handleApi: null,
|
|
1644
|
-
serveFilesystemRoute: async (candidate,
|
|
1645
|
-
if (
|
|
1643
|
+
serveFilesystemRoute: async (candidate, stagedHeaders, phase, _resolvedUrl, sourceRequest, candidateHasBasePath) => {
|
|
1644
|
+
if (isImageOptimizationPath(candidate) || !isServableFilesystemRoute(candidate, phase, sourceRequest.method, publicFiles, basePath, pagesAssetPathPrefix, candidateHasBasePath)) return false;
|
|
1646
1645
|
const lookupPath = staticCacheLookupPath(candidate);
|
|
1647
1646
|
if (lookupPath === null || lookupPath === "/" || !staticCache.lookup(lookupPath)) return false;
|
|
1648
|
-
|
|
1647
|
+
source = {
|
|
1648
|
+
path: candidate,
|
|
1649
|
+
cacheControl: readHeaderCaseInsensitive(stagedHeaders, "cache-control") ?? null
|
|
1650
|
+
};
|
|
1649
1651
|
return new Response(null);
|
|
1650
1652
|
}
|
|
1651
1653
|
});
|
|
1652
1654
|
if (result.type !== "response") return null;
|
|
1653
1655
|
cancelResponseBody(result.response);
|
|
1654
|
-
return isImageSourceBodyStatus(result.response.status) ?
|
|
1656
|
+
return isImageSourceBodyStatus(result.response.status) ? source : null;
|
|
1655
1657
|
};
|
|
1656
1658
|
const deps = {
|
|
1657
1659
|
assetPrefix,
|
|
@@ -1685,11 +1687,11 @@ async function createPagesRouterRequestHandler(options) {
|
|
|
1685
1687
|
return handleApi(request, apiUrl, createNodeExecutionContext(), resolveTrustedNodeRevalidateOrigin(req, host, port), "node", stagedHeaders);
|
|
1686
1688
|
} : null,
|
|
1687
1689
|
handleApiAppliesStagedHeaders: true,
|
|
1688
|
-
serveFilesystemRoute: async (requestPathname, stagedHeaders, phase, resolvedUrl,
|
|
1690
|
+
serveFilesystemRoute: async (requestPathname, stagedHeaders, phase, resolvedUrl, filesystemRequest, resolvedPathnameHasBasePath) => {
|
|
1689
1691
|
if (resolvedPathnameHasBasePath && isImageOptimizationPath(requestPathname)) {
|
|
1690
1692
|
for (const [name, value] of Object.entries(stagedHeaders)) res.setHeader(name, value);
|
|
1691
1693
|
const parsedUrl = new URL(resolvedUrl, "http://localhost");
|
|
1692
|
-
const params = parseImageParams(parsedUrl, allowedImageWidths, pagesImageConfig?.qualities);
|
|
1694
|
+
const params = parseImageParams(parsedUrl, allowedImageWidths, pagesImageConfig?.qualities, { localPatterns: pagesImageConfig?.localPatterns });
|
|
1693
1695
|
if (!params) {
|
|
1694
1696
|
res.writeHead(400);
|
|
1695
1697
|
res.end("Bad Request");
|
|
@@ -1697,7 +1699,7 @@ async function createPagesRouterRequestHandler(options) {
|
|
|
1697
1699
|
}
|
|
1698
1700
|
const sendUnsafeContentType = () => {
|
|
1699
1701
|
res.writeHead(400);
|
|
1700
|
-
res.end(
|
|
1702
|
+
res.end(IMAGE_TYPE_NOT_ALLOWED_MESSAGE);
|
|
1701
1703
|
};
|
|
1702
1704
|
const isSafeSourcePath = (sourcePath) => isSafeImageContentType(contentTypeForPath(sourcePath), pagesImageConfig?.dangerouslyAllowSVG);
|
|
1703
1705
|
const imageSecurityHeaders = {
|
|
@@ -1705,7 +1707,7 @@ async function createPagesRouterRequestHandler(options) {
|
|
|
1705
1707
|
"X-Content-Type-Options": "nosniff",
|
|
1706
1708
|
"Content-Disposition": pagesImageConfig?.contentDispositionType === "attachment" ? "attachment" : "inline"
|
|
1707
1709
|
};
|
|
1708
|
-
const serveSource = (sourcePath) => tryServeStatic(req, res, clientDir, sourcePath, false, staticCache, withImageSourceCacheControl(imageSecurityHeaders,
|
|
1710
|
+
const serveSource = (sourcePath, publicFileCacheControl) => tryServeStatic(req, res, clientDir, sourcePath, false, staticCache, withImageSourceCacheControl(imageSecurityHeaders, publicFileCacheControl, pagesImageConfig), void 0, IMAGE_REPRESENTATION_HEADERS);
|
|
1709
1711
|
const sourceUrl = new URL(params.imageUrl, webRequest.url);
|
|
1710
1712
|
let sourcePathname = null;
|
|
1711
1713
|
try {
|
|
@@ -1715,22 +1717,22 @@ async function createPagesRouterRequestHandler(options) {
|
|
|
1715
1717
|
sendUnsafeContentType();
|
|
1716
1718
|
return true;
|
|
1717
1719
|
}
|
|
1718
|
-
const assetLookupPath = sourcePathname !== null ? resolveAppRouterAssetPath(sourcePathname, pagesAssetPathPrefix, assetPrefix) : null;
|
|
1720
|
+
const assetLookupPath = sourcePathname !== null ? resolveAppRouterAssetPath(sourcePathname, pagesAssetPathPrefix, assetPrefix) ?? resolveClientBuildFile(sourceUrl.pathname, basePath, clientBuildFiles) : null;
|
|
1719
1721
|
if (assetLookupPath && await serveSource(assetLookupPath)) return true;
|
|
1720
|
-
const
|
|
1722
|
+
const source = sourcePathname !== null ? await resolvePublicImageSource(sourceUrl) : null;
|
|
1721
1723
|
if (webRequest.signal.aborted) return true;
|
|
1722
|
-
if (
|
|
1723
|
-
if (!isSafeSourcePath(staticCacheLookupPath(
|
|
1724
|
+
if (source !== null) {
|
|
1725
|
+
if (!isSafeSourcePath(staticCacheLookupPath(source.path) ?? source.path)) {
|
|
1724
1726
|
sendUnsafeContentType();
|
|
1725
1727
|
return true;
|
|
1726
1728
|
}
|
|
1727
|
-
if (await serveSource(
|
|
1729
|
+
if (await serveSource(source.path, source.cacheControl)) return true;
|
|
1728
1730
|
}
|
|
1729
1731
|
res.writeHead(400);
|
|
1730
1732
|
res.end(INVALID_IMAGE_SOURCE_MESSAGE);
|
|
1731
1733
|
return true;
|
|
1732
1734
|
}
|
|
1733
|
-
if (!
|
|
1735
|
+
if (!isServableFilesystemRoute(requestPathname, phase, filesystemRequest.method, publicFiles, basePath, pagesAssetPathPrefix, resolvedPathnameHasBasePath)) return false;
|
|
1734
1736
|
return tryServeStatic(req, res, clientDir, requestPathname, compress, staticCache, stagedHeaders);
|
|
1735
1737
|
}
|
|
1736
1738
|
};
|
|
@@ -1750,7 +1752,7 @@ async function createPagesRouterRequestHandler(options) {
|
|
|
1750
1752
|
}
|
|
1751
1753
|
const responseBody = await bufferResponseBodyUntilAborted(response, webRequest.signal);
|
|
1752
1754
|
if (!responseBody) return;
|
|
1753
|
-
const ct = response.headers.get("content-type") ?? result.defaultContentType;
|
|
1755
|
+
const ct = response.headers.get("content-type") ?? (responseBody.length > 0 ? result.defaultContentType : void 0);
|
|
1754
1756
|
const responseHeaders = {};
|
|
1755
1757
|
response.headers.forEach((v, k) => {
|
|
1756
1758
|
if (k === "set-cookie") return;
|
|
@@ -16,6 +16,11 @@ import { getRepeatedSlashRedirect, isOpenRedirectShaped, sendRepeatedSlashRedire
|
|
|
16
16
|
export declare function canonicalizeRequestPathname(pathname: string): string;
|
|
17
17
|
/** Canonicalize only the pathname portion while preserving the raw query. */
|
|
18
18
|
export declare function canonicalizeRequestUrlPathname(url: string): string;
|
|
19
|
+
/**
|
|
20
|
+
* Whether the pathname has a segment the URL parser resolves as `.` or `..`,
|
|
21
|
+
* literally or percent-encoded. Such a pathname changes when it becomes a URL.
|
|
22
|
+
*/
|
|
23
|
+
export declare function hasUrlParserDotSegment(pathname: string): boolean;
|
|
19
24
|
/**
|
|
20
25
|
* Shared request pipeline utilities.
|
|
21
26
|
*
|