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

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 (193) hide show
  1. package/README.md +8 -1
  2. package/dest/bench/shared_map_bench.d.ts +19 -0
  3. package/dest/bench/shared_map_bench.d.ts.map +1 -0
  4. package/dest/bench/shared_map_bench.js +91 -0
  5. package/dest/indexeddb/array.d.ts +1 -1
  6. package/dest/indexeddb/array.d.ts.map +1 -1
  7. package/dest/indexeddb/array.js +21 -7
  8. package/dest/indexeddb/index.d.ts +3 -3
  9. package/dest/indexeddb/index.d.ts.map +1 -1
  10. package/dest/indexeddb/index.js +3 -6
  11. package/dest/indexeddb/map.d.ts +8 -2
  12. package/dest/indexeddb/map.d.ts.map +1 -1
  13. package/dest/indexeddb/map.js +23 -13
  14. package/dest/indexeddb/multi_map.d.ts +3 -1
  15. package/dest/indexeddb/multi_map.d.ts.map +1 -1
  16. package/dest/indexeddb/multi_map.js +30 -1
  17. package/dest/indexeddb/set.d.ts +1 -1
  18. package/dest/indexeddb/set.d.ts.map +1 -1
  19. package/dest/indexeddb/singleton.d.ts +1 -1
  20. package/dest/indexeddb/singleton.d.ts.map +1 -1
  21. package/dest/indexeddb/singleton.js +3 -1
  22. package/dest/indexeddb/store.d.ts +3 -3
  23. package/dest/indexeddb/store.d.ts.map +1 -1
  24. package/dest/indexeddb/store.js +6 -4
  25. package/dest/interfaces/array.d.ts +1 -1
  26. package/dest/interfaces/array_test_suite.d.ts +1 -1
  27. package/dest/interfaces/array_test_suite.d.ts.map +1 -1
  28. package/dest/interfaces/array_test_suite.js +33 -34
  29. package/dest/interfaces/common.d.ts +1 -1
  30. package/dest/interfaces/counter.d.ts +1 -1
  31. package/dest/interfaces/index.d.ts +2 -2
  32. package/dest/interfaces/index.d.ts.map +1 -1
  33. package/dest/interfaces/map.d.ts +1 -1
  34. package/dest/interfaces/map_test_suite.d.ts +1 -1
  35. package/dest/interfaces/map_test_suite.d.ts.map +1 -1
  36. package/dest/interfaces/map_test_suite.js +74 -29
  37. package/dest/interfaces/multi_map.d.ts +7 -1
  38. package/dest/interfaces/multi_map.d.ts.map +1 -1
  39. package/dest/interfaces/multi_map_test_suite.d.ts +1 -1
  40. package/dest/interfaces/multi_map_test_suite.d.ts.map +1 -1
  41. package/dest/interfaces/multi_map_test_suite.js +154 -36
  42. package/dest/interfaces/set.d.ts +1 -1
  43. package/dest/interfaces/set_test_suite.d.ts +1 -1
  44. package/dest/interfaces/set_test_suite.d.ts.map +1 -1
  45. package/dest/interfaces/set_test_suite.js +13 -14
  46. package/dest/interfaces/singleton.d.ts +1 -1
  47. package/dest/interfaces/singleton_test_suite.d.ts +1 -1
  48. package/dest/interfaces/singleton_test_suite.d.ts.map +1 -1
  49. package/dest/interfaces/singleton_test_suite.js +6 -7
  50. package/dest/interfaces/store.d.ts +1 -1
  51. package/dest/interfaces/utils.d.ts +2 -1
  52. package/dest/interfaces/utils.d.ts.map +1 -1
  53. package/dest/interfaces/utils.js +2 -1
  54. package/dest/lmdb/array.d.ts +1 -1
  55. package/dest/lmdb/array.d.ts.map +1 -1
  56. package/dest/lmdb/array.js +4 -2
  57. package/dest/lmdb/counter.d.ts +1 -1
  58. package/dest/lmdb/counter.d.ts.map +1 -1
  59. package/dest/lmdb/index.d.ts +3 -3
  60. package/dest/lmdb/index.d.ts.map +1 -1
  61. package/dest/lmdb/index.js +4 -4
  62. package/dest/lmdb/map.d.ts +1 -1
  63. package/dest/lmdb/map.d.ts.map +1 -1
  64. package/dest/lmdb/multi_map.d.ts +2 -1
  65. package/dest/lmdb/multi_map.d.ts.map +1 -1
  66. package/dest/lmdb/multi_map.js +15 -0
  67. package/dest/lmdb/set.d.ts +1 -1
  68. package/dest/lmdb/set.d.ts.map +1 -1
  69. package/dest/lmdb/singleton.d.ts +1 -1
  70. package/dest/lmdb/singleton.d.ts.map +1 -1
  71. package/dest/lmdb/store.d.ts +4 -5
  72. package/dest/lmdb/store.d.ts.map +1 -1
  73. package/dest/lmdb/store.js +13 -9
  74. package/dest/lmdb-v2/array.d.ts +2 -2
  75. package/dest/lmdb-v2/array.d.ts.map +1 -1
  76. package/dest/lmdb-v2/array.js +4 -3
  77. package/dest/lmdb-v2/factory.d.ts +32 -10
  78. package/dest/lmdb-v2/factory.d.ts.map +1 -1
  79. package/dest/lmdb-v2/factory.js +71 -16
  80. package/dest/lmdb-v2/index.d.ts +1 -1
  81. package/dest/lmdb-v2/map.d.ts +2 -2
  82. package/dest/lmdb-v2/map.d.ts.map +1 -1
  83. package/dest/lmdb-v2/map.js +1 -2
  84. package/dest/lmdb-v2/message.d.ts +1 -1
  85. package/dest/lmdb-v2/multi_map.d.ts +3 -2
  86. package/dest/lmdb-v2/multi_map.d.ts.map +1 -1
  87. package/dest/lmdb-v2/multi_map.js +11 -1
  88. package/dest/lmdb-v2/read_transaction.d.ts +1 -1
  89. package/dest/lmdb-v2/read_transaction.d.ts.map +1 -1
  90. package/dest/lmdb-v2/read_transaction.js +21 -19
  91. package/dest/lmdb-v2/set.d.ts +1 -1
  92. package/dest/lmdb-v2/set.d.ts.map +1 -1
  93. package/dest/lmdb-v2/singleton.d.ts +2 -2
  94. package/dest/lmdb-v2/singleton.d.ts.map +1 -1
  95. package/dest/lmdb-v2/singleton.js +1 -2
  96. package/dest/lmdb-v2/store.d.ts +4 -5
  97. package/dest/lmdb-v2/store.d.ts.map +1 -1
  98. package/dest/lmdb-v2/store.js +6 -28
  99. package/dest/lmdb-v2/tx-helpers.d.ts +6 -0
  100. package/dest/lmdb-v2/tx-helpers.d.ts.map +1 -0
  101. package/dest/lmdb-v2/tx-helpers.js +21 -0
  102. package/dest/lmdb-v2/utils.d.ts +1 -10
  103. package/dest/lmdb-v2/utils.d.ts.map +1 -1
  104. package/dest/lmdb-v2/utils.js +0 -94
  105. package/dest/lmdb-v2/write_transaction.d.ts +1 -1
  106. package/dest/lmdb-v2/write_transaction.d.ts.map +1 -1
  107. package/dest/lmdb-v2/write_transaction.js +9 -5
  108. package/dest/sqlite-opfs/array.d.ts +21 -0
  109. package/dest/sqlite-opfs/array.d.ts.map +1 -0
  110. package/dest/sqlite-opfs/array.js +128 -0
  111. package/dest/sqlite-opfs/errors.d.ts +27 -0
  112. package/dest/sqlite-opfs/errors.d.ts.map +1 -0
  113. package/dest/sqlite-opfs/errors.js +34 -0
  114. package/dest/sqlite-opfs/index.d.ts +16 -0
  115. package/dest/sqlite-opfs/index.d.ts.map +1 -0
  116. package/dest/sqlite-opfs/index.js +22 -0
  117. package/dest/sqlite-opfs/internal/ordered-binary-browser.d.ts +32 -0
  118. package/dest/sqlite-opfs/internal/ordered-binary-browser.d.ts.map +1 -0
  119. package/dest/sqlite-opfs/internal/ordered-binary-browser.js +448 -0
  120. package/dest/sqlite-opfs/map.d.ts +35 -0
  121. package/dest/sqlite-opfs/map.d.ts.map +1 -0
  122. package/dest/sqlite-opfs/map.js +163 -0
  123. package/dest/sqlite-opfs/messages.d.ts +66 -0
  124. package/dest/sqlite-opfs/messages.d.ts.map +1 -0
  125. package/dest/sqlite-opfs/messages.js +5 -0
  126. package/dest/sqlite-opfs/multi_map.d.ts +16 -0
  127. package/dest/sqlite-opfs/multi_map.d.ts.map +1 -0
  128. package/dest/sqlite-opfs/multi_map.js +67 -0
  129. package/dest/sqlite-opfs/set.d.ts +13 -0
  130. package/dest/sqlite-opfs/set.d.ts.map +1 -0
  131. package/dest/sqlite-opfs/set.js +19 -0
  132. package/dest/sqlite-opfs/singleton.d.ts +13 -0
  133. package/dest/sqlite-opfs/singleton.d.ts.map +1 -0
  134. package/dest/sqlite-opfs/singleton.js +48 -0
  135. package/dest/sqlite-opfs/store.d.ts +79 -0
  136. package/dest/sqlite-opfs/store.d.ts.map +1 -0
  137. package/dest/sqlite-opfs/store.js +281 -0
  138. package/dest/sqlite-opfs/worker.d.ts +2 -0
  139. package/dest/sqlite-opfs/worker.d.ts.map +1 -0
  140. package/dest/sqlite-opfs/worker.js +249 -0
  141. package/dest/stores/index.d.ts +1 -1
  142. package/dest/stores/l2_tips_store.d.ts +25 -10
  143. package/dest/stores/l2_tips_store.d.ts.map +1 -1
  144. package/dest/stores/l2_tips_store.js +62 -53
  145. package/dest/utils.d.ts +9 -6
  146. package/dest/utils.d.ts.map +1 -1
  147. package/dest/utils.js +51 -16
  148. package/package.json +33 -26
  149. package/src/bench/shared_map_bench.ts +111 -0
  150. package/src/indexeddb/array.ts +4 -4
  151. package/src/indexeddb/index.ts +9 -7
  152. package/src/indexeddb/map.ts +24 -11
  153. package/src/indexeddb/multi_map.ts +27 -1
  154. package/src/indexeddb/singleton.ts +1 -1
  155. package/src/indexeddb/store.ts +13 -6
  156. package/src/interfaces/array_test_suite.ts +33 -35
  157. package/src/interfaces/index.ts +1 -1
  158. package/src/interfaces/map_test_suite.ts +56 -30
  159. package/src/interfaces/multi_map.ts +7 -0
  160. package/src/interfaces/multi_map_test_suite.ts +167 -37
  161. package/src/interfaces/set_test_suite.ts +13 -15
  162. package/src/interfaces/singleton_test_suite.ts +6 -8
  163. package/src/interfaces/utils.ts +1 -0
  164. package/src/lmdb/index.ts +10 -5
  165. package/src/lmdb/multi_map.ts +16 -0
  166. package/src/lmdb/store.ts +13 -9
  167. package/src/lmdb-v2/array.ts +2 -2
  168. package/src/lmdb-v2/factory.ts +93 -19
  169. package/src/lmdb-v2/map.ts +2 -2
  170. package/src/lmdb-v2/multi_map.ts +10 -1
  171. package/src/lmdb-v2/read_transaction.ts +23 -23
  172. package/src/lmdb-v2/singleton.ts +2 -2
  173. package/src/lmdb-v2/store.ts +9 -33
  174. package/src/lmdb-v2/tx-helpers.ts +29 -0
  175. package/src/lmdb-v2/utils.ts +0 -118
  176. package/src/lmdb-v2/write_transaction.ts +9 -8
  177. package/src/sqlite-opfs/array.ts +124 -0
  178. package/src/sqlite-opfs/errors.ts +44 -0
  179. package/src/sqlite-opfs/index.ts +39 -0
  180. package/src/sqlite-opfs/internal/ordered-binary-browser.js +465 -0
  181. package/src/sqlite-opfs/map.ts +163 -0
  182. package/src/sqlite-opfs/messages.ts +39 -0
  183. package/src/sqlite-opfs/multi_map.ts +74 -0
  184. package/src/sqlite-opfs/set.ts +29 -0
  185. package/src/sqlite-opfs/singleton.ts +48 -0
  186. package/src/sqlite-opfs/store.ts +296 -0
  187. package/src/sqlite-opfs/worker.ts +231 -0
  188. package/src/stores/l2_tips_store.ts +66 -56
  189. package/src/utils.ts +79 -21
  190. package/dest/config.d.ts +0 -17
  191. package/dest/config.d.ts.map +0 -1
  192. package/dest/config.js +0 -26
  193. package/src/config.ts +0 -36
