vinext 1.0.0-beta.2 → 1.0.0-beta.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -9
- package/dist/build/client-build-config.js +7 -1
- package/dist/check.js +4 -0
- package/dist/config/config-matchers.js +4 -1
- package/dist/config/next-config.d.ts +12 -3
- package/dist/config/next-config.js +4 -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 +3 -0
- 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 +10 -3
- package/dist/index.js +112 -43
- package/dist/init-cloudflare.d.ts +3 -1
- package/dist/init-cloudflare.js +59 -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/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 +3 -3
- package/dist/server/app-browser-entry.js +103 -31
- package/dist/server/app-browser-navigation-controller.d.ts +23 -21
- package/dist/server/app-browser-navigation-controller.js +1 -1
- 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 +8 -0
- package/dist/server/app-page-dispatch.js +31 -6
- package/dist/server/app-page-element-builder.d.ts +2 -0
- package/dist/server/app-page-element-builder.js +33 -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-request.d.ts +3 -0
- package/dist/server/app-page-request.js +1 -1
- package/dist/server/app-page-response.js +1 -1
- package/dist/server/app-page-route-wiring.d.ts +7 -0
- package/dist/server/app-page-route-wiring.js +211 -49
- package/dist/server/app-pages-bridge.d.ts +1 -1
- package/dist/server/app-pages-bridge.js +2 -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-errors.js +10 -0
- package/dist/server/app-rsc-handler.js +21 -9
- 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-ssr-entry.js +4 -4
- 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 +145 -312
- package/dist/server/headers.d.ts +2 -2
- package/dist/server/headers.js +4 -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 +2 -0
- package/dist/server/pages-api-route.js +4 -1
- package/dist/server/pages-dev-hydration.js +0 -1
- 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 +7 -3
- 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 +5 -1
- package/dist/server/pages-page-handler.js +115 -19
- package/dist/server/pages-page-response.d.ts +5 -3
- package/dist/server/pages-page-response.js +23 -26
- 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/prod-server.d.ts +2 -1
- package/dist/server/prod-server.js +37 -13
- package/dist/server/revalidation-host.d.ts +11 -0
- package/dist/server/revalidation-host.js +19 -0
- package/dist/server/worker-revalidation-context.d.ts +13 -0
- package/dist/server/worker-revalidation-context.js +29 -0
- 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/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/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.js +18 -6
- package/dist/shims/metadata.d.ts +7 -17
- package/dist/shims/metadata.js +74 -24
- package/dist/shims/navigation.js +1 -1
- package/dist/shims/request-context.d.ts +4 -1
- 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/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
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
|
/**
|
|
@@ -107,4 +107,4 @@ declare const INTERNAL_HEADERS: string[];
|
|
|
107
107
|
/** Vinext-only internal headers stripped alongside Next.js protocol internals. */
|
|
108
108
|
declare const VINEXT_INTERNAL_HEADERS: string[];
|
|
109
109
|
//#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 };
|
|
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_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.
|
|
@@ -127,7 +127,8 @@ const INTERNAL_HEADERS = [
|
|
|
127
127
|
const VINEXT_INTERNAL_HEADERS = [
|
|
128
128
|
VINEXT_PRERENDER_ROUTE_PARAMS_HEADER,
|
|
129
129
|
VINEXT_PRERENDER_SPECULATIVE_HEADER,
|
|
130
|
-
VINEXT_PRERENDER_CACHE_LIFE_HEADER
|
|
130
|
+
VINEXT_PRERENDER_CACHE_LIFE_HEADER,
|
|
131
|
+
VINEXT_REVALIDATE_HOST_HEADER
|
|
131
132
|
];
|
|
132
133
|
//#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 };
|
|
134
|
+
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_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
|
*
|
|
@@ -74,9 +74,11 @@ type HandlePagesApiRouteOptions = {
|
|
|
74
74
|
url: string;
|
|
75
75
|
nextConfig?: {
|
|
76
76
|
basePath?: string;
|
|
77
|
+
allowedRevalidateHeaderKeys?: readonly string[];
|
|
77
78
|
i18n?: NextI18nConfig | null;
|
|
78
79
|
trailingSlash?: boolean;
|
|
79
80
|
};
|
|
81
|
+
trustedRevalidateOrigin?: string;
|
|
80
82
|
};
|
|
81
83
|
declare function handlePagesApiRoute(options: HandlePagesApiRouteOptions): Promise<Response>;
|
|
82
84
|
//#endregion
|
|
@@ -48,10 +48,13 @@ async function _handlePagesApiRoute(options) {
|
|
|
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;
|
|
@@ -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
|
|
@@ -93,23 +93,71 @@ function stripI18nLocaleForApiRoute(url, i18nConfig) {
|
|
|
93
93
|
*/
|
|
94
94
|
function detectLocaleFromAcceptLanguage(acceptLang, i18nConfig) {
|
|
95
95
|
if (!acceptLang) return null;
|
|
96
|
-
const
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
}).sort((a, b) => b.q - a.q);
|
|
104
|
-
for (const { lang } of langs) {
|
|
105
|
-
const exactMatch = i18nConfig.locales.find((locale) => locale.toLowerCase() === lang);
|
|
106
|
-
if (exactMatch) return exactMatch;
|
|
107
|
-
const prefix = lang.split("-")[0];
|
|
108
|
-
const prefixMatch = i18nConfig.locales.find((locale) => {
|
|
109
|
-
const lowered = locale.toLowerCase();
|
|
110
|
-
return lowered === prefix || lowered.startsWith(prefix + "-");
|
|
96
|
+
const configured = /* @__PURE__ */ new Map();
|
|
97
|
+
let position = 0;
|
|
98
|
+
for (const locale of i18nConfig.locales) {
|
|
99
|
+
const lower = locale.toLowerCase();
|
|
100
|
+
configured.set(lower, {
|
|
101
|
+
locale,
|
|
102
|
+
position: position++
|
|
111
103
|
});
|
|
112
|
-
|
|
104
|
+
const parts = lower.split("-");
|
|
105
|
+
while (parts.length > 1) {
|
|
106
|
+
parts.pop();
|
|
107
|
+
const prefix = parts.join("-");
|
|
108
|
+
if (!configured.has(prefix)) configured.set(prefix, {
|
|
109
|
+
locale,
|
|
110
|
+
position: position++
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
const selections = [];
|
|
115
|
+
const listedTokens = /* @__PURE__ */ new Set();
|
|
116
|
+
try {
|
|
117
|
+
const parts = acceptLang.replace(/[ \t]/g, "").split(",");
|
|
118
|
+
for (let headerPosition = 0; headerPosition < parts.length; headerPosition++) {
|
|
119
|
+
const part = parts[headerPosition];
|
|
120
|
+
if (!part) continue;
|
|
121
|
+
const params = part.split(";");
|
|
122
|
+
if (params.length > 2) throw new Error("Invalid Accept-Language header");
|
|
123
|
+
const token = params[0].toLowerCase();
|
|
124
|
+
if (!token) throw new Error("Invalid Accept-Language header");
|
|
125
|
+
const selection = {
|
|
126
|
+
token,
|
|
127
|
+
headerPosition,
|
|
128
|
+
quality: 1
|
|
129
|
+
};
|
|
130
|
+
const configuredMatch = configured.get(token);
|
|
131
|
+
if (configuredMatch) selection.preference = configuredMatch.position;
|
|
132
|
+
listedTokens.add(token);
|
|
133
|
+
if (params.length === 2) {
|
|
134
|
+
const [key, value] = params[1].split("=");
|
|
135
|
+
if (!value || key !== "q" && key !== "Q") throw new Error("Invalid Accept-Language header");
|
|
136
|
+
const quality = parseFloat(value);
|
|
137
|
+
if (quality === 0) continue;
|
|
138
|
+
if (Number.isFinite(quality) && quality >= .001 && quality <= 1) selection.quality = quality;
|
|
139
|
+
}
|
|
140
|
+
selections.push(selection);
|
|
141
|
+
}
|
|
142
|
+
} catch {
|
|
143
|
+
return null;
|
|
144
|
+
}
|
|
145
|
+
selections.sort((a, b) => {
|
|
146
|
+
if (b.quality !== a.quality) return b.quality - a.quality;
|
|
147
|
+
if (a.preference !== b.preference) {
|
|
148
|
+
if (a.preference === void 0) return 1;
|
|
149
|
+
if (b.preference === void 0) return -1;
|
|
150
|
+
return a.preference - b.preference;
|
|
151
|
+
}
|
|
152
|
+
return a.headerPosition - b.headerPosition;
|
|
153
|
+
});
|
|
154
|
+
for (const { token } of selections) {
|
|
155
|
+
if (token === "*") {
|
|
156
|
+
for (const [preference, { locale }] of configured) if (!listedTokens.has(preference)) return locale;
|
|
157
|
+
continue;
|
|
158
|
+
}
|
|
159
|
+
const match = configured.get(token);
|
|
160
|
+
if (match) return match.locale;
|
|
113
161
|
}
|
|
114
162
|
return null;
|
|
115
163
|
}
|
|
@@ -49,9 +49,11 @@ type PagesRequestCookiesCarrier = {
|
|
|
49
49
|
cookies?: unknown;
|
|
50
50
|
};
|
|
51
51
|
type CreatePagesReqResOptions = {
|
|
52
|
+
allowedRevalidateHeaderKeys?: readonly string[];
|
|
52
53
|
body: unknown;
|
|
53
54
|
query: PagesRequestQuery;
|
|
54
55
|
request: Request;
|
|
56
|
+
trustedRevalidateOrigin?: string;
|
|
55
57
|
url: string;
|
|
56
58
|
};
|
|
57
59
|
type CreatePagesReqResResult = {
|
|
@@ -121,6 +121,8 @@ var PagesResponseStream = class extends Writable {
|
|
|
121
121
|
resolveResponse;
|
|
122
122
|
rejectResponse;
|
|
123
123
|
requestHeaders;
|
|
124
|
+
trustedRevalidateOrigin;
|
|
125
|
+
allowedRevalidateHeaderKeys;
|
|
124
126
|
resStatusCode = 200;
|
|
125
127
|
resHeaders = {};
|
|
126
128
|
setCookieHeaders = [];
|
|
@@ -128,11 +130,13 @@ var PagesResponseStream = class extends Writable {
|
|
|
128
130
|
controller = null;
|
|
129
131
|
bufferedChunks = [];
|
|
130
132
|
streamEnded = false;
|
|
131
|
-
constructor(resolveResponse, rejectResponse, requestHeaders) {
|
|
133
|
+
constructor(resolveResponse, rejectResponse, requestHeaders, trustedRevalidateOrigin, allowedRevalidateHeaderKeys = []) {
|
|
132
134
|
super();
|
|
133
135
|
this.resolveResponse = resolveResponse;
|
|
134
136
|
this.rejectResponse = rejectResponse;
|
|
135
137
|
this.requestHeaders = requestHeaders;
|
|
138
|
+
this.trustedRevalidateOrigin = trustedRevalidateOrigin;
|
|
139
|
+
this.allowedRevalidateHeaderKeys = allowedRevalidateHeaderKeys;
|
|
136
140
|
this.once("error", (err) => {
|
|
137
141
|
if (!this.resolved) {
|
|
138
142
|
this.resolved = true;
|
|
@@ -202,7 +206,7 @@ var PagesResponseStream = class extends Writable {
|
|
|
202
206
|
return headers;
|
|
203
207
|
}
|
|
204
208
|
async revalidate(urlPath, opts) {
|
|
205
|
-
await performOnDemandRevalidate(this.requestHeaders, urlPath, opts);
|
|
209
|
+
await performOnDemandRevalidate(this.requestHeaders, urlPath, opts, this.trustedRevalidateOrigin, this.allowedRevalidateHeaderKeys);
|
|
206
210
|
}
|
|
207
211
|
setPreviewData(data, options = {}) {
|
|
208
212
|
setPagesPreviewData(this, data, options);
|
|
@@ -299,7 +303,7 @@ function createPagesReqRes(options) {
|
|
|
299
303
|
resolveResponse = resolve;
|
|
300
304
|
rejectResponse = reject;
|
|
301
305
|
});
|
|
302
|
-
const res = new PagesResponseStream(resolveResponse, rejectResponse, options.request.headers);
|
|
306
|
+
const res = new PagesResponseStream(resolveResponse, rejectResponse, options.request.headers, options.trustedRevalidateOrigin ?? new URL(options.request.url).origin, options.allowedRevalidateHeaderKeys);
|
|
303
307
|
attachPagesPreviewApi(req, res);
|
|
304
308
|
return {
|
|
305
309
|
req,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Route } from "../routing/pages-router.js";
|
|
2
2
|
import { VinextNextData } from "../client/vinext-next-data.js";
|
|
3
|
-
import { CachedPagesValue } from "../shims/cache-handler.js";
|
|
3
|
+
import { CachedPagesValue, CachedRedirectValue } from "../shims/cache-handler.js";
|
|
4
4
|
import { ISRCacheEntry } from "./isr-cache.js";
|
|
5
5
|
import { PagesPreviewData } from "./pages-preview.js";
|
|
6
6
|
import { PagesGetInitialPropsRouter } from "./pages-get-initial-props.js";
|
|
@@ -12,6 +12,12 @@ type PagesRedirectResult = {
|
|
|
12
12
|
destination: string;
|
|
13
13
|
permanent?: boolean;
|
|
14
14
|
statusCode?: number;
|
|
15
|
+
basePath?: boolean;
|
|
16
|
+
};
|
|
17
|
+
type ResolvedPagesRedirect = {
|
|
18
|
+
destination: string;
|
|
19
|
+
statusCode: number;
|
|
20
|
+
basePath?: boolean;
|
|
15
21
|
};
|
|
16
22
|
type PagesStaticPathsEntry = string | {
|
|
17
23
|
params?: Record<string, unknown>;
|
|
@@ -22,11 +28,23 @@ type PagesStaticPathsResult = {
|
|
|
22
28
|
paths?: PagesStaticPathsEntry[];
|
|
23
29
|
};
|
|
24
30
|
type PagesPagePropsResult = {
|
|
25
|
-
props?: Record<string, unknown
|
|
31
|
+
props?: Record<string, unknown> | Promise<Record<string, unknown>>;
|
|
26
32
|
redirect?: PagesRedirectResult;
|
|
27
33
|
notFound?: boolean;
|
|
28
|
-
revalidate?:
|
|
34
|
+
revalidate?: unknown;
|
|
29
35
|
};
|
|
36
|
+
declare function assertPages404DoesNotReturnNotFound(routePattern: string, result: Pick<PagesPagePropsResult, "notFound"> | null | undefined): void;
|
|
37
|
+
/**
|
|
38
|
+
* Next.js preserves an omitted/false Pages `revalidate` result as an indefinite
|
|
39
|
+
* cache lifetime. The one-year sentinel is only an HTTP Cache-Control detail;
|
|
40
|
+
* storing it as a revalidation deadline would incorrectly regenerate static
|
|
41
|
+
* pages after one year.
|
|
42
|
+
*
|
|
43
|
+
* Next.js source:
|
|
44
|
+
* - packages/next/src/server/render.tsx (`metadata.cacheControl`)
|
|
45
|
+
* - packages/next/src/server/route-modules/pages/pages-handler.ts
|
|
46
|
+
*/
|
|
47
|
+
declare function resolvePagesRevalidateSeconds(result: PagesPagePropsResult, routeUrl?: string): number | false;
|
|
30
48
|
type PagesMutableGsspResponse = {
|
|
31
49
|
headersSent: boolean;
|
|
32
50
|
} & PagesGsspResponse;
|
|
@@ -36,8 +54,16 @@ type PagesGsspContextResponse = {
|
|
|
36
54
|
responsePromise: Promise<Response>;
|
|
37
55
|
};
|
|
38
56
|
type PagesRenderProps = Record<string, unknown> & {
|
|
39
|
-
pageProps
|
|
57
|
+
pageProps?: unknown;
|
|
40
58
|
};
|
|
59
|
+
/**
|
|
60
|
+
* Merge gSP/gSSP data into the custom App's raw pageProps value.
|
|
61
|
+
*
|
|
62
|
+
* Next.js deliberately uses Object.assign rather than object spread, so
|
|
63
|
+
* enumerable keys from arrays and primitive wrapper objects are retained.
|
|
64
|
+
* https://github.com/vercel/next.js/blob/canary/packages/next/src/server/render.tsx
|
|
65
|
+
*/
|
|
66
|
+
declare function mergePagesDataProps(appPageProps: unknown, dataProps: unknown): Record<string, unknown>;
|
|
41
67
|
type PagesPageModule = {
|
|
42
68
|
default?: unknown;
|
|
43
69
|
getStaticPaths?: (context: {
|
|
@@ -106,6 +132,7 @@ type RenderPagesIsrHtmlOptions = {
|
|
|
106
132
|
};
|
|
107
133
|
type ResolvePagesPageDataOptions = {
|
|
108
134
|
applyRequestContexts: () => void;
|
|
135
|
+
basePath?: string;
|
|
109
136
|
buildId: string | null;
|
|
110
137
|
/**
|
|
111
138
|
* When true, this is a `/_next/data/<buildId>/<page>.json` request. Callers
|
|
@@ -124,7 +151,7 @@ type ResolvePagesPageDataOptions = {
|
|
|
124
151
|
i18n: PagesI18nRenderContext;
|
|
125
152
|
isrCacheKey: (router: string, pathname: string) => string;
|
|
126
153
|
isrGet: (key: string) => Promise<ISRCacheEntry | null>;
|
|
127
|
-
isrSet: (key: string, data: CachedPagesValue, revalidateSeconds: number, tags?: string[], expireSeconds?: number) => Promise<void>;
|
|
154
|
+
isrSet: (key: string, data: CachedPagesValue | CachedRedirectValue | null, revalidateSeconds: number | false, tags?: string[], expireSeconds?: number) => Promise<void>;
|
|
128
155
|
expireSeconds?: number;
|
|
129
156
|
/**
|
|
130
157
|
* When true, this dispatch corresponds to a build-time prerender (the
|
|
@@ -152,6 +179,12 @@ type ResolvePagesPageDataOptions = {
|
|
|
152
179
|
* presence — see the security note in `isr-cache.ts`.
|
|
153
180
|
*/
|
|
154
181
|
isOnDemandRevalidate?: boolean;
|
|
182
|
+
/**
|
|
183
|
+
* When true alongside an authenticated on-demand request, return a
|
|
184
|
+
* successful 404 no-op if no cache entry exists instead of generating a new
|
|
185
|
+
* fallback path. Mirrors Next.js `unstable_onlyGenerated`.
|
|
186
|
+
*/
|
|
187
|
+
revalidateOnlyGenerated?: boolean;
|
|
155
188
|
previewData?: PagesPreviewData | false;
|
|
156
189
|
/**
|
|
157
190
|
* The deployment ID used for deployment-skew protection. When set, it is
|
|
@@ -171,6 +204,12 @@ type ResolvePagesPageDataOptions = {
|
|
|
171
204
|
route: Pick<Route, "isDynamic">;
|
|
172
205
|
routePattern: string;
|
|
173
206
|
routeUrl: string;
|
|
207
|
+
/**
|
|
208
|
+
* Filesystem-route identity used for Pages ISR reads and writes. Error pages
|
|
209
|
+
* render against the original request URL but cache under /404, /500, or
|
|
210
|
+
* /_error, matching Next.js's error-page cache-key override.
|
|
211
|
+
*/
|
|
212
|
+
isrCachePathname?: string;
|
|
174
213
|
runInFreshUnifiedContext: <T>(callback: () => Promise<T>) => Promise<T>;
|
|
175
214
|
safeJsonStringify: (value: unknown) => string;
|
|
176
215
|
sanitizeDestination: (destination: string) => string;
|
|
@@ -210,7 +249,8 @@ type ResolvePagesPageDataRenderResult = {
|
|
|
210
249
|
kind: "render";
|
|
211
250
|
documentReqRes: PagesGsspContextResponse | null;
|
|
212
251
|
gsspRes: PagesGsspResponse | null;
|
|
213
|
-
isrRevalidateSeconds: number | null;
|
|
252
|
+
isrRevalidateSeconds: number | false | null;
|
|
253
|
+
isrExpireSeconds?: number;
|
|
214
254
|
pageProps: Record<string, unknown>;
|
|
215
255
|
props: PagesRenderProps;
|
|
216
256
|
/**
|
|
@@ -223,12 +263,24 @@ type ResolvePagesPageDataRenderResult = {
|
|
|
223
263
|
};
|
|
224
264
|
type ResolvePagesPageDataResponseResult = {
|
|
225
265
|
kind: "response";
|
|
226
|
-
response: Response;
|
|
266
|
+
response: Response; /** False when an on-demand request must be reported as a failed revalidation. */
|
|
267
|
+
onDemandRevalidateSuccess?: boolean;
|
|
227
268
|
};
|
|
228
269
|
type ResolvePagesPageDataNotFoundResult = {
|
|
229
|
-
kind: "notFound";
|
|
270
|
+
kind: "notFound"; /** Current getStaticProps cache lifetime, when this is an SSG result. */
|
|
271
|
+
revalidateSeconds?: number | false;
|
|
272
|
+
expireSeconds?: number;
|
|
273
|
+
cacheState?: "MISS" | "HIT" | "STALE";
|
|
230
274
|
};
|
|
231
275
|
type ResolvePagesPageDataResult = ResolvePagesPageDataRenderResult | ResolvePagesPageDataResponseResult | ResolvePagesPageDataNotFoundResult;
|
|
276
|
+
/** Validate and normalize the redirect metadata returned by gSP/gSSP. */
|
|
277
|
+
declare function resolvePagesRedirect(redirect: PagesRedirectResult, options: {
|
|
278
|
+
method: "getStaticProps" | "getServerSideProps";
|
|
279
|
+
routeUrl: string;
|
|
280
|
+
sanitizeDestination: (destination: string) => string;
|
|
281
|
+
}): ResolvedPagesRedirect;
|
|
282
|
+
declare function resolvePagesRedirectLocation(redirect: ResolvedPagesRedirect, configuredBasePath?: string): string;
|
|
283
|
+
declare function buildPagesRedirectProps(redirect: ResolvedPagesRedirect, props: PagesRenderProps): PagesRenderProps;
|
|
232
284
|
/**
|
|
233
285
|
* Compare a `getStaticPaths` entry against the actual request params.
|
|
234
286
|
*
|
|
@@ -254,4 +306,4 @@ declare function matchesPagesStaticPath(pathEntry: PagesStaticPathsEntry, params
|
|
|
254
306
|
declare function renderPagesIsrHtml(options: RenderPagesIsrHtmlOptions): Promise<string>;
|
|
255
307
|
declare function resolvePagesPageData(options: ResolvePagesPageDataOptions): Promise<ResolvePagesPageDataResult>;
|
|
256
308
|
//#endregion
|
|
257
|
-
export { PagesPageModule, PagesRouteParam, PagesStaticPathsEntry, ResolvePagesPageDataOptions, getPagesRouteParams, matchesPagesStaticPath, renderPagesIsrHtml, resolvePagesPageData };
|
|
309
|
+
export { PagesPageModule, PagesRedirectResult, PagesRouteParam, PagesStaticPathsEntry, ResolvePagesPageDataOptions, ResolvedPagesRedirect, assertPages404DoesNotReturnNotFound, buildPagesRedirectProps, getPagesRouteParams, matchesPagesStaticPath, mergePagesDataProps, renderPagesIsrHtml, resolvePagesPageData, resolvePagesRedirect, resolvePagesRedirectLocation, resolvePagesRevalidateSeconds };
|