@mcp-b/do-runtime 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/LICENSE +110 -0
  3. package/LICENSE.workerd +176 -0
  4. package/NOTICE +7 -0
  5. package/README.md +282 -0
  6. package/dist/backends/node-sqlite.d.ts +38 -0
  7. package/dist/backends/node-sqlite.js +335 -0
  8. package/dist/backends/node-sqlite.js.map +1 -0
  9. package/dist/backends/sqlite-wasm.d.ts +130 -0
  10. package/dist/backends/sqlite-wasm.js +259 -0
  11. package/dist/backends/sqlite-wasm.js.map +1 -0
  12. package/dist/chunks/sqlite-DFg92Tgt.js +498 -0
  13. package/dist/chunks/sqlite-DFg92Tgt.js.map +1 -0
  14. package/dist/cloudflare-workers.js +351 -0
  15. package/dist/cloudflare-workers.js.map +1 -0
  16. package/dist/conformance/host.d.ts +58 -0
  17. package/dist/conformance.js +18 -0
  18. package/dist/conformance.js.map +1 -0
  19. package/dist/index.js +7184 -0
  20. package/dist/index.js.map +1 -0
  21. package/dist/server/alarm-scheduler.js +513 -0
  22. package/dist/server/alarm-scheduler.js.map +1 -0
  23. package/dist/src/api/actor-state.d.ts +396 -0
  24. package/dist/src/api/actor.d.ts +306 -0
  25. package/dist/src/api/cloudflare-workers.d.ts +259 -0
  26. package/dist/src/api/export-loopback.d.ts +264 -0
  27. package/dist/src/api/global-scope.d.ts +262 -0
  28. package/dist/src/api/http.d.ts +52 -0
  29. package/dist/src/api/sql.d.ts +188 -0
  30. package/dist/src/api/sync-kv.d.ts +51 -0
  31. package/dist/src/api/web-socket.d.ts +93 -0
  32. package/dist/src/api/worker-loader.d.ts +354 -0
  33. package/dist/src/index.d.ts +130 -0
  34. package/dist/src/io/actor-cache.d.ts +203 -0
  35. package/dist/src/io/actor-id.d.ts +74 -0
  36. package/dist/src/io/actor-sqlite.d.ts +298 -0
  37. package/dist/src/io/io-channels.d.ts +191 -0
  38. package/dist/src/io/io-context.d.ts +451 -0
  39. package/dist/src/io/io-gate.d.ts +298 -0
  40. package/dist/src/io/worker-source.d.ts +108 -0
  41. package/dist/src/io/worker.d.ts +88 -0
  42. package/dist/src/server/actor-container.d.ts +525 -0
  43. package/dist/src/server/actor-id-impl.d.ts +118 -0
  44. package/dist/src/server/alarm-scheduler.d.ts +201 -0
  45. package/dist/src/server/facet-deletion.d.ts +156 -0
  46. package/dist/src/server/facet-tree-index.d.ts +94 -0
  47. package/dist/src/server/sha256.d.ts +39 -0
  48. package/dist/src/transport/rpc-session.d.ts +34 -0
  49. package/dist/src/util/sqlite-kv.d.ts +98 -0
  50. package/dist/src/util/sqlite-metadata.d.ts +46 -0
  51. package/dist/src/util/sqlite.d.ts +291 -0
  52. package/package.json +111 -0