@@ -1,7 +1,5 @@
1
1
  import { toArray } from '@aztec/foundation/iterable';
2
2
 
3
- import { expect } from 'chai';
4
-
5
3
  import type { Key, Range } from './common.js';
6
4
  import type { AztecAsyncMultiMap, AztecMultiMap } from './multi_map.js';
7
5
  import type { AztecAsyncKVStore, AztecKVStore } from './store.js';
@@ -61,20 +59,27 @@ export function describeAztecMultiMap(
61
59
  : await toArray((multiMap as AztecAsyncMultiMap<any, any>).getValuesAsync(key));
62
60
  }
63
61
 
62
+ async function getValueCount(
63
+ key: Key,
64
+ sut: AztecAsyncMultiMap<any, any> | AztecMultiMap<any, any> = multiMap,
65
+ ): Promise<number> {
66
+ return await (sut as AztecAsyncMultiMap<any, any>).getValueCountAsync(key);
67
+ }
68
+
64
69
  it('should be able to set and get values', async () => {
65
70
  await multiMap.set('foo', 'bar');
66
71
  await multiMap.set('baz', 'qux');
67
72
 
68
- expect(await get('foo')).to.equal('bar');
69
- expect(await get('baz')).to.equal('qux');
70
- expect(await get('quux')).to.equal(undefined);
73
+ expect(await get('foo')).toBe('bar');
74
+ expect(await get('baz')).toBe('qux');
75
+ expect(await get('quux')).toBe(undefined);
71
76
  });
