@aztec/kv-store 0.0.1-commit.3100065 → 0.0.1-commit.321f6a9

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.
@@ -21,6 +21,7 @@ import type { ResultRow, SqlValue } from './messages.js';
21
21
  export declare class AztecSQLiteOPFSStore implements AztecAsyncKVStore {
22
22
  #private;
23
23
  readonly isEphemeral: boolean;
24
+ private readonly poolLock?;
24
25
  private constructor();
25
26
  /**
26
27
  * Opens (or creates) a SQLite database stored in the OPFS SAH Pool. When `ephemeral`
@@ -29,6 +30,10 @@ export declare class AztecSQLiteOPFSStore implements AztecAsyncKVStore {
29
30
  * required when multiple stores coexist in the same tab, because the SAH Pool holds
30
31
  * an exclusive lock on its directory.
31
32
  *
33
+ * Persistent stores hold an origin-wide Web Lock for the pool directory until close
34
+ * or delete. If another store instance already owns it, open fails immediately with
35
+ * `SqlitePoolBusyError`.
36
+ *
32
37
  * Pass `encryptionKey` (exactly 32 bytes) to enable at-rest encryption via sqlite3mc's
33
38
  * ChaCha20 page cipher. The key buffer is **transferred** to the worker — its
34
39
  * ArrayBuffer detaches on the caller side after `postMessage`. This is intentional:
@@ -76,4 +81,4 @@ export declare class AztecSQLiteOPFSStore implements AztecAsyncKVStore {
76
81
  /** Runs a SELECT statement and returns rows in array row-mode. */
77
82
  allAsync(sql: string, bind?: SqlValue[]): Promise<ResultRow[]>;
78
83
  }
