@aztec/kv-store 0.0.0-test.0

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 (166) hide show
  1. package/README.md +10 -0
  2. package/dest/config.d.ts +17 -0
  3. package/dest/config.d.ts.map +1 -0
  4. package/dest/config.js +24 -0
  5. package/dest/indexeddb/array.d.ts +21 -0
  6. package/dest/indexeddb/array.d.ts.map +1 -0
  7. package/dest/indexeddb/array.js +104 -0
  8. package/dest/indexeddb/index.d.ts +7 -0
  9. package/dest/indexeddb/index.d.ts.map +1 -0
  10. package/dest/indexeddb/index.js +19 -0
  11. package/dest/indexeddb/map.d.ts +26 -0
  12. package/dest/indexeddb/map.d.ts.map +1 -0
  13. package/dest/indexeddb/map.js +133 -0
  14. package/dest/indexeddb/set.d.ts +17 -0
  15. package/dest/indexeddb/set.d.ts.map +1 -0
  16. package/dest/indexeddb/set.js +24 -0
  17. package/dest/indexeddb/singleton.d.ts +16 -0
  18. package/dest/indexeddb/singleton.d.ts.map +1 -0
  19. package/dest/indexeddb/singleton.js +37 -0
  20. package/dest/indexeddb/store.d.ts +97 -0
  21. package/dest/indexeddb/store.d.ts.map +1 -0
  22. package/dest/indexeddb/store.js +164 -0
  23. package/dest/interfaces/array.d.ts +80 -0
  24. package/dest/interfaces/array.d.ts.map +1 -0
  25. package/dest/interfaces/array.js +3 -0
  26. package/dest/interfaces/array_test_suite.d.ts +3 -0
  27. package/dest/interfaces/array_test_suite.d.ts.map +1 -0
  28. package/dest/interfaces/array_test_suite.js +104 -0
  29. package/dest/interfaces/common.d.ts +23 -0
  30. package/dest/interfaces/common.d.ts.map +1 -0
  31. package/dest/interfaces/common.js +3 -0
  32. package/dest/interfaces/counter.d.ts +59 -0
  33. package/dest/interfaces/counter.d.ts.map +1 -0
  34. package/dest/interfaces/counter.js +1 -0
  35. package/dest/interfaces/index.d.ts +8 -0
  36. package/dest/interfaces/index.d.ts.map +1 -0
  37. package/dest/interfaces/index.js +6 -0
  38. package/dest/interfaces/map.d.ts +134 -0
  39. package/dest/interfaces/map.d.ts.map +1 -0
  40. package/dest/interfaces/map.js +3 -0
  41. package/dest/interfaces/map_test_suite.d.ts +3 -0
  42. package/dest/interfaces/map_test_suite.d.ts.map +1 -0
  43. package/dest/interfaces/map_test_suite.js +151 -0
  44. package/dest/interfaces/set.d.ts +44 -0
  45. package/dest/interfaces/set.d.ts.map +1 -0
  46. package/dest/interfaces/set.js +1 -0
  47. package/dest/interfaces/set_test_suite.d.ts +3 -0
  48. package/dest/interfaces/set_test_suite.d.ts.map +1 -0
  49. package/dest/interfaces/set_test_suite.js +96 -0
  50. package/dest/interfaces/singleton.d.ts +29 -0
  51. package/dest/interfaces/singleton.d.ts.map +1 -0
  52. package/dest/interfaces/singleton.js +4 -0
  53. package/dest/interfaces/singleton_test_suite.d.ts +3 -0
  54. package/dest/interfaces/singleton_test_suite.d.ts.map +1 -0
  55. package/dest/interfaces/singleton_test_suite.js +30 -0
  56. package/dest/interfaces/store.d.ts +145 -0
  57. package/dest/interfaces/store.d.ts.map +1 -0
  58. package/dest/interfaces/store.js +1 -0
  59. package/dest/interfaces/store_test_suite.d.ts +3 -0
  60. package/dest/interfaces/store_test_suite.d.ts.map +1 -0
  61. package/dest/interfaces/store_test_suite.js +37 -0
  62. package/dest/interfaces/utils.d.ts +16 -0
  63. package/dest/interfaces/utils.d.ts.map +1 -0
  64. package/dest/interfaces/utils.js +16 -0
  65. package/dest/lmdb/array.d.ts +23 -0
  66. package/dest/lmdb/array.d.ts.map +1 -0
  67. package/dest/lmdb/array.js +117 -0
  68. package/dest/lmdb/counter.d.ts +19 -0
  69. package/dest/lmdb/counter.d.ts.map +1 -0
  70. package/dest/lmdb/counter.js +50 -0
  71. package/dest/lmdb/index.d.ts +12 -0
  72. package/dest/lmdb/index.d.ts.map +1 -0
  73. package/dest/lmdb/index.js +25 -0
  74. package/dest/lmdb/map.d.ts +52 -0
  75. package/dest/lmdb/map.d.ts.map +1 -0
  76. package/dest/lmdb/map.js +222 -0
  77. package/dest/lmdb/set.d.ts +18 -0
  78. package/dest/lmdb/set.d.ts.map +1 -0
  79. package/dest/lmdb/set.js +32 -0
  80. package/dest/lmdb/singleton.d.ts +14 -0
  81. package/dest/lmdb/singleton.d.ts.map +1 -0
  82. package/dest/lmdb/singleton.js +26 -0
  83. package/dest/lmdb/store.d.ts +110 -0
  84. package/dest/lmdb/store.d.ts.map +1 -0
  85. package/dest/lmdb/store.js +216 -0
  86. package/dest/lmdb-v2/array.d.ts +18 -0
  87. package/dest/lmdb-v2/array.d.ts.map +1 -0
  88. package/dest/lmdb-v2/array.js +101 -0
  89. package/dest/lmdb-v2/factory.d.ts +12 -0
  90. package/dest/lmdb-v2/factory.d.ts.map +1 -0
  91. package/dest/lmdb-v2/factory.js +60 -0
  92. package/dest/lmdb-v2/index.d.ts +3 -0
  93. package/dest/lmdb-v2/index.d.ts.map +1 -0
  94. package/dest/lmdb-v2/index.js +2 -0
  95. package/dest/lmdb-v2/map.d.ts +86 -0
  96. package/dest/lmdb-v2/map.d.ts.map +1 -0
  97. package/dest/lmdb-v2/map.js +193 -0
  98. package/dest/lmdb-v2/message.d.ts +112 -0
  99. package/dest/lmdb-v2/message.d.ts.map +1 -0
  100. package/dest/lmdb-v2/message.js +18 -0
  101. package/dest/lmdb-v2/read_transaction.d.ts +14 -0
  102. package/dest/lmdb-v2/read_transaction.d.ts.map +1 -0
  103. package/dest/lmdb-v2/read_transaction.js +101 -0
  104. package/dest/lmdb-v2/singleton.d.ts +12 -0
  105. package/dest/lmdb-v2/singleton.d.ts.map +1 -0
  106. package/dest/lmdb-v2/singleton.js +31 -0
  107. package/dest/lmdb-v2/store.d.ts +43 -0
  108. package/dest/lmdb-v2/store.d.ts.map +1 -0
  109. package/dest/lmdb-v2/store.js +181 -0
  110. package/dest/lmdb-v2/utils.d.ts +19 -0
  111. package/dest/lmdb-v2/utils.d.ts.map +1 -0
  112. package/dest/lmdb-v2/utils.js +126 -0
  113. package/dest/lmdb-v2/write_transaction.d.ts +19 -0
  114. package/dest/lmdb-v2/write_transaction.d.ts.map +1 -0
  115. package/dest/lmdb-v2/write_transaction.js +254 -0
  116. package/dest/stores/index.d.ts +2 -0
  117. package/dest/stores/index.d.ts.map +1 -0
  118. package/dest/stores/index.js +1 -0
  119. package/dest/stores/l2_tips_store.d.ts +13 -0
  120. package/dest/stores/l2_tips_store.d.ts.map +1 -0
  121. package/dest/stores/l2_tips_store.js +65 -0
  122. package/dest/utils.d.ts +12 -0
  123. package/dest/utils.d.ts.map +1 -0
  124. package/dest/utils.js +24 -0
  125. package/package.json +99 -0
  126. package/src/config.ts +34 -0
  127. package/src/indexeddb/array.ts +118 -0
  128. package/src/indexeddb/index.ts +29 -0
  129. package/src/indexeddb/map.ts +142 -0
  130. package/src/indexeddb/set.ts +37 -0
  131. package/src/indexeddb/singleton.ts +49 -0
  132. package/src/indexeddb/store.ts +197 -0
  133. package/src/interfaces/array.ts +90 -0
  134. package/src/interfaces/array_test_suite.ts +130 -0
  135. package/src/interfaces/common.ts +20 -0
  136. package/src/interfaces/counter.ts +65 -0
  137. package/src/interfaces/index.ts +7 -0
  138. package/src/interfaces/map.ts +151 -0
  139. package/src/interfaces/map_test_suite.ts +143 -0
  140. package/src/interfaces/set.ts +48 -0
  141. package/src/interfaces/set_test_suite.ts +81 -0
  142. package/src/interfaces/singleton.ts +29 -0
  143. package/src/interfaces/singleton_test_suite.ts +46 -0
  144. package/src/interfaces/store.ts +177 -0
  145. package/src/interfaces/store_test_suite.ts +56 -0
  146. package/src/interfaces/utils.ts +21 -0
  147. package/src/lmdb/array.ts +139 -0
  148. package/src/lmdb/counter.ts +67 -0
  149. package/src/lmdb/index.ts +37 -0
  150. package/src/lmdb/map.ts +248 -0
  151. package/src/lmdb/set.ts +45 -0
  152. package/src/lmdb/singleton.ts +35 -0
  153. package/src/lmdb/store.ts +263 -0
  154. package/src/lmdb-v2/array.ts +115 -0
  155. package/src/lmdb-v2/factory.ts +98 -0
  156. package/src/lmdb-v2/index.ts +2 -0
  157. package/src/lmdb-v2/map.ts +233 -0
  158. package/src/lmdb-v2/message.ts +146 -0
  159. package/src/lmdb-v2/read_transaction.ts +116 -0
  160. package/src/lmdb-v2/singleton.ts +34 -0
  161. package/src/lmdb-v2/store.ts +237 -0
  162. package/src/lmdb-v2/utils.ts +150 -0
  163. package/src/lmdb-v2/write_transaction.ts +314 -0
  164. package/src/stores/index.ts +1 -0
  165. package/src/stores/l2_tips_store.ts +70 -0
  166. package/src/utils.ts +40 -0
