@aztec/kv-store 0.86.0 → 0.87.0-nightly.20250521

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 (48) hide show
  1. package/dest/indexeddb/multi_map.d.ts.map +1 -1
  2. package/dest/indexeddb/multi_map.js +20 -6
  3. package/dest/indexeddb/store.d.ts +1 -1
  4. package/dest/indexeddb/store.d.ts.map +1 -1
  5. package/dest/indexeddb/store.js +9 -6
  6. package/dest/interfaces/common.d.ts +9 -8
  7. package/dest/interfaces/common.d.ts.map +1 -1
  8. package/dest/interfaces/common.js +8 -3
  9. package/dest/interfaces/index.d.ts +2 -1
  10. package/dest/interfaces/index.d.ts.map +1 -1
  11. package/dest/interfaces/index.js +1 -0
  12. package/dest/interfaces/map_test_suite.d.ts.map +1 -1
  13. package/dest/interfaces/map_test_suite.js +107 -52
  14. package/dest/interfaces/multi_map_test_suite.d.ts.map +1 -1
  15. package/dest/interfaces/multi_map_test_suite.js +66 -2
  16. package/dest/lmdb/map.d.ts +1 -3
  17. package/dest/lmdb/map.d.ts.map +1 -1
  18. package/dest/lmdb/store.d.ts.map +1 -1
  19. package/dest/lmdb/store.js +5 -0
  20. package/dest/lmdb-v2/array.d.ts.map +1 -1
  21. package/dest/lmdb-v2/map.d.ts.map +1 -1
  22. package/dest/lmdb-v2/map.js +3 -3
  23. package/dest/lmdb-v2/message.d.ts +1 -0
  24. package/dest/lmdb-v2/message.d.ts.map +1 -1
  25. package/dest/lmdb-v2/multi_map.d.ts.map +1 -1
  26. package/dest/lmdb-v2/singleton.d.ts.map +1 -1
  27. package/dest/lmdb-v2/store.d.ts.map +1 -1
  28. package/dest/lmdb-v2/store.js +1 -0
  29. package/dest/lmdb-v2/utils.d.ts +2 -4
  30. package/dest/lmdb-v2/utils.d.ts.map +1 -1
  31. package/dest/lmdb-v2/write_transaction.d.ts +2 -4
  32. package/dest/lmdb-v2/write_transaction.d.ts.map +1 -1
  33. package/package.json +9 -9
  34. package/src/indexeddb/multi_map.ts +17 -3
  35. package/src/indexeddb/store.ts +13 -7
  36. package/src/interfaces/common.ts +18 -9
  37. package/src/interfaces/index.ts +2 -1
  38. package/src/interfaces/map_test_suite.ts +25 -14
  39. package/src/interfaces/multi_map_test_suite.ts +63 -2
  40. package/src/lmdb/map.ts +5 -5
  41. package/src/lmdb/store.ts +10 -1
  42. package/src/lmdb-v2/array.ts +4 -1
  43. package/src/lmdb-v2/map.ts +9 -4
  44. package/src/lmdb-v2/message.ts +1 -0
  45. package/src/lmdb-v2/multi_map.ts +4 -1
  46. package/src/lmdb-v2/singleton.ts +4 -1
  47. package/src/lmdb-v2/store.ts +1 -0
  48. package/src/lmdb-v2/write_transaction.ts +2 -2
