@frostpillar/frostpillar-btree 0.2.6 → 0.2.8

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.
@@ -47,11 +47,12 @@ var createBranchNode = (children, parent) => {
47
47
  const child = children[i];
48
48
  child.parent = branch;
49
49
  child.indexInParent = i;
50
- const target = { key: void 0, sequence: 0 };
50
+ const target = {
51
+ key: void 0,
52
+ sequence: 0
53
+ };
51
54
  if (!writeMinKeyTo(child, target)) {
52
- throw new BTreeInvariantError(
53
- "branch child has no min key"
54
- );
55
+ throw new BTreeInvariantError("branch child has no min key");
55
56
  }
56
57
  keys.push(target);
57
58
  }
@@ -105,7 +106,14 @@ var leafInsertAt = (leaf, logicalIndex, entry) => {
105
106
  leaf.entryOffset -= 1;
106
107
  leaf.entries[phys - 1] = entry;
107
108
  } else {
108
- leaf.entries.splice(phys, 0, entry);
109
+ const len = leaf.entries.length;
110
+ if (phys >= len) {
111
+ leaf.entries.push(entry);
112
+ } else {
113
+ leaf.entries.push(leaf.entries[len - 1]);
114
+ leaf.entries.copyWithin(phys + 1, phys, len);
115
+ leaf.entries[phys] = entry;
116
+ }
109
117
  }
110
118
  };