@@ -0,0 +1,233 @@
1
+ import { Encoder } from 'msgpackr';
2
+
3
+ import type { Key, Range } from '../interfaces/common.js';
4
+ import type { AztecAsyncMap, AztecAsyncMultiMap } from '../interfaces/map.js';
5
+ import type { ReadTransaction } from './read_transaction.js';
6
+ import { type AztecLMDBStoreV2, execInReadTx, execInWriteTx } from './store.js';
7
+ import { deserializeKey, maxKey, minKey, serializeKey } from './utils.js';
8
+
9
+ export class LMDBMap<K extends Key, V> implements AztecAsyncMap<K, V> {
10
+ private prefix: string;
11
+ private encoder = new Encoder();
12
+
13
+ constructor(private store: AztecLMDBStoreV2, name: string) {
14
+ this.prefix = `map:${name}`;
15
+ }
16
+ /**
17
+ * Sets the value at the given key.
18
+ * @param key - The key to set the value at
19
+ * @param val - The value to set
20
+ */
21
+ set(key: K, val: V): Promise<void> {
22
+ return execInWriteTx(this.store, tx => tx.set(serializeKey(this.prefix, key), this.encoder.pack(val)));
23
+ }
24
+
25
+ /**
26
+ * Sets the value at the given key if it does not already exist.
27
+ * @param key - The key to set the value at
28
+ * @param val - The value to set
29
+ */
30
+ setIfNotExists(key: K, val: V): Promise<boolean> {
31
+ return execInWriteTx(this.store, async tx => {
32
+ const strKey = serializeKey(this.prefix, key);
33
+ const exists = !!(await tx.get(strKey));
34
+ if (!exists) {
35
+ await tx.set(strKey, this.encoder.pack(val));
36
+ return true;
37
+ }
38
+ return false;
39
+ });
40
+ }
41
+
42
+ /**
43
+ * Deletes the value at the given key.
44
+ * @param key - The key to delete the value at
45
+ */
46
+ delete(key: K): Promise<void> {
47
+ return execInWriteTx(this.store, tx => tx.remove(serializeKey(this.prefix, key)));
48
+ }
49
+
50
+ getAsync(key: K): Promise<V | undefined> {
51
+ return execInReadTx(this.store, async tx => {
52
+ const val = await tx.get(serializeKey(this.prefix, key));
53
+ return val ? this.encoder.unpack(val) : undefined;
54
+ });
55
+ }
56
+
57
+ hasAsync(key: K): Promise<boolean> {
58
+ return execInReadTx(this.store, async tx => !!(await tx.get(serializeKey(this.prefix, key))));
59
+ }
60
+
61
+ /**
62
+ * Iterates over the map's key-value entries in the key's natural order
63
+ * @param range - The range of keys to iterate over
64
+ */
65
+ async *entriesAsync(range?: Range<K>): AsyncIterableIterator<[K, V]> {
66
+ const reverse = range?.reverse ?? false;
67
+ const startKey = range?.start ? serializeKey(this.prefix, range.start) : minKey(this.prefix);
68
+
69
+ const endKey = range?.end ? serializeKey(this.prefix, range.end) : reverse ? maxKey(this.prefix) : undefined;
70
+
71
+ let tx: ReadTransaction | undefined = this.store.getCurrentWriteTx();
72
+ const shouldClose = !tx;
73
+ tx ??= this.store.getReadTx();
74
+
75
+ try {
76
+ for await (const [key, val] of tx.iterate(
77
+ reverse ? endKey! : startKey,
78
+ reverse ? startKey : endKey,
79
+ reverse,
80
+ range?.limit,
81
+ )) {
82
+ const deserializedKey = deserializeKey<K>(this.prefix, key);
83
+ if (!deserializedKey) {
84
+ break;
85
+ }
86
+ yield [deserializedKey, this.encoder.unpack(val)];
87
+ }
88
+ } finally {
89
+ if (shouldClose) {
90
+ tx.close();
91
+ }
92
+ }
93
+ }
94
+
95
+ /**
96
+ * Iterates over the map's values in the key's natural order
97
+ * @param range - The range of keys to iterate over
98
+ */
99
+ async *valuesAsync(range?: Range<K>): AsyncIterableIterator<V> {
100
+ for await (const [_, value] of this.entriesAsync(range)) {
101
+ yield value;
102
+ }
103
+ }
104
+
105
+ /**
106
+ * Iterates over the map's keys in the key's natural order
107
+ * @param range - The range of keys to iterate over
108
+ */
109
+ async *keysAsync(range?: Range<K>): AsyncIterableIterator<K> {
110
+ for await (const [key, _] of this.entriesAsync(range)) {
111
+ yield key;
112
+ }
113
+ }
114
+ }
115
+
116
+ export class LMDBMultiMap<K extends Key, V> implements AztecAsyncMultiMap<K, V> {
117
+ private prefix: string;
118
+ private encoder = new Encoder();
119
+ constructor(private store: AztecLMDBStoreV2, name: string) {
120
+ this.prefix = `multimap:${name}`;
121
+ }
122
+
123
+ /**
124
+ * Sets the value at the given key.
125
+ * @param key - The key to set the value at
126
+ * @param val - The value to set
127
+ */
128
+ set(key: K, val: V): Promise<void> {
129
+ return execInWriteTx(this.store, tx => tx.setIndex(serializeKey(this.prefix, key), this.encoder.pack(val)));
130
+ }
131
+
132
+ /**
133
+ * Sets the value at the given key if it does not already exist.
134
+ * @param key - The key to set the value at
135
+ * @param val - The value to set
136
+ */
137
+ setIfNotExists(key: K, val: V): Promise<boolean> {
138
+ return execInWriteTx(this.store, async tx => {
139
+ const exists = !!(await this.getAsync(key));
140
+ if (!exists) {
141
+ await tx.setIndex(serializeKey(this.prefix, key), this.encoder.pack(val));
142
+ return true;
143
+ }
144
+ return false;
145
+ });
146
+ }
147
+
148
+ /**
149
+ * Deletes the value at the given key.
150
+ * @param key - The key to delete the value at
151
+ */
152
+ delete(key: K): Promise<void> {
153
+ return execInWriteTx(this.store, tx => tx.removeIndex(serializeKey(this.prefix, key)));
154
+ }
155
+
156
+ getAsync(key: K): Promise<V | undefined> {
157
+ return execInReadTx(this.store, async tx => {
158
+ const val = await tx.getIndex(serializeKey(this.prefix, key));
159
+ return val.length > 0 ? this.encoder.unpack(val[0]) : undefined;
160
+ });
161
+ }
162
+
163
+ hasAsync(key: K): Promise<boolean> {
164
+ return execInReadTx(this.store, async tx => (await tx.getIndex(serializeKey(this.prefix, key))).length > 0);
165
+ }
166
+
167
+ /**
168
+ * Iterates over the map's key-value entries in the key's natural order
169
+ * @param range - The range of keys to iterate over
170
+ */
171
+ async *entriesAsync(range?: Range<K>): AsyncIterableIterator<[K, V]> {
172
+ const reverse = range?.reverse ?? false;
173
+ const startKey = range?.start ? serializeKey(this.prefix, range.start) : minKey(this.prefix);
174
+ const endKey = range?.end ? serializeKey(this.prefix, range.end) : reverse ? maxKey(this.prefix) : undefined;
175
+
176
+ let tx: ReadTransaction | undefined = this.store.getCurrentWriteTx();
177
+ const shouldClose = !tx;
178
+ tx ??= this.store.getReadTx();
179
+
180
+ try {
181
+ for await (const [key, vals] of tx.iterateIndex(
182
+ reverse ? endKey! : startKey,
183
+ reverse ? startKey : endKey,
184
+ reverse,
185
+ range?.limit,
186
+ )) {
187
+ const deserializedKey = deserializeKey<K>(this.prefix, key);
188
+ if (!deserializedKey) {
189
+ break;
190
+ }
191
+
192
+ for (const val of vals) {
193
+ yield [deserializedKey, this.encoder.unpack(val)];
194
+ }
195
+ }
196
+ } finally {
197
+ if (shouldClose) {
198
+ tx.close();
199
+ }
200
+ }
201
+ }
202
+
203
+ /**
204
+ * Iterates over the map's values in the key's natural order
205
+ * @param range - The range of keys to iterate over
206
+ */
207
+ async *valuesAsync(range?: Range<K>): AsyncIterableIterator<V> {
208
+ for await (const [_, value] of this.entriesAsync(range)) {
209
+ yield value;
210
+ }
211
+ }
212
+
213
+ /**
214
+ * Iterates over the map's keys in the key's natural order
215
+ * @param range - The range of keys to iterate over
216
+ */
217
+ async *keysAsync(range?: Range<K>): AsyncIterableIterator<K> {
218
+ for await (const [key, _] of this.entriesAsync(range)) {
219
+ yield key;
220
+ }
221
+ }
222
+
223
+ deleteValue(key: K, val: V | undefined): Promise<void> {
224
+ return execInWriteTx(this.store, tx => tx.removeIndex(serializeKey(this.prefix, key), this.encoder.pack(val)));
225
+ }
226
+
227
+ async *getValuesAsync(key: K): AsyncIterableIterator<V> {
228
+ const values = await execInReadTx(this.store, tx => tx.getIndex(serializeKey(this.prefix, key)));
229
+ for (const value of values) {
230
+ yield this.encoder.unpack(value);
231
+ }
232
+ }
233
+ }
@@ -0,0 +1,146 @@
1
+ export enum Database {
2
+ DATA = 'data',
3
+ INDEX = 'index',
4
+ }
5
+
6
+ export const CURSOR_PAGE_SIZE = 10;
7
+
8
+ export enum LMDBMessageType {
9
+ OPEN_DATABASE = 100,
10
+ GET,
11
+ HAS,
12
+
13
+ START_CURSOR,
14
+ ADVANCE_CURSOR,
15
+ CLOSE_CURSOR,
16
+
17
+ BATCH,
18
+
19
+ STATS,
20
+
21
+ CLOSE,
22
+ }
23
+
24
+ type Key = Uint8Array;
25
+ type Value = Uint8Array;
26
+ type OptionalValues = Array<Value[] | null>;
27
+ type KeyOptionalValues = [Key, null | Array<Value>];
28
+ type KeyValues = [Key, Value[]];
29
+
30
+ interface OpenDatabaseRequest {
31
+ db: string;
32
+ uniqueKeys?: boolean;
33
+ }
34
+
35
+ interface GetRequest {
36
+ keys: Key[];
37
+ db: string;
38
+ }
39
+
40
+ interface GetResponse {
41
+ values: OptionalValues;
42
+ }
43
+
44
+ interface HasRequest {
45
+ entries: KeyOptionalValues[];
46
+ db: string;
47
+ }
48
+
49
+ interface StartCursorRequest {
50
+ key: Key;
51
+ reverse: boolean;
52
+ count: number | null;
53
+ onePage: boolean | null;
54
+ db: string;
55
+ }
56
+
57
+ interface AdvanceCursorRequest {
58
+ cursor: number;
59
+ count: number | null;
60
+ }
61
+
62
+ interface CloseCursorRequest {
63
+ cursor: number;
64
+ }
65
+
66
+ export interface Batch {
67
+ addEntries: Array<KeyValues>;
68
+ removeEntries: Array<KeyOptionalValues>;
69
+ }
70
+
71
+ interface BatchRequest {
72
+ batches: Map<string, Batch>;
73
+ }
74
+
75
+ export type LMDBRequestBody = {
76
+ [LMDBMessageType.OPEN_DATABASE]: OpenDatabaseRequest;
77
+
78
+ [LMDBMessageType.GET]: GetRequest;
79
+ [LMDBMessageType.HAS]: HasRequest;
80
+
81
+ [LMDBMessageType.START_CURSOR]: StartCursorRequest;
82
+ [LMDBMessageType.ADVANCE_CURSOR]: AdvanceCursorRequest;
83
+ [LMDBMessageType.CLOSE_CURSOR]: CloseCursorRequest;
84
+
85
+ [LMDBMessageType.BATCH]: BatchRequest;
86
+
87
+ [LMDBMessageType.STATS]: void;
88
+
89
+ [LMDBMessageType.CLOSE]: void;
90
+ };
91
+
92
+ interface GetResponse {
93
+ values: OptionalValues;
94
+ }
95
+
96
+ interface HasResponse {
97
+ exists: boolean[];
98
+ }
99
+
100
+ interface StartCursorResponse {
101
+ cursor: number | null;
102
+ entries: Array<KeyValues>;
103
+ }
104
+
105
+ interface AdvanceCursorResponse {
106
+ entries: Array<KeyValues>;
107
+ done: boolean;
108
+ }
109
+
110
+ interface BatchResponse {
111
+ durationNs: number;
112
+ }
113
+
114
+ interface BoolResponse {
115
+ ok: true;
116
+ }
117
+
118
+ interface StatsResponse {
119
+ stats: Array<{
120
+ name: string;
121
+ numDataItems: bigint | number;
122
+ totalUsedSize: bigint | number;
123
+ }>;
124
+ dbMapSizeBytes: bigint | number;
125
+ }
126
+
127
+ export type LMDBResponseBody = {
128
+ [LMDBMessageType.OPEN_DATABASE]: BoolResponse;
129
+
130
+ [LMDBMessageType.GET]: GetResponse;
131
+ [LMDBMessageType.HAS]: HasResponse;
132
+
133
+ [LMDBMessageType.START_CURSOR]: StartCursorResponse;
134
+ [LMDBMessageType.ADVANCE_CURSOR]: AdvanceCursorResponse;
135
+ [LMDBMessageType.CLOSE_CURSOR]: BoolResponse;
136
+
137
+ [LMDBMessageType.BATCH]: BatchResponse;
138
+
139
+ [LMDBMessageType.STATS]: StatsResponse;
140
+
141
+ [LMDBMessageType.CLOSE]: BoolResponse;
142
+ };
143
+
144
+ export interface LMDBMessageChannel {
145
+ sendMessage<T extends LMDBMessageType>(msgType: T, body: LMDBRequestBody[T]): Promise<LMDBResponseBody[T]>;
146
+ }
@@ -0,0 +1,116 @@
1
+ import { CURSOR_PAGE_SIZE, Database, type LMDBMessageChannel, LMDBMessageType } from './message.js';
2
+
3
+ export class ReadTransaction {
4
+ protected open = true;
5
+
6
+ constructor(protected channel: LMDBMessageChannel) {}
7
+
8
+ public close(): void {
9
+ if (!this.open) {
10
+ return;
11
+ }
12
+ this.open = false;
13
+ }
14
+
15
+ protected assertIsOpen() {
16
+ if (!this.open) {
17
+ throw new Error('Transaction is closed');
18
+ }
19
+ }
20
+
21
+ public async get(key: Uint8Array): Promise<Uint8Array | undefined> {
22
+ this.assertIsOpen();
23
+ const response = await this.channel.sendMessage(LMDBMessageType.GET, { keys: [key], db: Database.DATA });
24
+ return response.values[0]?.[0] ?? undefined;
25
+ }
26
+
27
+ public async getIndex(key: Uint8Array): Promise<Uint8Array[]> {
28
+ this.assertIsOpen();
29
+ const response = await this.channel.sendMessage(LMDBMessageType.GET, { keys: [key], db: Database.INDEX });
30
+ return response.values[0] ?? [];
31
+ }
32
+
33
+ public async *iterate(
34
+ startKey: Uint8Array,
35
+ endKey?: Uint8Array,
36
+ reverse = false,
37
+ limit?: number,
38
+ ): AsyncIterable<[Uint8Array, Uint8Array]> {
39
+ yield* this.#iterate(Database.DATA, startKey, endKey, reverse, limit, vals => vals[0]);
40
+ }
41
+
42
+ public async *iterateIndex(
43
+ startKey: Uint8Array,
44
+ endKey?: Uint8Array,
45
+ reverse = false,
46
+ limit?: number,
47
+ ): AsyncIterable<[Uint8Array, Uint8Array[]]> {
48
+ yield* this.#iterate(Database.INDEX, startKey, endKey, reverse, limit, vals => vals);
49
+ }
50
+
51
+ async *#iterate<T>(
52
+ db: string,
53
+ startKey: Uint8Array,
54
+ endKey: Uint8Array | undefined,
55
+ reverse: boolean,
56
+ limit: number | undefined,
57
+ map: (val: Uint8Array[]) => T,
58
+ ): AsyncIterable<[Uint8Array, T]> {
59
+ this.assertIsOpen();
60
+
61
+ const response = await this.channel.sendMessage(LMDBMessageType.START_CURSOR, {
62
+ key: startKey,
63
+ reverse,
64
+ count: typeof limit === 'number' ? Math.min(limit, CURSOR_PAGE_SIZE) : CURSOR_PAGE_SIZE,
65
+ onePage: typeof limit === 'number' && limit < CURSOR_PAGE_SIZE,
66
+ db,
67
+ });
68
+
69
+ const cursor = response.cursor;
70
+ let entries = response.entries;
71
+ let done = typeof cursor !== 'number';
72
+ let count = 0;
73
+
74
+ try {
75
+ // emit the first page and any subsequent pages in a while loop
76
+ // NB: end contition is in the middle of the while loop
77
+ while (entries.length > 0) {
78
+ for (const [key, values] of entries) {
79
+ if (typeof limit === 'number' && count >= limit) {
80
+ done = true;
81
+ break;
82
+ }
83
+
84
+ if (endKey) {
85
+ const cmp = Buffer.compare(key, endKey);
86
+ if ((!reverse && cmp >= 0) || (reverse && cmp <= 0)) {
87
+ done = true;
88
+ break;
89
+ }
90
+ }
91
+
92
+ count++;
93
+ yield [key, map(values)];
94
+ }
95
+
96
+ // cursor is null if DB returned everything in the first page
97
+ if (typeof cursor !== 'number' || done) {
98
+ break;
99
+ }
100
+
101
+ const response = await this.channel.sendMessage(LMDBMessageType.ADVANCE_CURSOR, {
102
+ cursor,
103
+ count: CURSOR_PAGE_SIZE,
104
+ });
105
+
106
+ done = response.done;
107
+ entries = response.entries;
108
+ }
109
+ } finally {
110
+ // we might not have anything to close
111
+ if (typeof cursor === 'number') {
112
+ await this.channel.sendMessage(LMDBMessageType.CLOSE_CURSOR, { cursor });
113
+ }
114
+ }
115
+ }
116
+ }
@@ -0,0 +1,34 @@
1
+ import { Encoder } from 'msgpackr';
2
+
3
+ import type { AztecAsyncSingleton } from '../interfaces/singleton.js';
4
+ import { type AztecLMDBStoreV2, execInReadTx, execInWriteTx } from './store.js';
5
+ import { serializeKey } from './utils.js';
6
+
7
+ export class LMDBSingleValue<T> implements AztecAsyncSingleton<T> {
8
+ private key: Uint8Array;
9
+ private encoder = new Encoder();
10
+ constructor(private store: AztecLMDBStoreV2, name: string) {
11
+ this.key = serializeKey(`singleton:${name}`, 'value');
12
+ }
13
+
14
+ getAsync(): Promise<T | undefined> {
15
+ return execInReadTx(this.store, async tx => {
16
+ const val = await tx.get(this.key);
17
+ return val ? this.encoder.unpack(val) : undefined;
18
+ });
19
+ }
20
+
21
+ set(val: T): Promise<boolean> {
22
+ return execInWriteTx(this.store, async tx => {
23
+ await tx.set(this.key, this.encoder.pack(val));
24
+ return true;
25
+ });
26
+ }
27
+
28
+ delete(): Promise<boolean> {
29
+ return execInWriteTx(this.store, async tx => {
30
+ await tx.remove(this.key);
31
+ return true;
32
+ });
33
+ }
34
+ }