vinext 0.0.46 → 0.0.47
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 +7 -5
- package/dist/build/prerender.d.ts +2 -1
- package/dist/build/prerender.js +70 -14
- package/dist/build/prerender.js.map +1 -1
- package/dist/build/report.d.ts +1 -1
- package/dist/build/route-classification-injector.d.ts +35 -0
- package/dist/build/route-classification-injector.js +61 -0
- package/dist/build/route-classification-injector.js.map +1 -0
- package/dist/build/route-classification-manifest.d.ts +1 -1
- package/dist/build/static-export.d.ts +1 -1
- package/dist/cli-args.d.ts +31 -0
- package/dist/cli-args.js +104 -0
- package/dist/cli-args.js.map +1 -0
- package/dist/cli.js +2 -19
- package/dist/cli.js.map +1 -1
- package/dist/cloudflare/kv-cache-handler.js +29 -9
- package/dist/cloudflare/kv-cache-handler.js.map +1 -1
- package/dist/config/next-config.d.ts +4 -2
- package/dist/config/next-config.js +3 -0
- package/dist/config/next-config.js.map +1 -1
- package/dist/entries/app-rsc-entry.d.ts +4 -3
- package/dist/entries/app-rsc-entry.js +373 -854
- package/dist/entries/app-rsc-entry.js.map +1 -1
- package/dist/entries/app-rsc-manifest.d.ts +1 -1
- package/dist/entries/app-rsc-manifest.js +2 -0
- package/dist/entries/app-rsc-manifest.js.map +1 -1
- package/dist/entries/pages-server-entry.js +5 -2
- package/dist/entries/pages-server-entry.js.map +1 -1
- package/dist/index.js +28 -51
- package/dist/index.js.map +1 -1
- package/dist/plugins/fonts.js +54 -32
- package/dist/plugins/fonts.js.map +1 -1
- package/dist/plugins/rsc-client-shim-excludes.js +1 -0
- package/dist/plugins/rsc-client-shim-excludes.js.map +1 -1
- package/dist/routing/app-route-graph.d.ts +109 -0
- package/dist/routing/app-route-graph.js +819 -0
- package/dist/routing/app-route-graph.js.map +1 -0
- package/dist/routing/app-router.d.ts +2 -88
- package/dist/routing/app-router.js +6 -694
- package/dist/routing/app-router.js.map +1 -1
- package/dist/server/app-browser-entry.js +86 -252
- package/dist/server/app-browser-entry.js.map +1 -1
- package/dist/server/app-browser-error.d.ts +3 -4
- package/dist/server/app-browser-error.js +8 -4
- package/dist/server/app-browser-error.js.map +1 -1
- package/dist/server/app-browser-navigation-controller.d.ts +73 -0
- package/dist/server/app-browser-navigation-controller.js +282 -0
- package/dist/server/app-browser-navigation-controller.js.map +1 -0
- package/dist/server/app-browser-state.d.ts +1 -1
- package/dist/server/app-elements.js +1 -5
- package/dist/server/app-elements.js.map +1 -1
- package/dist/server/app-fallback-renderer.d.ts +57 -0
- package/dist/server/app-fallback-renderer.js +79 -0
- package/dist/server/app-fallback-renderer.js.map +1 -0
- package/dist/server/app-hook-warning-suppression.d.ts +7 -0
- package/dist/server/app-hook-warning-suppression.js +12 -0
- package/dist/server/app-hook-warning-suppression.js.map +1 -0
- package/dist/server/app-mounted-slots-header.d.ts +17 -0
- package/dist/server/app-mounted-slots-header.js +21 -0
- package/dist/server/app-mounted-slots-header.js.map +1 -0
- package/dist/server/app-page-boundary-render.d.ts +2 -2
- package/dist/server/app-page-boundary-render.js.map +1 -1
- package/dist/server/app-page-cache.d.ts +18 -4
- package/dist/server/app-page-cache.js +53 -10
- package/dist/server/app-page-cache.js.map +1 -1
- package/dist/server/app-page-dispatch.d.ts +7 -4
- package/dist/server/app-page-dispatch.js +24 -8
- package/dist/server/app-page-dispatch.js.map +1 -1
- package/dist/server/app-page-element-builder.d.ts +61 -0
- package/dist/server/app-page-element-builder.js +139 -0
- package/dist/server/app-page-element-builder.js.map +1 -0
- package/dist/server/app-page-params.d.ts +2 -1
- package/dist/server/app-page-params.js +3 -3
- package/dist/server/app-page-params.js.map +1 -1
- package/dist/server/app-page-render.d.ts +5 -1
- package/dist/server/app-page-render.js +80 -27
- package/dist/server/app-page-render.js.map +1 -1
- package/dist/server/app-page-request.d.ts +19 -4
- package/dist/server/app-page-request.js +51 -6
- package/dist/server/app-page-request.js.map +1 -1
- package/dist/server/app-page-response.d.ts +1 -0
- package/dist/server/app-page-response.js +3 -7
- package/dist/server/app-page-response.js.map +1 -1
- package/dist/server/app-page-route-wiring.d.ts +15 -2
- package/dist/server/app-page-route-wiring.js.map +1 -1
- package/dist/server/app-post-middleware-context.d.ts +16 -0
- package/dist/server/app-post-middleware-context.js +28 -0
- package/dist/server/app-post-middleware-context.js.map +1 -0
- package/dist/server/app-request-context.d.ts +22 -0
- package/dist/server/app-request-context.js +30 -0
- package/dist/server/app-request-context.js.map +1 -0
- package/dist/server/app-route-handler-cache.d.ts +1 -0
- package/dist/server/app-route-handler-cache.js +5 -1
- package/dist/server/app-route-handler-cache.js.map +1 -1
- package/dist/server/app-route-handler-dispatch.d.ts +1 -0
- package/dist/server/app-route-handler-dispatch.js +2 -0
- package/dist/server/app-route-handler-dispatch.js.map +1 -1
- package/dist/server/app-route-handler-execution.d.ts +2 -1
- package/dist/server/app-route-handler-execution.js +2 -2
- package/dist/server/app-route-handler-execution.js.map +1 -1
- package/dist/server/app-route-handler-response.d.ts +4 -2
- package/dist/server/app-route-handler-response.js +8 -7
- package/dist/server/app-route-handler-response.js.map +1 -1
- package/dist/server/app-rsc-error-handler.d.ts +21 -0
- package/dist/server/app-rsc-error-handler.js +30 -0
- package/dist/server/app-rsc-error-handler.js.map +1 -0
- package/dist/server/app-rsc-handler.d.ts +117 -0
- package/dist/server/app-rsc-handler.js +260 -0
- package/dist/server/app-rsc-handler.js.map +1 -0
- package/dist/server/app-rsc-request-normalization.d.ts +40 -0
- package/dist/server/app-rsc-request-normalization.js +63 -0
- package/dist/server/app-rsc-request-normalization.js.map +1 -0
- package/dist/server/app-rsc-response-finalizer.d.ts +30 -0
- package/dist/server/app-rsc-response-finalizer.js +38 -0
- package/dist/server/app-rsc-response-finalizer.js.map +1 -0
- package/dist/server/app-segment-config.d.ts +33 -0
- package/dist/server/app-segment-config.js +86 -0
- package/dist/server/app-segment-config.js.map +1 -0
- package/dist/server/app-server-action-execution.d.ts +2 -0
- package/dist/server/app-server-action-execution.js +2 -0
- package/dist/server/app-server-action-execution.js.map +1 -1
- package/dist/server/cache-control.d.ts +24 -0
- package/dist/server/cache-control.js +33 -0
- package/dist/server/cache-control.js.map +1 -0
- package/dist/server/dev-error-overlay-store.d.ts +23 -0
- package/dist/server/dev-error-overlay-store.js +67 -0
- package/dist/server/dev-error-overlay-store.js.map +1 -0
- package/dist/server/dev-error-overlay.d.ts +15 -0
- package/dist/server/dev-error-overlay.js +548 -0
- package/dist/server/dev-error-overlay.js.map +1 -0
- package/dist/server/instrumentation-runtime.d.ts +44 -0
- package/dist/server/instrumentation-runtime.js +29 -0
- package/dist/server/instrumentation-runtime.js.map +1 -0
- package/dist/server/isr-cache.d.ts +2 -7
- package/dist/server/isr-cache.js +7 -10
- package/dist/server/isr-cache.js.map +1 -1
- package/dist/server/pages-page-data.d.ts +2 -1
- package/dist/server/pages-page-data.js +6 -5
- package/dist/server/pages-page-data.js.map +1 -1
- package/dist/server/pages-page-response.d.ts +2 -1
- package/dist/server/pages-page-response.js +3 -2
- package/dist/server/pages-page-response.js.map +1 -1
- package/dist/server/rsc-stream-hints.d.ts +3 -1
- package/dist/server/rsc-stream-hints.js +4 -1
- package/dist/server/rsc-stream-hints.js.map +1 -1
- package/dist/server/seed-cache.js +19 -8
- package/dist/server/seed-cache.js.map +1 -1
- package/dist/shims/cache-runtime.js +28 -11
- package/dist/shims/cache-runtime.js.map +1 -1
- package/dist/shims/cache.d.ts +15 -3
- package/dist/shims/cache.js +42 -15
- package/dist/shims/cache.js.map +1 -1
- package/dist/shims/error-boundary.d.ts +17 -1
- package/dist/shims/error-boundary.js +31 -1
- package/dist/shims/error-boundary.js.map +1 -1
- package/dist/shims/fetch-cache.d.ts +4 -1
- package/dist/shims/fetch-cache.js +55 -13
- package/dist/shims/fetch-cache.js.map +1 -1
- package/dist/shims/image.js +93 -5
- package/dist/shims/image.js.map +1 -1
- package/dist/shims/request-state-types.d.ts +1 -1
- package/dist/shims/unified-request-context.d.ts +1 -1
- package/dist/shims/unified-request-context.js +1 -0
- package/dist/shims/unified-request-context.js.map +1 -1
- package/dist/shims/use-merged-ref.d.ts +7 -0
- package/dist/shims/use-merged-ref.js +40 -0
- package/dist/shims/use-merged-ref.js.map +1 -0
- package/dist/utils/cache-control-metadata.d.ts +6 -0
- package/dist/utils/cache-control-metadata.js +16 -0
- package/dist/utils/cache-control-metadata.js.map +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { normalizePathnameForRouteMatchStrict } from "../routing/utils.js";
|
|
2
|
+
import { normalizePath } from "./normalize-path.js";
|
|
3
|
+
import { hasBasePath, stripBasePath } from "../utils/base-path.js";
|
|
4
|
+
import { guardProtocolRelativeUrl } from "./request-pipeline.js";
|
|
5
|
+
import { normalizeMountedSlotsHeader } from "./app-mounted-slots-header.js";
|
|
6
|
+
//#region src/server/app-rsc-request-normalization.ts
|
|
7
|
+
/**
|
|
8
|
+
* Normalize an App Router RSC request.
|
|
9
|
+
*
|
|
10
|
+
* Performs all security-sensitive and compatibility-sensitive preprocessing before
|
|
11
|
+
* route matching. The ordering of steps is security-critical — changing it introduces
|
|
12
|
+
* vulnerabilities:
|
|
13
|
+
*
|
|
14
|
+
* 1. Parse URL
|
|
15
|
+
* 2. Protocol-relative URL guard — on the raw pathname, BEFORE normalizePath collapses
|
|
16
|
+
* `//` to `/`. If the guard ran after normalization, `//evil.com` → `/evil.com`
|
|
17
|
+
* would bypass the check and reach the trailing-slash redirector, which echoes the
|
|
18
|
+
* path into a `Location` header that browsers interpret as protocol-relative.
|
|
19
|
+
* 3. Strict percent-decode each segment — throws on malformed sequences (→ 400). Must
|
|
20
|
+
* run before basePath check so %2F-encoded slashes cannot create fake basePath prefixes.
|
|
21
|
+
* 4. Collapse double-slashes, resolve `.` and `..` segments (normalizePath)
|
|
22
|
+
* 5. basePath check + strip — 404 when pathname lacks the basePath prefix.
|
|
23
|
+
* `/__vinext/` bypasses this for internal prerender endpoints.
|
|
24
|
+
* 6. RSC detection: `.rsc` suffix or `Accept: text/x-component`
|
|
25
|
+
* 7. cleanPathname — pathname with `.rsc` suffix stripped
|
|
26
|
+
* 8. Sanitize X-Vinext-Interception-Context — strip null bytes (header injection)
|
|
27
|
+
* 9. Normalize x-vinext-mounted-slots — dedup and sort for canonical cache keys
|
|
28
|
+
*
|
|
29
|
+
* @returns A 400 or 404 Response for invalid or out-of-scope inputs,
|
|
30
|
+
* or a NormalizedRscRequest for valid requests.
|
|
31
|
+
*/
|
|
32
|
+
function normalizeRscRequest(request, basePath) {
|
|
33
|
+
const url = new URL(request.url);
|
|
34
|
+
const protoGuard = guardProtocolRelativeUrl(url.pathname);
|
|
35
|
+
if (protoGuard) return protoGuard;
|
|
36
|
+
let decoded;
|
|
37
|
+
try {
|
|
38
|
+
decoded = normalizePathnameForRouteMatchStrict(url.pathname);
|
|
39
|
+
} catch {
|
|
40
|
+
return new Response("Bad Request", { status: 400 });
|
|
41
|
+
}
|
|
42
|
+
let pathname = normalizePath(decoded);
|
|
43
|
+
if (basePath) {
|
|
44
|
+
if (!hasBasePath(pathname, basePath) && !pathname.startsWith("/__vinext/")) return new Response("Not Found", { status: 404 });
|
|
45
|
+
pathname = stripBasePath(pathname, basePath);
|
|
46
|
+
}
|
|
47
|
+
const isRscRequest = pathname.endsWith(".rsc") || (request.headers.get("accept")?.includes("text/x-component") ?? false);
|
|
48
|
+
const cleanPathname = pathname.replace(/\.rsc$/, "");
|
|
49
|
+
const interceptionContextHeader = request.headers.get("X-Vinext-Interception-Context")?.replaceAll("\0", "") || null;
|
|
50
|
+
const mountedSlotsHeader = normalizeMountedSlotsHeader(request.headers.get("x-vinext-mounted-slots"));
|
|
51
|
+
return {
|
|
52
|
+
url,
|
|
53
|
+
pathname,
|
|
54
|
+
cleanPathname,
|
|
55
|
+
isRscRequest,
|
|
56
|
+
interceptionContextHeader,
|
|
57
|
+
mountedSlotsHeader
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
//#endregion
|
|
61
|
+
export { normalizeMountedSlotsHeader, normalizeRscRequest };
|
|
62
|
+
|
|
63
|
+
//# sourceMappingURL=app-rsc-request-normalization.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"app-rsc-request-normalization.js","names":[],"sources":["../../src/server/app-rsc-request-normalization.ts"],"sourcesContent":["import { normalizePath } from \"./normalize-path.js\";\nimport { normalizePathnameForRouteMatchStrict } from \"../routing/utils.js\";\nimport { guardProtocolRelativeUrl } from \"./request-pipeline.js\";\nimport { hasBasePath, stripBasePath } from \"../utils/base-path.js\";\nimport { normalizeMountedSlotsHeader } from \"./app-mounted-slots-header.js\";\n\nexport { normalizeMountedSlotsHeader } from \"./app-mounted-slots-header.js\";\n\nexport type NormalizedRscRequest = {\n /** Parsed URL. Callers may mutate `url.search` after middleware runs. */\n url: URL;\n /** Normalized pathname with basePath stripped. Used for all internal routing. */\n pathname: string;\n /** Pathname with `.rsc` suffix removed. Used for route matching and navigation context. */\n cleanPathname: string;\n /** True when the client requests the RSC payload (.rsc suffix or Accept: text/x-component). */\n isRscRequest: boolean;\n /** Sanitized X-Vinext-Interception-Context header (null bytes stripped). null when absent. */\n interceptionContextHeader: string | null;\n /** Normalized x-vinext-mounted-slots header (deduplicated, sorted). null when absent or blank. */\n mountedSlotsHeader: string | null;\n};\n\n/**\n * Normalize an App Router RSC request.\n *\n * Performs all security-sensitive and compatibility-sensitive preprocessing before\n * route matching. The ordering of steps is security-critical — changing it introduces\n * vulnerabilities:\n *\n * 1. Parse URL\n * 2. Protocol-relative URL guard — on the raw pathname, BEFORE normalizePath collapses\n * `//` to `/`. If the guard ran after normalization, `//evil.com` → `/evil.com`\n * would bypass the check and reach the trailing-slash redirector, which echoes the\n * path into a `Location` header that browsers interpret as protocol-relative.\n * 3. Strict percent-decode each segment — throws on malformed sequences (→ 400). Must\n * run before basePath check so %2F-encoded slashes cannot create fake basePath prefixes.\n * 4. Collapse double-slashes, resolve `.` and `..` segments (normalizePath)\n * 5. basePath check + strip — 404 when pathname lacks the basePath prefix.\n * `/__vinext/` bypasses this for internal prerender endpoints.\n * 6. RSC detection: `.rsc` suffix or `Accept: text/x-component`\n * 7. cleanPathname — pathname with `.rsc` suffix stripped\n * 8. Sanitize X-Vinext-Interception-Context — strip null bytes (header injection)\n * 9. Normalize x-vinext-mounted-slots — dedup and sort for canonical cache keys\n *\n * @returns A 400 or 404 Response for invalid or out-of-scope inputs,\n * or a NormalizedRscRequest for valid requests.\n */\nexport function normalizeRscRequest(\n request: Request,\n basePath: string,\n): Response | NormalizedRscRequest {\n const url = new URL(request.url);\n\n // Step 2: Guard against protocol-relative open redirects on the raw pathname.\n // normalizePath (step 4) would collapse //evil.com to /evil.com, causing the\n // guard to miss it. Raw pathname must be checked first.\n const protoGuard = guardProtocolRelativeUrl(url.pathname);\n if (protoGuard) return protoGuard;\n\n // Step 3: Strict segment-wise percent-decode. Preserves encoded path delimiters\n // (%2F stays %2F) to prevent encoded slashes from acting as path separators.\n // Throws on malformed sequences like %GG — caller must return 400.\n let decoded: string;\n try {\n decoded = normalizePathnameForRouteMatchStrict(url.pathname);\n } catch {\n return new Response(\"Bad Request\", { status: 400 });\n }\n\n // Step 4: Collapse double-slashes and resolve . / .. segments.\n let pathname = normalizePath(decoded);\n\n // Step 5: basePath check and strip.\n // Skipped when basePath is empty (no basePath configured).\n // /__vinext/ prefix bypasses the check for internal prerender endpoints\n // that must be reachable regardless of basePath configuration.\n if (basePath) {\n if (!hasBasePath(pathname, basePath) && !pathname.startsWith(\"/__vinext/\")) {\n return new Response(\"Not Found\", { status: 404 });\n }\n pathname = stripBasePath(pathname, basePath);\n }\n\n // Steps 6-7: RSC detection and cleanPathname.\n const isRscRequest =\n pathname.endsWith(\".rsc\") ||\n (request.headers.get(\"accept\")?.includes(\"text/x-component\") ?? false);\n const cleanPathname = pathname.replace(/\\.rsc$/, \"\");\n\n // Step 8: Sanitize X-Vinext-Interception-Context.\n // Null bytes in header values can be used for injection in some HTTP stacks.\n const interceptionContextHeader =\n request.headers.get(\"X-Vinext-Interception-Context\")?.replaceAll(\"\\0\", \"\") || null;\n\n // Step 9: Normalize mounted-slots header for canonical cache keying.\n const mountedSlotsHeader = normalizeMountedSlotsHeader(\n request.headers.get(\"x-vinext-mounted-slots\"),\n );\n\n return {\n url,\n pathname,\n cleanPathname,\n isRscRequest,\n interceptionContextHeader,\n mountedSlotsHeader,\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAgDA,SAAgB,oBACd,SACA,UACiC;CACjC,MAAM,MAAM,IAAI,IAAI,QAAQ,IAAI;CAKhC,MAAM,aAAa,yBAAyB,IAAI,SAAS;AACzD,KAAI,WAAY,QAAO;CAKvB,IAAI;AACJ,KAAI;AACF,YAAU,qCAAqC,IAAI,SAAS;SACtD;AACN,SAAO,IAAI,SAAS,eAAe,EAAE,QAAQ,KAAK,CAAC;;CAIrD,IAAI,WAAW,cAAc,QAAQ;AAMrC,KAAI,UAAU;AACZ,MAAI,CAAC,YAAY,UAAU,SAAS,IAAI,CAAC,SAAS,WAAW,aAAa,CACxE,QAAO,IAAI,SAAS,aAAa,EAAE,QAAQ,KAAK,CAAC;AAEnD,aAAW,cAAc,UAAU,SAAS;;CAI9C,MAAM,eACJ,SAAS,SAAS,OAAO,KACxB,QAAQ,QAAQ,IAAI,SAAS,EAAE,SAAS,mBAAmB,IAAI;CAClE,MAAM,gBAAgB,SAAS,QAAQ,UAAU,GAAG;CAIpD,MAAM,4BACJ,QAAQ,QAAQ,IAAI,gCAAgC,EAAE,WAAW,MAAM,GAAG,IAAI;CAGhF,MAAM,qBAAqB,4BACzB,QAAQ,QAAQ,IAAI,yBAAyB,CAC9C;AAED,QAAO;EACL;EACA;EACA;EACA;EACA;EACA;EACD"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { NextHeader } from "../config/next-config.js";
|
|
2
|
+
import { RequestContext } from "../config/config-matchers.js";
|
|
3
|
+
|
|
4
|
+
//#region src/server/app-rsc-response-finalizer.d.ts
|
|
5
|
+
type FinalizeAppRscResponseOptions = {
|
|
6
|
+
basePath: string;
|
|
7
|
+
configHeaders: NextHeader[];
|
|
8
|
+
/**
|
|
9
|
+
* Original pre-middleware request context.
|
|
10
|
+
* Next.js evaluates config header has/missing conditions against the
|
|
11
|
+
* unmodified incoming request, so callers must pass the snapshot taken
|
|
12
|
+
* before middleware runs.
|
|
13
|
+
*/
|
|
14
|
+
requestContext: RequestContext;
|
|
15
|
+
};
|
|
16
|
+
/**
|
|
17
|
+
* Apply next.config.js response headers to an App Router response.
|
|
18
|
+
*
|
|
19
|
+
* Called once per request in the outer handler() wrapper, after all route
|
|
20
|
+
* handling, so that every response path (page, route handler, server action,
|
|
21
|
+
* metadata, not-found) gets headers applied consistently.
|
|
22
|
+
*
|
|
23
|
+
* Skips 3xx redirect responses. Response.redirect() creates immutable
|
|
24
|
+
* headers that throw on mutation, and Next.js does not apply config headers
|
|
25
|
+
* to redirects regardless.
|
|
26
|
+
*/
|
|
27
|
+
declare function finalizeAppRscResponse(response: Response, request: Request, options: FinalizeAppRscResponseOptions): Response;
|
|
28
|
+
//#endregion
|
|
29
|
+
export { finalizeAppRscResponse };
|
|
30
|
+
//# sourceMappingURL=app-rsc-response-finalizer.d.ts.map
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { normalizePathnameForRouteMatch } from "../routing/utils.js";
|
|
2
|
+
import { normalizePath } from "./normalize-path.js";
|
|
3
|
+
import { stripBasePath } from "../utils/base-path.js";
|
|
4
|
+
import { applyConfigHeadersToResponse } from "./request-pipeline.js";
|
|
5
|
+
//#region src/server/app-rsc-response-finalizer.ts
|
|
6
|
+
/**
|
|
7
|
+
* Apply next.config.js response headers to an App Router response.
|
|
8
|
+
*
|
|
9
|
+
* Called once per request in the outer handler() wrapper, after all route
|
|
10
|
+
* handling, so that every response path (page, route handler, server action,
|
|
11
|
+
* metadata, not-found) gets headers applied consistently.
|
|
12
|
+
*
|
|
13
|
+
* Skips 3xx redirect responses. Response.redirect() creates immutable
|
|
14
|
+
* headers that throw on mutation, and Next.js does not apply config headers
|
|
15
|
+
* to redirects regardless.
|
|
16
|
+
*/
|
|
17
|
+
function finalizeAppRscResponse(response, request, options) {
|
|
18
|
+
if (response.status >= 300 && response.status < 400) return response;
|
|
19
|
+
if (!options.configHeaders.length) return response;
|
|
20
|
+
const url = new URL(request.url);
|
|
21
|
+
let pathname;
|
|
22
|
+
try {
|
|
23
|
+
pathname = normalizePath(normalizePathnameForRouteMatch(url.pathname));
|
|
24
|
+
} catch {
|
|
25
|
+
pathname = url.pathname;
|
|
26
|
+
}
|
|
27
|
+
pathname = stripBasePath(pathname, options.basePath);
|
|
28
|
+
applyConfigHeadersToResponse(response.headers, {
|
|
29
|
+
configHeaders: options.configHeaders,
|
|
30
|
+
pathname,
|
|
31
|
+
requestContext: options.requestContext
|
|
32
|
+
});
|
|
33
|
+
return response;
|
|
34
|
+
}
|
|
35
|
+
//#endregion
|
|
36
|
+
export { finalizeAppRscResponse };
|
|
37
|
+
|
|
38
|
+
//# sourceMappingURL=app-rsc-response-finalizer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"app-rsc-response-finalizer.js","names":[],"sources":["../../src/server/app-rsc-response-finalizer.ts"],"sourcesContent":["import type { NextHeader } from \"../config/next-config.js\";\nimport type { RequestContext } from \"../config/config-matchers.js\";\nimport { applyConfigHeadersToResponse } from \"./request-pipeline.js\";\nimport { stripBasePath } from \"../utils/base-path.js\";\nimport { normalizePath } from \"./normalize-path.js\";\nimport { normalizePathnameForRouteMatch } from \"../routing/utils.js\";\n\ntype FinalizeAppRscResponseOptions = {\n basePath: string;\n configHeaders: NextHeader[];\n /**\n * Original pre-middleware request context.\n * Next.js evaluates config header has/missing conditions against the\n * unmodified incoming request, so callers must pass the snapshot taken\n * before middleware runs.\n */\n requestContext: RequestContext;\n};\n\n/**\n * Apply next.config.js response headers to an App Router response.\n *\n * Called once per request in the outer handler() wrapper, after all route\n * handling, so that every response path (page, route handler, server action,\n * metadata, not-found) gets headers applied consistently.\n *\n * Skips 3xx redirect responses. Response.redirect() creates immutable\n * headers that throw on mutation, and Next.js does not apply config headers\n * to redirects regardless.\n */\nexport function finalizeAppRscResponse(\n response: Response,\n request: Request,\n options: FinalizeAppRscResponseOptions,\n): Response {\n // 3xx responses: Response.redirect() headers are immutable (throws on write),\n // and Next.js deliberately excludes config headers from redirect responses.\n if (response.status >= 300 && response.status < 400) {\n return response;\n }\n\n if (!options.configHeaders.length) {\n return response;\n }\n\n const url = new URL(request.url);\n let pathname: string;\n try {\n pathname = normalizePath(normalizePathnameForRouteMatch(url.pathname));\n } catch {\n // Malformed percent-encoding. The request reached this point only because\n // normalizePathnameForRouteMatchStrict ran earlier and returned 400 for\n // truly-malformed paths. This catch exists as a safety net for edge cases;\n // keep the historical raw-path fallback rather than crashing the response.\n pathname = url.pathname;\n }\n\n // Config header sources are defined without basePath prefix. Strip basePath\n // at a segment boundary (not a string prefix) so /app2/page with basePath\n // /app is not incorrectly treated as /app with suffix /2/page.\n pathname = stripBasePath(pathname, options.basePath);\n\n applyConfigHeadersToResponse(response.headers, {\n configHeaders: options.configHeaders,\n pathname,\n requestContext: options.requestContext,\n });\n\n return response;\n}\n"],"mappings":";;;;;;;;;;;;;;;;AA8BA,SAAgB,uBACd,UACA,SACA,SACU;AAGV,KAAI,SAAS,UAAU,OAAO,SAAS,SAAS,IAC9C,QAAO;AAGT,KAAI,CAAC,QAAQ,cAAc,OACzB,QAAO;CAGT,MAAM,MAAM,IAAI,IAAI,QAAQ,IAAI;CAChC,IAAI;AACJ,KAAI;AACF,aAAW,cAAc,+BAA+B,IAAI,SAAS,CAAC;SAChE;AAKN,aAAW,IAAI;;AAMjB,YAAW,cAAc,UAAU,QAAQ,SAAS;AAEpD,8BAA6B,SAAS,SAAS;EAC7C,eAAe,QAAQ;EACvB;EACA,gBAAgB,QAAQ;EACzB,CAAC;AAEF,QAAO"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { FetchCacheMode } from "../shims/fetch-cache.js";
|
|
2
|
+
|
|
3
|
+
//#region src/server/app-segment-config.d.ts
|
|
4
|
+
type AppRouteSegmentDynamic = "auto" | "error" | "force-dynamic" | "force-static";
|
|
5
|
+
type AppRouteSegmentConfigModule = {
|
|
6
|
+
dynamic?: unknown;
|
|
7
|
+
dynamicParams?: unknown;
|
|
8
|
+
fetchCache?: unknown;
|
|
9
|
+
revalidate?: unknown;
|
|
10
|
+
};
|
|
11
|
+
type EffectiveAppPageSegmentConfig = {
|
|
12
|
+
dynamicConfig?: AppRouteSegmentDynamic;
|
|
13
|
+
dynamicParamsConfig?: boolean;
|
|
14
|
+
fetchCache?: FetchCacheMode;
|
|
15
|
+
revalidateSeconds: number | null;
|
|
16
|
+
};
|
|
17
|
+
type ResolveAppPageSegmentConfigOptions = {
|
|
18
|
+
layouts?: readonly (AppRouteSegmentConfigModule | null | undefined)[];
|
|
19
|
+
page?: AppRouteSegmentConfigModule | null;
|
|
20
|
+
};
|
|
21
|
+
/**
|
|
22
|
+
* Resolve the route segment config that applies to an App page route.
|
|
23
|
+
*
|
|
24
|
+
* Next.js collects config from every segment in the loader tree and reduces it
|
|
25
|
+
* into the effective route config. The generated vinext entry already knows
|
|
26
|
+
* the concrete layout/page modules for a route, so it should only describe
|
|
27
|
+
* those modules and delegate the behavior to this helper.
|
|
28
|
+
*/
|
|
29
|
+
declare function resolveAppPageSegmentConfig(options: ResolveAppPageSegmentConfigOptions): EffectiveAppPageSegmentConfig;
|
|
30
|
+
declare function resolveAppPageFetchCacheMode(options: ResolveAppPageSegmentConfigOptions): FetchCacheMode | null;
|
|
31
|
+
//#endregion
|
|
32
|
+
export { resolveAppPageFetchCacheMode, resolveAppPageSegmentConfig };
|
|
33
|
+
//# sourceMappingURL=app-segment-config.d.ts.map
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
//#region src/server/app-segment-config.ts
|
|
2
|
+
const DYNAMIC_VALUES = new Set([
|
|
3
|
+
"auto",
|
|
4
|
+
"error",
|
|
5
|
+
"force-dynamic",
|
|
6
|
+
"force-static"
|
|
7
|
+
]);
|
|
8
|
+
const FETCH_CACHE_VALUES = new Set([
|
|
9
|
+
"auto",
|
|
10
|
+
"default-cache",
|
|
11
|
+
"default-no-store",
|
|
12
|
+
"force-cache",
|
|
13
|
+
"force-no-store",
|
|
14
|
+
"only-cache",
|
|
15
|
+
"only-no-store"
|
|
16
|
+
]);
|
|
17
|
+
function isRouteSegmentDynamic(value) {
|
|
18
|
+
return DYNAMIC_VALUES.has(value);
|
|
19
|
+
}
|
|
20
|
+
function isRouteSegmentFetchCache(value) {
|
|
21
|
+
return FETCH_CACHE_VALUES.has(value);
|
|
22
|
+
}
|
|
23
|
+
function resolveRevalidateSeconds(current, value) {
|
|
24
|
+
if (typeof value !== "number") return current;
|
|
25
|
+
if (current === null) return value;
|
|
26
|
+
return value < current ? value : current;
|
|
27
|
+
}
|
|
28
|
+
function isCacheFetchCacheMode(value) {
|
|
29
|
+
return value === "default-cache" || value === "force-cache" || value === "only-cache";
|
|
30
|
+
}
|
|
31
|
+
function describeFetchCacheConflict(value) {
|
|
32
|
+
return `Route segment config has incompatible fetchCache values including "${value}".`;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Resolve the route segment config that applies to an App page route.
|
|
36
|
+
*
|
|
37
|
+
* Next.js collects config from every segment in the loader tree and reduces it
|
|
38
|
+
* into the effective route config. The generated vinext entry already knows
|
|
39
|
+
* the concrete layout/page modules for a route, so it should only describe
|
|
40
|
+
* those modules and delegate the behavior to this helper.
|
|
41
|
+
*/
|
|
42
|
+
function resolveAppPageSegmentConfig(options) {
|
|
43
|
+
const segments = [...options.layouts ?? [], options.page];
|
|
44
|
+
const config = { revalidateSeconds: null };
|
|
45
|
+
let hasForceCache = false;
|
|
46
|
+
let hasForceNoStore = false;
|
|
47
|
+
let hasOnlyCache = false;
|
|
48
|
+
let hasOnlyNoStore = false;
|
|
49
|
+
let hasParentDefaultNoStore = false;
|
|
50
|
+
for (const segment of segments) {
|
|
51
|
+
if (!segment) continue;
|
|
52
|
+
if (isRouteSegmentDynamic(segment.dynamic)) config.dynamicConfig = segment.dynamic;
|
|
53
|
+
if (segment.dynamicParams === false) config.dynamicParamsConfig = false;
|
|
54
|
+
else if (segment.dynamicParams === true && config.dynamicParamsConfig !== false) config.dynamicParamsConfig = true;
|
|
55
|
+
if (isRouteSegmentFetchCache(segment.fetchCache)) {
|
|
56
|
+
const fetchCache = segment.fetchCache;
|
|
57
|
+
if (hasParentDefaultNoStore && (fetchCache === "auto" || isCacheFetchCacheMode(fetchCache))) throw new Error(describeFetchCacheConflict(fetchCache));
|
|
58
|
+
if (fetchCache === "force-cache") hasForceCache = true;
|
|
59
|
+
if (fetchCache === "force-no-store") hasForceNoStore = true;
|
|
60
|
+
if (fetchCache === "only-cache") hasOnlyCache = true;
|
|
61
|
+
if (fetchCache === "only-no-store") hasOnlyNoStore = true;
|
|
62
|
+
if ((hasForceCache || hasOnlyCache) && (hasForceNoStore || hasOnlyNoStore)) throw new Error(describeFetchCacheConflict(fetchCache));
|
|
63
|
+
if (fetchCache === "default-no-store") hasParentDefaultNoStore = true;
|
|
64
|
+
if (hasForceCache) config.fetchCache = "force-cache";
|
|
65
|
+
else if (hasForceNoStore) config.fetchCache = "force-no-store";
|
|
66
|
+
else if (hasOnlyCache) config.fetchCache = "only-cache";
|
|
67
|
+
else if (hasOnlyNoStore) config.fetchCache = "only-no-store";
|
|
68
|
+
else config.fetchCache = fetchCache;
|
|
69
|
+
}
|
|
70
|
+
config.revalidateSeconds = resolveRevalidateSeconds(config.revalidateSeconds, segment.revalidate);
|
|
71
|
+
}
|
|
72
|
+
if (config.dynamicConfig === "force-dynamic") config.revalidateSeconds = 0;
|
|
73
|
+
if (config.fetchCache === void 0) {
|
|
74
|
+
if (config.dynamicConfig === "force-dynamic") config.fetchCache = "force-no-store";
|
|
75
|
+
else if (config.dynamicConfig === "error") config.fetchCache = "only-cache";
|
|
76
|
+
}
|
|
77
|
+
if (config.dynamicParamsConfig === void 0 && (config.dynamicConfig === "error" || config.dynamicConfig === "force-static")) config.dynamicParamsConfig = false;
|
|
78
|
+
return config;
|
|
79
|
+
}
|
|
80
|
+
function resolveAppPageFetchCacheMode(options) {
|
|
81
|
+
return resolveAppPageSegmentConfig(options).fetchCache ?? null;
|
|
82
|
+
}
|
|
83
|
+
//#endregion
|
|
84
|
+
export { resolveAppPageFetchCacheMode, resolveAppPageSegmentConfig };
|
|
85
|
+
|
|
86
|
+
//# sourceMappingURL=app-segment-config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"app-segment-config.js","names":[],"sources":["../../src/server/app-segment-config.ts"],"sourcesContent":["import type { FetchCacheMode } from \"vinext/shims/fetch-cache\";\n\ntype AppRouteSegmentDynamic = \"auto\" | \"error\" | \"force-dynamic\" | \"force-static\";\n\ntype AppRouteSegmentConfigModule = {\n dynamic?: unknown;\n dynamicParams?: unknown;\n fetchCache?: unknown;\n revalidate?: unknown;\n};\n\ntype EffectiveAppPageSegmentConfig = {\n dynamicConfig?: AppRouteSegmentDynamic;\n dynamicParamsConfig?: boolean;\n fetchCache?: FetchCacheMode;\n revalidateSeconds: number | null;\n};\n\ntype ResolveAppPageSegmentConfigOptions = {\n layouts?: readonly (AppRouteSegmentConfigModule | null | undefined)[];\n page?: AppRouteSegmentConfigModule | null;\n};\n\nconst DYNAMIC_VALUES = new Set<unknown>([\"auto\", \"error\", \"force-dynamic\", \"force-static\"]);\nconst FETCH_CACHE_VALUES = new Set<unknown>([\n \"auto\",\n \"default-cache\",\n \"default-no-store\",\n \"force-cache\",\n \"force-no-store\",\n \"only-cache\",\n \"only-no-store\",\n]);\n\nfunction isRouteSegmentDynamic(value: unknown): value is AppRouteSegmentDynamic {\n return DYNAMIC_VALUES.has(value);\n}\n\nfunction isRouteSegmentFetchCache(value: unknown): value is FetchCacheMode {\n return FETCH_CACHE_VALUES.has(value);\n}\n\nfunction resolveRevalidateSeconds(current: number | null, value: unknown): number | null {\n // TODO: Next.js also accepts `revalidate = false` for indefinite caching.\n // Existing vinext code ignores non-numeric values; keep that behavior until\n // the cache layer can represent \"cache forever\" distinctly from \"unset\".\n if (typeof value !== \"number\") {\n return current;\n }\n\n if (current === null) {\n return value;\n }\n\n return value < current ? value : current;\n}\n\nfunction isCacheFetchCacheMode(value: FetchCacheMode): boolean {\n return value === \"default-cache\" || value === \"force-cache\" || value === \"only-cache\";\n}\n\nfunction describeFetchCacheConflict(value: FetchCacheMode): string {\n return `Route segment config has incompatible fetchCache values including \"${value}\".`;\n}\n\n/**\n * Resolve the route segment config that applies to an App page route.\n *\n * Next.js collects config from every segment in the loader tree and reduces it\n * into the effective route config. The generated vinext entry already knows\n * the concrete layout/page modules for a route, so it should only describe\n * those modules and delegate the behavior to this helper.\n */\nexport function resolveAppPageSegmentConfig(\n options: ResolveAppPageSegmentConfigOptions,\n): EffectiveAppPageSegmentConfig {\n const segments = [...(options.layouts ?? []), options.page];\n // Reduction strategies differ by field:\n // - dynamic: child segments override parents.\n // - dynamicParams: false is sticky across the route tree.\n // - fetchCache: force/only modes take route-level precedence and reject conflicts.\n // - revalidate: the shortest numeric interval wins.\n const config: EffectiveAppPageSegmentConfig = {\n revalidateSeconds: null,\n };\n let hasForceCache = false;\n let hasForceNoStore = false;\n let hasOnlyCache = false;\n let hasOnlyNoStore = false;\n let hasParentDefaultNoStore = false;\n\n for (const segment of segments) {\n if (!segment) continue;\n\n if (isRouteSegmentDynamic(segment.dynamic)) {\n config.dynamicConfig = segment.dynamic;\n }\n\n if (segment.dynamicParams === false) {\n config.dynamicParamsConfig = false;\n } else if (segment.dynamicParams === true && config.dynamicParamsConfig !== false) {\n config.dynamicParamsConfig = true;\n }\n\n if (isRouteSegmentFetchCache(segment.fetchCache)) {\n const fetchCache = segment.fetchCache;\n\n if (hasParentDefaultNoStore && (fetchCache === \"auto\" || isCacheFetchCacheMode(fetchCache))) {\n throw new Error(describeFetchCacheConflict(fetchCache));\n }\n\n if (fetchCache === \"force-cache\") hasForceCache = true;\n if (fetchCache === \"force-no-store\") hasForceNoStore = true;\n if (fetchCache === \"only-cache\") hasOnlyCache = true;\n if (fetchCache === \"only-no-store\") hasOnlyNoStore = true;\n\n const hasCacheEnforcer = hasForceCache || hasOnlyCache;\n const hasNoStoreEnforcer = hasForceNoStore || hasOnlyNoStore;\n if (hasCacheEnforcer && hasNoStoreEnforcer) {\n throw new Error(describeFetchCacheConflict(fetchCache));\n }\n\n if (fetchCache === \"default-no-store\") {\n hasParentDefaultNoStore = true;\n }\n\n if (hasForceCache) {\n config.fetchCache = \"force-cache\";\n } else if (hasForceNoStore) {\n config.fetchCache = \"force-no-store\";\n } else if (hasOnlyCache) {\n config.fetchCache = \"only-cache\";\n } else if (hasOnlyNoStore) {\n config.fetchCache = \"only-no-store\";\n } else {\n config.fetchCache = fetchCache;\n }\n }\n\n config.revalidateSeconds = resolveRevalidateSeconds(\n config.revalidateSeconds,\n segment.revalidate,\n );\n }\n\n if (config.dynamicConfig === \"force-dynamic\") {\n config.revalidateSeconds = 0;\n }\n\n // Top-level dynamic modes supply fetchCache defaults unless a segment does.\n if (config.fetchCache === undefined) {\n if (config.dynamicConfig === \"force-dynamic\") {\n config.fetchCache = \"force-no-store\";\n } else if (config.dynamicConfig === \"error\") {\n config.fetchCache = \"only-cache\";\n }\n }\n\n // Static-only dynamic modes change the default, but explicit dynamicParams wins.\n if (\n config.dynamicParamsConfig === undefined &&\n (config.dynamicConfig === \"error\" || config.dynamicConfig === \"force-static\")\n ) {\n config.dynamicParamsConfig = false;\n }\n\n return config;\n}\n\nexport function resolveAppPageFetchCacheMode(\n options: ResolveAppPageSegmentConfigOptions,\n): FetchCacheMode | null {\n return resolveAppPageSegmentConfig(options).fetchCache ?? null;\n}\n"],"mappings":";AAuBA,MAAM,iBAAiB,IAAI,IAAa;CAAC;CAAQ;CAAS;CAAiB;CAAe,CAAC;AAC3F,MAAM,qBAAqB,IAAI,IAAa;CAC1C;CACA;CACA;CACA;CACA;CACA;CACA;CACD,CAAC;AAEF,SAAS,sBAAsB,OAAiD;AAC9E,QAAO,eAAe,IAAI,MAAM;;AAGlC,SAAS,yBAAyB,OAAyC;AACzE,QAAO,mBAAmB,IAAI,MAAM;;AAGtC,SAAS,yBAAyB,SAAwB,OAA+B;AAIvF,KAAI,OAAO,UAAU,SACnB,QAAO;AAGT,KAAI,YAAY,KACd,QAAO;AAGT,QAAO,QAAQ,UAAU,QAAQ;;AAGnC,SAAS,sBAAsB,OAAgC;AAC7D,QAAO,UAAU,mBAAmB,UAAU,iBAAiB,UAAU;;AAG3E,SAAS,2BAA2B,OAA+B;AACjE,QAAO,sEAAsE,MAAM;;;;;;;;;;AAWrF,SAAgB,4BACd,SAC+B;CAC/B,MAAM,WAAW,CAAC,GAAI,QAAQ,WAAW,EAAE,EAAG,QAAQ,KAAK;CAM3D,MAAM,SAAwC,EAC5C,mBAAmB,MACpB;CACD,IAAI,gBAAgB;CACpB,IAAI,kBAAkB;CACtB,IAAI,eAAe;CACnB,IAAI,iBAAiB;CACrB,IAAI,0BAA0B;AAE9B,MAAK,MAAM,WAAW,UAAU;AAC9B,MAAI,CAAC,QAAS;AAEd,MAAI,sBAAsB,QAAQ,QAAQ,CACxC,QAAO,gBAAgB,QAAQ;AAGjC,MAAI,QAAQ,kBAAkB,MAC5B,QAAO,sBAAsB;WACpB,QAAQ,kBAAkB,QAAQ,OAAO,wBAAwB,MAC1E,QAAO,sBAAsB;AAG/B,MAAI,yBAAyB,QAAQ,WAAW,EAAE;GAChD,MAAM,aAAa,QAAQ;AAE3B,OAAI,4BAA4B,eAAe,UAAU,sBAAsB,WAAW,EACxF,OAAM,IAAI,MAAM,2BAA2B,WAAW,CAAC;AAGzD,OAAI,eAAe,cAAe,iBAAgB;AAClD,OAAI,eAAe,iBAAkB,mBAAkB;AACvD,OAAI,eAAe,aAAc,gBAAe;AAChD,OAAI,eAAe,gBAAiB,kBAAiB;AAIrD,QAFyB,iBAAiB,kBACf,mBAAmB,gBAE5C,OAAM,IAAI,MAAM,2BAA2B,WAAW,CAAC;AAGzD,OAAI,eAAe,mBACjB,2BAA0B;AAG5B,OAAI,cACF,QAAO,aAAa;YACX,gBACT,QAAO,aAAa;YACX,aACT,QAAO,aAAa;YACX,eACT,QAAO,aAAa;OAEpB,QAAO,aAAa;;AAIxB,SAAO,oBAAoB,yBACzB,OAAO,mBACP,QAAQ,WACT;;AAGH,KAAI,OAAO,kBAAkB,gBAC3B,QAAO,oBAAoB;AAI7B,KAAI,OAAO,eAAe,KAAA;MACpB,OAAO,kBAAkB,gBAC3B,QAAO,aAAa;WACX,OAAO,kBAAkB,QAClC,QAAO,aAAa;;AAKxB,KACE,OAAO,wBAAwB,KAAA,MAC9B,OAAO,kBAAkB,WAAW,OAAO,kBAAkB,gBAE9D,QAAO,sBAAsB;AAG/B,QAAO;;AAGT,SAAgB,6BACd,SACuB;AACvB,QAAO,4BAA4B,QAAQ,CAAC,cAAc"}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { FetchCacheMode } from "../shims/fetch-cache.js";
|
|
1
2
|
import { HeadersAccessPhase } from "../shims/headers.js";
|
|
2
3
|
|
|
3
4
|
//#region src/server/app-server-action-execution.d.ts
|
|
@@ -99,6 +100,7 @@ type HandleServerActionRscRequestOptions<TElement, TRoute extends AppServerActio
|
|
|
99
100
|
readFormDataWithLimit: ReadFormDataWithLimit;
|
|
100
101
|
renderToReadableStream: (model: AppServerActionRscModel<TElement>, options: RenderServerActionRscStreamOptions<TTemporaryReferences>) => BodyInit | null | Promise<BodyInit | null>;
|
|
101
102
|
reportRequestError: AppServerActionErrorReporter;
|
|
103
|
+
resolveRouteFetchCacheMode?: (route: TRoute) => FetchCacheMode | null;
|
|
102
104
|
request: Request;
|
|
103
105
|
sanitizeErrorForClient: (error: unknown) => unknown;
|
|
104
106
|
searchParams: URLSearchParams;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { validateCsrfOrigin, validateServerActionPayload } from "./request-pipeline.js";
|
|
2
|
+
import { setCurrentFetchCacheMode } from "../shims/fetch-cache.js";
|
|
2
3
|
import { createServerActionNotFoundResponse, getServerActionNotFoundMessage, isServerActionNotFoundError } from "./server-action-not-found.js";
|
|
3
4
|
import { mergeMiddlewareResponseHeaders } from "./middleware-response-headers.js";
|
|
4
5
|
import { resolveAppPageActionRerenderTarget } from "./app-page-request.js";
|
|
@@ -300,6 +301,7 @@ async function handleServerActionRscRequest(options) {
|
|
|
300
301
|
searchParams: options.searchParams,
|
|
301
302
|
params: actionRerenderTarget.navigationParams
|
|
302
303
|
});
|
|
304
|
+
setCurrentFetchCacheMode(options.resolveRouteFetchCacheMode?.(actionRerenderTarget.route) ?? null);
|
|
303
305
|
element = options.buildPageElement({
|
|
304
306
|
cleanPathname: options.cleanPathname,
|
|
305
307
|
interceptOpts: actionRerenderTarget.interceptOpts,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"app-server-action-execution.js","names":[],"sources":["../../src/server/app-server-action-execution.ts"],"sourcesContent":["import type { HeadersAccessPhase } from \"vinext/shims/headers\";\nimport { resolveAppPageActionRerenderTarget } from \"./app-page-request.js\";\nimport { mergeMiddlewareResponseHeaders } from \"./middleware-response-headers.js\";\nimport { validateCsrfOrigin, validateServerActionPayload } from \"./request-pipeline.js\";\nimport {\n createServerActionNotFoundResponse,\n getServerActionNotFoundMessage,\n isServerActionNotFoundError,\n} from \"./server-action-not-found.js\";\n\ntype AppPageParams = Record<string, string | string[]>;\n\ntype AppServerActionErrorReporter = (\n error: Error,\n request: { path: string; method: string; headers: Record<string, string> },\n route: { routerKind: \"App Router\"; routePath: string; routeType: \"action\" },\n) => void;\n\ntype AppServerActionDecoder = (body: FormData) => Promise<unknown>;\n\ntype ReadFormDataWithLimit = (request: Request, maxBytes: number) => Promise<FormData>;\n\ntype ReadBodyWithLimit = (request: Request, maxBytes: number) => Promise<string>;\n\ntype AppServerActionFunction = (...args: unknown[]) => unknown;\n\ntype AppServerActionReturnValue =\n | {\n data: unknown;\n ok: true;\n }\n | {\n data: unknown;\n ok: false;\n };\n\ntype AppServerActionRedirect = {\n status: number;\n type: string;\n url: string;\n};\n\ntype AppServerActionRoute = {\n pattern: string;\n};\n\ntype AppServerActionMatch<TRoute extends AppServerActionRoute> = {\n params: AppPageParams;\n route: TRoute;\n};\n\ntype AppServerActionIntercept<TPage = unknown> = {\n matchedParams: AppPageParams;\n page: TPage;\n slotKey: string;\n sourceRouteIndex: number;\n};\n\ntype BuildServerActionPageElementOptions<TRoute extends AppServerActionRoute, TInterceptOpts> = {\n cleanPathname: string;\n interceptOpts: TInterceptOpts | undefined;\n isRscRequest: boolean;\n mountedSlotsHeader: string | null;\n params: AppPageParams;\n request: Request;\n route: TRoute;\n searchParams: URLSearchParams;\n};\n\ntype AppServerActionRscModel<TElement> = {\n returnValue: AppServerActionReturnValue;\n root: TElement;\n};\n\ntype RenderServerActionRscStreamOptions<TTemporaryReferences> = {\n onError: (error: unknown) => unknown;\n temporaryReferences: TTemporaryReferences;\n};\n\ntype DecodeServerActionReplyOptions<TTemporaryReferences> = {\n temporaryReferences: TTemporaryReferences;\n};\n\nexport type HandleProgressiveServerActionRequestOptions = {\n actionId: string | null;\n allowedOrigins: string[];\n cleanPathname: string;\n clearRequestContext: () => void;\n contentType: string;\n decodeAction: AppServerActionDecoder;\n getAndClearPendingCookies: () => string[];\n getDraftModeCookieHeader: () => string | null | undefined;\n maxActionBodySize: number;\n middlewareHeaders: Headers | null;\n readFormDataWithLimit: ReadFormDataWithLimit;\n reportRequestError: AppServerActionErrorReporter;\n request: Request;\n setHeadersAccessPhase: (phase: HeadersAccessPhase) => HeadersAccessPhase;\n};\n\nexport type HandleServerActionRscRequestOptions<\n TElement,\n TRoute extends AppServerActionRoute,\n TInterceptOpts,\n TTemporaryReferences,\n TPage = unknown,\n> = {\n actionId: string | null;\n allowedOrigins: string[];\n buildPageElement: (\n options: BuildServerActionPageElementOptions<TRoute, TInterceptOpts>,\n ) => TElement;\n cleanPathname: string;\n clearRequestContext: () => void;\n contentType: string;\n createNotFoundElement: (routeId: string) => TElement;\n createPayloadRouteId: (pathname: string, interceptionContext: string | null) => string;\n createRscOnErrorHandler: (\n request: Request,\n pathname: string,\n pattern: string,\n ) => (error: unknown) => unknown;\n createTemporaryReferenceSet: () => TTemporaryReferences;\n decodeReply: (\n body: string | FormData,\n options: DecodeServerActionReplyOptions<TTemporaryReferences>,\n ) => Promise<unknown[]> | unknown[];\n findIntercept: (pathname: string) => AppServerActionIntercept<TPage> | null;\n getAndClearPendingCookies: () => string[];\n getDraftModeCookieHeader: () => string | null | undefined;\n getRouteParamNames: (route: TRoute) => readonly string[];\n getSourceRoute: (sourceRouteIndex: number) => TRoute | undefined;\n isRscRequest: boolean;\n loadServerAction: (actionId: string) => Promise<unknown>;\n matchRoute: (pathname: string) => AppServerActionMatch<TRoute> | null;\n maxActionBodySize: number;\n middlewareHeaders: Headers | null;\n middlewareStatus: number | null | undefined;\n mountedSlotsHeader: string | null;\n readBodyWithLimit: ReadBodyWithLimit;\n readFormDataWithLimit: ReadFormDataWithLimit;\n renderToReadableStream: (\n model: AppServerActionRscModel<TElement>,\n options: RenderServerActionRscStreamOptions<TTemporaryReferences>,\n ) => BodyInit | null | Promise<BodyInit | null>;\n reportRequestError: AppServerActionErrorReporter;\n request: Request;\n sanitizeErrorForClient: (error: unknown) => unknown;\n searchParams: URLSearchParams;\n setHeadersAccessPhase: (phase: HeadersAccessPhase) => HeadersAccessPhase;\n setNavigationContext: (context: {\n params: AppPageParams;\n pathname: string;\n searchParams: URLSearchParams;\n }) => void;\n toInterceptOpts: (intercept: AppServerActionIntercept<TPage>) => TInterceptOpts;\n};\n\ntype ActionControlResponse =\n | {\n kind: \"redirect\";\n url: string;\n }\n | {\n kind: \"status\";\n statusCode: number;\n };\n\nfunction isRequestBodyTooLarge(error: unknown): boolean {\n return error instanceof Error && error.message === \"Request body too large\";\n}\n\nfunction isAppServerActionFunction(action: unknown): action is AppServerActionFunction {\n return typeof action === \"function\";\n}\n\nfunction normalizeError(error: unknown): Error {\n return error instanceof Error ? error : new Error(String(error));\n}\n\nfunction getServerActionFailureMessage(error: unknown): string {\n return error instanceof Error && error.message ? error.message : String(error);\n}\n\nexport async function readActionBodyWithLimit(request: Request, maxBytes: number): Promise<string> {\n if (!request.body) return \"\";\n\n const reader = request.body.getReader();\n const decoder = new TextDecoder();\n const chunks: string[] = [];\n let totalSize = 0;\n\n for (;;) {\n const result = await reader.read();\n if (result.done) break;\n\n totalSize += result.value.byteLength;\n if (totalSize > maxBytes) {\n await reader.cancel();\n throw new Error(\"Request body too large\");\n }\n chunks.push(decoder.decode(result.value, { stream: true }));\n }\n\n chunks.push(decoder.decode());\n return chunks.join(\"\");\n}\n\nexport async function readActionFormDataWithLimit(\n request: Request,\n maxBytes: number,\n): Promise<FormData> {\n if (!request.body) return new FormData();\n\n const reader = request.body.getReader();\n const chunks: Uint8Array[] = [];\n let totalSize = 0;\n\n for (;;) {\n const result = await reader.read();\n if (result.done) break;\n\n totalSize += result.value.byteLength;\n if (totalSize > maxBytes) {\n await reader.cancel();\n throw new Error(\"Request body too large\");\n }\n chunks.push(result.value);\n }\n\n const combined = new Uint8Array(totalSize);\n let offset = 0;\n for (const chunk of chunks) {\n combined.set(chunk, offset);\n offset += chunk.byteLength;\n }\n\n return new Response(combined, {\n headers: { \"Content-Type\": request.headers.get(\"content-type\") || \"\" },\n }).formData();\n}\n\nfunction getErrorDigest(error: unknown): string | null {\n if (!error || typeof error !== \"object\" || !(\"digest\" in error)) {\n return null;\n }\n\n return String(error.digest);\n}\n\nfunction getActionControlResponse(error: unknown): ActionControlResponse | null {\n const digest = getErrorDigest(error);\n if (!digest) return null;\n\n if (digest.startsWith(\"NEXT_REDIRECT;\")) {\n const parts = digest.split(\";\");\n const encodedUrl = parts[2];\n if (!encodedUrl) {\n return null;\n }\n\n return {\n kind: \"redirect\",\n url: decodeURIComponent(encodedUrl),\n };\n }\n\n if (digest === \"NEXT_NOT_FOUND\" || digest.startsWith(\"NEXT_HTTP_ERROR_FALLBACK;\")) {\n const statusCode = digest === \"NEXT_NOT_FOUND\" ? 404 : parseInt(digest.split(\";\")[1], 10);\n if (!Number.isInteger(statusCode)) {\n return null;\n }\n\n return {\n kind: \"status\",\n statusCode,\n };\n }\n\n return null;\n}\n\nfunction getActionRedirect(error: unknown): AppServerActionRedirect | null {\n const digest = getErrorDigest(error);\n if (!digest?.startsWith(\"NEXT_REDIRECT;\")) {\n return null;\n }\n\n const parts = digest.split(\";\");\n const encodedUrl = parts[2];\n if (!encodedUrl) {\n return null;\n }\n\n return {\n status: parts[3] ? parseInt(parts[3], 10) : 307,\n type: parts[1] || \"push\",\n url: decodeURIComponent(encodedUrl),\n };\n}\n\nfunction isActionHttpFallback(error: unknown): boolean {\n const digest = getErrorDigest(error);\n return digest === \"NEXT_NOT_FOUND\" || digest?.startsWith(\"NEXT_HTTP_ERROR_FALLBACK;\") === true;\n}\n\nfunction createServerActionErrorResponse(\n error: unknown,\n options: {\n cleanPathname: string;\n clearRequestContext: () => void;\n getAndClearPendingCookies: () => string[];\n reportRequestError: AppServerActionErrorReporter;\n request: Request;\n },\n): Response {\n options.getAndClearPendingCookies();\n console.error(\"[vinext] Server action error:\", error);\n options.reportRequestError(\n normalizeError(error),\n {\n path: options.cleanPathname,\n method: options.request.method,\n headers: Object.fromEntries(options.request.headers.entries()),\n },\n { routerKind: \"App Router\", routePath: options.cleanPathname, routeType: \"action\" },\n );\n options.clearRequestContext();\n return new Response(\n process.env.NODE_ENV === \"production\"\n ? \"Internal Server Error\"\n : \"Server action failed: \" + getServerActionFailureMessage(error),\n { status: 500 },\n );\n}\n\nfunction createActionNotFoundResponse(\n actionId: string | null,\n options: {\n clearRequestContext: () => void;\n getAndClearPendingCookies: () => string[];\n },\n): Response {\n options.getAndClearPendingCookies();\n console.warn(getServerActionNotFoundMessage(actionId));\n options.clearRequestContext();\n return createServerActionNotFoundResponse();\n}\n\nexport function isProgressiveServerActionRequest(\n request: Pick<Request, \"method\">,\n contentType: string,\n actionId: string | null,\n): boolean {\n return (\n request.method.toUpperCase() === \"POST\" &&\n contentType.startsWith(\"multipart/form-data\") &&\n !actionId\n );\n}\n\nexport async function handleProgressiveServerActionRequest(\n options: HandleProgressiveServerActionRequestOptions,\n): Promise<Response | null> {\n if (!isProgressiveServerActionRequest(options.request, options.contentType, options.actionId)) {\n return null;\n }\n\n const csrfResponse = validateCsrfOrigin(options.request, options.allowedOrigins);\n if (csrfResponse) {\n return csrfResponse;\n }\n\n const contentLength = parseInt(options.request.headers.get(\"content-length\") || \"0\", 10);\n if (contentLength > options.maxActionBodySize) {\n options.clearRequestContext();\n return new Response(\"Payload Too Large\", { status: 413 });\n }\n\n try {\n let body: FormData;\n try {\n // Progressive submissions can still fall through to a regular page render when\n // the multipart body is not an action payload. Read a clone so that fallback\n // code can still consume the original request body.\n body = await options.readFormDataWithLimit(\n options.request.clone(),\n options.maxActionBodySize,\n );\n } catch (error) {\n if (isRequestBodyTooLarge(error)) {\n options.clearRequestContext();\n return new Response(\"Payload Too Large\", { status: 413 });\n }\n throw error;\n }\n\n const payloadResponse = await validateServerActionPayload(body);\n if (payloadResponse) {\n options.clearRequestContext();\n return payloadResponse;\n }\n\n const action = await options.decodeAction(body);\n if (typeof action !== \"function\") {\n return null;\n }\n\n let actionControlResponse: ActionControlResponse | null = null;\n const previousHeadersPhase = options.setHeadersAccessPhase(\"action\");\n try {\n await action();\n } catch (error) {\n actionControlResponse = getActionControlResponse(error);\n if (!actionControlResponse) {\n throw error;\n }\n } finally {\n options.setHeadersAccessPhase(previousHeadersPhase);\n }\n\n if (!actionControlResponse) {\n // Next.js decodes form state and re-renders after a successful MPA action.\n // vinext currently supports the redirect/error status cases; successful\n // non-redirect actions intentionally fall through to the page render.\n return null;\n }\n\n const actionPendingCookies = options.getAndClearPendingCookies();\n const actionDraftCookie = options.getDraftModeCookieHeader();\n options.clearRequestContext();\n\n const headers = new Headers();\n if (actionControlResponse.kind === \"redirect\") {\n headers.set(\"Location\", new URL(actionControlResponse.url, options.request.url).toString());\n }\n mergeMiddlewareResponseHeaders(headers, options.middlewareHeaders);\n for (const cookie of actionPendingCookies) {\n headers.append(\"Set-Cookie\", cookie);\n }\n if (actionDraftCookie) {\n headers.append(\"Set-Cookie\", actionDraftCookie);\n }\n\n return new Response(null, {\n status: actionControlResponse.kind === \"redirect\" ? 303 : actionControlResponse.statusCode,\n headers,\n });\n } catch (error) {\n if (isServerActionNotFoundError(error, null)) {\n return createActionNotFoundResponse(null, {\n clearRequestContext: options.clearRequestContext,\n getAndClearPendingCookies: options.getAndClearPendingCookies,\n });\n }\n\n options.getAndClearPendingCookies();\n // Next.js rethrows generic MPA action errors into its page render path.\n // vinext does not yet implement that form-state render path, so unexpected\n // action failures remain request failures here.\n console.error(\"[vinext] Server action error:\", error);\n options.reportRequestError(\n normalizeError(error),\n {\n path: options.cleanPathname,\n method: options.request.method,\n headers: Object.fromEntries(options.request.headers.entries()),\n },\n { routerKind: \"App Router\", routePath: options.cleanPathname, routeType: \"action\" },\n );\n options.clearRequestContext();\n return new Response(\n process.env.NODE_ENV === \"production\"\n ? \"Internal Server Error\"\n : \"Server action failed: \" + getServerActionFailureMessage(error),\n { status: 500 },\n );\n }\n}\n\nexport async function handleServerActionRscRequest<\n TElement,\n TRoute extends AppServerActionRoute,\n TInterceptOpts,\n TTemporaryReferences,\n TPage = unknown,\n>(\n options: HandleServerActionRscRequestOptions<\n TElement,\n TRoute,\n TInterceptOpts,\n TTemporaryReferences,\n TPage\n >,\n): Promise<Response | null> {\n if (options.request.method.toUpperCase() !== \"POST\" || !options.actionId) {\n return null;\n }\n\n const csrfResponse = validateCsrfOrigin(options.request, options.allowedOrigins);\n if (csrfResponse) return csrfResponse;\n\n const contentLength = parseInt(options.request.headers.get(\"content-length\") || \"0\", 10);\n if (contentLength > options.maxActionBodySize) {\n options.clearRequestContext();\n return new Response(\"Payload Too Large\", { status: 413 });\n }\n\n try {\n let body: string | FormData;\n try {\n body = options.contentType.startsWith(\"multipart/form-data\")\n ? await options.readFormDataWithLimit(options.request, options.maxActionBodySize)\n : await options.readBodyWithLimit(options.request, options.maxActionBodySize);\n } catch (error) {\n if (isRequestBodyTooLarge(error)) {\n options.clearRequestContext();\n return new Response(\"Payload Too Large\", { status: 413 });\n }\n throw error;\n }\n\n const payloadResponse = await validateServerActionPayload(body);\n if (payloadResponse) {\n options.clearRequestContext();\n return payloadResponse;\n }\n\n let action: unknown;\n try {\n action = await options.loadServerAction(options.actionId);\n } catch (error) {\n if (isServerActionNotFoundError(error, options.actionId)) {\n return createActionNotFoundResponse(options.actionId, {\n clearRequestContext: options.clearRequestContext,\n getAndClearPendingCookies: options.getAndClearPendingCookies,\n });\n }\n\n throw error;\n }\n\n if (!isAppServerActionFunction(action)) {\n return createActionNotFoundResponse(options.actionId, {\n clearRequestContext: options.clearRequestContext,\n getAndClearPendingCookies: options.getAndClearPendingCookies,\n });\n }\n\n const temporaryReferences = options.createTemporaryReferenceSet();\n const args = await options.decodeReply(body, { temporaryReferences });\n let returnValue: AppServerActionReturnValue;\n let actionRedirect: AppServerActionRedirect | null = null;\n const previousHeadersPhase = options.setHeadersAccessPhase(\"action\");\n try {\n try {\n const data = await action.apply(null, args);\n returnValue = { ok: true, data };\n } catch (error) {\n actionRedirect = getActionRedirect(error);\n if (actionRedirect) {\n returnValue = { ok: true, data: undefined };\n } else if (isActionHttpFallback(error)) {\n returnValue = { ok: false, data: error };\n } else {\n console.error(\"[vinext] Server action error:\", error);\n returnValue = { ok: false, data: options.sanitizeErrorForClient(error) };\n }\n }\n } finally {\n options.setHeadersAccessPhase(previousHeadersPhase);\n }\n\n if (actionRedirect) {\n const actionPendingCookies = options.getAndClearPendingCookies();\n const actionDraftCookie = options.getDraftModeCookieHeader();\n options.clearRequestContext();\n const redirectHeaders = new Headers({\n \"Content-Type\": \"text/x-component; charset=utf-8\",\n Vary: \"RSC, Accept\",\n });\n mergeMiddlewareResponseHeaders(redirectHeaders, options.middlewareHeaders);\n redirectHeaders.set(\"x-action-redirect\", actionRedirect.url);\n redirectHeaders.set(\"x-action-redirect-type\", actionRedirect.type);\n redirectHeaders.set(\"x-action-redirect-status\", String(actionRedirect.status));\n for (const cookie of actionPendingCookies) {\n redirectHeaders.append(\"Set-Cookie\", cookie);\n }\n if (actionDraftCookie) redirectHeaders.append(\"Set-Cookie\", actionDraftCookie);\n return new Response(\"\", { status: 200, headers: redirectHeaders });\n }\n\n const match = options.matchRoute(options.cleanPathname);\n let element: TElement;\n let errorPattern = match ? match.route.pattern : options.cleanPathname;\n if (match) {\n const { route: actionRoute, params: actionParams } = match;\n const actionRerenderTarget = resolveAppPageActionRerenderTarget({\n cleanPathname: options.cleanPathname,\n currentParams: actionParams,\n currentRoute: actionRoute,\n findIntercept: options.findIntercept,\n getRouteParamNames: options.getRouteParamNames,\n getSourceRoute: options.getSourceRoute,\n isRscRequest: options.isRscRequest,\n toInterceptOpts: options.toInterceptOpts,\n });\n\n options.setNavigationContext({\n pathname: options.cleanPathname,\n searchParams: options.searchParams,\n params: actionRerenderTarget.navigationParams,\n });\n element = options.buildPageElement({\n cleanPathname: options.cleanPathname,\n interceptOpts: actionRerenderTarget.interceptOpts,\n isRscRequest: options.isRscRequest,\n mountedSlotsHeader: options.mountedSlotsHeader,\n params: actionRerenderTarget.params,\n request: options.request,\n route: actionRerenderTarget.route,\n searchParams: options.searchParams,\n });\n errorPattern = actionRerenderTarget.route.pattern;\n } else {\n const actionRouteId = options.createPayloadRouteId(options.cleanPathname, null);\n element = options.createNotFoundElement(actionRouteId);\n }\n\n const onRenderError = options.createRscOnErrorHandler(\n options.request,\n options.cleanPathname,\n errorPattern,\n );\n const rscStream = await options.renderToReadableStream(\n { root: element, returnValue },\n { temporaryReferences, onError: onRenderError },\n );\n\n const actionPendingCookies = options.getAndClearPendingCookies();\n const actionDraftCookie = options.getDraftModeCookieHeader();\n\n const actionHeaders = new Headers({\n \"Content-Type\": \"text/x-component; charset=utf-8\",\n Vary: \"RSC, Accept\",\n });\n mergeMiddlewareResponseHeaders(actionHeaders, options.middlewareHeaders);\n const actionResponse = new Response(rscStream, {\n status: options.middlewareStatus ?? 200,\n headers: actionHeaders,\n });\n if (actionPendingCookies.length > 0 || actionDraftCookie) {\n for (const cookie of actionPendingCookies) {\n actionResponse.headers.append(\"Set-Cookie\", cookie);\n }\n if (actionDraftCookie) actionResponse.headers.append(\"Set-Cookie\", actionDraftCookie);\n }\n return actionResponse;\n } catch (error) {\n return createServerActionErrorResponse(error, {\n cleanPathname: options.cleanPathname,\n clearRequestContext: options.clearRequestContext,\n getAndClearPendingCookies: options.getAndClearPendingCookies,\n reportRequestError: options.reportRequestError,\n request: options.request,\n });\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"app-server-action-execution.js","names":[],"sources":["../../src/server/app-server-action-execution.ts"],"sourcesContent":["import type { HeadersAccessPhase } from \"vinext/shims/headers\";\nimport { type FetchCacheMode, setCurrentFetchCacheMode } from \"vinext/shims/fetch-cache\";\nimport { resolveAppPageActionRerenderTarget } from \"./app-page-request.js\";\nimport { mergeMiddlewareResponseHeaders } from \"./middleware-response-headers.js\";\nimport { validateCsrfOrigin, validateServerActionPayload } from \"./request-pipeline.js\";\nimport {\n createServerActionNotFoundResponse,\n getServerActionNotFoundMessage,\n isServerActionNotFoundError,\n} from \"./server-action-not-found.js\";\n\ntype AppPageParams = Record<string, string | string[]>;\n\ntype AppServerActionErrorReporter = (\n error: Error,\n request: { path: string; method: string; headers: Record<string, string> },\n route: { routerKind: \"App Router\"; routePath: string; routeType: \"action\" },\n) => void;\n\ntype AppServerActionDecoder = (body: FormData) => Promise<unknown>;\n\ntype ReadFormDataWithLimit = (request: Request, maxBytes: number) => Promise<FormData>;\n\ntype ReadBodyWithLimit = (request: Request, maxBytes: number) => Promise<string>;\n\ntype AppServerActionFunction = (...args: unknown[]) => unknown;\n\ntype AppServerActionReturnValue =\n | {\n data: unknown;\n ok: true;\n }\n | {\n data: unknown;\n ok: false;\n };\n\ntype AppServerActionRedirect = {\n status: number;\n type: string;\n url: string;\n};\n\ntype AppServerActionRoute = {\n pattern: string;\n};\n\ntype AppServerActionMatch<TRoute extends AppServerActionRoute> = {\n params: AppPageParams;\n route: TRoute;\n};\n\ntype AppServerActionIntercept<TPage = unknown> = {\n matchedParams: AppPageParams;\n page: TPage;\n slotKey: string;\n sourceRouteIndex: number;\n};\n\ntype BuildServerActionPageElementOptions<TRoute extends AppServerActionRoute, TInterceptOpts> = {\n cleanPathname: string;\n interceptOpts: TInterceptOpts | undefined;\n isRscRequest: boolean;\n mountedSlotsHeader: string | null;\n params: AppPageParams;\n request: Request;\n route: TRoute;\n searchParams: URLSearchParams;\n};\n\ntype AppServerActionRscModel<TElement> = {\n returnValue: AppServerActionReturnValue;\n root: TElement;\n};\n\ntype RenderServerActionRscStreamOptions<TTemporaryReferences> = {\n onError: (error: unknown) => unknown;\n temporaryReferences: TTemporaryReferences;\n};\n\ntype DecodeServerActionReplyOptions<TTemporaryReferences> = {\n temporaryReferences: TTemporaryReferences;\n};\n\nexport type HandleProgressiveServerActionRequestOptions = {\n actionId: string | null;\n allowedOrigins: string[];\n cleanPathname: string;\n clearRequestContext: () => void;\n contentType: string;\n decodeAction: AppServerActionDecoder;\n getAndClearPendingCookies: () => string[];\n getDraftModeCookieHeader: () => string | null | undefined;\n maxActionBodySize: number;\n middlewareHeaders: Headers | null;\n readFormDataWithLimit: ReadFormDataWithLimit;\n reportRequestError: AppServerActionErrorReporter;\n request: Request;\n setHeadersAccessPhase: (phase: HeadersAccessPhase) => HeadersAccessPhase;\n};\n\nexport type HandleServerActionRscRequestOptions<\n TElement,\n TRoute extends AppServerActionRoute,\n TInterceptOpts,\n TTemporaryReferences,\n TPage = unknown,\n> = {\n actionId: string | null;\n allowedOrigins: string[];\n buildPageElement: (\n options: BuildServerActionPageElementOptions<TRoute, TInterceptOpts>,\n ) => TElement;\n cleanPathname: string;\n clearRequestContext: () => void;\n contentType: string;\n createNotFoundElement: (routeId: string) => TElement;\n createPayloadRouteId: (pathname: string, interceptionContext: string | null) => string;\n createRscOnErrorHandler: (\n request: Request,\n pathname: string,\n pattern: string,\n ) => (error: unknown) => unknown;\n createTemporaryReferenceSet: () => TTemporaryReferences;\n decodeReply: (\n body: string | FormData,\n options: DecodeServerActionReplyOptions<TTemporaryReferences>,\n ) => Promise<unknown[]> | unknown[];\n findIntercept: (pathname: string) => AppServerActionIntercept<TPage> | null;\n getAndClearPendingCookies: () => string[];\n getDraftModeCookieHeader: () => string | null | undefined;\n getRouteParamNames: (route: TRoute) => readonly string[];\n getSourceRoute: (sourceRouteIndex: number) => TRoute | undefined;\n isRscRequest: boolean;\n loadServerAction: (actionId: string) => Promise<unknown>;\n matchRoute: (pathname: string) => AppServerActionMatch<TRoute> | null;\n maxActionBodySize: number;\n middlewareHeaders: Headers | null;\n middlewareStatus: number | null | undefined;\n mountedSlotsHeader: string | null;\n readBodyWithLimit: ReadBodyWithLimit;\n readFormDataWithLimit: ReadFormDataWithLimit;\n renderToReadableStream: (\n model: AppServerActionRscModel<TElement>,\n options: RenderServerActionRscStreamOptions<TTemporaryReferences>,\n ) => BodyInit | null | Promise<BodyInit | null>;\n reportRequestError: AppServerActionErrorReporter;\n resolveRouteFetchCacheMode?: (route: TRoute) => FetchCacheMode | null;\n request: Request;\n sanitizeErrorForClient: (error: unknown) => unknown;\n searchParams: URLSearchParams;\n setHeadersAccessPhase: (phase: HeadersAccessPhase) => HeadersAccessPhase;\n setNavigationContext: (context: {\n params: AppPageParams;\n pathname: string;\n searchParams: URLSearchParams;\n }) => void;\n toInterceptOpts: (intercept: AppServerActionIntercept<TPage>) => TInterceptOpts;\n};\n\ntype ActionControlResponse =\n | {\n kind: \"redirect\";\n url: string;\n }\n | {\n kind: \"status\";\n statusCode: number;\n };\n\nfunction isRequestBodyTooLarge(error: unknown): boolean {\n return error instanceof Error && error.message === \"Request body too large\";\n}\n\nfunction isAppServerActionFunction(action: unknown): action is AppServerActionFunction {\n return typeof action === \"function\";\n}\n\nfunction normalizeError(error: unknown): Error {\n return error instanceof Error ? error : new Error(String(error));\n}\n\nfunction getServerActionFailureMessage(error: unknown): string {\n return error instanceof Error && error.message ? error.message : String(error);\n}\n\nexport async function readActionBodyWithLimit(request: Request, maxBytes: number): Promise<string> {\n if (!request.body) return \"\";\n\n const reader = request.body.getReader();\n const decoder = new TextDecoder();\n const chunks: string[] = [];\n let totalSize = 0;\n\n for (;;) {\n const result = await reader.read();\n if (result.done) break;\n\n totalSize += result.value.byteLength;\n if (totalSize > maxBytes) {\n await reader.cancel();\n throw new Error(\"Request body too large\");\n }\n chunks.push(decoder.decode(result.value, { stream: true }));\n }\n\n chunks.push(decoder.decode());\n return chunks.join(\"\");\n}\n\nexport async function readActionFormDataWithLimit(\n request: Request,\n maxBytes: number,\n): Promise<FormData> {\n if (!request.body) return new FormData();\n\n const reader = request.body.getReader();\n const chunks: Uint8Array[] = [];\n let totalSize = 0;\n\n for (;;) {\n const result = await reader.read();\n if (result.done) break;\n\n totalSize += result.value.byteLength;\n if (totalSize > maxBytes) {\n await reader.cancel();\n throw new Error(\"Request body too large\");\n }\n chunks.push(result.value);\n }\n\n const combined = new Uint8Array(totalSize);\n let offset = 0;\n for (const chunk of chunks) {\n combined.set(chunk, offset);\n offset += chunk.byteLength;\n }\n\n return new Response(combined, {\n headers: { \"Content-Type\": request.headers.get(\"content-type\") || \"\" },\n }).formData();\n}\n\nfunction getErrorDigest(error: unknown): string | null {\n if (!error || typeof error !== \"object\" || !(\"digest\" in error)) {\n return null;\n }\n\n return String(error.digest);\n}\n\nfunction getActionControlResponse(error: unknown): ActionControlResponse | null {\n const digest = getErrorDigest(error);\n if (!digest) return null;\n\n if (digest.startsWith(\"NEXT_REDIRECT;\")) {\n const parts = digest.split(\";\");\n const encodedUrl = parts[2];\n if (!encodedUrl) {\n return null;\n }\n\n return {\n kind: \"redirect\",\n url: decodeURIComponent(encodedUrl),\n };\n }\n\n if (digest === \"NEXT_NOT_FOUND\" || digest.startsWith(\"NEXT_HTTP_ERROR_FALLBACK;\")) {\n const statusCode = digest === \"NEXT_NOT_FOUND\" ? 404 : parseInt(digest.split(\";\")[1], 10);\n if (!Number.isInteger(statusCode)) {\n return null;\n }\n\n return {\n kind: \"status\",\n statusCode,\n };\n }\n\n return null;\n}\n\nfunction getActionRedirect(error: unknown): AppServerActionRedirect | null {\n const digest = getErrorDigest(error);\n if (!digest?.startsWith(\"NEXT_REDIRECT;\")) {\n return null;\n }\n\n const parts = digest.split(\";\");\n const encodedUrl = parts[2];\n if (!encodedUrl) {\n return null;\n }\n\n return {\n status: parts[3] ? parseInt(parts[3], 10) : 307,\n type: parts[1] || \"push\",\n url: decodeURIComponent(encodedUrl),\n };\n}\n\nfunction isActionHttpFallback(error: unknown): boolean {\n const digest = getErrorDigest(error);\n return digest === \"NEXT_NOT_FOUND\" || digest?.startsWith(\"NEXT_HTTP_ERROR_FALLBACK;\") === true;\n}\n\nfunction createServerActionErrorResponse(\n error: unknown,\n options: {\n cleanPathname: string;\n clearRequestContext: () => void;\n getAndClearPendingCookies: () => string[];\n reportRequestError: AppServerActionErrorReporter;\n request: Request;\n },\n): Response {\n options.getAndClearPendingCookies();\n console.error(\"[vinext] Server action error:\", error);\n options.reportRequestError(\n normalizeError(error),\n {\n path: options.cleanPathname,\n method: options.request.method,\n headers: Object.fromEntries(options.request.headers.entries()),\n },\n { routerKind: \"App Router\", routePath: options.cleanPathname, routeType: \"action\" },\n );\n options.clearRequestContext();\n return new Response(\n process.env.NODE_ENV === \"production\"\n ? \"Internal Server Error\"\n : \"Server action failed: \" + getServerActionFailureMessage(error),\n { status: 500 },\n );\n}\n\nfunction createActionNotFoundResponse(\n actionId: string | null,\n options: {\n clearRequestContext: () => void;\n getAndClearPendingCookies: () => string[];\n },\n): Response {\n options.getAndClearPendingCookies();\n console.warn(getServerActionNotFoundMessage(actionId));\n options.clearRequestContext();\n return createServerActionNotFoundResponse();\n}\n\nexport function isProgressiveServerActionRequest(\n request: Pick<Request, \"method\">,\n contentType: string,\n actionId: string | null,\n): boolean {\n return (\n request.method.toUpperCase() === \"POST\" &&\n contentType.startsWith(\"multipart/form-data\") &&\n !actionId\n );\n}\n\nexport async function handleProgressiveServerActionRequest(\n options: HandleProgressiveServerActionRequestOptions,\n): Promise<Response | null> {\n if (!isProgressiveServerActionRequest(options.request, options.contentType, options.actionId)) {\n return null;\n }\n\n const csrfResponse = validateCsrfOrigin(options.request, options.allowedOrigins);\n if (csrfResponse) {\n return csrfResponse;\n }\n\n const contentLength = parseInt(options.request.headers.get(\"content-length\") || \"0\", 10);\n if (contentLength > options.maxActionBodySize) {\n options.clearRequestContext();\n return new Response(\"Payload Too Large\", { status: 413 });\n }\n\n try {\n let body: FormData;\n try {\n // Progressive submissions can still fall through to a regular page render when\n // the multipart body is not an action payload. Read a clone so that fallback\n // code can still consume the original request body.\n body = await options.readFormDataWithLimit(\n options.request.clone(),\n options.maxActionBodySize,\n );\n } catch (error) {\n if (isRequestBodyTooLarge(error)) {\n options.clearRequestContext();\n return new Response(\"Payload Too Large\", { status: 413 });\n }\n throw error;\n }\n\n const payloadResponse = await validateServerActionPayload(body);\n if (payloadResponse) {\n options.clearRequestContext();\n return payloadResponse;\n }\n\n const action = await options.decodeAction(body);\n if (typeof action !== \"function\") {\n return null;\n }\n\n let actionControlResponse: ActionControlResponse | null = null;\n const previousHeadersPhase = options.setHeadersAccessPhase(\"action\");\n try {\n await action();\n } catch (error) {\n actionControlResponse = getActionControlResponse(error);\n if (!actionControlResponse) {\n throw error;\n }\n } finally {\n options.setHeadersAccessPhase(previousHeadersPhase);\n }\n\n if (!actionControlResponse) {\n // Next.js decodes form state and re-renders after a successful MPA action.\n // vinext currently supports the redirect/error status cases; successful\n // non-redirect actions intentionally fall through to the page render.\n return null;\n }\n\n const actionPendingCookies = options.getAndClearPendingCookies();\n const actionDraftCookie = options.getDraftModeCookieHeader();\n options.clearRequestContext();\n\n const headers = new Headers();\n if (actionControlResponse.kind === \"redirect\") {\n headers.set(\"Location\", new URL(actionControlResponse.url, options.request.url).toString());\n }\n mergeMiddlewareResponseHeaders(headers, options.middlewareHeaders);\n for (const cookie of actionPendingCookies) {\n headers.append(\"Set-Cookie\", cookie);\n }\n if (actionDraftCookie) {\n headers.append(\"Set-Cookie\", actionDraftCookie);\n }\n\n return new Response(null, {\n status: actionControlResponse.kind === \"redirect\" ? 303 : actionControlResponse.statusCode,\n headers,\n });\n } catch (error) {\n if (isServerActionNotFoundError(error, null)) {\n return createActionNotFoundResponse(null, {\n clearRequestContext: options.clearRequestContext,\n getAndClearPendingCookies: options.getAndClearPendingCookies,\n });\n }\n\n options.getAndClearPendingCookies();\n // Next.js rethrows generic MPA action errors into its page render path.\n // vinext does not yet implement that form-state render path, so unexpected\n // action failures remain request failures here.\n console.error(\"[vinext] Server action error:\", error);\n options.reportRequestError(\n normalizeError(error),\n {\n path: options.cleanPathname,\n method: options.request.method,\n headers: Object.fromEntries(options.request.headers.entries()),\n },\n { routerKind: \"App Router\", routePath: options.cleanPathname, routeType: \"action\" },\n );\n options.clearRequestContext();\n return new Response(\n process.env.NODE_ENV === \"production\"\n ? \"Internal Server Error\"\n : \"Server action failed: \" + getServerActionFailureMessage(error),\n { status: 500 },\n );\n }\n}\n\nexport async function handleServerActionRscRequest<\n TElement,\n TRoute extends AppServerActionRoute,\n TInterceptOpts,\n TTemporaryReferences,\n TPage = unknown,\n>(\n options: HandleServerActionRscRequestOptions<\n TElement,\n TRoute,\n TInterceptOpts,\n TTemporaryReferences,\n TPage\n >,\n): Promise<Response | null> {\n if (options.request.method.toUpperCase() !== \"POST\" || !options.actionId) {\n return null;\n }\n\n const csrfResponse = validateCsrfOrigin(options.request, options.allowedOrigins);\n if (csrfResponse) return csrfResponse;\n\n const contentLength = parseInt(options.request.headers.get(\"content-length\") || \"0\", 10);\n if (contentLength > options.maxActionBodySize) {\n options.clearRequestContext();\n return new Response(\"Payload Too Large\", { status: 413 });\n }\n\n try {\n let body: string | FormData;\n try {\n body = options.contentType.startsWith(\"multipart/form-data\")\n ? await options.readFormDataWithLimit(options.request, options.maxActionBodySize)\n : await options.readBodyWithLimit(options.request, options.maxActionBodySize);\n } catch (error) {\n if (isRequestBodyTooLarge(error)) {\n options.clearRequestContext();\n return new Response(\"Payload Too Large\", { status: 413 });\n }\n throw error;\n }\n\n const payloadResponse = await validateServerActionPayload(body);\n if (payloadResponse) {\n options.clearRequestContext();\n return payloadResponse;\n }\n\n let action: unknown;\n try {\n action = await options.loadServerAction(options.actionId);\n } catch (error) {\n if (isServerActionNotFoundError(error, options.actionId)) {\n return createActionNotFoundResponse(options.actionId, {\n clearRequestContext: options.clearRequestContext,\n getAndClearPendingCookies: options.getAndClearPendingCookies,\n });\n }\n\n throw error;\n }\n\n if (!isAppServerActionFunction(action)) {\n return createActionNotFoundResponse(options.actionId, {\n clearRequestContext: options.clearRequestContext,\n getAndClearPendingCookies: options.getAndClearPendingCookies,\n });\n }\n\n const temporaryReferences = options.createTemporaryReferenceSet();\n const args = await options.decodeReply(body, { temporaryReferences });\n let returnValue: AppServerActionReturnValue;\n let actionRedirect: AppServerActionRedirect | null = null;\n const previousHeadersPhase = options.setHeadersAccessPhase(\"action\");\n try {\n try {\n const data = await action.apply(null, args);\n returnValue = { ok: true, data };\n } catch (error) {\n actionRedirect = getActionRedirect(error);\n if (actionRedirect) {\n returnValue = { ok: true, data: undefined };\n } else if (isActionHttpFallback(error)) {\n returnValue = { ok: false, data: error };\n } else {\n console.error(\"[vinext] Server action error:\", error);\n returnValue = { ok: false, data: options.sanitizeErrorForClient(error) };\n }\n }\n } finally {\n options.setHeadersAccessPhase(previousHeadersPhase);\n }\n\n if (actionRedirect) {\n const actionPendingCookies = options.getAndClearPendingCookies();\n const actionDraftCookie = options.getDraftModeCookieHeader();\n options.clearRequestContext();\n const redirectHeaders = new Headers({\n \"Content-Type\": \"text/x-component; charset=utf-8\",\n Vary: \"RSC, Accept\",\n });\n mergeMiddlewareResponseHeaders(redirectHeaders, options.middlewareHeaders);\n redirectHeaders.set(\"x-action-redirect\", actionRedirect.url);\n redirectHeaders.set(\"x-action-redirect-type\", actionRedirect.type);\n redirectHeaders.set(\"x-action-redirect-status\", String(actionRedirect.status));\n for (const cookie of actionPendingCookies) {\n redirectHeaders.append(\"Set-Cookie\", cookie);\n }\n if (actionDraftCookie) redirectHeaders.append(\"Set-Cookie\", actionDraftCookie);\n return new Response(\"\", { status: 200, headers: redirectHeaders });\n }\n\n const match = options.matchRoute(options.cleanPathname);\n let element: TElement;\n let errorPattern = match ? match.route.pattern : options.cleanPathname;\n if (match) {\n const { route: actionRoute, params: actionParams } = match;\n const actionRerenderTarget = resolveAppPageActionRerenderTarget({\n cleanPathname: options.cleanPathname,\n currentParams: actionParams,\n currentRoute: actionRoute,\n findIntercept: options.findIntercept,\n getRouteParamNames: options.getRouteParamNames,\n getSourceRoute: options.getSourceRoute,\n isRscRequest: options.isRscRequest,\n toInterceptOpts: options.toInterceptOpts,\n });\n\n options.setNavigationContext({\n pathname: options.cleanPathname,\n searchParams: options.searchParams,\n params: actionRerenderTarget.navigationParams,\n });\n setCurrentFetchCacheMode(\n options.resolveRouteFetchCacheMode?.(actionRerenderTarget.route) ?? null,\n );\n element = options.buildPageElement({\n cleanPathname: options.cleanPathname,\n interceptOpts: actionRerenderTarget.interceptOpts,\n isRscRequest: options.isRscRequest,\n mountedSlotsHeader: options.mountedSlotsHeader,\n params: actionRerenderTarget.params,\n request: options.request,\n route: actionRerenderTarget.route,\n searchParams: options.searchParams,\n });\n errorPattern = actionRerenderTarget.route.pattern;\n } else {\n const actionRouteId = options.createPayloadRouteId(options.cleanPathname, null);\n element = options.createNotFoundElement(actionRouteId);\n }\n\n const onRenderError = options.createRscOnErrorHandler(\n options.request,\n options.cleanPathname,\n errorPattern,\n );\n const rscStream = await options.renderToReadableStream(\n { root: element, returnValue },\n { temporaryReferences, onError: onRenderError },\n );\n\n const actionPendingCookies = options.getAndClearPendingCookies();\n const actionDraftCookie = options.getDraftModeCookieHeader();\n\n const actionHeaders = new Headers({\n \"Content-Type\": \"text/x-component; charset=utf-8\",\n Vary: \"RSC, Accept\",\n });\n mergeMiddlewareResponseHeaders(actionHeaders, options.middlewareHeaders);\n const actionResponse = new Response(rscStream, {\n status: options.middlewareStatus ?? 200,\n headers: actionHeaders,\n });\n if (actionPendingCookies.length > 0 || actionDraftCookie) {\n for (const cookie of actionPendingCookies) {\n actionResponse.headers.append(\"Set-Cookie\", cookie);\n }\n if (actionDraftCookie) actionResponse.headers.append(\"Set-Cookie\", actionDraftCookie);\n }\n return actionResponse;\n } catch (error) {\n return createServerActionErrorResponse(error, {\n cleanPathname: options.cleanPathname,\n clearRequestContext: options.clearRequestContext,\n getAndClearPendingCookies: options.getAndClearPendingCookies,\n reportRequestError: options.reportRequestError,\n request: options.request,\n });\n }\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
//#region src/server/cache-control.d.ts
|
|
2
|
+
declare const NEVER_CACHE_CONTROL = "private, no-cache, no-store, max-age=0, must-revalidate";
|
|
3
|
+
declare const STATIC_CACHE_CONTROL = "s-maxage=31536000, stale-while-revalidate";
|
|
4
|
+
declare const NO_STORE_CACHE_CONTROL = "no-store, must-revalidate";
|
|
5
|
+
/**
|
|
6
|
+
* Matches Next.js's `getCacheControlHeader` stale window semantics while
|
|
7
|
+
* preserving vinext's legacy unbounded SWR header when no expire ceiling is
|
|
8
|
+
* available yet.
|
|
9
|
+
*
|
|
10
|
+
* Next.js source:
|
|
11
|
+
* https://github.com/vercel/next.js/blob/canary/packages/next/src/server/lib/cache-control.ts
|
|
12
|
+
*/
|
|
13
|
+
declare function buildRevalidateCacheControl(revalidateSeconds: number, expireSeconds?: number): string;
|
|
14
|
+
/**
|
|
15
|
+
* Builds Cache-Control for ISR cache reads. HIT responses and STALE responses
|
|
16
|
+
* with stored expire metadata use the same route policy because Next.js derives
|
|
17
|
+
* this header from cache-control metadata, not from the cache hit/stale state.
|
|
18
|
+
* STALE entries without expire metadata keep vinext's legacy `s-maxage=0`
|
|
19
|
+
* fallback so older cache entries are not treated as newly fresh downstream.
|
|
20
|
+
*/
|
|
21
|
+
declare function buildCachedRevalidateCacheControl(cacheState: "HIT" | "STALE", revalidateSeconds: number, expireSeconds?: number): string;
|
|
22
|
+
//#endregion
|
|
23
|
+
export { NEVER_CACHE_CONTROL, NO_STORE_CACHE_CONTROL, STATIC_CACHE_CONTROL, buildCachedRevalidateCacheControl, buildRevalidateCacheControl };
|
|
24
|
+
//# sourceMappingURL=cache-control.d.ts.map
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
//#region src/server/cache-control.ts
|
|
2
|
+
const NEVER_CACHE_CONTROL = "private, no-cache, no-store, max-age=0, must-revalidate";
|
|
3
|
+
const STATIC_CACHE_CONTROL = "s-maxage=31536000, stale-while-revalidate";
|
|
4
|
+
const STALE_REVALIDATE_CACHE_CONTROL = "s-maxage=0, stale-while-revalidate";
|
|
5
|
+
const NO_STORE_CACHE_CONTROL = "no-store, must-revalidate";
|
|
6
|
+
/**
|
|
7
|
+
* Matches Next.js's `getCacheControlHeader` stale window semantics while
|
|
8
|
+
* preserving vinext's legacy unbounded SWR header when no expire ceiling is
|
|
9
|
+
* available yet.
|
|
10
|
+
*
|
|
11
|
+
* Next.js source:
|
|
12
|
+
* https://github.com/vercel/next.js/blob/canary/packages/next/src/server/lib/cache-control.ts
|
|
13
|
+
*/
|
|
14
|
+
function buildRevalidateCacheControl(revalidateSeconds, expireSeconds) {
|
|
15
|
+
if (expireSeconds === void 0) return `s-maxage=${revalidateSeconds}, stale-while-revalidate`;
|
|
16
|
+
if (revalidateSeconds >= expireSeconds) return `s-maxage=${revalidateSeconds}`;
|
|
17
|
+
return `s-maxage=${revalidateSeconds}, stale-while-revalidate=${expireSeconds - revalidateSeconds}`;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Builds Cache-Control for ISR cache reads. HIT responses and STALE responses
|
|
21
|
+
* with stored expire metadata use the same route policy because Next.js derives
|
|
22
|
+
* this header from cache-control metadata, not from the cache hit/stale state.
|
|
23
|
+
* STALE entries without expire metadata keep vinext's legacy `s-maxage=0`
|
|
24
|
+
* fallback so older cache entries are not treated as newly fresh downstream.
|
|
25
|
+
*/
|
|
26
|
+
function buildCachedRevalidateCacheControl(cacheState, revalidateSeconds, expireSeconds) {
|
|
27
|
+
if (cacheState === "STALE" && expireSeconds === void 0) return STALE_REVALIDATE_CACHE_CONTROL;
|
|
28
|
+
return buildRevalidateCacheControl(revalidateSeconds, expireSeconds);
|
|
29
|
+
}
|
|
30
|
+
//#endregion
|
|
31
|
+
export { NEVER_CACHE_CONTROL, NO_STORE_CACHE_CONTROL, STATIC_CACHE_CONTROL, buildCachedRevalidateCacheControl, buildRevalidateCacheControl };
|
|
32
|
+
|
|
33
|
+
//# sourceMappingURL=cache-control.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cache-control.js","names":[],"sources":["../../src/server/cache-control.ts"],"sourcesContent":["export const NEVER_CACHE_CONTROL = \"private, no-cache, no-store, max-age=0, must-revalidate\";\n\nexport const STATIC_CACHE_CONTROL = \"s-maxage=31536000, stale-while-revalidate\";\n\nconst STALE_REVALIDATE_CACHE_CONTROL = \"s-maxage=0, stale-while-revalidate\";\n\nexport const NO_STORE_CACHE_CONTROL = \"no-store, must-revalidate\";\n\n/**\n * Matches Next.js's `getCacheControlHeader` stale window semantics while\n * preserving vinext's legacy unbounded SWR header when no expire ceiling is\n * available yet.\n *\n * Next.js source:\n * https://github.com/vercel/next.js/blob/canary/packages/next/src/server/lib/cache-control.ts\n */\nexport function buildRevalidateCacheControl(\n revalidateSeconds: number,\n expireSeconds?: number,\n): string {\n if (expireSeconds === undefined) {\n return `s-maxage=${revalidateSeconds}, stale-while-revalidate`;\n }\n\n // `expire <= revalidate` is a zero-width stale window: downstream caches\n // should refetch after s-maxage instead of serving stale.\n if (revalidateSeconds >= expireSeconds) {\n return `s-maxage=${revalidateSeconds}`;\n }\n\n return `s-maxage=${revalidateSeconds}, stale-while-revalidate=${\n expireSeconds - revalidateSeconds\n }`;\n}\n\n/**\n * Builds Cache-Control for ISR cache reads. HIT responses and STALE responses\n * with stored expire metadata use the same route policy because Next.js derives\n * this header from cache-control metadata, not from the cache hit/stale state.\n * STALE entries without expire metadata keep vinext's legacy `s-maxage=0`\n * fallback so older cache entries are not treated as newly fresh downstream.\n */\nexport function buildCachedRevalidateCacheControl(\n cacheState: \"HIT\" | \"STALE\",\n revalidateSeconds: number,\n expireSeconds?: number,\n): string {\n // When expire is known, match Next.js and emit the route policy even for\n // vinext-served STALE entries. The hard-expire gate has already decided the\n // stale payload is still usable, and downstream caches should see the same\n // finite SWR window Next.js would emit from cacheControl metadata.\n if (cacheState === \"STALE\" && expireSeconds === undefined) {\n return STALE_REVALIDATE_CACHE_CONTROL;\n }\n\n return buildRevalidateCacheControl(revalidateSeconds, expireSeconds);\n}\n"],"mappings":";AAAA,MAAa,sBAAsB;AAEnC,MAAa,uBAAuB;AAEpC,MAAM,iCAAiC;AAEvC,MAAa,yBAAyB;;;;;;;;;AAUtC,SAAgB,4BACd,mBACA,eACQ;AACR,KAAI,kBAAkB,KAAA,EACpB,QAAO,YAAY,kBAAkB;AAKvC,KAAI,qBAAqB,cACvB,QAAO,YAAY;AAGrB,QAAO,YAAY,kBAAkB,2BACnC,gBAAgB;;;;;;;;;AAWpB,SAAgB,kCACd,YACA,mBACA,eACQ;AAKR,KAAI,eAAe,WAAW,kBAAkB,KAAA,EAC9C,QAAO;AAGT,QAAO,4BAA4B,mBAAmB,cAAc"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
//#region src/server/dev-error-overlay-store.d.ts
|
|
2
|
+
type Source = "uncaught" | "caught" | "window-error" | "unhandledrejection";
|
|
3
|
+
type ReportedError = {
|
|
4
|
+
source: Source;
|
|
5
|
+
message: string;
|
|
6
|
+
stack: string | undefined;
|
|
7
|
+
componentStack: string | undefined;
|
|
8
|
+
};
|
|
9
|
+
type OverlayState = {
|
|
10
|
+
errors: ReportedError[];
|
|
11
|
+
index: number;
|
|
12
|
+
minimized: boolean;
|
|
13
|
+
};
|
|
14
|
+
declare function subscribeOverlay(fn: () => void): () => void;
|
|
15
|
+
declare function getOverlaySnapshot(): OverlayState;
|
|
16
|
+
declare function reportToOverlay(error: ReportedError): void;
|
|
17
|
+
declare function dismissOverlay(): void;
|
|
18
|
+
declare function setOverlayIndex(index: number): void;
|
|
19
|
+
declare function minimizeOverlay(): void;
|
|
20
|
+
declare function expandOverlay(): void;
|
|
21
|
+
//#endregion
|
|
22
|
+
export { OverlayState, ReportedError, Source, dismissOverlay, expandOverlay, getOverlaySnapshot, minimizeOverlay, reportToOverlay, setOverlayIndex, subscribeOverlay };
|
|
23
|
+
//# sourceMappingURL=dev-error-overlay-store.d.ts.map
|