79
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic3RvcmUuZC50cyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9zcWxpdGUtb3Bmcy9zdG9yZS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEtBQUssRUFBRSxNQUFNLEVBQUUsTUFBTSx1QkFBdUIsQ0FBQztBQUdwRCxPQUFPLEtBQUssRUFBRSxlQUFlLEVBQUUsTUFBTSx3QkFBd0IsQ0FBQztBQUM5RCxPQUFPLEtBQUssRUFBRSxHQUFHLEVBQUUsU0FBUyxFQUFFLEtBQUssRUFBRSxNQUFNLHlCQUF5QixDQUFDO0FBQ3JFLE9BQU8sS0FBSyxFQUFFLGlCQUFpQixFQUFFLE1BQU0sMEJBQTBCLENBQUM7QUFDbEUsT0FBTyxLQUFLLEVBQUUsYUFBYSxFQUFFLE1BQU0sc0JBQXNCLENBQUM7QUFDMUQsT0FBTyxLQUFLLEVBQUUsa0JBQWtCLEVBQUUsTUFBTSw0QkFBNEIsQ0FBQztBQUNyRSxPQUFPLEtBQUssRUFBRSxhQUFhLEVBQUUsTUFBTSxzQkFBc0IsQ0FBQztBQUMxRCxPQUFPLEtBQUssRUFBRSxtQkFBbUIsRUFBRSxNQUFNLDRCQUE0QixDQUFDO0FBQ3RFLE9BQU8sS0FBSyxFQUFFLGlCQUFpQixFQUFFLE1BQU0sd0JBQXdCLENBQUM7QUFJaEUsT0FBTyxLQUFLLEVBQUUsU0FBUyxFQUFFLFFBQVEsRUFBaUMsTUFBTSxlQUFlLENBQUM7QUFLeEY7Ozs7Ozs7OztHQVNHO0FBQ0gscUJBQWEsb0JBQXFCLFlBQVcsaUJBQWlCOzthQWMxQyxXQUFXLEVBQUUsT0FBTztJQUp0QyxPQUFPLGVBd0JOO0lBRUQ7Ozs7Ozs7Ozs7Ozs7OztPQWVHO0lBQ0gsT0FBYSxJQUFJLENBQ2YsR0FBRyxFQUFFLE1BQU0sRUFDWCxJQUFJLENBQUMsRUFBRSxNQUFNLEVBQ2IsU0FBUyxHQUFFLE9BQWUsRUFDMUIsYUFBYSxDQUFDLEVBQUUsTUFBTSxFQUN0QixhQUFhLENBQUMsRUFBRSxVQUFVLEdBQ3pCLE9BQU8sQ0FBQyxvQkFBb0IsQ0FBQyxDQWlDL0I7SUFFRCxPQUFPLENBQUMsQ0FBQyxTQUFTLEdBQUcsRUFBRSxDQUFDLFNBQVMsS0FBSyxFQUFFLElBQUksRUFBRSxNQUFNLEdBQUcsYUFBYSxDQUFDLENBQUMsRUFBRSxDQUFDLENBQUMsQ0FFekU7SUFFRCxPQUFPLENBQUMsQ0FBQyxTQUFTLEdBQUcsRUFBRSxJQUFJLEVBQUUsTUFBTSxHQUFHLGFBQWEsQ0FBQyxDQUFDLENBQUMsQ0FFckQ7SUFFRCxZQUFZLENBQUMsQ0FBQyxTQUFTLEdBQUcsRUFBRSxDQUFDLFNBQVMsS0FBSyxFQUFFLElBQUksRUFBRSxNQUFNLEdBQUcsa0JBQWtCLENBQUMsQ0FBQyxFQUFFLENBQUMsQ0FBQyxDQUVuRjtJQUVELFdBQVcsQ0FBQyxDQUFDLFNBQVMsR0FBRyxFQUFFLEtBQUssRUFBRSxNQUFNLEdBQUcsaUJBQWlCLENBQUMsQ0FBQyxDQUFDLENBRTlEO0lBRUQsU0FBUyxDQUFDLENBQUMsU0FBUyxLQUFLLEVBQUUsSUFBSSxFQUFFLE1BQU0sR0FBRyxlQUFlLENBQUMsQ0FBQyxDQUFDLENBRTNEO0lBRUQsYUFBYSxDQUFDLENBQUMsU0FBUyxLQUFLLEVBQUUsSUFBSSxFQUFFLE1BQU0sR0FBRyxtQkFBbUIsQ0FBQyxDQUFDLENBQUMsQ0FFbkU7SUFFRCxnQkFBZ0IsQ0FBQyxDQUFDLEVBQUUsUUFBUSxFQUFFLE1BQU0sT0FBTyxDQUFDLENBQUMsQ0FBQyxHQUFHLE9BQU8sQ0FBQyxDQUFDLENBQUMsQ0F3QjFEO0lBRUssS0FBSyxJQUFJLE9BQU8sQ0FBQyxJQUFJLENBQUMsQ0FFM0I7SUFFSyxNQUFNLElBQUksT0FBTyxDQUFDLElBQUksQ0FBQyxDQVc1QjtJQUVEOzs7Ozs7O09BT0c7SUFDSCxZQUFZLElBQUksT0FBTyxDQUFDLFNBQVMsQ0FBQyxDQUVqQztJQUVLLEtBQUssSUFBSSxPQUFPLENBQUMsSUFBSSxDQUFDLENBUzNCO0lBRUQsUUFBUSxDQUFDLFFBQVEsRUFBRSxNQUFNLEVBQUUsUUFBUSxDQUFDLEVBQUUsT0FBTyxHQUFHLE9BQU8sQ0FBQyxJQUFJLENBQUMsQ0FFNUQ7SUFFRDs7OztPQUlHO0lBQ0csUUFBUSxJQUFJLE9BQU8sQ0FBQyxVQUFVLENBQUMsQ0FNcEM7SUFFRDs7OztPQUlHO0lBQ0gsUUFBUSxDQUFDLEdBQUcsRUFBRSxNQUFNLEVBQUUsSUFBSSxDQUFDLEVBQUUsUUFBUSxFQUFFLEdBQUcsT0FBTyxDQUFDO1FBQUUsT0FBTyxFQUFFLE1BQU0sQ0FBQTtLQUFFLENBQUMsQ0FNckU7SUFFRCxrRUFBa0U7SUFDbEUsUUFBUSxDQUFDLEdBQUcsRUFBRSxNQUFNLEVBQUUsSUFBSSxDQUFDLEVBQUUsUUFBUSxFQUFFLEdBQUcsT0FBTyxDQUFDLFNBQVMsRUFBRSxDQUFDLENBSTdEO0NBZ0RGIn0=
84
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic3RvcmUuZC50cyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9zcWxpdGUtb3Bmcy9zdG9yZS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEtBQUssRUFBRSxNQUFNLEVBQUUsTUFBTSx1QkFBdUIsQ0FBQztBQUdwRCxPQUFPLEtBQUssRUFBRSxlQUFlLEVBQUUsTUFBTSx3QkFBd0IsQ0FBQztBQUM5RCxPQUFPLEtBQUssRUFBRSxHQUFHLEVBQUUsU0FBUyxFQUFFLEtBQUssRUFBRSxNQUFNLHlCQUF5QixDQUFDO0FBQ3JFLE9BQU8sS0FBSyxFQUFFLGlCQUFpQixFQUFFLE1BQU0sMEJBQTBCLENBQUM7QUFDbEUsT0FBTyxLQUFLLEVBQUUsYUFBYSxFQUFFLE1BQU0sc0JBQXNCLENBQUM7QUFDMUQsT0FBTyxLQUFLLEVBQUUsa0JBQWtCLEVBQUUsTUFBTSw0QkFBNEIsQ0FBQztBQUNyRSxPQUFPLEtBQUssRUFBRSxhQUFhLEVBQUUsTUFBTSxzQkFBc0IsQ0FBQztBQUMxRCxPQUFPLEtBQUssRUFBRSxtQkFBbUIsRUFBRSxNQUFNLDRCQUE0QixDQUFDO0FBQ3RFLE9BQU8sS0FBSyxFQUFFLGlCQUFpQixFQUFFLE1BQU0sd0JBQXdCLENBQUM7QUFJaEUsT0FBTyxLQUFLLEVBQUUsU0FBUyxFQUFFLFFBQVEsRUFBaUMsTUFBTSxlQUFlLENBQUM7QUFPeEY7Ozs7Ozs7OztHQVNHO0FBQ0gscUJBQWEsb0JBQXFCLFlBQVcsaUJBQWlCOzthQWUxQyxXQUFXLEVBQUUsT0FBTztJQUNwQyxPQUFPLENBQUMsUUFBUSxDQUFDLFFBQVEsQ0FBQztJQUw1QixPQUFPLGVBMEJOO0lBRUQ7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7T0FtQkc7SUFDSCxPQUFhLElBQUksQ0FDZixHQUFHLEVBQUUsTUFBTSxFQUNYLElBQUksQ0FBQyxFQUFFLE1BQU0sRUFDYixTQUFTLEdBQUUsT0FBZSxFQUMxQixhQUFhLENBQUMsRUFBRSxNQUFNLEVBQ3RCLGFBQWEsQ0FBQyxFQUFFLFVBQVUsR0FDekIsT0FBTyxDQUFDLG9CQUFvQixDQUFDLENBMkMvQjtJQUVELE9BQU8sQ0FBQyxDQUFDLFNBQVMsR0FBRyxFQUFFLENBQUMsU0FBUyxLQUFLLEVBQUUsSUFBSSxFQUFFLE1BQU0sR0FBRyxhQUFhLENBQUMsQ0FBQyxFQUFFLENBQUMsQ0FBQyxDQUV6RTtJQUVELE9BQU8sQ0FBQyxDQUFDLFNBQVMsR0FBRyxFQUFFLElBQUksRUFBRSxNQUFNLEdBQUcsYUFBYSxDQUFDLENBQUMsQ0FBQyxDQUVyRDtJQUVELFlBQVksQ0FBQyxDQUFDLFNBQVMsR0FBRyxFQUFFLENBQUMsU0FBUyxLQUFLLEVBQUUsSUFBSSxFQUFFLE1BQU0sR0FBRyxrQkFBa0IsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxDQUFDLENBRW5GO0lBRUQsV0FBVyxDQUFDLENBQUMsU0FBUyxHQUFHLEVBQUUsS0FBSyxFQUFFLE1BQU0sR0FBRyxpQkFBaUIsQ0FBQyxDQUFDLENBQUMsQ0FFOUQ7SUFFRCxTQUFTLENBQUMsQ0FBQyxTQUFTLEtBQUssRUFBRSxJQUFJLEVBQUUsTUFBTSxHQUFHLGVBQWUsQ0FBQyxDQUFDLENBQUMsQ0FFM0Q7SUFFRCxhQUFhLENBQUMsQ0FBQyxTQUFTLEtBQUssRUFBRSxJQUFJLEVBQUUsTUFBTSxHQUFHLG1CQUFtQixDQUFDLENBQUMsQ0FBQyxDQUVuRTtJQUVELGdCQUFnQixDQUFDLENBQUMsRUFBRSxRQUFRLEVBQUUsTUFBTSxPQUFPLENBQUMsQ0FBQyxDQUFDLEdBQUcsT0FBTyxDQUFDLENBQUMsQ0FBQyxDQXdCMUQ7SUFFSyxLQUFLLElBQUksT0FBTyxDQUFDLElBQUksQ0FBQyxDQUUzQjtJQUVLLE1BQU0sSUFBSSxPQUFPLENBQUMsSUFBSSxDQUFDLENBZTVCO0lBRUQ7Ozs7Ozs7T0FPRztJQUNILFlBQVksSUFBSSxPQUFPLENBQUMsU0FBUyxDQUFDLENBRWpDO0lBRUssS0FBSyxJQUFJLE9BQU8sQ0FBQyxJQUFJLENBQUMsQ0FhM0I7SUFFRCxRQUFRLENBQUMsUUFBUSxFQUFFLE1BQU0sRUFBRSxRQUFRLENBQUMsRUFBRSxPQUFPLEdBQUcsT0FBTyxDQUFDLElBQUksQ0FBQyxDQUU1RDtJQUVEOzs7O09BSUc7SUFDRyxRQUFRLElBQUksT0FBTyxDQUFDLFVBQVUsQ0FBQyxDQU1wQztJQUVEOzs7O09BSUc7SUFDSCxRQUFRLENBQUMsR0FBRyxFQUFFLE1BQU0sRUFBRSxJQUFJLENBQUMsRUFBRSxRQUFRLEVBQUUsR0FBRyxPQUFPLENBQUM7UUFBRSxPQUFPLEVBQUUsTUFBTSxDQUFBO0tBQUUsQ0FBQyxDQU1yRTtJQUVELGtFQUFrRTtJQUNsRSxRQUFRLENBQUMsR0FBRyxFQUFFLE1BQU0sRUFBRSxJQUFJLENBQUMsRUFBRSxRQUFRLEVBQUUsR0FBRyxPQUFPLENBQUMsU0FBUyxFQUFFLENBQUMsQ0FJN0Q7Q0F1REYifQ==
@@ -1 +1 @@
1
- {"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../../src/sqlite-opfs/store.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../../src/sqlite-opfs/store.ts"],"names":[],"mappings":"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"}
@@ -1,8 +1,10 @@
1
1
  import { SerialQueue } from '@aztec/foundation/queue';
