vinext 1.0.0-beta.2 → 1.0.0-beta.4
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 +6 -9
- package/dist/build/client-build-config.js +7 -1
- package/dist/build/precompress.js +1 -1
- package/dist/build/prerender.d.ts +2 -1
- package/dist/build/prerender.js +12 -2
- package/dist/check.js +4 -0
- package/dist/cli-args.d.ts +2 -1
- package/dist/cli-args.js +10 -0
- package/dist/cli.js +14 -8
- package/dist/client/navigation-runtime.d.ts +6 -0
- package/dist/client/navigation-runtime.js +1 -1
- package/dist/config/config-matchers.js +4 -1
- package/dist/config/dotenv.d.ts +3 -4
- package/dist/config/dotenv.js +30 -22
- package/dist/config/next-config.d.ts +16 -5
- package/dist/config/next-config.js +7 -0
- package/dist/entries/app-browser-entry.d.ts +4 -10
- package/dist/entries/app-browser-entry.js +20 -4
- package/dist/entries/app-rsc-entry.js +21 -3
- package/dist/entries/app-rsc-manifest.js +19 -0
- package/dist/entries/pages-client-entry.js +0 -1
- package/dist/entries/pages-server-entry.js +13 -3
- package/dist/index.js +122 -49
- package/dist/init-cloudflare.d.ts +3 -1
- package/dist/init-cloudflare.js +59 -5
- package/dist/plugins/client-reference-dedup.js +7 -5
- package/dist/plugins/extensionless-dynamic-import.js +23 -18
- package/dist/plugins/ignore-dynamic-requests.js +3 -1
- package/dist/plugins/import-meta-url.js +17 -6
- package/dist/plugins/require-context.js +20 -15
- package/dist/plugins/sass.js +3 -4
- package/dist/plugins/transform-cache.d.ts +19 -0
- package/dist/plugins/transform-cache.js +36 -0
- package/dist/routing/app-route-graph.d.ts +14 -6
- package/dist/routing/app-route-graph.js +106 -16
- package/dist/server/api-handler.d.ts +2 -0
- package/dist/server/api-handler.js +7 -6
- package/dist/server/app-bfcache-identity.js +28 -2
- package/dist/server/app-browser-entry.js +175 -75
- package/dist/server/app-browser-navigation-controller.d.ts +23 -21
- package/dist/server/app-browser-navigation-controller.js +30 -5
- package/dist/server/app-browser-prefetch-response.d.ts +9 -0
- package/dist/server/app-browser-prefetch-response.js +32 -0
- package/dist/server/app-browser-state.d.ts +8 -3
- package/dist/server/app-browser-state.js +9 -3
- package/dist/server/app-page-cache-finalizer.js +1 -1
- package/dist/server/app-page-cache.d.ts +1 -1
- package/dist/server/app-page-cache.js +16 -2
- package/dist/server/app-page-dispatch.d.ts +9 -1
- package/dist/server/app-page-dispatch.js +37 -8
- package/dist/server/app-page-element-builder.d.ts +4 -1
- package/dist/server/app-page-element-builder.js +34 -7
- package/dist/server/app-page-head.d.ts +3 -3
- package/dist/server/app-page-head.js +58 -13
- package/dist/server/app-page-http-access-fallback-metadata.d.ts +2 -2
- package/dist/server/app-page-http-access-fallback-metadata.js +9 -1
- package/dist/server/app-page-probe.d.ts +4 -0
- package/dist/server/app-page-probe.js +6 -2
- package/dist/server/app-page-render.d.ts +1 -1
- package/dist/server/app-page-render.js +13 -3
- package/dist/server/app-page-request.d.ts +3 -1
- package/dist/server/app-page-request.js +4 -13
- package/dist/server/app-page-response.d.ts +2 -0
- package/dist/server/app-page-response.js +7 -2
- package/dist/server/app-page-route-wiring.d.ts +10 -2
- package/dist/server/app-page-route-wiring.js +269 -59
- package/dist/server/app-pages-bridge.d.ts +2 -1
- package/dist/server/app-pages-bridge.js +3 -1
- package/dist/server/app-route-handler-cache.js +4 -0
- package/dist/server/app-route-handler-dispatch.js +9 -3
- package/dist/server/app-route-handler-response.js +1 -1
- package/dist/server/app-route-module-loader.d.ts +6 -0
- package/dist/server/app-route-module-loader.js +3 -0
- package/dist/server/app-router-entry.js +6 -3
- package/dist/server/app-rsc-cache-busting.d.ts +7 -0
- package/dist/server/app-rsc-cache-busting.js +6 -0
- package/dist/server/app-rsc-errors.js +10 -0
- package/dist/server/app-rsc-handler.d.ts +2 -0
- package/dist/server/app-rsc-handler.js +30 -13
- package/dist/server/app-rsc-render-mode.d.ts +3 -2
- package/dist/server/app-rsc-render-mode.js +4 -1
- package/dist/server/app-rsc-request-normalization.js +65 -4
- package/dist/server/app-rsc-route-matching.d.ts +9 -0
- package/dist/server/app-rsc-route-matching.js +6 -0
- package/dist/server/app-server-action-execution.d.ts +1 -1
- package/dist/server/app-ssr-entry.js +4 -4
- package/dist/server/app-visited-response-cache.d.ts +6 -1
- package/dist/server/app-visited-response-cache.js +49 -1
- package/dist/server/cache-control.d.ts +2 -2
- package/dist/server/cache-control.js +2 -1
- package/dist/server/dev-server.d.ts +3 -1
- package/dist/server/dev-server.js +204 -348
- package/dist/server/edge-api-runtime.d.ts +10 -1
- package/dist/server/edge-api-runtime.js +29 -1
- package/dist/server/headers.d.ts +4 -2
- package/dist/server/headers.js +6 -3
- package/dist/server/isr-cache.d.ts +10 -33
- package/dist/server/isr-cache.js +33 -43
- package/dist/server/isr-decision.d.ts +2 -2
- package/dist/server/pages-api-route.d.ts +4 -0
- package/dist/server/pages-api-route.js +6 -3
- package/dist/server/pages-asset-tags.d.ts +1 -0
- package/dist/server/pages-asset-tags.js +6 -4
- package/dist/server/pages-dev-hydration.js +0 -1
- package/dist/server/pages-document-asset-props.d.ts +26 -0
- package/dist/server/pages-document-asset-props.js +69 -0
- package/dist/server/pages-get-initial-props.d.ts +1 -1
- package/dist/server/pages-get-initial-props.js +1 -4
- package/dist/server/pages-i18n.js +64 -16
- package/dist/server/pages-node-compat.d.ts +2 -0
- package/dist/server/pages-node-compat.js +10 -6
- package/dist/server/pages-page-data.d.ts +61 -9
- package/dist/server/pages-page-data.js +334 -74
- package/dist/server/pages-page-handler.d.ts +6 -1
- package/dist/server/pages-page-handler.js +119 -20
- package/dist/server/pages-page-response.d.ts +7 -3
- package/dist/server/pages-page-response.js +41 -33
- package/dist/server/pages-request-pipeline.d.ts +7 -0
- package/dist/server/pages-request-pipeline.js +5 -1
- package/dist/server/pages-revalidate.d.ts +1 -1
- package/dist/server/pages-revalidate.js +54 -8
- package/dist/server/pages-router-entry.d.ts +1 -0
- package/dist/server/pages-router-entry.js +10 -4
- package/dist/server/prerender-manifest.d.ts +1 -0
- package/dist/server/prod-server.d.ts +2 -1
- package/dist/server/prod-server.js +38 -13
- package/dist/server/revalidation-host.d.ts +11 -0
- package/dist/server/revalidation-host.js +19 -0
- package/dist/server/seed-cache.js +1 -1
- package/dist/server/worker-revalidation-context.d.ts +13 -0
- package/dist/server/worker-revalidation-context.js +30 -0
- package/dist/shims/app-router-scroll-state.d.ts +2 -1
- package/dist/shims/app-router-scroll-state.js +7 -2
- package/dist/shims/cache-handler.d.ts +1 -1
- package/dist/shims/cache-handler.js +10 -7
- package/dist/shims/cache-runtime.d.ts +6 -0
- package/dist/shims/cache-runtime.js +4 -1
- package/dist/shims/cache.js +1 -1
- package/dist/shims/document.js +15 -2
- package/dist/shims/error.js +1 -1
- package/dist/shims/fetch-cache.js +1 -1
- package/dist/shims/font-google-base.js +17 -8
- package/dist/shims/font-local.js +41 -10
- package/dist/shims/hash-scroll.d.ts +1 -1
- package/dist/shims/hash-scroll.js +2 -1
- package/dist/shims/headers.d.ts +10 -13
- package/dist/shims/headers.js +85 -36
- package/dist/shims/internal/cookie-serialize.d.ts +10 -9
- package/dist/shims/internal/cookie-serialize.js +9 -8
- package/dist/shims/internal/interpolate-as.d.ts +15 -1
- package/dist/shims/internal/interpolate-as.js +27 -2
- package/dist/shims/link.d.ts +2 -0
- package/dist/shims/link.js +77 -24
- package/dist/shims/metadata.d.ts +18 -34
- package/dist/shims/metadata.js +98 -56
- package/dist/shims/navigation.d.ts +18 -2
- package/dist/shims/navigation.js +70 -21
- package/dist/shims/request-context.d.ts +6 -2
- package/dist/shims/router.js +18 -7
- package/dist/shims/script.js +1 -1
- package/dist/shims/server.d.ts +7 -6
- package/dist/shims/server.js +156 -57
- package/dist/shims/slot.d.ts +2 -1
- package/dist/shims/slot.js +6 -2
- package/dist/shims/streamed-icons.d.ts +9 -0
- package/dist/shims/streamed-icons.js +33 -0
- package/dist/shims/unified-request-context.d.ts +21 -2
- package/dist/shims/unified-request-context.js +82 -1
- package/dist/utils/cache-control-metadata.d.ts +2 -1
- package/dist/utils/cache-control-metadata.js +5 -1
- package/dist/utils/html-limited-bots.js +1 -1
- package/dist/utils/protocol-headers.d.ts +7 -1
- package/dist/utils/protocol-headers.js +7 -1
- package/package.json +1 -1
|
@@ -1,4 +1,13 @@
|
|
|
1
1
|
//#region src/server/edge-api-runtime.d.ts
|
|
2
2
|
declare function isEdgeApiRuntime(runtime: string | undefined): boolean;
|
|
3
|
+
type EdgeApiExecutionRuntime = "node" | "worker";
|
|
4
|
+
/**
|
|
5
|
+
* Remove wire-level body headers after Node's fetch implementation has decoded
|
|
6
|
+
* an Edge API response. Next.js applies the same boundary in its edge sandbox.
|
|
7
|
+
*
|
|
8
|
+
* Workers keep the original response untouched: workerd preserves the encoded
|
|
9
|
+
* body/header pair and can stream it directly to the client.
|
|
10
|
+
*/
|
|
11
|
+
declare function finalizeEdgeApiResponse(response: Response, runtime: EdgeApiExecutionRuntime): Response;
|
|
3
12
|
//#endregion
|
|
4
|
-
export { isEdgeApiRuntime };
|
|
13
|
+
export { EdgeApiExecutionRuntime, finalizeEdgeApiResponse, isEdgeApiRuntime };
|
|
@@ -2,5 +2,33 @@
|
|
|
2
2
|
function isEdgeApiRuntime(runtime) {
|
|
3
3
|
return runtime === "edge" || runtime === "experimental-edge";
|
|
4
4
|
}
|
|
5
|
+
const NODE_EDGE_RESPONSE_FORBIDDEN_HEADERS = [
|
|
6
|
+
"content-length",
|
|
7
|
+
"content-encoding",
|
|
8
|
+
"transfer-encoding"
|
|
9
|
+
];
|
|
10
|
+
/**
|
|
11
|
+
* Remove wire-level body headers after Node's fetch implementation has decoded
|
|
12
|
+
* an Edge API response. Next.js applies the same boundary in its edge sandbox.
|
|
13
|
+
*
|
|
14
|
+
* Workers keep the original response untouched: workerd preserves the encoded
|
|
15
|
+
* body/header pair and can stream it directly to the client.
|
|
16
|
+
*/
|
|
17
|
+
function finalizeEdgeApiResponse(response, runtime) {
|
|
18
|
+
if (runtime === "worker") return response;
|
|
19
|
+
const headers = new Headers(response.headers);
|
|
20
|
+
let changed = false;
|
|
21
|
+
for (const name of NODE_EDGE_RESPONSE_FORBIDDEN_HEADERS) {
|
|
22
|
+
if (!headers.has(name)) continue;
|
|
23
|
+
headers.delete(name);
|
|
24
|
+
changed = true;
|
|
25
|
+
}
|
|
26
|
+
if (!changed) return response;
|
|
27
|
+
return new Response(response.body, {
|
|
28
|
+
headers,
|
|
29
|
+
status: response.status,
|
|
30
|
+
statusText: response.statusText
|
|
31
|
+
});
|
|
32
|
+
}
|
|
5
33
|
//#endregion
|
|
6
|
-
export { isEdgeApiRuntime };
|
|
34
|
+
export { finalizeEdgeApiResponse, isEdgeApiRuntime };
|
package/dist/server/headers.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { MIDDLEWARE_HEADER_PREFIX, MIDDLEWARE_SET_COOKIE_HEADER, MIDDLEWARE_SKIP_HEADER, VINEXT_MW_CTX_HEADER, VINEXT_PRERENDER_ROUTE_PARAMS_HEADER, VINEXT_PRERENDER_SECRET_HEADER, VINEXT_PRERENDER_SPECULATIVE_HEADER } from "../utils/protocol-headers.js";
|
|
1
|
+
import { MIDDLEWARE_HEADER_PREFIX, MIDDLEWARE_SET_COOKIE_HEADER, MIDDLEWARE_SKIP_HEADER, VINEXT_MW_CTX_HEADER, VINEXT_PRERENDER_ROUTE_PARAMS_HEADER, VINEXT_PRERENDER_SECRET_HEADER, VINEXT_PRERENDER_SPECULATIVE_HEADER, VINEXT_REVALIDATE_HOST_HEADER } from "../utils/protocol-headers.js";
|
|
2
2
|
|
|
3
3
|
//#region src/server/headers.d.ts
|
|
4
4
|
/**
|
|
@@ -15,6 +15,8 @@ import { MIDDLEWARE_HEADER_PREFIX, MIDDLEWARE_SET_COOKIE_HEADER, MIDDLEWARE_SKIP
|
|
|
15
15
|
declare const VINEXT_CACHE_HEADER = "X-Vinext-Cache";
|
|
16
16
|
/** Next.js public ISR / page cache state indicator. */
|
|
17
17
|
declare const NEXTJS_CACHE_HEADER = "x-nextjs-cache";
|
|
18
|
+
/** Next.js cache-tag metadata carried by prerendered App Router responses. */
|
|
19
|
+
declare const NEXT_CACHE_TAGS_HEADER = "x-next-cache-tags";
|
|
18
20
|
/** Static file signal — value is URL-encoded pathname. */
|
|
19
21
|
declare const VINEXT_STATIC_FILE_HEADER = "x-vinext-static-file";
|
|
20
22
|
/** Timing metrics: `handlerStart,compileMs,renderMs`. */
|
|
@@ -107,4 +109,4 @@ declare const INTERNAL_HEADERS: string[];
|
|
|
107
109
|
/** Vinext-only internal headers stripped alongside Next.js protocol internals. */
|
|
108
110
|
declare const VINEXT_INTERNAL_HEADERS: string[];
|
|
109
111
|
//#endregion
|
|
110
|
-
export { ACTION_FORWARDED_HEADER, ACTION_REDIRECT_HEADER, ACTION_REDIRECT_STATUS_HEADER, ACTION_REDIRECT_TYPE_HEADER, ACTION_REVALIDATED_HEADER, FLIGHT_HEADERS, INTERNAL_HEADERS, MIDDLEWARE_HEADER_PREFIX, MIDDLEWARE_NEXT_HEADER, MIDDLEWARE_REWRITE_HEADER, MIDDLEWARE_SET_COOKIE_HEADER, MIDDLEWARE_SKIP_HEADER, NEXTJS_ACTION_NOT_FOUND_HEADER, NEXTJS_CACHE_HEADER, NEXTJS_DEPLOYMENT_ID_HEADER, NEXT_ACTION_HEADER, NEXT_HTML_REQUEST_ID_HEADER, NEXT_REQUEST_ID_HEADER, NEXT_ROUTER_PREFETCH_HEADER, NEXT_ROUTER_SEGMENT_PREFETCH_HEADER, NEXT_ROUTER_STATE_TREE_HEADER, NEXT_URL_HEADER, RSC_ACTION_HEADER, RSC_HEADER, VINEXT_CACHE_HEADER, VINEXT_CLIENT_REUSE_MANIFEST_HEADER, VINEXT_DYNAMIC_STALE_TIME_HEADER, VINEXT_INTERCEPTION_CONTEXT_HEADER, VINEXT_INTERNAL_HEADERS, VINEXT_MOUNTED_SLOTS_HEADER, VINEXT_MW_CTX_HEADER, VINEXT_PARAMS_HEADER, VINEXT_PRERENDER_CACHE_LIFE_HEADER, VINEXT_PRERENDER_PAGES_STATIC_PATHS_PATH, VINEXT_PRERENDER_ROUTE_PARAMS_HEADER, VINEXT_PRERENDER_SECRET_HEADER, VINEXT_PRERENDER_SPECULATIVE_HEADER, VINEXT_PRERENDER_STATIC_PARAMS_PATH, VINEXT_RENDERED_PATH_AND_SEARCH_HEADER, VINEXT_REVALIDATE_HEADER, VINEXT_RSC_MARKER_HEADER, VINEXT_RSC_REDIRECT_HEADER, VINEXT_RSC_REDIRECT_TYPE_HEADER, VINEXT_RSC_RENDER_MODE_HEADER, VINEXT_STATIC_FILE_HEADER, VINEXT_TIMING_HEADER };
|
|
112
|
+
export { ACTION_FORWARDED_HEADER, ACTION_REDIRECT_HEADER, ACTION_REDIRECT_STATUS_HEADER, ACTION_REDIRECT_TYPE_HEADER, ACTION_REVALIDATED_HEADER, FLIGHT_HEADERS, INTERNAL_HEADERS, MIDDLEWARE_HEADER_PREFIX, MIDDLEWARE_NEXT_HEADER, MIDDLEWARE_REWRITE_HEADER, MIDDLEWARE_SET_COOKIE_HEADER, MIDDLEWARE_SKIP_HEADER, NEXTJS_ACTION_NOT_FOUND_HEADER, NEXTJS_CACHE_HEADER, NEXTJS_DEPLOYMENT_ID_HEADER, NEXT_ACTION_HEADER, NEXT_CACHE_TAGS_HEADER, NEXT_HTML_REQUEST_ID_HEADER, NEXT_REQUEST_ID_HEADER, NEXT_ROUTER_PREFETCH_HEADER, NEXT_ROUTER_SEGMENT_PREFETCH_HEADER, NEXT_ROUTER_STATE_TREE_HEADER, NEXT_URL_HEADER, RSC_ACTION_HEADER, RSC_HEADER, VINEXT_CACHE_HEADER, VINEXT_CLIENT_REUSE_MANIFEST_HEADER, VINEXT_DYNAMIC_STALE_TIME_HEADER, VINEXT_INTERCEPTION_CONTEXT_HEADER, VINEXT_INTERNAL_HEADERS, VINEXT_MOUNTED_SLOTS_HEADER, VINEXT_MW_CTX_HEADER, VINEXT_PARAMS_HEADER, VINEXT_PRERENDER_CACHE_LIFE_HEADER, VINEXT_PRERENDER_PAGES_STATIC_PATHS_PATH, VINEXT_PRERENDER_ROUTE_PARAMS_HEADER, VINEXT_PRERENDER_SECRET_HEADER, VINEXT_PRERENDER_SPECULATIVE_HEADER, VINEXT_PRERENDER_STATIC_PARAMS_PATH, VINEXT_RENDERED_PATH_AND_SEARCH_HEADER, VINEXT_REVALIDATE_HEADER, VINEXT_REVALIDATE_HOST_HEADER, VINEXT_RSC_MARKER_HEADER, VINEXT_RSC_REDIRECT_HEADER, VINEXT_RSC_REDIRECT_TYPE_HEADER, VINEXT_RSC_RENDER_MODE_HEADER, VINEXT_STATIC_FILE_HEADER, VINEXT_TIMING_HEADER };
|
package/dist/server/headers.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { MIDDLEWARE_HEADER_PREFIX, MIDDLEWARE_OVERRIDE_HEADERS, MIDDLEWARE_SET_COOKIE_HEADER, MIDDLEWARE_SKIP_HEADER, VINEXT_MW_CTX_HEADER, VINEXT_PRERENDER_ROUTE_PARAMS_HEADER, VINEXT_PRERENDER_SECRET_HEADER, VINEXT_PRERENDER_SPECULATIVE_HEADER } from "../utils/protocol-headers.js";
|
|
1
|
+
import { MIDDLEWARE_HEADER_PREFIX, MIDDLEWARE_OVERRIDE_HEADERS, MIDDLEWARE_SET_COOKIE_HEADER, MIDDLEWARE_SKIP_HEADER, VINEXT_MW_CTX_HEADER, VINEXT_PRERENDER_ROUTE_PARAMS_HEADER, VINEXT_PRERENDER_SECRET_HEADER, VINEXT_PRERENDER_SPECULATIVE_HEADER, VINEXT_REVALIDATE_HOST_HEADER } from "../utils/protocol-headers.js";
|
|
2
2
|
//#region src/server/headers.ts
|
|
3
3
|
/**
|
|
4
4
|
* Internal HTTP header name constants used throughout vinext.
|
|
@@ -14,6 +14,8 @@ import { MIDDLEWARE_HEADER_PREFIX, MIDDLEWARE_OVERRIDE_HEADERS, MIDDLEWARE_SET_C
|
|
|
14
14
|
const VINEXT_CACHE_HEADER = "X-Vinext-Cache";
|
|
15
15
|
/** Next.js public ISR / page cache state indicator. */
|
|
16
16
|
const NEXTJS_CACHE_HEADER = "x-nextjs-cache";
|
|
17
|
+
/** Next.js cache-tag metadata carried by prerendered App Router responses. */
|
|
18
|
+
const NEXT_CACHE_TAGS_HEADER = "x-next-cache-tags";
|
|
17
19
|
/** Static file signal — value is URL-encoded pathname. */
|
|
18
20
|
const VINEXT_STATIC_FILE_HEADER = "x-vinext-static-file";
|
|
19
21
|
/** Timing metrics: `handlerStart,compileMs,renderMs`. */
|
|
@@ -127,7 +129,8 @@ const INTERNAL_HEADERS = [
|
|
|
127
129
|
const VINEXT_INTERNAL_HEADERS = [
|
|
128
130
|
VINEXT_PRERENDER_ROUTE_PARAMS_HEADER,
|
|
129
131
|
VINEXT_PRERENDER_SPECULATIVE_HEADER,
|
|
130
|
-
VINEXT_PRERENDER_CACHE_LIFE_HEADER
|
|
132
|
+
VINEXT_PRERENDER_CACHE_LIFE_HEADER,
|
|
133
|
+
VINEXT_REVALIDATE_HOST_HEADER
|
|
131
134
|
];
|
|
132
135
|
//#endregion
|
|
133
|
-
export { ACTION_FORWARDED_HEADER, ACTION_REDIRECT_HEADER, ACTION_REDIRECT_STATUS_HEADER, ACTION_REDIRECT_TYPE_HEADER, ACTION_REVALIDATED_HEADER, FLIGHT_HEADERS, INTERNAL_HEADERS, MIDDLEWARE_HEADER_PREFIX, MIDDLEWARE_NEXT_HEADER, MIDDLEWARE_REWRITE_HEADER, MIDDLEWARE_SET_COOKIE_HEADER, MIDDLEWARE_SKIP_HEADER, NEXTJS_ACTION_NOT_FOUND_HEADER, NEXTJS_CACHE_HEADER, NEXTJS_DEPLOYMENT_ID_HEADER, NEXT_ACTION_HEADER, NEXT_HTML_REQUEST_ID_HEADER, NEXT_REQUEST_ID_HEADER, NEXT_ROUTER_PREFETCH_HEADER, NEXT_ROUTER_SEGMENT_PREFETCH_HEADER, NEXT_ROUTER_STATE_TREE_HEADER, NEXT_URL_HEADER, RSC_ACTION_HEADER, RSC_HEADER, VINEXT_CACHE_HEADER, VINEXT_CLIENT_REUSE_MANIFEST_HEADER, VINEXT_DYNAMIC_STALE_TIME_HEADER, VINEXT_INTERCEPTION_CONTEXT_HEADER, VINEXT_INTERNAL_HEADERS, VINEXT_MOUNTED_SLOTS_HEADER, VINEXT_MW_CTX_HEADER, VINEXT_PARAMS_HEADER, VINEXT_PRERENDER_CACHE_LIFE_HEADER, VINEXT_PRERENDER_PAGES_STATIC_PATHS_PATH, VINEXT_PRERENDER_ROUTE_PARAMS_HEADER, VINEXT_PRERENDER_SECRET_HEADER, VINEXT_PRERENDER_SPECULATIVE_HEADER, VINEXT_PRERENDER_STATIC_PARAMS_PATH, VINEXT_RENDERED_PATH_AND_SEARCH_HEADER, VINEXT_REVALIDATE_HEADER, VINEXT_RSC_MARKER_HEADER, VINEXT_RSC_REDIRECT_HEADER, VINEXT_RSC_REDIRECT_TYPE_HEADER, VINEXT_RSC_RENDER_MODE_HEADER, VINEXT_STATIC_FILE_HEADER, VINEXT_TIMING_HEADER };
|
|
136
|
+
export { ACTION_FORWARDED_HEADER, ACTION_REDIRECT_HEADER, ACTION_REDIRECT_STATUS_HEADER, ACTION_REDIRECT_TYPE_HEADER, ACTION_REVALIDATED_HEADER, FLIGHT_HEADERS, INTERNAL_HEADERS, MIDDLEWARE_HEADER_PREFIX, MIDDLEWARE_NEXT_HEADER, MIDDLEWARE_REWRITE_HEADER, MIDDLEWARE_SET_COOKIE_HEADER, MIDDLEWARE_SKIP_HEADER, NEXTJS_ACTION_NOT_FOUND_HEADER, NEXTJS_CACHE_HEADER, NEXTJS_DEPLOYMENT_ID_HEADER, NEXT_ACTION_HEADER, NEXT_CACHE_TAGS_HEADER, NEXT_HTML_REQUEST_ID_HEADER, NEXT_REQUEST_ID_HEADER, NEXT_ROUTER_PREFETCH_HEADER, NEXT_ROUTER_SEGMENT_PREFETCH_HEADER, NEXT_ROUTER_STATE_TREE_HEADER, NEXT_URL_HEADER, RSC_ACTION_HEADER, RSC_HEADER, VINEXT_CACHE_HEADER, VINEXT_CLIENT_REUSE_MANIFEST_HEADER, VINEXT_DYNAMIC_STALE_TIME_HEADER, VINEXT_INTERCEPTION_CONTEXT_HEADER, VINEXT_INTERNAL_HEADERS, VINEXT_MOUNTED_SLOTS_HEADER, VINEXT_MW_CTX_HEADER, VINEXT_PARAMS_HEADER, VINEXT_PRERENDER_CACHE_LIFE_HEADER, VINEXT_PRERENDER_PAGES_STATIC_PATHS_PATH, VINEXT_PRERENDER_ROUTE_PARAMS_HEADER, VINEXT_PRERENDER_SECRET_HEADER, VINEXT_PRERENDER_SPECULATIVE_HEADER, VINEXT_PRERENDER_STATIC_PARAMS_PATH, VINEXT_RENDERED_PATH_AND_SEARCH_HEADER, VINEXT_REVALIDATE_HEADER, VINEXT_REVALIDATE_HOST_HEADER, VINEXT_RSC_MARKER_HEADER, VINEXT_RSC_REDIRECT_HEADER, VINEXT_RSC_REDIRECT_TYPE_HEADER, VINEXT_RSC_RENDER_MODE_HEADER, VINEXT_STATIC_FILE_HEADER, VINEXT_TIMING_HEADER };
|
|
@@ -1,38 +1,11 @@
|
|
|
1
1
|
import { OnRequestErrorContext } from "./instrumentation.js";
|
|
2
2
|
import { RenderObservation } from "./cache-proof.js";
|
|
3
3
|
import { AppRscRenderMode } from "./app-rsc-render-mode.js";
|
|
4
|
+
import { PRERENDER_REVALIDATE_HEADER, PRERENDER_REVALIDATE_ONLY_GENERATED_HEADER } from "../utils/protocol-headers.js";
|
|
4
5
|
import { normalizeMountedSlotsHeader } from "./app-mounted-slots-header.js";
|
|
5
6
|
import { CacheHandlerValue, CachedAppPageValue, CachedPagesValue, IncrementalCacheValue } from "../shims/cache-handler.js";
|
|
6
7
|
|
|
7
8
|
//#region src/server/isr-cache.d.ts
|
|
8
|
-
/**
|
|
9
|
-
* Header set on the internal request that `res.revalidate()` issues to
|
|
10
|
-
* trigger on-demand ISR regeneration of a Pages Router route. Mirrors Next.js's
|
|
11
|
-
* `PRERENDER_REVALIDATE_HEADER` (`x-prerender-revalidate`) — see
|
|
12
|
-
* `.nextjs-ref/packages/next/src/lib/constants.ts`.
|
|
13
|
-
*
|
|
14
|
-
* SECURITY: in Next.js this header is NOT a presence flag — it carries the
|
|
15
|
-
* secret `previewModeId`, and `checkIsOnDemandRevalidate`
|
|
16
|
-
* (`.nextjs-ref/packages/next/src/server/api-utils/index.ts`) only treats a
|
|
17
|
-
* request as on-demand revalidation when the value *equals* that secret. If we
|
|
18
|
-
* gated on presence alone, any external client could send
|
|
19
|
-
* `x-prerender-revalidate: <anything>` to force synchronous regeneration of any
|
|
20
|
-
* ISR page, bypassing the fresh/stale cache short-circuits — a
|
|
21
|
-
* cache-stampede/DoS vector. We therefore validate the value against
|
|
22
|
-
* {@link getRevalidateSecret} (a build-time secret shared across all Workers
|
|
23
|
-
* isolates) with a constant-time comparison, and only the matching value (sent
|
|
24
|
-
* by our own `res.revalidate()`) is honored.
|
|
25
|
-
*/
|
|
26
|
-
declare const PRERENDER_REVALIDATE_HEADER = "x-prerender-revalidate";
|
|
27
|
-
/**
|
|
28
|
-
* Companion header to {@link PRERENDER_REVALIDATE_HEADER}. When set,
|
|
29
|
-
* `res.revalidate(path, { unstable_onlyGenerated: true })` only revalidates the
|
|
30
|
-
* path if it was already generated, and a 404 response counts as a successful
|
|
31
|
-
* no-op. Mirrors Next.js's `PRERENDER_REVALIDATE_ONLY_GENERATED_HEADER`
|
|
32
|
-
* (`x-prerender-revalidate-if-generated`) — see
|
|
33
|
-
* `.nextjs-ref/packages/next/src/lib/constants.ts`.
|
|
34
|
-
*/
|
|
35
|
-
declare const PRERENDER_REVALIDATE_ONLY_GENERATED_HEADER = "x-prerender-revalidate-if-generated";
|
|
36
9
|
declare function getRevalidateSecret(): string;
|
|
37
10
|
declare function isRevalidateSecret(value: string | null | undefined): boolean;
|
|
38
11
|
/**
|
|
@@ -44,20 +17,22 @@ declare function isRevalidateSecret(value: string | null | undefined): boolean;
|
|
|
44
17
|
declare function isOnDemandRevalidateRequest(headerValue: string | string[] | null | undefined): boolean;
|
|
45
18
|
type ISRCacheEntry = {
|
|
46
19
|
value: CacheHandlerValue;
|
|
47
|
-
isStale: boolean;
|
|
20
|
+
isStale: boolean; /** The entry crossed its hard expire boundary and must not be served. */
|
|
21
|
+
isExpired?: boolean;
|
|
48
22
|
};
|
|
49
23
|
/**
|
|
50
24
|
* Get a cache entry with staleness information.
|
|
51
25
|
*
|
|
52
26
|
* Returns { value, isStale: false } for fresh entries,
|
|
53
|
-
* { value, isStale: true } for
|
|
54
|
-
*
|
|
27
|
+
* { value, isStale: true } for stale-but-usable entries,
|
|
28
|
+
* { value, isStale: true, isExpired: true } for entries that must be retained
|
|
29
|
+
* as regeneration input but not served, or null for cache misses.
|
|
55
30
|
*/
|
|
56
31
|
declare function isrGet(key: string): Promise<ISRCacheEntry | null>;
|
|
57
32
|
/**
|
|
58
33
|
* Store a value in the ISR cache with a revalidation period.
|
|
59
34
|
*/
|
|
60
|
-
declare function isrSet(key: string, data: IncrementalCacheValue, revalidateSeconds: number, tags?: string[], expireSeconds?: number): Promise<void>;
|
|
35
|
+
declare function isrSet(key: string, data: IncrementalCacheValue | null, revalidateSeconds: number | false, tags?: string[], expireSeconds?: number): Promise<void>;
|
|
61
36
|
declare function isrSetPrerenderedAppPage(key: string, data: CachedAppPageValue, metadata: {
|
|
62
37
|
expireSeconds?: number;
|
|
63
38
|
revalidateSeconds?: number;
|
|
@@ -70,6 +45,8 @@ declare function isrSetPrerenderedAppPage(key: string, data: CachedAppPageValue,
|
|
|
70
45
|
*/
|
|
71
46
|
tags?: string[];
|
|
72
47
|
}): Promise<void>;
|
|
48
|
+
/** Coalesce same-key synchronous on-demand revalidations. */
|
|
49
|
+
declare function coalesceOnDemandRevalidation<T>(key: string, renderFn: () => Promise<T>): Promise<T>;
|
|
73
50
|
/**
|
|
74
51
|
* Trigger a background regeneration for a cache key.
|
|
75
52
|
*
|
|
@@ -133,4 +110,4 @@ declare function setRevalidateDuration(key: string, seconds: number): void;
|
|
|
133
110
|
*/
|
|
134
111
|
declare function getRevalidateDuration(key: string): number | undefined;
|
|
135
112
|
//#endregion
|
|
136
|
-
export { ISRCacheEntry, PRERENDER_REVALIDATE_HEADER, PRERENDER_REVALIDATE_ONLY_GENERATED_HEADER, appIsrCacheKey, appIsrHtmlKey, appIsrRouteKey, appIsrRscKey, buildAppPageCacheValue, buildPagesCacheValue, getRevalidateDuration, getRevalidateSecret, isOnDemandRevalidateRequest, isRevalidateSecret, isrCacheKey, isrGet, isrSet, isrSetPrerenderedAppPage, normalizeMountedSlotsHeader, setRevalidateDuration, triggerBackgroundRegeneration };
|
|
113
|
+
export { ISRCacheEntry, PRERENDER_REVALIDATE_HEADER, PRERENDER_REVALIDATE_ONLY_GENERATED_HEADER, appIsrCacheKey, appIsrHtmlKey, appIsrRouteKey, appIsrRscKey, buildAppPageCacheValue, buildPagesCacheValue, coalesceOnDemandRevalidation, getRevalidateDuration, getRevalidateSecret, isOnDemandRevalidateRequest, isRevalidateSecret, isrCacheKey, isrGet, isrSet, isrSetPrerenderedAppPage, normalizeMountedSlotsHeader, setRevalidateDuration, triggerBackgroundRegeneration };
|
package/dist/server/isr-cache.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { getRequestExecutionContext } from "../shims/request-context.js";
|
|
2
|
+
import { PRERENDER_REVALIDATE_HEADER, PRERENDER_REVALIDATE_ONLY_GENERATED_HEADER } from "../utils/protocol-headers.js";
|
|
2
3
|
import { reportRequestError } from "./instrumentation.js";
|
|
3
4
|
import { getCdnCacheAdapter } from "../shims/cdn-cache.js";
|
|
4
5
|
import { fnv1a64 } from "../utils/hash.js";
|
|
@@ -7,34 +8,6 @@ import { APP_RSC_RENDER_MODE_NAVIGATION, getRscRenderModeCacheVariant } from "./
|
|
|
7
8
|
import { normalizeAppPageInterceptionProofPathname } from "./app-page-render-identity.js";
|
|
8
9
|
//#region src/server/isr-cache.ts
|
|
9
10
|
/**
|
|
10
|
-
* Header set on the internal request that `res.revalidate()` issues to
|
|
11
|
-
* trigger on-demand ISR regeneration of a Pages Router route. Mirrors Next.js's
|
|
12
|
-
* `PRERENDER_REVALIDATE_HEADER` (`x-prerender-revalidate`) — see
|
|
13
|
-
* `.nextjs-ref/packages/next/src/lib/constants.ts`.
|
|
14
|
-
*
|
|
15
|
-
* SECURITY: in Next.js this header is NOT a presence flag — it carries the
|
|
16
|
-
* secret `previewModeId`, and `checkIsOnDemandRevalidate`
|
|
17
|
-
* (`.nextjs-ref/packages/next/src/server/api-utils/index.ts`) only treats a
|
|
18
|
-
* request as on-demand revalidation when the value *equals* that secret. If we
|
|
19
|
-
* gated on presence alone, any external client could send
|
|
20
|
-
* `x-prerender-revalidate: <anything>` to force synchronous regeneration of any
|
|
21
|
-
* ISR page, bypassing the fresh/stale cache short-circuits — a
|
|
22
|
-
* cache-stampede/DoS vector. We therefore validate the value against
|
|
23
|
-
* {@link getRevalidateSecret} (a build-time secret shared across all Workers
|
|
24
|
-
* isolates) with a constant-time comparison, and only the matching value (sent
|
|
25
|
-
* by our own `res.revalidate()`) is honored.
|
|
26
|
-
*/
|
|
27
|
-
const PRERENDER_REVALIDATE_HEADER = "x-prerender-revalidate";
|
|
28
|
-
/**
|
|
29
|
-
* Companion header to {@link PRERENDER_REVALIDATE_HEADER}. When set,
|
|
30
|
-
* `res.revalidate(path, { unstable_onlyGenerated: true })` only revalidates the
|
|
31
|
-
* path if it was already generated, and a 404 response counts as a successful
|
|
32
|
-
* no-op. Mirrors Next.js's `PRERENDER_REVALIDATE_ONLY_GENERATED_HEADER`
|
|
33
|
-
* (`x-prerender-revalidate-if-generated`) — see
|
|
34
|
-
* `.nextjs-ref/packages/next/src/lib/constants.ts`.
|
|
35
|
-
*/
|
|
36
|
-
const PRERENDER_REVALIDATE_ONLY_GENERATED_HEADER = "x-prerender-revalidate-if-generated";
|
|
37
|
-
/**
|
|
38
11
|
* Build-time secret that authenticates on-demand revalidation requests, the
|
|
39
12
|
* vinext analog of Next.js's prerender-manifest `previewModeId`.
|
|
40
13
|
*
|
|
@@ -54,19 +27,22 @@ const PRERENDER_REVALIDATE_ONLY_GENERATED_HEADER = "x-prerender-revalidate-if-ge
|
|
|
54
27
|
*
|
|
55
28
|
* When the build-time define is absent — dev mode, and any path that doesn't
|
|
56
29
|
* run through `vinext build` — we fall back to a lazily-generated random secret.
|
|
57
|
-
* Those paths are single-process,
|
|
58
|
-
* and
|
|
30
|
+
* Those paths are single-process, but Vite can evaluate this module separately
|
|
31
|
+
* in its RSC and SSR module graphs. Store the fallback on `globalThis` under a
|
|
32
|
+
* registry symbol so every module copy in the process reads the same value.
|
|
59
33
|
*/
|
|
60
|
-
|
|
34
|
+
const _DEV_REVALIDATE_SECRET_KEY = Symbol.for("vinext.isrCache.devRevalidateSecret");
|
|
61
35
|
function getRevalidateSecret() {
|
|
62
36
|
const baked = process.env.__VINEXT_REVALIDATE_SECRET;
|
|
63
37
|
if (baked) return baked;
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
38
|
+
const globals = globalThis;
|
|
39
|
+
const existing = globals[_DEV_REVALIDATE_SECRET_KEY];
|
|
40
|
+
if (typeof existing === "string") return existing;
|
|
41
|
+
const bytes = /* @__PURE__ */ new Uint8Array(32);
|
|
42
|
+
crypto.getRandomValues(bytes);
|
|
43
|
+
const secret = Array.from(bytes, (b) => b.toString(16).padStart(2, "0")).join("");
|
|
44
|
+
globals[_DEV_REVALIDATE_SECRET_KEY] = secret;
|
|
45
|
+
return secret;
|
|
70
46
|
}
|
|
71
47
|
/**
|
|
72
48
|
* Constant-time string equality. Avoids leaking secret length / prefix via
|
|
@@ -98,16 +74,18 @@ function isOnDemandRevalidateRequest(headerValue) {
|
|
|
98
74
|
* Get a cache entry with staleness information.
|
|
99
75
|
*
|
|
100
76
|
* Returns { value, isStale: false } for fresh entries,
|
|
101
|
-
* { value, isStale: true } for
|
|
102
|
-
*
|
|
77
|
+
* { value, isStale: true } for stale-but-usable entries,
|
|
78
|
+
* { value, isStale: true, isExpired: true } for entries that must be retained
|
|
79
|
+
* as regeneration input but not served, or null for cache misses.
|
|
103
80
|
*/
|
|
104
81
|
async function isrGet(key) {
|
|
105
82
|
const result = await getCdnCacheAdapter().get(key);
|
|
106
|
-
if (!result
|
|
107
|
-
|
|
83
|
+
if (!result) return null;
|
|
84
|
+
const isExpired = result.cacheState === "expired";
|
|
108
85
|
return {
|
|
109
86
|
value: result,
|
|
110
|
-
isStale: result.cacheState === "stale"
|
|
87
|
+
isStale: isExpired || result.cacheState === "stale",
|
|
88
|
+
...isExpired ? { isExpired: true } : {}
|
|
111
89
|
};
|
|
112
90
|
}
|
|
113
91
|
/**
|
|
@@ -142,6 +120,18 @@ async function isrSetPrerenderedAppPage(key, data, metadata) {
|
|
|
142
120
|
const _PENDING_REGEN_KEY = Symbol.for("vinext.isrCache.pendingRegenerations");
|
|
143
121
|
const _g = globalThis;
|
|
144
122
|
const pendingRegenerations = _g[_PENDING_REGEN_KEY] ??= /* @__PURE__ */ new Map();
|
|
123
|
+
const _PENDING_ON_DEMAND_REGEN_KEY = Symbol.for("vinext.isrCache.pendingOnDemandRegenerations");
|
|
124
|
+
const pendingOnDemandRegenerations = _g[_PENDING_ON_DEMAND_REGEN_KEY] ??= /* @__PURE__ */ new Map();
|
|
125
|
+
/** Coalesce same-key synchronous on-demand revalidations. */
|
|
126
|
+
function coalesceOnDemandRevalidation(key, renderFn) {
|
|
127
|
+
const pending = pendingOnDemandRegenerations.get(key);
|
|
128
|
+
if (pending) return pending;
|
|
129
|
+
const promise = Promise.resolve().then(renderFn).finally(() => {
|
|
130
|
+
if (pendingOnDemandRegenerations.get(key) === promise) pendingOnDemandRegenerations.delete(key);
|
|
131
|
+
});
|
|
132
|
+
pendingOnDemandRegenerations.set(key, promise);
|
|
133
|
+
return promise;
|
|
134
|
+
}
|
|
145
135
|
/**
|
|
146
136
|
* Trigger a background regeneration for a cache key.
|
|
147
137
|
*
|
|
@@ -283,4 +273,4 @@ function getRevalidateDuration(key) {
|
|
|
283
273
|
return revalidateDurations.get(key);
|
|
284
274
|
}
|
|
285
275
|
//#endregion
|
|
286
|
-
export { PRERENDER_REVALIDATE_HEADER, PRERENDER_REVALIDATE_ONLY_GENERATED_HEADER, appIsrCacheKey, appIsrHtmlKey, appIsrRouteKey, appIsrRscKey, buildAppPageCacheValue, buildPagesCacheValue, getRevalidateDuration, getRevalidateSecret, isOnDemandRevalidateRequest, isRevalidateSecret, isrCacheKey, isrGet, isrSet, isrSetPrerenderedAppPage, normalizeMountedSlotsHeader, setRevalidateDuration, triggerBackgroundRegeneration };
|
|
276
|
+
export { PRERENDER_REVALIDATE_HEADER, PRERENDER_REVALIDATE_ONLY_GENERATED_HEADER, appIsrCacheKey, appIsrHtmlKey, appIsrRouteKey, appIsrRscKey, buildAppPageCacheValue, buildPagesCacheValue, coalesceOnDemandRevalidation, getRevalidateDuration, getRevalidateSecret, isOnDemandRevalidateRequest, isRevalidateSecret, isrCacheKey, isrGet, isrSet, isrSetPrerenderedAppPage, normalizeMountedSlotsHeader, setRevalidateDuration, triggerBackgroundRegeneration };
|
|
@@ -33,7 +33,7 @@ type DecideIsrOptions = {
|
|
|
33
33
|
* For `"dev"` call sites this is the only source of the revalidate value —
|
|
34
34
|
* dev never has metadata attached to a cache entry.
|
|
35
35
|
*/
|
|
36
|
-
revalidateSeconds: number;
|
|
36
|
+
revalidateSeconds: number | false;
|
|
37
37
|
/**
|
|
38
38
|
* The expire ceiling (seconds from epoch) read from the route config.
|
|
39
39
|
* Absent when the route pre-dates expire metadata support.
|
|
@@ -63,7 +63,7 @@ declare function decideIsr(options: DecideIsrOptions): IsrDecision;
|
|
|
63
63
|
* therefore never has `cacheControlMeta`. `expireSeconds` here is the route
|
|
64
64
|
* config ceiling passed directly from the caller (not a per-entry fallback).
|
|
65
65
|
*/
|
|
66
|
-
declare function buildMissIsrCacheControl(revalidateSeconds: number, expireSeconds?: number): string;
|
|
66
|
+
declare function buildMissIsrCacheControl(revalidateSeconds: number | false, expireSeconds?: number): string;
|
|
67
67
|
/**
|
|
68
68
|
* Build the `Cache-Control` string for a fresh (MISS) app-route response.
|
|
69
69
|
*
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { NextI18nConfig } from "../config/next-config.js";
|
|
2
2
|
import { Route } from "../routing/pages-router.js";
|
|
3
3
|
import { ExecutionContextLike } from "../shims/request-context.js";
|
|
4
|
+
import { EdgeApiExecutionRuntime } from "./edge-api-runtime.js";
|
|
4
5
|
import { PagesReqResRequest, PagesReqResResponse, PagesRequestQuery } from "./pages-node-compat.js";
|
|
5
6
|
|
|
6
7
|
//#region src/server/pages-api-route.d.ts
|
|
@@ -68,15 +69,18 @@ type HandlePagesApiRouteOptions = {
|
|
|
68
69
|
* response. Omit on Node.js dev where no Workers lifecycle exists.
|
|
69
70
|
*/
|
|
70
71
|
ctx?: ExecutionContextLike;
|
|
72
|
+
edgeRuntime?: EdgeApiExecutionRuntime;
|
|
71
73
|
match: PagesApiRouteMatch | null;
|
|
72
74
|
reportRequestError?: (error: Error, routePattern: string) => void | Promise<void>;
|
|
73
75
|
request: Request;
|
|
74
76
|
url: string;
|
|
75
77
|
nextConfig?: {
|
|
76
78
|
basePath?: string;
|
|
79
|
+
allowedRevalidateHeaderKeys?: readonly string[];
|
|
77
80
|
i18n?: NextI18nConfig | null;
|
|
78
81
|
trailingSlash?: boolean;
|
|
79
82
|
};
|
|
83
|
+
trustedRevalidateOrigin?: string;
|
|
80
84
|
};
|
|
81
85
|
declare function handlePagesApiRoute(options: HandlePagesApiRouteOptions): Promise<Response>;
|
|
82
86
|
//#endregion
|
|
@@ -7,7 +7,7 @@ import { mergeRouteParamsIntoQuery, parseQueryString, urlQueryToSearchParams } f
|
|
|
7
7
|
import { resolveBodyParserConfig } from "./pages-body-parser-config.js";
|
|
8
8
|
import { PagesBodyParseError } from "./pages-media-type.js";
|
|
9
9
|
import { createPagesReqRes, parsePagesApiBody } from "./pages-node-compat.js";
|
|
10
|
-
import { isEdgeApiRuntime } from "./edge-api-runtime.js";
|
|
10
|
+
import { finalizeEdgeApiResponse, isEdgeApiRuntime } from "./edge-api-runtime.js";
|
|
11
11
|
//#region src/server/pages-api-route.ts
|
|
12
12
|
function resolveModuleRuntime(module) {
|
|
13
13
|
return module.runtime ?? module.config?.runtime;
|
|
@@ -42,16 +42,19 @@ async function _handlePagesApiRoute(options) {
|
|
|
42
42
|
trailingSlash: options.nextConfig.trailingSlash
|
|
43
43
|
} } : void 0);
|
|
44
44
|
const response = await route.module.default(nextRequest);
|
|
45
|
-
if (response instanceof Response) return response;
|
|
45
|
+
if (response instanceof Response) return finalizeEdgeApiResponse(response, options.edgeRuntime ?? "worker");
|
|
46
46
|
throw new Error("Edge API route did not return a Response");
|
|
47
47
|
}
|
|
48
48
|
if (!isNodeApiRouteModule(route.module)) return new Response("API route does not export a default function", { status: 500 });
|
|
49
49
|
const query = buildPagesApiQuery(options.url, params);
|
|
50
50
|
const bodyParserConfig = resolveBodyParserConfig(route.module.config);
|
|
51
|
+
const body = bodyParserConfig.enabled ? await parsePagesApiBody(options.request, bodyParserConfig.sizeLimit) : void 0;
|
|
51
52
|
const { req, res, responsePromise } = createPagesReqRes({
|
|
52
|
-
|
|
53
|
+
allowedRevalidateHeaderKeys: options.nextConfig?.allowedRevalidateHeaderKeys,
|
|
54
|
+
body,
|
|
53
55
|
query,
|
|
54
56
|
request: options.request,
|
|
57
|
+
trustedRevalidateOrigin: options.trustedRevalidateOrigin,
|
|
55
58
|
url: options.url
|
|
56
59
|
});
|
|
57
60
|
let resWasPiped = false;
|
|
@@ -70,6 +70,8 @@ function collectAssetTags(options) {
|
|
|
70
70
|
const seen = /* @__PURE__ */ new Set();
|
|
71
71
|
const nonceAttr = createNonceAttribute(options.scriptNonce);
|
|
72
72
|
const deferAttr = options.disableOptimizedLoading ? "" : " defer";
|
|
73
|
+
const scriptCrossOriginAttr = options.crossOrigin ? ` crossorigin="${options.crossOrigin}"` : " crossorigin";
|
|
74
|
+
const preloadCrossOriginAttr = options.crossOrigin ? ` crossorigin="${options.crossOrigin}"` : "";
|
|
73
75
|
const basePath = options.basePath ?? "";
|
|
74
76
|
const assetPrefix = options.assetPrefix ?? "";
|
|
75
77
|
const href = (value) => {
|
|
@@ -82,8 +84,8 @@ function collectAssetTags(options) {
|
|
|
82
84
|
const clientEntry = runtimeAssets.clientEntry;
|
|
83
85
|
if (clientEntry) {
|
|
84
86
|
seen.add(clientEntry);
|
|
85
|
-
tags.push("<link rel=\"modulepreload\"" + nonceAttr + " href=\"" + href(clientEntry) + "\" />");
|
|
86
|
-
tags.push("<script type=\"module\"" + deferAttr + nonceAttr + " src=\"" + href(clientEntry) + "\"
|
|
87
|
+
tags.push("<link rel=\"modulepreload\"" + nonceAttr + " href=\"" + href(clientEntry) + "\"" + preloadCrossOriginAttr + " />");
|
|
88
|
+
tags.push("<script type=\"module\"" + deferAttr + nonceAttr + " src=\"" + href(clientEntry) + "\"" + scriptCrossOriginAttr + "><\/script>");
|
|
87
89
|
}
|
|
88
90
|
if (m) {
|
|
89
91
|
const allFiles = [];
|
|
@@ -115,8 +117,8 @@ function collectAssetTags(options) {
|
|
|
115
117
|
if (tf.endsWith(".css")) tags.push("<link rel=\"stylesheet\"" + nonceAttr + " href=\"" + href(tf) + "\" />");
|
|
116
118
|
else if (tf.endsWith(".js")) {
|
|
117
119
|
if (lazySet && lazySet.has(tf)) continue;
|
|
118
|
-
tags.push("<link rel=\"modulepreload\"" + nonceAttr + " href=\"" + href(tf) + "\" />");
|
|
119
|
-
tags.push("<script type=\"module\"" + deferAttr + nonceAttr + " src=\"" + href(tf) + "\"
|
|
120
|
+
tags.push("<link rel=\"modulepreload\"" + nonceAttr + " href=\"" + href(tf) + "\"" + preloadCrossOriginAttr + " />");
|
|
121
|
+
tags.push("<script type=\"module\"" + deferAttr + nonceAttr + " src=\"" + href(tf) + "\"" + scriptCrossOriginAttr + "><\/script>");
|
|
120
122
|
}
|
|
121
123
|
}
|
|
122
124
|
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
//#region src/server/pages-document-asset-props.d.ts
|
|
2
|
+
type DocumentAssetProps = {
|
|
3
|
+
headNonce?: string;
|
|
4
|
+
headCrossOrigin?: string;
|
|
5
|
+
scriptNonce?: string;
|
|
6
|
+
scriptCrossOrigin?: string;
|
|
7
|
+
};
|
|
8
|
+
type ApplyDocumentAssetPropsOptions = {
|
|
9
|
+
configuredCrossOrigin?: string;
|
|
10
|
+
protectedAssetMarker?: string;
|
|
11
|
+
scriptOwner?: "head" | "next-script";
|
|
12
|
+
};
|
|
13
|
+
declare function extractDocumentAssetProps(html: string): {
|
|
14
|
+
html: string;
|
|
15
|
+
props: DocumentAssetProps;
|
|
16
|
+
};
|
|
17
|
+
/**
|
|
18
|
+
* Protect asset tags whose attributes already have a more specific owner while
|
|
19
|
+
* Vite runs its HTML transforms. Tags injected by Vite after this marker pass
|
|
20
|
+
* remain unmarked, so Document/config props can still be applied to them.
|
|
21
|
+
*/
|
|
22
|
+
declare function markDocumentAssetPropsProtectedTags(html: string, markerAttribute: string): string;
|
|
23
|
+
declare function stripDocumentAssetPropsProtectionMarkers(html: string, markerAttribute: string): string;
|
|
24
|
+
declare function applyDocumentAssetProps(html: string, props: DocumentAssetProps, options?: ApplyDocumentAssetPropsOptions): string;
|
|
25
|
+
//#endregion
|
|
26
|
+
export { DocumentAssetProps, applyDocumentAssetProps, extractDocumentAssetProps, markDocumentAssetPropsProtectedTags, stripDocumentAssetPropsProtectionMarkers };
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { escapeHtmlAttr } from "./html.js";
|
|
2
|
+
//#region src/server/pages-document-asset-props.ts
|
|
3
|
+
const HEAD_NONCE_ATTR = "data-vinext-head-nonce";
|
|
4
|
+
const HEAD_CROSS_ORIGIN_ATTR = "data-vinext-head-cross-origin";
|
|
5
|
+
const SCRIPT_NONCE_ATTR = "data-vinext-script-nonce";
|
|
6
|
+
const SCRIPT_CROSS_ORIGIN_ATTR = "data-vinext-script-cross-origin";
|
|
7
|
+
function readAttribute(tag, name) {
|
|
8
|
+
if (!tag) return void 0;
|
|
9
|
+
return tag.match(new RegExp(`\\s${name}="([^"]*)"`))?.[1];
|
|
10
|
+
}
|
|
11
|
+
function removeAttributes(tag, names) {
|
|
12
|
+
return tag.replace(new RegExp(`\\s(?:${names.join("|")})="[^"]*"`, "g"), "");
|
|
13
|
+
}
|
|
14
|
+
function extractDocumentAssetProps(html) {
|
|
15
|
+
const headTag = html.match(/<head\b[^>]*>/i)?.[0];
|
|
16
|
+
const nextScriptTag = html.match(/<span\b[^>]*>(?=<!-- __NEXT_SCRIPTS__ -->)/i)?.[0];
|
|
17
|
+
const props = {
|
|
18
|
+
headNonce: readAttribute(headTag, HEAD_NONCE_ATTR),
|
|
19
|
+
headCrossOrigin: readAttribute(headTag, HEAD_CROSS_ORIGIN_ATTR),
|
|
20
|
+
scriptNonce: readAttribute(nextScriptTag, SCRIPT_NONCE_ATTR),
|
|
21
|
+
scriptCrossOrigin: readAttribute(nextScriptTag, SCRIPT_CROSS_ORIGIN_ATTR)
|
|
22
|
+
};
|
|
23
|
+
return {
|
|
24
|
+
html: html.replace(/<head\b[^>]*>/i, (tag) => removeAttributes(tag, [HEAD_NONCE_ATTR, HEAD_CROSS_ORIGIN_ATTR])).replace(/<span\b[^>]*>(?=<!-- __NEXT_SCRIPTS__ -->)/i, (tag) => removeAttributes(tag, [SCRIPT_NONCE_ATTR, SCRIPT_CROSS_ORIGIN_ATTR])),
|
|
25
|
+
props
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
function addAttribute(tag, name, value, replaceExisting = false) {
|
|
29
|
+
if (value === void 0) return tag;
|
|
30
|
+
const attributePattern = new RegExp(`\\s${name}(?:="[^"]*")?`, "i");
|
|
31
|
+
if (attributePattern.test(tag)) return replaceExisting ? tag.replace(attributePattern, ` ${name}="${escapeHtmlAttr(value)}"`) : tag;
|
|
32
|
+
if (!tag.endsWith(">")) return tag;
|
|
33
|
+
const selfClosing = tag.endsWith("/>");
|
|
34
|
+
const closingStart = selfClosing ? tag.length - 2 : tag.length - 1;
|
|
35
|
+
const opening = tag.slice(0, closingStart).trimEnd();
|
|
36
|
+
const closing = selfClosing ? " />" : ">";
|
|
37
|
+
return `${opening} ${name}="${escapeHtmlAttr(value)}"${closing}`;
|
|
38
|
+
}
|
|
39
|
+
function hasAttribute(tag, name) {
|
|
40
|
+
return name !== void 0 && new RegExp(`\\s${name}(?:="[^"]*")?`, "i").test(tag);
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Protect asset tags whose attributes already have a more specific owner while
|
|
44
|
+
* Vite runs its HTML transforms. Tags injected by Vite after this marker pass
|
|
45
|
+
* remain unmarked, so Document/config props can still be applied to them.
|
|
46
|
+
*/
|
|
47
|
+
function markDocumentAssetPropsProtectedTags(html, markerAttribute) {
|
|
48
|
+
return html.replace(/<script\b[^>]*>/gi, (tag) => addAttribute(tag, markerAttribute, "")).replace(/<link\b[^>]*\brel="(?:preload|modulepreload)"[^>]*>/gi, (tag) => addAttribute(tag, markerAttribute, ""));
|
|
49
|
+
}
|
|
50
|
+
function stripDocumentAssetPropsProtectionMarkers(html, markerAttribute) {
|
|
51
|
+
const stripMarker = (tag) => removeAttributes(tag, [markerAttribute]);
|
|
52
|
+
return html.replace(/<script\b[^>]*>/gi, stripMarker).replace(/<link\b[^>]*\brel="(?:preload|modulepreload)"[^>]*>/gi, stripMarker);
|
|
53
|
+
}
|
|
54
|
+
function applyDocumentAssetProps(html, props, options = {}) {
|
|
55
|
+
const scriptOwner = options.scriptOwner ?? "next-script";
|
|
56
|
+
const scriptNonce = scriptOwner === "head" ? props.headNonce : props.scriptNonce;
|
|
57
|
+
const preloadNonce = props.headNonce;
|
|
58
|
+
const scriptCrossOrigin = (scriptOwner === "head" ? props.headCrossOrigin : props.scriptCrossOrigin) ?? options.configuredCrossOrigin;
|
|
59
|
+
const preloadCrossOrigin = props.headCrossOrigin ?? options.configuredCrossOrigin;
|
|
60
|
+
return html.replace(/<script\b[^>]*>/gi, (tag) => {
|
|
61
|
+
if (hasAttribute(tag, options.protectedAssetMarker)) return tag;
|
|
62
|
+
return addAttribute(addAttribute(tag, "nonce", scriptNonce, true), "crossorigin", scriptCrossOrigin, true);
|
|
63
|
+
}).replace(/<link\b[^>]*\brel="(?:preload|modulepreload)"[^>]*>/gi, (tag) => {
|
|
64
|
+
if (hasAttribute(tag, options.protectedAssetMarker)) return tag;
|
|
65
|
+
return addAttribute(addAttribute(tag, "nonce", preloadNonce, true), "crossorigin", preloadCrossOrigin, true);
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
//#endregion
|
|
69
|
+
export { applyDocumentAssetProps, extractDocumentAssetProps, markDocumentAssetPropsProtectedTags, stripDocumentAssetPropsProtectionMarkers };
|
|
@@ -92,10 +92,7 @@ async function loadDevAppInitialProps(ctx) {
|
|
|
92
92
|
return {
|
|
93
93
|
kind: "render",
|
|
94
94
|
pageProps: isPropsObject(initialPageProps) ? initialPageProps : {},
|
|
95
|
-
renderProps: isPropsObject(initialProps) ? {
|
|
96
|
-
...initialProps,
|
|
97
|
-
pageProps: initialPageProps
|
|
98
|
-
} : { pageProps: initialPageProps }
|
|
95
|
+
renderProps: isPropsObject(initialProps) ? initialProps : {}
|
|
99
96
|
};
|
|
100
97
|
}
|
|
101
98
|
//#endregion
|