@rsc-kit/core 0.20.7 → 0.20.10
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/cache.d.ts +11 -0
- package/dist/cache.js +13 -0
- package/dist/cache.js.map +1 -1
- package/dist/clientEntries.js +4 -1
- package/dist/clientEntries.js.map +1 -1
- package/dist/earlyHints.d.ts +14 -2
- package/dist/earlyHints.js +15 -8
- package/dist/earlyHints.js.map +1 -1
- package/dist/files.d.ts +38 -0
- package/dist/files.js +109 -0
- package/dist/files.js.map +1 -1
- package/dist/host.d.ts +11 -2
- package/dist/host.js +38 -8
- package/dist/host.js.map +1 -1
- package/dist/js/ActivityRouter.d.ts +11 -0
- package/dist/js/ActivityRouter.js +19 -4
- package/dist/js/ActivityRouter.js.map +1 -1
- package/dist/js/Form.js +24 -2
- package/dist/js/Form.js.map +1 -1
- package/dist/js/PathnameProvider.d.ts +15 -2
- package/dist/js/PathnameProvider.js +17 -2
- package/dist/js/PathnameProvider.js.map +1 -1
- package/dist/js/SegmentBoundary.js +19 -3
- package/dist/js/SegmentBoundary.js.map +1 -1
- package/dist/js/activityMarkers.d.ts +7 -0
- package/dist/js/activityMarkers.js +18 -0
- package/dist/js/activityMarkers.js.map +1 -0
- package/dist/js/createViteRscApp.js +122 -76
- package/dist/js/createViteRscApp.js.map +1 -1
- package/dist/js/earlyClicks.d.ts +3 -1
- package/dist/js/earlyClicks.js +28 -5
- package/dist/js/earlyClicks.js.map +1 -1
- package/dist/js/errors.d.ts +2 -0
- package/dist/js/errors.js +11 -0
- package/dist/js/errors.js.map +1 -1
- package/dist/js/imagePreload.d.ts +37 -0
- package/dist/js/imagePreload.js +116 -0
- package/dist/js/imagePreload.js.map +1 -0
- package/dist/js/navigate.d.ts +33 -0
- package/dist/js/navigate.js +200 -12
- package/dist/js/navigate.js.map +1 -1
- package/dist/js/segmentStore.d.ts +58 -0
- package/dist/js/segmentStore.js +134 -4
- package/dist/js/segmentStore.js.map +1 -1
- package/dist/js/staleAssets.d.ts +10 -0
- package/dist/js/staleAssets.js +23 -2
- package/dist/js/staleAssets.js.map +1 -1
- package/dist/js/viewportPrefetch.js +27 -2
- package/dist/js/viewportPrefetch.js.map +1 -1
- package/dist/shellHead.d.ts +22 -0
- package/dist/shellHead.js +43 -0
- package/dist/shellHead.js.map +1 -0
- package/dist/vite.js +237 -55
- package/dist/vite.js.map +1 -1
- package/package.json +1 -1
package/dist/cache.d.ts
CHANGED
|
@@ -22,3 +22,14 @@ export declare function isCaching(): boolean;
|
|
|
22
22
|
* wraps and an engine that also wraps do not end up with two.
|
|
23
23
|
*/
|
|
24
24
|
export declare function withCache<T>(run: () => Promise<T>): Promise<T>;
|
|
25
|
+
/**
|
|
26
|
+
* Forget every answer this request has cached so far.
|
|
27
|
+
*
|
|
28
|
+
* For the render that follows a mutation. An action's request memoises what
|
|
29
|
+
* the middleware and the action read before the row was changed, and a
|
|
30
|
+
* revalidation rendered in the same request would read those answers back:
|
|
31
|
+
* a port renamed its agent and the sidebar the action re-rendered still
|
|
32
|
+
* said the old name, while the row was updated. What was true before the
|
|
33
|
+
* write is not a memo of what is true after it.
|
|
34
|
+
*/
|
|
35
|
+
export declare function forgetCached(): void;
|
package/dist/cache.js
CHANGED
|
@@ -109,4 +109,17 @@ export async function withCache(run) {
|
|
|
109
109
|
return await run();
|
|
110
110
|
return await scope().run(new Map(), run);
|
|
111
111
|
}
|
|
112
|
+
/**
|
|
113
|
+
* Forget every answer this request has cached so far.
|
|
114
|
+
*
|
|
115
|
+
* For the render that follows a mutation. An action's request memoises what
|
|
116
|
+
* the middleware and the action read before the row was changed, and a
|
|
117
|
+
* revalidation rendered in the same request would read those answers back:
|
|
118
|
+
* a port renamed its agent and the sidebar the action re-rendered still
|
|
119
|
+
* said the old name, while the row was updated. What was true before the
|
|
120
|
+
* write is not a memo of what is true after it.
|
|
121
|
+
*/
|
|
122
|
+
export function forgetCached() {
|
|
123
|
+
scope()?.getStore()?.clear();
|
|
124
|
+
}
|
|
112
125
|
//# sourceMappingURL=cache.js.map
|
package/dist/cache.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cache.js","sourceRoot":"","sources":["../src/cache.ts"],"names":[],"mappings":"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","sourcesContent":["// Asking the same question twice in one request.\n//\n// import { cache } from '@rsc-kit/core/cache'\n//\n// export const currentUser = cache(async () => db.user(await sessionId()))\n//\n// A guard checks who you are; the layout wants their name; the page wants their\n// permissions. Three calls, one request, one answer — without threading the\n// result through props that only exist to carry it.\n//\n// This is the same shape as React's cache(), and deliberately not React's: the\n// engine's own boundaries are not React's. A guard runs before any component\n// does, which is exactly where the duplication starts, and React has no scope\n// open there.\n//\n// Scoped to the request, so two in flight cannot see each other's answers. The\n// scope is opened by whoever owns the request — the host, the worker, or the\n// prerenderer — and everything below it shares one store.\n\nimport { noteAwaiter, withHelper } from \"./request.js\";\nimport { resolveScope } from \"./revalidate.js\";\n\n/** One request's memo table. */\ntype Store = Map<unknown, ArgNode>;\n\n/**\n * Arguments keyed the way React keys them: a tree, one level per argument.\n *\n * Primitives compare by value and objects by identity, which falls out of\n * using a Map for the first and a WeakMap for the second. Serialising the\n * arguments instead would make `f({ a: 1 })` and `f({ a: 1 })` the same call —\n * which they are not, if the object is mutable.\n */\ninterface ArgNode {\n /** The answer for the argument list that ends here. */\n result?: { value: unknown };\n byValue?: Map<unknown, ArgNode>;\n byIdentity?: WeakMap<object, ArgNode>;\n}\n\nconst SCOPE = Symbol.for(\"@rsc-kit/core.cache-scope\");\n\nconst globals = globalThis as Record<symbol | string, unknown>;\n\nlet ready: Promise<void> | null = null;\n\ninterface Scope {\n getStore(): Store | undefined;\n run<T>(store: Store, fn: () => T): T;\n}\n\nfunction scope(): Scope | null {\n return (globals[SCOPE] as Scope | undefined) ?? null;\n}\n\nfunction isObject(value: unknown): value is object {\n return (\n (typeof value === \"object\" && value !== null) || typeof value === \"function\"\n );\n}\n\n/** Walk to the node for this argument list, creating it as we go. */\nfunction nodeFor(root: ArgNode, args: unknown[]): ArgNode {\n let node = root;\n\n for (const arg of args) {\n if (isObject(arg)) {\n node.byIdentity ??= new WeakMap();\n\n let next = node.byIdentity.get(arg);\n\n if (!next) {\n next = {};\n node.byIdentity.set(arg, next);\n }\n\n node = next;\n } else {\n node.byValue ??= new Map();\n\n let next = node.byValue.get(arg);\n\n if (!next) {\n next = {};\n node.byValue.set(arg, next);\n }\n\n node = next;\n }\n }\n\n return node;\n}\n\n/**\n * Memoise a function for the length of one request.\n *\n * The *promise* is cached, not the value, so callers that arrive while the\n * first is still in flight wait on it rather than starting a second. A\n * rejection is cached too: within one request the answer to a question does\n * not change because you asked again.\n *\n * Outside a request this calls straight through. Nothing is stored, nothing\n * leaks between requests, and shared code does not have to know which side of\n * the boundary it is on — the same reason revalidate() is a no-op outside an\n * action.\n */\nexport function cache<A extends unknown[], R>(\n fn: (...args: A) => R,\n): (...args: A) => R {\n return (...args: A): R => {\n const store = scope()?.getStore();\n\n if (!store) return fn(...args);\n\n let root = store.get(fn);\n\n if (!root) {\n root = {};\n store.set(fn, root);\n }\n\n const node = nodeFor(root, args);\n\n // Who asked, every time: the body runs once, and a read it makes can\n // then name every component that waited on it, not only the first.\n noteAwaiter(fn);\n\n if (node.result) return node.result.value as R;\n\n const value = withHelper(fn, () => fn(...args));\n\n node.result = { value };\n\n return value;\n };\n}\n\n/** Whether anything is listening, for code that wants to know. */\nexport function isCaching(): boolean {\n return scope()?.getStore() !== undefined;\n}\n\n/**\n * Run one request with a memo table of its own.\n *\n * Opened by whoever owns the request. Nesting is harmless and does not create a\n * second table — an inner call reuses the one already open, so a host that\n * wraps and an engine that also wraps do not end up with two.\n */\nexport async function withCache<T>(run: () => Promise<T>): Promise<T> {\n if (!globals[SCOPE]) {\n ready ??= resolveScope().then((resolved) => {\n globals[SCOPE] ??= resolved as unknown as Scope;\n });\n\n await ready;\n }\n\n const existing = scope()!.getStore();\n\n if (existing) return await run();\n\n return await scope()!.run(new Map(), run);\n}\n"]}
|
|
1
|
+
{"version":3,"file":"cache.js","sourceRoot":"","sources":["../src/cache.ts"],"names":[],"mappings":"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","sourcesContent":["// Asking the same question twice in one request.\n//\n// import { cache } from '@rsc-kit/core/cache'\n//\n// export const currentUser = cache(async () => db.user(await sessionId()))\n//\n// A guard checks who you are; the layout wants their name; the page wants their\n// permissions. Three calls, one request, one answer — without threading the\n// result through props that only exist to carry it.\n//\n// This is the same shape as React's cache(), and deliberately not React's: the\n// engine's own boundaries are not React's. A guard runs before any component\n// does, which is exactly where the duplication starts, and React has no scope\n// open there.\n//\n// Scoped to the request, so two in flight cannot see each other's answers. The\n// scope is opened by whoever owns the request — the host, the worker, or the\n// prerenderer — and everything below it shares one store.\n\nimport { noteAwaiter, withHelper } from \"./request.js\";\nimport { resolveScope } from \"./revalidate.js\";\n\n/** One request's memo table. */\ntype Store = Map<unknown, ArgNode>;\n\n/**\n * Arguments keyed the way React keys them: a tree, one level per argument.\n *\n * Primitives compare by value and objects by identity, which falls out of\n * using a Map for the first and a WeakMap for the second. Serialising the\n * arguments instead would make `f({ a: 1 })` and `f({ a: 1 })` the same call —\n * which they are not, if the object is mutable.\n */\ninterface ArgNode {\n /** The answer for the argument list that ends here. */\n result?: { value: unknown };\n byValue?: Map<unknown, ArgNode>;\n byIdentity?: WeakMap<object, ArgNode>;\n}\n\nconst SCOPE = Symbol.for(\"@rsc-kit/core.cache-scope\");\n\nconst globals = globalThis as Record<symbol | string, unknown>;\n\nlet ready: Promise<void> | null = null;\n\ninterface Scope {\n getStore(): Store | undefined;\n run<T>(store: Store, fn: () => T): T;\n}\n\nfunction scope(): Scope | null {\n return (globals[SCOPE] as Scope | undefined) ?? null;\n}\n\nfunction isObject(value: unknown): value is object {\n return (\n (typeof value === \"object\" && value !== null) || typeof value === \"function\"\n );\n}\n\n/** Walk to the node for this argument list, creating it as we go. */\nfunction nodeFor(root: ArgNode, args: unknown[]): ArgNode {\n let node = root;\n\n for (const arg of args) {\n if (isObject(arg)) {\n node.byIdentity ??= new WeakMap();\n\n let next = node.byIdentity.get(arg);\n\n if (!next) {\n next = {};\n node.byIdentity.set(arg, next);\n }\n\n node = next;\n } else {\n node.byValue ??= new Map();\n\n let next = node.byValue.get(arg);\n\n if (!next) {\n next = {};\n node.byValue.set(arg, next);\n }\n\n node = next;\n }\n }\n\n return node;\n}\n\n/**\n * Memoise a function for the length of one request.\n *\n * The *promise* is cached, not the value, so callers that arrive while the\n * first is still in flight wait on it rather than starting a second. A\n * rejection is cached too: within one request the answer to a question does\n * not change because you asked again.\n *\n * Outside a request this calls straight through. Nothing is stored, nothing\n * leaks between requests, and shared code does not have to know which side of\n * the boundary it is on — the same reason revalidate() is a no-op outside an\n * action.\n */\nexport function cache<A extends unknown[], R>(\n fn: (...args: A) => R,\n): (...args: A) => R {\n return (...args: A): R => {\n const store = scope()?.getStore();\n\n if (!store) return fn(...args);\n\n let root = store.get(fn);\n\n if (!root) {\n root = {};\n store.set(fn, root);\n }\n\n const node = nodeFor(root, args);\n\n // Who asked, every time: the body runs once, and a read it makes can\n // then name every component that waited on it, not only the first.\n noteAwaiter(fn);\n\n if (node.result) return node.result.value as R;\n\n const value = withHelper(fn, () => fn(...args));\n\n node.result = { value };\n\n return value;\n };\n}\n\n/** Whether anything is listening, for code that wants to know. */\nexport function isCaching(): boolean {\n return scope()?.getStore() !== undefined;\n}\n\n/**\n * Run one request with a memo table of its own.\n *\n * Opened by whoever owns the request. Nesting is harmless and does not create a\n * second table — an inner call reuses the one already open, so a host that\n * wraps and an engine that also wraps do not end up with two.\n */\nexport async function withCache<T>(run: () => Promise<T>): Promise<T> {\n if (!globals[SCOPE]) {\n ready ??= resolveScope().then((resolved) => {\n globals[SCOPE] ??= resolved as unknown as Scope;\n });\n\n await ready;\n }\n\n const existing = scope()!.getStore();\n\n if (existing) return await run();\n\n return await scope()!.run(new Map(), run);\n}\n\n/**\n * Forget every answer this request has cached so far.\n *\n * For the render that follows a mutation. An action's request memoises what\n * the middleware and the action read before the row was changed, and a\n * revalidation rendered in the same request would read those answers back:\n * a port renamed its agent and the sidebar the action re-rendered still\n * said the old name, while the row was updated. What was true before the\n * write is not a memo of what is true after it.\n */\nexport function forgetCached(): void {\n scope()?.getStore()?.clear();\n}\n"]}
|
package/dist/clientEntries.js
CHANGED
|
@@ -91,7 +91,10 @@ export function clientScanPlugin() {
|
|
|
91
91
|
return {
|
|
92
92
|
name: "rsc-kit:client-scan",
|
|
93
93
|
resolveId(id) {
|
|
94
|
-
|
|
94
|
+
// The runtime's own modules - Bun's, and cloudflare:workers, where a
|
|
95
|
+
// Worker reads its bindings. The same list the server bundles keep
|
|
96
|
+
// external; see RUNTIME_BUILTINS in vite.ts.
|
|
97
|
+
return id === "bun" || id.startsWith("bun:") || id.startsWith("cloudflare:")
|
|
95
98
|
? { id, external: true }
|
|
96
99
|
: null;
|
|
97
100
|
},
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"clientEntries.js","sourceRoot":"","sources":["../src/clientEntries.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACpD,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAEzC;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,MAAM,GAAG,qBAAqB,CAAC;AACrC,MAAM,MAAM,GAAG,SAAS,CAAC,YAAY,CAAC,CAAC;AACvC,MAAM,MAAM,GAAG,SAAS,CAAC,YAAY,CAAC,CAAC;AAEvC,SAAS,SAAS,CAAC,IAAY;IAC7B,OAAO,IAAI,MAAM,CACf,4DAA4D,IAAI,MAAM,CACvE,CAAC;AACJ,CAAC;AAED,SAAS,IAAI,CAAC,IAAY;IACxB,IAAI,CAAC;QACH,OAAO,YAAY,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC;IACpD,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,SAAiB;IAC7C,MAAM,KAAK,GAAa,EAAE,CAAC;IAE3B,MAAM,IAAI,GAAG,CAAC,GAAW,EAAQ,EAAE;QACjC,IAAI,OAAO,CAAC;QAEZ,IAAI,CAAC;YACH,OAAO,GAAG,WAAW,CAAC,GAAG,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAC;QACtD,CAAC;QAAC,MAAM,CAAC;YACP,OAAO;QACT,CAAC;QAED,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;YAC5B,IAAI,KAAK,CAAC,IAAI,KAAK,cAAc,IAAI,KAAK,CAAC,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC;gBAAE,SAAS;YAE1E,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;YAEnC,IAAI,KAAK,CAAC,WAAW,EAAE,EAAE,CAAC;gBACxB,IAAI,CAAC,IAAI,CAAC,CAAC;gBAEX,SAAS;YACX,CAAC;YAED,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC;gBAAE,SAAS;YAEvE,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC;YAE1B,IAAI,MAAM,KAAK,IAAI,IAAI,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC;gBAAE,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC/D,CAAC;IACH,CAAC,CAAC;IAEF,IAAI,CAAC,SAAS,CAAC,CAAC;IAEhB,OAAO,KAAK,CAAC,IAAI,EAAE,CAAC;AACtB,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,mBAAmB;IACjC,OAAO,aAAa,CAAC,aAAa,CAAC,IAAI,GAAG,CAAC,OAAO,EAAE,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;AACzE,CAAC;AAED;;;;;;;;;;;;;GAaG;AACH,MAAM,UAAU,gBAAgB;IAK9B,OAAO;QACL,IAAI,EAAE,qBAAqB;QAC3B,SAAS,CAAC,EAAE;YACV,OAAO,EAAE,KAAK,KAAK,IAAI,EAAE,CAAC,UAAU,CAAC,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"clientEntries.js","sourceRoot":"","sources":["../src/clientEntries.ts"],"names":[],"mappings":"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","sourcesContent":["import { readdirSync, readFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\n\n/**\n * The app's \"use client\" files, for Vite's dependency scanner.\n *\n * Vite pre-bundles the browser's dependencies once, at startup, from what it\n * can reach by crawling the client entry. Here that entry imports the engine\n * and React and nothing else: an app's client components are reached through\n * RSC payloads, lazily, page by page. So the first visit to a page that uses\n * base-ui, or lucide, or a form library, discovered a dependency the scanner\n * never saw, and Vite re-optimised everything mid-session: new hashes for\n * every pre-bundle, modules already in memory still pointing at the old\n * React, the new page's modules at the new one - two Reacts, a null hook\n * dispatcher, React unmounting the document. Blank, then Vite's own reload.\n *\n * Handing the scanner every \"use client\" file up front is what Next's\n * bundler gets for free by walking one graph. The scanner follows imports,\n * so a component without the directive that a client file imports is\n * covered too; a server file is never scanned, so a server-only dependency\n * is never pre-bundled for the browser by mistake.\n */\nconst SOURCE = /\\.(?:[cm]?[jt]sx?)$/;\nconst CLIENT = directive(\"use client\");\nconst SERVER = directive(\"use server\");\n\nfunction directive(name: string): RegExp {\n return new RegExp(\n `^\\\\s*(?:\\\\/\\\\/[^\\\\n]*\\\\n|\\\\/\\\\*[\\\\s\\\\S]*?\\\\*\\\\/\\\\s*)*[\"']${name}[\"']`,\n );\n}\n\nfunction head(file: string): string | null {\n try {\n return readFileSync(file, \"utf-8\").slice(0, 2048);\n } catch {\n return null;\n }\n}\n\nexport function clientEntries(sourceDir: string): string[] {\n const found: string[] = [];\n\n const walk = (dir: string): void => {\n let entries;\n\n try {\n entries = readdirSync(dir, { withFileTypes: true });\n } catch {\n return;\n }\n\n for (const entry of entries) {\n if (entry.name === \"node_modules\" || entry.name.startsWith(\".\")) continue;\n\n const path = join(dir, entry.name);\n\n if (entry.isDirectory()) {\n walk(path);\n\n continue;\n }\n\n if (!SOURCE.test(entry.name) || entry.name.endsWith(\".d.ts\")) continue;\n\n const source = head(path);\n\n if (source !== null && CLIENT.test(source)) found.push(path);\n }\n };\n\n walk(sourceDir);\n\n return found.sort();\n}\n\n/**\n * The engine's own \"use client\" modules: Link, Form, the nuqs adapter, the\n * boundaries. The package is excluded from pre-bundling so that a payload's\n * reference to one of these files is the file itself, and an excluded\n * package is one the scanner does not enter - so the dependencies these\n * modules carry (the nuqs adapter's, for one) were found only when a page\n * first loaded the module, and that page re-optimised under itself.\n */\nexport function engineClientEntries(): string[] {\n return clientEntries(fileURLToPath(new URL(\"./js/\", import.meta.url)));\n}\n\n/**\n * What the scanner may follow from those files: the browser's graph, not\n * the server's.\n *\n * A client component imports a \"use server\" module to call its actions. In\n * the browser build that module is replaced by a proxy of its exports, and\n * nothing below it - the database, the runtime's own `bun` modules - is\n * ever bundled. The scanner runs before that replacement exists, follows\n * the raw file, reaches `import { SQL } from \"bun\"`, and gives up on the\n * whole scan: \"Failed to run dependency scan\", nothing pre-bundled, and the\n * re-optimisation the entries were meant to prevent. So an action module\n * scans as empty, and a runtime builtin is external here as it is in the\n * server bundles.\n */\nexport function clientScanPlugin(): {\n name: string;\n resolveId(id: string): { id: string; external: true } | null;\n load(id: string): string | null;\n} {\n return {\n name: \"rsc-kit:client-scan\",\n resolveId(id) {\n // The runtime's own modules - Bun's, and cloudflare:workers, where a\n // Worker reads its bindings. The same list the server bundles keep\n // external; see RUNTIME_BUILTINS in vite.ts.\n return id === \"bun\" || id.startsWith(\"bun:\") || id.startsWith(\"cloudflare:\")\n ? { id, external: true }\n : null;\n },\n load(id) {\n const file = id.split(\"?\")[0];\n\n if (file.includes(\"/node_modules/\") || !SOURCE.test(file)) return null;\n\n const source = head(file);\n\n return source !== null && SERVER.test(source) ? \"export {};\" : null;\n },\n };\n}\n"]}
|
package/dist/earlyHints.d.ts
CHANGED
|
@@ -13,5 +13,17 @@ export declare function linkHeader(assets: CriticalAssets): string;
|
|
|
13
13
|
* The first 16 KB, which is the head of any document this build writes.
|
|
14
14
|
*/
|
|
15
15
|
export declare function criticalAssetsOf(html: string): CriticalAssets;
|
|
16
|
-
/**
|
|
17
|
-
|
|
16
|
+
/**
|
|
17
|
+
* One set over another: the build's names, for a document whose own are not
|
|
18
|
+
* known.
|
|
19
|
+
*
|
|
20
|
+
* `primary` is null when nothing was read - a document rendered per request,
|
|
21
|
+
* whose head does not exist until after its headers have gone. A document
|
|
22
|
+
* that WAS read is the authority on itself, including when the answer is
|
|
23
|
+
* "nothing": a stored page with no client component names no module, and
|
|
24
|
+
* hinting the build's entry made the browser fetch 82 KB of runtime that
|
|
25
|
+
* the page never runs - the whole claim of a route that ships no
|
|
26
|
+
* JavaScript, undone by its own headers. Per field it was worse still: a
|
|
27
|
+
* page with its stylesheet inlined was hinted the build's stylesheet too.
|
|
28
|
+
*/
|
|
29
|
+
export declare function mergeAssets(primary: CriticalAssets | null, fallback: CriticalAssets | null): CriticalAssets;
|
package/dist/earlyHints.js
CHANGED
|
@@ -52,14 +52,21 @@ export function criticalAssetsOf(html) {
|
|
|
52
52
|
found.modules.push(entry[1]);
|
|
53
53
|
return found;
|
|
54
54
|
}
|
|
55
|
-
/**
|
|
55
|
+
/**
|
|
56
|
+
* One set over another: the build's names, for a document whose own are not
|
|
57
|
+
* known.
|
|
58
|
+
*
|
|
59
|
+
* `primary` is null when nothing was read - a document rendered per request,
|
|
60
|
+
* whose head does not exist until after its headers have gone. A document
|
|
61
|
+
* that WAS read is the authority on itself, including when the answer is
|
|
62
|
+
* "nothing": a stored page with no client component names no module, and
|
|
63
|
+
* hinting the build's entry made the browser fetch 82 KB of runtime that
|
|
64
|
+
* the page never runs - the whole claim of a route that ships no
|
|
65
|
+
* JavaScript, undone by its own headers. Per field it was worse still: a
|
|
66
|
+
* page with its stylesheet inlined was hinted the build's stylesheet too.
|
|
67
|
+
*/
|
|
56
68
|
export function mergeAssets(primary, fallback) {
|
|
57
|
-
|
|
58
|
-
return primary;
|
|
59
|
-
return {
|
|
60
|
-
styles: primary.styles.length ? primary.styles : fallback.styles,
|
|
61
|
-
modules: primary.modules.length ? primary.modules : fallback.modules,
|
|
62
|
-
fonts: primary.fonts.length ? primary.fonts : fallback.fonts,
|
|
63
|
-
};
|
|
69
|
+
return primary ?? fallback ?? EMPTY;
|
|
64
70
|
}
|
|
71
|
+
const EMPTY = { styles: [], modules: [], fonts: [] };
|
|
65
72
|
//# sourceMappingURL=earlyHints.js.map
|
package/dist/earlyHints.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"earlyHints.js","sourceRoot":"","sources":["../src/earlyHints.ts"],"names":[],"mappings":"AAAA,0EAA0E;AAC1E,yEAAyE;AACzE,wEAAwE;AACxE,sEAAsE;AACtE,yEAAyE;AACzE,oBAAoB;AACpB,EAAE;AACF,4EAA4E;AAC5E,0EAA0E;AAC1E,0EAA0E;AAC1E,4EAA4E;AAC5E,4EAA4E;AAC5E,+DAA+D;AAW/D,0EAA0E;AAC1E,MAAM,UAAU,UAAU,CAAC,MAAsB;IAC/C,MAAM,KAAK,GAAa,EAAE,CAAA;IAE1B,KAAK,MAAM,IAAI,IAAI,MAAM,CAAC,MAAM;QAAE,KAAK,CAAC,IAAI,CAAC,IAAI,IAAI,0BAA0B,CAAC,CAAA;IAChF,KAAK,MAAM,IAAI,IAAI,MAAM,CAAC,OAAO;QAAE,KAAK,CAAC,IAAI,CAAC,IAAI,IAAI,sBAAsB,CAAC,CAAA;IAC7E,KAAK,MAAM,IAAI,IAAI,MAAM,CAAC,KAAK;QAAE,KAAK,CAAC,IAAI,CAAC,IAAI,IAAI,sCAAsC,CAAC,CAAA;IAE3F,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;AACzB,CAAC;AAED,MAAM,UAAU,GAAG,MAAM,CAAA;AACzB,MAAM,IAAI,GAAG,gBAAgB,CAAA;AAC7B,MAAM,IAAI,GAAG,CAAC,IAAY,EAAE,GAAW,EAAiB,EAAE;IACxD,MAAM,KAAK,GAAG,IAAI,MAAM,CAAC,MAAM,IAAI,mBAAmB,EAAE,GAAG,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;IAEtE,OAAO,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAA;AAChC,CAAC,CAAA;AAED;;;GAGG;AACH,MAAM,UAAU,gBAAgB,CAAC,IAAY;IAC3C,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,UAAU,CAAC,CAAA;IACtC,MAAM,KAAK,GAAmB,EAAE,MAAM,EAAE,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,CAAA;IAEpE,KAAK,MAAM,GAAG,IAAI,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,EAAE,EAAE,CAAC;QACzC,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,EAAE,GAAG,CAAC,EAAE,WAAW,EAAE,CAAA;QAC3C,MAAM,IAAI,GAAG,IAAI,CAAC,MAAM,EAAE,GAAG,CAAC,CAAA;QAE9B,IAAI,CAAC,IAAI,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC;YAAE,SAAQ;QAE5C,IAAI,GAAG,KAAK,YAAY;YAAE,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;aAC5C,IAAI,GAAG,KAAK,SAAS,IAAI,IAAI,CAAC,IAAI,EAAE,GAAG,CAAC,EAAE,WAAW,EAAE,KAAK,MAAM;YAAE,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;IACjG,CAAC;IAED,2EAA2E;IAC3E,yEAAyE;IACzE,MAAM,KAAK,GAAG,kCAAkC,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,kCAAkC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;IAEzH,IAAI,KAAK;QAAE,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAA;IAEvC,OAAO,KAAK,CAAA;AACd,CAAC;AAED
|
|
1
|
+
{"version":3,"file":"earlyHints.js","sourceRoot":"","sources":["../src/earlyHints.ts"],"names":[],"mappings":"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","sourcesContent":["// The Link header a document answers with, for a CDN to send ahead as 103\n// Early Hints - so the stylesheet, the client entry and the fonts are on\n// their way while the HTML is still being written. Cloudflare turns the\n// header into hints and caches them at the edge for the next visitor;\n// anything else passes it through, where a browser still reads it as the\n// document arrives.\n//\n// The critical set and nothing more: a hint promotes whatever it names, and\n// naming every chunk demotes the document behind them. The stylesheet and\n// the client entry come from the build's own manifest, the same names the\n// document carries. The fonts are the app's - preloaded in its layout - and\n// are read from a stored document's own head; a rendered document's head is\n// not known until it is rendered, after its headers have gone.\n\nexport interface CriticalAssets {\n /** Stylesheets the document links: `<link rel=\"stylesheet\">`. */\n styles: string[];\n /** The client entry: `<script type=\"module\">`'s import. */\n modules: string[];\n /** Fonts the document preloads: `<link rel=\"preload\" as=\"font\">`. */\n fonts: string[];\n}\n\n/** One `Link` header value for the set. Empty string for an empty set. */\nexport function linkHeader(assets: CriticalAssets): string {\n const parts: string[] = []\n\n for (const href of assets.styles) parts.push(`<${href}>; rel=preload; as=style`)\n for (const href of assets.modules) parts.push(`<${href}>; rel=modulepreload`)\n for (const href of assets.fonts) parts.push(`<${href}>; rel=preload; as=font; crossorigin`)\n\n return parts.join(', ')\n}\n\nconst HEAD_LIMIT = 16_384\nconst LINK = /<link\\s[^>]*>/g\nconst ATTR = (name: string, tag: string): string | null => {\n const match = new RegExp(`\\\\b${name}=[\"']([^\"']*)[\"']`, 'i').exec(tag)\n\n return match ? match[1] : null\n}\n\n/**\n * What a stored document's head names: its stylesheets and preloaded fonts.\n * The first 16 KB, which is the head of any document this build writes.\n */\nexport function criticalAssetsOf(html: string): CriticalAssets {\n const head = html.slice(0, HEAD_LIMIT)\n const found: CriticalAssets = { styles: [], modules: [], fonts: [] }\n\n for (const tag of head.match(LINK) ?? []) {\n const rel = ATTR('rel', tag)?.toLowerCase()\n const href = ATTR('href', tag)\n\n if (!href || !href.startsWith('/')) continue\n\n if (rel === 'stylesheet') found.styles.push(href)\n else if (rel === 'preload' && ATTR('as', tag)?.toLowerCase() === 'font') found.fonts.push(href)\n }\n\n // The bootstrap script is the last thing in the body, after the shell; its\n // import names the entry. The tail, then, and the head for a short page.\n const entry = /import\\([\"'](\\/[^\"']+\\.js)[\"']\\)/.exec(html.slice(-4096)) ?? /import\\([\"'](\\/[^\"']+\\.js)[\"']\\)/.exec(head)\n\n if (entry) found.modules.push(entry[1])\n\n return found\n}\n\n/**\n * One set over another: the build's names, for a document whose own are not\n * known.\n *\n * `primary` is null when nothing was read - a document rendered per request,\n * whose head does not exist until after its headers have gone. A document\n * that WAS read is the authority on itself, including when the answer is\n * \"nothing\": a stored page with no client component names no module, and\n * hinting the build's entry made the browser fetch 82 KB of runtime that\n * the page never runs - the whole claim of a route that ships no\n * JavaScript, undone by its own headers. Per field it was worse still: a\n * page with its stylesheet inlined was hinted the build's stylesheet too.\n */\nexport function mergeAssets(primary: CriticalAssets | null, fallback: CriticalAssets | null): CriticalAssets {\n return primary ?? fallback ?? EMPTY\n}\n\nconst EMPTY: CriticalAssets = { styles: [], modules: [], fonts: [] }\n"]}
|
package/dist/files.d.ts
CHANGED
|
@@ -37,6 +37,31 @@ export declare function prerenderedFrom(dir: string): (name: string) => Promise<
|
|
|
37
37
|
*/
|
|
38
38
|
export declare const EMBEDDED_PAGES: unique symbol;
|
|
39
39
|
export declare function prerenderedBeside(moduleUrl: string, dirName: string, levels?: number): (name: string) => Promise<string | null>;
|
|
40
|
+
/**
|
|
41
|
+
* Where the stored pages live among a Worker's static assets.
|
|
42
|
+
*
|
|
43
|
+
* Under a prefix, beside the app's own public files, because that is the
|
|
44
|
+
* one store a Worker has that costs nothing in script size: an inline module
|
|
45
|
+
* counts against the bundle's limit, and a port's 571 stored category pages
|
|
46
|
+
* came to 69 MB of module against a limit of 10. The prefix is one the
|
|
47
|
+
* Worker answers first - `run_worker_first` in wrangler.json, written by
|
|
48
|
+
* the build - so a browser asking for `/_rsc-static/x.ppr.html` reaches the
|
|
49
|
+
* app, which has no such route, rather than the asset; a stored page of a
|
|
50
|
+
* guarded route is served only by the host that runs the guard.
|
|
51
|
+
*/
|
|
52
|
+
export declare const ASSETS_PREFIX: (dirName: string) => string;
|
|
53
|
+
interface AssetsBinding {
|
|
54
|
+
fetch(input: Request | string): Promise<Response>;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Read stored pages through the assets binding, each name once per isolate.
|
|
58
|
+
*
|
|
59
|
+
* The host asks for `{url}.html`, then `{url}.ppr.html`, then the pattern's,
|
|
60
|
+
* on every request: three asks per page, most of them misses. The binding
|
|
61
|
+
* is a fetch, not a memory read, so the answers - misses included - are kept
|
|
62
|
+
* for the life of the isolate, the way the disk reader keeps its listing.
|
|
63
|
+
*/
|
|
64
|
+
export declare function assetsReader(assets: AssetsBinding, prefix: string): (name: string) => Promise<string | null>;
|
|
40
65
|
/**
|
|
41
66
|
* The entry a binary is compiled from, written beside the server.
|
|
42
67
|
*
|
|
@@ -76,3 +101,16 @@ export declare function writeTo(dir: string): ((name: string, contents: string)
|
|
|
76
101
|
* has its own way to do that, and passes its own callback instead.
|
|
77
102
|
*/
|
|
78
103
|
export declare function copyAssets(from: string, to: string, url?: string): () => Promise<void>;
|
|
104
|
+
/**
|
|
105
|
+
* Put the stored pages among a Worker's static assets, and have the Worker
|
|
106
|
+
* answer their prefix first.
|
|
107
|
+
*
|
|
108
|
+
* Run after Nitro has written its public-asset list and wrangler.json, so
|
|
109
|
+
* the copies are in neither: not served as assets ahead of the Worker, and
|
|
110
|
+
* not needing a wrangler key Nitro would override. `run_worker_first` is
|
|
111
|
+
* added to the written config for the one prefix. The server-side directory
|
|
112
|
+
* is removed once copied, so nothing in it is uploaded twice. Returns how
|
|
113
|
+
* many files were moved; none when nothing was stored.
|
|
114
|
+
*/
|
|
115
|
+
export declare function storedPagesToAssets(staticDir: string, publicDir: string, wranglerPath: string): Promise<number>;
|
|
116
|
+
export {};
|
package/dist/files.js
CHANGED
|
@@ -139,6 +139,11 @@ export function prerenderedBeside(moduleUrl, dirName, levels = 4) {
|
|
|
139
139
|
}
|
|
140
140
|
}
|
|
141
141
|
}
|
|
142
|
+
// A Worker: the pages were uploaded as static assets, under a prefix
|
|
143
|
+
// the Worker answers first, and are read back through the binding.
|
|
144
|
+
const assets = await workersAssets();
|
|
145
|
+
if (assets)
|
|
146
|
+
return assetsReader(assets, `/${ASSETS_PREFIX(dirName)}`);
|
|
142
147
|
const inline = await inlinePrerendered(moduleUrl, `${dirName}-inline.mjs`, levels);
|
|
143
148
|
return inline ? async (name) => inline[name] ?? null : async () => null;
|
|
144
149
|
};
|
|
@@ -147,6 +152,57 @@ export function prerenderedBeside(moduleUrl, dirName, levels = 4) {
|
|
|
147
152
|
return await (await reader)(name);
|
|
148
153
|
};
|
|
149
154
|
}
|
|
155
|
+
/**
|
|
156
|
+
* Where the stored pages live among a Worker's static assets.
|
|
157
|
+
*
|
|
158
|
+
* Under a prefix, beside the app's own public files, because that is the
|
|
159
|
+
* one store a Worker has that costs nothing in script size: an inline module
|
|
160
|
+
* counts against the bundle's limit, and a port's 571 stored category pages
|
|
161
|
+
* came to 69 MB of module against a limit of 10. The prefix is one the
|
|
162
|
+
* Worker answers first - `run_worker_first` in wrangler.json, written by
|
|
163
|
+
* the build - so a browser asking for `/_rsc-static/x.ppr.html` reaches the
|
|
164
|
+
* app, which has no such route, rather than the asset; a stored page of a
|
|
165
|
+
* guarded route is served only by the host that runs the guard.
|
|
166
|
+
*/
|
|
167
|
+
export const ASSETS_PREFIX = (dirName) => `_${dirName}`;
|
|
168
|
+
/** The Workers assets binding, where this runs on Workers and it is bound. */
|
|
169
|
+
async function workersAssets() {
|
|
170
|
+
if (typeof navigator === 'undefined' || navigator.userAgent !== 'Cloudflare-Workers')
|
|
171
|
+
return null;
|
|
172
|
+
try {
|
|
173
|
+
// Computed, so neither the bundler nor tsc looks for a module only the
|
|
174
|
+
// Workers runtime has.
|
|
175
|
+
const specifier = 'cloudflare:workers';
|
|
176
|
+
const { env } = (await import(/* @vite-ignore */ specifier));
|
|
177
|
+
return env.ASSETS && typeof env.ASSETS.fetch === 'function' ? env.ASSETS : null;
|
|
178
|
+
}
|
|
179
|
+
catch {
|
|
180
|
+
return null;
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
/**
|
|
184
|
+
* Read stored pages through the assets binding, each name once per isolate.
|
|
185
|
+
*
|
|
186
|
+
* The host asks for `{url}.html`, then `{url}.ppr.html`, then the pattern's,
|
|
187
|
+
* on every request: three asks per page, most of them misses. The binding
|
|
188
|
+
* is a fetch, not a memory read, so the answers - misses included - are kept
|
|
189
|
+
* for the life of the isolate, the way the disk reader keeps its listing.
|
|
190
|
+
*/
|
|
191
|
+
export function assetsReader(assets, prefix) {
|
|
192
|
+
const known = new Map();
|
|
193
|
+
return (name) => {
|
|
194
|
+
let pending = known.get(name);
|
|
195
|
+
if (!pending) {
|
|
196
|
+
const path = `${prefix}/${name.split('/').map(encodeURIComponent).join('/')}`;
|
|
197
|
+
pending = assets
|
|
198
|
+
.fetch(new Request(`https://assets.invalid${path}`))
|
|
199
|
+
.then((response) => (response.ok ? response.text() : null))
|
|
200
|
+
.catch(() => null);
|
|
201
|
+
known.set(name, pending);
|
|
202
|
+
}
|
|
203
|
+
return pending;
|
|
204
|
+
};
|
|
205
|
+
}
|
|
150
206
|
/**
|
|
151
207
|
* The frozen pages as one module, for a runtime with no filesystem.
|
|
152
208
|
*
|
|
@@ -292,4 +348,57 @@ export function copyAssets(from, to, url = '/assets/') {
|
|
|
292
348
|
await cp(from, target, { recursive: true });
|
|
293
349
|
};
|
|
294
350
|
}
|
|
351
|
+
/**
|
|
352
|
+
* Put the stored pages among a Worker's static assets, and have the Worker
|
|
353
|
+
* answer their prefix first.
|
|
354
|
+
*
|
|
355
|
+
* Run after Nitro has written its public-asset list and wrangler.json, so
|
|
356
|
+
* the copies are in neither: not served as assets ahead of the Worker, and
|
|
357
|
+
* not needing a wrangler key Nitro would override. `run_worker_first` is
|
|
358
|
+
* added to the written config for the one prefix. The server-side directory
|
|
359
|
+
* is removed once copied, so nothing in it is uploaded twice. Returns how
|
|
360
|
+
* many files were moved; none when nothing was stored.
|
|
361
|
+
*/
|
|
362
|
+
export async function storedPagesToAssets(staticDir, publicDir, wranglerPath) {
|
|
363
|
+
const { cp, readFile: read, writeFile: write, stat, readdir: list } = await import('node:fs/promises');
|
|
364
|
+
const prefix = ASSETS_PREFIX(staticDir.split(/[\\/]/).pop() || 'rsc-static');
|
|
365
|
+
try {
|
|
366
|
+
await stat(staticDir);
|
|
367
|
+
}
|
|
368
|
+
catch {
|
|
369
|
+
return 0;
|
|
370
|
+
}
|
|
371
|
+
let copied = 0;
|
|
372
|
+
const count = async (dir) => {
|
|
373
|
+
for (const entry of await list(dir, { withFileTypes: true })) {
|
|
374
|
+
if (entry.isDirectory())
|
|
375
|
+
await count(join(dir, entry.name));
|
|
376
|
+
else
|
|
377
|
+
copied++;
|
|
378
|
+
}
|
|
379
|
+
};
|
|
380
|
+
await count(staticDir);
|
|
381
|
+
await cp(staticDir, join(publicDir, prefix), { recursive: true });
|
|
382
|
+
// Moved, not copied: wrangler's default rules upload every .html and
|
|
383
|
+
// .txt under the server directory as a text module, and 573 stored
|
|
384
|
+
// shells left beside the bundle were 55 MB of modules against a 10 MB
|
|
385
|
+
// limit - a deploy that happened to fit, once.
|
|
386
|
+
const { rm } = await import('node:fs/promises');
|
|
387
|
+
await rm(staticDir, { recursive: true, force: true });
|
|
388
|
+
try {
|
|
389
|
+
const config = JSON.parse(await read(wranglerPath, 'utf-8'));
|
|
390
|
+
if (config.assets && config.assets.run_worker_first !== true) {
|
|
391
|
+
const first = Array.isArray(config.assets.run_worker_first) ? config.assets.run_worker_first : [];
|
|
392
|
+
const pattern = `/${prefix}/*`;
|
|
393
|
+
if (!first.includes(pattern))
|
|
394
|
+
config.assets.run_worker_first = [...first, pattern];
|
|
395
|
+
await write(wranglerPath, JSON.stringify(config, null, 2));
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
catch {
|
|
399
|
+
// No wrangler.json to amend: a deploy config the app writes itself, which
|
|
400
|
+
// then carries the prefix in its own run_worker_first.
|
|
401
|
+
}
|
|
402
|
+
return copied;
|
|
403
|
+
}
|
|
295
404
|
//# sourceMappingURL=files.js.map
|
package/dist/files.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"files.js","sourceRoot":"","sources":["../src/files.ts"],"names":[],"mappings":"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","sourcesContent":["// Reading built output off a disk.\n//\n// Kept apart from the host on purpose: it is the only part of serving an RSC\n// app that assumes a filesystem, and plenty of places to run one do not have\n// one. Nothing in `@rsc-kit/core/host` imports this module, so a bundle for a\n// host without a filesystem carries no reference to `node:fs` at all.\n//\n// Assets are not here any more — Nitro publishes and serves those. What is\n// left is the prerendered output: written by the build, read by the server it\n// generates, and copied by a static export.\n\nimport { cp, mkdir, readdir, readFile, writeFile } from 'node:fs/promises'\nimport { dirname, join } from 'node:path'\n\n/**\n * Read what the prerenderer wrote, from a directory.\n *\n * Missing is an answer, not an error: a partial prerender is a valid state,\n * and anything not found here is rendered on demand instead.\n */\nexport function prerenderedFrom(dir: string) {\n /**\n * What is on disk, listed once.\n *\n * A miss used to be a thrown ENOENT, and misses are the common case: the\n * host asks for `{url}.html`, then `{url}.ppr.html`, then the route's\n * pattern — so every request to a page served by a pattern shell threw and\n * caught two exceptions before finding anything. Measured at 3,000 req/s\n * against 28,000 for the same page read from memory.\n *\n * Contents are cached for the life of the process. A build's output does\n * not change under a running server - a deploy restarts it, and a server\n * that outlived a rebuild would be serving a stale engine against new\n * pages anyway - so the first read is the only one. Under load the same\n * page used to be opened, read and closed thousands of times a second,\n * which a profile showed as the one cost on a stored page that was ours\n * rather than React's. Capped by bytes, so a site with more pages than\n * memory holds the hot ones and re-reads the rest.\n */\n let present: Promise<Set<string>> | null = null\n\n const CONTENT_CAP_BYTES = 64 * 1024 * 1024\n const contents = new Map<string, string>()\n let held = 0\n\n const remember = (name: string, text: string): void => {\n const size = text.length * 2\n\n if (size > CONTENT_CAP_BYTES / 4) return\n\n while (held + size > CONTENT_CAP_BYTES && contents.size > 0) {\n const oldest = contents.keys().next().value as string\n held -= contents.get(oldest)!.length * 2\n contents.delete(oldest)\n }\n\n contents.set(name, text)\n held += size\n }\n\n const listing = async (): Promise<Set<string>> => {\n present ??= readdir(dir, { recursive: true })\n .then((names) => new Set(names.map((n) => String(n).replace(/\\\\/g, '/'))))\n // An absent directory is an empty one: a partial prerender is a valid\n // state, and everything falls through to being rendered.\n .catch(() => new Set<string>())\n\n return await present\n }\n\n return async (name: string): Promise<string | null> => {\n if (name.includes('..')) return null\n\n if (!(await listing()).has(name)) return null\n\n const cached = contents.get(name)\n\n if (cached !== undefined) return cached\n\n try {\n const text = await readFile(join(dir, name), 'utf-8')\n\n remember(name, text)\n\n return text\n } catch {\n return null\n }\n }\n}\n\n/**\n * The same reader, for a directory whose absolute path is not known until the\n * server runs.\n *\n * Nitro bundles the generated entry to a different depth depending on the\n * preset — `.output/server/index.mjs` for one, `.output/server/_ssr/rsc.mjs`\n * for another — so neither a fixed relative path nor a path baked in at build\n * time survives: the first is wrong for half the presets, and the second stops\n * being true the moment `.output/` is copied to the machine that serves it.\n *\n * So the caller passes its own `import.meta.url` and the directory is found by\n * walking up from it. A few levels, bounded, and resolved once.\n *\n * prerendered: prerenderedBeside(import.meta.url, 'rsc-static')\n *\n * Not finding it is a valid state and not an error: nothing was frozen, or\n * this host has no filesystem to have frozen it on. Every page renders live,\n * which is what happens today everywhere.\n */\n/**\n * Where a compiled binary puts the frozen pages.\n *\n * `bun build --compile` embeds what is imported statically and nothing a\n * computed import names, so the walk below finds no directory and no\n * module inside a binary. The build writes `compile.mjs` beside the\n * server: it imports the inline module by name - which embeds it - and\n * hands the pages over here before importing the server. Shared as a\n * global under a registered symbol, because the wrapper and this module\n * are bundled apart.\n */\nexport const EMBEDDED_PAGES = Symbol.for('rsc-kit.embedded-pages')\n\nfunction embeddedPages(): Record<string, string> | null {\n const pages = (globalThis as Record<symbol, unknown>)[EMBEDDED_PAGES]\n\n return pages && typeof pages === 'object' ? (pages as Record<string, string>) : null\n}\n\nexport function prerenderedBeside(moduleUrl: string, dirName: string, levels = 4) {\n let reader: Promise<(name: string) => Promise<string | null>> | null = null\n\n const resolveDir = async (): Promise<(name: string) => Promise<string | null>> => {\n const embedded = embeddedPages()\n\n if (embedded) return async (name) => embedded[name] ?? null\n\n let dir: string | null = null\n\n try {\n const { fileURLToPath } = await import('node:url')\n\n dir = dirname(fileURLToPath(moduleUrl))\n } catch {\n // No filesystem, or no file: url to speak of. The inline module below\n // is the only place the pages can be.\n }\n\n if (dir !== null) {\n for (let i = 0; i <= levels; i++) {\n const candidate = join(dir, ...Array(i).fill('..'), dirName)\n\n try {\n await readdir(candidate)\n\n return prerenderedFrom(candidate)\n } catch {\n // Not at this level. Keep walking.\n }\n }\n }\n\n const inline = await inlinePrerendered(moduleUrl, `${dirName}-inline.mjs`, levels)\n\n return inline ? async (name) => inline[name] ?? null : async () => null\n }\n\n return async (name: string): Promise<string | null> => {\n reader ??= resolveDir()\n\n return await (await reader)(name)\n }\n}\n\n/**\n * The frozen pages as one module, for a runtime with no filesystem.\n *\n * A Worker cannot readdir. Its modules are what wrangler uploaded, and a\n * sibling .mjs beside the bundle is one of them - so the build writes every\n * stored file into `rsc-static-inline.mjs` as strings, and this walks up from\n * the caller the way the disk reader does, importing rather than reading.\n * The specifier is computed, and marked for the bundler to leave alone: at\n * bundle time the file does not exist yet, because it is written by the\n * prerender that runs the bundle.\n *\n * Two forms of the specifier, because runtimes disagree. workerd resolves a\n * relative string against the importing module's name and refuses a url;\n * Node and Bun want the url, since a relative string would resolve against\n * this file rather than the caller's.\n */\nasync function inlinePrerendered(\n moduleUrl: string,\n fileName: string,\n levels: number,\n): Promise<Record<string, string> | null> {\n for (let i = 0; i <= levels; i++) {\n const relative = './' + '../'.repeat(i) + fileName\n\n for (const specifier of [relative, safeUrl(relative, moduleUrl)]) {\n if (!specifier) continue\n\n try {\n const loaded = (await import(/* @vite-ignore */ specifier)) as { default?: Record<string, string> }\n\n if (loaded.default && typeof loaded.default === 'object') return loaded.default\n } catch {\n // Not there, or not at this level.\n }\n }\n }\n\n return null\n}\n\nfunction safeUrl(relative: string, base: string): string | null {\n try {\n return new URL(relative, base).href\n } catch {\n return null\n }\n}\n\n/**\n * The entry a binary is compiled from, written beside the server.\n *\n * Two static imports, in this order: the inline module first - evaluating\n * it hands the pages over - and the server second. A module's imports are\n * evaluated in order, before its own body, so the pages are in place before\n * the server's first line runs. Bun embeds both.\n *\n * Static rather than `await import()`: a top-level await is what made Bun\n * skip `--bytecode` for this entry, silently, byte for byte - and a port\n * whose other Nitro app compiled with it asked why this one could not.\n * `bun build --compile --bytecode .output/server/compile.mjs` works now.\n */\nexport function compileEntrySource(dirName: string): string {\n return (\n '// The entry to compile into one binary: bun build --compile .output/server/compile.mjs\\n' +\n '//\\n' +\n '// A binary carries what is imported by name. The server reads its frozen pages\\n' +\n '// through a computed import, which a compile cannot see - imported here, they\\n' +\n '// travel inside the binary and are handed to the server before it starts.\\n' +\n '// Two static imports in this order, and no top-level await: imports evaluate\\n' +\n '// in order, and an await here is what stopped --bytecode from applying.\\n' +\n `import \"./${inlineModuleName(dirName)}\";\\n` +\n 'import \"./index.mjs\";\\n'\n )\n}\n\n/** The name the build writes the inline module under, beside the stored pages' directory. */\nexport function inlineModuleName(dirName: string): string {\n return `${dirName}-inline.mjs`\n}\n\n/**\n * Every stored file under `dir`, as the source of the inline module.\n * Strings only: a frozen page, its flight payload, its meta, an api answer.\n */\nexport async function inlineModuleSource(dir: string): Promise<string> {\n const { readdir: list, readFile: read } = await import('node:fs/promises')\n const entries: Record<string, string> = {}\n\n const walk = async (sub: string): Promise<void> => {\n for (const entry of await list(join(dir, sub), { withFileTypes: true })) {\n const rel = sub ? `${sub}/${entry.name}` : entry.name\n\n if (entry.isDirectory()) await walk(rel)\n else entries[rel] = await read(join(dir, rel), 'utf-8')\n }\n }\n\n await walk('')\n\n // Exported for the Worker, which imports it when the directory is not\n // there, and handed over as a global for the binary, whose compile entry\n // imports it first for exactly this side effect. The same data either way.\n return (\n '// @generated by the RSC build: the stored pages, for a runtime with no filesystem.\\n' +\n 'const pages = ' +\n JSON.stringify(entries) +\n ';\\n' +\n 'globalThis[Symbol.for(\"rsc-kit.embedded-pages\")] = pages;\\n' +\n 'export default pages;\\n'\n )\n}\n\nfunction contentTypeOf(pathname: string): string {\n if (pathname.endsWith('.js')) return 'text/javascript; charset=utf-8'\n if (pathname.endsWith('.css')) return 'text/css; charset=utf-8'\n if (pathname.endsWith('.map')) return 'application/json; charset=utf-8'\n if (pathname.endsWith('.svg')) return 'image/svg+xml'\n if (pathname.endsWith('.woff2')) return 'font/woff2'\n\n return 'application/octet-stream'\n}\n\n/**\n * Write build output into a directory.\n *\n * The sink `prerender` and `exportSite` take. Names are relative and may\n * contain directories — `docs/index.html` — so each one's parent is created\n * as it goes.\n */\nexport function writeTo(dir: string): ((name: string, contents: string) => Promise<void>) & { dir: string } {\n const write = async (name: string, contents: string): Promise<void> => {\n const path = join(dir, name)\n\n await mkdir(dirname(path), { recursive: true })\n await writeFile(path, contents)\n }\n\n // Where it wrote, for a caller that has to read it back. The embeddable\n // module has to list these files, and being told the directory a second time\n // is a second place for it to be wrong.\n return Object.assign(write, { dir })\n}\n\n/**\n * Copy the built browser bundle into an exported site.\n *\n * Separate from the export itself because copying a tree of files is a\n * filesystem operation by nature — a deploy that uploads them to a bucket\n * has its own way to do that, and passes its own callback instead.\n */\nexport function copyAssets(from: string, to: string, url = '/assets/') {\n return async (): Promise<void> => {\n const at = url.replace(/^\\/+|\\/+$/g, '')\n\n if (at === '') return\n\n const target = join(to, at)\n\n await mkdir(dirname(target), { recursive: true })\n await cp(from, target, { recursive: true })\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"files.js","sourceRoot":"","sources":["../src/files.ts"],"names":[],"mappings":"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","sourcesContent":["// Reading built output off a disk.\n//\n// Kept apart from the host on purpose: it is the only part of serving an RSC\n// app that assumes a filesystem, and plenty of places to run one do not have\n// one. Nothing in `@rsc-kit/core/host` imports this module, so a bundle for a\n// host without a filesystem carries no reference to `node:fs` at all.\n//\n// Assets are not here any more — Nitro publishes and serves those. What is\n// left is the prerendered output: written by the build, read by the server it\n// generates, and copied by a static export.\n\nimport { cp, mkdir, readdir, readFile, writeFile } from 'node:fs/promises'\nimport { dirname, join } from 'node:path'\n\n/**\n * Read what the prerenderer wrote, from a directory.\n *\n * Missing is an answer, not an error: a partial prerender is a valid state,\n * and anything not found here is rendered on demand instead.\n */\nexport function prerenderedFrom(dir: string) {\n /**\n * What is on disk, listed once.\n *\n * A miss used to be a thrown ENOENT, and misses are the common case: the\n * host asks for `{url}.html`, then `{url}.ppr.html`, then the route's\n * pattern — so every request to a page served by a pattern shell threw and\n * caught two exceptions before finding anything. Measured at 3,000 req/s\n * against 28,000 for the same page read from memory.\n *\n * Contents are cached for the life of the process. A build's output does\n * not change under a running server - a deploy restarts it, and a server\n * that outlived a rebuild would be serving a stale engine against new\n * pages anyway - so the first read is the only one. Under load the same\n * page used to be opened, read and closed thousands of times a second,\n * which a profile showed as the one cost on a stored page that was ours\n * rather than React's. Capped by bytes, so a site with more pages than\n * memory holds the hot ones and re-reads the rest.\n */\n let present: Promise<Set<string>> | null = null\n\n const CONTENT_CAP_BYTES = 64 * 1024 * 1024\n const contents = new Map<string, string>()\n let held = 0\n\n const remember = (name: string, text: string): void => {\n const size = text.length * 2\n\n if (size > CONTENT_CAP_BYTES / 4) return\n\n while (held + size > CONTENT_CAP_BYTES && contents.size > 0) {\n const oldest = contents.keys().next().value as string\n held -= contents.get(oldest)!.length * 2\n contents.delete(oldest)\n }\n\n contents.set(name, text)\n held += size\n }\n\n const listing = async (): Promise<Set<string>> => {\n present ??= readdir(dir, { recursive: true })\n .then((names) => new Set(names.map((n) => String(n).replace(/\\\\/g, '/'))))\n // An absent directory is an empty one: a partial prerender is a valid\n // state, and everything falls through to being rendered.\n .catch(() => new Set<string>())\n\n return await present\n }\n\n return async (name: string): Promise<string | null> => {\n if (name.includes('..')) return null\n\n if (!(await listing()).has(name)) return null\n\n const cached = contents.get(name)\n\n if (cached !== undefined) return cached\n\n try {\n const text = await readFile(join(dir, name), 'utf-8')\n\n remember(name, text)\n\n return text\n } catch {\n return null\n }\n }\n}\n\n/**\n * The same reader, for a directory whose absolute path is not known until the\n * server runs.\n *\n * Nitro bundles the generated entry to a different depth depending on the\n * preset — `.output/server/index.mjs` for one, `.output/server/_ssr/rsc.mjs`\n * for another — so neither a fixed relative path nor a path baked in at build\n * time survives: the first is wrong for half the presets, and the second stops\n * being true the moment `.output/` is copied to the machine that serves it.\n *\n * So the caller passes its own `import.meta.url` and the directory is found by\n * walking up from it. A few levels, bounded, and resolved once.\n *\n * prerendered: prerenderedBeside(import.meta.url, 'rsc-static')\n *\n * Not finding it is a valid state and not an error: nothing was frozen, or\n * this host has no filesystem to have frozen it on. Every page renders live,\n * which is what happens today everywhere.\n */\n/**\n * Where a compiled binary puts the frozen pages.\n *\n * `bun build --compile` embeds what is imported statically and nothing a\n * computed import names, so the walk below finds no directory and no\n * module inside a binary. The build writes `compile.mjs` beside the\n * server: it imports the inline module by name - which embeds it - and\n * hands the pages over here before importing the server. Shared as a\n * global under a registered symbol, because the wrapper and this module\n * are bundled apart.\n */\nexport const EMBEDDED_PAGES = Symbol.for('rsc-kit.embedded-pages')\n\nfunction embeddedPages(): Record<string, string> | null {\n const pages = (globalThis as Record<symbol, unknown>)[EMBEDDED_PAGES]\n\n return pages && typeof pages === 'object' ? (pages as Record<string, string>) : null\n}\n\nexport function prerenderedBeside(moduleUrl: string, dirName: string, levels = 4) {\n let reader: Promise<(name: string) => Promise<string | null>> | null = null\n\n const resolveDir = async (): Promise<(name: string) => Promise<string | null>> => {\n const embedded = embeddedPages()\n\n if (embedded) return async (name) => embedded[name] ?? null\n\n let dir: string | null = null\n\n try {\n const { fileURLToPath } = await import('node:url')\n\n dir = dirname(fileURLToPath(moduleUrl))\n } catch {\n // No filesystem, or no file: url to speak of. The inline module below\n // is the only place the pages can be.\n }\n\n if (dir !== null) {\n for (let i = 0; i <= levels; i++) {\n const candidate = join(dir, ...Array(i).fill('..'), dirName)\n\n try {\n await readdir(candidate)\n\n return prerenderedFrom(candidate)\n } catch {\n // Not at this level. Keep walking.\n }\n }\n }\n\n // A Worker: the pages were uploaded as static assets, under a prefix\n // the Worker answers first, and are read back through the binding.\n const assets = await workersAssets()\n\n if (assets) return assetsReader(assets, `/${ASSETS_PREFIX(dirName)}`)\n\n const inline = await inlinePrerendered(moduleUrl, `${dirName}-inline.mjs`, levels)\n\n return inline ? async (name) => inline[name] ?? null : async () => null\n }\n\n return async (name: string): Promise<string | null> => {\n reader ??= resolveDir()\n\n return await (await reader)(name)\n }\n}\n\n/**\n * Where the stored pages live among a Worker's static assets.\n *\n * Under a prefix, beside the app's own public files, because that is the\n * one store a Worker has that costs nothing in script size: an inline module\n * counts against the bundle's limit, and a port's 571 stored category pages\n * came to 69 MB of module against a limit of 10. The prefix is one the\n * Worker answers first - `run_worker_first` in wrangler.json, written by\n * the build - so a browser asking for `/_rsc-static/x.ppr.html` reaches the\n * app, which has no such route, rather than the asset; a stored page of a\n * guarded route is served only by the host that runs the guard.\n */\nexport const ASSETS_PREFIX = (dirName: string): string => `_${dirName}`\n\n/** The Workers assets binding, where this runs on Workers and it is bound. */\nasync function workersAssets(): Promise<AssetsBinding | null> {\n if (typeof navigator === 'undefined' || navigator.userAgent !== 'Cloudflare-Workers') return null\n\n try {\n // Computed, so neither the bundler nor tsc looks for a module only the\n // Workers runtime has.\n const specifier = 'cloudflare:workers'\n const { env } = (await import(/* @vite-ignore */ specifier)) as { env: { ASSETS?: AssetsBinding } }\n\n return env.ASSETS && typeof env.ASSETS.fetch === 'function' ? env.ASSETS : null\n } catch {\n return null\n }\n}\n\ninterface AssetsBinding {\n fetch(input: Request | string): Promise<Response>\n}\n\n/**\n * Read stored pages through the assets binding, each name once per isolate.\n *\n * The host asks for `{url}.html`, then `{url}.ppr.html`, then the pattern's,\n * on every request: three asks per page, most of them misses. The binding\n * is a fetch, not a memory read, so the answers - misses included - are kept\n * for the life of the isolate, the way the disk reader keeps its listing.\n */\nexport function assetsReader(assets: AssetsBinding, prefix: string): (name: string) => Promise<string | null> {\n const known = new Map<string, Promise<string | null>>()\n\n return (name) => {\n let pending = known.get(name)\n\n if (!pending) {\n const path = `${prefix}/${name.split('/').map(encodeURIComponent).join('/')}`\n\n pending = assets\n .fetch(new Request(`https://assets.invalid${path}`))\n .then((response) => (response.ok ? response.text() : null))\n .catch(() => null)\n\n known.set(name, pending)\n }\n\n return pending\n }\n}\n\n/**\n * The frozen pages as one module, for a runtime with no filesystem.\n *\n * A Worker cannot readdir. Its modules are what wrangler uploaded, and a\n * sibling .mjs beside the bundle is one of them - so the build writes every\n * stored file into `rsc-static-inline.mjs` as strings, and this walks up from\n * the caller the way the disk reader does, importing rather than reading.\n * The specifier is computed, and marked for the bundler to leave alone: at\n * bundle time the file does not exist yet, because it is written by the\n * prerender that runs the bundle.\n *\n * Two forms of the specifier, because runtimes disagree. workerd resolves a\n * relative string against the importing module's name and refuses a url;\n * Node and Bun want the url, since a relative string would resolve against\n * this file rather than the caller's.\n */\nasync function inlinePrerendered(\n moduleUrl: string,\n fileName: string,\n levels: number,\n): Promise<Record<string, string> | null> {\n for (let i = 0; i <= levels; i++) {\n const relative = './' + '../'.repeat(i) + fileName\n\n for (const specifier of [relative, safeUrl(relative, moduleUrl)]) {\n if (!specifier) continue\n\n try {\n const loaded = (await import(/* @vite-ignore */ specifier)) as { default?: Record<string, string> }\n\n if (loaded.default && typeof loaded.default === 'object') return loaded.default\n } catch {\n // Not there, or not at this level.\n }\n }\n }\n\n return null\n}\n\nfunction safeUrl(relative: string, base: string): string | null {\n try {\n return new URL(relative, base).href\n } catch {\n return null\n }\n}\n\n/**\n * The entry a binary is compiled from, written beside the server.\n *\n * Two static imports, in this order: the inline module first - evaluating\n * it hands the pages over - and the server second. A module's imports are\n * evaluated in order, before its own body, so the pages are in place before\n * the server's first line runs. Bun embeds both.\n *\n * Static rather than `await import()`: a top-level await is what made Bun\n * skip `--bytecode` for this entry, silently, byte for byte - and a port\n * whose other Nitro app compiled with it asked why this one could not.\n * `bun build --compile --bytecode .output/server/compile.mjs` works now.\n */\nexport function compileEntrySource(dirName: string): string {\n return (\n '// The entry to compile into one binary: bun build --compile .output/server/compile.mjs\\n' +\n '//\\n' +\n '// A binary carries what is imported by name. The server reads its frozen pages\\n' +\n '// through a computed import, which a compile cannot see - imported here, they\\n' +\n '// travel inside the binary and are handed to the server before it starts.\\n' +\n '// Two static imports in this order, and no top-level await: imports evaluate\\n' +\n '// in order, and an await here is what stopped --bytecode from applying.\\n' +\n `import \"./${inlineModuleName(dirName)}\";\\n` +\n 'import \"./index.mjs\";\\n'\n )\n}\n\n/** The name the build writes the inline module under, beside the stored pages' directory. */\nexport function inlineModuleName(dirName: string): string {\n return `${dirName}-inline.mjs`\n}\n\n/**\n * Every stored file under `dir`, as the source of the inline module.\n * Strings only: a frozen page, its flight payload, its meta, an api answer.\n */\nexport async function inlineModuleSource(dir: string): Promise<string> {\n const { readdir: list, readFile: read } = await import('node:fs/promises')\n const entries: Record<string, string> = {}\n\n const walk = async (sub: string): Promise<void> => {\n for (const entry of await list(join(dir, sub), { withFileTypes: true })) {\n const rel = sub ? `${sub}/${entry.name}` : entry.name\n\n if (entry.isDirectory()) await walk(rel)\n else entries[rel] = await read(join(dir, rel), 'utf-8')\n }\n }\n\n await walk('')\n\n // Exported for the Worker, which imports it when the directory is not\n // there, and handed over as a global for the binary, whose compile entry\n // imports it first for exactly this side effect. The same data either way.\n return (\n '// @generated by the RSC build: the stored pages, for a runtime with no filesystem.\\n' +\n 'const pages = ' +\n JSON.stringify(entries) +\n ';\\n' +\n 'globalThis[Symbol.for(\"rsc-kit.embedded-pages\")] = pages;\\n' +\n 'export default pages;\\n'\n )\n}\n\nfunction contentTypeOf(pathname: string): string {\n if (pathname.endsWith('.js')) return 'text/javascript; charset=utf-8'\n if (pathname.endsWith('.css')) return 'text/css; charset=utf-8'\n if (pathname.endsWith('.map')) return 'application/json; charset=utf-8'\n if (pathname.endsWith('.svg')) return 'image/svg+xml'\n if (pathname.endsWith('.woff2')) return 'font/woff2'\n\n return 'application/octet-stream'\n}\n\n/**\n * Write build output into a directory.\n *\n * The sink `prerender` and `exportSite` take. Names are relative and may\n * contain directories — `docs/index.html` — so each one's parent is created\n * as it goes.\n */\nexport function writeTo(dir: string): ((name: string, contents: string) => Promise<void>) & { dir: string } {\n const write = async (name: string, contents: string): Promise<void> => {\n const path = join(dir, name)\n\n await mkdir(dirname(path), { recursive: true })\n await writeFile(path, contents)\n }\n\n // Where it wrote, for a caller that has to read it back. The embeddable\n // module has to list these files, and being told the directory a second time\n // is a second place for it to be wrong.\n return Object.assign(write, { dir })\n}\n\n/**\n * Copy the built browser bundle into an exported site.\n *\n * Separate from the export itself because copying a tree of files is a\n * filesystem operation by nature — a deploy that uploads them to a bucket\n * has its own way to do that, and passes its own callback instead.\n */\nexport function copyAssets(from: string, to: string, url = '/assets/') {\n return async (): Promise<void> => {\n const at = url.replace(/^\\/+|\\/+$/g, '')\n\n if (at === '') return\n\n const target = join(to, at)\n\n await mkdir(dirname(target), { recursive: true })\n await cp(from, target, { recursive: true })\n }\n}\n\n/**\n * Put the stored pages among a Worker's static assets, and have the Worker\n * answer their prefix first.\n *\n * Run after Nitro has written its public-asset list and wrangler.json, so\n * the copies are in neither: not served as assets ahead of the Worker, and\n * not needing a wrangler key Nitro would override. `run_worker_first` is\n * added to the written config for the one prefix. The server-side directory\n * is removed once copied, so nothing in it is uploaded twice. Returns how\n * many files were moved; none when nothing was stored.\n */\nexport async function storedPagesToAssets(staticDir: string, publicDir: string, wranglerPath: string): Promise<number> {\n const { cp, readFile: read, writeFile: write, stat, readdir: list } = await import('node:fs/promises')\n const prefix = ASSETS_PREFIX(staticDir.split(/[\\\\/]/).pop() || 'rsc-static')\n\n try {\n await stat(staticDir)\n } catch {\n return 0\n }\n\n let copied = 0\n const count = async (dir: string): Promise<void> => {\n for (const entry of await list(dir, { withFileTypes: true })) {\n if (entry.isDirectory()) await count(join(dir, entry.name))\n else copied++\n }\n }\n\n await count(staticDir)\n await cp(staticDir, join(publicDir, prefix), { recursive: true })\n\n // Moved, not copied: wrangler's default rules upload every .html and\n // .txt under the server directory as a text module, and 573 stored\n // shells left beside the bundle were 55 MB of modules against a 10 MB\n // limit - a deploy that happened to fit, once.\n const { rm } = await import('node:fs/promises')\n\n await rm(staticDir, { recursive: true, force: true })\n\n try {\n const config = JSON.parse(await read(wranglerPath, 'utf-8')) as {\n assets?: { run_worker_first?: boolean | string[] } & Record<string, unknown>\n }\n\n if (config.assets && config.assets.run_worker_first !== true) {\n const first = Array.isArray(config.assets.run_worker_first) ? config.assets.run_worker_first : []\n const pattern = `/${prefix}/*`\n\n if (!first.includes(pattern)) config.assets.run_worker_first = [...first, pattern]\n\n await write(wranglerPath, JSON.stringify(config, null, 2))\n }\n } catch {\n // No wrangler.json to amend: a deploy config the app writes itself, which\n // then carries the prefix in its own run_worker_first.\n }\n\n return copied\n}\n"]}
|
package/dist/host.d.ts
CHANGED
|
@@ -27,7 +27,7 @@ export interface RscEngine {
|
|
|
27
27
|
handleRscStream(component: string, props?: Record<string, unknown>, layouts?: {
|
|
28
28
|
component: string;
|
|
29
29
|
props: Record<string, unknown>;
|
|
30
|
-
}[], loadings?: string[], parallelSlots?: Record<string, string>, slotOverrides?: Record<string, unknown>, from?: number, pageKey?: string): Promise<{
|
|
30
|
+
}[], loadings?: string[], parallelSlots?: Record<string, string>, slotOverrides?: Record<string, unknown>, from?: number, pageKey?: string, pathname?: string | null): Promise<{
|
|
31
31
|
stream: ReadableStream;
|
|
32
32
|
segmentDepth: number;
|
|
33
33
|
}>;
|
|
@@ -60,12 +60,21 @@ export interface RscEngine {
|
|
|
60
60
|
handleRscResume?(component: string, props?: Record<string, unknown>, layouts?: {
|
|
61
61
|
component: string;
|
|
62
62
|
props: Record<string, unknown>;
|
|
63
|
-
}[], loadings?: string[], parallelSlots?: Record<string, string>, slotOverrides?: Record<string, unknown>, postponed?: unknown, nonce?: string, pageKey?: string): Promise<{
|
|
63
|
+
}[], loadings?: string[], parallelSlots?: Record<string, string>, slotOverrides?: Record<string, unknown>, postponed?: unknown, nonce?: string, pageKey?: string, pathname?: string | null): Promise<{
|
|
64
64
|
htmlStream: ReadableStream;
|
|
65
65
|
}>;
|
|
66
66
|
handleRscRevalidate?(target: string, page: unknown): Promise<{
|
|
67
67
|
rscPayload: string;
|
|
68
68
|
}>;
|
|
69
|
+
/**
|
|
70
|
+
* The page's metadata for these params, merged with its layouts'. Used to
|
|
71
|
+
* put the real title into a shell stored for a whole pattern, whose build
|
|
72
|
+
* could not know the url.
|
|
73
|
+
*/
|
|
74
|
+
resolveMetadata?(component: string, props?: Record<string, unknown>, layouts?: {
|
|
75
|
+
component: string;
|
|
76
|
+
props: Record<string, unknown>;
|
|
77
|
+
}[]): Promise<Record<string, unknown> | null>;
|
|
69
78
|
/**
|
|
70
79
|
* Run a route's middleware without rendering anything.
|
|
71
80
|
*
|