111
119
  var leafCompact = (leaf) => {
@@ -134,7 +142,11 @@ var branchInsertAt = (branch, logicalIndex, child, key) => {
134
142
  const phys = branch.childOffset + logicalIndex;
135
143
  const count = branch.children.length - branch.childOffset;
136
144
  if (branch.childOffset > 0 && logicalIndex < count >>> 1) {
137
- branch.children.copyWithin(branch.childOffset - 1, branch.childOffset, phys);
145
+ branch.children.copyWithin(
146
+ branch.childOffset - 1,
147
+ branch.childOffset,
148
+ phys
149
+ );
138
150
  branch.keys.copyWithin(branch.childOffset - 1, branch.childOffset, phys);
139
151
  branch.childOffset -= 1;
140
152
  branch.children[phys - 1] = child;
@@ -155,8 +167,16 @@ var branchRemoveAt = (branch, physIndex) => {
155
167
  const logicalIndex = physIndex - branch.childOffset;
156
168
  const count = branch.children.length - branch.childOffset;
157
169
  if (logicalIndex < count - 1 - logicalIndex) {
158
- branch.children.copyWithin(branch.childOffset + 1, branch.childOffset, physIndex);
159
- branch.keys.copyWithin(branch.childOffset + 1, branch.childOffset, physIndex);
170
+ branch.children.copyWithin(
171
+ branch.childOffset + 1,
172
+ branch.childOffset,
173
+ physIndex
174
+ );
175
+ branch.keys.copyWithin(
176
+ branch.childOffset + 1,
177
+ branch.childOffset,
178
+ physIndex
179
+ );
160
180
  branch.childOffset += 1;
161
181
  for (let i = branch.childOffset; i <= physIndex; i += 1) {
162
182
  branch.children[i].indexInParent = i;
@@ -186,17 +206,21 @@ var normalizeDuplicateKeyPolicy = (value) => {
186
206
  return "replace";
187
207
  }
188
208
  if (value !== "allow" && value !== "reject" && value !== "replace") {
189
- throw new BTreeValidationError(
190
- `Invalid duplicateKeys option.`
191
- );
209
+ throw new BTreeValidationError(`Invalid duplicateKeys option.`);
210
+ }
211
+ return value;
212
+ };
213
+ var normalizeDeleteRebalancePolicy = (value) => {
214
+ if (value === void 0) {
215
+ return "standard";
216
+ }
217
+ if (value !== "standard" && value !== "lazy") {
218
+ throw new BTreeValidationError(`Invalid deleteRebalancePolicy option.`);
192
219
  }
193
220
  return value;
194
221
  };
195
- var toPublicEntry = (entry) => ({
196
- entryId: entry.entryId,
197
- key: entry.key,
198
- value: entry.value
199
- });
222
+ var freezeEntry = (entry) => Object.freeze(entry);
223
+ var createEntry = (key, entryId, value) => Object.freeze({ key, entryId, value });
200
224
  var isLeafNode = (node) => {
201
225
  return node.kind === NODE_LEAF;
202
226
  };
@@ -346,7 +370,11 @@ var hasKeyEntry = (state, key) => {
346
370
  var findNextHigherKey = (state, key) => {
347
371
  if (state.entryCount === 0) return null;
348
372
  const compare = state.compareKeys;
349
- let leaf = findLeafForKey(state, key, Number.MAX_SAFE_INTEGER);
373
+ let leaf = findLeafForKey(
374
+ state,
375
+ key,
376
+ Number.MAX_SAFE_INTEGER
377
+ );
350
378
  let idx = upperBoundInLeaf(state, leaf, key, Number.MAX_SAFE_INTEGER);
351
379
  while (leaf !== null) {
352
380
  if (idx < leafEntryCount(leaf)) {
@@ -449,55 +477,57 @@ var countRangeEntries = (state, startKey, endKey, options) => {
449
477
  cursorIndex = 0;
450
478
  continue;
451
479
  }
452
- const lastEntry = leafEntryAt(cursorLeaf, leafCount - 1);
453
- const cmpLast = compare(lastEntry.key, endKey);
454
- if (upperExclusive ? cmpLast < 0 : cmpLast <= 0) {
480
+ const lastKey = leafEntryAt(cursorLeaf, leafCount - 1).key;
481
+ if (isLastEntryInRange(lastKey, endKey, compare, upperExclusive)) {
455
482
  count += leafCount - cursorIndex;
456
483
  cursorLeaf = cursorLeaf.next;
457
484
  cursorIndex = 0;
458
485
  continue;
459
486
  }
460
- const endSeq = upperExclusive ? 0 : Number.MAX_SAFE_INTEGER;
461
- const endBound = upperExclusive ? lowerBoundInLeaf(state, cursorLeaf, endKey, endSeq) : upperBoundInLeaf(state, cursorLeaf, endKey, endSeq);
462
- const limit = endBound < leafCount ? endBound : leafCount;
463
- count += limit - cursorIndex;
487
+ count += findBoundaryEnd(state, cursorLeaf, endKey, upperExclusive, leafCount) - cursorIndex;
464
488
  return count;
465
489
  }
466
490
  return count;
467
491
  };
492
+ var isLastEntryInRange = (lastKey, endKey, compare, upperExclusive) => {
493
+ const cmp = compare(lastKey, endKey);
494
+ return upperExclusive ? cmp < 0 : cmp <= 0;
495
+ };
496
+ var findBoundaryEnd = (state, leaf, endKey, upperExclusive, leafCount) => {
497
+ const endSeq = upperExclusive ? 0 : Number.MAX_SAFE_INTEGER;
498
+ const endBound = upperExclusive ? lowerBoundInLeaf(state, leaf, endKey, endSeq) : upperBoundInLeaf(state, leaf, endKey, endSeq);
499
+ return endBound < leafCount ? endBound : leafCount;
500
+ };
468
501
  var RANGE_PREALLOC_THRESHOLD = 200;
469
- var allocateRangeOutput = (state, cursorLeaf, cursorIndex, compare, upperExclusive, startKey, endKey, options) => {
470
- const firstLeafCount = leafEntryCount(cursorLeaf);
471
- const firstLeafRemainder = firstLeafCount - cursorIndex;
472
- if (firstLeafRemainder >= RANGE_PREALLOC_THRESHOLD && cursorLeaf.next !== null) {
473
- const lastEntry = leafEntryAt(cursorLeaf, firstLeafCount - 1);
474
- const cmpLast = compare(lastEntry.key, endKey);
475
- if (upperExclusive ? cmpLast < 0 : cmpLast <= 0) {
476
- const total = countRangeEntries(state, startKey, endKey, options);
477
- return new Array(total);
502
+ var allocateRangeOutput = (state, cursor, startKey, endKey, options) => {
503
+ const firstLeafCount = leafEntryCount(cursor.leaf);
504
+ const firstLeafRemainder = firstLeafCount - cursor.index;
505
+ if (firstLeafRemainder >= RANGE_PREALLOC_THRESHOLD && cursor.leaf.next !== null) {
506
+ const lastKey = leafEntryAt(cursor.leaf, firstLeafCount - 1).key;
507
+ if (isLastEntryInRange(lastKey, endKey, cursor.compare, cursor.upperExclusive)) {
508
+ return new Array(
509
+ countRangeEntries(state, startKey, endKey, options)
510
+ );
478
511
  }
479
512
  }
480
513
  return [];
481
514
  };
482
- var appendLeafSlice = (leaf, from, to, output, useIndexed, writeIdx) => {
515
+ var appendLeafSlicePublic = (leaf, from, to, output, useIndexed, writeIdx) => {
483
516
  if (useIndexed) {
484
517
  for (let i = from; i < to; i += 1) {
485
- output[writeIdx++] = leafEntryAt(leaf, i);
518
+ output[writeIdx++] = freezeEntry(leafEntryAt(leaf, i));
486
519
  }
487
520
  } else {
488
521
  for (let i = from; i < to; i += 1) {
489
- output.push(leafEntryAt(leaf, i));
522
+ output.push(freezeEntry(leafEntryAt(leaf, i)));
490
523
  }
491
524
  }
492
525
  return writeIdx;
493
526
  };
494
- var rangeQueryEntries = (state, startKey, endKey, options) => {
495
- const cursor = initRangeCursor(state, startKey, endKey, options);
496
- if (cursor === null) return [];
527
+ var collectPublicEntries = (state, cursor, endKey, output) => {
528
+ const { compare, upperExclusive } = cursor;
497
529
  let cursorLeaf = cursor.leaf;
498
530
  let cursorIndex = cursor.index;
499
- const { compare, upperExclusive } = cursor;
500
- const output = allocateRangeOutput(state, cursorLeaf, cursorIndex, compare, upperExclusive, startKey, endKey, options);
501
531
  let writeIdx = 0;
502
532
  const useIndexed = output.length > 0;
503
533
  while (cursorLeaf !== null) {
@@ -507,24 +537,82 @@ var rangeQueryEntries = (state, startKey, endKey, options) => {
507
537
  cursorIndex = 0;
508
538
  continue;
509
539
  }
510
- const lastEntry = leafEntryAt(cursorLeaf, leafCount - 1);
511
- const cmpLast = compare(lastEntry.key, endKey);
512
- if (upperExclusive ? cmpLast < 0 : cmpLast <= 0) {
513
- writeIdx = appendLeafSlice(cursorLeaf, cursorIndex, leafCount, output, useIndexed, writeIdx);
540
+ const lastKey = leafEntryAt(cursorLeaf, leafCount - 1).key;
541
+ if (isLastEntryInRange(lastKey, endKey, compare, upperExclusive)) {
542
+ writeIdx = appendLeafSlicePublic(
543
+ cursorLeaf,
544
+ cursorIndex,
545
+ leafCount,
546
+ output,
547
+ useIndexed,
548
+ writeIdx
549
+ );
514
550
  cursorLeaf = cursorLeaf.next;
515
551
  cursorIndex = 0;
516
552
  continue;
517
553
  }
518
- const endSeq = upperExclusive ? 0 : Number.MAX_SAFE_INTEGER;
519
- const endBound = upperExclusive ? lowerBoundInLeaf(state, cursorLeaf, endKey, endSeq) : upperBoundInLeaf(state, cursorLeaf, endKey, endSeq);
520
- const limit = endBound < leafCount ? endBound : leafCount;
521
- appendLeafSlice(cursorLeaf, cursorIndex, limit, output, useIndexed, writeIdx);
522
- return output;
554
+ const limit = findBoundaryEnd(
555
+ state,
556
+ cursorLeaf,
557
+ endKey,
558
+ upperExclusive,
559
+ leafCount
560
+ );
561
+ appendLeafSlicePublic(
562
+ cursorLeaf,
563
+ cursorIndex,
564
+ limit,
565
+ output,
566
+ useIndexed,
567
+ writeIdx
568
+ );
569
+ return;
523
570
  }
571
+ };
572
+ var rangeQueryPublicEntries = (state, startKey, endKey, options) => {
573
+ const cursor = initRangeCursor(state, startKey, endKey, options);
574
+ if (cursor === null) return [];
575
+ const output = allocateRangeOutput(state, cursor, startKey, endKey, options);
576
+ collectPublicEntries(state, cursor, endKey, output);
524
577
  return output;
525
578
  };
579
+ var forEachRangeEntries = (state, startKey, endKey, callback, options) => {
580
+ const cursor = initRangeCursor(state, startKey, endKey, options);
581
+ if (cursor === null) return;
582
+ let cursorLeaf = cursor.leaf;
583
+ let cursorIndex = cursor.index;
584
+ const { compare, upperExclusive } = cursor;
585
+ while (cursorLeaf !== null) {
586
+ const leafCount = leafEntryCount(cursorLeaf);
587
+ if (cursorIndex >= leafCount) {
588
+ cursorLeaf = cursorLeaf.next;
589
+ cursorIndex = 0;
590
+ continue;
591
+ }
592
+ const lastKey = leafEntryAt(cursorLeaf, leafCount - 1).key;
593
+ if (isLastEntryInRange(lastKey, endKey, compare, upperExclusive)) {
594
+ for (let i = cursorIndex; i < leafCount; i += 1) {
595
+ callback(freezeEntry(leafEntryAt(cursorLeaf, i)));
596
+ }
597
+ cursorLeaf = cursorLeaf.next;
598
+ cursorIndex = 0;
599
+ continue;
600
+ }
601
+ const limit = findBoundaryEnd(
602
+ state,
603
+ cursorLeaf,
604
+ endKey,
605
+ upperExclusive,
606
+ leafCount
607
+ );
608
+ for (let i = cursorIndex; i < limit; i += 1) {
609
+ callback(freezeEntry(leafEntryAt(cursorLeaf, i)));
610
+ }
611
+ return;
612
+ }
613
+ };
526
614
 
527
- // src/btree/rebalance.ts
615
+ // src/btree/rebalance-branch.ts
528
616
  var updateMinKeyInAncestors = (node) => {
529
617
  let current = node;
530
618
  while (current.parent !== null) {
@@ -540,12 +628,9 @@ var requireParent = (node) => {
540
628
  }
541
629
  return node.parent;
542
630
  };
543
- var requireLeafNode = (node) => {
544
- if (!isLeafNode(node)) throw new BTreeInvariantError("expected leaf, got branch");
545
- return node;
546
- };
547
631
  var requireBranchNode = (node) => {
548
- if (isLeafNode(node)) throw new BTreeInvariantError("expected branch, got leaf");
632
+ if (isLeafNode(node))
633
+ throw new BTreeInvariantError("expected branch, got leaf");
549
634
  return node;
550
635
  };
551
636
  var removeChildFromBranch = (branch, childIndex) => {
@@ -554,27 +639,18 @@ var removeChildFromBranch = (branch, childIndex) => {
554
639
  }
555
640
  branchRemoveAt(branch, childIndex);
556
641
  };
557
- var detachLeafFromChain = (state, leaf) => {
558
- if (leaf.prev !== null) {
559
- leaf.prev.next = leaf.next;
560
- } else if (leaf.next !== null) {
561
- state.leftmostLeaf = leaf.next;
562
- }
563
- if (leaf.next !== null) {
564
- leaf.next.prev = leaf.prev;
565
- } else if (leaf.prev !== null) {
566
- state.rightmostLeaf = leaf.prev;
567
- }
568
- leaf.prev = null;
569
- leaf.next = null;
570
- };
571
642
  var borrowFromLeftBranch = (branch, leftSibling, branchIndex) => {
572
643
  const borrowedChild = leftSibling.children.pop();
573
- if (borrowedChild === void 0) throw new BTreeInvariantError("left branch borrow failed");
644
+ if (borrowedChild === void 0)
645
+ throw new BTreeInvariantError("left branch borrow failed");
574
646
  leftSibling.keys.pop();
575
647
  borrowedChild.parent = branch;
576
- const borrowedMinKey = { key: void 0, sequence: 0 };
577
- if (!writeMinKeyTo(borrowedChild, borrowedMinKey)) throw new BTreeInvariantError("borrowed child has no min key");
648
+ const borrowedMinKey = {
649
+ key: void 0,
650
+ sequence: 0
651
+ };
652
+ if (!writeMinKeyTo(borrowedChild, borrowedMinKey))
653
+ throw new BTreeInvariantError("borrowed child has no min key");
578
654
  if (branch.childOffset > 0) {
579
655
  branch.childOffset -= 1;
580
656
  branch.children[branch.childOffset] = borrowedChild;
@@ -583,15 +659,20 @@ var borrowFromLeftBranch = (branch, leftSibling, branchIndex) => {
583
659
  } else {
584
660
  branch.children.unshift(borrowedChild);
585
661
  branch.keys.unshift(borrowedMinKey);
586
- for (let i = 0; i < branch.children.length; i += 1) branch.children[i].indexInParent = i;
662
+ for (let i = 0; i < branch.children.length; i += 1)
663
+ branch.children[i].indexInParent = i;
587
664
  }
588
665
  const parent = requireParent(branch);
589
- parent.keys[branchIndex] = { key: borrowedMinKey.key, sequence: borrowedMinKey.sequence };
666
+ parent.keys[branchIndex] = {
667
+ key: borrowedMinKey.key,
668
+ sequence: borrowedMinKey.sequence
669
+ };
590
670
  updateMinKeyInAncestors(branch);
591
671
  };
592
672
  var borrowFromRightBranch = (branch, rightSibling, branchIndex) => {
593
673
  const shiftIdx = rightSibling.childOffset;
594
- if (shiftIdx >= rightSibling.children.length) throw new BTreeInvariantError("right branch borrow failed");
674
+ if (shiftIdx >= rightSibling.children.length)
675
+ throw new BTreeInvariantError("right branch borrow failed");
595
676
  const borrowedChild = rightSibling.children[shiftIdx];
596
677
  rightSibling.childOffset += 1;
597
678
  if (rightSibling.childOffset >= rightSibling.children.length >>> 1) {
@@ -599,8 +680,12 @@ var borrowFromRightBranch = (branch, rightSibling, branchIndex) => {
599
680
  }
600
681
  branch.children.push(borrowedChild);
601
682
  borrowedChild.parent = branch;
602
- const borrowedMinKey = { key: void 0, sequence: 0 };
603
- if (!writeMinKeyTo(borrowedChild, borrowedMinKey)) throw new BTreeInvariantError("borrowed child has no min key");
683
+ const borrowedMinKey = {
684
+ key: void 0,
685
+ sequence: 0
686
+ };
687
+ if (!writeMinKeyTo(borrowedChild, borrowedMinKey))
688
+ throw new BTreeInvariantError("borrowed child has no min key");
604
689
  branch.keys.push(borrowedMinKey);
605
690
  borrowedChild.indexInParent = branch.children.length - 1;
606
691
  const parent = requireParent(branch);
@@ -653,7 +738,10 @@ var rebalanceAfterBranchRemoval = (state, branch) => {
653
738
  const parent = branch.parent;
654
739
  if (parent === null) throw new BTreeInvariantError("branch has no parent");
655
740
  const branchIndex = branch.indexInParent;
656
- const { left: leftSibling, right: rightSibling } = findBranchSiblings(parent, branchIndex);
741
+ const { left: leftSibling, right: rightSibling } = findBranchSiblings(
742
+ parent,
743
+ branchIndex
744
+ );
657
745
  if (rightSibling !== null && branchChildCount(rightSibling) > state.minBranchChildren) {
658
746
  borrowFromRightBranch(branch, rightSibling, branchIndex);
659
747
  return;
@@ -672,44 +760,70 @@ var rebalanceAfterBranchRemoval = (state, branch) => {
672
760
  }
673
761
  throw new BTreeInvariantError("no branch siblings to rebalance");
674
762
  };
763
+
764
+ // src/btree/rebalance.ts
765
+ var requireLeafNode = (node) => {
766
+ if (!isLeafNode(node))
767
+ throw new BTreeInvariantError("expected leaf, got branch");
768
+ return node;
769
+ };
770
+ var detachLeafFromChain = (state, leaf) => {
771
+ if (leaf.prev !== null) {
772
+ leaf.prev.next = leaf.next;
773
+ } else if (leaf.next !== null) {
774
+ state.leftmostLeaf = leaf.next;
775
+ }
776
+ if (leaf.next !== null) {
777
+ leaf.next.prev = leaf.prev;
778
+ } else if (leaf.prev !== null) {
779
+ state.rightmostLeaf = leaf.prev;
780
+ }
781
+ leaf.prev = null;
782
+ leaf.next = null;
783
+ };
675
784
  var mergeLeafEntries = (target, source) => {
676
785
  if (target.entryOffset > 0) {
677
786
  target.entries.copyWithin(0, target.entryOffset);
678
787
  target.entries.length = target.entries.length - target.entryOffset;
679
788
  target.entryOffset = 0;
680
789
  }
681
- const src = source.entries;
682
- for (let i = source.entryOffset; i < src.length; i += 1) target.entries.push(src[i]);
790
+ target.entries.push(...source.entries.slice(source.entryOffset));
683
791
  };
684
- var rebalanceAfterLeafRemoval = (state, leaf) => {
685
- if (leaf === state.root) {
686
- if (state.entryCount === 0) {
687
- state.leftmostLeaf = leaf;
688
- state.rightmostLeaf = leaf;
689
- }
690
- return;
792
+ var applyLazyThreshold = (min) => Math.max(1, Math.ceil(min / 4));
793
+ var leafRebalanceThreshold = (state) => {
794
+ if (state.deleteRebalancePolicy === "lazy") {
795
+ return applyLazyThreshold(state.minLeafEntries);
691
796
  }
692
- if (leafEntryCount(leaf) >= state.minLeafEntries) return;
693
- const parent = leaf.parent;
694
- if (parent === null) throw new BTreeInvariantError("Leaf node has no parent during rebalance.");
695
- const leafIndex = leaf.indexInParent;
696
- const leftSibling = leafIndex > parent.childOffset ? requireLeafNode(parent.children[leafIndex - 1]) : null;
697
- const rightSibling = leafIndex + 1 < parent.children.length ? requireLeafNode(parent.children[leafIndex + 1]) : null;
797
+ return state.minLeafEntries;
798
+ };
799
+ var findLeafSiblings = (parent, leafIndex) => {
800
+ const left = leafIndex > parent.childOffset ? requireLeafNode(parent.children[leafIndex - 1]) : null;
801
+ const right = leafIndex + 1 < parent.children.length ? requireLeafNode(parent.children[leafIndex + 1]) : null;
802
+ return { left, right };
803
+ };
804
+ var tryBorrowFromLeafSibling = (state, leaf, parent, leafIndex, leftSibling, rightSibling) => {
698
805
  if (rightSibling !== null && leafEntryCount(rightSibling) > state.minLeafEntries) {
699
806
  const borrowed = leafShiftEntry(rightSibling);
700
- if (borrowed === void 0) throw new BTreeInvariantError("right leaf borrow failed");
807
+ if (borrowed === void 0)
808
+ throw new BTreeInvariantError("right leaf borrow failed");
809
+ const leafWasEmpty = leafEntryCount(leaf) === 0;
701
810
  leaf.entries.push(borrowed);
702
811
  writeMinKeyTo(rightSibling, parent.keys[leafIndex + 1]);
703
- return;
812
+ if (leafWasEmpty) updateMinKeyInAncestors(leaf);
813
+ return true;
704
814
  }
705
815
  if (leftSibling !== null && leafEntryCount(leftSibling) > state.minLeafEntries) {
706
816
  const borrowed = leftSibling.entries.pop();
707
- if (borrowed === void 0) throw new BTreeInvariantError("left leaf borrow failed");
817
+ if (borrowed === void 0)
818
+ throw new BTreeInvariantError("left leaf borrow failed");
708
819
  leafUnshiftEntry(leaf, borrowed);
709
820
  parent.keys[leafIndex] = { key: borrowed.key, sequence: borrowed.entryId };
710
821
  updateMinKeyInAncestors(leaf);
711
- return;
822
+ return true;
712
823
  }
824
+ return false;
825
+ };
826
+ var mergeLeafWithSibling = (state, leaf, parent, leafIndex, leftSibling, rightSibling) => {
713
827
  if (leftSibling !== null) {
714
828
  mergeLeafEntries(leftSibling, leaf);
715
829
  detachLeafFromChain(state, leaf);
@@ -718,14 +832,34 @@ var rebalanceAfterLeafRemoval = (state, leaf) => {
718
832
  return;
719
833
  }
720
834
  if (rightSibling !== null) {
835
+ const leafWasEmpty = leafEntryCount(leaf) === 0;
721
836
  mergeLeafEntries(leaf, rightSibling);
722
837
  detachLeafFromChain(state, rightSibling);
838
+ if (leafWasEmpty) updateMinKeyInAncestors(leaf);
723
839
  removeChildFromBranch(parent, leafIndex + 1);
724
840
  rebalanceAfterBranchRemoval(state, parent);
725
841
  return;
726
842
  }
727
843
  throw new BTreeInvariantError("no leaf siblings to rebalance");
728
844
  };
845
+ var rebalanceAfterLeafRemoval = (state, leaf) => {
846
+ if (leaf === state.root) {
847
+ if (state.entryCount === 0) {
848
+ state.leftmostLeaf = leaf;
849
+ state.rightmostLeaf = leaf;
850
+ }
851
+ return;
852
+ }
853
+ if (leafEntryCount(leaf) >= leafRebalanceThreshold(state)) return;
854
+ const parent = leaf.parent;
855
+ if (parent === null)
856
+ throw new BTreeInvariantError("Leaf node has no parent during rebalance.");
857
+ const leafIndex = leaf.indexInParent;
858
+ const { left, right } = findLeafSiblings(parent, leafIndex);
859
+ if (tryBorrowFromLeafSibling(state, leaf, parent, leafIndex, left, right))
860
+ return;
861
+ mergeLeafWithSibling(state, leaf, parent, leafIndex, left, right);
862
+ };
729
863
 
730
864
  // src/btree/deleteRange.ts
731
865
  var navigateToStart = (state, startKey, lowerExclusive) => {
@@ -738,16 +872,16 @@ var navigateToStart = (state, startKey, lowerExclusive) => {
738
872
  }
739
873
  return { leaf, idx };
740
874
  };
741
- var findRemoveEnd = (state, leaf, idx, endKey, upperExclusive) => {
875
+ var findRemoveEnd = (state, leaf, endKey, upperExclusive) => {
742
876
  const count = leafEntryCount(leaf);
743
- let removeEnd = idx;
744
- while (removeEnd < count) {
745
- const e = leafEntryAt(leaf, removeEnd);
746
- const cmpEnd = state.compareKeys(e.key, endKey);
747
- if (upperExclusive ? cmpEnd >= 0 : cmpEnd > 0) break;
748
- removeEnd += 1;
877
+ const lastEntry = leafEntryAt(leaf, count - 1);
878
+ const cmpLast = state.compareKeys(lastEntry.key, endKey);
879
+ if (upperExclusive ? cmpLast < 0 : cmpLast <= 0) {
880
+ return count;
749
881
  }
750
- return removeEnd;
882
+ const endSeq = upperExclusive ? 0 : Number.MAX_SAFE_INTEGER;
883
+ const endBound = upperExclusive ? lowerBoundInLeaf(state, leaf, endKey, endSeq) : upperBoundInLeaf(state, leaf, endKey, endSeq);
884
+ return endBound < count ? endBound : count;
751
885
  };
752
886
  var spliceLeafAndRebalance = (state, leaf, idx, removeCount) => {
753
887
  if (state.entryKeys !== null) {
@@ -764,15 +898,14 @@ var spliceLeafAndRebalance = (state, leaf, idx, removeCount) => {
764
898
  updateMinKeyInAncestors(leaf);
765
899
  }
766
900
  const countAfterSplice = leafEntryCount(leaf);
901
+ const rebalThreshold = leafRebalanceThreshold(state);
767
902
  let safetyGuard = state.minLeafEntries + 4;
768
- while (safetyGuard > 0 && leaf !== state.root && leafEntryCount(leaf) < state.minLeafEntries) {
903
+ while (safetyGuard > 0 && leaf !== state.root && leafEntryCount(leaf) < rebalThreshold) {
769
904
  rebalanceAfterLeafRemoval(state, leaf);
770
- if (leaf.parent !== null && leaf.parent.children[leaf.indexInParent] !== leaf) break;
905
+ if (leaf.parent !== null && leaf.parent.children[leaf.indexInParent] !== leaf)
906
+ break;
771
907
  safetyGuard -= 1;
772
908
  }
773
- if (leafEmptied && leafEntryCount(leaf) > 0 && leaf.parent !== null && leaf.parent.children[leaf.indexInParent] === leaf) {
774
- updateMinKeyInAncestors(leaf);
775
- }
776
909
  return countAfterSplice;
777
910
  };
778
911
  var isLeafStillValid = (state, leaf) => leaf.parent === null ? leaf === state.root : leaf.parent.children[leaf.indexInParent] === leaf;
@@ -795,10 +928,15 @@ var deleteRangeEntries = (state, startKey, endKey, options) => {
795
928
  }
796
929
  if (idx >= leafEntryCount(leaf)) break;
797
930
  const count = leafEntryCount(leaf);
798
- const removeEnd = findRemoveEnd(state, leaf, idx, endKey, upperExclusive);
931
+ const removeEnd = findRemoveEnd(state, leaf, endKey, upperExclusive);
799
932
  const removeCount = removeEnd - idx;
800
933
  if (removeCount === 0) break;
801
- const countAfterSplice = spliceLeafAndRebalance(state, leaf, idx, removeCount);
934
+ const countAfterSplice = spliceLeafAndRebalance(
935
+ state,
936
+ leaf,
937
+ idx,
938
+ removeCount
939
+ );
802
940
  deleted += removeCount;
803
941
  if (removeEnd < count) break;
804
942
  if (!isLeafStillValid(state, leaf)) {
@@ -844,25 +982,42 @@ var computeNextAutoScaleThreshold = (entryCount) => {
844
982
  }
845
983
  return Number.MAX_SAFE_INTEGER;
846
984
  };
847
- var createInitialState = (config) => {
848
- if (typeof config.compareKeys !== "function") {
849
- throw new BTreeValidationError("compareKeys must be a function.");
850
- }
851
- const autoScale = config.autoScale === true;
985
+ var resolveInitialCapacity = (config, autoScale) => {
852
986
  if (autoScale && (config.maxLeafEntries !== void 0 || config.maxBranchChildren !== void 0)) {
853
- throw new BTreeValidationError("autoScale conflicts with explicit capacity.");
987
+ throw new BTreeValidationError(
988
+ "autoScale conflicts with explicit capacity."
989
+ );
854
990
  }
855
- let maxLeafEntries;
856
- let maxBranchChildren;
857
991
  if (autoScale) {
858
992
  const tier = computeAutoScaleTier(0);
859
- maxLeafEntries = tier.maxLeaf;
860
- maxBranchChildren = tier.maxBranch;
861
- } else {
862
- maxLeafEntries = normalizeNodeCapacity(config.maxLeafEntries, "maxLeafEntries", DEFAULT_MAX_LEAF_ENTRIES);
863
- maxBranchChildren = normalizeNodeCapacity(config.maxBranchChildren, "maxBranchChildren", DEFAULT_MAX_BRANCH_CHILDREN);
993
+ return { maxLeafEntries: tier.maxLeaf, maxBranchChildren: tier.maxBranch };
864
994
  }
995
+ return {
996
+ maxLeafEntries: normalizeNodeCapacity(
997
+ config.maxLeafEntries,
998
+ "maxLeafEntries",
999
+ DEFAULT_MAX_LEAF_ENTRIES
1000
+ ),
1001
+ maxBranchChildren: normalizeNodeCapacity(
1002
+ config.maxBranchChildren,
1003
+ "maxBranchChildren",
1004
+ DEFAULT_MAX_BRANCH_CHILDREN
1005
+ )
1006
+ };
1007
+ };
1008
+ var createInitialState = (config) => {
1009
+ if (typeof config.compareKeys !== "function") {
1010
+ throw new BTreeValidationError("compareKeys must be a function.");
1011
+ }
1012
+ const autoScale = config.autoScale === true;
1013
+ const { maxLeafEntries, maxBranchChildren } = resolveInitialCapacity(
1014
+ config,
1015
+ autoScale
1016
+ );
865
1017
  const duplicateKeys = normalizeDuplicateKeyPolicy(config.duplicateKeys);
1018
+ const deleteRebalancePolicy = normalizeDeleteRebalancePolicy(
1019
+ config.deleteRebalancePolicy
1020
+ );
866
1021
  const emptyLeaf = createLeafNode([], null);
867
1022
  return {
868
1023
  compareKeys: config.compareKeys,
@@ -878,6 +1033,7 @@ var createInitialState = (config) => {
878
1033
  nextSequence: 0,
879
1034
  entryKeys: config.enableEntryIdLookup === true ? /* @__PURE__ */ new Map() : null,
880
1035
  autoScale,
1036
+ deleteRebalancePolicy,
881
1037
  _nextAutoScaleThreshold: autoScale ? computeNextAutoScaleThreshold(0) : Number.MAX_SAFE_INTEGER,
882
1038
  _cursor: { leaf: emptyLeaf, index: 0 }
883
1039
  };
@@ -893,7 +1049,9 @@ var maybeAutoScale = (state) => {
893
1049
  state.maxBranchChildren = maxBranch;
894
1050
  state.minBranchChildren = minOccupancy(maxBranch);
895
1051
  }
896
- state._nextAutoScaleThreshold = computeNextAutoScaleThreshold(state.entryCount);
1052
+ state._nextAutoScaleThreshold = computeNextAutoScaleThreshold(
1053
+ state.entryCount
1054
+ );
897
1055
  };
898
1056
  var applyAutoScaleCapacitySnapshot = (state, maxLeafEntries, maxBranchChildren) => {
899
1057
  if (!state.autoScale) {
@@ -920,6 +1078,14 @@ var applyAutoScaleCapacitySnapshot = (state, maxLeafEntries, maxBranchChildren)
920
1078
  state.minLeafEntries = minOccupancy(normalizedLeaf);
921
1079
  state.minBranchChildren = minOccupancy(normalizedBranch);
922
1080
  };
1081
+ var resetAutoScaleToTier0 = (state) => {
1082
+ const tier = computeAutoScaleTier(0);
1083
+ state.maxLeafEntries = tier.maxLeaf;
1084
+ state.maxBranchChildren = tier.maxBranch;
1085
+ state.minLeafEntries = minOccupancy(tier.maxLeaf);
1086
+ state.minBranchChildren = minOccupancy(tier.maxBranch);
1087
+ state._nextAutoScaleThreshold = computeNextAutoScaleThreshold(0);
1088
+ };
923
1089
 
924
1090
  // src/btree/bulkLoad.ts
925
1091
  var computeChunkBoundaries = (total, max, min) => {
@@ -940,7 +1106,11 @@ var computeChunkBoundaries = (total, max, min) => {
940
1106
  return boundaries;
941
1107
  };
942
1108
  var buildLeaves = (state, entries, ids, baseSequence) => {
943
- const boundaries = computeChunkBoundaries(entries.length, state.maxLeafEntries, state.minLeafEntries);
1109
+ const boundaries = computeChunkBoundaries(
1110
+ entries.length,
1111
+ state.maxLeafEntries,
1112
+ state.minLeafEntries
1113
+ );
944
1114
  const leaves = new Array(boundaries.length);
945
1115
  let chunkStart = 0;
946
1116
  for (let c = 0; c < boundaries.length; c += 1) {
@@ -948,7 +1118,11 @@ var buildLeaves = (state, entries, ids, baseSequence) => {
948
1118
  const chunk = new Array(chunkEnd - chunkStart);
949
1119
  for (let i = chunkStart; i < chunkEnd; i += 1) {
950
1120
  const seq = baseSequence + i;
951
- chunk[i - chunkStart] = { key: entries[i].key, entryId: seq, value: entries[i].value };
1121
+ chunk[i - chunkStart] = createEntry(
1122
+ entries[i].key,
1123
+ seq,
1124
+ entries[i].value
1125
+ );
952
1126
  ids[i] = seq;
953
1127
  if (state.entryKeys !== null) {
954
1128
  state.entryKeys.set(seq, entries[i].key);
@@ -982,11 +1156,18 @@ var bulkLoadEntries = (state, entries) => {
982
1156
  } else {
983
1157
  let currentLevel = leaves;
984
1158
  while (currentLevel.length > 1) {
985
- const bounds = computeChunkBoundaries(currentLevel.length, state.maxBranchChildren, state.minBranchChildren);
1159
+ const bounds = computeChunkBoundaries(
1160
+ currentLevel.length,
1161
+ state.maxBranchChildren,
1162
+ state.minBranchChildren
1163
+ );
986
1164
  const nextLevel = new Array(bounds.length);
987
1165
  let start = 0;
988
1166
  for (let b = 0; b < bounds.length; b += 1) {
989
- nextLevel[b] = createBranchNode(currentLevel.slice(start, bounds[b]), null);
1167
+ nextLevel[b] = createBranchNode(
1168
+ currentLevel.slice(start, bounds[b]),
1169
+ null
1170
+ );
990
1171
  start = bounds[b];
991
1172
  }
992
1173
  currentLevel = nextLevel;
@@ -997,9 +1178,12 @@ var bulkLoadEntries = (state, entries) => {
997
1178
  return ids;
998
1179
  };
999
1180
 
1000
- // src/btree/mutations.ts
1181
+ // src/btree/split.ts
1001
1182
  var insertChildAfter = (state, parent, existingChild, childToInsert) => {
1002
- const newChildMinKey = { key: void 0, sequence: 0 };
1183
+ const newChildMinKey = {
1184
+ key: void 0,
1185
+ sequence: 0
1186
+ };
1003
1187
  if (!writeMinKeyTo(childToInsert, newChildMinKey)) {
1004
1188
  throw new BTreeInvariantError("inserted child has no min key");
1005
1189
  }
@@ -1037,7 +1221,10 @@ var splitLeaf = (state, leaf) => {
1037
1221
  var splitBranch = (state, branch) => {
1038
1222
  branchCompact(branch);
1039
1223
  const splitAt = Math.ceil(branch.children.length / 2);
1040
- const sibling = createBranchNode(branch.children.splice(splitAt), branch.parent);
1224
+ const sibling = createBranchNode(
1225
+ branch.children.splice(splitAt),
1226
+ branch.parent
1227
+ );
1041
1228
  branch.keys.splice(splitAt);
1042
1229
  if (branch.parent === null) {
1043
1230
  state.root = createBranchNode([branch, sibling], null);
@@ -1045,42 +1232,125 @@ var splitBranch = (state, branch) => {
1045
1232
  }
1046
1233
  insertChildAfter(state, branch.parent, branch, sibling);
1047
1234
  };
1235
+
1236
+ // src/btree/entry-lookup.ts
1237
+ var findLeafEntryBySequence = (state, userKey, sequence) => {
1238
+ const targetLeaf = findLeafForKey(state, userKey, sequence);
1239
+ const index = lowerBoundInLeaf(state, targetLeaf, userKey, sequence);
1240
+ if (index >= leafEntryCount(targetLeaf)) return null;
1241
+ const entry = leafEntryAt(targetLeaf, index);
1242
+ if (entry.entryId !== sequence) return null;
1243
+ return { leaf: targetLeaf, index };
1244
+ };
1245
+ var peekEntryById = (state, entryId) => {
1246
+ if (state.entryKeys === null) {
1247
+ throw new BTreeInvariantError(
1248
+ "entryKeys lookup map is not enabled on this tree."
1249
+ );
1250
+ }
1251
+ const userKey = state.entryKeys.get(entryId);
1252
+ if (userKey === void 0) return null;
1253
+ const found = findLeafEntryBySequence(state, userKey, entryId);
1254
+ if (found === null) return null;
1255
+ const entry = leafEntryAt(found.leaf, found.index);
1256
+ return entry;
1257
+ };
1258
+ var updateEntryById = (state, entryId, newValue) => {
1259
+ if (state.entryKeys === null) {
1260
+ throw new BTreeInvariantError(
1261
+ "entryKeys lookup map is not enabled on this tree."
1262
+ );
1263
+ }
1264
+ const userKey = state.entryKeys.get(entryId);
1265
+ if (userKey === void 0) return null;
1266
+ const found = findLeafEntryBySequence(state, userKey, entryId);
1267
+ if (found === null) return null;
1268
+ const entry = leafEntryAt(found.leaf, found.index);
1269
+ const updated = createEntry(entry.key, entry.entryId, newValue);
1270
+ found.leaf.entries[found.leaf.entryOffset + found.index] = updated;
1271
+ return updated;
1272
+ };
1273
+ var removeEntryById = (state, entryId) => {
1274
+ if (state.entryKeys === null) {
1275
+ throw new BTreeInvariantError(
1276
+ "entryKeys lookup map is not enabled on this tree."
1277
+ );
1278
+ }
1279
+ const userKey = state.entryKeys.get(entryId);
1280
+ if (userKey === void 0) return null;
1281
+ const found = findLeafEntryBySequence(state, userKey, entryId);
1282
+ if (found === null) return null;
1283
+ const entry = leafEntryAt(found.leaf, found.index);
1284
+ leafRemoveAt(found.leaf, found.index);
1285
+ state.entryCount -= 1;
1286
+ state.entryKeys.delete(entryId);
1287
+ if (found.index === 0 && leafEntryCount(found.leaf) > 0 && found.leaf.parent !== null) {
1288
+ updateMinKeyInAncestors(found.leaf);
1289
+ }
1290
+ if (found.leaf !== state.root && leafEntryCount(found.leaf) < state.minLeafEntries) {
1291
+ rebalanceAfterLeafRemoval(state, found.leaf);
1292
+ }
1293
+ return entry;
1294
+ };
1295
+
1296
+ // src/btree/mutations.ts
1297
+ var findDuplicateEntry = (state, targetLeaf, key, insertAt) => {
1298
+ if (state.duplicateKeys === "allow") return null;
1299
+ if (insertAt > 0) {
1300
+ const candidate = leafEntryAt(targetLeaf, insertAt - 1);
1301
+ if (state.compareKeys(candidate.key, key) === 0) {
1302
+ return {
1303
+ leaf: targetLeaf,
1304
+ physIndex: targetLeaf.entryOffset + insertAt - 1,
1305
+ entry: candidate
1306
+ };
1307
+ }
1308
+ } else if (targetLeaf.prev !== null && leafEntryCount(targetLeaf.prev) > 0) {
1309
+ const prevLeaf = targetLeaf.prev;
1310
+ const prevCount = leafEntryCount(prevLeaf);
1311
+ const candidate = leafEntryAt(prevLeaf, prevCount - 1);
1312
+ if (state.compareKeys(candidate.key, key) === 0) {
1313
+ return {
1314
+ leaf: prevLeaf,
1315
+ physIndex: prevLeaf.entryOffset + prevCount - 1,
1316
+ entry: candidate
1317
+ };
1318
+ }
1319
+ }
1320
+ return null;
1321
+ };
1048
1322
  var putEntryIntoLeaf = (state, targetLeaf, key, value) => {
1049
1323
  const sequence = state.nextSequence;
1050
1324
  const insertAt = upperBoundInLeaf(state, targetLeaf, key, sequence);
1051
- if (state.duplicateKeys !== "allow") {
1052
- let existingEntry = null;
1053
- if (insertAt > 0) {
1054
- const candidate = leafEntryAt(targetLeaf, insertAt - 1);
1055
- if (state.compareKeys(candidate.key, key) === 0) {
1056
- existingEntry = candidate;
1057
- }
1058
- } else if (targetLeaf.prev !== null && leafEntryCount(targetLeaf.prev) > 0) {
1059
- const prevLeaf = targetLeaf.prev;
1060
- const candidate = leafEntryAt(prevLeaf, leafEntryCount(prevLeaf) - 1);
1061
- if (state.compareKeys(candidate.key, key) === 0) {
1062
- existingEntry = candidate;
1063
- }
1064
- }
1065
- if (existingEntry !== null) {
1066
- if (state.duplicateKeys === "reject") {
1067
- throw new BTreeValidationError("Duplicate key rejected.");
1068
- }
1069
- existingEntry.value = value;
1070
- return existingEntry.entryId;
1071
- }
1325
+ const dup = findDuplicateEntry(state, targetLeaf, key, insertAt);
1326
+ if (dup !== null) {
1327
+ if (state.duplicateKeys === "reject") {
1328
+ throw new BTreeValidationError("Duplicate key rejected.");
1329
+ }
1330
+ dup.leaf.entries[dup.physIndex] = createEntry(
1331
+ dup.entry.key,
1332
+ dup.entry.entryId,
1333
+ value
1334
+ );
1335
+ return dup.entry.entryId;
1072
1336
  }
1073
1337
  if (state.nextSequence >= Number.MAX_SAFE_INTEGER) {
1074
1338
  throw new BTreeValidationError("Sequence overflow.");
1075
1339
  }
1076
1340
  state.nextSequence += 1;
1077
- leafInsertAt(targetLeaf, insertAt, { key, entryId: sequence, value });
1341
+ leafInsertAt(
1342
+ targetLeaf,
1343
+ insertAt,
1344
+ createEntry(key, sequence, value)
1345
+ );
1078
1346
  state.entryCount += 1;
1079
1347
  if (state.entryKeys !== null) {
1080
1348
  state.entryKeys.set(sequence, key);
1081
1349
  }
1082
- if (insertAt === 0 && targetLeaf.parent !== null) updateMinKeyInAncestors(targetLeaf);
1083
- if (leafEntryCount(targetLeaf) > state.maxLeafEntries) splitLeaf(state, targetLeaf);
1350
+ if (insertAt === 0 && targetLeaf.parent !== null)
1351
+ updateMinKeyInAncestors(targetLeaf);
1352
+ if (leafEntryCount(targetLeaf) > state.maxLeafEntries)
1353
+ splitLeaf(state, targetLeaf);
1084
1354
  maybeAutoScale(state);
1085
1355
  return sequence;
1086
1356
  };
@@ -1091,7 +1361,8 @@ var putEntry = (state, key, value) => {
1091
1361
  var popFirstEntry = (state) => {
1092
1362
  if (state.entryCount === 0) return null;
1093
1363
  const firstEntry = leafShiftEntry(state.leftmostLeaf);
1094
- if (firstEntry === void 0) throw new BTreeInvariantError("leftmost leaf empty but count > 0");
1364
+ if (firstEntry === void 0)
1365
+ throw new BTreeInvariantError("leftmost leaf empty but count > 0");
1095
1366
  state.entryCount -= 1;
1096
1367
  if (state.entryKeys !== null) {
1097
1368
  state.entryKeys.delete(firstEntry.entryId);
@@ -1107,7 +1378,8 @@ var popFirstEntry = (state) => {
1107
1378
  var popLastEntry = (state) => {
1108
1379
  if (state.entryCount === 0) return null;
1109
1380
  const lastEntry = leafPopEntry(state.rightmostLeaf);
1110
- if (lastEntry === void 0) throw new BTreeInvariantError("rightmost leaf empty but count > 0");
1381
+ if (lastEntry === void 0)
1382
+ throw new BTreeInvariantError("rightmost leaf empty but count > 0");
1111
1383
  state.entryCount -= 1;
1112
1384
  if (state.entryKeys !== null) {
1113
1385
  state.entryKeys.delete(lastEntry.entryId);
@@ -1128,70 +1400,22 @@ var removeFirstMatchingEntry = (state, key) => {
1128
1400
  if (state.entryKeys !== null) {
1129
1401
  state.entryKeys.delete(targetEntry.entryId);
1130
1402
  }
1131
- if (removeAt === 0 && leafEntryCount(targetLeaf) > 0 && targetLeaf.parent !== null) updateMinKeyInAncestors(targetLeaf);
1403
+ if (removeAt === 0 && leafEntryCount(targetLeaf) > 0 && targetLeaf.parent !== null)
1404
+ updateMinKeyInAncestors(targetLeaf);
1132
1405
  if (targetLeaf !== state.root && leafEntryCount(targetLeaf) < state.minLeafEntries) {
1133
1406
  rebalanceAfterLeafRemoval(state, targetLeaf);
1134
1407
  }
1135
1408
  return targetEntry;
1136
1409
  };
1137
- var findLeafEntryBySequence = (state, userKey, sequence) => {
1138
- const targetLeaf = findLeafForKey(state, userKey, sequence);
1139
- const index = lowerBoundInLeaf(state, targetLeaf, userKey, sequence);
1140
- if (index >= leafEntryCount(targetLeaf)) return null;
1141
- const entry = leafEntryAt(targetLeaf, index);
1142
- if (entry.entryId !== sequence) return null;
1143
- return { leaf: targetLeaf, index };
1144
- };
1145
- var removeEntryById = (state, entryId) => {
1146
- if (state.entryKeys === null) {
1147
- throw new BTreeInvariantError("entryKeys lookup map is not enabled on this tree.");
1148
- }
1149
- const userKey = state.entryKeys.get(entryId);
1150
- if (userKey === void 0) return null;
1151
- const found = findLeafEntryBySequence(state, userKey, entryId);
1152
- if (found === null) return null;
1153
- const entry = leafEntryAt(found.leaf, found.index);
1154
- leafRemoveAt(found.leaf, found.index);
1155
- state.entryCount -= 1;
1156
- state.entryKeys.delete(entryId);
1157
- if (found.index === 0 && leafEntryCount(found.leaf) > 0 && found.leaf.parent !== null) {
1158
- updateMinKeyInAncestors(found.leaf);
1159
- }
1160
- if (found.leaf !== state.root && leafEntryCount(found.leaf) < state.minLeafEntries) {
1161
- rebalanceAfterLeafRemoval(state, found.leaf);
1162
- }
1163
- return entry;
1164
- };
1165
- var peekEntryById = (state, entryId) => {
1166
- if (state.entryKeys === null) {
1167
- throw new BTreeInvariantError("entryKeys lookup map is not enabled on this tree.");
1168
- }
1169
- const userKey = state.entryKeys.get(entryId);
1170
- if (userKey === void 0) return null;
1171
- const found = findLeafEntryBySequence(state, userKey, entryId);
1172
- if (found === null) return null;
1173
- const entry = leafEntryAt(found.leaf, found.index);
1174
- return entry;
1175
- };
1176
- var updateEntryById = (state, entryId, newValue) => {
1177
- if (state.entryKeys === null) {
1178
- throw new BTreeInvariantError("entryKeys lookup map is not enabled on this tree.");
1179
- }
1180
- const userKey = state.entryKeys.get(entryId);
1181
- if (userKey === void 0) return null;
1182
- const found = findLeafEntryBySequence(state, userKey, entryId);
1183
- if (found === null) return null;
1184
- const entry = leafEntryAt(found.leaf, found.index);
1185
- entry.value = newValue;
1186
- return entry;
1187
- };
1188
1410
  var putManyEntries = (state, entries) => {
1189
1411
  if (entries.length === 0) return [];
1190
1412
  const strictlyAscending = state.duplicateKeys !== "allow";
1191
1413
  for (let i = 1; i < entries.length; i += 1) {
1192
1414
  const cmp = state.compareKeys(entries[i - 1].key, entries[i].key);
1193
1415
  if (cmp > 0) {
1194
- throw new BTreeValidationError("putMany: entries not in ascending order.");
1416
+ throw new BTreeValidationError(
1417
+ "putMany: entries not in ascending order."
1418
+ );
1195
1419
  }
1196
1420
  if (strictlyAscending && cmp === 0) {
1197
1421
  throw new BTreeValidationError(
@@ -1204,7 +1428,12 @@ var putManyEntries = (state, entries) => {
1204
1428
  let hintLeaf = findLeafForKey(state, entries[0].key, state.nextSequence);
1205
1429
  for (let i = 0; i < entries.length; i += 1) {
1206
1430
  const entry = entries[i];
1207
- const targetLeaf = findLeafFromHint(state, hintLeaf, entry.key, state.nextSequence);
1431
+ const targetLeaf = findLeafFromHint(
1432
+ state,
1433
+ hintLeaf,
1434
+ entry.key,
1435
+ state.nextSequence
1436
+ );
1208
1437
  ids[i] = putEntryIntoLeaf(state, targetLeaf, entry.key, entry.value);
1209
1438
  hintLeaf = targetLeaf;
1210
1439
  }
@@ -1219,7 +1448,8 @@ var buildConfigFromState = (state) => {
1219
1448
  compareKeys: state.compareKeys,
1220
1449
  duplicateKeys: state.duplicateKeys,
1221
1450
  enableEntryIdLookup: state.entryKeys !== null,
1222
- autoScale: state.autoScale
1451
+ autoScale: state.autoScale,
1452
+ deleteRebalancePolicy: state.deleteRebalancePolicy
1223
1453
  };
1224
1454
  if (!state.autoScale) {
1225
1455
  config.maxLeafEntries = state.maxLeafEntries;
@@ -1239,17 +1469,17 @@ var serializeToJSON = (state) => {
1239
1469
  }
1240
1470
  leaf = leaf.next;
1241
1471
  }
1242
- return {
1243
- version: 1,
1244
- config: {
1245
- maxLeafEntries: state.maxLeafEntries,
1246
- maxBranchChildren: state.maxBranchChildren,
1247
- duplicateKeys: state.duplicateKeys,
1248
- enableEntryIdLookup: state.entryKeys !== null,
1249
- autoScale: state.autoScale
1250
- },
1251
- entries
1472
+ const config = {
1473
+ maxLeafEntries: state.maxLeafEntries,
1474
+ maxBranchChildren: state.maxBranchChildren,
1475
+ duplicateKeys: state.duplicateKeys,
1476
+ enableEntryIdLookup: state.entryKeys !== null,
1477
+ autoScale: state.autoScale
1252
1478
  };
1479
+ if (state.deleteRebalancePolicy !== "standard") {
1480
+ config.deleteRebalancePolicy = state.deleteRebalancePolicy;
1481
+ }
1482
+ return { version: 1, config, entries };
1253
1483
  };
1254
1484
  var MAX_SERIALIZED_ENTRIES = 1e6;
1255
1485
  var validateStructure = (json) => {
@@ -1312,13 +1542,28 @@ var validateBTreeJSON = (json) => {
1312
1542
  validateStructure(json);
1313
1543
  validateConfig(json.config);
1314
1544
  };
1545
+ var validateBTreeJSONSortOrder = (json, compareKeys) => {
1546
+ const strict = json.config.duplicateKeys !== "allow";
1547
+ for (let i = 1; i < json.entries.length; i += 1) {
1548
+ const cmp = compareKeys(json.entries[i - 1][0], json.entries[i][0]);
1549
+ if (cmp > 0) {
1550
+ throw new BTreeValidationError("fromJSON: entries not sorted.");
1551
+ }
1552
+ if (strict && cmp === 0) {
1553
+ throw new BTreeValidationError(
1554
+ 'fromJSON: duplicate keys require duplicateKeys "allow".'
1555
+ );
1556
+ }
1557
+ }
1558
+ };
1315
1559
  var buildConfigFromJSON = (json, compareKeys) => {
1316
1560
  const cfg = json.config;
1317
1561
  const config = {
1318
1562
  compareKeys,
1319
1563
  duplicateKeys: cfg.duplicateKeys,
1320
1564
  enableEntryIdLookup: cfg.enableEntryIdLookup,
1321
- autoScale: cfg.autoScale
1565
+ autoScale: cfg.autoScale,
1566
+ deleteRebalancePolicy: cfg.deleteRebalancePolicy
1322
1567
  };
1323
1568
  if (!cfg.autoScale) {
1324
1569
  config.maxLeafEntries = cfg.maxLeafEntries;
@@ -1327,6 +1572,44 @@ var buildConfigFromJSON = (json, compareKeys) => {
1327
1572
  return config;
1328
1573
  };
1329
1574
 
1575
+ // src/btree/traversal.ts
1576
+ var snapshotEntries = (state) => {
1577
+ const result = new Array(state.entryCount);
1578
+ let leaf = state.leftmostLeaf;
1579
+ let writeIdx = 0;
1580
+ while (leaf !== null) {
1581
+ const count = leafEntryCount(leaf);
1582
+ for (let i = 0; i < count; i += 1) {
1583
+ result[writeIdx++] = freezeEntry(leafEntryAt(leaf, i));
1584
+ }
1585
+ leaf = leaf.next;
1586
+ }
1587
+ return result;
1588
+ };
1589
+ var collectInternalEntries = (state) => {
1590
+ const result = new Array(state.entryCount);
1591
+ let leaf = state.leftmostLeaf;
1592
+ let writeIdx = 0;
1593
+ while (leaf !== null) {
1594
+ const count = leafEntryCount(leaf);
1595
+ for (let i = 0; i < count; i += 1) {
1596
+ result[writeIdx++] = leafEntryAt(leaf, i);
1597
+ }
1598
+ leaf = leaf.next;
1599
+ }
1600
+ return result;
1601
+ };
1602
+ var forEachEntry = (state, callback, thisArg) => {
1603
+ let leaf = state.leftmostLeaf;
1604
+ while (leaf !== null) {
1605
+ const count = leafEntryCount(leaf);
1606
+ for (let i = 0; i < count; i += 1) {
1607
+ callback.call(thisArg, freezeEntry(leafEntryAt(leaf, i)));
1608
+ }
1609
+ leaf = leaf.next;
1610
+ }
1611
+ };
1612
+
1330
1613
  // src/btree/integrity-helpers.ts
1331
1614
  var nodeMinKey = (node) => {
1332
1615
  if (isLeafNode(node)) {
@@ -1335,7 +1618,10 @@ var nodeMinKey = (node) => {
1335
1618
  return { key: e.key, sequence: e.entryId };
1336
1619
  }
1337
1620
  if (node.childOffset >= node.keys.length) return null;
1338
- return { key: node.keys[node.childOffset].key, sequence: node.keys[node.childOffset].sequence };
1621
+ return {
1622
+ key: node.keys[node.childOffset].key,
1623
+ sequence: node.keys[node.childOffset].sequence
1624
+ };
1339
1625
  };
1340
1626
  var compareNodeKeys = (comparator, leftKey, leftSeq, rightKey, rightSeq) => {
1341
1627
  const cmp = comparator(leftKey, rightKey);
@@ -1357,9 +1643,7 @@ var getNodeMaxKey = (node) => {
1357
1643
  };
1358
1644
  var validateComparatorResult = (result) => {
1359
1645
  if (!Number.isFinite(result)) {
1360
- throw new BTreeValidationError(
1361
- "compareKeys must return a finite number."
1362
- );
1646
+ throw new BTreeValidationError("compareKeys must return a finite number.");
1363
1647
  }
1364
1648
  return result;
1365
1649
  };
@@ -1419,6 +1703,34 @@ var assertTransitivityAsInvariant = (state, first, second, third) => {
1419
1703
  throw error;
1420
1704
  }
1421
1705
  };
1706
+ var validateAdjacentLeafOrdering = (state, previous, cursor) => {
1707
+ const prevMax = getNodeMaxKey(previous);
1708
+ const currentMin = nodeMinKey(cursor);
1709
+ if (prevMax === null || currentMin === null) {
1710
+ throw new BTreeInvariantError(
1711
+ "Non-empty tree leaf chain contains empty leaf node."
1712
+ );
1713
+ }
1714
+ if (compareNodeKeys(
1715
+ state.compareKeys,
1716
+ prevMax.key,
1717
+ prevMax.sequence,
1718
+ currentMin.key,
1719
+ currentMin.sequence
1720
+ ) > 0) {
1721
+ throw new BTreeInvariantError("Adjacent leaf key ranges are out of order.");
1722
+ }
1723
+ const prevCount = leafEntryCount(previous);
1724
+ const curCount = leafEntryCount(cursor);
1725
+ if (state.duplicateKeys !== "allow" && prevCount > 0 && curCount > 0 && state.compareKeys(
1726
+ leafEntryAt(previous, prevCount - 1).key,
1727
+ leafEntryAt(cursor, 0).key
1728
+ ) === 0) {
1729
+ throw new BTreeInvariantError(
1730
+ "Duplicate user key detected across adjacent leaves with uniqueness policy."
1731
+ );
1732
+ }
1733
+ };
1422
1734
  var validateLeafChainStep = (state, cursor, previous, visited) => {
1423
1735
  if (!isLeafNode(cursor)) {
1424
1736
  throw new BTreeInvariantError("Leaf linkage cursor reached non-leaf node.");
@@ -1430,22 +1742,7 @@ var validateLeafChainStep = (state, cursor, previous, visited) => {
1430
1742
  throw new BTreeInvariantError("Leaf prev pointer mismatch.");
1431
1743
  }
1432
1744
  if (previous !== null && isLeafNode(previous)) {
1433
- const prevMax = getNodeMaxKey(previous);
1434
- const currentMin = nodeMinKey(cursor);
1435
- if (prevMax === null || currentMin === null) {
1436
- throw new BTreeInvariantError("Non-empty tree leaf chain contains empty leaf node.");
1437
- }
1438
- if (compareNodeKeys(state.compareKeys, prevMax.key, prevMax.sequence, currentMin.key, currentMin.sequence) > 0) {
1439
- throw new BTreeInvariantError("Adjacent leaf key ranges are out of order.");
1440
- }
1441
- const prevCount = leafEntryCount(previous);
1442
- const curCount = leafEntryCount(cursor);
1443
- if (state.duplicateKeys !== "allow" && prevCount > 0 && curCount > 0 && state.compareKeys(
1444
- leafEntryAt(previous, prevCount - 1).key,
1445
- leafEntryAt(cursor, 0).key
1446
- ) === 0) {
1447
- throw new BTreeInvariantError("Duplicate user key detected across adjacent leaves with uniqueness policy.");
1448
- }
1745
+ validateAdjacentLeafOrdering(state, previous, cursor);
1449
1746
  }
1450
1747
  };
1451
1748
  var validateLeafLinks = (state, expectedLeafCount) => {
@@ -1454,7 +1751,9 @@ var validateLeafLinks = (state, expectedLeafCount) => {
1454
1751
  throw new BTreeInvariantError("Empty tree root must be a leaf node.");
1455
1752
  }
1456
1753
  if (state.leftmostLeaf !== state.root || state.rightmostLeaf !== state.root) {
1457
- throw new BTreeInvariantError("Empty tree leaf pointers must reference root leaf.");
1754
+ throw new BTreeInvariantError(
1755
+ "Empty tree leaf pointers must reference root leaf."
1756
+ );
1458
1757
  }
1459
1758
  return;
1460
1759
  }
@@ -1479,7 +1778,9 @@ var validateLeafLinks = (state, expectedLeafCount) => {
1479
1778
  throw new BTreeInvariantError("Rightmost leaf pointer mismatch.");
1480
1779
  }
1481
1780
  if (leafCount !== expectedLeafCount) {
1482
- throw new BTreeInvariantError("Leaf chain count mismatch with tree traversal count.");
1781
+ throw new BTreeInvariantError(
1782
+ "Leaf chain count mismatch with tree traversal count."
1783
+ );
1483
1784
  }
1484
1785
  };
1485
1786
 
@@ -1506,7 +1807,9 @@ var validateLeafNodeOrdering = (state, node) => {
1506
1807
  leafEntryAt(node, index - 1).key,
1507
1808
  leafEntryAt(node, index).key
1508
1809
  ) === 0) {
1509
- throw new BTreeInvariantError("Duplicate user key detected in tree with uniqueness policy.");
1810
+ throw new BTreeInvariantError(
1811
+ "Duplicate user key detected in tree with uniqueness policy."
1812
+ );
1510
1813
  }
1511
1814
  }
1512
1815
  }
@@ -1514,12 +1817,7 @@ var validateLeafNodeOrdering = (state, node) => {
1514
1817
  const first = leafEntryAt(node, index - 2);
1515
1818
  const second = leafEntryAt(node, index - 1);
1516
1819
  const third = leafEntryAt(node, index);
1517
- assertTransitivityAsInvariant(
1518
- state,
1519
- first.key,
1520
- second.key,
1521
- third.key
1522
- );
1820
+ assertTransitivityAsInvariant(state, first.key, second.key, third.key);
1523
1821
  }
1524
1822
  if (count > state.maxLeafEntries) {
1525
1823
  throw new BTreeInvariantError("Leaf node exceeds maximum occupancy.");
@@ -1528,9 +1826,14 @@ var validateLeafNodeOrdering = (state, node) => {
1528
1826
  var validateLeafNode = (state, node, depth) => {
1529
1827
  validateLeafNodeOrdering(state, node);
1530
1828
  const count = leafEntryCount(node);
1531
- const baseMinLeaf = state.autoScale ? Math.ceil(computeAutoScaleTier(0).maxLeaf / 2) : state.minLeafEntries;
1829
+ let baseMinLeaf = state.autoScale ? Math.ceil(computeAutoScaleTier(0).maxLeaf / 2) : state.minLeafEntries;
1830
+ if (state.deleteRebalancePolicy === "lazy") {
1831
+ baseMinLeaf = applyLazyThreshold(baseMinLeaf);
1832
+ }
1532
1833
  if (node !== state.root && count < baseMinLeaf) {
1533
- throw new BTreeInvariantError("Non-root leaf node violates minimum occupancy.");
1834
+ throw new BTreeInvariantError(
1835
+ "Non-root leaf node violates minimum occupancy."
1836
+ );
1534
1837
  }
1535
1838
  const first = count === 0 ? null : leafEntryAt(node, 0);
1536
1839
  const last = count === 0 ? null : leafEntryAt(node, count - 1);
@@ -1552,76 +1855,110 @@ var validateBranchStructure = (state, node) => {
1552
1855
  }
1553
1856
  const baseMinBranch = state.autoScale ? Math.ceil(computeAutoScaleTier(0).maxBranch / 2) : state.minBranchChildren;
1554
1857
  if (node !== state.root && liveCount < baseMinBranch) {
1555
- throw new BTreeInvariantError("Non-root branch node violates minimum occupancy.");
1858
+ throw new BTreeInvariantError(
1859
+ "Non-root branch node violates minimum occupancy."
1860
+ );
1556
1861
  }
1557
1862
  if (liveCount > state.maxBranchChildren) {
1558
1863
  throw new BTreeInvariantError("Branch node exceeds maximum occupancy.");
1559
1864
  }
1560
1865
  if (node.keys.length !== node.children.length) {
1561
- throw new BTreeInvariantError("Branch keys array length does not match children array length.");
1866
+ throw new BTreeInvariantError(
1867
+ "Branch keys array length does not match children array length."
1868
+ );
1562
1869
  }
1563
1870
  };
1564
1871
  var validateBranchChild = (state, node, childIndex, depth) => {
1565
1872
  const child = node.children[childIndex];
1566
1873
  if (child.parent !== node) {
1567
- throw new BTreeInvariantError("Child-parent pointer mismatch in branch node.");
1874
+ throw new BTreeInvariantError(
1875
+ "Child-parent pointer mismatch in branch node."
1876
+ );
1568
1877
  }
1569
1878
  if (child.indexInParent !== childIndex) {
1570
- throw new BTreeInvariantError("Child indexInParent does not match actual position in parent.");
1879
+ throw new BTreeInvariantError(
1880
+ "Child indexInParent does not match actual position in parent."
1881
+ );
1571
1882
  }
1572
1883
  const childValidation = validateNode(state, child, depth + 1);
1573
1884
  if (childValidation.minKey === null || childValidation.maxKey === null) {
1574
- throw new BTreeInvariantError("Branch child must not be empty in non-root branch tree.");
1885
+ throw new BTreeInvariantError(
1886
+ "Branch child must not be empty in non-root branch tree."
1887
+ );
1575
1888
  }
1576
1889
  const cachedMinKey = node.keys[childIndex];
1577
1890
  const actualMinKey = nodeMinKey(child);
1578
- if (actualMinKey === null || compareNodeKeys(state.compareKeys, cachedMinKey.key, cachedMinKey.sequence, actualMinKey.key, actualMinKey.sequence) !== 0) {
1579
- throw new BTreeInvariantError("Branch cached key does not match actual child minimum key.");
1891
+ if (actualMinKey === null || compareNodeKeys(
1892
+ state.compareKeys,
1893
+ cachedMinKey.key,
1894
+ cachedMinKey.sequence,
1895
+ actualMinKey.key,
1896
+ actualMinKey.sequence
1897
+ ) !== 0) {
1898
+ throw new BTreeInvariantError(
1899
+ "Branch cached key does not match actual child minimum key."
1900
+ );
1580
1901
  }
1581
1902
  return childValidation;
1582
1903
  };
1904
+ var mergeChildValidation = (state, accumulated, childValidation) => {
1905
+ if (accumulated.leafDepth !== null && childValidation.leafDepth !== null && childValidation.leafDepth !== accumulated.leafDepth) {
1906
+ throw new BTreeInvariantError("Leaf depth mismatch detected in tree.");
1907
+ }
1908
+ if (accumulated.leafDepth === null && childValidation.leafDepth !== null) {
1909
+ accumulated.leafDepth = childValidation.leafDepth;
1910
+ }
1911
+ if (accumulated.previousChildMax !== null && compareNodeKeys(
1912
+ state.compareKeys,
1913
+ accumulated.previousChildMax.key,
1914
+ accumulated.previousChildMax.sequence,
1915
+ childValidation.minKey.key,
1916
+ childValidation.minKey.sequence
1917
+ ) >= 0) {
1918
+ throw new BTreeInvariantError(
1919
+ "Branch child key ranges are not strictly ordered."
1920
+ );
1921
+ }
1922
+ if (accumulated.minKey === null) accumulated.minKey = childValidation.minKey;
1923
+ accumulated.maxKey = childValidation.maxKey;
1924
+ accumulated.previousChildMax = childValidation.maxKey;
1925
+ accumulated.leafCount += childValidation.leafCount;
1926
+ accumulated.branchCount += childValidation.branchCount;
1927
+ accumulated.entryCount += childValidation.entryCount;
1928
+ };
1583
1929
  var validateNode = (state, node, depth) => {
1584
1930
  if (isLeafNode(node)) {
1585
1931
  return validateLeafNode(state, node, depth);
1586
1932
  }
1587
1933
  validateBranchStructure(state, node);
1588
- let leafDepth = null;
1589
- let leafCount = 0;
1590
- let branchCount = 1;
1591
- let entryCount = 0;
1592
- let minKey = null;
1593
- let maxKey = null;
1594
- let previousChildMax = null;
1934
+ const acc = {
1935
+ leafDepth: null,
1936
+ leafCount: 0,
1937
+ branchCount: 1,
1938
+ entryCount: 0,
1939
+ minKey: null,
1940
+ maxKey: null,
1941
+ previousChildMax: null
1942
+ };
1595
1943
  for (let childIndex = node.childOffset; childIndex < node.children.length; childIndex += 1) {
1596
1944
  const childValidation = validateBranchChild(state, node, childIndex, depth);
1597
- if (leafDepth !== null && childValidation.leafDepth !== null && childValidation.leafDepth !== leafDepth) {
1598
- throw new BTreeInvariantError("Leaf depth mismatch detected in tree.");
1599
- }
1600
- if (leafDepth === null && childValidation.leafDepth !== null) {
1601
- leafDepth = childValidation.leafDepth;
1602
- }
1603
- if (previousChildMax !== null && compareNodeKeys(
1604
- state.compareKeys,
1605
- previousChildMax.key,
1606
- previousChildMax.sequence,
1607
- childValidation.minKey.key,
1608
- childValidation.minKey.sequence
1609
- ) >= 0) {
1610
- throw new BTreeInvariantError("Branch child key ranges are not strictly ordered.");
1611
- }
1612
- if (minKey === null) minKey = childValidation.minKey;
1613
- maxKey = childValidation.maxKey;
1614
- previousChildMax = childValidation.maxKey;
1615
- leafCount += childValidation.leafCount;
1616
- branchCount += childValidation.branchCount;
1617
- entryCount += childValidation.entryCount;
1945
+ mergeChildValidation(state, acc, childValidation);
1618
1946
  }
1619
- return { minKey, maxKey, leafDepth, leafCount, branchCount, entryCount };
1947
+ return {
1948
+ minKey: acc.minKey,
1949
+ maxKey: acc.maxKey,
1950
+ leafDepth: acc.leafDepth,
1951
+ leafCount: acc.leafCount,
1952
+ branchCount: acc.branchCount,
1953
+ entryCount: acc.entryCount
1954
+ };
1620
1955
  };
1621
1956
  var assertInvariants = (state) => {
1622
1957
  const validation = validateNode(state, state.root, 0);
1623
1958
  if (validation.entryCount !== state.entryCount) {
1624
- throw new BTreeInvariantError("Index entry count mismatch between tree traversal and tracked state.");
1959
+ throw new BTreeInvariantError(
1960
+ "Index entry count mismatch between tree traversal and tracked state."
1961
+ );
1625
1962
  }
1626
1963
  validateLeafLinks(state, validation.leafCount);
1627
1964
  };
@@ -1677,7 +2014,7 @@ var InMemoryBTree = class _InMemoryBTree {
1677
2014
  remove(key) {
1678
2015
  const entry = removeFirstMatchingEntry(this.state, key);
1679
2016
  if (entry === null) return null;
1680
- return toPublicEntry(entry);
2017
+ return freezeEntry(entry);
1681
2018
  }
1682
2019
  removeById(entryId) {
1683
2020
  if (this.state.entryKeys === null) {
@@ -1685,7 +2022,7 @@ var InMemoryBTree = class _InMemoryBTree {
1685
2022
  }
1686
2023
  const entry = removeEntryById(this.state, entryId);
1687
2024
  if (entry === null) return null;
1688
- return toPublicEntry(entry);
2025
+ return freezeEntry(entry);
1689
2026
  }
1690
2027
  peekById(entryId) {
1691
2028
  if (this.state.entryKeys === null) {
@@ -1693,7 +2030,7 @@ var InMemoryBTree = class _InMemoryBTree {
1693
2030
  }
1694
2031
  const entry = peekEntryById(this.state, entryId);
1695
2032
  if (entry === null) return null;
1696
- return toPublicEntry(entry);
2033
+ return freezeEntry(entry);
1697
2034
  }
1698
2035
  updateById(entryId, value) {
1699
2036
  if (this.state.entryKeys === null) {
@@ -1701,30 +2038,30 @@ var InMemoryBTree = class _InMemoryBTree {
1701
2038
  }
1702
2039
  const entry = updateEntryById(this.state, entryId, value);
1703
2040
  if (entry === null) return null;
1704
- return toPublicEntry(entry);
2041
+ return freezeEntry(entry);
1705
2042
  }
1706
2043
  popFirst() {
1707
2044
  const entry = popFirstEntry(this.state);
1708
2045
  if (entry === null) return null;
1709
- return toPublicEntry(entry);
2046
+ return freezeEntry(entry);
1710
2047
  }
1711
2048
  peekFirst() {
1712
2049
  if (this.state.entryCount === 0) {
1713
2050
  return null;
1714
2051
  }
1715
- return toPublicEntry(leafEntryAt(this.state.leftmostLeaf, 0));
2052
+ return freezeEntry(leafEntryAt(this.state.leftmostLeaf, 0));
1716
2053
  }
1717
2054
  peekLast() {
1718
2055
  if (this.state.entryCount === 0) {
1719
2056
  return null;
1720
2057
  }
1721
2058
  const leaf = this.state.rightmostLeaf;
1722
- return toPublicEntry(leafEntryAt(leaf, leafEntryCount(leaf) - 1));
2059
+ return freezeEntry(leafEntryAt(leaf, leafEntryCount(leaf) - 1));
1723
2060
  }
1724
2061
  popLast() {
1725
2062
  const entry = popLastEntry(this.state);
1726
2063
  if (entry === null) return null;
1727
- return toPublicEntry(entry);
2064
+ return freezeEntry(entry);
1728
2065
  }
1729
2066
  clear() {
1730
2067
  const emptyLeaf = createLeafNode([], null);
@@ -1734,16 +2071,9 @@ var InMemoryBTree = class _InMemoryBTree {
1734
2071
  this.state.entryCount = 0;
1735
2072
  this.state._cursor.leaf = emptyLeaf;
1736
2073
  this.state._cursor.index = 0;
1737
- if (this.state.entryKeys !== null) {
1738
- this.state.entryKeys.clear();
1739
- }
2074
+ this.state.entryKeys?.clear();
1740
2075
  if (this.state.autoScale) {
1741
- const tier = computeAutoScaleTier(0);
1742
- this.state.maxLeafEntries = tier.maxLeaf;
1743
- this.state.maxBranchChildren = tier.maxBranch;
1744
- this.state.minLeafEntries = minOccupancy(tier.maxLeaf);
1745
- this.state.minBranchChildren = minOccupancy(tier.maxBranch);
1746
- this.state._nextAutoScaleThreshold = computeNextAutoScaleThreshold(0);
2076
+ resetAutoScaleToTier0(this.state);
1747
2077
  }
1748
2078
  }
1749
2079
  get(key) {
@@ -1757,65 +2087,39 @@ var InMemoryBTree = class _InMemoryBTree {
1757
2087
  findFirst(key) {
1758
2088
  const found = findFirstMatchingUserKey(this.state, key);
1759
2089
  if (found === null) return null;
1760
- return toPublicEntry(leafEntryAt(found.leaf, found.index));
2090
+ return freezeEntry(leafEntryAt(found.leaf, found.index));
1761
2091
  }
1762
- /**
1763
- * Returns the last entry whose key matches `key`, or `null` if not found.
1764
- * Useful when `duplicateKeys` is `'allow'` and multiple entries share the same key.
1765
- */
1766
2092
  findLast(key) {
1767
2093
  const found = findLastMatchingUserKey(this.state, key);
1768
2094
  if (found === null) return null;
1769
- return toPublicEntry(leafEntryAt(found.leaf, found.index));
2095
+ return freezeEntry(leafEntryAt(found.leaf, found.index));
1770
2096
  }
1771
- /**
1772
- * Returns the smallest key in the tree that is strictly greater than `key`,
1773
- * or `null` if no such key exists.
1774
- */
1775
2097
  nextHigherKey(key) {
1776
2098
  return findNextHigherKey(this.state, key);
1777
2099
  }
1778
- /**
1779
- * Returns the largest key in the tree that is strictly less than `key`,
1780
- * or `null` if no such key exists.
1781
- */
1782
2100
  nextLowerKey(key) {
1783
2101
  return findNextLowerKey(this.state, key);
1784
2102
  }
1785
- /**
1786
- * Returns the entry for `key` if it exists; otherwise returns the entry with
1787
- * the largest key strictly less than `key`. Returns `null` when the tree is
1788
- * empty or every key is greater than `key`.
1789
- */
1790
2103
  getPairOrNextLower(key) {
1791
2104
  const found = findPairOrNextLower(this.state, key);
1792
2105
  if (found === null) return null;
1793
- return toPublicEntry(leafEntryAt(found.leaf, found.index));
2106
+ return freezeEntry(leafEntryAt(found.leaf, found.index));
1794
2107
  }
1795
- /**
1796
- * Returns the number of entries whose keys fall within [`startKey`, `endKey`].
1797
- * Pass `options` to make either bound exclusive.
1798
- */
1799
2108
  count(startKey, endKey, options) {
1800
2109
  return countRangeEntries(this.state, startKey, endKey, options);
1801
2110
  }
1802
- /**
1803
- * Deletes all entries whose keys fall within [`startKey`, `endKey`].
1804
- * Pass `options` to make either bound exclusive.
1805
- * @returns The number of entries deleted.
1806
- */
1807
2111
  deleteRange(startKey, endKey, options) {
1808
2112
  return deleteRangeEntries(this.state, startKey, endKey, options);
1809
2113
  }
1810
2114
  range(startKey, endKey, options) {
1811
- return rangeQueryEntries(this.state, startKey, endKey, options).map(toPublicEntry);
2115
+ return rangeQueryPublicEntries(this.state, startKey, endKey, options);
1812
2116
  }
1813
2117
  *entries() {
1814
2118
  let leaf = this.state.leftmostLeaf;
1815
2119
  while (leaf !== null) {
1816
2120
  const count = leafEntryCount(leaf);
1817
2121
  for (let i = 0; i < count; i += 1) {
1818
- yield toPublicEntry(leafEntryAt(leaf, i));
2122
+ yield freezeEntry(leafEntryAt(leaf, i));
1819
2123
  }
1820
2124
  leaf = leaf.next;
1821
2125
  }
@@ -1825,7 +2129,7 @@ var InMemoryBTree = class _InMemoryBTree {
1825
2129
  while (leaf !== null) {
1826
2130
  const count = leafEntryCount(leaf);
1827
2131
  for (let i = count - 1; i >= 0; i -= 1) {
1828
- yield toPublicEntry(leafEntryAt(leaf, i));
2132
+ yield freezeEntry(leafEntryAt(leaf, i));
1829
2133
  }
1830
2134
  leaf = leaf.prev;
1831
2135
  }
@@ -1843,55 +2147,26 @@ var InMemoryBTree = class _InMemoryBTree {
1843
2147
  [Symbol.iterator]() {
1844
2148
  return this.entries();
1845
2149
  }
2150
+ forEachRange(startKey, endKey, callback, options) {
2151
+ forEachRangeEntries(this.state, startKey, endKey, callback, options);
2152
+ }
1846
2153
  forEach(callback, thisArg) {
1847
- let leaf = this.state.leftmostLeaf;
1848
- while (leaf !== null) {
1849
- const count = leafEntryCount(leaf);
1850
- for (let i = 0; i < count; i += 1) {
1851
- callback.call(thisArg, toPublicEntry(leafEntryAt(leaf, i)));
1852
- }
1853
- leaf = leaf.next;
1854
- }
2154
+ forEachEntry(this.state, callback, thisArg);
1855
2155
  }
1856
2156
  snapshot() {
1857
- const result = 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
- result[writeIdx++] = toPublicEntry(leafEntryAt(leaf, i));
1864
- }
1865
- leaf = leaf.next;
1866
- }
1867
- return result;
2157
+ return snapshotEntries(this.state);
1868
2158
  }
1869
- /**
1870
- * Returns a structurally independent `InMemoryBTree` with identical
1871
- * configuration and entries. The tree structure (nodes, links, entry IDs)
1872
- * is fully independent, but stored key and value references are shared
1873
- * with the source tree.
1874
- * Note: `EntryId` values are reassigned in the clone — IDs from the source tree are not valid for the clone.
1875
- */
1876
2159
  clone() {
1877
- const cloned = new _InMemoryBTree(buildConfigFromState(this.state));
2160
+ const cloned = new _InMemoryBTree(
2161
+ buildConfigFromState(this.state)
2162
+ );
1878
2163
  applyAutoScaleCapacitySnapshot(
1879
2164
  cloned.state,
1880
2165
  this.state.maxLeafEntries,
1881
2166
  this.state.maxBranchChildren
1882
2167
  );
1883
2168
  if (this.state.entryCount > 0) {
1884
- const pairs = new Array(this.state.entryCount);
1885
- let leaf = this.state.leftmostLeaf;
1886
- let writeIdx = 0;
1887
- while (leaf !== null) {
1888
- const count = leafEntryCount(leaf);
1889
- for (let i = 0; i < count; i += 1) {
1890
- pairs[writeIdx++] = leafEntryAt(leaf, i);
1891
- }
1892
- leaf = leaf.next;
1893
- }
1894
- cloned.putMany(pairs);
2169
+ cloned.putMany(collectInternalEntries(this.state));
1895
2170
  }
1896
2171
  return cloned;
1897
2172
  }
@@ -1900,26 +2175,19 @@ var InMemoryBTree = class _InMemoryBTree {
1900
2175
  }
1901
2176
  static fromJSON(json, compareKeys) {
1902
2177
  validateBTreeJSON(json);
1903
- const strict = json.config.duplicateKeys !== "allow";
1904
- for (let i = 1; i < json.entries.length; i += 1) {
1905
- const cmp = compareKeys(json.entries[i - 1][0], json.entries[i][0]);
1906
- if (cmp > 0) {
1907
- throw new BTreeValidationError("fromJSON: entries not sorted.");
1908
- }
1909
- if (strict && cmp === 0) {
1910
- throw new BTreeValidationError(
1911
- 'fromJSON: duplicate keys require duplicateKeys "allow".'
1912
- );
1913
- }
1914
- }
1915
- const tree = new _InMemoryBTree(buildConfigFromJSON(json, compareKeys));
2178
+ validateBTreeJSONSortOrder(json, compareKeys);
2179
+ const tree = new _InMemoryBTree(
2180
+ buildConfigFromJSON(json, compareKeys)
2181
+ );
1916
2182
  applyAutoScaleCapacitySnapshot(
1917
2183
  tree.state,
1918
2184
  json.config.maxLeafEntries,
1919
2185
  json.config.maxBranchChildren
1920
2186
  );
1921
2187
  if (json.entries.length > 0) {
1922
- const pairs = new Array(json.entries.length);
2188
+ const pairs = new Array(
2189
+ json.entries.length
2190
+ );
1923
2191
  for (let i = 0; i < json.entries.length; i += 1) {
1924
2192
  pairs[i] = { key: json.entries[i][0], value: json.entries[i][1] };
1925
2193
  }