vinext 1.0.0-beta.1 → 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 +10 -10
- package/dist/build/client-build-config.js +8 -1
- package/dist/build/report.d.ts +8 -1
- package/dist/build/report.js +12 -2
- package/dist/check.js +19 -2
- package/dist/cli.js +3 -2
- package/dist/config/config-matchers.d.ts +13 -37
- package/dist/config/config-matchers.js +35 -122
- package/dist/config/next-config.d.ts +15 -5
- package/dist/config/next-config.js +7 -1
- package/dist/config/request-context.d.ts +14 -0
- package/dist/config/request-context.js +26 -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 +30 -9
- package/dist/entries/app-rsc-manifest.js +36 -0
- package/dist/entries/pages-client-entry.js +0 -1
- package/dist/entries/pages-server-entry.js +13 -5
- package/dist/index.js +194 -101
- package/dist/init-cloudflare.d.ts +3 -1
- package/dist/init-cloudflare.js +59 -5
- package/dist/init.d.ts +6 -4
- package/dist/init.js +20 -22
- 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/og-assets.js +1 -1
- 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 +27 -11
- package/dist/routing/app-route-graph.js +158 -22
- package/dist/routing/app-router.js +12 -6
- package/dist/routing/file-matcher.d.ts +11 -22
- package/dist/routing/file-matcher.js +21 -33
- package/dist/routing/pages-router.js +23 -29
- package/dist/routing/route-matching.d.ts +14 -1
- package/dist/routing/route-matching.js +13 -1
- package/dist/routing/route-pattern.d.ts +2 -1
- package/dist/routing/route-pattern.js +7 -4
- package/dist/routing/route-trie.d.ts +5 -1
- package/dist/routing/route-trie.js +5 -2
- package/dist/server/api-handler.d.ts +2 -0
- package/dist/server/api-handler.js +3 -3
- package/dist/server/app-browser-entry.js +110 -37
- package/dist/server/app-browser-history-controller.d.ts +1 -1
- package/dist/server/app-browser-history-controller.js +3 -3
- package/dist/server/app-browser-navigation-controller.d.ts +23 -21
- package/dist/server/app-browser-navigation-controller.js +2 -2
- package/dist/server/app-browser-state.js +1 -1
- package/dist/server/app-browser-visible-commit.js +1 -1
- package/dist/server/app-fallback-renderer.d.ts +3 -0
- package/dist/server/app-fallback-renderer.js +4 -2
- package/dist/server/app-middleware.js +2 -1
- package/dist/server/app-page-boundary-render.d.ts +12 -1
- package/dist/server/app-page-boundary-render.js +84 -2
- package/dist/server/app-page-cache-finalizer.d.ts +1 -1
- package/dist/server/app-page-cache-finalizer.js +1 -1
- package/dist/server/app-page-cache.d.ts +2 -2
- package/dist/server/app-page-cache.js +16 -2
- package/dist/server/app-page-dispatch.d.ts +19 -2
- package/dist/server/app-page-dispatch.js +45 -23
- package/dist/server/app-page-element-builder.d.ts +9 -2
- package/dist/server/app-page-element-builder.js +144 -33
- package/dist/server/app-page-head.d.ts +56 -4
- package/dist/server/app-page-head.js +216 -85
- package/dist/server/app-page-http-access-fallback-metadata.d.ts +29 -0
- package/dist/server/app-page-http-access-fallback-metadata.js +114 -0
- 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 +2 -2
- package/dist/server/app-page-render.js +1 -1
- package/dist/server/app-page-request.d.ts +15 -0
- package/dist/server/app-page-request.js +134 -14
- package/dist/server/app-page-response.js +1 -1
- package/dist/server/app-page-route-wiring.d.ts +16 -0
- package/dist/server/app-page-route-wiring.js +254 -52
- package/dist/server/app-pages-bridge.d.ts +1 -1
- package/dist/server/app-pages-bridge.js +2 -1
- package/dist/server/app-post-middleware-context.d.ts +1 -1
- package/dist/server/app-post-middleware-context.js +1 -1
- package/dist/server/app-route-handler-cache.js +4 -0
- package/dist/server/app-route-handler-dispatch.js +40 -20
- package/dist/server/app-route-handler-execution.d.ts +6 -1
- package/dist/server/app-route-handler-execution.js +42 -16
- package/dist/server/app-route-handler-policy.d.ts +2 -0
- package/dist/server/app-route-handler-policy.js +3 -3
- package/dist/server/app-route-handler-response.js +1 -1
- package/dist/server/app-route-module-loader.d.ts +9 -1
- package/dist/server/app-route-module-loader.js +4 -0
- package/dist/server/app-router-entry.js +6 -3
- package/dist/server/app-rsc-cache-busting.d.ts +1 -1
- package/dist/server/app-rsc-errors.d.ts +1 -1
- package/dist/server/app-rsc-errors.js +22 -2
- package/dist/server/app-rsc-handler.d.ts +10 -5
- package/dist/server/app-rsc-handler.js +73 -33
- package/dist/server/app-rsc-request-normalization.d.ts +3 -2
- package/dist/server/app-rsc-request-normalization.js +8 -2
- package/dist/server/app-rsc-response-finalizer.d.ts +2 -2
- package/dist/server/app-rsc-response-finalizer.js +5 -11
- package/dist/server/app-rsc-route-matching.d.ts +29 -0
- package/dist/server/app-rsc-route-matching.js +93 -11
- package/dist/server/app-segment-config.js +13 -3
- package/dist/server/app-server-action-execution.d.ts +3 -1
- package/dist/server/app-server-action-execution.js +9 -7
- package/dist/server/app-ssr-entry.js +6 -6
- package/dist/server/app-ssr-stream.d.ts +1 -7
- package/dist/server/app-ssr-stream.js +2 -10
- package/dist/server/cache-control.d.ts +2 -2
- package/dist/server/cache-control.js +2 -1
- package/dist/server/config-headers.d.ts +24 -0
- package/dist/server/config-headers.js +52 -0
- package/dist/server/dev-initial-server-error.d.ts +1 -1
- package/dist/server/dev-server.d.ts +3 -1
- package/dist/server/dev-server.js +170 -321
- package/dist/server/headers.d.ts +2 -2
- package/dist/server/headers.js +4 -3
- package/dist/server/isr-cache.d.ts +11 -34
- package/dist/server/isr-cache.js +33 -43
- package/dist/server/isr-decision.d.ts +2 -2
- package/dist/server/middleware-matcher-pattern.d.ts +22 -0
- package/dist/server/middleware-matcher-pattern.js +219 -0
- package/dist/server/middleware-matcher.d.ts +2 -7
- package/dist/server/middleware-matcher.js +13 -60
- package/dist/server/middleware-path-to-regexp.d.ts +14 -0
- package/dist/server/middleware-path-to-regexp.js +228 -0
- package/dist/server/middleware-runtime.js +4 -3
- package/dist/server/middleware.d.ts +1 -1
- package/dist/server/middleware.js +2 -1
- package/dist/server/pages-api-route.d.ts +2 -0
- package/dist/server/pages-api-route.js +4 -1
- package/dist/server/pages-data-route.d.ts +11 -1
- package/dist/server/pages-data-route.js +16 -1
- package/dist/server/pages-dev-hydration.js +0 -1
- package/dist/server/pages-get-initial-props.d.ts +16 -3
- package/dist/server/pages-get-initial-props.js +17 -10
- 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 +64 -10
- package/dist/server/pages-page-data.js +349 -80
- package/dist/server/pages-page-handler.d.ts +9 -3
- package/dist/server/pages-page-handler.js +131 -36
- package/dist/server/pages-page-response.d.ts +5 -3
- package/dist/server/pages-page-response.js +24 -27
- package/dist/server/pages-readiness.d.ts +3 -3
- package/dist/server/pages-request-pipeline.d.ts +8 -0
- package/dist/server/pages-request-pipeline.js +30 -12
- 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 +16 -4
- package/dist/server/prerender-route-params.js +2 -2
- package/dist/server/prod-server.d.ts +3 -5
- package/dist/server/prod-server.js +72 -35
- package/dist/server/request-pipeline.d.ts +15 -29
- package/dist/server/request-pipeline.js +24 -59
- package/dist/server/revalidation-host.d.ts +11 -0
- package/dist/server/revalidation-host.js +19 -0
- package/dist/server/rsc-stream-hints.js +122 -18
- package/dist/server/worker-revalidation-context.d.ts +13 -0
- package/dist/server/worker-revalidation-context.js +29 -0
- package/dist/shims/app.d.ts +1 -1
- package/dist/shims/cache-handler.d.ts +1 -1
- package/dist/shims/cache-handler.js +10 -7
- package/dist/shims/cache-request-state.d.ts +27 -1
- package/dist/shims/cache-request-state.js +60 -1
- package/dist/shims/cache-runtime.d.ts +18 -1
- package/dist/shims/cache-runtime.js +26 -8
- package/dist/shims/cache.d.ts +5 -5
- package/dist/shims/cache.js +27 -11
- package/dist/shims/document.d.ts +42 -83
- package/dist/shims/document.js +62 -53
- package/dist/shims/dynamic.d.ts +5 -25
- package/dist/shims/dynamic.js +29 -8
- package/dist/shims/error.d.ts +1 -1
- package/dist/shims/error.js +2 -2
- package/dist/shims/fetch-cache.js +13 -4
- package/dist/shims/font-google-base.d.ts +11 -4
- package/dist/shims/font-google-base.js +17 -8
- package/dist/shims/font-local.d.ts +10 -3
- package/dist/shims/font-local.js +41 -10
- package/dist/shims/head.d.ts +13 -2
- package/dist/shims/head.js +3 -3
- package/dist/shims/headers.d.ts +37 -43
- package/dist/shims/headers.js +183 -74
- package/dist/shims/image.d.ts +27 -37
- package/dist/shims/image.js +21 -14
- package/dist/shims/internal/cookie-serialize.d.ts +12 -9
- package/dist/shims/internal/cookie-serialize.js +10 -7
- package/dist/shims/internal/hybrid-client-route-owner.js +3 -1
- package/dist/shims/internal/interpolate-as.d.ts +15 -1
- package/dist/shims/internal/interpolate-as.js +27 -2
- package/dist/shims/internal/utils.d.ts +3 -3
- package/dist/shims/legacy-image.d.ts +3 -37
- package/dist/shims/legacy-image.js +6 -16
- package/dist/shims/link.d.ts +15 -22
- package/dist/shims/link.js +34 -13
- package/dist/shims/metadata.d.ts +7 -17
- package/dist/shims/metadata.js +74 -24
- package/dist/shims/navigation-context-state.d.ts +1 -1
- package/dist/shims/navigation.d.ts +7 -3
- package/dist/shims/navigation.js +25 -14
- package/dist/shims/navigation.react-server.js +1 -1
- package/dist/shims/next-shims-augmentations.d.ts +1 -0
- package/dist/shims/next-shims-public.d.ts +1 -0
- package/dist/shims/public-shim-map.json.js +103 -0
- package/dist/shims/readonly-url-search-params.d.ts +3 -3
- package/dist/shims/readonly-url-search-params.js +3 -3
- package/dist/shims/request-context.d.ts +4 -1
- package/dist/shims/request-state-types.d.ts +1 -1
- package/dist/shims/router.d.ts +55 -21
- package/dist/shims/router.js +180 -44
- package/dist/shims/script.d.ts +6 -10
- package/dist/shims/script.js +7 -4
- package/dist/shims/server.d.ts +50 -26
- package/dist/shims/server.js +193 -55
- package/dist/shims/unified-request-context.d.ts +22 -3
- package/dist/shims/unified-request-context.js +84 -1
- package/dist/shims/url-safety.d.ts +1 -1
- package/dist/shims/url-safety.js +2 -2
- package/dist/typegen.d.ts +8 -2
- package/dist/typegen.js +76 -15
- package/dist/utils/cache-control-metadata.d.ts +2 -1
- package/dist/utils/cache-control-metadata.js +5 -1
- package/dist/utils/domain-locale.d.ts +2 -2
- package/dist/utils/external-url.d.ts +5 -0
- package/dist/utils/external-url.js +7 -0
- 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/dist/utils/regex-safety.d.ts +8 -0
- package/dist/utils/regex-safety.js +737 -0
- package/package.json +13 -2
|
@@ -1,10 +1,21 @@
|
|
|
1
|
-
import { NextHeader } from "../config/next-config.js";
|
|
2
|
-
import { BasePathMatchState, RequestContext } from "../config/config-matchers.js";
|
|
3
1
|
import { INTERNAL_HEADERS, VINEXT_INTERNAL_HEADERS } from "./headers.js";
|
|
4
|
-
import { isOpenRedirectShaped } from "./open-redirect.js";
|
|
5
2
|
import { hasBasePath, stripBasePath } from "../utils/base-path.js";
|
|
3
|
+
import { isOpenRedirectShaped } from "./open-redirect.js";
|
|
6
4
|
|
|
7
5
|
//#region src/server/request-pipeline.d.ts
|
|
6
|
+
/**
|
|
7
|
+
* Apply the URL Standard's pathname canonicalization without decoding and
|
|
8
|
+
* re-encoding ordinary percent escapes.
|
|
9
|
+
*
|
|
10
|
+
* In particular, WHATWG URLs remove literal and percent-encoded dot segments
|
|
11
|
+
* (`/%2e/about` becomes `/about`) while preserving unrelated spellings such
|
|
12
|
+
* as `/%61bout`, `%2F`, `%5C`, and `%252F` byte-for-byte. Node request adapters
|
|
13
|
+
* must do this before comparing raw route/config/basePath identity so those
|
|
14
|
+
* comparisons agree with the `Request` that userland eventually receives.
|
|
15
|
+
*/
|
|
16
|
+
declare function canonicalizeRequestPathname(pathname: string): string;
|
|
17
|
+
/** Canonicalize only the pathname portion while preserving the raw query. */
|
|
18
|
+
declare function canonicalizeRequestUrlPathname(url: string): string;
|
|
8
19
|
/**
|
|
9
20
|
* Shared request pipeline utilities.
|
|
10
21
|
*
|
|
@@ -44,18 +55,6 @@ import { hasBasePath, stripBasePath } from "../utils/base-path.js";
|
|
|
44
55
|
*/
|
|
45
56
|
declare function guardProtocolRelativeUrl(rawPathname: string): Response | null;
|
|
46
57
|
type HeaderRecord = Record<string, string | string[]>;
|
|
47
|
-
type ApplyConfigHeadersOptions = {
|
|
48
|
-
configHeaders: NextHeader[];
|
|
49
|
-
pathname: string;
|
|
50
|
-
requestContext: RequestContext;
|
|
51
|
-
/**
|
|
52
|
-
* basePath gating state. When omitted, every rule is treated as a default
|
|
53
|
-
* (basePath: true) rule for backward compatibility — callers that need to
|
|
54
|
-
* support `basePath: false` headers must pass this in.
|
|
55
|
-
*/
|
|
56
|
-
basePathState?: BasePathMatchState; /** Existing framework-generated headers that matching config rules may replace. */
|
|
57
|
-
overwriteExisting?: ReadonlySet<string>;
|
|
58
|
-
};
|
|
59
58
|
type StaticFileSignalContext = {
|
|
60
59
|
headers: Headers | null;
|
|
61
60
|
status: number | null;
|
|
@@ -67,19 +66,6 @@ type ResolvePublicFileRouteOptions = {
|
|
|
67
66
|
publicFiles: ReadonlySet<string>;
|
|
68
67
|
request: Request;
|
|
69
68
|
};
|
|
70
|
-
/**
|
|
71
|
-
* Apply matched next.config.js headers to a Web Headers object.
|
|
72
|
-
*
|
|
73
|
-
* Next.js evaluates config header match conditions against the original
|
|
74
|
-
* request snapshot. Middleware response headers still win for the same
|
|
75
|
-
* response key, while multi-value headers are additive.
|
|
76
|
-
*/
|
|
77
|
-
declare function applyConfigHeadersToResponse(responseHeaders: Headers, options: ApplyConfigHeadersOptions): void;
|
|
78
|
-
/**
|
|
79
|
-
* Apply matched next.config.js headers to the early response header record used
|
|
80
|
-
* by Node and Worker Pages Router pipelines before a concrete response exists.
|
|
81
|
-
*/
|
|
82
|
-
declare function applyConfigHeadersToHeaderRecord(headers: HeaderRecord, options: ApplyConfigHeadersOptions): void;
|
|
83
69
|
declare function createStaticFileSignal(pathname: string, context: StaticFileSignalContext): Response;
|
|
84
70
|
/**
|
|
85
71
|
* Resolve the public/ filesystem-route slot in the Next.js routing order.
|
|
@@ -184,4 +170,4 @@ declare function cloneRequestWithHeaders(request: Request, headers: Headers): Re
|
|
|
184
170
|
*/
|
|
185
171
|
declare function cloneRequestWithUrl(request: Request, url: string): Request;
|
|
186
172
|
//#endregion
|
|
187
|
-
export { HeaderRecord, INTERNAL_HEADERS, VINEXT_INTERNAL_HEADERS,
|
|
173
|
+
export { HeaderRecord, INTERNAL_HEADERS, VINEXT_INTERNAL_HEADERS, canonicalizeRequestPathname, canonicalizeRequestUrlPathname, cloneRequestWithHeaders, cloneRequestWithUrl, createStaticFileSignal, filterInternalHeaders, guardProtocolRelativeUrl, hasBasePath, isOpenRedirectShaped, isOriginAllowed, normalizeTrailingSlash, normalizeTrailingSlashPathname, processMiddlewareHeaders, resolvePublicFileRoute, stripBasePath, validateCsrfOrigin, validateServerActionPayload };
|
|
@@ -1,10 +1,32 @@
|
|
|
1
1
|
import { hasBasePath, removeTrailingSlash, stripBasePath } from "../utils/base-path.js";
|
|
2
2
|
import "../utils/protocol-headers.js";
|
|
3
3
|
import { INTERNAL_HEADERS, VINEXT_INTERNAL_HEADERS, VINEXT_STATIC_FILE_HEADER } from "./headers.js";
|
|
4
|
-
import { matchHeaders } from "../config/config-matchers.js";
|
|
5
4
|
import { forbiddenResponse, notFoundResponse } from "./http-error-responses.js";
|
|
6
5
|
import { isOpenRedirectShaped } from "./open-redirect.js";
|
|
7
6
|
//#region src/server/request-pipeline.ts
|
|
7
|
+
const PATHNAME_CANONICALIZATION_BASE = new URL("http://vinext.invalid/");
|
|
8
|
+
/**
|
|
9
|
+
* Apply the URL Standard's pathname canonicalization without decoding and
|
|
10
|
+
* re-encoding ordinary percent escapes.
|
|
11
|
+
*
|
|
12
|
+
* In particular, WHATWG URLs remove literal and percent-encoded dot segments
|
|
13
|
+
* (`/%2e/about` becomes `/about`) while preserving unrelated spellings such
|
|
14
|
+
* as `/%61bout`, `%2F`, `%5C`, and `%252F` byte-for-byte. Node request adapters
|
|
15
|
+
* must do this before comparing raw route/config/basePath identity so those
|
|
16
|
+
* comparisons agree with the `Request` that userland eventually receives.
|
|
17
|
+
*/
|
|
18
|
+
function canonicalizeRequestPathname(pathname) {
|
|
19
|
+
const url = new URL(PATHNAME_CANONICALIZATION_BASE);
|
|
20
|
+
url.pathname = pathname;
|
|
21
|
+
return url.pathname;
|
|
22
|
+
}
|
|
23
|
+
/** Canonicalize only the pathname portion while preserving the raw query. */
|
|
24
|
+
function canonicalizeRequestUrlPathname(url) {
|
|
25
|
+
const queryIndex = url.indexOf("?");
|
|
26
|
+
const pathname = queryIndex === -1 ? url : url.slice(0, queryIndex);
|
|
27
|
+
const search = queryIndex === -1 ? "" : url.slice(queryIndex);
|
|
28
|
+
return canonicalizeRequestPathname(pathname) + search;
|
|
29
|
+
}
|
|
8
30
|
/**
|
|
9
31
|
* Shared request pipeline utilities.
|
|
10
32
|
*
|
|
@@ -50,63 +72,6 @@ const FILE_LIKE_PATHNAME_RE = /\.[^/]+\/?$/;
|
|
|
50
72
|
function isWellKnownPathname(pathname) {
|
|
51
73
|
return pathname === "/.well-known" || pathname.startsWith("/.well-known/");
|
|
52
74
|
}
|
|
53
|
-
function findHeaderRecordKey(headers, lowerName) {
|
|
54
|
-
for (const key of Object.keys(headers)) if (key.toLowerCase() === lowerName) return key;
|
|
55
|
-
}
|
|
56
|
-
function appendHeaderRecord(headers, lowerName, value) {
|
|
57
|
-
const key = findHeaderRecordKey(headers, lowerName) ?? lowerName;
|
|
58
|
-
const existing = headers[key];
|
|
59
|
-
if (existing === void 0) {
|
|
60
|
-
headers[key] = value;
|
|
61
|
-
return;
|
|
62
|
-
}
|
|
63
|
-
if (Array.isArray(existing)) {
|
|
64
|
-
existing.push(value);
|
|
65
|
-
return;
|
|
66
|
-
}
|
|
67
|
-
headers[key] = [existing, value];
|
|
68
|
-
}
|
|
69
|
-
function appendVaryHeaderRecord(headers, value) {
|
|
70
|
-
const key = findHeaderRecordKey(headers, "vary") ?? "vary";
|
|
71
|
-
const existing = headers[key];
|
|
72
|
-
if (existing === void 0) {
|
|
73
|
-
headers[key] = value;
|
|
74
|
-
return;
|
|
75
|
-
}
|
|
76
|
-
if (Array.isArray(existing)) {
|
|
77
|
-
existing.push(value);
|
|
78
|
-
return;
|
|
79
|
-
}
|
|
80
|
-
headers[key] = existing + ", " + value;
|
|
81
|
-
}
|
|
82
|
-
/**
|
|
83
|
-
* Apply matched next.config.js headers to a Web Headers object.
|
|
84
|
-
*
|
|
85
|
-
* Next.js evaluates config header match conditions against the original
|
|
86
|
-
* request snapshot. Middleware response headers still win for the same
|
|
87
|
-
* response key, while multi-value headers are additive.
|
|
88
|
-
*/
|
|
89
|
-
function applyConfigHeadersToResponse(responseHeaders, options) {
|
|
90
|
-
const matched = matchHeaders(options.pathname, options.configHeaders, options.requestContext, options.basePathState);
|
|
91
|
-
for (const header of matched) {
|
|
92
|
-
const lowerName = header.key.toLowerCase();
|
|
93
|
-
if (lowerName === "vary" || lowerName === "set-cookie") responseHeaders.append(header.key, header.value);
|
|
94
|
-
else if (options.overwriteExisting?.has(lowerName) || !responseHeaders.has(lowerName)) responseHeaders.set(header.key, header.value);
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
/**
|
|
98
|
-
* Apply matched next.config.js headers to the early response header record used
|
|
99
|
-
* by Node and Worker Pages Router pipelines before a concrete response exists.
|
|
100
|
-
*/
|
|
101
|
-
function applyConfigHeadersToHeaderRecord(headers, options) {
|
|
102
|
-
const matched = matchHeaders(options.pathname, options.configHeaders, options.requestContext, options.basePathState);
|
|
103
|
-
for (const header of matched) {
|
|
104
|
-
const lowerName = header.key.toLowerCase();
|
|
105
|
-
if (lowerName === "set-cookie") appendHeaderRecord(headers, lowerName, header.value);
|
|
106
|
-
else if (lowerName === "vary") appendVaryHeaderRecord(headers, header.value);
|
|
107
|
-
else if (findHeaderRecordKey(headers, lowerName) === void 0) headers[lowerName] = header.value;
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
75
|
function createStaticFileSignal(pathname, context) {
|
|
111
76
|
const headers = new Headers({ [VINEXT_STATIC_FILE_HEADER]: encodeURIComponent(pathname) });
|
|
112
77
|
if (context.headers) for (const [key, value] of context.headers) headers.append(key, value);
|
|
@@ -445,4 +410,4 @@ function cloneRequestWithUrl(request, url) {
|
|
|
445
410
|
return cloned;
|
|
446
411
|
}
|
|
447
412
|
//#endregion
|
|
448
|
-
export { INTERNAL_HEADERS, VINEXT_INTERNAL_HEADERS,
|
|
413
|
+
export { INTERNAL_HEADERS, VINEXT_INTERNAL_HEADERS, canonicalizeRequestPathname, canonicalizeRequestUrlPathname, cloneRequestWithHeaders, cloneRequestWithUrl, createStaticFileSignal, filterInternalHeaders, guardProtocolRelativeUrl, hasBasePath, isOpenRedirectShaped, isOriginAllowed, normalizeTrailingSlash, normalizeTrailingSlashPathname, processMiddlewareHeaders, resolvePublicFileRoute, stripBasePath, validateCsrfOrigin, validateServerActionPayload };
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { NextI18nConfig } from "../config/next-config.js";
|
|
2
|
+
|
|
3
|
+
//#region src/server/revalidation-host.d.ts
|
|
4
|
+
/**
|
|
5
|
+
* Read the logical request hostname carried by a server-pinned revalidation
|
|
6
|
+
* loopback. The side channel is accepted only as part of the authenticated
|
|
7
|
+
* revalidation protocol and only for an exact configured i18n domain.
|
|
8
|
+
*/
|
|
9
|
+
declare function readTrustedRevalidationHostname(headers: Headers, i18nConfig: NextI18nConfig | null | undefined, authorizeRevalidation?: (headerValue: string | null) => boolean): string | null;
|
|
10
|
+
//#endregion
|
|
11
|
+
export { readTrustedRevalidationHostname };
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { VINEXT_REVALIDATE_HOST_HEADER } from "../utils/protocol-headers.js";
|
|
2
|
+
import { isOnDemandRevalidateRequest } from "./isr-cache.js";
|
|
3
|
+
import { normalizeDomainHostname } from "../utils/domain-locale.js";
|
|
4
|
+
//#region src/server/revalidation-host.ts
|
|
5
|
+
/**
|
|
6
|
+
* Read the logical request hostname carried by a server-pinned revalidation
|
|
7
|
+
* loopback. The side channel is accepted only as part of the authenticated
|
|
8
|
+
* revalidation protocol and only for an exact configured i18n domain.
|
|
9
|
+
*/
|
|
10
|
+
function readTrustedRevalidationHostname(headers, i18nConfig, authorizeRevalidation = isOnDemandRevalidateRequest) {
|
|
11
|
+
if (!i18nConfig?.domains?.length || !authorizeRevalidation(headers.get("x-prerender-revalidate"))) return null;
|
|
12
|
+
const rawHostname = headers.get(VINEXT_REVALIDATE_HOST_HEADER);
|
|
13
|
+
if (!rawHostname) return null;
|
|
14
|
+
const hostname = rawHostname.trim().toLowerCase();
|
|
15
|
+
if (normalizeDomainHostname(hostname) !== hostname) return null;
|
|
16
|
+
return i18nConfig.domains.find((item) => normalizeDomainHostname(item.domain) === hostname)?.domain.toLowerCase() ?? null;
|
|
17
|
+
}
|
|
18
|
+
//#endregion
|
|
19
|
+
export { readTrustedRevalidationHostname };
|
|
@@ -1,31 +1,135 @@
|
|
|
1
1
|
//#region src/server/rsc-stream-hints.ts
|
|
2
|
-
const REACT_FLIGHT_STYLESHEET_PRELOAD_HINT =
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
2
|
+
const REACT_FLIGHT_STYLESHEET_PRELOAD_HINT = /^([0-9a-f]*:HL\[.*?),"stylesheet"(\]|,)/;
|
|
3
|
+
const STYLESHEET_TO_STYLE_JSON_PADDING = " ".repeat(5);
|
|
4
|
+
const LENGTH_PREFIXED_ROW_TAGS = /* @__PURE__ */ new Set([
|
|
5
|
+
"T",
|
|
6
|
+
"A",
|
|
7
|
+
"O",
|
|
8
|
+
"o",
|
|
9
|
+
"b",
|
|
10
|
+
"U",
|
|
11
|
+
"S",
|
|
12
|
+
"s",
|
|
13
|
+
"L",
|
|
14
|
+
"l",
|
|
15
|
+
"G",
|
|
16
|
+
"g",
|
|
17
|
+
"M",
|
|
18
|
+
"m",
|
|
19
|
+
"V"
|
|
20
|
+
]);
|
|
21
|
+
const NEWLINE_PREFIXED_ROW_TAGS = /* @__PURE__ */ new Set([
|
|
22
|
+
"I",
|
|
23
|
+
"H",
|
|
24
|
+
"E",
|
|
25
|
+
"N",
|
|
26
|
+
"D",
|
|
27
|
+
"J",
|
|
28
|
+
"W",
|
|
29
|
+
"R",
|
|
30
|
+
"r",
|
|
31
|
+
"X",
|
|
32
|
+
"x",
|
|
33
|
+
"C",
|
|
34
|
+
"P",
|
|
35
|
+
"#"
|
|
36
|
+
]);
|
|
37
|
+
const decoder = new TextDecoder();
|
|
38
|
+
const encoder = new TextEncoder();
|
|
39
|
+
/** Rewrite only a complete React Flight stylesheet hint row. */
|
|
8
40
|
function normalizeReactFlightHintLine(line) {
|
|
9
|
-
|
|
41
|
+
const text = decoder.decode(line);
|
|
42
|
+
const normalized = text.replace(REACT_FLIGHT_STYLESHEET_PRELOAD_HINT, `$1,"style"${STYLESHEET_TO_STYLE_JSON_PADDING}$2`);
|
|
43
|
+
if (normalized === text) return line;
|
|
44
|
+
const normalizedBytes = encoder.encode(normalized);
|
|
45
|
+
return normalizedBytes.byteLength === line.byteLength ? normalizedBytes : line;
|
|
46
|
+
}
|
|
47
|
+
function concatBytes(first, second) {
|
|
48
|
+
if (first.byteLength === 0) return second;
|
|
49
|
+
const combined = new Uint8Array(first.byteLength + second.byteLength);
|
|
50
|
+
combined.set(first);
|
|
51
|
+
combined.set(second, first.byteLength);
|
|
52
|
+
return combined;
|
|
53
|
+
}
|
|
54
|
+
function indexOfByte(bytes, byte, from = 0) {
|
|
55
|
+
for (let index = from; index < bytes.byteLength; index++) if (bytes[index] === byte) return index;
|
|
56
|
+
return -1;
|
|
57
|
+
}
|
|
58
|
+
function parseHexBytes(bytes, start, end) {
|
|
59
|
+
if (start === end) return null;
|
|
60
|
+
let value = 0;
|
|
61
|
+
for (let index = start; index < end; index++) {
|
|
62
|
+
const byte = bytes[index];
|
|
63
|
+
const digit = byte >= 48 && byte <= 57 ? byte - 48 : byte >= 97 && byte <= 102 ? byte - 87 : -1;
|
|
64
|
+
if (digit === -1) return null;
|
|
65
|
+
value = value * 16 + digit;
|
|
66
|
+
if (!Number.isSafeInteger(value)) return null;
|
|
67
|
+
}
|
|
68
|
+
return value;
|
|
69
|
+
}
|
|
70
|
+
function isUntaggedJsonRowStart(byte) {
|
|
71
|
+
return byte === 34 || byte === 45 || byte >= 48 && byte <= 57 || byte === 91 || byte === 102 || byte === 110 || byte === 116 || byte === 123;
|
|
10
72
|
}
|
|
11
73
|
function normalizeReactFlightPreloadHints(stream) {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
let
|
|
74
|
+
let carry = /* @__PURE__ */ new Uint8Array();
|
|
75
|
+
let rawBytesRemaining = 0;
|
|
76
|
+
let passThrough = false;
|
|
15
77
|
return stream.pipeThrough(new TransformStream({
|
|
16
78
|
transform(chunk, controller) {
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
if (lastNewline === -1) {
|
|
20
|
-
carry = text;
|
|
79
|
+
if (passThrough) {
|
|
80
|
+
controller.enqueue(chunk);
|
|
21
81
|
return;
|
|
22
82
|
}
|
|
23
|
-
|
|
24
|
-
|
|
83
|
+
let bytes = concatBytes(carry, chunk);
|
|
84
|
+
carry = /* @__PURE__ */ new Uint8Array();
|
|
85
|
+
while (bytes.byteLength > 0) {
|
|
86
|
+
if (rawBytesRemaining > 0) {
|
|
87
|
+
const length = Math.min(rawBytesRemaining, bytes.byteLength);
|
|
88
|
+
controller.enqueue(bytes.slice(0, length));
|
|
89
|
+
rawBytesRemaining -= length;
|
|
90
|
+
bytes = bytes.subarray(length);
|
|
91
|
+
continue;
|
|
92
|
+
}
|
|
93
|
+
const colon = indexOfByte(bytes, 58);
|
|
94
|
+
if (colon === -1 || colon + 1 === bytes.byteLength) {
|
|
95
|
+
carry = bytes.slice();
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
const tag = String.fromCharCode(bytes[colon + 1]);
|
|
99
|
+
if (LENGTH_PREFIXED_ROW_TAGS.has(tag)) {
|
|
100
|
+
const comma = indexOfByte(bytes, 44, colon + 2);
|
|
101
|
+
if (comma === -1) {
|
|
102
|
+
carry = bytes.slice();
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
const length = parseHexBytes(bytes, colon + 2, comma);
|
|
106
|
+
if (length != null) {
|
|
107
|
+
controller.enqueue(bytes.slice(0, comma + 1));
|
|
108
|
+
rawBytesRemaining = length;
|
|
109
|
+
bytes = bytes.subarray(comma + 1);
|
|
110
|
+
continue;
|
|
111
|
+
}
|
|
112
|
+
passThrough = true;
|
|
113
|
+
controller.enqueue(bytes);
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
const tagByte = bytes[colon + 1];
|
|
117
|
+
if (!NEWLINE_PREFIXED_ROW_TAGS.has(tag) && !isUntaggedJsonRowStart(tagByte)) {
|
|
118
|
+
passThrough = true;
|
|
119
|
+
controller.enqueue(bytes);
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
const newline = indexOfByte(bytes, 10);
|
|
123
|
+
if (newline === -1) {
|
|
124
|
+
carry = bytes.slice();
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
controller.enqueue(normalizeReactFlightHintLine(bytes.slice(0, newline + 1)));
|
|
128
|
+
bytes = bytes.subarray(newline + 1);
|
|
129
|
+
}
|
|
25
130
|
},
|
|
26
131
|
flush(controller) {
|
|
27
|
-
|
|
28
|
-
if (text) controller.enqueue(encoder.encode(normalizeReactFlightHintLine(text)));
|
|
132
|
+
if (carry.byteLength > 0) controller.enqueue(rawBytesRemaining > 0 ? carry : normalizeReactFlightHintLine(carry));
|
|
29
133
|
}
|
|
30
134
|
}));
|
|
31
135
|
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { ExecutionContextLike } from "../shims/request-context.js";
|
|
2
|
+
|
|
3
|
+
//#region src/server/worker-revalidation-context.d.ts
|
|
4
|
+
type PlatformExecutionContext = Partial<ExecutionContextLike>;
|
|
5
|
+
/**
|
|
6
|
+
* Add a request-local, in-process Pages revalidation dispatcher to a Worker
|
|
7
|
+
* execution context. Re-entering with the derived internal context preserves
|
|
8
|
+
* the authenticated protocol headers while keeping ordinary inbound requests
|
|
9
|
+
* on the normal header-scrubbing path.
|
|
10
|
+
*/
|
|
11
|
+
declare function createWorkerRevalidationContext(base: PlatformExecutionContext | undefined, handleInternalRequest: (request: Request, ctx: ExecutionContextLike) => Promise<Response>): ExecutionContextLike;
|
|
12
|
+
//#endregion
|
|
13
|
+
export { createWorkerRevalidationContext };
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
//#region src/server/worker-revalidation-context.ts
|
|
2
|
+
function deriveExecutionContext(base, dispatchPagesRevalidate, isInternalPagesRevalidation) {
|
|
3
|
+
return {
|
|
4
|
+
waitUntil(promise) {
|
|
5
|
+
if (typeof base?.waitUntil === "function") base.waitUntil(promise);
|
|
6
|
+
else Promise.resolve(promise).catch(() => {});
|
|
7
|
+
},
|
|
8
|
+
...typeof base?.passThroughOnException === "function" ? { passThroughOnException() {
|
|
9
|
+
base.passThroughOnException?.();
|
|
10
|
+
} } : {},
|
|
11
|
+
...base?.cache === void 0 ? {} : { cache: base.cache },
|
|
12
|
+
...base?.trustedRevalidateOrigin === void 0 ? {} : { trustedRevalidateOrigin: base.trustedRevalidateOrigin },
|
|
13
|
+
dispatchPagesRevalidate,
|
|
14
|
+
isInternalPagesRevalidation
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Add a request-local, in-process Pages revalidation dispatcher to a Worker
|
|
19
|
+
* execution context. Re-entering with the derived internal context preserves
|
|
20
|
+
* the authenticated protocol headers while keeping ordinary inbound requests
|
|
21
|
+
* on the normal header-scrubbing path.
|
|
22
|
+
*/
|
|
23
|
+
function createWorkerRevalidationContext(base, handleInternalRequest) {
|
|
24
|
+
if (typeof base?.dispatchPagesRevalidate === "function") return base;
|
|
25
|
+
const dispatchPagesRevalidate = (request) => handleInternalRequest(request, deriveExecutionContext(base, dispatchPagesRevalidate, true));
|
|
26
|
+
return deriveExecutionContext(base, dispatchPagesRevalidate, false);
|
|
27
|
+
}
|
|
28
|
+
//#endregion
|
|
29
|
+
export { createWorkerRevalidationContext };
|
package/dist/shims/app.d.ts
CHANGED
|
@@ -36,7 +36,7 @@ declare function appGetInitialProps({
|
|
|
36
36
|
declare class App<P = any, CP = any, S = any> extends React.Component<P & AppProps<CP>, S> {
|
|
37
37
|
static origGetInitialProps: typeof appGetInitialProps;
|
|
38
38
|
static getInitialProps: typeof appGetInitialProps;
|
|
39
|
-
render(): React.
|
|
39
|
+
render(): React.ReactElement;
|
|
40
40
|
}
|
|
41
41
|
//#endregion
|
|
42
42
|
export { AppContext, AppInitialProps, AppProps, App as default };
|
|
@@ -9,7 +9,7 @@ type CacheHandlerValue = {
|
|
|
9
9
|
value: IncrementalCacheValue | null;
|
|
10
10
|
};
|
|
11
11
|
type CacheControlMetadata = {
|
|
12
|
-
revalidate: number;
|
|
12
|
+
revalidate: number | false;
|
|
13
13
|
expire?: number;
|
|
14
14
|
};
|
|
15
15
|
type IncrementalCacheValue = CachedFetchValue | CachedAppPageValue | CachedPagesValue | CachedRouteValue | CachedRedirectValue | CachedImageValue;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { readCacheControlNumberField } from "../utils/cache-control-metadata.js";
|
|
1
|
+
import { readCacheControlNumberField, readCacheControlRevalidateField } from "../utils/cache-control-metadata.js";
|
|
2
2
|
//#region src/shims/cache-handler.ts
|
|
3
3
|
var NoOpCacheHandler = class {
|
|
4
4
|
async get(_key, _ctx) {
|
|
@@ -88,12 +88,14 @@ var MemoryCacheHandler = class {
|
|
|
88
88
|
const revalidatedAt = this.tagRevalidatedAt.get(tag);
|
|
89
89
|
if (revalidatedAt && revalidatedAt >= entry.lastModified) return null;
|
|
90
90
|
}
|
|
91
|
-
if (entry.expireAt !== null && Date.now() > entry.expireAt) {
|
|
92
|
-
this.deleteEntry(key);
|
|
93
|
-
return null;
|
|
94
|
-
}
|
|
95
91
|
this.touchEntry(key, entry);
|
|
96
92
|
const now = Date.now();
|
|
93
|
+
if (entry.expireAt !== null && now > entry.expireAt) return {
|
|
94
|
+
lastModified: entry.lastModified,
|
|
95
|
+
value: entry.value,
|
|
96
|
+
cacheState: "expired",
|
|
97
|
+
cacheControl: entry.cacheControl
|
|
98
|
+
};
|
|
97
99
|
const requestedRevalidate = readPositiveNumberField(ctx, "revalidate");
|
|
98
100
|
const requestedRevalidateAt = requestedRevalidate === void 0 ? null : entry.lastModified + requestedRevalidate * 1e3;
|
|
99
101
|
if (entry.revalidateAt !== null && now > entry.revalidateAt || requestedRevalidateAt !== null && now > requestedRevalidateAt) return {
|
|
@@ -113,14 +115,15 @@ var MemoryCacheHandler = class {
|
|
|
113
115
|
if (data && "tags" in data && Array.isArray(data.tags)) for (const tag of data.tags) tagSet.add(tag);
|
|
114
116
|
for (const tag of readStringArrayField(ctx, "tags")) tagSet.add(tag);
|
|
115
117
|
const tags = [...tagSet];
|
|
116
|
-
let effectiveRevalidate =
|
|
118
|
+
let effectiveRevalidate = readCacheControlRevalidateField(ctx);
|
|
117
119
|
const effectiveExpire = readCacheControlNumberField(ctx, "expire");
|
|
118
120
|
if (data && "revalidate" in data && typeof data.revalidate === "number") effectiveRevalidate = data.revalidate;
|
|
121
|
+
else if (data && "revalidate" in data && data.revalidate === false) effectiveRevalidate ??= false;
|
|
119
122
|
if (effectiveRevalidate === 0) return;
|
|
120
123
|
const now = Date.now();
|
|
121
124
|
const revalidateAt = typeof effectiveRevalidate === "number" && effectiveRevalidate > 0 ? now + effectiveRevalidate * 1e3 : null;
|
|
122
125
|
const expireAt = typeof effectiveExpire === "number" && effectiveExpire > 0 ? now + effectiveExpire * 1e3 : null;
|
|
123
|
-
const cacheControl = typeof effectiveRevalidate === "number" ? effectiveExpire === void 0 ? { revalidate: effectiveRevalidate } : {
|
|
126
|
+
const cacheControl = typeof effectiveRevalidate === "number" || effectiveRevalidate === false ? effectiveExpire === void 0 ? { revalidate: effectiveRevalidate } : {
|
|
124
127
|
revalidate: effectiveRevalidate,
|
|
125
128
|
expire: effectiveExpire
|
|
126
129
|
} : void 0;
|
|
@@ -26,6 +26,8 @@ type UnstableCacheObservation = Readonly<{
|
|
|
26
26
|
}>;
|
|
27
27
|
type CacheState = {
|
|
28
28
|
actionRevalidationKind: ActionRevalidationKind;
|
|
29
|
+
pendingRevalidatedTags: Set<string>;
|
|
30
|
+
pendingRevalidations: Set<Promise<void>>;
|
|
29
31
|
requestScopedCacheLife: CacheLifeConfig | null;
|
|
30
32
|
unstableCacheObservations: Map<string, UnstableCacheObservation>;
|
|
31
33
|
unstableCacheRevalidation: UnstableCacheRevalidationMode;
|
|
@@ -37,6 +39,30 @@ declare function _runWithCacheState<T>(fn: () => T | Promise<T>): T | Promise<T>
|
|
|
37
39
|
declare function _initRequestScopedCacheState(): void;
|
|
38
40
|
declare function markActionRevalidation(kind: ActionRevalidationKind): void;
|
|
39
41
|
declare function getAndClearActionRevalidationKind(): ActionRevalidationKind;
|
|
42
|
+
/** @internal */
|
|
43
|
+
declare function _markPendingRevalidatedTag(tag: string): void;
|
|
44
|
+
/** @internal */
|
|
45
|
+
declare function _hasPendingRevalidatedTag(tags: readonly string[]): boolean;
|
|
46
|
+
/**
|
|
47
|
+
* Record a cache invalidation that must finish before the current action or
|
|
48
|
+
* route-handler request is finalized. The public revalidation APIs remain
|
|
49
|
+
* synchronous, matching Next.js, while the request boundary owns the await.
|
|
50
|
+
*
|
|
51
|
+
* Returns false outside a request-like phase so standalone calls can retain
|
|
52
|
+
* their historical background-work behavior without accumulating promises in
|
|
53
|
+
* the process-global fallback state.
|
|
54
|
+
*
|
|
55
|
+
* @internal
|
|
56
|
+
*/
|
|
57
|
+
declare function _queuePendingRevalidation(promise: Promise<void>): boolean;
|
|
58
|
+
/**
|
|
59
|
+
* Await and clear every cache invalidation queued in the current request.
|
|
60
|
+
* Clearing before awaiting also lets a later drain observe work enqueued by
|
|
61
|
+
* an async continuation while this batch is settling.
|
|
62
|
+
*
|
|
63
|
+
* @internal
|
|
64
|
+
*/
|
|
65
|
+
declare function _drainPendingRevalidations(): Promise<void>;
|
|
40
66
|
declare function _setRequestScopedCacheLife(config: CacheLifeConfig): void;
|
|
41
67
|
declare function _peekRequestScopedCacheLife(): CacheLifeConfig | null;
|
|
42
68
|
declare function _consumeRequestScopedCacheLife(): CacheLifeConfig | null;
|
|
@@ -44,4 +70,4 @@ declare function recordUnstableCacheObservation(observation: UnstableCacheObserv
|
|
|
44
70
|
declare function _peekUnstableCacheObservations(): UnstableCacheObservation[];
|
|
45
71
|
declare function shouldServeStaleUnstableCacheEntry(): boolean;
|
|
46
72
|
//#endregion
|
|
47
|
-
export { ACTION_DID_REVALIDATE_DYNAMIC_ONLY, ACTION_DID_REVALIDATE_STATIC_AND_DYNAMIC, ActionRevalidationKind, CacheLifeConfig, CacheState, UnstableCacheObservation, UnstableCacheRevalidationMode, _consumeRequestScopedCacheLife, _initRequestScopedCacheState, _peekRequestScopedCacheLife, _peekUnstableCacheObservations, _registerCacheContextAccessor, _runWithCacheState, _setRequestScopedCacheLife, cacheLifeProfiles, getAndClearActionRevalidationKind, getRegisteredCacheContext, markActionRevalidation, recordUnstableCacheObservation, shouldServeStaleUnstableCacheEntry };
|
|
73
|
+
export { ACTION_DID_REVALIDATE_DYNAMIC_ONLY, ACTION_DID_REVALIDATE_STATIC_AND_DYNAMIC, ActionRevalidationKind, CacheLifeConfig, CacheState, UnstableCacheObservation, UnstableCacheRevalidationMode, _consumeRequestScopedCacheLife, _drainPendingRevalidations, _hasPendingRevalidatedTag, _initRequestScopedCacheState, _markPendingRevalidatedTag, _peekRequestScopedCacheLife, _peekUnstableCacheObservations, _queuePendingRevalidation, _registerCacheContextAccessor, _runWithCacheState, _setRequestScopedCacheLife, cacheLifeProfiles, getAndClearActionRevalidationKind, getRegisteredCacheContext, markActionRevalidation, recordUnstableCacheObservation, shouldServeStaleUnstableCacheEntry };
|
|
@@ -53,6 +53,8 @@ const ACTION_DID_REVALIDATE_STATIC_AND_DYNAMIC = 1;
|
|
|
53
53
|
const ACTION_DID_REVALIDATE_DYNAMIC_ONLY = 2;
|
|
54
54
|
const fallbackState = globalState[FALLBACK_KEY] ??= {
|
|
55
55
|
actionRevalidationKind: ACTION_DID_NOT_REVALIDATE,
|
|
56
|
+
pendingRevalidatedTags: /* @__PURE__ */ new Set(),
|
|
57
|
+
pendingRevalidations: /* @__PURE__ */ new Set(),
|
|
56
58
|
requestScopedCacheLife: null,
|
|
57
59
|
unstableCacheObservations: /* @__PURE__ */ new Map(),
|
|
58
60
|
unstableCacheRevalidation: "foreground"
|
|
@@ -70,6 +72,8 @@ function _runWithCacheState(fn) {
|
|
|
70
72
|
}, fn);
|
|
71
73
|
const state = {
|
|
72
74
|
actionRevalidationKind: ACTION_DID_NOT_REVALIDATE,
|
|
75
|
+
pendingRevalidatedTags: /* @__PURE__ */ new Set(),
|
|
76
|
+
pendingRevalidations: /* @__PURE__ */ new Set(),
|
|
73
77
|
requestScopedCacheLife: null,
|
|
74
78
|
unstableCacheObservations: /* @__PURE__ */ new Map(),
|
|
75
79
|
unstableCacheRevalidation: "foreground"
|
|
@@ -93,6 +97,61 @@ function getAndClearActionRevalidationKind() {
|
|
|
93
97
|
state.actionRevalidationKind = ACTION_DID_NOT_REVALIDATE;
|
|
94
98
|
return kind;
|
|
95
99
|
}
|
|
100
|
+
function hasRequestScopedCacheState() {
|
|
101
|
+
if (isInsideUnifiedScope() || cacheAls.getStore() !== void 0) return true;
|
|
102
|
+
const phase = getHeadersAccessPhase();
|
|
103
|
+
return phase === "action" || phase === "route-handler";
|
|
104
|
+
}
|
|
105
|
+
/** @internal */
|
|
106
|
+
function _markPendingRevalidatedTag(tag) {
|
|
107
|
+
if (!hasRequestScopedCacheState()) return;
|
|
108
|
+
getCacheState().pendingRevalidatedTags.add(tag);
|
|
109
|
+
}
|
|
110
|
+
/** @internal */
|
|
111
|
+
function _hasPendingRevalidatedTag(tags) {
|
|
112
|
+
if (!hasRequestScopedCacheState()) return false;
|
|
113
|
+
const pendingTags = getCacheState().pendingRevalidatedTags;
|
|
114
|
+
return tags.some((tag) => pendingTags.has(tag));
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Record a cache invalidation that must finish before the current action or
|
|
118
|
+
* route-handler request is finalized. The public revalidation APIs remain
|
|
119
|
+
* synchronous, matching Next.js, while the request boundary owns the await.
|
|
120
|
+
*
|
|
121
|
+
* Returns false outside a request-like phase so standalone calls can retain
|
|
122
|
+
* their historical background-work behavior without accumulating promises in
|
|
123
|
+
* the process-global fallback state.
|
|
124
|
+
*
|
|
125
|
+
* @internal
|
|
126
|
+
*/
|
|
127
|
+
function _queuePendingRevalidation(promise) {
|
|
128
|
+
if (!hasRequestScopedCacheState()) return false;
|
|
129
|
+
getCacheState().pendingRevalidations.add(promise);
|
|
130
|
+
promise.catch(() => {});
|
|
131
|
+
return true;
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* Await and clear every cache invalidation queued in the current request.
|
|
135
|
+
* Clearing before awaiting also lets a later drain observe work enqueued by
|
|
136
|
+
* an async continuation while this batch is settling.
|
|
137
|
+
*
|
|
138
|
+
* @internal
|
|
139
|
+
*/
|
|
140
|
+
async function _drainPendingRevalidations() {
|
|
141
|
+
const state = getCacheState();
|
|
142
|
+
let didReject = false;
|
|
143
|
+
let firstRejection;
|
|
144
|
+
while (state.pendingRevalidations.size > 0) {
|
|
145
|
+
const pending = [...state.pendingRevalidations];
|
|
146
|
+
state.pendingRevalidations.clear();
|
|
147
|
+
const results = await Promise.allSettled(pending);
|
|
148
|
+
for (const result of results) if (result.status === "rejected" && !didReject) {
|
|
149
|
+
didReject = true;
|
|
150
|
+
firstRejection = result.reason;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
if (didReject) throw firstRejection;
|
|
154
|
+
}
|
|
96
155
|
function _setRequestScopedCacheLife(config) {
|
|
97
156
|
const state = getCacheState();
|
|
98
157
|
if (state.requestScopedCacheLife === null) {
|
|
@@ -123,4 +182,4 @@ function shouldServeStaleUnstableCacheEntry() {
|
|
|
123
182
|
return getCacheState().unstableCacheRevalidation === "background";
|
|
124
183
|
}
|
|
125
184
|
//#endregion
|
|
126
|
-
export { ACTION_DID_REVALIDATE_DYNAMIC_ONLY, ACTION_DID_REVALIDATE_STATIC_AND_DYNAMIC, _consumeRequestScopedCacheLife, _initRequestScopedCacheState, _peekRequestScopedCacheLife, _peekUnstableCacheObservations, _registerCacheContextAccessor, _runWithCacheState, _setRequestScopedCacheLife, cacheLifeProfiles, getAndClearActionRevalidationKind, getRegisteredCacheContext, markActionRevalidation, recordUnstableCacheObservation, shouldServeStaleUnstableCacheEntry };
|
|
185
|
+
export { ACTION_DID_REVALIDATE_DYNAMIC_ONLY, ACTION_DID_REVALIDATE_STATIC_AND_DYNAMIC, _consumeRequestScopedCacheLife, _drainPendingRevalidations, _hasPendingRevalidatedTag, _initRequestScopedCacheState, _markPendingRevalidatedTag, _peekRequestScopedCacheLife, _peekUnstableCacheObservations, _queuePendingRevalidation, _registerCacheContextAccessor, _runWithCacheState, _setRequestScopedCacheLife, cacheLifeProfiles, getAndClearActionRevalidationKind, getRegisteredCacheContext, markActionRevalidation, recordUnstableCacheObservation, shouldServeStaleUnstableCacheEntry };
|
|
@@ -35,6 +35,17 @@ declare const cacheContextStorage: import("node:async_hooks").AsyncLocalStorage<
|
|
|
35
35
|
* Get the current cache context. Returns null if not inside a "use cache" function.
|
|
36
36
|
*/
|
|
37
37
|
declare function getCacheContext(): CacheContext | null;
|
|
38
|
+
/**
|
|
39
|
+
* Build the shared-cache key for a "use cache" function from its build-scoped
|
|
40
|
+
* identity and serialized arguments.
|
|
41
|
+
*
|
|
42
|
+
* This is a logical handler key, not a storage key. Backend-specific adapters
|
|
43
|
+
* are responsible for mapping it to their physical key constraints after
|
|
44
|
+
* applying any storage prefixes.
|
|
45
|
+
*
|
|
46
|
+
* Exported for testing.
|
|
47
|
+
*/
|
|
48
|
+
declare function buildUseCacheKey(id: string, keySeed: string | undefined, argsKey?: string): string;
|
|
38
49
|
/**
|
|
39
50
|
* Convert an encodeReply result (string | FormData) to a cache key string.
|
|
40
51
|
* For FormData (binary args), produces a deterministic SHA-256 hash over
|
|
@@ -60,6 +71,12 @@ declare function runWithPrivateCache<T>(fn: () => T | Promise<T>): T | Promise<T
|
|
|
60
71
|
*/
|
|
61
72
|
declare function clearPrivateCache(): void;
|
|
62
73
|
type RegisterCachedFunctionOptions = {
|
|
74
|
+
/**
|
|
75
|
+
* Whether the original function declaration accepts a second argument.
|
|
76
|
+
* Function.length cannot represent default or rest parameters, so the
|
|
77
|
+
* transform records this separately for metadata parent resolution.
|
|
78
|
+
*/
|
|
79
|
+
acceptsSecondArgument?: boolean;
|
|
63
80
|
/**
|
|
64
81
|
* Internal transform metadata for file-level `"use cache"` default exports
|
|
65
82
|
* in App Router `page.*` files. Page components receive framework-owned
|
|
@@ -80,4 +97,4 @@ type RegisterCachedFunctionOptions = {
|
|
|
80
97
|
*/
|
|
81
98
|
declare function registerCachedFunction<TArgs extends unknown[], TResult>(fn: (...args: TArgs) => Promise<TResult>, id: string, variant?: string, options?: RegisterCachedFunctionOptions): (...args: TArgs) => Promise<TResult>;
|
|
82
99
|
//#endregion
|
|
83
|
-
export { CacheContext, NestedDynamicUseCacheError, PrivateCacheState, cacheContextStorage, clearPrivateCache, getCacheContext, markAppPagePropsForUseCache, registerCachedFunction, replyToCacheKey, runWithPrivateCache };
|
|
100
|
+
export { CacheContext, NestedDynamicUseCacheError, PrivateCacheState, buildUseCacheKey, cacheContextStorage, clearPrivateCache, getCacheContext, markAppPagePropsForUseCache, registerCachedFunction, replyToCacheKey, runWithPrivateCache };
|