@statewalker/cellb 0.8.7 → 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 (70) hide show
  1. package/README.md +1 -1
  2. package/dist/adapters/node-sqlite.js +42 -13
  3. package/dist/adapters/node-sqlite.js.map +1 -1
  4. package/dist/bindings/cjs-module.d.ts +7 -0
  5. package/dist/bindings/cjs-module.js +96 -0
  6. package/dist/bindings/cjs-module.js.map +1 -0
  7. package/dist/bindings/module-registry.d.ts +33 -0
  8. package/dist/bindings/module-registry.js +180 -0
  9. package/dist/bindings/module-registry.js.map +1 -0
  10. package/dist/bindings/worker-loader.d.ts +23 -5
  11. package/dist/bindings/worker-loader.js +75 -4
  12. package/dist/bindings/worker-loader.js.map +1 -1
  13. package/dist/core/handles.d.ts +21 -1
  14. package/dist/core/handles.js +49 -3
  15. package/dist/core/handles.js.map +1 -1
  16. package/dist/core/namespace.js +4 -1
  17. package/dist/core/namespace.js.map +1 -1
  18. package/dist/core/runtime.d.ts +8 -0
  19. package/dist/core/runtime.js +3 -3
  20. package/dist/core/runtime.js.map +1 -1
  21. package/dist/index.d.ts +5 -0
  22. package/dist/index.js +5 -0
  23. package/dist/index.js.map +1 -1
  24. package/dist/isolate/browser.d.ts +1 -1
  25. package/dist/isolate/browser.js +3 -1
  26. package/dist/isolate/browser.js.map +1 -1
  27. package/dist/isolate/host.js +20 -2
  28. package/dist/isolate/host.js.map +1 -1
  29. package/dist/isolate/isolate-worker.js +13 -1
  30. package/dist/isolate/isolate-worker.js.map +1 -1
  31. package/dist/isolate/isolate.js +11 -0
  32. package/dist/isolate/isolate.js.map +1 -1
  33. package/dist/isolate/node-entry.js +33 -0
  34. package/dist/isolate/node-entry.js.map +1 -1
  35. package/dist/isolate/protocol.d.ts +1 -0
  36. package/dist/isolate/protocol.js +228 -13
  37. package/dist/isolate/protocol.js.map +1 -1
  38. package/dist/isolate/writable-globals.d.ts +9 -0
  39. package/dist/isolate/writable-globals.js +45 -0
  40. package/dist/isolate/writable-globals.js.map +1 -0
  41. package/dist/node-compat/bundle-files.d.ts +26 -0
  42. package/dist/node-compat/bundle-files.js +210 -0
  43. package/dist/node-compat/bundle-files.js.map +1 -0
  44. package/dist/node-compat/fs.d.ts +13 -1
  45. package/dist/node-compat/fs.js +54 -15
  46. package/dist/node-compat/fs.js.map +1 -1
  47. package/dist/project/browser-modules.d.ts +1 -1
  48. package/dist/project/browser-modules.js +25 -5
  49. package/dist/project/browser-modules.js.map +1 -1
  50. package/dist/project/cloudflare-workers.d.ts +9 -0
  51. package/dist/project/cloudflare-workers.js +14 -0
  52. package/dist/project/cloudflare-workers.js.map +1 -1
  53. package/dist/project/module-rules.js +31 -10
  54. package/dist/project/module-rules.js.map +1 -1
  55. package/dist/project/node-project.d.ts +2 -0
  56. package/dist/project/node-project.js +154 -25
  57. package/dist/project/node-project.js.map +1 -1
  58. package/dist/project/site-loader.js +2 -1
  59. package/dist/project/site-loader.js.map +1 -1
  60. package/dist/project/uint8array-base64.d.ts +7 -0
  61. package/dist/project/uint8array-base64.js +96 -0
  62. package/dist/project/uint8array-base64.js.map +1 -0
  63. package/dist/project/workers-globals-module.d.ts +1 -0
  64. package/dist/project/workers-globals-module.js +3 -0
  65. package/dist/project/workers-globals-module.js.map +1 -1
  66. package/dist/project/wrangler.js +8 -9
  67. package/dist/project/wrangler.js.map +1 -1
  68. package/dist/ws/upgrade.js +18 -1
  69. package/dist/ws/upgrade.js.map +1 -1
  70. 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 |
@@ -22,18 +22,35 @@ export class NodeSqliteDriver {
22
22
  this.#temporary = !!opts.temporary && path !== ":memory:";
23
23
  this.#exclusive = !!opts.exclusive;
24
24
  this.#pageCapture = !!opts.pageCapture && path !== ":memory:";
25
+ this.#db = this.#connect();
26
+ this.#walSetup();
27
+ }
28
+ /** Extensions loaded so far, by name: a new connection (restore()) loads them again. */
29
+ #extensions = [];
30
+ /** The directory restore() made for an in-memory database's file. */
31
+ #restoredDir;
32
+ /** A connection to `#path`, set up as every connection of this driver is. */
33
+ #connect() {
25
34
  // As celld and workerd: a double-quoted name that is no column is a string literal.
