@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.cjs CHANGED
@@ -51,54 +51,10 @@ var BTreeConcurrencyError = class extends Error {
51
51
  }
52
52
  };
53
53
 
54
- // src/btree/types.ts
55
- var DEFAULT_MAX_LEAF_ENTRIES = 64;
56
- var DEFAULT_MAX_BRANCH_CHILDREN = 64;
57
- var MIN_NODE_CAPACITY = 3;
58
- var MAX_NODE_CAPACITY = 16384;
59
- var NODE_LEAF = 0;
60
- var NODE_BRANCH = 1;
61
- var normalizeDuplicateKeyPolicy = (value) => {
62
- if (value === void 0) {
63
- return "replace";
64
- }
65
- if (value !== "allow" && value !== "reject" && value !== "replace") {
66
- throw new BTreeValidationError(
67
- `Invalid duplicateKeys option.`
68
- );
69
- }
70
- return value;
71
- };
72
- var isLeafNode = (node) => {
73
- return node.kind === NODE_LEAF;
74
- };
75
- var writeMinKeyTo = (node, target) => {
76
- if (node.kind === NODE_LEAF) {
77
- if (node.entryOffset >= node.entries.length) return false;
78
- const e = node.entries[node.entryOffset];
79
- target.key = e.key;
80
- target.sequence = e.entryId;
81
- return true;
82
- }
83
- if (node.childOffset >= node.keys.length) return false;
84
- target.key = node.keys[node.childOffset].key;
85
- target.sequence = node.keys[node.childOffset].sequence;
86
- return true;
87
- };
88
- var normalizeNodeCapacity = (value, field, defaultValue) => {
89
- if (value === void 0) {
90
- return defaultValue;
91
- }
92
- if (!Number.isInteger(value) || value < MIN_NODE_CAPACITY || value > MAX_NODE_CAPACITY) {
93
- throw new BTreeValidationError(
94
- `${field}: integer ${MIN_NODE_CAPACITY}\u2013${MAX_NODE_CAPACITY} required.`
95
- );
96
- }
97
- return value;
98
- };
54
+ // src/btree/node-ops.ts
99
55
  var createLeafNode = (entries, parent) => {
100
56
  return {
101
- kind: NODE_LEAF,
57
+ kind: 0,
102
58
  entries,
103
59
  entryOffset: 0,
104
60
  parent,
@@ -110,7 +66,7 @@ var createLeafNode = (entries, parent) => {
110
66
  var createBranchNode = (children, parent) => {
111
67
  const keys = [];
112
68
  const branch = {
113
- kind: NODE_BRANCH,
69
+ kind: 1,
114
70
  children,
115
71
  keys,
116
72
  childOffset: 0,
@@ -121,11 +77,12 @@ var createBranchNode = (children, parent) => {
121
77
  const child = children[i];
122
78
  child.parent = branch;
123
79
  child.indexInParent = i;
124
- const target = { key: void 0, sequence: 0 };
80
+ const target = {
81
+ key: void 0,
82
+ sequence: 0
83
+ };
125
84
  if (!writeMinKeyTo(child, target)) {
126
- throw new BTreeInvariantError(
127
- "branch child has no min key"
128
- );
85
+ throw new BTreeInvariantError("branch child has no min key");
129
86
  }
130
87
  keys.push(target);
131
88
  }
@@ -179,7 +136,14 @@ var leafInsertAt = (leaf, logicalIndex, entry) => {
179
136
  leaf.entryOffset -= 1;
180
137
  leaf.entries[phys - 1] = entry;
181
138
  } else {
182
- leaf.entries.splice(phys, 0, entry);
139
+ const len = leaf.entries.length;
140
+ if (phys >= len) {
141
+ leaf.entries.push(entry);
142
+ } else {
143
+ leaf.entries.push(leaf.entries[len - 1]);
144
+ leaf.entries.copyWithin(phys + 1, phys, len);
145
+ leaf.entries[phys] = entry;
146
+ }
183
147
  }
184
148
  };
185
149
  var leafCompact = (leaf) => {
@@ -208,7 +172,11 @@ var branchInsertAt = (branch, logicalIndex, child, key) => {
208
172
  const phys = branch.childOffset + logicalIndex;
209
173
  const count = branch.children.length - branch.childOffset;
210
174
  if (branch.childOffset > 0 && logicalIndex < count >>> 1) {
211
- branch.children.copyWithin(branch.childOffset - 1, branch.childOffset, phys);
175
+ branch.children.copyWithin(
176
+ branch.childOffset - 1,
177
+ branch.childOffset,
178
+ phys
179
+ );
212
180
  branch.keys.copyWithin(branch.childOffset - 1, branch.childOffset, phys);
213
181
  branch.childOffset -= 1;
214
182
  branch.children[phys - 1] = child;
@@ -229,8 +197,16 @@ var branchRemoveAt = (branch, physIndex) => {
229
197
  const logicalIndex = physIndex - branch.childOffset;
230
198
  const count = branch.children.length - branch.childOffset;
231
199
  if (logicalIndex < count - 1 - logicalIndex) {
232
- branch.children.copyWithin(branch.childOffset + 1, branch.childOffset, physIndex);
233
- branch.keys.copyWithin(branch.childOffset + 1, branch.childOffset, physIndex);
200
+ branch.children.copyWithin(
201
+ branch.childOffset + 1,
202
+ branch.childOffset,
203
+ physIndex
204
+ );
205
+ branch.keys.copyWithin(
206
+ branch.childOffset + 1,
207
+ branch.childOffset,
208
+ physIndex
209
+ );
234
210
  branch.childOffset += 1;
235
211
  for (let i = branch.childOffset; i <= physIndex; i += 1) {
236
212
  branch.children[i].indexInParent = i;
@@ -249,6 +225,60 @@ var branchRemoveAt = (branch, physIndex) => {
249
225
  }
250
226
  };
251
227
 
228
+ // src/btree/types.ts
229
+ var DEFAULT_MAX_LEAF_ENTRIES = 64;
230
+ var DEFAULT_MAX_BRANCH_CHILDREN = 64;
231
+ var MIN_NODE_CAPACITY = 3;
232
+ var MAX_NODE_CAPACITY = 16384;
233
+ var NODE_LEAF = 0;
234
+ var normalizeDuplicateKeyPolicy = (value) => {
235
+ if (value === void 0) {
236
+ return "replace";
237
+ }
238
+ if (value !== "allow" && value !== "reject" && value !== "replace") {
239
+ throw new BTreeValidationError(`Invalid duplicateKeys option.`);
240
+ }
241
+ return value;
242
+ };
243
+ var normalizeDeleteRebalancePolicy = (value) => {
244
+ if (value === void 0) {
245
+ return "standard";
246
+ }
247
+ if (value !== "standard" && value !== "lazy") {
248
+ throw new BTreeValidationError(`Invalid deleteRebalancePolicy option.`);
249
+ }
250
+ return value;
251
+ };
252
+ var freezeEntry = (entry) => Object.freeze(entry);
253
+ var createEntry = (key, entryId, value) => Object.freeze({ key, entryId, value });
254
+ var isLeafNode = (node) => {
255
+ return node.kind === NODE_LEAF;
256
+ };
257
+ var writeMinKeyTo = (node, target) => {
258
+ if (node.kind === NODE_LEAF) {
259
+ if (node.entryOffset >= node.entries.length) return false;
260
+ const e = node.entries[node.entryOffset];
261
+ target.key = e.key;
262
+ target.sequence = e.entryId;
263
+ return true;
264
+ }
265
+ if (node.childOffset >= node.keys.length) return false;
266
+ target.key = node.keys[node.childOffset].key;
267
+ target.sequence = node.keys[node.childOffset].sequence;
268
+ return true;
269
+ };
270
+ var normalizeNodeCapacity = (value, field, defaultValue) => {
271
+ if (value === void 0) {
272
+ return defaultValue;
273
+ }
274
+ if (!Number.isInteger(value) || value < MIN_NODE_CAPACITY || value > MAX_NODE_CAPACITY) {
275
+ throw new BTreeValidationError(
276
+ `${field}: integer ${MIN_NODE_CAPACITY}\u2013${MAX_NODE_CAPACITY} required.`
277
+ );
278
+ }
279
+ return value;
280
+ };
281
+
252
282
  // src/btree/navigation.ts
253
283
  var selectBranchChild = (compare, branch, userKey, sequence) => {
254
284
  const off = branch.childOffset;
@@ -262,7 +292,7 @@ var selectBranchChild = (compare, branch, userKey, sequence) => {
262
292
  const mid = lower + upper >>> 1;
263
293
  const k = branch.keys[mid];
264
294
  const cmp = compare(k.key, userKey);
265
- if ((cmp !== 0 ? cmp : k.sequence - sequence) <= 0) {
295
+ if ((cmp !== 0 ? cmp : k.sequence < sequence ? -1 : k.sequence > sequence ? 1 : 0) <= 0) {
266
296
  selectedIndex = mid;
267
297
  lower = mid + 1;
268
298
  } else {
@@ -287,7 +317,7 @@ var lowerBoundInLeaf = (state, leaf, userKey, sequence) => {
287
317
  const mid = lower + upper >>> 1;
288
318
  const e = leaf.entries[mid];
289
319
  const cmp = compare(e.key, userKey);
290
- if ((cmp !== 0 ? cmp : e.entryId - sequence) < 0) {
320
+ if ((cmp !== 0 ? cmp : e.entryId < sequence ? -1 : e.entryId > sequence ? 1 : 0) < 0) {
291
321
  lower = mid + 1;
292
322
  } else {
293
323
  upper = mid;
@@ -303,7 +333,7 @@ var upperBoundInLeaf = (state, leaf, userKey, sequence) => {
303
333
  const mid = lower + upper >>> 1;
304
334
  const e = leaf.entries[mid];
305
335
  const cmp = compare(e.key, userKey);
306
- if ((cmp !== 0 ? cmp : e.entryId - sequence) <= 0) {
336
+ if ((cmp !== 0 ? cmp : e.entryId < sequence ? -1 : e.entryId > sequence ? 1 : 0) <= 0) {
307
337
  lower = mid + 1;
308
338
  } else {
309
339
  upper = mid;
@@ -370,7 +400,11 @@ var hasKeyEntry = (state, key) => {
370
400
  var findNextHigherKey = (state, key) => {
371
401
  if (state.entryCount === 0) return null;
372
402
  const compare = state.compareKeys;
373
- let leaf = findLeafForKey(state, key, Number.MAX_SAFE_INTEGER);
403
+ let leaf = findLeafForKey(
404
+ state,
405
+ key,
406
+ Number.MAX_SAFE_INTEGER
407
+ );
374
408
  let idx = upperBoundInLeaf(state, leaf, key, Number.MAX_SAFE_INTEGER);
375
409
  while (leaf !== null) {
376
410
  if (idx < leafEntryCount(leaf)) {
@@ -440,7 +474,175 @@ var findPairOrNextLower = (state, key) => {
440
474
  return null;
441
475
  };
442
476
 
443
- // src/btree/rebalance.ts
477
+ // src/btree/rangeQuery.ts
478
+ function isEmptyRange(compare, startKey, endKey, options) {
479
+ const cmp = compare(startKey, endKey);
480
+ if (cmp > 0) return true;
481
+ const lowerExclusive = options?.lowerBound === "exclusive";
482
+ const upperExclusive = options?.upperBound === "exclusive";
483
+ return lowerExclusive && upperExclusive && cmp === 0;
484
+ }
485
+ var initRangeCursor = (state, startKey, endKey, options) => {
486
+ if (state.entryCount === 0) return null;
487
+ const compare = state.compareKeys;
488
+ if (isEmptyRange(compare, startKey, endKey, options)) return null;
489
+ const lowerExclusive = options?.lowerBound === "exclusive";
490
+ const upperExclusive = options?.upperBound === "exclusive";
491
+ const startSeq = lowerExclusive ? Number.MAX_SAFE_INTEGER : 0;
492
+ const leaf = findLeafForKey(state, startKey, startSeq);
493
+ const index = lowerExclusive ? upperBoundInLeaf(state, leaf, startKey, Number.MAX_SAFE_INTEGER) : lowerBoundInLeaf(state, leaf, startKey, 0);
494
+ return { leaf, index, compare, upperExclusive };
495
+ };
496
+ var countRangeEntries = (state, startKey, endKey, options) => {
497
+ const cursor = initRangeCursor(state, startKey, endKey, options);
498
+ if (cursor === null) return 0;
499
+ let cursorLeaf = cursor.leaf;
500
+ let cursorIndex = cursor.index;
501
+ const { compare, upperExclusive } = cursor;
502
+ let count = 0;
503
+ while (cursorLeaf !== null) {
504
+ const leafCount = leafEntryCount(cursorLeaf);
505
+ if (cursorIndex >= leafCount) {
506
+ cursorLeaf = cursorLeaf.next;
507
+ cursorIndex = 0;
508
+ continue;
509
+ }
510
+ const lastKey = leafEntryAt(cursorLeaf, leafCount - 1).key;
511
+ if (isLastEntryInRange(lastKey, endKey, compare, upperExclusive)) {
512
+ count += leafCount - cursorIndex;
513
+ cursorLeaf = cursorLeaf.next;
514
+ cursorIndex = 0;
515
+ continue;
516
+ }
517
+ count += findBoundaryEnd(state, cursorLeaf, endKey, upperExclusive, leafCount) - cursorIndex;
518
+ return count;
519
+ }
520
+ return count;
521
+ };
522
+ var isLastEntryInRange = (lastKey, endKey, compare, upperExclusive) => {
523
+ const cmp = compare(lastKey, endKey);
524
+ return upperExclusive ? cmp < 0 : cmp <= 0;
525
+ };
526
+ var findBoundaryEnd = (state, leaf, endKey, upperExclusive, leafCount) => {
527
+ const endSeq = upperExclusive ? 0 : Number.MAX_SAFE_INTEGER;
528
+ const endBound = upperExclusive ? lowerBoundInLeaf(state, leaf, endKey, endSeq) : upperBoundInLeaf(state, leaf, endKey, endSeq);
529
+ return endBound < leafCount ? endBound : leafCount;
530
+ };
531
+ var RANGE_PREALLOC_THRESHOLD = 200;
532
+ var allocateRangeOutput = (state, cursor, startKey, endKey, options) => {
533
+ const firstLeafCount = leafEntryCount(cursor.leaf);
534
+ const firstLeafRemainder = firstLeafCount - cursor.index;
535
+ if (firstLeafRemainder >= RANGE_PREALLOC_THRESHOLD && cursor.leaf.next !== null) {
536
+ const lastKey = leafEntryAt(cursor.leaf, firstLeafCount - 1).key;
537
+ if (isLastEntryInRange(lastKey, endKey, cursor.compare, cursor.upperExclusive)) {
538
+ return new Array(
539
+ countRangeEntries(state, startKey, endKey, options)
540
+ );
541
+ }
542
+ }
543
+ return [];
544
+ };
545
+ var appendLeafSlicePublic = (leaf, from, to, output, useIndexed, writeIdx) => {
546
+ if (useIndexed) {
547
+ for (let i = from; i < to; i += 1) {
548
+ output[writeIdx++] = freezeEntry(leafEntryAt(leaf, i));
549
+ }
550
+ } else {
551
+ for (let i = from; i < to; i += 1) {
552
+ output.push(freezeEntry(leafEntryAt(leaf, i)));
553
+ }
554
+ }
555
+ return writeIdx;
556
+ };
557
+ var collectPublicEntries = (state, cursor, endKey, output) => {
558
+ const { compare, upperExclusive } = cursor;
559
+ let cursorLeaf = cursor.leaf;
560
+ let cursorIndex = cursor.index;
561
+ let writeIdx = 0;
562
+ const useIndexed = output.length > 0;
563
+ while (cursorLeaf !== null) {
564
+ const leafCount = leafEntryCount(cursorLeaf);
565
+ if (cursorIndex >= leafCount) {
566
+ cursorLeaf = cursorLeaf.next;
567
+ cursorIndex = 0;
568
+ continue;
569
+ }
570
+ const lastKey = leafEntryAt(cursorLeaf, leafCount - 1).key;
571
+ if (isLastEntryInRange(lastKey, endKey, compare, upperExclusive)) {
572
+ writeIdx = appendLeafSlicePublic(
573
+ cursorLeaf,
574
+ cursorIndex,
575
+ leafCount,
576
+ output,
577
+ useIndexed,
578
+ writeIdx
579
+ );
580
+ cursorLeaf = cursorLeaf.next;
581
+ cursorIndex = 0;
582
+ continue;
583
+ }
584
+ const limit = findBoundaryEnd(
585
+ state,
586
+ cursorLeaf,
587
+ endKey,
588
+ upperExclusive,
589
+ leafCount
590
+ );
591
+ appendLeafSlicePublic(
592
+ cursorLeaf,
593
+ cursorIndex,
594
+ limit,
595
+ output,
596
+ useIndexed,
597
+ writeIdx
598
+ );
599
+ return;
600
+ }
601
+ };
602
+ var rangeQueryPublicEntries = (state, startKey, endKey, options) => {
603
+ const cursor = initRangeCursor(state, startKey, endKey, options);
604
+ if (cursor === null) return [];
605
+ const output = allocateRangeOutput(state, cursor, startKey, endKey, options);
606
+ collectPublicEntries(state, cursor, endKey, output);
607
+ return output;
608
+ };
609
+ var forEachRangeEntries = (state, startKey, endKey, callback, options) => {
610
+ const cursor = initRangeCursor(state, startKey, endKey, options);
611
+ if (cursor === null) return;
612
+ let cursorLeaf = cursor.leaf;
613
+ let cursorIndex = cursor.index;
614
+ const { compare, upperExclusive } = cursor;
615
+ while (cursorLeaf !== null) {
616
+ const leafCount = leafEntryCount(cursorLeaf);
617
+ if (cursorIndex >= leafCount) {
618
+ cursorLeaf = cursorLeaf.next;
619
+ cursorIndex = 0;
620
+ continue;
621
+ }
622
+ const lastKey = leafEntryAt(cursorLeaf, leafCount - 1).key;
623
+ if (isLastEntryInRange(lastKey, endKey, compare, upperExclusive)) {
624
+ for (let i = cursorIndex; i < leafCount; i += 1) {
625
+ callback(freezeEntry(leafEntryAt(cursorLeaf, i)));
626
+ }
627
+ cursorLeaf = cursorLeaf.next;
628
+ cursorIndex = 0;
629
+ continue;
630
+ }
631
+ const limit = findBoundaryEnd(
632
+ state,
633
+ cursorLeaf,
634
+ endKey,
635
+ upperExclusive,
636
+ leafCount
637
+ );
638
+ for (let i = cursorIndex; i < limit; i += 1) {
639
+ callback(freezeEntry(leafEntryAt(cursorLeaf, i)));
640
+ }
641
+ return;
642
+ }
643
+ };
644
+
645
+ // src/btree/rebalance-branch.ts
444
646
  var updateMinKeyInAncestors = (node) => {
445
647
  let current = node;
446
648
  while (current.parent !== null) {
@@ -456,12 +658,9 @@ var requireParent = (node) => {
456
658
  }
457
659
  return node.parent;
458
660
  };
459
- var requireLeafNode = (node) => {
460
- if (!isLeafNode(node)) throw new BTreeInvariantError("expected leaf, got branch");
461
- return node;
462
- };
463
661
  var requireBranchNode = (node) => {
464
- if (isLeafNode(node)) throw new BTreeInvariantError("expected branch, got leaf");
662
+ if (isLeafNode(node))
663
+ throw new BTreeInvariantError("expected branch, got leaf");
465
664
  return node;
466
665
  };
467
666
  var removeChildFromBranch = (branch, childIndex) => {
@@ -470,27 +669,18 @@ var removeChildFromBranch = (branch, childIndex) => {
470
669
  }
471
670
  branchRemoveAt(branch, childIndex);
472
671
  };
473
- var detachLeafFromChain = (state, leaf) => {
474
- if (leaf.prev !== null) {
475
- leaf.prev.next = leaf.next;
476
- } else if (leaf.next !== null) {
477
- state.leftmostLeaf = leaf.next;
478
- }
479
- if (leaf.next !== null) {
480
- leaf.next.prev = leaf.prev;
481
- } else if (leaf.prev !== null) {
482
- state.rightmostLeaf = leaf.prev;
483
- }
484
- leaf.prev = null;
485
- leaf.next = null;
486
- };
487
672
  var borrowFromLeftBranch = (branch, leftSibling, branchIndex) => {
488
673
  const borrowedChild = leftSibling.children.pop();
489
- if (borrowedChild === void 0) throw new BTreeInvariantError("left branch borrow failed");
674
+ if (borrowedChild === void 0)
675
+ throw new BTreeInvariantError("left branch borrow failed");
490
676
  leftSibling.keys.pop();
491
677
  borrowedChild.parent = branch;
492
- const borrowedMinKey = { key: void 0, sequence: 0 };
493
- if (!writeMinKeyTo(borrowedChild, borrowedMinKey)) throw new BTreeInvariantError("borrowed child has no min key");
678
+ const borrowedMinKey = {
679
+ key: void 0,
680
+ sequence: 0
681
+ };
682
+ if (!writeMinKeyTo(borrowedChild, borrowedMinKey))
683
+ throw new BTreeInvariantError("borrowed child has no min key");
494
684
  if (branch.childOffset > 0) {
495
685
  branch.childOffset -= 1;
496
686
  branch.children[branch.childOffset] = borrowedChild;
@@ -499,15 +689,20 @@ var borrowFromLeftBranch = (branch, leftSibling, branchIndex) => {
499
689
  } else {
500
690
  branch.children.unshift(borrowedChild);
501
691
  branch.keys.unshift(borrowedMinKey);
502
- for (let i = 0; i < branch.children.length; i += 1) branch.children[i].indexInParent = i;
692
+ for (let i = 0; i < branch.children.length; i += 1)
693
+ branch.children[i].indexInParent = i;
503
694
  }
504
695
  const parent = requireParent(branch);
505
- parent.keys[branchIndex] = { key: borrowedMinKey.key, sequence: borrowedMinKey.sequence };
696
+ parent.keys[branchIndex] = {
697
+ key: borrowedMinKey.key,
698
+ sequence: borrowedMinKey.sequence
699
+ };
506
700
  updateMinKeyInAncestors(branch);
507
701
  };
508
702
  var borrowFromRightBranch = (branch, rightSibling, branchIndex) => {
509
703
  const shiftIdx = rightSibling.childOffset;
510
- if (shiftIdx >= rightSibling.children.length) throw new BTreeInvariantError("right branch borrow failed");
704
+ if (shiftIdx >= rightSibling.children.length)
705
+ throw new BTreeInvariantError("right branch borrow failed");
511
706
  const borrowedChild = rightSibling.children[shiftIdx];
512
707
  rightSibling.childOffset += 1;
513
708
  if (rightSibling.childOffset >= rightSibling.children.length >>> 1) {
@@ -515,8 +710,12 @@ var borrowFromRightBranch = (branch, rightSibling, branchIndex) => {
515
710
  }
516
711
  branch.children.push(borrowedChild);
517
712
  borrowedChild.parent = branch;
518
- const borrowedMinKey = { key: void 0, sequence: 0 };
519
- if (!writeMinKeyTo(borrowedChild, borrowedMinKey)) throw new BTreeInvariantError("borrowed child has no min key");
713
+ const borrowedMinKey = {
714
+ key: void 0,
715
+ sequence: 0
716
+ };
717
+ if (!writeMinKeyTo(borrowedChild, borrowedMinKey))
718
+ throw new BTreeInvariantError("borrowed child has no min key");
520
719
  branch.keys.push(borrowedMinKey);
521
720
  borrowedChild.indexInParent = branch.children.length - 1;
522
721
  const parent = requireParent(branch);
@@ -569,7 +768,10 @@ var rebalanceAfterBranchRemoval = (state, branch) => {
569
768
  const parent = branch.parent;
570
769
  if (parent === null) throw new BTreeInvariantError("branch has no parent");
571
770
  const branchIndex = branch.indexInParent;
572
- const { left: leftSibling, right: rightSibling } = findBranchSiblings(parent, branchIndex);
771
+ const { left: leftSibling, right: rightSibling } = findBranchSiblings(
772
+ parent,
773
+ branchIndex
774
+ );
573
775
  if (rightSibling !== null && branchChildCount(rightSibling) > state.minBranchChildren) {
574
776
  borrowFromRightBranch(branch, rightSibling, branchIndex);
575
777
  return;
@@ -588,44 +790,68 @@ var rebalanceAfterBranchRemoval = (state, branch) => {
588
790
  }
589
791
  throw new BTreeInvariantError("no branch siblings to rebalance");
590
792
  };
793
+
794
+ // src/btree/rebalance.ts
795
+ var requireLeafNode = (node) => {
796
+ if (!isLeafNode(node))
797
+ throw new BTreeInvariantError("expected leaf, got branch");
798
+ return node;
799
+ };
800
+ var detachLeafFromChain = (state, leaf) => {
801
+ if (leaf.prev !== null) {
802
+ leaf.prev.next = leaf.next;
803
+ } else if (leaf.next !== null) {
804
+ state.leftmostLeaf = leaf.next;
805
+ }
806
+ if (leaf.next !== null) {
807
+ leaf.next.prev = leaf.prev;
808
+ } else if (leaf.prev !== null) {
809
+ state.rightmostLeaf = leaf.prev;
810
+ }
811
+ leaf.prev = null;
812
+ leaf.next = null;
813
+ };
591
814
  var mergeLeafEntries = (target, source) => {
592
815
  if (target.entryOffset > 0) {
593
816
  target.entries.copyWithin(0, target.entryOffset);
594
817
  target.entries.length = target.entries.length - target.entryOffset;
595
818
  target.entryOffset = 0;
596
819
  }
597
- const src = source.entries;
598
- for (let i = source.entryOffset; i < src.length; i += 1) target.entries.push(src[i]);
820
+ target.entries.push(...source.entries.slice(source.entryOffset));
599
821
  };
600
- var rebalanceAfterLeafRemoval = (state, leaf) => {
601
- if (leaf === state.root) {
602
- if (state.entryCount === 0) {
603
- state.leftmostLeaf = leaf;
604
- state.rightmostLeaf = leaf;
605
- }
606
- return;
822
+ var applyLazyThreshold = (min) => Math.max(1, Math.ceil(min / 4));
823
+ var leafRebalanceThreshold = (state) => {
824
+ if (state.deleteRebalancePolicy === "lazy") {
825
+ return applyLazyThreshold(state.minLeafEntries);
607
826
  }
608
- if (leafEntryCount(leaf) >= state.minLeafEntries) return;
609
- const parent = leaf.parent;
610
- if (parent === null) throw new BTreeInvariantError("Leaf node has no parent during rebalance.");
611
- const leafIndex = leaf.indexInParent;
612
- const leftSibling = leafIndex > parent.childOffset ? requireLeafNode(parent.children[leafIndex - 1]) : null;
613
- const rightSibling = leafIndex + 1 < parent.children.length ? requireLeafNode(parent.children[leafIndex + 1]) : null;
827
+ return state.minLeafEntries;
828
+ };
829
+ var findLeafSiblings = (parent, leafIndex) => {
830
+ const left = leafIndex > parent.childOffset ? requireLeafNode(parent.children[leafIndex - 1]) : null;
831
+ const right = leafIndex + 1 < parent.children.length ? requireLeafNode(parent.children[leafIndex + 1]) : null;
832
+ return { left, right };
833
+ };
834
+ var tryBorrowFromLeafSibling = (state, leaf, parent, leafIndex, leftSibling, rightSibling) => {
614
835
  if (rightSibling !== null && leafEntryCount(rightSibling) > state.minLeafEntries) {
615
836
  const borrowed = leafShiftEntry(rightSibling);
616
- if (borrowed === void 0) throw new BTreeInvariantError("right leaf borrow failed");
837
+ if (borrowed === void 0)
838
+ throw new BTreeInvariantError("right leaf borrow failed");
617
839
  leaf.entries.push(borrowed);
618
840
  writeMinKeyTo(rightSibling, parent.keys[leafIndex + 1]);
619
- return;
841
+ return true;
620
842
  }
621
843
  if (leftSibling !== null && leafEntryCount(leftSibling) > state.minLeafEntries) {
622
844
  const borrowed = leftSibling.entries.pop();
623
- if (borrowed === void 0) throw new BTreeInvariantError("left leaf borrow failed");
845
+ if (borrowed === void 0)
846
+ throw new BTreeInvariantError("left leaf borrow failed");
624
847
  leafUnshiftEntry(leaf, borrowed);
625
848
  parent.keys[leafIndex] = { key: borrowed.key, sequence: borrowed.entryId };
626
849
  updateMinKeyInAncestors(leaf);
627
- return;
850
+ return true;
628
851
  }
852
+ return false;
853
+ };
854
+ var mergeLeafWithSibling = (state, leaf, parent, leafIndex, leftSibling, rightSibling) => {
629
855
  if (leftSibling !== null) {
630
856
  mergeLeafEntries(leftSibling, leaf);
631
857
  detachLeafFromChain(state, leaf);
@@ -642,6 +868,24 @@ var rebalanceAfterLeafRemoval = (state, leaf) => {
642
868
  }
643
869
  throw new BTreeInvariantError("no leaf siblings to rebalance");
644
870
  };
871
+ var rebalanceAfterLeafRemoval = (state, leaf) => {
872
+ if (leaf === state.root) {
873
+ if (state.entryCount === 0) {
874
+ state.leftmostLeaf = leaf;
875
+ state.rightmostLeaf = leaf;
876
+ }
877
+ return;
878
+ }
879
+ if (leafEntryCount(leaf) >= leafRebalanceThreshold(state)) return;
880
+ const parent = leaf.parent;
881
+ if (parent === null)
882
+ throw new BTreeInvariantError("Leaf node has no parent during rebalance.");
883
+ const leafIndex = leaf.indexInParent;
884
+ const { left, right } = findLeafSiblings(parent, leafIndex);
885
+ if (tryBorrowFromLeafSibling(state, leaf, parent, leafIndex, left, right))
886
+ return;
887
+ mergeLeafWithSibling(state, leaf, parent, leafIndex, left, right);
888
+ };
645
889
 
646
890
  // src/btree/deleteRange.ts
647
891
  var navigateToStart = (state, startKey, lowerExclusive) => {
@@ -654,27 +898,18 @@ var navigateToStart = (state, startKey, lowerExclusive) => {
654
898
  }
655
899
  return { leaf, idx };
656
900
  };
657
- var findRemoveEnd = (state, leaf, idx, endKey, upperExclusive) => {
901
+ var findRemoveEnd = (state, leaf, endKey, upperExclusive) => {
658
902
  const count = leafEntryCount(leaf);
659
- let removeEnd = idx;
660
- while (removeEnd < count) {
661
- const e = leafEntryAt(leaf, removeEnd);
662
- const cmpEnd = state.compareKeys(e.key, endKey);
663
- if (upperExclusive ? cmpEnd >= 0 : cmpEnd > 0) break;
664
- removeEnd += 1;
665
- }
666
- return removeEnd;
667
- };
668
- var computeTreeHeight = (state) => {
669
- let h = 0;
670
- let n = state.root;
671
- while (!isLeafNode(n)) {
672
- n = n.children[n.childOffset];
673
- h += 1;
903
+ const lastEntry = leafEntryAt(leaf, count - 1);
904
+ const cmpLast = state.compareKeys(lastEntry.key, endKey);
905
+ if (upperExclusive ? cmpLast < 0 : cmpLast <= 0) {
906
+ return count;
674
907
  }
675
- return h;
908
+ const endSeq = upperExclusive ? 0 : Number.MAX_SAFE_INTEGER;
909
+ const endBound = upperExclusive ? lowerBoundInLeaf(state, leaf, endKey, endSeq) : upperBoundInLeaf(state, leaf, endKey, endSeq);
910
+ return endBound < count ? endBound : count;
676
911
  };
677
- var spliceLeafAndRebalance = (state, leaf, idx, removeCount, maxRebalanceDepth) => {
912
+ var spliceLeafAndRebalance = (state, leaf, idx, removeCount) => {
678
913
  if (state.entryKeys !== null) {
679
914
  for (let i = idx; i < idx + removeCount; i += 1) {
680
915
  state.entryKeys.delete(leafEntryAt(leaf, i).entryId);
@@ -689,11 +924,13 @@ var spliceLeafAndRebalance = (state, leaf, idx, removeCount, maxRebalanceDepth)
689
924
  updateMinKeyInAncestors(leaf);
690
925
  }
691
926
  const countAfterSplice = leafEntryCount(leaf);
692
- let maxIter = maxRebalanceDepth;
693
- while (maxIter > 0 && leaf !== state.root && leafEntryCount(leaf) < state.minLeafEntries) {
927
+ const rebalThreshold = leafRebalanceThreshold(state);
928
+ let safetyGuard = state.minLeafEntries + 4;
929
+ while (safetyGuard > 0 && leaf !== state.root && leafEntryCount(leaf) < rebalThreshold) {
694
930
  rebalanceAfterLeafRemoval(state, leaf);
695
- if (leaf.parent !== null && leaf.parent.children[leaf.indexInParent] !== leaf) break;
696
- maxIter -= 1;
931
+ if (leaf.parent !== null && leaf.parent.children[leaf.indexInParent] !== leaf)
932
+ break;
933
+ safetyGuard -= 1;
697
934
  }
698
935
  if (leafEmptied && leafEntryCount(leaf) > 0 && leaf.parent !== null && leaf.parent.children[leaf.indexInParent] === leaf) {
699
936
  updateMinKeyInAncestors(leaf);
@@ -703,12 +940,9 @@ var spliceLeafAndRebalance = (state, leaf, idx, removeCount, maxRebalanceDepth)
703
940
  var isLeafStillValid = (state, leaf) => leaf.parent === null ? leaf === state.root : leaf.parent.children[leaf.indexInParent] === leaf;
704
941
  var deleteRangeEntries = (state, startKey, endKey, options) => {
705
942
  if (state.entryCount === 0) return 0;
706
- const boundCompared = state.compareKeys(startKey, endKey);
707
- if (boundCompared > 0) return 0;
943
+ if (isEmptyRange(state.compareKeys, startKey, endKey, options)) return 0;
708
944
  const lowerExclusive = options?.lowerBound === "exclusive";
709
945
  const upperExclusive = options?.upperBound === "exclusive";
710
- if (lowerExclusive && upperExclusive && boundCompared === 0) return 0;
711
- const treeHeight = computeTreeHeight(state);
712
946
  let deleted = 0;
713
947
  let needsNavigate = true;
714
948
  let leaf = null;
@@ -723,10 +957,15 @@ var deleteRangeEntries = (state, startKey, endKey, options) => {
723
957
  }
724
958
  if (idx >= leafEntryCount(leaf)) break;
725
959
  const count = leafEntryCount(leaf);
726
- const removeEnd = findRemoveEnd(state, leaf, idx, endKey, upperExclusive);
960
+ const removeEnd = findRemoveEnd(state, leaf, endKey, upperExclusive);
727
961
  const removeCount = removeEnd - idx;
728
962
  if (removeCount === 0) break;
729
- const countAfterSplice = spliceLeafAndRebalance(state, leaf, idx, removeCount, treeHeight);
963
+ const countAfterSplice = spliceLeafAndRebalance(
964
+ state,
965
+ leaf,
966
+ idx,
967
+ removeCount
968
+ );
730
969
  deleted += removeCount;
731
970
  if (removeEnd < count) break;
732
971
  if (!isLeafStillValid(state, leaf)) {
@@ -745,6 +984,7 @@ var deleteRangeEntries = (state, startKey, endKey, options) => {
745
984
  };
746
985
 
747
986
  // src/btree/autoScale.ts
987
+ var minOccupancy = (max) => Math.ceil(max / 2);
748
988
  var AUTO_SCALE_TIERS = [
749
989
  { threshold: 0, maxLeaf: 32, maxBranch: 32 },
750
990
  { threshold: 1e3, maxLeaf: 64, maxBranch: 64 },
@@ -771,33 +1011,50 @@ var computeNextAutoScaleThreshold = (entryCount) => {
771
1011
  }
772
1012
  return Number.MAX_SAFE_INTEGER;
773
1013
  };
774
- var createInitialState = (config) => {
775
- if (typeof config.compareKeys !== "function") {
776
- throw new BTreeValidationError("compareKeys must be a function.");
777
- }
778
- const autoScale = config.autoScale === true;
1014
+ var resolveInitialCapacity = (config, autoScale) => {
779
1015
  if (autoScale && (config.maxLeafEntries !== void 0 || config.maxBranchChildren !== void 0)) {
780
- throw new BTreeValidationError("autoScale conflicts with explicit capacity.");
1016
+ throw new BTreeValidationError(
1017
+ "autoScale conflicts with explicit capacity."
1018
+ );
781
1019
  }
782
- let maxLeafEntries;
783
- let maxBranchChildren;
784
1020
  if (autoScale) {
785
1021
  const tier = computeAutoScaleTier(0);
786
- maxLeafEntries = tier.maxLeaf;
787
- maxBranchChildren = tier.maxBranch;
788
- } else {
789
- maxLeafEntries = normalizeNodeCapacity(config.maxLeafEntries, "maxLeafEntries", DEFAULT_MAX_LEAF_ENTRIES);
790
- maxBranchChildren = normalizeNodeCapacity(config.maxBranchChildren, "maxBranchChildren", DEFAULT_MAX_BRANCH_CHILDREN);
1022
+ return { maxLeafEntries: tier.maxLeaf, maxBranchChildren: tier.maxBranch };
1023
+ }
1024
+ return {
1025
+ maxLeafEntries: normalizeNodeCapacity(
1026
+ config.maxLeafEntries,
1027
+ "maxLeafEntries",
1028
+ DEFAULT_MAX_LEAF_ENTRIES
1029
+ ),
1030
+ maxBranchChildren: normalizeNodeCapacity(
1031
+ config.maxBranchChildren,
1032
+ "maxBranchChildren",
1033
+ DEFAULT_MAX_BRANCH_CHILDREN
1034
+ )
1035
+ };
1036
+ };
1037
+ var createInitialState = (config) => {
1038
+ if (typeof config.compareKeys !== "function") {
1039
+ throw new BTreeValidationError("compareKeys must be a function.");
791
1040
  }
1041
+ const autoScale = config.autoScale === true;
1042
+ const { maxLeafEntries, maxBranchChildren } = resolveInitialCapacity(
1043
+ config,
1044
+ autoScale
1045
+ );
792
1046
  const duplicateKeys = normalizeDuplicateKeyPolicy(config.duplicateKeys);
1047
+ const deleteRebalancePolicy = normalizeDeleteRebalancePolicy(
1048
+ config.deleteRebalancePolicy
1049
+ );
793
1050
  const emptyLeaf = createLeafNode([], null);
794
1051
  return {
795
1052
  compareKeys: config.compareKeys,
796
1053
  maxLeafEntries,
797
1054
  maxBranchChildren,
798
1055
  duplicateKeys,
799
- minLeafEntries: Math.ceil(maxLeafEntries / 2),
800
- minBranchChildren: Math.ceil(maxBranchChildren / 2),
1056
+ minLeafEntries: minOccupancy(maxLeafEntries),
1057
+ minBranchChildren: minOccupancy(maxBranchChildren),
801
1058
  root: emptyLeaf,
802
1059
  leftmostLeaf: emptyLeaf,
803
1060
  rightmostLeaf: emptyLeaf,
@@ -805,6 +1062,7 @@ var createInitialState = (config) => {
805
1062
  nextSequence: 0,
806
1063
  entryKeys: config.enableEntryIdLookup === true ? /* @__PURE__ */ new Map() : null,
807
1064
  autoScale,
1065
+ deleteRebalancePolicy,
808
1066
  _nextAutoScaleThreshold: autoScale ? computeNextAutoScaleThreshold(0) : Number.MAX_SAFE_INTEGER,
809
1067
  _cursor: { leaf: emptyLeaf, index: 0 }
810
1068
  };
@@ -814,13 +1072,15 @@ var maybeAutoScale = (state) => {
814
1072
  const { maxLeaf, maxBranch } = computeAutoScaleTier(state.entryCount);
815
1073
  if (maxLeaf > state.maxLeafEntries) {
816
1074
  state.maxLeafEntries = maxLeaf;
817
- state.minLeafEntries = Math.ceil(maxLeaf / 2);
1075
+ state.minLeafEntries = minOccupancy(maxLeaf);
818
1076
  }
819
1077
  if (maxBranch > state.maxBranchChildren) {
820
1078
  state.maxBranchChildren = maxBranch;
821
- state.minBranchChildren = Math.ceil(maxBranch / 2);
1079
+ state.minBranchChildren = minOccupancy(maxBranch);
822
1080
  }
823
- state._nextAutoScaleThreshold = computeNextAutoScaleThreshold(state.entryCount);
1081
+ state._nextAutoScaleThreshold = computeNextAutoScaleThreshold(
1082
+ state.entryCount
1083
+ );
824
1084
  };
825
1085
  var applyAutoScaleCapacitySnapshot = (state, maxLeafEntries, maxBranchChildren) => {
826
1086
  if (!state.autoScale) {
@@ -844,8 +1104,16 @@ var applyAutoScaleCapacitySnapshot = (state, maxLeafEntries, maxBranchChildren)
844
1104
  }
845
1105
  state.maxLeafEntries = normalizedLeaf;
846
1106
  state.maxBranchChildren = normalizedBranch;
847
- state.minLeafEntries = Math.ceil(normalizedLeaf / 2);
848
- state.minBranchChildren = Math.ceil(normalizedBranch / 2);
1107
+ state.minLeafEntries = minOccupancy(normalizedLeaf);
1108
+ state.minBranchChildren = minOccupancy(normalizedBranch);
1109
+ };
1110
+ var resetAutoScaleToTier0 = (state) => {
1111
+ const tier = computeAutoScaleTier(0);
1112
+ state.maxLeafEntries = tier.maxLeaf;
1113
+ state.maxBranchChildren = tier.maxBranch;
1114
+ state.minLeafEntries = minOccupancy(tier.maxLeaf);
1115
+ state.minBranchChildren = minOccupancy(tier.maxBranch);
1116
+ state._nextAutoScaleThreshold = computeNextAutoScaleThreshold(0);
849
1117
  };
850
1118
 
851
1119
  // src/btree/bulkLoad.ts
@@ -867,7 +1135,11 @@ var computeChunkBoundaries = (total, max, min) => {
867
1135
  return boundaries;
868
1136
  };
869
1137
  var buildLeaves = (state, entries, ids, baseSequence) => {
870
- const boundaries = computeChunkBoundaries(entries.length, state.maxLeafEntries, state.minLeafEntries);
1138
+ const boundaries = computeChunkBoundaries(
1139
+ entries.length,
1140
+ state.maxLeafEntries,
1141
+ state.minLeafEntries
1142
+ );
871
1143
  const leaves = new Array(boundaries.length);
872
1144
  let chunkStart = 0;
873
1145
  for (let c = 0; c < boundaries.length; c += 1) {
@@ -875,7 +1147,11 @@ var buildLeaves = (state, entries, ids, baseSequence) => {
875
1147
  const chunk = new Array(chunkEnd - chunkStart);
876
1148
  for (let i = chunkStart; i < chunkEnd; i += 1) {
877
1149
  const seq = baseSequence + i;
878
- chunk[i - chunkStart] = { key: entries[i].key, entryId: seq, value: entries[i].value };
1150
+ chunk[i - chunkStart] = createEntry(
1151
+ entries[i].key,
1152
+ seq,
1153
+ entries[i].value
1154
+ );
879
1155
  ids[i] = seq;
880
1156
  if (state.entryKeys !== null) {
881
1157
  state.entryKeys.set(seq, entries[i].key);
@@ -909,11 +1185,18 @@ var bulkLoadEntries = (state, entries) => {
909
1185
  } else {
910
1186
  let currentLevel = leaves;
911
1187
  while (currentLevel.length > 1) {
912
- const bounds = computeChunkBoundaries(currentLevel.length, state.maxBranchChildren, state.minBranchChildren);
1188
+ const bounds = computeChunkBoundaries(
1189
+ currentLevel.length,
1190
+ state.maxBranchChildren,
1191
+ state.minBranchChildren
1192
+ );
913
1193
  const nextLevel = new Array(bounds.length);
914
1194
  let start = 0;
915
1195
  for (let b = 0; b < bounds.length; b += 1) {
916
- nextLevel[b] = createBranchNode(currentLevel.slice(start, bounds[b]), null);
1196
+ nextLevel[b] = createBranchNode(
1197
+ currentLevel.slice(start, bounds[b]),
1198
+ null
1199
+ );
917
1200
  start = bounds[b];
918
1201
  }
919
1202
  currentLevel = nextLevel;
@@ -924,9 +1207,12 @@ var bulkLoadEntries = (state, entries) => {
924
1207
  return ids;
925
1208
  };
926
1209
 
927
- // src/btree/mutations.ts
1210
+ // src/btree/split.ts
928
1211
  var insertChildAfter = (state, parent, existingChild, childToInsert) => {
929
- const newChildMinKey = { key: void 0, sequence: 0 };
1212
+ const newChildMinKey = {
1213
+ key: void 0,
1214
+ sequence: 0
1215
+ };
930
1216
  if (!writeMinKeyTo(childToInsert, newChildMinKey)) {
931
1217
  throw new BTreeInvariantError("inserted child has no min key");
932
1218
  }
@@ -964,7 +1250,10 @@ var splitLeaf = (state, leaf) => {
964
1250
  var splitBranch = (state, branch) => {
965
1251
  branchCompact(branch);
966
1252
  const splitAt = Math.ceil(branch.children.length / 2);
967
- const sibling = createBranchNode(branch.children.splice(splitAt), branch.parent);
1253
+ const sibling = createBranchNode(
1254
+ branch.children.splice(splitAt),
1255
+ branch.parent
1256
+ );
968
1257
  branch.keys.splice(splitAt);
969
1258
  if (branch.parent === null) {
970
1259
  state.root = createBranchNode([branch, sibling], null);
@@ -972,42 +1261,125 @@ var splitBranch = (state, branch) => {
972
1261
  }
973
1262
  insertChildAfter(state, branch.parent, branch, sibling);
974
1263
  };
1264
+
1265
+ // src/btree/entry-lookup.ts
1266
+ var findLeafEntryBySequence = (state, userKey, sequence) => {
1267
+ const targetLeaf = findLeafForKey(state, userKey, sequence);
1268
+ const index = lowerBoundInLeaf(state, targetLeaf, userKey, sequence);
1269
+ if (index >= leafEntryCount(targetLeaf)) return null;
1270
+ const entry = leafEntryAt(targetLeaf, index);
1271
+ if (entry.entryId !== sequence) return null;
1272
+ return { leaf: targetLeaf, index };
1273
+ };
1274
+ var peekEntryById = (state, entryId) => {
1275
+ if (state.entryKeys === null) {
1276
+ throw new BTreeInvariantError(
1277
+ "entryKeys lookup map is not enabled on this tree."
1278
+ );
1279
+ }
1280
+ const userKey = state.entryKeys.get(entryId);
1281
+ if (userKey === void 0) return null;
1282
+ const found = findLeafEntryBySequence(state, userKey, entryId);
1283
+ if (found === null) return null;
1284
+ const entry = leafEntryAt(found.leaf, found.index);
1285
+ return entry;
1286
+ };
1287
+ var updateEntryById = (state, entryId, newValue) => {
1288
+ if (state.entryKeys === null) {
1289
+ throw new BTreeInvariantError(
1290
+ "entryKeys lookup map is not enabled on this tree."
1291
+ );
1292
+ }
1293
+ const userKey = state.entryKeys.get(entryId);
1294
+ if (userKey === void 0) return null;
1295
+ const found = findLeafEntryBySequence(state, userKey, entryId);
1296
+ if (found === null) return null;
1297
+ const entry = leafEntryAt(found.leaf, found.index);
1298
+ const updated = createEntry(entry.key, entry.entryId, newValue);
1299
+ found.leaf.entries[found.leaf.entryOffset + found.index] = updated;
1300
+ return updated;
1301
+ };
1302
+ var removeEntryById = (state, entryId) => {
1303
+ if (state.entryKeys === null) {
1304
+ throw new BTreeInvariantError(
1305
+ "entryKeys lookup map is not enabled on this tree."
1306
+ );
1307
+ }
1308
+ const userKey = state.entryKeys.get(entryId);
1309
+ if (userKey === void 0) return null;
1310
+ const found = findLeafEntryBySequence(state, userKey, entryId);
1311
+ if (found === null) return null;
1312
+ const entry = leafEntryAt(found.leaf, found.index);
1313
+ leafRemoveAt(found.leaf, found.index);
1314
+ state.entryCount -= 1;
1315
+ state.entryKeys.delete(entryId);
1316
+ if (found.index === 0 && leafEntryCount(found.leaf) > 0 && found.leaf.parent !== null) {
1317
+ updateMinKeyInAncestors(found.leaf);
1318
+ }
1319
+ if (found.leaf !== state.root && leafEntryCount(found.leaf) < state.minLeafEntries) {
1320
+ rebalanceAfterLeafRemoval(state, found.leaf);
1321
+ }
1322
+ return entry;
1323
+ };
1324
+
1325
+ // src/btree/mutations.ts
1326
+ var findDuplicateEntry = (state, targetLeaf, key, insertAt) => {
1327
+ if (state.duplicateKeys === "allow") return null;
1328
+ if (insertAt > 0) {
1329
+ const candidate = leafEntryAt(targetLeaf, insertAt - 1);
1330
+ if (state.compareKeys(candidate.key, key) === 0) {
1331
+ return {
1332
+ leaf: targetLeaf,
1333
+ physIndex: targetLeaf.entryOffset + insertAt - 1,
1334
+ entry: candidate
1335
+ };
1336
+ }
1337
+ } else if (targetLeaf.prev !== null && leafEntryCount(targetLeaf.prev) > 0) {
1338
+ const prevLeaf = targetLeaf.prev;
1339
+ const prevCount = leafEntryCount(prevLeaf);
1340
+ const candidate = leafEntryAt(prevLeaf, prevCount - 1);
1341
+ if (state.compareKeys(candidate.key, key) === 0) {
1342
+ return {
1343
+ leaf: prevLeaf,
1344
+ physIndex: prevLeaf.entryOffset + prevCount - 1,
1345
+ entry: candidate
1346
+ };
1347
+ }
1348
+ }
1349
+ return null;
1350
+ };
975
1351
  var putEntryIntoLeaf = (state, targetLeaf, key, value) => {
976
1352
  const sequence = state.nextSequence;
977
1353
  const insertAt = upperBoundInLeaf(state, targetLeaf, key, sequence);
978
- if (state.duplicateKeys !== "allow") {
979
- let existingEntry = null;
980
- if (insertAt > 0) {
981
- const candidate = leafEntryAt(targetLeaf, insertAt - 1);
982
- if (state.compareKeys(candidate.key, key) === 0) {
983
- existingEntry = candidate;
984
- }
985
- } else if (targetLeaf.prev !== null && leafEntryCount(targetLeaf.prev) > 0) {
986
- const prevLeaf = targetLeaf.prev;
987
- const candidate = leafEntryAt(prevLeaf, leafEntryCount(prevLeaf) - 1);
988
- if (state.compareKeys(candidate.key, key) === 0) {
989
- existingEntry = candidate;
990
- }
991
- }
992
- if (existingEntry !== null) {
993
- if (state.duplicateKeys === "reject") {
994
- throw new BTreeValidationError("Duplicate key rejected.");
995
- }
996
- existingEntry.value = value;
997
- return existingEntry.entryId;
998
- }
1354
+ const dup = findDuplicateEntry(state, targetLeaf, key, insertAt);
1355
+ if (dup !== null) {
1356
+ if (state.duplicateKeys === "reject") {
1357
+ throw new BTreeValidationError("Duplicate key rejected.");
1358
+ }
1359
+ dup.leaf.entries[dup.physIndex] = createEntry(
1360
+ dup.entry.key,
1361
+ dup.entry.entryId,
1362
+ value
1363
+ );
1364
+ return dup.entry.entryId;
999
1365
  }
1000
1366
  if (state.nextSequence >= Number.MAX_SAFE_INTEGER) {
1001
1367
  throw new BTreeValidationError("Sequence overflow.");
1002
1368
  }
1003
1369
  state.nextSequence += 1;
1004
- leafInsertAt(targetLeaf, insertAt, { key, entryId: sequence, value });
1370
+ leafInsertAt(
1371
+ targetLeaf,
1372
+ insertAt,
1373
+ createEntry(key, sequence, value)
1374
+ );
1005
1375
  state.entryCount += 1;
1006
1376
  if (state.entryKeys !== null) {
1007
1377
  state.entryKeys.set(sequence, key);
1008
1378
  }
1009
- if (insertAt === 0 && targetLeaf.parent !== null) updateMinKeyInAncestors(targetLeaf);
1010
- if (leafEntryCount(targetLeaf) > state.maxLeafEntries) splitLeaf(state, targetLeaf);
1379
+ if (insertAt === 0 && targetLeaf.parent !== null)
1380
+ updateMinKeyInAncestors(targetLeaf);
1381
+ if (leafEntryCount(targetLeaf) > state.maxLeafEntries)
1382
+ splitLeaf(state, targetLeaf);
1011
1383
  maybeAutoScale(state);
1012
1384
  return sequence;
1013
1385
  };
@@ -1018,7 +1390,8 @@ var putEntry = (state, key, value) => {
1018
1390
  var popFirstEntry = (state) => {
1019
1391
  if (state.entryCount === 0) return null;
1020
1392
  const firstEntry = leafShiftEntry(state.leftmostLeaf);
1021
- if (firstEntry === void 0) throw new BTreeInvariantError("leftmost leaf empty but count > 0");
1393
+ if (firstEntry === void 0)
1394
+ throw new BTreeInvariantError("leftmost leaf empty but count > 0");
1022
1395
  state.entryCount -= 1;
1023
1396
  if (state.entryKeys !== null) {
1024
1397
  state.entryKeys.delete(firstEntry.entryId);
@@ -1034,7 +1407,8 @@ var popFirstEntry = (state) => {
1034
1407
  var popLastEntry = (state) => {
1035
1408
  if (state.entryCount === 0) return null;
1036
1409
  const lastEntry = leafPopEntry(state.rightmostLeaf);
1037
- if (lastEntry === void 0) throw new BTreeInvariantError("rightmost leaf empty but count > 0");
1410
+ if (lastEntry === void 0)
1411
+ throw new BTreeInvariantError("rightmost leaf empty but count > 0");
1038
1412
  state.entryCount -= 1;
1039
1413
  if (state.entryKeys !== null) {
1040
1414
  state.entryKeys.delete(lastEntry.entryId);
@@ -1055,62 +1429,26 @@ var removeFirstMatchingEntry = (state, key) => {
1055
1429
  if (state.entryKeys !== null) {
1056
1430
  state.entryKeys.delete(targetEntry.entryId);
1057
1431
  }
1058
- if (removeAt === 0 && leafEntryCount(targetLeaf) > 0 && targetLeaf.parent !== null) updateMinKeyInAncestors(targetLeaf);
1432
+ if (removeAt === 0 && leafEntryCount(targetLeaf) > 0 && targetLeaf.parent !== null)
1433
+ updateMinKeyInAncestors(targetLeaf);
1059
1434
  if (targetLeaf !== state.root && leafEntryCount(targetLeaf) < state.minLeafEntries) {
1060
1435
  rebalanceAfterLeafRemoval(state, targetLeaf);
1061
1436
  }
1062
1437
  return targetEntry;
1063
1438
  };
1064
- var findLeafEntryBySequence = (state, userKey, sequence) => {
1065
- const targetLeaf = findLeafForKey(state, userKey, sequence);
1066
- const index = lowerBoundInLeaf(state, targetLeaf, userKey, sequence);
1067
- if (index >= leafEntryCount(targetLeaf)) return null;
1068
- const entry = leafEntryAt(targetLeaf, index);
1069
- if (entry.entryId !== sequence) return null;
1070
- return { leaf: targetLeaf, index };
1071
- };
1072
- var removeEntryById = (state, entryId) => {
1073
- const userKey = state.entryKeys.get(entryId);
1074
- if (userKey === void 0) return null;
1075
- const found = findLeafEntryBySequence(state, userKey, entryId);
1076
- if (found === null) return null;
1077
- const entry = leafEntryAt(found.leaf, found.index);
1078
- leafRemoveAt(found.leaf, found.index);
1079
- state.entryCount -= 1;
1080
- state.entryKeys.delete(entryId);
1081
- if (found.index === 0 && leafEntryCount(found.leaf) > 0 && found.leaf.parent !== null) {
1082
- updateMinKeyInAncestors(found.leaf);
1083
- }
1084
- if (found.leaf !== state.root && leafEntryCount(found.leaf) < state.minLeafEntries) {
1085
- rebalanceAfterLeafRemoval(state, found.leaf);
1086
- }
1087
- return entry;
1088
- };
1089
- var peekEntryById = (state, entryId) => {
1090
- const userKey = state.entryKeys.get(entryId);
1091
- if (userKey === void 0) return null;
1092
- const found = findLeafEntryBySequence(state, userKey, entryId);
1093
- if (found === null) return null;
1094
- const entry = leafEntryAt(found.leaf, found.index);
1095
- return entry;
1096
- };
1097
- var updateEntryById = (state, entryId, newValue) => {
1098
- const userKey = state.entryKeys.get(entryId);
1099
- if (userKey === void 0) return null;
1100
- const found = findLeafEntryBySequence(state, userKey, entryId);
1101
- if (found === null) return null;
1102
- const entry = leafEntryAt(found.leaf, found.index);
1103
- entry.value = newValue;
1104
- return entry;
1105
- };
1106
1439
  var putManyEntries = (state, entries) => {
1107
1440
  if (entries.length === 0) return [];
1108
1441
  const strictlyAscending = state.duplicateKeys !== "allow";
1109
1442
  for (let i = 1; i < entries.length; i += 1) {
1110
1443
  const cmp = state.compareKeys(entries[i - 1].key, entries[i].key);
1111
- if (strictlyAscending ? cmp >= 0 : cmp > 0) {
1444
+ if (cmp > 0) {
1112
1445
  throw new BTreeValidationError(
1113
- strictlyAscending ? "putMany: not sorted in strict ascending order." : "putMany: not sorted in non-descending order."
1446
+ "putMany: entries not in ascending order."
1447
+ );
1448
+ }
1449
+ if (strictlyAscending && cmp === 0) {
1450
+ throw new BTreeValidationError(
1451
+ state.duplicateKeys === "reject" ? "putMany: duplicate key rejected." : "putMany: equal keys not allowed in strict mode."
1114
1452
  );
1115
1453
  }
1116
1454
  }
@@ -1119,7 +1457,12 @@ var putManyEntries = (state, entries) => {
1119
1457
  let hintLeaf = findLeafForKey(state, entries[0].key, state.nextSequence);
1120
1458
  for (let i = 0; i < entries.length; i += 1) {
1121
1459
  const entry = entries[i];
1122
- const targetLeaf = findLeafFromHint(state, hintLeaf, entry.key, state.nextSequence);
1460
+ const targetLeaf = findLeafFromHint(
1461
+ state,
1462
+ hintLeaf,
1463
+ entry.key,
1464
+ state.nextSequence
1465
+ );
1123
1466
  ids[i] = putEntryIntoLeaf(state, targetLeaf, entry.key, entry.value);
1124
1467
  hintLeaf = targetLeaf;
1125
1468
  }
@@ -1128,116 +1471,14 @@ var putManyEntries = (state, entries) => {
1128
1471
  return bulkLoadEntries(state, entries);
1129
1472
  };
1130
1473
 
1131
- // src/btree/rangeQuery.ts
1132
- var initRangeCursor = (state, startKey, endKey, options) => {
1133
- if (state.entryCount === 0) return null;
1134
- const compare = state.compareKeys;
1135
- const boundCompared = compare(startKey, endKey);
1136
- if (boundCompared > 0) return null;
1137
- const lowerExclusive = options?.lowerBound === "exclusive";
1138
- const upperExclusive = options?.upperBound === "exclusive";
1139
- if (lowerExclusive && upperExclusive && boundCompared === 0) return null;
1140
- const startSeq = lowerExclusive ? Number.MAX_SAFE_INTEGER : 0;
1141
- const leaf = findLeafForKey(state, startKey, startSeq);
1142
- const index = lowerExclusive ? upperBoundInLeaf(state, leaf, startKey, Number.MAX_SAFE_INTEGER) : lowerBoundInLeaf(state, leaf, startKey, 0);
1143
- return { leaf, index, compare, upperExclusive };
1144
- };
1145
- var countRangeEntries = (state, startKey, endKey, options) => {
1146
- const cursor = initRangeCursor(state, startKey, endKey, options);
1147
- if (cursor === null) return 0;
1148
- let cursorLeaf = cursor.leaf;
1149
- let cursorIndex = cursor.index;
1150
- const { compare, upperExclusive } = cursor;
1151
- let count = 0;
1152
- while (cursorLeaf !== null) {
1153
- const leafCount = leafEntryCount(cursorLeaf);
1154
- if (cursorIndex >= leafCount) {
1155
- cursorLeaf = cursorLeaf.next;
1156
- cursorIndex = 0;
1157
- continue;
1158
- }
1159
- const lastEntry = leafEntryAt(cursorLeaf, leafCount - 1);
1160
- const cmpLast = compare(lastEntry.key, endKey);
1161
- if (upperExclusive ? cmpLast < 0 : cmpLast <= 0) {
1162
- count += leafCount - cursorIndex;
1163
- cursorLeaf = cursorLeaf.next;
1164
- cursorIndex = 0;
1165
- continue;
1166
- }
1167
- const endSeq = upperExclusive ? 0 : Number.MAX_SAFE_INTEGER;
1168
- const endBound = upperExclusive ? lowerBoundInLeaf(state, cursorLeaf, endKey, endSeq) : upperBoundInLeaf(state, cursorLeaf, endKey, endSeq);
1169
- const limit = endBound < leafCount ? endBound : leafCount;
1170
- count += limit - cursorIndex;
1171
- return count;
1172
- }
1173
- return count;
1174
- };
1175
- var RANGE_PREALLOC_THRESHOLD = 200;
1176
- var allocateRangeOutput = (state, cursorLeaf, cursorIndex, compare, upperExclusive, startKey, endKey, options) => {
1177
- const firstLeafCount = leafEntryCount(cursorLeaf);
1178
- const firstLeafRemainder = firstLeafCount - cursorIndex;
1179
- if (firstLeafRemainder >= RANGE_PREALLOC_THRESHOLD && cursorLeaf.next !== null) {
1180
- const lastEntry = leafEntryAt(cursorLeaf, firstLeafCount - 1);
1181
- const cmpLast = compare(lastEntry.key, endKey);
1182
- if (upperExclusive ? cmpLast < 0 : cmpLast <= 0) {
1183
- const total = countRangeEntries(state, startKey, endKey, options);
1184
- return new Array(total);
1185
- }
1186
- }
1187
- return [];
1188
- };
1189
- var appendLeafSlice = (leaf, from, to, output, useIndexed, writeIdx) => {
1190
- if (useIndexed) {
1191
- for (let i = from; i < to; i += 1) {
1192
- output[writeIdx++] = leafEntryAt(leaf, i);
1193
- }
1194
- } else {
1195
- for (let i = from; i < to; i += 1) {
1196
- output.push(leafEntryAt(leaf, i));
1197
- }
1198
- }
1199
- return writeIdx;
1200
- };
1201
- var rangeQueryEntries = (state, startKey, endKey, options) => {
1202
- const cursor = initRangeCursor(state, startKey, endKey, options);
1203
- if (cursor === null) return [];
1204
- let cursorLeaf = cursor.leaf;
1205
- let cursorIndex = cursor.index;
1206
- const { compare, upperExclusive } = cursor;
1207
- const output = allocateRangeOutput(state, cursorLeaf, cursorIndex, compare, upperExclusive, startKey, endKey, options);
1208
- let writeIdx = 0;
1209
- const useIndexed = output.length > 0;
1210
- while (cursorLeaf !== null) {
1211
- const leafCount = leafEntryCount(cursorLeaf);
1212
- if (cursorIndex >= leafCount) {
1213
- cursorLeaf = cursorLeaf.next;
1214
- cursorIndex = 0;
1215
- continue;
1216
- }
1217
- const lastEntry = leafEntryAt(cursorLeaf, leafCount - 1);
1218
- const cmpLast = compare(lastEntry.key, endKey);
1219
- if (upperExclusive ? cmpLast < 0 : cmpLast <= 0) {
1220
- writeIdx = appendLeafSlice(cursorLeaf, cursorIndex, leafCount, output, useIndexed, writeIdx);
1221
- cursorLeaf = cursorLeaf.next;
1222
- cursorIndex = 0;
1223
- continue;
1224
- }
1225
- const endSeq = upperExclusive ? 0 : Number.MAX_SAFE_INTEGER;
1226
- const endBound = upperExclusive ? lowerBoundInLeaf(state, cursorLeaf, endKey, endSeq) : upperBoundInLeaf(state, cursorLeaf, endKey, endSeq);
1227
- const limit = endBound < leafCount ? endBound : leafCount;
1228
- appendLeafSlice(cursorLeaf, cursorIndex, limit, output, useIndexed, writeIdx);
1229
- return output;
1230
- }
1231
- return output;
1232
- };
1233
-
1234
1474
  // src/btree/serialization.ts
1235
1475
  var buildConfigFromState = (state) => {
1236
1476
  const config = {
1237
1477
  compareKeys: state.compareKeys,
1238
1478
  duplicateKeys: state.duplicateKeys,
1239
1479
  enableEntryIdLookup: state.entryKeys !== null,
1240
- autoScale: state.autoScale
1480
+ autoScale: state.autoScale,
1481
+ deleteRebalancePolicy: state.deleteRebalancePolicy
1241
1482
  };
1242
1483
  if (!state.autoScale) {
1243
1484
  config.maxLeafEntries = state.maxLeafEntries;
@@ -1257,17 +1498,17 @@ var serializeToJSON = (state) => {
1257
1498
  }
1258
1499
  leaf = leaf.next;
1259
1500
  }
1260
- return {
1261
- version: 1,
1262
- config: {
1263
- maxLeafEntries: state.maxLeafEntries,
1264
- maxBranchChildren: state.maxBranchChildren,
1265
- duplicateKeys: state.duplicateKeys,
1266
- enableEntryIdLookup: state.entryKeys !== null,
1267
- autoScale: state.autoScale
1268
- },
1269
- entries
1501
+ const config = {
1502
+ maxLeafEntries: state.maxLeafEntries,
1503
+ maxBranchChildren: state.maxBranchChildren,
1504
+ duplicateKeys: state.duplicateKeys,
1505
+ enableEntryIdLookup: state.entryKeys !== null,
1506
+ autoScale: state.autoScale
1270
1507
  };
1508
+ if (state.deleteRebalancePolicy !== "standard") {
1509
+ config.deleteRebalancePolicy = state.deleteRebalancePolicy;
1510
+ }
1511
+ return { version: 1, config, entries };
1271
1512
  };
1272
1513
  var MAX_SERIALIZED_ENTRIES = 1e6;
1273
1514
  var validateStructure = (json) => {
@@ -1330,13 +1571,28 @@ var validateBTreeJSON = (json) => {
1330
1571
  validateStructure(json);
1331
1572
  validateConfig(json.config);
1332
1573
  };
1574
+ var validateBTreeJSONSortOrder = (json, compareKeys) => {
1575
+ const strict = json.config.duplicateKeys !== "allow";
1576
+ for (let i = 1; i < json.entries.length; i += 1) {
1577
+ const cmp = compareKeys(json.entries[i - 1][0], json.entries[i][0]);
1578
+ if (cmp > 0) {
1579
+ throw new BTreeValidationError("fromJSON: entries not sorted.");
1580
+ }
1581
+ if (strict && cmp === 0) {
1582
+ throw new BTreeValidationError(
1583
+ 'fromJSON: duplicate keys require duplicateKeys "allow".'
1584
+ );
1585
+ }
1586
+ }
1587
+ };
1333
1588
  var buildConfigFromJSON = (json, compareKeys) => {
1334
1589
  const cfg = json.config;
1335
1590
  const config = {
1336
1591
  compareKeys,
1337
1592
  duplicateKeys: cfg.duplicateKeys,
1338
1593
  enableEntryIdLookup: cfg.enableEntryIdLookup,
1339
- autoScale: cfg.autoScale
1594
+ autoScale: cfg.autoScale,
1595
+ deleteRebalancePolicy: cfg.deleteRebalancePolicy
1340
1596
  };
1341
1597
  if (!cfg.autoScale) {
1342
1598
  config.maxLeafEntries = cfg.maxLeafEntries;
@@ -1345,22 +1601,63 @@ var buildConfigFromJSON = (json, compareKeys) => {
1345
1601
  return config;
1346
1602
  };
1347
1603
 
1348
- // src/btree/integrity-helpers.ts
1349
- var nodeMinKey = (node) => {
1350
- if (isLeafNode(node)) {
1351
- if (node.entryOffset >= node.entries.length) return null;
1352
- const e = node.entries[node.entryOffset];
1353
- return { key: e.key, sequence: e.entryId };
1604
+ // src/btree/traversal.ts
1605
+ var snapshotEntries = (state) => {
1606
+ const result = new Array(state.entryCount);
1607
+ let leaf = state.leftmostLeaf;
1608
+ let writeIdx = 0;
1609
+ while (leaf !== null) {
1610
+ const count = leafEntryCount(leaf);
1611
+ for (let i = 0; i < count; i += 1) {
1612
+ result[writeIdx++] = freezeEntry(leafEntryAt(leaf, i));
1613
+ }
1614
+ leaf = leaf.next;
1615
+ }
1616
+ return result;
1617
+ };
1618
+ var collectInternalEntries = (state) => {
1619
+ const result = new Array(state.entryCount);
1620
+ let leaf = state.leftmostLeaf;
1621
+ let writeIdx = 0;
1622
+ while (leaf !== null) {
1623
+ const count = leafEntryCount(leaf);
1624
+ for (let i = 0; i < count; i += 1) {
1625
+ result[writeIdx++] = leafEntryAt(leaf, i);
1626
+ }
1627
+ leaf = leaf.next;
1628
+ }
1629
+ return result;
1630
+ };
1631
+ var forEachEntry = (state, callback, thisArg) => {
1632
+ let leaf = state.leftmostLeaf;
1633
+ while (leaf !== null) {
1634
+ const count = leafEntryCount(leaf);
1635
+ for (let i = 0; i < count; i += 1) {
1636
+ callback.call(thisArg, freezeEntry(leafEntryAt(leaf, i)));
1637
+ }
1638
+ leaf = leaf.next;
1639
+ }
1640
+ };
1641
+
1642
+ // src/btree/integrity-helpers.ts
1643
+ var nodeMinKey = (node) => {
1644
+ if (isLeafNode(node)) {
1645
+ if (node.entryOffset >= node.entries.length) return null;
1646
+ const e = node.entries[node.entryOffset];
1647
+ return { key: e.key, sequence: e.entryId };
1354
1648
  }
1355
1649
  if (node.childOffset >= node.keys.length) return null;
1356
- return { key: node.keys[node.childOffset].key, sequence: node.keys[node.childOffset].sequence };
1650
+ return {
1651
+ key: node.keys[node.childOffset].key,
1652
+ sequence: node.keys[node.childOffset].sequence
1653
+ };
1357
1654
  };
1358
1655
  var compareNodeKeys = (comparator, leftKey, leftSeq, rightKey, rightSeq) => {
1359
1656
  const cmp = comparator(leftKey, rightKey);
1360
1657
  if (cmp !== 0) {
1361
1658
  return cmp;
1362
1659
  }
1363
- return leftSeq - rightSeq;
1660
+ return leftSeq < rightSeq ? -1 : leftSeq > rightSeq ? 1 : 0;
1364
1661
  };
1365
1662
  var getNodeMaxKey = (node) => {
1366
1663
  if (isLeafNode(node)) {
@@ -1375,9 +1672,7 @@ var getNodeMaxKey = (node) => {
1375
1672
  };
1376
1673
  var validateComparatorResult = (result) => {
1377
1674
  if (!Number.isFinite(result)) {
1378
- throw new BTreeValidationError(
1379
- "compareKeys must return a finite number."
1380
- );
1675
+ throw new BTreeValidationError("compareKeys must return a finite number.");
1381
1676
  }
1382
1677
  return result;
1383
1678
  };
@@ -1437,6 +1732,34 @@ var assertTransitivityAsInvariant = (state, first, second, third) => {
1437
1732
  throw error;
1438
1733
  }
1439
1734
  };
1735
+ var validateAdjacentLeafOrdering = (state, previous, cursor) => {
1736
+ const prevMax = getNodeMaxKey(previous);
1737
+ const currentMin = nodeMinKey(cursor);
1738
+ if (prevMax === null || currentMin === null) {
1739
+ throw new BTreeInvariantError(
1740
+ "Non-empty tree leaf chain contains empty leaf node."
1741
+ );
1742
+ }
1743
+ if (compareNodeKeys(
1744
+ state.compareKeys,
1745
+ prevMax.key,
1746
+ prevMax.sequence,
1747
+ currentMin.key,
1748
+ currentMin.sequence
1749
+ ) > 0) {
1750
+ throw new BTreeInvariantError("Adjacent leaf key ranges are out of order.");
1751
+ }
1752
+ const prevCount = leafEntryCount(previous);
1753
+ const curCount = leafEntryCount(cursor);
1754
+ if (state.duplicateKeys !== "allow" && prevCount > 0 && curCount > 0 && state.compareKeys(
1755
+ leafEntryAt(previous, prevCount - 1).key,
1756
+ leafEntryAt(cursor, 0).key
1757
+ ) === 0) {
1758
+ throw new BTreeInvariantError(
1759
+ "Duplicate user key detected across adjacent leaves with uniqueness policy."
1760
+ );
1761
+ }
1762
+ };
1440
1763
  var validateLeafChainStep = (state, cursor, previous, visited) => {
1441
1764
  if (!isLeafNode(cursor)) {
1442
1765
  throw new BTreeInvariantError("Leaf linkage cursor reached non-leaf node.");
@@ -1448,22 +1771,7 @@ var validateLeafChainStep = (state, cursor, previous, visited) => {
1448
1771
  throw new BTreeInvariantError("Leaf prev pointer mismatch.");
1449
1772
  }
1450
1773
  if (previous !== null && isLeafNode(previous)) {
1451
- const prevMax = getNodeMaxKey(previous);
1452
- const currentMin = nodeMinKey(cursor);
1453
- if (prevMax === null || currentMin === null) {
1454
- throw new BTreeInvariantError("Non-empty tree leaf chain contains empty leaf node.");
1455
- }
1456
- if (compareNodeKeys(state.compareKeys, prevMax.key, prevMax.sequence, currentMin.key, currentMin.sequence) > 0) {
1457
- throw new BTreeInvariantError("Adjacent leaf key ranges are out of order.");
1458
- }
1459
- const prevCount = leafEntryCount(previous);
1460
- const curCount = leafEntryCount(cursor);
1461
- if (state.duplicateKeys !== "allow" && prevCount > 0 && curCount > 0 && state.compareKeys(
1462
- leafEntryAt(previous, prevCount - 1).key,
1463
- leafEntryAt(cursor, 0).key
1464
- ) === 0) {
1465
- throw new BTreeInvariantError("Duplicate user key detected across adjacent leaves with uniqueness policy.");
1466
- }
1774
+ validateAdjacentLeafOrdering(state, previous, cursor);
1467
1775
  }
1468
1776
  };
1469
1777
  var validateLeafLinks = (state, expectedLeafCount) => {
@@ -1472,7 +1780,9 @@ var validateLeafLinks = (state, expectedLeafCount) => {
1472
1780
  throw new BTreeInvariantError("Empty tree root must be a leaf node.");
1473
1781
  }
1474
1782
  if (state.leftmostLeaf !== state.root || state.rightmostLeaf !== state.root) {
1475
- throw new BTreeInvariantError("Empty tree leaf pointers must reference root leaf.");
1783
+ throw new BTreeInvariantError(
1784
+ "Empty tree leaf pointers must reference root leaf."
1785
+ );
1476
1786
  }
1477
1787
  return;
1478
1788
  }
@@ -1497,7 +1807,9 @@ var validateLeafLinks = (state, expectedLeafCount) => {
1497
1807
  throw new BTreeInvariantError("Rightmost leaf pointer mismatch.");
1498
1808
  }
1499
1809
  if (leafCount !== expectedLeafCount) {
1500
- throw new BTreeInvariantError("Leaf chain count mismatch with tree traversal count.");
1810
+ throw new BTreeInvariantError(
1811
+ "Leaf chain count mismatch with tree traversal count."
1812
+ );
1501
1813
  }
1502
1814
  };
1503
1815
 
@@ -1524,7 +1836,9 @@ var validateLeafNodeOrdering = (state, node) => {
1524
1836
  leafEntryAt(node, index - 1).key,
1525
1837
  leafEntryAt(node, index).key
1526
1838
  ) === 0) {
1527
- throw new BTreeInvariantError("Duplicate user key detected in tree with uniqueness policy.");
1839
+ throw new BTreeInvariantError(
1840
+ "Duplicate user key detected in tree with uniqueness policy."
1841
+ );
1528
1842
  }
1529
1843
  }
1530
1844
  }
@@ -1532,12 +1846,7 @@ var validateLeafNodeOrdering = (state, node) => {
1532
1846
  const first = leafEntryAt(node, index - 2);
1533
1847
  const second = leafEntryAt(node, index - 1);
1534
1848
  const third = leafEntryAt(node, index);
1535
- assertTransitivityAsInvariant(
1536
- state,
1537
- first.key,
1538
- second.key,
1539
- third.key
1540
- );
1849
+ assertTransitivityAsInvariant(state, first.key, second.key, third.key);
1541
1850
  }
1542
1851
  if (count > state.maxLeafEntries) {
1543
1852
  throw new BTreeInvariantError("Leaf node exceeds maximum occupancy.");
@@ -1546,9 +1855,14 @@ var validateLeafNodeOrdering = (state, node) => {
1546
1855
  var validateLeafNode = (state, node, depth) => {
1547
1856
  validateLeafNodeOrdering(state, node);
1548
1857
  const count = leafEntryCount(node);
1549
- const baseMinLeaf = state.autoScale ? Math.ceil(computeAutoScaleTier(0).maxLeaf / 2) : state.minLeafEntries;
1858
+ let baseMinLeaf = state.autoScale ? Math.ceil(computeAutoScaleTier(0).maxLeaf / 2) : state.minLeafEntries;
1859
+ if (state.deleteRebalancePolicy === "lazy") {
1860
+ baseMinLeaf = applyLazyThreshold(baseMinLeaf);
1861
+ }
1550
1862
  if (node !== state.root && count < baseMinLeaf) {
1551
- throw new BTreeInvariantError("Non-root leaf node violates minimum occupancy.");
1863
+ throw new BTreeInvariantError(
1864
+ "Non-root leaf node violates minimum occupancy."
1865
+ );
1552
1866
  }
1553
1867
  const first = count === 0 ? null : leafEntryAt(node, 0);
1554
1868
  const last = count === 0 ? null : leafEntryAt(node, count - 1);
@@ -1570,76 +1884,110 @@ var validateBranchStructure = (state, node) => {
1570
1884
  }
1571
1885
  const baseMinBranch = state.autoScale ? Math.ceil(computeAutoScaleTier(0).maxBranch / 2) : state.minBranchChildren;
1572
1886
  if (node !== state.root && liveCount < baseMinBranch) {
1573
- throw new BTreeInvariantError("Non-root branch node violates minimum occupancy.");
1887
+ throw new BTreeInvariantError(
1888
+ "Non-root branch node violates minimum occupancy."
1889
+ );
1574
1890
  }
1575
1891
  if (liveCount > state.maxBranchChildren) {
1576
1892
  throw new BTreeInvariantError("Branch node exceeds maximum occupancy.");
1577
1893
  }
1578
1894
  if (node.keys.length !== node.children.length) {
1579
- throw new BTreeInvariantError("Branch keys array length does not match children array length.");
1895
+ throw new BTreeInvariantError(
1896
+ "Branch keys array length does not match children array length."
1897
+ );
1580
1898
  }
1581
1899
  };
1582
1900
  var validateBranchChild = (state, node, childIndex, depth) => {
1583
1901
  const child = node.children[childIndex];
1584
1902
  if (child.parent !== node) {
1585
- throw new BTreeInvariantError("Child-parent pointer mismatch in branch node.");
1903
+ throw new BTreeInvariantError(
1904
+ "Child-parent pointer mismatch in branch node."
1905
+ );
1586
1906
  }
1587
1907
  if (child.indexInParent !== childIndex) {
1588
- throw new BTreeInvariantError("Child indexInParent does not match actual position in parent.");
1908
+ throw new BTreeInvariantError(
1909
+ "Child indexInParent does not match actual position in parent."
1910
+ );
1589
1911
  }
1590
1912
  const childValidation = validateNode(state, child, depth + 1);
1591
1913
  if (childValidation.minKey === null || childValidation.maxKey === null) {
1592
- throw new BTreeInvariantError("Branch child must not be empty in non-root branch tree.");
1914
+ throw new BTreeInvariantError(
1915
+ "Branch child must not be empty in non-root branch tree."
1916
+ );
1593
1917
  }
1594
1918
  const cachedMinKey = node.keys[childIndex];
1595
1919
  const actualMinKey = nodeMinKey(child);
1596
- if (actualMinKey === null || compareNodeKeys(state.compareKeys, cachedMinKey.key, cachedMinKey.sequence, actualMinKey.key, actualMinKey.sequence) !== 0) {
1597
- throw new BTreeInvariantError("Branch cached key does not match actual child minimum key.");
1920
+ if (actualMinKey === null || compareNodeKeys(
1921
+ state.compareKeys,
1922
+ cachedMinKey.key,
1923
+ cachedMinKey.sequence,
1924
+ actualMinKey.key,
1925
+ actualMinKey.sequence
1926
+ ) !== 0) {
1927
+ throw new BTreeInvariantError(
1928
+ "Branch cached key does not match actual child minimum key."
1929
+ );
1598
1930
  }
1599
1931
  return childValidation;
1600
1932
  };
1933
+ var mergeChildValidation = (state, accumulated, childValidation) => {
1934
+ if (accumulated.leafDepth !== null && childValidation.leafDepth !== null && childValidation.leafDepth !== accumulated.leafDepth) {
1935
+ throw new BTreeInvariantError("Leaf depth mismatch detected in tree.");
1936
+ }
1937
+ if (accumulated.leafDepth === null && childValidation.leafDepth !== null) {
1938
+ accumulated.leafDepth = childValidation.leafDepth;
1939
+ }
1940
+ if (accumulated.previousChildMax !== null && compareNodeKeys(
1941
+ state.compareKeys,
1942
+ accumulated.previousChildMax.key,
1943
+ accumulated.previousChildMax.sequence,
1944
+ childValidation.minKey.key,
1945
+ childValidation.minKey.sequence
1946
+ ) >= 0) {
1947
+ throw new BTreeInvariantError(
1948
+ "Branch child key ranges are not strictly ordered."
1949
+ );
1950
+ }
1951
+ if (accumulated.minKey === null) accumulated.minKey = childValidation.minKey;
1952
+ accumulated.maxKey = childValidation.maxKey;
1953
+ accumulated.previousChildMax = childValidation.maxKey;
1954
+ accumulated.leafCount += childValidation.leafCount;
1955
+ accumulated.branchCount += childValidation.branchCount;
1956
+ accumulated.entryCount += childValidation.entryCount;
1957
+ };
1601
1958
  var validateNode = (state, node, depth) => {
1602
1959
  if (isLeafNode(node)) {
1603
1960
  return validateLeafNode(state, node, depth);
1604
1961
  }
1605
1962
  validateBranchStructure(state, node);
1606
- let leafDepth = null;
1607
- let leafCount = 0;
1608
- let branchCount = 1;
1609
- let entryCount = 0;
1610
- let minKey = null;
1611
- let maxKey = null;
1612
- let previousChildMax = null;
1963
+ const acc = {
1964
+ leafDepth: null,
1965
+ leafCount: 0,
1966
+ branchCount: 1,
1967
+ entryCount: 0,
1968
+ minKey: null,
1969
+ maxKey: null,
1970
+ previousChildMax: null
1971
+ };
1613
1972
  for (let childIndex = node.childOffset; childIndex < node.children.length; childIndex += 1) {
1614
1973
  const childValidation = validateBranchChild(state, node, childIndex, depth);
1615
- if (leafDepth !== null && childValidation.leafDepth !== null && childValidation.leafDepth !== leafDepth) {
1616
- throw new BTreeInvariantError("Leaf depth mismatch detected in tree.");
1617
- }
1618
- if (leafDepth === null && childValidation.leafDepth !== null) {
1619
- leafDepth = childValidation.leafDepth;
1620
- }
1621
- if (previousChildMax !== null && compareNodeKeys(
1622
- state.compareKeys,
1623
- previousChildMax.key,
1624
- previousChildMax.sequence,
1625
- childValidation.minKey.key,
1626
- childValidation.minKey.sequence
1627
- ) >= 0) {
1628
- throw new BTreeInvariantError("Branch child key ranges are not strictly ordered.");
1629
- }
1630
- if (minKey === null) minKey = childValidation.minKey;
1631
- maxKey = childValidation.maxKey;
1632
- previousChildMax = childValidation.maxKey;
1633
- leafCount += childValidation.leafCount;
1634
- branchCount += childValidation.branchCount;
1635
- entryCount += childValidation.entryCount;
1974
+ mergeChildValidation(state, acc, childValidation);
1636
1975
  }
1637
- return { minKey, maxKey, leafDepth, leafCount, branchCount, entryCount };
1976
+ return {
1977
+ minKey: acc.minKey,
1978
+ maxKey: acc.maxKey,
1979
+ leafDepth: acc.leafDepth,
1980
+ leafCount: acc.leafCount,
1981
+ branchCount: acc.branchCount,
1982
+ entryCount: acc.entryCount
1983
+ };
1638
1984
  };
1639
1985
  var assertInvariants = (state) => {
1640
1986
  const validation = validateNode(state, state.root, 0);
1641
1987
  if (validation.entryCount !== state.entryCount) {
1642
- throw new BTreeInvariantError("Index entry count mismatch between tree traversal and tracked state.");
1988
+ throw new BTreeInvariantError(
1989
+ "Index entry count mismatch between tree traversal and tracked state."
1990
+ );
1643
1991
  }
1644
1992
  validateLeafLinks(state, validation.leafCount);
1645
1993
  };
@@ -1693,44 +2041,56 @@ var InMemoryBTree = class _InMemoryBTree {
1693
2041
  return putManyEntries(this.state, entries);
1694
2042
  }
1695
2043
  remove(key) {
1696
- return removeFirstMatchingEntry(this.state, key);
2044
+ const entry = removeFirstMatchingEntry(this.state, key);
2045
+ if (entry === null) return null;
2046
+ return freezeEntry(entry);
1697
2047
  }
1698
2048
  removeById(entryId) {
1699
2049
  if (this.state.entryKeys === null) {
1700
2050
  throw new BTreeValidationError("Requires enableEntryIdLookup: true.");
1701
2051
  }
1702
- return removeEntryById(this.state, entryId);
2052
+ const entry = removeEntryById(this.state, entryId);
2053
+ if (entry === null) return null;
2054
+ return freezeEntry(entry);
1703
2055
  }
1704
2056
  peekById(entryId) {
1705
2057
  if (this.state.entryKeys === null) {
1706
2058
  throw new BTreeValidationError("Requires enableEntryIdLookup: true.");
1707
2059
  }
1708
- return peekEntryById(this.state, entryId);
2060
+ const entry = peekEntryById(this.state, entryId);
2061
+ if (entry === null) return null;
2062
+ return freezeEntry(entry);
1709
2063
  }
1710
2064
  updateById(entryId, value) {
1711
2065
  if (this.state.entryKeys === null) {
1712
2066
  throw new BTreeValidationError("Requires enableEntryIdLookup: true.");
1713
2067
  }
1714
- return updateEntryById(this.state, entryId, value);
2068
+ const entry = updateEntryById(this.state, entryId, value);
2069
+ if (entry === null) return null;
2070
+ return freezeEntry(entry);
1715
2071
  }
1716
2072
  popFirst() {
1717
- return popFirstEntry(this.state);
2073
+ const entry = popFirstEntry(this.state);
2074
+ if (entry === null) return null;
2075
+ return freezeEntry(entry);
1718
2076
  }
1719
2077
  peekFirst() {
1720
2078
  if (this.state.entryCount === 0) {
1721
2079
  return null;
1722
2080
  }
1723
- return leafEntryAt(this.state.leftmostLeaf, 0);
2081
+ return freezeEntry(leafEntryAt(this.state.leftmostLeaf, 0));
1724
2082
  }
1725
2083
  peekLast() {
1726
2084
  if (this.state.entryCount === 0) {
1727
2085
  return null;
1728
2086
  }
1729
2087
  const leaf = this.state.rightmostLeaf;
1730
- return leafEntryAt(leaf, leafEntryCount(leaf) - 1);
2088
+ return freezeEntry(leafEntryAt(leaf, leafEntryCount(leaf) - 1));
1731
2089
  }
1732
2090
  popLast() {
1733
- return popLastEntry(this.state);
2091
+ const entry = popLastEntry(this.state);
2092
+ if (entry === null) return null;
2093
+ return freezeEntry(entry);
1734
2094
  }
1735
2095
  clear() {
1736
2096
  const emptyLeaf = createLeafNode([], null);
@@ -1740,16 +2100,9 @@ var InMemoryBTree = class _InMemoryBTree {
1740
2100
  this.state.entryCount = 0;
1741
2101
  this.state._cursor.leaf = emptyLeaf;
1742
2102
  this.state._cursor.index = 0;
1743
- if (this.state.entryKeys !== null) {
1744
- this.state.entryKeys.clear();
1745
- }
2103
+ this.state.entryKeys?.clear();
1746
2104
  if (this.state.autoScale) {
1747
- const tier = computeAutoScaleTier(0);
1748
- this.state.maxLeafEntries = tier.maxLeaf;
1749
- this.state.maxBranchChildren = tier.maxBranch;
1750
- this.state.minLeafEntries = Math.ceil(tier.maxLeaf / 2);
1751
- this.state.minBranchChildren = Math.ceil(tier.maxBranch / 2);
1752
- this.state._nextAutoScaleThreshold = computeNextAutoScaleThreshold(0);
2105
+ resetAutoScaleToTier0(this.state);
1753
2106
  }
1754
2107
  }
1755
2108
  get(key) {
@@ -1763,12 +2116,12 @@ var InMemoryBTree = class _InMemoryBTree {
1763
2116
  findFirst(key) {
1764
2117
  const found = findFirstMatchingUserKey(this.state, key);
1765
2118
  if (found === null) return null;
1766
- return leafEntryAt(found.leaf, found.index);
2119
+ return freezeEntry(leafEntryAt(found.leaf, found.index));
1767
2120
  }
1768
2121
  findLast(key) {
1769
2122
  const found = findLastMatchingUserKey(this.state, key);
1770
2123
  if (found === null) return null;
1771
- return leafEntryAt(found.leaf, found.index);
2124
+ return freezeEntry(leafEntryAt(found.leaf, found.index));
1772
2125
  }
1773
2126
  nextHigherKey(key) {
1774
2127
  return findNextHigherKey(this.state, key);
@@ -1779,7 +2132,7 @@ var InMemoryBTree = class _InMemoryBTree {
1779
2132
  getPairOrNextLower(key) {
1780
2133
  const found = findPairOrNextLower(this.state, key);
1781
2134
  if (found === null) return null;
1782
- return leafEntryAt(found.leaf, found.index);
2135
+ return freezeEntry(leafEntryAt(found.leaf, found.index));
1783
2136
  }
1784
2137
  count(startKey, endKey, options) {
1785
2138
  return countRangeEntries(this.state, startKey, endKey, options);
@@ -1788,14 +2141,14 @@ var InMemoryBTree = class _InMemoryBTree {
1788
2141
  return deleteRangeEntries(this.state, startKey, endKey, options);
1789
2142
  }
1790
2143
  range(startKey, endKey, options) {
1791
- return rangeQueryEntries(this.state, startKey, endKey, options);
2144
+ return rangeQueryPublicEntries(this.state, startKey, endKey, options);
1792
2145
  }
1793
2146
  *entries() {
1794
2147
  let leaf = this.state.leftmostLeaf;
1795
2148
  while (leaf !== null) {
1796
2149
  const count = leafEntryCount(leaf);
1797
2150
  for (let i = 0; i < count; i += 1) {
1798
- yield leafEntryAt(leaf, i);
2151
+ yield freezeEntry(leafEntryAt(leaf, i));
1799
2152
  }
1800
2153
  leaf = leaf.next;
1801
2154
  }
@@ -1805,7 +2158,7 @@ var InMemoryBTree = class _InMemoryBTree {
1805
2158
  while (leaf !== null) {
1806
2159
  const count = leafEntryCount(leaf);
1807
2160
  for (let i = count - 1; i >= 0; i -= 1) {
1808
- yield leafEntryAt(leaf, i);
2161
+ yield freezeEntry(leafEntryAt(leaf, i));
1809
2162
  }
1810
2163
  leaf = leaf.prev;
1811
2164
  }
@@ -1823,48 +2176,26 @@ var InMemoryBTree = class _InMemoryBTree {
1823
2176
  [Symbol.iterator]() {
1824
2177
  return this.entries();
1825
2178
  }
2179
+ forEachRange(startKey, endKey, callback, options) {
2180
+ forEachRangeEntries(this.state, startKey, endKey, callback, options);
2181
+ }
1826
2182
  forEach(callback, thisArg) {
1827
- let leaf = this.state.leftmostLeaf;
1828
- while (leaf !== null) {
1829
- const count = leafEntryCount(leaf);
1830
- for (let i = 0; i < count; i += 1) {
1831
- callback.call(thisArg, leafEntryAt(leaf, i));
1832
- }
1833
- leaf = leaf.next;
1834
- }
2183
+ forEachEntry(this.state, callback, thisArg);
1835
2184
  }
1836
2185
  snapshot() {
1837
- const result = new Array(this.state.entryCount);
1838
- let leaf = this.state.leftmostLeaf;
1839
- let writeIdx = 0;
1840
- while (leaf !== null) {
1841
- const count = leafEntryCount(leaf);
1842
- for (let i = 0; i < count; i += 1) {
1843
- result[writeIdx++] = leafEntryAt(leaf, i);
1844
- }
1845
- leaf = leaf.next;
1846
- }
1847
- return result;
2186
+ return snapshotEntries(this.state);
1848
2187
  }
1849
2188
  clone() {
1850
- const cloned = new _InMemoryBTree(buildConfigFromState(this.state));
2189
+ const cloned = new _InMemoryBTree(
2190
+ buildConfigFromState(this.state)
2191
+ );
1851
2192
  applyAutoScaleCapacitySnapshot(
1852
2193
  cloned.state,
1853
2194
  this.state.maxLeafEntries,
1854
2195
  this.state.maxBranchChildren
1855
2196
  );
1856
2197
  if (this.state.entryCount > 0) {
1857
- const pairs = new Array(this.state.entryCount);
1858
- let leaf = this.state.leftmostLeaf;
1859
- let writeIdx = 0;
1860
- while (leaf !== null) {
1861
- const count = leafEntryCount(leaf);
1862
- for (let i = 0; i < count; i += 1) {
1863
- pairs[writeIdx++] = leafEntryAt(leaf, i);
1864
- }
1865
- leaf = leaf.next;
1866
- }
1867
- cloned.putMany(pairs);
2198
+ cloned.putMany(collectInternalEntries(this.state));
1868
2199
  }
1869
2200
  return cloned;
1870
2201
  }
@@ -1873,26 +2204,19 @@ var InMemoryBTree = class _InMemoryBTree {
1873
2204
  }
1874
2205
  static fromJSON(json, compareKeys) {
1875
2206
  validateBTreeJSON(json);
1876
- const strict = json.config.duplicateKeys !== "allow";
1877
- for (let i = 1; i < json.entries.length; i += 1) {
1878
- const cmp = compareKeys(json.entries[i - 1][0], json.entries[i][0]);
1879
- if (cmp > 0) {
1880
- throw new BTreeValidationError("fromJSON: entries not sorted.");
1881
- }
1882
- if (strict && cmp === 0) {
1883
- throw new BTreeValidationError(
1884
- 'fromJSON: duplicate keys require duplicateKeys "allow".'
1885
- );
1886
- }
1887
- }
1888
- const tree = new _InMemoryBTree(buildConfigFromJSON(json, compareKeys));
2207
+ validateBTreeJSONSortOrder(json, compareKeys);
2208
+ const tree = new _InMemoryBTree(
2209
+ buildConfigFromJSON(json, compareKeys)
2210
+ );
1889
2211
  applyAutoScaleCapacitySnapshot(
1890
2212
  tree.state,
1891
2213
  json.config.maxLeafEntries,
1892
2214
  json.config.maxBranchChildren
1893
2215
  );
1894
2216
  if (json.entries.length > 0) {
1895
- const pairs = new Array(json.entries.length);
2217
+ const pairs = new Array(
2218
+ json.entries.length
2219
+ );
1896
2220
  for (let i = 0; i < json.entries.length; i += 1) {
1897
2221
  pairs[i] = { key: json.entries[i][0], value: json.entries[i][1] };
1898
2222
  }
@@ -1933,51 +2257,110 @@ var assertNeverMutation = (mutation) => {
1933
2257
  `Unsupported mutation type from shared store: ${String(unknownMutation.type)}`
1934
2258
  );
1935
2259
  };
1936
- var validateMutationBatch = (mutations, expectedConfigFingerprint) => {
1937
- for (const mutation of mutations) {
1938
- if (typeof mutation !== "object" || mutation === null) {
1939
- throw new BTreeConcurrencyError("Malformed mutation: expected an object.");
2260
+ var validatePutManyEntries = (entries) => {
2261
+ for (const entry of entries) {
2262
+ if (typeof entry !== "object" || entry === null || !("key" in entry) || !("value" in entry)) {
2263
+ throw new BTreeConcurrencyError(
2264
+ "Malformed putMany mutation: each entry must have key and value."
2265
+ );
1940
2266
  }
1941
- const m = mutation;
1942
- switch (m.type) {
1943
- case "init":
1944
- if (typeof m.configFingerprint !== "string") {
1945
- throw new BTreeConcurrencyError("Malformed init mutation: missing configFingerprint.");
1946
- }
1947
- if (expectedConfigFingerprint !== void 0 && m.configFingerprint !== expectedConfigFingerprint) {
1948
- throw new BTreeConcurrencyError(
1949
- "Config mismatch: store peers must share identical tree config."
1950
- );
1951
- }
1952
- break;
1953
- case "put":
1954
- if (!("key" in m) || !("value" in m)) {
1955
- throw new BTreeConcurrencyError("Malformed put mutation: missing key or value.");
1956
- }
1957
- break;
1958
- case "remove":
1959
- if (!("key" in m)) {
1960
- throw new BTreeConcurrencyError("Malformed remove mutation: missing key.");
1961
- }
1962
- break;
1963
- case "removeById":
1964
- if (!("entryId" in m)) {
1965
- throw new BTreeConcurrencyError("Malformed removeById mutation: missing entryId.");
1966
- }
1967
- break;
1968
- case "updateById":
1969
- if (!("entryId" in m) || !("value" in m)) {
1970
- throw new BTreeConcurrencyError("Malformed updateById mutation: missing entryId or value.");
1971
- }
1972
- break;
1973
- case "popFirst":
1974
- case "popLast":
1975
- break;
1976
- default:
2267
+ }
2268
+ };
2269
+ var validateInitMutation = (m, expectedConfigFingerprint) => {
2270
+ if (typeof m.configFingerprint !== "string") {
2271
+ throw new BTreeConcurrencyError(
2272
+ "Malformed init mutation: missing configFingerprint."
2273
+ );
2274
+ }
2275
+ if (expectedConfigFingerprint !== void 0 && m.configFingerprint !== expectedConfigFingerprint) {
2276
+ throw new BTreeConcurrencyError(
2277
+ "Config mismatch: store peers must share identical tree config."
2278
+ );
2279
+ }
2280
+ };
2281
+ var validateMutationFields = (m) => {
2282
+ switch (m.type) {
2283
+ case "put":
2284
+ if (!("key" in m) || !("value" in m)) {
1977
2285
  throw new BTreeConcurrencyError(
1978
- `Unsupported mutation type from shared store: ${String(m.type)}`
2286
+ "Malformed put mutation: missing key or value."
1979
2287
  );
2288
+ }
2289
+ break;
2290
+ case "remove":
2291
+ if (!("key" in m)) {
2292
+ throw new BTreeConcurrencyError(
2293
+ "Malformed remove mutation: missing key."
2294
+ );
2295
+ }
2296
+ break;
2297
+ case "removeById":
2298
+ if (!("entryId" in m)) {
2299
+ throw new BTreeConcurrencyError(
2300
+ "Malformed removeById mutation: missing entryId."
2301
+ );
2302
+ }
2303
+ break;
2304
+ case "updateById":
2305
+ if (!("entryId" in m) || !("value" in m)) {
2306
+ throw new BTreeConcurrencyError(
2307
+ "Malformed updateById mutation: missing entryId or value."
2308
+ );
2309
+ }
2310
+ break;
2311
+ case "putMany":
2312
+ if (!("entries" in m) || !Array.isArray(m.entries)) {
2313
+ throw new BTreeConcurrencyError(
2314
+ "Malformed putMany mutation: missing entries array."
2315
+ );
2316
+ }
2317
+ validatePutManyEntries(m.entries);
2318
+ break;
2319
+ case "deleteRange":
2320
+ if (!("startKey" in m) || !("endKey" in m)) {
2321
+ throw new BTreeConcurrencyError(
2322
+ "Malformed deleteRange mutation: missing startKey or endKey."
2323
+ );
2324
+ }
2325
+ break;
2326
+ default:
2327
+ break;
2328
+ }
2329
+ };
2330
+ var validateSingleMutation = (m, expectedConfigFingerprint) => {
2331
+ switch (m.type) {
2332
+ case "init":
2333
+ validateInitMutation(m, expectedConfigFingerprint);
2334
+ break;
2335
+ case "put":
2336
+ case "remove":
2337
+ case "removeById":
2338
+ case "updateById":
2339
+ case "putMany":
2340
+ case "deleteRange":
2341
+ validateMutationFields(m);
2342
+ break;
2343
+ case "popFirst":
2344
+ case "popLast":
2345
+ case "clear":
2346
+ break;
2347
+ default:
2348
+ throw new BTreeConcurrencyError(
2349
+ `Unsupported mutation type from shared store: ${String(m.type)}`
2350
+ );
2351
+ }
2352
+ };
2353
+ var validateMutationBatch = (mutations, expectedConfigFingerprint) => {
2354
+ for (const mutation of mutations) {
2355
+ if (typeof mutation !== "object" || mutation === null) {
2356
+ throw new BTreeConcurrencyError(
2357
+ "Malformed mutation: expected an object."
2358
+ );
1980
2359
  }
2360
+ validateSingleMutation(
2361
+ mutation,
2362
+ expectedConfigFingerprint
2363
+ );
1981
2364
  }
1982
2365
  };
1983
2366
  var normalizeMaxRetries = (value) => {
@@ -2007,9 +2390,7 @@ var normalizeReadMode = (value) => {
2007
2390
  return "strong";
2008
2391
  }
2009
2392
  if (value !== "strong" && value !== "local") {
2010
- throw new BTreeConcurrencyError(
2011
- `readMode: must be 'strong' or 'local'.`
2012
- );
2393
+ throw new BTreeConcurrencyError(`readMode: must be 'strong' or 'local'.`);
2013
2394
  }
2014
2395
  return value;
2015
2396
  };
@@ -2038,79 +2419,170 @@ function assertAppendVersionContract(expectedVersion, appendResult) {
2038
2419
  }
2039
2420
  }
2040
2421
 
2041
- // src/concurrency/ConcurrentInMemoryBTree.ts
2042
- var ConcurrentInMemoryBTree = class {
2043
- constructor(config) {
2044
- this.store = config.store;
2045
- this.maxRetries = normalizeMaxRetries(config.maxRetries);
2046
- this.maxSyncMutationsPerBatch = normalizeMaxSyncMutationsPerBatch(
2047
- config.maxSyncMutationsPerBatch
2422
+ // src/concurrency/writeOps.ts
2423
+ var applyMutationLocal = (tree, mutation, onInit) => {
2424
+ switch (mutation.type) {
2425
+ case "init":
2426
+ onInit();
2427
+ return null;
2428
+ case "put":
2429
+ return tree.put(mutation.key, mutation.value);
2430
+ case "putMany":
2431
+ return tree.putMany(mutation.entries);
2432
+ case "remove":
2433
+ return tree.remove(mutation.key);
2434
+ case "removeById":
2435
+ return tree.removeById(mutation.entryId);
2436
+ case "updateById":
2437
+ return tree.updateById(mutation.entryId, mutation.value);
2438
+ case "popFirst":
2439
+ return tree.popFirst();
2440
+ case "popLast":
2441
+ return tree.popLast();
2442
+ case "deleteRange":
2443
+ return tree.deleteRange(
2444
+ mutation.startKey,
2445
+ mutation.endKey,
2446
+ mutation.options
2447
+ );
2448
+ case "clear":
2449
+ tree.clear();
2450
+ return null;
2451
+ default:
2452
+ return assertNeverMutation(mutation);
2453
+ }
2454
+ };
2455
+ var createPutEvaluator = (duplicateKeys, key, value) => {
2456
+ return (tree) => {
2457
+ if (duplicateKeys === "reject" && tree.hasKey(key)) {
2458
+ throw new BTreeValidationError("Duplicate key rejected.");
2459
+ }
2460
+ return { type: "put", key, value };
2461
+ };
2462
+ };
2463
+ var createRemoveEvaluator = (key) => {
2464
+ return (tree) => {
2465
+ return tree.hasKey(key) ? { type: "remove", key } : null;
2466
+ };
2467
+ };
2468
+ var createRemoveByIdEvaluator = (entryId) => {
2469
+ return (tree) => {
2470
+ return tree.peekById(entryId) !== null ? { type: "removeById", entryId } : null;
2471
+ };
2472
+ };
2473
+ var createUpdateByIdEvaluator = (entryId, value) => {
2474
+ return (tree) => {
2475
+ return tree.peekById(entryId) !== null ? { type: "updateById", entryId, value } : null;
2476
+ };
2477
+ };
2478
+ var createPopFirstEvaluator = () => {
2479
+ return (tree) => {
2480
+ return tree.peekFirst() !== null ? { type: "popFirst" } : null;
2481
+ };
2482
+ };
2483
+ var createPopLastEvaluator = () => {
2484
+ return (tree) => {
2485
+ return tree.peekLast() !== null ? { type: "popLast" } : null;
2486
+ };
2487
+ };
2488
+ var createPutManyEvaluator = (entries, duplicateKeys, compareKeys) => {
2489
+ return (tree) => {
2490
+ const strictlyAscending = duplicateKeys !== "allow";
2491
+ for (let i = 1; i < entries.length; i += 1) {
2492
+ const cmp = compareKeys(entries[i - 1].key, entries[i].key);
2493
+ if (cmp > 0) {
2494
+ throw new BTreeValidationError(
2495
+ "putMany: entries not in ascending order."
2496
+ );
2497
+ }
2498
+ if (strictlyAscending && cmp === 0) {
2499
+ throw new BTreeValidationError(
2500
+ duplicateKeys === "reject" ? "putMany: duplicate key rejected." : "putMany: equal keys not allowed in strict mode."
2501
+ );
2502
+ }
2503
+ }
2504
+ if (duplicateKeys === "reject") {
2505
+ for (const entry of entries) {
2506
+ if (tree.hasKey(entry.key)) {
2507
+ throw new BTreeValidationError("Duplicate key rejected.");
2508
+ }
2509
+ }
2510
+ }
2511
+ return { type: "putMany", entries };
2512
+ };
2513
+ };
2514
+ var createDeleteRangeEvaluator = (startKey, endKey, options) => {
2515
+ return (tree) => {
2516
+ const count = tree.count(startKey, endKey, options);
2517
+ if (count === 0) {
2518
+ return null;
2519
+ }
2520
+ return { type: "deleteRange", startKey, endKey, options };
2521
+ };
2522
+ };
2523
+ var createClearEvaluator = () => {
2524
+ return () => ({ type: "clear" });
2525
+ };
2526
+
2527
+ // src/concurrency/syncLogValidation.ts
2528
+ var validateSyncLog = (log, maxSyncMutationsPerBatch) => {
2529
+ if (typeof log.version !== "bigint") {
2530
+ throw new BTreeConcurrencyError("Store contract: version must be bigint.");
2531
+ }
2532
+ if (!Array.isArray(log.mutations)) {
2533
+ throw new BTreeConcurrencyError(
2534
+ "Store contract: mutations must be an array."
2048
2535
  );
2049
- this.duplicateKeys = config.duplicateKeys ?? "replace";
2050
- this.readMode = normalizeReadMode(config.readMode);
2051
- this.configFingerprint = computeConfigFingerprint(config);
2052
- this.tree = new InMemoryBTree({
2053
- compareKeys: config.compareKeys,
2054
- maxLeafEntries: config.maxLeafEntries,
2055
- maxBranchChildren: config.maxBranchChildren,
2056
- duplicateKeys: config.duplicateKeys,
2057
- enableEntryIdLookup: config.enableEntryIdLookup,
2058
- autoScale: config.autoScale
2059
- });
2536
+ }
2537
+ if (log.mutations.length > maxSyncMutationsPerBatch) {
2538
+ throw new BTreeConcurrencyError(
2539
+ `Sync batch exceeded limit (${String(maxSyncMutationsPerBatch)}).`
2540
+ );
2541
+ }
2542
+ };
2543
+
2544
+ // src/concurrency/coordinator.ts
2545
+ var Coordinator = class {
2546
+ constructor(tree, store, maxRetries, maxSyncMutationsPerBatch, configFingerprint, readMode) {
2547
+ this.tree = tree;
2548
+ this.store = store;
2549
+ this.maxRetries = maxRetries;
2550
+ this.maxSyncMutationsPerBatch = maxSyncMutationsPerBatch;
2551
+ this.configFingerprint = configFingerprint;
2552
+ this.readMode = readMode;
2060
2553
  this.currentVersion = 0n;
2061
2554
  this.operationQueue = Promise.resolve();
2062
2555
  this.initSeen = false;
2063
- }
2064
- async sync() {
2065
- await this.runExclusive(async () => {
2066
- await this.syncUnlocked();
2067
- });
2556
+ this.corrupted = false;
2068
2557
  }
2069
2558
  async syncUnlocked() {
2070
2559
  const log = await this.store.getLogEntriesSince(this.currentVersion);
2071
- if (typeof log.version !== "bigint") {
2072
- throw new BTreeConcurrencyError("Store contract: version must be bigint.");
2073
- }
2074
- if (!Array.isArray(log.mutations)) {
2075
- throw new BTreeConcurrencyError("Store contract: mutations must be an array.");
2076
- }
2077
- if (log.mutations.length > this.maxSyncMutationsPerBatch) {
2078
- throw new BTreeConcurrencyError(
2079
- `Sync batch exceeded limit (${String(this.maxSyncMutationsPerBatch)}).`
2080
- );
2081
- }
2082
- if (log.version <= this.currentVersion) {
2083
- return;
2084
- }
2560
+ validateSyncLog(log, this.maxSyncMutationsPerBatch);
2561
+ if (log.version <= this.currentVersion) return;
2085
2562
  validateMutationBatch(log.mutations, this.configFingerprint);
2086
- for (const mutation of log.mutations) {
2087
- this.applyMutationLocal(mutation);
2088
- }
2089
- this.currentVersion = log.version;
2090
- }
2091
- applyMutationLocal(mutation) {
2092
- switch (mutation.type) {
2093
- case "init":
2563
+ try {
2564
+ const markInit = () => {
2094
2565
  this.initSeen = true;
2095
- return null;
2096
- case "put":
2097
- return this.tree.put(mutation.key, mutation.value);
2098
- case "remove":
2099
- return this.tree.remove(mutation.key);
2100
- case "removeById":
2101
- return this.tree.removeById(mutation.entryId);
2102
- case "updateById":
2103
- return this.tree.updateById(mutation.entryId, mutation.value);
2104
- case "popFirst":
2105
- return this.tree.popFirst();
2106
- case "popLast":
2107
- return this.tree.popLast();
2108
- default:
2109
- return assertNeverMutation(mutation);
2566
+ };
2567
+ for (const mutation of log.mutations)
2568
+ applyMutationLocal(this.tree, mutation, markInit);
2569
+ this.currentVersion = log.version;
2570
+ } catch (error) {
2571
+ this.corrupted = true;
2572
+ const cause = error instanceof Error ? error.message : String(error);
2573
+ throw new BTreeConcurrencyError(
2574
+ `Replay failure: instance is permanently corrupted. Discard and create a new instance. Cause: ${cause}`
2575
+ );
2110
2576
  }
2111
2577
  }
2112
2578
  runExclusive(operation) {
2113
- const run = async () => operation();
2579
+ const run = async () => {
2580
+ if (this.corrupted)
2581
+ throw new BTreeConcurrencyError(
2582
+ "Instance is permanently corrupted. Discard and create a new instance."
2583
+ );
2584
+ return operation();
2585
+ };
2114
2586
  const result = this.operationQueue.then(run, run);
2115
2587
  this.operationQueue = result.then(
2116
2588
  () => void 0,
@@ -2120,137 +2592,207 @@ var ConcurrentInMemoryBTree = class {
2120
2592
  }
2121
2593
  readOp(fn) {
2122
2594
  return this.runExclusive(async () => {
2123
- if (this.readMode === "strong") {
2124
- await this.syncUnlocked();
2125
- }
2595
+ if (this.readMode === "strong") await this.syncUnlocked();
2126
2596
  return fn(this.tree);
2127
2597
  });
2128
2598
  }
2129
- async appendMutationAndApplyUnlocked(evaluate) {
2599
+ async appendAndApply(evaluate) {
2130
2600
  for (let attempt = 0; attempt < this.maxRetries; attempt += 1) {
2131
2601
  await this.syncUnlocked();
2132
2602
  const mutation = evaluate(this.tree);
2133
- if (mutation === null) {
2134
- return null;
2135
- }
2603
+ if (mutation === null) return null;
2136
2604
  const expectedVersion = this.currentVersion;
2137
- const mutations = this.initSeen ? [mutation] : [{ type: "init", configFingerprint: this.configFingerprint }, mutation];
2605
+ const mutations = this.initSeen ? [mutation] : [
2606
+ { type: "init", configFingerprint: this.configFingerprint },
2607
+ mutation
2608
+ ];
2138
2609
  const appendResult = await this.store.append(expectedVersion, mutations);
2139
2610
  assertAppendVersionContract(expectedVersion, appendResult);
2140
2611
  if (appendResult.applied) {
2141
- for (const m of mutations) {
2142
- if (m === mutation) break;
2143
- this.applyMutationLocal(m);
2612
+ try {
2613
+ const markInit = () => {
2614
+ this.initSeen = true;
2615
+ };
2616
+ for (const m of mutations) {
2617
+ if (m === mutation) break;
2618
+ applyMutationLocal(this.tree, m, markInit);
2619
+ }
2620
+ const result = applyMutationLocal(
2621
+ this.tree,
2622
+ mutation,
2623
+ markInit
2624
+ );
2625
+ this.currentVersion = appendResult.version;
2626
+ return result;
2627
+ } catch (error) {
2628
+ this.corrupted = true;
2629
+ const cause = error instanceof Error ? error.message : String(error);
2630
+ throw new BTreeConcurrencyError(
2631
+ `Local apply failure after successful append: instance is permanently corrupted. Discard and create a new instance. Cause: ${cause}`
2632
+ );
2144
2633
  }
2145
- const localResult = this.applyMutationLocal(mutation);
2146
- this.currentVersion = appendResult.version;
2147
- return localResult;
2148
2634
  }
2149
2635
  }
2150
2636
  throw new BTreeConcurrencyError(
2151
2637
  `Mutation failed after ${String(this.maxRetries)} retries.`
2152
2638
  );
2153
2639
  }
2154
- async put(key, value) {
2155
- return this.runExclusive(async () => {
2156
- return this.appendMutationAndApplyUnlocked(
2157
- (tree) => {
2158
- if (this.duplicateKeys === "reject" && tree.hasKey(key)) {
2159
- throw new BTreeValidationError("Duplicate key rejected.");
2160
- }
2161
- return { type: "put", key, value };
2162
- }
2163
- );
2640
+ writeOp(evaluator) {
2641
+ return this.runExclusive(async () => this.appendAndApply(evaluator));
2642
+ }
2643
+ };
2644
+
2645
+ // src/concurrency/ConcurrentInMemoryBTree.ts
2646
+ var ConcurrentInMemoryBTree = class {
2647
+ constructor(config) {
2648
+ this.compareKeys = config.compareKeys;
2649
+ this.duplicateKeys = config.duplicateKeys ?? "replace";
2650
+ const tree = new InMemoryBTree({
2651
+ compareKeys: config.compareKeys,
2652
+ maxLeafEntries: config.maxLeafEntries,
2653
+ maxBranchChildren: config.maxBranchChildren,
2654
+ duplicateKeys: config.duplicateKeys,
2655
+ enableEntryIdLookup: config.enableEntryIdLookup,
2656
+ autoScale: config.autoScale,
2657
+ deleteRebalancePolicy: config.deleteRebalancePolicy
2164
2658
  });
2659
+ this.coord = new Coordinator(
2660
+ tree,
2661
+ config.store,
2662
+ normalizeMaxRetries(config.maxRetries),
2663
+ normalizeMaxSyncMutationsPerBatch(config.maxSyncMutationsPerBatch),
2664
+ computeConfigFingerprint(config),
2665
+ normalizeReadMode(config.readMode)
2666
+ );
2165
2667
  }
2166
- async remove(key) {
2167
- return this.runExclusive(async () => {
2168
- return this.appendMutationAndApplyUnlocked(
2169
- (tree) => {
2170
- return tree.hasKey(key) ? { type: "remove", key } : null;
2171
- }
2172
- );
2668
+ async sync() {
2669
+ await this.coord.runExclusive(async () => {
2670
+ await this.coord.syncUnlocked();
2173
2671
  });
2174
2672
  }
2175
- async removeById(entryId) {
2176
- return this.runExclusive(async () => {
2177
- return this.appendMutationAndApplyUnlocked(
2178
- (tree) => {
2179
- return tree.peekById(entryId) !== null ? { type: "removeById", entryId } : null;
2180
- }
2181
- );
2673
+ async syncThenRead(fn) {
2674
+ return this.coord.runExclusive(async () => {
2675
+ await this.coord.syncUnlocked();
2676
+ return fn(this.coord.tree);
2182
2677
  });
2183
2678
  }
2679
+ async put(key, value) {
2680
+ return this.coord.writeOp(
2681
+ createPutEvaluator(this.duplicateKeys, key, value)
2682
+ );
2683
+ }
2684
+ async remove(key) {
2685
+ return this.coord.writeOp(createRemoveEvaluator(key));
2686
+ }
2687
+ async removeById(entryId) {
2688
+ return this.coord.writeOp(createRemoveByIdEvaluator(entryId));
2689
+ }
2184
2690
  async updateById(entryId, value) {
2185
- return this.runExclusive(async () => {
2186
- return this.appendMutationAndApplyUnlocked(
2187
- (tree) => {
2188
- return tree.peekById(entryId) !== null ? { type: "updateById", entryId, value } : null;
2189
- }
2190
- );
2191
- });
2691
+ return this.coord.writeOp(createUpdateByIdEvaluator(entryId, value));
2192
2692
  }
2193
2693
  async popFirst() {
2194
- return this.runExclusive(async () => {
2195
- return this.appendMutationAndApplyUnlocked((tree) => {
2196
- return tree.peekFirst() !== null ? { type: "popFirst" } : null;
2197
- });
2198
- });
2694
+ return this.coord.writeOp(createPopFirstEvaluator());
2695
+ }
2696
+ async popLast() {
2697
+ return this.coord.writeOp(createPopLastEvaluator());
2698
+ }
2699
+ async putMany(entries) {
2700
+ if (entries.length === 0) return [];
2701
+ return this.coord.writeOp(
2702
+ createPutManyEvaluator(entries, this.duplicateKeys, this.compareKeys)
2703
+ );
2704
+ }
2705
+ async deleteRange(startKey, endKey, options) {
2706
+ const result = await this.coord.writeOp(
2707
+ createDeleteRangeEvaluator(startKey, endKey, options)
2708
+ );
2709
+ return result ?? 0;
2710
+ }
2711
+ async clear() {
2712
+ await this.coord.writeOp(createClearEvaluator());
2199
2713
  }
2200
2714
  async get(key) {
2201
- return this.readOp((tree) => tree.get(key));
2715
+ return this.coord.readOp((t) => t.get(key));
2202
2716
  }
2203
2717
  async hasKey(key) {
2204
- return this.readOp((tree) => tree.hasKey(key));
2718
+ return this.coord.readOp((t) => t.hasKey(key));
2205
2719
  }
2206
2720
  async findFirst(key) {
2207
- return this.readOp((tree) => tree.findFirst(key));
2721
+ return this.coord.readOp((t) => t.findFirst(key));
2208
2722
  }
2209
2723
  async findLast(key) {
2210
- return this.readOp((tree) => tree.findLast(key));
2724
+ return this.coord.readOp((t) => t.findLast(key));
2211
2725
  }
2212
2726
  async range(startKey, endKey, options) {
2213
- return this.readOp((tree) => tree.range(startKey, endKey, options));
2727
+ return this.coord.readOp((t) => t.range(startKey, endKey, options));
2214
2728
  }
2215
2729
  async snapshot() {
2216
- return this.readOp((tree) => tree.snapshot());
2730
+ return this.coord.readOp((t) => t.snapshot());
2217
2731
  }
2218
2732
  async size() {
2219
- return this.readOp((tree) => tree.size());
2733
+ return this.coord.readOp((t) => t.size());
2220
2734
  }
2221
2735
  async assertInvariants() {
2222
- await this.readOp((tree) => tree.assertInvariants());
2736
+ await this.coord.readOp((t) => t.assertInvariants());
2223
2737
  }
2224
2738
  async getStats() {
2225
- return this.readOp((tree) => tree.getStats());
2739
+ return this.coord.readOp((t) => t.getStats());
2226
2740
  }
2227
2741
  async peekFirst() {
2228
- return this.readOp((tree) => tree.peekFirst());
2742
+ return this.coord.readOp((t) => t.peekFirst());
2229
2743
  }
2230
2744
  async peekLast() {
2231
- return this.readOp((tree) => tree.peekLast());
2232
- }
2233
- async popLast() {
2234
- return this.runExclusive(async () => {
2235
- return this.appendMutationAndApplyUnlocked((tree) => {
2236
- return tree.peekLast() !== null ? { type: "popLast" } : null;
2237
- });
2238
- });
2745
+ return this.coord.readOp((t) => t.peekLast());
2239
2746
  }
2240
2747
  async peekById(entryId) {
2241
- return this.readOp((tree) => tree.peekById(entryId));
2748
+ return this.coord.readOp((t) => t.peekById(entryId));
2242
2749
  }
2243
2750
  async count(startKey, endKey, options) {
2244
- return this.readOp((tree) => tree.count(startKey, endKey, options));
2751
+ return this.coord.readOp((t) => t.count(startKey, endKey, options));
2245
2752
  }
2246
2753
  async nextHigherKey(key) {
2247
- return this.readOp((tree) => tree.nextHigherKey(key));
2754
+ return this.coord.readOp((t) => t.nextHigherKey(key));
2248
2755
  }
2249
2756
  async nextLowerKey(key) {
2250
- return this.readOp((tree) => tree.nextLowerKey(key));
2757
+ return this.coord.readOp((t) => t.nextLowerKey(key));
2251
2758
  }
2252
2759
  async getPairOrNextLower(key) {
2253
- return this.readOp((tree) => tree.getPairOrNextLower(key));
2760
+ return this.coord.readOp((t) => t.getPairOrNextLower(key));
2761
+ }
2762
+ async entries() {
2763
+ return this.coord.readOp((t) => Array.from(t.entries()));
2764
+ }
2765
+ async entriesReversed() {
2766
+ return this.coord.readOp((t) => Array.from(t.entriesReversed()));
2767
+ }
2768
+ async keys() {
2769
+ return this.coord.readOp((t) => Array.from(t.keys()));
2770
+ }
2771
+ async values() {
2772
+ return this.coord.readOp((t) => Array.from(t.values()));
2773
+ }
2774
+ async forEach(callback) {
2775
+ await this.coord.readOp((t) => {
2776
+ t.forEach(callback);
2777
+ });
2778
+ }
2779
+ async forEachRange(startKey, endKey, callback, options) {
2780
+ await this.coord.readOp((t) => {
2781
+ t.forEachRange(startKey, endKey, callback, options);
2782
+ });
2783
+ }
2784
+ async *[Symbol.asyncIterator]() {
2785
+ const all = await this.entries();
2786
+ for (const entry of all) yield entry;
2787
+ }
2788
+ async clone() {
2789
+ return this.coord.readOp((t) => t.clone());
2790
+ }
2791
+ async toJSON() {
2792
+ return this.coord.readOp((t) => t.toJSON());
2793
+ }
2794
+ static fromJSON(json, compareKeys) {
2795
+ return InMemoryBTree.fromJSON(json, compareKeys);
2254
2796
  }
2255
2797
  };
2256
2798
  // Annotate the CommonJS export names for ESM import in node: