vinext 0.0.10 → 0.0.12
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/dist/cli.js +4 -4
- package/dist/cli.js.map +1 -1
- package/dist/config/config-matchers.d.ts +11 -2
- package/dist/config/config-matchers.d.ts.map +1 -1
- package/dist/config/config-matchers.js +167 -45
- package/dist/config/config-matchers.js.map +1 -1
- package/dist/deploy.d.ts.map +1 -1
- package/dist/deploy.js +6 -4
- package/dist/deploy.js.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +94 -51
- package/dist/index.js.map +1 -1
- package/dist/init.d.ts.map +1 -1
- package/dist/init.js +3 -2
- package/dist/init.js.map +1 -1
- package/dist/server/app-dev-server.d.ts.map +1 -1
- package/dist/server/app-dev-server.js +34 -9
- package/dist/server/app-dev-server.js.map +1 -1
- package/dist/server/app-router-entry.d.ts.map +1 -1
- package/dist/server/app-router-entry.js +8 -1
- package/dist/server/app-router-entry.js.map +1 -1
- package/dist/server/dev-server.d.ts.map +1 -1
- package/dist/server/dev-server.js +14 -2
- package/dist/server/dev-server.js.map +1 -1
- package/dist/server/middleware-codegen.d.ts.map +1 -1
- package/dist/server/middleware-codegen.js +13 -7
- package/dist/server/middleware-codegen.js.map +1 -1
- package/dist/server/middleware.d.ts.map +1 -1
- package/dist/server/middleware.js +34 -13
- package/dist/server/middleware.js.map +1 -1
- package/dist/server/prod-server.d.ts.map +1 -1
- package/dist/server/prod-server.js +26 -3
- package/dist/server/prod-server.js.map +1 -1
- package/dist/shims/fetch-cache.d.ts.map +1 -1
- package/dist/shims/fetch-cache.js +153 -55
- package/dist/shims/fetch-cache.js.map +1 -1
- package/dist/shims/head.d.ts.map +1 -1
- package/dist/shims/head.js +4 -1
- package/dist/shims/head.js.map +1 -1
- package/dist/shims/headers.d.ts.map +1 -1
- package/dist/shims/headers.js +4 -2
- package/dist/shims/headers.js.map +1 -1
- package/dist/shims/navigation.js +2 -2
- package/dist/shims/navigation.js.map +1 -1
- package/dist/shims/router.d.ts +1 -1
- package/dist/shims/router.d.ts.map +1 -1
- package/dist/shims/router.js +2 -2
- package/dist/shims/router.js.map +1 -1
- package/package.json +1 -1
|
@@ -24,67 +24,150 @@ import { AsyncLocalStorage } from "node:async_hooks";
|
|
|
24
24
|
// Cache key generation
|
|
25
25
|
// ---------------------------------------------------------------------------
|
|
26
26
|
/**
|
|
27
|
-
* Headers
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
* Checked case-insensitively to match HTTP header semantics.
|
|
27
|
+
* Headers excluded from the cache key. These are W3C trace context headers
|
|
28
|
+
* that can break request caching and deduplication.
|
|
29
|
+
* All other headers ARE included in the cache key, matching Next.js behavior.
|
|
32
30
|
*/
|
|
33
|
-
const
|
|
31
|
+
const HEADER_BLOCKLIST = ["traceparent", "tracestate"];
|
|
32
|
+
// Cache key version — bump when changing the key format to bust stale entries
|
|
33
|
+
const CACHE_KEY_PREFIX = "v1";
|
|
34
34
|
/**
|
|
35
|
-
*
|
|
36
|
-
*
|
|
37
|
-
*
|
|
35
|
+
* Collect all headers from the request, excluding the blocklist.
|
|
36
|
+
* Merges headers from both the Request object and the init object,
|
|
37
|
+
* with init taking precedence (matching fetch() spec behavior).
|
|
38
38
|
*/
|
|
39
|
-
function
|
|
40
|
-
const
|
|
41
|
-
//
|
|
39
|
+
function collectHeaders(input, init) {
|
|
40
|
+
const merged = {};
|
|
41
|
+
// Start with headers from Request object (if any)
|
|
42
|
+
if (input instanceof Request && input.headers) {
|
|
43
|
+
input.headers.forEach((v, k) => { merged[k] = v; });
|
|
44
|
+
}
|
|
45
|
+
// Override with headers from init (init takes precedence per fetch spec)
|
|
42
46
|
if (init?.headers) {
|
|
43
47
|
const headers = init.headers instanceof Headers
|
|
44
48
|
? init.headers
|
|
45
49
|
: new Headers(init.headers);
|
|
46
|
-
|
|
47
|
-
const value = headers.get(name);
|
|
48
|
-
if (value)
|
|
49
|
-
collected.push([name, value]);
|
|
50
|
-
}
|
|
50
|
+
headers.forEach((v, k) => { merged[k] = v; });
|
|
51
51
|
}
|
|
52
|
-
//
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
// Don't duplicate if already found in init
|
|
56
|
-
if (collected.some(([n]) => n === name))
|
|
57
|
-
continue;
|
|
58
|
-
const value = input.headers.get(name);
|
|
59
|
-
if (value)
|
|
60
|
-
collected.push([name, value]);
|
|
61
|
-
}
|
|
52
|
+
// Remove blocklisted headers
|
|
53
|
+
for (const blocked of HEADER_BLOCKLIST) {
|
|
54
|
+
delete merged[blocked];
|
|
62
55
|
}
|
|
63
|
-
|
|
64
|
-
return null;
|
|
65
|
-
// Sort for deterministic key ordering
|
|
66
|
-
collected.sort((a, b) => a[0].localeCompare(b[0]));
|
|
67
|
-
return collected.map(([k, v]) => `${k}=${v}`).join("&");
|
|
56
|
+
return merged;
|
|
68
57
|
}
|
|
69
58
|
/**
|
|
70
59
|
* Check whether a fetch request carries any per-user auth headers.
|
|
60
|
+
* Used for the safety bypass (skip caching when auth headers are present
|
|
61
|
+
* without an explicit cache opt-in).
|
|
71
62
|
*/
|
|
63
|
+
const AUTH_HEADERS = ["authorization", "cookie", "x-api-key"];
|
|
72
64
|
function hasAuthHeaders(input, init) {
|
|
73
|
-
|
|
65
|
+
const headers = collectHeaders(input, init);
|
|
66
|
+
return AUTH_HEADERS.some((name) => name in headers);
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Serialize request body into string chunks for cache key inclusion.
|
|
70
|
+
* Handles all body types: string, Uint8Array, ReadableStream, FormData, Blob.
|
|
71
|
+
* Returns the serialized body chunks and optionally stashes the original body
|
|
72
|
+
* on init as `_ogBody` so it can still be used after stream consumption.
|
|
73
|
+
*/
|
|
74
|
+
async function serializeBody(init) {
|
|
75
|
+
if (!init?.body)
|
|
76
|
+
return [];
|
|
77
|
+
const bodyChunks = [];
|
|
78
|
+
const encoder = new TextEncoder();
|
|
79
|
+
const decoder = new TextDecoder();
|
|
80
|
+
if (init.body instanceof Uint8Array) {
|
|
81
|
+
bodyChunks.push(decoder.decode(init.body));
|
|
82
|
+
init._ogBody = init.body;
|
|
83
|
+
}
|
|
84
|
+
else if (typeof init.body.getReader === "function") {
|
|
85
|
+
// ReadableStream
|
|
86
|
+
const readableBody = init.body;
|
|
87
|
+
const chunks = [];
|
|
88
|
+
try {
|
|
89
|
+
await readableBody.pipeTo(new WritableStream({
|
|
90
|
+
write(chunk) {
|
|
91
|
+
if (typeof chunk === "string") {
|
|
92
|
+
chunks.push(encoder.encode(chunk));
|
|
93
|
+
bodyChunks.push(chunk);
|
|
94
|
+
}
|
|
95
|
+
else {
|
|
96
|
+
chunks.push(chunk);
|
|
97
|
+
bodyChunks.push(decoder.decode(chunk, { stream: true }));
|
|
98
|
+
}
|
|
99
|
+
},
|
|
100
|
+
}));
|
|
101
|
+
// Flush the decoder
|
|
102
|
+
bodyChunks.push(decoder.decode());
|
|
103
|
+
// Reconstruct the body so it can still be sent
|
|
104
|
+
const length = chunks.reduce((total, arr) => total + arr.length, 0);
|
|
105
|
+
const arrayBuffer = new Uint8Array(length);
|
|
106
|
+
let offset = 0;
|
|
107
|
+
for (const chunk of chunks) {
|
|
108
|
+
arrayBuffer.set(chunk, offset);
|
|
109
|
+
offset += chunk.length;
|
|
110
|
+
}
|
|
111
|
+
init._ogBody = arrayBuffer;
|
|
112
|
+
}
|
|
113
|
+
catch (err) {
|
|
114
|
+
console.error("[vinext] Problem reading body for cache key", err);
|
|
115
|
+
// Still reconstruct what we have so originalFetch doesn't get a spent stream
|
|
116
|
+
if (chunks.length > 0) {
|
|
117
|
+
const length = chunks.reduce((total, arr) => total + arr.length, 0);
|
|
118
|
+
const partial = new Uint8Array(length);
|
|
119
|
+
let offset = 0;
|
|
120
|
+
for (const chunk of chunks) {
|
|
121
|
+
partial.set(chunk, offset);
|
|
122
|
+
offset += chunk.length;
|
|
123
|
+
}
|
|
124
|
+
init._ogBody = partial;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
else if (init.body instanceof URLSearchParams) {
|
|
129
|
+
// URLSearchParams — .toString() gives a stable serialization
|
|
130
|
+
init._ogBody = init.body;
|
|
131
|
+
bodyChunks.push(init.body.toString());
|
|
132
|
+
}
|
|
133
|
+
else if (typeof init.body.keys === "function") {
|
|
134
|
+
// FormData
|
|
135
|
+
const formData = init.body;
|
|
136
|
+
init._ogBody = init.body;
|
|
137
|
+
for (const key of new Set(formData.keys())) {
|
|
138
|
+
const values = formData.getAll(key);
|
|
139
|
+
bodyChunks.push(`${key}=${(await Promise.all(values.map(async (val) => {
|
|
140
|
+
if (typeof val === "string")
|
|
141
|
+
return val;
|
|
142
|
+
// Note: File name/type/lastModified are not included — only content.
|
|
143
|
+
// Two Files with identical content but different names produce the same key.
|
|
144
|
+
return await val.text();
|
|
145
|
+
}))).join(",")}`);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
else if (typeof init.body.arrayBuffer === "function") {
|
|
149
|
+
// Blob
|
|
150
|
+
const blob = init.body;
|
|
151
|
+
bodyChunks.push(await blob.text());
|
|
152
|
+
const arrayBuffer = await blob.arrayBuffer();
|
|
153
|
+
init._ogBody = new Blob([arrayBuffer], { type: blob.type });
|
|
154
|
+
}
|
|
155
|
+
else if (typeof init.body === "string") {
|
|
156
|
+
bodyChunks.push(init.body);
|
|
157
|
+
init._ogBody = init.body;
|
|
158
|
+
}
|
|
159
|
+
return bodyChunks;
|
|
74
160
|
}
|
|
75
161
|
/**
|
|
76
162
|
* Generate a deterministic cache key from a fetch request.
|
|
77
163
|
*
|
|
78
|
-
*
|
|
79
|
-
*
|
|
80
|
-
*
|
|
81
|
-
* they are included in the cache key so different users get separate cache
|
|
82
|
-
* entries. This prevents authenticated responses from leaking across users.
|
|
164
|
+
* Matches Next.js behavior: the key is a SHA-256 hash of a JSON array
|
|
165
|
+
* containing URL, method, all headers (minus blocklist), all RequestInit
|
|
166
|
+
* options, and the serialized body.
|
|
83
167
|
*/
|
|
84
|
-
function buildFetchCacheKey(input, init) {
|
|
168
|
+
async function buildFetchCacheKey(input, init) {
|
|
85
169
|
let url;
|
|
86
170
|
let method = "GET";
|
|
87
|
-
let body;
|
|
88
171
|
if (typeof input === "string") {
|
|
89
172
|
url = input;
|
|
90
173
|
}
|
|
@@ -98,17 +181,28 @@ function buildFetchCacheKey(input, init) {
|
|
|
98
181
|
}
|
|
99
182
|
if (init?.method)
|
|
100
183
|
method = init.method;
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
184
|
+
const headers = collectHeaders(input, init);
|
|
185
|
+
const bodyChunks = await serializeBody(init);
|
|
186
|
+
const cacheString = JSON.stringify([
|
|
187
|
+
CACHE_KEY_PREFIX,
|
|
188
|
+
url,
|
|
189
|
+
method,
|
|
190
|
+
headers,
|
|
191
|
+
init?.mode,
|
|
192
|
+
init?.redirect,
|
|
193
|
+
init?.credentials,
|
|
194
|
+
init?.referrer,
|
|
195
|
+
init?.referrerPolicy,
|
|
196
|
+
init?.integrity,
|
|
197
|
+
init?.cache,
|
|
198
|
+
bodyChunks,
|
|
199
|
+
]);
|
|
200
|
+
const encoder = new TextEncoder();
|
|
201
|
+
const buffer = encoder.encode(cacheString);
|
|
202
|
+
const hashBuffer = await crypto.subtle.digest("SHA-256", buffer);
|
|
203
|
+
return Array.prototype.map
|
|
204
|
+
.call(new Uint8Array(hashBuffer), (b) => b.toString(16).padStart(2, "0"))
|
|
205
|
+
.join("");
|
|
112
206
|
}
|
|
113
207
|
// ---------------------------------------------------------------------------
|
|
114
208
|
// Patching
|
|
@@ -210,7 +304,7 @@ function createPatchedFetch() {
|
|
|
210
304
|
}
|
|
211
305
|
}
|
|
212
306
|
const tags = nextOpts?.tags ?? [];
|
|
213
|
-
const cacheKey = buildFetchCacheKey(input, init);
|
|
307
|
+
const cacheKey = await buildFetchCacheKey(input, init);
|
|
214
308
|
const handler = getCacheHandler();
|
|
215
309
|
// Collect tags for this render pass
|
|
216
310
|
const reqTags = _getState().currentRequestTags;
|
|
@@ -314,9 +408,13 @@ function createPatchedFetch() {
|
|
|
314
408
|
function stripNextFromInit(init) {
|
|
315
409
|
if (!init)
|
|
316
410
|
return init;
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
411
|
+
const castInit = init;
|
|
412
|
+
const { next: _next, _ogBody, ...rest } = castInit;
|
|
413
|
+
// Restore the original body if it was stashed by serializeBody (e.g. after
|
|
414
|
+
// consuming a ReadableStream for cache key generation).
|
|
415
|
+
if (_ogBody !== undefined) {
|
|
416
|
+
rest.body = _ogBody;
|
|
417
|
+
}
|
|
320
418
|
return Object.keys(rest).length > 0 ? rest : undefined;
|
|
321
419
|
}
|
|
322
420
|
// ---------------------------------------------------------------------------
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fetch-cache.js","sourceRoot":"","sources":["../../src/shims/fetch-cache.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Extended fetch() with Next.js caching semantics.\n *\n * Patches `globalThis.fetch` during server rendering to support:\n *\n * fetch(url, { next: { revalidate: 60, tags: ['posts'] } })\n * fetch(url, { cache: 'force-cache' })\n * fetch(url, { cache: 'no-store' })\n *\n * Cached responses are stored via the pluggable CacheHandler, so\n * revalidateTag() and revalidatePath() invalidate fetch-level caches.\n *\n * Usage (in server entry):\n * import { withFetchCache, cleanupFetchCache } from './fetch-cache';\n * const cleanup = withFetchCache();\n * try { ... render ... } finally { cleanup(); }\n *\n * Or use the async helper:\n * await runWithFetchCache(async () => { ... render ... });\n */\n\nimport {\n getCacheHandler,\n type CachedFetchValue,\n} from \"./cache.js\";\nimport { AsyncLocalStorage } from \"node:async_hooks\";\n\n// ---------------------------------------------------------------------------\n// Cache key generation\n// ---------------------------------------------------------------------------\n\n/**\n * Headers that carry per-user identity. When any of these are present in a\n * fetch request, they MUST be included in the cache key to prevent one user's\n * authenticated response from being served to another user.\n *\n * Checked case-insensitively to match HTTP header semantics.\n */\nconst AUTH_HEADERS = [\"authorization\", \"cookie\", \"x-api-key\"];\n\n/**\n * Check whether a fetch request includes any per-user authentication headers.\n * Returns the normalized header values (sorted by name) for cache key inclusion,\n * or null if no auth headers are present.\n */\nfunction extractAuthHeaders(input: string | URL | Request, init?: RequestInit): string | null {\n const collected: [string, string][] = [];\n\n // Gather headers from the init object\n if (init?.headers) {\n const headers = init.headers instanceof Headers\n ? init.headers\n : new Headers(init.headers as HeadersInit);\n for (const name of AUTH_HEADERS) {\n const value = headers.get(name);\n if (value) collected.push([name, value]);\n }\n }\n\n // Also check headers from the Request object (if input is a Request)\n if (input instanceof Request && input.headers) {\n for (const name of AUTH_HEADERS) {\n // Don't duplicate if already found in init\n if (collected.some(([n]) => n === name)) continue;\n const value = input.headers.get(name);\n if (value) collected.push([name, value]);\n }\n }\n\n if (collected.length === 0) return null;\n\n // Sort for deterministic key ordering\n collected.sort((a, b) => a[0].localeCompare(b[0]));\n return collected.map(([k, v]) => `${k}=${v}`).join(\"&\");\n}\n\n/**\n * Check whether a fetch request carries any per-user auth headers.\n */\nfunction hasAuthHeaders(input: string | URL | Request, init?: RequestInit): boolean {\n return extractAuthHeaders(input, init) !== null;\n}\n\n/**\n * Generate a deterministic cache key from a fetch request.\n *\n * Key = \"fetch:\" + method + \":\" + URL [+ \"|\" + body] [+ \"|auth:\" + auth_headers].\n *\n * When per-user headers (Authorization, Cookie, X-API-Key) are present,\n * they are included in the cache key so different users get separate cache\n * entries. This prevents authenticated responses from leaking across users.\n */\nfunction buildFetchCacheKey(input: string | URL | Request, init?: RequestInit & { next?: NextFetchOptions }): string {\n let url: string;\n let method = \"GET\";\n let body: string | undefined;\n\n if (typeof input === \"string\") {\n url = input;\n } else if (input instanceof URL) {\n url = input.toString();\n } else {\n // Request object\n url = input.url;\n method = input.method || \"GET\";\n }\n\n if (init?.method) method = init.method;\n if (init?.body && typeof init.body === \"string\") body = init.body;\n\n // Build a stable key from URL + method + body + auth headers\n const parts = [`fetch:${method}:${url}`];\n if (body) parts.push(body);\n\n // Include per-user auth headers in the key to prevent cross-user leakage\n const authPart = extractAuthHeaders(input, init);\n if (authPart) parts.push(`auth:${authPart}`);\n\n return parts.join(\"|\");\n}\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\ninterface NextFetchOptions {\n revalidate?: number | false;\n tags?: string[];\n}\n\n// Extend the standard RequestInit to include `next`\ndeclare global {\n interface RequestInit {\n next?: NextFetchOptions;\n }\n}\n\n// ---------------------------------------------------------------------------\n// Patching\n// ---------------------------------------------------------------------------\n\n// Capture the real (unpatched) fetch once, shared across Vite's\n// multi-environment module instances via Symbol.for().\nconst _ORIG_FETCH_KEY = Symbol.for(\"vinext.fetchCache.originalFetch\");\nconst _gFetch = globalThis as unknown as Record<PropertyKey, unknown>;\nconst originalFetch: typeof globalThis.fetch = (_gFetch[_ORIG_FETCH_KEY] ??= globalThis.fetch) as typeof globalThis.fetch;\n\n// ---------------------------------------------------------------------------\n// AsyncLocalStorage for request-scoped fetch cache state.\n// Uses Symbol.for() on globalThis so the storage is shared across Vite's\n// multi-environment module instances.\n// ---------------------------------------------------------------------------\ninterface FetchCacheState {\n currentRequestTags: string[];\n}\n\nconst _ALS_KEY = Symbol.for(\"vinext.fetchCache.als\");\nconst _FALLBACK_KEY = Symbol.for(\"vinext.fetchCache.fallback\");\nconst _g = globalThis as unknown as Record<PropertyKey, unknown>;\nconst _als = (_g[_ALS_KEY] ??= new AsyncLocalStorage<FetchCacheState>()) as AsyncLocalStorage<FetchCacheState>;\n\nconst _fallbackState = (_g[_FALLBACK_KEY] ??= {\n currentRequestTags: [],\n} satisfies FetchCacheState) as FetchCacheState;\n\nfunction _getState(): FetchCacheState {\n return _als.getStore() ?? _fallbackState;\n}\n\n/**\n * Reset the fallback state for a new request. Used by `withFetchCache()`\n * in single-threaded contexts where ALS.run() isn't used.\n */\nfunction _resetFallbackState(): void {\n _fallbackState.currentRequestTags = [];\n}\n\n/**\n * Get tags collected during the current render pass.\n * Useful for associating page-level cache entries with all the\n * fetch tags used during rendering.\n */\nexport function getCollectedFetchTags(): string[] {\n return [..._getState().currentRequestTags];\n}\n\n/**\n * Create a patched fetch function with Next.js caching semantics.\n *\n * The patched fetch:\n * 1. Checks `cache` and `next` options to determine caching behavior\n * 2. On cache hit, returns the cached response without hitting the network\n * 3. On cache miss, fetches from network, stores in cache, returns response\n * 4. Respects `next.revalidate` for TTL-based revalidation\n * 5. Respects `next.tags` for tag-based invalidation via revalidateTag()\n */\nfunction createPatchedFetch(): typeof globalThis.fetch {\n return async function patchedFetch(\n input: string | URL | Request,\n init?: RequestInit,\n ): Promise<Response> {\n const nextOpts = init?.next as NextFetchOptions | undefined;\n const cacheDirective = init?.cache;\n\n // Determine caching behavior:\n // - cache: 'no-store' → skip cache entirely\n // - cache: 'force-cache' → cache indefinitely (revalidate = Infinity)\n // - next.revalidate: false → same as 'no-store'\n // - next.revalidate: 0 → same as 'no-store'\n // - next.revalidate: N → cache for N seconds\n // - No cache/next options → default behavior (no caching, pass-through)\n\n // If no caching options at all, just pass through to original fetch\n if (!nextOpts && !cacheDirective) {\n return originalFetch(input, init);\n }\n\n // Explicit no-store or no-cache — bypass cache entirely\n if (cacheDirective === \"no-store\" || cacheDirective === \"no-cache\" || nextOpts?.revalidate === false || nextOpts?.revalidate === 0) {\n // Strip the `next` property before passing to real fetch\n const cleanInit = stripNextFromInit(init);\n return originalFetch(input, cleanInit);\n }\n\n // Safety: when per-user auth headers are present and the developer hasn't\n // explicitly opted into caching with `cache: 'force-cache'` or an explicit\n // `next.revalidate`, skip caching to prevent accidental cross-user data\n // leakage. Developers who understand the implications can still force\n // caching by using `cache: 'force-cache'` or `next: { revalidate: N }`.\n const hasExplicitCacheOpt =\n cacheDirective === \"force-cache\" ||\n (typeof nextOpts?.revalidate === \"number\" && nextOpts.revalidate > 0);\n if (!hasExplicitCacheOpt && hasAuthHeaders(input, init)) {\n const cleanInit = stripNextFromInit(init);\n return originalFetch(input, cleanInit);\n }\n\n // Determine revalidation period\n let revalidateSeconds: number;\n if (cacheDirective === \"force-cache\") {\n // force-cache means cache indefinitely (we use a very large number)\n revalidateSeconds = nextOpts?.revalidate && typeof nextOpts.revalidate === \"number\"\n ? nextOpts.revalidate\n : 31536000; // 1 year\n } else if (typeof nextOpts?.revalidate === \"number\" && nextOpts.revalidate > 0) {\n revalidateSeconds = nextOpts.revalidate;\n } else {\n // Has `next` options but no explicit revalidate — Next.js defaults to\n // caching when `next` is present (force-cache behavior).\n // If only tags are specified, cache indefinitely.\n if (nextOpts?.tags && nextOpts.tags.length > 0) {\n revalidateSeconds = 31536000;\n } else {\n // next: {} with no revalidate or tags — pass through\n const cleanInit = stripNextFromInit(init);\n return originalFetch(input, cleanInit);\n }\n }\n\n const tags = nextOpts?.tags ?? [];\n const cacheKey = buildFetchCacheKey(input, init);\n const handler = getCacheHandler();\n\n // Collect tags for this render pass\n const reqTags = _getState().currentRequestTags;\n if (tags.length > 0) {\n for (const tag of tags) {\n if (!reqTags.includes(tag)) {\n reqTags.push(tag);\n }\n }\n }\n\n // Try cache first\n try {\n const cached = await handler.get(cacheKey, { kind: \"FETCH\", tags });\n if (cached?.value && cached.value.kind === \"FETCH\" && cached.cacheState !== \"stale\") {\n const cachedData = cached.value.data;\n // Reconstruct a Response from the cached data\n return new Response(cachedData.body, {\n status: cachedData.status ?? 200,\n headers: cachedData.headers,\n });\n }\n\n // Stale entry — we could do stale-while-revalidate here, but for fetch()\n // the simpler approach is to just re-fetch (the page-level ISR handles SWR).\n // However, if we have a stale entry, return it and trigger background refetch.\n if (cached?.value && cached.value.kind === \"FETCH\" && cached.cacheState === \"stale\") {\n const staleData = cached.value.data;\n\n // Background refetch\n const cleanInit = stripNextFromInit(init);\n originalFetch(input, cleanInit).then(async (freshResp) => {\n const freshBody = await freshResp.text();\n const freshHeaders: Record<string, string> = {};\n freshResp.headers.forEach((v, k) => { freshHeaders[k] = v; });\n\n const freshValue: CachedFetchValue = {\n kind: \"FETCH\",\n data: {\n headers: freshHeaders,\n body: freshBody,\n url: typeof input === \"string\" ? input : input instanceof URL ? input.toString() : input.url,\n status: freshResp.status,\n },\n tags,\n revalidate: revalidateSeconds,\n };\n await handler.set(cacheKey, freshValue, {\n fetchCache: true,\n tags,\n revalidate: revalidateSeconds,\n });\n }).catch((err) => {\n console.error(\"[vinext] fetch cache background revalidation failed:\", err);\n });\n\n // Return stale data immediately\n return new Response(staleData.body, {\n status: staleData.status ?? 200,\n headers: staleData.headers,\n });\n }\n } catch (cacheErr) {\n // Cache read failed — fall through to network\n console.error(\"[vinext] fetch cache read error:\", cacheErr);\n }\n\n // Cache miss — fetch from network\n const cleanInit = stripNextFromInit(init);\n const response = await originalFetch(input, cleanInit);\n\n // Only cache successful responses (2xx)\n if (response.ok) {\n // Clone before reading body\n const cloned = response.clone();\n const body = await cloned.text();\n const headers: Record<string, string> = {};\n cloned.headers.forEach((v, k) => { headers[k] = v; });\n\n const cacheValue: CachedFetchValue = {\n kind: \"FETCH\",\n data: {\n headers,\n body,\n url: typeof input === \"string\" ? input : input instanceof URL ? input.toString() : input.url,\n status: cloned.status,\n },\n tags,\n revalidate: revalidateSeconds,\n };\n\n // Store in cache (fire-and-forget)\n handler.set(cacheKey, cacheValue, {\n fetchCache: true,\n tags,\n revalidate: revalidateSeconds,\n }).catch((err) => {\n console.error(\"[vinext] fetch cache write error:\", err);\n });\n }\n\n return response;\n } as typeof globalThis.fetch;\n}\n\n/**\n * Strip the `next` property from RequestInit before passing to real fetch.\n * The `next` property is not a standard fetch option and would cause warnings\n * in some environments.\n */\nfunction stripNextFromInit(init?: RequestInit): RequestInit | undefined {\n if (!init) return init;\n if (!(\"next\" in init)) return init;\n const { next: _next, ...rest } = init as RequestInit & { next?: unknown };\n return Object.keys(rest).length > 0 ? rest : undefined;\n}\n\n// ---------------------------------------------------------------------------\n// Public API\n// ---------------------------------------------------------------------------\n\n// ---------------------------------------------------------------------------\n// Fetch patching — install once, not per-request.\n// The patched fetch uses _getState() internally, which reads from ALS\n// (concurrent) or _fallbackState (single-threaded), so per-request\n// isolation is handled at the state level, not by swapping globalThis.fetch.\n// ---------------------------------------------------------------------------\n\nconst _PATCH_KEY = Symbol.for(\"vinext.fetchCache.patchInstalled\");\n\nfunction _ensurePatchInstalled(): void {\n if (_g[_PATCH_KEY]) return;\n _g[_PATCH_KEY] = true;\n globalThis.fetch = createPatchedFetch();\n}\n\n/**\n * Install the patched fetch and reset per-request tag state.\n * Returns a cleanup function that clears tags.\n *\n * @deprecated Prefer `runWithFetchCache()` which uses `AsyncLocalStorage.run()`\n * for proper per-request isolation in concurrent environments.\n *\n * Usage:\n * const cleanup = withFetchCache();\n * try { await render(); } finally { cleanup(); }\n */\nexport function withFetchCache(): () => void {\n _ensurePatchInstalled();\n _resetFallbackState();\n\n return () => {\n _resetFallbackState();\n };\n}\n\n/**\n * Run an async function with patched fetch caching enabled.\n * Uses `AsyncLocalStorage.run()` for proper per-request isolation\n * of collected fetch tags in concurrent server environments.\n */\nexport async function runWithFetchCache<T>(fn: () => Promise<T>): Promise<T> {\n _ensurePatchInstalled();\n return _als.run({ currentRequestTags: [] }, fn);\n}\n\n/**\n * Get the original (unpatched) fetch function.\n * Useful for internal code that should bypass caching.\n */\nexport function getOriginalFetch(): typeof globalThis.fetch {\n return originalFetch;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"fetch-cache.js","sourceRoot":"","sources":["../../src/shims/fetch-cache.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Extended fetch() with Next.js caching semantics.\n *\n * Patches `globalThis.fetch` during server rendering to support:\n *\n * fetch(url, { next: { revalidate: 60, tags: ['posts'] } })\n * fetch(url, { cache: 'force-cache' })\n * fetch(url, { cache: 'no-store' })\n *\n * Cached responses are stored via the pluggable CacheHandler, so\n * revalidateTag() and revalidatePath() invalidate fetch-level caches.\n *\n * Usage (in server entry):\n * import { withFetchCache, cleanupFetchCache } from './fetch-cache';\n * const cleanup = withFetchCache();\n * try { ... render ... } finally { cleanup(); }\n *\n * Or use the async helper:\n * await runWithFetchCache(async () => { ... render ... });\n */\n\nimport {\n getCacheHandler,\n type CachedFetchValue,\n} from \"./cache.js\";\nimport { AsyncLocalStorage } from \"node:async_hooks\";\n\n// ---------------------------------------------------------------------------\n// Cache key generation\n// ---------------------------------------------------------------------------\n\n/**\n * Headers excluded from the cache key. These are W3C trace context headers\n * that can break request caching and deduplication.\n * All other headers ARE included in the cache key, matching Next.js behavior.\n */\nconst HEADER_BLOCKLIST = [\"traceparent\", \"tracestate\"];\n\n// Cache key version — bump when changing the key format to bust stale entries\nconst CACHE_KEY_PREFIX = \"v1\";\n\n/**\n * Collect all headers from the request, excluding the blocklist.\n * Merges headers from both the Request object and the init object,\n * with init taking precedence (matching fetch() spec behavior).\n */\nfunction collectHeaders(input: string | URL | Request, init?: RequestInit): Record<string, string> {\n const merged: Record<string, string> = {};\n\n // Start with headers from Request object (if any)\n if (input instanceof Request && input.headers) {\n input.headers.forEach((v, k) => { merged[k] = v; });\n }\n\n // Override with headers from init (init takes precedence per fetch spec)\n if (init?.headers) {\n const headers = init.headers instanceof Headers\n ? init.headers\n : new Headers(init.headers as HeadersInit);\n headers.forEach((v, k) => { merged[k] = v; });\n }\n\n // Remove blocklisted headers\n for (const blocked of HEADER_BLOCKLIST) {\n delete merged[blocked];\n }\n\n return merged;\n}\n\n/**\n * Check whether a fetch request carries any per-user auth headers.\n * Used for the safety bypass (skip caching when auth headers are present\n * without an explicit cache opt-in).\n */\nconst AUTH_HEADERS = [\"authorization\", \"cookie\", \"x-api-key\"];\n\nfunction hasAuthHeaders(input: string | URL | Request, init?: RequestInit): boolean {\n const headers = collectHeaders(input, init);\n return AUTH_HEADERS.some((name) => name in headers);\n}\n\n/**\n * Serialize request body into string chunks for cache key inclusion.\n * Handles all body types: string, Uint8Array, ReadableStream, FormData, Blob.\n * Returns the serialized body chunks and optionally stashes the original body\n * on init as `_ogBody` so it can still be used after stream consumption.\n */\nasync function serializeBody(init?: RequestInit): Promise<string[]> {\n if (!init?.body) return [];\n\n const bodyChunks: string[] = [];\n const encoder = new TextEncoder();\n const decoder = new TextDecoder();\n\n if (init.body instanceof Uint8Array) {\n bodyChunks.push(decoder.decode(init.body));\n (init as any)._ogBody = init.body;\n } else if (typeof (init.body as any).getReader === \"function\") {\n // ReadableStream\n const readableBody = init.body as ReadableStream<Uint8Array | string>;\n const chunks: Uint8Array[] = [];\n\n try {\n await readableBody.pipeTo(\n new WritableStream({\n write(chunk) {\n if (typeof chunk === \"string\") {\n chunks.push(encoder.encode(chunk));\n bodyChunks.push(chunk);\n } else {\n chunks.push(chunk);\n bodyChunks.push(decoder.decode(chunk, { stream: true }));\n }\n },\n })\n );\n // Flush the decoder\n bodyChunks.push(decoder.decode());\n\n // Reconstruct the body so it can still be sent\n const length = chunks.reduce((total, arr) => total + arr.length, 0);\n const arrayBuffer = new Uint8Array(length);\n let offset = 0;\n for (const chunk of chunks) {\n arrayBuffer.set(chunk, offset);\n offset += chunk.length;\n }\n (init as any)._ogBody = arrayBuffer;\n } catch (err) {\n console.error(\"[vinext] Problem reading body for cache key\", err);\n // Still reconstruct what we have so originalFetch doesn't get a spent stream\n if (chunks.length > 0) {\n const length = chunks.reduce((total, arr) => total + arr.length, 0);\n const partial = new Uint8Array(length);\n let offset = 0;\n for (const chunk of chunks) {\n partial.set(chunk, offset);\n offset += chunk.length;\n }\n (init as any)._ogBody = partial;\n }\n }\n } else if (init.body instanceof URLSearchParams) {\n // URLSearchParams — .toString() gives a stable serialization\n (init as any)._ogBody = init.body;\n bodyChunks.push(init.body.toString());\n } else if (typeof (init.body as any).keys === \"function\") {\n // FormData\n const formData = init.body as FormData;\n (init as any)._ogBody = init.body;\n for (const key of new Set(formData.keys())) {\n const values = formData.getAll(key);\n bodyChunks.push(\n `${key}=${(\n await Promise.all(\n values.map(async (val) => {\n if (typeof val === \"string\") return val;\n // Note: File name/type/lastModified are not included — only content.\n // Two Files with identical content but different names produce the same key.\n return await val.text();\n })\n )\n ).join(\",\")}`\n );\n }\n } else if (typeof (init.body as any).arrayBuffer === \"function\") {\n // Blob\n const blob = init.body as Blob;\n bodyChunks.push(await blob.text());\n const arrayBuffer = await blob.arrayBuffer();\n (init as any)._ogBody = new Blob([arrayBuffer], { type: blob.type });\n } else if (typeof init.body === \"string\") {\n bodyChunks.push(init.body);\n (init as any)._ogBody = init.body;\n }\n\n return bodyChunks;\n}\n\n/**\n * Generate a deterministic cache key from a fetch request.\n *\n * Matches Next.js behavior: the key is a SHA-256 hash of a JSON array\n * containing URL, method, all headers (minus blocklist), all RequestInit\n * options, and the serialized body.\n */\nasync function buildFetchCacheKey(input: string | URL | Request, init?: RequestInit & { next?: NextFetchOptions }): Promise<string> {\n let url: string;\n let method = \"GET\";\n\n if (typeof input === \"string\") {\n url = input;\n } else if (input instanceof URL) {\n url = input.toString();\n } else {\n // Request object\n url = input.url;\n method = input.method || \"GET\";\n }\n\n if (init?.method) method = init.method;\n\n const headers = collectHeaders(input, init);\n const bodyChunks = await serializeBody(init);\n\n const cacheString = JSON.stringify([\n CACHE_KEY_PREFIX,\n url,\n method,\n headers,\n init?.mode,\n init?.redirect,\n init?.credentials,\n init?.referrer,\n init?.referrerPolicy,\n init?.integrity,\n init?.cache,\n bodyChunks,\n ]);\n\n const encoder = new TextEncoder();\n const buffer = encoder.encode(cacheString);\n const hashBuffer = await crypto.subtle.digest(\"SHA-256\", buffer);\n return Array.prototype.map\n .call(new Uint8Array(hashBuffer), (b: number) => b.toString(16).padStart(2, \"0\"))\n .join(\"\");\n}\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\ninterface NextFetchOptions {\n revalidate?: number | false;\n tags?: string[];\n}\n\n// Extend the standard RequestInit to include `next`\ndeclare global {\n interface RequestInit {\n next?: NextFetchOptions;\n }\n}\n\n// ---------------------------------------------------------------------------\n// Patching\n// ---------------------------------------------------------------------------\n\n// Capture the real (unpatched) fetch once, shared across Vite's\n// multi-environment module instances via Symbol.for().\nconst _ORIG_FETCH_KEY = Symbol.for(\"vinext.fetchCache.originalFetch\");\nconst _gFetch = globalThis as unknown as Record<PropertyKey, unknown>;\nconst originalFetch: typeof globalThis.fetch = (_gFetch[_ORIG_FETCH_KEY] ??= globalThis.fetch) as typeof globalThis.fetch;\n\n// ---------------------------------------------------------------------------\n// AsyncLocalStorage for request-scoped fetch cache state.\n// Uses Symbol.for() on globalThis so the storage is shared across Vite's\n// multi-environment module instances.\n// ---------------------------------------------------------------------------\ninterface FetchCacheState {\n currentRequestTags: string[];\n}\n\nconst _ALS_KEY = Symbol.for(\"vinext.fetchCache.als\");\nconst _FALLBACK_KEY = Symbol.for(\"vinext.fetchCache.fallback\");\nconst _g = globalThis as unknown as Record<PropertyKey, unknown>;\nconst _als = (_g[_ALS_KEY] ??= new AsyncLocalStorage<FetchCacheState>()) as AsyncLocalStorage<FetchCacheState>;\n\nconst _fallbackState = (_g[_FALLBACK_KEY] ??= {\n currentRequestTags: [],\n} satisfies FetchCacheState) as FetchCacheState;\n\nfunction _getState(): FetchCacheState {\n return _als.getStore() ?? _fallbackState;\n}\n\n/**\n * Reset the fallback state for a new request. Used by `withFetchCache()`\n * in single-threaded contexts where ALS.run() isn't used.\n */\nfunction _resetFallbackState(): void {\n _fallbackState.currentRequestTags = [];\n}\n\n/**\n * Get tags collected during the current render pass.\n * Useful for associating page-level cache entries with all the\n * fetch tags used during rendering.\n */\nexport function getCollectedFetchTags(): string[] {\n return [..._getState().currentRequestTags];\n}\n\n/**\n * Create a patched fetch function with Next.js caching semantics.\n *\n * The patched fetch:\n * 1. Checks `cache` and `next` options to determine caching behavior\n * 2. On cache hit, returns the cached response without hitting the network\n * 3. On cache miss, fetches from network, stores in cache, returns response\n * 4. Respects `next.revalidate` for TTL-based revalidation\n * 5. Respects `next.tags` for tag-based invalidation via revalidateTag()\n */\nfunction createPatchedFetch(): typeof globalThis.fetch {\n return async function patchedFetch(\n input: string | URL | Request,\n init?: RequestInit,\n ): Promise<Response> {\n const nextOpts = init?.next as NextFetchOptions | undefined;\n const cacheDirective = init?.cache;\n\n // Determine caching behavior:\n // - cache: 'no-store' → skip cache entirely\n // - cache: 'force-cache' → cache indefinitely (revalidate = Infinity)\n // - next.revalidate: false → same as 'no-store'\n // - next.revalidate: 0 → same as 'no-store'\n // - next.revalidate: N → cache for N seconds\n // - No cache/next options → default behavior (no caching, pass-through)\n\n // If no caching options at all, just pass through to original fetch\n if (!nextOpts && !cacheDirective) {\n return originalFetch(input, init);\n }\n\n // Explicit no-store or no-cache — bypass cache entirely\n if (cacheDirective === \"no-store\" || cacheDirective === \"no-cache\" || nextOpts?.revalidate === false || nextOpts?.revalidate === 0) {\n // Strip the `next` property before passing to real fetch\n const cleanInit = stripNextFromInit(init);\n return originalFetch(input, cleanInit);\n }\n\n // Safety: when per-user auth headers are present and the developer hasn't\n // explicitly opted into caching with `cache: 'force-cache'` or an explicit\n // `next.revalidate`, skip caching to prevent accidental cross-user data\n // leakage. Developers who understand the implications can still force\n // caching by using `cache: 'force-cache'` or `next: { revalidate: N }`.\n const hasExplicitCacheOpt =\n cacheDirective === \"force-cache\" ||\n (typeof nextOpts?.revalidate === \"number\" && nextOpts.revalidate > 0);\n if (!hasExplicitCacheOpt && hasAuthHeaders(input, init)) {\n const cleanInit = stripNextFromInit(init);\n return originalFetch(input, cleanInit);\n }\n\n // Determine revalidation period\n let revalidateSeconds: number;\n if (cacheDirective === \"force-cache\") {\n // force-cache means cache indefinitely (we use a very large number)\n revalidateSeconds = nextOpts?.revalidate && typeof nextOpts.revalidate === \"number\"\n ? nextOpts.revalidate\n : 31536000; // 1 year\n } else if (typeof nextOpts?.revalidate === \"number\" && nextOpts.revalidate > 0) {\n revalidateSeconds = nextOpts.revalidate;\n } else {\n // Has `next` options but no explicit revalidate — Next.js defaults to\n // caching when `next` is present (force-cache behavior).\n // If only tags are specified, cache indefinitely.\n if (nextOpts?.tags && nextOpts.tags.length > 0) {\n revalidateSeconds = 31536000;\n } else {\n // next: {} with no revalidate or tags — pass through\n const cleanInit = stripNextFromInit(init);\n return originalFetch(input, cleanInit);\n }\n }\n\n const tags = nextOpts?.tags ?? [];\n const cacheKey = await buildFetchCacheKey(input, init);\n const handler = getCacheHandler();\n\n // Collect tags for this render pass\n const reqTags = _getState().currentRequestTags;\n if (tags.length > 0) {\n for (const tag of tags) {\n if (!reqTags.includes(tag)) {\n reqTags.push(tag);\n }\n }\n }\n\n // Try cache first\n try {\n const cached = await handler.get(cacheKey, { kind: \"FETCH\", tags });\n if (cached?.value && cached.value.kind === \"FETCH\" && cached.cacheState !== \"stale\") {\n const cachedData = cached.value.data;\n // Reconstruct a Response from the cached data\n return new Response(cachedData.body, {\n status: cachedData.status ?? 200,\n headers: cachedData.headers,\n });\n }\n\n // Stale entry — we could do stale-while-revalidate here, but for fetch()\n // the simpler approach is to just re-fetch (the page-level ISR handles SWR).\n // However, if we have a stale entry, return it and trigger background refetch.\n if (cached?.value && cached.value.kind === \"FETCH\" && cached.cacheState === \"stale\") {\n const staleData = cached.value.data;\n\n // Background refetch\n const cleanInit = stripNextFromInit(init);\n originalFetch(input, cleanInit).then(async (freshResp) => {\n const freshBody = await freshResp.text();\n const freshHeaders: Record<string, string> = {};\n freshResp.headers.forEach((v, k) => { freshHeaders[k] = v; });\n\n const freshValue: CachedFetchValue = {\n kind: \"FETCH\",\n data: {\n headers: freshHeaders,\n body: freshBody,\n url: typeof input === \"string\" ? input : input instanceof URL ? input.toString() : input.url,\n status: freshResp.status,\n },\n tags,\n revalidate: revalidateSeconds,\n };\n await handler.set(cacheKey, freshValue, {\n fetchCache: true,\n tags,\n revalidate: revalidateSeconds,\n });\n }).catch((err) => {\n console.error(\"[vinext] fetch cache background revalidation failed:\", err);\n });\n\n // Return stale data immediately\n return new Response(staleData.body, {\n status: staleData.status ?? 200,\n headers: staleData.headers,\n });\n }\n } catch (cacheErr) {\n // Cache read failed — fall through to network\n console.error(\"[vinext] fetch cache read error:\", cacheErr);\n }\n\n // Cache miss — fetch from network\n const cleanInit = stripNextFromInit(init);\n const response = await originalFetch(input, cleanInit);\n\n // Only cache successful responses (2xx)\n if (response.ok) {\n // Clone before reading body\n const cloned = response.clone();\n const body = await cloned.text();\n const headers: Record<string, string> = {};\n cloned.headers.forEach((v, k) => { headers[k] = v; });\n\n const cacheValue: CachedFetchValue = {\n kind: \"FETCH\",\n data: {\n headers,\n body,\n url: typeof input === \"string\" ? input : input instanceof URL ? input.toString() : input.url,\n status: cloned.status,\n },\n tags,\n revalidate: revalidateSeconds,\n };\n\n // Store in cache (fire-and-forget)\n handler.set(cacheKey, cacheValue, {\n fetchCache: true,\n tags,\n revalidate: revalidateSeconds,\n }).catch((err) => {\n console.error(\"[vinext] fetch cache write error:\", err);\n });\n }\n\n return response;\n } as typeof globalThis.fetch;\n}\n\n/**\n * Strip the `next` property from RequestInit before passing to real fetch.\n * The `next` property is not a standard fetch option and would cause warnings\n * in some environments.\n */\nfunction stripNextFromInit(init?: RequestInit): RequestInit | undefined {\n if (!init) return init;\n const castInit = init as RequestInit & { next?: unknown; _ogBody?: BodyInit };\n const { next: _next, _ogBody, ...rest } = castInit;\n // Restore the original body if it was stashed by serializeBody (e.g. after\n // consuming a ReadableStream for cache key generation).\n if (_ogBody !== undefined) {\n rest.body = _ogBody;\n }\n return Object.keys(rest).length > 0 ? rest : undefined;\n}\n\n// ---------------------------------------------------------------------------\n// Public API\n// ---------------------------------------------------------------------------\n\n// ---------------------------------------------------------------------------\n// Fetch patching — install once, not per-request.\n// The patched fetch uses _getState() internally, which reads from ALS\n// (concurrent) or _fallbackState (single-threaded), so per-request\n// isolation is handled at the state level, not by swapping globalThis.fetch.\n// ---------------------------------------------------------------------------\n\nconst _PATCH_KEY = Symbol.for(\"vinext.fetchCache.patchInstalled\");\n\nfunction _ensurePatchInstalled(): void {\n if (_g[_PATCH_KEY]) return;\n _g[_PATCH_KEY] = true;\n globalThis.fetch = createPatchedFetch();\n}\n\n/**\n * Install the patched fetch and reset per-request tag state.\n * Returns a cleanup function that clears tags.\n *\n * @deprecated Prefer `runWithFetchCache()` which uses `AsyncLocalStorage.run()`\n * for proper per-request isolation in concurrent environments.\n *\n * Usage:\n * const cleanup = withFetchCache();\n * try { await render(); } finally { cleanup(); }\n */\nexport function withFetchCache(): () => void {\n _ensurePatchInstalled();\n _resetFallbackState();\n\n return () => {\n _resetFallbackState();\n };\n}\n\n/**\n * Run an async function with patched fetch caching enabled.\n * Uses `AsyncLocalStorage.run()` for proper per-request isolation\n * of collected fetch tags in concurrent server environments.\n */\nexport async function runWithFetchCache<T>(fn: () => Promise<T>): Promise<T> {\n _ensurePatchInstalled();\n return _als.run({ currentRequestTags: [] }, fn);\n}\n\n/**\n * Get the original (unpatched) fetch function.\n * Useful for internal code that should bypass caching.\n */\nexport function getOriginalFetch(): typeof globalThis.fetch {\n return originalFetch;\n}\n"]}
|
package/dist/shims/head.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"head.d.ts","sourceRoot":"","sources":["../../src/shims/head.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,KAA8C,MAAM,OAAO,CAAC;AAEnE,UAAU,SAAS;IACjB,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;CAC5B;AAWD;;;GAGG;AACH,wBAAgB,2BAA2B,CAAC,SAAS,EAAE;IACrD,kBAAkB,EAAE,MAAM,MAAM,EAAE,CAAC;IACnC,YAAY,EAAE,MAAM,IAAI,CAAC;CAC1B,GAAG,IAAI,CAGP;AAED,wDAAwD;AACxD,wBAAgB,YAAY,IAAI,IAAI,CAEnC;AAED,kDAAkD;AAClD,wBAAgB,cAAc,IAAI,MAAM,CAEvC;
|
|
1
|
+
{"version":3,"file":"head.d.ts","sourceRoot":"","sources":["../../src/shims/head.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,KAA8C,MAAM,OAAO,CAAC;AAEnE,UAAU,SAAS;IACjB,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;CAC5B;AAWD;;;GAGG;AACH,wBAAgB,2BAA2B,CAAC,SAAS,EAAE;IACrD,kBAAkB,EAAE,MAAM,MAAM,EAAE,CAAC;IACnC,YAAY,EAAE,MAAM,IAAI,CAAC;CAC1B,GAAG,IAAI,CAGP;AAED,wDAAwD;AACxD,wBAAgB,YAAY,IAAI,IAAI,CAEnC;AAED,kDAAkD;AAClD,wBAAgB,cAAc,IAAI,MAAM,CAEvC;AAmED,wBAAgB,UAAU,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAE5C;AAID,iBAAS,IAAI,CAAC,EAAE,QAAQ,EAAE,EAAE,SAAS,GAAG,IAAI,CAqD3C;AAED,eAAe,IAAI,CAAC"}
|
package/dist/shims/head.js
CHANGED
|
@@ -59,7 +59,10 @@ function reactElementToHTML(child) {
|
|
|
59
59
|
}
|
|
60
60
|
}
|
|
61
61
|
else if (key === "dangerouslySetInnerHTML") {
|
|
62
|
-
// Intentionally raw — developer explicitly opted in
|
|
62
|
+
// Intentionally raw — developer explicitly opted in.
|
|
63
|
+
// SECURITY NOTE: This injects raw HTML during SSR. The client-side
|
|
64
|
+
// path (line ~148) skips dangerouslySetInnerHTML for safety. Developers
|
|
65
|
+
// must never pass unsanitized user input here — it is a stored XSS vector.
|
|
63
66
|
const html = value;
|
|
64
67
|
if (html?.__html)
|
|
65
68
|
innerHTML = html.__html;
|
package/dist/shims/head.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"head.js","sourceRoot":"","sources":["../../src/shims/head.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * next/head shim\n *\n * In the Pages Router, <Head> manages document <head> elements.\n * - On the server: collects elements into a module-level array that the\n * dev-server reads after render and injects into the HTML <head>.\n * - On the client: uses useEffect + DOM manipulation.\n */\nimport React, { useEffect, Children, isValidElement } from \"react\";\n\ninterface HeadProps {\n children?: React.ReactNode;\n}\n\n// --- SSR head collection ---\n// State uses a registration pattern so this module can be bundled for the\n// browser. The ALS-backed implementation lives in head-state.ts (server-only).\n\nlet _ssrHeadElements: string[] = [];\n\nlet _getSSRHeadElements = (): string[] => _ssrHeadElements;\nlet _resetSSRHeadImpl = (): void => { _ssrHeadElements = []; };\n\n/**\n * Register ALS-backed state accessors. Called by head-state.ts on import.\n * @internal\n */\nexport function _registerHeadStateAccessors(accessors: {\n getSSRHeadElements: () => string[];\n resetSSRHead: () => void;\n}): void {\n _getSSRHeadElements = accessors.getSSRHeadElements;\n _resetSSRHeadImpl = accessors.resetSSRHead;\n}\n\n/** Reset the SSR head collector. Call before render. */\nexport function resetSSRHead(): void {\n _resetSSRHeadImpl();\n}\n\n/** Get collected head HTML. Call after render. */\nexport function getSSRHeadHTML(): string {\n return _getSSRHeadElements().join(\"\\n \");\n}\n\n/**\n * Tags allowed inside <head>. Anything else is silently dropped.\n * This prevents injection of dangerous elements like <iframe>, <object>, etc.\n */\nconst ALLOWED_HEAD_TAGS = new Set([\n \"title\", \"meta\", \"link\", \"style\", \"script\", \"base\", \"noscript\",\n]);\n\n/**\n * Convert a React element to an HTML string for SSR head injection.\n * Returns an empty string for disallowed tag types.\n */\nfunction reactElementToHTML(child: React.ReactElement): string {\n const tag = child.type as string;\n\n if (!ALLOWED_HEAD_TAGS.has(tag)) {\n if (process.env.NODE_ENV !== \"production\") {\n console.warn(\n `[vinext] <Head> ignoring disallowed tag <${tag}>. ` +\n `Only ${[...ALLOWED_HEAD_TAGS].join(\", \")} are allowed.`,\n );\n }\n return \"\";\n }\n\n const props = child.props as Record<string, unknown>;\n const attrs: string[] = [];\n let innerHTML = \"\";\n\n for (const [key, value] of Object.entries(props)) {\n if (key === \"children\") {\n if (typeof value === \"string\") {\n innerHTML = escapeHTML(value);\n }\n } else if (key === \"dangerouslySetInnerHTML\") {\n // Intentionally raw — developer explicitly opted in\n const html = value as { __html: string };\n if (html?.__html) innerHTML = html.__html;\n } else if (key === \"className\") {\n attrs.push(`class=\"${escapeAttr(String(value))}\"`);\n } else if (typeof value === \"string\") {\n attrs.push(`${key}=\"${escapeAttr(value)}\"`);\n } else if (typeof value === \"boolean\" && value) {\n attrs.push(key);\n }\n }\n\n const attrStr = attrs.length ? \" \" + attrs.join(\" \") : \"\";\n\n // Self-closing tags\n const selfClosing = [\"meta\", \"link\", \"base\"];\n if (selfClosing.includes(tag)) {\n return `<${tag}${attrStr} data-vinext-head=\"true\" />`;\n }\n\n return `<${tag}${attrStr} data-vinext-head=\"true\">${innerHTML}</${tag}>`;\n}\n\nfunction escapeHTML(s: string): string {\n return s.replace(/&/g, \"&\").replace(/</g, \"<\").replace(/>/g, \">\");\n}\n\nexport function escapeAttr(s: string): string {\n return s.replace(/&/g, \"&\").replace(/\"/g, \""\").replace(/</g, \"<\").replace(/>/g, \">\");\n}\n\n// --- Component ---\n\nfunction Head({ children }: HeadProps): null {\n // SSR path: collect elements for later injection\n if (typeof window === \"undefined\") {\n Children.forEach(children, (child) => {\n if (!isValidElement(child)) return;\n if (typeof child.type !== \"string\") return;\n const html = reactElementToHTML(child);\n if (html) _getSSRHeadElements().push(html);\n });\n return null;\n }\n\n // Client path: useEffect DOM manipulation (runs after hydration)\n // eslint-disable-next-line react-hooks/rules-of-hooks\n useEffect(() => {\n const elements: Element[] = [];\n\n // Remove previous vinext-managed head elements\n document\n .querySelectorAll(\"[data-vinext-head]\")\n .forEach((el) => el.remove());\n\n Children.forEach(children, (child) => {\n if (!isValidElement(child)) return;\n if (typeof child.type !== \"string\") return;\n if (!ALLOWED_HEAD_TAGS.has(child.type)) return;\n\n const domEl = document.createElement(child.type);\n const props = child.props as Record<string, unknown>;\n\n for (const [key, value] of Object.entries(props)) {\n if (key === \"children\" && typeof value === \"string\") {\n domEl.textContent = value;\n } else if (key === \"dangerouslySetInnerHTML\") {\n // skip for safety\n } else if (key === \"className\") {\n domEl.setAttribute(\"class\", String(value));\n } else if (key !== \"children\" && typeof value === \"string\") {\n domEl.setAttribute(key, value);\n }\n }\n\n domEl.setAttribute(\"data-vinext-head\", \"true\");\n document.head.appendChild(domEl);\n elements.push(domEl);\n });\n\n return () => {\n elements.forEach((el) => el.remove());\n };\n }, [children]);\n\n return null;\n}\n\nexport default Head;\n"]}
|
|
1
|
+
{"version":3,"file":"head.js","sourceRoot":"","sources":["../../src/shims/head.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * next/head shim\n *\n * In the Pages Router, <Head> manages document <head> elements.\n * - On the server: collects elements into a module-level array that the\n * dev-server reads after render and injects into the HTML <head>.\n * - On the client: uses useEffect + DOM manipulation.\n */\nimport React, { useEffect, Children, isValidElement } from \"react\";\n\ninterface HeadProps {\n children?: React.ReactNode;\n}\n\n// --- SSR head collection ---\n// State uses a registration pattern so this module can be bundled for the\n// browser. The ALS-backed implementation lives in head-state.ts (server-only).\n\nlet _ssrHeadElements: string[] = [];\n\nlet _getSSRHeadElements = (): string[] => _ssrHeadElements;\nlet _resetSSRHeadImpl = (): void => { _ssrHeadElements = []; };\n\n/**\n * Register ALS-backed state accessors. Called by head-state.ts on import.\n * @internal\n */\nexport function _registerHeadStateAccessors(accessors: {\n getSSRHeadElements: () => string[];\n resetSSRHead: () => void;\n}): void {\n _getSSRHeadElements = accessors.getSSRHeadElements;\n _resetSSRHeadImpl = accessors.resetSSRHead;\n}\n\n/** Reset the SSR head collector. Call before render. */\nexport function resetSSRHead(): void {\n _resetSSRHeadImpl();\n}\n\n/** Get collected head HTML. Call after render. */\nexport function getSSRHeadHTML(): string {\n return _getSSRHeadElements().join(\"\\n \");\n}\n\n/**\n * Tags allowed inside <head>. Anything else is silently dropped.\n * This prevents injection of dangerous elements like <iframe>, <object>, etc.\n */\nconst ALLOWED_HEAD_TAGS = new Set([\n \"title\", \"meta\", \"link\", \"style\", \"script\", \"base\", \"noscript\",\n]);\n\n/**\n * Convert a React element to an HTML string for SSR head injection.\n * Returns an empty string for disallowed tag types.\n */\nfunction reactElementToHTML(child: React.ReactElement): string {\n const tag = child.type as string;\n\n if (!ALLOWED_HEAD_TAGS.has(tag)) {\n if (process.env.NODE_ENV !== \"production\") {\n console.warn(\n `[vinext] <Head> ignoring disallowed tag <${tag}>. ` +\n `Only ${[...ALLOWED_HEAD_TAGS].join(\", \")} are allowed.`,\n );\n }\n return \"\";\n }\n\n const props = child.props as Record<string, unknown>;\n const attrs: string[] = [];\n let innerHTML = \"\";\n\n for (const [key, value] of Object.entries(props)) {\n if (key === \"children\") {\n if (typeof value === \"string\") {\n innerHTML = escapeHTML(value);\n }\n } else if (key === \"dangerouslySetInnerHTML\") {\n // Intentionally raw — developer explicitly opted in.\n // SECURITY NOTE: This injects raw HTML during SSR. The client-side\n // path (line ~148) skips dangerouslySetInnerHTML for safety. Developers\n // must never pass unsanitized user input here — it is a stored XSS vector.\n const html = value as { __html: string };\n if (html?.__html) innerHTML = html.__html;\n } else if (key === \"className\") {\n attrs.push(`class=\"${escapeAttr(String(value))}\"`);\n } else if (typeof value === \"string\") {\n attrs.push(`${key}=\"${escapeAttr(value)}\"`);\n } else if (typeof value === \"boolean\" && value) {\n attrs.push(key);\n }\n }\n\n const attrStr = attrs.length ? \" \" + attrs.join(\" \") : \"\";\n\n // Self-closing tags\n const selfClosing = [\"meta\", \"link\", \"base\"];\n if (selfClosing.includes(tag)) {\n return `<${tag}${attrStr} data-vinext-head=\"true\" />`;\n }\n\n return `<${tag}${attrStr} data-vinext-head=\"true\">${innerHTML}</${tag}>`;\n}\n\nfunction escapeHTML(s: string): string {\n return s.replace(/&/g, \"&\").replace(/</g, \"<\").replace(/>/g, \">\");\n}\n\nexport function escapeAttr(s: string): string {\n return s.replace(/&/g, \"&\").replace(/\"/g, \""\").replace(/</g, \"<\").replace(/>/g, \">\");\n}\n\n// --- Component ---\n\nfunction Head({ children }: HeadProps): null {\n // SSR path: collect elements for later injection\n if (typeof window === \"undefined\") {\n Children.forEach(children, (child) => {\n if (!isValidElement(child)) return;\n if (typeof child.type !== \"string\") return;\n const html = reactElementToHTML(child);\n if (html) _getSSRHeadElements().push(html);\n });\n return null;\n }\n\n // Client path: useEffect DOM manipulation (runs after hydration)\n // eslint-disable-next-line react-hooks/rules-of-hooks\n useEffect(() => {\n const elements: Element[] = [];\n\n // Remove previous vinext-managed head elements\n document\n .querySelectorAll(\"[data-vinext-head]\")\n .forEach((el) => el.remove());\n\n Children.forEach(children, (child) => {\n if (!isValidElement(child)) return;\n if (typeof child.type !== \"string\") return;\n if (!ALLOWED_HEAD_TAGS.has(child.type)) return;\n\n const domEl = document.createElement(child.type);\n const props = child.props as Record<string, unknown>;\n\n for (const [key, value] of Object.entries(props)) {\n if (key === \"children\" && typeof value === \"string\") {\n domEl.textContent = value;\n } else if (key === \"dangerouslySetInnerHTML\") {\n // skip for safety\n } else if (key === \"className\") {\n domEl.setAttribute(\"class\", String(value));\n } else if (key !== \"children\" && typeof value === \"string\") {\n domEl.setAttribute(key, value);\n }\n }\n\n domEl.setAttribute(\"data-vinext-head\", \"true\");\n document.head.appendChild(domEl);\n elements.push(domEl);\n });\n\n return () => {\n elements.forEach((el) => el.remove());\n };\n }, [children]);\n\n return null;\n}\n\nexport default Head;\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"headers.d.ts","sourceRoot":"","sources":["../../src/shims/headers.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAQH,UAAU,cAAc;IACtB,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAC9B;AAiCD;;;;GAIG;AAGH;;;GAGG;AACH,wBAAgB,gBAAgB,IAAI,IAAI,CAEvC;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,IAAI,OAAO,CAK7C;AAED;;;;;;;;GAQG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,cAAc,GAAG,IAAI,GAAG,IAAI,CA4BlE;AAED;;;;;;;;;GASG;AACH,wBAAgB,qBAAqB,CAAC,CAAC,EACrC,GAAG,EAAE,cAAc,EACnB,EAAE,EAAE,MAAM,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,GACvB,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAShB;AAED;;;;;;;;GAQG;AACH,wBAAgB,6BAA6B,CAC3C,yBAAyB,EAAE,OAAO,GACjC,IAAI,CAyBN;AAED;;GAEG;AACH,wBAAgB,yBAAyB,CAAC,OAAO,EAAE,OAAO,GAAG,cAAc,CAa1E;AAMD;;;;GAIG;AACH,wBAAsB,OAAO,IAAI,OAAO,CAAC,OAAO,CAAC,CAUhD;AAED;;;GAGG;AACH,wBAAsB,OAAO,IAAI,OAAO,CAAC,cAAc,CAAC,CAUvD;AAMD,4EAA4E;AAG5E;;;GAGG;AACH,wBAAgB,yBAAyB,IAAI,MAAM,EAAE,CAKpD;AAsBD;;;GAGG;AACH,wBAAgB,wBAAwB,IAAI,MAAM,GAAG,IAAI,CAKxD;AAED,UAAU,eAAe;IACvB,SAAS,EAAE,OAAO,CAAC;IACnB,MAAM,IAAI,IAAI,CAAC;IACf,OAAO,IAAI,IAAI,CAAC;CACjB;AAED;;;;;;GAMG;AACH,wBAAsB,SAAS,IAAI,OAAO,CAAC,eAAe,CAAC,
|
|
1
|
+
{"version":3,"file":"headers.d.ts","sourceRoot":"","sources":["../../src/shims/headers.ts"],"names":[],"mappings":"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"}
|
package/dist/shims/headers.js
CHANGED
|
@@ -249,15 +249,17 @@ export async function draftMode() {
|
|
|
249
249
|
if (state.headersContext) {
|
|
250
250
|
state.headersContext.cookies.set(DRAFT_MODE_COOKIE, secret);
|
|
251
251
|
}
|
|
252
|
+
const secure = typeof process !== "undefined" && process.env?.NODE_ENV === "production" ? "; Secure" : "";
|
|
252
253
|
state.draftModeCookieHeader =
|
|
253
|
-
`${DRAFT_MODE_COOKIE}=${secret}; Path=/; HttpOnly; SameSite=Lax`;
|
|
254
|
+
`${DRAFT_MODE_COOKIE}=${secret}; Path=/; HttpOnly; SameSite=Lax${secure}`;
|
|
254
255
|
},
|
|
255
256
|
disable() {
|
|
256
257
|
if (state.headersContext) {
|
|
257
258
|
state.headersContext.cookies.delete(DRAFT_MODE_COOKIE);
|
|
258
259
|
}
|
|
260
|
+
const secure = typeof process !== "undefined" && process.env?.NODE_ENV === "production" ? "; Secure" : "";
|
|
259
261
|
state.draftModeCookieHeader =
|
|
260
|
-
`${DRAFT_MODE_COOKIE}=; Path=/; HttpOnly; SameSite=Lax; Max-Age=0`;
|
|
262
|
+
`${DRAFT_MODE_COOKIE}=; Path=/; HttpOnly; SameSite=Lax${secure}; Max-Age=0`;
|
|
261
263
|
},
|
|
262
264
|
};
|
|
263
265
|
}
|