@evolu/sqlite-wasm 2.2.4 → 3.53.4-build1

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 (62) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +55 -218
  3. package/dist/src/CApi.d.ts +2108 -0
  4. package/dist/src/CApi.d.ts.map +1 -0
  5. package/dist/src/CApi.js +1919 -0
  6. package/dist/src/Constants.d.ts +868 -0
  7. package/dist/src/Constants.d.ts.map +1 -0
  8. package/dist/src/Constants.js +602 -0
  9. package/dist/src/Database.d.ts +641 -0
  10. package/dist/src/Database.d.ts.map +1 -0
  11. package/dist/src/Database.js +1177 -0
  12. package/dist/src/Memory.d.ts +119 -0
  13. package/dist/src/Memory.d.ts.map +1 -0
  14. package/dist/src/Memory.js +207 -0
  15. package/dist/src/Pointer.d.ts +100 -0
  16. package/dist/src/Pointer.d.ts.map +1 -0
  17. package/dist/src/Pointer.js +15 -0
  18. package/dist/src/SahPool.d.ts +744 -0
  19. package/dist/src/SahPool.d.ts.map +1 -0
  20. package/dist/src/SahPool.js +1985 -0
  21. package/dist/src/Wasm.d.ts +315 -0
  22. package/dist/src/Wasm.d.ts.map +1 -0
  23. package/dist/src/Wasm.js +756 -0
  24. package/dist/src/WasmUrl.d.ts +21 -0
  25. package/dist/src/WasmUrl.d.ts.map +1 -0
  26. package/dist/src/WasmUrl.js +20 -0
  27. package/dist/src/c-api/index.d.ts +9 -0
  28. package/dist/src/c-api/index.d.ts.map +1 -0
  29. package/dist/src/c-api/index.js +8 -0
  30. package/dist/src/index.d.ts +15 -0
  31. package/dist/src/index.d.ts.map +1 -0
  32. package/dist/src/index.js +13 -0
  33. package/dist/wasm/sqlite3.wasm +0 -0
  34. package/package.json +60 -60
  35. package/src/CApi.test.ts +217 -0
  36. package/src/CApi.ts +3294 -0
  37. package/src/Constants.ts +803 -0
  38. package/src/Database.ts +1714 -0
  39. package/src/Memory.test.ts +319 -0
  40. package/src/Memory.ts +237 -0
  41. package/src/Pointer.ts +121 -0
  42. package/src/SahPool.test.ts +180 -0
  43. package/src/SahPool.ts +2627 -0
  44. package/src/Wasm.test.ts +444 -0
  45. package/src/Wasm.ts +1026 -0
  46. package/src/WasmUrl.ts +26 -0
  47. package/src/c-api/index.ts +9 -0
  48. package/src/index.ts +15 -0
  49. package/bin/index.js +0 -110
  50. package/index.d.ts +0 -8118
  51. package/index.mjs +0 -7
  52. package/node.mjs +0 -3
  53. package/sqlite-wasm/jswasm/sqlite3-bundler-friendly.mjs +0 -13659
  54. package/sqlite-wasm/jswasm/sqlite3-node.mjs +0 -11671
  55. package/sqlite-wasm/jswasm/sqlite3-opfs-async-proxy.js +0 -691
  56. package/sqlite-wasm/jswasm/sqlite3-worker1-bundler-friendly.mjs +0 -35
  57. package/sqlite-wasm/jswasm/sqlite3-worker1-promiser.js +0 -193
  58. package/sqlite-wasm/jswasm/sqlite3-worker1-promiser.mjs +0 -187
  59. package/sqlite-wasm/jswasm/sqlite3-worker1.js +0 -46
  60. package/sqlite-wasm/jswasm/sqlite3.js +0 -13697
  61. package/sqlite-wasm/jswasm/sqlite3.mjs +0 -13661
  62. package/sqlite-wasm/jswasm/sqlite3.wasm +0 -0
