vinext 0.0.34 → 0.0.36
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 +1 -0
- package/dist/build/prerender.d.ts +9 -4
- package/dist/build/prerender.js +27 -9
- package/dist/build/prerender.js.map +1 -1
- package/dist/build/run-prerender.js +4 -1
- package/dist/build/run-prerender.js.map +1 -1
- package/dist/config/config-matchers.js +1 -1
- package/dist/config/config-matchers.js.map +1 -1
- package/dist/config/next-config.d.ts +7 -1
- package/dist/config/next-config.js +39 -26
- package/dist/config/next-config.js.map +1 -1
- package/dist/entries/pages-server-entry.js +162 -354
- package/dist/entries/pages-server-entry.js.map +1 -1
- package/dist/index.d.ts +9 -1
- package/dist/index.js +40 -4
- package/dist/index.js.map +1 -1
- package/dist/plugins/optimize-imports.js +28 -2
- package/dist/plugins/optimize-imports.js.map +1 -1
- package/dist/server/app-browser-entry.js +2 -2
- package/dist/server/app-browser-entry.js.map +1 -1
- package/dist/server/app-page-render.js +6 -4
- package/dist/server/app-page-render.js.map +1 -1
- package/dist/server/app-page-response.js +1 -1
- package/dist/server/app-page-response.js.map +1 -1
- package/dist/server/app-page-stream.d.ts +9 -1
- package/dist/server/app-page-stream.js +37 -4
- package/dist/server/app-page-stream.js.map +1 -1
- package/dist/server/app-ssr-stream.js +5 -3
- package/dist/server/app-ssr-stream.js.map +1 -1
- package/dist/server/pages-page-data.d.ts +105 -0
- package/dist/server/pages-page-data.js +177 -0
- package/dist/server/pages-page-data.js.map +1 -0
- package/dist/server/pages-page-response.d.ts +55 -0
- package/dist/server/pages-page-response.js +140 -0
- package/dist/server/pages-page-response.js.map +1 -0
- package/dist/server/prod-server.js +3 -0
- package/dist/server/prod-server.js.map +1 -1
- package/dist/server/seed-cache.d.ts +44 -0
- package/dist/server/seed-cache.js +127 -0
- package/dist/server/seed-cache.js.map +1 -0
- package/dist/shims/image.js +4 -2
- package/dist/shims/image.js.map +1 -1
- package/dist/shims/navigation-state.js +5 -3
- package/dist/shims/navigation-state.js.map +1 -1
- package/dist/shims/navigation.d.ts +9 -7
- package/dist/shims/navigation.js +20 -5
- package/dist/shims/navigation.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
//#region src/server/seed-cache.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* Seed the memory cache from pre-rendered build output.
|
|
4
|
+
*
|
|
5
|
+
* Reads `vinext-prerender.json` and the corresponding HTML/RSC files from
|
|
6
|
+
* `dist/server/prerendered-routes/`, then populates the active CacheHandler
|
|
7
|
+
* so pre-rendered pages are served as cache HITs on the very first request
|
|
8
|
+
* instead of triggering a full re-render.
|
|
9
|
+
*
|
|
10
|
+
* This is only useful for the MemoryCacheHandler (the default for Node.js
|
|
11
|
+
* production). Persistent backends like KV already retain entries across
|
|
12
|
+
* deploys and can be pre-populated via TPR or similar mechanisms.
|
|
13
|
+
*
|
|
14
|
+
* Consistency model:
|
|
15
|
+
* - The manifest is authoritative for which routes were pre-rendered and their
|
|
16
|
+
* revalidation config. The HTML/RSC files on disk are the source of truth
|
|
17
|
+
* for content. Both are produced by the same build and are immutable after
|
|
18
|
+
* the build completes.
|
|
19
|
+
* - Cache keys include the buildId, so entries from a previous build are never
|
|
20
|
+
* matched by a new server process (new build = new buildId = new keys).
|
|
21
|
+
* - Seeded entries are indistinguishable from entries created by the ISR
|
|
22
|
+
* render path: same cache value shape, same revalidate duration tracking,
|
|
23
|
+
* same cache key construction. The serving path does not know or care
|
|
24
|
+
* whether an entry was seeded or rendered.
|
|
25
|
+
*
|
|
26
|
+
* Concurrency model:
|
|
27
|
+
* - This function runs at startup before the HTTP server begins accepting
|
|
28
|
+
* requests, so there are no concurrent readers during seeding. All I/O is
|
|
29
|
+
* synchronous (readFileSync) which is appropriate for a startup-only path
|
|
30
|
+
* that runs once before the event loop serves traffic.
|
|
31
|
+
*/
|
|
32
|
+
/**
|
|
33
|
+
* Read pre-rendered routes from disk and seed the active CacheHandler.
|
|
34
|
+
*
|
|
35
|
+
* Call this during production server startup, before any requests are served.
|
|
36
|
+
* If the manifest doesn't exist (no prerender phase was run), this is a no-op.
|
|
37
|
+
*
|
|
38
|
+
* @param serverDir - Path to `dist/server/` (where vinext-prerender.json lives)
|
|
39
|
+
* @returns The number of routes seeded (0 if no manifest or no renderable routes).
|
|
40
|
+
*/
|
|
41
|
+
declare function seedMemoryCacheFromPrerender(serverDir: string): Promise<number>;
|
|
42
|
+
//#endregion
|
|
43
|
+
export { seedMemoryCacheFromPrerender };
|
|
44
|
+
//# sourceMappingURL=seed-cache.d.ts.map
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import { getCacheHandler } from "../shims/cache.js";
|
|
2
|
+
import { isrCacheKey, setRevalidateDuration } from "./isr-cache.js";
|
|
3
|
+
import { getOutputPath, getRscOutputPath } from "../build/prerender.js";
|
|
4
|
+
import fs from "node:fs";
|
|
5
|
+
import path from "node:path";
|
|
6
|
+
//#region src/server/seed-cache.ts
|
|
7
|
+
/**
|
|
8
|
+
* Seed the memory cache from pre-rendered build output.
|
|
9
|
+
*
|
|
10
|
+
* Reads `vinext-prerender.json` and the corresponding HTML/RSC files from
|
|
11
|
+
* `dist/server/prerendered-routes/`, then populates the active CacheHandler
|
|
12
|
+
* so pre-rendered pages are served as cache HITs on the very first request
|
|
13
|
+
* instead of triggering a full re-render.
|
|
14
|
+
*
|
|
15
|
+
* This is only useful for the MemoryCacheHandler (the default for Node.js
|
|
16
|
+
* production). Persistent backends like KV already retain entries across
|
|
17
|
+
* deploys and can be pre-populated via TPR or similar mechanisms.
|
|
18
|
+
*
|
|
19
|
+
* Consistency model:
|
|
20
|
+
* - The manifest is authoritative for which routes were pre-rendered and their
|
|
21
|
+
* revalidation config. The HTML/RSC files on disk are the source of truth
|
|
22
|
+
* for content. Both are produced by the same build and are immutable after
|
|
23
|
+
* the build completes.
|
|
24
|
+
* - Cache keys include the buildId, so entries from a previous build are never
|
|
25
|
+
* matched by a new server process (new build = new buildId = new keys).
|
|
26
|
+
* - Seeded entries are indistinguishable from entries created by the ISR
|
|
27
|
+
* render path: same cache value shape, same revalidate duration tracking,
|
|
28
|
+
* same cache key construction. The serving path does not know or care
|
|
29
|
+
* whether an entry was seeded or rendered.
|
|
30
|
+
*
|
|
31
|
+
* Concurrency model:
|
|
32
|
+
* - This function runs at startup before the HTTP server begins accepting
|
|
33
|
+
* requests, so there are no concurrent readers during seeding. All I/O is
|
|
34
|
+
* synchronous (readFileSync) which is appropriate for a startup-only path
|
|
35
|
+
* that runs once before the event loop serves traffic.
|
|
36
|
+
*/
|
|
37
|
+
/**
|
|
38
|
+
* Read pre-rendered routes from disk and seed the active CacheHandler.
|
|
39
|
+
*
|
|
40
|
+
* Call this during production server startup, before any requests are served.
|
|
41
|
+
* If the manifest doesn't exist (no prerender phase was run), this is a no-op.
|
|
42
|
+
*
|
|
43
|
+
* @param serverDir - Path to `dist/server/` (where vinext-prerender.json lives)
|
|
44
|
+
* @returns The number of routes seeded (0 if no manifest or no renderable routes).
|
|
45
|
+
*/
|
|
46
|
+
async function seedMemoryCacheFromPrerender(serverDir) {
|
|
47
|
+
const manifestPath = path.join(serverDir, "vinext-prerender.json");
|
|
48
|
+
if (!fs.existsSync(manifestPath)) return 0;
|
|
49
|
+
let manifest;
|
|
50
|
+
try {
|
|
51
|
+
manifest = JSON.parse(fs.readFileSync(manifestPath, "utf-8"));
|
|
52
|
+
} catch (err) {
|
|
53
|
+
console.warn("[vinext] Failed to parse vinext-prerender.json, skipping cache seeding:", err);
|
|
54
|
+
return 0;
|
|
55
|
+
}
|
|
56
|
+
const { buildId, routes } = manifest;
|
|
57
|
+
if (!buildId || !Array.isArray(routes)) return 0;
|
|
58
|
+
const trailingSlash = manifest.trailingSlash ?? false;
|
|
59
|
+
const prerenderDir = path.join(serverDir, "prerendered-routes");
|
|
60
|
+
const handler = getCacheHandler();
|
|
61
|
+
let seeded = 0;
|
|
62
|
+
for (const route of routes) {
|
|
63
|
+
if (route.status !== "rendered") continue;
|
|
64
|
+
if (route.router !== "app") continue;
|
|
65
|
+
const pathname = route.path ?? route.route;
|
|
66
|
+
const baseKey = isrCacheKey("app", pathname, buildId);
|
|
67
|
+
const revalidateSeconds = typeof route.revalidate === "number" ? route.revalidate : void 0;
|
|
68
|
+
if (await seedHtml(handler, prerenderDir, baseKey, pathname, trailingSlash, revalidateSeconds)) {
|
|
69
|
+
await seedRsc(handler, prerenderDir, baseKey, pathname, revalidateSeconds);
|
|
70
|
+
seeded++;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
return seeded;
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Build the CacheHandler context object from a revalidate value.
|
|
77
|
+
* `revalidate: undefined` (static routes) → empty context → no expiry.
|
|
78
|
+
*/
|
|
79
|
+
function revalidateCtx(seconds) {
|
|
80
|
+
return seconds !== void 0 ? { revalidate: seconds } : {};
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Seed the HTML cache entry for a single route.
|
|
84
|
+
* Returns true if the file existed and was seeded.
|
|
85
|
+
*/
|
|
86
|
+
async function seedHtml(handler, prerenderDir, baseKey, pathname, trailingSlash, revalidateSeconds) {
|
|
87
|
+
const relPath = getOutputPath(pathname, trailingSlash);
|
|
88
|
+
const fullPath = path.join(prerenderDir, relPath);
|
|
89
|
+
if (!fs.existsSync(fullPath)) return false;
|
|
90
|
+
const htmlValue = {
|
|
91
|
+
kind: "APP_PAGE",
|
|
92
|
+
html: fs.readFileSync(fullPath, "utf-8"),
|
|
93
|
+
rscData: void 0,
|
|
94
|
+
headers: void 0,
|
|
95
|
+
postponed: void 0,
|
|
96
|
+
status: void 0
|
|
97
|
+
};
|
|
98
|
+
const key = baseKey + ":html";
|
|
99
|
+
await handler.set(key, htmlValue, revalidateCtx(revalidateSeconds));
|
|
100
|
+
if (revalidateSeconds !== void 0) setRevalidateDuration(key, revalidateSeconds);
|
|
101
|
+
return true;
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Seed the RSC cache entry for a single route.
|
|
105
|
+
* No-op if the .rsc file doesn't exist on disk.
|
|
106
|
+
*/
|
|
107
|
+
async function seedRsc(handler, prerenderDir, baseKey, pathname, revalidateSeconds) {
|
|
108
|
+
const relPath = getRscOutputPath(pathname);
|
|
109
|
+
const fullPath = path.join(prerenderDir, relPath);
|
|
110
|
+
if (!fs.existsSync(fullPath)) return;
|
|
111
|
+
const rscBuffer = fs.readFileSync(fullPath);
|
|
112
|
+
const rscValue = {
|
|
113
|
+
kind: "APP_PAGE",
|
|
114
|
+
html: "",
|
|
115
|
+
rscData: rscBuffer.buffer.slice(rscBuffer.byteOffset, rscBuffer.byteOffset + rscBuffer.byteLength),
|
|
116
|
+
headers: void 0,
|
|
117
|
+
postponed: void 0,
|
|
118
|
+
status: void 0
|
|
119
|
+
};
|
|
120
|
+
const key = baseKey + ":rsc";
|
|
121
|
+
await handler.set(key, rscValue, revalidateCtx(revalidateSeconds));
|
|
122
|
+
if (revalidateSeconds !== void 0) setRevalidateDuration(key, revalidateSeconds);
|
|
123
|
+
}
|
|
124
|
+
//#endregion
|
|
125
|
+
export { seedMemoryCacheFromPrerender };
|
|
126
|
+
|
|
127
|
+
//# sourceMappingURL=seed-cache.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"seed-cache.js","names":[],"sources":["../../src/server/seed-cache.ts"],"sourcesContent":["/**\n * Seed the memory cache from pre-rendered build output.\n *\n * Reads `vinext-prerender.json` and the corresponding HTML/RSC files from\n * `dist/server/prerendered-routes/`, then populates the active CacheHandler\n * so pre-rendered pages are served as cache HITs on the very first request\n * instead of triggering a full re-render.\n *\n * This is only useful for the MemoryCacheHandler (the default for Node.js\n * production). Persistent backends like KV already retain entries across\n * deploys and can be pre-populated via TPR or similar mechanisms.\n *\n * Consistency model:\n * - The manifest is authoritative for which routes were pre-rendered and their\n * revalidation config. The HTML/RSC files on disk are the source of truth\n * for content. Both are produced by the same build and are immutable after\n * the build completes.\n * - Cache keys include the buildId, so entries from a previous build are never\n * matched by a new server process (new build = new buildId = new keys).\n * - Seeded entries are indistinguishable from entries created by the ISR\n * render path: same cache value shape, same revalidate duration tracking,\n * same cache key construction. The serving path does not know or care\n * whether an entry was seeded or rendered.\n *\n * Concurrency model:\n * - This function runs at startup before the HTTP server begins accepting\n * requests, so there are no concurrent readers during seeding. All I/O is\n * synchronous (readFileSync) which is appropriate for a startup-only path\n * that runs once before the event loop serves traffic.\n */\n\nimport fs from \"node:fs\";\nimport path from \"node:path\";\nimport { getCacheHandler, type CachedAppPageValue } from \"../shims/cache.js\";\nimport { isrCacheKey, setRevalidateDuration } from \"./isr-cache.js\";\nimport { getOutputPath, getRscOutputPath } from \"../build/prerender.js\";\n\n// ─── Manifest types ───────────────────────────────────────────────────────────\n\ninterface PrerenderManifest {\n buildId: string;\n trailingSlash?: boolean;\n routes: PrerenderManifestRoute[];\n}\n\ninterface PrerenderManifestRoute {\n route: string;\n status: string;\n revalidate?: number | false;\n path?: string;\n router?: \"app\" | \"pages\";\n}\n\n// ─── Public API ───────────────────────────────────────────────────────────────\n\n/**\n * Read pre-rendered routes from disk and seed the active CacheHandler.\n *\n * Call this during production server startup, before any requests are served.\n * If the manifest doesn't exist (no prerender phase was run), this is a no-op.\n *\n * @param serverDir - Path to `dist/server/` (where vinext-prerender.json lives)\n * @returns The number of routes seeded (0 if no manifest or no renderable routes).\n */\nexport async function seedMemoryCacheFromPrerender(serverDir: string): Promise<number> {\n const manifestPath = path.join(serverDir, \"vinext-prerender.json\");\n if (!fs.existsSync(manifestPath)) return 0;\n\n let manifest: PrerenderManifest;\n try {\n manifest = JSON.parse(fs.readFileSync(manifestPath, \"utf-8\"));\n } catch (err) {\n console.warn(\"[vinext] Failed to parse vinext-prerender.json, skipping cache seeding:\", err);\n return 0;\n }\n\n const { buildId, routes } = manifest;\n if (!buildId || !Array.isArray(routes)) return 0;\n\n const trailingSlash = manifest.trailingSlash ?? false;\n const prerenderDir = path.join(serverDir, \"prerendered-routes\");\n const handler = getCacheHandler();\n let seeded = 0;\n\n for (const route of routes) {\n if (route.status !== \"rendered\") continue;\n if (route.router !== \"app\") continue;\n\n const pathname = route.path ?? route.route;\n const baseKey = isrCacheKey(\"app\", pathname, buildId);\n const revalidateSeconds = typeof route.revalidate === \"number\" ? route.revalidate : undefined;\n\n if (\n await seedHtml(handler, prerenderDir, baseKey, pathname, trailingSlash, revalidateSeconds)\n ) {\n await seedRsc(handler, prerenderDir, baseKey, pathname, revalidateSeconds);\n seeded++;\n }\n }\n\n return seeded;\n}\n\n// ─── Internals ────────────────────────────────────────────────────────────────\n\n/**\n * Build the CacheHandler context object from a revalidate value.\n * `revalidate: undefined` (static routes) → empty context → no expiry.\n */\nfunction revalidateCtx(seconds: number | undefined): Record<string, unknown> {\n return seconds !== undefined ? { revalidate: seconds } : {};\n}\n\n/**\n * Seed the HTML cache entry for a single route.\n * Returns true if the file existed and was seeded.\n */\nasync function seedHtml(\n handler: ReturnType<typeof getCacheHandler>,\n prerenderDir: string,\n baseKey: string,\n pathname: string,\n trailingSlash: boolean,\n revalidateSeconds: number | undefined,\n): Promise<boolean> {\n const relPath = getOutputPath(pathname, trailingSlash);\n const fullPath = path.join(prerenderDir, relPath);\n if (!fs.existsSync(fullPath)) return false;\n\n const htmlValue: CachedAppPageValue = {\n kind: \"APP_PAGE\",\n html: fs.readFileSync(fullPath, \"utf-8\"),\n rscData: undefined,\n headers: undefined,\n postponed: undefined,\n status: undefined,\n };\n\n const key = baseKey + \":html\";\n await handler.set(key, htmlValue, revalidateCtx(revalidateSeconds));\n\n if (revalidateSeconds !== undefined) {\n setRevalidateDuration(key, revalidateSeconds);\n }\n\n return true;\n}\n\n/**\n * Seed the RSC cache entry for a single route.\n * No-op if the .rsc file doesn't exist on disk.\n */\nasync function seedRsc(\n handler: ReturnType<typeof getCacheHandler>,\n prerenderDir: string,\n baseKey: string,\n pathname: string,\n revalidateSeconds: number | undefined,\n): Promise<void> {\n const relPath = getRscOutputPath(pathname);\n const fullPath = path.join(prerenderDir, relPath);\n if (!fs.existsSync(fullPath)) return;\n\n const rscBuffer = fs.readFileSync(fullPath);\n const rscValue: CachedAppPageValue = {\n kind: \"APP_PAGE\",\n html: \"\",\n rscData: rscBuffer.buffer.slice(\n rscBuffer.byteOffset,\n rscBuffer.byteOffset + rscBuffer.byteLength,\n ),\n headers: undefined,\n postponed: undefined,\n status: undefined,\n };\n\n const key = baseKey + \":rsc\";\n await handler.set(key, rscValue, revalidateCtx(revalidateSeconds));\n\n if (revalidateSeconds !== undefined) {\n setRevalidateDuration(key, revalidateSeconds);\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAgEA,eAAsB,6BAA6B,WAAoC;CACrF,MAAM,eAAe,KAAK,KAAK,WAAW,wBAAwB;AAClE,KAAI,CAAC,GAAG,WAAW,aAAa,CAAE,QAAO;CAEzC,IAAI;AACJ,KAAI;AACF,aAAW,KAAK,MAAM,GAAG,aAAa,cAAc,QAAQ,CAAC;UACtD,KAAK;AACZ,UAAQ,KAAK,2EAA2E,IAAI;AAC5F,SAAO;;CAGT,MAAM,EAAE,SAAS,WAAW;AAC5B,KAAI,CAAC,WAAW,CAAC,MAAM,QAAQ,OAAO,CAAE,QAAO;CAE/C,MAAM,gBAAgB,SAAS,iBAAiB;CAChD,MAAM,eAAe,KAAK,KAAK,WAAW,qBAAqB;CAC/D,MAAM,UAAU,iBAAiB;CACjC,IAAI,SAAS;AAEb,MAAK,MAAM,SAAS,QAAQ;AAC1B,MAAI,MAAM,WAAW,WAAY;AACjC,MAAI,MAAM,WAAW,MAAO;EAE5B,MAAM,WAAW,MAAM,QAAQ,MAAM;EACrC,MAAM,UAAU,YAAY,OAAO,UAAU,QAAQ;EACrD,MAAM,oBAAoB,OAAO,MAAM,eAAe,WAAW,MAAM,aAAa,KAAA;AAEpF,MACE,MAAM,SAAS,SAAS,cAAc,SAAS,UAAU,eAAe,kBAAkB,EAC1F;AACA,SAAM,QAAQ,SAAS,cAAc,SAAS,UAAU,kBAAkB;AAC1E;;;AAIJ,QAAO;;;;;;AAST,SAAS,cAAc,SAAsD;AAC3E,QAAO,YAAY,KAAA,IAAY,EAAE,YAAY,SAAS,GAAG,EAAE;;;;;;AAO7D,eAAe,SACb,SACA,cACA,SACA,UACA,eACA,mBACkB;CAClB,MAAM,UAAU,cAAc,UAAU,cAAc;CACtD,MAAM,WAAW,KAAK,KAAK,cAAc,QAAQ;AACjD,KAAI,CAAC,GAAG,WAAW,SAAS,CAAE,QAAO;CAErC,MAAM,YAAgC;EACpC,MAAM;EACN,MAAM,GAAG,aAAa,UAAU,QAAQ;EACxC,SAAS,KAAA;EACT,SAAS,KAAA;EACT,WAAW,KAAA;EACX,QAAQ,KAAA;EACT;CAED,MAAM,MAAM,UAAU;AACtB,OAAM,QAAQ,IAAI,KAAK,WAAW,cAAc,kBAAkB,CAAC;AAEnE,KAAI,sBAAsB,KAAA,EACxB,uBAAsB,KAAK,kBAAkB;AAG/C,QAAO;;;;;;AAOT,eAAe,QACb,SACA,cACA,SACA,UACA,mBACe;CACf,MAAM,UAAU,iBAAiB,SAAS;CAC1C,MAAM,WAAW,KAAK,KAAK,cAAc,QAAQ;AACjD,KAAI,CAAC,GAAG,WAAW,SAAS,CAAE;CAE9B,MAAM,YAAY,GAAG,aAAa,SAAS;CAC3C,MAAM,WAA+B;EACnC,MAAM;EACN,MAAM;EACN,SAAS,UAAU,OAAO,MACxB,UAAU,YACV,UAAU,aAAa,UAAU,WAClC;EACD,SAAS,KAAA;EACT,WAAW,KAAA;EACX,QAAQ,KAAA;EACT;CAED,MAAM,MAAM,UAAU;AACtB,OAAM,QAAQ,IAAI,KAAK,UAAU,cAAc,kBAAkB,CAAC;AAElE,KAAI,sBAAsB,KAAA,EACxB,uBAAsB,KAAK,kBAAkB"}
|
package/dist/shims/image.js
CHANGED
|
@@ -187,7 +187,8 @@ const Image = forwardRef(function Image({ src: srcProp, alt, width, height, fill
|
|
|
187
187
|
src,
|
|
188
188
|
alt,
|
|
189
189
|
layout: "fullWidth",
|
|
190
|
-
priority,
|
|
190
|
+
loading: priority ? "eager" : loading ?? "lazy",
|
|
191
|
+
fetchPriority: priority ? "high" : void 0,
|
|
191
192
|
sizes,
|
|
192
193
|
className,
|
|
193
194
|
background: bg,
|
|
@@ -199,7 +200,8 @@ const Image = forwardRef(function Image({ src: srcProp, alt, width, height, fill
|
|
|
199
200
|
width: imgWidth,
|
|
200
201
|
height: imgHeight,
|
|
201
202
|
layout: "constrained",
|
|
202
|
-
priority,
|
|
203
|
+
loading: priority ? "eager" : loading ?? "lazy",
|
|
204
|
+
fetchPriority: priority ? "high" : void 0,
|
|
203
205
|
sizes,
|
|
204
206
|
className,
|
|
205
207
|
background: bg,
|
package/dist/shims/image.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"image.js","names":["UnpicImage"],"sources":["../../src/shims/image.tsx"],"sourcesContent":["/**\n * next/image shim\n *\n * Translates Next.js Image props to @unpic/react Image component.\n * @unpic/react auto-detects CDN from URL and uses native transforms.\n * For local images (relative paths), routes through `/_vinext/image`\n * for server-side optimization (resize, format negotiation, quality).\n *\n * Remote images are validated against `images.remotePatterns` and\n * `images.domains` from next.config.js. Unmatched URLs are blocked\n * in production and warn in development, matching Next.js behavior.\n */\nimport React, { forwardRef } from \"react\";\nimport { Image as UnpicImage } from \"@unpic/react\";\nimport { hasRemoteMatch, type RemotePattern } from \"./image-config.js\";\n\nexport interface StaticImageData {\n src: string;\n height: number;\n width: number;\n blurDataURL?: string;\n}\n\n/**\n * Image config injected at build time via Vite define.\n * Serialized as JSON — parsed once at module level.\n */\nconst __imageRemotePatterns: RemotePattern[] = (() => {\n try {\n return JSON.parse(process.env.__VINEXT_IMAGE_REMOTE_PATTERNS ?? \"[]\");\n } catch {\n return [];\n }\n})();\nconst __imageDomains: string[] = (() => {\n try {\n return JSON.parse(process.env.__VINEXT_IMAGE_DOMAINS ?? \"[]\");\n } catch {\n return [];\n }\n})();\nconst __hasImageConfig = __imageRemotePatterns.length > 0 || __imageDomains.length > 0;\nconst __isDev = process.env.NODE_ENV !== \"production\";\nconst __imageDeviceSizes: number[] = (() => {\n try {\n return JSON.parse(\n process.env.__VINEXT_IMAGE_DEVICE_SIZES ?? \"[640,750,828,1080,1200,1920,2048,3840]\",\n );\n } catch {\n return [640, 750, 828, 1080, 1200, 1920, 2048, 3840];\n }\n})();\n/**\n * Whether dangerouslyAllowSVG is enabled in next.config.js.\n * When false (default), .svg sources auto-skip the optimization endpoint\n * and are served directly, matching Next.js behavior.\n * When true, .svg sources are routed through the optimizer (served as-is\n * with security headers).\n */\nconst __dangerouslyAllowSVG = process.env.__VINEXT_IMAGE_DANGEROUSLY_ALLOW_SVG === \"true\";\n/**\n * Validate that a remote URL is allowed by the configured remote patterns.\n * Returns true if the URL is allowed, false otherwise.\n *\n * When no remotePatterns/domains are configured, all remote URLs are allowed\n * (backwards-compatible — user hasn't opted into restriction).\n *\n * When patterns ARE configured, only matching URLs are allowed.\n * In development, non-matching URLs produce a console warning.\n * In production, non-matching URLs are blocked (src replaced with empty string).\n */\nfunction validateRemoteUrl(src: string): { allowed: boolean; reason?: string } {\n if (!__hasImageConfig) {\n // No image config — allow everything (backwards-compatible)\n return { allowed: true };\n }\n\n let url: URL;\n try {\n url = new URL(src, \"http://n\");\n } catch {\n return { allowed: false, reason: `Invalid URL: ${src}` };\n }\n\n if (hasRemoteMatch(__imageDomains, __imageRemotePatterns, url)) {\n return { allowed: true };\n }\n\n return {\n allowed: false,\n reason: `Image URL \"${src}\" is not configured in images.remotePatterns or images.domains in next.config.js. See: https://nextjs.org/docs/messages/next-image-unconfigured-host`,\n };\n}\n\ninterface ImageProps {\n src: string | StaticImageData;\n alt: string;\n width?: number;\n height?: number;\n fill?: boolean;\n priority?: boolean;\n quality?: number;\n placeholder?: \"blur\" | \"empty\";\n blurDataURL?: string;\n loader?: (params: { src: string; width: number; quality?: number }) => string;\n sizes?: string;\n className?: string;\n style?: React.CSSProperties;\n onLoad?: React.ReactEventHandler<HTMLImageElement>;\n /** @deprecated Use onLoad instead. Still supported for migration compat. */\n onLoadingComplete?: (img: HTMLImageElement) => void;\n onError?: React.ReactEventHandler<HTMLImageElement>;\n onClick?: React.MouseEventHandler<HTMLImageElement>;\n id?: string;\n // Accept and ignore Next.js-specific props that don't apply\n unoptimized?: boolean;\n overrideSrc?: string;\n loading?: \"lazy\" | \"eager\";\n}\n\n/**\n * Sanitize a blurDataURL to prevent CSS injection.\n *\n * A crafted data URL containing `)` can break out of the `url()` CSS function,\n * allowing injection of arbitrary CSS properties or rules. Characters like `{`,\n * `}`, and `\\` can also assist in crafting injection payloads.\n *\n * This validates the URL starts with `data:image/` and rejects characters that\n * could escape the `url()` context. Semicolons are allowed since they're part\n * of valid data URLs (`data:image/png;base64,...`) and harmless inside `url()`.\n *\n * Returns undefined for invalid URLs, which causes the blur placeholder to be\n * skipped gracefully.\n */\nfunction sanitizeBlurDataURL(url: string): string | undefined {\n // Must be a data: image URL\n if (!url.startsWith(\"data:image/\")) return undefined;\n // Reject characters that can break out of CSS url():\n // ) - closes url()\n // ( - could open nested functions\n // { } - CSS rule boundaries\n // \\ - CSS escape sequences\n // newlines - break CSS parsing\n if (/[)(}{\\\\'\"\\n\\r]/.test(url)) return undefined;\n return url;\n}\n\n/**\n * Determine if a src is a remote URL (CDN-optimizable) or local.\n */\nfunction isRemoteUrl(src: string): boolean {\n return src.startsWith(\"http://\") || src.startsWith(\"https://\") || src.startsWith(\"//\");\n}\n\n/**\n * Responsive image widths matching Next.js's device sizes config.\n * These are the breakpoints used for srcSet generation.\n * Configurable via `images.deviceSizes` in next.config.js.\n */\nconst RESPONSIVE_WIDTHS = __imageDeviceSizes;\n\n/**\n * Build a `/_vinext/image` optimization URL.\n *\n * In production (Cloudflare Workers), the worker intercepts this path and uses\n * the Images binding to resize/transcode on the fly. In dev, the Vite dev\n * server handles it as a passthrough (serves the original file).\n */\nexport function imageOptimizationUrl(src: string, width: number, quality: number = 75): string {\n return `/_vinext/image?url=${encodeURIComponent(src)}&w=${width}&q=${quality}`;\n}\n\n/**\n * Generate a srcSet string for responsive images.\n *\n * Each width points to the `/_vinext/image` optimization endpoint so the\n * server can resize and transcode the image. Only includes widths that are\n * <= 2x the original image width to avoid pointless upscaling.\n */\nfunction generateSrcSet(src: string, originalWidth: number, quality: number = 75): string {\n const widths = RESPONSIVE_WIDTHS.filter((w) => w <= originalWidth * 2);\n if (widths.length === 0)\n return `${imageOptimizationUrl(src, originalWidth, quality)} ${originalWidth}w`;\n return widths.map((w) => `${imageOptimizationUrl(src, w, quality)} ${w}w`).join(\", \");\n}\n\nconst Image = forwardRef<HTMLImageElement, ImageProps>(function Image(\n {\n src: srcProp,\n alt,\n width,\n height,\n fill,\n priority,\n quality,\n placeholder,\n blurDataURL,\n loader,\n sizes,\n className,\n style,\n onLoad,\n onLoadingComplete,\n unoptimized: _unoptimized,\n overrideSrc: _overrideSrc,\n loading,\n ...rest\n },\n ref,\n) {\n // Wire onLoadingComplete (deprecated) into onLoad — matches Next.js behavior.\n // onLoad fires first, then onLoadingComplete receives the HTMLImageElement.\n const handleLoad = onLoadingComplete\n ? (e: React.SyntheticEvent<HTMLImageElement>) => {\n onLoad?.(e);\n onLoadingComplete(e.currentTarget);\n }\n : onLoad;\n\n // Handle StaticImageData (import result)\n const src = typeof srcProp === \"string\" ? srcProp : srcProp.src;\n const imgWidth = width ?? (typeof srcProp === \"object\" ? srcProp.width : undefined);\n const imgHeight = height ?? (typeof srcProp === \"object\" ? srcProp.height : undefined);\n const imgBlurDataURL =\n blurDataURL ?? (typeof srcProp === \"object\" ? srcProp.blurDataURL : undefined);\n\n // If a custom loader is provided, use basic img with loader URL\n if (loader) {\n const resolvedSrc = loader({ src, width: imgWidth ?? 0, quality: quality ?? 75 });\n return (\n <img\n ref={ref}\n src={resolvedSrc}\n alt={alt}\n width={fill ? undefined : imgWidth}\n height={fill ? undefined : imgHeight}\n loading={priority ? \"eager\" : (loading ?? \"lazy\")}\n decoding=\"async\"\n sizes={sizes}\n className={className}\n onLoad={handleLoad}\n style={\n fill\n ? {\n position: \"absolute\",\n inset: 0,\n width: \"100%\",\n height: \"100%\",\n objectFit: \"cover\",\n ...style,\n }\n : style\n }\n {...rest}\n />\n );\n }\n\n // For remote URLs, validate against remotePatterns then use @unpic/react\n if (isRemoteUrl(src)) {\n const validation = validateRemoteUrl(src);\n if (!validation.allowed) {\n if (__isDev) {\n console.warn(`[next/image] ${validation.reason}`);\n // In dev, render the image but with a warning — matches Next.js dev behavior\n } else {\n // In production, block the image entirely\n console.error(`[next/image] ${validation.reason}`);\n return null;\n }\n }\n\n const sanitizedBlur = imgBlurDataURL ? sanitizeBlurDataURL(imgBlurDataURL) : undefined;\n const bg = placeholder === \"blur\" && sanitizedBlur ? `url(${sanitizedBlur})` : undefined;\n\n if (fill) {\n return (\n <UnpicImage\n src={src}\n alt={alt}\n layout=\"fullWidth\"\n priority={priority}\n sizes={sizes}\n className={className}\n background={bg}\n onLoad={handleLoad}\n />\n );\n }\n // constrained layout requires width+height or aspectRatio\n if (imgWidth && imgHeight) {\n return (\n <UnpicImage\n src={src}\n alt={alt}\n width={imgWidth}\n height={imgHeight}\n layout=\"constrained\"\n priority={priority}\n sizes={sizes}\n className={className}\n background={bg}\n onLoad={handleLoad}\n />\n );\n }\n // Fall through to basic <img> if dimensions not provided\n // (unpic requires them for constrained layout)\n }\n\n // Route local images through the /_vinext/image optimization endpoint.\n // In production on Cloudflare Workers, this resizes and transcodes via\n // the Images binding. In dev, it serves the original file as a passthrough.\n // When `unoptimized` is true, bypass the endpoint entirely (Next.js compat).\n // SVG sources auto-skip unless dangerouslyAllowSVG is enabled, matching\n // Next.js behavior where .svg triggers unoptimized=true by default.\n const imgQuality = quality ?? 75;\n const isSvg = src.endsWith(\".svg\");\n const skipOptimization = _unoptimized === true || (isSvg && !__dangerouslyAllowSVG);\n\n // Build srcSet for responsive local images (common breakpoints).\n // Each entry points to /_vinext/image with the appropriate width.\n const srcSet =\n imgWidth && !fill && !skipOptimization\n ? generateSrcSet(src, imgWidth, imgQuality)\n : imgWidth && !fill\n ? RESPONSIVE_WIDTHS.filter((w) => w <= imgWidth * 2)\n .map((w) => `${src} ${w}w`)\n .join(\", \") || `${src} ${imgWidth}w`\n : undefined;\n\n // The main `src` also goes through the optimization endpoint. Use the\n // declared width (or the first responsive width as fallback).\n const optimizedSrc = skipOptimization\n ? src\n : imgWidth\n ? imageOptimizationUrl(src, imgWidth, imgQuality)\n : imageOptimizationUrl(src, RESPONSIVE_WIDTHS[0], imgQuality);\n\n // Blur placeholder: show a low-quality background while the image loads.\n // Sanitize blurDataURL to prevent CSS injection via crafted data URLs.\n const sanitizedLocalBlur = imgBlurDataURL ? sanitizeBlurDataURL(imgBlurDataURL) : undefined;\n const blurStyle =\n placeholder === \"blur\" && sanitizedLocalBlur\n ? {\n backgroundImage: `url(${sanitizedLocalBlur})`,\n backgroundSize: \"cover\",\n backgroundRepeat: \"no-repeat\",\n backgroundPosition: \"center\",\n }\n : undefined;\n\n // For local images, render a standard <img> tag with srcSet and blur support.\n // The src and srcSet point to the /_vinext/image optimization endpoint.\n return (\n <img\n ref={ref}\n src={optimizedSrc}\n alt={alt}\n width={fill ? undefined : imgWidth}\n height={fill ? undefined : imgHeight}\n loading={priority ? \"eager\" : (loading ?? \"lazy\")}\n fetchPriority={priority ? \"high\" : undefined}\n decoding=\"async\"\n srcSet={srcSet}\n sizes={sizes ?? (fill ? \"100vw\" : undefined)}\n className={className}\n data-nimg={fill ? \"fill\" : \"1\"}\n onLoad={handleLoad}\n style={\n fill\n ? {\n position: \"absolute\",\n inset: 0,\n width: \"100%\",\n height: \"100%\",\n objectFit: \"cover\",\n ...blurStyle,\n ...style,\n }\n : { ...blurStyle, ...style }\n }\n {...rest}\n />\n );\n});\n\n/**\n * getImageProps — for advanced use cases (picture elements, background images).\n * Returns the props that would be passed to the underlying <img> element.\n */\nexport function getImageProps(props: ImageProps): {\n props: React.ImgHTMLAttributes<HTMLImageElement>;\n} {\n const {\n src: srcProp,\n alt,\n width,\n height,\n fill,\n priority,\n quality: _quality,\n placeholder,\n blurDataURL: blurDataURLProp,\n loader,\n sizes,\n className,\n style,\n onLoad: _onLoad,\n onLoadingComplete: _onLoadingComplete,\n unoptimized: _unoptimized,\n overrideSrc: _overrideSrc,\n loading,\n ...rest\n } = props;\n\n const src = typeof srcProp === \"string\" ? srcProp : srcProp.src;\n const imgWidth = width ?? (typeof srcProp === \"object\" ? srcProp.width : undefined);\n const imgHeight = height ?? (typeof srcProp === \"object\" ? srcProp.height : undefined);\n const imgBlurDataURL =\n blurDataURLProp ?? (typeof srcProp === \"object\" ? srcProp.blurDataURL : undefined);\n\n // Validate remote URLs against configured patterns\n let blockedInProd = false;\n if (isRemoteUrl(src)) {\n const validation = validateRemoteUrl(src);\n if (!validation.allowed) {\n if (__isDev) {\n console.warn(`[next/image] ${validation.reason}`);\n } else {\n console.error(`[next/image] ${validation.reason}`);\n blockedInProd = true;\n }\n }\n }\n\n // Resolve src through custom loader if provided\n const imgQuality = _quality ?? 75;\n const resolvedSrc = blockedInProd\n ? \"\"\n : loader\n ? loader({ src, width: imgWidth ?? 0, quality: imgQuality })\n : src;\n\n // For local images (no loader, not remote), route through optimization endpoint.\n // When `unoptimized` is true, bypass the endpoint entirely (Next.js compat).\n // SVG sources auto-skip unless dangerouslyAllowSVG is enabled.\n const isSvg = resolvedSrc.endsWith(\".svg\");\n const skipOpt =\n _unoptimized === true ||\n (isSvg && !__dangerouslyAllowSVG) ||\n blockedInProd ||\n !!loader ||\n isRemoteUrl(resolvedSrc);\n const optimizedSrc = skipOpt\n ? resolvedSrc\n : imgWidth\n ? imageOptimizationUrl(resolvedSrc, imgWidth, imgQuality)\n : imageOptimizationUrl(resolvedSrc, RESPONSIVE_WIDTHS[0], imgQuality);\n\n // Build srcSet for local images — each width points to /_vinext/image\n const srcSet =\n imgWidth && !fill && !isRemoteUrl(resolvedSrc) && !loader && !skipOpt\n ? generateSrcSet(resolvedSrc, imgWidth, imgQuality)\n : undefined;\n\n // Blur placeholder styles — sanitize to prevent CSS injection\n const sanitizedBlurURL = imgBlurDataURL ? sanitizeBlurDataURL(imgBlurDataURL) : undefined;\n const blurStyle =\n placeholder === \"blur\" && sanitizedBlurURL\n ? {\n backgroundImage: `url(${sanitizedBlurURL})`,\n backgroundSize: \"cover\",\n backgroundRepeat: \"no-repeat\" as const,\n backgroundPosition: \"center\" as const,\n }\n : undefined;\n\n return {\n props: {\n src: optimizedSrc,\n alt,\n width: fill ? undefined : imgWidth,\n height: fill ? undefined : imgHeight,\n loading: priority ? \"eager\" : (loading ?? \"lazy\"),\n fetchPriority: priority ? (\"high\" as const) : undefined,\n decoding: \"async\" as const,\n srcSet,\n sizes: sizes ?? (fill ? \"100vw\" : undefined),\n className,\n \"data-nimg\": fill ? \"fill\" : \"1\",\n style: fill\n ? {\n position: \"absolute\" as const,\n inset: 0,\n width: \"100%\",\n height: \"100%\",\n objectFit: \"cover\" as const,\n ...blurStyle,\n ...style,\n }\n : { ...blurStyle, ...style },\n ...rest,\n } as React.ImgHTMLAttributes<HTMLImageElement>,\n };\n}\n\nexport default Image;\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"image.js","names":["UnpicImage"],"sources":["../../src/shims/image.tsx"],"sourcesContent":["/**\n * next/image shim\n *\n * Translates Next.js Image props to @unpic/react Image component.\n * @unpic/react auto-detects CDN from URL and uses native transforms.\n * For local images (relative paths), routes through `/_vinext/image`\n * for server-side optimization (resize, format negotiation, quality).\n *\n * Remote images are validated against `images.remotePatterns` and\n * `images.domains` from next.config.js. Unmatched URLs are blocked\n * in production and warn in development, matching Next.js behavior.\n */\nimport React, { forwardRef } from \"react\";\nimport { Image as UnpicImage } from \"@unpic/react\";\nimport { hasRemoteMatch, type RemotePattern } from \"./image-config.js\";\n\nexport interface StaticImageData {\n src: string;\n height: number;\n width: number;\n blurDataURL?: string;\n}\n\n/**\n * Image config injected at build time via Vite define.\n * Serialized as JSON — parsed once at module level.\n */\nconst __imageRemotePatterns: RemotePattern[] = (() => {\n try {\n return JSON.parse(process.env.__VINEXT_IMAGE_REMOTE_PATTERNS ?? \"[]\");\n } catch {\n return [];\n }\n})();\nconst __imageDomains: string[] = (() => {\n try {\n return JSON.parse(process.env.__VINEXT_IMAGE_DOMAINS ?? \"[]\");\n } catch {\n return [];\n }\n})();\nconst __hasImageConfig = __imageRemotePatterns.length > 0 || __imageDomains.length > 0;\nconst __isDev = process.env.NODE_ENV !== \"production\";\nconst __imageDeviceSizes: number[] = (() => {\n try {\n return JSON.parse(\n process.env.__VINEXT_IMAGE_DEVICE_SIZES ?? \"[640,750,828,1080,1200,1920,2048,3840]\",\n );\n } catch {\n return [640, 750, 828, 1080, 1200, 1920, 2048, 3840];\n }\n})();\n/**\n * Whether dangerouslyAllowSVG is enabled in next.config.js.\n * When false (default), .svg sources auto-skip the optimization endpoint\n * and are served directly, matching Next.js behavior.\n * When true, .svg sources are routed through the optimizer (served as-is\n * with security headers).\n */\nconst __dangerouslyAllowSVG = process.env.__VINEXT_IMAGE_DANGEROUSLY_ALLOW_SVG === \"true\";\n/**\n * Validate that a remote URL is allowed by the configured remote patterns.\n * Returns true if the URL is allowed, false otherwise.\n *\n * When no remotePatterns/domains are configured, all remote URLs are allowed\n * (backwards-compatible — user hasn't opted into restriction).\n *\n * When patterns ARE configured, only matching URLs are allowed.\n * In development, non-matching URLs produce a console warning.\n * In production, non-matching URLs are blocked (src replaced with empty string).\n */\nfunction validateRemoteUrl(src: string): { allowed: boolean; reason?: string } {\n if (!__hasImageConfig) {\n // No image config — allow everything (backwards-compatible)\n return { allowed: true };\n }\n\n let url: URL;\n try {\n url = new URL(src, \"http://n\");\n } catch {\n return { allowed: false, reason: `Invalid URL: ${src}` };\n }\n\n if (hasRemoteMatch(__imageDomains, __imageRemotePatterns, url)) {\n return { allowed: true };\n }\n\n return {\n allowed: false,\n reason: `Image URL \"${src}\" is not configured in images.remotePatterns or images.domains in next.config.js. See: https://nextjs.org/docs/messages/next-image-unconfigured-host`,\n };\n}\n\ninterface ImageProps {\n src: string | StaticImageData;\n alt: string;\n width?: number;\n height?: number;\n fill?: boolean;\n priority?: boolean;\n quality?: number;\n placeholder?: \"blur\" | \"empty\";\n blurDataURL?: string;\n loader?: (params: { src: string; width: number; quality?: number }) => string;\n sizes?: string;\n className?: string;\n style?: React.CSSProperties;\n onLoad?: React.ReactEventHandler<HTMLImageElement>;\n /** @deprecated Use onLoad instead. Still supported for migration compat. */\n onLoadingComplete?: (img: HTMLImageElement) => void;\n onError?: React.ReactEventHandler<HTMLImageElement>;\n onClick?: React.MouseEventHandler<HTMLImageElement>;\n id?: string;\n // Accept and ignore Next.js-specific props that don't apply\n unoptimized?: boolean;\n overrideSrc?: string;\n loading?: \"lazy\" | \"eager\";\n}\n\n/**\n * Sanitize a blurDataURL to prevent CSS injection.\n *\n * A crafted data URL containing `)` can break out of the `url()` CSS function,\n * allowing injection of arbitrary CSS properties or rules. Characters like `{`,\n * `}`, and `\\` can also assist in crafting injection payloads.\n *\n * This validates the URL starts with `data:image/` and rejects characters that\n * could escape the `url()` context. Semicolons are allowed since they're part\n * of valid data URLs (`data:image/png;base64,...`) and harmless inside `url()`.\n *\n * Returns undefined for invalid URLs, which causes the blur placeholder to be\n * skipped gracefully.\n */\nfunction sanitizeBlurDataURL(url: string): string | undefined {\n // Must be a data: image URL\n if (!url.startsWith(\"data:image/\")) return undefined;\n // Reject characters that can break out of CSS url():\n // ) - closes url()\n // ( - could open nested functions\n // { } - CSS rule boundaries\n // \\ - CSS escape sequences\n // newlines - break CSS parsing\n if (/[)(}{\\\\'\"\\n\\r]/.test(url)) return undefined;\n return url;\n}\n\n/**\n * Determine if a src is a remote URL (CDN-optimizable) or local.\n */\nfunction isRemoteUrl(src: string): boolean {\n return src.startsWith(\"http://\") || src.startsWith(\"https://\") || src.startsWith(\"//\");\n}\n\n/**\n * Responsive image widths matching Next.js's device sizes config.\n * These are the breakpoints used for srcSet generation.\n * Configurable via `images.deviceSizes` in next.config.js.\n */\nconst RESPONSIVE_WIDTHS = __imageDeviceSizes;\n\n/**\n * Build a `/_vinext/image` optimization URL.\n *\n * In production (Cloudflare Workers), the worker intercepts this path and uses\n * the Images binding to resize/transcode on the fly. In dev, the Vite dev\n * server handles it as a passthrough (serves the original file).\n */\nexport function imageOptimizationUrl(src: string, width: number, quality: number = 75): string {\n return `/_vinext/image?url=${encodeURIComponent(src)}&w=${width}&q=${quality}`;\n}\n\n/**\n * Generate a srcSet string for responsive images.\n *\n * Each width points to the `/_vinext/image` optimization endpoint so the\n * server can resize and transcode the image. Only includes widths that are\n * <= 2x the original image width to avoid pointless upscaling.\n */\nfunction generateSrcSet(src: string, originalWidth: number, quality: number = 75): string {\n const widths = RESPONSIVE_WIDTHS.filter((w) => w <= originalWidth * 2);\n if (widths.length === 0)\n return `${imageOptimizationUrl(src, originalWidth, quality)} ${originalWidth}w`;\n return widths.map((w) => `${imageOptimizationUrl(src, w, quality)} ${w}w`).join(\", \");\n}\n\nconst Image = forwardRef<HTMLImageElement, ImageProps>(function Image(\n {\n src: srcProp,\n alt,\n width,\n height,\n fill,\n priority,\n quality,\n placeholder,\n blurDataURL,\n loader,\n sizes,\n className,\n style,\n onLoad,\n onLoadingComplete,\n unoptimized: _unoptimized,\n overrideSrc: _overrideSrc,\n loading,\n ...rest\n },\n ref,\n) {\n // Wire onLoadingComplete (deprecated) into onLoad — matches Next.js behavior.\n // onLoad fires first, then onLoadingComplete receives the HTMLImageElement.\n const handleLoad = onLoadingComplete\n ? (e: React.SyntheticEvent<HTMLImageElement>) => {\n onLoad?.(e);\n onLoadingComplete(e.currentTarget);\n }\n : onLoad;\n\n // Handle StaticImageData (import result)\n const src = typeof srcProp === \"string\" ? srcProp : srcProp.src;\n const imgWidth = width ?? (typeof srcProp === \"object\" ? srcProp.width : undefined);\n const imgHeight = height ?? (typeof srcProp === \"object\" ? srcProp.height : undefined);\n const imgBlurDataURL =\n blurDataURL ?? (typeof srcProp === \"object\" ? srcProp.blurDataURL : undefined);\n\n // If a custom loader is provided, use basic img with loader URL\n if (loader) {\n const resolvedSrc = loader({ src, width: imgWidth ?? 0, quality: quality ?? 75 });\n return (\n <img\n ref={ref}\n src={resolvedSrc}\n alt={alt}\n width={fill ? undefined : imgWidth}\n height={fill ? undefined : imgHeight}\n loading={priority ? \"eager\" : (loading ?? \"lazy\")}\n decoding=\"async\"\n sizes={sizes}\n className={className}\n onLoad={handleLoad}\n style={\n fill\n ? {\n position: \"absolute\",\n inset: 0,\n width: \"100%\",\n height: \"100%\",\n objectFit: \"cover\",\n ...style,\n }\n : style\n }\n {...rest}\n />\n );\n }\n\n // For remote URLs, validate against remotePatterns then use @unpic/react\n if (isRemoteUrl(src)) {\n const validation = validateRemoteUrl(src);\n if (!validation.allowed) {\n if (__isDev) {\n console.warn(`[next/image] ${validation.reason}`);\n // In dev, render the image but with a warning — matches Next.js dev behavior\n } else {\n // In production, block the image entirely\n console.error(`[next/image] ${validation.reason}`);\n return null;\n }\n }\n\n const sanitizedBlur = imgBlurDataURL ? sanitizeBlurDataURL(imgBlurDataURL) : undefined;\n const bg = placeholder === \"blur\" && sanitizedBlur ? `url(${sanitizedBlur})` : undefined;\n\n if (fill) {\n return (\n <UnpicImage\n src={src}\n alt={alt}\n layout=\"fullWidth\"\n // `priority` is a Next.js concept — translate it to HTML attributes so\n // it is never forwarded to the DOM as a non-boolean attribute, which\n // would trigger React's \"Received `true` for a non-boolean attribute\"\n // warning.\n loading={priority ? \"eager\" : (loading ?? \"lazy\")}\n fetchPriority={priority ? \"high\" : undefined}\n sizes={sizes}\n className={className}\n background={bg}\n onLoad={handleLoad}\n />\n );\n }\n // constrained layout requires width+height or aspectRatio\n if (imgWidth && imgHeight) {\n return (\n <UnpicImage\n src={src}\n alt={alt}\n width={imgWidth}\n height={imgHeight}\n layout=\"constrained\"\n // Same translation as above — never pass `priority` to the DOM.\n loading={priority ? \"eager\" : (loading ?? \"lazy\")}\n fetchPriority={priority ? \"high\" : undefined}\n sizes={sizes}\n className={className}\n background={bg}\n onLoad={handleLoad}\n />\n );\n }\n // Fall through to basic <img> if dimensions not provided\n // (unpic requires them for constrained layout)\n }\n\n // Route local images through the /_vinext/image optimization endpoint.\n // In production on Cloudflare Workers, this resizes and transcodes via\n // the Images binding. In dev, it serves the original file as a passthrough.\n // When `unoptimized` is true, bypass the endpoint entirely (Next.js compat).\n // SVG sources auto-skip unless dangerouslyAllowSVG is enabled, matching\n // Next.js behavior where .svg triggers unoptimized=true by default.\n const imgQuality = quality ?? 75;\n const isSvg = src.endsWith(\".svg\");\n const skipOptimization = _unoptimized === true || (isSvg && !__dangerouslyAllowSVG);\n\n // Build srcSet for responsive local images (common breakpoints).\n // Each entry points to /_vinext/image with the appropriate width.\n const srcSet =\n imgWidth && !fill && !skipOptimization\n ? generateSrcSet(src, imgWidth, imgQuality)\n : imgWidth && !fill\n ? RESPONSIVE_WIDTHS.filter((w) => w <= imgWidth * 2)\n .map((w) => `${src} ${w}w`)\n .join(\", \") || `${src} ${imgWidth}w`\n : undefined;\n\n // The main `src` also goes through the optimization endpoint. Use the\n // declared width (or the first responsive width as fallback).\n const optimizedSrc = skipOptimization\n ? src\n : imgWidth\n ? imageOptimizationUrl(src, imgWidth, imgQuality)\n : imageOptimizationUrl(src, RESPONSIVE_WIDTHS[0], imgQuality);\n\n // Blur placeholder: show a low-quality background while the image loads.\n // Sanitize blurDataURL to prevent CSS injection via crafted data URLs.\n const sanitizedLocalBlur = imgBlurDataURL ? sanitizeBlurDataURL(imgBlurDataURL) : undefined;\n const blurStyle =\n placeholder === \"blur\" && sanitizedLocalBlur\n ? {\n backgroundImage: `url(${sanitizedLocalBlur})`,\n backgroundSize: \"cover\",\n backgroundRepeat: \"no-repeat\",\n backgroundPosition: \"center\",\n }\n : undefined;\n\n // For local images, render a standard <img> tag with srcSet and blur support.\n // The src and srcSet point to the /_vinext/image optimization endpoint.\n return (\n <img\n ref={ref}\n src={optimizedSrc}\n alt={alt}\n width={fill ? undefined : imgWidth}\n height={fill ? undefined : imgHeight}\n loading={priority ? \"eager\" : (loading ?? \"lazy\")}\n fetchPriority={priority ? \"high\" : undefined}\n decoding=\"async\"\n srcSet={srcSet}\n sizes={sizes ?? (fill ? \"100vw\" : undefined)}\n className={className}\n data-nimg={fill ? \"fill\" : \"1\"}\n onLoad={handleLoad}\n style={\n fill\n ? {\n position: \"absolute\",\n inset: 0,\n width: \"100%\",\n height: \"100%\",\n objectFit: \"cover\",\n ...blurStyle,\n ...style,\n }\n : { ...blurStyle, ...style }\n }\n {...rest}\n />\n );\n});\n\n/**\n * getImageProps — for advanced use cases (picture elements, background images).\n * Returns the props that would be passed to the underlying <img> element.\n */\nexport function getImageProps(props: ImageProps): {\n props: React.ImgHTMLAttributes<HTMLImageElement>;\n} {\n const {\n src: srcProp,\n alt,\n width,\n height,\n fill,\n priority,\n quality: _quality,\n placeholder,\n blurDataURL: blurDataURLProp,\n loader,\n sizes,\n className,\n style,\n onLoad: _onLoad,\n onLoadingComplete: _onLoadingComplete,\n unoptimized: _unoptimized,\n overrideSrc: _overrideSrc,\n loading,\n ...rest\n } = props;\n\n const src = typeof srcProp === \"string\" ? srcProp : srcProp.src;\n const imgWidth = width ?? (typeof srcProp === \"object\" ? srcProp.width : undefined);\n const imgHeight = height ?? (typeof srcProp === \"object\" ? srcProp.height : undefined);\n const imgBlurDataURL =\n blurDataURLProp ?? (typeof srcProp === \"object\" ? srcProp.blurDataURL : undefined);\n\n // Validate remote URLs against configured patterns\n let blockedInProd = false;\n if (isRemoteUrl(src)) {\n const validation = validateRemoteUrl(src);\n if (!validation.allowed) {\n if (__isDev) {\n console.warn(`[next/image] ${validation.reason}`);\n } else {\n console.error(`[next/image] ${validation.reason}`);\n blockedInProd = true;\n }\n }\n }\n\n // Resolve src through custom loader if provided\n const imgQuality = _quality ?? 75;\n const resolvedSrc = blockedInProd\n ? \"\"\n : loader\n ? loader({ src, width: imgWidth ?? 0, quality: imgQuality })\n : src;\n\n // For local images (no loader, not remote), route through optimization endpoint.\n // When `unoptimized` is true, bypass the endpoint entirely (Next.js compat).\n // SVG sources auto-skip unless dangerouslyAllowSVG is enabled.\n const isSvg = resolvedSrc.endsWith(\".svg\");\n const skipOpt =\n _unoptimized === true ||\n (isSvg && !__dangerouslyAllowSVG) ||\n blockedInProd ||\n !!loader ||\n isRemoteUrl(resolvedSrc);\n const optimizedSrc = skipOpt\n ? resolvedSrc\n : imgWidth\n ? imageOptimizationUrl(resolvedSrc, imgWidth, imgQuality)\n : imageOptimizationUrl(resolvedSrc, RESPONSIVE_WIDTHS[0], imgQuality);\n\n // Build srcSet for local images — each width points to /_vinext/image\n const srcSet =\n imgWidth && !fill && !isRemoteUrl(resolvedSrc) && !loader && !skipOpt\n ? generateSrcSet(resolvedSrc, imgWidth, imgQuality)\n : undefined;\n\n // Blur placeholder styles — sanitize to prevent CSS injection\n const sanitizedBlurURL = imgBlurDataURL ? sanitizeBlurDataURL(imgBlurDataURL) : undefined;\n const blurStyle =\n placeholder === \"blur\" && sanitizedBlurURL\n ? {\n backgroundImage: `url(${sanitizedBlurURL})`,\n backgroundSize: \"cover\",\n backgroundRepeat: \"no-repeat\" as const,\n backgroundPosition: \"center\" as const,\n }\n : undefined;\n\n return {\n props: {\n src: optimizedSrc,\n alt,\n width: fill ? undefined : imgWidth,\n height: fill ? undefined : imgHeight,\n loading: priority ? \"eager\" : (loading ?? \"lazy\"),\n fetchPriority: priority ? (\"high\" as const) : undefined,\n decoding: \"async\" as const,\n srcSet,\n sizes: sizes ?? (fill ? \"100vw\" : undefined),\n className,\n \"data-nimg\": fill ? \"fill\" : \"1\",\n style: fill\n ? {\n position: \"absolute\" as const,\n inset: 0,\n width: \"100%\",\n height: \"100%\",\n objectFit: \"cover\" as const,\n ...blurStyle,\n ...style,\n }\n : { ...blurStyle, ...style },\n ...rest,\n } as React.ImgHTMLAttributes<HTMLImageElement>,\n };\n}\n\nexport default Image;\n"],"mappings":"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"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { getRequestContext, isInsideUnifiedScope, runWithUnifiedStateMutation } from "./unified-request-context.js";
|
|
2
|
-
import { _registerStateAccessors } from "./navigation.js";
|
|
2
|
+
import { GLOBAL_ACCESSORS_KEY, _registerStateAccessors } from "./navigation.js";
|
|
3
3
|
import { AsyncLocalStorage } from "node:async_hooks";
|
|
4
4
|
//#region src/shims/navigation-state.ts
|
|
5
5
|
/**
|
|
@@ -59,7 +59,7 @@ function runWithServerInsertedHTMLState(fn) {
|
|
|
59
59
|
};
|
|
60
60
|
return _als.run(state, fn);
|
|
61
61
|
}
|
|
62
|
-
|
|
62
|
+
const _accessors = {
|
|
63
63
|
getServerContext() {
|
|
64
64
|
return _getState().serverContext;
|
|
65
65
|
},
|
|
@@ -72,7 +72,9 @@ _registerStateAccessors({
|
|
|
72
72
|
clearInsertedHTMLCallbacks() {
|
|
73
73
|
_getState().serverInsertedHTMLCallbacks = [];
|
|
74
74
|
}
|
|
75
|
-
}
|
|
75
|
+
};
|
|
76
|
+
_registerStateAccessors(_accessors);
|
|
77
|
+
globalThis[GLOBAL_ACCESSORS_KEY] = _accessors;
|
|
76
78
|
//#endregion
|
|
77
79
|
export { runWithNavigationContext, runWithServerInsertedHTMLState };
|
|
78
80
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"navigation-state.js","names":[],"sources":["../../src/shims/navigation-state.ts"],"sourcesContent":["/**\n * Server-only navigation state backed by AsyncLocalStorage.\n *\n * This module provides request-scoped isolation for navigation context\n * and useServerInsertedHTML callbacks. Without ALS, concurrent requests\n * on Cloudflare Workers would share module-level state and leak data\n * (pathnames, params, CSS-in-JS styles) between requests.\n *\n * This module is server-only — it imports node:async_hooks and must NOT\n * be bundled for the browser. The dual-environment navigation.ts shim\n * uses a registration pattern so it works in both environments.\n */\n\nimport { AsyncLocalStorage } from \"node:async_hooks\";\nimport { _registerStateAccessors, type NavigationContext } from \"./navigation.js\";\nimport {\n isInsideUnifiedScope,\n getRequestContext,\n runWithUnifiedStateMutation,\n} from \"./unified-request-context.js\";\n\n// ---------------------------------------------------------------------------\n// ALS setup — same pattern as headers.ts\n// ---------------------------------------------------------------------------\n\nexport interface NavigationState {\n serverContext: NavigationContext | null;\n serverInsertedHTMLCallbacks: Array<() => unknown>;\n}\n\nconst _ALS_KEY = Symbol.for(\"vinext.navigation.als\");\nconst _FALLBACK_KEY = Symbol.for(\"vinext.navigation.fallback\");\nconst _g = globalThis as unknown as Record<PropertyKey, unknown>;\nconst _als = (_g[_ALS_KEY] ??=\n new AsyncLocalStorage<NavigationState>()) as AsyncLocalStorage<NavigationState>;\n\nconst _fallbackState = (_g[_FALLBACK_KEY] ??= {\n serverContext: null,\n serverInsertedHTMLCallbacks: [],\n} satisfies NavigationState) as NavigationState;\n\nfunction _getState(): NavigationState {\n if (isInsideUnifiedScope()) {\n return getRequestContext();\n }\n return _als.getStore() ?? _fallbackState;\n}\n\n/**\n * Run a function within a navigation ALS scope.\n * Ensures per-request isolation for navigation context and\n * useServerInsertedHTML callbacks on concurrent runtimes.\n */\nexport function runWithNavigationContext<T>(fn: () => T | Promise<T>): T | Promise<T> {\n if (isInsideUnifiedScope()) {\n return runWithUnifiedStateMutation((uCtx) => {\n uCtx.serverContext = null;\n uCtx.serverInsertedHTMLCallbacks = [];\n }, fn);\n }\n const state: NavigationState = {\n serverContext: null,\n serverInsertedHTMLCallbacks: [],\n };\n return _als.run(state, fn);\n}\n\n/**\n * Run a function with a fresh useServerInsertedHTML callback list while\n * preserving the current navigation context.\n *\n * Used by the Pages Router ISR cache-fill pass: it is the same request/path,\n * but it needs a fresh callback collection so CSS-in-JS insertions from the\n * streamed render cannot accumulate into the cache-fill render.\n */\nexport function runWithServerInsertedHTMLState<T>(fn: () => T | Promise<T>): T | Promise<T> {\n if (isInsideUnifiedScope()) {\n return runWithUnifiedStateMutation((uCtx) => {\n uCtx.serverInsertedHTMLCallbacks = [];\n }, fn);\n }\n\n const parentState = _als.getStore() ?? _fallbackState;\n const state: NavigationState = {\n serverContext: parentState.serverContext,\n serverInsertedHTMLCallbacks: [],\n };\n return _als.run(state, fn);\n}\n\n// ---------------------------------------------------------------------------\n// Register ALS-backed accessors into navigation.ts\n// ---------------------------------------------------------------------------\n\
|
|
1
|
+
{"version":3,"file":"navigation-state.js","names":[],"sources":["../../src/shims/navigation-state.ts"],"sourcesContent":["/**\n * Server-only navigation state backed by AsyncLocalStorage.\n *\n * This module provides request-scoped isolation for navigation context\n * and useServerInsertedHTML callbacks. Without ALS, concurrent requests\n * on Cloudflare Workers would share module-level state and leak data\n * (pathnames, params, CSS-in-JS styles) between requests.\n *\n * This module is server-only — it imports node:async_hooks and must NOT\n * be bundled for the browser. The dual-environment navigation.ts shim\n * uses a registration pattern so it works in both environments.\n */\n\nimport { AsyncLocalStorage } from \"node:async_hooks\";\nimport { _registerStateAccessors, type NavigationContext } from \"./navigation.js\";\nimport {\n isInsideUnifiedScope,\n getRequestContext,\n runWithUnifiedStateMutation,\n} from \"./unified-request-context.js\";\n\n// ---------------------------------------------------------------------------\n// ALS setup — same pattern as headers.ts\n// ---------------------------------------------------------------------------\n\nexport interface NavigationState {\n serverContext: NavigationContext | null;\n serverInsertedHTMLCallbacks: Array<() => unknown>;\n}\n\nconst _ALS_KEY = Symbol.for(\"vinext.navigation.als\");\nconst _FALLBACK_KEY = Symbol.for(\"vinext.navigation.fallback\");\nconst _g = globalThis as unknown as Record<PropertyKey, unknown>;\nconst _als = (_g[_ALS_KEY] ??=\n new AsyncLocalStorage<NavigationState>()) as AsyncLocalStorage<NavigationState>;\n\nconst _fallbackState = (_g[_FALLBACK_KEY] ??= {\n serverContext: null,\n serverInsertedHTMLCallbacks: [],\n} satisfies NavigationState) as NavigationState;\n\nfunction _getState(): NavigationState {\n if (isInsideUnifiedScope()) {\n return getRequestContext();\n }\n return _als.getStore() ?? _fallbackState;\n}\n\n/**\n * Run a function within a navigation ALS scope.\n * Ensures per-request isolation for navigation context and\n * useServerInsertedHTML callbacks on concurrent runtimes.\n */\nexport function runWithNavigationContext<T>(fn: () => T | Promise<T>): T | Promise<T> {\n if (isInsideUnifiedScope()) {\n return runWithUnifiedStateMutation((uCtx) => {\n uCtx.serverContext = null;\n uCtx.serverInsertedHTMLCallbacks = [];\n }, fn);\n }\n const state: NavigationState = {\n serverContext: null,\n serverInsertedHTMLCallbacks: [],\n };\n return _als.run(state, fn);\n}\n\n/**\n * Run a function with a fresh useServerInsertedHTML callback list while\n * preserving the current navigation context.\n *\n * Used by the Pages Router ISR cache-fill pass: it is the same request/path,\n * but it needs a fresh callback collection so CSS-in-JS insertions from the\n * streamed render cannot accumulate into the cache-fill render.\n */\nexport function runWithServerInsertedHTMLState<T>(fn: () => T | Promise<T>): T | Promise<T> {\n if (isInsideUnifiedScope()) {\n return runWithUnifiedStateMutation((uCtx) => {\n uCtx.serverInsertedHTMLCallbacks = [];\n }, fn);\n }\n\n const parentState = _als.getStore() ?? _fallbackState;\n const state: NavigationState = {\n serverContext: parentState.serverContext,\n serverInsertedHTMLCallbacks: [],\n };\n return _als.run(state, fn);\n}\n\n// ---------------------------------------------------------------------------\n// Register ALS-backed accessors into navigation.ts\n//\n// Two registration paths (issue #688):\n// 1. _registerStateAccessors — updates the module-level function pointers\n// in the same module instance that imported us (the SSR entry's copy).\n// 2. globalThis[Symbol.for(...)] — makes the accessors discoverable by ANY\n// module instance of navigation.ts, even if Vite created a separate one\n// for \"use client\" components due to pre-bundling or env separation.\n// ---------------------------------------------------------------------------\n\nimport { GLOBAL_ACCESSORS_KEY } from \"./navigation\";\n\nconst _accessors = {\n getServerContext(): NavigationContext | null {\n return _getState().serverContext;\n },\n\n setServerContext(ctx: NavigationContext | null): void {\n _getState().serverContext = ctx;\n },\n\n getInsertedHTMLCallbacks(): Array<() => unknown> {\n return _getState().serverInsertedHTMLCallbacks;\n },\n\n clearInsertedHTMLCallbacks(): void {\n _getState().serverInsertedHTMLCallbacks = [];\n },\n} satisfies Parameters<typeof _registerStateAccessors>[0];\n\n_registerStateAccessors(_accessors);\n(globalThis as unknown as Record<PropertyKey, unknown>)[GLOBAL_ACCESSORS_KEY] = _accessors;\n"],"mappings":";;;;;;;;;;;;;;;;AA8BA,MAAM,WAAW,OAAO,IAAI,wBAAwB;AACpD,MAAM,gBAAgB,OAAO,IAAI,6BAA6B;AAC9D,MAAM,KAAK;AACX,MAAM,OAAQ,GAAG,cACf,IAAI,mBAAoC;AAE1C,MAAM,iBAAkB,GAAG,mBAAmB;CAC5C,eAAe;CACf,6BAA6B,EAAE;CAChC;AAED,SAAS,YAA6B;AACpC,KAAI,sBAAsB,CACxB,QAAO,mBAAmB;AAE5B,QAAO,KAAK,UAAU,IAAI;;;;;;;AAQ5B,SAAgB,yBAA4B,IAA0C;AACpF,KAAI,sBAAsB,CACxB,QAAO,6BAA6B,SAAS;AAC3C,OAAK,gBAAgB;AACrB,OAAK,8BAA8B,EAAE;IACpC,GAAG;AAMR,QAAO,KAAK,IAJmB;EAC7B,eAAe;EACf,6BAA6B,EAAE;EAChC,EACsB,GAAG;;;;;;;;;;AAW5B,SAAgB,+BAAkC,IAA0C;AAC1F,KAAI,sBAAsB,CACxB,QAAO,6BAA6B,SAAS;AAC3C,OAAK,8BAA8B,EAAE;IACpC,GAAG;CAIR,MAAM,QAAyB;EAC7B,gBAFkB,KAAK,UAAU,IAAI,gBAEV;EAC3B,6BAA6B,EAAE;EAChC;AACD,QAAO,KAAK,IAAI,OAAO,GAAG;;AAgB5B,MAAM,aAAa;CACjB,mBAA6C;AAC3C,SAAO,WAAW,CAAC;;CAGrB,iBAAiB,KAAqC;AACpD,aAAW,CAAC,gBAAgB;;CAG9B,2BAAiD;AAC/C,SAAO,WAAW,CAAC;;CAGrB,6BAAmC;AACjC,aAAW,CAAC,8BAA8B,EAAE;;CAE/C;AAED,wBAAwB,WAAW;AACnC,WAAwD,wBAAwB"}
|
|
@@ -13,16 +13,18 @@ interface NavigationContext {
|
|
|
13
13
|
searchParams: URLSearchParams;
|
|
14
14
|
params: Record<string, string | string[]>;
|
|
15
15
|
}
|
|
16
|
-
|
|
17
|
-
* Register ALS-backed state accessors. Called by navigation-state.ts on import.
|
|
18
|
-
* @internal
|
|
19
|
-
*/
|
|
20
|
-
declare function _registerStateAccessors(accessors: {
|
|
16
|
+
interface _StateAccessors {
|
|
21
17
|
getServerContext: () => NavigationContext | null;
|
|
22
18
|
setServerContext: (ctx: NavigationContext | null) => void;
|
|
23
19
|
getInsertedHTMLCallbacks: () => Array<() => unknown>;
|
|
24
20
|
clearInsertedHTMLCallbacks: () => void;
|
|
25
|
-
}
|
|
21
|
+
}
|
|
22
|
+
declare const GLOBAL_ACCESSORS_KEY: unique symbol;
|
|
23
|
+
/**
|
|
24
|
+
* Register ALS-backed state accessors. Called by navigation-state.ts on import.
|
|
25
|
+
* @internal
|
|
26
|
+
*/
|
|
27
|
+
declare function _registerStateAccessors(accessors: _StateAccessors): void;
|
|
26
28
|
/**
|
|
27
29
|
* Get the navigation context for the current SSR/RSC render.
|
|
28
30
|
* Reads from AsyncLocalStorage when available (concurrent-safe),
|
|
@@ -195,5 +197,5 @@ declare function forbidden(): never;
|
|
|
195
197
|
*/
|
|
196
198
|
declare function unauthorized(): never;
|
|
197
199
|
//#endregion
|
|
198
|
-
export { HTTP_ERROR_FALLBACK_ERROR_CODE, MAX_PREFETCH_CACHE_SIZE, NavigationContext, PREFETCH_CACHE_TTL, PrefetchCacheEntry, ReadonlyURLSearchParams, RedirectType, ServerInsertedHTMLContext, _registerStateAccessors, clearServerInsertedHTML, flushServerInsertedHTML, forbidden, getAccessFallbackHTTPStatus, getClientParams, getLayoutSegmentContext, getNavigationContext, getPrefetchCache, getPrefetchedUrls, isHTTPAccessFallbackError, notFound, permanentRedirect, redirect, setClientParams, setNavigationContext, storePrefetchResponse, toRscUrl, unauthorized, useParams, usePathname, useRouter, useSearchParams, useSelectedLayoutSegment, useSelectedLayoutSegments, useServerInsertedHTML };
|
|
200
|
+
export { GLOBAL_ACCESSORS_KEY, HTTP_ERROR_FALLBACK_ERROR_CODE, MAX_PREFETCH_CACHE_SIZE, NavigationContext, PREFETCH_CACHE_TTL, PrefetchCacheEntry, ReadonlyURLSearchParams, RedirectType, ServerInsertedHTMLContext, _registerStateAccessors, clearServerInsertedHTML, flushServerInsertedHTML, forbidden, getAccessFallbackHTTPStatus, getClientParams, getLayoutSegmentContext, getNavigationContext, getPrefetchCache, getPrefetchedUrls, isHTTPAccessFallbackError, notFound, permanentRedirect, redirect, setClientParams, setNavigationContext, storePrefetchResponse, toRscUrl, unauthorized, useParams, usePathname, useRouter, useSearchParams, useSelectedLayoutSegment, useSelectedLayoutSegments, useServerInsertedHTML };
|
|
199
201
|
//# sourceMappingURL=navigation.d.ts.map
|
package/dist/shims/navigation.js
CHANGED
|
@@ -44,15 +44,30 @@ function useChildSegments() {
|
|
|
44
44
|
}
|
|
45
45
|
const _READONLY_SEARCH_PARAMS = Symbol("vinext.navigation.readonlySearchParams");
|
|
46
46
|
const _READONLY_SEARCH_PARAMS_SOURCE = Symbol("vinext.navigation.readonlySearchParamsSource");
|
|
47
|
+
const GLOBAL_ACCESSORS_KEY = Symbol.for("vinext.navigation.globalAccessors");
|
|
48
|
+
const _GLOBAL_ACCESSORS_KEY = GLOBAL_ACCESSORS_KEY;
|
|
49
|
+
function _getGlobalAccessors() {
|
|
50
|
+
return globalThis[_GLOBAL_ACCESSORS_KEY];
|
|
51
|
+
}
|
|
47
52
|
let _serverContext = null;
|
|
48
53
|
let _serverInsertedHTMLCallbacks = [];
|
|
49
|
-
let _getServerContext = () =>
|
|
54
|
+
let _getServerContext = () => {
|
|
55
|
+
const g = _getGlobalAccessors();
|
|
56
|
+
return g ? g.getServerContext() : _serverContext;
|
|
57
|
+
};
|
|
50
58
|
let _setServerContext = (ctx) => {
|
|
51
|
-
|
|
59
|
+
const g = _getGlobalAccessors();
|
|
60
|
+
if (g) g.setServerContext(ctx);
|
|
61
|
+
else _serverContext = ctx;
|
|
62
|
+
};
|
|
63
|
+
let _getInsertedHTMLCallbacks = () => {
|
|
64
|
+
const g = _getGlobalAccessors();
|
|
65
|
+
return g ? g.getInsertedHTMLCallbacks() : _serverInsertedHTMLCallbacks;
|
|
52
66
|
};
|
|
53
|
-
let _getInsertedHTMLCallbacks = () => _serverInsertedHTMLCallbacks;
|
|
54
67
|
let _clearInsertedHTMLCallbacks = () => {
|
|
55
|
-
|
|
68
|
+
const g = _getGlobalAccessors();
|
|
69
|
+
if (g) g.clearInsertedHTMLCallbacks();
|
|
70
|
+
else _serverInsertedHTMLCallbacks = [];
|
|
56
71
|
};
|
|
57
72
|
/**
|
|
58
73
|
* Register ALS-backed state accessors. Called by navigation-state.ts on import.
|
|
@@ -560,6 +575,6 @@ if (!isServer) {
|
|
|
560
575
|
};
|
|
561
576
|
}
|
|
562
577
|
//#endregion
|
|
563
|
-
export { HTTP_ERROR_FALLBACK_ERROR_CODE, MAX_PREFETCH_CACHE_SIZE, PREFETCH_CACHE_TTL, ReadonlyURLSearchParams, RedirectType, ServerInsertedHTMLContext, _registerStateAccessors, clearServerInsertedHTML, flushServerInsertedHTML, forbidden, getAccessFallbackHTTPStatus, getClientParams, getLayoutSegmentContext, getNavigationContext, getPrefetchCache, getPrefetchedUrls, isHTTPAccessFallbackError, notFound, permanentRedirect, redirect, setClientParams, setNavigationContext, storePrefetchResponse, toRscUrl, unauthorized, useParams, usePathname, useRouter, useSearchParams, useSelectedLayoutSegment, useSelectedLayoutSegments, useServerInsertedHTML };
|
|
578
|
+
export { GLOBAL_ACCESSORS_KEY, HTTP_ERROR_FALLBACK_ERROR_CODE, MAX_PREFETCH_CACHE_SIZE, PREFETCH_CACHE_TTL, ReadonlyURLSearchParams, RedirectType, ServerInsertedHTMLContext, _registerStateAccessors, clearServerInsertedHTML, flushServerInsertedHTML, forbidden, getAccessFallbackHTTPStatus, getClientParams, getLayoutSegmentContext, getNavigationContext, getPrefetchCache, getPrefetchedUrls, isHTTPAccessFallbackError, notFound, permanentRedirect, redirect, setClientParams, setNavigationContext, storePrefetchResponse, toRscUrl, unauthorized, useParams, usePathname, useRouter, useSearchParams, useSelectedLayoutSegment, useSelectedLayoutSegments, useServerInsertedHTML };
|
|
564
579
|
|
|
565
580
|
//# sourceMappingURL=navigation.js.map
|