vinext 1.1.0 → 1.2.0
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 +21 -9
- package/dist/build/client-build-config.d.ts +10 -0
- package/dist/build/client-build-config.js +18 -2
- package/dist/build/pages-client-assets-module.d.ts +8 -0
- package/dist/build/pages-client-assets-module.js +21 -1
- package/dist/build/prerender-paths.js +5 -3
- package/dist/build/run-prerender.js +1 -1
- package/dist/build/ssr-manifest.d.ts +2 -0
- package/dist/build/ssr-manifest.js +6 -2
- package/dist/check.js +7 -6
- package/dist/cli.js +2 -1
- package/dist/client/client-redirect-matcher.d.ts +14 -0
- package/dist/client/client-redirect-matcher.js +37 -0
- package/dist/client/client-redirects.d.ts +27 -0
- package/dist/client/client-redirects.js +28 -0
- package/dist/client/client-rewrites.d.ts +2 -1
- package/dist/client/client-rewrites.js +1 -1
- package/dist/config/config-matchers.d.ts +26 -11
- package/dist/config/config-matchers.js +78 -81
- package/dist/config/destination-params.d.ts +47 -0
- package/dist/config/destination-params.js +116 -0
- package/dist/config/next-config.d.ts +7 -0
- package/dist/config/next-config.js +3 -0
- package/dist/entries/app-rsc-entry.d.ts +4 -0
- package/dist/entries/app-rsc-entry.js +20 -6
- package/dist/entries/pages-client-entry.js +3 -1
- package/dist/entries/pages-server-entry.js +13 -37
- package/dist/index.d.ts +3 -1
- package/dist/index.js +134 -43
- package/dist/init-cloudflare.d.ts +9 -0
- package/dist/init-cloudflare.js +107 -31
- package/dist/init-platform.d.ts +4 -0
- package/dist/init-platform.js +27 -2
- package/dist/init.d.ts +1 -0
- package/dist/init.js +67 -3
- package/dist/plugins/ast-scope.d.ts +7 -0
- package/dist/plugins/ast-scope.js +59 -3
- package/dist/plugins/cache-flight-codec.d.ts +1 -1
- package/dist/plugins/dynamic-preload-metadata.d.ts +1 -1
- package/dist/plugins/dynamic-preload-metadata.js +9 -8
- package/dist/plugins/ignore-dynamic-requests.d.ts +1 -1
- package/dist/plugins/ignore-dynamic-requests.js +3 -4
- package/dist/plugins/middleware-server-only.d.ts +5 -4
- package/dist/plugins/middleware-server-only.js +8 -5
- package/dist/plugins/pages-node-externals.js +1 -1
- package/dist/plugins/rsc-scan-glob-imports.d.ts +19 -0
- package/dist/plugins/rsc-scan-glob-imports.js +50 -0
- package/dist/plugins/strip-server-exports.js +262 -98
- package/dist/plugins/transform-result.d.ts +9 -5
- package/dist/plugins/transitive-externals.js +1 -1
- package/dist/plugins/typeof-window.d.ts +2 -2
- package/dist/plugins/use-cache-callable.js +8 -2
- package/dist/plugins/worker-server-only-imports.d.ts +20 -0
- package/dist/plugins/worker-server-only-imports.js +31 -0
- package/dist/plugins/worker-use-server.d.ts +25 -0
- package/dist/plugins/worker-use-server.js +129 -0
- package/dist/server/api-handler.d.ts +1 -0
- package/dist/server/api-handler.js +2 -1
- package/dist/server/app-browser-action-result.d.ts +1 -1
- package/dist/server/app-browser-action-result.js +9 -2
- package/dist/server/app-browser-navigation-controller.js +1 -1
- package/dist/server/app-browser-server-action-client.js +1 -1
- package/dist/server/app-middleware.d.ts +11 -0
- package/dist/server/app-middleware.js +17 -2
- package/dist/server/app-page-cache.d.ts +1 -1
- package/dist/server/app-page-cache.js +1 -1
- package/dist/server/app-page-dispatch.d.ts +3 -1
- package/dist/server/app-page-dispatch.js +4 -3
- package/dist/server/app-page-ppr-runtime.js +1 -1
- package/dist/server/app-page-render-observation.js +1 -1
- package/dist/server/app-page-request.js +53 -15
- package/dist/server/app-page-route-wiring.js +2 -2
- package/dist/server/app-pages-bridge.d.ts +1 -1
- package/dist/server/app-pages-bridge.js +2 -3
- package/dist/server/app-prerender-endpoints.js +9 -6
- package/dist/server/app-request-stage-dispatch.js +6 -2
- package/dist/server/app-request-stage-independent-entry.js +25 -5
- package/dist/server/app-response-stage-entry.js +10 -2
- package/dist/server/app-route-handler-cache.d.ts +1 -0
- package/dist/server/app-route-handler-cache.js +1 -0
- package/dist/server/app-route-handler-dispatch.d.ts +1 -0
- package/dist/server/app-route-handler-dispatch.js +5 -2
- package/dist/server/app-route-handler-execution.d.ts +1 -0
- package/dist/server/app-route-handler-execution.js +1 -0
- package/dist/server/app-route-handler-runtime.d.ts +4 -1
- package/dist/server/app-route-handler-runtime.js +45 -24
- package/dist/server/app-router-entry.js +24 -5
- package/dist/server/app-rsc-cache-busting.js +1 -1
- package/dist/server/app-rsc-errors.js +20 -16
- package/dist/server/app-rsc-handler.d.ts +7 -0
- package/dist/server/app-rsc-handler.js +85 -35
- package/dist/server/app-rsc-response-stage.js +4 -2
- package/dist/server/app-segment-config.d.ts +2 -1
- package/dist/server/app-server-action-execution.d.ts +2 -1
- package/dist/server/app-server-action-execution.js +61 -22
- package/dist/server/app-ssr-entry.js +2 -1
- package/dist/server/app-worker-stages.d.ts +5 -0
- package/dist/server/app-worker-stages.js +1 -1
- package/dist/server/cacheability-request.js +2 -2
- package/dist/server/client-build-files.d.ts +16 -0
- package/dist/server/client-build-files.js +36 -0
- package/dist/server/client-reuse-manifest.js +1 -1
- package/dist/server/dev-server.js +38 -27
- package/dist/server/dev-stack-sourcemap.js +18 -23
- package/dist/server/document-initial-head.d.ts +0 -11
- package/dist/server/document-initial-head.js +1 -1
- package/dist/server/forwarded-middleware-context.d.ts +5 -0
- package/dist/server/forwarded-middleware-context.js +22 -0
- package/dist/server/headers.d.ts +4 -1
- package/dist/server/headers.js +4 -1
- package/dist/server/http-conditional.d.ts +11 -0
- package/dist/server/http-conditional.js +10 -1
- package/dist/server/http-error-responses.d.ts +1 -1
- package/dist/server/image-optimization.d.ts +38 -0
- package/dist/server/image-optimization.js +93 -4
- package/dist/server/isr-cache.d.ts +10 -0
- package/dist/server/isr-cache.js +41 -6
- package/dist/server/metadata-route-response.js +9 -8
- package/dist/server/middleware-matcher.d.ts +2 -0
- package/dist/server/middleware-matcher.js +23 -2
- package/dist/server/middleware-runtime.d.ts +13 -0
- package/dist/server/middleware-runtime.js +33 -20
- package/dist/server/middleware.d.ts +1 -1
- package/dist/server/middleware.js +3 -1
- package/dist/server/multi-stage.d.ts +11 -0
- package/dist/server/normalize-path.d.ts +0 -13
- package/dist/server/normalize-path.js +1 -34
- package/dist/server/pages-api-route.d.ts +3 -2
- package/dist/server/pages-api-route.js +4 -3
- package/dist/server/pages-cache-tag.d.ts +12 -0
- package/dist/server/pages-cache-tag.js +19 -0
- package/dist/server/pages-client-assets.d.ts +2 -0
- package/dist/server/pages-data-route.d.ts +10 -0
- package/dist/server/pages-data-route.js +19 -2
- package/dist/server/pages-document-asset-props.d.ts +5 -1
- package/dist/server/pages-document-asset-props.js +13 -5
- package/dist/server/pages-document-splice.d.ts +19 -0
- package/dist/server/pages-document-splice.js +55 -0
- package/dist/server/pages-i18n.d.ts +12 -1
- package/dist/server/pages-i18n.js +18 -4
- package/dist/server/pages-node-compat.d.ts +16 -5
- package/dist/server/pages-node-compat.js +20 -12
- package/dist/server/pages-page-data.d.ts +32 -24
- package/dist/server/pages-page-data.js +50 -120
- package/dist/server/pages-page-handler.d.ts +0 -6
- package/dist/server/pages-page-handler.js +188 -126
- package/dist/server/pages-page-response.d.ts +12 -6
- package/dist/server/pages-page-response.js +41 -34
- package/dist/server/pages-request-pipeline.d.ts +10 -2
- package/dist/server/pages-request-pipeline.js +37 -18
- package/dist/server/pages-request-stage-entry.d.ts +6 -0
- package/dist/server/pages-request-stage-entry.js +71 -12
- package/dist/server/pages-response-stage-entry.js +4 -0
- package/dist/server/pages-revalidate.js +1 -1
- package/dist/server/pages-router-entry.js +4 -3
- package/dist/server/prerender-manifest.d.ts +3 -3
- package/dist/server/prerender-manifest.js +4 -3
- package/dist/server/prerender-work-unit-setup.js +4 -0
- package/dist/server/prod-server.d.ts +12 -0
- package/dist/server/prod-server.js +199 -69
- package/dist/server/proper-error.d.ts +4 -0
- package/dist/server/proper-error.js +31 -0
- package/dist/server/request-pipeline.d.ts +4 -1
- package/dist/server/request-pipeline.js +3 -2
- package/dist/server/revalidation-request.d.ts +1 -0
- package/dist/server/revalidation-request.js +1 -1
- package/dist/server/rsc-stream-hints.js +31 -2
- package/dist/server/seed-cache.d.ts +1 -0
- package/dist/server/seed-cache.js +5 -4
- package/dist/server/server-reference-loader.d.ts +4 -0
- package/dist/server/server-reference-loader.js +29 -0
- package/dist/server/socket-error-backstop.d.ts +9 -1
- package/dist/server/socket-error-backstop.js +46 -17
- package/dist/server/static-file-signal.d.ts +15 -4
- package/dist/server/static-file-signal.js +46 -12
- package/dist/server/worker-image-optimization.d.ts +34 -0
- package/dist/server/worker-image-optimization.js +86 -0
- package/dist/server/worker-stages.d.ts +1 -1
- package/dist/server/worker-stages.js +2 -2
- package/dist/server/worker-utils.d.ts +8 -3
- package/dist/server/worker-utils.js +12 -7
- package/dist/shims/app-router-scroll.js +4 -1
- package/dist/shims/app-router-tree-context.d.ts +5 -0
- package/dist/shims/app-router-tree-context.js +27 -0
- package/dist/shims/cache-callable-runtime.d.ts +4 -2
- package/dist/shims/cache-callable-runtime.js +21 -12
- package/dist/shims/cache-handler.js +1 -9
- package/dist/shims/cache-runtime.d.ts +6 -1
- package/dist/shims/cache-runtime.js +2 -1
- package/dist/shims/cache.d.ts +2 -2
- package/dist/shims/cache.js +40 -13
- package/dist/shims/cdn-cache.d.ts +8 -0
- package/dist/shims/cdn-cache.js +17 -1
- package/dist/shims/disable-smooth-scroll.d.ts +3 -0
- package/dist/shims/disable-smooth-scroll.js +36 -0
- package/dist/shims/document-placeholders.d.ts +19 -0
- package/dist/shims/document-placeholders.js +45 -0
- package/dist/shims/document.js +21 -5
- package/dist/shims/dynamic-preload-chunks.d.ts +1 -0
- package/dist/shims/dynamic-preload-chunks.js +13 -9
- package/dist/shims/dynamic.d.ts +1 -1
- package/dist/shims/dynamic.js +60 -8
- package/dist/shims/fetch-cache-state.d.ts +66 -0
- package/dist/shims/fetch-cache-state.js +92 -0
- package/dist/shims/fetch-cache.d.ts +3 -67
- package/dist/shims/fetch-cache.js +160 -125
- package/dist/shims/head.d.ts +10 -6
- package/dist/shims/head.js +15 -13
- package/dist/shims/image.js +2 -1
- package/dist/shims/internal/work-store-scope.d.ts +5 -0
- package/dist/shims/internal/work-store-scope.js +24 -0
- package/dist/shims/navigation.js +31 -5
- package/dist/shims/request-context.d.ts +2 -0
- package/dist/shims/request-state-types.d.ts +2 -1
- package/dist/shims/router.js +12 -20
- package/dist/shims/server.d.ts +16 -0
- package/dist/shims/server.js +24 -4
- package/dist/shims/unified-request-context.d.ts +1 -1
- package/dist/shims/url-utils.d.ts +2 -0
- package/dist/shims/url-utils.js +3 -1
- package/dist/utils/client-runtime-metadata.js +1 -1
- package/dist/utils/hash.d.ts +7 -0
- package/dist/utils/hash.js +11 -1
- package/dist/utils/package-name.d.ts +6 -0
- package/dist/utils/package-name.js +12 -1
- package/dist/utils/plugin-rsc-version.d.ts +4 -0
- package/dist/utils/plugin-rsc-version.js +15 -0
- package/dist/utils/prerender-output-paths.js +15 -4
- package/dist/utils/public-file-path.d.ts +10 -2
- package/dist/utils/public-file-path.js +13 -9
- package/dist/utils/public-routes.js +2 -2
- package/dist/utils/react-version.d.ts +3 -0
- package/dist/utils/react-version.js +1 -1
- package/package.json +7 -3
|
@@ -53,6 +53,10 @@ async function renderPagesResponse(request, env, platformCtx, props, stagedHeade
|
|
|
53
53
|
...ctx,
|
|
54
54
|
isPrerenderPathDiscovery: true
|
|
55
55
|
};
|
|
56
|
+
else if (props.kind === "pages-page" && dispatchOptions.regeneratesStoredEntry === true) ctx = {
|
|
57
|
+
...ctx,
|
|
58
|
+
isStoredEntryRegeneration: true
|
|
59
|
+
};
|
|
56
60
|
const render = async (cacheabilityContext) => {
|
|
57
61
|
if (props.kind === "pages-prerender-discovery") {
|
|
58
62
|
const readinessResponse = createWorkerPrerenderReadinessResponse(cacheabilityContext, request);
|
|
@@ -29,7 +29,7 @@ async function performOnDemandRevalidate(source, urlPath, opts = {}, trustedOrig
|
|
|
29
29
|
const res = await fetchRevalidateTarget(target, headers, dispatchRevalidate);
|
|
30
30
|
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}`);
|
|
31
31
|
const regeneratedTag = res.headers.get(VINEXT_REVALIDATED_CACHE_TAG_HEADER);
|
|
32
|
-
if (regeneratedTag) await getCdnCacheAdapter().revalidateTag(regeneratedTag);
|
|
32
|
+
if (regeneratedTag) await getCdnCacheAdapter().revalidateTag(decodeURIComponent(regeneratedTag));
|
|
33
33
|
}
|
|
34
34
|
function readSourceHeader(source, key) {
|
|
35
35
|
if (isWebHeaders(source)) return source.get(key) ?? void 0;
|
|
@@ -5,7 +5,7 @@ import { getCdnCacheAdapter } from "../shims/cdn-cache.js";
|
|
|
5
5
|
import { validateCdnRequest } from "./cache-control.js";
|
|
6
6
|
import { createWorkerRevalidationContext } from "./worker-revalidation-context.js";
|
|
7
7
|
import { createWorkerPrerenderDiscoveryContext, createWorkerPrerenderReadinessResponse } from "./worker-prerender-discovery.js";
|
|
8
|
-
import { handleRequestStageLocally, pagesRequestStageBuildId, pagesRequestStagePrerenderSecret } from "./pages-request-stage-entry.js";
|
|
8
|
+
import { handleRequestStageLocally, isPagesClientBuildFileRequest, pagesRequestStageBuildId, pagesRequestStagePrerenderSecret } from "./pages-request-stage-entry.js";
|
|
9
9
|
import { renderPagesResponse } from "./pages-response-stage-entry.js";
|
|
10
10
|
import { registerConfiguredCacheAdapters } from "virtual:vinext-cache-adapters";
|
|
11
11
|
import { __ensureInstrumentation } from "virtual:vinext-pages-request-entry";
|
|
@@ -23,8 +23,9 @@ async function handleSingleStageRequest(request, env, platformCtx) {
|
|
|
23
23
|
if (readinessResponse.status === 204) await Promise.all([__ensureInstrumentation(), __ensureInstrumentation$1()]);
|
|
24
24
|
return await validateCdnRequest(request) ?? readinessResponse;
|
|
25
25
|
}
|
|
26
|
+
const isClientBuildFile = isPagesClientBuildFileRequest(request, env, ctx);
|
|
26
27
|
let finalize;
|
|
27
|
-
if (request.headers.has("X-Vinext-Cacheability-Probe")) {
|
|
28
|
+
if (!isClientBuildFile && request.headers.has("X-Vinext-Cacheability-Probe")) {
|
|
28
29
|
const cacheability = await import("./cacheability-request.js");
|
|
29
30
|
const probeContext = cacheability.createWorkerCacheabilityContext(ctx, request, pagesRequestStagePrerenderSecret, adapter.responseVary);
|
|
30
31
|
if (probeContext !== ctx) {
|
|
@@ -39,7 +40,7 @@ async function handleSingleStageRequest(request, env, platformCtx) {
|
|
|
39
40
|
}
|
|
40
41
|
}
|
|
41
42
|
const requiresCompletedResponseAdmission = adapter.requiresCompletedResponseAdmission === true;
|
|
42
|
-
if (!finalize && (__cacheabilityManifest || requiresCompletedResponseAdmission)) {
|
|
43
|
+
if (!finalize && !isClientBuildFile && (__cacheabilityManifest || requiresCompletedResponseAdmission)) {
|
|
43
44
|
const cacheability = await import("./cacheability-request.js");
|
|
44
45
|
const admissionContext = cacheability.createWorkerCacheabilityAdmissionContext(ctx, request, __cacheabilityManifest, pagesRequestStageBuildId, requiresCompletedResponseAdmission, adapter.responseVary);
|
|
45
46
|
if (admissionContext !== ctx) {
|
|
@@ -32,7 +32,6 @@ export type PrerenderManifest = {
|
|
|
32
32
|
pregeneratedConcretePaths?: Array<[string, string[]]>;
|
|
33
33
|
};
|
|
34
34
|
export type PrerenderedPathSelectionOptions = {
|
|
35
|
-
includeFallbackShells?: boolean;
|
|
36
35
|
includeErrorDocuments?: boolean;
|
|
37
36
|
};
|
|
38
37
|
export declare function readPrerenderManifest(manifestPath: string): PrerenderManifest | null;
|
|
@@ -58,8 +57,9 @@ export declare function buildPregeneratedConcretePathTable(manifest: PrerenderMa
|
|
|
58
57
|
* This intentionally includes both App Router and Pages Router entries because
|
|
59
58
|
* deploy-time cache warmup should exercise the same URLs the prerender phase
|
|
60
59
|
* proved are statically renderable. PPR fallback-shell placeholder artifacts
|
|
61
|
-
* and known error documents
|
|
62
|
-
* synthetic bracket paths or treat a healthy 404 response as a failed
|
|
60
|
+
* are always excluded, and known error documents by default, so warmup does not
|
|
61
|
+
* request synthetic bracket paths or treat a healthy 404 response as a failed
|
|
62
|
+
* warmup.
|
|
63
63
|
*/
|
|
64
64
|
export declare function getPrerenderedConcretePaths(manifest: PrerenderManifest, options?: PrerenderedPathSelectionOptions): string[];
|
|
65
65
|
//#endregion
|
|
@@ -65,8 +65,9 @@ function buildPregeneratedConcretePathTable(manifest) {
|
|
|
65
65
|
* This intentionally includes both App Router and Pages Router entries because
|
|
66
66
|
* deploy-time cache warmup should exercise the same URLs the prerender phase
|
|
67
67
|
* proved are statically renderable. PPR fallback-shell placeholder artifacts
|
|
68
|
-
* and known error documents
|
|
69
|
-
* synthetic bracket paths or treat a healthy 404 response as a failed
|
|
68
|
+
* are always excluded, and known error documents by default, so warmup does not
|
|
69
|
+
* request synthetic bracket paths or treat a healthy 404 response as a failed
|
|
70
|
+
* warmup.
|
|
70
71
|
*/
|
|
71
72
|
function getPrerenderedConcretePaths(manifest, options) {
|
|
72
73
|
const routes = manifest.routes;
|
|
@@ -76,7 +77,7 @@ function getPrerenderedConcretePaths(manifest, options) {
|
|
|
76
77
|
for (const route of routes) {
|
|
77
78
|
if (route.status !== "rendered" || route.notFound) continue;
|
|
78
79
|
const pathname = route.path ?? route.route;
|
|
79
|
-
if (
|
|
80
|
+
if (isFallbackShellArtifactPath(pathname, route)) continue;
|
|
80
81
|
if (!options?.includeErrorDocuments && isErrorDocumentRoute(pathname, route)) continue;
|
|
81
82
|
if (seen.has(pathname)) continue;
|
|
82
83
|
seen.add(pathname);
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { runInWorkStoreScope } from "../shims/internal/work-store-scope.js";
|
|
1
2
|
import { workUnitAsyncStorage } from "../shims/internal/work-unit-async-storage.js";
|
|
2
3
|
import { isRouteCacheabilityProbe } from "../shims/cacheability-classification.js";
|
|
3
4
|
import { NO_STORE_CACHE_CONTROL } from "./cache-control.js";
|
|
@@ -15,6 +16,9 @@ import { NO_STORE_CACHE_CONTROL } from "./cache-control.js";
|
|
|
15
16
|
*
|
|
16
17
|
*/
|
|
17
18
|
function runWithPrerenderWorkUnit(fn, options) {
|
|
19
|
+
return runInWorkStoreScope(() => runWithWorkUnit(fn, options));
|
|
20
|
+
}
|
|
21
|
+
function runWithWorkUnit(fn, options) {
|
|
18
22
|
if (process.env.VINEXT_PRERENDER === "1" || isRouteCacheabilityProbe()) {
|
|
19
23
|
if (options?.cacheComponents !== true) return workUnitAsyncStorage.run({ type: "prerender-legacy" }, fn);
|
|
20
24
|
return runWithPrerenderWorkUnitOwner(fn, options);
|
|
@@ -155,5 +155,17 @@ export declare function resolveAppRouterPrerenderSeeder(entryModule: unknown): A
|
|
|
155
155
|
* route through; the on-disk path is just `_next/static/...`.
|
|
156
156
|
*/
|
|
157
157
|
export declare function resolveAppRouterAssetPath(pathname: string, assetPathPrefix: string, assetPrefix: string): string | null;
|
|
158
|
+
/**
|
|
159
|
+
* Create the Pages Router request handler for `vite preview`. Vite's preview
|
|
160
|
+
* middleware (proxy, compression, `base`, static files) runs first, so this
|
|
161
|
+
* handles only the requests Vite doesn't serve and leaves compression to Vite.
|
|
162
|
+
* Resolves to null when the build has no Pages Router server entry.
|
|
163
|
+
*/
|
|
164
|
+
export declare function createPagesPreviewHandler(options: {
|
|
165
|
+
clientDir: string;
|
|
166
|
+
serverDir: string;
|
|
167
|
+
host: string;
|
|
168
|
+
port: number;
|
|
169
|
+
}): Promise<((req: IncomingMessage, res: ServerResponse) => void) | null>;
|
|
158
170
|
//#endregion
|
|
159
171
|
export { COMPRESS_THRESHOLD, isCompressibleContentType, mergeResponseHeaders, mergeWebResponse, negotiateEncoding, nodeToWebRequest, resolveRequestHost as resolveHost, sendCompressed, sendWebResponse, trustProxy, trustedHosts, tryServeStatic, waitForNodeResponseCompletion };
|
|
@@ -8,17 +8,17 @@ import { hasBasePath, stripBasePath } from "../utils/base-path.js";
|
|
|
8
8
|
import { VINEXT_PRERENDER_RENDER_ERROR_HEADER, VINEXT_PRERENDER_SPECIAL_ERROR_HEADER } from "./headers.js";
|
|
9
9
|
import { normalizePath } from "./normalize-path.js";
|
|
10
10
|
import { notFoundResponse } from "./http-error-responses.js";
|
|
11
|
-
import { isOpenRedirectShaped, sendRepeatedSlashRedirect } from "./open-redirect.js";
|
|
11
|
+
import { getRepeatedSlashRedirect, isOpenRedirectShaped, sendRepeatedSlashRedirect } from "./open-redirect.js";
|
|
12
12
|
import { readStaticFileSignal } from "./static-file-signal.js";
|
|
13
13
|
import { canonicalizeRequestPathname, filterInternalHeaders } from "./request-pipeline.js";
|
|
14
|
+
import { buildNextDataNotFoundResponse, encodeUrlParserIgnoredCharacters, isNextDataPathname, normalizeNextDataPagePathname, parseNextDataPathname, shouldAddTrailingSlashToPagesDataPath, urlParserCreatesPagesDataPath } from "./pages-data-route.js";
|
|
14
15
|
import { buildCacheStateHeaders } from "./cache-headers.js";
|
|
15
16
|
import { parseHttpDate } from "./http-date.js";
|
|
16
17
|
import { evaluateStaticPreconditions } from "./http-conditional.js";
|
|
17
|
-
import { buildNextDataNotFoundResponse, encodeUrlParserIgnoredCharacters, isNextDataPathname, normalizeNextDataPagePathname, parseNextDataPathname, shouldAddTrailingSlashToPagesDataPath, urlParserCreatesPagesDataPath } from "./pages-data-route.js";
|
|
18
18
|
import { setPagesClientAssets } from "./pages-client-assets.js";
|
|
19
19
|
import { resolveRequestHost, resolveRequestProtocol, trustProxy, trustedHosts } from "./proxy-trust.js";
|
|
20
20
|
import { signalFromNodeResponse } from "./node-response-signal.js";
|
|
21
|
-
import { DEFAULT_DEVICE_SIZES, DEFAULT_IMAGE_SIZES, IMAGE_RESPONSE_CACHE_STATE, isImageOptimizationPath, isSafeImageContentType, parseImageParams } from "./image-optimization.js";
|
|
21
|
+
import { DEFAULT_DEVICE_SIZES, DEFAULT_IMAGE_SIZES, IMAGE_RESPONSE_CACHE_STATE, INVALID_IMAGE_SOURCE_MESSAGE, isImageOptimizationPath, isImageSourceBodyStatus, isSafeImageContentType, parseImageParams, readImageOptimizationSignal } from "./image-optimization.js";
|
|
22
22
|
import { installSocketErrorBackstop } from "./socket-error-backstop.js";
|
|
23
23
|
import { collectInlineCssManifest } from "../build/inline-css.js";
|
|
24
24
|
import { readTrustedRevalidationHostname } from "./revalidation-host.js";
|
|
@@ -28,6 +28,7 @@ import { runPagesRequest, wrapMiddlewareWithBasePath } from "./pages-request-pip
|
|
|
28
28
|
import { computeClientRuntimeMetadata } from "../utils/client-runtime-metadata.js";
|
|
29
29
|
import { readPrerenderSecret, readServerCompress } from "../build/server-manifest.js";
|
|
30
30
|
import { readTrustedPrerenderRouteParamsFromHeaders, serializePrerenderRouteParamsHeader } from "./prerender-route-params.js";
|
|
31
|
+
import { resolveClientBuildFile } from "./client-build-files.js";
|
|
31
32
|
import { registerPrerenderCloudflareLoader } from "../build/prerender-cloudflare-loader.js";
|
|
32
33
|
import { seedMemoryCacheFromPrerender } from "./seed-cache.js";
|
|
33
34
|
import { negotiateEncoding, parseAcceptedEncodings, selectContentEncoding } from "./accept-encoding.js";
|
|
@@ -335,10 +336,15 @@ function installClientBuildManifestGlobals(clientDir, assetBase, assetPrefix, cr
|
|
|
335
336
|
function isNoBodyResponseStatus(status) {
|
|
336
337
|
return NO_BODY_RESPONSE_STATUSES.has(status);
|
|
337
338
|
}
|
|
338
|
-
|
|
339
|
+
/**
|
|
340
|
+
* Discard a response body nobody will read. When the client is gone, pass the
|
|
341
|
+
* request signal's reason: React reports a reasonless cancel of a pending
|
|
342
|
+
* render as a server error, but recognizes `ResponseAborted` as a disconnect.
|
|
343
|
+
*/
|
|
344
|
+
function cancelResponseBody(response, reason) {
|
|
339
345
|
const body = response.body;
|
|
340
346
|
if (!body || body.locked) return;
|
|
341
|
-
body.cancel().catch(() => {});
|
|
347
|
+
body.cancel(reason).catch(() => {});
|
|
342
348
|
}
|
|
343
349
|
/**
|
|
344
350
|
* Buffer a response body, cancelling it if the client disconnects first so a
|
|
@@ -433,6 +439,21 @@ function sendCompressed(req, res, body, contentType, statusCode, extraHeaders =
|
|
|
433
439
|
res.end(buf);
|
|
434
440
|
}
|
|
435
441
|
}
|
|
442
|
+
/** The StaticFileCache key for a request pathname, or null when it is never served. */
|
|
443
|
+
function staticCacheLookupPath(pathname) {
|
|
444
|
+
let lookupPath = pathname;
|
|
445
|
+
if (pathname.includes("%")) try {
|
|
446
|
+
lookupPath = decodeURIComponent(pathname);
|
|
447
|
+
} catch {
|
|
448
|
+
return null;
|
|
449
|
+
}
|
|
450
|
+
if (lookupPath.startsWith("/.vite/") || lookupPath === "/.vite") return null;
|
|
451
|
+
return lookupPath;
|
|
452
|
+
}
|
|
453
|
+
/** Pathnames the Pages Router serves from routes rather than the client directory. */
|
|
454
|
+
function isPagesFilesystemExcludedPath(pathname, phase) {
|
|
455
|
+
return pathname === "/" || pathname === "/api" || pathname.startsWith("/api/") || phase === "direct" && pathname.startsWith(`/_next/static/`);
|
|
456
|
+
}
|
|
436
457
|
/**
|
|
437
458
|
* Try to serve a static file from the client build directory.
|
|
438
459
|
*
|
|
@@ -456,18 +477,8 @@ async function tryServeStatic(req, res, clientDir, pathname, compress, cache, ex
|
|
|
456
477
|
const rawIfRange = req.headers["if-range"];
|
|
457
478
|
const ifRange = typeof rawIfRange === "string" ? rawIfRange : void 0;
|
|
458
479
|
if (cache) {
|
|
459
|
-
|
|
460
|
-
if (
|
|
461
|
-
try {
|
|
462
|
-
lookupPath = decodeURIComponent(pathname);
|
|
463
|
-
} catch {
|
|
464
|
-
return false;
|
|
465
|
-
}
|
|
466
|
-
if (lookupPath.startsWith("/.vite/") || lookupPath === "/.vite") return false;
|
|
467
|
-
} else {
|
|
468
|
-
if (pathname.startsWith("/.vite/") || pathname === "/.vite") return false;
|
|
469
|
-
lookupPath = pathname;
|
|
470
|
-
}
|
|
480
|
+
const lookupPath = staticCacheLookupPath(pathname);
|
|
481
|
+
if (lookupPath === null) return false;
|
|
471
482
|
const entry = cache.lookup(lookupPath);
|
|
472
483
|
if (!entry) return false;
|
|
473
484
|
if (req.method !== "GET" && req.method !== "HEAD") {
|
|
@@ -878,7 +889,7 @@ function waitForNodeResponseCompletion(res) {
|
|
|
878
889
|
* Pages Router (dist/server/entry.js) and configures the appropriate handler.
|
|
879
890
|
*/
|
|
880
891
|
async function startProdServer(options = {}) {
|
|
881
|
-
installSocketErrorBackstop();
|
|
892
|
+
installSocketErrorBackstop({ runningServer: options.purpose !== "prerender" });
|
|
882
893
|
const { port = process.env.PORT ? parseInt(process.env.PORT) : 3e3, host = "0.0.0.0", outDir = path.resolve("dist"), rscEntryPath: explicitRscEntryPath, serverDir: explicitServerDir, serverEntryPath: explicitServerEntryPath, noCompression = false, purpose, silent = false } = options;
|
|
883
894
|
if (purpose === "prerender") registerPrerenderCloudflareLoader();
|
|
884
895
|
const compress = !noCompression;
|
|
@@ -1110,6 +1121,59 @@ async function startAppRouterServer(options) {
|
|
|
1110
1121
|
const seededRoutes = await runWithServerEntryRequire(rscEntryRequire, () => seedPrerenderedRoutes(serverDir));
|
|
1111
1122
|
if (seededRoutes > 0) console.log(`[vinext] Seeded ${seededRoutes} pre-rendered route${seededRoutes !== 1 ? "s" : ""} into memory cache`);
|
|
1112
1123
|
const staticCache = await StaticFileCache.create(clientDir);
|
|
1124
|
+
const serveImageOptimization = async (req, res, request, signal) => {
|
|
1125
|
+
const stagedHeaders = omitHeadersCaseInsensitive(mergeResponseHeaders({}, new Response(null, { headers: signal.middlewareHeaders })), OMIT_STATIC_RESPONSE_HEADERS);
|
|
1126
|
+
for (const [name, value] of Object.entries(stagedHeaders)) res.setHeader(name, value);
|
|
1127
|
+
const parsedUrl = new URL(`/${signal.search}`, "http://localhost");
|
|
1128
|
+
const params = parseImageParams(parsedUrl, appImageAllowedWidths, imageConfig?.qualities);
|
|
1129
|
+
if (!params) {
|
|
1130
|
+
res.writeHead(400);
|
|
1131
|
+
res.end("Bad Request");
|
|
1132
|
+
return;
|
|
1133
|
+
}
|
|
1134
|
+
const sendUnsafeContentType = () => {
|
|
1135
|
+
res.writeHead(400);
|
|
1136
|
+
res.end("The requested resource is not an allowed image type");
|
|
1137
|
+
};
|
|
1138
|
+
const isSafeSourcePath = (sourcePath) => isSafeImageContentType(contentTypeForPath(sourcePath), imageConfig?.dangerouslyAllowSVG);
|
|
1139
|
+
const imageSecurityHeaders = {
|
|
1140
|
+
"Content-Security-Policy": imageConfig?.contentSecurityPolicy ?? "script-src 'none'; frame-src 'none'; sandbox;",
|
|
1141
|
+
"X-Content-Type-Options": "nosniff",
|
|
1142
|
+
"Content-Disposition": imageConfig?.contentDispositionType === "attachment" ? "attachment" : "inline"
|
|
1143
|
+
};
|
|
1144
|
+
const serveSource = (sourcePath) => tryServeStatic(req, res, clientDir, sourcePath, false, staticCache, imageSecurityHeaders, void 0, IMAGE_REPRESENTATION_HEADERS);
|
|
1145
|
+
const sourceUrl = new URL(params.imageUrl, request.url);
|
|
1146
|
+
let sourcePathname = null;
|
|
1147
|
+
try {
|
|
1148
|
+
sourcePathname = normalizePath(normalizePathnameForRouteMatchStrict(sourceUrl.pathname));
|
|
1149
|
+
} catch {}
|
|
1150
|
+
if (!isSafeSourcePath(sourcePathname ?? params.imageUrl)) {
|
|
1151
|
+
sendUnsafeContentType();
|
|
1152
|
+
return;
|
|
1153
|
+
}
|
|
1154
|
+
const assetLookupPath = sourcePathname !== null ? resolveAppRouterAssetPath(sourcePathname, appAssetPathPrefix, appRouterAssetPrefix) : null;
|
|
1155
|
+
if (assetLookupPath && await serveSource(assetLookupPath)) return;
|
|
1156
|
+
if (sourcePathname !== null) {
|
|
1157
|
+
const sourceResponse = await rscHandler(new Request(sourceUrl, { signal: request.signal }), createNodeExecutionContext(resolveTrustedNodeRevalidateOrigin(req, host, port)));
|
|
1158
|
+
const sourceSignal = isImageSourceBodyStatus(sourceResponse.status) ? readStaticFileSignal(sourceResponse) : null;
|
|
1159
|
+
cancelResponseBody(sourceResponse, request.signal.reason);
|
|
1160
|
+
if (request.signal.aborted) return;
|
|
1161
|
+
if (sourceSignal !== null) {
|
|
1162
|
+
let sourcePath = sourceSignal;
|
|
1163
|
+
try {
|
|
1164
|
+
sourcePath = decodeURIComponent(sourceSignal);
|
|
1165
|
+
} catch {}
|
|
1166
|
+
if (!isSafeSourcePath(sourcePath)) {
|
|
1167
|
+
sendUnsafeContentType();
|
|
1168
|
+
return;
|
|
1169
|
+
}
|
|
1170
|
+
if (await serveSource(sourcePath)) return;
|
|
1171
|
+
}
|
|
1172
|
+
}
|
|
1173
|
+
res.writeHead(400);
|
|
1174
|
+
res.end(INVALID_IMAGE_SOURCE_MESSAGE);
|
|
1175
|
+
};
|
|
1176
|
+
const clientBuildFiles = new Set(Array.isArray(rscModule.__clientBuildFiles) ? rscModule.__clientBuildFiles : []);
|
|
1113
1177
|
const handleRequestImpl = async (req, res) => {
|
|
1114
1178
|
const rawUrl = req.url ?? "/";
|
|
1115
1179
|
const rawPathname = rawUrl.split("?")[0];
|
|
@@ -1142,29 +1206,9 @@ async function startAppRouterServer(options) {
|
|
|
1142
1206
|
if (await tryServeStatic(req, res, clientDir, assetLookupPath, compress, staticCache)) return;
|
|
1143
1207
|
}
|
|
1144
1208
|
}
|
|
1145
|
-
if (
|
|
1146
|
-
const
|
|
1147
|
-
|
|
1148
|
-
if (!params) {
|
|
1149
|
-
res.writeHead(400);
|
|
1150
|
-
res.end("Bad Request");
|
|
1151
|
-
return;
|
|
1152
|
-
}
|
|
1153
|
-
const ct = contentTypeForPath(params.imageUrl);
|
|
1154
|
-
if (!isSafeImageContentType(ct, imageConfig?.dangerouslyAllowSVG)) {
|
|
1155
|
-
res.writeHead(400);
|
|
1156
|
-
res.end("The requested resource is not an allowed image type");
|
|
1157
|
-
return;
|
|
1158
|
-
}
|
|
1159
|
-
const imageSecurityHeaders = {
|
|
1160
|
-
"Content-Security-Policy": imageConfig?.contentSecurityPolicy ?? "script-src 'none'; frame-src 'none'; sandbox;",
|
|
1161
|
-
"X-Content-Type-Options": "nosniff",
|
|
1162
|
-
"Content-Disposition": imageConfig?.contentDispositionType === "attachment" ? "attachment" : "inline"
|
|
1163
|
-
};
|
|
1164
|
-
if (await tryServeStatic(req, res, clientDir, params.imageUrl, false, staticCache, imageSecurityHeaders, void 0, IMAGE_REPRESENTATION_HEADERS)) return;
|
|
1165
|
-
res.writeHead(404);
|
|
1166
|
-
res.end("Image not found");
|
|
1167
|
-
return;
|
|
1209
|
+
if (req.method === "GET" || req.method === "HEAD") {
|
|
1210
|
+
const clientBuildFile = resolveClientBuildFile(normalizedRawPathname, appRouterBasePath, clientBuildFiles);
|
|
1211
|
+
if (clientBuildFile && await tryServeStatic(req, res, clientDir, clientBuildFile, compress, staticCache)) return;
|
|
1168
1212
|
}
|
|
1169
1213
|
try {
|
|
1170
1214
|
const request = nodeToWebRequest(req, rawUrl, prerenderSecret, appRouterI18nConfig, appRouterAuthorizeOnDemandRevalidate, signalFromNodeResponse(res));
|
|
@@ -1175,7 +1219,13 @@ async function startAppRouterServer(options) {
|
|
|
1175
1219
|
};
|
|
1176
1220
|
const response = await rscHandler(request, ctx);
|
|
1177
1221
|
if (request.signal.aborted) {
|
|
1222
|
+
cancelResponseBody(response, request.signal.reason);
|
|
1223
|
+
return;
|
|
1224
|
+
}
|
|
1225
|
+
const imageSignal = readImageOptimizationSignal(response);
|
|
1226
|
+
if (imageSignal) {
|
|
1178
1227
|
cancelResponseBody(response);
|
|
1228
|
+
await serveImageOptimization(req, res, request, imageSignal);
|
|
1179
1229
|
return;
|
|
1180
1230
|
}
|
|
1181
1231
|
const staticFileSignal = readStaticFileSignal(response);
|
|
@@ -1270,7 +1320,45 @@ function readPagesServerEntryPageRoutes(value) {
|
|
|
1270
1320
|
* - vinextConfig — embedded next.config.js settings
|
|
1271
1321
|
*/
|
|
1272
1322
|
async function startPagesRouterServer(options) {
|
|
1273
|
-
const { port, host,
|
|
1323
|
+
const { port, host, purpose, silent } = options;
|
|
1324
|
+
const handler = await createPagesRouterRequestHandler(options);
|
|
1325
|
+
const server = createServer(handler);
|
|
1326
|
+
await new Promise((resolve) => {
|
|
1327
|
+
server.listen(port, host, () => {
|
|
1328
|
+
const addr = server.address();
|
|
1329
|
+
const actualPort = typeof addr === "object" && addr ? addr.port : port;
|
|
1330
|
+
if (!silent) logProdServerStarted(host, actualPort, purpose);
|
|
1331
|
+
resolve();
|
|
1332
|
+
});
|
|
1333
|
+
});
|
|
1334
|
+
const addr = server.address();
|
|
1335
|
+
return {
|
|
1336
|
+
server,
|
|
1337
|
+
port: typeof addr === "object" && addr ? addr.port : port
|
|
1338
|
+
};
|
|
1339
|
+
}
|
|
1340
|
+
/**
|
|
1341
|
+
* Create the Pages Router request handler for `vite preview`. Vite's preview
|
|
1342
|
+
* middleware (proxy, compression, `base`, static files) runs first, so this
|
|
1343
|
+
* handles only the requests Vite doesn't serve and leaves compression to Vite.
|
|
1344
|
+
* Resolves to null when the build has no Pages Router server entry.
|
|
1345
|
+
*/
|
|
1346
|
+
async function createPagesPreviewHandler(options) {
|
|
1347
|
+
const clientDir = path.resolve(options.clientDir);
|
|
1348
|
+
const serverDir = path.resolve(options.serverDir);
|
|
1349
|
+
const serverEntryPath = path.join(serverDir, "entry.js");
|
|
1350
|
+
if (!fs.existsSync(serverEntryPath)) return null;
|
|
1351
|
+
return createPagesRouterRequestHandler({
|
|
1352
|
+
port: options.port,
|
|
1353
|
+
host: options.host,
|
|
1354
|
+
clientDir,
|
|
1355
|
+
serverDir,
|
|
1356
|
+
serverEntryPath,
|
|
1357
|
+
compress: false
|
|
1358
|
+
});
|
|
1359
|
+
}
|
|
1360
|
+
async function createPagesRouterRequestHandler(options) {
|
|
1361
|
+
const { port, host, clientDir, serverDir, serverEntryPath, compress: compressOption, purpose } = options;
|
|
1274
1362
|
const serverEntry = await importServerEntryModule(serverEntryPath);
|
|
1275
1363
|
const serverEntryRequire = createServerEntryRequire(serverEntryPath);
|
|
1276
1364
|
const { renderPage, handleApiRoute: handleApi, authorizeOnDemandRevalidate, runMiddleware, vinextConfig, buildId: pagesBuildId } = serverEntry;
|
|
@@ -1286,6 +1374,7 @@ async function startPagesRouterServer(options) {
|
|
|
1286
1374
|
const pagesAssetPathPrefix = assetPrefixPathname(assetPrefix);
|
|
1287
1375
|
const assetBase = basePath ? `${basePath}/` : "/";
|
|
1288
1376
|
const trailingSlash = vinextConfig?.trailingSlash ?? false;
|
|
1377
|
+
const skipTrailingSlashRedirect = vinextConfig?.skipTrailingSlashRedirect ?? false;
|
|
1289
1378
|
const skipProxyUrlNormalize = vinextConfig?.skipProxyUrlNormalize ?? false;
|
|
1290
1379
|
const i18nConfig = vinextConfig?.i18n ?? null;
|
|
1291
1380
|
const configRedirects = vinextConfig?.redirects ?? [];
|
|
@@ -1445,7 +1534,8 @@ async function startPagesRouterServer(options) {
|
|
|
1445
1534
|
const method = req.method ?? "GET";
|
|
1446
1535
|
const signal = signalFromNodeResponse(res);
|
|
1447
1536
|
const hasBody = method !== "GET" && method !== "HEAD" && !signal.aborted;
|
|
1448
|
-
const
|
|
1537
|
+
const requestOrigin = `${protocol}://${revalidationHostname ?? hostHeader}`;
|
|
1538
|
+
const webRequest = new Request(new URL(url, requestOrigin), {
|
|
1449
1539
|
method,
|
|
1450
1540
|
headers: reqHeaders,
|
|
1451
1541
|
signal,
|
|
@@ -1453,10 +1543,44 @@ async function startPagesRouterServer(options) {
|
|
|
1453
1543
|
duplex: hasBody ? "half" : void 0
|
|
1454
1544
|
});
|
|
1455
1545
|
const middlewareRequest = isDataReq && skipProxyUrlNormalize ? new Request(new URL(originalRenderUrl, webRequest.url), webRequest) : void 0;
|
|
1546
|
+
const resolvePublicImageSource = async (sourceUrl) => {
|
|
1547
|
+
const rawSourcePathname = sourceUrl.pathname;
|
|
1548
|
+
if (getRepeatedSlashRedirect(rawSourcePathname) || isOpenRedirectShaped(rawSourcePathname)) return null;
|
|
1549
|
+
const sourceRequestPathname = normalizePath(rawSourcePathname);
|
|
1550
|
+
const sourceHadBasePath = !basePath || hasBasePath(sourceRequestPathname, basePath);
|
|
1551
|
+
const strippedSourcePathname = stripBasePath(sourceRequestPathname, basePath);
|
|
1552
|
+
if (isNextDataPathname(strippedSourcePathname)) return null;
|
|
1553
|
+
let sourcePath = null;
|
|
1554
|
+
const result = await runPagesRequest(new Request(new URL(strippedSourcePathname + sourceUrl.search, sourceUrl), { signal: webRequest.signal }), {
|
|
1555
|
+
...deps,
|
|
1556
|
+
hadBasePath: sourceHadBasePath,
|
|
1557
|
+
isDataReq: false,
|
|
1558
|
+
isDataRequest: false,
|
|
1559
|
+
middlewareRequest: void 0,
|
|
1560
|
+
dataNotFoundResponse: null,
|
|
1561
|
+
recordCacheability: false,
|
|
1562
|
+
rawSearch: sourceUrl.search,
|
|
1563
|
+
configMatchPathname: strippedSourcePathname,
|
|
1564
|
+
runMiddleware: typeof runMiddleware === "function" ? wrapMiddlewareWithBasePath(runMiddleware, basePath, sourceHadBasePath) : null,
|
|
1565
|
+
renderPage: null,
|
|
1566
|
+
handleApi: null,
|
|
1567
|
+
serveFilesystemRoute: async (candidate, _stagedHeaders, phase) => {
|
|
1568
|
+
if (isImageOptimizationPath(candidate) || isPagesFilesystemExcludedPath(candidate, phase)) return false;
|
|
1569
|
+
const lookupPath = staticCacheLookupPath(candidate);
|
|
1570
|
+
if (lookupPath === null || lookupPath === "/" || !staticCache.lookup(lookupPath)) return false;
|
|
1571
|
+
sourcePath = candidate;
|
|
1572
|
+
return new Response(null);
|
|
1573
|
+
}
|
|
1574
|
+
});
|
|
1575
|
+
if (result.type !== "response") return null;
|
|
1576
|
+
cancelResponseBody(result.response);
|
|
1577
|
+
return isImageSourceBodyStatus(result.response.status) ? sourcePath : null;
|
|
1578
|
+
};
|
|
1456
1579
|
const deps = {
|
|
1457
1580
|
assetPrefix,
|
|
1458
1581
|
basePath,
|
|
1459
1582
|
trailingSlash,
|
|
1583
|
+
skipTrailingSlashRedirect,
|
|
1460
1584
|
i18nConfig,
|
|
1461
1585
|
configRedirects,
|
|
1462
1586
|
configRewrites,
|
|
@@ -1479,8 +1603,8 @@ async function startPagesRouterServer(options) {
|
|
|
1479
1603
|
originalUrl: originalRenderUrl
|
|
1480
1604
|
}, stagedHeaders?.has("Cache-Control") ? new Headers({ "Cache-Control": stagedHeaders.get("Cache-Control") }) : void 0) : null,
|
|
1481
1605
|
handleApi: typeof handleApi === "function" ? (request, apiUrl) => handleApi(request, apiUrl, createNodeExecutionContext(), resolveTrustedNodeRevalidateOrigin(req, host, port), "node") : null,
|
|
1482
|
-
serveFilesystemRoute: async (requestPathname, stagedHeaders, phase, resolvedUrl) => {
|
|
1483
|
-
if (isImageOptimizationPath(requestPathname)) {
|
|
1606
|
+
serveFilesystemRoute: async (requestPathname, stagedHeaders, phase, resolvedUrl, _request, resolvedPathnameHasBasePath) => {
|
|
1607
|
+
if (resolvedPathnameHasBasePath && isImageOptimizationPath(requestPathname)) {
|
|
1484
1608
|
for (const [name, value] of Object.entries(stagedHeaders)) res.setHeader(name, value);
|
|
1485
1609
|
const parsedUrl = new URL(resolvedUrl, "http://localhost");
|
|
1486
1610
|
const params = parseImageParams(parsedUrl, allowedImageWidths, pagesImageConfig?.qualities);
|
|
@@ -1489,23 +1613,42 @@ async function startPagesRouterServer(options) {
|
|
|
1489
1613
|
res.end("Bad Request");
|
|
1490
1614
|
return true;
|
|
1491
1615
|
}
|
|
1492
|
-
const
|
|
1493
|
-
if (!isSafeImageContentType(ct, pagesImageConfig?.dangerouslyAllowSVG)) {
|
|
1616
|
+
const sendUnsafeContentType = () => {
|
|
1494
1617
|
res.writeHead(400);
|
|
1495
1618
|
res.end("The requested resource is not an allowed image type");
|
|
1496
|
-
|
|
1497
|
-
|
|
1619
|
+
};
|
|
1620
|
+
const isSafeSourcePath = (sourcePath) => isSafeImageContentType(contentTypeForPath(sourcePath), pagesImageConfig?.dangerouslyAllowSVG);
|
|
1498
1621
|
const imageSecurityHeaders = {
|
|
1499
1622
|
"Content-Security-Policy": pagesImageConfig?.contentSecurityPolicy ?? "script-src 'none'; frame-src 'none'; sandbox;",
|
|
1500
1623
|
"X-Content-Type-Options": "nosniff",
|
|
1501
1624
|
"Content-Disposition": pagesImageConfig?.contentDispositionType === "attachment" ? "attachment" : "inline"
|
|
1502
1625
|
};
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1626
|
+
const serveSource = (sourcePath) => tryServeStatic(req, res, clientDir, sourcePath, false, staticCache, imageSecurityHeaders, void 0, IMAGE_REPRESENTATION_HEADERS);
|
|
1627
|
+
const sourceUrl = new URL(params.imageUrl, webRequest.url);
|
|
1628
|
+
let sourcePathname = null;
|
|
1629
|
+
try {
|
|
1630
|
+
sourcePathname = normalizePath(normalizePathnameForRouteMatchStrict(sourceUrl.pathname));
|
|
1631
|
+
} catch {}
|
|
1632
|
+
if (!isSafeSourcePath(sourcePathname ?? params.imageUrl)) {
|
|
1633
|
+
sendUnsafeContentType();
|
|
1634
|
+
return true;
|
|
1635
|
+
}
|
|
1636
|
+
const assetLookupPath = sourcePathname !== null ? resolveAppRouterAssetPath(sourcePathname, pagesAssetPathPrefix, assetPrefix) : null;
|
|
1637
|
+
if (assetLookupPath && await serveSource(assetLookupPath)) return true;
|
|
1638
|
+
const sourcePath = sourcePathname !== null ? await resolvePublicImageSource(sourceUrl) : null;
|
|
1639
|
+
if (webRequest.signal.aborted) return true;
|
|
1640
|
+
if (sourcePath !== null) {
|
|
1641
|
+
if (!isSafeSourcePath(staticCacheLookupPath(sourcePath) ?? sourcePath)) {
|
|
1642
|
+
sendUnsafeContentType();
|
|
1643
|
+
return true;
|
|
1644
|
+
}
|
|
1645
|
+
if (await serveSource(sourcePath)) return true;
|
|
1646
|
+
}
|
|
1647
|
+
res.writeHead(400);
|
|
1648
|
+
res.end(INVALID_IMAGE_SOURCE_MESSAGE);
|
|
1506
1649
|
return true;
|
|
1507
1650
|
}
|
|
1508
|
-
if (requestPathname
|
|
1651
|
+
if (isPagesFilesystemExcludedPath(requestPathname, phase)) return false;
|
|
1509
1652
|
return tryServeStatic(req, res, clientDir, requestPathname, compress, staticCache, stagedHeaders);
|
|
1510
1653
|
}
|
|
1511
1654
|
};
|
|
@@ -1514,7 +1657,7 @@ async function startPagesRouterServer(options) {
|
|
|
1514
1657
|
if (result.type === "response") {
|
|
1515
1658
|
const { response } = result;
|
|
1516
1659
|
if (webRequest.signal.aborted) {
|
|
1517
|
-
cancelResponseBody(response);
|
|
1660
|
+
cancelResponseBody(response, webRequest.signal.reason);
|
|
1518
1661
|
return;
|
|
1519
1662
|
}
|
|
1520
1663
|
const streamedApi = isVinextStreamedApiResponse(response);
|
|
@@ -1548,22 +1691,9 @@ async function startPagesRouterServer(options) {
|
|
|
1548
1691
|
}
|
|
1549
1692
|
}
|
|
1550
1693
|
};
|
|
1551
|
-
|
|
1694
|
+
return (req, res) => {
|
|
1552
1695
|
runWithServerEntryRequire(serverEntryRequire, () => handleRequest(req, res));
|
|
1553
|
-
});
|
|
1554
|
-
await new Promise((resolve) => {
|
|
1555
|
-
server.listen(port, host, () => {
|
|
1556
|
-
const addr = server.address();
|
|
1557
|
-
const actualPort = typeof addr === "object" && addr ? addr.port : port;
|
|
1558
|
-
if (!silent) logProdServerStarted(host, actualPort, purpose);
|
|
1559
|
-
resolve();
|
|
1560
|
-
});
|
|
1561
|
-
});
|
|
1562
|
-
const addr = server.address();
|
|
1563
|
-
return {
|
|
1564
|
-
server,
|
|
1565
|
-
port: typeof addr === "object" && addr ? addr.port : port
|
|
1566
1696
|
};
|
|
1567
1697
|
}
|
|
1568
1698
|
//#endregion
|
|
1569
|
-
export { COMPRESS_THRESHOLD, importServerEntryModule, isCompressibleContentType, mergeResponseHeaders, mergeWebResponse, negotiateEncoding, nodeToWebRequest, readNodeStream, rememberCurrentServerEntryImportMtime, resolveAppRouterAssetPath, resolveAppRouterPrerenderSeeder, resolveRequestHost as resolveHost, resolveServerEntryImportUrl, sendCompressed, sendWebResponse, startProdServer, trustProxy, trustedHosts, tryServeStatic, waitForNodeResponseCompletion };
|
|
1699
|
+
export { COMPRESS_THRESHOLD, createPagesPreviewHandler, importServerEntryModule, isCompressibleContentType, mergeResponseHeaders, mergeWebResponse, negotiateEncoding, nodeToWebRequest, readNodeStream, rememberCurrentServerEntryImportMtime, resolveAppRouterAssetPath, resolveAppRouterPrerenderSeeder, resolveRequestHost as resolveHost, resolveServerEntryImportUrl, sendCompressed, sendWebResponse, startProdServer, trustProxy, trustedHosts, tryServeStatic, waitForNodeResponseCompletion };
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
//#region src/server/proper-error.ts
|
|
2
|
+
function isPlainObject(value) {
|
|
3
|
+
if (Object.prototype.toString.call(value) !== "[object Object]") return false;
|
|
4
|
+
const prototype = Object.getPrototypeOf(value);
|
|
5
|
+
return prototype === null || Object.prototype.hasOwnProperty.call(prototype, "isPrototypeOf");
|
|
6
|
+
}
|
|
7
|
+
/** JSON.stringify that replaces circular references with "[Circular]". */
|
|
8
|
+
function safeStringifyLite(value) {
|
|
9
|
+
const seen = /* @__PURE__ */ new WeakSet();
|
|
10
|
+
return JSON.stringify(value, (_key, nested) => {
|
|
11
|
+
if (typeof nested === "object" && nested !== null) {
|
|
12
|
+
if (seen.has(nested)) return "[Circular]";
|
|
13
|
+
seen.add(nested);
|
|
14
|
+
}
|
|
15
|
+
return nested;
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
function isError(value) {
|
|
19
|
+
return typeof value === "object" && value !== null && "name" in value && "message" in value;
|
|
20
|
+
}
|
|
21
|
+
/** Turn a thrown value into an Error, as Next.js does before reporting it. */
|
|
22
|
+
function getProperError(error) {
|
|
23
|
+
if (isError(error)) return error;
|
|
24
|
+
if (process.env.NODE_ENV === "development") {
|
|
25
|
+
if (typeof error === "undefined") return /* @__PURE__ */ new Error("An undefined error was thrown, see here for more info: https://nextjs.org/docs/messages/threw-undefined");
|
|
26
|
+
if (error === null) return /* @__PURE__ */ new Error("A null error was thrown, see here for more info: https://nextjs.org/docs/messages/threw-undefined");
|
|
27
|
+
}
|
|
28
|
+
return new Error(isPlainObject(error) ? safeStringifyLite(error) : String(error));
|
|
29
|
+
}
|
|
30
|
+
//#endregion
|
|
31
|
+
export { getProperError };
|
|
@@ -69,7 +69,10 @@ export declare function guardProtocolRelativeUrl(rawPathname: string, search?: s
|
|
|
69
69
|
export type HeaderRecord = Record<string, string | string[]>;
|
|
70
70
|
type ResolvePublicFileRouteOptions = {
|
|
71
71
|
cleanPathname: string;
|
|
72
|
-
|
|
72
|
+
/** `requestHeaders` holds middleware's request-header override instructions. */
|
|
73
|
+
middlewareContext: StaticFileSignalContext & {
|
|
74
|
+
requestHeaders?: Headers | null;
|
|
75
|
+
};
|
|
73
76
|
pathname: string;
|
|
74
77
|
publicFiles: ReadonlySet<string>;
|
|
75
78
|
request: Request;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import "../utils/protocol-headers.js";
|
|
2
|
-
import { getUnconsumedMiddlewareRequestHeaders } from "../utils/middleware-request-headers.js";
|
|
2
|
+
import { buildRequestHeadersFromMiddlewareResponse, getUnconsumedMiddlewareRequestHeaders } from "../utils/middleware-request-headers.js";
|
|
3
3
|
import { hasBasePath, removeTrailingSlash, stripBasePath } from "../utils/base-path.js";
|
|
4
4
|
import { INTERNAL_HEADERS, VINEXT_INTERNAL_HEADERS } from "./headers.js";
|
|
5
5
|
import { forbiddenResponse, methodNotAllowedResponse, notFoundResponse } from "./http-error-responses.js";
|
|
@@ -100,7 +100,8 @@ function resolvePublicFileRoute(options) {
|
|
|
100
100
|
if (options.pathname.endsWith(".rsc")) return null;
|
|
101
101
|
if (!options.publicFiles.has(options.cleanPathname)) return null;
|
|
102
102
|
if (options.request.method !== "GET" && options.request.method !== "HEAD") return methodNotAllowedResponse("GET, HEAD", { headers: options.middlewareContext.headers ?? void 0 });
|
|
103
|
-
|
|
103
|
+
const { requestHeaders } = options.middlewareContext;
|
|
104
|
+
return createStaticFileSignal(options.cleanPathname, options.middlewareContext, requestHeaders ? buildRequestHeadersFromMiddlewareResponse(options.request.headers, requestHeaders) : null);
|
|
104
105
|
}
|
|
105
106
|
function normalizeTrailingSlashPathname(pathname, trailingSlash) {
|
|
106
107
|
if (pathname === "/" || pathname === "/api" || pathname.startsWith("/api/")) return null;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { PRERENDER_REVALIDATE_HEADER } from "../utils/protocol-headers.js";
|
|
2
2
|
//#region src/server/revalidation-request.d.ts
|
|
3
3
|
export declare function getRevalidateSecret(): string;
|
|
4
|
+
export declare function safeEqual(first: string, second: string): boolean;
|
|
4
5
|
export declare function isRevalidateSecret(value: string | null | undefined): boolean;
|
|
5
6
|
/** Match Next.js: the header value must equal the preview/revalidation secret. */
|
|
6
7
|
export declare function isOnDemandRevalidateRequest(headerValue: string | string[] | null | undefined): boolean;
|
|
@@ -34,4 +34,4 @@ function isOnDemandRevalidateRequest(headerValue) {
|
|
|
34
34
|
return typeof headerValue === "string" && isRevalidateSecret(headerValue);
|
|
35
35
|
}
|
|
36
36
|
//#endregion
|
|
37
|
-
export { PRERENDER_REVALIDATE_HEADER, getRevalidateSecret, isOnDemandRevalidateRequest, isRevalidateSecret };
|
|
37
|
+
export { PRERENDER_REVALIDATE_HEADER, getRevalidateSecret, isOnDemandRevalidateRequest, isRevalidateSecret, safeEqual };
|