@@ -0,0 +1,498 @@
1
+ //#region src/util/sqlite.ts
2
+ var SQL_WRONG_BINDINGS_MESSAGE = "Wrong number of parameter bindings for SQL query.";
3
+ /** ← `SQLITE_LIMIT_LENGTH`, raised from 2.2 MB to 4 MiB in workerd 2026-08-20. */
4
+ var SQLITE_LENGTH_LIMIT = 4194304;
5
+ var SQLITE_TOOBIG_MESSAGE = "string or blob too big: SQLITE_TOOBIG";
6
+ var textEncoder = new TextEncoder();
7
+ /** The part of `sqlite3_limit(SQLITE_LIMIT_LENGTH)` visible at the JS binding seam. */
8
+ function requireSqliteLength(value) {
9
+ if ((typeof value === "string" ? textEncoder.encode(value).byteLength : value instanceof Uint8Array ? value.byteLength : 0) > 4194304) throw new Error(SQLITE_TOOBIG_MESSAGE);
10
+ }
11
+ var SQL_PRELUDE_BINDINGS_MESSAGE = "When executing multiple SQL statements in a single call, only the last statement can have parameters.";
12
+ var SAFE_DATABASE_NAME = /^[A-Za-z0-9_-]+$/;
13
+ var SQLITE_HEADER = "SQLite format 3";
14
+ function requireSafeDatabaseName(name) {
15
+ if (!SAFE_DATABASE_NAME.test(name)) throw new Error(`Database name is not a safe file name: ${name}`);
16
+ }
17
+ /** Validate the complete snapshot before a backend replaces any files. */
18
+ function requireValidSqlDatabaseSnapshot(snapshot) {
19
+ const candidate = snapshot;
20
+ if (candidate === null || typeof candidate !== "object" || !("version" in candidate) || candidate.version !== 1 || !("databases" in candidate) || !Array.isArray(candidate.databases)) throw new Error("Unsupported SQLite snapshot format.");
21
+ const names = /* @__PURE__ */ new Set();
22
+ for (const database of candidate.databases) {
23
+ if (database === null || typeof database !== "object" || !("name" in database) || typeof database.name !== "string" || !("image" in database)) throw new Error("SQLite snapshot contains an invalid database entry.");
24
+ const { name, image } = database;
25
+ requireSafeDatabaseName(name);
26
+ if (names.has(name)) throw new Error(`SQLite snapshot contains duplicate database name: ${name}`);
27
+ names.add(name);
28
+ if (!(image instanceof Uint8Array) || image.byteLength < 512 || image.byteLength % 512 !== 0 || [...SQLITE_HEADER].some((character, index) => image[index] !== character.charCodeAt(0))) throw new Error(`Snapshot entry ${name} is not a valid SQLite database image.`);
29
+ }
30
+ }
31
+ /**
32
+ * ← the state `SqliteDatabase::onCriticalError` reports and
33
+ * `observedCriticalError()` latches.
34
+ *
35
+ * Raised when SQLite has rolled back an open transaction on its own. Upstream
36
+ * hands this to `ActorSqlite`, which treats it as fatal; §1.6 is why — a
37
+ * storage failure this severe destroys the object rather than being survived.
38
+ * Until `io/actor-sqlite.ts` wires it to `onBroken`, latching it and refusing
39
+ * every subsequent statement is what keeps a caller from reading through a
40
+ * cache that is knowingly wrong.
41
+ */
42
+ var SqliteCriticalError = class extends Error {
43
+ name = "SqliteCriticalError";
44
+ };
45
+ /** ← `SqliteDatabase::Query::isNull(uint column)`. */
46
+ function isNull(row, column) {
47
+ return row[column] === null || row[column] === void 0;
48
+ }
49
+ /** ← `SqliteDatabase::Query::getBlob(uint column)`. Fails closed on any other column type. */
50
+ function getBlob(row, column) {
51
+ const value = row[column];
52
+ if (value instanceof Uint8Array) return value;
53
+ throw new Error(`Expected a BLOB in column ${column}, got ${describe(value)}.`);
54
+ }
55
+ /** ← `SqliteDatabase::Query::getText(uint column)`. Fails closed on any other column type. */
56
+ function getText(row, column) {
57
+ const value = row[column];
58
+ if (typeof value === "string") return value;
59
+ throw new Error(`Expected TEXT in column ${column}, got ${describe(value)}.`);
60
+ }
61
+ /**
62
+ * ← `SqliteDatabase::Query::getInt64(uint column)`.
63
+ *
64
+ * Narrowed to a safe integer rather than upstream's `int64_t`: a JS number
65
+ * cannot carry the full range, and a silently-rounded row id or alarm time is
66
+ * exactly the kind of corruption this layer must not produce.
67
+ */
68
+ function getInt64(row, column) {
69
+ const value = row[column];
70
+ if (typeof value === "number" && Number.isSafeInteger(value)) return value;
71
+ if (typeof value === "bigint") {
72
+ const narrowed = Number(value);
73
+ if (Number.isSafeInteger(narrowed) && BigInt(narrowed) === value) return narrowed;
74
+ }
75
+ throw new Error(`Expected a safe integer in column ${column}, got ${describe(value)}.`);
76
+ }
77
+ function describe(value) {
78
+ if (value === null) return "NULL";
79
+ if (value instanceof Uint8Array) return "a BLOB";
80
+ return `${typeof value} ${String(value)}`;
81
+ }
82
+ /**
83
+ * ← `SqliteDatabase`, restricted to the members `sqlite-kv` and
84
+ * `sqlite-metadata` actually call.
85
+ *
86
+ * The one piece of real machinery here is the transaction/savepoint stack that
87
+ * `onRollback()` needs. Upstream learns of a `BEGIN` / `SAVEPOINT` / `COMMIT` /
88
+ * `RELEASE` / `ROLLBACK` from the SQLite authorizer while the statement is
89
+ * being compiled (`prepareSql` fills a `ParseContext::stateChange`); we have no
90
+ * authorizer, so the statement text is the only source. `applyChange` below is
91
+ * a line-for-line port of upstream's; only where the `StateChange` comes from
92
+ * differs.
93
+ */
94
+ var SqliteDatabase = class {
95
+ #backend;
96
+ #resetListeners = /* @__PURE__ */ new Set();
97
+ /** Callbacks registered with onRollback that haven't been committed nor rolled back yet. */
98
+ #rollbackCallbacks = [];
99
+ /** Savepoints that haven't been committed nor rolled back yet. */
100
+ #savepoints = [];
101
+ /** True if in a BEGIN TRANSACTION transaction. */
102
+ #inTransaction = false;
103
+ /** ← `criticalErrorOccurred`, holding the exception rather than a bool. */
104
+ #criticalError;
105
+ /** ← `onWriteCallback`. */
106
+ #onWriteCallback;
107
+ /** ← `onCriticalErrorCallback`. */
108
+ #onCriticalErrorCallback;
109
+ constructor(backend) {
110
+ this.#backend = backend;
111
+ }
112
+ /**
113
+ * Invokes the given callback whenever a query begins which may write to the
114
+ * database. The callback is called just before executing the query.
115
+ *
116
+ * Durable Objects uses this to automatically begin a transaction and close the
117
+ * output gate.
118
+ *
119
+ * Note that the write callback is NOT called before (or at any point during) a
120
+ * `reset()`. Use the `ResetListener` mechanism for that case.
121
+ */
122
+ onWrite(callback) {
123
+ this.#onWriteCallback = callback;
124
+ }
125
+ /**
126
+ * Invokes the given callback when a "critical error" causes an automatic
127
+ * rollback during a transaction.
128
+ *
129
+ * See: https://www.sqlite.org/lang_transaction.html#response_to_errors_within_a_transaction
130
+ *
131
+ * Upstream passes `(errorMessage, maybeException)` and lets the caller build
132
+ * the exception; `#checkForAutoRollback` has already built one by the time it
133
+ * can tell a rollback happened, so the callback receives that.
134
+ */
135
+ onCriticalError(callback) {
136
+ this.#onCriticalErrorCallback = callback;
137
+ }
138
+ /**
139
+ * Invoke the onWrite() callback.
140
+ *
141
+ * "This is useful when the caller is about to execute a statement which SQLite
142
+ * considers read-only, but needs to be considered a write for our purposes. In
143
+ * particular, we use the onWrite callback to start automatic transactions, and
144
+ * we use the SAVEPOINT statement to implement explicit transactions. For
145
+ * synchronous transactions, the explicit transaction needs to be nested inside
146
+ * the automatic transaction, so we need to force an auto-transaction to start
147
+ * before the SAVEPOINT."
148
+ */
149
+ notifyWrite(allowUnconfirmed = false) {
150
+ this.#onWriteCallback?.(allowUnconfirmed);
151
+ }
152
+ run(first, ...rest) {
153
+ if (typeof first === "string") return this.#exec(first, rest, false);
154
+ const [sql, ...bindings] = rest;
155
+ if (typeof sql !== "string") throw new Error("run(options, sql, ...) takes a SQL string.");
156
+ return this.#exec(sql, bindings, first.allowUnconfirmed ?? false, first.regulate);
157
+ }
158
+ /** ← `SqliteDatabase::ingestSql`: execute complete statements, retain the partial tail. */
159
+ ingest(sql, regulate) {
160
+ this.assertUsable();
161
+ let remainder = sql;
162
+ let rowsRead = 0;
163
+ let rowsWritten = 0;
164
+ let statementCount = 0;
165
+ while (hasSqlStatement(remainder)) {
166
+ let statement;
167
+ try {
168
+ statement = this.#backend.prepare(remainder);
169
+ } catch (error) {
170
+ if (error instanceof Error && /incomplete input/i.test(error.message)) break;
171
+ throw error;
172
+ }
173
+ try {
174
+ if (!statement.sql.trimEnd().endsWith(";")) break;
175
+ regulate?.(statement.sql);
176
+ if (statement.parameterCount !== 0) throw new Error(SQL_WRONG_BINDINGS_MESSAGE);
177
+ const result = this.#execStatement(statement, [], false);
178
+ rowsRead += result.rawRows.length;
179
+ rowsWritten += result.rowsWritten;
180
+ statementCount += 1;
181
+ remainder = remainder.slice(statement.sql.length);
182
+ } finally {
183
+ statement.close();
184
+ }
185
+ }
186
+ return {
187
+ remainder,
188
+ rowsRead,
189
+ rowsWritten,
190
+ statementCount
191
+ };
192
+ }
193
+ #exec(sql, bindings, allowUnconfirmed, regulate) {
194
+ this.assertUsable();
195
+ let remaining = sql;
196
+ let result;
197
+ while (hasSqlStatement(remaining)) {
198
+ const statement = this.#backend.prepare(remaining);
199
+ const tail = remaining.slice(statement.sql.length);
200
+ const isFinal = !hasSqlStatement(tail);
201
+ try {
202
+ regulate?.(statement.sql);
203
+ if (!isFinal && statement.parameterCount !== 0) throw new Error(SQL_PRELUDE_BINDINGS_MESSAGE);
204
+ if (isFinal && statement.parameterCount !== bindings.length) throw new Error(SQL_WRONG_BINDINGS_MESSAGE);
205
+ result = this.#execStatement(statement, isFinal ? bindings : [], isFinal ? allowUnconfirmed : false);
206
+ } finally {
207
+ statement.close();
208
+ }
209
+ remaining = tail;
210
+ }
211
+ if (result === void 0) throw new Error("Expected at least one SQL statement.");
212
+ return result;
213
+ }
214
+ /** Execute one statement from `run()`'s batch; bindings and results belong to the last one. */
215
+ #execStatement(statement, bindings, allowUnconfirmed) {
216
+ const { sql } = statement;
217
+ const change = classify(sql);
218
+ if (isWrite(sql)) this.notifyWrite(allowUnconfirmed);
219
+ let result;
220
+ try {
221
+ result = statement.execute(bindings);
222
+ } catch (error) {
223
+ this.#checkForAutoRollback(error);
224
+ throw error;
225
+ }
226
+ this.#applyChange(change);
227
+ return result;
228
+ }
229
+ /**
230
+ * ← `SqliteDatabase::handleCriticalError`, which reaches the same conclusion
231
+ * from the error code plus `sqlite3_get_autocommit`. We do not see the error
232
+ * code — the backend has already turned it into a JS exception — so the
233
+ * disagreement between our stack and the backend's is the whole signal, and
234
+ * it is enough: we only ask after a statement has failed, and the only thing
235
+ * that closes a transaction without going through `run()` is SQLite itself.
236
+ *
237
+ * The callbacks are DISCARDED rather than invoked, which looks wrong for two
238
+ * lines and is not. Invoking them is only correct when a rollback actually
239
+ * happened, and the branch above is the sole case where that is knowable; in
240
+ * the ordinary case — a constraint violation, which does not roll anything
241
+ * back — firing them would restore a cache the database has moved past, which
242
+ * is corruption in the other direction. Upstream does not fire them here
243
+ * either. It makes the actor fatal instead, and so does this: the caches are
244
+ * knowingly stale, so the database is finished rather than repaired.
245
+ */
246
+ #checkForAutoRollback(cause) {
247
+ if (!this.#inTransaction && this.#savepoints.length === 0) return;
248
+ if (this.#backend.inTransaction) return;
249
+ this.#inTransaction = false;
250
+ this.#savepoints = [];
251
+ this.#rollbackCallbacks = [];
252
+ const critical = new SqliteCriticalError("SQLite rolled back the open transaction in response to a critical error, so every in-memory view of this database is now stale and it can no longer be used.", { cause });
253
+ this.#criticalError = critical;
254
+ this.#onCriticalErrorCallback?.(critical);
255
+ throw critical;
256
+ }
257
+ /**
258
+ * ← `SqliteDatabase::observedCriticalError()`. The named state
259
+ * `io/actor-sqlite.ts` wires to `onBroken`, so it does not have to re-derive
260
+ * the condition from an exception it caught.
261
+ */
262
+ observedCriticalError() {
263
+ return this.#criticalError;
264
+ }
265
+ /**
266
+ * The guard for any read this package serves from a cache rather than from a
267
+ * statement. Those are the only paths a latched critical error would not
268
+ * already stop, and they are exactly the paths whose answer is wrong once
269
+ * SQLite has rolled back underneath them.
270
+ */
271
+ assertUsable() {
272
+ if (this.#criticalError !== void 0) throw this.#criticalError;
273
+ }
274
+ get databaseSize() {
275
+ return this.#backend.databaseSize;
276
+ }
277
+ /**
278
+ * ← `SqliteDatabase::onRollback`.
279
+ *
280
+ * "Register a callback which shall be called if the current transaction is
281
+ * rolled back. If the current transaction commits, then the callback is
282
+ * discarded without invoking it. [...] When a rollback occurs, callbacks are
283
+ * invoked in the reverse of the order in which they were registered."
284
+ *
285
+ * With nothing open there is nothing that can roll back, so the callback is
286
+ * dropped — upstream's `if (inTransaction || !savepoints.empty())`.
287
+ */
288
+ onRollback(callback) {
289
+ if (this.#inTransaction || this.#savepoints.length > 0) this.#rollbackCallbacks.push(callback);
290
+ }
291
+ addResetListener(listener) {
292
+ this.#resetListeners.add(listener);
293
+ }
294
+ removeResetListener(listener) {
295
+ this.#resetListeners.delete(listener);
296
+ }
297
+ /** ← `SqliteDatabase::reset()`. */
298
+ reset() {
299
+ this.assertUsable();
300
+ if (this.#inTransaction || this.#savepoints.length > 0) throw new Error("can't reset() a database during a transaction");
301
+ for (const listener of this.#resetListeners) listener.beforeSqliteReset();
302
+ this.#backend.reset();
303
+ }
304
+ close() {
305
+ this.#backend.close();
306
+ }
307
+ /** ← `SqliteDatabase::applyChange`, ported statement for statement. */
308
+ #applyChange(change) {
309
+ switch (change.kind) {
310
+ case "none": break;
311
+ case "begin":
312
+ if (change.savepointName !== null) this.#savepoints.push({
313
+ name: change.savepointName,
314
+ rollbackCallbackIndex: this.#rollbackCallbacks.length
315
+ });
316
+ else {
317
+ assert(this.#savepoints.length === 0, "BEGIN TRANSACTION should have failed when savepoints are present?");
318
+ assert(!this.#inTransaction, "BEGIN TRANSACTION should have failed when already in a transaction?");
319
+ assert(this.#rollbackCallbacks.length === 0, "we shouldn't have been keeping rollback callbacks with no transaction open!");
320
+ this.#inTransaction = true;
321
+ }
322
+ break;
323
+ case "commit":
324
+ if (change.savepointName !== null) for (;;) {
325
+ const savepoint = this.#savepoints.pop();
326
+ assert(savepoint !== void 0, "released a savepoint that didn't exist?");
327
+ if (savepoint.name === change.savepointName) break;
328
+ }
329
+ else {
330
+ assert(this.#inTransaction, "COMMIT TRANSACTION without BEGIN TRANSACTION?");
331
+ this.#savepoints = [];
332
+ this.#inTransaction = false;
333
+ }
334
+ if (this.#savepoints.length === 0 && !this.#inTransaction) this.#rollbackCallbacks = [];
335
+ break;
336
+ case "rollback": if (change.savepointName !== null) for (;;) {
337
+ const savepoint = this.#savepoints[this.#savepoints.length - 1];
338
+ assert(savepoint !== void 0, "released a savepoint that didn't exist?");
339
+ if (savepoint.name === change.savepointName) {
340
+ this.#runRollbackCallbacksDownTo(savepoint.rollbackCallbackIndex);
341
+ break;
342
+ }
343
+ this.#savepoints.pop();
344
+ }
345
+ else {
346
+ assert(this.#inTransaction, "ROLLBACK TRANSACTION without BEGIN TRANSACTION?");
347
+ this.#savepoints = [];
348
+ this.#inTransaction = false;
349
+ this.#runRollbackCallbacksDownTo(0);
350
+ }
351
+ }
352
+ }
353
+ #runRollbackCallbacksDownTo(index) {
354
+ assert(this.#rollbackCallbacks.length >= index, "rollback callback stack shrank?");
355
+ while (this.#rollbackCallbacks.length > index) {
356
+ const callback = this.#rollbackCallbacks.pop();
357
+ assert(callback !== void 0, "rollback callback stack shrank?");
358
+ callback();
359
+ }
360
+ }
361
+ };
362
+ /**
363
+ * Returns whether a runtime-owned table exists, and refuses any present shape
364
+ * other than the one this release writes.
365
+ */
366
+ function hasCurrentSqliteTable(db, name, createSql) {
367
+ const query = "SELECT type, sql FROM sqlite_master WHERE name = ? COLLATE NOCASE";
368
+ const rows = "run" in db ? db.run(query, name).rawRows : db.exec(query, [name]).rawRows;
369
+ const row = rows[0];
370
+ if (row === void 0) return false;
371
+ if (rows.length !== 1 || row[0] !== "table" || typeof row[1] !== "string" || normalizeSchemaSql(row[1]) !== normalizeSchemaSql(createSql)) throw new Error(`Incompatible @mcp-b/do-runtime storage schema for table "${name}". This release accepts only the current schema and does not migrate stored runtime data.`);
372
+ return true;
373
+ }
374
+ function normalizeSchemaSql(sql) {
375
+ return sql.replace(/\bIF\s+NOT\s+EXISTS\b/giu, "").replace(/\s+/gu, " ").trim().replace(/;$/u, "").toLowerCase();
376
+ }
377
+ function assert(condition, message) {
378
+ if (!condition) throw new Error(message);
379
+ }
380
+ var NO_CHANGE = { kind: "none" };
381
+ /** SQLite savepoint names compare case-insensitively, so the stack stores them folded. */
382
+ function savepointName(raw) {
383
+ return (raw.startsWith("\"") && raw.endsWith("\"") || raw.startsWith("'") && raw.endsWith("'") || raw.startsWith("`") && raw.endsWith("`") ? raw.slice(1, -1) : raw.startsWith("[") && raw.endsWith("]") ? raw.slice(1, -1) : raw).toLowerCase();
384
+ }
385
+ var NAME = String.raw`("[^"]*"|'[^']*'|\`[^\`]*\`|\[[^\]]*\]|[A-Za-z_][A-Za-z0-9_$]*)`;
386
+ var BEGIN = new RegExp(String.raw`^BEGIN(\s+(DEFERRED|IMMEDIATE|EXCLUSIVE))?(\s+TRANSACTION)?$`, "i");
387
+ var SAVEPOINT = new RegExp(String.raw`^SAVEPOINT\s+${NAME}$`, "i");
388
+ var COMMIT = new RegExp(String.raw`^(COMMIT|END)(\s+TRANSACTION)?$`, "i");
389
+ var RELEASE = new RegExp(String.raw`^RELEASE(\s+SAVEPOINT)?\s+${NAME}$`, "i");
390
+ var ROLLBACK = new RegExp(String.raw`^ROLLBACK(\s+TRANSACTION)?$`, "i");
391
+ var ROLLBACK_TO = new RegExp(String.raw`^ROLLBACK(\s+TRANSACTION)?\s+TO(\s+SAVEPOINT)?\s+${NAME}$`, "i");
392
+ var TRANSACTION_KEYWORD = /^(BEGIN|COMMIT|END|ROLLBACK|SAVEPOINT|RELEASE)\b/i;
393
+ /** Every group referenced below is mandatory in its pattern, so an absent one is a broken pattern. */
394
+ function group(match, index) {
395
+ const value = match[index];
396
+ if (value === void 0) throw new Error(`SQL pattern group ${index} did not match: ${match[0]}`);
397
+ return value;
398
+ }
399
+ /**
400
+ * Derives upstream's `StateChange` from the statement text.
401
+ *
402
+ * A statement that opens with a transaction keyword but does not match one of
403
+ * the forms below throws rather than being classified as `NoChange`, since
404
+ * guessing in that direction is what loses a rollback callback. `run()` asks
405
+ * the backend to compile one native statement at a time and applies this state
406
+ * change after each executes, matching workerd's `prepareMulti()` prelude.
407
+ */
408
+ function classify(sql) {
409
+ const statement = stripLeadingTrivia(sql).trim().replace(/;$/, "").trimEnd();
410
+ const rollbackTo = ROLLBACK_TO.exec(statement);
411
+ if (rollbackTo !== null) return {
412
+ kind: "rollback",
413
+ savepointName: savepointName(group(rollbackTo, 3))
414
+ };
415
+ if (ROLLBACK.test(statement)) return {
416
+ kind: "rollback",
417
+ savepointName: null
418
+ };
419
+ const savepoint = SAVEPOINT.exec(statement);
420
+ if (savepoint !== null) return {
421
+ kind: "begin",
422
+ savepointName: savepointName(group(savepoint, 1))
423
+ };
424
+ if (BEGIN.test(statement)) return {
425
+ kind: "begin",
426
+ savepointName: null
427
+ };
428
+ const release = RELEASE.exec(statement);
429
+ if (release !== null) return {
430
+ kind: "commit",
431
+ savepointName: savepointName(group(release, 2))
432
+ };
433
+ if (COMMIT.test(statement)) return {
434
+ kind: "commit",
435
+ savepointName: null
436
+ };
437
+ if (TRANSACTION_KEYWORD.test(statement)) throw new Error(`Unrecognized transaction-control statement: ${statement}`);
438
+ return NO_CHANGE;
439
+ }
440
+ /**
441
+ * ← `!sqlite3_stmt_readonly(statement)`, the test upstream's `onWrite` gate is
442
+ * written against (`sqlite.c++:1562-1568`).
443
+ *
444
+ * It is NOT the authorizer — the authorizer never sees this question, and the
445
+ * distinction is load bearing: `sqlite3_stmt_readonly()` reports BEGIN, COMMIT,
446
+ * ROLLBACK, SAVEPOINT and RELEASE as read-only, which is why `notifyWrite()`
447
+ * exists at all and why an automatic transaction's own `BEGIN` does not recurse
448
+ * into the callback that issued it.
449
+ *
450
+ * Neither backend exposes the compiled statement, so the text is the only
451
+ * source, and it fails closed the way `classify` does: **a statement is a write
452
+ * unless it provably is not.** The complete read set is `SELECT` and `EXPLAIN`
453
+ * plus the five transaction-control forms. `WITH`, `PRAGMA` and anything
454
+ * unrecognised are writes, which costs a read-only CTE a transaction and an
455
+ * output-gate lock it does not need, and cannot cost atomicity — which is the
456
+ * only error this classification is allowed to make.
457
+ */
458
+ function isWrite(sql) {
459
+ return !NON_WRITE_KEYWORDS.has(leadingKeyword(sql));
460
+ }
461
+ var NON_WRITE_KEYWORDS = /* @__PURE__ */ new Set([
462
+ "SELECT",
463
+ "EXPLAIN",
464
+ "BEGIN",
465
+ "COMMIT",
466
+ "END",
467
+ "ROLLBACK",
468
+ "SAVEPOINT",
469
+ "RELEASE"
470
+ ]);
471
+ /** The first bare word, skipping whitespace and both comment forms. */
472
+ function leadingKeyword(sql) {
473
+ return (/^[A-Za-z]+/.exec(stripLeadingTrivia(sql))?.[0] ?? "").toUpperCase();
474
+ }
475
+ function hasSqlStatement(sql) {
476
+ return stripLeadingTrivia(sql).trim().length > 0;
477
+ }
478
+ function stripLeadingTrivia(statement) {
479
+ let index = 0;
480
+ for (;;) {
481
+ while (index < statement.length && /\s/.test(statement.charAt(index))) index += 1;
482
+ if (statement[index] === "-" && statement[index + 1] === "-") {
483
+ const newline = statement.indexOf("\n", index);
484
+ index = newline === -1 ? statement.length : newline + 1;
485
+ continue;
486
+ }
487
+ if (statement[index] === "/" && statement[index + 1] === "*") {
488
+ const close = statement.indexOf("*/", index + 2);
489
+ index = close === -1 ? statement.length : close + 2;
490
+ continue;
491
+ }
492
+ return statement.slice(index);
493
+ }
494
+ }
495
+ //#endregion
496
+ export { getInt64 as a, isNull as c, requireValidSqlDatabaseSnapshot as d, getBlob as i, requireSafeDatabaseName as l, SQL_WRONG_BINDINGS_MESSAGE as n, getText as o, SqliteDatabase as r, hasCurrentSqliteTable as s, SQLITE_LENGTH_LIMIT as t, requireSqliteLength as u };
497
+
498
+ //# sourceMappingURL=sqlite-DFg92Tgt.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sqlite-DFg92Tgt.js","names":["#backend","#resetListeners","#onWriteCallback","#onCriticalErrorCallback","#exec","#execStatement","#checkForAutoRollback","#applyChange","#inTransaction","#savepoints","#rollbackCallbacks","#criticalError","#runRollbackCallbacksDownTo"],"sources":["../../src/util/sqlite.ts"],"sourcesContent":["/**\n * ← workerd `src/workerd/util/sqlite.{h,c++}`\n *\n * The SQL backend port. Upstream's seam is the same one: `server.c++` opens\n * `<actor-id>.<facetId>.sqlite` and hands `ActorSqlite` a `SqliteDatabase`.\n *\n * Almost none of upstream's 3,768 lines are ours. `sqlite.{h,c++}` is workerd's\n * binding to the SQLite C API — statement caching, the VFS, regulators, the\n * authorizer, the memory-metering allocator. Underneath us that role is played\n * by `node:sqlite` and sqlite-wasm, which is the storage-backend adaptation the\n * design record sanctions: `io-gate.h` knows nothing about SQLite, `ActorSqlite`\n * calls into it, and that seam is upstream's rather than ours.\n *\n * So this file is two things stacked:\n *\n * 1. `SqlDatabase` / `SqlDatabaseProvider` — the backend seam. `backends/`\n * implements it, and nothing above `util/` ever sees a driver type.\n * 2. `SqliteDatabase` — the small part of upstream's class that is genuinely\n * ours, because the layers above call into it and a stateless exec interface\n * cannot express it: `onRollback`, the transaction/savepoint stack it needs,\n * `reset()` and its `ResetListener` notification, all three of which\n * `sqlite-kv.c++` and `sqlite-metadata.c++` reach for; plus `onWrite`,\n * `notifyWrite` and `onCriticalError`, which only `io/actor-sqlite.ts`\n * reaches for. That last trio lives here rather than one directory up\n * because it lives here upstream (`sqlite.h:240`, `:248`, `:267`): a\n * callback slot is not actor knowledge, and a reader who finds `onWrite` in\n * `sqlite.h` has to find it in this file too. Every consumer takes a\n * `SqliteDatabase`, exactly as upstream's take a `SqliteDatabase&`.\n *\n * `transactionSync` is NOT here — it lives in `io/actor-sqlite.ts` as\n * SAVEPOINT/RELEASE with a depth counter, exactly as upstream has it. Today\n * both browser and Node adapters duplicate `BEGIN IMMEDIATE`, which is why a\n * nested call is a live SQLite error (§2.4). Moving it inward fixes that.\n *\n * Not ported, because the substrate has no equivalent: the `Regulator` /\n * authorizer machinery (there is no untrusted-SQL path in `util/`, and\n * `api/sql.ts` owns that question); `SqliteObserver` row-count billing, whose\n * counters are libsql `STMTSTATUS` extensions neither backend exposes;\n * `sqlite-metering.{h,c++}`, which swaps SQLite's process-wide allocator to\n * meter per-database memory — a C-API facility with no JS analogue; and the\n * point-in-time-recovery APIs, a named substrate boundary in the package README.\n *\n * Spec: §1.4, §2.4 in docs/decisions.md.\n */\n\n/** The four values SQLite itself accepts after public JSG-style conversion. */\nexport type SqlValue = string | number | null | Uint8Array;\n\nexport type SqlResult = {\n readonly columnNames: readonly string[];\n readonly rawRows: readonly (readonly unknown[])[];\n /** Rows changed by this statement, including DML with `RETURNING`. */\n readonly rowsWritten: number;\n};\n\n/** ← `SqliteDatabase::IngestResult`. */\nexport type SqlIngestResult = {\n readonly remainder: string;\n readonly rowsRead: number;\n readonly rowsWritten: number;\n readonly statementCount: number;\n};\n\n/**\n * One SQLite-compiled statement from the front of a SQL string.\n *\n * `sql` is the exact prefix SQLite consumed, including trigger bodies. Keeping\n * that boundary on the backend is what prevents JavaScript from inventing a\n * second, subtly different SQL grammar.\n */\nexport interface SqlDatabaseStatement {\n readonly sql: string;\n readonly parameterCount: number;\n execute(params: readonly SqlValue[]): SqlResult;\n close(): void;\n}\n\nexport const SQL_WRONG_BINDINGS_MESSAGE = \"Wrong number of parameter bindings for SQL query.\";\n\n/** ← `SQLITE_LIMIT_LENGTH`, raised from 2.2 MB to 4 MiB in workerd 2026-08-20. */\nexport const SQLITE_LENGTH_LIMIT = 4 * 1024 * 1024;\n\nexport const SQLITE_TOOBIG_MESSAGE = \"string or blob too big: SQLITE_TOOBIG\";\n\nconst textEncoder = new TextEncoder();\n\n/** The part of `sqlite3_limit(SQLITE_LIMIT_LENGTH)` visible at the JS binding seam. */\nexport function requireSqliteLength(value: unknown): void {\n const length =\n typeof value === \"string\"\n ? textEncoder.encode(value).byteLength\n : value instanceof Uint8Array\n ? value.byteLength\n : 0;\n if (length > SQLITE_LENGTH_LIMIT) throw new Error(SQLITE_TOOBIG_MESSAGE);\n}\n\nexport const SQL_PRELUDE_BINDINGS_MESSAGE =\n \"When executing multiple SQL statements in a single call, only the last statement can have \" +\n \"parameters.\";\n\n/**\n * One open database. Synchronous exec, matching every substrate we have: in a\n * SQLite-backed Durable Object reads return a value rather than a promise\n * (§1.4), which is what makes the input gate cheap.\n */\nexport interface SqlDatabase {\n /** Compile exactly the first statement, using SQLite's own statement boundary. */\n prepare(sql: string): SqlDatabaseStatement;\n exec(sql: string, params: readonly SqlValue[]): SqlResult;\n readonly databaseSize: number;\n /**\n * ← `sqlite3_get_autocommit(db) == 0`, which is how upstream's\n * `handleCriticalError` learns that SQLite rolled a transaction back on its\n * own (`sqlite.c++:669-691`).\n *\n * SQLite auto-rolls-back on `SQLITE_FULL`, `SQLITE_IOERR`, `SQLITE_NOMEM` and\n * `SQLITE_INTERRUPT`. Nothing announces it, so without this the savepoint\n * stack above would keep believing a transaction is open and the rollback\n * callbacks would never fire — a stale cache with nothing thrown, which is\n * the one failure this layer must never produce.\n */\n readonly inTransaction: boolean;\n /**\n * ← `SqliteDatabase::reset()` — \"delete the underlying database file and\n * create a new one in its place\", which is how upstream implements\n * `deleteAll()`.\n *\n * On the backend rather than above it because only the backend knows how to\n * recreate its own file, and because the alternative — enumerating and\n * dropping every table — is the fragile dance today's `storage.ts` performs,\n * complete with an FTS5 shadow-table ordering hazard its comment documents.\n * The `SqlDatabase` reference stays valid across the call; what changes is\n * the file behind it.\n */\n reset(): void;\n close(): void;\n}\n\n/**\n * Opens databases within ONE actor's storage scope. The package derives the\n * names (`\"root\"`, `` `facet-${facetId}` ``); the host maps them onto files.\n * OPFS layout knowledge stays with the host — this package never reaches for\n * `navigator.storage`.\n */\nexport interface SqlDatabaseProvider {\n open(name: string): Promise<SqlDatabase>;\n}\n\n/** A portable, host-owned image of every SQLite database in one actor storage scope. */\nexport type SqlDatabaseSnapshot = {\n readonly version: 1;\n readonly databases: readonly {\n readonly name: string;\n readonly image: Uint8Array;\n }[];\n};\n\n/** Local backup/restore. This is deliberately not Cloudflare's time-indexed PITR service. */\nexport interface SqlDatabaseSnapshotProvider extends SqlDatabaseProvider {\n /** Close every database opened through this provider before snapshot or placement teardown. */\n close(): void;\n exportSnapshot(): Promise<SqlDatabaseSnapshot>;\n importSnapshot(snapshot: SqlDatabaseSnapshot): Promise<void>;\n}\n\nconst SAFE_DATABASE_NAME = /^[A-Za-z0-9_-]+$/;\nconst SQLITE_HEADER = \"SQLite format 3\";\n\nexport function requireSafeDatabaseName(name: string): void {\n if (!SAFE_DATABASE_NAME.test(name)) {\n throw new Error(`Database name is not a safe file name: ${name}`);\n }\n}\n\n/** Validate the complete snapshot before a backend replaces any files. */\nexport function requireValidSqlDatabaseSnapshot(snapshot: SqlDatabaseSnapshot): void {\n const candidate: unknown = snapshot;\n if (\n candidate === null ||\n typeof candidate !== \"object\" ||\n !(\"version\" in candidate) ||\n candidate.version !== 1 ||\n !(\"databases\" in candidate) ||\n !Array.isArray(candidate.databases)\n ) {\n throw new Error(\"Unsupported SQLite snapshot format.\");\n }\n const names = new Set<string>();\n for (const database of candidate.databases) {\n if (\n database === null ||\n typeof database !== \"object\" ||\n !(\"name\" in database) ||\n typeof database.name !== \"string\" ||\n !(\"image\" in database)\n ) {\n throw new Error(\"SQLite snapshot contains an invalid database entry.\");\n }\n const { name, image } = database;\n requireSafeDatabaseName(name);\n if (names.has(name)) {\n throw new Error(`SQLite snapshot contains duplicate database name: ${name}`);\n }\n names.add(name);\n if (\n !(image instanceof Uint8Array) ||\n image.byteLength < 512 ||\n image.byteLength % 512 !== 0 ||\n [...SQLITE_HEADER].some((character, index) => image[index] !== character.charCodeAt(0))\n ) {\n throw new Error(`Snapshot entry ${name} is not a valid SQLite database image.`);\n }\n }\n}\n\n/**\n * ← `SqliteDatabase::QueryOptions`. The C++ regulator pointer is narrowed to\n * a callback over the exact SQL source SQLite compiled; `api/sql.ts` owns the\n * policy because this backend seam owns no public-API knowledge.\n *\n * `allowUnconfirmed`'s only destination is `onWrite(bool allowUnconfirmed)`,\n * which fires *before* the statement executes so the automatic transaction\n * opens first — see `isWrite` below for how a statement is known to be a write\n * without the compiled plan upstream reads it from.\n */\nexport type QueryOptions = {\n allowUnconfirmed?: boolean;\n /** The public SQL regulator, run once against each SQLite-compiled statement. */\n regulate?: (sql: string) => void;\n};\n\n/**\n * ← the state `SqliteDatabase::onCriticalError` reports and\n * `observedCriticalError()` latches.\n *\n * Raised when SQLite has rolled back an open transaction on its own. Upstream\n * hands this to `ActorSqlite`, which treats it as fatal; §1.6 is why — a\n * storage failure this severe destroys the object rather than being survived.\n * Until `io/actor-sqlite.ts` wires it to `onBroken`, latching it and refusing\n * every subsequent statement is what keeps a caller from reading through a\n * cache that is knowingly wrong.\n */\nexport class SqliteCriticalError extends Error {\n override readonly name = \"SqliteCriticalError\";\n}\n\n/**\n * ← `SqliteDatabase::ResetListener`.\n *\n * Upstream's is a base class whose constructor registers and whose destructor\n * unregisters. JS has neither, so registration is the explicit\n * `db.addResetListener(this)` call — the same translation Section 1 applied to\n * every kj destructor.\n */\nexport interface ResetListener {\n /** Called before the database is actually reset. */\n beforeSqliteReset(): void;\n}\n\n/** ← `SqliteDatabase::Query::isNull(uint column)`. */\nexport function isNull(row: readonly unknown[], column: number): boolean {\n return row[column] === null || row[column] === undefined;\n}\n\n/** ← `SqliteDatabase::Query::getBlob(uint column)`. Fails closed on any other column type. */\nexport function getBlob(row: readonly unknown[], column: number): Uint8Array {\n const value = row[column];\n if (value instanceof Uint8Array) return value;\n throw new Error(`Expected a BLOB in column ${column}, got ${describe(value)}.`);\n}\n\n/** ← `SqliteDatabase::Query::getText(uint column)`. Fails closed on any other column type. */\nexport function getText(row: readonly unknown[], column: number): string {\n const value = row[column];\n if (typeof value === \"string\") return value;\n throw new Error(`Expected TEXT in column ${column}, got ${describe(value)}.`);\n}\n\n/**\n * ← `SqliteDatabase::Query::getInt64(uint column)`.\n *\n * Narrowed to a safe integer rather than upstream's `int64_t`: a JS number\n * cannot carry the full range, and a silently-rounded row id or alarm time is\n * exactly the kind of corruption this layer must not produce.\n */\nexport function getInt64(row: readonly unknown[], column: number): number {\n const value = row[column];\n if (typeof value === \"number\" && Number.isSafeInteger(value)) return value;\n if (typeof value === \"bigint\") {\n const narrowed = Number(value);\n if (Number.isSafeInteger(narrowed) && BigInt(narrowed) === value) return narrowed;\n }\n throw new Error(`Expected a safe integer in column ${column}, got ${describe(value)}.`);\n}\n\nfunction describe(value: unknown): string {\n if (value === null) return \"NULL\";\n if (value instanceof Uint8Array) return \"a BLOB\";\n return `${typeof value} ${String(value)}`;\n}\n\ntype Savepoint = {\n name: string;\n /** Size of `rollbackCallbacks` when this savepoint was created. */\n rollbackCallbackIndex: number;\n};\n\n/**\n * ← `SqliteDatabase`, restricted to the members `sqlite-kv` and\n * `sqlite-metadata` actually call.\n *\n * The one piece of real machinery here is the transaction/savepoint stack that\n * `onRollback()` needs. Upstream learns of a `BEGIN` / `SAVEPOINT` / `COMMIT` /\n * `RELEASE` / `ROLLBACK` from the SQLite authorizer while the statement is\n * being compiled (`prepareSql` fills a `ParseContext::stateChange`); we have no\n * authorizer, so the statement text is the only source. `applyChange` below is\n * a line-for-line port of upstream's; only where the `StateChange` comes from\n * differs.\n */\nexport class SqliteDatabase {\n readonly #backend: SqlDatabase;\n readonly #resetListeners = new Set<ResetListener>();\n\n /** Callbacks registered with onRollback that haven't been committed nor rolled back yet. */\n #rollbackCallbacks: (() => void)[] = [];\n /** Savepoints that haven't been committed nor rolled back yet. */\n #savepoints: Savepoint[] = [];\n /** True if in a BEGIN TRANSACTION transaction. */\n #inTransaction = false;\n /** ← `criticalErrorOccurred`, holding the exception rather than a bool. */\n #criticalError: SqliteCriticalError | undefined;\n /** ← `onWriteCallback`. */\n #onWriteCallback: ((allowUnconfirmed: boolean) => void) | undefined;\n /** ← `onCriticalErrorCallback`. */\n #onCriticalErrorCallback: ((exception: SqliteCriticalError) => void) | undefined;\n\n constructor(backend: SqlDatabase) {\n this.#backend = backend;\n }\n\n /**\n * Invokes the given callback whenever a query begins which may write to the\n * database. The callback is called just before executing the query.\n *\n * Durable Objects uses this to automatically begin a transaction and close the\n * output gate.\n *\n * Note that the write callback is NOT called before (or at any point during) a\n * `reset()`. Use the `ResetListener` mechanism for that case.\n */\n onWrite(callback: (allowUnconfirmed: boolean) => void): void {\n this.#onWriteCallback = callback;\n }\n\n /**\n * Invokes the given callback when a \"critical error\" causes an automatic\n * rollback during a transaction.\n *\n * See: https://www.sqlite.org/lang_transaction.html#response_to_errors_within_a_transaction\n *\n * Upstream passes `(errorMessage, maybeException)` and lets the caller build\n * the exception; `#checkForAutoRollback` has already built one by the time it\n * can tell a rollback happened, so the callback receives that.\n */\n onCriticalError(callback: (exception: SqliteCriticalError) => void): void {\n this.#onCriticalErrorCallback = callback;\n }\n\n /**\n * Invoke the onWrite() callback.\n *\n * \"This is useful when the caller is about to execute a statement which SQLite\n * considers read-only, but needs to be considered a write for our purposes. In\n * particular, we use the onWrite callback to start automatic transactions, and\n * we use the SAVEPOINT statement to implement explicit transactions. For\n * synchronous transactions, the explicit transaction needs to be nested inside\n * the automatic transaction, so we need to force an auto-transaction to start\n * before the SAVEPOINT.\"\n */\n notifyWrite(allowUnconfirmed = false): void {\n this.#onWriteCallback?.(allowUnconfirmed);\n }\n\n /** ← `SqliteDatabase::run`, in both its bare and its `QueryOptions` form. */\n run(sql: string, ...bindings: SqlValue[]): SqlResult;\n run(options: QueryOptions, sql: string, ...bindings: SqlValue[]): SqlResult;\n run(first: string | QueryOptions, ...rest: SqlValue[]): SqlResult {\n if (typeof first === \"string\") return this.#exec(first, rest, false);\n\n const [sql, ...bindings] = rest;\n if (typeof sql !== \"string\") throw new Error(\"run(options, sql, ...) takes a SQL string.\");\n return this.#exec(sql, bindings, first.allowUnconfirmed ?? false, first.regulate);\n }\n\n /** ← `SqliteDatabase::ingestSql`: execute complete statements, retain the partial tail. */\n ingest(sql: string, regulate?: (sql: string) => void): SqlIngestResult {\n this.assertUsable();\n let remainder = sql;\n let rowsRead = 0;\n let rowsWritten = 0;\n let statementCount = 0;\n\n while (hasSqlStatement(remainder)) {\n let statement: SqlDatabaseStatement;\n try {\n statement = this.#backend.prepare(remainder);\n } catch (error) {\n if (error instanceof Error && /incomplete input/i.test(error.message)) break;\n throw error;\n }\n try {\n // sqlite3_complete_length(), which upstream uses, requires the terminating semicolon.\n if (!statement.sql.trimEnd().endsWith(\";\")) break;\n regulate?.(statement.sql);\n if (statement.parameterCount !== 0) throw new Error(SQL_WRONG_BINDINGS_MESSAGE);\n const result = this.#execStatement(statement, [], false);\n rowsRead += result.rawRows.length;\n rowsWritten += result.rowsWritten;\n statementCount += 1;\n remainder = remainder.slice(statement.sql.length);\n } finally {\n statement.close();\n }\n }\n\n return { remainder, rowsRead, rowsWritten, statementCount };\n }\n\n #exec(\n sql: string,\n bindings: readonly SqlValue[],\n allowUnconfirmed: boolean,\n regulate?: (sql: string) => void,\n ): SqlResult {\n this.assertUsable();\n\n let remaining = sql;\n let result: SqlResult | undefined;\n while (hasSqlStatement(remaining)) {\n const statement = this.#backend.prepare(remaining);\n const tail = remaining.slice(statement.sql.length);\n const isFinal = !hasSqlStatement(tail);\n try {\n regulate?.(statement.sql);\n if (!isFinal && statement.parameterCount !== 0) {\n throw new Error(SQL_PRELUDE_BINDINGS_MESSAGE);\n }\n if (isFinal && statement.parameterCount !== bindings.length) {\n throw new Error(SQL_WRONG_BINDINGS_MESSAGE);\n }\n result = this.#execStatement(\n statement,\n isFinal ? bindings : [],\n isFinal ? allowUnconfirmed : false,\n );\n } finally {\n statement.close();\n }\n remaining = tail;\n }\n if (result === undefined) throw new Error(\"Expected at least one SQL statement.\");\n return result;\n }\n\n /** Execute one statement from `run()`'s batch; bindings and results belong to the last one. */\n #execStatement(\n statement: SqlDatabaseStatement,\n bindings: readonly SqlValue[],\n allowUnconfirmed: boolean,\n ): SqlResult {\n const { sql } = statement;\n const change = classify(sql);\n\n // Before the statement runs, as upstream's `Query::checkRequirements` does: the callback opens\n // the transaction this statement is about to write into, and it is also allowed to refuse the\n // statement outright when whatever owns the transaction is already broken.\n if (isWrite(sql)) this.notifyWrite(allowUnconfirmed);\n\n let result: SqlResult;\n try {\n result = statement.execute(bindings);\n } catch (error) {\n this.#checkForAutoRollback(error);\n throw error;\n }\n // Upstream applies the effect on the statement's first step, i.e. after it\n // has actually run, so a statement that throws changes nothing.\n this.#applyChange(change);\n return result;\n }\n\n /**\n * ← `SqliteDatabase::handleCriticalError`, which reaches the same conclusion\n * from the error code plus `sqlite3_get_autocommit`. We do not see the error\n * code — the backend has already turned it into a JS exception — so the\n * disagreement between our stack and the backend's is the whole signal, and\n * it is enough: we only ask after a statement has failed, and the only thing\n * that closes a transaction without going through `run()` is SQLite itself.\n *\n * The callbacks are DISCARDED rather than invoked, which looks wrong for two\n * lines and is not. Invoking them is only correct when a rollback actually\n * happened, and the branch above is the sole case where that is knowable; in\n * the ordinary case — a constraint violation, which does not roll anything\n * back — firing them would restore a cache the database has moved past, which\n * is corruption in the other direction. Upstream does not fire them here\n * either. It makes the actor fatal instead, and so does this: the caches are\n * knowingly stale, so the database is finished rather than repaired.\n */\n #checkForAutoRollback(cause: unknown): void {\n if (!this.#inTransaction && this.#savepoints.length === 0) return;\n if (this.#backend.inTransaction) return;\n\n this.#inTransaction = false;\n this.#savepoints = [];\n this.#rollbackCallbacks = [];\n const critical = new SqliteCriticalError(\n \"SQLite rolled back the open transaction in response to a critical error, so every \" +\n \"in-memory view of this database is now stale and it can no longer be used.\",\n { cause },\n );\n this.#criticalError = critical;\n this.#onCriticalErrorCallback?.(critical);\n throw critical;\n }\n\n /**\n * ← `SqliteDatabase::observedCriticalError()`. The named state\n * `io/actor-sqlite.ts` wires to `onBroken`, so it does not have to re-derive\n * the condition from an exception it caught.\n */\n observedCriticalError(): SqliteCriticalError | undefined {\n return this.#criticalError;\n }\n\n /**\n * The guard for any read this package serves from a cache rather than from a\n * statement. Those are the only paths a latched critical error would not\n * already stop, and they are exactly the paths whose answer is wrong once\n * SQLite has rolled back underneath them.\n */\n assertUsable(): void {\n if (this.#criticalError !== undefined) throw this.#criticalError;\n }\n\n get databaseSize(): number {\n return this.#backend.databaseSize;\n }\n\n /**\n * ← `SqliteDatabase::onRollback`.\n *\n * \"Register a callback which shall be called if the current transaction is\n * rolled back. If the current transaction commits, then the callback is\n * discarded without invoking it. [...] When a rollback occurs, callbacks are\n * invoked in the reverse of the order in which they were registered.\"\n *\n * With nothing open there is nothing that can roll back, so the callback is\n * dropped — upstream's `if (inTransaction || !savepoints.empty())`.\n */\n onRollback(callback: () => void): void {\n if (this.#inTransaction || this.#savepoints.length > 0) {\n this.#rollbackCallbacks.push(callback);\n }\n }\n\n addResetListener(listener: ResetListener): void {\n this.#resetListeners.add(listener);\n }\n\n removeResetListener(listener: ResetListener): void {\n this.#resetListeners.delete(listener);\n }\n\n /** ← `SqliteDatabase::reset()`. */\n reset(): void {\n // Refused for the same reason `run()` is: the listeners below read their own\n // state on the way out, and after a critical error that state is stale.\n this.assertUsable();\n // \"If transactions are open during reset(), whatever had the transaction\n // open is going to get confused at best, or lose data at worst.\"\n if (this.#inTransaction || this.#savepoints.length > 0) {\n throw new Error(\"can't reset() a database during a transaction\");\n }\n for (const listener of this.#resetListeners) {\n listener.beforeSqliteReset();\n }\n this.#backend.reset();\n }\n\n close(): void {\n this.#backend.close();\n }\n\n /** ← `SqliteDatabase::applyChange`, ported statement for statement. */\n #applyChange(change: StateChange): void {\n switch (change.kind) {\n case \"none\":\n break;\n\n case \"begin\":\n if (change.savepointName !== null) {\n this.#savepoints.push({\n name: change.savepointName,\n rollbackCallbackIndex: this.#rollbackCallbacks.length,\n });\n } else {\n assert(\n this.#savepoints.length === 0,\n \"BEGIN TRANSACTION should have failed when savepoints are present?\",\n );\n assert(\n !this.#inTransaction,\n \"BEGIN TRANSACTION should have failed when already in a transaction?\",\n );\n assert(\n this.#rollbackCallbacks.length === 0,\n \"we shouldn't have been keeping rollback callbacks with no transaction open!\",\n );\n this.#inTransaction = true;\n }\n break;\n\n case \"commit\":\n if (change.savepointName !== null) {\n // Per https://www.sqlite.org/lang_savepoint.html, releasing a savepoint also releases\n // all later savepoints.\n for (;;) {\n const savepoint = this.#savepoints.pop();\n assert(savepoint !== undefined, \"released a savepoint that didn't exist?\");\n if (savepoint.name === change.savepointName) break;\n }\n } else {\n assert(this.#inTransaction, \"COMMIT TRANSACTION without BEGIN TRANSACTION?\");\n // Since BEGIN TRANSACTION cannot be nested within a savepoint, this must have released\n // all savepoints implicitly.\n this.#savepoints = [];\n this.#inTransaction = false;\n }\n if (this.#savepoints.length === 0 && !this.#inTransaction) {\n this.#rollbackCallbacks = [];\n }\n break;\n\n case \"rollback\":\n if (change.savepointName !== null) {\n for (;;) {\n const savepoint = this.#savepoints[this.#savepoints.length - 1];\n assert(savepoint !== undefined, \"released a savepoint that didn't exist?\");\n if (savepoint.name === change.savepointName) {\n this.#runRollbackCallbacksDownTo(savepoint.rollbackCallbackIndex);\n // Rolling back to a savepoint does not release it, so it stays on the stack and\n // must be released separately.\n break;\n }\n this.#savepoints.pop();\n }\n } else {\n assert(this.#inTransaction, \"ROLLBACK TRANSACTION without BEGIN TRANSACTION?\");\n this.#savepoints = [];\n this.#inTransaction = false;\n this.#runRollbackCallbacksDownTo(0);\n }\n break;\n }\n }\n\n #runRollbackCallbacksDownTo(index: number): void {\n assert(this.#rollbackCallbacks.length >= index, \"rollback callback stack shrank?\");\n while (this.#rollbackCallbacks.length > index) {\n // Upstream pops first and then invokes, so a callback that throws does not leave itself on\n // the stack to be invoked a second time by the next rollback.\n const callback = this.#rollbackCallbacks.pop();\n assert(callback !== undefined, \"rollback callback stack shrank?\");\n callback();\n }\n }\n}\n\ntype SqliteSchemaDatabase = Pick<SqlDatabase, \"exec\"> | Pick<SqliteDatabase, \"run\">;\n\n/**\n * Returns whether a runtime-owned table exists, and refuses any present shape\n * other than the one this release writes.\n */\nexport function hasCurrentSqliteTable(\n db: SqliteSchemaDatabase,\n name: string,\n createSql: string,\n): boolean {\n // SQLite identifiers are ASCII case-insensitive, so schema validation must\n // find the same object that CREATE TABLE IF NOT EXISTS would collide with.\n const query = \"SELECT type, sql FROM sqlite_master WHERE name = ? COLLATE NOCASE\";\n const rows = \"run\" in db ? db.run(query, name).rawRows : db.exec(query, [name]).rawRows;\n const row = rows[0];\n if (row === undefined) return false;\n if (\n rows.length !== 1 ||\n row[0] !== \"table\" ||\n typeof row[1] !== \"string\" ||\n normalizeSchemaSql(row[1]) !== normalizeSchemaSql(createSql)\n ) {\n throw new Error(\n `Incompatible @mcp-b/do-runtime storage schema for table \"${name}\". ` +\n \"This release accepts only the current schema and does not migrate stored runtime data.\",\n );\n }\n return true;\n}\n\nfunction normalizeSchemaSql(sql: string): string {\n return sql\n .replace(/\\bIF\\s+NOT\\s+EXISTS\\b/giu, \"\")\n .replace(/\\s+/gu, \" \")\n .trim()\n .replace(/;$/u, \"\")\n .toLowerCase();\n}\n\nfunction assert(condition: boolean, message: string): asserts condition {\n if (!condition) throw new Error(message);\n}\n\n/** ← `SqliteDatabase::StateChange`. */\ntype StateChange =\n | { kind: \"none\" }\n | { kind: \"begin\"; savepointName: string | null }\n | { kind: \"commit\"; savepointName: string | null }\n | { kind: \"rollback\"; savepointName: string | null };\n\nconst NO_CHANGE: StateChange = { kind: \"none\" };\n\n/** SQLite savepoint names compare case-insensitively, so the stack stores them folded. */\nfunction savepointName(raw: string): string {\n const unquoted =\n (raw.startsWith('\"') && raw.endsWith('\"')) ||\n (raw.startsWith(\"'\") && raw.endsWith(\"'\")) ||\n (raw.startsWith(\"`\") && raw.endsWith(\"`\"))\n ? raw.slice(1, -1)\n : raw.startsWith(\"[\") && raw.endsWith(\"]\")\n ? raw.slice(1, -1)\n : raw;\n return unquoted.toLowerCase();\n}\n\nconst NAME = String.raw`(\"[^\"]*\"|'[^']*'|\\`[^\\`]*\\`|\\[[^\\]]*\\]|[A-Za-z_][A-Za-z0-9_$]*)`;\nconst BEGIN = new RegExp(\n String.raw`^BEGIN(\\s+(DEFERRED|IMMEDIATE|EXCLUSIVE))?(\\s+TRANSACTION)?$`,\n \"i\",\n);\nconst SAVEPOINT = new RegExp(String.raw`^SAVEPOINT\\s+${NAME}$`, \"i\");\nconst COMMIT = new RegExp(String.raw`^(COMMIT|END)(\\s+TRANSACTION)?$`, \"i\");\nconst RELEASE = new RegExp(String.raw`^RELEASE(\\s+SAVEPOINT)?\\s+${NAME}$`, \"i\");\nconst ROLLBACK = new RegExp(String.raw`^ROLLBACK(\\s+TRANSACTION)?$`, \"i\");\nconst ROLLBACK_TO = new RegExp(\n String.raw`^ROLLBACK(\\s+TRANSACTION)?\\s+TO(\\s+SAVEPOINT)?\\s+${NAME}$`,\n \"i\",\n);\nconst TRANSACTION_KEYWORD = /^(BEGIN|COMMIT|END|ROLLBACK|SAVEPOINT|RELEASE)\\b/i;\n\n/** Every group referenced below is mandatory in its pattern, so an absent one is a broken pattern. */\nfunction group(match: RegExpExecArray, index: number): string {\n const value = match[index];\n if (value === undefined) throw new Error(`SQL pattern group ${index} did not match: ${match[0]}`);\n return value;\n}\n\n/**\n * Derives upstream's `StateChange` from the statement text.\n *\n * A statement that opens with a transaction keyword but does not match one of\n * the forms below throws rather than being classified as `NoChange`, since\n * guessing in that direction is what loses a rollback callback. `run()` asks\n * the backend to compile one native statement at a time and applies this state\n * change after each executes, matching workerd's `prepareMulti()` prelude.\n */\nfunction classify(sql: string): StateChange {\n const statement = stripLeadingTrivia(sql).trim().replace(/;$/, \"\").trimEnd();\n\n const rollbackTo = ROLLBACK_TO.exec(statement);\n if (rollbackTo !== null) {\n return { kind: \"rollback\", savepointName: savepointName(group(rollbackTo, 3)) };\n }\n if (ROLLBACK.test(statement)) return { kind: \"rollback\", savepointName: null };\n\n const savepoint = SAVEPOINT.exec(statement);\n if (savepoint !== null) {\n return { kind: \"begin\", savepointName: savepointName(group(savepoint, 1)) };\n }\n if (BEGIN.test(statement)) return { kind: \"begin\", savepointName: null };\n\n const release = RELEASE.exec(statement);\n if (release !== null) {\n return { kind: \"commit\", savepointName: savepointName(group(release, 2)) };\n }\n if (COMMIT.test(statement)) return { kind: \"commit\", savepointName: null };\n\n if (TRANSACTION_KEYWORD.test(statement)) {\n throw new Error(`Unrecognized transaction-control statement: ${statement}`);\n }\n return NO_CHANGE;\n}\n\n/**\n * ← `!sqlite3_stmt_readonly(statement)`, the test upstream's `onWrite` gate is\n * written against (`sqlite.c++:1562-1568`).\n *\n * It is NOT the authorizer — the authorizer never sees this question, and the\n * distinction is load bearing: `sqlite3_stmt_readonly()` reports BEGIN, COMMIT,\n * ROLLBACK, SAVEPOINT and RELEASE as read-only, which is why `notifyWrite()`\n * exists at all and why an automatic transaction's own `BEGIN` does not recurse\n * into the callback that issued it.\n *\n * Neither backend exposes the compiled statement, so the text is the only\n * source, and it fails closed the way `classify` does: **a statement is a write\n * unless it provably is not.** The complete read set is `SELECT` and `EXPLAIN`\n * plus the five transaction-control forms. `WITH`, `PRAGMA` and anything\n * unrecognised are writes, which costs a read-only CTE a transaction and an\n * output-gate lock it does not need, and cannot cost atomicity — which is the\n * only error this classification is allowed to make.\n */\nfunction isWrite(sql: string): boolean {\n return !NON_WRITE_KEYWORDS.has(leadingKeyword(sql));\n}\n\nconst NON_WRITE_KEYWORDS = new Set([\n \"SELECT\",\n \"EXPLAIN\",\n \"BEGIN\",\n \"COMMIT\",\n \"END\",\n \"ROLLBACK\",\n \"SAVEPOINT\",\n \"RELEASE\",\n]);\n\n/** The first bare word, skipping whitespace and both comment forms. */\nfunction leadingKeyword(sql: string): string {\n return (/^[A-Za-z]+/.exec(stripLeadingTrivia(sql))?.[0] ?? \"\").toUpperCase();\n}\n\nfunction hasSqlStatement(sql: string): boolean {\n return stripLeadingTrivia(sql).trim().length > 0;\n}\n\nfunction stripLeadingTrivia(statement: string): string {\n let index = 0;\n for (;;) {\n while (index < statement.length && /\\s/.test(statement.charAt(index))) index += 1;\n if (statement[index] === \"-\" && statement[index + 1] === \"-\") {\n const newline = statement.indexOf(\"\\n\", index);\n index = newline === -1 ? statement.length : newline + 1;\n continue;\n }\n if (statement[index] === \"/\" && statement[index + 1] === \"*\") {\n const close = statement.indexOf(\"*/\", index + 2);\n index = close === -1 ? statement.length : close + 2;\n continue;\n }\n return statement.slice(index);\n }\n}\n"],"mappings":"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"}