2
2
  import { SQLiteOPFSAztecArray } from './array.js';
3
- import { SqliteEncryptionError } from './errors.js';
3
+ import { SqliteCorruptionError, SqliteEncryptionError, isCorruptionMessage } from './errors.js';
4
4
  import { SQLiteOPFSAztecMap } from './map.js';
5
5
  import { SQLiteOPFSAztecMultiMap } from './multi_map.js';
6
+ import { quarantineDuplicatePool } from './pool_integrity.js';
7
+ import { acquirePoolLock, normalizePoolDirectory } from './pool_lock.js';
6
8
  import { SQLiteOPFSAztecSet } from './set.js';
7
9
  import { SQLiteOPFSAztecSingleton } from './singleton.js';
8
10
  /**
@@ -16,6 +18,7 @@ import { SQLiteOPFSAztecSingleton } from './singleton.js';
16
18
  * nested ops bypass it to avoid deadlock.
17
19
  */ export class AztecSQLiteOPFSStore {
18
20
  isEphemeral;
21
+ poolLock;
19
22
  #worker;
20
23
  #pending;
21
24
  #txQueue;
@@ -24,13 +27,16 @@ import { SQLiteOPFSAztecSingleton } from './singleton.js';
24
27
  #nextId;
25
28
  #inTx;
26
29
  #closed;
27
- constructor(worker, name, log, isEphemeral){
30
+ #workerFailed;
31
+ constructor(worker, name, log, isEphemeral, poolLock){
28
32
  this.isEphemeral = isEphemeral;
33
+ this.poolLock = poolLock;
29
34
  this.#pending = new Map();
30
35
  this.#txQueue = new SerialQueue();
31
36
  this.#nextId = 0;
32
37
  this.#inTx = false;
33
38
  this.#closed = false;
39
+ this.#workerFailed = false;
34
40
  this.#worker = worker;
35
41
  this.#name = name;
36
42
  this.#log = log;
@@ -45,6 +51,7 @@ import { SQLiteOPFSAztecSingleton } from './singleton.js';
45
51
  handler.resolve(ev.data);
46
52
  };
47
53
  this.#worker.onerror = (ev)=>{
54
+ this.#workerFailed = true;
48
55
  this.#log.error(`SQLite worker crashed: ${ev.message}`);
49
56
  this.#rejectPending(`SQLite worker crashed: ${ev.message}`);
50
57
  };
@@ -57,6 +64,10 @@ import { SQLiteOPFSAztecSingleton } from './singleton.js';
57
64
  * required when multiple stores coexist in the same tab, because the SAH Pool holds
58
65
  * an exclusive lock on its directory.
59
66
  *
67
+ * Persistent stores hold an origin-wide Web Lock for the pool directory until close
68
+ * or delete. If another store instance already owns it, open fails immediately with
69
+ * `SqlitePoolBusyError`.
70
+ *
60
71
  * Pass `encryptionKey` (exactly 32 bytes) to enable at-rest encryption via sqlite3mc's
61
72
  * ChaCha20 page cipher. The key buffer is **transferred** to the worker — its
62
73
  * ArrayBuffer detaches on the caller side after `postMessage`. This is intentional:
@@ -74,30 +85,40 @@ import { SQLiteOPFSAztecSingleton } from './singleton.js';
74
85
  }