@@ -163,6 +163,7 @@ export class AztecLMDBStoreV2 {
163
163
  const resp = await this.sendMessage(LMDBMessageType.STATS, undefined);
164
164
  return {
165
165
  mappingSize: Number(resp.dbMapSizeBytes),
166
+ physicalFileSize: Number(resp.dbPhysicalFileSizeBytes),
166
167
  actualSize: resp.stats.reduce((s, db)=>Number(db.totalUsedSize) + s, 0),
167
168
  numItems: resp.stats.reduce((s, db)=>Number(db.numDataItems) + s, 0)
168
169
  };
@@ -1,5 +1,3 @@
1
- /// <reference types="node" resolution-mode="require"/>
2
- /// <reference types="node" resolution-mode="require"/>
3
1
  import type { Key } from '../interfaces/common.js';
4
2
  type Cmp<T> = (a: T, b: T) => -1 | 0 | 1;
5
3
  export declare function dedupeSortedArray<T>(arr: T[], cmp: Cmp<T>): void;
@@ -11,8 +9,8 @@ export declare function removeFromSortedArray<T, N>(arr: T[], val: N, cmp: (a: T
11
9
  export declare function merge<T>(arr: T[], toInsert: T[], cmp: (a: T, b: T) => -1 | 0 | 1): void;
12
10
  export declare function keyCmp(a: [Uint8Array, Uint8Array[] | null], b: [Uint8Array, Uint8Array[] | null]): -1 | 0 | 1;
13
11
  export declare function singleKeyCmp(a: [Uint8Array, Uint8Array[] | null], b: Uint8Array): -1 | 0 | 1;
14
- export declare function minKey(prefix: string): Buffer;
15
- export declare function maxKey(prefix: string): Buffer;
12
+ export declare function minKey(prefix: string): Buffer<ArrayBufferLike>;
13
+ export declare function maxKey(prefix: string): Buffer<ArrayBufferLike>;
16
14
  export declare function serializeKey(prefix: string, key: Key): Buffer;
17
15
  export declare function deserializeKey<K extends Key>(prefix: string, key: Uint8Array): K | false;
18
16
  export {};
@@ -1 +1 @@
1
- {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../src/lmdb-v2/utils.ts"],"names":[],"mappings":";;AAEA,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,yBAAyB,CAAC;AAEnD,KAAK,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;AAEzC,wBAAgB,iBAAiB,CAAC,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC,CAAC,GAAG,IAAI,CAkBhE;AAED,wBAAgB,qBAAqB,CAAC,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,EAAE,IAAI,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,KAAK,MAAM,GAAG,IAAI,CAgB7F;AAED,wBAAgB,sBAAsB,CAAC,CAAC,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,EAAE,MAAM,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,KAAK,MAAM,GAAG,MAAM,CAiBxG;AAED,wBAAgB,iBAAiB,CAAC,CAAC,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,EAAE,MAAM,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,KAAK,MAAM,GAAG,CAAC,GAAG,SAAS,CAG1G;AAED,wBAAgB,WAAW,CAAC,CAAC,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,EAAE,IAAI,EAAE,CAAC,EAAE,EAAE,GAAG,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,IAAI,CAsB5F;AAED,wBAAgB,qBAAqB,CAAC,CAAC,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,EAAE,GAAG,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,QAK5F;AAED,wBAAgB,KAAK,CAAC,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,EAAE,QAAQ,EAAE,CAAC,EAAE,EAAE,GAAG,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,IAAI,CAqBvF;AAED,wBAAgB,MAAM,CAAC,CAAC,EAAE,CAAC,UAAU,EAAE,UAAU,EAAE,GAAG,IAAI,CAAC,EAAE,CAAC,EAAE,CAAC,UAAU,EAAE,UAAU,EAAE,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAE7G;AAED,wBAAgB,YAAY,CAAC,CAAC,EAAE,CAAC,UAAU,EAAE,UAAU,EAAE,GAAG,IAAI,CAAC,EAAE,CAAC,EAAE,UAAU,GAAG,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAE5F;AAED,wBAAgB,MAAM,CAAC,MAAM,EAAE,MAAM,UAEpC;AAED,wBAAgB,MAAM,CAAC,MAAM,EAAE,MAAM,UAEpC;AAED,wBAAgB,YAAY,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,GAAG,GAAG,MAAM,CAE7D;AAED,wBAAgB,cAAc,CAAC,CAAC,SAAS,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,UAAU,GAAG,CAAC,GAAG,KAAK,CAOxF"}
1
+ {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../src/lmdb-v2/utils.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,yBAAyB,CAAC;AAEnD,KAAK,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;AAEzC,wBAAgB,iBAAiB,CAAC,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC,CAAC,GAAG,IAAI,CAkBhE;AAED,wBAAgB,qBAAqB,CAAC,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,EAAE,IAAI,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,KAAK,MAAM,GAAG,IAAI,CAgB7F;AAED,wBAAgB,sBAAsB,CAAC,CAAC,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,EAAE,MAAM,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,KAAK,MAAM,GAAG,MAAM,CAiBxG;AAED,wBAAgB,iBAAiB,CAAC,CAAC,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,EAAE,MAAM,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,KAAK,MAAM,GAAG,CAAC,GAAG,SAAS,CAG1G;AAED,wBAAgB,WAAW,CAAC,CAAC,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,EAAE,IAAI,EAAE,CAAC,EAAE,EAAE,GAAG,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,IAAI,CAsB5F;AAED,wBAAgB,qBAAqB,CAAC,CAAC,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,EAAE,GAAG,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,QAK5F;AAED,wBAAgB,KAAK,CAAC,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,EAAE,QAAQ,EAAE,CAAC,EAAE,EAAE,GAAG,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,IAAI,CAqBvF;AAED,wBAAgB,MAAM,CAAC,CAAC,EAAE,CAAC,UAAU,EAAE,UAAU,EAAE,GAAG,IAAI,CAAC,EAAE,CAAC,EAAE,CAAC,UAAU,EAAE,UAAU,EAAE,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAE7G;AAED,wBAAgB,YAAY,CAAC,CAAC,EAAE,CAAC,UAAU,EAAE,UAAU,EAAE,GAAG,IAAI,CAAC,EAAE,CAAC,EAAE,UAAU,GAAG,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAE5F;AAED,wBAAgB,MAAM,CAAC,MAAM,EAAE,MAAM,2BAEpC;AAED,wBAAgB,MAAM,CAAC,MAAM,EAAE,MAAM,2BAEpC;AAED,wBAAgB,YAAY,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,GAAG,GAAG,MAAM,CAE7D;AAED,wBAAgB,cAAc,CAAC,CAAC,SAAS,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,UAAU,GAAG,CAAC,GAAG,KAAK,CAOxF"}
@@ -1,5 +1,3 @@
1
- /// <reference types="node" resolution-mode="require"/>
2
- /// <reference types="node" resolution-mode="require"/>
3
1
  import { type Batch } from './message.js';
4
2
  import { ReadTransaction } from './read_transaction.js';
5
3
  export declare class WriteTransaction extends ReadTransaction {
@@ -12,8 +10,8 @@ export declare class WriteTransaction extends ReadTransaction {
12
10
  setIndex(key: Buffer, ...values: Buffer[]): Promise<void>;
13
11
  removeIndex(key: Buffer, ...values: Buffer[]): Promise<void>;
14
12
  getIndex(key: Buffer): Promise<Uint8Array[]>;
15
- iterate(startKey: Uint8Array, endKey?: Uint8Array | undefined, reverse?: boolean, limit?: number): AsyncIterable<[Uint8Array, Uint8Array]>;
16
- iterateIndex(startKey: Uint8Array, endKey?: Uint8Array | undefined, reverse?: boolean, limit?: number): AsyncIterable<[Uint8Array, Uint8Array[]]>;
13
+ iterate(startKey: Uint8Array, endKey?: Uint8Array, reverse?: boolean, limit?: number): AsyncIterable<[Uint8Array, Uint8Array]>;
14
+ iterateIndex(startKey: Uint8Array, endKey?: Uint8Array, reverse?: boolean, limit?: number): AsyncIterable<[Uint8Array, Uint8Array[]]>;
17
15
  commit(): Promise<void>;
18
16
  }
19
17
  //# sourceMappingURL=write_transaction.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"write_transaction.d.ts","sourceRoot":"","sources":["../../src/lmdb-v2/write_transaction.ts"],"names":[],"mappings":";;AAAA,OAAO,EAAE,KAAK,KAAK,EAA6B,MAAM,cAAc,CAAC;AACrE,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAaxD,qBAAa,gBAAiB,SAAQ,eAAe;;IAEnD,SAAgB,SAAS,EAAE,KAAK,CAG9B;IACF,SAAgB,UAAU,EAAE,KAAK,CAG/B;IAEF,GAAG,CAAC,GAAG,EAAE,UAAU,EAAE,KAAK,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC;IAkBtD,MAAM,CAAC,GAAG,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC;IAchB,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,GAAG,SAAS,CAAC;IAevE,QAAQ,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,MAAM,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IA6BzD,WAAW,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,MAAM,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IAuCtC,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,EAAE,CAAC;IAuB3C,OAAO,CAC5B,QAAQ,EAAE,UAAU,EACpB,MAAM,CAAC,EAAE,UAAU,GAAG,SAAS,EAC/B,OAAO,CAAC,EAAE,OAAO,EACjB,KAAK,CAAC,EAAE,MAAM,GACb,aAAa,CAAC,CAAC,UAAU,EAAE,UAAU,CAAC,CAAC;IAanB,YAAY,CACjC,QAAQ,EAAE,UAAU,EACpB,MAAM,CAAC,EAAE,UAAU,GAAG,SAAS,EAC/B,OAAO,CAAC,EAAE,OAAO,EACjB,KAAK,CAAC,EAAE,MAAM,GACb,aAAa,CAAC,CAAC,UAAU,EAAE,UAAU,EAAE,CAAC,CAAC;IAqH/B,MAAM;CAUpB"}
1
+ {"version":3,"file":"write_transaction.d.ts","sourceRoot":"","sources":["../../src/lmdb-v2/write_transaction.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,KAAK,EAA6B,MAAM,cAAc,CAAC;AACrE,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAaxD,qBAAa,gBAAiB,SAAQ,eAAe;;IAEnD,SAAgB,SAAS,EAAE,KAAK,CAG9B;IACF,SAAgB,UAAU,EAAE,KAAK,CAG/B;IAEF,GAAG,CAAC,GAAG,EAAE,UAAU,EAAE,KAAK,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC;IAkBtD,MAAM,CAAC,GAAG,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC;IAchB,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,GAAG,SAAS,CAAC;IAevE,QAAQ,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,MAAM,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IA6BzD,WAAW,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,MAAM,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IAuCtC,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,EAAE,CAAC;IAuB3C,OAAO,CAC5B,QAAQ,EAAE,UAAU,EACpB,MAAM,CAAC,EAAE,UAAU,EACnB,OAAO,CAAC,EAAE,OAAO,EACjB,KAAK,CAAC,EAAE,MAAM,GACb,aAAa,CAAC,CAAC,UAAU,EAAE,UAAU,CAAC,CAAC;IAanB,YAAY,CACjC,QAAQ,EAAE,UAAU,EACpB,MAAM,CAAC,EAAE,UAAU,EACnB,OAAO,CAAC,EAAE,OAAO,EACjB,KAAK,CAAC,EAAE,MAAM,GACb,aAAa,CAAC,CAAC,UAAU,EAAE,UAAU,EAAE,CAAC,CAAC;IAqH/B,MAAM;CAUpB"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aztec/kv-store",
3
- "version": "0.86.0",
3
+ "version": "0.87.0-nightly.20250521",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  ".": "./dest/interfaces/index.js",
@@ -16,17 +16,17 @@
16
16
  "clean": "rm -rf ./dest .tsbuildinfo",
17
17
  "test:node": "NODE_NO_WARNINGS=1 mocha --config ./.mocharc.json",
18
18
  "test:browser": "wtr --config ./web-test-runner.config.mjs",
19
- "test": "yarn test:node && yarn test:browser && true"
19
+ "test": "yarn test:node"
20
20
  },
21
21
  "inherits": [
22
22
  "../package.common.json",
23
23
  "./package.local.json"
24
24
  ],
25
25
  "dependencies": {
26
- "@aztec/ethereum": "0.86.0",
27
- "@aztec/foundation": "0.86.0",
28
- "@aztec/native": "0.86.0",
29
- "@aztec/stdlib": "0.86.0",
26
+ "@aztec/ethereum": "0.87.0-nightly.20250521",
27
+ "@aztec/foundation": "0.87.0-nightly.20250521",
28
+ "@aztec/native": "0.87.0-nightly.20250521",
29
+ "@aztec/stdlib": "0.87.0-nightly.20250521",
30
30
  "idb": "^8.0.0",
31
31
  "lmdb": "^3.2.0",
32
32
  "msgpackr": "^1.11.2",
@@ -40,7 +40,7 @@
40
40
  "@types/jest": "^29.5.0",
41
41
  "@types/mocha": "^10.0.10",
42
42
  "@types/mocha-each": "^2.0.4",
43
- "@types/node": "^18.7.23",
43
+ "@types/node": "^22.15.17",
44
44
  "@types/sinon": "^17.0.3",
45
45
  "@web/dev-server-esbuild": "^1.0.3",
46
46
  "@web/test-runner": "^0.19.0",
@@ -52,7 +52,7 @@
52
52
  "mocha-each": "^2.0.1",
53
53
  "sinon": "^19.0.2",
54
54
  "ts-node": "^10.9.1",
55
- "typescript": "^5.0.4"
55
+ "typescript": "^5.3.3"
56
56
  },
57
57
  "files": [
58
58
  "dest",
@@ -60,7 +60,7 @@
60
60
  "!*.test.*"
61
61
  ],
62
62
  "engines": {
63
- "node": ">=18"
63
+ "node": ">=20.10"
64
64
  },
65
65
  "jest": {
66
66
  "extensionsToTreatAsEsm": [
@@ -12,6 +12,7 @@ export class IndexedDBAztecMultiMap<K extends Key, V extends Value>
12
12
  implements AztecAsyncMultiMap<K, V>
13
13
  {
14
14
  override async set(key: K, val: V): Promise<void> {
15
+ // Inserting repeated values is a no-op
15
16
  const exists = !!(await this.db
16
17
  .index('hash')
17
18
  .get(
@@ -23,9 +24,19 @@ export class IndexedDBAztecMultiMap<K extends Key, V extends Value>
23
24
  if (exists) {
24
25
  return;
25
26
  }
26
- const count = await this.db
27
- .index('key')
28
- .count(IDBKeyRange.bound([this.container, this.normalizeKey(key)], [this.container, this.normalizeKey(key)]));
27
+ // Get the maximum keyCount for the given key
28
+ // In order to support sparse multimaps, we cannot rely
29
+ // on just counting the number of entries for the key, since we would repeat slots
30
+ // if we delete an entry
31
+ // set -> container:key:0 (keyCount = 1)
32
+ // set -> container:key:1 (keyCount = 2)
33
+ // delete -> container:key:0 (keyCount = 1)
34
+ // set -> container:key:1 <--- already exists!
35
+ // Instead, we iterate in reverse order to get the last inserted entry
36
+ const index = this.db.index('keyCount');
37
+ const rangeQuery = IDBKeyRange.upperBound([this.container, this.normalizeKey(key), Number.MAX_SAFE_INTEGER]);
38
+ const maxEntry = (await index.iterate(rangeQuery, 'prevunique').next()).value;
39
+ const count = maxEntry?.value?.keyCount ?? 0;
29
40
  await this.db.put({
30
41
  value: val,
31
42
  hash: hash(val),
@@ -37,6 +48,7 @@ export class IndexedDBAztecMultiMap<K extends Key, V extends Value>
37
48
  }
38
49
 
39
50
  async *getValuesAsync(key: K): AsyncIterableIterator<V> {
51
+ // Iterate over the whole range of keyCount for the given key
40
52
  const index = this.db.index('keyCount');
41
53
  const rangeQuery = IDBKeyRange.bound(
42
54
  [this.container, this.normalizeKey(key), 0],
@@ -50,6 +62,8 @@ export class IndexedDBAztecMultiMap<K extends Key, V extends Value>
50
62
  }
51
63
 
52
64
  async deleteValue(key: K, val: V): Promise<void> {
65
+ // Since we know the value, we can hash it and directly query the "hash" index
66
+ // to avoid having to iterate over all the values
53
67
  const fullKey = await this.db
54
68
  .index('hash')
55
69
  .getKey(
@@ -38,7 +38,6 @@ export interface AztecIDBSchema extends DBSchema {
38
38
  */
39
39
 
40
40
  export class AztecIndexedDBStore implements AztecAsyncKVStore {
41
- #log: Logger;
42
41
  #rootDB: IDBPDatabase<AztecIDBSchema>;
43
42
  #name: string;
44
43
 
@@ -50,9 +49,12 @@ export class AztecIndexedDBStore implements AztecAsyncKVStore {
50
49
  | IndexedDBAztecSingleton<any>
51
50
  >();
52
51
 
53
- constructor(rootDB: IDBPDatabase<AztecIDBSchema>, public readonly isEphemeral: boolean, log: Logger, name: string) {
52
+ constructor(
53
+ rootDB: IDBPDatabase<AztecIDBSchema>,
54
+ public readonly isEphemeral: boolean,
55
+ name: string,
56
+ ) {
54
57
  this.#rootDB = rootDB;
55
- this.#log = log;
56
58
  this.#name = name;
57
59
  }
58
60
  /**
@@ -66,19 +68,23 @@ export class AztecIndexedDBStore implements AztecAsyncKVStore {
66
68
  * @returns The store
67
69
  */
68
70
  static async open(log: Logger, name?: string, ephemeral: boolean = false): Promise<AztecIndexedDBStore> {
69
- name = name && !ephemeral ? name : self.crypto.getRandomValues(new Uint8Array(16)).join('');
71
+ name = name && !ephemeral ? name : globalThis.crypto.getRandomValues(new Uint8Array(16)).join('');
70
72
  log.debug(`Opening IndexedDB ${ephemeral ? 'temp ' : ''}database with name ${name}`);
71
73
  const rootDB = await openDB<AztecIDBSchema>(name, 1, {
72
74
  upgrade(db) {
73
75
  const objectStore = db.createObjectStore('data', { keyPath: 'slot' });
74
76
 
75
77
  objectStore.createIndex('key', ['container', 'key'], { unique: false });
76
- objectStore.createIndex('keyCount', ['container', 'key', 'keyCount'], { unique: false });
78
+ // Keep count of the maximum number of keys ever inserted in the container
79
+ // This allows unique slots for repeated keys, which is useful for multi-maps
80
+ objectStore.createIndex('keyCount', ['container', 'key', 'keyCount'], { unique: true });
81
+ // Keep an index on the pair key-hash for a given container, allowing us to efficiently
82
+ // delete unique values from multi-maps
77
83
  objectStore.createIndex('hash', ['container', 'key', 'hash'], { unique: true });
78
84
  },
79
85
  });
80
86
 
81
- const kvStore = new AztecIndexedDBStore(rootDB, ephemeral, log, name);
87
+ const kvStore = new AztecIndexedDBStore(rootDB, ephemeral, name);
82
88
  return kvStore;
83
89
  }
84
90
 
@@ -179,7 +185,7 @@ export class AztecIndexedDBStore implements AztecAsyncKVStore {
179
185
  }
180
186
 
181
187
  estimateSize(): Promise<StoreSize> {
182
- return Promise.resolve({ mappingSize: 0, actualSize: 0, numItems: 0 });
188
+ return Promise.resolve({ mappingSize: 0, physicalFileSize: 0, actualSize: 0, numItems: 0 });
183
189
  }
184
190
 
185
191
  close(): Promise<void> {
@@ -1,14 +1,10 @@
1
- /**
2
- * The key type for use with the kv-store
3
- */
4
- export type Key = string | number | Array<string | number>;
1
+ /** The key type for use with the kv-store */
2
+ export type Key = string | number | Uint8Array | Array<string | number>;
5
3
 
6
4
  export type Value = NonNullable<any>;
7
5
 
8
- /**
9
- * A range of keys to iterate over.
10
- */
11
- export type Range<K extends Key = Key> = {
6
+ /** A range of keys of arbitrary type. */
7
+ export type CustomRange<K> = {
12
8
  /** The key of the first item to include */
13
9
  start?: K;
14
10
  /** The key of the last item to include */
@@ -19,4 +15,17 @@ export type Range<K extends Key = Key> = {
19
15
  limit?: number;
20
16
  };
21
17
 
22
- export type StoreSize = { mappingSize: number; actualSize: number; numItems: number };
18
+ /** Maps a custom range into a range of valid key types to iterate over. */
19
+ export function mapRange<CK, K extends Key = Key>(range: CustomRange<CK>, mapFn: (key: CK) => K): Range<K> {
20
+ return {
21
+ start: range.start ? mapFn(range.start) : undefined,
22
+ end: range.end ? mapFn(range.end) : undefined,
23
+ reverse: range.reverse,
24
+ limit: range.limit,
25
+ };
26
+ }
27
+
28
+ /** A range of keys to iterate over. */
29
+ export type Range<K extends Key = Key> = CustomRange<K>;
30
+
31
+ export type StoreSize = { mappingSize: number; physicalFileSize: number; actualSize: number; numItems: number };
@@ -5,4 +5,5 @@ export * from './singleton.js';
5
5
  export * from './store.js';
6
6
  export * from './set.js';
7
7
  export * from './multi_map.js';
8
- export type { Range, StoreSize } from './common.js';
8
+ export { mapRange } from './common.js';
9
+ export type { CustomRange, Range, StoreSize } from './common.js';
@@ -110,19 +110,30 @@ export function describeAztecMap(
110
110
  expect(await keys()).to.deep.equal(['baz', 'foo']);
111
111
  });
112
112
 
113
- it('supports range queries', async () => {
114
- await map.set('a', 'a');
115
- await map.set('b', 'b');
116
- await map.set('c', 'c');
117
- await map.set('d', 'd');
118
-
119
- expect(await keys({ start: 'b', end: 'c' })).to.deep.equal(['b']);
120
- expect(await keys({ start: 'b' })).to.deep.equal(['b', 'c', 'd']);
121
- expect(await keys({ end: 'c' })).to.deep.equal(['a', 'b']);
122
- expect(await keys({ start: 'b', end: 'c', reverse: true })).to.deep.equal(['c']);
123
- expect(await keys({ start: 'b', limit: 1 })).to.deep.equal(['b']);
124
- expect(await keys({ start: 'b', reverse: true })).to.deep.equal(['d', 'c']);
125
- expect(await keys({ end: 'b', reverse: true })).to.deep.equal(['b', 'a']);
126
- });
113
+ for (const [name, data] of [
114
+ ['chars', ['a', 'b', 'c', 'd']],
115
+ ['numbers', [1, 2, 3, 4]],
116
+ ['negative numbers', [-4, -3, -2, -1]],
117
+ ['strings', ['aaa', 'bbb', 'ccc', 'ddd']],
118
+ ['zero-based numbers', [0, 1, 2, 3]],
119
+ ]) {
120
+ it(`supports range queries over ${name} keys`, async () => {
121
+ const [a, b, c, d] = data;
122
+
123
+ await map.set(a, 'a');
124
+ await map.set(b, 'b');
125
+ await map.set(c, 'c');
126
+ await map.set(d, 'd');
127
+
128
+ expect(await keys()).to.deep.equal([a, b, c, d]);
129
+ expect(await keys({ start: b, end: c })).to.deep.equal([b]);
130
+ expect(await keys({ start: b })).to.deep.equal([b, c, d]);
131
+ expect(await keys({ end: c })).to.deep.equal([a, b]);
132
+ expect(await keys({ start: b, end: c, reverse: true })).to.deep.equal([c]);
133
+ expect(await keys({ start: b, limit: 1 })).to.deep.equal([b]);
134
+ expect(await keys({ start: b, reverse: true })).to.deep.equal([d, c]);
135
+ expect(await keys({ end: b, reverse: true })).to.deep.equal([b, a]);
136
+ });
137
+ }
127
138
  });
128
139
  }
@@ -124,12 +124,73 @@ export function describeAztecMultiMap(
124
124
  });
125
125
 
126
126
  it('should be able to delete individual values for a single key', async () => {
127
+ await multiMap.set('foo', '1');
128
+ await multiMap.set('foo', '2');
129
+ await multiMap.set('foo', '3');
130
+
131
+ await multiMap.deleteValue('foo', '2');
132
+
133
+ expect(await getValues('foo')).to.deep.equal(['1', '3']);
134
+ });
135
+
136
+ it('should be able to delete the last and first values for a key', async () => {
137
+ await multiMap.set('foo', '1');
138
+ await multiMap.set('foo', '2');
139
+ await multiMap.set('foo', '3');
140
+
141
+ await multiMap.deleteValue('foo', '1');
142
+
143
+ expect(await getValues('foo')).to.deep.equal(['2', '3']);
144
+
145
+ await multiMap.deleteValue('foo', '3');
146
+
147
+ expect(await getValues('foo')).to.deep.equal(['2']);
148
+ });
149
+
150
+ it('should be able to fully clear a key', async () => {
151
+ await multiMap.set('foo', '1');
152
+ await multiMap.set('foo', '2');
153
+ await multiMap.set('foo', '3');
154
+
155
+ await multiMap.deleteValue('foo', '1');
156
+ await multiMap.deleteValue('foo', '3');
157
+ await multiMap.deleteValue('foo', '2');
158
+
159
+ expect(await getValues('foo')).to.deep.equal([]);
160
+ });
161
+
162
+ it('should be able to insert after deletion', async () => {
163
+ await multiMap.set('foo', '1');
164
+ await multiMap.set('foo', '2');
165
+ await multiMap.set('foo', '3');
166
+
167
+ await multiMap.deleteValue('foo', '2');
127
168
  await multiMap.set('foo', 'bar');
128
- await multiMap.set('foo', 'baz');
129
169
 
170
+ expect(await getValues('foo')).to.deep.equal(['1', '3', 'bar']);
171
+
172
+ // Delete the just-added entry
130
173
  await multiMap.deleteValue('foo', 'bar');
131
174
 
132
- expect(await getValues('foo')).to.deep.equal(['baz']);
175
+ expect(await getValues('foo')).to.deep.equal(['1', '3']);
176
+
177
+ // Reinsert the initially deleted key
178
+ await multiMap.set('foo', '2');
179
+
180
+ // LMDB and IndexedDB behave differently here, the former ordering by value and the latter by insertion. This is
181
+ // fine because there is no expectation for values in a multimap to be ordered.
182
+ const values = (await getValues('foo')).sort((a, b) => a.localeCompare(b));
183
+ expect(values).to.deep.equal(['1', '2', '3']);
184
+
185
+ // Fully clear the key
186
+ await multiMap.deleteValue('foo', '1');
187
+ await multiMap.deleteValue('foo', '3');
188
+ await multiMap.deleteValue('foo', '2');
189
+
190
+ // Insert some more
191
+ await multiMap.set('foo', 'baz');
192
+ await multiMap.set('foo', 'qux');
193
+ expect(await getValues('foo')).to.deep.equal(['baz', 'qux']);
133
194
  });
134
195
 
135
196
  it('supports range queries', async () => {
package/src/lmdb/map.ts CHANGED
@@ -82,16 +82,16 @@ export class LmdbAztecMap<K extends Key, V extends Value> implements AztecMap<K,
82
82
  ? this.slot(range.end)
83
83
  : this.endSentinel
84
84
  : range.start
85
- ? this.slot(range.start)
86
- : this.startSentinel;
85
+ ? this.slot(range.start)
86
+ : this.startSentinel;
87
87
 
88
88
  const end = reverse
89
89
  ? range.start
90
90
  ? this.slot(range.start)
91
91
  : this.startSentinel
92
92
  : range.end
93
- ? this.slot(range.end)
94
- : this.endSentinel;
93
+ ? this.slot(range.end)
94
+ : this.endSentinel;
95
95
 
96
96
  const lmdbRange: RangeOptions = {
97
97
  start,
@@ -113,7 +113,7 @@ export class LmdbAztecMap<K extends Key, V extends Value> implements AztecMap<K,
113
113
  }
114
114
  }
115
115
 
116
- async *entriesAsync(range?: Range<K> | undefined): AsyncIterableIterator<[K, V]> {
116
+ async *entriesAsync(range?: Range<K>): AsyncIterableIterator<[K, V]> {
117
117
  for (const entry of this.entries(range)) {
118
118
  yield entry;
119
119
  }
package/src/lmdb/store.ts CHANGED
@@ -32,7 +32,11 @@ export class AztecLmdbStore implements AztecKVStore, AztecAsyncKVStore {
32
32
  #multiMapData: Database<unknown, Key>;
33
33
  #log = createLogger('kv-store:lmdb');
34
34
 
35
- constructor(rootDb: RootDatabase, public readonly isEphemeral: boolean, private path: string) {
35
+ constructor(
36
+ rootDb: RootDatabase,
37
+ public readonly isEphemeral: boolean,
38
+ private path: string,
39
+ ) {
36
40
  this.#rootDb = rootDb;
37
41
 
38
42
  // big bucket to store all the data
@@ -187,10 +191,15 @@ export class AztecLmdbStore implements AztecKVStore, AztecAsyncKVStore {
187
191
  if ('mapSize' in stats && typeof stats.mapSize === 'number') {
188
192
  mapSize = stats.mapSize;
189
193
  }
194
+ let physicalFileSize = 0;
195
+ if ('physicalFileSize' in stats && typeof stats.physicalFileSize === 'number') {
196
+ physicalFileSize = stats.physicalFileSize;
197
+ }
190
198
  const dataResult = this.estimateSubDBSize(this.#data);
191
199
  const multiResult = this.estimateSubDBSize(this.#multiMapData);
192
200
  return Promise.resolve({
193
201
  mappingSize: mapSize,
202
+ physicalFileSize: physicalFileSize,
194
203
  actualSize: dataResult.actualSize + multiResult.actualSize,
195
204
  numItems: dataResult.numItems + multiResult.numItems,
196
205
  });
@@ -13,7 +13,10 @@ export class LMDBArray<T extends Value> implements AztecAsyncArray<T> {
13
13
  private encoder = new Encoder();
14
14
  private prefix: string;
15
15
 
16
- constructor(private store: AztecLMDBStoreV2, name: string) {
16
+ constructor(
17
+ private store: AztecLMDBStoreV2,
18
+ name: string,
19
+ ) {
17
20
  this.length = store.openSingleton(name + ':length');
18
21
  this.prefix = `array:${name}`;
19
22
  }
@@ -11,7 +11,10 @@ export class LMDBMap<K extends Key, V extends Value> implements AztecAsyncMap<K,
11
11
  private prefix: string;
12
12
  private encoder = new Encoder();
13
13
 
14
- constructor(private store: AztecLMDBStoreV2, name: string) {
14
+ constructor(
15
+ private store: AztecLMDBStoreV2,
16
+ name: string,
17
+ ) {
15
18
  this.prefix = `map:${name}`;
16
19
  }
17
20
  /**
@@ -65,9 +68,11 @@ export class LMDBMap<K extends Key, V extends Value> implements AztecAsyncMap<K,
65
68
  */
66
69
  async *entriesAsync(range?: Range<K>): AsyncIterableIterator<[K, V]> {
67
70
  const reverse = range?.reverse ?? false;
68
- const startKey = range?.start ? serializeKey(this.prefix, range.start) : minKey(this.prefix);
69
71
 
70
- const endKey = range?.end ? serializeKey(this.prefix, range.end) : reverse ? maxKey(this.prefix) : undefined;
72
+ const startKey = range?.start !== undefined ? serializeKey(this.prefix, range.start) : minKey(this.prefix);
73
+
74
+ const endKey =
75
+ range?.end !== undefined ? serializeKey(this.prefix, range.end) : reverse ? maxKey(this.prefix) : undefined;
71
76
 
72
77
  let tx: ReadTransaction | undefined = this.store.getCurrentWriteTx();
73
78
  const shouldClose = !tx;
@@ -81,7 +86,7 @@ export class LMDBMap<K extends Key, V extends Value> implements AztecAsyncMap<K,
81
86
  range?.limit,
82
87
  )) {
83
88
  const deserializedKey = deserializeKey<K>(this.prefix, key);
84
- if (!deserializedKey) {
89
+ if (deserializedKey === false) {
85
90
  break;
86
91
  }
87
92
  yield [deserializedKey, this.encoder.unpack(val)];
@@ -129,6 +129,7 @@ interface StatsResponse {
129
129
  totalUsedSize: bigint | number;
130
130
  }>;
131
131
  dbMapSizeBytes: bigint | number;
132
+ dbPhysicalFileSizeBytes: bigint | number;
132
133
  }
133
134
 
134
135
  export type LMDBResponseBody = {
@@ -9,7 +9,10 @@ import { deserializeKey, maxKey, minKey, serializeKey } from './utils.js';
9
9
  export class LMDBMultiMap<K extends Key, V extends Value> implements AztecAsyncMultiMap<K, V> {
10
10
  private prefix: string;
11
11
  private encoder = new Encoder();
12
- constructor(private store: AztecLMDBStoreV2, name: string) {
12
+ constructor(
13
+ private store: AztecLMDBStoreV2,
14
+ name: string,
15
+ ) {
13
16
  this.prefix = `multimap:${name}`;
14
17
  }
15
18
 
@@ -8,7 +8,10 @@ import { serializeKey } from './utils.js';
8
8
  export class LMDBSingleValue<T> implements AztecAsyncSingleton<T> {
9
9
  private key: Uint8Array;
10
10
  private encoder = new Encoder();
11
- constructor(private store: AztecLMDBStoreV2, name: string) {
11
+ constructor(
12
+ private store: AztecLMDBStoreV2,
13
+ name: string,
14
+ ) {
12
15
  this.key = serializeKey(`singleton:${name}`, 'value');
13
16
  }
14
17
 
@@ -210,6 +210,7 @@ export class AztecLMDBStoreV2 implements AztecAsyncKVStore, LMDBMessageChannel {
210
210
  const resp = await this.sendMessage(LMDBMessageType.STATS, undefined);
211
211
  return {
212
212
  mappingSize: Number(resp.dbMapSizeBytes),
213
+ physicalFileSize: Number(resp.dbPhysicalFileSizeBytes),
213
214
  actualSize: resp.stats.reduce((s, db) => Number(db.totalUsedSize) + s, 0),
214
215
  numItems: resp.stats.reduce((s, db) => Number(db.numDataItems) + s, 0),
215
216
  };
@@ -163,7 +163,7 @@ export class WriteTransaction extends ReadTransaction {
163
163
 
164
164
  public override async *iterate(
165
165
  startKey: Uint8Array,
166
- endKey?: Uint8Array | undefined,
166
+ endKey?: Uint8Array,
167
167
  reverse?: boolean,
168
168
  limit?: number,
169
169
  ): AsyncIterable<[Uint8Array, Uint8Array]> {
@@ -181,7 +181,7 @@ export class WriteTransaction extends ReadTransaction {
181
181
 
182
182
  public override async *iterateIndex(
183
183
  startKey: Uint8Array,
184
- endKey?: Uint8Array | undefined,
184
+ endKey?: Uint8Array,
185
185
  reverse?: boolean,
186
186
  limit?: number,
187
187
  ): AsyncIterable<[Uint8Array, Uint8Array[]]> {