@aztec/kv-store 0.0.1-commit.b9865e97 → 0.0.1-commit.be03c316

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 (80) hide show
  1. package/README.md +10 -8
  2. package/dest/deprecated/indexeddb/array.d.ts +22 -0
  3. package/dest/deprecated/indexeddb/array.d.ts.map +1 -0
  4. package/dest/deprecated/indexeddb/index.d.ts +7 -0
  5. package/dest/deprecated/indexeddb/index.d.ts.map +1 -0
  6. package/dest/deprecated/indexeddb/index.js +8 -0
  7. package/dest/deprecated/indexeddb/map.d.ts +38 -0
  8. package/dest/deprecated/indexeddb/map.d.ts.map +1 -0
  9. package/dest/deprecated/indexeddb/multi_map.d.ts +14 -0
  10. package/dest/deprecated/indexeddb/multi_map.d.ts.map +1 -0
  11. package/dest/deprecated/indexeddb/set.d.ts +17 -0
  12. package/dest/deprecated/indexeddb/set.d.ts.map +1 -0
  13. package/dest/deprecated/indexeddb/singleton.d.ts +17 -0
  14. package/dest/deprecated/indexeddb/singleton.d.ts.map +1 -0
  15. package/dest/deprecated/indexeddb/store.d.ts +98 -0
  16. package/dest/deprecated/indexeddb/store.d.ts.map +1 -0
  17. package/dest/{indexeddb → deprecated/indexeddb}/store.js +2 -0
  18. package/dest/lmdb-v2/factory.d.ts +3 -2
  19. package/dest/lmdb-v2/factory.d.ts.map +1 -1
  20. package/dest/lmdb-v2/factory.js +2 -1
  21. package/dest/sqlite-opfs/errors.d.ts +28 -1
  22. package/dest/sqlite-opfs/errors.d.ts.map +1 -1
  23. package/dest/sqlite-opfs/errors.js +43 -0
  24. package/dest/sqlite-opfs/index.d.ts +3 -5
  25. package/dest/sqlite-opfs/index.d.ts.map +1 -1
  26. package/dest/sqlite-opfs/index.js +2 -8
  27. package/dest/sqlite-opfs/manage.d.ts +22 -0
  28. package/dest/sqlite-opfs/manage.d.ts.map +1 -0
  29. package/dest/sqlite-opfs/manage.js +38 -0
  30. package/dest/sqlite-opfs/pool_integrity.d.ts +20 -0
  31. package/dest/sqlite-opfs/pool_integrity.d.ts.map +1 -0
  32. package/dest/sqlite-opfs/pool_integrity.js +282 -0
  33. package/dest/sqlite-opfs/pool_lock.d.ts +8 -0
  34. package/dest/sqlite-opfs/pool_lock.d.ts.map +1 -0
  35. package/dest/sqlite-opfs/pool_lock.js +47 -0
  36. package/dest/sqlite-opfs/store.d.ts +6 -1
  37. package/dest/sqlite-opfs/store.d.ts.map +1 -1
  38. package/dest/sqlite-opfs/store.js +69 -33
  39. package/dest/sqlite-opfs/worker.js +39 -13
  40. package/dest/stores/l2_tips_store.d.ts +6 -12
  41. package/dest/stores/l2_tips_store.d.ts.map +1 -1
  42. package/dest/stores/l2_tips_store.js +22 -41
  43. package/package.json +8 -8
  44. package/src/{indexeddb → deprecated/indexeddb}/array.ts +2 -2
  45. package/src/deprecated/indexeddb/index.ts +12 -0
  46. package/src/{indexeddb → deprecated/indexeddb}/map.ts +2 -2
  47. package/src/{indexeddb → deprecated/indexeddb}/multi_map.ts +2 -2
  48. package/src/{indexeddb → deprecated/indexeddb}/set.ts +2 -2
  49. package/src/{indexeddb → deprecated/indexeddb}/singleton.ts +2 -2
  50. package/src/{indexeddb → deprecated/indexeddb}/store.ts +10 -9
  51. package/src/lmdb-v2/factory.ts +7 -1
  52. package/src/sqlite-opfs/errors.ts +50 -0
  53. package/src/sqlite-opfs/index.ts +8 -20
  54. package/src/sqlite-opfs/manage.ts +46 -0
  55. package/src/sqlite-opfs/pool_integrity.ts +327 -0
  56. package/src/sqlite-opfs/pool_lock.ts +62 -0
  57. package/src/sqlite-opfs/store.ts +57 -23
  58. package/src/sqlite-opfs/worker.ts +41 -13
  59. package/src/stores/l2_tips_store.ts +18 -40
  60. package/dest/indexeddb/array.d.ts +0 -22
  61. package/dest/indexeddb/array.d.ts.map +0 -1
  62. package/dest/indexeddb/index.d.ts +0 -7
  63. package/dest/indexeddb/index.d.ts.map +0 -1
  64. package/dest/indexeddb/index.js +0 -16
  65. package/dest/indexeddb/map.d.ts +0 -38
  66. package/dest/indexeddb/map.d.ts.map +0 -1
  67. package/dest/indexeddb/multi_map.d.ts +0 -14
  68. package/dest/indexeddb/multi_map.d.ts.map +0 -1
  69. package/dest/indexeddb/set.d.ts +0 -17
  70. package/dest/indexeddb/set.d.ts.map +0 -1
  71. package/dest/indexeddb/singleton.d.ts +0 -17
  72. package/dest/indexeddb/singleton.d.ts.map +0 -1
  73. package/dest/indexeddb/store.d.ts +0 -96
  74. package/dest/indexeddb/store.d.ts.map +0 -1
  75. package/src/indexeddb/index.ts +0 -31
  76. /package/dest/{indexeddb → deprecated/indexeddb}/array.js +0 -0
  77. /package/dest/{indexeddb → deprecated/indexeddb}/map.js +0 -0
  78. /package/dest/{indexeddb → deprecated/indexeddb}/multi_map.js +0 -0
  79. /package/dest/{indexeddb → deprecated/indexeddb}/set.js +0 -0
  80. /package/dest/{indexeddb → deprecated/indexeddb}/singleton.js +0 -0
