@statewalker/cellb 0.8.8 → 0.8.9

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.
Files changed (68) hide show
  1. package/README.md +1 -1
  2. package/dist/bindings/cjs-module.d.ts +7 -0
  3. package/dist/bindings/cjs-module.js +96 -0
  4. package/dist/bindings/cjs-module.js.map +1 -0
  5. package/dist/bindings/module-registry.d.ts +33 -0
  6. package/dist/bindings/module-registry.js +180 -0
  7. package/dist/bindings/module-registry.js.map +1 -0
  8. package/dist/bindings/worker-loader.d.ts +23 -5
  9. package/dist/bindings/worker-loader.js +75 -4
  10. package/dist/bindings/worker-loader.js.map +1 -1
  11. package/dist/core/handles.d.ts +21 -1
  12. package/dist/core/handles.js +49 -3
  13. package/dist/core/handles.js.map +1 -1
  14. package/dist/core/namespace.js +4 -1
  15. package/dist/core/namespace.js.map +1 -1
  16. package/dist/core/runtime.d.ts +8 -0
  17. package/dist/core/runtime.js +3 -3
  18. package/dist/core/runtime.js.map +1 -1
  19. package/dist/index.d.ts +5 -0
  20. package/dist/index.js +5 -0
  21. package/dist/index.js.map +1 -1
  22. package/dist/isolate/browser.d.ts +1 -1
  23. package/dist/isolate/browser.js +3 -1
  24. package/dist/isolate/browser.js.map +1 -1
  25. package/dist/isolate/host.js +20 -2
  26. package/dist/isolate/host.js.map +1 -1
  27. package/dist/isolate/isolate-worker.js +13 -1
  28. package/dist/isolate/isolate-worker.js.map +1 -1
  29. package/dist/isolate/isolate.js +11 -0
  30. package/dist/isolate/isolate.js.map +1 -1
  31. package/dist/isolate/node-entry.js +33 -0
  32. package/dist/isolate/node-entry.js.map +1 -1
  33. package/dist/isolate/protocol.d.ts +1 -0
  34. package/dist/isolate/protocol.js +228 -13
  35. package/dist/isolate/protocol.js.map +1 -1
  36. package/dist/isolate/writable-globals.d.ts +9 -0
  37. package/dist/isolate/writable-globals.js +45 -0
  38. package/dist/isolate/writable-globals.js.map +1 -0
  39. package/dist/node-compat/bundle-files.d.ts +26 -0
  40. package/dist/node-compat/bundle-files.js +210 -0
  41. package/dist/node-compat/bundle-files.js.map +1 -0
  42. package/dist/node-compat/fs.d.ts +13 -1
  43. package/dist/node-compat/fs.js +54 -15
  44. package/dist/node-compat/fs.js.map +1 -1
  45. package/dist/project/browser-modules.d.ts +1 -1
  46. package/dist/project/browser-modules.js +25 -5
  47. package/dist/project/browser-modules.js.map +1 -1
  48. package/dist/project/cloudflare-workers.d.ts +9 -0
  49. package/dist/project/cloudflare-workers.js +14 -0
  50. package/dist/project/cloudflare-workers.js.map +1 -1
  51. package/dist/project/module-rules.js +31 -10
  52. package/dist/project/module-rules.js.map +1 -1
  53. package/dist/project/node-project.d.ts +2 -0
  54. package/dist/project/node-project.js +154 -25
  55. package/dist/project/node-project.js.map +1 -1
  56. package/dist/project/site-loader.js +2 -1
  57. package/dist/project/site-loader.js.map +1 -1
  58. package/dist/project/uint8array-base64.d.ts +7 -0
  59. package/dist/project/uint8array-base64.js +96 -0
  60. package/dist/project/uint8array-base64.js.map +1 -0
  61. package/dist/project/workers-globals-module.d.ts +1 -0
  62. package/dist/project/workers-globals-module.js +3 -0
  63. package/dist/project/workers-globals-module.js.map +1 -1
  64. package/dist/project/wrangler.js +8 -9
  65. package/dist/project/wrangler.js.map +1 -1
  66. package/dist/ws/upgrade.js +18 -1
  67. package/dist/ws/upgrade.js.map +1 -1
  68. package/package.json +10 -10