26
- this.#db = new DatabaseSync(path, {
35
+ const db = new DatabaseSync(this.#path, {
27
36
  allowExtension: true,
28
37
  enableDoubleQuotedStringLiterals: true,
29
38
  });
30
- this.#db.enableLoadExtension(false); // only loadExtension() below may load, by name
39
+ db.enableLoadExtension(false); // only loadExtension() below may load, by name
31
40
  // EXCLUSIVE locking: the first write takes an OS lock on the file that is
32
41
  // kept until close (or process death) — a second process can neither read
33
42
  // nor write the cell. Storage-level fencing on one host, no lease needed.
34
- if (opts.exclusive)
35
- this.#db.exec("PRAGMA locking_mode = EXCLUSIVE; PRAGMA busy_timeout = 0;");
36
- this.#walSetup();
43
+ if (this.#exclusive)
44
+ db.exec("PRAGMA locking_mode = EXCLUSIVE; PRAGMA busy_timeout = 0;");
45
+ try {
46
+ for (const name of this.#extensions)
47
+ this.#load(db, name);
48
+ }
49
+ catch (e) {
50
+ db.close(); // not left open behind the error
51
+ throw e;
52
+ }
53
+ return db;
37
54
  }
38
55
  // ---- page capture through the WAL (what the WASM driver does with its xWrite hook) ----
39
56
  // In WAL mode every commit appends its changed pages to `<db>-wal` as frames (24-byte frame
@@ -171,6 +188,11 @@ export class NodeSqliteDriver {
171
188
  }
172
189
  close() {
173
190
  this.#db.close();
191
+ if (this.#restoredDir) {
192
+ rmSync(this.#restoredDir, { recursive: true, force: true });
193
+ temporaryDirs.delete(this.#restoredDir);
194
+ this.#restoredDir = undefined;
195
+ }
174
196
  if (!this.#temporary)
175
197
  return;
176
198
  for (const suffix of ["", "-wal", "-shm", "-journal"])
@@ -215,15 +237,20 @@ export class NodeSqliteDriver {
215
237
  }
216
238
  /** Known extensions only: "sqlite_vec" → the sqlite-vec npm package's loadable library. */
217
239
  loadExtension(name) {
240
+ this.#load(this.#db, name);
241
+ if (!this.#extensions.includes(name))
242
+ this.#extensions.push(name);
243
+ }
244
+ #load(db, name) {
218
245
  const path = KNOWN_EXTENSIONS[name]?.();
219
246
  if (!path)
220
247
  throw new Error(`Unknown SQLite extension "${name}"`);
221
- this.#db.enableLoadExtension(true);
248
+ db.enableLoadExtension(true);
222
249
  try {
223
- this.#db.loadExtension(path);
250
+ db.loadExtension(path);
224
251
  }
225
252
  finally {
226
- this.#db.enableLoadExtension(false);
253
+ db.enableLoadExtension(false);
227
254
  }
228
255
  }
229
256
  /**
@@ -256,14 +283,16 @@ export class NodeSqliteDriver {
256
283
  /** Replaces the database file with `image` and reopens it (an in-memory DB becomes file-backed). */
257
284
  restore(image) {
258
285
  this.#db.close();
259
- if (this.#path === ":memory:")
260
- this.#path = join(mkdtempSync(join(tmpdir(), "cell-mem-")), "db.sqlite");
286
+ if (this.#path === ":memory:") {
287
+ // A directory of its own, removed with the driver (close() or process exit).
288
+ this.#restoredDir = mkdtempSync(join(tmpdir(), "cell-mem-"));
289
+ removeAtExit(this.#restoredDir);
290
+ this.#path = join(this.#restoredDir, "db.sqlite");
291
+ }
261
292
  for (const suffix of ["-wal", "-shm", "-journal"])
262
293
  rmSync(this.#path + suffix, { force: true });
263
294
  writeFileSync(this.#path, image);
264
- this.#db = new DatabaseSync(this.#path, { enableDoubleQuotedStringLiterals: true });
265
- if (this.#exclusive)
266
- this.#db.exec("PRAGMA locking_mode = EXCLUSIVE; PRAGMA busy_timeout = 0;");
295
+ this.#db = this.#connect(); // with the extensions loaded before
267
296
  this.#walSetup(); // a new file: a new WAL
268
297
  }
269
298
  }
@@ -1 +1 @@
1
- {"version":3,"file":"node-sqlite.js","sourceRoot":"","sources":["../../src/adapters/node-sqlite.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n closeSync,\n existsSync,\n fstatSync,\n mkdtempSync,\n openSync,\n readFileSync,\n readSync,\n rmdirSync,\n rmSync,\n statSync,\n writeFileSync,\n} from \"node:fs\";\nimport { createRequire } from \"node:module\";\nimport { tmpdir } from \"node:os\";\nimport { dirname, join } from \"node:path\";\nimport type { DatabaseSync as DatabaseSyncT } from \"node:sqlite\";\n\nimport { splitSqlStatements } from \"../core/sql-split.js\";\nimport type { OpenCellSql, SqlResult, SyncSqlDriver } from \"../ports.js\";\nimport { requireNodeSqlite } from \"./node-sqlite-module.js\";\n\nconst { DatabaseSync } = requireNodeSqlite();\n\nconst requireCjs = createRequire(import.meta.url);\nconst KNOWN_EXTENSIONS: Record<string, () => string> = {\n sqlite_vec: () => (requireCjs(\"sqlite-vec\") as { getLoadablePath(): string }).getLoadablePath(),\n};\n\n/** Server-side SyncSqlDriver over node:sqlite (Node >= 22.13). */\nexport class NodeSqliteDriver implements SyncSqlDriver {\n #db: DatabaseSyncT;\n #path: string;\n readonly #exclusive: boolean;\n readonly #pageCapture: boolean;\n /** A file of its own that close() removes (a cell \"in memory\" that needs a WAL). */\n readonly #temporary: boolean;\n constructor(\n path = \":memory:\",\n opts: { exclusive?: boolean; pageCapture?: boolean; temporary?: boolean } = {},\n ) {\n this.#path = path;\n this.#temporary = !!opts.temporary && path !== \":memory:\";\n this.#exclusive = !!opts.exclusive;\n this.#pageCapture = !!opts.pageCapture && path !== \":memory:\";\n // As celld and workerd: a double-quoted name that is no column is a string literal.\n this.#db = new DatabaseSync(path, {\n allowExtension: true,\n enableDoubleQuotedStringLiterals: true,\n });\n this.#db.enableLoadExtension(false); // only loadExtension() below may load, by name\n // EXCLUSIVE locking: the first write takes an OS lock on the file that is\n // kept until close (or process death) — a second process can neither read\n // nor write the cell. Storage-level fencing on one host, no lease needed.\n if (opts.exclusive) this.#db.exec(\"PRAGMA locking_mode = EXCLUSIVE; PRAGMA busy_timeout = 0;\");\n this.#walSetup();\n }\n\n // ---- page capture through the WAL (what the WASM driver does with its xWrite hook) ----\n // In WAL mode every commit appends its changed pages to `<db>-wal` as frames (24-byte frame\n // header + page; a commit frame carries the database size in pages). Reading the frames\n // appended since the last read gives exactly the committed transaction's pages, O(changed),\n // instead of reading the whole file. Automatic checkpoints are off; take() checkpoints\n // (TRUNCATE) itself once the WAL grows, and restarts from its start.\n #walOffset = 0;\n /** Whether anyone reads the captured pages; until then exec() keeps the WAL bounded itself. */\n #taken = false;\n #writes = 0;\n #walSetup(): void {\n if (!this.#pageCapture) return;\n this.#db.exec(\"PRAGMA journal_mode = WAL; PRAGMA wal_autocheckpoint = 0;\");\n // A temporary file is only the WAL's carrier (the bucket keeps the cell): no fsync per commit.\n if (this.#temporary) this.#db.exec(\"PRAGMA synchronous = OFF\");\n this.#db.exec(\"PRAGMA wal_checkpoint(TRUNCATE)\");\n this.#walOffset = 0;\n }\n /** The pages of the transactions committed since the previous call (WAL frames). */\n takeDirtyPages():\n | { pageSize: number; pageCount: number; pages: Map<number, Uint8Array> }\n | undefined {\n if (!this.#pageCapture) return undefined;\n this.#taken = true;\n const walPath = `${this.#path}-wal`;\n let fd: number;\n try {\n fd = openSync(walPath, \"r\");\n } catch {\n return undefined;\n }\n try {\n const size = fstatSync(fd).size;\n if (size < 32) return undefined;\n const header = Buffer.alloc(32);\n readSync(fd, header, 0, 32, 0);\n const pageSize = header.readUInt32BE(8) === 1 ? 65536 : header.readUInt32BE(8);\n const [salt1, salt2] = [header.readUInt32BE(16), header.readUInt32BE(20)];\n const frameSize = 24 + pageSize;\n let offset = Math.max(32, this.#walOffset);\n const pending = new Map<number, Uint8Array>();\n const pages = new Map<number, Uint8Array>();\n let pageCount = 0;\n let committedTo = offset;\n const frame = Buffer.alloc(frameSize);\n while (offset + frameSize <= size) {\n readSync(fd, frame, 0, frameSize, offset);\n if (frame.readUInt32BE(8) !== salt1 || frame.readUInt32BE(12) !== salt2) break; // stale frames\n pending.set(frame.readUInt32BE(0), new Uint8Array(frame.subarray(24)));\n offset += frameSize;\n const commit = frame.readUInt32BE(4);\n if (commit) {\n for (const [p, d] of pending) pages.set(p, d);\n pending.clear();\n pageCount = commit;\n committedTo = offset;\n }\n }\n this.#walOffset = committedTo;\n if (!pageCount)\n return {\n pageSize,\n pageCount: Number(this.exec(\"PRAGMA page_count\", []).rows[0][0]),\n pages,\n };\n for (const p of [...pages.keys()]) if (p > pageCount) pages.delete(p);\n // keep the WAL bounded: fold it into the database and start over\n if (committedTo > 4 * 1024 * 1024) {\n this.#db.exec(\"PRAGMA wal_checkpoint(TRUNCATE)\");\n this.#walOffset = 0;\n }\n return { pageSize, pageCount, pages };\n } finally {\n closeSync(fd);\n }\n }\n exec(sql: string, params: unknown[]): SqlResult {\n const statements = params.length === 0 ? splitSqlStatements(sql) : [sql];\n // Several statements: the result of the last one, with the rows written by all of them.\n let last: SqlResult = { columnNames: [], rows: [], rowsWritten: 0 };\n let written = 0;\n for (const text of statements) {\n last = this.#one(text, params);\n written += last.rowsWritten;\n }\n if (this.#pageCapture && !this.#taken && ++this.#writes % 64 === 0) this.#boundWal();\n return { ...last, rowsWritten: written };\n }\n #one(sql: string, params: unknown[]): SqlResult {\n const stmt = this.#db.prepare(sql);\n const columnNames = stmt.columns().map((c) => c.name);\n const args = params.map(toSqlite) as any[];\n if (columnNames.length === 0) {\n const r = stmt.run(...args);\n return { columnNames, rows: [], rowsWritten: Number(r.changes) };\n }\n stmt.setReturnArrays(true);\n const rows = (stmt.all(...args) as unknown as unknown[][]).map((row) => row.map(fromSqlite));\n return { columnNames, rows, rowsWritten: 0 };\n }\n /**\n * A database whose pages nobody takes (the runtime's own tables, a storage that logs no pages)\n * has no one to checkpoint its WAL (automatic checkpoints are off): past 4 MiB, fold it in.\n */\n #boundWal(): void {\n if (this.inTransaction()) return;\n try {\n if (statSync(`${this.#path}-wal`).size <= 4 * 1024 * 1024) return;\n } catch {\n return;\n }\n this.#db.exec(\"PRAGMA wal_checkpoint(TRUNCATE)\");\n this.#walOffset = 0;\n }\n inTransaction(): boolean {\n return (this.#db as unknown as { isTransaction: boolean }).isTransaction;\n }\n databaseSize(): number {\n const [pc] = this.exec(\"PRAGMA page_count\", []).rows[0] as [number];\n const [ps] = this.exec(\"PRAGMA page_size\", []).rows[0] as [number];\n return Number(pc) * Number(ps);\n }\n close(): void {\n this.#db.close();\n if (!this.#temporary) return;\n for (const suffix of [\"\", \"-wal\", \"-shm\", \"-journal\"])\n rmSync(this.#path + suffix, { force: true });\n try {\n rmdirSync(dirname(this.#path)); // the opener's directory, once its last cell is closed\n temporaryDirs.delete(dirname(this.#path));\n } catch {}\n }\n\n // ---- change capture (session extension) for transaction logs -------------------------\n #session: { changeset(): Uint8Array; close(): void } | undefined;\n /** node:sqlite exposes no ROWID session option: PK-less tables are logged as statements. */\n readonly captureCoversRowidTables = false;\n captureStart(): void {\n this.#session?.close();\n this.#session = (\n this.#db as unknown as { createSession(): { changeset(): Uint8Array; close(): void } }\n ).createSession();\n }\n captureTake(): Uint8Array {\n const s = this.#session;\n this.#session = undefined;\n if (!s) return new Uint8Array();\n try {\n return s.changeset();\n } finally {\n s.close();\n }\n }\n captureDiscard(): void {\n this.#session?.close();\n this.#session = undefined;\n }\n applyChangeset(changeset: Uint8Array): void {\n if (!changeset.byteLength) return;\n const SQLITE_CHANGESET_REPLACE = 1;\n (this.#db as unknown as { applyChangeset(c: Uint8Array, o: unknown): boolean }).applyChangeset(\n changeset,\n {\n onConflict: () => SQLITE_CHANGESET_REPLACE,\n },\n );\n }\n\n /** Known extensions only: \"sqlite_vec\" → the sqlite-vec npm package's loadable library. */\n loadExtension(name: string): void {\n const path = KNOWN_EXTENSIONS[name]?.();\n if (!path) throw new Error(`Unknown SQLite extension \"${name}\"`);\n this.#db.enableLoadExtension(true);\n try {\n this.#db.loadExtension(path);\n } finally {\n this.#db.enableLoadExtension(false);\n }\n }\n\n /**\n * The database file as SQLite wrote it (page for page), for page-level logs; an in-memory\n * database falls back to image(). Valid between transactions (rollback journal mode).\n */\n pageImage(): Uint8Array {\n if (this.#path === \":memory:\") return this.image();\n // A database restored from celld's files is in WAL mode (litestream needs it): its last\n // commits are in the -wal file until a checkpoint moves them into the main file.\n const mode = String(this.#db.prepare(\"PRAGMA journal_mode\").get()?.journal_mode ?? \"\");\n if (mode.toLowerCase() === \"wal\") {\n this.#db.exec(\"PRAGMA wal_checkpoint(TRUNCATE)\");\n this.#walOffset = 0; // the WAL starts over\n }\n return new Uint8Array(readFileSync(this.#path));\n }\n\n /** `VACUUM INTO` a temp file: a consistent, compact image (no open transaction allowed). */\n image(): Uint8Array {\n const tmp = join(mkdtempSync(join(tmpdir(), \"cell-snap-\")), \"db.sqlite\");\n try {\n this.#db.exec(`VACUUM INTO '${tmp.replace(/'/g, \"''\")}'`);\n return new Uint8Array(readFileSync(tmp));\n } finally {\n rmSync(dirname(tmp), { recursive: true, force: true });\n }\n }\n\n /** Replaces the database file with `image` and reopens it (an in-memory DB becomes file-backed). */\n restore(image: Uint8Array): void {\n this.#db.close();\n if (this.#path === \":memory:\")\n this.#path = join(mkdtempSync(join(tmpdir(), \"cell-mem-\")), \"db.sqlite\");\n for (const suffix of [\"-wal\", \"-shm\", \"-journal\"]) rmSync(this.#path + suffix, { force: true });\n writeFileSync(this.#path, image);\n this.#db = new DatabaseSync(this.#path, { enableDoubleQuotedStringLiterals: true });\n if (this.#exclusive) this.#db.exec(\"PRAGMA locking_mode = EXCLUSIVE; PRAGMA busy_timeout = 0;\");\n this.#walSetup(); // a new file: a new WAL\n }\n}\n\nfunction toSqlite(v: unknown): unknown {\n if (v instanceof ArrayBuffer) return new Uint8Array(v);\n if (typeof v === \"boolean\") return v ? 1 : 0;\n return v === undefined ? null : v;\n}\n/** Workers return BLOBs as ArrayBuffer. */\nfunction fromSqlite(v: unknown): unknown {\n if (v instanceof Uint8Array) return v.buffer.slice(v.byteOffset, v.byteOffset + v.byteLength);\n return v;\n}\n\n/** Temporary cell directories still present: removed if the process exits without closing them. */\nconst temporaryDirs = new Set<string>();\nlet exitHook = false;\nfunction removeAtExit(dir: string): void {\n temporaryDirs.add(dir);\n if (exitHook) return;\n exitHook = true;\n process.once(\"exit\", () => {\n for (const d of temporaryDirs) rmSync(d, { recursive: true, force: true });\n });\n}\n\n/**\n * One file per cell in `dir`; without `dir`, each cell in memory.\n *\n * `pageCapture`: WAL mode, and each commit's changed pages read from the WAL (takeDirtyPages),\n * so LTX logs (a celld bucket) cost O(changed pages) on Node as with SQLite WASM, instead of an\n * image of the whole database (VACUUM INTO) and a page-by-page diff per commit. A WAL needs a\n * file: without `dir`, each cell opened gets a temporary file (in a `cellb-cells-…` directory\n * under the OS temp directory), removed when the cell closes, so it still starts empty each\n * time as in memory (a bucket restores it).\n */\nexport function nodeSqliteOpener(\n dir?: string,\n opts: { exclusive?: boolean; pageCapture?: boolean } = {},\n): OpenCellSql {\n let temp: string | undefined;\n let opened = 0;\n return (cellKey) => {\n const name = cellKey.replace(/[^\\w.-]/g, \"_\");\n // The runtime's own databases (alarms index, cron state) log no pages: never captured.\n const capture = !!opts.pageCapture && !cellKey.startsWith(\"__cellb/\");\n const o = { ...opts, pageCapture: capture };\n if (dir) return new NodeSqliteDriver(`${dir}/${name}.sqlite`, o);\n if (!capture) return new NodeSqliteDriver(\":memory:\", o);\n if (!temp || !existsSync(temp)) {\n temp = mkdtempSync(join(tmpdir(), \"cellb-cells-\"));\n removeAtExit(temp);\n }\n // A file per opening: a cell reopened while its previous database is still closing gets\n // a fresh one, as in memory.\n return new NodeSqliteDriver(join(temp, `${name}-${++opened}.sqlite`), {\n ...o,\n temporary: true,\n });\n };\n}\n"]}
1
+ {"version":3,"file":"node-sqlite.js","sourceRoot":"","sources":["../../src/adapters/node-sqlite.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n closeSync,\n existsSync,\n fstatSync,\n mkdtempSync,\n openSync,\n readFileSync,\n readSync,\n rmdirSync,\n rmSync,\n statSync,\n writeFileSync,\n} from \"node:fs\";\nimport { createRequire } from \"node:module\";\nimport { tmpdir } from \"node:os\";\nimport { dirname, join } from \"node:path\";\nimport type { DatabaseSync as DatabaseSyncT } from \"node:sqlite\";\n\nimport { splitSqlStatements } from \"../core/sql-split.js\";\nimport type { OpenCellSql, SqlResult, SyncSqlDriver } from \"../ports.js\";\nimport { requireNodeSqlite } from \"./node-sqlite-module.js\";\n\nconst { DatabaseSync } = requireNodeSqlite();\n\nconst requireCjs = createRequire(import.meta.url);\nconst KNOWN_EXTENSIONS: Record<string, () => string> = {\n sqlite_vec: () => (requireCjs(\"sqlite-vec\") as { getLoadablePath(): string }).getLoadablePath(),\n};\n\n/** Server-side SyncSqlDriver over node:sqlite (Node >= 22.13). */\nexport class NodeSqliteDriver implements SyncSqlDriver {\n #db: DatabaseSyncT;\n #path: string;\n readonly #exclusive: boolean;\n readonly #pageCapture: boolean;\n /** A file of its own that close() removes (a cell \"in memory\" that needs a WAL). */\n readonly #temporary: boolean;\n constructor(\n path = \":memory:\",\n opts: { exclusive?: boolean; pageCapture?: boolean; temporary?: boolean } = {},\n ) {\n this.#path = path;\n this.#temporary = !!opts.temporary && path !== \":memory:\";\n this.#exclusive = !!opts.exclusive;\n this.#pageCapture = !!opts.pageCapture && path !== \":memory:\";\n this.#db = this.#connect();\n this.#walSetup();\n }\n\n /** Extensions loaded so far, by name: a new connection (restore()) loads them again. */\n #extensions: string[] = [];\n /** The directory restore() made for an in-memory database's file. */\n #restoredDir: string | undefined;\n\n /** A connection to `#path`, set up as every connection of this driver is. */\n #connect(): DatabaseSyncT {\n // As celld and workerd: a double-quoted name that is no column is a string literal.\n const db = new DatabaseSync(this.#path, {\n allowExtension: true,\n enableDoubleQuotedStringLiterals: true,\n });\n db.enableLoadExtension(false); // only loadExtension() below may load, by name\n // EXCLUSIVE locking: the first write takes an OS lock on the file that is\n // kept until close (or process death) — a second process can neither read\n // nor write the cell. Storage-level fencing on one host, no lease needed.\n if (this.#exclusive) db.exec(\"PRAGMA locking_mode = EXCLUSIVE; PRAGMA busy_timeout = 0;\");\n try {\n for (const name of this.#extensions) this.#load(db, name);\n } catch (e) {\n db.close(); // not left open behind the error\n throw e;\n }\n return db;\n }\n\n // ---- page capture through the WAL (what the WASM driver does with its xWrite hook) ----\n // In WAL mode every commit appends its changed pages to `<db>-wal` as frames (24-byte frame\n // header + page; a commit frame carries the database size in pages). Reading the frames\n // appended since the last read gives exactly the committed transaction's pages, O(changed),\n // instead of reading the whole file. Automatic checkpoints are off; take() checkpoints\n // (TRUNCATE) itself once the WAL grows, and restarts from its start.\n #walOffset = 0;\n /** Whether anyone reads the captured pages; until then exec() keeps the WAL bounded itself. */\n #taken = false;\n #writes = 0;\n #walSetup(): void {\n if (!this.#pageCapture) return;\n this.#db.exec(\"PRAGMA journal_mode = WAL; PRAGMA wal_autocheckpoint = 0;\");\n // A temporary file is only the WAL's carrier (the bucket keeps the cell): no fsync per commit.\n if (this.#temporary) this.#db.exec(\"PRAGMA synchronous = OFF\");\n this.#db.exec(\"PRAGMA wal_checkpoint(TRUNCATE)\");\n this.#walOffset = 0;\n }\n /** The pages of the transactions committed since the previous call (WAL frames). */\n takeDirtyPages():\n | { pageSize: number; pageCount: number; pages: Map<number, Uint8Array> }\n | undefined {\n if (!this.#pageCapture) return undefined;\n this.#taken = true;\n const walPath = `${this.#path}-wal`;\n let fd: number;\n try {\n fd = openSync(walPath, \"r\");\n } catch {\n return undefined;\n }\n try {\n const size = fstatSync(fd).size;\n if (size < 32) return undefined;\n const header = Buffer.alloc(32);\n readSync(fd, header, 0, 32, 0);\n const pageSize = header.readUInt32BE(8) === 1 ? 65536 : header.readUInt32BE(8);\n const [salt1, salt2] = [header.readUInt32BE(16), header.readUInt32BE(20)];\n const frameSize = 24 + pageSize;\n let offset = Math.max(32, this.#walOffset);\n const pending = new Map<number, Uint8Array>();\n const pages = new Map<number, Uint8Array>();\n let pageCount = 0;\n let committedTo = offset;\n const frame = Buffer.alloc(frameSize);\n while (offset + frameSize <= size) {\n readSync(fd, frame, 0, frameSize, offset);\n if (frame.readUInt32BE(8) !== salt1 || frame.readUInt32BE(12) !== salt2) break; // stale frames\n pending.set(frame.readUInt32BE(0), new Uint8Array(frame.subarray(24)));\n offset += frameSize;\n const commit = frame.readUInt32BE(4);\n if (commit) {\n for (const [p, d] of pending) pages.set(p, d);\n pending.clear();\n pageCount = commit;\n committedTo = offset;\n }\n }\n this.#walOffset = committedTo;\n if (!pageCount)\n return {\n pageSize,\n pageCount: Number(this.exec(\"PRAGMA page_count\", []).rows[0][0]),\n pages,\n };\n for (const p of [...pages.keys()]) if (p > pageCount) pages.delete(p);\n // keep the WAL bounded: fold it into the database and start over\n if (committedTo > 4 * 1024 * 1024) {\n this.#db.exec(\"PRAGMA wal_checkpoint(TRUNCATE)\");\n this.#walOffset = 0;\n }\n return { pageSize, pageCount, pages };\n } finally {\n closeSync(fd);\n }\n }\n exec(sql: string, params: unknown[]): SqlResult {\n const statements = params.length === 0 ? splitSqlStatements(sql) : [sql];\n // Several statements: the result of the last one, with the rows written by all of them.\n let last: SqlResult = { columnNames: [], rows: [], rowsWritten: 0 };\n let written = 0;\n for (const text of statements) {\n last = this.#one(text, params);\n written += last.rowsWritten;\n }\n if (this.#pageCapture && !this.#taken && ++this.#writes % 64 === 0) this.#boundWal();\n return { ...last, rowsWritten: written };\n }\n #one(sql: string, params: unknown[]): SqlResult {\n const stmt = this.#db.prepare(sql);\n const columnNames = stmt.columns().map((c) => c.name);\n const args = params.map(toSqlite) as any[];\n if (columnNames.length === 0) {\n const r = stmt.run(...args);\n return { columnNames, rows: [], rowsWritten: Number(r.changes) };\n }\n stmt.setReturnArrays(true);\n const rows = (stmt.all(...args) as unknown as unknown[][]).map((row) => row.map(fromSqlite));\n return { columnNames, rows, rowsWritten: 0 };\n }\n /**\n * A database whose pages nobody takes (the runtime's own tables, a storage that logs no pages)\n * has no one to checkpoint its WAL (automatic checkpoints are off): past 4 MiB, fold it in.\n */\n #boundWal(): void {\n if (this.inTransaction()) return;\n try {\n if (statSync(`${this.#path}-wal`).size <= 4 * 1024 * 1024) return;\n } catch {\n return;\n }\n this.#db.exec(\"PRAGMA wal_checkpoint(TRUNCATE)\");\n this.#walOffset = 0;\n }\n inTransaction(): boolean {\n return (this.#db as unknown as { isTransaction: boolean }).isTransaction;\n }\n databaseSize(): number {\n const [pc] = this.exec(\"PRAGMA page_count\", []).rows[0] as [number];\n const [ps] = this.exec(\"PRAGMA page_size\", []).rows[0] as [number];\n return Number(pc) * Number(ps);\n }\n close(): void {\n this.#db.close();\n if (this.#restoredDir) {\n rmSync(this.#restoredDir, { recursive: true, force: true });\n temporaryDirs.delete(this.#restoredDir);\n this.#restoredDir = undefined;\n }\n if (!this.#temporary) return;\n for (const suffix of [\"\", \"-wal\", \"-shm\", \"-journal\"])\n rmSync(this.#path + suffix, { force: true });\n try {\n rmdirSync(dirname(this.#path)); // the opener's directory, once its last cell is closed\n temporaryDirs.delete(dirname(this.#path));\n } catch {}\n }\n\n // ---- change capture (session extension) for transaction logs -------------------------\n #session: { changeset(): Uint8Array; close(): void } | undefined;\n /** node:sqlite exposes no ROWID session option: PK-less tables are logged as statements. */\n readonly captureCoversRowidTables = false;\n captureStart(): void {\n this.#session?.close();\n this.#session = (\n this.#db as unknown as { createSession(): { changeset(): Uint8Array; close(): void } }\n ).createSession();\n }\n captureTake(): Uint8Array {\n const s = this.#session;\n this.#session = undefined;\n if (!s) return new Uint8Array();\n try {\n return s.changeset();\n } finally {\n s.close();\n }\n }\n captureDiscard(): void {\n this.#session?.close();\n this.#session = undefined;\n }\n applyChangeset(changeset: Uint8Array): void {\n if (!changeset.byteLength) return;\n const SQLITE_CHANGESET_REPLACE = 1;\n (this.#db as unknown as { applyChangeset(c: Uint8Array, o: unknown): boolean }).applyChangeset(\n changeset,\n {\n onConflict: () => SQLITE_CHANGESET_REPLACE,\n },\n );\n }\n\n /** Known extensions only: \"sqlite_vec\" → the sqlite-vec npm package's loadable library. */\n loadExtension(name: string): void {\n this.#load(this.#db, name);\n if (!this.#extensions.includes(name)) this.#extensions.push(name);\n }\n #load(db: DatabaseSyncT, name: string): void {\n const path = KNOWN_EXTENSIONS[name]?.();\n if (!path) throw new Error(`Unknown SQLite extension \"${name}\"`);\n db.enableLoadExtension(true);\n try {\n db.loadExtension(path);\n } finally {\n db.enableLoadExtension(false);\n }\n }\n\n /**\n * The database file as SQLite wrote it (page for page), for page-level logs; an in-memory\n * database falls back to image(). Valid between transactions (rollback journal mode).\n */\n pageImage(): Uint8Array {\n if (this.#path === \":memory:\") return this.image();\n // A database restored from celld's files is in WAL mode (litestream needs it): its last\n // commits are in the -wal file until a checkpoint moves them into the main file.\n const mode = String(this.#db.prepare(\"PRAGMA journal_mode\").get()?.journal_mode ?? \"\");\n if (mode.toLowerCase() === \"wal\") {\n this.#db.exec(\"PRAGMA wal_checkpoint(TRUNCATE)\");\n this.#walOffset = 0; // the WAL starts over\n }\n return new Uint8Array(readFileSync(this.#path));\n }\n\n /** `VACUUM INTO` a temp file: a consistent, compact image (no open transaction allowed). */\n image(): Uint8Array {\n const tmp = join(mkdtempSync(join(tmpdir(), \"cell-snap-\")), \"db.sqlite\");\n try {\n this.#db.exec(`VACUUM INTO '${tmp.replace(/'/g, \"''\")}'`);\n return new Uint8Array(readFileSync(tmp));\n } finally {\n rmSync(dirname(tmp), { recursive: true, force: true });\n }\n }\n\n /** Replaces the database file with `image` and reopens it (an in-memory DB becomes file-backed). */\n restore(image: Uint8Array): void {\n this.#db.close();\n if (this.#path === \":memory:\") {\n // A directory of its own, removed with the driver (close() or process exit).\n this.#restoredDir = mkdtempSync(join(tmpdir(), \"cell-mem-\"));\n removeAtExit(this.#restoredDir);\n this.#path = join(this.#restoredDir, \"db.sqlite\");\n }\n for (const suffix of [\"-wal\", \"-shm\", \"-journal\"]) rmSync(this.#path + suffix, { force: true });\n writeFileSync(this.#path, image);\n this.#db = this.#connect(); // with the extensions loaded before\n this.#walSetup(); // a new file: a new WAL\n }\n}\n\nfunction toSqlite(v: unknown): unknown {\n if (v instanceof ArrayBuffer) return new Uint8Array(v);\n if (typeof v === \"boolean\") return v ? 1 : 0;\n return v === undefined ? null : v;\n}\n/** Workers return BLOBs as ArrayBuffer. */\nfunction fromSqlite(v: unknown): unknown {\n if (v instanceof Uint8Array) return v.buffer.slice(v.byteOffset, v.byteOffset + v.byteLength);\n return v;\n}\n\n/** Temporary cell directories still present: removed if the process exits without closing them. */\nconst temporaryDirs = new Set<string>();\nlet exitHook = false;\nfunction removeAtExit(dir: string): void {\n temporaryDirs.add(dir);\n if (exitHook) return;\n exitHook = true;\n process.once(\"exit\", () => {\n for (const d of temporaryDirs) rmSync(d, { recursive: true, force: true });\n });\n}\n\n/**\n * One file per cell in `dir`; without `dir`, each cell in memory.\n *\n * `pageCapture`: WAL mode, and each commit's changed pages read from the WAL (takeDirtyPages),\n * so LTX logs (a celld bucket) cost O(changed pages) on Node as with SQLite WASM, instead of an\n * image of the whole database (VACUUM INTO) and a page-by-page diff per commit. A WAL needs a\n * file: without `dir`, each cell opened gets a temporary file (in a `cellb-cells-…` directory\n * under the OS temp directory), removed when the cell closes, so it still starts empty each\n * time as in memory (a bucket restores it).\n */\nexport function nodeSqliteOpener(\n dir?: string,\n opts: { exclusive?: boolean; pageCapture?: boolean } = {},\n): OpenCellSql {\n let temp: string | undefined;\n let opened = 0;\n return (cellKey) => {\n const name = cellKey.replace(/[^\\w.-]/g, \"_\");\n // The runtime's own databases (alarms index, cron state) log no pages: never captured.\n const capture = !!opts.pageCapture && !cellKey.startsWith(\"__cellb/\");\n const o = { ...opts, pageCapture: capture };\n if (dir) return new NodeSqliteDriver(`${dir}/${name}.sqlite`, o);\n if (!capture) return new NodeSqliteDriver(\":memory:\", o);\n if (!temp || !existsSync(temp)) {\n temp = mkdtempSync(join(tmpdir(), \"cellb-cells-\"));\n removeAtExit(temp);\n }\n // A file per opening: a cell reopened while its previous database is still closing gets\n // a fresh one, as in memory.\n return new NodeSqliteDriver(join(temp, `${name}-${++opened}.sqlite`), {\n ...o,\n temporary: true,\n });\n };\n}\n"]}
@@ -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"]}