@@ -0,0 +1,1177 @@
1
+ /**
2
+ * Evolu's SQLite API: databases and statements over the CApi functions, with
3
+ * typed errors and explicit resource management.
4
+ *
5
+ * Status: {@link createSqliteDatabase} opens Memory databases, and File and
6
+ * EncryptedFile databases on a {@link SqliteVfs} or {@link SqliteEncryptingVfs},
7
+ * which a {@link SahPool} is, and {@link createEncryptedSqliteDatabase} also
8
+ * opens the encrypted databases `@evolu/web` 3 created with
9
+ * `@evolu/sqlite-wasm` 2.2.4 in a pool. The pool's own encryption is tested in
10
+ * Node.js over a fake OPFS against 2.2.4, which creates databases as
11
+ * `@evolu/web` does and leaves a hot journal the pool rolls back, and in
12
+ * Chromium, Firefox and WebKit on real OPFS against 2.2.4.
13
+ *
14
+ * ## Calls
15
+ *
16
+ * - Synchronous. Every call returns when SQLite returns. The only asynchronous
17
+ * step, deriving a legacy key with WebCrypto, runs between the two opens of
18
+ * {@link createEncryptedSqliteDatabase}.
19
+ * - Bound once per database. {@link createSqliteDatabase} binds the C functions
20
+ * and memory helpers it needs and allocates one {@link SqliteScratch}; running
21
+ * a statement then calls wasm exports directly.
22
+ * - Every fallible call returns a Result. {@link SqliteError} copies
23
+ * `sqlite3_errmsg` and `sqlite3_extended_errcode` right after the failing
24
+ * call, before any other call on the connection replaces them. It copies
25
+ * `sqlite3_error_offset` only for SQLITE_ERROR from prepare, exec or step,
26
+ * the code of the parser errors that set it, because SQLite keeps an offset
27
+ * until a later prepare succeeds or a statement resets.
28
+ * - The VFS records the first failure since the last top-level call (open,
29
+ * prepare, run, exec, export), and {@link SqliteError.cause} carries it. The
30
+ * database clears the record before each such call. A call that runs inside
31
+ * another call of a database on the same VFS, as a query a callback runs
32
+ * does, is not top-level, so it leaves the record to the outer call.
33
+ * - Every call, disposal included, enters wasm through {@link SqliteWasm.call}. An
34
+ * exception or trap that escapes wasm, such as from a call through a disposed
35
+ * function pointer, is a defect: it is rethrown, and every later call on any
36
+ * database of the instance throws without entering wasm, because the C stack
37
+ * and SQLite's state are left inconsistent. A trap after a File or
38
+ * EncryptedFile connection opened reaches the caller of the open as a
39
+ * `SuppressedError` whose `suppressed` is the trap, because the open's
40
+ * deferred close and the free of its scratch memory are refused as it
41
+ * unwinds.
42
+ *
43
+ * ## Opening
44
+ *
45
+ * - A Memory database opens `:memory:` on the default VFS.
46
+ * - A File or EncryptedFile database's path is a {@link SqliteVfsPath}, a
47
+ * canonical path, which SQLite passes to the VFS as it is, never as a URI or
48
+ * an in-memory database, followed by the name of the VFS.
49
+ * - A File or EncryptedFile database opens its path on its VFS, an EncryptedFile
50
+ * database keyed as the next section describes. Either then prepares a query
51
+ * of `sqlite_schema`, because SQLite reads a file only when a statement needs
52
+ * it. So the open rolls back a hot journal, and fails for a file that is not
53
+ * a database, with SQLITE_NOTADB, or for a hot journal that cannot be rolled
54
+ * back.
55
+ * - The database uses its VFS only through {@link SqliteVfs} or
56
+ * {@link SqliteEncryptingVfs}, so any VFS that implements them can hold it,
57
+ * and a driver can open each database on the VFS that has it, as
58
+ * {@link SqliteVfs.getPaths} tells.
59
+ * - Flags are READWRITE, CREATE and EXRESCODE, so failures during the open
60
+ * already carry extended codes.
61
+ * - When the open fails, the message is read first and then the handle SQLite
62
+ * returned anyway is closed with `sqlite3_close_v2`. Without a handle, as
63
+ * when memory runs out, the error is the open's result code.
64
+ *
65
+ * ## Encryption
66
+ *
67
+ * - The VFS encrypts an EncryptedFile database itself, and a {@link SahPool} does
68
+ * it as SQLite3 Multiple Ciphers' `sqlcipher` scheme did with its default
69
+ * parameters, those of SQLCipher 4, which `PRAGMA cipher = 'sqlcipher'`
70
+ * selected in `@evolu/web` 3 with 2.2.4. The open registers the key for the
71
+ * path with {@link SqliteEncryptingVfs.registerKey} before it enters wasm and
72
+ * disposes the registration when it returns, by when the VFS has taken the
73
+ * key for the database file; the module documentation of `SahPool.ts`
74
+ * describes the pool's format. The key never appears in SQL, a URI, an error
75
+ * or SQLite's memory, and the caller's key is never modified.
76
+ * - Then `sqlite3_file_control` with SQLITE_FCNTL_RESERVE_BYTES asks for 80 bytes
77
+ * at the end of each page, where the pool keeps the page's IV and HMAC, which
78
+ * takes effect for a new database, and `PRAGMA secure_delete = ON` makes
79
+ * deletes overwrite freed content, as SQLite3 Multiple Ciphers did for every
80
+ * encrypted connection. A {@link SahPool} refuses to turn it off for an
81
+ * encrypted database, because a freed page SQLite does not write would fail
82
+ * to authenticate, as `SahPool.ts` describes.
83
+ * - Registering validates nothing, so the read of `sqlite_schema` checks the key.
84
+ * A wrong key fails there with SQLITE_NOTADB, from page 1, or with
85
+ * SQLITE_CORRUPT when the first page of a hot journal is another page, and
86
+ * {@link SqliteError.cause} is the VFS's {@link SqlitePageAuthenticationError}.
87
+ * The rollback stops at that first page, which fails to authenticate, so it
88
+ * writes no page of the journal and keeps the journal for the right key.
89
+ * Before that page, SQLite truncates the database to the size the journal
90
+ * records, as the right key's rollback does too. It would extend a shorter
91
+ * database by one zeroed page, encrypted with the wrong key, but a commit
92
+ * shrinks the file only after it deletes the journal, and a pool refuses a
93
+ * VACUUM that would change an encrypted database's page size, so no database
94
+ * is shorter than its hot journal records.
95
+ * - `@evolu/web` with 2.2.4 ran `PRAGMA key = "x'<hex>'"`, SQLCipher's notation
96
+ * for a raw key, which SQLite3 Multiple Ciphers 2.2.4 took as a passphrase
97
+ * because of a bug
98
+ * (https://github.com/utelle/SQLite3MultipleCiphers/issues/218, fixed in
99
+ * 2.2.5), deriving the key with the `sqlcipher` scheme, so those databases
100
+ * fail as with a wrong key. {@link createEncryptedSqliteDatabase} then opens
101
+ * them with the key {@link deriveLegacySqliteKey} derives through
102
+ * {@link SubtleCryptoDep}, and {@link SqliteEncryptedDatabase.keyDerivation}
103
+ * says which key opened the database. Nothing rekeys a database.
104
+ * - `@evolu/web` 1.0.1-preview.6 to 2.4.0 also ran `PRAGMA legacy = 4`, so the
105
+ * databases it encrypted fail with SQLITE_NOTADB, as with a wrong key, since
106
+ * page 1 is encrypted from byte 16; they have not opened since `@evolu/web`
107
+ * 3.0.0 (see `SahPool.ts`).
108
+ * - Only page 1 of the database or a record of its journal that failed to
109
+ * authenticate counts as a wrong key. Another page of the database that fails
110
+ * means that page 1 proved the key and the page is damaged, so it fails with
111
+ * SQLITE_CORRUPT and that page as the cause, also with the key 2.2.4 derived.
112
+ * An I/O error fails with its own code, and the VFS's record as its cause,
113
+ * and a database whose pages authenticate but whose content is malformed
114
+ * fails with SQLite's code and no cause.
115
+ *
116
+ * ## Statements
117
+ *
118
+ * - SQL is copied into its own memory, which is freed when the call returns,
119
+ * because SQLite parses it in place and copies a statement's SQL, which a
120
+ * schema change prepares again, only after the parse. Meanwhile a callback,
121
+ * such as a collation-needed callback during the parse or a function during a
122
+ * step of {@link SqliteDatabase.exec}, can run a query on the same database,
123
+ * and that query reuses the scratch memory.
124
+ * - `sqlite3_prepare_v3` gets the SQL's byte length, counting a NUL terminator,
125
+ * which spares SQLite a copy, and, for statements
126
+ * {@link SqliteDatabase.prepare} returns, SQLITE_PREPARE_PERSISTENT. Empty SQL
127
+ * and SQL with more than one statement are errors, so nothing after the first
128
+ * statement is silently ignored. SQLite reads SQL only up to a NUL character,
129
+ * and UTF-8 cannot encode a lone surrogate, which encoding would replace with
130
+ * U+FFFD, so SQL that contains either fails with
131
+ * {@link SqliteInvalidSqlTextError} before SQLite sees it, in
132
+ * {@link SqliteDatabase.exec} too.
133
+ * - A second statement is found by scanning the rest of the SQL as SQLite
134
+ * 3.53.4's tokenizer reads it, not by preparing it, because SQLite applies
135
+ * many pragmas, such as `foreign_keys`, when it prepares them, so SQL that
136
+ * fails with {@link SqliteMultipleStatementsError} would still change the
137
+ * connection. Only semicolons, whitespace, UTF-8 byte order marks and
138
+ * comments may follow the first statement. A pragma in the first statement
139
+ * still takes effect when the SQL then fails, as SQLite documents for prepare
140
+ * (https://sqlite.org/pragma.html).
141
+ * - {@link SqliteDatabase.exec} prepares each statement where the previous one
142
+ * ended and steps it to completion, so {@link SqliteError.sqlOffset} counts
143
+ * from the start of the whole SQL.
144
+ * - Parameters are positional. Their number is read once at prepare and a run
145
+ * with a different number fails, so no binding leaks from a previous run.
146
+ * {@link SqliteDatabase.exec} binds none, so a statement with a parameter
147
+ * fails there without running, rather than run with NULL.
148
+ * - Binding: null binds NULL; a 32-bit integer binds with `sqlite3_bind_int`;
149
+ * another safe integer with `sqlite3_bind_int64`, so it stays INTEGER; any
150
+ * other number with `sqlite3_bind_double`. Text is encoded with `encodeInto`
151
+ * and bytes are copied into the scratch memory, both bound with
152
+ * SQLITE_TRANSIENT and an explicit byte length, so embedded NUL characters
153
+ * survive and an empty value stays non-NULL. A value that can take more than
154
+ * 64 KiB, counting 3 bytes per UTF-16 code unit of text, is allocated exactly
155
+ * and handed over with SQLITE_WASM_DEALLOC instead, so the scratch memory
156
+ * does not keep the size of the largest value, and SQLite does not copy it
157
+ * twice. Such text is encoded into a new array first, and when JavaScript
158
+ * cannot allocate it, the run fails with SQLITE_NOMEM and the operation
159
+ * `bind`, while the instance keeps working, because the encoding runs no
160
+ * wasm. {@link SqliteDatabase.exec} copies its SQL the same way and fails with
161
+ * the operation `exec`. SQLite keeps a bound value until its parameter is
162
+ * bound again, so a run that handed a value over clears the bindings before
163
+ * it returns, also when it fails, and a prepared statement keeps no large
164
+ * value between runs, while smaller ones keep the buffers SQLite reuses for
165
+ * them. A {@link SqliteValue} string may contain a lone surrogate, which UTF-8
166
+ * cannot encode, so text binds it as U+FFFD, as better-sqlite3 does.
167
+ * - Reading: `sqlite3_column_type` first. INTEGER and FLOAT read with
168
+ * `sqlite3_column_double`, so no BigInt is created; integers beyond 2^53 lose
169
+ * precision, as in better-sqlite3. TEXT reads the pointer, then the byte
170
+ * length, and decodes keeping a leading byte order mark. A BLOB is one copy
171
+ * into its own `ArrayBuffer`, so it can be transferred. With the type read
172
+ * first, a NULL pointer is never SQL NULL: for TEXT it is SQLITE_NOMEM, and
173
+ * for a BLOB it is an empty value unless `sqlite3_errcode`, read right after,
174
+ * returns SQLITE_NOMEM. A NULL column name is SQLITE_NOMEM too, and so is a
175
+ * TEXT or BLOB that JavaScript cannot allocate a copy of, whatever error the
176
+ * engine throws then, because the copy runs no wasm, so the instance keeps
177
+ * working. Each fails the run with the operation `step`. Rows are
178
+ * null-prototype objects, so a column named `__proto__` is an ordinary
179
+ * property, and of columns with the same name, the last one wins.
180
+ * - Column names are read after the first row and cached with the statement's
181
+ * SQLITE_STMTSTATUS_REPREPARE counter. A schema change re-prepares the
182
+ * statement, and `SELECT *` then returns other columns, so the names are read
183
+ * again when the counter changed.
184
+ * - {@link SqliteStatement.run} always resets the statement, so it is reusable
185
+ * after an error, and ignores the result: SQLITE_OK after SQLITE_DONE, and a
186
+ * failed step's error again after it. A deferred constraint or commit failure
187
+ * comes from the last step, so it is not lost. A run whose read fails is cut
188
+ * short, and its reset completes the statement, so a write with RETURNING is
189
+ * committed although the run fails.
190
+ * - `changes` compares `sqlite3_total_changes64` before and after the run: 0 when
191
+ * it did not move, `sqlite3_changes64` otherwise, as in better-sqlite3, and
192
+ * unlike it also 0 when `sqlite3_stmt_readonly` is true, as for a SELECT
193
+ * whose function writes. SQLite keeps one count per connection, of the last
194
+ * INSERT, UPDATE or DELETE that finished, so reading it alone would report a
195
+ * stale count after a SELECT or DDL. {@link SqliteRunResult.changes} says
196
+ * which writes the count can come from.
197
+ * - A callback may run queries on the database, but SQLite forbids resetting or
198
+ * finalizing a statement that is stepping and closing a connection while it
199
+ * runs. So running or disposing a statement during its own run, and disposing
200
+ * the database while one of its operations runs, throw before wasm is
201
+ * entered, and the instance keeps working. A callback installed with
202
+ * `installWasmFunctions` then fails as a defect.
203
+ *
204
+ * ## Exporting and closing
205
+ *
206
+ * - {@link SqliteDatabase.export} calls `sqlite3_serialize(db, NULL, &size, 0)`
207
+ * for the main schema, copies the pages into their own `ArrayBuffer` and
208
+ * frees SQLite's copy. The size has its own memory, because SQLite writes it
209
+ * before it finalizes its query, whose trace callback can run a query on the
210
+ * same database. `sqlite3_serialize` does not report a failed allocation to
211
+ * the connection, so the connection's error is cleared with
212
+ * `sqlite3_set_errmsg` first, and NULL then fails with the error the
213
+ * connection reports, or else SQLITE_NOMEM, as when JavaScript cannot
214
+ * allocate the copy of the pages, which still frees SQLite's copy. SQLite
215
+ * gives an empty database its first page before copying, so NULL with a size
216
+ * of 0 and no error is only a database that has no pages and cannot get one,
217
+ * such as a read-only empty one, which exports as zero bytes. Pages come
218
+ * through the pager, so an encrypted database exports as plaintext. SQLite
219
+ * zero-fills a page that fails to read and reports success, so the export of
220
+ * a File or EncryptedFile database fails when its VFS recorded a failure
221
+ * during it, caused by the VFS's record: with SQLITE_CORRUPT for a page that
222
+ * failed to authenticate, a {@link SqlitePageAuthenticationError}, and with
223
+ * SQLITE_IOERR otherwise.
224
+ * - Disposing a database finalizes its statements that are still open and then
225
+ * calls `sqlite3_close_v2`, so no deferred close keeps the VFS's files open.
226
+ *
227
+ * @module
228
+ */
229
+ var __addDisposableResource = (this && this.__addDisposableResource) || function (env, value, async) {
230
+ if (value !== null && value !== void 0) {
231
+ if (typeof value !== "object" && typeof value !== "function") throw new TypeError("Object expected.");
232
+ var dispose, inner;
233
+ if (async) {
234
+ if (!Symbol.asyncDispose) throw new TypeError("Symbol.asyncDispose is not defined.");
235
+ dispose = value[Symbol.asyncDispose];
236
+ }
237
+ if (dispose === void 0) {
238
+ if (!Symbol.dispose) throw new TypeError("Symbol.dispose is not defined.");
239
+ dispose = value[Symbol.dispose];
240
+ if (async) inner = dispose;
241
+ }
242
+ if (typeof dispose !== "function") throw new TypeError("Object not disposable.");
243
+ if (inner) dispose = function() { try { inner.call(this); } catch (e) { return Promise.reject(e); } };
244
+ env.stack.push({ value: value, dispose: dispose, async: async });
245
+ }
246
+ else if (async) {
247
+ env.stack.push({ async: true });
248
+ }
249
+ return value;
250
+ };
251
+ var __disposeResources = (this && this.__disposeResources) || (function (SuppressedError) {
252
+ return function (env) {
253
+ function fail(e) {
254
+ env.error = env.hasError ? new SuppressedError(e, env.error, "An error was suppressed during disposal.") : e;
255
+ env.hasError = true;
256
+ }
257
+ var r, s = 0;
258
+ function next() {
259
+ while (r = env.stack.pop()) {
260
+ try {
261
+ if (!r.async && s === 1) return s = 0, env.stack.push(r), Promise.resolve().then(next);
262
+ if (r.dispose) {
263
+ var result = r.dispose.call(r.value);
264
+ if (r.async) return s |= 2, Promise.resolve(result).then(next, function(e) { fail(e); return next(); });
265
+ }
266
+ else s |= 1;
267
+ }
268
+ catch (e) {
269
+ fail(e);
270
+ }
271
+ }
272
+ if (s === 1) return env.hasError ? Promise.reject(env.error) : Promise.resolve();
273
+ if (env.hasError) throw env.error;
274
+ }
275
+ return next();
276
+ };
277
+ })(typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) {
278
+ var e = new Error(message);
279
+ return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
280
+ });
281
+ import { assert, brand, disposable, err, exhaustiveCheck, ok, safelyStringifyUnknownValue, String, trySync, } from "@evolu/common";
282
+ import { sqlite3_bind_blob, sqlite3_bind_double, sqlite3_bind_int, sqlite3_bind_int64, sqlite3_bind_null, sqlite3_bind_parameter_count, sqlite3_bind_text, sqlite3_changes64, sqlite3_clear_bindings, sqlite3_close_v2, sqlite3_column_blob, sqlite3_column_bytes, sqlite3_column_count, sqlite3_column_double, sqlite3_column_name, sqlite3_column_text, sqlite3_column_type, sqlite3_errcode, sqlite3_errmsg, sqlite3_error_offset, sqlite3_errstr, sqlite3_extended_errcode, sqlite3_file_control, sqlite3_finalize, sqlite3_free, sqlite3_get_autocommit, sqlite3_open_v2, sqlite3_prepare_v3, sqlite3_reset, sqlite3_serialize, sqlite3_set_errmsg, sqlite3_step, sqlite3_stmt_readonly, sqlite3_stmt_status, sqlite3_total_changes64, } from "./CApi.js";
283
+ import { SQLITE_BLOB, SQLITE_CORRUPT, SQLITE_DONE, SQLITE_ERROR, SQLITE_FCNTL_RESERVE_BYTES, SQLITE_FLOAT, SQLITE_INTEGER, SQLITE_IOERR, SQLITE_NOMEM, SQLITE_NULL, SQLITE_OK, SQLITE_OPEN_CREATE, SQLITE_OPEN_EXRESCODE, SQLITE_OPEN_READWRITE, SQLITE_PREPARE_PERSISTENT, SQLITE_ROW, SQLITE_STMTSTATUS_REPREPARE, SQLITE_TEXT, SQLITE_TRANSIENT, SQLITE_WASM_DEALLOC, } from "./Constants.js";
284
+ import { allocCString, allocWasm, copyWasmBytes, createSqliteScratch, isInt32, readCString, readUtf8, writeUtf8, writeWasmBytes, } from "./Memory.js";
285
+ import { sahPoolMaxDatabasePathSize, } from "./SahPool.js";
286
+ /**
287
+ * Returns the primary result code of a result code, its low 8 bits, such as
288
+ * SQLITE_CONSTRAINT for SQLITE_CONSTRAINT_NOTNULL. A primary result code
289
+ * returns itself.
290
+ */
291
+ export const sqliteResultCodeToPrimary = (code) => (code & 0xff);
292
+ /**
293
+ * The canonical path of a database file on a {@link SqliteVfs}, such as
294
+ * `/evolu1.db`, one spelling per file, as {@link SqliteVfs.getPaths} lists it.
295
+ *
296
+ * A path is canonical when it is its own `new URL(path,
297
+ * "file://localhost/").pathname`, the path a {@link SahPool} maps a name to, so
298
+ * the VFS opens, lists and deletes the file by the path the database opened it
299
+ * with, and an EncryptedFile database registers its key for that file. A
300
+ * canonical path starts with `/` and is printable ASCII without `?` or `#`, so
301
+ * SQLite passes it to the VFS as it is, and the paths SQLite derives from it by
302
+ * appending a suffix, as for the journal, are canonical too. It rejects:
303
+ *
304
+ * - Another spelling of a path, such as `evolu1.db`, `/x/../evolu1.db`,
305
+ * `FILE:evolu1.db` or `/a b.db` for `/a%20b.db`, and a name with a scheme,
306
+ * such as `db:evolu1.db`, which a pool would map to the relative path
307
+ * `evolu1.db`.
308
+ * - A path SQLite reads itself: an empty path and `:memory:`, which open an
309
+ * in-memory database, `:localStorage:` and `:sessionStorage:`, which SQLite
310
+ * opens on its kvvfs, and a `file:` URI, whose query and fragment SQLite
311
+ * strips and whose escapes it decodes, so the VFS would get another name, and
312
+ * an EncryptedFile database would be written without its key.
313
+ * - A path longer than {@link sahPoolMaxDatabasePathSize}, 498 bytes, because a
314
+ * pool slot's header holds a path of up to 510 bytes, and SQLite names a
315
+ * super-journal by appending 12 bytes to the database's path.
316
+ */
317
+ export const SqliteVfsPath = /*#__PURE__*/ brand("SqliteVfsPath", String, (value) => {
318
+ const canonical = trySync(() => new URL(value, "file://localhost/").pathname, () => null);
319
+ // A canonical path is ASCII, because a URL path percent-encodes every other
320
+ // character, so its length is its length in UTF-8.
321
+ return canonical.ok &&
322
+ canonical.value === value &&
323
+ value.length <= sahPoolMaxDatabasePathSize
324
+ ? ok()
325
+ : err({ type: "SqliteVfsPath", value });
326
+ }, (error) => `The value ${safelyStringifyUnknownValue(error.value)} is not a valid SqliteVfsPath.`);
327
+ /**
328
+ * Opens a database.
329
+ *
330
+ * With a wrong key or a file that is not a database, it fails with
331
+ * SQLITE_NOTADB, or with SQLITE_CORRUPT for a wrong key and a hot journal whose
332
+ * first record is not page 1. On a {@link SahPool}, an encrypted database opened
333
+ * without its key while it has a hot journal that records pages fails with
334
+ * SQLITE_CANTOPEN, and the journal stays for the key. A leftover journal that
335
+ * is not hot, such as one `journal_mode = PERSIST` or TRUNCATE keeps, gives
336
+ * SQLITE_NOTADB, as no journal does. For encrypted databases that 2.2.4 may
337
+ * have keyed, use {@link createEncryptedSqliteDatabase}.
338
+ */
339
+ export const createSqliteDatabase = (deps) => (options) => {
340
+ const env_1 = { stack: [], error: void 0, hasError: false };
341
+ try {
342
+ const { call } = deps.sqliteWasm;
343
+ const vfs = options.type === "Memory" ? null : options.vfs;
344
+ // The operations of this database that are running, as one that a
345
+ // callback of another runs, so the database is not disposed under them.
346
+ let runningOperations = 0;
347
+ // The VFS takes the key when SQLite opens the file. Registered before wasm
348
+ // is entered, so a VFS that refuses it, as a pool with a key already
349
+ // registered for the path does, throws without breaking the instance.
350
+ const _registration = __addDisposableResource(env_1, options.type === "EncryptedFile"
351
+ ? options.vfs.registerKey(options.path, options.key)
352
+ : null, false);
353
+ // Clears the VFS's failure record and then calls an operation through
354
+ // call, so the cause of a SqliteError is a failure of this operation. A
355
+ // VFS that throws, as a disposed pool does, then throws before wasm is
356
+ // entered, without breaking the instance. An operation inside another on
357
+ // the same VFS, as a query a callback runs, keeps the record, which the
358
+ // outer operation still reads.
359
+ const callOperation = (fn) => {
360
+ runningOperations++;
361
+ try {
362
+ if (vfs == null)
363
+ return call(fn);
364
+ const running = runningOperationsByVfs.get(vfs) ?? 0;
365
+ if (running === 0)
366
+ vfs.clearFailure();
367
+ runningOperationsByVfs.set(vfs, running + 1);
368
+ try {
369
+ return call(fn);
370
+ }
371
+ finally {
372
+ runningOperationsByVfs.set(vfs, running);
373
+ }
374
+ }
375
+ finally {
376
+ runningOperations--;
377
+ }
378
+ };
379
+ return callOperation(() => {
380
+ const env_2 = { stack: [], error: void 0, hasError: false };
381
+ try {
382
+ const bindBlob = sqlite3_bind_blob(deps);
383
+ const bindDouble = sqlite3_bind_double(deps);
384
+ const bindInt = sqlite3_bind_int(deps);
385
+ const bindInt64 = sqlite3_bind_int64(deps);
386
+ const bindNull = sqlite3_bind_null(deps);
387
+ const bindParameterCount = sqlite3_bind_parameter_count(deps);
388
+ const bindText = sqlite3_bind_text(deps);
389
+ const changes64 = sqlite3_changes64(deps);
390
+ const clearBindings = sqlite3_clear_bindings(deps);
391
+ const closeV2 = sqlite3_close_v2(deps);
392
+ const columnBlob = sqlite3_column_blob(deps);
393
+ const columnBytes = sqlite3_column_bytes(deps);
394
+ const columnCount = sqlite3_column_count(deps);
395
+ const columnDouble = sqlite3_column_double(deps);
396
+ const columnName = sqlite3_column_name(deps);
397
+ const columnText = sqlite3_column_text(deps);
398
+ const columnType = sqlite3_column_type(deps);
399
+ const errcode = sqlite3_errcode(deps);
400
+ const errmsg = sqlite3_errmsg(deps);
401
+ const errorOffset = sqlite3_error_offset(deps);
402
+ const errstr = sqlite3_errstr(deps);
403
+ const extendedErrcode = sqlite3_extended_errcode(deps);
404
+ const finalize = sqlite3_finalize(deps);
405
+ const free = sqlite3_free(deps);
406
+ const getAutocommit = sqlite3_get_autocommit(deps);
407
+ const prepareV3 = sqlite3_prepare_v3(deps);
408
+ const reset = sqlite3_reset(deps);
409
+ const serialize = sqlite3_serialize(deps);
410
+ const setErrmsg = sqlite3_set_errmsg(deps);
411
+ const step = sqlite3_step(deps);
412
+ const stmtReadonly = sqlite3_stmt_readonly(deps);
413
+ const stmtStatus = sqlite3_stmt_status(deps);
414
+ const totalChanges64 = sqlite3_total_changes64(deps);
415
+ const alloc = allocWasm(deps);
416
+ const allocString = allocCString(deps);
417
+ const copyBytes = copyWasmBytes(deps);
418
+ const readString = readCString(deps);
419
+ const readText = readUtf8(deps);
420
+ const writeBytes = writeWasmBytes(deps);
421
+ const writeText = writeUtf8(deps);
422
+ const statements = new Set();
423
+ // Reads the connection's error right after the failing call, before any
424
+ // other call replaces it.
425
+ const toSqliteError = (db, operation) => {
426
+ const extendedCode = extendedErrcode(db);
427
+ // SQLite sets the offset only for parser errors and keeps it until a
428
+ // prepare succeeds or a statement resets or finalizes, so any other code
429
+ // or operation would read the offset of an earlier failed prepare.
430
+ const offset = sqliteResultCodeToPrimary(extendedCode) === SQLITE_ERROR &&
431
+ (operation === "prepare" ||
432
+ operation === "exec" ||
433
+ operation === "step")
434
+ ? errorOffset(db)
435
+ : -1;
436
+ return {
437
+ type: "SqliteError",
438
+ operation,
439
+ extendedCode,
440
+ // SQLite falls back to sqlite3_errstr, so it is never NULL.
441
+ message: readString(errmsg(db)),
442
+ sqlOffset: offset < 0 ? null : offset,
443
+ cause: vfs?.getFailure() ?? null,
444
+ };
445
+ };
446
+ // For a failure without a connection that could report it.
447
+ const toCodeError = (operation, extendedCode) => ({
448
+ type: "SqliteError",
449
+ operation,
450
+ extendedCode,
451
+ // SQLite describes an unknown code as "unknown error", never NULL.
452
+ message: readString(errstr(extendedCode)),
453
+ sqlOffset: null,
454
+ cause: null,
455
+ });
456
+ const readPtr = (ptr) => deps.sqliteWasm.getHeapDataView().getUint32(ptr, true);
457
+ const disposer = __addDisposableResource(env_2, new DisposableStack(), false);
458
+ const createdScratch = createSqliteScratch(deps);
459
+ if (!createdScratch.ok)
460
+ return err(toCodeError("open", SQLITE_NOMEM));
461
+ const scratch = createdScratch.value;
462
+ disposer.defer(() => {
463
+ call(() => {
464
+ scratch[Symbol.dispose]();
465
+ });
466
+ });
467
+ const pzTail = (scratch.out + 4);
468
+ // A File or EncryptedFile database's path is followed by the name of its
469
+ // VFS.
470
+ const filename = allocString(options.type === "Memory"
471
+ ? ":memory:"
472
+ : `${options.path}\0${options.vfs.vfsName}`);
473
+ if (!filename.ok)
474
+ return err(toCodeError("open", SQLITE_NOMEM));
475
+ const zFilename = filename.value;
476
+ const pathEnd = deps.sqliteWasm.getHeapU8().indexOf(0, zFilename);
477
+ const rc = (() => {
478
+ const env_3 = { stack: [], error: void 0, hasError: false };
479
+ try {
480
+ const filenameDisposer = __addDisposableResource(env_3, new DisposableStack(), false);
481
+ filenameDisposer.defer(() => {
482
+ free(zFilename);
483
+ });
484
+ return sqlite3_open_v2(deps)(zFilename, scratch.out, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_EXRESCODE, options.type === "Memory" ? 0 : (pathEnd + 1));
485
+ }
486
+ catch (e_3) {
487
+ env_3.error = e_3;
488
+ env_3.hasError = true;
489
+ }
490
+ finally {
491
+ __disposeResources(env_3);
492
+ }
493
+ })();
494
+ const db = readPtr(scratch.out);
495
+ if (rc !== SQLITE_OK) {
496
+ const error = db === 0 ? toCodeError("open", rc) : toSqliteError(db, "open");
497
+ closeV2(db);
498
+ return err(error);
499
+ }
500
+ disposer.defer(() => {
501
+ call(() => closeV2(db));
502
+ });
503
+ // Finalized before the close, so no deferred close keeps files open.
504
+ disposer.defer(() => {
505
+ for (const statement of statements)
506
+ statement[Symbol.dispose]();
507
+ });
508
+ const prepareStatement = (sql, prepFlags, operation = "prepare") => {
509
+ if (!isValidSqlText(sql))
510
+ return err({ type: "SqliteInvalidSqlText" });
511
+ // SQLite parses the SQL in place, while a callback can run a query on
512
+ // this database, which reuses the scratch memory. Three bytes per
513
+ // UTF-16 code unit hold any UTF-8, and encoding into them is faster
514
+ // than allocCString's exact copy, which encodes into a temporary array
515
+ // first. A run prepares SQL every time, so it is a hot path.
516
+ const capacity = sql.length * 3;
517
+ const allocated = alloc(capacity + 1);
518
+ if (!allocated.ok)
519
+ return err(toCodeError(operation, SQLITE_NOMEM));
520
+ const zSql = allocated.value;
521
+ try {
522
+ const written = writeText(sql, zSql, capacity);
523
+ deps.sqliteWasm.getHeapU8()[zSql + written] = 0;
524
+ const rc = prepareV3(db, zSql, written + 1, prepFlags, scratch.out, pzTail);
525
+ if (rc !== SQLITE_OK)
526
+ return err(toSqliteError(db, operation));
527
+ const stmt = readPtr(scratch.out);
528
+ if (stmt === 0)
529
+ return err({ type: "SqliteEmptySqlError" });
530
+ // The rest is scanned, not prepared, because SQLite applies some
531
+ // pragmas, such as foreign_keys, when it prepares them.
532
+ if (!isEmptySql(deps.sqliteWasm.getHeapU8(), readPtr(pzTail), zSql + written)) {
533
+ finalize(stmt);
534
+ return err({ type: "SqliteMultipleStatementsError" });
535
+ }
536
+ return ok(stmt);
537
+ }
538
+ finally {
539
+ free(zSql);
540
+ }
541
+ };
542
+ // The steps after the connection opened run in a call of their own, so
543
+ // a trap in them breaks the instance before it unwinds to the deferred
544
+ // close, which then refuses to close the connection on the state the
545
+ // trap left.
546
+ const prepared = call(() => {
547
+ if (options.type === "EncryptedFile") {
548
+ // The IV and the HMAC at the end of each page, for a new database. It
549
+ // returns SQLITE_OK for main, a NULL schema name, and an existing
550
+ // database keeps what its header says.
551
+ deps.sqliteWasm
552
+ .getHeapDataView()
553
+ .setInt32(scratch.out, encryptedReservedBytes, true);
554
+ sqlite3_file_control(deps)(db, 0, SQLITE_FCNTL_RESERVE_BYTES, scratch.out);
555
+ // As SQLite3 Multiple Ciphers did for every encrypted connection.
556
+ const secureDelete = prepareStatement("PRAGMA secure_delete = ON", 0, "open");
557
+ if (!secureDelete.ok) {
558
+ assert(secureDelete.error.type === "SqliteError", "The pragma is one statement.");
559
+ return err(secureDelete.error);
560
+ }
561
+ const stepped = step(secureDelete.value);
562
+ const error = stepped === SQLITE_ROW ? null : toSqliteError(db, "open");
563
+ finalize(secureDelete.value);
564
+ if (error)
565
+ return err(error);
566
+ }
567
+ // SQLite reads a file only when a statement needs it, so a File or
568
+ // EncryptedFile database reads its schema now: a hot journal is rolled
569
+ // back, and a file that is not a database fails the open rather than the
570
+ // first statement.
571
+ if (options.type !== "Memory") {
572
+ const schema = prepareStatement("SELECT 1 FROM sqlite_schema", 0, "open");
573
+ if (!schema.ok) {
574
+ assert(schema.error.type === "SqliteError", "The schema query is one statement.");
575
+ return err(schema.error);
576
+ }
577
+ finalize(schema.value);
578
+ }
579
+ return ok();
580
+ });
581
+ if (!prepared.ok)
582
+ return err(prepared.error);
583
+ const createRunner = (stmt) => {
584
+ const parameterCount = bindParameterCount(stmt);
585
+ let names = null;
586
+ let namesReprepares = 0;
587
+ const run = (parameters) => {
588
+ if (parameters.length !== parameterCount)
589
+ return err({
590
+ type: "SqliteParameterCountError",
591
+ expected: parameterCount,
592
+ actual: parameters.length,
593
+ });
594
+ // SQLite keeps a bound value until its parameter is bound again, so
595
+ // a run that handed SQLite a value above 64 KiB clears the bindings
596
+ // before it returns, also when it fails.
597
+ let clearsBindings = false;
598
+ let bindError = null;
599
+ for (const [index, value] of parameters.entries()) {
600
+ const position = index + 1;
601
+ let bindRc;
602
+ if (value === null)
603
+ bindRc = bindNull(stmt, position);
604
+ else if (typeof value === "number")
605
+ bindRc = isInt32(value)
606
+ ? bindInt(stmt, position, value)
607
+ : Number.isSafeInteger(value)
608
+ ? bindInt64(stmt, position, BigInt(value))
609
+ : bindDouble(stmt, position, value);
610
+ else if (typeof value === "string" &&
611
+ value.length * 3 <= maxScratchValue) {
612
+ const reserved = scratch.reserve(value.length * 3);
613
+ if (!reserved.ok) {
614
+ bindError = toCodeError("bind", SQLITE_NOMEM);
615
+ break;
616
+ }
617
+ const length = writeText(value, reserved.value, value.length * 3);
618
+ bindRc = bindText(stmt, position, reserved.value, length, SQLITE_TRANSIENT);
619
+ }
620
+ else if (typeof value === "string") {
621
+ // JavaScript can fail to allocate the UTF-8, which runs no wasm,
622
+ // so the instance keeps working.
623
+ const bytes = trySync(() => utf8Encoder.encode(value));
624
+ if (!bytes.ok) {
625
+ bindError = toCodeError("bind", SQLITE_NOMEM);
626
+ break;
627
+ }
628
+ const allocated = alloc(bytes.value.length);
629
+ if (!allocated.ok) {
630
+ bindError = toCodeError("bind", SQLITE_NOMEM);
631
+ break;
632
+ }
633
+ writeBytes(allocated.value, bytes.value);
634
+ clearsBindings = true;
635
+ bindRc = bindText(stmt, position, allocated.value, bytes.value.length, SQLITE_WASM_DEALLOC);
636
+ }
637
+ else if (value.length <= maxScratchValue) {
638
+ const reserved = scratch.reserve(value.length);
639
+ if (!reserved.ok) {
640
+ bindError = toCodeError("bind", SQLITE_NOMEM);
641
+ break;
642
+ }
643
+ writeBytes(reserved.value, value);
644
+ bindRc = bindBlob(stmt, position, reserved.value, value.length, SQLITE_TRANSIENT);
645
+ }
646
+ else {
647
+ const allocated = alloc(value.length);
648
+ if (!allocated.ok) {
649
+ bindError = toCodeError("bind", SQLITE_NOMEM);
650
+ break;
651
+ }
652
+ writeBytes(allocated.value, value);
653
+ clearsBindings = true;
654
+ bindRc = bindBlob(stmt, position, allocated.value, value.length, SQLITE_WASM_DEALLOC);
655
+ }
656
+ if (bindRc !== SQLITE_OK) {
657
+ bindError = toSqliteError(db, "bind");
658
+ break;
659
+ }
660
+ }
661
+ if (bindError) {
662
+ if (clearsBindings)
663
+ clearBindings(stmt);
664
+ return err(bindError);
665
+ }
666
+ const totalChanges = totalChanges64(db);
667
+ const rows = [];
668
+ let rc;
669
+ // A read that fails because SQLite is out of memory leaves the loop
670
+ // with SQLITE_ROW.
671
+ steps: while ((rc = step(stmt)) === SQLITE_ROW) {
672
+ // A schema change re-prepares the statement, and SELECT * can then
673
+ // return other columns, so the first row checks the counter.
674
+ const reprepares = rows.length === 0
675
+ ? stmtStatus(stmt, SQLITE_STMTSTATUS_REPREPARE, 0)
676
+ : namesReprepares;
677
+ if (names == null || reprepares !== namesReprepares) {
678
+ const columnNames = [];
679
+ for (let index = 0; index < columnCount(stmt); index++) {
680
+ const name = columnName(stmt, index);
681
+ if (name === 0)
682
+ break steps;
683
+ columnNames.push(readString(name));
684
+ }
685
+ names = columnNames;
686
+ namesReprepares = reprepares;
687
+ }
688
+ const row = Object.create(null);
689
+ for (const [index, name] of names.entries()) {
690
+ // With the type read first, a NULL pointer is never SQL NULL.
691
+ const type = columnType(stmt, index);
692
+ switch (type) {
693
+ case SQLITE_INTEGER:
694
+ case SQLITE_FLOAT:
695
+ row[name] = columnDouble(stmt, index);
696
+ break;
697
+ case SQLITE_TEXT: {
698
+ const ptr = columnText(stmt, index);
699
+ if (ptr === 0)
700
+ break steps;
701
+ const text = tryCopy(readText, ptr, columnBytes(stmt, index));
702
+ if (text == null)
703
+ break steps;
704
+ row[name] = text;
705
+ break;
706
+ }
707
+ case SQLITE_BLOB: {
708
+ const ptr = columnBlob(stmt, index);
709
+ // NULL for an empty BLOB too.
710
+ if (ptr !== 0) {
711
+ const bytes = tryCopy(copyBytes, ptr, columnBytes(stmt, index));
712
+ if (bytes == null)
713
+ break steps;
714
+ row[name] = bytes;
715
+ }
716
+ else if (errcode(db) === SQLITE_NOMEM)
717
+ break steps;
718
+ else
719
+ row[name] = new Uint8Array();
720
+ break;
721
+ }
722
+ case SQLITE_NULL:
723
+ row[name] = null;
724
+ break;
725
+ default:
726
+ exhaustiveCheck(type);
727
+ }
728
+ }
729
+ rows.push(row);
730
+ }
731
+ const error = rc === SQLITE_DONE
732
+ ? null
733
+ : rc === SQLITE_ROW
734
+ ? toCodeError("step", SQLITE_NOMEM)
735
+ : toSqliteError(db, "step");
736
+ // After SQLITE_DONE it returns SQLITE_OK, and after a failure it
737
+ // repeats it, so its result is ignored.
738
+ reset(stmt);
739
+ if (clearsBindings)
740
+ clearBindings(stmt);
741
+ if (error)
742
+ return err(error);
743
+ // SQLite keeps the count of the last INSERT, UPDATE or DELETE across
744
+ // other statements, also one that a function of this statement ran,
745
+ // and a statement that cannot write changes no rows itself.
746
+ const changes = totalChanges64(db) === totalChanges || stmtReadonly(stmt) !== 0
747
+ ? 0
748
+ : Number(changes64(db));
749
+ return ok({ rows, changes });
750
+ };
751
+ return { parameterCount, run };
752
+ };
753
+ const database = disposable({
754
+ prepare: (sql) => callOperation(() => {
755
+ const env_4 = { stack: [], error: void 0, hasError: false };
756
+ try {
757
+ const prepared = prepareStatement(sql, SQLITE_PREPARE_PERSISTENT);
758
+ if (!prepared.ok)
759
+ return prepared;
760
+ const stmt = prepared.value;
761
+ const runner = createRunner(stmt);
762
+ // SQLite forbids resetting or finalizing a statement that is
763
+ // stepping, which running or disposing it from a callback of its
764
+ // run would do. Both throw before wasm is entered, so the
765
+ // instance keeps working.
766
+ let running = false;
767
+ const statementDisposer = __addDisposableResource(env_4, new DisposableStack(), false);
768
+ statementDisposer.defer(() => {
769
+ statements.delete(statement);
770
+ call(() => finalize(stmt));
771
+ });
772
+ const statement = disposable({
773
+ parameterCount: runner.parameterCount,
774
+ run: (parameters) => {
775
+ if (running)
776
+ throw new Error(runningStatementMessage);
777
+ running = true;
778
+ try {
779
+ return callOperation(() => runner.run(parameters));
780
+ }
781
+ finally {
782
+ running = false;
783
+ }
784
+ },
785
+ }, statementDisposer);
786
+ const disposeStatement = statement[Symbol.dispose];
787
+ statement[Symbol.dispose] = () => {
788
+ if (running)
789
+ throw new Error(runningStatementMessage);
790
+ disposeStatement();
791
+ };
792
+ statements.add(statement);
793
+ return ok(statement);
794
+ }
795
+ catch (e_4) {
796
+ env_4.error = e_4;
797
+ env_4.hasError = true;
798
+ }
799
+ finally {
800
+ __disposeResources(env_4);
801
+ }
802
+ }),
803
+ run: (sql, parameters) => callOperation(() => {
804
+ const prepared = prepareStatement(sql, 0);
805
+ if (!prepared.ok)
806
+ return prepared;
807
+ const result = createRunner(prepared.value).run(parameters);
808
+ finalize(prepared.value);
809
+ return result;
810
+ }),
811
+ exec: (sql) => callOperation(() => {
812
+ const env_5 = { stack: [], error: void 0, hasError: false };
813
+ try {
814
+ if (!isValidSqlText(sql))
815
+ return err({ type: "SqliteInvalidSqlText" });
816
+ // A step can call back into this connection, as a function that runs
817
+ // a query does, and that call reuses the scratch memory, so the
818
+ // statements not yet prepared need their own copy. JavaScript can
819
+ // fail to allocate the UTF-8, which runs no wasm, so the instance
820
+ // keeps working.
821
+ const bytes = trySync(() => utf8Encoder.encode(sql));
822
+ if (!bytes.ok)
823
+ return err(toCodeError("exec", SQLITE_NOMEM));
824
+ const copied = alloc(bytes.value.length + 1);
825
+ if (!copied.ok)
826
+ return err(toCodeError("exec", SQLITE_NOMEM));
827
+ const zSql = copied.value;
828
+ const execDisposer = __addDisposableResource(env_5, new DisposableStack(), false);
829
+ execDisposer.defer(() => {
830
+ free(zSql);
831
+ });
832
+ writeBytes(zSql, bytes.value);
833
+ // The lengths SQLite gets count the NUL terminator, as in
834
+ // prepare.
835
+ const end = zSql + bytes.value.length + 1;
836
+ deps.sqliteWasm.getHeapU8()[end - 1] = 0;
837
+ // Each statement is prepared from where the previous one ended, so
838
+ // an error offset is relative to it, also when a step re-prepares
839
+ // the statement after a schema change and fails.
840
+ const toExecError = (statementStart) => {
841
+ const error = toSqliteError(db, "exec");
842
+ return error.sqlOffset == null
843
+ ? error
844
+ : {
845
+ ...error,
846
+ sqlOffset: statementStart - zSql + error.sqlOffset,
847
+ };
848
+ };
849
+ for (let start = zSql; start < end - 1;) {
850
+ const rc = prepareV3(db, start, end - start, 0, scratch.out, pzTail);
851
+ if (rc !== SQLITE_OK)
852
+ return err(toExecError(start));
853
+ const stmt = readPtr(scratch.out);
854
+ const statementStart = start;
855
+ start = readPtr(pzTail);
856
+ // NULL for whitespace and comments.
857
+ if (stmt === 0)
858
+ continue;
859
+ // A parameter exec cannot bind would run as NULL.
860
+ const parameterCount = bindParameterCount(stmt);
861
+ if (parameterCount !== 0) {
862
+ finalize(stmt);
863
+ return err({
864
+ type: "SqliteParameterCountError",
865
+ expected: parameterCount,
866
+ actual: 0,
867
+ });
868
+ }
869
+ let stepRc;
870
+ while ((stepRc = step(stmt)) === SQLITE_ROW)
871
+ ;
872
+ const error = stepRc === SQLITE_DONE ? null : toExecError(statementStart);
873
+ finalize(stmt);
874
+ if (error)
875
+ return err(error);
876
+ }
877
+ return ok();
878
+ }
879
+ catch (e_5) {
880
+ env_5.error = e_5;
881
+ env_5.hasError = true;
882
+ }
883
+ finally {
884
+ __disposeResources(env_5);
885
+ }
886
+ }),
887
+ export: () => callOperation(() => {
888
+ const env_6 = { stack: [], error: void 0, hasError: false };
889
+ try {
890
+ // SQLite writes the size before it finalizes its query, which
891
+ // can call a trace callback that runs a query on this
892
+ // database, which reuses the scratch memory.
893
+ const allocatedSize = alloc(8);
894
+ if (!allocatedSize.ok)
895
+ return err(toCodeError("export", SQLITE_NOMEM));
896
+ const pSize = allocatedSize.value;
897
+ const sizeDisposer = __addDisposableResource(env_6, new DisposableStack(), false);
898
+ sizeDisposer.defer(() => {
899
+ free(pSize);
900
+ });
901
+ // An error the connection reports after a NULL then comes from this
902
+ // call, which does not report its own failed allocations.
903
+ setErrmsg(db, SQLITE_OK, 0);
904
+ // NULL is the main schema.
905
+ const pages = serialize(db, 0, pSize, 0);
906
+ const size = deps.sqliteWasm
907
+ .getHeapDataView()
908
+ .getBigInt64(pSize, true);
909
+ if (pages === 0) {
910
+ if (errcode(db) !== SQLITE_OK)
911
+ return err(toSqliteError(db, "export"));
912
+ // NULL with a size of 0 is a database without pages that cannot
913
+ // get one.
914
+ return size === 0n
915
+ ? ok(new Uint8Array())
916
+ : err(toCodeError("export", SQLITE_NOMEM));
917
+ }
918
+ const bytes = tryCopy(copyBytes, pages, Number(size));
919
+ free(pages);
920
+ // SQLite zero-fills a page it cannot read and reports success.
921
+ const failure = vfs?.getFailure() ?? null;
922
+ if (failure != null)
923
+ return err({
924
+ ...toCodeError("export", isPageAuthenticationError(failure.error)
925
+ ? SQLITE_CORRUPT
926
+ : SQLITE_IOERR),
927
+ cause: failure,
928
+ });
929
+ if (bytes == null)
930
+ return err(toCodeError("export", SQLITE_NOMEM));
931
+ return ok(bytes);
932
+ }
933
+ catch (e_6) {
934
+ env_6.error = e_6;
935
+ env_6.hasError = true;
936
+ }
937
+ finally {
938
+ __disposeResources(env_6);
939
+ }
940
+ }),
941
+ isAutocommit: () => call(() => getAutocommit(db) !== 0),
942
+ }, disposer);
943
+ // SQLite forbids closing a connection while it runs, which disposing the
944
+ // database from a callback of one of its operations would do.
945
+ const disposeDatabase = database[Symbol.dispose];
946
+ database[Symbol.dispose] = () => {
947
+ if (runningOperations > 0)
948
+ throw new Error("A SqliteDatabase cannot be disposed while one of its operations runs.");
949
+ disposeDatabase();
950
+ };
951
+ return ok(database);
952
+ }
953
+ catch (e_2) {
954
+ env_2.error = e_2;
955
+ env_2.hasError = true;
956
+ }
957
+ finally {
958
+ __disposeResources(env_2);
959
+ }
960
+ });
961
+ }
962
+ catch (e_1) {
963
+ env_1.error = e_1;
964
+ env_1.hasError = true;
965
+ }
966
+ finally {
967
+ __disposeResources(env_1);
968
+ }
969
+ };
970
+ /**
971
+ * Opens an EncryptedFile database on a pool, falling back to the key
972
+ * `@evolu/sqlite-wasm` 2.2.4 derived.
973
+ *
974
+ * `@evolu/web` passed 2.2.4 the key as the SQL text `x'<hex>'`, SQLCipher's
975
+ * notation for a raw key, which SQLite3 Multiple Ciphers 2.2.4 treated as a
976
+ * passphrase because of a bug
977
+ * (https://github.com/utelle/SQLite3MultipleCiphers/issues/218, fixed in
978
+ * 2.2.5). So every database it encrypted is keyed with PBKDF2-HMAC-SHA512 of
979
+ * that text, not with the key itself. This Task:
980
+ *
981
+ * 1. Opens with the raw key. A new or empty file always accepts it.
982
+ * 2. Only when a wrong key failed it, so page 1 of the database or a record of its
983
+ * hot journal failed to authenticate, which the pool records as a
984
+ * {@link SqlitePageAuthenticationError}, reads the first 16 bytes of the file
985
+ * through {@link SahPool.read}: the unencrypted cipher salt. Another page of
986
+ * the database that fails to authenticate is damaged, because page 1 proved
987
+ * the key, so the open fails with SQLITE_CORRUPT.
988
+ * 3. Derives the legacy key with {@link deriveLegacySqliteKey}, opens again with it
989
+ * as the raw key, and zeroes it.
990
+ *
991
+ * The result says which key opened the database. When neither key opens it, the
992
+ * error is the raw key's, unless the second open failed otherwise. Rekeying a
993
+ * legacy database to the raw key is a separate decision: it rewrites every
994
+ * page, needs space, and makes the database unreadable to 2.2.4 tabs that share
995
+ * the pool.
996
+ */
997
+ export const createEncryptedSqliteDatabase = (options) => async (run) => {
998
+ const open = createSqliteDatabase(run.deps);
999
+ const raw = open(options);
1000
+ if (raw.ok)
1001
+ return ok({ database: raw.value, keyDerivation: "Raw" });
1002
+ if (!isWrongKey(raw.error, options.path))
1003
+ return err(raw.error);
1004
+ const salt = options.vfs.read(options.path, 0, 16);
1005
+ if (!salt.ok) {
1006
+ assert(salt.error.type === "SqliteVfsIoError", "The pool has the file the raw key failed to open.");
1007
+ return err(salt.error);
1008
+ }
1009
+ const legacyKey = await run.ok(deriveLegacySqliteKey(options.key, salt.value));
1010
+ let legacy;
1011
+ try {
1012
+ legacy = open({ ...options, key: legacyKey });
1013
+ }
1014
+ finally {
1015
+ legacyKey.fill(0);
1016
+ }
1017
+ if (legacy.ok)
1018
+ return ok({ database: legacy.value, keyDerivation: "Legacy224" });
1019
+ return err(isWrongKey(legacy.error, options.path) ? raw.error : legacy.error);
1020
+ };
1021
+ /**
1022
+ * Derives the key `@evolu/sqlite-wasm` 2.2.4 used: PBKDF2-HMAC-SHA512 with
1023
+ * 256000 iterations and a 32-byte output, over the UTF-8 text `x'` followed by
1024
+ * the key in lowercase hex and `'`, salted with the database's first 16 bytes.
1025
+ *
1026
+ * It exists because of a bug in SQLite3 Multiple Ciphers 2.2.4, which 2.2.4
1027
+ * packaged. `x'<hex>'` is SQLCipher's notation for a raw key, which
1028
+ * `@evolu/web` used, but that version took it as a passphrase and derived the
1029
+ * key from the text
1030
+ * (https://github.com/utelle/SQLite3MultipleCiphers/issues/218, fixed in
1031
+ * 2.2.5). So the databases it encrypted are keyed with this derivation, and
1032
+ * opening them needs it for as long as they exist, because nothing rekeys
1033
+ * them.
1034
+ *
1035
+ * The text is built byte by byte, so no string holds the key, and zeroed once
1036
+ * WebCrypto has imported it.
1037
+ */
1038
+ export const deriveLegacySqliteKey = (key, salt) => async (run) => {
1039
+ const { subtleCrypto } = run.deps;
1040
+ // x' followed by the key in lowercase hex and ', where 0x78 is x and 0x27
1041
+ // is '.
1042
+ const passphrase = new Uint8Array(2 * key.length + 3).fill(0x27);
1043
+ passphrase[0] = 0x78;
1044
+ for (const [index, byte] of key.entries()) {
1045
+ passphrase[2 + 2 * index] = lowercaseHexDigit(byte >> 4);
1046
+ passphrase[3 + 2 * index] = lowercaseHexDigit(byte & 0xf);
1047
+ }
1048
+ let passphraseKey;
1049
+ try {
1050
+ passphraseKey = await subtleCrypto.importKey("raw", passphrase, "PBKDF2", false, ["deriveBits"]);
1051
+ }
1052
+ finally {
1053
+ passphrase.fill(0);
1054
+ }
1055
+ const bits = await subtleCrypto.deriveBits({
1056
+ name: "PBKDF2",
1057
+ hash: "SHA-512",
1058
+ salt: new Uint8Array(salt),
1059
+ iterations: 256_000,
1060
+ }, passphraseKey, 256);
1061
+ return ok(new Uint8Array(bits));
1062
+ };
1063
+ // A wrong key fails to authenticate page 1 of the database, which is
1064
+ // SQLITE_NOTADB, or first a hot journal's first record, which is SQLITE_CORRUPT
1065
+ // for any other page. Without a hot journal, SQLite reads page 1 before any
1066
+ // other page, and a pool records a record of a journal that fails only until a
1067
+ // page 1 proved the key, so another page of the database that fails means that
1068
+ // the key fit and the page is damaged.
1069
+ const isWrongKey = (error, path) => error.cause != null &&
1070
+ isPageAuthenticationError(error.cause.error) &&
1071
+ (error.cause.path !== path || error.cause.error.pageNumber === 1);
1072
+ const isPageAuthenticationError = (error) => typeof error === "object" &&
1073
+ error !== null &&
1074
+ "type" in error &&
1075
+ error.type === "SqlitePageAuthenticationError";
1076
+ // The character code of a hex digit in lowercase, as 2.2.4's hex had it.
1077
+ const lowercaseHexDigit = (nibble) => nibble < 10 ? 0x30 + nibble : 0x61 - 10 + nibble;
1078
+ const runningStatementMessage = "A SqliteStatement cannot run or be disposed while it runs.";
1079
+ // Copies a value out of wasm memory, or returns null when JavaScript cannot
1080
+ // allocate the copy, as SQLite returns NULL when it cannot allocate. Engines
1081
+ // throw different errors then, such as V8's RangeError for an ArrayBuffer and
1082
+ // Node.js's Error for a string longer than V8's limit. The copy runs no wasm,
1083
+ // so whatever it throws leaves SQLite as it was, unlike an exception that
1084
+ // escapes wasm, which breaks the instance.
1085
+ const tryCopy = (copy, ptr, byteLength) => {
1086
+ try {
1087
+ return copy(ptr, byteLength);
1088
+ }
1089
+ catch {
1090
+ return null;
1091
+ }
1092
+ };
1093
+ // Whether SQLite reads SQL as the string holds it: SQLite reads SQL only up to
1094
+ // a NUL character, and UTF-8 cannot encode a lone surrogate, which encoding
1095
+ // would replace with U+FFFD.
1096
+ const isValidSqlText = (sql) => !sql.includes("\0") && sql.isWellFormed();
1097
+ // How many operations of databases on each VFS are running, so only the
1098
+ // outermost clears the VFS's failure record. The record is one per VFS, and a
1099
+ // query a callback runs during an operation is an operation too, on the same
1100
+ // database or another one on the VFS.
1101
+ const runningOperationsByVfs = /*#__PURE__*/ new WeakMap();
1102
+ // Whether UTF-8 SQL from start to end holds no statement, only the tokens
1103
+ // SQLite 3.53.4's tokenizer skips: a semicolon; a space, which starts with a
1104
+ // space, \t, \n, \f or \r and continues over sqlite3Isspace, which adds \v,
1105
+ // while a \v that starts a token is illegal; a UTF-8 byte order mark, a space
1106
+ // of its own; a comment from "--" to a newline or the end; and a comment from
1107
+ // "/*" that a byte follows, to the first "*/" after the "/*" or the end, so
1108
+ // "/*/" stays open. An auto-extension can turn comments off with
1109
+ // SQLITE_DBCONFIG_ENABLE_COMMENTS, which makes SQLite reject them, but a tail
1110
+ // of only comments is still accepted here, and it runs nothing.
1111
+ const isEmptySql = (bytes, start, end) => {
1112
+ let index = start;
1113
+ while (index < end) {
1114
+ switch (bytes[index]) {
1115
+ case semicolon:
1116
+ index++;
1117
+ break;
1118
+ case space:
1119
+ case tab:
1120
+ case newline:
1121
+ case formFeed:
1122
+ case carriageReturn:
1123
+ index++;
1124
+ while (index < end &&
1125
+ (bytes[index] === space ||
1126
+ (bytes[index] >= tab && bytes[index] <= carriageReturn)))
1127
+ index++;
1128
+ break;
1129
+ // The first byte of a UTF-8 byte order mark.
1130
+ case 0xef:
1131
+ if (index + 2 >= end ||
1132
+ bytes[index + 1] !== 0xbb ||
1133
+ bytes[index + 2] !== 0xbf)
1134
+ return false;
1135
+ index += 3;
1136
+ break;
1137
+ case hyphen:
1138
+ if (index + 1 === end || bytes[index + 1] !== hyphen)
1139
+ return false;
1140
+ index += 2;
1141
+ while (index < end && bytes[index] !== newline)
1142
+ index++;
1143
+ break;
1144
+ case slash: {
1145
+ if (index + 2 >= end || bytes[index + 1] !== asterisk)
1146
+ return false;
1147
+ let close = index + 2;
1148
+ while (close + 1 < end &&
1149
+ !(bytes[close] === asterisk && bytes[close + 1] === slash))
1150
+ close++;
1151
+ index = close + 1 < end ? close + 2 : end;
1152
+ break;
1153
+ }
1154
+ default:
1155
+ return false;
1156
+ }
1157
+ }
1158
+ return true;
1159
+ };
1160
+ // The ASCII codes isEmptySql reads; \v (11) is between \n and \f.
1161
+ const tab = 0x09;
1162
+ const newline = 0x0a;
1163
+ const formFeed = 0x0c;
1164
+ const carriageReturn = 0x0d;
1165
+ const space = 0x20;
1166
+ const asterisk = 0x2a;
1167
+ const hyphen = 0x2d;
1168
+ const slash = 0x2f;
1169
+ const semicolon = 0x3b;
1170
+ // The bytes an EncryptedFile database leaves to its VFS at the end of each
1171
+ // page, where the pool keeps the IV and the HMAC.
1172
+ const encryptedReservedBytes = 80;
1173
+ // The largest text or blob bound from the scratch memory, which keeps the
1174
+ // memory of the largest value it held. A larger value is allocated exactly and
1175
+ // handed over, which also spares SQLite a copy.
1176
+ const maxScratchValue = 65_536;
1177
+ const utf8Encoder = /*#__PURE__*/ new TextEncoder();