75
86
  const dbName = name && !ephemeral ? name : `tmp-${globalThis.crypto.getRandomValues(new Uint8Array(8)).join('')}`;
76
87
  log.debug(`Opening SQLite-OPFS ${ephemeral ? 'ephemeral ' : ''}${encryptionKey ? 'encrypted ' : ''}database ${dbName}`);
77
- const worker = new Worker(new URL('./worker.js', import.meta.url), {
78
- type: 'module'
79
- });
80
- const store = new AztecSQLiteOPFSStore(worker, dbName, log, ephemeral);
81
- // Transfer (not clone) the key buffer to the worker so we don't leave a
82
- // second copy on the main thread. Caveat: this detaches the caller's
83
- // encryptionKey.buffer — subsequent reads from the same Uint8Array are empty.
84
- const transfer = encryptionKey ? [
85
- encryptionKey.buffer
86
- ] : undefined;
88
+ const effectivePoolDirectory = ephemeral ? undefined : normalizePoolDirectory(poolDirectory);
89
+ const poolLock = effectivePoolDirectory ? await acquirePoolLock(effectivePoolDirectory) : undefined;
90
+ let worker;
87
91
  try {
92
+ if (effectivePoolDirectory) {
93
+ const quarantine = await quarantineDuplicatePool(effectivePoolDirectory);
94
+ if (quarantine) {
95
+ log.warn(`Quarantined SQLite-OPFS pool with duplicate logical file mappings`, quarantine);
96
+ }
97
+ }
98
+ worker = new Worker(new URL('./worker.js', import.meta.url), {
99
+ type: 'module'
100
+ });
101
+ const store = new AztecSQLiteOPFSStore(worker, dbName, log, ephemeral, poolLock);
102
+ // Transfer (not clone) the key buffer to the worker so we don't leave a
103
+ // second copy on the main thread. Caveat: this detaches the caller's
104
+ // encryptionKey.buffer — subsequent reads from the same Uint8Array are empty.
105
+ const transfer = encryptionKey ? [
106
+ encryptionKey.buffer
107
+ ] : undefined;
88
108
  await store.#sendRequest({
89
109
  type: 'init',
90
110
  id: store.#allocId(),
91
111
  dbName,
92
112
  ephemeral,
93
- poolDirectory,
113
+ poolDirectory: effectivePoolDirectory,
94
114
  encryptionKey
95
115
  }, transfer);
