@frostpillar/frostpillar-btree 0.2.5 → 0.2.7

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.
package/dist/index.js CHANGED
@@ -6,7 +6,7 @@ import {
6
6
  DEFAULT_MAX_LEAF_ENTRIES,
7
7
  InMemoryBTree,
8
8
  computeAutoScaleTier
9
- } from "./chunk-CZFRT2NN.js";
9
+ } from "./chunk-UGGWGP4E.js";
10
10
 
11
11
  // src/concurrency/helpers.ts
12
12
  var DEFAULT_MAX_RETRIES = 16;
@@ -30,51 +30,110 @@ var assertNeverMutation = (mutation) => {
30
30
  `Unsupported mutation type from shared store: ${String(unknownMutation.type)}`
31
31
  );
32
32
  };
33
- var validateMutationBatch = (mutations, expectedConfigFingerprint) => {
34
- for (const mutation of mutations) {
35
- if (typeof mutation !== "object" || mutation === null) {
36
- throw new BTreeConcurrencyError("Malformed mutation: expected an object.");
33
+ var validatePutManyEntries = (entries) => {
34
+ for (const entry of entries) {
35
+ if (typeof entry !== "object" || entry === null || !("key" in entry) || !("value" in entry)) {
36
+ throw new BTreeConcurrencyError(
37
+ "Malformed putMany mutation: each entry must have key and value."
38
+ );
37
39
  }
38
- const m = mutation;
39
- switch (m.type) {
40
- case "init":
41
- if (typeof m.configFingerprint !== "string") {
42
- throw new BTreeConcurrencyError("Malformed init mutation: missing configFingerprint.");
43
- }
44
- if (expectedConfigFingerprint !== void 0 && m.configFingerprint !== expectedConfigFingerprint) {
45
- throw new BTreeConcurrencyError(
46
- "Config mismatch: store peers must share identical tree config."
47
- );
48
- }
49
- break;
50
- case "put":
51
- if (!("key" in m) || !("value" in m)) {
52
- throw new BTreeConcurrencyError("Malformed put mutation: missing key or value.");
53
- }
54
- break;
55
- case "remove":
56
- if (!("key" in m)) {
57
- throw new BTreeConcurrencyError("Malformed remove mutation: missing key.");
58
- }
59
- break;
60
- case "removeById":
61
- if (!("entryId" in m)) {
62
- throw new BTreeConcurrencyError("Malformed removeById mutation: missing entryId.");
63
- }
64
- break;
65
- case "updateById":
66
- if (!("entryId" in m) || !("value" in m)) {
67
- throw new BTreeConcurrencyError("Malformed updateById mutation: missing entryId or value.");
68
- }
69
- break;
70
- case "popFirst":
71
- case "popLast":
72
- break;
73
- default:
40
+ }
41
+ };
42
+ var validateInitMutation = (m, expectedConfigFingerprint) => {
43
+ if (typeof m.configFingerprint !== "string") {
44
+ throw new BTreeConcurrencyError(
45
+ "Malformed init mutation: missing configFingerprint."
46
+ );
47
+ }
48
+ if (expectedConfigFingerprint !== void 0 && m.configFingerprint !== expectedConfigFingerprint) {
49
+ throw new BTreeConcurrencyError(
50
+ "Config mismatch: store peers must share identical tree config."
51
+ );
52
+ }
53
+ };
54
+ var validateMutationFields = (m) => {
55
+ switch (m.type) {
56
+ case "put":
57
+ if (!("key" in m) || !("value" in m)) {
74
58
  throw new BTreeConcurrencyError(
75
- `Unsupported mutation type from shared store: ${String(m.type)}`
59
+ "Malformed put mutation: missing key or value."
76
60
  );
61
+ }
62
+ break;
63
+ case "remove":
64
+ if (!("key" in m)) {
65
+ throw new BTreeConcurrencyError(
66
+ "Malformed remove mutation: missing key."
67
+ );
68
+ }
69
+ break;
70
+ case "removeById":
71
+ if (!("entryId" in m)) {
72
+ throw new BTreeConcurrencyError(
73
+ "Malformed removeById mutation: missing entryId."
74
+ );
75
+ }
76
+ break;
77
+ case "updateById":
78
+ if (!("entryId" in m) || !("value" in m)) {
79
+ throw new BTreeConcurrencyError(
80
+ "Malformed updateById mutation: missing entryId or value."
81
+ );
82
+ }
83
+ break;
84
+ case "putMany":
85
+ if (!("entries" in m) || !Array.isArray(m.entries)) {
86
+ throw new BTreeConcurrencyError(
87
+ "Malformed putMany mutation: missing entries array."
88
+ );
89
+ }
90
+ validatePutManyEntries(m.entries);
91
+ break;
92
+ case "deleteRange":
93
+ if (!("startKey" in m) || !("endKey" in m)) {
94
+ throw new BTreeConcurrencyError(
95
+ "Malformed deleteRange mutation: missing startKey or endKey."
96
+ );
97
+ }
98
+ break;
99
+ default:
100
+ break;
101
+ }
102
+ };
103
+ var validateSingleMutation = (m, expectedConfigFingerprint) => {
104
+ switch (m.type) {
105
+ case "init":
106
+ validateInitMutation(m, expectedConfigFingerprint);
107
+ break;
108
+ case "put":
109
+ case "remove":
110
+ case "removeById":
111
+ case "updateById":
112
+ case "putMany":
113
+ case "deleteRange":
114
+ validateMutationFields(m);
115
+ break;
116
+ case "popFirst":
117
+ case "popLast":
118
+ case "clear":
119
+ break;
120
+ default:
121
+ throw new BTreeConcurrencyError(
122
+ `Unsupported mutation type from shared store: ${String(m.type)}`
123
+ );
124
+ }
125
+ };
126
+ var validateMutationBatch = (mutations, expectedConfigFingerprint) => {
127
+ for (const mutation of mutations) {
128
+ if (typeof mutation !== "object" || mutation === null) {
129
+ throw new BTreeConcurrencyError(
130
+ "Malformed mutation: expected an object."
131
+ );
77
132
  }
133
+ validateSingleMutation(
134
+ mutation,
135
+ expectedConfigFingerprint
136
+ );
78
137
  }
79
138
  };
80
139
  var normalizeMaxRetries = (value) => {
@@ -104,9 +163,7 @@ var normalizeReadMode = (value) => {
104
163
  return "strong";
105
164
  }
106
165
  if (value !== "strong" && value !== "local") {
107
- throw new BTreeConcurrencyError(
108
- `readMode: must be 'strong' or 'local'.`
109
- );
166
+ throw new BTreeConcurrencyError(`readMode: must be 'strong' or 'local'.`);
110
167
  }
111
168
  return value;
112
169
  };
@@ -135,79 +192,170 @@ function assertAppendVersionContract(expectedVersion, appendResult) {
135
192
  }
136
193
  }
137
194
 
138
- // src/concurrency/ConcurrentInMemoryBTree.ts
139
- var ConcurrentInMemoryBTree = class {
140
- constructor(config) {
141
- this.store = config.store;
142
- this.maxRetries = normalizeMaxRetries(config.maxRetries);
143
- this.maxSyncMutationsPerBatch = normalizeMaxSyncMutationsPerBatch(
144
- config.maxSyncMutationsPerBatch
195
+ // src/concurrency/writeOps.ts
196
+ var applyMutationLocal = (tree, mutation, onInit) => {
197
+ switch (mutation.type) {
198
+ case "init":
199
+ onInit();
200
+ return null;
201
+ case "put":
202
+ return tree.put(mutation.key, mutation.value);
203
+ case "putMany":
204
+ return tree.putMany(mutation.entries);
205
+ case "remove":
206
+ return tree.remove(mutation.key);
207
+ case "removeById":
208
+ return tree.removeById(mutation.entryId);
209
+ case "updateById":
210
+ return tree.updateById(mutation.entryId, mutation.value);
211
+ case "popFirst":
212
+ return tree.popFirst();
213
+ case "popLast":
214
+ return tree.popLast();
215
+ case "deleteRange":
216
+ return tree.deleteRange(
217
+ mutation.startKey,
218
+ mutation.endKey,
219
+ mutation.options
220
+ );
221
+ case "clear":
222
+ tree.clear();
223
+ return null;
224
+ default:
225
+ return assertNeverMutation(mutation);
226
+ }
227
+ };
228
+ var createPutEvaluator = (duplicateKeys, key, value) => {
229
+ return (tree) => {
230
+ if (duplicateKeys === "reject" && tree.hasKey(key)) {
231
+ throw new BTreeValidationError("Duplicate key rejected.");
232
+ }
233
+ return { type: "put", key, value };
234
+ };
235
+ };
236
+ var createRemoveEvaluator = (key) => {
237
+ return (tree) => {
238
+ return tree.hasKey(key) ? { type: "remove", key } : null;
239
+ };
240
+ };
241
+ var createRemoveByIdEvaluator = (entryId) => {
242
+ return (tree) => {
243
+ return tree.peekById(entryId) !== null ? { type: "removeById", entryId } : null;
244
+ };
245
+ };
246
+ var createUpdateByIdEvaluator = (entryId, value) => {
247
+ return (tree) => {
248
+ return tree.peekById(entryId) !== null ? { type: "updateById", entryId, value } : null;
249
+ };
250
+ };
251
+ var createPopFirstEvaluator = () => {
252
+ return (tree) => {
253
+ return tree.peekFirst() !== null ? { type: "popFirst" } : null;
254
+ };
255
+ };
256
+ var createPopLastEvaluator = () => {
257
+ return (tree) => {
258
+ return tree.peekLast() !== null ? { type: "popLast" } : null;
259
+ };
260
+ };
261
+ var createPutManyEvaluator = (entries, duplicateKeys, compareKeys) => {
262
+ return (tree) => {
263
+ const strictlyAscending = duplicateKeys !== "allow";
264
+ for (let i = 1; i < entries.length; i += 1) {
265
+ const cmp = compareKeys(entries[i - 1].key, entries[i].key);
266
+ if (cmp > 0) {
267
+ throw new BTreeValidationError(
268
+ "putMany: entries not in ascending order."
269
+ );
270
+ }
271
+ if (strictlyAscending && cmp === 0) {
272
+ throw new BTreeValidationError(
273
+ duplicateKeys === "reject" ? "putMany: duplicate key rejected." : "putMany: equal keys not allowed in strict mode."
274
+ );
275
+ }
276
+ }
277
+ if (duplicateKeys === "reject") {
278
+ for (const entry of entries) {
279
+ if (tree.hasKey(entry.key)) {
280
+ throw new BTreeValidationError("Duplicate key rejected.");
281
+ }
282
+ }
283
+ }
284
+ return { type: "putMany", entries };
285
+ };
286
+ };
287
+ var createDeleteRangeEvaluator = (startKey, endKey, options) => {
288
+ return (tree) => {
289
+ const count = tree.count(startKey, endKey, options);
290
+ if (count === 0) {
291
+ return null;
292
+ }
293
+ return { type: "deleteRange", startKey, endKey, options };
294
+ };
295
+ };
296
+ var createClearEvaluator = () => {
297
+ return () => ({ type: "clear" });
298
+ };
299
+
300
+ // src/concurrency/syncLogValidation.ts
301
+ var validateSyncLog = (log, maxSyncMutationsPerBatch) => {
302
+ if (typeof log.version !== "bigint") {
303
+ throw new BTreeConcurrencyError("Store contract: version must be bigint.");
304
+ }
305
+ if (!Array.isArray(log.mutations)) {
306
+ throw new BTreeConcurrencyError(
307
+ "Store contract: mutations must be an array."
145
308
  );
146
- this.duplicateKeys = config.duplicateKeys ?? "replace";
147
- this.readMode = normalizeReadMode(config.readMode);
148
- this.configFingerprint = computeConfigFingerprint(config);
149
- this.tree = new InMemoryBTree({
150
- compareKeys: config.compareKeys,
151
- maxLeafEntries: config.maxLeafEntries,
152
- maxBranchChildren: config.maxBranchChildren,
153
- duplicateKeys: config.duplicateKeys,
154
- enableEntryIdLookup: config.enableEntryIdLookup,
155
- autoScale: config.autoScale
156
- });
309
+ }
310
+ if (log.mutations.length > maxSyncMutationsPerBatch) {
311
+ throw new BTreeConcurrencyError(
312
+ `Sync batch exceeded limit (${String(maxSyncMutationsPerBatch)}).`
313
+ );
314
+ }
315
+ };
316
+
317
+ // src/concurrency/coordinator.ts
318
+ var Coordinator = class {
319
+ constructor(tree, store, maxRetries, maxSyncMutationsPerBatch, configFingerprint, readMode) {
320
+ this.tree = tree;
321
+ this.store = store;
322
+ this.maxRetries = maxRetries;
323
+ this.maxSyncMutationsPerBatch = maxSyncMutationsPerBatch;
324
+ this.configFingerprint = configFingerprint;
325
+ this.readMode = readMode;
157
326
  this.currentVersion = 0n;
158
327
  this.operationQueue = Promise.resolve();
159
328
  this.initSeen = false;
160
- }
161
- async sync() {
162
- await this.runExclusive(async () => {
163
- await this.syncUnlocked();
164
- });
329
+ this.corrupted = false;
165
330
  }
166
331
  async syncUnlocked() {
167
332
  const log = await this.store.getLogEntriesSince(this.currentVersion);
168
- if (typeof log.version !== "bigint") {
169
- throw new BTreeConcurrencyError("Store contract: version must be bigint.");
170
- }
171
- if (!Array.isArray(log.mutations)) {
172
- throw new BTreeConcurrencyError("Store contract: mutations must be an array.");
173
- }
174
- if (log.mutations.length > this.maxSyncMutationsPerBatch) {
175
- throw new BTreeConcurrencyError(
176
- `Sync batch exceeded limit (${String(this.maxSyncMutationsPerBatch)}).`
177
- );
178
- }
179
- if (log.version <= this.currentVersion) {
180
- return;
181
- }
333
+ validateSyncLog(log, this.maxSyncMutationsPerBatch);
334
+ if (log.version <= this.currentVersion) return;
182
335
  validateMutationBatch(log.mutations, this.configFingerprint);
183
- for (const mutation of log.mutations) {
184
- this.applyMutationLocal(mutation);
185
- }
186
- this.currentVersion = log.version;
187
- }
188
- applyMutationLocal(mutation) {
189
- switch (mutation.type) {
190
- case "init":
336
+ try {
337
+ const markInit = () => {
191
338
  this.initSeen = true;
192
- return null;
193
- case "put":
194
- return this.tree.put(mutation.key, mutation.value);
195
- case "remove":
196
- return this.tree.remove(mutation.key);
197
- case "removeById":
198
- return this.tree.removeById(mutation.entryId);
199
- case "updateById":
200
- return this.tree.updateById(mutation.entryId, mutation.value);
201
- case "popFirst":
202
- return this.tree.popFirst();
203
- case "popLast":
204
- return this.tree.popLast();
205
- default:
206
- return assertNeverMutation(mutation);
339
+ };
340
+ for (const mutation of log.mutations)
341
+ applyMutationLocal(this.tree, mutation, markInit);
342
+ this.currentVersion = log.version;
343
+ } catch (error) {
344
+ this.corrupted = true;
345
+ const cause = error instanceof Error ? error.message : String(error);
346
+ throw new BTreeConcurrencyError(
347
+ `Replay failure: instance is permanently corrupted. Discard and create a new instance. Cause: ${cause}`
348
+ );
207
349
  }
208
350
  }
209
351
  runExclusive(operation) {
210
- const run = async () => operation();
352
+ const run = async () => {
353
+ if (this.corrupted)
354
+ throw new BTreeConcurrencyError(
355
+ "Instance is permanently corrupted. Discard and create a new instance."
356
+ );
357
+ return operation();
358
+ };
211
359
  const result = this.operationQueue.then(run, run);
212
360
  this.operationQueue = result.then(
213
361
  () => void 0,
@@ -217,137 +365,207 @@ var ConcurrentInMemoryBTree = class {
217
365
  }
218
366
  readOp(fn) {
219
367
  return this.runExclusive(async () => {
220
- if (this.readMode === "strong") {
221
- await this.syncUnlocked();
222
- }
368
+ if (this.readMode === "strong") await this.syncUnlocked();
223
369
  return fn(this.tree);
224
370
  });
225
371
  }
226
- async appendMutationAndApplyUnlocked(evaluate) {
372
+ async appendAndApply(evaluate) {
227
373
  for (let attempt = 0; attempt < this.maxRetries; attempt += 1) {
228
374
  await this.syncUnlocked();
229
375
  const mutation = evaluate(this.tree);
230
- if (mutation === null) {
231
- return null;
232
- }
376
+ if (mutation === null) return null;
233
377
  const expectedVersion = this.currentVersion;
234
- const mutations = this.initSeen ? [mutation] : [{ type: "init", configFingerprint: this.configFingerprint }, mutation];
378
+ const mutations = this.initSeen ? [mutation] : [
379
+ { type: "init", configFingerprint: this.configFingerprint },
380
+ mutation
381
+ ];
235
382
  const appendResult = await this.store.append(expectedVersion, mutations);
236
383
  assertAppendVersionContract(expectedVersion, appendResult);
237
384
  if (appendResult.applied) {
238
- for (const m of mutations) {
239
- if (m === mutation) break;
240
- this.applyMutationLocal(m);
385
+ try {
386
+ const markInit = () => {
387
+ this.initSeen = true;
388
+ };
389
+ for (const m of mutations) {
390
+ if (m === mutation) break;
391
+ applyMutationLocal(this.tree, m, markInit);
392
+ }
393
+ const result = applyMutationLocal(
394
+ this.tree,
395
+ mutation,
396
+ markInit
397
+ );
398
+ this.currentVersion = appendResult.version;
399
+ return result;
400
+ } catch (error) {
401
+ this.corrupted = true;
402
+ const cause = error instanceof Error ? error.message : String(error);
403
+ throw new BTreeConcurrencyError(
404
+ `Local apply failure after successful append: instance is permanently corrupted. Discard and create a new instance. Cause: ${cause}`
405
+ );
241
406
  }
242
- const localResult = this.applyMutationLocal(mutation);
243
- this.currentVersion = appendResult.version;
244
- return localResult;
245
407
  }
246
408
  }
247
409
  throw new BTreeConcurrencyError(
248
410
  `Mutation failed after ${String(this.maxRetries)} retries.`
249
411
  );
250
412
  }
251
- async put(key, value) {
252
- return this.runExclusive(async () => {
253
- return this.appendMutationAndApplyUnlocked(
254
- (tree) => {
255
- if (this.duplicateKeys === "reject" && tree.hasKey(key)) {
256
- throw new BTreeValidationError("Duplicate key rejected.");
257
- }
258
- return { type: "put", key, value };
259
- }
260
- );
413
+ writeOp(evaluator) {
414
+ return this.runExclusive(async () => this.appendAndApply(evaluator));
415
+ }
416
+ };
417
+
418
+ // src/concurrency/ConcurrentInMemoryBTree.ts
419
+ var ConcurrentInMemoryBTree = class {
420
+ constructor(config) {
421
+ this.compareKeys = config.compareKeys;
422
+ this.duplicateKeys = config.duplicateKeys ?? "replace";
423
+ const tree = new InMemoryBTree({
424
+ compareKeys: config.compareKeys,
425
+ maxLeafEntries: config.maxLeafEntries,
426
+ maxBranchChildren: config.maxBranchChildren,
427
+ duplicateKeys: config.duplicateKeys,
428
+ enableEntryIdLookup: config.enableEntryIdLookup,
429
+ autoScale: config.autoScale,
430
+ deleteRebalancePolicy: config.deleteRebalancePolicy
261
431
  });
432
+ this.coord = new Coordinator(
433
+ tree,
434
+ config.store,
435
+ normalizeMaxRetries(config.maxRetries),
436
+ normalizeMaxSyncMutationsPerBatch(config.maxSyncMutationsPerBatch),
437
+ computeConfigFingerprint(config),
438
+ normalizeReadMode(config.readMode)
439
+ );
262
440
  }
263
- async remove(key) {
264
- return this.runExclusive(async () => {
265
- return this.appendMutationAndApplyUnlocked(
266
- (tree) => {
267
- return tree.hasKey(key) ? { type: "remove", key } : null;
268
- }
269
- );
441
+ async sync() {
442
+ await this.coord.runExclusive(async () => {
443
+ await this.coord.syncUnlocked();
270
444
  });
271
445
  }
272
- async removeById(entryId) {
273
- return this.runExclusive(async () => {
274
- return this.appendMutationAndApplyUnlocked(
275
- (tree) => {
276
- return tree.peekById(entryId) !== null ? { type: "removeById", entryId } : null;
277
- }
278
- );
446
+ async syncThenRead(fn) {
447
+ return this.coord.runExclusive(async () => {
448
+ await this.coord.syncUnlocked();
449
+ return fn(this.coord.tree);
279
450
  });
280
451
  }
452
+ async put(key, value) {
453
+ return this.coord.writeOp(
454
+ createPutEvaluator(this.duplicateKeys, key, value)
455
+ );
456
+ }
457
+ async remove(key) {
458
+ return this.coord.writeOp(createRemoveEvaluator(key));
459
+ }
460
+ async removeById(entryId) {
461
+ return this.coord.writeOp(createRemoveByIdEvaluator(entryId));
462
+ }
281
463
  async updateById(entryId, value) {
282
- return this.runExclusive(async () => {
283
- return this.appendMutationAndApplyUnlocked(
284
- (tree) => {
285
- return tree.peekById(entryId) !== null ? { type: "updateById", entryId, value } : null;
286
- }
287
- );
288
- });
464
+ return this.coord.writeOp(createUpdateByIdEvaluator(entryId, value));
289
465
  }
290
466
  async popFirst() {
291
- return this.runExclusive(async () => {
292
- return this.appendMutationAndApplyUnlocked((tree) => {
293
- return tree.peekFirst() !== null ? { type: "popFirst" } : null;
294
- });
295
- });
467
+ return this.coord.writeOp(createPopFirstEvaluator());
468
+ }
469
+ async popLast() {
470
+ return this.coord.writeOp(createPopLastEvaluator());
471
+ }
472
+ async putMany(entries) {
473
+ if (entries.length === 0) return [];
474
+ return this.coord.writeOp(
475
+ createPutManyEvaluator(entries, this.duplicateKeys, this.compareKeys)
476
+ );
477
+ }
478
+ async deleteRange(startKey, endKey, options) {
479
+ const result = await this.coord.writeOp(
480
+ createDeleteRangeEvaluator(startKey, endKey, options)
481
+ );
482
+ return result ?? 0;
483
+ }
484
+ async clear() {
485
+ await this.coord.writeOp(createClearEvaluator());
296
486
  }
297
487
  async get(key) {
298
- return this.readOp((tree) => tree.get(key));
488
+ return this.coord.readOp((t) => t.get(key));
299
489
  }
300
490
  async hasKey(key) {
301
- return this.readOp((tree) => tree.hasKey(key));
491
+ return this.coord.readOp((t) => t.hasKey(key));
302
492
  }
303
493
  async findFirst(key) {
304
- return this.readOp((tree) => tree.findFirst(key));
494
+ return this.coord.readOp((t) => t.findFirst(key));
305
495
  }
306
496
  async findLast(key) {
307
- return this.readOp((tree) => tree.findLast(key));
497
+ return this.coord.readOp((t) => t.findLast(key));
308
498
  }
309
499
  async range(startKey, endKey, options) {
310
- return this.readOp((tree) => tree.range(startKey, endKey, options));
500
+ return this.coord.readOp((t) => t.range(startKey, endKey, options));
311
501
  }
312
502
  async snapshot() {
313
- return this.readOp((tree) => tree.snapshot());
503
+ return this.coord.readOp((t) => t.snapshot());
314
504
  }
315
505
  async size() {
316
- return this.readOp((tree) => tree.size());
506
+ return this.coord.readOp((t) => t.size());
317
507
  }
318
508
  async assertInvariants() {
319
- await this.readOp((tree) => tree.assertInvariants());
509
+ await this.coord.readOp((t) => t.assertInvariants());
320
510
  }
321
511
  async getStats() {
322
- return this.readOp((tree) => tree.getStats());
512
+ return this.coord.readOp((t) => t.getStats());
323
513
  }
324
514
  async peekFirst() {
325
- return this.readOp((tree) => tree.peekFirst());
515
+ return this.coord.readOp((t) => t.peekFirst());
326
516
  }
327
517
  async peekLast() {
328
- return this.readOp((tree) => tree.peekLast());
329
- }
330
- async popLast() {
331
- return this.runExclusive(async () => {
332
- return this.appendMutationAndApplyUnlocked((tree) => {
333
- return tree.peekLast() !== null ? { type: "popLast" } : null;
334
- });
335
- });
518
+ return this.coord.readOp((t) => t.peekLast());
336
519
  }
337
520
  async peekById(entryId) {
338
- return this.readOp((tree) => tree.peekById(entryId));
521
+ return this.coord.readOp((t) => t.peekById(entryId));
339
522
  }
340
523
  async count(startKey, endKey, options) {
341
- return this.readOp((tree) => tree.count(startKey, endKey, options));
524
+ return this.coord.readOp((t) => t.count(startKey, endKey, options));
342
525
  }
343
526
  async nextHigherKey(key) {
344
- return this.readOp((tree) => tree.nextHigherKey(key));
527
+ return this.coord.readOp((t) => t.nextHigherKey(key));
345
528
  }
346
529
  async nextLowerKey(key) {
347
- return this.readOp((tree) => tree.nextLowerKey(key));
530
+ return this.coord.readOp((t) => t.nextLowerKey(key));
348
531
  }
349
532
  async getPairOrNextLower(key) {
350
- return this.readOp((tree) => tree.getPairOrNextLower(key));
533
+ return this.coord.readOp((t) => t.getPairOrNextLower(key));
534
+ }
535
+ async entries() {
536
+ return this.coord.readOp((t) => Array.from(t.entries()));
537
+ }
538
+ async entriesReversed() {
539
+ return this.coord.readOp((t) => Array.from(t.entriesReversed()));
540
+ }
541
+ async keys() {
542
+ return this.coord.readOp((t) => Array.from(t.keys()));
543
+ }
544
+ async values() {
545
+ return this.coord.readOp((t) => Array.from(t.values()));
546
+ }
547
+ async forEach(callback) {
548
+ await this.coord.readOp((t) => {
549
+ t.forEach(callback);
550
+ });
551
+ }
552
+ async forEachRange(startKey, endKey, callback, options) {
553
+ await this.coord.readOp((t) => {
554
+ t.forEachRange(startKey, endKey, callback, options);
555
+ });
556
+ }
557
+ async *[Symbol.asyncIterator]() {
558
+ const all = await this.entries();
559
+ for (const entry of all) yield entry;
560
+ }
561
+ async clone() {
562
+ return this.coord.readOp((t) => t.clone());
563
+ }
564
+ async toJSON() {
565
+ return this.coord.readOp((t) => t.toJSON());
566
+ }
567
+ static fromJSON(json, compareKeys) {
568
+ return InMemoryBTree.fromJSON(json, compareKeys);
351
569
  }
352
570
  };
353
571
  export {