@@ -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;
@@ -23,8 +23,7 @@ let pool;
23
23
  let db;
24
24
  let dbPath;
25
25
  async function ensurePool(directory) {
26
- sqlite3 ??= await sqlite3InitModule();
27
- const s = sqlite3;
26
+ const s = sqlite3 ??= await sqlite3InitModule();
28
27
  if (!pool) {
29
28
  pool = await s.installOpfsSAHPoolVfs({
30
29
  name: SAH_POOL_VFS_NAME,
@@ -51,33 +50,48 @@ async function ensurePool(directory) {
51
50
  key.fill(0);
52
51
  }
53
52
  async function handleInit(dbName, ephemeral, directory, encryptionKey) {
54
- sqlite3 ??= await sqlite3InitModule();
55
- const s = sqlite3;
53
+ const s = sqlite3 ??= await sqlite3InitModule();
56
54
  if (encryptionKey !== undefined && ephemeral) {
57
55
  throw new SqliteEncryptionError('encryption_not_supported_for_ephemeral', 'encryptionKey is not supported for ephemeral (:memory:) stores');
58
56
  }
59
57
  if (ephemeral) {
60
58
  db = new s.oo1.DB(':memory:', 'c');
61
59
  } else {
62
- await ensurePool(directory ?? DEFAULT_SAH_POOL_DIRECTORY);
60
+ const activePool = await ensurePool(directory ?? DEFAULT_SAH_POOL_DIRECTORY);
63
61
  dbPath = normalizeDbPath(dbName);
64
62
  if (encryptionKey !== undefined) {
65
- db = new s.oo1.DB({
63
+ const conn = new s.oo1.DB({
66
64
  filename: dbPath,
67
65
  flags: 'c',
68
66
  vfs: MC_SAH_POOL_VFS_NAME
69
67
  });
70
- applyEncryptionKey(db, encryptionKey);
68
+ db = conn;
69
+ applyEncryptionKey(conn, encryptionKey);
71
70
  } else {
72
- db = new pool.OpfsSAHPoolDb(dbPath);
71
+ db = new activePool.OpfsSAHPoolDb(dbPath);
73
72
  }
74
73
  }
75
74
  runSql(SCHEMA_SQL);
76
75
  }
77
76
  function handleClose() {
78
- db?.close();
79
- db = undefined;
80
- 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;
81
95
  }
82
96
  async function handleExport() {
83
97
  if (!db || !dbPath) {
@@ -108,6 +122,10 @@ function handleDeleteDb(dbName) {
108
122
  } catch {
109
123
  // File may not exist; ignore.
110
124
  }
125
+ // Guarded because pauseVfs refuses (SQLITE_MISUSE) while any file is open through the VFS.
126
+ if (!db) {
127
+ releasePool();
128
+ }
111
129
  }
112
130
  function requireDb() {
113
131
  if (!db) {
@@ -217,11 +235,19 @@ self.onmessage = async (ev)=>{
217
235
  }
218
236
  } catch (err) {
219
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
+ }
220
246
  respond({
221
247
  type: 'err',
222
248
  id: req.id,
223
249
  message,
224
- encryptionCode: detectEncryptionCode(req, err, message)
250
+ encryptionCode
225
251
  });
226
252
  }
227
253
  };
@@ -1,6 +1,5 @@
1
- import { BlockNumber, CheckpointNumber } from '@aztec/foundation/branded-types';
2
- import { type BlockHash, type L2BlockTag, L2TipsStoreBase } from '@aztec/stdlib/block';
3
- import { PublishedCheckpoint } from '@aztec/stdlib/checkpoint';
1
+ import { BlockNumber } from '@aztec/foundation/branded-types';
2
+ import { type BlockHash, type CheckpointId, type L2BlockTag, L2TipsStoreBase } from '@aztec/stdlib/block';
4
3
  import type { AztecAsyncKVStore } from '../interfaces/store.js';
5
4
  /**
6
5
  * Persistent implementation of L2 tips store backed by a KV store.
@@ -9,21 +8,16 @@ import type { AztecAsyncKVStore } from '../interfaces/store.js';
9
8
  export declare class L2TipsKVStore extends L2TipsStoreBase {
10
9
  private store;
11
10
  private readonly l2TipsStore;
11
+ private readonly l2TipCheckpointsStore;
12
12
  private readonly l2BlockHashesStore;
13
- private readonly l2BlockNumberToCheckpointNumberStore;
14
- private readonly l2CheckpointStore;
15
13
  constructor(store: AztecAsyncKVStore, namespace: string, initialBlockHash: BlockHash);
16
14
  protected getTip(tag: L2BlockTag): Promise<BlockNumber | undefined>;
17
15
  protected setTip(tag: L2BlockTag, blockNumber: BlockNumber): Promise<void>;
16
+ protected getTipCheckpoint(tag: L2BlockTag): Promise<CheckpointId | undefined>;
17
+ protected setTipCheckpoint(tag: L2BlockTag, checkpoint: CheckpointId): Promise<void>;
18
18
  protected getStoredBlockHash(blockNumber: BlockNumber): Promise<string | undefined>;
19
19
  protected setBlockHash(blockNumber: BlockNumber, hash: string): Promise<void>;
20
20
  protected deleteBlockHashesBefore(blockNumber: BlockNumber): Promise<void>;
21
- protected getCheckpointNumberForBlock(blockNumber: BlockNumber): Promise<CheckpointNumber | undefined>;
22
- protected setCheckpointNumberForBlock(blockNumber: BlockNumber, checkpointNumber: CheckpointNumber): Promise<void>;
23
- protected deleteBlockToCheckpointBefore(blockNumber: BlockNumber): Promise<void>;
24
- protected getCheckpoint(checkpointNumber: CheckpointNumber): Promise<PublishedCheckpoint | undefined>;
25
- protected saveCheckpointData(checkpoint: PublishedCheckpoint): Promise<void>;
26
- protected deleteCheckpointsBefore(checkpointNumber: CheckpointNumber): Promise<void>;
27
21
  protected runInTransaction<T>(fn: () => Promise<T>): Promise<T>;
28
22
  }
29
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibDJfdGlwc19zdG9yZS5kLnRzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL3N0b3Jlcy9sMl90aXBzX3N0b3JlLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFBRSxXQUFXLEVBQUUsZ0JBQWdCLEVBQUUsTUFBTSxpQ0FBaUMsQ0FBQztBQUNoRixPQUFPLEVBQUUsS0FBSyxTQUFTLEVBQUUsS0FBSyxVQUFVLEVBQUUsZUFBZSxFQUFFLE1BQU0scUJBQXFCLENBQUM7QUFDdkYsT0FBTyxFQUFFLG1CQUFtQixFQUFFLE1BQU0sMEJBQTBCLENBQUM7QUFHL0QsT0FBTyxLQUFLLEVBQUUsaUJBQWlCLEVBQUUsTUFBTSx3QkFBd0IsQ0FBQztBQUVoRTs7O0dBR0c7QUFDSCxxQkFBYSxhQUFjLFNBQVEsZUFBZTtJQU85QyxPQUFPLENBQUMsS0FBSztJQU5mLE9BQU8sQ0FBQyxRQUFRLENBQUMsV0FBVyxDQUF5QztJQUNyRSxPQUFPLENBQUMsUUFBUSxDQUFDLGtCQUFrQixDQUFxQztJQUN4RSxPQUFPLENBQUMsUUFBUSxDQUFDLG9DQUFvQyxDQUErQztJQUNwRyxPQUFPLENBQUMsUUFBUSxDQUFDLGlCQUFpQixDQUEwQztJQUU1RSxZQUNVLEtBQUssRUFBRSxpQkFBaUIsRUFDaEMsU0FBUyxFQUFFLE1BQU0sRUFDakIsZ0JBQWdCLEVBQUUsU0FBUyxFQVM1QjtJQUVELFNBQVMsQ0FBQyxNQUFNLENBQUMsR0FBRyxFQUFFLFVBQVUsR0FBRyxPQUFPLENBQUMsV0FBVyxHQUFHLFNBQVMsQ0FBQyxDQUVsRTtJQUVELFNBQVMsQ0FBQyxNQUFNLENBQUMsR0FBRyxFQUFFLFVBQVUsRUFBRSxXQUFXLEVBQUUsV0FBVyxHQUFHLE9BQU8sQ0FBQyxJQUFJLENBQUMsQ0FFekU7SUFFRCxTQUFTLENBQUMsa0JBQWtCLENBQUMsV0FBVyxFQUFFLFdBQVcsR0FBRyxPQUFPLENBQUMsTUFBTSxHQUFHLFNBQVMsQ0FBQyxDQUVsRjtJQUVELFNBQVMsQ0FBQyxZQUFZLENBQUMsV0FBVyxFQUFFLFdBQVcsRUFBRSxJQUFJLEVBQUUsTUFBTSxHQUFHLE9BQU8sQ0FBQyxJQUFJLENBQUMsQ0FFNUU7SUFFRCxVQUFnQix1QkFBdUIsQ0FBQyxXQUFXLEVBQUUsV0FBVyxHQUFHLE9BQU8sQ0FBQyxJQUFJLENBQUMsQ0FJL0U7SUFFRCxTQUFTLENBQUMsMkJBQTJCLENBQUMsV0FBVyxFQUFFLFdBQVcsR0FBRyxPQUFPLENBQUMsZ0JBQWdCLEdBQUcsU0FBUyxDQUFDLENBRXJHO0lBRUQsU0FBUyxDQUFDLDJCQUEyQixDQUFDLFdBQVcsRUFBRSxXQUFXLEVBQUUsZ0JBQWdCLEVBQUUsZ0JBQWdCLEdBQUcsT0FBTyxDQUFDLElBQUksQ0FBQyxDQUVqSDtJQUVELFVBQWdCLDZCQUE2QixDQUFDLFdBQVcsRUFBRSxXQUFXLEdBQUcsT0FBTyxDQUFDLElBQUksQ0FBQyxDQUlyRjtJQUVELFVBQWdCLGFBQWEsQ0FBQyxnQkFBZ0IsRUFBRSxnQkFBZ0IsR0FBRyxPQUFPLENBQUMsbUJBQW1CLEdBQUcsU0FBUyxDQUFDLENBTTFHO0lBRUQsU0FBUyxDQUFDLGtCQUFrQixDQUFDLFVBQVUsRUFBRSxtQkFBbUIsR0FBRyxPQUFPLENBQUMsSUFBSSxDQUFDLENBRTNFO0lBRUQsVUFBZ0IsdUJBQXVCLENBQUMsZ0JBQWdCLEVBQUUsZ0JBQWdCLEdBQUcsT0FBTyxDQUFDLElBQUksQ0FBQyxDQUl6RjtJQUVELFNBQVMsQ0FBQyxnQkFBZ0IsQ0FBQyxDQUFDLEVBQUUsRUFBRSxFQUFFLE1BQU0sT0FBTyxDQUFDLENBQUMsQ0FBQyxHQUFHLE9BQU8sQ0FBQyxDQUFDLENBQUMsQ0FFOUQ7Q0FDRiJ9
23
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibDJfdGlwc19zdG9yZS5kLnRzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL3N0b3Jlcy9sMl90aXBzX3N0b3JlLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFBRSxXQUFXLEVBQW9CLE1BQU0saUNBQWlDLENBQUM7QUFDaEYsT0FBTyxFQUFFLEtBQUssU0FBUyxFQUFFLEtBQUssWUFBWSxFQUFFLEtBQUssVUFBVSxFQUFFLGVBQWUsRUFBRSxNQUFNLHFCQUFxQixDQUFDO0FBRzFHLE9BQU8sS0FBSyxFQUFFLGlCQUFpQixFQUFFLE1BQU0sd0JBQXdCLENBQUM7QUFLaEU7OztHQUdHO0FBQ0gscUJBQWEsYUFBYyxTQUFRLGVBQWU7SUFNOUMsT0FBTyxDQUFDLEtBQUs7SUFMZixPQUFPLENBQUMsUUFBUSxDQUFDLFdBQVcsQ0FBeUM7SUFDckUsT0FBTyxDQUFDLFFBQVEsQ0FBQyxxQkFBcUIsQ0FBZ0Q7SUFDdEYsT0FBTyxDQUFDLFFBQVEsQ0FBQyxrQkFBa0IsQ0FBcUM7SUFFeEUsWUFDVSxLQUFLLEVBQUUsaUJBQWlCLEVBQ2hDLFNBQVMsRUFBRSxNQUFNLEVBQ2pCLGdCQUFnQixFQUFFLFNBQVMsRUFNNUI7SUFFRCxTQUFTLENBQUMsTUFBTSxDQUFDLEdBQUcsRUFBRSxVQUFVLEdBQUcsT0FBTyxDQUFDLFdBQVcsR0FBRyxTQUFTLENBQUMsQ0FFbEU7SUFFRCxTQUFTLENBQUMsTUFBTSxDQUFDLEdBQUcsRUFBRSxVQUFVLEVBQUUsV0FBVyxFQUFFLFdBQVcsR0FBRyxPQUFPLENBQUMsSUFBSSxDQUFDLENBRXpFO0lBRUQsVUFBZ0IsZ0JBQWdCLENBQUMsR0FBRyxFQUFFLFVBQVUsR0FBRyxPQUFPLENBQUMsWUFBWSxHQUFHLFNBQVMsQ0FBQyxDQU1uRjtJQUVELFNBQVMsQ0FBQyxnQkFBZ0IsQ0FBQyxHQUFHLEVBQUUsVUFBVSxFQUFFLFVBQVUsRUFBRSxZQUFZLEdBQUcsT0FBTyxDQUFDLElBQUksQ0FBQyxDQUVuRjtJQUVELFNBQVMsQ0FBQyxrQkFBa0IsQ0FBQyxXQUFXLEVBQUUsV0FBVyxHQUFHLE9BQU8sQ0FBQyxNQUFNLEdBQUcsU0FBUyxDQUFDLENBRWxGO0lBRUQsU0FBUyxDQUFDLFlBQVksQ0FBQyxXQUFXLEVBQUUsV0FBVyxFQUFFLElBQUksRUFBRSxNQUFNLEdBQUcsT0FBTyxDQUFDLElBQUksQ0FBQyxDQUU1RTtJQUVELFVBQWdCLHVCQUF1QixDQUFDLFdBQVcsRUFBRSxXQUFXLEdBQUcsT0FBTyxDQUFDLElBQUksQ0FBQyxDQUkvRTtJQUVELFNBQVMsQ0FBQyxnQkFBZ0IsQ0FBQyxDQUFDLEVBQUUsRUFBRSxFQUFFLE1BQU0sT0FBTyxDQUFDLENBQUMsQ0FBQyxHQUFHLE9BQU8sQ0FBQyxDQUFDLENBQUMsQ0FFOUQ7Q0FDRiJ9
@@ -1 +1 @@
1
- {"version":3,"file":"l2_tips_store.d.ts","sourceRoot":"","sources":["../../src/stores/l2_tips_store.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,gBAAgB,EAAE,MAAM,iCAAiC,CAAC;AAChF,OAAO,EAAE,KAAK,SAAS,EAAE,KAAK,UAAU,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AACvF,OAAO,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAC;AAG/D,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAEhE;;;GAGG;AACH,qBAAa,aAAc,SAAQ,eAAe;IAO9C,OAAO,CAAC,KAAK;IANf,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAyC;IACrE,OAAO,CAAC,QAAQ,CAAC,kBAAkB,CAAqC;IACxE,OAAO,CAAC,QAAQ,CAAC,oCAAoC,CAA+C;IACpG,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAA0C;IAE5E,YACU,KAAK,EAAE,iBAAiB,EAChC,SAAS,EAAE,MAAM,EACjB,gBAAgB,EAAE,SAAS,EAS5B;IAED,SAAS,CAAC,MAAM,CAAC,GAAG,EAAE,UAAU,GAAG,OAAO,CAAC,WAAW,GAAG,SAAS,CAAC,CAElE;IAED,SAAS,CAAC,MAAM,CAAC,GAAG,EAAE,UAAU,EAAE,WAAW,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CAEzE;IAED,SAAS,CAAC,kBAAkB,CAAC,WAAW,EAAE,WAAW,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAElF;IAED,SAAS,CAAC,YAAY,CAAC,WAAW,EAAE,WAAW,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAE5E;IAED,UAAgB,uBAAuB,CAAC,WAAW,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CAI/E;IAED,SAAS,CAAC,2BAA2B,CAAC,WAAW,EAAE,WAAW,GAAG,OAAO,CAAC,gBAAgB,GAAG,SAAS,CAAC,CAErG;IAED,SAAS,CAAC,2BAA2B,CAAC,WAAW,EAAE,WAAW,EAAE,gBAAgB,EAAE,gBAAgB,GAAG,OAAO,CAAC,IAAI,CAAC,CAEjH;IAED,UAAgB,6BAA6B,CAAC,WAAW,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CAIrF;IAED,UAAgB,aAAa,CAAC,gBAAgB,EAAE,gBAAgB,GAAG,OAAO,CAAC,mBAAmB,GAAG,SAAS,CAAC,CAM1G;IAED,SAAS,CAAC,kBAAkB,CAAC,UAAU,EAAE,mBAAmB,GAAG,OAAO,CAAC,IAAI,CAAC,CAE3E;IAED,UAAgB,uBAAuB,CAAC,gBAAgB,EAAE,gBAAgB,GAAG,OAAO,CAAC,IAAI,CAAC,CAIzF;IAED,SAAS,CAAC,gBAAgB,CAAC,CAAC,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAE9D;CACF"}
1
+ {"version":3,"file":"l2_tips_store.d.ts","sourceRoot":"","sources":["../../src/stores/l2_tips_store.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAoB,MAAM,iCAAiC,CAAC;AAChF,OAAO,EAAE,KAAK,SAAS,EAAE,KAAK,YAAY,EAAE,KAAK,UAAU,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAG1G,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAKhE;;;GAGG;AACH,qBAAa,aAAc,SAAQ,eAAe;IAM9C,OAAO,CAAC,KAAK;IALf,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAyC;IACrE,OAAO,CAAC,QAAQ,CAAC,qBAAqB,CAAgD;IACtF,OAAO,CAAC,QAAQ,CAAC,kBAAkB,CAAqC;IAExE,YACU,KAAK,EAAE,iBAAiB,EAChC,SAAS,EAAE,MAAM,EACjB,gBAAgB,EAAE,SAAS,EAM5B;IAED,SAAS,CAAC,MAAM,CAAC,GAAG,EAAE,UAAU,GAAG,OAAO,CAAC,WAAW,GAAG,SAAS,CAAC,CAElE;IAED,SAAS,CAAC,MAAM,CAAC,GAAG,EAAE,UAAU,EAAE,WAAW,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CAEzE;IAED,UAAgB,gBAAgB,CAAC,GAAG,EAAE,UAAU,GAAG,OAAO,CAAC,YAAY,GAAG,SAAS,CAAC,CAMnF;IAED,SAAS,CAAC,gBAAgB,CAAC,GAAG,EAAE,UAAU,EAAE,UAAU,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CAEnF;IAED,SAAS,CAAC,kBAAkB,CAAC,WAAW,EAAE,WAAW,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAElF;IAED,SAAS,CAAC,YAAY,CAAC,WAAW,EAAE,WAAW,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAE5E;IAED,UAAgB,uBAAuB,CAAC,WAAW,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CAI/E;IAED,SAAS,CAAC,gBAAgB,CAAC,CAAC,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAE9D;CACF"}
@@ -1,31 +1,26 @@
1
+ import { CheckpointNumber } from '@aztec/foundation/branded-types';
1
2
  import { L2TipsStoreBase } from '@aztec/stdlib/block';
2
- import { PublishedCheckpoint } from '@aztec/stdlib/checkpoint';
3
3
  /**
4
4
  * Persistent implementation of L2 tips store backed by a KV store.
5
5
  * Used by nodes that need to persist chain state across restarts.
6
6
  */ export class L2TipsKVStore extends L2TipsStoreBase {
7
7
  store;
8
8
  l2TipsStore;
9
+ l2TipCheckpointsStore;
9
10
  l2BlockHashesStore;
10
- l2BlockNumberToCheckpointNumberStore;
11
- l2CheckpointStore;
12
11
  constructor(store, namespace, initialBlockHash){
13
12
  super(initialBlockHash), this.store = store;
14
13
  this.l2TipsStore = store.openMap([
15
14
  namespace,
16
15
  'l2_tips'
17
16
  ].join('_'));
18
- this.l2BlockHashesStore = store.openMap([
19
- namespace,
20
- 'l2_block_hashes'
21
- ].join('_'));
22
- this.l2BlockNumberToCheckpointNumberStore = store.openMap([
17
+ this.l2TipCheckpointsStore = store.openMap([
23
18
  namespace,
24
- 'l2_block_number_to_checkpoint_number'
19
+ 'l2_tip_checkpoints'
25
20
  ].join('_'));
26
- this.l2CheckpointStore = store.openMap([
21
+ this.l2BlockHashesStore = store.openMap([
27
22
  namespace,
28
- 'l2_checkpoint_store'
23
+ 'l2_block_hashes'
29
24
  ].join('_'));
30
25
  }
31
26
  getTip(tag) {
@@ -34,6 +29,22 @@ import { PublishedCheckpoint } from '@aztec/stdlib/checkpoint';
34
29
  setTip(tag, blockNumber) {
35
30
  return this.l2TipsStore.set(tag, blockNumber);
36
31
  }
32
+ async getTipCheckpoint(tag) {
33
+ const stored = await this.l2TipCheckpointsStore.getAsync(tag);
34
+ if (stored === undefined) {
35
+ return undefined;
36
+ }
37
+ return {
38
+ number: CheckpointNumber(stored.number),
39
+ hash: stored.hash
40
+ };
41
+ }
42
+ setTipCheckpoint(tag, checkpoint) {
43
+ return this.l2TipCheckpointsStore.set(tag, {
44
+ number: checkpoint.number,
45
+ hash: checkpoint.hash
46
+ });
47
+ }
37
48
  getStoredBlockHash(blockNumber) {
38
49
  return this.l2BlockHashesStore.getAsync(blockNumber);
39
50
  }
@@ -47,36 +58,6 @@ import { PublishedCheckpoint } from '@aztec/stdlib/checkpoint';
47
58
  await this.l2BlockHashesStore.delete(key);
48
59
  }
49
60
  }
50
- getCheckpointNumberForBlock(blockNumber) {
51
- return this.l2BlockNumberToCheckpointNumberStore.getAsync(blockNumber);
52
- }
53
- setCheckpointNumberForBlock(blockNumber, checkpointNumber) {
54
- return this.l2BlockNumberToCheckpointNumberStore.set(blockNumber, checkpointNumber);
55
- }
56
- async deleteBlockToCheckpointBefore(blockNumber) {
57
- for await (const key of this.l2BlockNumberToCheckpointNumberStore.keysAsync({
58
- end: blockNumber
59
- })){
60
- await this.l2BlockNumberToCheckpointNumberStore.delete(key);
61
- }
62
- }
63
- async getCheckpoint(checkpointNumber) {
64
- const buffer = await this.l2CheckpointStore.getAsync(checkpointNumber);
65
- if (!buffer) {
66
- return undefined;
67
- }
68
- return PublishedCheckpoint.fromBuffer(buffer);
69
- }
70
- saveCheckpointData(checkpoint) {
71
- return this.l2CheckpointStore.set(checkpoint.checkpoint.number, checkpoint.toBuffer());
72
- }
73
- async deleteCheckpointsBefore(checkpointNumber) {
74
- for await (const key of this.l2CheckpointStore.keysAsync({
75
- end: checkpointNumber
76
- })){
77
- await this.l2CheckpointStore.delete(key);
78
- }
79
- }
80
61
  runInTransaction(fn) {
81
62
  return this.store.transactionAsync(fn);
82
63
  }
package/package.json CHANGED
@@ -1,13 +1,13 @@
1
1
  {
2
2
  "name": "@aztec/kv-store",
3
- "version": "0.0.1-commit.b9865e97",
3
+ "version": "0.0.1-commit.be03c316",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  ".": "./dest/interfaces/index.js",
7
7
  "./interfaces": "./dest/interfaces/index.js",
8
8
  "./lmdb": "./dest/lmdb/index.js",
9
9
  "./lmdb-v2": "./dest/lmdb-v2/index.js",
10
- "./indexeddb": "./dest/indexeddb/index.js",
10
+ "./deprecated/indexeddb": "./dest/deprecated/indexeddb/index.js",
11
11
  "./sqlite-opfs": "./dest/sqlite-opfs/index.js",
12
12
  "./stores": "./dest/stores/index.js"
13
13
  },
@@ -36,12 +36,12 @@
36
36
  "./package.local.json"
37
37
  ],
38
38
  "dependencies": {
39
- "@aztec/constants": "0.0.1-commit.b9865e97",
40
- "@aztec/ethereum": "0.0.1-commit.b9865e97",
41
- "@aztec/foundation": "0.0.1-commit.b9865e97",
42
- "@aztec/native": "0.0.1-commit.b9865e97",
43
- "@aztec/sqlite3mc-wasm": "0.0.1-commit.b9865e97",
44
- "@aztec/stdlib": "0.0.1-commit.b9865e97",
39
+ "@aztec/constants": "0.0.1-commit.be03c316",
40
+ "@aztec/ethereum": "0.0.1-commit.be03c316",
41
+ "@aztec/foundation": "0.0.1-commit.be03c316",
42
+ "@aztec/native": "0.0.1-commit.be03c316",
43
+ "@aztec/sqlite3mc-wasm": "0.0.1-commit.be03c316",
44
+ "@aztec/stdlib": "0.0.1-commit.be03c316",
45
45
  "idb": "^8.0.0",
46
46
  "lmdb": "^3.2.0",
47
47
  "msgpackr": "^1.11.2",
@@ -1,8 +1,8 @@
1
1
  import type { IDBPDatabase, IDBPObjectStore } from 'idb';
2
2
  import { hash } from 'ohash';
3
3
 
4
- import type { AztecAsyncArray } from '../interfaces/array.js';
5
- import type { Value } from '../interfaces/common.js';
4
+ import type { AztecAsyncArray } from '../../interfaces/array.js';
5
+ import type { Value } from '../../interfaces/common.js';
6
6
  import type { AztecIDBSchema } from './store.js';
7
7
 
8
8
  /**
@@ -0,0 +1,12 @@
1
+ import { createLogger } from '@aztec/foundation/log';
2
+
3
+ import { AztecIndexedDBStore } from './store.js';
4
+
5
+ export { AztecIndexedDBStore } from './store.js';
6
+
7
+ /**
8
+ * @deprecated The IndexedDB backend is being retired. Use `@aztec/kv-store/sqlite-opfs` instead.
9
+ */
10
+ export function openTmpStore(ephemeral: boolean = false): Promise<AztecIndexedDBStore> {
11
+ return AztecIndexedDBStore.open(createLogger('kv-store:indexeddb'), undefined, ephemeral);
12
+ }
@@ -1,8 +1,8 @@
1
1
  import type { IDBPDatabase, IDBPObjectStore } from 'idb';
2
2
  import { hash } from 'ohash';
3
3
 
4
- import type { Key, Range, Value } from '../interfaces/common.js';
5
- import type { AztecAsyncMap } from '../interfaces/map.js';
4
+ import type { Key, Range, Value } from '../../interfaces/common.js';
5
+ import type { AztecAsyncMap } from '../../interfaces/map.js';
6
6
  import type { AztecIDBSchema } from './store.js';
7
7
 
8
8
  /**
@@ -1,7 +1,7 @@
1
1
  import { hash } from 'ohash';
2
2
 
3
- import type { Key, Value } from '../interfaces/common.js';
4
- import type { AztecAsyncMultiMap } from '../interfaces/multi_map.js';
3
+ import type { Key, Value } from '../../interfaces/common.js';
4
+ import type { AztecAsyncMultiMap } from '../../interfaces/multi_map.js';
5
5
  import { IndexedDBAztecMap } from './map.js';
6
6
 
7
7
  /**
@@ -1,7 +1,7 @@
1
1
  import type { IDBPDatabase, IDBPObjectStore } from 'idb';
2
2
 
3
- import type { Key, Range } from '../interfaces/common.js';
4
- import type { AztecAsyncSet } from '../interfaces/set.js';
3
+ import type { Key, Range } from '../../interfaces/common.js';
4
+ import type { AztecAsyncSet } from '../../interfaces/set.js';
5
5
  import { IndexedDBAztecMap } from './map.js';
6
6
  import type { AztecIDBSchema } from './store.js';
7
7
 
@@ -1,8 +1,8 @@
1
1
  import type { IDBPDatabase, IDBPObjectStore } from 'idb';
2
2
  import { hash } from 'ohash';
3
3
 
4
- import type { Value } from '../interfaces/common.js';
5
- import type { AztecAsyncSingleton } from '../interfaces/singleton.js';
4
+ import type { Value } from '../../interfaces/common.js';
5
+ import type { AztecAsyncSingleton } from '../../interfaces/singleton.js';
6
6
  import type { AztecIDBSchema } from './store.js';
7
7
 
8
8
  /**
@@ -3,14 +3,14 @@ import { SerialQueue } from '@aztec/foundation/queue';
3
3
 
4
4
  import { type DBSchema, type IDBPDatabase, deleteDB, openDB } from 'idb';
5
5
 
6
- import type { AztecAsyncArray } from '../interfaces/array.js';
7
- import type { Key, StoreSize, Value } from '../interfaces/common.js';
8
- import type { AztecAsyncCounter } from '../interfaces/counter.js';
9
- import type { AztecAsyncMap } from '../interfaces/map.js';
10
- import type { AztecAsyncMultiMap } from '../interfaces/multi_map.js';
11
- import type { AztecAsyncSet } from '../interfaces/set.js';
12
- import type { AztecAsyncSingleton } from '../interfaces/singleton.js';
13
- import type { AztecAsyncKVStore } from '../interfaces/store.js';
6
+ import type { AztecAsyncArray } from '../../interfaces/array.js';
7
+ import type { Key, StoreSize, Value } from '../../interfaces/common.js';
8
+ import type { AztecAsyncCounter } from '../../interfaces/counter.js';
9
+ import type { AztecAsyncMap } from '../../interfaces/map.js';
10
+ import type { AztecAsyncMultiMap } from '../../interfaces/multi_map.js';
11
+ import type { AztecAsyncSet } from '../../interfaces/set.js';
12
+ import type { AztecAsyncSingleton } from '../../interfaces/singleton.js';
13
+ import type { AztecAsyncKVStore } from '../../interfaces/store.js';
14
14
  import { IndexedDBAztecArray } from './array.js';
15
15
  import { IndexedDBAztecMap } from './map.js';
16
16
  import { IndexedDBAztecMultiMap } from './multi_map.js';
@@ -41,8 +41,9 @@ export interface AztecIDBSchema extends DBSchema {
41
41
 
42
42
  /**
43
43
  * A key-value store backed by IndexedDB.
44
+ *
45
+ * @deprecated The IndexedDB backend is being retired. Use `@aztec/kv-store/sqlite-opfs` instead.
44
46
  */
45
-
46
47
  export class AztecIndexedDBStore implements AztecAsyncKVStore {
47
48
  #rootDB: IDBPDatabase<AztecIDBSchema>;
48
49
  #name: string;
@@ -1,6 +1,10 @@
1
1
  import { EthAddress } from '@aztec/foundation/eth-address';
2
2
  import { type LoggerBindings, createLogger } from '@aztec/foundation/log';
3
- import { DatabaseVersionManager, type SchemaVersionMismatchPolicy } from '@aztec/stdlib/database-version/manager';
3
+ import {
4
+ DatabaseVersionManager,
5
+ type SchemaVersionMismatchPolicy,
6
+ type VersionFileReadFailurePolicy,
7
+ } from '@aztec/stdlib/database-version/manager';
4
8
  import type { DataStoreConfig } from '@aztec/stdlib/kv-store';
5
9
 
6
10
  import { copyFile, mkdir, mkdtemp, rm } from 'fs/promises';
@@ -15,6 +19,7 @@ const MAX_READERS = 16;
15
19
  export type CreateStoreOptions = {
16
20
  onUpgrade?: (dataDir: string, currentVersion: number, latestVersion: number) => Promise<void>;
17
21
  schemaVersionMismatchPolicy?: SchemaVersionMismatchPolicy;
22
+ versionFileReadFailurePolicy?: VersionFileReadFailurePolicy;
18
23
  };
19
24
 
20
25
  export async function createStore(
@@ -44,6 +49,7 @@ export async function createStore(
44
49
  AztecLMDBStoreV2.new(dbDirectory, config.dataStoreMapSizeKb, MAX_READERS, () => Promise.resolve(), bindings),
45
50
  onUpgrade: options.onUpgrade,
46
51
  schemaVersionMismatchPolicy: options.schemaVersionMismatchPolicy,
52
+ versionFileReadFailurePolicy: options.versionFileReadFailurePolicy,
47
53
  });
48
54
 
49
55
  log.info(
@@ -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
+ }