116
+ return store;
96
117
  } catch (err) {
97
- worker.terminate();
118
+ worker?.terminate();
119
+ await poolLock?.release();
98
120
  throw err;
99
121
  }
100
- return store;
101
122
  }
102
123
  openMap(name) {
103
124
  return new SQLiteOPFSAztecMap(this, name);
@@ -157,14 +178,18 @@ import { SQLiteOPFSAztecSingleton } from './singleton.js';
157
178
  return;
158
179
  }
159
180
  this.#closed = true;
160
- await this.#txQueue.end();
161
- await this.#sendRequest({
162
- type: 'deleteDb',
163
- id: this.#allocId(),
164
- dbName: this.#name
165
- }).catch((err)=>this.#log.warn(`SQLite deleteDb failed: ${err instanceof Error ? err.message : err}`));
166
- this.#worker.terminate();
167
- this.#rejectPending('SQLite store deleted');
181
+ try {
182
+ await this.#txQueue.end();
183
+ await this.#sendRequest({
184
+ type: 'deleteDb',
185
+ id: this.#allocId(),
186
+ dbName: this.#name
187
+ }).catch((err)=>this.#log.warn(`SQLite deleteDb failed: ${err instanceof Error ? err.message : err}`));
188
+ } finally{
189
+ this.#worker.terminate();
190
+ this.#rejectPending('SQLite store deleted');
191
+ await this.poolLock?.release();
192
+ }
168
193
  }
