@statewalker/cellb 0.8.0 → 0.8.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -308,8 +308,10 @@ docs/ topic pages
308
308
  the server end acknowledges the bytes of each message it dispatched and a client's
309
309
  `bufferedAmount` counts the bytes not acknowledged yet: the WHATWG signal a sender throttles on.
310
310
  workerd's server-side WebSocket has no `bufferedAmount`, and cellb's has none.
311
- - **Each loaded Worker gets a realm of its own**, a thread or a Dedicated Worker: as in celld's
312
- isolates, loaded code must not see the host's globals or env.
311
+ - **Each loaded Worker gets a realm of its own**, a worker thread in Node, or in a browser a
312
+ Dedicated Worker when `isolateScript` is set (the browser `loadProject` sets it;
313
+ `browserProjectRuntime` and the shard functions do not): as in celld's isolates, loaded code must
314
+ not see the host's globals or env.
313
315
  - **`idFromName` is synchronous**, with `@noble/hashes` instead of `crypto.subtle`, which is
314
316
  asynchronous only; Worker code relies on getting the id at once.
315
317
  - **Ownership depends on the deployment**: a Web Lock per shard in an origin, celld's ownership
@@ -84,8 +84,10 @@ export declare function sahpoolOpener(sqlite3: {
84
84
  }): Promise<{
85
85
  OpfsSAHPoolDb: new (path: string) => Oo1Db;
86
86
  reserveMinimumCapacity(n: number): Promise<number>;
87
+ addCapacity?(n: number): Promise<number>;
87
88
  getCapacity(): number;
88
89
  getFileCount(): number;
90
+ getFileNames?(): string[];
89
91
  pauseVfs?(): unknown;
90
92
  isPaused?(): boolean;
91
93
  unpauseVfs?(): Promise<unknown>;
@@ -209,41 +209,80 @@ export async function sahpoolOpener(sqlite3, opts = {}) {
209
209
  if (pool.isPaused?.())
210
210
  await pool.unpauseVfs?.();
211
211
  // A full pool fails the open (`SAH pool is full`), and opening is synchronous while growing
212
- // the pool is not. A cell about to open reserves room first (`room`, awaited by the runtime
213
- // before the cell's first event); the pool grows when the reservations would not fit, and in
214
- // the background whenever less than half of it is free.
212
+ // the pool is not. Each new slot is an OPFS file with a sync access handle, made one at a time,
213
+ // and once the worker is busy each one takes seconds (about 5 s with 32 Flue conversations in
214
+ // one worker), so the pool grows ahead of need, in small steps:
215
+ // - a cell about to open reserves room first (`room`, awaited by the runtime before the cell's
216
+ // first event): its database, its journal and a spare, and it waits until what was needed
217
+ // when it asked fits, one step of 16 slots at a time (not a doubling, nor later reservations);
218
+ // - an open cell counts its journal until it has one (SQLite makes it at the cell's first
219
+ // write), so the pool grows while it still has room, not once the journals fill it;
220
+ // - at each open (failed ones too: a cell can reopen without `room`), the pool grows in the
221
+ // background until two thirds of it are free: an unused slot costs a small file, a missing
222
+ // one a wait of seconds per slot, so it grows while the worker is still quiet.
215
223
  const SLOTS_PER_CELL = 3; // the database, its journal, and one spare
224
+ const STEP = 16; // slots per growth step, made in parallel
225
+ const AHEAD = 3; // the pool's size, in multiples of what it needs, kept by the background growth
216
226
  const reserved = new Set();
227
+ const opened = new Map(); // the open cells' databases
217
228
  let growing;
218
- const grow = (min) => {
219
- growing ??= pool
220
- .reserveMinimumCapacity(Math.max(min, pool.getCapacity() * 2))
229
+ /**
230
+ * One growth step toward `target` (one at a time: a second caller joins it). SQLite makes a
231
+ * pool's new slots one after another, two awaited OPFS calls each, and in a busy worker each
232
+ * await waits seconds for the event loop: the step asks for its slots in parallel instead,
233
+ * one `addCapacity(1)` each, so it costs about one slot's wait.
234
+ */
235
+ const step = (target) => {
236
+ const n = Math.min(target, pool.getCapacity() + STEP) - pool.getCapacity();
237
+ growing ??= (pool.addCapacity && n > 0
238
+ ? Promise.all(Array.from({ length: n }, () => pool.addCapacity?.(1)))
239
+ : pool.reserveMinimumCapacity(pool.getCapacity() + Math.max(n, 0)))
221
240
  .then(() => { })
222
241
  .finally(() => {
223
242
  growing = undefined;
224
243
  });
225
244
  return growing;
226
245
  };
227
- const needed = () => pool.getFileCount() + SLOTS_PER_CELL * reserved.size;
246
+ const needed = () => {
247
+ // Journals not made yet. Without the pool's file names (older SQLite WASM builds) none
248
+ // is counted, as before: the files that exist are.
249
+ let journals = 0;
250
+ if (pool.getFileNames) {
251
+ const files = new Set(pool.getFileNames());
252
+ for (const { path } of opened.values())
253
+ if (!files.has(`${path}-journal`))
254
+ journals++;
255
+ }
256
+ return pool.getFileCount() + journals + SLOTS_PER_CELL * reserved.size;
257
+ };
258
+ /** Grows until `target()` fits; stops when a step of its own adds nothing (the open says why). */
259
+ const growTo = async (target) => {
260
+ while (target() > pool.getCapacity()) {
261
+ // A step already under way may have added its slots and not ended yet: joining it says
262
+ // nothing about whether the pool can grow.
263
+ const joined = growing !== undefined;
264
+ const before = pool.getCapacity();
265
+ await step(target());
266
+ if (!joined && pool.getCapacity() <= before)
267
+ return;
268
+ }
269
+ };
228
270
  const room = (cellKey) => {
229
271
  reserved.add(cellKey);
230
- if (needed() <= pool.getCapacity())
272
+ const target = needed();
273
+ if (target <= pool.getCapacity())
231
274
  return undefined;
232
- // Grow until this reservation (and every other one) fits; the open fails visibly if not.
233
- const fit = async () => {
234
- while (needed() > pool.getCapacity()) {
235
- const before = pool.getCapacity();
236
- await grow(needed());
237
- if (pool.getCapacity() <= before)
238
- return; // it did not grow: the open will say why
239
- }
240
- };
241
- return fit();
275
+ return growTo(() => target);
242
276
  };
277
+ let ahead;
243
278
  const keepRoom = () => {
244
- const capacity = pool.getCapacity();
245
- if (!growing && capacity - needed() < capacity / 2)
246
- grow(capacity * 2).catch(() => { });
279
+ if (ahead || pool.getCapacity() >= AHEAD * needed())
280
+ return;
281
+ ahead = growTo(() => AHEAD * needed())
282
+ .catch(() => { })
283
+ .finally(() => {
284
+ ahead = undefined;
285
+ });
247
286
  };
248
287
  // The rollback journal stays in the pool between transactions (PERSIST): DELETE gives its pool
249
288
  // slot back and takes it again on every commit, which costs a header write each time. The
@@ -257,16 +296,35 @@ export async function sahpoolOpener(sqlite3, opts = {}) {
257
296
  const open = (cellKey) => {
258
297
  const path = `/${cellKey.replace(/[^\w.-]/g, "_")}.sqlite`;
259
298
  reserved.delete(cellKey);
260
- queueMicrotask(keepRoom); // after this open (and its first slot) is counted
261
- return new WasmSqliteDriver(openDb(path), {
262
- image: (db) => sqlite3.capi.sqlite3_js_db_export(db),
263
- // Replace the file inside the pool, then reopen it.
264
- restore(db, image) {
265
- db.close();
266
- pool.importDb(path, image);
267
- return openDb(path);
268
- },
269
- }, sqlite3);
299
+ // Counted from now on, with its journal until it has one; no longer once it is closed.
300
+ const entry = { path };
301
+ opened.set(cellKey, entry);
302
+ keepRoom();
303
+ let driver;
304
+ try {
305
+ driver = new WasmSqliteDriver(openDb(path), {
306
+ image: (db) => sqlite3.capi.sqlite3_js_db_export(db),
307
+ // Replace the file inside the pool, then reopen it.
308
+ restore(db, image) {
309
+ db.close();
310
+ pool.importDb(path, image);
311
+ return openDb(path);
312
+ },
313
+ }, sqlite3);
314
+ }
315
+ catch (err) {
316
+ if (opened.get(cellKey) === entry)
317
+ opened.delete(cellKey);
318
+ throw err;
319
+ }
320
+ const close = driver.close.bind(driver);
321
+ driver.close = () => {
322
+ // Only this driver's entry: the cell may have been opened again since.
323
+ if (opened.get(cellKey) === entry)
324
+ opened.delete(cellKey);
325
+ close();
326
+ };
327
+ return driver;
270
328
  };
271
329
  return Object.assign(open, {
272
330
  room,
@@ -1 +1 @@
1
- {"version":3,"file":"wasm-sqlite.js","sourceRoot":"","sources":["../../src/adapters/wasm-sqlite.ts"],"names":[],"mappings":"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","sourcesContent":["import { splitSqlStatements } from \"../core/sql-split.js\";\nimport type { OpenCellSql, SqlResult, SyncSqlDriver } from \"../ports.js\";\nimport { type DirtyPages, installPageHook } from \"./wasm-page-hook.js\";\nimport { type WasmSqlite3, wasmSessionCapture } from \"./wasm-session.js\";\n\n/** Minimal slice of the @sqlite.org/sqlite-wasm `oo1.DB` API we rely on. */\nexport interface Oo1Db {\n prepare(sql: string): Oo1Stmt;\n exec(sql: string): unknown;\n changes(): number;\n close(): void;\n}\ninterface Oo1Stmt {\n bind(values: unknown[]): Oo1Stmt;\n getColumnNames(): string[];\n columnCount: number;\n step(): boolean;\n get(target: unknown[]): unknown[];\n finalize(): void;\n}\n\n/**\n * SyncSqlDriver over the official SQLite WASM build (oo1 API). Synchronous,\n * so it satisfies `ctx.storage.sql`. Works in Node (in-memory, for tests) and\n * in browsers; persistence comes from the VFS behind the DB (see sahpoolOpener).\n */\n/** Backup hooks: how to export an image of the DB and how to replace it (returns the reopened DB). */\nexport interface WasmImageHooks {\n image(db: Oo1Db): Uint8Array;\n restore(db: Oo1Db, image: Uint8Array): Oo1Db;\n}\n\nexport class WasmSqliteDriver implements SyncSqlDriver {\n captureStart?: () => void;\n captureTake?: () => Uint8Array;\n captureDiscard?: () => void;\n applyChangeset?: (changeset: Uint8Array) => void;\n captureCoversRowidTables?: boolean;\n #db: Oo1Db;\n readonly #hooks?: WasmImageHooks;\n readonly #sqlite3?: WasmSqlite3;\n readonly #capi?: { sqlite3_get_autocommit(db: number): number };\n #pageHook?: ReturnType<typeof installPageHook>;\n constructor(\n db: Oo1Db,\n hooks?: WasmImageHooks,\n sqlite3?: WasmSqlite3,\n /** SQLite's C API, for `inTransaction()` alone (when `sqlite3` is not given). */\n capi?: { sqlite3_get_autocommit(db: number): number },\n ) {\n this.#db = db;\n this.#hooks = hooks;\n this.#sqlite3 = sqlite3;\n this.#capi = capi ?? sqlite3?.capi;\n if (!this.#capi?.sqlite3_get_autocommit)\n (this as { inTransaction?: unknown }).inTransaction = undefined;\n this.#enableDqs();\n this.#hookPages();\n if (sqlite3?.capi?.sqlite3session_create) {\n // Transaction-log capture (session extension), following the current DB handle.\n Object.assign(\n this,\n wasmSessionCapture(sqlite3, () => (this.#db as unknown as { pointer: number }).pointer),\n );\n }\n if (!hooks) {\n // No image support for this driver: hide the optional port methods.\n (this as { image?: unknown }).image = undefined;\n (this as { restore?: unknown }).restore = undefined;\n }\n }\n image(): Uint8Array {\n return this.#hooks!.image(this.#db);\n }\n\n /**\n * As celld and workerd: a double-quoted name that is no column is a string literal (SQLite's\n * DQS misfeature, off in this build).\n */\n #enableDqs(): void {\n const capi = this.#sqlite3?.capi;\n if (!capi?.sqlite3_db_config) return;\n const db = (this.#db as unknown as { pointer: number }).pointer;\n capi.sqlite3_db_config(db, capi.SQLITE_DBCONFIG_DQS_DML, 1, 0);\n capi.sqlite3_db_config(db, capi.SQLITE_DBCONFIG_DQS_DDL, 1, 0);\n }\n\n /** Installs the page hook on the current main database file (see wasm-page-hook.ts). */\n #hookPages(): void {\n this.#pageHook = undefined;\n if (!this.#sqlite3?.capi?.sqlite3_file_control) return;\n try {\n this.#pageHook = installPageHook(\n this.#sqlite3,\n (this.#db as unknown as { pointer: number }).pointer,\n );\n } catch {\n this.#pageHook = undefined;\n }\n }\n /** Pages written since the previous call — a committed transaction's changed pages. */\n takeDirtyPages(): DirtyPages | undefined {\n if (!this.#pageHook) return undefined;\n const pageSize = Number(this.exec(\"PRAGMA page_size\", []).rows[0][0]);\n const pageCount = Number(this.exec(\"PRAGMA page_count\", []).rows[0][0]);\n return this.#pageHook.take(pageSize, pageCount);\n }\n discardDirtyPages(): void {\n this.#pageHook?.discard();\n }\n /**\n * WASM builds cannot load extensions at run time; an extension must be compiled into the\n * build (e.g. the `sqlite-vec-wasm-demo` package for \"sqlite_vec\"). This only checks it is.\n */\n loadExtension(name: string): void {\n const probe: Record<string, string> = { sqlite_vec: \"SELECT vec_version()\" };\n if (!probe[name]) throw new Error(`Unknown SQLite extension \"${name}\"`);\n try {\n this.exec(probe[name], []);\n } catch {\n throw new Error(`SQLite extension \"${name}\" is not compiled into this SQLite WASM build`);\n }\n }\n restore(image: Uint8Array): void {\n // celld's databases are in WAL mode (header bytes 18–19 = 2); the WASM VFSes (memdb,\n // opfs-sahpool) have no shared memory for WAL and refuse to open such a file. The pages\n // are the same in rollback-journal mode: switch the header to 1 (celld sets WAL again\n // when it opens the database).\n if (image.length > 19 && image[18] === 2 && image[19] === 2) {\n image = image.slice();\n image[18] = 1;\n image[19] = 1;\n }\n this.#db = this.#hooks!.restore(this.#db, image);\n this.#enableDqs();\n this.#hookPages(); // a new database file behind the connection\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 return { ...last, rowsWritten: written };\n }\n #one(sql: string, params: unknown[]): SqlResult {\n const st = this.#db.prepare(sql);\n try {\n if (params.length) st.bind(params.map(toSqlite));\n const columnNames = st.columnCount ? st.getColumnNames() : [];\n const rows: unknown[][] = [];\n while (st.step()) if (columnNames.length) rows.push(st.get([]).map(fromSqlite));\n return { columnNames, rows, rowsWritten: columnNames.length ? 0 : this.#db.changes() };\n } finally {\n st.finalize();\n }\n }\n inTransaction(): boolean {\n const pointer = (this.#db as unknown as { pointer: number }).pointer;\n return this.#capi!.sqlite3_get_autocommit(pointer) === 0;\n }\n databaseSize(): number {\n const pc = this.exec(\"PRAGMA page_count\", []).rows[0][0];\n const ps = this.exec(\"PRAGMA page_size\", []).rows[0][0];\n return Number(pc) * Number(ps);\n }\n close(): void {\n this.#db.close();\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}\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/** In-memory WASM databases (tests, ephemeral cells). `sqlite3` = await sqlite3InitModule(). */\n/** Image hooks for an in-memory DB: `sqlite3_js_db_export` / `sqlite3_deserialize`. */\nexport function memoryImageHooks(sqlite3: any): WasmImageHooks {\n const { capi, wasm } = sqlite3;\n return {\n image: (db) => sqlite3.capi.sqlite3_js_db_export(db),\n restore(db, image) {\n const p = wasm.allocFromTypedArray(image);\n const rc = capi.sqlite3_deserialize(\n (db as any).pointer,\n \"main\",\n p,\n image.byteLength,\n image.byteLength,\n capi.SQLITE_DESERIALIZE_FREEONCLOSE | capi.SQLITE_DESERIALIZE_RESIZEABLE,\n );\n if (rc) throw new Error(`sqlite3_deserialize failed: ${capi.sqlite3_js_rc_str?.(rc) ?? rc}`);\n return db;\n },\n };\n}\n\n/**\n * Sets up a new SQLite WASM connection for a cell. One worker holds many small cells, and SQLite\n * WASM's heap is capped at 2 GiB and never shrinks, so each connection's fixed memory decides how\n * many cells can be open at once:\n * - no lookaside: SQLite's per-connection allocator of small blocks, about 45 KiB each;\n * - 4 KiB pages for a database that is still empty (celld's page size: SQLite's native default;\n * the WASM build's is 8 KiB). The page cache starts with a block of pages, so the page size\n * sets most of a connection's memory. A database that already holds data keeps its page size.\n * Together they bring a connection with one small table from about 258 KiB to about 120 KiB.\n */\nexport function tuneCellConnection(sqlite3: unknown, db: Oo1Db): void {\n const capi = (sqlite3 as { capi?: Record<string, any> }).capi;\n const pointer = (db as unknown as { pointer?: number }).pointer;\n // Before the connection runs any statement: SQLite refuses (SQLITE_BUSY) while lookaside is used.\n if (capi?.sqlite3_db_config && pointer) {\n capi.sqlite3_db_config(pointer, capi.SQLITE_DBCONFIG_LOOKASIDE, 0, 0, 0);\n }\n db.exec(\"PRAGMA page_size=4096\");\n}\n\nlet memdbCounter = 0;\nexport function wasmMemoryOpener(sqlite3: {\n oo1: { DB: new (path: string, mode?: string) => Oo1Db };\n}): OpenCellSql {\n return () => {\n // memdb (not \":memory:\"): an in-memory database that IS a VFS file, so the page hook works.\n const db = new sqlite3.oo1.DB(`file:/cells-mem-${++memdbCounter}?vfs=memdb`, \"c\");\n tuneCellConnection(sqlite3, db);\n return new WasmSqliteDriver(db, memoryImageHooks(sqlite3), sqlite3 as never);\n };\n}\n\n/**\n * Browser persistence: one OPFS \"SyncAccessHandle pool\" database per cell.\n * Must run in a Dedicated Worker; needs neither COOP/COEP nor SharedArrayBuffer.\n * Not runnable in Node (installOpfsSAHPoolVfs is browser-only) — covered by P04's Playwright spec.\n */\nexport async function sahpoolOpener(\n sqlite3: {\n installOpfsSAHPoolVfs(opts: { name?: string; initialCapacity?: number }): Promise<{\n OpfsSAHPoolDb: new (path: string) => Oo1Db;\n reserveMinimumCapacity(n: number): Promise<number>;\n getCapacity(): number;\n getFileCount(): number;\n // Not in older SQLite WASM builds (sqlite-vec's): there `release()` does nothing.\n pauseVfs?(): unknown;\n isPaused?(): boolean;\n unpauseVfs?(): Promise<unknown>;\n }>;\n },\n opts: { name?: string; initialCapacity?: number } = {},\n): Promise<OpenCellSql & { reserve(n: number): Promise<number>; release(): void }> {\n const pool = await sqlite3.installOpfsSAHPoolVfs({\n name: opts.name ?? \"cellb\",\n initialCapacity: opts.initialCapacity ?? 16,\n });\n // The same pool again in this worker (SQLite WASM keeps one per name), after a `release()`.\n if (pool.isPaused?.()) await pool.unpauseVfs?.();\n // A full pool fails the open (`SAH pool is full`), and opening is synchronous while growing\n // the pool is not. A cell about to open reserves room first (`room`, awaited by the runtime\n // before the cell's first event); the pool grows when the reservations would not fit, and in\n // the background whenever less than half of it is free.\n const SLOTS_PER_CELL = 3; // the database, its journal, and one spare\n const reserved = new Set<string>();\n let growing: Promise<void> | undefined;\n const grow = (min: number): Promise<void> => {\n growing ??= pool\n .reserveMinimumCapacity(Math.max(min, pool.getCapacity() * 2))\n .then(() => {})\n .finally(() => {\n growing = undefined;\n });\n return growing;\n };\n const needed = () => pool.getFileCount() + SLOTS_PER_CELL * reserved.size;\n const room = (cellKey: string): Promise<void> | undefined => {\n reserved.add(cellKey);\n if (needed() <= pool.getCapacity()) return undefined;\n // Grow until this reservation (and every other one) fits; the open fails visibly if not.\n const fit = async (): Promise<void> => {\n while (needed() > pool.getCapacity()) {\n const before = pool.getCapacity();\n await grow(needed());\n if (pool.getCapacity() <= before) return; // it did not grow: the open will say why\n }\n };\n return fit();\n };\n const keepRoom = () => {\n const capacity = pool.getCapacity();\n if (!growing && capacity - needed() < capacity / 2) grow(capacity * 2).catch(() => {});\n };\n // The rollback journal stays in the pool between transactions (PERSIST): DELETE gives its pool\n // slot back and takes it again on every commit, which costs a header write each time. The\n // database file is the same in both modes (only WAL marks it).\n const openDb = (path: string) => {\n const db = new pool.OpfsSAHPoolDb(path);\n tuneCellConnection(sqlite3, db);\n db.exec(\"PRAGMA journal_mode=PERSIST\");\n return db;\n };\n const open: OpenCellSql = (cellKey) => {\n const path = `/${cellKey.replace(/[^\\w.-]/g, \"_\")}.sqlite`;\n reserved.delete(cellKey);\n queueMicrotask(keepRoom); // after this open (and its first slot) is counted\n return new WasmSqliteDriver(\n openDb(path),\n {\n image: (db) => (sqlite3 as any).capi.sqlite3_js_db_export(db),\n // Replace the file inside the pool, then reopen it.\n restore(db, image) {\n db.close();\n (pool as any).importDb(path, image);\n return openDb(path);\n },\n },\n sqlite3 as never,\n );\n };\n return Object.assign(open, {\n room,\n reserve: (n: number) => pool.reserveMinimumCapacity(n),\n /**\n * Gives the pool's OPFS files back once every database is closed, so that another worker\n * (another tab's) can open the pool at once instead of when this worker is gone.\n */\n release: () => void pool.pauseVfs?.(),\n });\n}\n"]}
1
+ {"version":3,"file":"wasm-sqlite.js","sourceRoot":"","sources":["../../src/adapters/wasm-sqlite.ts"],"names":[],"mappings":"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","sourcesContent":["import { splitSqlStatements } from \"../core/sql-split.js\";\nimport type { OpenCellSql, SqlResult, SyncSqlDriver } from \"../ports.js\";\nimport { type DirtyPages, installPageHook } from \"./wasm-page-hook.js\";\nimport { type WasmSqlite3, wasmSessionCapture } from \"./wasm-session.js\";\n\n/** Minimal slice of the @sqlite.org/sqlite-wasm `oo1.DB` API we rely on. */\nexport interface Oo1Db {\n prepare(sql: string): Oo1Stmt;\n exec(sql: string): unknown;\n changes(): number;\n close(): void;\n}\ninterface Oo1Stmt {\n bind(values: unknown[]): Oo1Stmt;\n getColumnNames(): string[];\n columnCount: number;\n step(): boolean;\n get(target: unknown[]): unknown[];\n finalize(): void;\n}\n\n/**\n * SyncSqlDriver over the official SQLite WASM build (oo1 API). Synchronous,\n * so it satisfies `ctx.storage.sql`. Works in Node (in-memory, for tests) and\n * in browsers; persistence comes from the VFS behind the DB (see sahpoolOpener).\n */\n/** Backup hooks: how to export an image of the DB and how to replace it (returns the reopened DB). */\nexport interface WasmImageHooks {\n image(db: Oo1Db): Uint8Array;\n restore(db: Oo1Db, image: Uint8Array): Oo1Db;\n}\n\nexport class WasmSqliteDriver implements SyncSqlDriver {\n captureStart?: () => void;\n captureTake?: () => Uint8Array;\n captureDiscard?: () => void;\n applyChangeset?: (changeset: Uint8Array) => void;\n captureCoversRowidTables?: boolean;\n #db: Oo1Db;\n readonly #hooks?: WasmImageHooks;\n readonly #sqlite3?: WasmSqlite3;\n readonly #capi?: { sqlite3_get_autocommit(db: number): number };\n #pageHook?: ReturnType<typeof installPageHook>;\n constructor(\n db: Oo1Db,\n hooks?: WasmImageHooks,\n sqlite3?: WasmSqlite3,\n /** SQLite's C API, for `inTransaction()` alone (when `sqlite3` is not given). */\n capi?: { sqlite3_get_autocommit(db: number): number },\n ) {\n this.#db = db;\n this.#hooks = hooks;\n this.#sqlite3 = sqlite3;\n this.#capi = capi ?? sqlite3?.capi;\n if (!this.#capi?.sqlite3_get_autocommit)\n (this as { inTransaction?: unknown }).inTransaction = undefined;\n this.#enableDqs();\n this.#hookPages();\n if (sqlite3?.capi?.sqlite3session_create) {\n // Transaction-log capture (session extension), following the current DB handle.\n Object.assign(\n this,\n wasmSessionCapture(sqlite3, () => (this.#db as unknown as { pointer: number }).pointer),\n );\n }\n if (!hooks) {\n // No image support for this driver: hide the optional port methods.\n (this as { image?: unknown }).image = undefined;\n (this as { restore?: unknown }).restore = undefined;\n }\n }\n image(): Uint8Array {\n return this.#hooks!.image(this.#db);\n }\n\n /**\n * As celld and workerd: a double-quoted name that is no column is a string literal (SQLite's\n * DQS misfeature, off in this build).\n */\n #enableDqs(): void {\n const capi = this.#sqlite3?.capi;\n if (!capi?.sqlite3_db_config) return;\n const db = (this.#db as unknown as { pointer: number }).pointer;\n capi.sqlite3_db_config(db, capi.SQLITE_DBCONFIG_DQS_DML, 1, 0);\n capi.sqlite3_db_config(db, capi.SQLITE_DBCONFIG_DQS_DDL, 1, 0);\n }\n\n /** Installs the page hook on the current main database file (see wasm-page-hook.ts). */\n #hookPages(): void {\n this.#pageHook = undefined;\n if (!this.#sqlite3?.capi?.sqlite3_file_control) return;\n try {\n this.#pageHook = installPageHook(\n this.#sqlite3,\n (this.#db as unknown as { pointer: number }).pointer,\n );\n } catch {\n this.#pageHook = undefined;\n }\n }\n /** Pages written since the previous call — a committed transaction's changed pages. */\n takeDirtyPages(): DirtyPages | undefined {\n if (!this.#pageHook) return undefined;\n const pageSize = Number(this.exec(\"PRAGMA page_size\", []).rows[0][0]);\n const pageCount = Number(this.exec(\"PRAGMA page_count\", []).rows[0][0]);\n return this.#pageHook.take(pageSize, pageCount);\n }\n discardDirtyPages(): void {\n this.#pageHook?.discard();\n }\n /**\n * WASM builds cannot load extensions at run time; an extension must be compiled into the\n * build (e.g. the `sqlite-vec-wasm-demo` package for \"sqlite_vec\"). This only checks it is.\n */\n loadExtension(name: string): void {\n const probe: Record<string, string> = { sqlite_vec: \"SELECT vec_version()\" };\n if (!probe[name]) throw new Error(`Unknown SQLite extension \"${name}\"`);\n try {\n this.exec(probe[name], []);\n } catch {\n throw new Error(`SQLite extension \"${name}\" is not compiled into this SQLite WASM build`);\n }\n }\n restore(image: Uint8Array): void {\n // celld's databases are in WAL mode (header bytes 18–19 = 2); the WASM VFSes (memdb,\n // opfs-sahpool) have no shared memory for WAL and refuse to open such a file. The pages\n // are the same in rollback-journal mode: switch the header to 1 (celld sets WAL again\n // when it opens the database).\n if (image.length > 19 && image[18] === 2 && image[19] === 2) {\n image = image.slice();\n image[18] = 1;\n image[19] = 1;\n }\n this.#db = this.#hooks!.restore(this.#db, image);\n this.#enableDqs();\n this.#hookPages(); // a new database file behind the connection\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 return { ...last, rowsWritten: written };\n }\n #one(sql: string, params: unknown[]): SqlResult {\n const st = this.#db.prepare(sql);\n try {\n if (params.length) st.bind(params.map(toSqlite));\n const columnNames = st.columnCount ? st.getColumnNames() : [];\n const rows: unknown[][] = [];\n while (st.step()) if (columnNames.length) rows.push(st.get([]).map(fromSqlite));\n return { columnNames, rows, rowsWritten: columnNames.length ? 0 : this.#db.changes() };\n } finally {\n st.finalize();\n }\n }\n inTransaction(): boolean {\n const pointer = (this.#db as unknown as { pointer: number }).pointer;\n return this.#capi!.sqlite3_get_autocommit(pointer) === 0;\n }\n databaseSize(): number {\n const pc = this.exec(\"PRAGMA page_count\", []).rows[0][0];\n const ps = this.exec(\"PRAGMA page_size\", []).rows[0][0];\n return Number(pc) * Number(ps);\n }\n close(): void {\n this.#db.close();\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}\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/** In-memory WASM databases (tests, ephemeral cells). `sqlite3` = await sqlite3InitModule(). */\n/** Image hooks for an in-memory DB: `sqlite3_js_db_export` / `sqlite3_deserialize`. */\nexport function memoryImageHooks(sqlite3: any): WasmImageHooks {\n const { capi, wasm } = sqlite3;\n return {\n image: (db) => sqlite3.capi.sqlite3_js_db_export(db),\n restore(db, image) {\n const p = wasm.allocFromTypedArray(image);\n const rc = capi.sqlite3_deserialize(\n (db as any).pointer,\n \"main\",\n p,\n image.byteLength,\n image.byteLength,\n capi.SQLITE_DESERIALIZE_FREEONCLOSE | capi.SQLITE_DESERIALIZE_RESIZEABLE,\n );\n if (rc) throw new Error(`sqlite3_deserialize failed: ${capi.sqlite3_js_rc_str?.(rc) ?? rc}`);\n return db;\n },\n };\n}\n\n/**\n * Sets up a new SQLite WASM connection for a cell. One worker holds many small cells, and SQLite\n * WASM's heap is capped at 2 GiB and never shrinks, so each connection's fixed memory decides how\n * many cells can be open at once:\n * - no lookaside: SQLite's per-connection allocator of small blocks, about 45 KiB each;\n * - 4 KiB pages for a database that is still empty (celld's page size: SQLite's native default;\n * the WASM build's is 8 KiB). The page cache starts with a block of pages, so the page size\n * sets most of a connection's memory. A database that already holds data keeps its page size.\n * Together they bring a connection with one small table from about 258 KiB to about 120 KiB.\n */\nexport function tuneCellConnection(sqlite3: unknown, db: Oo1Db): void {\n const capi = (sqlite3 as { capi?: Record<string, any> }).capi;\n const pointer = (db as unknown as { pointer?: number }).pointer;\n // Before the connection runs any statement: SQLite refuses (SQLITE_BUSY) while lookaside is used.\n if (capi?.sqlite3_db_config && pointer) {\n capi.sqlite3_db_config(pointer, capi.SQLITE_DBCONFIG_LOOKASIDE, 0, 0, 0);\n }\n db.exec(\"PRAGMA page_size=4096\");\n}\n\nlet memdbCounter = 0;\nexport function wasmMemoryOpener(sqlite3: {\n oo1: { DB: new (path: string, mode?: string) => Oo1Db };\n}): OpenCellSql {\n return () => {\n // memdb (not \":memory:\"): an in-memory database that IS a VFS file, so the page hook works.\n const db = new sqlite3.oo1.DB(`file:/cells-mem-${++memdbCounter}?vfs=memdb`, \"c\");\n tuneCellConnection(sqlite3, db);\n return new WasmSqliteDriver(db, memoryImageHooks(sqlite3), sqlite3 as never);\n };\n}\n\n/**\n * Browser persistence: one OPFS \"SyncAccessHandle pool\" database per cell.\n * Must run in a Dedicated Worker; needs neither COOP/COEP nor SharedArrayBuffer.\n * Not runnable in Node (installOpfsSAHPoolVfs is browser-only) — covered by P04's Playwright spec.\n */\nexport async function sahpoolOpener(\n sqlite3: {\n installOpfsSAHPoolVfs(opts: { name?: string; initialCapacity?: number }): Promise<{\n OpfsSAHPoolDb: new (path: string) => Oo1Db;\n reserveMinimumCapacity(n: number): Promise<number>;\n addCapacity?(n: number): Promise<number>;\n getCapacity(): number;\n getFileCount(): number;\n getFileNames?(): string[];\n // Not in older SQLite WASM builds (sqlite-vec's): there `release()` does nothing.\n pauseVfs?(): unknown;\n isPaused?(): boolean;\n unpauseVfs?(): Promise<unknown>;\n }>;\n },\n opts: { name?: string; initialCapacity?: number } = {},\n): Promise<OpenCellSql & { reserve(n: number): Promise<number>; release(): void }> {\n const pool = await sqlite3.installOpfsSAHPoolVfs({\n name: opts.name ?? \"cellb\",\n initialCapacity: opts.initialCapacity ?? 16,\n });\n // The same pool again in this worker (SQLite WASM keeps one per name), after a `release()`.\n if (pool.isPaused?.()) await pool.unpauseVfs?.();\n // A full pool fails the open (`SAH pool is full`), and opening is synchronous while growing\n // the pool is not. Each new slot is an OPFS file with a sync access handle, made one at a time,\n // and once the worker is busy each one takes seconds (about 5 s with 32 Flue conversations in\n // one worker), so the pool grows ahead of need, in small steps:\n // - a cell about to open reserves room first (`room`, awaited by the runtime before the cell's\n // first event): its database, its journal and a spare, and it waits until what was needed\n // when it asked fits, one step of 16 slots at a time (not a doubling, nor later reservations);\n // - an open cell counts its journal until it has one (SQLite makes it at the cell's first\n // write), so the pool grows while it still has room, not once the journals fill it;\n // - at each open (failed ones too: a cell can reopen without `room`), the pool grows in the\n // background until two thirds of it are free: an unused slot costs a small file, a missing\n // one a wait of seconds per slot, so it grows while the worker is still quiet.\n const SLOTS_PER_CELL = 3; // the database, its journal, and one spare\n const STEP = 16; // slots per growth step, made in parallel\n const AHEAD = 3; // the pool's size, in multiples of what it needs, kept by the background growth\n const reserved = new Set<string>();\n const opened = new Map<string, { path: string }>(); // the open cells' databases\n let growing: Promise<void> | undefined;\n /**\n * One growth step toward `target` (one at a time: a second caller joins it). SQLite makes a\n * pool's new slots one after another, two awaited OPFS calls each, and in a busy worker each\n * await waits seconds for the event loop: the step asks for its slots in parallel instead,\n * one `addCapacity(1)` each, so it costs about one slot's wait.\n */\n const step = (target: number): Promise<void> => {\n const n = Math.min(target, pool.getCapacity() + STEP) - pool.getCapacity();\n growing ??= (\n pool.addCapacity && n > 0\n ? Promise.all(Array.from({ length: n }, () => pool.addCapacity?.(1)))\n : pool.reserveMinimumCapacity(pool.getCapacity() + Math.max(n, 0))\n )\n .then(() => {})\n .finally(() => {\n growing = undefined;\n });\n return growing;\n };\n const needed = () => {\n // Journals not made yet. Without the pool's file names (older SQLite WASM builds) none\n // is counted, as before: the files that exist are.\n let journals = 0;\n if (pool.getFileNames) {\n const files = new Set(pool.getFileNames());\n for (const { path } of opened.values()) if (!files.has(`${path}-journal`)) journals++;\n }\n return pool.getFileCount() + journals + SLOTS_PER_CELL * reserved.size;\n };\n /** Grows until `target()` fits; stops when a step of its own adds nothing (the open says why). */\n const growTo = async (target: () => number): Promise<void> => {\n while (target() > pool.getCapacity()) {\n // A step already under way may have added its slots and not ended yet: joining it says\n // nothing about whether the pool can grow.\n const joined = growing !== undefined;\n const before = pool.getCapacity();\n await step(target());\n if (!joined && pool.getCapacity() <= before) return;\n }\n };\n const room = (cellKey: string): Promise<void> | undefined => {\n reserved.add(cellKey);\n const target = needed();\n if (target <= pool.getCapacity()) return undefined;\n return growTo(() => target);\n };\n let ahead: Promise<void> | undefined;\n const keepRoom = () => {\n if (ahead || pool.getCapacity() >= AHEAD * needed()) return;\n ahead = growTo(() => AHEAD * needed())\n .catch(() => {})\n .finally(() => {\n ahead = undefined;\n });\n };\n // The rollback journal stays in the pool between transactions (PERSIST): DELETE gives its pool\n // slot back and takes it again on every commit, which costs a header write each time. The\n // database file is the same in both modes (only WAL marks it).\n const openDb = (path: string) => {\n const db = new pool.OpfsSAHPoolDb(path);\n tuneCellConnection(sqlite3, db);\n db.exec(\"PRAGMA journal_mode=PERSIST\");\n return db;\n };\n const open: OpenCellSql = (cellKey) => {\n const path = `/${cellKey.replace(/[^\\w.-]/g, \"_\")}.sqlite`;\n reserved.delete(cellKey);\n // Counted from now on, with its journal until it has one; no longer once it is closed.\n const entry = { path };\n opened.set(cellKey, entry);\n keepRoom();\n let driver: WasmSqliteDriver;\n try {\n driver = new WasmSqliteDriver(\n openDb(path),\n {\n image: (db) => (sqlite3 as any).capi.sqlite3_js_db_export(db),\n // Replace the file inside the pool, then reopen it.\n restore(db, image) {\n db.close();\n (pool as any).importDb(path, image);\n return openDb(path);\n },\n },\n sqlite3 as never,\n );\n } catch (err) {\n if (opened.get(cellKey) === entry) opened.delete(cellKey);\n throw err;\n }\n const close = driver.close.bind(driver);\n driver.close = () => {\n // Only this driver's entry: the cell may have been opened again since.\n if (opened.get(cellKey) === entry) opened.delete(cellKey);\n close();\n };\n return driver;\n };\n return Object.assign(open, {\n room,\n reserve: (n: number) => pool.reserveMinimumCapacity(n),\n /**\n * Gives the pool's OPFS files back once every database is closed, so that another worker\n * (another tab's) can open the pool at once instead of when this worker is gone.\n */\n release: () => void pool.pauseVfs?.(),\n });\n}\n"]}
@@ -1,5 +1,6 @@
1
1
  import { moduleText, outboundFetch, } from "../bindings/worker-loader.js";
2
2
  import { ctxExports } from "../core/handles.js";
3
+ import { rewriteModuleSpecifiers } from "./module-specifiers.js";
3
4
  import { workerScope } from "./worker-scope.js";
4
5
  const WORKERS_CLASSES = "const m = globalThis.__cellsWorkersModule; export const DurableObject = m.DurableObject, WorkerEntrypoint = m.WorkerEntrypoint, WorkflowEntrypoint = m.WorkflowEntrypoint, RpcTarget = m.RpcTarget; export default m;";
5
6
  /**
@@ -59,6 +60,9 @@ export function browserModuleLoader(workersModuleShim) {
59
60
  consolePrelude += `const fetch = globalThis.__cellsOutbound[${JSON.stringify(key)}];\n`;
60
61
  }
61
62
  const urls = new Map();
63
+ // Modules whose Blob URL is being made: a Blob URL exists only once its text (with its
64
+ // imports' URLs) does, so modules that import each other cannot be loaded this way.
65
+ const making = [];
62
66
  const source = (name) => {
63
67
  const m = code.modules[name];
64
68
  if (m === undefined)
@@ -69,16 +73,22 @@ export function browserModuleLoader(workersModuleShim) {
69
73
  const known = urls.get(name);
70
74
  if (known)
71
75
  return known;
76
+ if (making.includes(name))
77
+ throw new Error(`Worker Loader: modules import each other (${[...making.slice(making.indexOf(name)), name].join(" → ")}); a browser realm loads each module from a Blob URL, which cannot form a cycle`);
78
+ making.push(name);
72
79
  const base = name.includes("/") ? name.slice(0, name.lastIndexOf("/") + 1) : "";
73
- const text = source(name).replace(/(\bfrom\s*|\bimport\s*\(?\s*)(["'])([^"']+)\2/g, (all, pre, q, spec) => {
80
+ // Only the module's own imports: the same words in a string, a template or a comment are
81
+ // not imports (a code generator's template may well contain `from "cloudflare:workers"`).
82
+ const text = rewriteModuleSpecifiers(source(name), (spec) => {
74
83
  if (spec === "cloudflare:workers")
75
- return `${pre}${q}${shimUrl}${q}`;
84
+ return shimUrl;
76
85
  if (spec.startsWith("./") || spec.startsWith("../"))
77
- return `${pre}${q}${urlOf(new URL(spec, `file:///${base}`).pathname.slice(1))}${q}`;
78
- return all;
86
+ return urlOf(new URL(spec, `file:///${base}`).pathname.slice(1));
87
+ return undefined;
79
88
  });
80
89
  const url = URL.createObjectURL(new Blob([consolePrelude + text], { type: "text/javascript" }));
81
90
  urls.set(name, url);
91
+ making.pop();
82
92
  return url;
83
93
  };
84
94
  const module = (await import(/* @vite-ignore */ urlOf(code.mainModule)));
@@ -1 +1 @@
1
- {"version":3,"file":"browser-modules.js","sourceRoot":"","sources":["../../src/project/browser-modules.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n type LogSink,\n type ModuleLoader,\n moduleText,\n outboundFetch,\n type WorkerCode,\n} from \"../bindings/worker-loader.js\";\nimport { ctxExports } from \"../core/handles.js\";\nimport { workerScope } from \"./worker-scope.js\";\n\nconst WORKERS_CLASSES =\n \"const m = globalThis.__cellsWorkersModule; export const DurableObject = m.DurableObject, WorkerEntrypoint = m.WorkerEntrypoint, WorkflowEntrypoint = m.WorkflowEntrypoint, RpcTarget = m.RpcTarget; export default m;\";\n\n/**\n * `cloudflare:workers` for a project's own bundle: the classes, and the project's `env` and\n * `exports` from `globalThis.__cellsProjectScope` (set by `browserProjectRuntime`, and by\n * `serveIsolate` for the loaded Worker an isolate runs). The shim is\n * bundled with the Worker, so it declares no names beyond its exports: esbuild would rename the\n * Worker's own variables of the same name, and those names show in error messages.\n */\nexport const PROJECT_WORKERS_MODULE_SHIM = `${WORKERS_CLASSES} export const env = globalThis.__cellsProjectScope?.env ?? {}, exports = globalThis.__cellsProjectScope?.exports ?? {}, waitUntil = (p) => globalThis.__cellsProjectScope?.waitUntil(p);`;\n\n/**\n * Worker Loader in a browser realm: each module of a WorkerCode becomes a Blob-URL ES\n * module. `cloudflare:workers` resolves to a shim reading the runtime's own classes from\n * `globalThis.__cellsWorkersModule` and the loaded code's own `env` and `exports` (or to\n * `workersModuleShim`, the same for every load); relative imports resolve to the Blob URLs of\n * sibling modules; with a log sink the module gets a module-scoped `console` (Tail Workers).\n */\nexport function browserModuleLoader(workersModuleShim?: string): ModuleLoader {\n const blob = (text: string) => URL.createObjectURL(new Blob([text], { type: \"text/javascript\" }));\n const fixedShimUrl = workersModuleShim && blob(workersModuleShim);\n return async (_id: string, code: WorkerCode, logs?: LogSink) => {\n const env = code.env ?? {};\n const scope = fixedShimUrl ? undefined : workerScope(env);\n let shimUrl = fixedShimUrl;\n if (scope) {\n const key = `s${Math.random().toString(36).slice(2)}`;\n const g = globalThis as Record<string, unknown>;\n g.__cellsWorkerScopes ??= {};\n (g.__cellsWorkerScopes as Record<string, unknown>)[key] = scope;\n shimUrl = blob(\n `${WORKERS_CLASSES} const s = globalThis.__cellsWorkerScopes[${JSON.stringify(key)}]; export const env = s.env, exports = s.exports, waitUntil = s.waitUntil;`,\n );\n }\n let consolePrelude = \"\";\n if (logs) {\n const key = `c${Math.random().toString(36).slice(2)}`;\n const g = globalThis as Record<string, unknown>;\n g.__cellsConsoles ??= {};\n const real = globalThis.console;\n const wrap =\n (level: \"log\" | \"info\" | \"warn\" | \"error\" | \"debug\") =>\n (...args: unknown[]) => {\n logs(level, args);\n real[level](...args);\n };\n (g.__cellsConsoles as Record<string, unknown>)[key] = Object.assign(Object.create(real), {\n log: wrap(\"log\"),\n info: wrap(\"info\"),\n warn: wrap(\"warn\"),\n error: wrap(\"error\"),\n debug: wrap(\"debug\"),\n });\n consolePrelude = `const console = globalThis.__cellsConsoles[${JSON.stringify(key)}];\\n`;\n }\n const outbound = outboundFetch(code);\n if (outbound) {\n const key = `o${Math.random().toString(36).slice(2)}`;\n const g = globalThis as Record<string, unknown>;\n g.__cellsOutbound ??= {};\n (g.__cellsOutbound as Record<string, unknown>)[key] = outbound;\n consolePrelude += `const fetch = globalThis.__cellsOutbound[${JSON.stringify(key)}];\\n`;\n }\n const urls = new Map<string, string>();\n const source = (name: string) => {\n const m = code.modules[name];\n if (m === undefined) throw new Error(`Worker Loader: module \"${name}\" not found`);\n return moduleText(m);\n };\n const urlOf = (name: string): string => {\n const known = urls.get(name);\n if (known) return known;\n const base = name.includes(\"/\") ? name.slice(0, name.lastIndexOf(\"/\") + 1) : \"\";\n const text = source(name).replace(\n /(\\bfrom\\s*|\\bimport\\s*\\(?\\s*)([\"'])([^\"']+)\\2/g,\n (all, pre, q, spec: string) => {\n if (spec === \"cloudflare:workers\") return `${pre}${q}${shimUrl}${q}`;\n if (spec.startsWith(\"./\") || spec.startsWith(\"../\"))\n return `${pre}${q}${urlOf(new URL(spec, `file:///${base}`).pathname.slice(1))}${q}`;\n return all;\n },\n );\n const url = URL.createObjectURL(\n new Blob([consolePrelude + text], { type: \"text/javascript\" }),\n );\n urls.set(name, url);\n return url;\n };\n const module = (await import(/* @vite-ignore */ urlOf(code.mainModule))) as Record<\n string,\n unknown\n >;\n scope?.bind({ env, exports: ctxExports(module, env) });\n return module;\n };\n}\n"]}
1
+ {"version":3,"file":"browser-modules.js","sourceRoot":"","sources":["../../src/project/browser-modules.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n type LogSink,\n type ModuleLoader,\n moduleText,\n outboundFetch,\n type WorkerCode,\n} from \"../bindings/worker-loader.js\";\nimport { ctxExports } from \"../core/handles.js\";\nimport { rewriteModuleSpecifiers } from \"./module-specifiers.js\";\nimport { workerScope } from \"./worker-scope.js\";\n\nconst WORKERS_CLASSES =\n \"const m = globalThis.__cellsWorkersModule; export const DurableObject = m.DurableObject, WorkerEntrypoint = m.WorkerEntrypoint, WorkflowEntrypoint = m.WorkflowEntrypoint, RpcTarget = m.RpcTarget; export default m;\";\n\n/**\n * `cloudflare:workers` for a project's own bundle: the classes, and the project's `env` and\n * `exports` from `globalThis.__cellsProjectScope` (set by `browserProjectRuntime`, and by\n * `serveIsolate` for the loaded Worker an isolate runs). The shim is\n * bundled with the Worker, so it declares no names beyond its exports: esbuild would rename the\n * Worker's own variables of the same name, and those names show in error messages.\n */\nexport const PROJECT_WORKERS_MODULE_SHIM = `${WORKERS_CLASSES} export const env = globalThis.__cellsProjectScope?.env ?? {}, exports = globalThis.__cellsProjectScope?.exports ?? {}, waitUntil = (p) => globalThis.__cellsProjectScope?.waitUntil(p);`;\n\n/**\n * Worker Loader in a browser realm: each module of a WorkerCode becomes a Blob-URL ES\n * module. `cloudflare:workers` resolves to a shim reading the runtime's own classes from\n * `globalThis.__cellsWorkersModule` and the loaded code's own `env` and `exports` (or to\n * `workersModuleShim`, the same for every load); relative imports resolve to the Blob URLs of\n * sibling modules; with a log sink the module gets a module-scoped `console` (Tail Workers).\n */\nexport function browserModuleLoader(workersModuleShim?: string): ModuleLoader {\n const blob = (text: string) => URL.createObjectURL(new Blob([text], { type: \"text/javascript\" }));\n const fixedShimUrl = workersModuleShim && blob(workersModuleShim);\n return async (_id: string, code: WorkerCode, logs?: LogSink) => {\n const env = code.env ?? {};\n const scope = fixedShimUrl ? undefined : workerScope(env);\n let shimUrl = fixedShimUrl;\n if (scope) {\n const key = `s${Math.random().toString(36).slice(2)}`;\n const g = globalThis as Record<string, unknown>;\n g.__cellsWorkerScopes ??= {};\n (g.__cellsWorkerScopes as Record<string, unknown>)[key] = scope;\n shimUrl = blob(\n `${WORKERS_CLASSES} const s = globalThis.__cellsWorkerScopes[${JSON.stringify(key)}]; export const env = s.env, exports = s.exports, waitUntil = s.waitUntil;`,\n );\n }\n let consolePrelude = \"\";\n if (logs) {\n const key = `c${Math.random().toString(36).slice(2)}`;\n const g = globalThis as Record<string, unknown>;\n g.__cellsConsoles ??= {};\n const real = globalThis.console;\n const wrap =\n (level: \"log\" | \"info\" | \"warn\" | \"error\" | \"debug\") =>\n (...args: unknown[]) => {\n logs(level, args);\n real[level](...args);\n };\n (g.__cellsConsoles as Record<string, unknown>)[key] = Object.assign(Object.create(real), {\n log: wrap(\"log\"),\n info: wrap(\"info\"),\n warn: wrap(\"warn\"),\n error: wrap(\"error\"),\n debug: wrap(\"debug\"),\n });\n consolePrelude = `const console = globalThis.__cellsConsoles[${JSON.stringify(key)}];\\n`;\n }\n const outbound = outboundFetch(code);\n if (outbound) {\n const key = `o${Math.random().toString(36).slice(2)}`;\n const g = globalThis as Record<string, unknown>;\n g.__cellsOutbound ??= {};\n (g.__cellsOutbound as Record<string, unknown>)[key] = outbound;\n consolePrelude += `const fetch = globalThis.__cellsOutbound[${JSON.stringify(key)}];\\n`;\n }\n const urls = new Map<string, string>();\n // Modules whose Blob URL is being made: a Blob URL exists only once its text (with its\n // imports' URLs) does, so modules that import each other cannot be loaded this way.\n const making: string[] = [];\n const source = (name: string) => {\n const m = code.modules[name];\n if (m === undefined) throw new Error(`Worker Loader: module \"${name}\" not found`);\n return moduleText(m);\n };\n const urlOf = (name: string): string => {\n const known = urls.get(name);\n if (known) return known;\n if (making.includes(name))\n throw new Error(\n `Worker Loader: modules import each other (${[...making.slice(making.indexOf(name)), name].join(\" → \")}); a browser realm loads each module from a Blob URL, which cannot form a cycle`,\n );\n making.push(name);\n const base = name.includes(\"/\") ? name.slice(0, name.lastIndexOf(\"/\") + 1) : \"\";\n // Only the module's own imports: the same words in a string, a template or a comment are\n // not imports (a code generator's template may well contain `from \"cloudflare:workers\"`).\n const text = rewriteModuleSpecifiers(source(name), (spec) => {\n if (spec === \"cloudflare:workers\") return shimUrl;\n if (spec.startsWith(\"./\") || spec.startsWith(\"../\"))\n return urlOf(new URL(spec, `file:///${base}`).pathname.slice(1));\n return undefined;\n });\n const url = URL.createObjectURL(\n new Blob([consolePrelude + text], { type: \"text/javascript\" }),\n );\n urls.set(name, url);\n making.pop();\n return url;\n };\n const module = (await import(/* @vite-ignore */ urlOf(code.mainModule))) as Record<\n string,\n unknown\n >;\n scope?.bind({ env, exports: ctxExports(module, env) });\n return module;\n };\n}\n"]}
@@ -52,6 +52,26 @@ function usesSuperOrNewTarget(fn) {
52
52
  visit(fn.body);
53
53
  return found;
54
54
  }
55
+ /**
56
+ * Whether an arrow reads `arguments` itself: an `arguments` identifier in its body, not in a
57
+ * function of its own (which has its own `arguments`). A word in a string or a comment is not.
58
+ */
59
+ function readsArguments(fn) {
60
+ let found = false;
61
+ const visit = (n) => {
62
+ if (found)
63
+ return;
64
+ if (n.type === "Identifier" && n.name === "arguments") {
65
+ found = true;
66
+ return;
67
+ }
68
+ for (const c of childrenOf(n))
69
+ if (!(isFunction(c) && c.type !== "ArrowFunctionExpression"))
70
+ visit(c);
71
+ };
72
+ visit(fn.body);
73
+ return found;
74
+ }
55
75
  const lowered = (n) => isFunction(n) && n.async && !n.generator && !usesSuperOrNewTarget(n);
56
76
  /** `code` with its async functions lowered; `code` itself when it has none or does not parse. */
57
77
  export function lowerAwait(code) {
@@ -121,8 +141,8 @@ export function lowerAwait(code) {
121
141
  if (child !== fn.body)
122
142
  return body;
123
143
  // An arrow's `arguments` is its enclosing function's: pass them on only when it reads them
124
- // (a module has none).
125
- const call = !arrow || /\barguments\b/.test(body) ? ".apply(this, arguments)" : ".call(this)";
144
+ // (a module, a class field or a static block has none, and naming it there is an error).
145
+ const call = !arrow || readsArguments(fn) ? ".apply(this, arguments)" : ".call(this)";
126
146
  if (fn.expression)
127
147
  return `__cellbAsync(function* () { return ${body}; }${call})`;
128
148
  return `{ return __cellbAsync(function* () ${body}${call}); }`;
@@ -1 +1 @@
1
- {"version":3,"file":"lower-await.js","sourceRoot":"","sources":["../../src/project/lower-await.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * `async`/`await` lowered to generators, for the modules an in-browser site serves (what\n * esbuild's `supported: { \"async-await\": false }` does for `bundleWorkerForBrowser`): each `await`\n * then resumes through `Promise.prototype.then`, which cellb's `node:async_hooks` polyfill makes\n * carry the AsyncLocalStorage stores (see `installAsyncContext`).\n *\n * async function f(a) { BODY } → function f(a) { return __cellbAsync(function* () { BODY }.apply(this, arguments)); }\n * async (a) => EXPR → (a) => __cellbAsync(function* () { return EXPR; }.call(this))\n * await X → (yield X)\n * for await (L of R) BODY → a loop over `yield it.next()`, closing the iterator on exit\n *\n * Left as they are: async generators (their awaits stay native, as their bodies), functions that\n * use `super` or `new.target` (a generator nested in them cannot), and top-level await. Their\n * awaits resume in the polyfill's root frame: they see no store rather than another event's.\n */\nimport { type Node, parse } from \"acorn\";\n\n// biome-ignore lint/suspicious/noExplicitAny: walking acorn's ESTree nodes by their properties\ntype AnyNode = Node & Record<string, any>;\n\nconst ASYNC_RUNNER =\n 'function __cellbAsync(g){return new Promise(function(res,rej){function step(m,v){var r;try{r=g[m](v)}catch(e){rej(e);return}if(r.done)res(r.value);else Promise.resolve(r.value).then(function(x){step(\"next\",x)},function(e){step(\"throw\",e)})}step(\"next\")})}';\nconst ASYNC_ITERATOR =\n \"function __cellbIter(o){var m=o[Symbol.asyncIterator];if(m)return m.call(o);var it=o[Symbol.iterator](),w=function(r){return Promise.resolve(r.value).then(function(x){return{value:x,done:r.done}})};return{next:function(v){return w(it.next(v))},return:it.return&&function(v){return w(it.return(v))}}}\";\n\nconst isFunction = (n: AnyNode) =>\n n.type === \"FunctionDeclaration\" ||\n n.type === \"FunctionExpression\" ||\n n.type === \"ArrowFunctionExpression\";\n\nfunction childrenOf(node: AnyNode): AnyNode[] {\n const out: AnyNode[] = [];\n for (const key in node) {\n if (key === \"type\" || key === \"start\" || key === \"end\" || key === \"loc\" || key === \"range\")\n continue;\n const v = node[key];\n if (Array.isArray(v)) {\n for (const c of v) if (c && typeof c.type === \"string\") out.push(c);\n } else if (v && typeof v.type === \"string\") out.push(v);\n }\n return out.sort((a, b) => a.start - b.start);\n}\n\n/** `super` or `new.target` in `fn`'s own body (not in the functions it declares). */\nfunction usesSuperOrNewTarget(fn: AnyNode): boolean {\n let found = false;\n const visit = (n: AnyNode) => {\n if (found) return;\n if (n.type === \"Super\" || n.type === \"MetaProperty\") {\n found = true;\n return;\n }\n for (const c of childrenOf(n))\n if (!(isFunction(c) && c.type !== \"ArrowFunctionExpression\") && c.type !== \"ClassBody\")\n visit(c);\n };\n visit(fn.body);\n return found;\n}\n\nconst lowered = (n: AnyNode) =>\n isFunction(n) && n.async && !n.generator && !usesSuperOrNewTarget(n);\n\n/** `code` with its async functions lowered; `code` itself when it has none or does not parse. */\nexport function lowerAwait(code: string): string {\n if (!/\\basync\\b/.test(code)) return code;\n let program: AnyNode;\n try {\n program = parse(code, {\n ecmaVersion: \"latest\",\n sourceType: \"module\",\n allowHashBang: true,\n }) as AnyNode;\n } catch {\n return code;\n }\n let changed = false;\n let iterUsed = false;\n let temp = 0;\n\n // `inLowered`: whether `await` here belongs to a function being lowered.\n const render = (node: AnyNode, inLowered: boolean): string => {\n if (isFunction(node)) return renderFunction(node);\n if (inLowered && node.type === \"AwaitExpression\")\n return `(yield ${render(node.argument, inLowered)})`;\n if (inLowered && node.type === \"ForOfStatement\" && node.await)\n return renderForAwait(node, \"\", inLowered);\n if (inLowered && node.type === \"LabeledStatement\") {\n let labels = \"\";\n let body = node;\n while (body.type === \"LabeledStatement\") {\n labels += `${body.label.name}: `;\n body = body.body;\n }\n if (body.type === \"ForOfStatement\" && body.await)\n return renderForAwait(body, labels, inLowered);\n }\n let text = generic(node, inLowered);\n // `async` of a lowered method sits before its key (`static async [k]() {}`).\n if (\n (node.type === \"MethodDefinition\" || (node.type === \"Property\" && node.method)) &&\n lowered(node.value)\n ) {\n const head = node.key.start - node.start - (node.computed ? 1 : 0);\n const prefix = text.slice(0, head).replace(/\\basync\\b\\s*/, \"\");\n text = prefix + text.slice(head);\n }\n return text;\n };\n\n const generic = (\n node: AnyNode,\n inLowered: boolean,\n wrap?: (child: AnyNode, text: string) => string,\n ): string => {\n let out = \"\";\n let at = node.start;\n for (const c of childrenOf(node)) {\n if (c.start < at) continue; // the same node twice (a shorthand property's key and value)\n out += code.slice(at, c.start);\n const text = render(c, inLowered);\n out += wrap ? wrap(c, text) : text;\n at = c.end;\n }\n return out + code.slice(at, node.end);\n };\n\n const renderFunction = (fn: AnyNode): string => {\n if (!lowered(fn)) return generic(fn, false);\n changed = true;\n const arrow = fn.type === \"ArrowFunctionExpression\";\n const text = generic(fn, true, (child, body) => {\n if (child !== fn.body) return body;\n // An arrow's `arguments` is its enclosing function's: pass them on only when it reads them\n // (a module has none).\n const call = !arrow || /\\barguments\\b/.test(body) ? \".apply(this, arguments)\" : \".call(this)\";\n if (fn.expression) return `__cellbAsync(function* () { return ${body}; }${call})`;\n return `{ return __cellbAsync(function* () ${body}${call}); }`;\n });\n return text.replace(/^async\\b\\s*/, \"\");\n };\n\n const renderForAwait = (loop: AnyNode, labels: string, inLowered: boolean): string => {\n iterUsed = true;\n const n = temp++;\n const it = `__cellbIt${n}`;\n const step = `__cellbStep${n}`;\n const left = render(loop.left, inLowered);\n const assign =\n loop.left.type === \"VariableDeclaration\"\n ? `${left} = ${step}.value;`\n : `(${left} = ${step}.value);`;\n return (\n `{ const ${it} = __cellbIter(${render(loop.right, inLowered)}); let ${step}; ` +\n `try { ${labels}for (;;) { ${step} = undefined; ${step} = yield ${it}.next(); ` +\n `if (${step}.done) break; { ${assign} ${render(loop.body, inLowered)} } } } ` +\n `finally { if (${step} && !${step}.done && ${it}.return) yield ${it}.return(); } }`\n );\n };\n\n const out = render(program, false);\n if (!changed) return code;\n return `${out}\\n${ASYNC_RUNNER}\\n${iterUsed ? `${ASYNC_ITERATOR}\\n` : \"\"}`;\n}\n"]}
1
+ {"version":3,"file":"lower-await.js","sourceRoot":"","sources":["../../src/project/lower-await.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * `async`/`await` lowered to generators, for the modules an in-browser site serves (what\n * esbuild's `supported: { \"async-await\": false }` does for `bundleWorkerForBrowser`): each `await`\n * then resumes through `Promise.prototype.then`, which cellb's `node:async_hooks` polyfill makes\n * carry the AsyncLocalStorage stores (see `installAsyncContext`).\n *\n * async function f(a) { BODY } → function f(a) { return __cellbAsync(function* () { BODY }.apply(this, arguments)); }\n * async (a) => EXPR → (a) => __cellbAsync(function* () { return EXPR; }.call(this))\n * await X → (yield X)\n * for await (L of R) BODY → a loop over `yield it.next()`, closing the iterator on exit\n *\n * Left as they are: async generators (their awaits stay native, as their bodies), functions that\n * use `super` or `new.target` (a generator nested in them cannot), and top-level await. Their\n * awaits resume in the polyfill's root frame: they see no store rather than another event's.\n */\nimport { type Node, parse } from \"acorn\";\n\n// biome-ignore lint/suspicious/noExplicitAny: walking acorn's ESTree nodes by their properties\ntype AnyNode = Node & Record<string, any>;\n\nconst ASYNC_RUNNER =\n 'function __cellbAsync(g){return new Promise(function(res,rej){function step(m,v){var r;try{r=g[m](v)}catch(e){rej(e);return}if(r.done)res(r.value);else Promise.resolve(r.value).then(function(x){step(\"next\",x)},function(e){step(\"throw\",e)})}step(\"next\")})}';\nconst ASYNC_ITERATOR =\n \"function __cellbIter(o){var m=o[Symbol.asyncIterator];if(m)return m.call(o);var it=o[Symbol.iterator](),w=function(r){return Promise.resolve(r.value).then(function(x){return{value:x,done:r.done}})};return{next:function(v){return w(it.next(v))},return:it.return&&function(v){return w(it.return(v))}}}\";\n\nconst isFunction = (n: AnyNode) =>\n n.type === \"FunctionDeclaration\" ||\n n.type === \"FunctionExpression\" ||\n n.type === \"ArrowFunctionExpression\";\n\nfunction childrenOf(node: AnyNode): AnyNode[] {\n const out: AnyNode[] = [];\n for (const key in node) {\n if (key === \"type\" || key === \"start\" || key === \"end\" || key === \"loc\" || key === \"range\")\n continue;\n const v = node[key];\n if (Array.isArray(v)) {\n for (const c of v) if (c && typeof c.type === \"string\") out.push(c);\n } else if (v && typeof v.type === \"string\") out.push(v);\n }\n return out.sort((a, b) => a.start - b.start);\n}\n\n/** `super` or `new.target` in `fn`'s own body (not in the functions it declares). */\nfunction usesSuperOrNewTarget(fn: AnyNode): boolean {\n let found = false;\n const visit = (n: AnyNode) => {\n if (found) return;\n if (n.type === \"Super\" || n.type === \"MetaProperty\") {\n found = true;\n return;\n }\n for (const c of childrenOf(n))\n if (!(isFunction(c) && c.type !== \"ArrowFunctionExpression\") && c.type !== \"ClassBody\")\n visit(c);\n };\n visit(fn.body);\n return found;\n}\n\n/**\n * Whether an arrow reads `arguments` itself: an `arguments` identifier in its body, not in a\n * function of its own (which has its own `arguments`). A word in a string or a comment is not.\n */\nfunction readsArguments(fn: AnyNode): boolean {\n let found = false;\n const visit = (n: AnyNode) => {\n if (found) return;\n if (n.type === \"Identifier\" && n.name === \"arguments\") {\n found = true;\n return;\n }\n for (const c of childrenOf(n))\n if (!(isFunction(c) && c.type !== \"ArrowFunctionExpression\")) visit(c);\n };\n visit(fn.body);\n return found;\n}\n\nconst lowered = (n: AnyNode) =>\n isFunction(n) && n.async && !n.generator && !usesSuperOrNewTarget(n);\n\n/** `code` with its async functions lowered; `code` itself when it has none or does not parse. */\nexport function lowerAwait(code: string): string {\n if (!/\\basync\\b/.test(code)) return code;\n let program: AnyNode;\n try {\n program = parse(code, {\n ecmaVersion: \"latest\",\n sourceType: \"module\",\n allowHashBang: true,\n }) as AnyNode;\n } catch {\n return code;\n }\n let changed = false;\n let iterUsed = false;\n let temp = 0;\n\n // `inLowered`: whether `await` here belongs to a function being lowered.\n const render = (node: AnyNode, inLowered: boolean): string => {\n if (isFunction(node)) return renderFunction(node);\n if (inLowered && node.type === \"AwaitExpression\")\n return `(yield ${render(node.argument, inLowered)})`;\n if (inLowered && node.type === \"ForOfStatement\" && node.await)\n return renderForAwait(node, \"\", inLowered);\n if (inLowered && node.type === \"LabeledStatement\") {\n let labels = \"\";\n let body = node;\n while (body.type === \"LabeledStatement\") {\n labels += `${body.label.name}: `;\n body = body.body;\n }\n if (body.type === \"ForOfStatement\" && body.await)\n return renderForAwait(body, labels, inLowered);\n }\n let text = generic(node, inLowered);\n // `async` of a lowered method sits before its key (`static async [k]() {}`).\n if (\n (node.type === \"MethodDefinition\" || (node.type === \"Property\" && node.method)) &&\n lowered(node.value)\n ) {\n const head = node.key.start - node.start - (node.computed ? 1 : 0);\n const prefix = text.slice(0, head).replace(/\\basync\\b\\s*/, \"\");\n text = prefix + text.slice(head);\n }\n return text;\n };\n\n const generic = (\n node: AnyNode,\n inLowered: boolean,\n wrap?: (child: AnyNode, text: string) => string,\n ): string => {\n let out = \"\";\n let at = node.start;\n for (const c of childrenOf(node)) {\n if (c.start < at) continue; // the same node twice (a shorthand property's key and value)\n out += code.slice(at, c.start);\n const text = render(c, inLowered);\n out += wrap ? wrap(c, text) : text;\n at = c.end;\n }\n return out + code.slice(at, node.end);\n };\n\n const renderFunction = (fn: AnyNode): string => {\n if (!lowered(fn)) return generic(fn, false);\n changed = true;\n const arrow = fn.type === \"ArrowFunctionExpression\";\n const text = generic(fn, true, (child, body) => {\n if (child !== fn.body) return body;\n // An arrow's `arguments` is its enclosing function's: pass them on only when it reads them\n // (a module, a class field or a static block has none, and naming it there is an error).\n const call = !arrow || readsArguments(fn) ? \".apply(this, arguments)\" : \".call(this)\";\n if (fn.expression) return `__cellbAsync(function* () { return ${body}; }${call})`;\n return `{ return __cellbAsync(function* () ${body}${call}); }`;\n });\n return text.replace(/^async\\b\\s*/, \"\");\n };\n\n const renderForAwait = (loop: AnyNode, labels: string, inLowered: boolean): string => {\n iterUsed = true;\n const n = temp++;\n const it = `__cellbIt${n}`;\n const step = `__cellbStep${n}`;\n const left = render(loop.left, inLowered);\n const assign =\n loop.left.type === \"VariableDeclaration\"\n ? `${left} = ${step}.value;`\n : `(${left} = ${step}.value);`;\n return (\n `{ const ${it} = __cellbIter(${render(loop.right, inLowered)}); let ${step}; ` +\n `try { ${labels}for (;;) { ${step} = undefined; ${step} = yield ${it}.next(); ` +\n `if (${step}.done) break; { ${assign} ${render(loop.body, inLowered)} } } } ` +\n `finally { if (${step} && !${step}.done && ${it}.return) yield ${it}.return(); } }`\n );\n };\n\n const out = render(program, false);\n if (!changed) return code;\n return `${out}\\n${ASYNC_RUNNER}\\n${iterUsed ? `${ASYNC_ITERATOR}\\n` : \"\"}`;\n}\n"]}
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Rewrites the module specifiers of an ES module's own imports: `import … from "x"`,
3
+ * `import "x"`, `export … from "x"` and `import("x")`, and only those. The text is scanned as
4
+ * code, so the same words inside a string, a template literal's text, a comment or a regular
5
+ * expression are left alone (a regex over the whole text would rewrite them, e.g. a code
6
+ * generator's template that contains `from "cloudflare:workers"`).
7
+ *
8
+ * A small scanner rather than a parser: the browser module loader runs in every isolate and
9
+ * shard worker, where a full JS parser would add its whole size.
10
+ *
11
+ * `resolve(spec)` returns the replacement specifier, or undefined to keep it. A dynamic import
12
+ * is rewritten only when its argument is one literal (a string, or a template without `${`).
13
+ */
14
+ export declare function rewriteModuleSpecifiers(code: string, resolve: (spec: string) => string | undefined): string;