72
77
 
73
78
  it('should be able to set values if they do not exist', async () => {
74
- expect(await multiMap.setIfNotExists('foo', 'bar')).to.equal(true);
75
- expect(await multiMap.setIfNotExists('foo', 'baz')).to.equal(false);
79
+ expect(await multiMap.setIfNotExists('foo', 'bar')).toBe(true);
80
+ expect(await multiMap.setIfNotExists('foo', 'baz')).toBe(false);
76
81
 
77
- expect(await get('foo')).to.equal('bar');
82
+ expect(await get('foo')).toBe('bar');
78
83
  });
79
84
 
80
85
  it('should be able to delete values', async () => {
@@ -83,29 +88,61 @@ export function describeAztecMultiMap(
83
88
 
84
89
  await multiMap.delete('foo');
85
90
 
86
- expect(await get('foo')).to.equal(undefined);
87
- expect(await get('baz')).to.equal('qux');
91
+ expect(await get('foo')).toBe(undefined);
92
+ expect(await get('baz')).toBe('qux');
88
93
  });
89
94
 
90
95
  it('should be able to get size of the map', async () => {
91
96
  await multiMap.set('foo', 'bar');
92
- expect(await size()).to.equal(1);
97
+ expect(await size()).toBe(1);
93
98
  await multiMap.set('baz', 'qux');
94
- expect(await size()).to.equal(2);
99
+ expect(await size()).toBe(2);
95
100
 
96
101
  await multiMap.delete('foo');
97
- expect(await size()).to.equal(1);
102
+ expect(await size()).toBe(1);
103
+ });
104
+
105
+ it('returns 0 for empty multimap size', async () => {
106
+ expect(await size()).toBe(0);
107
+ });
108
+
109
+ it('calculates size correctly with multiple values per key', async () => {
110
+ expect(await size()).toBe(0);
111
+
112
+ // Add multiple values for same key
113
+ await multiMap.set('key1', 'value1');
114
+ expect(await size()).toBe(1);
115
+ await multiMap.set('key1', 'value2');
116
+ expect(await size()).toBe(2);
117
+ await multiMap.set('key1', 'value3');
118
+ expect(await size()).toBe(3);
119
+
120
+ // Add values for different key
121
+ await multiMap.set('key2', 'value4');
122
+ expect(await size()).toBe(4);
123
+
124
+ // Delete one value from key1
125
+ await multiMap.deleteValue('key1', 'value2');
126
+ expect(await size()).toBe(3);
127
+
128
+ // Delete entire key
129
+ await multiMap.delete('key1');
130
+ expect(await size()).toBe(1);
131
+
132
+ // Delete last key
133
+ await multiMap.delete('key2');
134
+ expect(await size()).toBe(0);
98
135
  });
99
136
 
100
137
  it('should be able to iterate over entries when there are no keys', async () => {
101
- expect(await entries()).to.deep.equal([]);
138
+ expect(await entries()).toEqual([]);
102
139
  });
103
140
 
104
141
  it('should be able to iterate over entries', async () => {
105
142
  await multiMap.set('foo', 'bar');
106
143
  await multiMap.set('baz', 'qux');
107
144
 
108
- expect(await entries()).to.deep.equal([
145
+ expect(await entries()).toEqual([
109
146
  ['baz', 'qux'],
110
147
  ['foo', 'bar'],
111
148
  ]);
@@ -115,28 +152,28 @@ export function describeAztecMultiMap(
115
152
  await multiMap.set('foo', 'bar');
116
153
  await multiMap.set('baz', 'quux');
117
154
 
118
- expect(await values()).to.deep.equal(['quux', 'bar']);
155
+ expect(await values()).toEqual(['quux', 'bar']);
119
156
  });
120
157
 
121
158
  it('should be able to iterate over keys', async () => {
122
159
  await multiMap.set('foo', 'bar');
123
160
  await multiMap.set('baz', 'qux');
124
161
 
125
- expect(await keys()).to.deep.equal(['baz', 'foo']);
162
+ expect(await keys()).toEqual(['baz', 'foo']);
126
163
  });
127
164
 
128
165
  it('should be able to get multiple values for a single key', async () => {
129
166
  await multiMap.set('foo', 'bar');
130
167
  await multiMap.set('foo', 'baz');
131
168
 
132
- expect(await getValues('foo')).to.deep.equal(['bar', 'baz']);
169
+ expect(await getValues('foo')).toEqual(['bar', 'baz']);
133
170
  });
134
171
 
135
172
  it('should ignore multiple identical values', async () => {
136
173
  await multiMap.set('foo', 'bar');
137
174
  await multiMap.set('foo', 'bar');
138
175
 
139
- expect(await getValues('foo')).to.deep.equal(['bar']);
176
+ expect(await getValues('foo')).toEqual(['bar']);
140
177
  });
141
178
 
142
179
  it('should be able to delete individual values for a single key', async () => {
@@ -146,22 +183,22 @@ export function describeAztecMultiMap(
146
183
 
147
184
  await multiMap.deleteValue('foo', '2');
148
185
 
149
- expect(await getValues('foo')).to.deep.equal(['1', '3']);
186
+ expect(await getValues('foo')).toEqual(['1', '3']);
150
187
  });
151
188
 
152
189
  it('should be able to get size of the map with duplicate keys', async () => {
153
190
  await multiMap.set('foo', '1');
154
191
  await multiMap.set('foo', '2');
155
192
  await multiMap.set('foo', '3');
156
- expect(await size()).to.equal(3);
193
+ expect(await size()).toBe(3);
157
194
 
158
195
  await multiMap.set('bar', '1');
159
196
  await multiMap.set('bar', '2');
160
197
  await multiMap.set('bar', '3');
161
- expect(await size()).to.equal(6);
198
+ expect(await size()).toBe(6);
162
199
 
163
200
  await multiMap.deleteValue('foo', '2');
164
- expect(await size()).to.equal(5);
201
+ expect(await size()).toBe(5);
165
202
  });
166
203
 
167
204
  it('should be able to delete the last and first values for a key', async () => {
@@ -171,11 +208,11 @@ export function describeAztecMultiMap(
171
208
 
172
209
  await multiMap.deleteValue('foo', '1');
173
210
 
174
- expect(await getValues('foo')).to.deep.equal(['2', '3']);
211
+ expect(await getValues('foo')).toEqual(['2', '3']);
175
212
 
176
213
  await multiMap.deleteValue('foo', '3');
177
214
 
178
- expect(await getValues('foo')).to.deep.equal(['2']);
215
+ expect(await getValues('foo')).toEqual(['2']);
179
216
  });
180
217
 
181
218
  it('should be able to fully clear a key', async () => {
@@ -187,7 +224,7 @@ export function describeAztecMultiMap(
187
224
  await multiMap.deleteValue('foo', '3');
188
225
  await multiMap.deleteValue('foo', '2');
189
226
 
190
- expect(await getValues('foo')).to.deep.equal([]);
227
+ expect(await getValues('foo')).toEqual([]);
191
228
  });
192
229
 
193
230
  it('should be able to insert after deletion', async () => {
@@ -198,12 +235,12 @@ export function describeAztecMultiMap(
198
235
  await multiMap.deleteValue('foo', '2');
199
236
  await multiMap.set('foo', 'bar');
200
237
 
201
- expect(await getValues('foo')).to.deep.equal(['1', '3', 'bar']);
238
+ expect(await getValues('foo')).toEqual(['1', '3', 'bar']);
202
239
 
203
240
  // Delete the just-added entry
204
241
  await multiMap.deleteValue('foo', 'bar');
205
242
 
206
- expect(await getValues('foo')).to.deep.equal(['1', '3']);
243
+ expect(await getValues('foo')).toEqual(['1', '3']);
207
244
 
208
245
  // Reinsert the initially deleted key
209
246
  await multiMap.set('foo', '2');
@@ -211,7 +248,7 @@ export function describeAztecMultiMap(
211
248
  // LMDB and IndexedDB behave differently here, the former ordering by value and the latter by insertion. This is
212
249
  // fine because there is no expectation for values in a multimap to be ordered.
213
250
  const values = (await getValues('foo')).sort((a, b) => a.localeCompare(b));
214
- expect(values).to.deep.equal(['1', '2', '3']);
251
+ expect(values).toEqual(['1', '2', '3']);
215
252
 
216
253
  // Fully clear the key
217
254
  await multiMap.deleteValue('foo', '1');
@@ -221,7 +258,7 @@ export function describeAztecMultiMap(
221
258
  // Insert some more
222
259
  await multiMap.set('foo', 'baz');
223
260
  await multiMap.set('foo', 'qux');
224
- expect(await getValues('foo')).to.deep.equal(['baz', 'qux']);
261
+ expect(await getValues('foo')).toEqual(['baz', 'qux']);
225
262
  });
226
263
 
227
264
  it('supports range queries', async () => {
@@ -230,13 +267,106 @@ export function describeAztecMultiMap(
230
267
  await multiMap.set('c', 'c');
231
268
  await multiMap.set('d', 'd');
232
269
 
233
- expect(await keys({ start: 'b', end: 'c' })).to.deep.equal(['b']);
234
- expect(await keys({ start: 'b' })).to.deep.equal(['b', 'c', 'd']);
235
- expect(await keys({ end: 'c' })).to.deep.equal(['a', 'b']);
236
- expect(await keys({ start: 'b', end: 'c', reverse: true })).to.deep.equal(['c']);
237
- expect(await keys({ start: 'b', limit: 1 })).to.deep.equal(['b']);
238
- expect(await keys({ start: 'b', reverse: true })).to.deep.equal(['d', 'c']);
239
- expect(await keys({ end: 'b', reverse: true })).to.deep.equal(['b', 'a']);
270
+ expect(await keys({ start: 'b', end: 'c' })).toEqual(['b']);
271
+ expect(await keys({ start: 'b' })).toEqual(['b', 'c', 'd']);
272
+ expect(await keys({ end: 'c' })).toEqual(['a', 'b']);
273
+ expect(await keys({ start: 'b', end: 'c', reverse: true })).toEqual(['c']);
274
+ expect(await keys({ start: 'b', limit: 1 })).toEqual(['b']);
275
+ expect(await keys({ start: 'b', reverse: true })).toEqual(['d', 'c']);
276
+ expect(await keys({ end: 'b', reverse: true })).toEqual(['b', 'a']);
277
+ });
278
+
279
+ it('returns 0 for missing key', async () => {
280
+ expect(await getValueCount('missing')).toBe(0);
281
+ });
282
+
283
+ it('counts a single value', async () => {
284
+ await multiMap.set('foo', 'bar');
285
+ expect(await getValueCount('foo')).toBe(1);
286
+ });
287
+
288
+ it('counts multiple distinct values for same key', async () => {
289
+ await multiMap.set('foo', 'bar');
290
+ await multiMap.set('foo', 'baz');
291
+ await multiMap.set('foo', 'qux');
292
+ expect(await getValueCount('foo')).toBe(3);
293
+ });
294
+
295
+ it('does not increase count for duplicate inserts', async () => {
296
+ await multiMap.set('foo', 'bar');
297
+ await multiMap.set('foo', 'bar');
298
+ await multiMap.set('foo', 'baz');
299
+ await multiMap.set('foo', 'baz');
300
+ expect(await getValueCount('foo')).toBe(2);
301
+ expect((await getValues('foo')).sort()).toEqual(['bar', 'baz'].sort());
302
+ });
303
+
304
+ it('decrements when deleting a single value', async () => {
305
+ await multiMap.set('foo', '1');
306
+ await multiMap.set('foo', '2');
307
+ await multiMap.set('foo', '3');
308
+ expect(await getValueCount('foo')).toBe(3);
309
+ await multiMap.deleteValue('foo', '2');
310
+ expect(await getValueCount('foo')).toBe(2);
311
+ expect((await getValues('foo')).sort()).toEqual(['1', '3'].sort());
312
+ });
313
+
314
+ it('does not change count when deleting a non-existent value', async () => {
315
+ await multiMap.set('foo', '1');
316
+ await multiMap.set('foo', '3');
317
+ expect(await getValueCount('foo')).toBe(2);
318
+ await multiMap.deleteValue('foo', '2');
319
+ expect(await getValueCount('foo')).toBe(2);
320
+ });
321
+
322
+ it('clears all values when deleting a key', async () => {
323
+ await multiMap.set('foo', 'bar');
324
+ await multiMap.set('foo', 'baz');
325
+ expect(await getValueCount('foo')).toBe(2);
326
+ await multiMap.delete('foo');
327
+ expect(await getValueCount('foo')).toBe(0);
328
+ expect(await getValues('foo')).toEqual([]);
329
+ });
330
+
331
+ it('count equals enumerated values length', async () => {
332
+ await multiMap.set('foo', '1');
333
+ await multiMap.set('foo', '2');
334
+ const vals = await getValues('foo');
335
+ expect(await getValueCount('foo')).toBe(vals.length);
336
+ });
337
+
338
+ it('sum of per-key counts equals total size', async () => {
339
+ await multiMap.set('foo', '1');
340
+ await multiMap.set('foo', '2');
341
+ await multiMap.set('bar', '3');
342
+ await multiMap.set('bar', '4');
343
+ await multiMap.set('baz', '5');
344
+
345
+ const allKeys = await keys();
346
+ const uniqueKeys = Array.from(new Set(allKeys));
347
+ const counts = await Promise.all(uniqueKeys.map(k => getValueCount(k)));
348
+ const sum = counts.reduce((s, n) => s + n, 0);
349
+ expect(sum).toBe(await size());
350
+ });
351
+
352
+ it('supports sparse slots: delete middle, reinsert new, count remains correct', async () => {
353
+ await multiMap.set('foo', '1');
354
+ await multiMap.set('foo', '2');
355
+ await multiMap.set('foo', '3');
356
+ expect(await getValueCount('foo')).toBe(3);
357
+ await multiMap.deleteValue('foo', '2');
358
+ expect(await getValueCount('foo')).toBe(2);
359
+ await multiMap.set('foo', '4');
360
+ expect(await getValueCount('foo')).toBe(3);
361
+ expect((await getValues('foo')).sort()).toEqual(['1', '3', '4'].sort());
362
+ });
363
+
364
+ it('multiple keys are independent', async () => {
365
+ await multiMap.set('foo', '1');
366
+ await multiMap.set('foo', '2');
367
+ await multiMap.set('bar', '3');
368
+ expect(await getValueCount('foo')).toBe(2);
369
+ expect(await getValueCount('bar')).toBe(1);
240
370
  });
241
371
  });
242
372
  }
@@ -1,7 +1,5 @@
1
1
  import { toArray } from '@aztec/foundation/iterable';
2
2
 
3
- import { expect } from 'chai';
4
-
5
3
  import type { Range } from './common.js';
6
4
  import type { AztecAsyncSet, AztecSet } from './set.js';
7
5
  import type { AztecAsyncKVStore, AztecKVStore } from './store.js';
@@ -41,9 +39,9 @@ export function describeAztecSet(
41
39
  await set.add('foo');
42
40
  await set.add('baz');
43
41
 
44
- expect(await has('foo')).to.equal(true);
45
- expect(await has('baz')).to.equal(true);
46
- expect(await has('bar')).to.equal(false);
42
+ expect(await has('foo')).toBe(true);
43
+ expect(await has('baz')).toBe(true);
44
+ expect(await has('bar')).toBe(false);
47
45
  });
48
46
 
49
47
  it('should be able to delete values', async () => {
@@ -52,15 +50,15 @@ export function describeAztecSet(
52
50
 
53
51
  await set.delete('foo');
54
52
 
55
- expect(await has('foo')).to.equal(false);
56
- expect(await has('baz')).to.equal(true);
53
+ expect(await has('foo')).toBe(false);
54
+ expect(await has('baz')).toBe(true);
57
55
  });
58
56
 
59
57
  it('should be able to iterate over entries', async () => {
60
58
  await set.add('baz');
61
59
  await set.add('foo');
62
60
 
63
- expect(await entries()).to.deep.equal(['baz', 'foo']);
61
+ expect(await entries()).toEqual(['baz', 'foo']);
64
62
  });
65
63
 
66
64
  it('supports range queries', async () => {
@@ -69,13 +67,13 @@ export function describeAztecSet(
69
67
  await set.add('c');
70
68
  await set.add('d');
71
69
 
72
- expect(await entries({ start: 'b', end: 'c' })).to.deep.equal(['b']);
73
- expect(await entries({ start: 'b' })).to.deep.equal(['b', 'c', 'd']);
74
- expect(await entries({ end: 'c' })).to.deep.equal(['a', 'b']);
75
- expect(await entries({ start: 'b', end: 'c', reverse: true })).to.deep.equal(['c']);
76
- expect(await entries({ start: 'b', limit: 1 })).to.deep.equal(['b']);
77
- expect(await entries({ start: 'b', reverse: true })).to.deep.equal(['d', 'c']);
78
- expect(await entries({ end: 'b', reverse: true })).to.deep.equal(['b', 'a']);
70
+ expect(await entries({ start: 'b', end: 'c' })).toEqual(['b']);
71
+ expect(await entries({ start: 'b' })).toEqual(['b', 'c', 'd']);
72
+ expect(await entries({ end: 'c' })).toEqual(['a', 'b']);
73
+ expect(await entries({ start: 'b', end: 'c', reverse: true })).toEqual(['c']);
74
+ expect(await entries({ start: 'b', limit: 1 })).toEqual(['b']);
75
+ expect(await entries({ start: 'b', reverse: true })).toEqual(['d', 'c']);
76
+ expect(await entries({ end: 'b', reverse: true })).toEqual(['b', 'a']);
79
77
  });
80
78
  });
81
79
  }
@@ -1,5 +1,3 @@
1
- import { expect } from 'chai';
2
-
3
1
  import type { AztecAsyncSingleton, AztecSingleton } from './singleton.js';
4
2
  import type { AztecAsyncKVStore, AztecKVStore } from './store.js';
5
3
  import { isSyncStore } from './utils.js';
@@ -29,18 +27,18 @@ export function describeAztecSingleton(
29
27
  }
30
28
 
31
29
  it('returns undefined if the value is not set', async () => {
32
- expect(await get()).to.equal(undefined);
30
+ expect(await get()).toBe(undefined);
33
31
  });
34
32
 
35
33
  it('should be able to set and get values', async () => {
36
- expect(await singleton.set('foo')).to.equal(true);
37
- expect(await get()).to.equal('foo');
34
+ expect(await singleton.set('foo')).toBe(true);
35
+ expect(await get()).toBe('foo');
38
36
  });
39
37
 
40
38
  it('overwrites the value if it is set again', async () => {
41
- expect(await singleton.set('foo')).to.equal(true);
42
- expect(await singleton.set('bar')).to.equal(true);
43
- expect(await get()).to.equal('bar');
39
+ expect(await singleton.set('foo')).toBe(true);
40
+ expect(await singleton.set('bar')).toBe(true);
41
+ expect(await get()).toBe('bar');
44
42
  });
45
43
  });
46
44
  }
@@ -14,6 +14,7 @@ export const mockLogger = {
14
14
  isLevelEnabled: (_level: string) => true,
15
15
  module: 'kv-store:mock-logger',
16
16
  createChild: () => mockLogger,
17
+ getBindings: () => ({}),
17
18
  };
18
19
  /* eslint-enable no-console */
19
20
 
package/src/lmdb/index.ts CHANGED
@@ -1,14 +1,19 @@
1
1
  import { type Logger, createLogger } from '@aztec/foundation/log';
2
+ import type { DataStoreConfig } from '@aztec/stdlib/kv-store';
2
3
 
3
4
  import { join } from 'path';
4
5
 
5
- import type { DataStoreConfig } from '../config.js';
6
- import { initStoreForRollup } from '../utils.js';
6
+ import { initStoreForRollupAndSchemaVersion } from '../utils.js';
7
7
  import { AztecLmdbStore } from './store.js';
8
8
 
9
9
  export { AztecLmdbStore } from './store.js';
10
10
 
11
- export function createStore(name: string, config: DataStoreConfig, log: Logger = createLogger('kv-store')) {
11
+ export function createStore(
12
+ name: string,
13
+ config: DataStoreConfig,
14
+ schemaVersion: number | undefined = undefined,
15
+ log: Logger = createLogger('kv-store'),
16
+ ) {
12
17
  let { dataDirectory } = config;
13
18
  if (typeof dataDirectory !== 'undefined') {
14
19
  dataDirectory = join(dataDirectory, name);
@@ -21,8 +26,8 @@ export function createStore(name: string, config: DataStoreConfig, log: Logger =
21
26
  );
22
27
 
23
28
  const store = AztecLmdbStore.open(dataDirectory, config.dataStoreMapSizeKb, false);
24
- if (config.l1Contracts?.rollupAddress) {
25
- return initStoreForRollup(store, config.l1Contracts.rollupAddress, log);
29
+ if (config.rollupAddress) {
30
+ return initStoreForRollupAndSchemaVersion(store, schemaVersion, config.rollupAddress, log);
26
31
  }
27
32
  return store;
28
33
  }
@@ -29,6 +29,22 @@ export class LmdbAztecMultiMap<K extends Key, V extends Value>
29
29
  }
30
30
  }
31
31
 
32
+ getValueCountAsync(key: K): Promise<number> {
33
+ const transaction = this.db.useReadTransaction();
34
+ try {
35
+ const values = this.db.getValues(this.slot(key), {
36
+ transaction,
37
+ });
38
+ let count = 0;
39
+ for (const _ of values) {
40
+ count++;
41
+ }
42
+ return Promise.resolve(count);
43
+ } finally {
44
+ transaction.done();
45
+ }
46
+ }
47
+
32
48
  async deleteValue(key: K, val: V): Promise<void> {
33
49
  await this.db.remove(this.slot(key), [key, val]);
34
50
  }
package/src/lmdb/store.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { randomBytes } from '@aztec/foundation/crypto';
1
+ import { randomBytes } from '@aztec/foundation/crypto/random';
2
2
  import { createLogger } from '@aztec/foundation/log';
3
3
 
4
4
  import { promises as fs, mkdirSync } from 'fs';
@@ -147,21 +147,25 @@ export class AztecLmdbStore implements AztecKVStore, AztecAsyncKVStore {
147
147
  }
148
148
 
149
149
  /**
150
- * Clears all entries in the store & sub DBs.
150
+ * Clears all entries in the store & sub DBs atomically within a single transaction.
151
151
  */
152
152
  async clear() {
153
- await this.#data.clearAsync();
154
- await this.#multiMapData.clearAsync();
155
- await this.#rootDb.clearAsync();
153
+ await this.#rootDb.transaction(async () => {
154
+ await this.#data.clearAsync();
155
+ await this.#multiMapData.clearAsync();
156
+ await this.#rootDb.clearAsync();
157
+ });
156
158
  }
157
159
 
158
160
  /**
159
- * Drops the database & sub DBs.
161
+ * Drops the database & sub DBs atomically within a single transaction.
160
162
  */
161
163
  async drop() {
162
- await this.#data.drop();
163
- await this.#multiMapData.drop();
164
- await this.#rootDb.drop();
164
+ await this.#rootDb.transaction(async () => {
165
+ await this.#data.drop();
166
+ await this.#multiMapData.drop();
167
+ await this.#rootDb.drop();
168
+ });
165
169
  }
166
170
 
167
171
  /**
@@ -4,8 +4,8 @@ import type { AztecAsyncArray } from '../interfaces/array.js';
4
4
  import type { Value } from '../interfaces/common.js';
5
5
  import type { AztecAsyncSingleton } from '../interfaces/singleton.js';
6
6
  import type { ReadTransaction } from './read_transaction.js';
7
- // eslint-disable-next-line import/no-cycle
8
- import { AztecLMDBStoreV2, execInReadTx, execInWriteTx } from './store.js';
7
+ import type { AztecLMDBStoreV2 } from './store.js';
8
+ import { execInReadTx, execInWriteTx } from './tx-helpers.js';
9
9
  import { deserializeKey, serializeKey } from './utils.js';
10
10
 
11
11
  export class LMDBArray<T extends Value> implements AztecAsyncArray<T> {