169
194
  /**
170
195
  * Placeholder — returns zeros to mirror the IndexedDB backend. SQLite exposes real
@@ -186,13 +211,17 @@ import { SQLiteOPFSAztecSingleton } from './singleton.js';
186
211
  return;
187
212
  }
188
213
  this.#closed = true;
189
- await this.#txQueue.end();
190
- await this.#sendRequest({
191
- type: 'close',
192
- id: this.#allocId()
193
- }).catch(()=>{});
194
- this.#worker.terminate();
195
- this.#rejectPending('SQLite store closed');
214
+ try {
215
+ await this.#txQueue.end();
216
+ await this.#sendRequest({
217
+ type: 'close',
218
+ id: this.#allocId()
219
+ }).catch(()=>{});
220
+ } finally{
221
+ this.#worker.terminate();
222
+ this.#rejectPending('SQLite store closed');
223
+ await this.poolLock?.release();
224
+ }
196
225
  }
197
226
  backupTo(_dstPath, _compact) {
198
227
  throw new Error('Method not implemented.');
@@ -253,15 +282,22 @@ import { SQLiteOPFSAztecSingleton } from './singleton.js';
253
282
  this.#pending.clear();
254
283
  }
255
284
  #sendRequest(req, transfer) {
285
+ if (this.#workerFailed) {
286
+ return Promise.reject(new Error('SQLite worker has crashed'));
287
+ }
256
288
  return new Promise((resolve, reject)=>{
257
289
  this.#pending.set(req.id, {
258
290
  resolve: (resp)=>{
259
291
  if (resp.type === 'err') {
260
- // Re-hydrate encryption-shaped errors as the typed class so consumers
261
- // can pattern-match on `instanceof SqliteEncryptionError`. Plain
262
- // errors stay plain the wire protocol only tags encryption paths.
292
+ // Re-hydrate typed errors so consumers can pattern-match on
293
+ // `instanceof`. Encryption is tagged on the wire (some cases are
294
+ // pre-flight throws with no message to match); corruption is a
295
+ // single unambiguous message, so we classify it here rather than
296
+ // adding a redundant wire field. Everything else stays a plain Error.
263
297
  if (resp.encryptionCode !== undefined) {
264
298
  reject(new SqliteEncryptionError(resp.encryptionCode, resp.message));
299
+ } else if (isCorruptionMessage(resp.message)) {
300
+ reject(new SqliteCorruptionError(resp.message));
265
301
  } else {
266
302
  reject(new Error(resp.message));
267
303
  }
@@ -1,6 +1,7 @@
1
1
  /// <reference lib="webworker" />
2
2
  import sqlite3InitModule from '@aztec/sqlite3mc-wasm';
3
3
  import { SqliteEncryptionError, isDecryptFailureMessage } from './errors.js';
4
+ import { DEFAULT_SAH_POOL_DIRECTORY } from './pool_lock.js';
4
5
  const SCHEMA_SQL = `
5
6
  CREATE TABLE IF NOT EXISTS data (
6
7
  slot TEXT NOT NULL PRIMARY KEY,
@@ -15,7 +16,6 @@ const SCHEMA_SQL = `
15
16
  CREATE UNIQUE INDEX IF NOT EXISTS idx_container_key_count ON data(container, key, key_count);
16
17
  CREATE UNIQUE INDEX IF NOT EXISTS idx_container_key_hash ON data(container, key, hash);
17
18
  `;
18
- const DEFAULT_SAH_POOL_DIRECTORY = '.aztec-kv';
19
19
  const SAH_POOL_VFS_NAME = 'aztec-kv-opfs';
20
20
  const MC_SAH_POOL_VFS_NAME = `multipleciphers-${SAH_POOL_VFS_NAME}`;
21
21
  let sqlite3;
@@ -74,9 +74,24 @@ async function handleInit(dbName, ephemeral, directory, encryptionKey) {
74
74
  runSql(SCHEMA_SQL);
75
75
  }
76
76
  function handleClose() {
77
- db?.close();
78
- db = undefined;
79
- dbPath = undefined;
77
+ try {
78
+ db?.close();
79
+ } finally{
80
+ db = undefined;
81
+ dbPath = undefined;
82
+ releasePool();
83
+ }
84
+ }
85
+ /**
86
+ * Releases the SAH pool's OPFS sync access handles before the terminal RPC is acked. Worker
87
+ * termination releases them only asynchronously, so without this a caller that deletes or reopens
88
+ * the store directory right after close()/delete() resolves races Chromium's cleanup
89
+ * (NoModificationAllowedError from removeEntry, or a hang installing a new pool on the directory).
90
+ * pauseVfs releases the handles without touching file contents; the worker is terminated right
91
+ * after, so the pool is never resumed.
92
+ */ function releasePool() {
93
+ pool?.pauseVfs();
94
+ pool = undefined;
80
95
  }
