@statewalker/cellb 0.8.3 → 0.8.6

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.
@@ -102,6 +102,8 @@ const DDL = /^\s*(CREATE|ALTER|DROP)\b/i;
102
102
  const ROLLS_BACK = /SQLITE_(IOERR|FULL|NOMEM|BUSY|INTERRUPT|CORRUPT)|disk I\/O error|database or disk is full|out of memory|interrupted/i;
103
103
  /** Statements that may write (a conservative test: WITH … and PRAGMAs count as writes). */
104
104
  const WRITES = /^\s*(INSERT|REPLACE|UPDATE|DELETE|CREATE|ALTER|DROP|WITH|PRAGMA)\b/i;
105
+ /** Statements that surely write, for the warning of `checkWrite()`. */
106
+ const SURE_WRITES = /^\s*(INSERT|REPLACE|UPDATE|DELETE|CREATE|ALTER|DROP)\b/i;
105
107
  const WRITE_TARGET = /^\s*(?:INSERT|REPLACE)(?:\s+OR\s+\w+)?\s+INTO\s+["`[]?([\w$]+)|^\s*UPDATE(?:\s+OR\s+\w+)?\s+["`[]?([\w$]+)|^\s*DELETE\s+FROM\s+["`[]?([\w$]+)/i;
106
108
  export class SqlStorage {
107
109
  #driver;
@@ -113,6 +115,12 @@ export class SqlStorage {
113
115
  #lost;
114
116
  #holds = 0;
115
117
  #depth = 0;
118
+ /**
119
+ * Set by `ctx.storage`: true when called from another event while an async `transaction()` is
120
+ * open, where async context tells them apart.
121
+ */
122
+ outsideTransaction;
123
+ #warnedOutside = false;
116
124
  // Transaction-log capture (only when the durability port asks for it).
117
125
  #capture;
118
126
  #ddl = [];
@@ -171,6 +179,8 @@ export class SqlStorage {
171
179
  // of comments alone, is refused.
172
180
  if (lacksStatement(query))
173
181
  throw new Error("SQL code did not contain a statement.");
182
+ if (SURE_WRITES.test(query))
183
+ this.checkWrite();
174
184
  return new SqlStorageCursor(this.internalExec(query, bindings));
175
185
  }
176
186
  /**
@@ -213,6 +223,18 @@ export class SqlStorage {
213
223
  rowsWritten: this.#totalChanges() - before,
214
224
  };
215
225
  }
226
+ /**
227
+ * A synchronous write made from another event while an async `transaction()` is open lands in
228
+ * that transaction's savepoint, and is undone if it rolls back. On workerd that event would not
229
+ * run until the transaction ends; a synchronous call cannot wait, so this warns, once per
230
+ * object. (Async storage calls wait for the transaction instead.)
231
+ */
232
+ checkWrite() {
233
+ if (this.#warnedOutside || !this.outsideTransaction?.())
234
+ return;
235
+ this.#warnedOutside = true;
236
+ console.warn("cellb: a synchronous storage write from another event while storage.transaction() is open; it is undone if that transaction rolls back (on workerd the event would wait for it)");
237
+ }
216
238
  get databaseSize() {
217
239
  return this.#driver.databaseSize();
218
240
  }
@@ -1 +1 @@
1
- {"version":3,"file":"sql-storage.js","sourceRoot":"","sources":["../../src/core/sql-storage.ts"],"names":[],"mappings":"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","sourcesContent":["import type { CommitChanges, SqlResult, SyncSqlDriver } from \"../ports.js\";\nimport { LOG_SEQ_READ, LOG_SEQ_WRITE, LOG_TABLE } from \"./celld-schema.js\";\nimport { completeSqlStatements, lacksStatement } from \"./sql-split.js\";\n\nconst FORBIDDEN_TXN = /^\\s*(BEGIN|COMMIT|END|ROLLBACK|SAVEPOINT|RELEASE)\\b/i;\nconst FORBIDDEN_INTERNAL = /\\b_cf_\\w*/i;\n/** Creating or dropping an object with an internal name (any case): celld refuses it outright. */\nconst FORBIDDEN_DDL = /^\\s*(CREATE|DROP)\\b[^(]*?\\b_cf_/i;\n\n/**\n * Runs `fn` after the current macrotask, as workerd commits an implicit transaction after the\n * current turn of its event loop: the writes of every microtask until then share one commit.\n * `setImmediate` on Node; a MessageChannel message in browsers (no timer clamping).\n */\nconst afterTurn: (fn: () => void) => void = (() => {\n const g = globalThis as { setImmediate?: (fn: () => void) => unknown };\n if (typeof g.setImmediate === \"function\") return (fn) => void g.setImmediate!(fn);\n if (typeof MessageChannel === \"function\") {\n const queue: (() => void)[] = [];\n const channel = new MessageChannel();\n channel.port1.onmessage = () => {\n for (const f of queue.splice(0)) f();\n };\n return (fn) => {\n if (queue.push(fn) === 1) channel.port2.postMessage(null);\n };\n }\n return (fn) => void setTimeout(fn, 0);\n})();\n\n/** The SQL text with string literals and comments blanked out (quoted identifiers stay). */\nfunction withoutLiterals(sql: string): string {\n return sql.replace(/'(?:[^']|'')*'|--[^\\n]*|\\/\\*[\\s\\S]*?\\*\\//g, \" \");\n}\n\n/**\n * Why celld's authorizer refuses a statement at prepare time, or undefined: transaction\n * statements and internal tables. Names inside string literals and comments are values, not\n * tables: `WHERE callback = '_cf_x'` is allowed (the Agents SDK writes one).\n */\nfunction refusal(query: string): string | undefined {\n const code = withoutLiterals(query);\n if (FORBIDDEN_TXN.test(code) || FORBIDDEN_DDL.test(code)) return \"not authorized\";\n const internal = FORBIDDEN_INTERNAL.exec(code);\n if (!internal) return undefined;\n const table = internal[0];\n // celld names the first column of its own internal schema (_cf_KV starts with `scope`).\n return `access to ${table}.${table.toLowerCase() === \"_cf_kv\" ? \"scope\" : \"key\"} is prohibited`;\n}\n\n/** SQLite's own message, without the prefix SQLite WASM adds to it. */\nconst sqliteMessage = (e: unknown) =>\n String((e as Error)?.message ?? e).replace(/^SQLITE_\\w+: sqlite3 result code \\d+: /, \"\");\n\n/** `ctx.storage.sql.exec()` cursor (iterable, one-shot like the CF one). */\nexport class SqlStorageCursor<T = Record<string, unknown>> implements Iterable<T> {\n readonly columnNames: string[];\n readonly rowsRead: number;\n readonly rowsWritten: number;\n #rows: unknown[][];\n #pos = 0;\n constructor(r: SqlResult) {\n this.columnNames = r.columnNames;\n this.#rows = r.rows;\n this.rowsRead = r.rows.length;\n this.rowsWritten = r.rowsWritten;\n }\n next(): IteratorResult<T> {\n // As workerd: the final result has no `value` key ({ done: true }).\n if (this.#pos >= this.#rows.length) return { done: true } as IteratorReturnResult<undefined>;\n return { done: false, value: this.#obj(this.#rows[this.#pos++]) };\n }\n [Symbol.iterator](): Iterator<T> {\n return this;\n }\n toArray(): T[] {\n return [...this];\n }\n one(): T {\n const rest = this.toArray();\n if (rest.length !== 1)\n throw new Error(\n `Expected exactly one result from SQL query, but got ${rest.length === 0 ? \"no\" : \"multiple\"} results`,\n );\n return rest[0];\n }\n *raw(): IterableIterator<unknown[]> {\n while (this.#pos < this.#rows.length) yield this.#rows[this.#pos++];\n }\n #obj(row: unknown[]): T {\n const o: Record<string, unknown> = {};\n for (const [i, c] of this.columnNames.entries()) o[c] = row[i];\n return o as T;\n }\n}\n\n/**\n * `ctx.storage.sql` with SQLite-backed-DO semantics: every write opens an\n * IMPLICIT transaction that commits on the next microtask (i.e. when the\n * handler yields). Sync writes therefore coalesce atomically, and the\n * output gate only has to await durability of the commit.\n */\nconst DDL = /^\\s*(CREATE|ALTER|DROP)\\b/i;\n/** Errors after which SQLite may roll the whole transaction back by itself. */\nconst ROLLS_BACK =\n /SQLITE_(IOERR|FULL|NOMEM|BUSY|INTERRUPT|CORRUPT)|disk I\\/O error|database or disk is full|out of memory|interrupted/i;\n/** Statements that may write (a conservative test: WITH … and PRAGMAs count as writes). */\nconst WRITES = /^\\s*(INSERT|REPLACE|UPDATE|DELETE|CREATE|ALTER|DROP|WITH|PRAGMA)\\b/i;\nconst WRITE_TARGET =\n /^\\s*(?:INSERT|REPLACE)(?:\\s+OR\\s+\\w+)?\\s+INTO\\s+[\"`[]?([\\w$]+)|^\\s*UPDATE(?:\\s+OR\\s+\\w+)?\\s+[\"`[]?([\\w$]+)|^\\s*DELETE\\s+FROM\\s+[\"`[]?([\\w$]+)/i;\n\nexport class SqlStorage {\n #driver: SyncSqlDriver;\n #onCommit: (changes?: CommitChanges) => void;\n #inTxn = false;\n /** The implicit transaction has written (its loss must fail the commit, not pass silently). */\n #wrote = false;\n /** Why SQLite rolled the implicit transaction back by itself (after an I/O error), if it did. */\n #lost: unknown;\n #holds = 0;\n #depth = 0;\n // Transaction-log capture (only when the durability port asks for it).\n readonly #capture: boolean;\n #ddl: string[] = [];\n #statements: [string, unknown[]][] = [];\n readonly #noPk = new Map<string, boolean>();\n #changesAtBegin = 0;\n #schemaAtBegin = 0;\n #schemaVersion(): number {\n return Number(this.#driver.exec(\"PRAGMA schema_version\", []).rows[0]?.[0] ?? 0);\n }\n #totalChanges(): number {\n return Number(this.#driver.exec(\"SELECT total_changes()\", []).rows[0]?.[0] ?? 0);\n }\n\n constructor(\n driver: SyncSqlDriver,\n onCommit: (changes?: CommitChanges) => void,\n opts: { capture?: boolean } = {},\n ) {\n this.#driver = driver;\n this.#onCommit = onCommit;\n this.#capture = !!opts.capture;\n if (this.#capture && !driver.captureStart)\n throw new Error(\"This SyncSqlDriver cannot capture changes: no transaction-log durability\");\n }\n\n /** Records what the changeset cannot carry: schema changes, and writes to PK-less tables. */\n #record(query: string, bindings: unknown[]): void {\n if (!this.#capture) return;\n if (DDL.test(query)) {\n this.#ddl.push(query);\n this.#noPk.clear();\n return;\n }\n if (this.#driver.captureCoversRowidTables !== false) return;\n const m = WRITE_TARGET.exec(query);\n const table = m?.[1] ?? m?.[2] ?? m?.[3];\n if (table && this.#tableHasNoPk(table)) this.#statements.push([query, bindings]);\n }\n\n #tableHasNoPk(table: string): boolean {\n let v = this.#noPk.get(table);\n if (v === undefined) {\n const info = this.#driver.exec(`SELECT count(*) FROM pragma_table_info(?) WHERE pk > 0`, [\n table,\n ]);\n v = Number(info.rows[0]?.[0] ?? 0) === 0;\n this.#noPk.set(table, v);\n }\n return v;\n }\n\n exec<T = Record<string, unknown>>(query: string, ...bindings: unknown[]): SqlStorageCursor<T> {\n // Messages as celld words them (storage-details project).\n const refused = refusal(query);\n if (refused) throw new Error(`SQL error: prepare SQL cursor: ${refused}`);\n if (bindings.length && countParameters(query) !== bindings.length)\n throw new Error(\"SQL error: Wrong number of parameter bindings for SQL query.\");\n // Statement after statement, as workerd and celld prepare them: none at all, or a last one\n // of comments alone, is refused.\n if (lacksStatement(query)) throw new Error(\"SQL code did not contain a statement.\");\n return new SqlStorageCursor<T>(this.internalExec(query, bindings));\n }\n\n /**\n * workerd's `sql.prepare(query)`: a reusable statement, called with bindings.\n * Not in Cloudflare's public docs, but used by Miniflare's simulator objects.\n */\n prepare<T = Record<string, unknown>>(\n query: string,\n ): (...bindings: unknown[]) => SqlStorageCursor<T> {\n return (...bindings) => this.exec<T>(query, ...bindings);\n }\n\n /**\n * workerd's experimental `sql.ingest(text)`, for SQL that arrives in pieces: runs the complete\n * statements at the start of `text` and returns the rest, to be prefixed to the next piece.\n * As celld: errors read `prepare ingested SQL: …` (refused, or not valid SQL) or `step ingested\n * SQL: …` (failed while running); statements before a failing one stay applied; `rowsWritten`\n * includes trigger writes; there is no `rowsRead`.\n */\n ingest(text: string): { remainder: string; statementCount: number; rowsWritten: number } {\n const { statements, remainder } = completeSqlStatements(String(text));\n const before = this.#totalChanges();\n for (const statement of statements) {\n const refused = refusal(statement);\n if (refused) throw new Error(`SQL error: prepare ingested SQL: ${refused}`);\n try {\n this.#driver.exec(`EXPLAIN ${statement}`, []); // prepares it without running it\n } catch (e) {\n throw new Error(`SQL error: prepare ingested SQL: ${sqliteMessage(e)}`);\n }\n try {\n this.internalExec(statement);\n } catch (e) {\n throw new Error(`SQL error: step ingested SQL: ${sqliteMessage(e)}`);\n }\n }\n return {\n remainder,\n statementCount: statements.length,\n rowsWritten: this.#totalChanges() - before,\n };\n }\n\n get databaseSize(): number {\n return this.#driver.databaseSize();\n }\n\n /** Unguarded path for the runtime's own tables (_cf_KV, _cf_METADATA). */\n internalExec(query: string, bindings: unknown[] = []): SqlResult {\n this.#ensureTxn();\n let r: SqlResult;\n try {\n r = this.#driver.exec(query, bindings);\n } catch (e) {\n this.#afterFailure(e);\n throw e;\n }\n if (!this.#wrote && WRITES.test(query)) this.#wrote = true;\n this.#record(query, bindings);\n return r;\n }\n\n /**\n * After a failed statement: SQLite rolls the whole transaction back by itself on some errors\n * (I/O, disk full, out of memory). A read-only transaction lost nothing: the statement's error\n * fails the request, and the next one starts afresh. One that had written lost those writes:\n * its commit fails with the cause (the object resets, as workerd's does).\n */\n #afterFailure(e: unknown): void {\n if (!this.#inTxn) return;\n const open = this.#driver.inTransaction?.();\n if (open === true) return; // only the statement failed\n if (open === undefined) {\n // No way to ask the driver: only errors after which SQLite rolls back are checked, with a\n // statement (one that fails, and is logged by some builds, while the transaction is open).\n if (!ROLLS_BACK.test(String((e as Error)?.message ?? e))) return;\n try {\n this.#driver.exec(\"BEGIN\", []); // succeeds only outside a transaction\n } catch {\n return;\n }\n this.#driver.exec(\"ROLLBACK\", []); // the probe's own transaction\n }\n const wrote = this.#wrote;\n this.#inTxn = false;\n this.#wrote = false;\n this.#driver.discardDirtyPages?.();\n for (const f of this.rolledBack) f();\n if (this.#capture) {\n this.#driver.captureDiscard?.();\n this.#ddl = [];\n this.#statements = [];\n }\n if (wrote) this.#lost = e;\n }\n\n transactionSync<T>(fn: () => T): T {\n this.#ensureTxn();\n const sp = `_cf_sp${++this.#depth}`;\n this.#driver.exec(`SAVEPOINT ${sp}`, []);\n const marks = [this.#ddl.length, this.#statements.length] as const;\n try {\n const r = fn();\n this.#driver.exec(`RELEASE ${sp}`, []);\n return r;\n } catch (e) {\n // SQLite rolled the whole transaction back by itself (#afterFailure): no savepoint left.\n if (!this.#inTxn) throw e;\n this.#driver.exec(`ROLLBACK TO ${sp}`, []);\n for (const f of this.rolledBack) f();\n this.#ddl.length = marks[0];\n this.#statements.length = marks[1];\n this.#driver.exec(`RELEASE ${sp}`, []);\n throw e;\n } finally {\n this.#depth--;\n }\n }\n\n /** Suspends auto-commit (used by the async `transaction()` which spans awaits). */\n hold(): () => void {\n this.#holds++;\n let released = false;\n return () => {\n if (released) return;\n released = true;\n if (--this.#holds === 0) afterTurn(() => this.commit());\n };\n }\n\n /** True while an async transaction() holds auto-commit (no consistent snapshot possible). */\n get held(): boolean {\n return this.#holds > 0;\n }\n\n /** Commits the implicit transaction now (no-op if none, or if held). */\n commit(): void {\n if (this.#lost !== undefined && this.#holds === 0) {\n const cause = this.#lost;\n this.#lost = undefined;\n throw new Error(`the transaction's writes were rolled back after: ${sqliteMessage(cause)}`, {\n cause,\n });\n }\n if (!this.#inTxn || this.#holds > 0) return;\n let changes: CommitChanges | undefined;\n // Read-only transactions produce no log segment (\"IF NOT EXISTS\" DDL that changed\n // nothing included: the schema version tells).\n if (this.#capture && this.#ddl.length && this.#schemaVersion() === this.#schemaAtBegin)\n this.#ddl = [];\n if (this.#capture && (this.#ddl.length || this.#totalChanges() !== this.#changesAtBegin)) {\n // The log sequence number lives in the database, inside the same transaction.\n this.#driver.exec(LOG_TABLE, []);\n const row = this.#driver.exec(LOG_SEQ_READ, []).rows[0];\n const logSeq = Number(row?.[0] ?? 0) + 1;\n this.#driver.exec(LOG_SEQ_WRITE, [logSeq]);\n changes = {\n logSeq,\n ddl: this.#ddl,\n changeset: new Uint8Array(),\n statements: this.#statements,\n };\n }\n this.#inTxn = false;\n this.#wrote = false;\n this.#driver.exec(\"COMMIT\", []);\n if (this.#capture) {\n const changeset = this.#driver.captureTake!();\n if (changes) changes.changeset = changeset;\n this.#ddl = [];\n this.#statements = [];\n }\n this.#onCommit(changes);\n }\n\n /** Called after a rollback (lazily created internal tables may be gone). */\n readonly rolledBack = new Set<() => void>();\n\n rollback(): void {\n this.#lost = undefined;\n if (!this.#inTxn) return;\n this.#inTxn = false;\n this.#wrote = false;\n this.#driver.exec(\"ROLLBACK\", []);\n this.#driver.discardDirtyPages?.(); // pages a rolled-back transaction spilled are not a commit\n for (const f of this.rolledBack) f();\n if (this.#capture) {\n this.#driver.captureDiscard?.();\n this.#ddl = [];\n this.#statements = [];\n }\n }\n\n #ensureTxn(): void {\n if (this.#inTxn) return;\n this.#driver.exec(\"BEGIN\", []);\n if (this.#capture) {\n this.#driver.captureStart!();\n this.#changesAtBegin = this.#totalChanges();\n this.#schemaAtBegin = this.#schemaVersion();\n }\n this.#inTxn = true;\n afterTurn(() => this.commit());\n }\n}\n\n/** Parameters a statement expects: anonymous `?` count, highest `?NNN`, distinct named ones. */\nexport function countParameters(sql: string): number {\n let anon = 0;\n let maxNumbered = 0;\n const named = new Set<string>();\n const re = /'(?:[^']|'')*'|\"(?:[^\"]|\"\")*\"|--[^\\n]*|\\/\\*[\\s\\S]*?\\*\\/|\\?(\\d*)|([:@$][A-Za-z_]\\w*)/g;\n for (let m = re.exec(sql); m; m = re.exec(sql)) {\n if (m[1] !== undefined) {\n if (m[1] === \"\") anon++;\n else maxNumbered = Math.max(maxNumbered, Number(m[1]));\n } else if (m[2]) named.add(m[2]);\n }\n const positional = maxNumbered ? Math.max(maxNumbered, anon) : anon;\n return positional + named.size;\n}\n"]}
1
+ {"version":3,"file":"sql-storage.js","sourceRoot":"","sources":["../../src/core/sql-storage.ts"],"names":[],"mappings":"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","sourcesContent":["import type { CommitChanges, SqlResult, SyncSqlDriver } from \"../ports.js\";\nimport { LOG_SEQ_READ, LOG_SEQ_WRITE, LOG_TABLE } from \"./celld-schema.js\";\nimport { completeSqlStatements, lacksStatement } from \"./sql-split.js\";\n\nconst FORBIDDEN_TXN = /^\\s*(BEGIN|COMMIT|END|ROLLBACK|SAVEPOINT|RELEASE)\\b/i;\nconst FORBIDDEN_INTERNAL = /\\b_cf_\\w*/i;\n/** Creating or dropping an object with an internal name (any case): celld refuses it outright. */\nconst FORBIDDEN_DDL = /^\\s*(CREATE|DROP)\\b[^(]*?\\b_cf_/i;\n\n/**\n * Runs `fn` after the current macrotask, as workerd commits an implicit transaction after the\n * current turn of its event loop: the writes of every microtask until then share one commit.\n * `setImmediate` on Node; a MessageChannel message in browsers (no timer clamping).\n */\nconst afterTurn: (fn: () => void) => void = (() => {\n const g = globalThis as { setImmediate?: (fn: () => void) => unknown };\n if (typeof g.setImmediate === \"function\") return (fn) => void g.setImmediate!(fn);\n if (typeof MessageChannel === \"function\") {\n const queue: (() => void)[] = [];\n const channel = new MessageChannel();\n channel.port1.onmessage = () => {\n for (const f of queue.splice(0)) f();\n };\n return (fn) => {\n if (queue.push(fn) === 1) channel.port2.postMessage(null);\n };\n }\n return (fn) => void setTimeout(fn, 0);\n})();\n\n/** The SQL text with string literals and comments blanked out (quoted identifiers stay). */\nfunction withoutLiterals(sql: string): string {\n return sql.replace(/'(?:[^']|'')*'|--[^\\n]*|\\/\\*[\\s\\S]*?\\*\\//g, \" \");\n}\n\n/**\n * Why celld's authorizer refuses a statement at prepare time, or undefined: transaction\n * statements and internal tables. Names inside string literals and comments are values, not\n * tables: `WHERE callback = '_cf_x'` is allowed (the Agents SDK writes one).\n */\nfunction refusal(query: string): string | undefined {\n const code = withoutLiterals(query);\n if (FORBIDDEN_TXN.test(code) || FORBIDDEN_DDL.test(code)) return \"not authorized\";\n const internal = FORBIDDEN_INTERNAL.exec(code);\n if (!internal) return undefined;\n const table = internal[0];\n // celld names the first column of its own internal schema (_cf_KV starts with `scope`).\n return `access to ${table}.${table.toLowerCase() === \"_cf_kv\" ? \"scope\" : \"key\"} is prohibited`;\n}\n\n/** SQLite's own message, without the prefix SQLite WASM adds to it. */\nconst sqliteMessage = (e: unknown) =>\n String((e as Error)?.message ?? e).replace(/^SQLITE_\\w+: sqlite3 result code \\d+: /, \"\");\n\n/** `ctx.storage.sql.exec()` cursor (iterable, one-shot like the CF one). */\nexport class SqlStorageCursor<T = Record<string, unknown>> implements Iterable<T> {\n readonly columnNames: string[];\n readonly rowsRead: number;\n readonly rowsWritten: number;\n #rows: unknown[][];\n #pos = 0;\n constructor(r: SqlResult) {\n this.columnNames = r.columnNames;\n this.#rows = r.rows;\n this.rowsRead = r.rows.length;\n this.rowsWritten = r.rowsWritten;\n }\n next(): IteratorResult<T> {\n // As workerd: the final result has no `value` key ({ done: true }).\n if (this.#pos >= this.#rows.length) return { done: true } as IteratorReturnResult<undefined>;\n return { done: false, value: this.#obj(this.#rows[this.#pos++]) };\n }\n [Symbol.iterator](): Iterator<T> {\n return this;\n }\n toArray(): T[] {\n return [...this];\n }\n one(): T {\n const rest = this.toArray();\n if (rest.length !== 1)\n throw new Error(\n `Expected exactly one result from SQL query, but got ${rest.length === 0 ? \"no\" : \"multiple\"} results`,\n );\n return rest[0];\n }\n *raw(): IterableIterator<unknown[]> {\n while (this.#pos < this.#rows.length) yield this.#rows[this.#pos++];\n }\n #obj(row: unknown[]): T {\n const o: Record<string, unknown> = {};\n for (const [i, c] of this.columnNames.entries()) o[c] = row[i];\n return o as T;\n }\n}\n\n/**\n * `ctx.storage.sql` with SQLite-backed-DO semantics: every write opens an\n * IMPLICIT transaction that commits on the next microtask (i.e. when the\n * handler yields). Sync writes therefore coalesce atomically, and the\n * output gate only has to await durability of the commit.\n */\nconst DDL = /^\\s*(CREATE|ALTER|DROP)\\b/i;\n/** Errors after which SQLite may roll the whole transaction back by itself. */\nconst ROLLS_BACK =\n /SQLITE_(IOERR|FULL|NOMEM|BUSY|INTERRUPT|CORRUPT)|disk I\\/O error|database or disk is full|out of memory|interrupted/i;\n/** Statements that may write (a conservative test: WITH … and PRAGMAs count as writes). */\nconst WRITES = /^\\s*(INSERT|REPLACE|UPDATE|DELETE|CREATE|ALTER|DROP|WITH|PRAGMA)\\b/i;\n/** Statements that surely write, for the warning of `checkWrite()`. */\nconst SURE_WRITES = /^\\s*(INSERT|REPLACE|UPDATE|DELETE|CREATE|ALTER|DROP)\\b/i;\nconst WRITE_TARGET =\n /^\\s*(?:INSERT|REPLACE)(?:\\s+OR\\s+\\w+)?\\s+INTO\\s+[\"`[]?([\\w$]+)|^\\s*UPDATE(?:\\s+OR\\s+\\w+)?\\s+[\"`[]?([\\w$]+)|^\\s*DELETE\\s+FROM\\s+[\"`[]?([\\w$]+)/i;\n\nexport class SqlStorage {\n #driver: SyncSqlDriver;\n #onCommit: (changes?: CommitChanges) => void;\n #inTxn = false;\n /** The implicit transaction has written (its loss must fail the commit, not pass silently). */\n #wrote = false;\n /** Why SQLite rolled the implicit transaction back by itself (after an I/O error), if it did. */\n #lost: unknown;\n #holds = 0;\n #depth = 0;\n /**\n * Set by `ctx.storage`: true when called from another event while an async `transaction()` is\n * open, where async context tells them apart.\n */\n outsideTransaction?: () => boolean;\n #warnedOutside = false;\n // Transaction-log capture (only when the durability port asks for it).\n readonly #capture: boolean;\n #ddl: string[] = [];\n #statements: [string, unknown[]][] = [];\n readonly #noPk = new Map<string, boolean>();\n #changesAtBegin = 0;\n #schemaAtBegin = 0;\n #schemaVersion(): number {\n return Number(this.#driver.exec(\"PRAGMA schema_version\", []).rows[0]?.[0] ?? 0);\n }\n #totalChanges(): number {\n return Number(this.#driver.exec(\"SELECT total_changes()\", []).rows[0]?.[0] ?? 0);\n }\n\n constructor(\n driver: SyncSqlDriver,\n onCommit: (changes?: CommitChanges) => void,\n opts: { capture?: boolean } = {},\n ) {\n this.#driver = driver;\n this.#onCommit = onCommit;\n this.#capture = !!opts.capture;\n if (this.#capture && !driver.captureStart)\n throw new Error(\"This SyncSqlDriver cannot capture changes: no transaction-log durability\");\n }\n\n /** Records what the changeset cannot carry: schema changes, and writes to PK-less tables. */\n #record(query: string, bindings: unknown[]): void {\n if (!this.#capture) return;\n if (DDL.test(query)) {\n this.#ddl.push(query);\n this.#noPk.clear();\n return;\n }\n if (this.#driver.captureCoversRowidTables !== false) return;\n const m = WRITE_TARGET.exec(query);\n const table = m?.[1] ?? m?.[2] ?? m?.[3];\n if (table && this.#tableHasNoPk(table)) this.#statements.push([query, bindings]);\n }\n\n #tableHasNoPk(table: string): boolean {\n let v = this.#noPk.get(table);\n if (v === undefined) {\n const info = this.#driver.exec(`SELECT count(*) FROM pragma_table_info(?) WHERE pk > 0`, [\n table,\n ]);\n v = Number(info.rows[0]?.[0] ?? 0) === 0;\n this.#noPk.set(table, v);\n }\n return v;\n }\n\n exec<T = Record<string, unknown>>(query: string, ...bindings: unknown[]): SqlStorageCursor<T> {\n // Messages as celld words them (storage-details project).\n const refused = refusal(query);\n if (refused) throw new Error(`SQL error: prepare SQL cursor: ${refused}`);\n if (bindings.length && countParameters(query) !== bindings.length)\n throw new Error(\"SQL error: Wrong number of parameter bindings for SQL query.\");\n // Statement after statement, as workerd and celld prepare them: none at all, or a last one\n // of comments alone, is refused.\n if (lacksStatement(query)) throw new Error(\"SQL code did not contain a statement.\");\n if (SURE_WRITES.test(query)) this.checkWrite();\n return new SqlStorageCursor<T>(this.internalExec(query, bindings));\n }\n\n /**\n * workerd's `sql.prepare(query)`: a reusable statement, called with bindings.\n * Not in Cloudflare's public docs, but used by Miniflare's simulator objects.\n */\n prepare<T = Record<string, unknown>>(\n query: string,\n ): (...bindings: unknown[]) => SqlStorageCursor<T> {\n return (...bindings) => this.exec<T>(query, ...bindings);\n }\n\n /**\n * workerd's experimental `sql.ingest(text)`, for SQL that arrives in pieces: runs the complete\n * statements at the start of `text` and returns the rest, to be prefixed to the next piece.\n * As celld: errors read `prepare ingested SQL: …` (refused, or not valid SQL) or `step ingested\n * SQL: …` (failed while running); statements before a failing one stay applied; `rowsWritten`\n * includes trigger writes; there is no `rowsRead`.\n */\n ingest(text: string): { remainder: string; statementCount: number; rowsWritten: number } {\n const { statements, remainder } = completeSqlStatements(String(text));\n const before = this.#totalChanges();\n for (const statement of statements) {\n const refused = refusal(statement);\n if (refused) throw new Error(`SQL error: prepare ingested SQL: ${refused}`);\n try {\n this.#driver.exec(`EXPLAIN ${statement}`, []); // prepares it without running it\n } catch (e) {\n throw new Error(`SQL error: prepare ingested SQL: ${sqliteMessage(e)}`);\n }\n try {\n this.internalExec(statement);\n } catch (e) {\n throw new Error(`SQL error: step ingested SQL: ${sqliteMessage(e)}`);\n }\n }\n return {\n remainder,\n statementCount: statements.length,\n rowsWritten: this.#totalChanges() - before,\n };\n }\n\n /**\n * A synchronous write made from another event while an async `transaction()` is open lands in\n * that transaction's savepoint, and is undone if it rolls back. On workerd that event would not\n * run until the transaction ends; a synchronous call cannot wait, so this warns, once per\n * object. (Async storage calls wait for the transaction instead.)\n */\n checkWrite(): void {\n if (this.#warnedOutside || !this.outsideTransaction?.()) return;\n this.#warnedOutside = true;\n console.warn(\n \"cellb: a synchronous storage write from another event while storage.transaction() is open; it is undone if that transaction rolls back (on workerd the event would wait for it)\",\n );\n }\n\n get databaseSize(): number {\n return this.#driver.databaseSize();\n }\n\n /** Unguarded path for the runtime's own tables (_cf_KV, _cf_METADATA). */\n internalExec(query: string, bindings: unknown[] = []): SqlResult {\n this.#ensureTxn();\n let r: SqlResult;\n try {\n r = this.#driver.exec(query, bindings);\n } catch (e) {\n this.#afterFailure(e);\n throw e;\n }\n if (!this.#wrote && WRITES.test(query)) this.#wrote = true;\n this.#record(query, bindings);\n return r;\n }\n\n /**\n * After a failed statement: SQLite rolls the whole transaction back by itself on some errors\n * (I/O, disk full, out of memory). A read-only transaction lost nothing: the statement's error\n * fails the request, and the next one starts afresh. One that had written lost those writes:\n * its commit fails with the cause (the object resets, as workerd's does).\n */\n #afterFailure(e: unknown): void {\n if (!this.#inTxn) return;\n const open = this.#driver.inTransaction?.();\n if (open === true) return; // only the statement failed\n if (open === undefined) {\n // No way to ask the driver: only errors after which SQLite rolls back are checked, with a\n // statement (one that fails, and is logged by some builds, while the transaction is open).\n if (!ROLLS_BACK.test(String((e as Error)?.message ?? e))) return;\n try {\n this.#driver.exec(\"BEGIN\", []); // succeeds only outside a transaction\n } catch {\n return;\n }\n this.#driver.exec(\"ROLLBACK\", []); // the probe's own transaction\n }\n const wrote = this.#wrote;\n this.#inTxn = false;\n this.#wrote = false;\n this.#driver.discardDirtyPages?.();\n for (const f of this.rolledBack) f();\n if (this.#capture) {\n this.#driver.captureDiscard?.();\n this.#ddl = [];\n this.#statements = [];\n }\n if (wrote) this.#lost = e;\n }\n\n transactionSync<T>(fn: () => T): T {\n this.#ensureTxn();\n const sp = `_cf_sp${++this.#depth}`;\n this.#driver.exec(`SAVEPOINT ${sp}`, []);\n const marks = [this.#ddl.length, this.#statements.length] as const;\n try {\n const r = fn();\n this.#driver.exec(`RELEASE ${sp}`, []);\n return r;\n } catch (e) {\n // SQLite rolled the whole transaction back by itself (#afterFailure): no savepoint left.\n if (!this.#inTxn) throw e;\n this.#driver.exec(`ROLLBACK TO ${sp}`, []);\n for (const f of this.rolledBack) f();\n this.#ddl.length = marks[0];\n this.#statements.length = marks[1];\n this.#driver.exec(`RELEASE ${sp}`, []);\n throw e;\n } finally {\n this.#depth--;\n }\n }\n\n /** Suspends auto-commit (used by the async `transaction()` which spans awaits). */\n hold(): () => void {\n this.#holds++;\n let released = false;\n return () => {\n if (released) return;\n released = true;\n if (--this.#holds === 0) afterTurn(() => this.commit());\n };\n }\n\n /** True while an async transaction() holds auto-commit (no consistent snapshot possible). */\n get held(): boolean {\n return this.#holds > 0;\n }\n\n /** Commits the implicit transaction now (no-op if none, or if held). */\n commit(): void {\n if (this.#lost !== undefined && this.#holds === 0) {\n const cause = this.#lost;\n this.#lost = undefined;\n throw new Error(`the transaction's writes were rolled back after: ${sqliteMessage(cause)}`, {\n cause,\n });\n }\n if (!this.#inTxn || this.#holds > 0) return;\n let changes: CommitChanges | undefined;\n // Read-only transactions produce no log segment (\"IF NOT EXISTS\" DDL that changed\n // nothing included: the schema version tells).\n if (this.#capture && this.#ddl.length && this.#schemaVersion() === this.#schemaAtBegin)\n this.#ddl = [];\n if (this.#capture && (this.#ddl.length || this.#totalChanges() !== this.#changesAtBegin)) {\n // The log sequence number lives in the database, inside the same transaction.\n this.#driver.exec(LOG_TABLE, []);\n const row = this.#driver.exec(LOG_SEQ_READ, []).rows[0];\n const logSeq = Number(row?.[0] ?? 0) + 1;\n this.#driver.exec(LOG_SEQ_WRITE, [logSeq]);\n changes = {\n logSeq,\n ddl: this.#ddl,\n changeset: new Uint8Array(),\n statements: this.#statements,\n };\n }\n this.#inTxn = false;\n this.#wrote = false;\n this.#driver.exec(\"COMMIT\", []);\n if (this.#capture) {\n const changeset = this.#driver.captureTake!();\n if (changes) changes.changeset = changeset;\n this.#ddl = [];\n this.#statements = [];\n }\n this.#onCommit(changes);\n }\n\n /** Called after a rollback (lazily created internal tables may be gone). */\n readonly rolledBack = new Set<() => void>();\n\n rollback(): void {\n this.#lost = undefined;\n if (!this.#inTxn) return;\n this.#inTxn = false;\n this.#wrote = false;\n this.#driver.exec(\"ROLLBACK\", []);\n this.#driver.discardDirtyPages?.(); // pages a rolled-back transaction spilled are not a commit\n for (const f of this.rolledBack) f();\n if (this.#capture) {\n this.#driver.captureDiscard?.();\n this.#ddl = [];\n this.#statements = [];\n }\n }\n\n #ensureTxn(): void {\n if (this.#inTxn) return;\n this.#driver.exec(\"BEGIN\", []);\n if (this.#capture) {\n this.#driver.captureStart!();\n this.#changesAtBegin = this.#totalChanges();\n this.#schemaAtBegin = this.#schemaVersion();\n }\n this.#inTxn = true;\n afterTurn(() => this.commit());\n }\n}\n\n/** Parameters a statement expects: anonymous `?` count, highest `?NNN`, distinct named ones. */\nexport function countParameters(sql: string): number {\n let anon = 0;\n let maxNumbered = 0;\n const named = new Set<string>();\n const re = /'(?:[^']|'')*'|\"(?:[^\"]|\"\")*\"|--[^\\n]*|\\/\\*[\\s\\S]*?\\*\\/|\\?(\\d*)|([:@$][A-Za-z_]\\w*)/g;\n for (let m = re.exec(sql); m; m = re.exec(sql)) {\n if (m[1] !== undefined) {\n if (m[1] === \"\") anon++;\n else maxNumbered = Math.max(maxNumbered, Number(m[1]));\n } else if (m[2]) named.add(m[2]);\n }\n const positional = maxNumbered ? Math.max(maxNumbered, anon) : anon;\n return positional + named.size;\n}\n"]}
@@ -36,6 +36,8 @@ export declare class AsyncKvApi {
36
36
  constructor(kv: SyncKvStorage, gate: InputGate);
37
37
  /** Every async storage op closes the input gate until it (and its continuation) completes. */
38
38
  protected op<T>(fn: () => T): Promise<T>;
39
+ /** An async op that writes (`ctx.storage` holds it back while another event's transaction is open). */
40
+ protected writeOp<T>(fn: () => T): Promise<T>;
39
41
  get<T = unknown>(key: string): Promise<T | undefined>;
40
42
  get<T = unknown>(keys: string[]): Promise<Map<string, T>>;
41
43
  put(key: string, value: unknown): Promise<void>;
@@ -54,12 +56,18 @@ export declare class DurableObjectStorage extends AsyncKvApi {
54
56
  readonly sql: SqlStorage;
55
57
  readonly kv: SyncKvStorage;
56
58
  constructor(sql: SqlStorage, kv: SyncKvStorage, gate: InputGate, sync: () => Promise<void>);
57
- deleteAll(): Promise<void>;
58
- transactionSync<T>(fn: () => T): T;
59
59
  /**
60
- * Async transaction spanning awaits: the input gate stays closed (no other
61
- * event can interleave writes into the savepoint) and auto-commit is held.
60
+ * Async writes from another event wait while a transaction() is open, as that event would on
61
+ * workerd: they would otherwise land in its savepoint and be undone if it rolls back. Reads do
62
+ * not wait: an event may await, inside its transaction, a read another event started (a
63
+ * memoized load). Events are told apart by the token `runEvent()` gives each one, which needs
64
+ * async context; without it nothing waits.
62
65
  */
66
+ protected writeOp<T>(fn: () => T): Promise<T>;
67
+ /** Runs one event's handler under a token of its own (the object host delivers through it). */
68
+ runEvent<R>(fn: () => R): R;
69
+ deleteAll(): Promise<void>;
70
+ transactionSync<T>(fn: () => T): T;
63
71
  transaction<T>(closure: (txn: DurableObjectTransaction) => Promise<T>): Promise<T>;
64
72
  /** Resolves once all writes so far are durable (the output gate barrier). */
65
73
  sync(): Promise<void>;
@@ -1,4 +1,5 @@
1
1
  import { serialize } from "../v8/serialize.js";
2
+ import { nativeAsyncContext } from "./async-context.js";
2
3
  import { CELLD_SCHEMA } from "./celld-schema.js";
3
4
  /** `EXPORT` of core/handles.ts, by its registered name: importing handles.ts here is a cycle. */
4
5
  const EXPORT = Symbol.for("cellb.export");
@@ -66,6 +67,7 @@ export class SyncKvStorage {
66
67
  // Browsers prefix the engine message ("Failed to execute 'structuredClone' on …: ").
67
68
  throw new TypeError(e.message.replace(/^Failed to execute '[^']+' on '[^']+': /, ""));
68
69
  }
70
+ this.#sql.checkWrite();
69
71
  this.#sql.internalExec("INSERT OR REPLACE INTO _cf_KV (scope, k, v) VALUES (?, ?, ?)", [
70
72
  this.scope,
71
73
  key,
@@ -74,6 +76,7 @@ export class SyncKvStorage {
74
76
  }
75
77
  delete(key) {
76
78
  this.#init();
79
+ this.#sql.checkWrite();
77
80
  return (this.#sql.internalExec("DELETE FROM _cf_KV WHERE scope = ? AND k = ?", [this.scope, key])
78
81
  .rowsWritten > 0);
79
82
  }
@@ -120,6 +123,7 @@ export class SyncKvStorage {
120
123
  }
121
124
  deleteAll() {
122
125
  this.#init();
126
+ this.#sql.checkWrite();
123
127
  this.#sql.internalExec("DELETE FROM _cf_KV WHERE scope = ?", [this.scope]);
124
128
  }
125
129
  }
@@ -138,6 +142,10 @@ export class AsyncKvApi {
138
142
  return fn();
139
143
  });
140
144
  }
145
+ /** An async op that writes (`ctx.storage` holds it back while another event's transaction is open). */
146
+ writeOp(fn) {
147
+ return this.op(fn);
148
+ }
141
149
  get(keyOrKeys) {
142
150
  return this.op(() => {
143
151
  if (!Array.isArray(keyOrKeys))
@@ -153,7 +161,7 @@ export class AsyncKvApi {
153
161
  });
154
162
  }
155
163
  put(keyOrEntries, value) {
156
- return this.op(() => {
164
+ return this.writeOp(() => {
157
165
  if (typeof keyOrEntries === "string")
158
166
  return this.kv.put(keyOrEntries, value);
159
167
  const entries = Object.entries(keyOrEntries);
@@ -163,7 +171,7 @@ export class AsyncKvApi {
163
171
  });
164
172
  }
165
173
  delete(keyOrKeys) {
166
- return this.op(() => {
174
+ return this.writeOp(() => {
167
175
  if (!Array.isArray(keyOrKeys))
168
176
  return this.kv.delete(keyOrKeys);
169
177
  checkKeys(keyOrKeys);
@@ -195,9 +203,48 @@ export class DurableObjectStorage extends AsyncKvApi {
195
203
  });
196
204
  this.kv = kv;
197
205
  this.#sync = sync;
206
+ sql.outsideTransaction = () => this.#otherEvent();
207
+ }
208
+ /**
209
+ * Async writes from another event wait while a transaction() is open, as that event would on
210
+ * workerd: they would otherwise land in its savepoint and be undone if it rolls back. Reads do
211
+ * not wait: an event may await, inside its transaction, a read another event started (a
212
+ * memoized load). Events are told apart by the token `runEvent()` gives each one, which needs
213
+ * async context; without it nothing waits.
214
+ */
215
+ writeOp(fn) {
216
+ return this.gate.runClosed(async () => {
217
+ await Promise.resolve();
218
+ if (this.#waitFor()) {
219
+ // On workerd such a wait cannot end if the transaction awaits this very write: say so.
220
+ const slow = setTimeout(() => console.warn("cellb: a storage write from another event has waited 5 s for an open storage.transaction(); does the transaction await it?"), 5000);
221
+ try {
222
+ for (let done = this.#waitFor(); done; done = this.#waitFor())
223
+ await done;
224
+ }
225
+ finally {
226
+ clearTimeout(slow);
227
+ }
228
+ }
229
+ return fn();
230
+ });
231
+ }
232
+ #events = asyncSlot("cellb.event");
233
+ #txnOwner;
234
+ /** Runs one event's handler under a token of its own (the object host delivers through it). */
235
+ runEvent(fn) {
236
+ return this.#events ? this.#events.run({}, fn) : fn();
237
+ }
238
+ /** The open transaction's end, when a write from the calling event must wait for it. */
239
+ #waitFor() {
240
+ return this.#otherEvent() ? this.#txnDone : undefined;
241
+ }
242
+ /** Called from an event other than the one whose transaction() is open now. */
243
+ #otherEvent() {
244
+ return (this.#txnDepth > 0 && this.#events !== undefined && this.#events.get() !== this.#txnOwner);
198
245
  }
199
246
  deleteAll() {
200
- return this.op(() => {
247
+ return this.writeOp(() => {
201
248
  this.kv.deleteAll();
202
249
  // deleteAll() also deletes the alarm, as on Cloudflare, unless `delete_all_preserves_alarm`.
203
250
  if (!this.deleteAllPreservesAlarm)
@@ -210,18 +257,58 @@ export class DurableObjectStorage extends AsyncKvApi {
210
257
  transactionSync(fn) {
211
258
  return this.sql.transactionSync(fn);
212
259
  }
260
+ /** The last async transaction() queued: each one starts once the one before has settled. */
261
+ #transactions = Promise.resolve();
213
262
  /**
214
263
  * Async transaction spanning awaits: the input gate stays closed (no other
215
264
  * event can interleave writes into the savepoint) and auto-commit is held.
265
+ *
266
+ * Transactions of one object run one after another, as on workerd. The gate alone does not
267
+ * order them: events already past it (Miniflare's D1 object awaits each request's body
268
+ * first) opened their savepoints into each other, and one's rollback undid the other's
269
+ * writes.
270
+ *
271
+ * A transaction() awaited inside another's closure nests, as on workerd, and runs at once on
272
+ * a savepoint of its own. Telling it apart from another event's call needs async context
273
+ * (AsyncLocalStorage on Node, or the engine's AsyncContext); without it, such a call waits
274
+ * for its parent forever: use the `txn` argument there.
216
275
  */
276
+ #txnScope = asyncSlot("cellb.storage.transaction");
277
+ #txnDepth = 0;
278
+ /** Settles when the transaction open now (the outermost one) ends. */
279
+ #txnDone;
280
+ /** A token per transaction open now; its closure runs with it in `#txnScope`. */
281
+ #openTxns = new Set();
217
282
  transaction(closure) {
283
+ // Nested: inside the closure of a transaction still open. (A call its closure left running
284
+ // after returning queues like any other.)
285
+ const parent = this.#txnScope?.get();
286
+ if (parent && this.#openTxns.has(parent))
287
+ return this.#transaction(closure);
288
+ const run = () => this.#transaction(closure);
289
+ const result = this.#transactions.then(run, run);
290
+ this.#transactions = result.catch(() => { });
291
+ return result;
292
+ }
293
+ #transaction(closure) {
218
294
  return this.gate.runClosed(async () => {
219
295
  const release = this.sql.hold();
220
- const sp = "_cf_atx";
296
+ let ended;
297
+ if (this.#txnDepth === 0) {
298
+ this.#txnDone = new Promise((r) => (ended = r));
299
+ this.#txnOwner = this.#events?.get();
300
+ }
301
+ const depth = ++this.#txnDepth;
302
+ const sp = `_cf_atx${depth}`;
221
303
  this.sql.internalExec(`SAVEPOINT ${sp}`);
222
304
  const txn = new DurableObjectTransaction(this.kv, this.gate);
305
+ const token = {};
306
+ this.#openTxns.add(token);
223
307
  try {
224
- const r = await closure(txn);
308
+ const scope = this.#txnScope;
309
+ const r = await (scope ? scope.run(token, () => closure(txn)) : closure(txn));
310
+ if (this.#txnDepth !== depth)
311
+ console.warn("cellb: a storage.transaction() ended while one nested in it was still running; await nested transactions");
225
312
  if (txn.rolledBack)
226
313
  this.#rollbackTo(sp);
227
314
  else
@@ -233,7 +320,13 @@ export class DurableObjectStorage extends AsyncKvApi {
233
320
  throw e;
234
321
  }
235
322
  finally {
323
+ this.#openTxns.delete(token);
324
+ this.#txnDepth--;
236
325
  release();
326
+ if (ended) {
327
+ this.#txnDone = undefined;
328
+ ended();
329
+ }
237
330
  }
238
331
  });
239
332
  }
@@ -325,7 +418,7 @@ export class DurableObjectStorage extends AsyncKvApi {
325
418
  // As celld: the time is coerced to a number and a non-number becomes 0 (due at once).
326
419
  const n = scheduledTime instanceof Date ? scheduledTime.getTime() : Number(scheduledTime);
327
420
  const at = Number.isNaN(n) ? 0 : n;
328
- return this.op(() => {
421
+ return this.writeOp(() => {
329
422
  this.#meta();
330
423
  this.sql.internalExec(
331
424
  // celld's statement: counters reset, a new random generation
@@ -340,7 +433,7 @@ export class DurableObjectStorage extends AsyncKvApi {
340
433
  this.onAlarmChange(at);
341
434
  }
342
435
  deleteAlarm(_opts) {
343
- return this.op(() => this.clearAlarmSync());
436
+ return this.writeOp(() => this.clearAlarmSync());
344
437
  }
345
438
  clearAlarmSync() {
346
439
  this.#meta();
@@ -348,4 +441,18 @@ export class DurableObjectStorage extends AsyncKvApi {
348
441
  this.onAlarmChange(null);
349
442
  }
350
443
  }
444
+ /** A value that follows async context, where the platform has it (Node, or AsyncContext). */
445
+ function asyncSlot(name) {
446
+ const hooks = globalThis.process?.getBuiltinModule?.("node:async_hooks");
447
+ if (hooks?.AsyncLocalStorage) {
448
+ const als = new hooks.AsyncLocalStorage();
449
+ return { get: () => als.getStore(), run: (v, fn) => als.run(v, fn) };
450
+ }
451
+ const native = nativeAsyncContext();
452
+ if (native) {
453
+ const v = new native.Variable({ name });
454
+ return { get: () => v.get(), run: (value, fn) => v.run(value, fn) };
455
+ }
456
+ return undefined;
457
+ }
351
458
  //# sourceMappingURL=storage.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"storage.js","sourceRoot":"","sources":["../../src/core/storage.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ValueCodec } from \"../ports.js\";\nimport { serialize } from \"../v8/serialize.js\";\nimport { CELLD_SCHEMA } from \"./celld-schema.js\";\nimport type { InputGate } from \"./gates.js\";\nimport type { SqlStorage } from \"./sql-storage.js\";\n\nexport interface ListOptions {\n start?: string;\n startAfter?: string;\n end?: string;\n prefix?: string;\n reverse?: boolean;\n limit?: number;\n}\n\n/** `EXPORT` of core/handles.ts, by its registered name: importing handles.ts here is a cycle. */\nconst EXPORT = Symbol.for(\"cellb.export\");\nconst withoutStoredStubs = (v: unknown): unknown =>\n v && typeof v === \"object\" && (v as Record<symbol, unknown>)[EXPORT]\n ? null\n : Array.isArray(v)\n ? v.map(withoutStoredStubs)\n : v && Object.getPrototypeOf(v) === Object.prototype\n ? Object.fromEntries(Object.entries(v).map(([k, x]) => [k, withoutStoredStubs(x)]))\n : v;\n\n/**\n * Synchronous KV API of SQLite-backed objects (`ctx.storage.kv`), stored as celld stores it:\n * `_cf_KV(scope, k, v)` with the cell's scope and V8-serialized values (celld-schema.ts).\n */\nexport class SyncKvStorage {\n #sql: SqlStorage;\n #codec: ValueCodec;\n #ready = false;\n readonly scope: string;\n /** The name the object was addressed by: recorded as celld's _cf_METADATA.actor_name. */\n actorName: string | undefined;\n\n constructor(sql: SqlStorage, codec: ValueCodec, scope = \"\") {\n this.#sql = sql;\n this.#codec = codec;\n this.scope = scope;\n sql.rolledBack.add(() => {\n this.#ready = false; // the table may have been created inside the rolled-back transaction\n });\n }\n #init(): void {\n if (this.#ready) return;\n for (const ddl of CELLD_SCHEMA) this.#sql.internalExec(ddl);\n if (this.actorName !== undefined)\n this.#sql.internalExec(\n \"INSERT OR IGNORE INTO _cf_METADATA (scope, actor_name) VALUES (?, ?)\",\n [this.scope, this.actorName],\n );\n this.#ready = true;\n }\n get<T = unknown>(key: string): T | undefined {\n this.#init();\n const r = this.#sql.internalExec(\"SELECT v FROM _cf_KV WHERE scope = ? AND k = ?\", [\n this.scope,\n key,\n ]);\n if (!r.rows.length) return undefined;\n return this.#codec.decode(new Uint8Array(r.rows[0][0] as ArrayBuffer)) as T;\n }\n put(key: string, value: unknown): void {\n this.#init();\n // As celld: `undefined` is a storable value; what structured clone refuses is a TypeError.\n // ctx.exports.X({ props }) stubs are left to the codec, which stores them (workerd).\n try {\n structuredClone(withoutStoredStubs(value));\n } catch (e) {\n // Worded as V8 (workerd, celld) words it, by cellb's V8 serializer: SpiderMonkey says\n // \"Function object could not be cloned.\" for V8's \"() => 1 could not be cloned.\".\n try {\n serialize(withoutStoredStubs(value));\n } catch (v8) {\n throw new TypeError((v8 as Error).message);\n }\n // Browsers prefix the engine message (\"Failed to execute 'structuredClone' on …: \").\n throw new TypeError(\n (e as Error).message.replace(/^Failed to execute '[^']+' on '[^']+': /, \"\"),\n );\n }\n this.#sql.internalExec(\"INSERT OR REPLACE INTO _cf_KV (scope, k, v) VALUES (?, ?, ?)\", [\n this.scope,\n key,\n this.#codec.encode(value),\n ]);\n }\n delete(key: string): boolean {\n this.#init();\n return (\n this.#sql.internalExec(\"DELETE FROM _cf_KV WHERE scope = ? AND k = ?\", [this.scope, key])\n .rowsWritten > 0\n );\n }\n /**\n * Only one list() iterator is live at a time, as in workerd: a new call invalidates the\n * previous one, which throws on its next step.\n */\n list<T = unknown>(opts: ListOptions = {}): IterableIterator<[string, T]> {\n const generation = ++this.#listGeneration;\n const live = () => {\n if (generation !== this.#listGeneration)\n throw new Error(\n \"kv.list() iterator was invalidated because a new call to kv.list() was started. \" +\n \"Only one kv.list() iterator can exist at a time.\",\n );\n };\n const rows = this.#rows<T>(opts);\n return (function* () {\n live();\n for (const row of rows) {\n yield row;\n live();\n }\n })();\n }\n #listGeneration = 0;\n *#rows<T>(opts: ListOptions): IterableIterator<[string, T]> {\n this.#init();\n const where: string[] = [\"scope = ?\"];\n const params: unknown[] = [this.scope];\n if (opts.start !== undefined) where.push(\"k >= ?\"), params.push(opts.start);\n if (opts.startAfter !== undefined) where.push(\"k > ?\"), params.push(opts.startAfter);\n if (opts.end !== undefined) where.push(\"k < ?\"), params.push(opts.end);\n if (opts.prefix !== undefined)\n where.push(\"substr(k, 1, ?) = ?\"), params.push(opts.prefix.length, opts.prefix);\n let q = \"SELECT k, v FROM _cf_KV\";\n q += ` WHERE ${where.join(\" AND \")}`;\n q += ` ORDER BY k ${opts.reverse ? \"DESC\" : \"ASC\"}`;\n if (opts.limit !== undefined) (q += \" LIMIT ?\"), params.push(opts.limit);\n for (const [k, v] of this.#sql.internalExec(q, params).rows)\n yield [k as string, this.#codec.decode(new Uint8Array(v as ArrayBuffer)) as T];\n }\n deleteAll(): void {\n this.#init();\n this.#sql.internalExec(\"DELETE FROM _cf_KV WHERE scope = ?\", [this.scope]);\n }\n}\n\n/** Methods shared by `ctx.storage` and the `txn` object of `transaction()`. */\nexport class AsyncKvApi {\n protected kv: SyncKvStorage;\n protected gate: InputGate;\n constructor(kv: SyncKvStorage, gate: InputGate) {\n this.kv = kv;\n this.gate = gate;\n }\n /** Every async storage op closes the input gate until it (and its continuation) completes. */\n protected op<T>(fn: () => T): Promise<T> {\n return this.gate.runClosed(async () => {\n await Promise.resolve();\n return fn();\n });\n }\n get<T = unknown>(key: string): Promise<T | undefined>;\n get<T = unknown>(keys: string[]): Promise<Map<string, T>>;\n get(keyOrKeys: string | string[]): Promise<unknown> {\n return this.op(() => {\n if (!Array.isArray(keyOrKeys)) return this.kv.get(keyOrKeys);\n checkKeys(keyOrKeys);\n const m = new Map<string, unknown>();\n for (const k of [...keyOrKeys].sort()) {\n const v = this.kv.get(k);\n if (v !== undefined) m.set(k, v);\n }\n return m;\n });\n }\n put(key: string, value: unknown): Promise<void>;\n put(entries: Record<string, unknown>): Promise<void>;\n put(keyOrEntries: string | Record<string, unknown>, value?: unknown): Promise<void> {\n return this.op(() => {\n if (typeof keyOrEntries === \"string\") return this.kv.put(keyOrEntries, value);\n const entries = Object.entries(keyOrEntries);\n checkKeys(entries.map(([k]) => k));\n for (const [k, v] of entries) this.kv.put(k, v);\n });\n }\n delete(key: string): Promise<boolean>;\n delete(keys: string[]): Promise<number>;\n delete(keyOrKeys: string | string[]): Promise<boolean | number> {\n return this.op(() => {\n if (!Array.isArray(keyOrKeys)) return this.kv.delete(keyOrKeys);\n checkKeys(keyOrKeys);\n return keyOrKeys.filter((k) => this.kv.delete(k)).length;\n });\n }\n list<T = unknown>(opts?: ListOptions): Promise<Map<string, T>> {\n return this.op(() => new Map(this.kv.list<T>(opts)));\n }\n}\n\n// celld sets no limit on the number of keys of one get/put/delete (storage-details).\nfunction checkKeys(_keys: string[]): void {}\n\nexport class DurableObjectTransaction extends AsyncKvApi {\n rolledBack = false;\n rollback(): void {\n this.rolledBack = true;\n }\n}\n\n/** `ctx.storage` of a SQLite-backed Durable Object. */\nexport class DurableObjectStorage extends AsyncKvApi {\n readonly sql: SqlStorage;\n readonly kv: SyncKvStorage;\n #sync: () => Promise<void>;\n\n constructor(sql: SqlStorage, kv: SyncKvStorage, gate: InputGate, sync: () => Promise<void>) {\n super(kv, gate);\n this.sql = sql;\n sql.rolledBack.add(() => {\n this.#metaReady = false;\n });\n this.kv = kv;\n this.#sync = sync;\n }\n deleteAll(): Promise<void> {\n return this.op(() => {\n this.kv.deleteAll();\n // deleteAll() also deletes the alarm, as on Cloudflare, unless `delete_all_preserves_alarm`.\n if (!this.deleteAllPreservesAlarm) this.clearAlarmSync();\n const tables = this.sql.internalExec(\n \"SELECT name FROM sqlite_master WHERE type='table' AND name NOT LIKE '\\\\_cf\\\\_%' ESCAPE '\\\\' AND name NOT LIKE 'sqlite\\\\_%' ESCAPE '\\\\'\",\n ).rows;\n for (const [name] of tables)\n this.sql.internalExec(`DROP TABLE IF EXISTS \"${String(name).replace(/\"/g, '\"\"')}\"`);\n });\n }\n transactionSync<T>(fn: () => T): T {\n return this.sql.transactionSync(fn);\n }\n /**\n * Async transaction spanning awaits: the input gate stays closed (no other\n * event can interleave writes into the savepoint) and auto-commit is held.\n */\n transaction<T>(closure: (txn: DurableObjectTransaction) => Promise<T>): Promise<T> {\n return this.gate.runClosed(async () => {\n const release = this.sql.hold();\n const sp = \"_cf_atx\";\n this.sql.internalExec(`SAVEPOINT ${sp}`);\n const txn = new DurableObjectTransaction(this.kv, this.gate);\n try {\n const r = await closure(txn);\n if (txn.rolledBack) this.#rollbackTo(sp);\n else this.sql.internalExec(`RELEASE ${sp}`);\n return r;\n } catch (e) {\n this.#rollbackTo(sp);\n throw e;\n } finally {\n release();\n }\n });\n }\n #rollbackTo(sp: string): void {\n this.sql.internalExec(`ROLLBACK TO ${sp}`);\n this.sql.internalExec(`RELEASE ${sp}`);\n for (const f of this.sql.rolledBack) f(); // tables created inside may be gone\n }\n /** Resolves once all writes so far are durable (the output gate barrier). */\n sync(): Promise<void> {\n return this.#sync();\n }\n /** `delete_all_preserves_alarm`: deleteAll() leaves the alarm in place. */\n deleteAllPreservesAlarm = false;\n /** celld's harness hands its storage \"scope\" to engine ops; here it is the storage itself. */\n get _scope(): this {\n return this;\n }\n /** Called whenever the stored alarm changes (the runtime schedules it). */\n onAlarmChange: (at: number | null) => void = () => {};\n\n #metaReady = false;\n #meta(): void {\n if (this.#metaReady) return;\n for (const ddl of CELLD_SCHEMA) this.sql.internalExec(ddl);\n if (this.kv.actorName !== undefined)\n this.sql.internalExec(\n \"INSERT OR IGNORE INTO _cf_METADATA (scope, actor_name) VALUES (?, ?)\",\n [this.kv.scope, this.kv.actorName],\n );\n this.#metaReady = true;\n }\n /**\n * The alarm row as celld keeps it (storage.rs): due time, failure counters, and a random\n * generation that changes with every setAlarm() — so a handler's own change is detected.\n */\n alarmRowSync(): { at: number; retry: number; counted: number; generation: string } | null {\n this.#meta();\n const r = this.sql.internalExec(\n // generation is a 64-bit random(): read as text (beyond JS's safe integers)\n \"SELECT at_ms, retry, counted_retry, CAST(generation AS TEXT) FROM _cf_ALARM WHERE scope = ?\",\n [this.kv.scope],\n ).rows[0];\n return r\n ? { at: Number(r[0]), retry: Number(r[1]), counted: Number(r[2]), generation: String(r[3]) }\n : null;\n }\n /** The alarm being handled: getAlarm() reads null until the handler changes the alarm. */\n #active: { generation: string } | undefined;\n beginAlarm(row: { generation: string }): void {\n this.#active = { generation: row.generation };\n }\n /**\n * After the handler (celld's finish_alarm_handler): unless the handler changed the alarm,\n * success deletes it and failure reschedules it with backoff (2 s << min(retry, 6)) —\n * abandoned after 6 counted failures. Returns the alarm now stored.\n */\n finishAlarm(\n fired: { at: number; generation: string },\n succeeded: boolean,\n now: number,\n ): number | null {\n this.#active = undefined;\n const cur = this.alarmRowSync();\n if (!cur || cur.generation !== fired.generation) return cur?.at ?? null;\n if (succeeded) {\n this.sql.internalExec(\"DELETE FROM _cf_ALARM WHERE scope = ? AND at_ms = ?\", [\n this.kv.scope,\n fired.at,\n ]);\n } else if (cur.counted >= 6) {\n this.sql.internalExec(\"DELETE FROM _cf_ALARM WHERE scope = ?\", [this.kv.scope]);\n } else {\n const at = now + 2_000 * 2 ** Math.min(Math.max(cur.retry, 0), 6);\n this.sql.internalExec(\n \"UPDATE _cf_ALARM SET at_ms = ?, retry = retry + 1, counted_retry = counted_retry + 1 WHERE scope = ?\",\n [at, this.kv.scope],\n );\n }\n const after = this.alarmRowSync()?.at ?? null;\n this.onAlarmChange(after);\n return after;\n }\n /** Synchronous read of the stored alarm (ms since epoch) or null (null while it is handled). */\n alarmSync(): number | null {\n const row = this.alarmRowSync();\n if (row && this.#active && this.#active.generation === row.generation) return null;\n return row ? row.at : null;\n }\n /** One alarm per object, in celld's `_cf_ALARM` (same transaction as the event's writes). */\n getAlarm(_opts?: unknown): Promise<number | null> {\n return this.op(() => this.alarmSync());\n }\n setAlarm(scheduledTime: number | Date, _opts?: unknown): Promise<void> {\n // As celld: the time is coerced to a number and a non-number becomes 0 (due at once).\n const n = scheduledTime instanceof Date ? scheduledTime.getTime() : Number(scheduledTime);\n const at = Number.isNaN(n) ? 0 : n;\n return this.op(() => {\n this.#meta();\n this.sql.internalExec(\n // celld's statement: counters reset, a new random generation\n \"INSERT INTO _cf_ALARM(scope,at_ms,retry,counted_retry,generation) VALUES(?,?,0,0,random()) ON CONFLICT(scope) DO UPDATE SET at_ms=excluded.at_ms, retry=0, counted_retry=0, generation=random()\",\n [this.kv.scope, at],\n );\n this.onAlarmChange(at);\n });\n }\n /** setAlarm() inside a synchronous transaction (celld's harness ops). */\n setAlarmSync(at: number): void {\n this.#meta();\n this.sql.internalExec(\n \"INSERT INTO _cf_ALARM(scope,at_ms,retry,counted_retry,generation) VALUES(?,?,0,0,random()) ON CONFLICT(scope) DO UPDATE SET at_ms=excluded.at_ms, retry=0, counted_retry=0, generation=random()\",\n [this.kv.scope, at],\n );\n this.onAlarmChange(at);\n }\n deleteAlarm(_opts?: unknown): Promise<void> {\n return this.op(() => this.clearAlarmSync());\n }\n clearAlarmSync(): void {\n this.#meta();\n this.sql.internalExec(\"DELETE FROM _cf_ALARM WHERE scope = ?\", [this.kv.scope]);\n this.onAlarmChange(null);\n }\n}\n"]}
1
+ {"version":3,"file":"storage.js","sourceRoot":"","sources":["../../src/core/storage.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ValueCodec } from \"../ports.js\";\nimport { serialize } from \"../v8/serialize.js\";\nimport { nativeAsyncContext } from \"./async-context.js\";\nimport { CELLD_SCHEMA } from \"./celld-schema.js\";\nimport type { InputGate } from \"./gates.js\";\nimport type { SqlStorage } from \"./sql-storage.js\";\n\nexport interface ListOptions {\n start?: string;\n startAfter?: string;\n end?: string;\n prefix?: string;\n reverse?: boolean;\n limit?: number;\n}\n\n/** `EXPORT` of core/handles.ts, by its registered name: importing handles.ts here is a cycle. */\nconst EXPORT = Symbol.for(\"cellb.export\");\nconst withoutStoredStubs = (v: unknown): unknown =>\n v && typeof v === \"object\" && (v as Record<symbol, unknown>)[EXPORT]\n ? null\n : Array.isArray(v)\n ? v.map(withoutStoredStubs)\n : v && Object.getPrototypeOf(v) === Object.prototype\n ? Object.fromEntries(Object.entries(v).map(([k, x]) => [k, withoutStoredStubs(x)]))\n : v;\n\n/**\n * Synchronous KV API of SQLite-backed objects (`ctx.storage.kv`), stored as celld stores it:\n * `_cf_KV(scope, k, v)` with the cell's scope and V8-serialized values (celld-schema.ts).\n */\nexport class SyncKvStorage {\n #sql: SqlStorage;\n #codec: ValueCodec;\n #ready = false;\n readonly scope: string;\n /** The name the object was addressed by: recorded as celld's _cf_METADATA.actor_name. */\n actorName: string | undefined;\n\n constructor(sql: SqlStorage, codec: ValueCodec, scope = \"\") {\n this.#sql = sql;\n this.#codec = codec;\n this.scope = scope;\n sql.rolledBack.add(() => {\n this.#ready = false; // the table may have been created inside the rolled-back transaction\n });\n }\n #init(): void {\n if (this.#ready) return;\n for (const ddl of CELLD_SCHEMA) this.#sql.internalExec(ddl);\n if (this.actorName !== undefined)\n this.#sql.internalExec(\n \"INSERT OR IGNORE INTO _cf_METADATA (scope, actor_name) VALUES (?, ?)\",\n [this.scope, this.actorName],\n );\n this.#ready = true;\n }\n get<T = unknown>(key: string): T | undefined {\n this.#init();\n const r = this.#sql.internalExec(\"SELECT v FROM _cf_KV WHERE scope = ? AND k = ?\", [\n this.scope,\n key,\n ]);\n if (!r.rows.length) return undefined;\n return this.#codec.decode(new Uint8Array(r.rows[0][0] as ArrayBuffer)) as T;\n }\n put(key: string, value: unknown): void {\n this.#init();\n // As celld: `undefined` is a storable value; what structured clone refuses is a TypeError.\n // ctx.exports.X({ props }) stubs are left to the codec, which stores them (workerd).\n try {\n structuredClone(withoutStoredStubs(value));\n } catch (e) {\n // Worded as V8 (workerd, celld) words it, by cellb's V8 serializer: SpiderMonkey says\n // \"Function object could not be cloned.\" for V8's \"() => 1 could not be cloned.\".\n try {\n serialize(withoutStoredStubs(value));\n } catch (v8) {\n throw new TypeError((v8 as Error).message);\n }\n // Browsers prefix the engine message (\"Failed to execute 'structuredClone' on …: \").\n throw new TypeError(\n (e as Error).message.replace(/^Failed to execute '[^']+' on '[^']+': /, \"\"),\n );\n }\n this.#sql.checkWrite();\n this.#sql.internalExec(\"INSERT OR REPLACE INTO _cf_KV (scope, k, v) VALUES (?, ?, ?)\", [\n this.scope,\n key,\n this.#codec.encode(value),\n ]);\n }\n delete(key: string): boolean {\n this.#init();\n this.#sql.checkWrite();\n return (\n this.#sql.internalExec(\"DELETE FROM _cf_KV WHERE scope = ? AND k = ?\", [this.scope, key])\n .rowsWritten > 0\n );\n }\n /**\n * Only one list() iterator is live at a time, as in workerd: a new call invalidates the\n * previous one, which throws on its next step.\n */\n list<T = unknown>(opts: ListOptions = {}): IterableIterator<[string, T]> {\n const generation = ++this.#listGeneration;\n const live = () => {\n if (generation !== this.#listGeneration)\n throw new Error(\n \"kv.list() iterator was invalidated because a new call to kv.list() was started. \" +\n \"Only one kv.list() iterator can exist at a time.\",\n );\n };\n const rows = this.#rows<T>(opts);\n return (function* () {\n live();\n for (const row of rows) {\n yield row;\n live();\n }\n })();\n }\n #listGeneration = 0;\n *#rows<T>(opts: ListOptions): IterableIterator<[string, T]> {\n this.#init();\n const where: string[] = [\"scope = ?\"];\n const params: unknown[] = [this.scope];\n if (opts.start !== undefined) where.push(\"k >= ?\"), params.push(opts.start);\n if (opts.startAfter !== undefined) where.push(\"k > ?\"), params.push(opts.startAfter);\n if (opts.end !== undefined) where.push(\"k < ?\"), params.push(opts.end);\n if (opts.prefix !== undefined)\n where.push(\"substr(k, 1, ?) = ?\"), params.push(opts.prefix.length, opts.prefix);\n let q = \"SELECT k, v FROM _cf_KV\";\n q += ` WHERE ${where.join(\" AND \")}`;\n q += ` ORDER BY k ${opts.reverse ? \"DESC\" : \"ASC\"}`;\n if (opts.limit !== undefined) (q += \" LIMIT ?\"), params.push(opts.limit);\n for (const [k, v] of this.#sql.internalExec(q, params).rows)\n yield [k as string, this.#codec.decode(new Uint8Array(v as ArrayBuffer)) as T];\n }\n deleteAll(): void {\n this.#init();\n this.#sql.checkWrite();\n this.#sql.internalExec(\"DELETE FROM _cf_KV WHERE scope = ?\", [this.scope]);\n }\n}\n\n/** Methods shared by `ctx.storage` and the `txn` object of `transaction()`. */\nexport class AsyncKvApi {\n protected kv: SyncKvStorage;\n protected gate: InputGate;\n constructor(kv: SyncKvStorage, gate: InputGate) {\n this.kv = kv;\n this.gate = gate;\n }\n /** Every async storage op closes the input gate until it (and its continuation) completes. */\n protected op<T>(fn: () => T): Promise<T> {\n return this.gate.runClosed(async () => {\n await Promise.resolve();\n return fn();\n });\n }\n /** An async op that writes (`ctx.storage` holds it back while another event's transaction is open). */\n protected writeOp<T>(fn: () => T): Promise<T> {\n return this.op(fn);\n }\n get<T = unknown>(key: string): Promise<T | undefined>;\n get<T = unknown>(keys: string[]): Promise<Map<string, T>>;\n get(keyOrKeys: string | string[]): Promise<unknown> {\n return this.op(() => {\n if (!Array.isArray(keyOrKeys)) return this.kv.get(keyOrKeys);\n checkKeys(keyOrKeys);\n const m = new Map<string, unknown>();\n for (const k of [...keyOrKeys].sort()) {\n const v = this.kv.get(k);\n if (v !== undefined) m.set(k, v);\n }\n return m;\n });\n }\n put(key: string, value: unknown): Promise<void>;\n put(entries: Record<string, unknown>): Promise<void>;\n put(keyOrEntries: string | Record<string, unknown>, value?: unknown): Promise<void> {\n return this.writeOp(() => {\n if (typeof keyOrEntries === \"string\") return this.kv.put(keyOrEntries, value);\n const entries = Object.entries(keyOrEntries);\n checkKeys(entries.map(([k]) => k));\n for (const [k, v] of entries) this.kv.put(k, v);\n });\n }\n delete(key: string): Promise<boolean>;\n delete(keys: string[]): Promise<number>;\n delete(keyOrKeys: string | string[]): Promise<boolean | number> {\n return this.writeOp(() => {\n if (!Array.isArray(keyOrKeys)) return this.kv.delete(keyOrKeys);\n checkKeys(keyOrKeys);\n return keyOrKeys.filter((k) => this.kv.delete(k)).length;\n });\n }\n list<T = unknown>(opts?: ListOptions): Promise<Map<string, T>> {\n return this.op(() => new Map(this.kv.list<T>(opts)));\n }\n}\n\n// celld sets no limit on the number of keys of one get/put/delete (storage-details).\nfunction checkKeys(_keys: string[]): void {}\n\nexport class DurableObjectTransaction extends AsyncKvApi {\n rolledBack = false;\n rollback(): void {\n this.rolledBack = true;\n }\n}\n\n/** `ctx.storage` of a SQLite-backed Durable Object. */\nexport class DurableObjectStorage extends AsyncKvApi {\n readonly sql: SqlStorage;\n readonly kv: SyncKvStorage;\n #sync: () => Promise<void>;\n\n constructor(sql: SqlStorage, kv: SyncKvStorage, gate: InputGate, sync: () => Promise<void>) {\n super(kv, gate);\n this.sql = sql;\n sql.rolledBack.add(() => {\n this.#metaReady = false;\n });\n this.kv = kv;\n this.#sync = sync;\n sql.outsideTransaction = () => this.#otherEvent();\n }\n /**\n * Async writes from another event wait while a transaction() is open, as that event would on\n * workerd: they would otherwise land in its savepoint and be undone if it rolls back. Reads do\n * not wait: an event may await, inside its transaction, a read another event started (a\n * memoized load). Events are told apart by the token `runEvent()` gives each one, which needs\n * async context; without it nothing waits.\n */\n protected override writeOp<T>(fn: () => T): Promise<T> {\n return this.gate.runClosed(async () => {\n await Promise.resolve();\n if (this.#waitFor()) {\n // On workerd such a wait cannot end if the transaction awaits this very write: say so.\n const slow = setTimeout(\n () =>\n console.warn(\n \"cellb: a storage write from another event has waited 5 s for an open storage.transaction(); does the transaction await it?\",\n ),\n 5000,\n );\n try {\n for (let done = this.#waitFor(); done; done = this.#waitFor()) await done;\n } finally {\n clearTimeout(slow);\n }\n }\n return fn();\n });\n }\n #events = asyncSlot(\"cellb.event\");\n #txnOwner: unknown;\n /** Runs one event's handler under a token of its own (the object host delivers through it). */\n runEvent<R>(fn: () => R): R {\n return this.#events ? this.#events.run({}, fn) : fn();\n }\n /** The open transaction's end, when a write from the calling event must wait for it. */\n #waitFor(): Promise<void> | undefined {\n return this.#otherEvent() ? this.#txnDone : undefined;\n }\n /** Called from an event other than the one whose transaction() is open now. */\n #otherEvent(): boolean {\n return (\n this.#txnDepth > 0 && this.#events !== undefined && this.#events.get() !== this.#txnOwner\n );\n }\n deleteAll(): Promise<void> {\n return this.writeOp(() => {\n this.kv.deleteAll();\n // deleteAll() also deletes the alarm, as on Cloudflare, unless `delete_all_preserves_alarm`.\n if (!this.deleteAllPreservesAlarm) this.clearAlarmSync();\n const tables = this.sql.internalExec(\n \"SELECT name FROM sqlite_master WHERE type='table' AND name NOT LIKE '\\\\_cf\\\\_%' ESCAPE '\\\\' AND name NOT LIKE 'sqlite\\\\_%' ESCAPE '\\\\'\",\n ).rows;\n for (const [name] of tables)\n this.sql.internalExec(`DROP TABLE IF EXISTS \"${String(name).replace(/\"/g, '\"\"')}\"`);\n });\n }\n transactionSync<T>(fn: () => T): T {\n return this.sql.transactionSync(fn);\n }\n /** The last async transaction() queued: each one starts once the one before has settled. */\n #transactions: Promise<unknown> = Promise.resolve();\n /**\n * Async transaction spanning awaits: the input gate stays closed (no other\n * event can interleave writes into the savepoint) and auto-commit is held.\n *\n * Transactions of one object run one after another, as on workerd. The gate alone does not\n * order them: events already past it (Miniflare's D1 object awaits each request's body\n * first) opened their savepoints into each other, and one's rollback undid the other's\n * writes.\n *\n * A transaction() awaited inside another's closure nests, as on workerd, and runs at once on\n * a savepoint of its own. Telling it apart from another event's call needs async context\n * (AsyncLocalStorage on Node, or the engine's AsyncContext); without it, such a call waits\n * for its parent forever: use the `txn` argument there.\n */\n #txnScope = asyncSlot(\"cellb.storage.transaction\");\n #txnDepth = 0;\n /** Settles when the transaction open now (the outermost one) ends. */\n #txnDone: Promise<void> | undefined;\n /** A token per transaction open now; its closure runs with it in `#txnScope`. */\n #openTxns = new Set<object>();\n transaction<T>(closure: (txn: DurableObjectTransaction) => Promise<T>): Promise<T> {\n // Nested: inside the closure of a transaction still open. (A call its closure left running\n // after returning queues like any other.)\n const parent = this.#txnScope?.get();\n if (parent && this.#openTxns.has(parent as object)) return this.#transaction(closure);\n const run = () => this.#transaction(closure);\n const result = this.#transactions.then(run, run);\n this.#transactions = result.catch(() => {});\n return result;\n }\n #transaction<T>(closure: (txn: DurableObjectTransaction) => Promise<T>): Promise<T> {\n return this.gate.runClosed(async () => {\n const release = this.sql.hold();\n let ended: (() => void) | undefined;\n if (this.#txnDepth === 0) {\n this.#txnDone = new Promise<void>((r) => (ended = r));\n this.#txnOwner = this.#events?.get();\n }\n const depth = ++this.#txnDepth;\n const sp = `_cf_atx${depth}`;\n this.sql.internalExec(`SAVEPOINT ${sp}`);\n const txn = new DurableObjectTransaction(this.kv, this.gate);\n const token = {};\n this.#openTxns.add(token);\n try {\n const scope = this.#txnScope;\n const r = await (scope ? scope.run(token, () => closure(txn)) : closure(txn));\n if (this.#txnDepth !== depth)\n console.warn(\n \"cellb: a storage.transaction() ended while one nested in it was still running; await nested transactions\",\n );\n if (txn.rolledBack) this.#rollbackTo(sp);\n else this.sql.internalExec(`RELEASE ${sp}`);\n return r;\n } catch (e) {\n this.#rollbackTo(sp);\n throw e;\n } finally {\n this.#openTxns.delete(token);\n this.#txnDepth--;\n release();\n if (ended) {\n this.#txnDone = undefined;\n ended();\n }\n }\n });\n }\n #rollbackTo(sp: string): void {\n this.sql.internalExec(`ROLLBACK TO ${sp}`);\n this.sql.internalExec(`RELEASE ${sp}`);\n for (const f of this.sql.rolledBack) f(); // tables created inside may be gone\n }\n /** Resolves once all writes so far are durable (the output gate barrier). */\n sync(): Promise<void> {\n return this.#sync();\n }\n /** `delete_all_preserves_alarm`: deleteAll() leaves the alarm in place. */\n deleteAllPreservesAlarm = false;\n /** celld's harness hands its storage \"scope\" to engine ops; here it is the storage itself. */\n get _scope(): this {\n return this;\n }\n /** Called whenever the stored alarm changes (the runtime schedules it). */\n onAlarmChange: (at: number | null) => void = () => {};\n\n #metaReady = false;\n #meta(): void {\n if (this.#metaReady) return;\n for (const ddl of CELLD_SCHEMA) this.sql.internalExec(ddl);\n if (this.kv.actorName !== undefined)\n this.sql.internalExec(\n \"INSERT OR IGNORE INTO _cf_METADATA (scope, actor_name) VALUES (?, ?)\",\n [this.kv.scope, this.kv.actorName],\n );\n this.#metaReady = true;\n }\n /**\n * The alarm row as celld keeps it (storage.rs): due time, failure counters, and a random\n * generation that changes with every setAlarm() — so a handler's own change is detected.\n */\n alarmRowSync(): { at: number; retry: number; counted: number; generation: string } | null {\n this.#meta();\n const r = this.sql.internalExec(\n // generation is a 64-bit random(): read as text (beyond JS's safe integers)\n \"SELECT at_ms, retry, counted_retry, CAST(generation AS TEXT) FROM _cf_ALARM WHERE scope = ?\",\n [this.kv.scope],\n ).rows[0];\n return r\n ? { at: Number(r[0]), retry: Number(r[1]), counted: Number(r[2]), generation: String(r[3]) }\n : null;\n }\n /** The alarm being handled: getAlarm() reads null until the handler changes the alarm. */\n #active: { generation: string } | undefined;\n beginAlarm(row: { generation: string }): void {\n this.#active = { generation: row.generation };\n }\n /**\n * After the handler (celld's finish_alarm_handler): unless the handler changed the alarm,\n * success deletes it and failure reschedules it with backoff (2 s << min(retry, 6)) —\n * abandoned after 6 counted failures. Returns the alarm now stored.\n */\n finishAlarm(\n fired: { at: number; generation: string },\n succeeded: boolean,\n now: number,\n ): number | null {\n this.#active = undefined;\n const cur = this.alarmRowSync();\n if (!cur || cur.generation !== fired.generation) return cur?.at ?? null;\n if (succeeded) {\n this.sql.internalExec(\"DELETE FROM _cf_ALARM WHERE scope = ? AND at_ms = ?\", [\n this.kv.scope,\n fired.at,\n ]);\n } else if (cur.counted >= 6) {\n this.sql.internalExec(\"DELETE FROM _cf_ALARM WHERE scope = ?\", [this.kv.scope]);\n } else {\n const at = now + 2_000 * 2 ** Math.min(Math.max(cur.retry, 0), 6);\n this.sql.internalExec(\n \"UPDATE _cf_ALARM SET at_ms = ?, retry = retry + 1, counted_retry = counted_retry + 1 WHERE scope = ?\",\n [at, this.kv.scope],\n );\n }\n const after = this.alarmRowSync()?.at ?? null;\n this.onAlarmChange(after);\n return after;\n }\n /** Synchronous read of the stored alarm (ms since epoch) or null (null while it is handled). */\n alarmSync(): number | null {\n const row = this.alarmRowSync();\n if (row && this.#active && this.#active.generation === row.generation) return null;\n return row ? row.at : null;\n }\n /** One alarm per object, in celld's `_cf_ALARM` (same transaction as the event's writes). */\n getAlarm(_opts?: unknown): Promise<number | null> {\n return this.op(() => this.alarmSync());\n }\n setAlarm(scheduledTime: number | Date, _opts?: unknown): Promise<void> {\n // As celld: the time is coerced to a number and a non-number becomes 0 (due at once).\n const n = scheduledTime instanceof Date ? scheduledTime.getTime() : Number(scheduledTime);\n const at = Number.isNaN(n) ? 0 : n;\n return this.writeOp(() => {\n this.#meta();\n this.sql.internalExec(\n // celld's statement: counters reset, a new random generation\n \"INSERT INTO _cf_ALARM(scope,at_ms,retry,counted_retry,generation) VALUES(?,?,0,0,random()) ON CONFLICT(scope) DO UPDATE SET at_ms=excluded.at_ms, retry=0, counted_retry=0, generation=random()\",\n [this.kv.scope, at],\n );\n this.onAlarmChange(at);\n });\n }\n /** setAlarm() inside a synchronous transaction (celld's harness ops). */\n setAlarmSync(at: number): void {\n this.#meta();\n this.sql.internalExec(\n \"INSERT INTO _cf_ALARM(scope,at_ms,retry,counted_retry,generation) VALUES(?,?,0,0,random()) ON CONFLICT(scope) DO UPDATE SET at_ms=excluded.at_ms, retry=0, counted_retry=0, generation=random()\",\n [this.kv.scope, at],\n );\n this.onAlarmChange(at);\n }\n deleteAlarm(_opts?: unknown): Promise<void> {\n return this.writeOp(() => this.clearAlarmSync());\n }\n clearAlarmSync(): void {\n this.#meta();\n this.sql.internalExec(\"DELETE FROM _cf_ALARM WHERE scope = ?\", [this.kv.scope]);\n this.onAlarmChange(null);\n }\n}\n\ninterface AsyncSlot {\n get(): unknown;\n run<R>(value: unknown, fn: () => R): R;\n}\n\n/** A value that follows async context, where the platform has it (Node, or AsyncContext). */\nfunction asyncSlot(name: string): AsyncSlot | undefined {\n const hooks = (\n globalThis as { process?: { getBuiltinModule?(id: string): unknown } }\n ).process?.getBuiltinModule?.(\"node:async_hooks\") as\n | {\n AsyncLocalStorage?: new () => {\n getStore(): unknown;\n run<R>(store: unknown, fn: () => R): R;\n };\n }\n | undefined;\n if (hooks?.AsyncLocalStorage) {\n const als = new hooks.AsyncLocalStorage();\n return { get: () => als.getStore(), run: (v, fn) => als.run(v, fn) };\n }\n const native = nativeAsyncContext();\n if (native) {\n const v = new native.Variable<unknown>({ name });\n return { get: () => v.get(), run: (value, fn) => v.run(value, fn) };\n }\n return undefined;\n}\n"]}
@@ -18,6 +18,7 @@ Object.assign(globalThis, {
18
18
  });
19
19
  serveIsolate(parentPort, async ({ source }) => import(`data:text/javascript;base64,${Buffer.from(source ?? "").toString("base64")}`),
20
20
  // Facets of the loaded code keep their SQLite here: files under `dataDir` when the host
21
- // gives one, else in memory (lost with the isolate).
22
- { openSql: (load) => nodeSqliteOpener(load?.dataDir) });
21
+ // gives one, else temporary files (lost with the isolate, as in memory). The isolate logs
22
+ // every commit's pages to its host, so they are read from the WAL, not from an image.
23
+ { openSql: (load) => nodeSqliteOpener(load?.dataDir, { pageCapture: true }) });
23
24
  //# sourceMappingURL=node-entry.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"node-entry.js","sourceRoot":"","sources":["../../src/isolate/node-entry.ts"],"names":[],"mappings":"AAAA,uFAAuF;AACvF,OAAO,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AACjD,OAAO,EAAE,gBAAgB,EAAE,MAAM,4BAA4B,CAAC;AAC9D,OAAO,EACL,YAAY,EACZ,aAAa,EACb,SAAS,EACT,aAAa,EACb,4BAA4B,GAC7B,MAAM,sCAAsC,CAAC;AAC9C,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAG5C,8FAA8F;AAC9F,qFAAqF;AACrF,OAAO,CAAC,EAAE,CAAC,mBAAmB,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC,UAAU,EAAE,CAAC,CAAC,CAAC,CAAC;AAErE,8FAA8F;AAC9F,gGAAgG;AAChG,yEAAyE;AACzE,MAAM,CAAC,MAAM,CAAC,UAAU,EAAE;IACxB,OAAO,EAAE,YAAY;IACrB,QAAQ,EAAE,aAAa;IACvB,SAAS;IACT,aAAa;IACb,4BAA4B;CAC7B,CAAC,CAAC;AAEH,YAAY,CACV,UAAiC,EACjC,KAAK,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,CACnB,MAAM,CAAC,+BAA+B,MAAM,CAAC,IAAI,CAAC,MAAM,IAAI,EAAE,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC;AACvF,wFAAwF;AACxF,qDAAqD;AACrD,EAAE,OAAO,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,gBAAgB,CAAC,IAAI,EAAE,OAAO,CAAC,EAAE,CACvD,CAAC","sourcesContent":["// A worker thread hosting ONE loaded Worker (an isolate); see src/isolate/protocol.ts.\nimport { parentPort } from \"node:worker_threads\";\nimport { nodeSqliteOpener } from \"../adapters/node-sqlite.js\";\nimport {\n CellsRequest,\n CellsResponse,\n WebSocket,\n WebSocketPair,\n WebSocketRequestResponsePair,\n} from \"../project/workers-globals-module.js\";\nimport { serveIsolate } from \"./isolate.js\";\nimport type { PortLike } from \"./protocol.js\";\n\n// An exception no request awaits (a timer, ctx.abort() from one) is reported, and the isolate\n// lives on, as in a browser Dedicated Worker and workerd; Node would end the thread.\nprocess.on(\"uncaughtException\", (e) => console.error(\"Uncaught\", e));\n\n// This thread is the loaded Worker's realm: its globals are the Workers ones, as in a browser\n// isolate (src/isolate/isolate-worker.ts). A `Response` that takes `{ status: 101, webSocket }`\n// and a `WebSocketPair` are what a Worker that accepts WebSockets needs.\nObject.assign(globalThis, {\n Request: CellsRequest,\n Response: CellsResponse,\n WebSocket,\n WebSocketPair,\n WebSocketRequestResponsePair,\n});\n\nserveIsolate(\n parentPort as unknown as PortLike,\n async ({ source }) =>\n import(`data:text/javascript;base64,${Buffer.from(source ?? \"\").toString(\"base64\")}`),\n // Facets of the loaded code keep their SQLite here: files under `dataDir` when the host\n // gives one, else in memory (lost with the isolate).\n { openSql: (load) => nodeSqliteOpener(load?.dataDir) },\n);\n"]}
1
+ {"version":3,"file":"node-entry.js","sourceRoot":"","sources":["../../src/isolate/node-entry.ts"],"names":[],"mappings":"AAAA,uFAAuF;AACvF,OAAO,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AACjD,OAAO,EAAE,gBAAgB,EAAE,MAAM,4BAA4B,CAAC;AAC9D,OAAO,EACL,YAAY,EACZ,aAAa,EACb,SAAS,EACT,aAAa,EACb,4BAA4B,GAC7B,MAAM,sCAAsC,CAAC;AAC9C,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAG5C,8FAA8F;AAC9F,qFAAqF;AACrF,OAAO,CAAC,EAAE,CAAC,mBAAmB,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC,UAAU,EAAE,CAAC,CAAC,CAAC,CAAC;AAErE,8FAA8F;AAC9F,gGAAgG;AAChG,yEAAyE;AACzE,MAAM,CAAC,MAAM,CAAC,UAAU,EAAE;IACxB,OAAO,EAAE,YAAY;IACrB,QAAQ,EAAE,aAAa;IACvB,SAAS;IACT,aAAa;IACb,4BAA4B;CAC7B,CAAC,CAAC;AAEH,YAAY,CACV,UAAiC,EACjC,KAAK,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,CACnB,MAAM,CAAC,+BAA+B,MAAM,CAAC,IAAI,CAAC,MAAM,IAAI,EAAE,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC;AACvF,wFAAwF;AACxF,0FAA0F;AAC1F,sFAAsF;AACtF,EAAE,OAAO,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,gBAAgB,CAAC,IAAI,EAAE,OAAO,EAAE,EAAE,WAAW,EAAE,IAAI,EAAE,CAAC,EAAE,CAC9E,CAAC","sourcesContent":["// A worker thread hosting ONE loaded Worker (an isolate); see src/isolate/protocol.ts.\nimport { parentPort } from \"node:worker_threads\";\nimport { nodeSqliteOpener } from \"../adapters/node-sqlite.js\";\nimport {\n CellsRequest,\n CellsResponse,\n WebSocket,\n WebSocketPair,\n WebSocketRequestResponsePair,\n} from \"../project/workers-globals-module.js\";\nimport { serveIsolate } from \"./isolate.js\";\nimport type { PortLike } from \"./protocol.js\";\n\n// An exception no request awaits (a timer, ctx.abort() from one) is reported, and the isolate\n// lives on, as in a browser Dedicated Worker and workerd; Node would end the thread.\nprocess.on(\"uncaughtException\", (e) => console.error(\"Uncaught\", e));\n\n// This thread is the loaded Worker's realm: its globals are the Workers ones, as in a browser\n// isolate (src/isolate/isolate-worker.ts). A `Response` that takes `{ status: 101, webSocket }`\n// and a `WebSocketPair` are what a Worker that accepts WebSockets needs.\nObject.assign(globalThis, {\n Request: CellsRequest,\n Response: CellsResponse,\n WebSocket,\n WebSocketPair,\n WebSocketRequestResponsePair,\n});\n\nserveIsolate(\n parentPort as unknown as PortLike,\n async ({ source }) =>\n import(`data:text/javascript;base64,${Buffer.from(source ?? \"\").toString(\"base64\")}`),\n // Facets of the loaded code keep their SQLite here: files under `dataDir` when the host\n // gives one, else temporary files (lost with the isolate, as in memory). The isolate logs\n // every commit's pages to its host, so they are read from the WAL, not from an image.\n { openSql: (load) => nodeSqliteOpener(load?.dataDir, { pageCapture: true }) },\n);\n"]}