package/README.md CHANGED
@@ -36,7 +36,7 @@ worker is started that way (checked with a Vite build, see `scripts/pack-check.m
36
36
 
37
37
  | Import | What it gives |
38
38
  |---|---|
39
- | `@statewalker/cellb` | the runtime (`createCellRuntime`, `createProjectRuntime` with `loadProjectBindings`), `DurableObject` and `WorkerEntrypoint`, the ports and `CellStorage`, `celldBucket`, the object stores (`s3ObjectStore`, `memoryObjectStore`, `filesApiObjectStore`), `filesApiLtxLog`, `filesApiChangeLog`, `filesApiBackup`, `wasmMemoryOpener` (in-memory SQLite WASM), links over ports, `CellRouter`, `createShardWorker`, the WebSocket classes, and the errors embedders catch |
39
+ | `@statewalker/cellb` | the runtime (`createCellRuntime`, `createProjectRuntime` with `loadProjectBindings`), `DurableObject` and `WorkerEntrypoint`, the ports and `CellStorage`, `celldBucket`, the object stores (`s3ObjectStore`, `memoryObjectStore`, `filesApiObjectStore`), `filesApiLtxLog`, `filesApiChangeLog`, `filesApiBackup`, `wasmMemoryOpener` (in-memory SQLite WASM), links over ports, `CellRouter`, `createShardWorker`, the WebSocket classes (and `withUpgrade`, for an upgrade request a browser can make), and the errors embedders catch |
40
40
  | `@statewalker/cellb/node` | `loadProject` and `buildProject` (the `cellb build` command), `nodeSqliteOpener`, `sqliteObjectStore` (celld dev's local bucket), `loadNodeProject`, `applyNodeD1Migrations`, and celld's peer protocol (`startCelldPeerServer`, `celldPeers`) |
41
41
  | `@statewalker/cellb/browser` | `loadProject` (a `cellb build` folder by URL, or sources with a compiler), `browserProjectRuntime`, `startProjectShards`, `joinProjectShards` and `serveProjectShard` (shards in workers and tabs), `installProjectRouter` (the ServiceWorker side), `sahpoolOpener`, `idbObjectStore`, `serviceWorkerConnector` |
42
42
  | `@statewalker/cellb/tabs` | the tab side and the ServiceWorker side of a project shared by the tabs (`joinProjectShards`, `installProjectRouter`, `shardOwner`) without the shard runtime: what a page and its ServiceWorker import |
@@ -0,0 +1,7 @@
1
+ /**
2
+ * `names`: the loaded Worker's modules. Only a specifier that names one of them is listed (a
3
+ * relative path the bundler links): any other `require()` falls back to the realm's own, or throws
4
+ * when it is called, so `try { require("optional") } catch {}` works as in CommonJS; any other
5
+ * `import()` goes through the realm's resolution (the loaders' module registry).
6
+ */
7
+ export declare function commonJsModule(source: string, name: string, names?: ReadonlySet<string>): string;
@@ -0,0 +1,96 @@
1
+ /**
2
+ * A Worker Loader `{ cjs }` module as an ES module, run as workerd runs CommonJS: not in strict
3
+ * mode unless it says so, `this` is `module.exports`, and importing it gives `module.exports` as
4
+ * the default export.
5
+ *
6
+ * An ES module's own code is always strict, so the CommonJS code runs through `Function`. Code
7
+ * there is invisible to the bundler (esbuild on Node, the Blob-URL loader in a browser), and its
8
+ * `import()` would resolve against the wrong module. So the code is parsed first (acorn), and each
9
+ * `import("x")` and `require("x")` with a literal specifier is listed in the ES module itself,
10
+ * where the bundler sees it: `import()` calls go to `__cellbImport`, which runs the listed
11
+ * `import("x")`, and `require("x")` returns the listed static import (a CommonJS module's
12
+ * `module.exports`, an ES module's namespace). Other specifiers fall back to `import()` and the
13
+ * realm's own `require`.
14
+ */
15
+ import { parse } from "acorn";
16
+ import { resolveModuleName } from "./module-registry.js";
17
+ /** Marks the namespace of a module made here: `require()` of it gives its `module.exports`. */
18
+ const CJS_MARK = "__cellbCommonJs";
19
+ function scan(source) {
20
+ let ast;
21
+ try {
22
+ ast = parse(source, {
23
+ ecmaVersion: "latest",
24
+ sourceType: "script",
25
+ allowReturnOutsideFunction: true,
26
+ allowHashBang: true,
27
+ });
28
+ }
29
+ catch {
30
+ return undefined; // `Function` reports the syntax error when the module is evaluated
31
+ }
32
+ const found = { imports: [], requires: new Set() };
33
+ const literal = (n) => n?.type === "Literal" && typeof n.value === "string" ? n.value : undefined;
34
+ const visit = (n) => {
35
+ if (!n || typeof n.type !== "string")
36
+ return;
37
+ if (n.type === "ImportExpression")
38
+ found.imports.push({ spec: literal(n.source), at: n.start });
39
+ if (n.type === "CallExpression" &&
40
+ n.callee?.type === "Identifier" &&
41
+ n.callee.name === "require" &&
42
+ n.arguments.length === 1) {
43
+ const spec = literal(n.arguments[0]);
44
+ if (spec !== undefined)
45
+ found.requires.add(spec);
46
+ }
47
+ for (const key of Object.keys(n)) {
48
+ const v = n[key];
49
+ if (Array.isArray(v))
50
+ for (const c of v)
51
+ visit(c);
52
+ else if (v && typeof v === "object" && typeof v.type === "string")
53
+ visit(v);
54
+ }
55
+ };
56
+ visit(ast);
57
+ return found;
58
+ }
59
+ /**
60
+ * `names`: the loaded Worker's modules. Only a specifier that names one of them is listed (a
61
+ * relative path the bundler links): any other `require()` falls back to the realm's own, or throws
62
+ * when it is called, so `try { require("optional") } catch {}` works as in CommonJS; any other
63
+ * `import()` goes through the realm's resolution (the loaders' module registry).
64
+ */
65
+ export function commonJsModule(source, name, names) {
66
+ const found = scan(source) ?? { imports: [], requires: new Set() };
67
+ const listed = (spec) => spec !== undefined &&
68
+ /^\.{1,2}\//.test(spec) &&
69
+ (!names || resolveModuleName(spec, name, names) !== undefined);
70
+ // Each `import(…)` keyword becomes `__cellbImport` (same length: positions stay valid).
71
+ let body = source;
72
+ for (const { at } of [...found.imports].sort((a, b) => b.at - a.at))
73
+ body = `${body.slice(0, at)}__cellbImport${body.slice(at + "import".length)}`;
74
+ const q = JSON.stringify;
75
+ const requires = [...found.requires].filter(listed);
76
+ const imports = [...new Set(found.imports.map((i) => i.spec).filter(listed))];
77
+ const lines = [
78
+ ...requires.map((spec, i) => `import * as __cellbDep${i} from ${q(spec)};`),
79
+ `const __cellbRequired = { ${requires.map((spec, i) => `${q(spec)}: __cellbDep${i}`).join(", ")} };`,
80
+ `const __cellbImported = { ${imports.map((spec) => `${q(spec)}: () => import(${q(spec)})`).join(", ")} };`,
81
+ "const __cellbImport = (spec) => (__cellbImported[spec] ?? (() => import(spec)))();",
82
+ "const __cellbRequire = (spec) => {",
83
+ " const ns = __cellbRequired[spec];",
84
+ ` if (ns) return ns.${CJS_MARK} ? ns.default : ns;`,
85
+ ' if (typeof require === "function") return require(spec);',
86
+ " throw new Error(`Cannot find module '${spec}'`);",
87
+ "};",
88
+ "const module = { exports: {} };",
89
+ `new Function("module", "exports", "require", "__filename", "__dirname", "__cellbImport", ${q(`${body}\n//# sourceURL=${name.replace(/\s/g, "%20")}`)})`,
90
+ ` .call(module.exports, module, module.exports, __cellbRequire, ${q(name)}, "/", __cellbImport);`,
91
+ `export const ${CJS_MARK} = true;`,
92
+ "export default module.exports;",
93
+ ];
94
+ return lines.join("\n");
95
+ }
96
+ //# sourceMappingURL=cjs-module.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cjs-module.js","sourceRoot":"","sources":["../../src/bindings/cjs-module.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * A Worker Loader `{ cjs }` module as an ES module, run as workerd runs CommonJS: not in strict\n * mode unless it says so, `this` is `module.exports`, and importing it gives `module.exports` as\n * the default export.\n *\n * An ES module's own code is always strict, so the CommonJS code runs through `Function`. Code\n * there is invisible to the bundler (esbuild on Node, the Blob-URL loader in a browser), and its\n * `import()` would resolve against the wrong module. So the code is parsed first (acorn), and each\n * `import(\"x\")` and `require(\"x\")` with a literal specifier is listed in the ES module itself,\n * where the bundler sees it: `import()` calls go to `__cellbImport`, which runs the listed\n * `import(\"x\")`, and `require(\"x\")` returns the listed static import (a CommonJS module's\n * `module.exports`, an ES module's namespace). Other specifiers fall back to `import()` and the\n * realm's own `require`.\n */\nimport { type Node, parse } from \"acorn\";\nimport { resolveModuleName } from \"./module-registry.js\";\n\n/** Marks the namespace of a module made here: `require()` of it gives its `module.exports`. */\nconst CJS_MARK = \"__cellbCommonJs\";\n\ninterface Scan {\n /** Literal specifiers of `import()` calls, and where each call's `import` keyword starts. */\n imports: { spec?: string; at: number }[];\n /** Literal specifiers of `require()` calls. */\n requires: Set<string>;\n}\n\nfunction scan(source: string): Scan | undefined {\n let ast: Node;\n try {\n ast = parse(source, {\n ecmaVersion: \"latest\",\n sourceType: \"script\",\n allowReturnOutsideFunction: true,\n allowHashBang: true,\n });\n } catch {\n return undefined; // `Function` reports the syntax error when the module is evaluated\n }\n const found: Scan = { imports: [], requires: new Set() };\n const literal = (n: any) =>\n n?.type === \"Literal\" && typeof n.value === \"string\" ? (n.value as string) : undefined;\n const visit = (n: any) => {\n if (!n || typeof n.type !== \"string\") return;\n if (n.type === \"ImportExpression\") found.imports.push({ spec: literal(n.source), at: n.start });\n if (\n n.type === \"CallExpression\" &&\n n.callee?.type === \"Identifier\" &&\n n.callee.name === \"require\" &&\n n.arguments.length === 1\n ) {\n const spec = literal(n.arguments[0]);\n if (spec !== undefined) found.requires.add(spec);\n }\n for (const key of Object.keys(n)) {\n const v = n[key];\n if (Array.isArray(v)) for (const c of v) visit(c);\n else if (v && typeof v === \"object\" && typeof v.type === \"string\") visit(v);\n }\n };\n visit(ast);\n return found;\n}\n\n/**\n * `names`: the loaded Worker's modules. Only a specifier that names one of them is listed (a\n * relative path the bundler links): any other `require()` falls back to the realm's own, or throws\n * when it is called, so `try { require(\"optional\") } catch {}` works as in CommonJS; any other\n * `import()` goes through the realm's resolution (the loaders' module registry).\n */\nexport function commonJsModule(source: string, name: string, names?: ReadonlySet<string>): string {\n const found = scan(source) ?? { imports: [], requires: new Set<string>() };\n const listed = (spec: string | undefined): spec is string =>\n spec !== undefined &&\n /^\\.{1,2}\\//.test(spec) &&\n (!names || resolveModuleName(spec, name, names) !== undefined);\n // Each `import(…)` keyword becomes `__cellbImport` (same length: positions stay valid).\n let body = source;\n for (const { at } of [...found.imports].sort((a, b) => b.at - a.at))\n body = `${body.slice(0, at)}__cellbImport${body.slice(at + \"import\".length)}`;\n const q = JSON.stringify;\n const requires = [...found.requires].filter(listed);\n const imports = [...new Set(found.imports.map((i) => i.spec).filter(listed))];\n const lines = [\n ...requires.map((spec, i) => `import * as __cellbDep${i} from ${q(spec)};`),\n `const __cellbRequired = { ${requires.map((spec, i) => `${q(spec)}: __cellbDep${i}`).join(\", \")} };`,\n `const __cellbImported = { ${imports.map((spec) => `${q(spec)}: () => import(${q(spec)})`).join(\", \")} };`,\n \"const __cellbImport = (spec) => (__cellbImported[spec] ?? (() => import(spec)))();\",\n \"const __cellbRequire = (spec) => {\",\n \" const ns = __cellbRequired[spec];\",\n ` if (ns) return ns.${CJS_MARK} ? ns.default : ns;`,\n ' if (typeof require === \"function\") return require(spec);',\n \" throw new Error(`Cannot find module '${spec}'`);\",\n \"};\",\n \"const module = { exports: {} };\",\n `new Function(\"module\", \"exports\", \"require\", \"__filename\", \"__dirname\", \"__cellbImport\", ${q(`${body}\\n//# sourceURL=${name.replace(/\\s/g, \"%20\")}`)})`,\n ` .call(module.exports, module, module.exports, __cellbRequire, ${q(name)}, \"/\", __cellbImport);`,\n `export const ${CJS_MARK} = true;`,\n \"export default module.exports;\",\n ];\n return lines.join(\"\\n\");\n}\n"]}
@@ -0,0 +1,33 @@
1
+ /** The name of the loaded module `spec` names from module `importer`, or `undefined`. */
2
+ export declare function resolveModuleName(spec: string, importer: string, names: ReadonlySet<string>): string | undefined;
3
+ /** The same function as source text, for a registry module generated into a bundle. */
4
+ export declare const RESOLVE_MODULE_NAME_SOURCE = "(spec, importer, names) => {\n if (names.has(spec)) return spec;\n if (!/^\\.{0,2}\\//.test(spec)) return undefined;\n const name = new URL(spec, \"file:///\" + importer).pathname.slice(1);\n return names.has(name) ? name : undefined;\n}";
5
+ /** The identifier rewritten calls use. */
6
+ export declare const IMPORT_FROM = "__cellbImportFrom";
7
+ /**
8
+ * `source` (an ES module named `name`) with each `import(spec)` that the bundler cannot link (a
9
+ * computed specifier, or a literal that is not a relative path) calling `__cellbImportFrom(spec,
10
+ * name)`. Returns `undefined` when there is none (or the module does not parse: the loader then
11
+ * reports its syntax error).
12
+ */
13
+ export declare function routeDynamicImports(source: string, name: string): string | undefined;
14
+ /** `prelude` put in front of `source` on its first line (after a hashbang), so lines keep their numbers. */
15
+ export declare function prefixModule(source: string, prelude: string): string;
16
+ /** What an ES module exports by name, and the specifiers it re-exports everything from. */
17
+ export interface ModuleExports {
18
+ names: string[];
19
+ stars: string[];
20
+ }
21
+ /** The exports of ES module `source` (`undefined` if it does not parse). */
22
+ export declare function moduleExports(source: string): ModuleExports | undefined;
23
+ /**
24
+ * Each module's export names, `export *` followed within the modules (as far as they resolve).
25
+ * A module that does not parse, or a star from outside them, adds nothing.
26
+ */
27
+ export declare function exportNamesByModule(modules: Record<string, string>): Record<string, string[]>;
28
+ /**
29
+ * An ES module that stands for loaded module `name` where code imports it by a specifier no
30
+ * bundler saw (an `import()` in code built with `Function`): it takes the bundled module's
31
+ * namespace from the realm's registry (`globalThis.__cellbLoadedModules`) and re-exports it.
32
+ */
33
+ export declare function loadedModuleShim(name: string, exportNames: string[]): string;
@@ -0,0 +1,180 @@
1
+ /**
2
+ * `import()` in a loaded Worker resolves against its own modules, as on workerd: the specifier
3
+ * names a module of the `WorkerCode` exactly (Kitesurf names page modules by their URL), or is
4
+ * a path relative to the importing module. The loaders bundle (esbuild on Node) or link (Blob URLs
5
+ * in a browser) the modules, which handles a literal relative `import("./x.js")`; a computed
6
+ * specifier, or one that is a module's whole name, is resolved at run time instead.
7
+ *
8
+ * `routeDynamicImports` rewrites those `import(spec)` calls of a module to
9
+ * `__cellbImportFrom(spec, "<module name>")`; each loader defines `__cellbImportFrom` with
10
+ * `resolveModuleName` over its own modules, falling back to the realm's `import()`.
11
+ */
12
+ import { parse } from "acorn";
13
+ /** The name of the loaded module `spec` names from module `importer`, or `undefined`. */
14
+ export function resolveModuleName(spec, importer, names) {
15
+ if (names.has(spec))
16
+ return spec;
17
+ if (!/^\.{0,2}\//.test(spec))
18
+ return undefined;
19
+ const name = new URL(spec, `file:///${importer}`).pathname.slice(1);
20
+ return names.has(name) ? name : undefined;
21
+ }
22
+ /** The same function as source text, for a registry module generated into a bundle. */
23
+ export const RESOLVE_MODULE_NAME_SOURCE = `(spec, importer, names) => {
24
+ if (names.has(spec)) return spec;
25
+ if (!/^\\.{0,2}\\//.test(spec)) return undefined;
26
+ const name = new URL(spec, "file:///" + importer).pathname.slice(1);
27
+ return names.has(name) ? name : undefined;
28
+ }`;
29
+ /** The identifier rewritten calls use. */
30
+ export const IMPORT_FROM = "__cellbImportFrom";
31
+ /**
32
+ * `source` (an ES module named `name`) with each `import(spec)` that the bundler cannot link (a
33
+ * computed specifier, or a literal that is not a relative path) calling `__cellbImportFrom(spec,
34
+ * name)`. Returns `undefined` when there is none (or the module does not parse: the loader then
35
+ * reports its syntax error).
36
+ */
37
+ export function routeDynamicImports(source, name) {
38
+ if (!source.includes("import"))
39
+ return undefined;
40
+ let ast;
41
+ try {
42
+ ast = parse(source, { ecmaVersion: "latest", sourceType: "module", allowHashBang: true });
43
+ }
44
+ catch {
45
+ return undefined;
46
+ }
47
+ const calls = [];
48
+ const visit = (n) => {
49
+ if (!n || typeof n.type !== "string")
50
+ return;
51
+ if (n.type === "ImportExpression") {
52
+ const literal = n.source.type === "Literal" && typeof n.source.value === "string"
53
+ ? n.source.value
54
+ : undefined;
55
+ if (literal === undefined || !/^\.{1,2}\//.test(literal))
56
+ calls.push({ start: n.start, sourceEnd: n.source.end });
57
+ }
58
+ for (const key of Object.keys(n)) {
59
+ const v = n[key];
60
+ if (Array.isArray(v))
61
+ for (const c of v)
62
+ visit(c);
63
+ else if (v && typeof v === "object" && typeof v.type === "string")
64
+ visit(v);
65
+ }
66
+ };
67
+ visit(ast);
68
+ if (!calls.length)
69
+ return undefined;
70
+ let out = source;
71
+ // From the end, so earlier positions stay valid: `import(spec` → `__cellbImportFrom(spec, name`.
72
+ for (const { start, sourceEnd } of calls.sort((a, b) => b.start - a.start))
73
+ out =
74
+ out.slice(0, start) +
75
+ IMPORT_FROM +
76
+ out.slice(start + "import".length, sourceEnd) +
77
+ `, ${JSON.stringify(name)}` +
78
+ out.slice(sourceEnd);
79
+ return out;
80
+ }
81
+ /** `prelude` put in front of `source` on its first line (after a hashbang), so lines keep their numbers. */
82
+ export function prefixModule(source, prelude) {
83
+ if (!source.startsWith("#!"))
84
+ return prelude + source;
85
+ const nl = source.indexOf("\n");
86
+ return nl < 0
87
+ ? `${source}\n${prelude}`
88
+ : `${source.slice(0, nl + 1)}${prelude}${source.slice(nl + 1)}`;
89
+ }
90
+ /** The exports of ES module `source` (`undefined` if it does not parse). */
91
+ export function moduleExports(source) {
92
+ let ast;
93
+ try {
94
+ ast = parse(source, { ecmaVersion: "latest", sourceType: "module", allowHashBang: true });
95
+ }
96
+ catch {
97
+ return undefined;
98
+ }
99
+ const names = new Set();
100
+ const stars = [];
101
+ const nameOf = (n) => (n.type === "Identifier" ? n.name : String(n.value));
102
+ const bound = (p) => {
103
+ if (!p)
104
+ return;
105
+ if (p.type === "Identifier")
106
+ names.add(p.name);
107
+ else if (p.type === "ObjectPattern")
108
+ for (const q of p.properties)
109
+ bound(q.type === "RestElement" ? q.argument : q.value);
110
+ else if (p.type === "ArrayPattern")
111
+ for (const q of p.elements)
112
+ bound(q);
113
+ else if (p.type === "RestElement")
114
+ bound(p.argument);
115
+ else if (p.type === "AssignmentPattern")
116
+ bound(p.left);
117
+ };
118
+ for (const node of ast.body) {
119
+ if (node.type === "ExportDefaultDeclaration")
120
+ names.add("default");
121
+ else if (node.type === "ExportAllDeclaration") {
122
+ if (node.exported)
123
+ names.add(nameOf(node.exported));
124
+ else
125
+ stars.push(String(node.source.value));
126
+ }
127
+ else if (node.type === "ExportNamedDeclaration") {
128
+ const d = node.declaration;
129
+ if (d?.type === "VariableDeclaration")
130
+ for (const v of d.declarations)
131
+ bound(v.id);
132
+ else if (d?.id)
133
+ names.add(d.id.name);
134
+ for (const s of node.specifiers ?? [])
135
+ names.add(nameOf(s.exported));
136
+ }
137
+ }
138
+ return { names: [...names], stars };
139
+ }
140
+ /**
141
+ * Each module's export names, `export *` followed within the modules (as far as they resolve).
142
+ * A module that does not parse, or a star from outside them, adds nothing.
143
+ */
144
+ export function exportNamesByModule(modules) {
145
+ const own = new Map();
146
+ for (const [name, text] of Object.entries(modules))
147
+ own.set(name, moduleExports(text) ?? { names: [], stars: [] });
148
+ const names = new Set(own.keys());
149
+ const all = (name, seen) => {
150
+ const out = new Set(own.get(name)?.names ?? []);
151
+ if (seen.has(name))
152
+ return out;
153
+ seen.add(name);
154
+ for (const spec of own.get(name)?.stars ?? []) {
155
+ const target = resolveModuleName(spec, name, names);
156
+ if (target === undefined)
157
+ continue;
158
+ for (const n of all(target, seen))
159
+ if (n !== "default")
160
+ out.add(n);
161
+ }
162
+ return out;
163
+ };
164
+ return Object.fromEntries([...names].map((n) => [n, [...all(n, new Set())]]));
165
+ }
166
+ /**
167
+ * An ES module that stands for loaded module `name` where code imports it by a specifier no
168
+ * bundler saw (an `import()` in code built with `Function`): it takes the bundled module's
169
+ * namespace from the realm's registry (`globalThis.__cellbLoadedModules`) and re-exports it.
170
+ */
171
+ export function loadedModuleShim(name, exportNames) {
172
+ const ident = /^[A-Za-z_$][\w$]*$/;
173
+ const named = exportNames.filter((n) => n !== "default" && ident.test(n));
174
+ return [
175
+ `const ns = await globalThis.__cellbLoadedModules.load(${JSON.stringify(name)});`,
176
+ ...(exportNames.includes("default") ? ["export default ns.default;"] : []),
177
+ ...(named.length ? [`export const { ${named.join(", ")} } = ns;`] : []),
178
+ ].join("\n");
179
+ }
180
+ //# sourceMappingURL=module-registry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"module-registry.js","sourceRoot":"","sources":["../../src/bindings/module-registry.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * `import()` in a loaded Worker resolves against its own modules, as on workerd: the specifier\n * names a module of the `WorkerCode` exactly (Kitesurf names page modules by their URL), or is\n * a path relative to the importing module. The loaders bundle (esbuild on Node) or link (Blob URLs\n * in a browser) the modules, which handles a literal relative `import(\"./x.js\")`; a computed\n * specifier, or one that is a module's whole name, is resolved at run time instead.\n *\n * `routeDynamicImports` rewrites those `import(spec)` calls of a module to\n * `__cellbImportFrom(spec, \"<module name>\")`; each loader defines `__cellbImportFrom` with\n * `resolveModuleName` over its own modules, falling back to the realm's `import()`.\n */\nimport { parse } from \"acorn\";\n\n/** The name of the loaded module `spec` names from module `importer`, or `undefined`. */\nexport function resolveModuleName(\n spec: string,\n importer: string,\n names: ReadonlySet<string>,\n): string | undefined {\n if (names.has(spec)) return spec;\n if (!/^\\.{0,2}\\//.test(spec)) return undefined;\n const name = new URL(spec, `file:///${importer}`).pathname.slice(1);\n return names.has(name) ? name : undefined;\n}\n\n/** The same function as source text, for a registry module generated into a bundle. */\nexport const RESOLVE_MODULE_NAME_SOURCE = `(spec, importer, names) => {\n if (names.has(spec)) return spec;\n if (!/^\\\\.{0,2}\\\\//.test(spec)) return undefined;\n const name = new URL(spec, \"file:///\" + importer).pathname.slice(1);\n return names.has(name) ? name : undefined;\n}`;\n\n/** The identifier rewritten calls use. */\nexport const IMPORT_FROM = \"__cellbImportFrom\";\n\n/**\n * `source` (an ES module named `name`) with each `import(spec)` that the bundler cannot link (a\n * computed specifier, or a literal that is not a relative path) calling `__cellbImportFrom(spec,\n * name)`. Returns `undefined` when there is none (or the module does not parse: the loader then\n * reports its syntax error).\n */\nexport function routeDynamicImports(source: string, name: string): string | undefined {\n if (!source.includes(\"import\")) return undefined;\n let ast: any;\n try {\n ast = parse(source, { ecmaVersion: \"latest\", sourceType: \"module\", allowHashBang: true });\n } catch {\n return undefined;\n }\n const calls: { start: number; sourceEnd: number }[] = [];\n const visit = (n: any) => {\n if (!n || typeof n.type !== \"string\") return;\n if (n.type === \"ImportExpression\") {\n const literal =\n n.source.type === \"Literal\" && typeof n.source.value === \"string\"\n ? (n.source.value as string)\n : undefined;\n if (literal === undefined || !/^\\.{1,2}\\//.test(literal))\n calls.push({ start: n.start, sourceEnd: n.source.end });\n }\n for (const key of Object.keys(n)) {\n const v = n[key];\n if (Array.isArray(v)) for (const c of v) visit(c);\n else if (v && typeof v === \"object\" && typeof v.type === \"string\") visit(v);\n }\n };\n visit(ast);\n if (!calls.length) return undefined;\n let out = source;\n // From the end, so earlier positions stay valid: `import(spec` → `__cellbImportFrom(spec, name`.\n for (const { start, sourceEnd } of calls.sort((a, b) => b.start - a.start))\n out =\n out.slice(0, start) +\n IMPORT_FROM +\n out.slice(start + \"import\".length, sourceEnd) +\n `, ${JSON.stringify(name)}` +\n out.slice(sourceEnd);\n return out;\n}\n\n/** `prelude` put in front of `source` on its first line (after a hashbang), so lines keep their numbers. */\nexport function prefixModule(source: string, prelude: string): string {\n if (!source.startsWith(\"#!\")) return prelude + source;\n const nl = source.indexOf(\"\\n\");\n return nl < 0\n ? `${source}\\n${prelude}`\n : `${source.slice(0, nl + 1)}${prelude}${source.slice(nl + 1)}`;\n}\n\n/** What an ES module exports by name, and the specifiers it re-exports everything from. */\nexport interface ModuleExports {\n names: string[];\n stars: string[];\n}\n\n/** The exports of ES module `source` (`undefined` if it does not parse). */\nexport function moduleExports(source: string): ModuleExports | undefined {\n let ast: any;\n try {\n ast = parse(source, { ecmaVersion: \"latest\", sourceType: \"module\", allowHashBang: true });\n } catch {\n return undefined;\n }\n const names = new Set<string>();\n const stars: string[] = [];\n const nameOf = (n: any) => (n.type === \"Identifier\" ? n.name : String(n.value));\n const bound = (p: any) => {\n if (!p) return;\n if (p.type === \"Identifier\") names.add(p.name);\n else if (p.type === \"ObjectPattern\")\n for (const q of p.properties) bound(q.type === \"RestElement\" ? q.argument : q.value);\n else if (p.type === \"ArrayPattern\") for (const q of p.elements) bound(q);\n else if (p.type === \"RestElement\") bound(p.argument);\n else if (p.type === \"AssignmentPattern\") bound(p.left);\n };\n for (const node of ast.body) {\n if (node.type === \"ExportDefaultDeclaration\") names.add(\"default\");\n else if (node.type === \"ExportAllDeclaration\") {\n if (node.exported) names.add(nameOf(node.exported));\n else stars.push(String(node.source.value));\n } else if (node.type === \"ExportNamedDeclaration\") {\n const d = node.declaration;\n if (d?.type === \"VariableDeclaration\") for (const v of d.declarations) bound(v.id);\n else if (d?.id) names.add(d.id.name);\n for (const s of node.specifiers ?? []) names.add(nameOf(s.exported));\n }\n }\n return { names: [...names], stars };\n}\n\n/**\n * Each module's export names, `export *` followed within the modules (as far as they resolve).\n * A module that does not parse, or a star from outside them, adds nothing.\n */\nexport function exportNamesByModule(modules: Record<string, string>): Record<string, string[]> {\n const own = new Map<string, ModuleExports>();\n for (const [name, text] of Object.entries(modules))\n own.set(name, moduleExports(text) ?? { names: [], stars: [] });\n const names = new Set(own.keys());\n const all = (name: string, seen: Set<string>): Set<string> => {\n const out = new Set(own.get(name)?.names ?? []);\n if (seen.has(name)) return out;\n seen.add(name);\n for (const spec of own.get(name)?.stars ?? []) {\n const target = resolveModuleName(spec, name, names);\n if (target === undefined) continue;\n for (const n of all(target, seen)) if (n !== \"default\") out.add(n);\n }\n return out;\n };\n return Object.fromEntries([...names].map((n) => [n, [...all(n, new Set())]]));\n}\n\n/**\n * An ES module that stands for loaded module `name` where code imports it by a specifier no\n * bundler saw (an `import()` in code built with `Function`): it takes the bundled module's\n * namespace from the realm's registry (`globalThis.__cellbLoadedModules`) and re-exports it.\n */\nexport function loadedModuleShim(name: string, exportNames: string[]): string {\n const ident = /^[A-Za-z_$][\\w$]*$/;\n const named = exportNames.filter((n) => n !== \"default\" && ident.test(n));\n return [\n `const ns = await globalThis.__cellbLoadedModules.load(${JSON.stringify(name)});`,\n ...(exportNames.includes(\"default\") ? [\"export default ns.default;\"] : []),\n ...(named.length ? [`export const { ${named.join(\", \")} } = ns;`] : []),\n ].join(\"\\n\");\n}\n"]}
@@ -6,9 +6,11 @@ export interface WorkerCode {
6
6
  modules: Record<string, string | {
7
7
  js?: string;
8
8
  esModule?: string;
9
+ cjs?: string;
9
10
  text?: string;
10
11
  json?: unknown;
11
- wasm?: BufferSource;
12
+ /** The bytes, or (from `compileWasmModules`) the compiled module. */
13
+ wasm?: BufferSource | WebAssembly.Module;
12
14
  } | {
13
15
  data: BufferSource;
14
16
  }>;
@@ -44,9 +46,25 @@ export interface TailEvent {
44
46
  }
45
47
  /**
46
48
  * One `WorkerCode.modules` entry as ES module source; text and JSON become a default export,
47
- * wasm bytes a compiled `WebAssembly.Module`.
49
+ * wasm bytes a compiled `WebAssembly.Module`, and a CommonJS module's `module.exports` the
50
+ * default export. `name` names a CommonJS module in stack traces.
48
51
  */
49
- export declare function moduleText(entry: WorkerCode["modules"][string]): string;
52
+ export declare function moduleText(entry: WorkerCode["modules"][string], name?: string, names?: ReadonlySet<string>): string;
53
+ /** The realm's table of compiled wasm modules of loaded code, by key. */
54
+ export declare const WASM_TABLE = "__cellsWasmModules";
55
+ /** The key of `mod` in this realm's wasm table (registered on first use). */
56
+ export declare function wasmModuleKey(mod: WebAssembly.Module): string;
57
+ /** Puts compiled modules in this realm's wasm table (an isolate receives its loader's). */
58
+ export declare function registerWasmModules(modules: Record<string, WebAssembly.Module>): void;
59
+ /** The table entries the wasm modules of `code` use (what an isolate needs of this realm's table). */
60
+ export declare function wasmModulesOf(code: WorkerCode): Record<string, WebAssembly.Module>;
61
+ /**
62
+ * `code` with each `wasm` module compiled (asynchronously, once per bytes object: Kitesurf hands
63
+ * the same ArrayBuffer to every page's Worker), so loaders put a compiled module in the realm's
64
+ * table instead of the bytes as base64 text, which a large module makes slow to build, parse,
65
+ * decode and compile synchronously on every load.
66
+ */
67
+ export declare function compileWasmModules(code: WorkerCode): Promise<WorkerCode>;
50
68
  /**
51
69
  * The `fetch` loaded code sees, from `WorkerCode.globalOutbound`: omitted → the loader's own
52
70
  * (undefined: no override); `null` → every fetch throws; a Fetcher → each fetch goes through it.
@@ -70,7 +88,7 @@ export declare function workerLoader(load: ModuleLoader, opts?: {
70
88
  dispose(): void;
71
89
  getEntrypoint(name?: string, options?: {
72
90
  props?: unknown;
73
- }): {};
91
+ }): Record<string, unknown>;
74
92
  };
75
93
  get(id: string, getCode: () => WorkerCode | Promise<WorkerCode>, cached?: boolean): {
76
94
  getDurableObjectClass(name: string): import("../core/handles.js").ClassHandle;
@@ -78,6 +96,6 @@ export declare function workerLoader(load: ModuleLoader, opts?: {
78
96
  dispose(): void;
79
97
  getEntrypoint(name?: string, options?: {
80
98
  props?: unknown;
81
- }): {};
99
+ }): Record<string, unknown>;
82
100
  };
83
101
  };
@@ -1,14 +1,20 @@
1
- import { classHandle, cross, ctxExports, entrypointStub, ISOLATED, pipeTo, REFUSE, rpcPromise, } from "../core/handles.js";
1
+ import { classHandle, cross, ctxExports, entrypointStub, ISOLATED, ON_ABORT, pipeTo, REFUSE, rpcPromise, } from "../core/handles.js";
2
+ import { commonJsModule } from "./cjs-module.js";
2
3
  /** workerd's refusal of a loaded Worker's entrypoint passed on to another Worker. */
3
4
  const DYNAMIC_ENTRYPOINT = "Entrypoints to dynamically-loaded workers cannot be transferred to other Workers, because the system does not know how to reload this Worker from scratch. Instead, have the parent Worker expose an entrypoint which constructs the dynamic worker and forwards to it.";
4
5
  /**
5
6
  * One `WorkerCode.modules` entry as ES module source; text and JSON become a default export,
6
- * wasm bytes a compiled `WebAssembly.Module`.
7
+ * wasm bytes a compiled `WebAssembly.Module`, and a CommonJS module's `module.exports` the
8
+ * default export. `name` names a CommonJS module in stack traces.
7
9
  */
8
- export function moduleText(entry) {
10
+ export function moduleText(entry, name = "module.cjs", names) {
9
11
  if (typeof entry === "string")
10
12
  return entry;
11
13
  const m = entry; // workerLoader refuses data
14
+ if (typeof m.cjs === "string")
15
+ return commonJsModule(m.cjs, name, names);
16
+ if (m.wasm instanceof WebAssembly.Module)
17
+ return `export default globalThis.${WASM_TABLE}[${JSON.stringify(wasmModuleKey(m.wasm))}];`;
12
18
  if (m.wasm) {
13
19
  const bytes = ArrayBuffer.isView(m.wasm)
14
20
  ? new Uint8Array(m.wasm.buffer, m.wasm.byteOffset, m.wasm.byteLength)
@@ -20,6 +26,60 @@ export function moduleText(entry) {
20
26
  }
21
27
  return m.js ?? m.esModule ?? `export default ${JSON.stringify(m.text ?? m.json)};`;
22
28
  }
29
+ /** The realm's table of compiled wasm modules of loaded code, by key. */
30
+ export const WASM_TABLE = "__cellsWasmModules";
31
+ const keys = new WeakMap();
32
+ let lastKey = 0;
33
+ /** The key of `mod` in this realm's wasm table (registered on first use). */
34
+ export function wasmModuleKey(mod) {
35
+ let key = keys.get(mod);
36
+ if (!key) {
37
+ key = `w${++lastKey}`;
38
+ keys.set(mod, key);
39
+ registerWasmModules({ [key]: mod });
40
+ }
41
+ return key;
42
+ }
43
+ /** Puts compiled modules in this realm's wasm table (an isolate receives its loader's). */
44
+ export function registerWasmModules(modules) {
45
+ const g = globalThis;
46
+ g[WASM_TABLE] ??= {};
47
+ Object.assign(g[WASM_TABLE], modules);
48
+ }
49
+ /** The table entries the wasm modules of `code` use (what an isolate needs of this realm's table). */
50
+ export function wasmModulesOf(code) {
51
+ const out = {};
52
+ for (const m of Object.values(code.modules))
53
+ if (typeof m === "object" && "wasm" in m && m.wasm instanceof WebAssembly.Module)
54
+ out[wasmModuleKey(m.wasm)] = m.wasm;
55
+ return out;
56
+ }
57
+ const compiled = new WeakMap();
58
+ /**
59
+ * `code` with each `wasm` module compiled (asynchronously, once per bytes object: Kitesurf hands
60
+ * the same ArrayBuffer to every page's Worker), so loaders put a compiled module in the realm's
61
+ * table instead of the bytes as base64 text, which a large module makes slow to build, parse,
62
+ * decode and compile synchronously on every load.
63
+ */
64
+ export async function compileWasmModules(code) {
65
+ let modules;
66
+ for (const [name, m] of Object.entries(code.modules)) {
67
+ if (typeof m !== "object" || !("wasm" in m) || !m.wasm)
68
+ continue;
69
+ if (m.wasm instanceof WebAssembly.Module)
70
+ continue;
71
+ const bytes = m.wasm;
72
+ let p = compiled.get(bytes);
73
+ if (!p) {
74
+ p = WebAssembly.compile(bytes);
75
+ compiled.set(bytes, p);
76
+ p.catch(() => compiled.delete(bytes));
77
+ }
78
+ modules ??= { ...code.modules };
79
+ modules[name] = { ...m, wasm: await p };
80
+ }
81
+ return modules ? { ...code, modules } : code;
82
+ }
23
83
  /**
24
84
  * The `fetch` loaded code sees, from `WorkerCode.globalOutbound`: omitted → the loader's own
25
85
  * (undefined: no override); `null` → every fetch throws; a Fetcher → each fetch goes through it.
@@ -64,6 +124,13 @@ export function workerLoader(load, opts = {}) {
64
124
  const logs = [];
65
125
  const sink = (level, message) => logs.push({ level, message, timestamp: Date.now() });
66
126
  const module = await load(id, code, sink);
127
+ // Its isolate ended (`abortIsolate()`): the next get() of this id loads it again.
128
+ module[ON_ABORT]?.(() => {
129
+ if (cache.get(id) === p)
130
+ cache.delete(id);
131
+ if (own === p)
132
+ own = undefined;
133
+ });
67
134
  const env = code.env ?? {};
68
135
  return { module, env, exports: ctxExports(module, env), code, logs };
69
136
  })();
@@ -152,12 +219,15 @@ export function workerLoader(load, opts = {}) {
152
219
  refused.catch(() => { });
153
220
  return refused;
154
221
  }
155
- return new Proxy({}, {
222
+ const entrypoint = new Proxy({}, {
156
223
  get(_t, prop) {
157
224
  if (prop === REFUSE)
158
225
  return DYNAMIC_ENTRYPOINT;
159
226
  if (prop === "then" || typeof prop === "symbol")
160
227
  return undefined;
228
+ // workerd's `dup()`: this entrypoint holds nothing to release, so it is itself.
229
+ if (prop === "dup")
230
+ return () => entrypoint;
161
231
  return (...args) => {
162
232
  // Boxed, so awaiting the load does not await the call: calls on its
163
233
  // RpcPromise are pipelined.
@@ -174,6 +244,7 @@ export function workerLoader(load, opts = {}) {
174
244
  };
175
245
  },
176
246
  });
247
+ return entrypoint;
177
248
  },
178
249
  };
179
250
  },