81
96
  async function handleExport() {
82
97
  if (!db || !dbPath) {
@@ -107,6 +122,10 @@ function handleDeleteDb(dbName) {
107
122
  } catch {
108
123
  // File may not exist; ignore.
109
124
  }
125
+ // Guarded because pauseVfs refuses (SQLITE_MISUSE) while any file is open through the VFS.
126
+ if (!db) {
127
+ releasePool();
128
+ }
110
129
  }
111
130
  function requireDb() {
112
131
  if (!db) {
@@ -216,11 +235,19 @@ self.onmessage = async (ev)=>{
216
235
  }
217
236
  } catch (err) {
218
237
  const message = err instanceof Error ? err.message : String(err);
238
+ const encryptionCode = detectEncryptionCode(req, err, message);
239
+ if (req.type === 'init') {
240
+ try {
241
+ handleClose();
242
+ } catch {
243
+ // The main thread terminates this worker after a failed init, which releases any remaining OPFS handles.
244
+ }
245
+ }
219
246
  respond({
220
247
  type: 'err',
221
248
  id: req.id,
222
249
  message,
223
- encryptionCode: detectEncryptionCode(req, err, message)
250
+ encryptionCode
224
251
  });
225
252
  }
226
253
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aztec/kv-store",
3
- "version": "0.0.1-commit.3100065",
3
+ "version": "0.0.1-commit.321f6a9",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  ".": "./dest/interfaces/index.js",
@@ -36,12 +36,12 @@
36
36
  "./package.local.json"
37
37
  ],
38
38
  "dependencies": {
39
- "@aztec/constants": "0.0.1-commit.3100065",
40
- "@aztec/ethereum": "0.0.1-commit.3100065",
41
- "@aztec/foundation": "0.0.1-commit.3100065",
42
- "@aztec/native": "0.0.1-commit.3100065",
43
- "@aztec/sqlite3mc-wasm": "0.0.1-commit.3100065",
44
- "@aztec/stdlib": "0.0.1-commit.3100065",
39
+ "@aztec/constants": "0.0.1-commit.321f6a9",
40
+ "@aztec/ethereum": "0.0.1-commit.321f6a9",
41
+ "@aztec/foundation": "0.0.1-commit.321f6a9",
42
+ "@aztec/native": "0.0.1-commit.321f6a9",
43
+ "@aztec/sqlite3mc-wasm": "0.0.1-commit.321f6a9",
44
+ "@aztec/stdlib": "0.0.1-commit.321f6a9",
45
45
  "idb": "^8.0.0",
46
46
  "lmdb": "^3.2.0",
47
47
  "msgpackr": "^1.11.2",
@@ -1,34 +1,9 @@
1
- import { type Logger, createLogger } from '@aztec/foundation/log';
2
- import type { DataStoreConfig } from '@aztec/stdlib/kv-store';
1
+ import { createLogger } from '@aztec/foundation/log';
3
2
 
4
- import { initStoreForRollupAndSchemaVersion } from '../../utils.js';
5
3
  import { AztecIndexedDBStore } from './store.js';
6
4
 
7
5
  export { AztecIndexedDBStore } from './store.js';
8
6
 
9
- /**
10
- * @deprecated The IndexedDB backend is being retired. Use `@aztec/kv-store/sqlite-opfs` instead.
11
- */
12
- export async function createStore(
13
- name: string,
14
- config: DataStoreConfig,
15
- schemaVersion: number | undefined = undefined,
16
- log: Logger = createLogger('kv-store'),
17
- ) {
18
- let { dataDirectory } = config;
19
- if (typeof dataDirectory !== 'undefined') {
20
- dataDirectory = `${dataDirectory}/${name}`;
21
- }
22
-
23
- log.info(
24
- dataDirectory
25
- ? `Creating ${name} data store at directory ${dataDirectory} with map size ${config.dataStoreMapSizeKb} KB`
26
- : `Creating ${name} ephemeral data store with map size ${config.dataStoreMapSizeKb} KB`,
27
- );
28
- const store = await AztecIndexedDBStore.open(createLogger('kv-store:indexeddb'), dataDirectory ?? '', false);
29
- return initStoreForRollupAndSchemaVersion(store, schemaVersion, config.rollupAddress, log);
30
- }
31
-
32
7
  /**
33
8
  * @deprecated The IndexedDB backend is being retired. Use `@aztec/kv-store/sqlite-opfs` instead.
34
9
  */
@@ -42,3 +42,53 @@ const SQLITE3MC_DECRYPT_ERROR_PATTERNS: readonly RegExp[] = [
42
42
  export function isDecryptFailureMessage(message: string): boolean {
43
43
  return SQLITE3MC_DECRYPT_ERROR_PATTERNS.some(p => p.test(message));
44
44
  }
45
+
46
+ /**
47
+ * Error thrown by sqlite-opfs when the on-disk database image is corrupt
48
+ * (`SQLITE_CORRUPT`, result code 11 — "database disk image is malformed").
49
+ *
50
+ * Distinct from {@link SqliteEncryptionError}: no key recovers a corrupt image,
51
+ * so there is nothing to retry. The only escape is to delete the store and start
52
+ * fresh, so consumers pattern-match on `instanceof SqliteCorruptionError` to wipe
53
+ * and reopen rather than surfacing a dead-end error.
54
+ **/
55
+ export class SqliteCorruptionError extends Error {
56
+ constructor(message: string, opts?: { cause?: unknown }) {
57
+ super(message, opts?.cause !== undefined ? { cause: opts.cause } : undefined);
58
+ this.name = 'SqliteCorruptionError';
59
+ }
60
+ }
61
+
62
+ /** Error thrown when another browser context already owns a store's OPFS pool. */
63
+ export class SqlitePoolBusyError extends Error {
64
+ constructor(public readonly poolDirectory: string) {
65
+ super(`SQLite-OPFS pool "${poolDirectory}" is already in use by another store instance`);
66
+ this.name = 'SqlitePoolBusyError';
67
+ }
68
+ }
69
+
70
+ /** Error thrown when the browser context does not expose the Web Locks API required by the OPFS SAH pool. */
71
+ export class SqliteWebLocksUnavailableError extends Error {
72
+ constructor() {
73
+ super('SQLite-OPFS requires the Web Locks API, but it is unavailable in this browser context');
74
+ this.name = 'SqliteWebLocksUnavailableError';
75
+ }
76
+ }
77
+
78
+ /**
79
+ * Strings/codes raised by SQLite when a database image is corrupt. Kept disjoint
80
+ * from {@link SQLITE3MC_DECRYPT_ERROR_PATTERNS}: "file is not a database"
81
+ * (SQLITE_NOTADB) is the decrypt signal, whereas a malformed image is the
82
+ * genuinely-unrecoverable SQLITE_CORRUPT.
83
+ **/
84
+ const SQLITE_CORRUPTION_ERROR_PATTERNS: readonly RegExp[] = [
85
+ /database disk image is malformed/i,
86
+ /\bSQLITE_CORRUPT\b/i,
87
+ ];
88
+
89
+ /**
90
+ * Returns `true` if `message` matches one of the known SQLite corruption strings.
91
+ **/
92
+ export function isCorruptionMessage(message: string): boolean {
93
+ return SQLITE_CORRUPTION_ERROR_PATTERNS.some(p => p.test(message));
94
+ }
@@ -1,28 +1,16 @@
1
- import { type Logger, createLogger } from '@aztec/foundation/log';
2
- import type { DataStoreConfig } from '@aztec/stdlib/kv-store';
1
+ import { createLogger } from '@aztec/foundation/log';
3
2
 
4
- import { initStoreForRollupAndSchemaVersion } from '../utils.js';
5
3
  import { AztecSQLiteOPFSStore } from './store.js';
6
4
 
7
5
  export { AztecSQLiteOPFSStore } from './store.js';
8
- export { SqliteEncryptionError } from './errors.js';
6
+ export {
7
+ SqliteCorruptionError,
8
+ SqliteEncryptionError,
9
+ SqlitePoolBusyError,
10
+ SqliteWebLocksUnavailableError,
11
+ } from './errors.js';
9
12
  export type { SqliteEncryptionErrorCode } from './errors.js';
10
-
11
- export async function createStore(
12
- name: string,
13
- config: DataStoreConfig,
14
- schemaVersion: number | undefined = undefined,
15
- log: Logger = createLogger('kv-store'),
16
- ) {
17
- const { dataDirectory } = config;
18
- log.info(
19
- dataDirectory
20
- ? `Creating ${name} SQLite-OPFS data store with map size ${config.dataStoreMapSizeKb} KB`
21
- : `Creating ${name} ephemeral SQLite-OPFS data store with map size ${config.dataStoreMapSizeKb} KB`,
22
- );
23
- const store = await AztecSQLiteOPFSStore.open(createLogger('kv-store:sqlite-opfs'), name, false);
24
- return initStoreForRollupAndSchemaVersion(store, schemaVersion, config.rollupAddress, log);
25
- }
13
+ export { OPFS_POOL_DIR_PREFIX, deletePoolDirectory, deleteStore, listStores, storePoolDirectory } from './manage.js';
26
14
 
27
15
  export function openTmpStore(ephemeral: boolean = false): Promise<AztecSQLiteOPFSStore> {
28
16
  return AztecSQLiteOPFSStore.open(createLogger('kv-store:sqlite-opfs'), undefined, ephemeral);
@@ -0,0 +1,46 @@
1
+ import { normalizePoolDirectory, withPoolLock } from './pool_lock.js';
2
+
3
+ /** Prefix for the per-store OPFS SAH pool directories owned by this package. */
4
+ export const OPFS_POOL_DIR_PREFIX = '.aztec-kv-';
5
+
6
+ /**
7
+ * OPFS directory holding a store's SAH pool. One directory per store: the SAH-pool VFS allows only one
8
+ * concurrent instance per directory and its capacity does not grow automatically, so sharing a pool across
9
+ * stores would make concurrently opened stores contend for locks and orphaned stores exhaust pool slots.
10
+ */
11
+ export function storePoolDirectory(effectiveName: string): string {
12
+ return `${OPFS_POOL_DIR_PREFIX}${effectiveName}`;
13
+ }
14
+
15
+ /**
16
+ * Lists the names of every persistent sqlite-opfs store in this origin, by enumerating the per-store pool
17
+ * directories. Includes stores other than the current one, so wallets can surface and clean up data for
18
+ * networks/versions no longer in use.
19
+ */
20
+ export async function listStores(): Promise<string[]> {
21
+ const root = await navigator.storage.getDirectory();
22
+ const names: string[] = [];
23
+ for await (const [entryName, handle] of root.entries()) {
24
+ if (handle.kind === 'directory' && entryName.startsWith(OPFS_POOL_DIR_PREFIX)) {
25
+ names.push(entryName.slice(OPFS_POOL_DIR_PREFIX.length));
26
+ }
27
+ }
28
+ return names;
29
+ }
30
+
31
+ /**
32
+ * Permanently deletes a store by effective name (as returned by {@link listStores}). The store must be closed:
33
+ * an open store holds the directory's Web Lock and the removal will reject with `SqlitePoolBusyError`.
34
+ */
35
+ export async function deleteStore(effectiveName: string): Promise<void> {
36
+ await deletePoolDirectory(storePoolDirectory(effectiveName));
37
+ }
38
+
39
+ /** Permanently deletes one OPFS SAH-pool directory after exclusively locking it. */
40
+ export async function deletePoolDirectory(poolDirectory: string): Promise<void> {
41
+ poolDirectory = normalizePoolDirectory(poolDirectory);
42
+ await withPoolLock(poolDirectory, async () => {
43
+ const root = await navigator.storage.getDirectory();
44
+ await root.removeEntry(poolDirectory, { recursive: true });
45
+ });
46
+ }