@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.
package/dist/core.cjs CHANGED
@@ -68,11 +68,12 @@ var createBranchNode = (children, parent) => {
68
68
  const child = children[i];
69
69
  child.parent = branch;
70
70
  child.indexInParent = i;
71
- const target = { key: void 0, sequence: 0 };
71
+ const target = {
72
+ key: void 0,
73
+ sequence: 0
74
+ };
72
75
  if (!writeMinKeyTo(child, target)) {
73
- throw new BTreeInvariantError(
74
- "branch child has no min key"
75
- );
76
+ throw new BTreeInvariantError("branch child has no min key");
76
77
  }
77
78
  keys.push(target);
78
79
  }
@@ -126,7 +127,14 @@ var leafInsertAt = (leaf, logicalIndex, entry) => {
126
127
  leaf.entryOffset -= 1;
127
128
  leaf.entries[phys - 1] = entry;
128
129
  } else {
129
- leaf.entries.splice(phys, 0, entry);
130
+ const len = leaf.entries.length;
131
+ if (phys >= len) {
132
+ leaf.entries.push(entry);
133
+ } else {
134
+ leaf.entries.push(leaf.entries[len - 1]);
135
+ leaf.entries.copyWithin(phys + 1, phys, len);
136
+ leaf.entries[phys] = entry;
137
+ }
130
138
  }
131
139
  };
132
140
  var leafCompact = (leaf) => {
@@ -155,7 +163,11 @@ var branchInsertAt = (branch, logicalIndex, child, key) => {
155
163
  const phys = branch.childOffset + logicalIndex;
156
164
  const count = branch.children.length - branch.childOffset;
157
165
  if (branch.childOffset > 0 && logicalIndex < count >>> 1) {
158
- branch.children.copyWithin(branch.childOffset - 1, branch.childOffset, phys);
166
+ branch.children.copyWithin(
167
+ branch.childOffset - 1,
168
+ branch.childOffset,
169
+ phys
170
+ );
159
171
  branch.keys.copyWithin(branch.childOffset - 1, branch.childOffset, phys);
160
172
  branch.childOffset -= 1;
161
173
  branch.children[phys - 1] = child;
@@ -176,8 +188,16 @@ var branchRemoveAt = (branch, physIndex) => {
176
188
  const logicalIndex = physIndex - branch.childOffset;
177
189
  const count = branch.children.length - branch.childOffset;
178
190
  if (logicalIndex < count - 1 - logicalIndex) {
179
- branch.children.copyWithin(branch.childOffset + 1, branch.childOffset, physIndex);
180
- branch.keys.copyWithin(branch.childOffset + 1, branch.childOffset, physIndex);
191
+ branch.children.copyWithin(
192
+ branch.childOffset + 1,
193
+ branch.childOffset,
194
+ physIndex
195
+ );
196
+ branch.keys.copyWithin(
197
+ branch.childOffset + 1,
198
+ branch.childOffset,
199
+ physIndex
200
+ );
181
201
  branch.childOffset += 1;
182
202
  for (let i = branch.childOffset; i <= physIndex; i += 1) {
183
203
  branch.children[i].indexInParent = i;
@@ -207,17 +227,21 @@ var normalizeDuplicateKeyPolicy = (value) => {
207
227
  return "replace";
208
228
  }
209
229
  if (value !== "allow" && value !== "reject" && value !== "replace") {
210
- throw new BTreeValidationError(
211
- `Invalid duplicateKeys option.`
212
- );
230
+ throw new BTreeValidationError(`Invalid duplicateKeys option.`);
213
231
  }
214
232
  return value;
215
233
  };
216
- var toPublicEntry = (entry) => ({
217
- entryId: entry.entryId,
218
- key: entry.key,
219
- value: entry.value
220
- });
234
+ var normalizeDeleteRebalancePolicy = (value) => {
235
+ if (value === void 0) {
236
+ return "standard";
237
+ }
238
+ if (value !== "standard" && value !== "lazy") {
239
+ throw new BTreeValidationError(`Invalid deleteRebalancePolicy option.`);
240
+ }
241
+ return value;
242
+ };
243
+ var freezeEntry = (entry) => Object.freeze(entry);
244
+ var createEntry = (key, entryId, value) => Object.freeze({ key, entryId, value });
221
245
  var isLeafNode = (node) => {
222
246
  return node.kind === NODE_LEAF;
223
247
  };
@@ -367,7 +391,11 @@ var hasKeyEntry = (state, key) => {
367
391
  var findNextHigherKey = (state, key) => {
368
392
  if (state.entryCount === 0) return null;
369
393
  const compare = state.compareKeys;
370
- let leaf = findLeafForKey(state, key, Number.MAX_SAFE_INTEGER);
394
+ let leaf = findLeafForKey(
395
+ state,
396
+ key,
397
+ Number.MAX_SAFE_INTEGER
398
+ );
371
399
  let idx = upperBoundInLeaf(state, leaf, key, Number.MAX_SAFE_INTEGER);
372
400
  while (leaf !== null) {
373
401
  if (idx < leafEntryCount(leaf)) {
@@ -470,55 +498,57 @@ var countRangeEntries = (state, startKey, endKey, options) => {
470
498
  cursorIndex = 0;
471
499
  continue;
472
500
  }
473
- const lastEntry = leafEntryAt(cursorLeaf, leafCount - 1);
474
- const cmpLast = compare(lastEntry.key, endKey);
475
- if (upperExclusive ? cmpLast < 0 : cmpLast <= 0) {
501
+ const lastKey = leafEntryAt(cursorLeaf, leafCount - 1).key;
502
+ if (isLastEntryInRange(lastKey, endKey, compare, upperExclusive)) {
476
503
  count += leafCount - cursorIndex;
477
504
  cursorLeaf = cursorLeaf.next;
478
505
  cursorIndex = 0;
479
506
  continue;
480
507
  }
481
- const endSeq = upperExclusive ? 0 : Number.MAX_SAFE_INTEGER;
482
- const endBound = upperExclusive ? lowerBoundInLeaf(state, cursorLeaf, endKey, endSeq) : upperBoundInLeaf(state, cursorLeaf, endKey, endSeq);
483
- const limit = endBound < leafCount ? endBound : leafCount;
484
- count += limit - cursorIndex;
508
+ count += findBoundaryEnd(state, cursorLeaf, endKey, upperExclusive, leafCount) - cursorIndex;
485
509
  return count;
486
510
  }
487
511
  return count;
488
512
  };
513
+ var isLastEntryInRange = (lastKey, endKey, compare, upperExclusive) => {
514
+ const cmp = compare(lastKey, endKey);
515
+ return upperExclusive ? cmp < 0 : cmp <= 0;
516
+ };
517
+ var findBoundaryEnd = (state, leaf, endKey, upperExclusive, leafCount) => {
518
+ const endSeq = upperExclusive ? 0 : Number.MAX_SAFE_INTEGER;
519
+ const endBound = upperExclusive ? lowerBoundInLeaf(state, leaf, endKey, endSeq) : upperBoundInLeaf(state, leaf, endKey, endSeq);
520
+ return endBound < leafCount ? endBound : leafCount;
521
+ };
489
522
  var RANGE_PREALLOC_THRESHOLD = 200;
490
- var allocateRangeOutput = (state, cursorLeaf, cursorIndex, compare, upperExclusive, startKey, endKey, options) => {
491
- const firstLeafCount = leafEntryCount(cursorLeaf);
492
- const firstLeafRemainder = firstLeafCount - cursorIndex;
493
- if (firstLeafRemainder >= RANGE_PREALLOC_THRESHOLD && cursorLeaf.next !== null) {
494
- const lastEntry = leafEntryAt(cursorLeaf, firstLeafCount - 1);
495
- const cmpLast = compare(lastEntry.key, endKey);
496
- if (upperExclusive ? cmpLast < 0 : cmpLast <= 0) {
497
- const total = countRangeEntries(state, startKey, endKey, options);
498
- return new Array(total);
523
+ var allocateRangeOutput = (state, cursor, startKey, endKey, options) => {
524
+ const firstLeafCount = leafEntryCount(cursor.leaf);
525
+ const firstLeafRemainder = firstLeafCount - cursor.index;
526
+ if (firstLeafRemainder >= RANGE_PREALLOC_THRESHOLD && cursor.leaf.next !== null) {
527
+ const lastKey = leafEntryAt(cursor.leaf, firstLeafCount - 1).key;
528
+ if (isLastEntryInRange(lastKey, endKey, cursor.compare, cursor.upperExclusive)) {
529
+ return new Array(
530
+ countRangeEntries(state, startKey, endKey, options)
531
+ );
499
532
  }
500
533
  }
501
534
  return [];
502
535
  };
503
- var appendLeafSlice = (leaf, from, to, output, useIndexed, writeIdx) => {
536
+ var appendLeafSlicePublic = (leaf, from, to, output, useIndexed, writeIdx) => {
504
537
  if (useIndexed) {
505
538
  for (let i = from; i < to; i += 1) {
506
- output[writeIdx++] = leafEntryAt(leaf, i);
539
+ output[writeIdx++] = freezeEntry(leafEntryAt(leaf, i));
507
540
  }
508
541
  } else {
509
542
  for (let i = from; i < to; i += 1) {
510
- output.push(leafEntryAt(leaf, i));
543
+ output.push(freezeEntry(leafEntryAt(leaf, i)));
511
544
  }
512
545
  }
513
546
  return writeIdx;
514
547
  };
515
- var rangeQueryEntries = (state, startKey, endKey, options) => {
516
- const cursor = initRangeCursor(state, startKey, endKey, options);
517
- if (cursor === null) return [];
548
+ var collectPublicEntries = (state, cursor, endKey, output) => {
549
+ const { compare, upperExclusive } = cursor;
518
550
  let cursorLeaf = cursor.leaf;
519
551
  let cursorIndex = cursor.index;
520
- const { compare, upperExclusive } = cursor;
521
- const output = allocateRangeOutput(state, cursorLeaf, cursorIndex, compare, upperExclusive, startKey, endKey, options);
522
552
  let writeIdx = 0;
523
553
  const useIndexed = output.length > 0;
524
554
  while (cursorLeaf !== null) {
@@ -528,24 +558,82 @@ var rangeQueryEntries = (state, startKey, endKey, options) => {
528
558
  cursorIndex = 0;
529
559
  continue;
530
560
  }
531
- const lastEntry = leafEntryAt(cursorLeaf, leafCount - 1);
532
- const cmpLast = compare(lastEntry.key, endKey);
533
- if (upperExclusive ? cmpLast < 0 : cmpLast <= 0) {
534
- writeIdx = appendLeafSlice(cursorLeaf, cursorIndex, leafCount, output, useIndexed, writeIdx);
561
+ const lastKey = leafEntryAt(cursorLeaf, leafCount - 1).key;
562
+ if (isLastEntryInRange(lastKey, endKey, compare, upperExclusive)) {
563
+ writeIdx = appendLeafSlicePublic(
564
+ cursorLeaf,
565
+ cursorIndex,
566
+ leafCount,
567
+ output,
568
+ useIndexed,
569
+ writeIdx
570
+ );
535
571
  cursorLeaf = cursorLeaf.next;
536
572
  cursorIndex = 0;
537
573
  continue;
538
574
  }
539
- const endSeq = upperExclusive ? 0 : Number.MAX_SAFE_INTEGER;
540
- const endBound = upperExclusive ? lowerBoundInLeaf(state, cursorLeaf, endKey, endSeq) : upperBoundInLeaf(state, cursorLeaf, endKey, endSeq);
541
- const limit = endBound < leafCount ? endBound : leafCount;
542
- appendLeafSlice(cursorLeaf, cursorIndex, limit, output, useIndexed, writeIdx);
543
- return output;
575
+ const limit = findBoundaryEnd(
576
+ state,
577
+ cursorLeaf,
578
+ endKey,
579
+ upperExclusive,
580
+ leafCount
581
+ );
582
+ appendLeafSlicePublic(
583
+ cursorLeaf,
584
+ cursorIndex,
585
+ limit,
586
+ output,
587
+ useIndexed,
588
+ writeIdx
589
+ );
590
+ return;
544
591
  }
592
+ };
593
+ var rangeQueryPublicEntries = (state, startKey, endKey, options) => {
594
+ const cursor = initRangeCursor(state, startKey, endKey, options);
595
+ if (cursor === null) return [];
596
+ const output = allocateRangeOutput(state, cursor, startKey, endKey, options);
597
+ collectPublicEntries(state, cursor, endKey, output);
545
598
  return output;
546
599
  };
600
+ var forEachRangeEntries = (state, startKey, endKey, callback, options) => {
601
+ const cursor = initRangeCursor(state, startKey, endKey, options);
602
+ if (cursor === null) return;
603
+ let cursorLeaf = cursor.leaf;
604
+ let cursorIndex = cursor.index;
605
+ const { compare, upperExclusive } = cursor;
606
+ while (cursorLeaf !== null) {
607
+ const leafCount = leafEntryCount(cursorLeaf);
608
+ if (cursorIndex >= leafCount) {
609
+ cursorLeaf = cursorLeaf.next;
610
+ cursorIndex = 0;
611
+ continue;
612
+ }
613
+ const lastKey = leafEntryAt(cursorLeaf, leafCount - 1).key;
614
+ if (isLastEntryInRange(lastKey, endKey, compare, upperExclusive)) {
615
+ for (let i = cursorIndex; i < leafCount; i += 1) {
616
+ callback(freezeEntry(leafEntryAt(cursorLeaf, i)));
617
+ }
618
+ cursorLeaf = cursorLeaf.next;
619
+ cursorIndex = 0;
620
+ continue;
621
+ }
622
+ const limit = findBoundaryEnd(
623
+ state,
624
+ cursorLeaf,
625
+ endKey,
626
+ upperExclusive,
627
+ leafCount
628
+ );
629
+ for (let i = cursorIndex; i < limit; i += 1) {
630
+ callback(freezeEntry(leafEntryAt(cursorLeaf, i)));
631
+ }
632
+ return;
633
+ }
634
+ };
547
635
 
548
- // src/btree/rebalance.ts
636
+ // src/btree/rebalance-branch.ts
549
637
  var updateMinKeyInAncestors = (node) => {
550
638
  let current = node;
551
639
  while (current.parent !== null) {
@@ -561,12 +649,9 @@ var requireParent = (node) => {
561
649
  }
562
650
  return node.parent;
563
651
  };
564
- var requireLeafNode = (node) => {
565
- if (!isLeafNode(node)) throw new BTreeInvariantError("expected leaf, got branch");
566
- return node;
567
- };
568
652
  var requireBranchNode = (node) => {
569
- if (isLeafNode(node)) throw new BTreeInvariantError("expected branch, got leaf");
653
+ if (isLeafNode(node))
654
+ throw new BTreeInvariantError("expected branch, got leaf");
570
655
  return node;
571
656
  };
572
657
  var removeChildFromBranch = (branch, childIndex) => {
@@ -575,27 +660,18 @@ var removeChildFromBranch = (branch, childIndex) => {
575
660
  }
576
661
  branchRemoveAt(branch, childIndex);
577
662
  };
578
- var detachLeafFromChain = (state, leaf) => {
579
- if (leaf.prev !== null) {
580
- leaf.prev.next = leaf.next;
581
- } else if (leaf.next !== null) {
582
- state.leftmostLeaf = leaf.next;
583
- }
584
- if (leaf.next !== null) {
585
- leaf.next.prev = leaf.prev;
586
- } else if (leaf.prev !== null) {
587
- state.rightmostLeaf = leaf.prev;
588
- }
589
- leaf.prev = null;
590
- leaf.next = null;
591
- };
592
663
  var borrowFromLeftBranch = (branch, leftSibling, branchIndex) => {
593
664
  const borrowedChild = leftSibling.children.pop();
594
- if (borrowedChild === void 0) throw new BTreeInvariantError("left branch borrow failed");
665
+ if (borrowedChild === void 0)
666
+ throw new BTreeInvariantError("left branch borrow failed");
595
667
  leftSibling.keys.pop();
596
668
  borrowedChild.parent = branch;
597
- const borrowedMinKey = { key: void 0, sequence: 0 };
598
- if (!writeMinKeyTo(borrowedChild, borrowedMinKey)) throw new BTreeInvariantError("borrowed child has no min key");
669
+ const borrowedMinKey = {
670
+ key: void 0,
671
+ sequence: 0
672
+ };
673
+ if (!writeMinKeyTo(borrowedChild, borrowedMinKey))
674
+ throw new BTreeInvariantError("borrowed child has no min key");
599
675
  if (branch.childOffset > 0) {
600
676
  branch.childOffset -= 1;
601
677
  branch.children[branch.childOffset] = borrowedChild;
@@ -604,15 +680,20 @@ var borrowFromLeftBranch = (branch, leftSibling, branchIndex) => {
604
680
  } else {
605
681
  branch.children.unshift(borrowedChild);
606
682
  branch.keys.unshift(borrowedMinKey);
607
- for (let i = 0; i < branch.children.length; i += 1) branch.children[i].indexInParent = i;
683
+ for (let i = 0; i < branch.children.length; i += 1)
684
+ branch.children[i].indexInParent = i;
608
685
  }
609
686
  const parent = requireParent(branch);
610
- parent.keys[branchIndex] = { key: borrowedMinKey.key, sequence: borrowedMinKey.sequence };
687
+ parent.keys[branchIndex] = {
688
+ key: borrowedMinKey.key,
689
+ sequence: borrowedMinKey.sequence
690
+ };
611
691
  updateMinKeyInAncestors(branch);
612
692
  };
613
693
  var borrowFromRightBranch = (branch, rightSibling, branchIndex) => {
614
694
  const shiftIdx = rightSibling.childOffset;
615
- if (shiftIdx >= rightSibling.children.length) throw new BTreeInvariantError("right branch borrow failed");
695
+ if (shiftIdx >= rightSibling.children.length)
696
+ throw new BTreeInvariantError("right branch borrow failed");
616
697
  const borrowedChild = rightSibling.children[shiftIdx];
617
698
  rightSibling.childOffset += 1;
618
699
  if (rightSibling.childOffset >= rightSibling.children.length >>> 1) {
@@ -620,8 +701,12 @@ var borrowFromRightBranch = (branch, rightSibling, branchIndex) => {
620
701
  }
621
702
  branch.children.push(borrowedChild);
622
703
  borrowedChild.parent = branch;
623
- const borrowedMinKey = { key: void 0, sequence: 0 };
624
- if (!writeMinKeyTo(borrowedChild, borrowedMinKey)) throw new BTreeInvariantError("borrowed child has no min key");
704
+ const borrowedMinKey = {
705
+ key: void 0,
706
+ sequence: 0
707
+ };
708
+ if (!writeMinKeyTo(borrowedChild, borrowedMinKey))
709
+ throw new BTreeInvariantError("borrowed child has no min key");
625
710
  branch.keys.push(borrowedMinKey);
626
711
  borrowedChild.indexInParent = branch.children.length - 1;
627
712
  const parent = requireParent(branch);
@@ -674,7 +759,10 @@ var rebalanceAfterBranchRemoval = (state, branch) => {
674
759
  const parent = branch.parent;
675
760
  if (parent === null) throw new BTreeInvariantError("branch has no parent");
676
761
  const branchIndex = branch.indexInParent;
677
- const { left: leftSibling, right: rightSibling } = findBranchSiblings(parent, branchIndex);
762
+ const { left: leftSibling, right: rightSibling } = findBranchSiblings(
763
+ parent,
764
+ branchIndex
765
+ );
678
766
  if (rightSibling !== null && branchChildCount(rightSibling) > state.minBranchChildren) {
679
767
  borrowFromRightBranch(branch, rightSibling, branchIndex);
680
768
  return;
@@ -693,44 +781,70 @@ var rebalanceAfterBranchRemoval = (state, branch) => {
693
781
  }
694
782
  throw new BTreeInvariantError("no branch siblings to rebalance");
695
783
  };
784
+
785
+ // src/btree/rebalance.ts
786
+ var requireLeafNode = (node) => {
787
+ if (!isLeafNode(node))
788
+ throw new BTreeInvariantError("expected leaf, got branch");
789
+ return node;
790
+ };
791
+ var detachLeafFromChain = (state, leaf) => {
792
+ if (leaf.prev !== null) {
793
+ leaf.prev.next = leaf.next;
794
+ } else if (leaf.next !== null) {
795
+ state.leftmostLeaf = leaf.next;
796
+ }
797
+ if (leaf.next !== null) {
798
+ leaf.next.prev = leaf.prev;
799
+ } else if (leaf.prev !== null) {
800
+ state.rightmostLeaf = leaf.prev;
801
+ }
802
+ leaf.prev = null;
803
+ leaf.next = null;
804
+ };
696
805
  var mergeLeafEntries = (target, source) => {
697
806
  if (target.entryOffset > 0) {
698
807
  target.entries.copyWithin(0, target.entryOffset);
699
808
  target.entries.length = target.entries.length - target.entryOffset;
700
809
  target.entryOffset = 0;
701
810
  }
702
- const src = source.entries;
703
- for (let i = source.entryOffset; i < src.length; i += 1) target.entries.push(src[i]);
811
+ target.entries.push(...source.entries.slice(source.entryOffset));
704
812
  };
705
- var rebalanceAfterLeafRemoval = (state, leaf) => {
706
- if (leaf === state.root) {
707
- if (state.entryCount === 0) {
708
- state.leftmostLeaf = leaf;
709
- state.rightmostLeaf = leaf;
710
- }
711
- return;
813
+ var applyLazyThreshold = (min) => Math.max(1, Math.ceil(min / 4));
814
+ var leafRebalanceThreshold = (state) => {
815
+ if (state.deleteRebalancePolicy === "lazy") {
816
+ return applyLazyThreshold(state.minLeafEntries);
712
817
  }
713
- if (leafEntryCount(leaf) >= state.minLeafEntries) return;
714
- const parent = leaf.parent;
715
- if (parent === null) throw new BTreeInvariantError("Leaf node has no parent during rebalance.");
716
- const leafIndex = leaf.indexInParent;
717
- const leftSibling = leafIndex > parent.childOffset ? requireLeafNode(parent.children[leafIndex - 1]) : null;
718
- const rightSibling = leafIndex + 1 < parent.children.length ? requireLeafNode(parent.children[leafIndex + 1]) : null;
818
+ return state.minLeafEntries;
819
+ };
820
+ var findLeafSiblings = (parent, leafIndex) => {
821
+ const left = leafIndex > parent.childOffset ? requireLeafNode(parent.children[leafIndex - 1]) : null;
822
+ const right = leafIndex + 1 < parent.children.length ? requireLeafNode(parent.children[leafIndex + 1]) : null;
823
+ return { left, right };
824
+ };
825
+ var tryBorrowFromLeafSibling = (state, leaf, parent, leafIndex, leftSibling, rightSibling) => {
719
826
  if (rightSibling !== null && leafEntryCount(rightSibling) > state.minLeafEntries) {
720
827
  const borrowed = leafShiftEntry(rightSibling);
721
- if (borrowed === void 0) throw new BTreeInvariantError("right leaf borrow failed");
828
+ if (borrowed === void 0)
829
+ throw new BTreeInvariantError("right leaf borrow failed");
830
+ const leafWasEmpty = leafEntryCount(leaf) === 0;
722
831
  leaf.entries.push(borrowed);
723
832
  writeMinKeyTo(rightSibling, parent.keys[leafIndex + 1]);
724
- return;
833
+ if (leafWasEmpty) updateMinKeyInAncestors(leaf);
834
+ return true;
725
835
  }
726
836
  if (leftSibling !== null && leafEntryCount(leftSibling) > state.minLeafEntries) {
727
837
  const borrowed = leftSibling.entries.pop();
728
- if (borrowed === void 0) throw new BTreeInvariantError("left leaf borrow failed");
838
+ if (borrowed === void 0)
839
+ throw new BTreeInvariantError("left leaf borrow failed");
729
840
  leafUnshiftEntry(leaf, borrowed);
730
841
  parent.keys[leafIndex] = { key: borrowed.key, sequence: borrowed.entryId };
731
842
  updateMinKeyInAncestors(leaf);
732
- return;
843
+ return true;
733
844
  }
845
+ return false;
846
+ };
847
+ var mergeLeafWithSibling = (state, leaf, parent, leafIndex, leftSibling, rightSibling) => {
734
848
  if (leftSibling !== null) {
735
849
  mergeLeafEntries(leftSibling, leaf);
736
850
  detachLeafFromChain(state, leaf);
@@ -739,14 +853,34 @@ var rebalanceAfterLeafRemoval = (state, leaf) => {
739
853
  return;
740
854
  }
741
855
  if (rightSibling !== null) {
856
+ const leafWasEmpty = leafEntryCount(leaf) === 0;
742
857
  mergeLeafEntries(leaf, rightSibling);
743
858
  detachLeafFromChain(state, rightSibling);
859
+ if (leafWasEmpty) updateMinKeyInAncestors(leaf);
744
860
  removeChildFromBranch(parent, leafIndex + 1);
745
861
  rebalanceAfterBranchRemoval(state, parent);
746
862
  return;
747
863
  }
748
864
  throw new BTreeInvariantError("no leaf siblings to rebalance");
749
865
  };
866
+ var rebalanceAfterLeafRemoval = (state, leaf) => {
867
+ if (leaf === state.root) {
868
+ if (state.entryCount === 0) {
869
+ state.leftmostLeaf = leaf;
870
+ state.rightmostLeaf = leaf;
871
+ }
872
+ return;
873
+ }
874
+ if (leafEntryCount(leaf) >= leafRebalanceThreshold(state)) return;
875
+ const parent = leaf.parent;
876
+ if (parent === null)
877
+ throw new BTreeInvariantError("Leaf node has no parent during rebalance.");
878
+ const leafIndex = leaf.indexInParent;
879
+ const { left, right } = findLeafSiblings(parent, leafIndex);
880
+ if (tryBorrowFromLeafSibling(state, leaf, parent, leafIndex, left, right))
881
+ return;
882
+ mergeLeafWithSibling(state, leaf, parent, leafIndex, left, right);
883
+ };
750
884
 
751
885
  // src/btree/deleteRange.ts
752
886
  var navigateToStart = (state, startKey, lowerExclusive) => {
@@ -759,16 +893,16 @@ var navigateToStart = (state, startKey, lowerExclusive) => {
759
893
  }
760
894
  return { leaf, idx };
761
895
  };
762
- var findRemoveEnd = (state, leaf, idx, endKey, upperExclusive) => {
896
+ var findRemoveEnd = (state, leaf, endKey, upperExclusive) => {
763
897
  const count = leafEntryCount(leaf);
764
- let removeEnd = idx;
765
- while (removeEnd < count) {
766
- const e = leafEntryAt(leaf, removeEnd);
767
- const cmpEnd = state.compareKeys(e.key, endKey);
768
- if (upperExclusive ? cmpEnd >= 0 : cmpEnd > 0) break;
769
- removeEnd += 1;
898
+ const lastEntry = leafEntryAt(leaf, count - 1);
899
+ const cmpLast = state.compareKeys(lastEntry.key, endKey);
900
+ if (upperExclusive ? cmpLast < 0 : cmpLast <= 0) {
901
+ return count;
770
902
  }
771
- return removeEnd;
903
+ const endSeq = upperExclusive ? 0 : Number.MAX_SAFE_INTEGER;
904
+ const endBound = upperExclusive ? lowerBoundInLeaf(state, leaf, endKey, endSeq) : upperBoundInLeaf(state, leaf, endKey, endSeq);
905
+ return endBound < count ? endBound : count;
772
906
  };
773
907
  var spliceLeafAndRebalance = (state, leaf, idx, removeCount) => {
774
908
  if (state.entryKeys !== null) {
@@ -785,15 +919,14 @@ var spliceLeafAndRebalance = (state, leaf, idx, removeCount) => {
785
919
  updateMinKeyInAncestors(leaf);
786
920
  }
787
921
  const countAfterSplice = leafEntryCount(leaf);
922
+ const rebalThreshold = leafRebalanceThreshold(state);
788
923
  let safetyGuard = state.minLeafEntries + 4;
789
- while (safetyGuard > 0 && leaf !== state.root && leafEntryCount(leaf) < state.minLeafEntries) {
924
+ while (safetyGuard > 0 && leaf !== state.root && leafEntryCount(leaf) < rebalThreshold) {
790
925
  rebalanceAfterLeafRemoval(state, leaf);
791
- if (leaf.parent !== null && leaf.parent.children[leaf.indexInParent] !== leaf) break;
926
+ if (leaf.parent !== null && leaf.parent.children[leaf.indexInParent] !== leaf)
927
+ break;
792
928
  safetyGuard -= 1;
793
929
  }
794
- if (leafEmptied && leafEntryCount(leaf) > 0 && leaf.parent !== null && leaf.parent.children[leaf.indexInParent] === leaf) {
795
- updateMinKeyInAncestors(leaf);
796
- }
797
930
  return countAfterSplice;
798
931
  };
799
932
  var isLeafStillValid = (state, leaf) => leaf.parent === null ? leaf === state.root : leaf.parent.children[leaf.indexInParent] === leaf;
@@ -816,10 +949,15 @@ var deleteRangeEntries = (state, startKey, endKey, options) => {
816
949
  }
817
950
  if (idx >= leafEntryCount(leaf)) break;
818
951
  const count = leafEntryCount(leaf);
819
- const removeEnd = findRemoveEnd(state, leaf, idx, endKey, upperExclusive);
952
+ const removeEnd = findRemoveEnd(state, leaf, endKey, upperExclusive);
820
953
  const removeCount = removeEnd - idx;
821
954
  if (removeCount === 0) break;
822
- const countAfterSplice = spliceLeafAndRebalance(state, leaf, idx, removeCount);
955
+ const countAfterSplice = spliceLeafAndRebalance(
956
+ state,
957
+ leaf,
958
+ idx,
959
+ removeCount
960
+ );
823
961
  deleted += removeCount;
824
962
  if (removeEnd < count) break;
825
963
  if (!isLeafStillValid(state, leaf)) {
@@ -865,25 +1003,42 @@ var computeNextAutoScaleThreshold = (entryCount) => {
865
1003
  }
866
1004
  return Number.MAX_SAFE_INTEGER;
867
1005
  };
868
- var createInitialState = (config) => {
869
- if (typeof config.compareKeys !== "function") {
870
- throw new BTreeValidationError("compareKeys must be a function.");
871
- }
872
- const autoScale = config.autoScale === true;
1006
+ var resolveInitialCapacity = (config, autoScale) => {
873
1007
  if (autoScale && (config.maxLeafEntries !== void 0 || config.maxBranchChildren !== void 0)) {
874
- throw new BTreeValidationError("autoScale conflicts with explicit capacity.");
1008
+ throw new BTreeValidationError(
1009
+ "autoScale conflicts with explicit capacity."
1010
+ );
875
1011
  }
876
- let maxLeafEntries;
877
- let maxBranchChildren;
878
1012
  if (autoScale) {
879
1013
  const tier = computeAutoScaleTier(0);
880
- maxLeafEntries = tier.maxLeaf;
881
- maxBranchChildren = tier.maxBranch;
882
- } else {
883
- maxLeafEntries = normalizeNodeCapacity(config.maxLeafEntries, "maxLeafEntries", DEFAULT_MAX_LEAF_ENTRIES);
884
- maxBranchChildren = normalizeNodeCapacity(config.maxBranchChildren, "maxBranchChildren", DEFAULT_MAX_BRANCH_CHILDREN);
1014
+ return { maxLeafEntries: tier.maxLeaf, maxBranchChildren: tier.maxBranch };
885
1015
  }
1016
+ return {
1017
+ maxLeafEntries: normalizeNodeCapacity(
1018
+ config.maxLeafEntries,
1019
+ "maxLeafEntries",
1020
+ DEFAULT_MAX_LEAF_ENTRIES
1021
+ ),
1022
+ maxBranchChildren: normalizeNodeCapacity(
1023
+ config.maxBranchChildren,
1024
+ "maxBranchChildren",
1025
+ DEFAULT_MAX_BRANCH_CHILDREN
1026
+ )
1027
+ };
1028
+ };
1029
+ var createInitialState = (config) => {
1030
+ if (typeof config.compareKeys !== "function") {
1031
+ throw new BTreeValidationError("compareKeys must be a function.");
1032
+ }
1033
+ const autoScale = config.autoScale === true;
1034
+ const { maxLeafEntries, maxBranchChildren } = resolveInitialCapacity(
1035
+ config,
1036
+ autoScale
1037
+ );
886
1038
  const duplicateKeys = normalizeDuplicateKeyPolicy(config.duplicateKeys);
1039
+ const deleteRebalancePolicy = normalizeDeleteRebalancePolicy(
1040
+ config.deleteRebalancePolicy
1041
+ );
887
1042
  const emptyLeaf = createLeafNode([], null);
888
1043
  return {
889
1044
  compareKeys: config.compareKeys,
@@ -899,6 +1054,7 @@ var createInitialState = (config) => {
899
1054
  nextSequence: 0,
900
1055
  entryKeys: config.enableEntryIdLookup === true ? /* @__PURE__ */ new Map() : null,
901
1056
  autoScale,
1057
+ deleteRebalancePolicy,
902
1058
  _nextAutoScaleThreshold: autoScale ? computeNextAutoScaleThreshold(0) : Number.MAX_SAFE_INTEGER,
903
1059
  _cursor: { leaf: emptyLeaf, index: 0 }
904
1060
  };
@@ -914,7 +1070,9 @@ var maybeAutoScale = (state) => {
914
1070
  state.maxBranchChildren = maxBranch;
915
1071
  state.minBranchChildren = minOccupancy(maxBranch);
916
1072
  }
917
- state._nextAutoScaleThreshold = computeNextAutoScaleThreshold(state.entryCount);
1073
+ state._nextAutoScaleThreshold = computeNextAutoScaleThreshold(
1074
+ state.entryCount
1075
+ );
918
1076
  };
919
1077
  var applyAutoScaleCapacitySnapshot = (state, maxLeafEntries, maxBranchChildren) => {
920
1078
  if (!state.autoScale) {
@@ -941,6 +1099,14 @@ var applyAutoScaleCapacitySnapshot = (state, maxLeafEntries, maxBranchChildren)
941
1099
  state.minLeafEntries = minOccupancy(normalizedLeaf);
942
1100
  state.minBranchChildren = minOccupancy(normalizedBranch);
943
1101
  };
1102
+ var resetAutoScaleToTier0 = (state) => {
1103
+ const tier = computeAutoScaleTier(0);
1104
+ state.maxLeafEntries = tier.maxLeaf;
1105
+ state.maxBranchChildren = tier.maxBranch;
1106
+ state.minLeafEntries = minOccupancy(tier.maxLeaf);
1107
+ state.minBranchChildren = minOccupancy(tier.maxBranch);
1108
+ state._nextAutoScaleThreshold = computeNextAutoScaleThreshold(0);
1109
+ };
944
1110
 
945
1111
  // src/btree/bulkLoad.ts
946
1112
  var computeChunkBoundaries = (total, max, min) => {
@@ -961,7 +1127,11 @@ var computeChunkBoundaries = (total, max, min) => {
961
1127
  return boundaries;
962
1128
  };
963
1129
  var buildLeaves = (state, entries, ids, baseSequence) => {
964
- const boundaries = computeChunkBoundaries(entries.length, state.maxLeafEntries, state.minLeafEntries);
1130
+ const boundaries = computeChunkBoundaries(
1131
+ entries.length,
1132
+ state.maxLeafEntries,
1133
+ state.minLeafEntries
1134
+ );
965
1135
  const leaves = new Array(boundaries.length);
966
1136
  let chunkStart = 0;
967
1137
  for (let c = 0; c < boundaries.length; c += 1) {
@@ -969,7 +1139,11 @@ var buildLeaves = (state, entries, ids, baseSequence) => {
969
1139
  const chunk = new Array(chunkEnd - chunkStart);
970
1140
  for (let i = chunkStart; i < chunkEnd; i += 1) {
971
1141
  const seq = baseSequence + i;
972
- chunk[i - chunkStart] = { key: entries[i].key, entryId: seq, value: entries[i].value };
1142
+ chunk[i - chunkStart] = createEntry(
1143
+ entries[i].key,
1144
+ seq,
1145
+ entries[i].value
1146
+ );
973
1147
  ids[i] = seq;
974
1148
  if (state.entryKeys !== null) {
975
1149
  state.entryKeys.set(seq, entries[i].key);
@@ -1003,11 +1177,18 @@ var bulkLoadEntries = (state, entries) => {
1003
1177
  } else {
1004
1178
  let currentLevel = leaves;
1005
1179
  while (currentLevel.length > 1) {
1006
- const bounds = computeChunkBoundaries(currentLevel.length, state.maxBranchChildren, state.minBranchChildren);
1180
+ const bounds = computeChunkBoundaries(
1181
+ currentLevel.length,
1182
+ state.maxBranchChildren,
1183
+ state.minBranchChildren
1184
+ );
1007
1185
  const nextLevel = new Array(bounds.length);
1008
1186
  let start = 0;
1009
1187
  for (let b = 0; b < bounds.length; b += 1) {
1010
- nextLevel[b] = createBranchNode(currentLevel.slice(start, bounds[b]), null);
1188
+ nextLevel[b] = createBranchNode(
1189
+ currentLevel.slice(start, bounds[b]),
1190
+ null
1191
+ );
1011
1192
  start = bounds[b];
1012
1193
  }
1013
1194
  currentLevel = nextLevel;
@@ -1018,9 +1199,12 @@ var bulkLoadEntries = (state, entries) => {
1018
1199
  return ids;
1019
1200
  };
1020
1201
 
1021
- // src/btree/mutations.ts
1202
+ // src/btree/split.ts
1022
1203
  var insertChildAfter = (state, parent, existingChild, childToInsert) => {
1023
- const newChildMinKey = { key: void 0, sequence: 0 };
1204
+ const newChildMinKey = {
1205
+ key: void 0,
1206
+ sequence: 0
1207
+ };
1024
1208
  if (!writeMinKeyTo(childToInsert, newChildMinKey)) {
1025
1209
  throw new BTreeInvariantError("inserted child has no min key");
1026
1210
  }
@@ -1058,7 +1242,10 @@ var splitLeaf = (state, leaf) => {
1058
1242
  var splitBranch = (state, branch) => {
1059
1243
  branchCompact(branch);
1060
1244
  const splitAt = Math.ceil(branch.children.length / 2);
1061
- const sibling = createBranchNode(branch.children.splice(splitAt), branch.parent);
1245
+ const sibling = createBranchNode(
1246
+ branch.children.splice(splitAt),
1247
+ branch.parent
1248
+ );
1062
1249
  branch.keys.splice(splitAt);
1063
1250
  if (branch.parent === null) {
1064
1251
  state.root = createBranchNode([branch, sibling], null);
@@ -1066,42 +1253,125 @@ var splitBranch = (state, branch) => {
1066
1253
  }
1067
1254
  insertChildAfter(state, branch.parent, branch, sibling);
1068
1255
  };
1256
+
1257
+ // src/btree/entry-lookup.ts
1258
+ var findLeafEntryBySequence = (state, userKey, sequence) => {
1259
+ const targetLeaf = findLeafForKey(state, userKey, sequence);
1260
+ const index = lowerBoundInLeaf(state, targetLeaf, userKey, sequence);
1261
+ if (index >= leafEntryCount(targetLeaf)) return null;
1262
+ const entry = leafEntryAt(targetLeaf, index);
1263
+ if (entry.entryId !== sequence) return null;
1264
+ return { leaf: targetLeaf, index };
1265
+ };
1266
+ var peekEntryById = (state, entryId) => {
1267
+ if (state.entryKeys === null) {
1268
+ throw new BTreeInvariantError(
1269
+ "entryKeys lookup map is not enabled on this tree."
1270
+ );
1271
+ }
1272
+ const userKey = state.entryKeys.get(entryId);
1273
+ if (userKey === void 0) return null;
1274
+ const found = findLeafEntryBySequence(state, userKey, entryId);
1275
+ if (found === null) return null;
1276
+ const entry = leafEntryAt(found.leaf, found.index);
1277
+ return entry;
1278
+ };
1279
+ var updateEntryById = (state, entryId, newValue) => {
1280
+ if (state.entryKeys === null) {
1281
+ throw new BTreeInvariantError(
1282
+ "entryKeys lookup map is not enabled on this tree."
1283
+ );
1284
+ }
1285
+ const userKey = state.entryKeys.get(entryId);
1286
+ if (userKey === void 0) return null;
1287
+ const found = findLeafEntryBySequence(state, userKey, entryId);
1288
+ if (found === null) return null;
1289
+ const entry = leafEntryAt(found.leaf, found.index);
1290
+ const updated = createEntry(entry.key, entry.entryId, newValue);
1291
+ found.leaf.entries[found.leaf.entryOffset + found.index] = updated;
1292
+ return updated;
1293
+ };
1294
+ var removeEntryById = (state, entryId) => {
1295
+ if (state.entryKeys === null) {
1296
+ throw new BTreeInvariantError(
1297
+ "entryKeys lookup map is not enabled on this tree."
1298
+ );
1299
+ }
1300
+ const userKey = state.entryKeys.get(entryId);
1301
+ if (userKey === void 0) return null;
1302
+ const found = findLeafEntryBySequence(state, userKey, entryId);
1303
+ if (found === null) return null;
1304
+ const entry = leafEntryAt(found.leaf, found.index);
1305
+ leafRemoveAt(found.leaf, found.index);
1306
+ state.entryCount -= 1;
1307
+ state.entryKeys.delete(entryId);
1308
+ if (found.index === 0 && leafEntryCount(found.leaf) > 0 && found.leaf.parent !== null) {
1309
+ updateMinKeyInAncestors(found.leaf);
1310
+ }
1311
+ if (found.leaf !== state.root && leafEntryCount(found.leaf) < state.minLeafEntries) {
1312
+ rebalanceAfterLeafRemoval(state, found.leaf);
1313
+ }
1314
+ return entry;
1315
+ };
1316
+
1317
+ // src/btree/mutations.ts
1318
+ var findDuplicateEntry = (state, targetLeaf, key, insertAt) => {
1319
+ if (state.duplicateKeys === "allow") return null;
1320
+ if (insertAt > 0) {
1321
+ const candidate = leafEntryAt(targetLeaf, insertAt - 1);
1322
+ if (state.compareKeys(candidate.key, key) === 0) {
1323
+ return {
1324
+ leaf: targetLeaf,
1325
+ physIndex: targetLeaf.entryOffset + insertAt - 1,
1326
+ entry: candidate
1327
+ };
1328
+ }
1329
+ } else if (targetLeaf.prev !== null && leafEntryCount(targetLeaf.prev) > 0) {
1330
+ const prevLeaf = targetLeaf.prev;
1331
+ const prevCount = leafEntryCount(prevLeaf);
1332
+ const candidate = leafEntryAt(prevLeaf, prevCount - 1);
1333
+ if (state.compareKeys(candidate.key, key) === 0) {
1334
+ return {
1335
+ leaf: prevLeaf,
1336
+ physIndex: prevLeaf.entryOffset + prevCount - 1,
1337
+ entry: candidate
1338
+ };
1339
+ }
1340
+ }
1341
+ return null;
1342
+ };
1069
1343
  var putEntryIntoLeaf = (state, targetLeaf, key, value) => {
1070
1344
  const sequence = state.nextSequence;
1071
1345
  const insertAt = upperBoundInLeaf(state, targetLeaf, key, sequence);
1072
- if (state.duplicateKeys !== "allow") {
1073
- let existingEntry = null;
1074
- if (insertAt > 0) {
1075
- const candidate = leafEntryAt(targetLeaf, insertAt - 1);
1076
- if (state.compareKeys(candidate.key, key) === 0) {
1077
- existingEntry = candidate;
1078
- }
1079
- } else if (targetLeaf.prev !== null && leafEntryCount(targetLeaf.prev) > 0) {
1080
- const prevLeaf = targetLeaf.prev;
1081
- const candidate = leafEntryAt(prevLeaf, leafEntryCount(prevLeaf) - 1);
1082
- if (state.compareKeys(candidate.key, key) === 0) {
1083
- existingEntry = candidate;
1084
- }
1085
- }
1086
- if (existingEntry !== null) {
1087
- if (state.duplicateKeys === "reject") {
1088
- throw new BTreeValidationError("Duplicate key rejected.");
1089
- }
1090
- existingEntry.value = value;
1091
- return existingEntry.entryId;
1092
- }
1346
+ const dup = findDuplicateEntry(state, targetLeaf, key, insertAt);
1347
+ if (dup !== null) {
1348
+ if (state.duplicateKeys === "reject") {
1349
+ throw new BTreeValidationError("Duplicate key rejected.");
1350
+ }
1351
+ dup.leaf.entries[dup.physIndex] = createEntry(
1352
+ dup.entry.key,
1353
+ dup.entry.entryId,
1354
+ value
1355
+ );
1356
+ return dup.entry.entryId;
1093
1357
  }
1094
1358
  if (state.nextSequence >= Number.MAX_SAFE_INTEGER) {
1095
1359
  throw new BTreeValidationError("Sequence overflow.");
1096
1360
  }
1097
1361
  state.nextSequence += 1;
1098
- leafInsertAt(targetLeaf, insertAt, { key, entryId: sequence, value });
1362
+ leafInsertAt(
1363
+ targetLeaf,
1364
+ insertAt,
1365
+ createEntry(key, sequence, value)
1366
+ );
1099
1367
  state.entryCount += 1;
1100
1368
  if (state.entryKeys !== null) {
1101
1369
  state.entryKeys.set(sequence, key);
1102
1370
  }
1103
- if (insertAt === 0 && targetLeaf.parent !== null) updateMinKeyInAncestors(targetLeaf);
1104
- if (leafEntryCount(targetLeaf) > state.maxLeafEntries) splitLeaf(state, targetLeaf);
1371
+ if (insertAt === 0 && targetLeaf.parent !== null)
1372
+ updateMinKeyInAncestors(targetLeaf);
1373
+ if (leafEntryCount(targetLeaf) > state.maxLeafEntries)
1374
+ splitLeaf(state, targetLeaf);
1105
1375
  maybeAutoScale(state);
1106
1376
  return sequence;
1107
1377
  };
@@ -1112,7 +1382,8 @@ var putEntry = (state, key, value) => {
1112
1382
  var popFirstEntry = (state) => {
1113
1383
  if (state.entryCount === 0) return null;
1114
1384
  const firstEntry = leafShiftEntry(state.leftmostLeaf);
1115
- if (firstEntry === void 0) throw new BTreeInvariantError("leftmost leaf empty but count > 0");
1385
+ if (firstEntry === void 0)
1386
+ throw new BTreeInvariantError("leftmost leaf empty but count > 0");
1116
1387
  state.entryCount -= 1;
1117
1388
  if (state.entryKeys !== null) {
1118
1389
  state.entryKeys.delete(firstEntry.entryId);
@@ -1128,7 +1399,8 @@ var popFirstEntry = (state) => {
1128
1399
  var popLastEntry = (state) => {
1129
1400
  if (state.entryCount === 0) return null;
1130
1401
  const lastEntry = leafPopEntry(state.rightmostLeaf);
1131
- if (lastEntry === void 0) throw new BTreeInvariantError("rightmost leaf empty but count > 0");
1402
+ if (lastEntry === void 0)
1403
+ throw new BTreeInvariantError("rightmost leaf empty but count > 0");
1132
1404
  state.entryCount -= 1;
1133
1405
  if (state.entryKeys !== null) {
1134
1406
  state.entryKeys.delete(lastEntry.entryId);
@@ -1149,70 +1421,22 @@ var removeFirstMatchingEntry = (state, key) => {
1149
1421
  if (state.entryKeys !== null) {
1150
1422
  state.entryKeys.delete(targetEntry.entryId);
1151
1423
  }
1152
- if (removeAt === 0 && leafEntryCount(targetLeaf) > 0 && targetLeaf.parent !== null) updateMinKeyInAncestors(targetLeaf);
1424
+ if (removeAt === 0 && leafEntryCount(targetLeaf) > 0 && targetLeaf.parent !== null)
1425
+ updateMinKeyInAncestors(targetLeaf);
1153
1426
  if (targetLeaf !== state.root && leafEntryCount(targetLeaf) < state.minLeafEntries) {
1154
1427
  rebalanceAfterLeafRemoval(state, targetLeaf);
1155
1428
  }
1156
1429
  return targetEntry;
1157
1430
  };
1158
- var findLeafEntryBySequence = (state, userKey, sequence) => {
1159
- const targetLeaf = findLeafForKey(state, userKey, sequence);
1160
- const index = lowerBoundInLeaf(state, targetLeaf, userKey, sequence);
1161
- if (index >= leafEntryCount(targetLeaf)) return null;
1162
- const entry = leafEntryAt(targetLeaf, index);
1163
- if (entry.entryId !== sequence) return null;
1164
- return { leaf: targetLeaf, index };
1165
- };
1166
- var removeEntryById = (state, entryId) => {
1167
- if (state.entryKeys === null) {
1168
- throw new BTreeInvariantError("entryKeys lookup map is not enabled on this tree.");
1169
- }
1170
- const userKey = state.entryKeys.get(entryId);
1171
- if (userKey === void 0) return null;
1172
- const found = findLeafEntryBySequence(state, userKey, entryId);
1173
- if (found === null) return null;
1174
- const entry = leafEntryAt(found.leaf, found.index);
1175
- leafRemoveAt(found.leaf, found.index);
1176
- state.entryCount -= 1;
1177
- state.entryKeys.delete(entryId);
1178
- if (found.index === 0 && leafEntryCount(found.leaf) > 0 && found.leaf.parent !== null) {
1179
- updateMinKeyInAncestors(found.leaf);
1180
- }
1181
- if (found.leaf !== state.root && leafEntryCount(found.leaf) < state.minLeafEntries) {
1182
- rebalanceAfterLeafRemoval(state, found.leaf);
1183
- }
1184
- return entry;
1185
- };
1186
- var peekEntryById = (state, entryId) => {
1187
- if (state.entryKeys === null) {
1188
- throw new BTreeInvariantError("entryKeys lookup map is not enabled on this tree.");
1189
- }
1190
- const userKey = state.entryKeys.get(entryId);
1191
- if (userKey === void 0) return null;
1192
- const found = findLeafEntryBySequence(state, userKey, entryId);
1193
- if (found === null) return null;
1194
- const entry = leafEntryAt(found.leaf, found.index);
1195
- return entry;
1196
- };
1197
- var updateEntryById = (state, entryId, newValue) => {
1198
- if (state.entryKeys === null) {
1199
- throw new BTreeInvariantError("entryKeys lookup map is not enabled on this tree.");
1200
- }
1201
- const userKey = state.entryKeys.get(entryId);
1202
- if (userKey === void 0) return null;
1203
- const found = findLeafEntryBySequence(state, userKey, entryId);
1204
- if (found === null) return null;
1205
- const entry = leafEntryAt(found.leaf, found.index);
1206
- entry.value = newValue;
1207
- return entry;
1208
- };
1209
1431
  var putManyEntries = (state, entries) => {
1210
1432
  if (entries.length === 0) return [];
1211
1433
  const strictlyAscending = state.duplicateKeys !== "allow";
1212
1434
  for (let i = 1; i < entries.length; i += 1) {
1213
1435
  const cmp = state.compareKeys(entries[i - 1].key, entries[i].key);
1214
1436
  if (cmp > 0) {
1215
- throw new BTreeValidationError("putMany: entries not in ascending order.");
1437
+ throw new BTreeValidationError(
1438
+ "putMany: entries not in ascending order."
1439
+ );
1216
1440
  }
1217
1441
  if (strictlyAscending && cmp === 0) {
1218
1442
  throw new BTreeValidationError(
@@ -1225,7 +1449,12 @@ var putManyEntries = (state, entries) => {
1225
1449
  let hintLeaf = findLeafForKey(state, entries[0].key, state.nextSequence);
1226
1450
  for (let i = 0; i < entries.length; i += 1) {
1227
1451
  const entry = entries[i];
1228
- const targetLeaf = findLeafFromHint(state, hintLeaf, entry.key, state.nextSequence);
1452
+ const targetLeaf = findLeafFromHint(
1453
+ state,
1454
+ hintLeaf,
1455
+ entry.key,
1456
+ state.nextSequence
1457
+ );
1229
1458
  ids[i] = putEntryIntoLeaf(state, targetLeaf, entry.key, entry.value);
1230
1459
  hintLeaf = targetLeaf;
1231
1460
  }
@@ -1240,7 +1469,8 @@ var buildConfigFromState = (state) => {
1240
1469
  compareKeys: state.compareKeys,
1241
1470
  duplicateKeys: state.duplicateKeys,
1242
1471
  enableEntryIdLookup: state.entryKeys !== null,
1243
- autoScale: state.autoScale
1472
+ autoScale: state.autoScale,
1473
+ deleteRebalancePolicy: state.deleteRebalancePolicy
1244
1474
  };
1245
1475
  if (!state.autoScale) {
1246
1476
  config.maxLeafEntries = state.maxLeafEntries;
@@ -1260,17 +1490,17 @@ var serializeToJSON = (state) => {
1260
1490
  }
1261
1491
  leaf = leaf.next;
1262
1492
  }
1263
- return {
1264
- version: 1,
1265
- config: {
1266
- maxLeafEntries: state.maxLeafEntries,
1267
- maxBranchChildren: state.maxBranchChildren,
1268
- duplicateKeys: state.duplicateKeys,
1269
- enableEntryIdLookup: state.entryKeys !== null,
1270
- autoScale: state.autoScale
1271
- },
1272
- entries
1493
+ const config = {
1494
+ maxLeafEntries: state.maxLeafEntries,
1495
+ maxBranchChildren: state.maxBranchChildren,
1496
+ duplicateKeys: state.duplicateKeys,
1497
+ enableEntryIdLookup: state.entryKeys !== null,
1498
+ autoScale: state.autoScale
1273
1499
  };
1500
+ if (state.deleteRebalancePolicy !== "standard") {
1501
+ config.deleteRebalancePolicy = state.deleteRebalancePolicy;
1502
+ }
1503
+ return { version: 1, config, entries };
1274
1504
  };
1275
1505
  var MAX_SERIALIZED_ENTRIES = 1e6;
1276
1506
  var validateStructure = (json) => {
@@ -1333,13 +1563,28 @@ var validateBTreeJSON = (json) => {
1333
1563
  validateStructure(json);
1334
1564
  validateConfig(json.config);
1335
1565
  };
1566
+ var validateBTreeJSONSortOrder = (json, compareKeys) => {
1567
+ const strict = json.config.duplicateKeys !== "allow";
1568
+ for (let i = 1; i < json.entries.length; i += 1) {
1569
+ const cmp = compareKeys(json.entries[i - 1][0], json.entries[i][0]);
1570
+ if (cmp > 0) {
1571
+ throw new BTreeValidationError("fromJSON: entries not sorted.");
1572
+ }
1573
+ if (strict && cmp === 0) {
1574
+ throw new BTreeValidationError(
1575
+ 'fromJSON: duplicate keys require duplicateKeys "allow".'
1576
+ );
1577
+ }
1578
+ }
1579
+ };
1336
1580
  var buildConfigFromJSON = (json, compareKeys) => {
1337
1581
  const cfg = json.config;
1338
1582
  const config = {
1339
1583
  compareKeys,
1340
1584
  duplicateKeys: cfg.duplicateKeys,
1341
1585
  enableEntryIdLookup: cfg.enableEntryIdLookup,
1342
- autoScale: cfg.autoScale
1586
+ autoScale: cfg.autoScale,
1587
+ deleteRebalancePolicy: cfg.deleteRebalancePolicy
1343
1588
  };
1344
1589
  if (!cfg.autoScale) {
1345
1590
  config.maxLeafEntries = cfg.maxLeafEntries;
@@ -1348,6 +1593,44 @@ var buildConfigFromJSON = (json, compareKeys) => {
1348
1593
  return config;
1349
1594
  };
1350
1595
 
1596
+ // src/btree/traversal.ts
1597
+ var snapshotEntries = (state) => {
1598
+ const result = new Array(state.entryCount);
1599
+ let leaf = state.leftmostLeaf;
1600
+ let writeIdx = 0;
1601
+ while (leaf !== null) {
1602
+ const count = leafEntryCount(leaf);
1603
+ for (let i = 0; i < count; i += 1) {
1604
+ result[writeIdx++] = freezeEntry(leafEntryAt(leaf, i));
1605
+ }
1606
+ leaf = leaf.next;
1607
+ }
1608
+ return result;
1609
+ };
1610
+ var collectInternalEntries = (state) => {
1611
+ const result = new Array(state.entryCount);
1612
+ let leaf = state.leftmostLeaf;
1613
+ let writeIdx = 0;
1614
+ while (leaf !== null) {
1615
+ const count = leafEntryCount(leaf);
1616
+ for (let i = 0; i < count; i += 1) {
1617
+ result[writeIdx++] = leafEntryAt(leaf, i);
1618
+ }
1619
+ leaf = leaf.next;
1620
+ }
1621
+ return result;
1622
+ };
1623
+ var forEachEntry = (state, callback, thisArg) => {
1624
+ let leaf = state.leftmostLeaf;
1625
+ while (leaf !== null) {
1626
+ const count = leafEntryCount(leaf);
1627
+ for (let i = 0; i < count; i += 1) {
1628
+ callback.call(thisArg, freezeEntry(leafEntryAt(leaf, i)));
1629
+ }
1630
+ leaf = leaf.next;
1631
+ }
1632
+ };
1633
+
1351
1634
  // src/btree/integrity-helpers.ts
1352
1635
  var nodeMinKey = (node) => {
1353
1636
  if (isLeafNode(node)) {
@@ -1356,7 +1639,10 @@ var nodeMinKey = (node) => {
1356
1639
  return { key: e.key, sequence: e.entryId };
1357
1640
  }
1358
1641
  if (node.childOffset >= node.keys.length) return null;
1359
- return { key: node.keys[node.childOffset].key, sequence: node.keys[node.childOffset].sequence };
1642
+ return {
1643
+ key: node.keys[node.childOffset].key,
1644
+ sequence: node.keys[node.childOffset].sequence
1645
+ };
1360
1646
  };
1361
1647
  var compareNodeKeys = (comparator, leftKey, leftSeq, rightKey, rightSeq) => {
1362
1648
  const cmp = comparator(leftKey, rightKey);
@@ -1378,9 +1664,7 @@ var getNodeMaxKey = (node) => {
1378
1664
  };
1379
1665
  var validateComparatorResult = (result) => {
1380
1666
  if (!Number.isFinite(result)) {
1381
- throw new BTreeValidationError(
1382
- "compareKeys must return a finite number."
1383
- );
1667
+ throw new BTreeValidationError("compareKeys must return a finite number.");
1384
1668
  }
1385
1669
  return result;
1386
1670
  };
@@ -1440,6 +1724,34 @@ var assertTransitivityAsInvariant = (state, first, second, third) => {
1440
1724
  throw error;
1441
1725
  }
1442
1726
  };
1727
+ var validateAdjacentLeafOrdering = (state, previous, cursor) => {
1728
+ const prevMax = getNodeMaxKey(previous);
1729
+ const currentMin = nodeMinKey(cursor);
1730
+ if (prevMax === null || currentMin === null) {
1731
+ throw new BTreeInvariantError(
1732
+ "Non-empty tree leaf chain contains empty leaf node."
1733
+ );
1734
+ }
1735
+ if (compareNodeKeys(
1736
+ state.compareKeys,
1737
+ prevMax.key,
1738
+ prevMax.sequence,
1739
+ currentMin.key,
1740
+ currentMin.sequence
1741
+ ) > 0) {
1742
+ throw new BTreeInvariantError("Adjacent leaf key ranges are out of order.");
1743
+ }
1744
+ const prevCount = leafEntryCount(previous);
1745
+ const curCount = leafEntryCount(cursor);
1746
+ if (state.duplicateKeys !== "allow" && prevCount > 0 && curCount > 0 && state.compareKeys(
1747
+ leafEntryAt(previous, prevCount - 1).key,
1748
+ leafEntryAt(cursor, 0).key
1749
+ ) === 0) {
1750
+ throw new BTreeInvariantError(
1751
+ "Duplicate user key detected across adjacent leaves with uniqueness policy."
1752
+ );
1753
+ }
1754
+ };
1443
1755
  var validateLeafChainStep = (state, cursor, previous, visited) => {
1444
1756
  if (!isLeafNode(cursor)) {
1445
1757
  throw new BTreeInvariantError("Leaf linkage cursor reached non-leaf node.");
@@ -1451,22 +1763,7 @@ var validateLeafChainStep = (state, cursor, previous, visited) => {
1451
1763
  throw new BTreeInvariantError("Leaf prev pointer mismatch.");
1452
1764
  }
1453
1765
  if (previous !== null && isLeafNode(previous)) {
1454
- const prevMax = getNodeMaxKey(previous);
1455
- const currentMin = nodeMinKey(cursor);
1456
- if (prevMax === null || currentMin === null) {
1457
- throw new BTreeInvariantError("Non-empty tree leaf chain contains empty leaf node.");
1458
- }
1459
- if (compareNodeKeys(state.compareKeys, prevMax.key, prevMax.sequence, currentMin.key, currentMin.sequence) > 0) {
1460
- throw new BTreeInvariantError("Adjacent leaf key ranges are out of order.");
1461
- }
1462
- const prevCount = leafEntryCount(previous);
1463
- const curCount = leafEntryCount(cursor);
1464
- if (state.duplicateKeys !== "allow" && prevCount > 0 && curCount > 0 && state.compareKeys(
1465
- leafEntryAt(previous, prevCount - 1).key,
1466
- leafEntryAt(cursor, 0).key
1467
- ) === 0) {
1468
- throw new BTreeInvariantError("Duplicate user key detected across adjacent leaves with uniqueness policy.");
1469
- }
1766
+ validateAdjacentLeafOrdering(state, previous, cursor);
1470
1767
  }
1471
1768
  };
1472
1769
  var validateLeafLinks = (state, expectedLeafCount) => {
@@ -1475,7 +1772,9 @@ var validateLeafLinks = (state, expectedLeafCount) => {
1475
1772
  throw new BTreeInvariantError("Empty tree root must be a leaf node.");
1476
1773
  }
1477
1774
  if (state.leftmostLeaf !== state.root || state.rightmostLeaf !== state.root) {
1478
- throw new BTreeInvariantError("Empty tree leaf pointers must reference root leaf.");
1775
+ throw new BTreeInvariantError(
1776
+ "Empty tree leaf pointers must reference root leaf."
1777
+ );
1479
1778
  }
1480
1779
  return;
1481
1780
  }
@@ -1500,7 +1799,9 @@ var validateLeafLinks = (state, expectedLeafCount) => {
1500
1799
  throw new BTreeInvariantError("Rightmost leaf pointer mismatch.");
1501
1800
  }
1502
1801
  if (leafCount !== expectedLeafCount) {
1503
- throw new BTreeInvariantError("Leaf chain count mismatch with tree traversal count.");
1802
+ throw new BTreeInvariantError(
1803
+ "Leaf chain count mismatch with tree traversal count."
1804
+ );
1504
1805
  }
1505
1806
  };
1506
1807
 
@@ -1527,7 +1828,9 @@ var validateLeafNodeOrdering = (state, node) => {
1527
1828
  leafEntryAt(node, index - 1).key,
1528
1829
  leafEntryAt(node, index).key
1529
1830
  ) === 0) {
1530
- throw new BTreeInvariantError("Duplicate user key detected in tree with uniqueness policy.");
1831
+ throw new BTreeInvariantError(
1832
+ "Duplicate user key detected in tree with uniqueness policy."
1833
+ );
1531
1834
  }
1532
1835
  }
1533
1836
  }
@@ -1535,12 +1838,7 @@ var validateLeafNodeOrdering = (state, node) => {
1535
1838
  const first = leafEntryAt(node, index - 2);
1536
1839
  const second = leafEntryAt(node, index - 1);
1537
1840
  const third = leafEntryAt(node, index);
1538
- assertTransitivityAsInvariant(
1539
- state,
1540
- first.key,
1541
- second.key,
1542
- third.key
1543
- );
1841
+ assertTransitivityAsInvariant(state, first.key, second.key, third.key);
1544
1842
  }
1545
1843
  if (count > state.maxLeafEntries) {
1546
1844
  throw new BTreeInvariantError("Leaf node exceeds maximum occupancy.");
@@ -1549,9 +1847,14 @@ var validateLeafNodeOrdering = (state, node) => {
1549
1847
  var validateLeafNode = (state, node, depth) => {
1550
1848
  validateLeafNodeOrdering(state, node);
1551
1849
  const count = leafEntryCount(node);
1552
- const baseMinLeaf = state.autoScale ? Math.ceil(computeAutoScaleTier(0).maxLeaf / 2) : state.minLeafEntries;
1850
+ let baseMinLeaf = state.autoScale ? Math.ceil(computeAutoScaleTier(0).maxLeaf / 2) : state.minLeafEntries;
1851
+ if (state.deleteRebalancePolicy === "lazy") {
1852
+ baseMinLeaf = applyLazyThreshold(baseMinLeaf);
1853
+ }
1553
1854
  if (node !== state.root && count < baseMinLeaf) {
1554
- throw new BTreeInvariantError("Non-root leaf node violates minimum occupancy.");
1855
+ throw new BTreeInvariantError(
1856
+ "Non-root leaf node violates minimum occupancy."
1857
+ );
1555
1858
  }
1556
1859
  const first = count === 0 ? null : leafEntryAt(node, 0);
1557
1860
  const last = count === 0 ? null : leafEntryAt(node, count - 1);
@@ -1573,76 +1876,110 @@ var validateBranchStructure = (state, node) => {
1573
1876
  }
1574
1877
  const baseMinBranch = state.autoScale ? Math.ceil(computeAutoScaleTier(0).maxBranch / 2) : state.minBranchChildren;
1575
1878
  if (node !== state.root && liveCount < baseMinBranch) {
1576
- throw new BTreeInvariantError("Non-root branch node violates minimum occupancy.");
1879
+ throw new BTreeInvariantError(
1880
+ "Non-root branch node violates minimum occupancy."
1881
+ );
1577
1882
  }
1578
1883
  if (liveCount > state.maxBranchChildren) {
1579
1884
  throw new BTreeInvariantError("Branch node exceeds maximum occupancy.");
1580
1885
  }
1581
1886
  if (node.keys.length !== node.children.length) {
1582
- throw new BTreeInvariantError("Branch keys array length does not match children array length.");
1887
+ throw new BTreeInvariantError(
1888
+ "Branch keys array length does not match children array length."
1889
+ );
1583
1890
  }
1584
1891
  };
1585
1892
  var validateBranchChild = (state, node, childIndex, depth) => {
1586
1893
  const child = node.children[childIndex];
1587
1894
  if (child.parent !== node) {
1588
- throw new BTreeInvariantError("Child-parent pointer mismatch in branch node.");
1895
+ throw new BTreeInvariantError(
1896
+ "Child-parent pointer mismatch in branch node."
1897
+ );
1589
1898
  }
1590
1899
  if (child.indexInParent !== childIndex) {
1591
- throw new BTreeInvariantError("Child indexInParent does not match actual position in parent.");
1900
+ throw new BTreeInvariantError(
1901
+ "Child indexInParent does not match actual position in parent."
1902
+ );
1592
1903
  }
1593
1904
  const childValidation = validateNode(state, child, depth + 1);
1594
1905
  if (childValidation.minKey === null || childValidation.maxKey === null) {
1595
- throw new BTreeInvariantError("Branch child must not be empty in non-root branch tree.");
1906
+ throw new BTreeInvariantError(
1907
+ "Branch child must not be empty in non-root branch tree."
1908
+ );
1596
1909
  }
1597
1910
  const cachedMinKey = node.keys[childIndex];
1598
1911
  const actualMinKey = nodeMinKey(child);
1599
- if (actualMinKey === null || compareNodeKeys(state.compareKeys, cachedMinKey.key, cachedMinKey.sequence, actualMinKey.key, actualMinKey.sequence) !== 0) {
1600
- throw new BTreeInvariantError("Branch cached key does not match actual child minimum key.");
1912
+ if (actualMinKey === null || compareNodeKeys(
1913
+ state.compareKeys,
1914
+ cachedMinKey.key,
1915
+ cachedMinKey.sequence,
1916
+ actualMinKey.key,
1917
+ actualMinKey.sequence
1918
+ ) !== 0) {
1919
+ throw new BTreeInvariantError(
1920
+ "Branch cached key does not match actual child minimum key."
1921
+ );
1601
1922
  }
1602
1923
  return childValidation;
1603
1924
  };
1925
+ var mergeChildValidation = (state, accumulated, childValidation) => {
1926
+ if (accumulated.leafDepth !== null && childValidation.leafDepth !== null && childValidation.leafDepth !== accumulated.leafDepth) {
1927
+ throw new BTreeInvariantError("Leaf depth mismatch detected in tree.");
1928
+ }
1929
+ if (accumulated.leafDepth === null && childValidation.leafDepth !== null) {
1930
+ accumulated.leafDepth = childValidation.leafDepth;
1931
+ }
1932
+ if (accumulated.previousChildMax !== null && compareNodeKeys(
1933
+ state.compareKeys,
1934
+ accumulated.previousChildMax.key,
1935
+ accumulated.previousChildMax.sequence,
1936
+ childValidation.minKey.key,
1937
+ childValidation.minKey.sequence
1938
+ ) >= 0) {
1939
+ throw new BTreeInvariantError(
1940
+ "Branch child key ranges are not strictly ordered."
1941
+ );
1942
+ }
1943
+ if (accumulated.minKey === null) accumulated.minKey = childValidation.minKey;
1944
+ accumulated.maxKey = childValidation.maxKey;
1945
+ accumulated.previousChildMax = childValidation.maxKey;
1946
+ accumulated.leafCount += childValidation.leafCount;
1947
+ accumulated.branchCount += childValidation.branchCount;
1948
+ accumulated.entryCount += childValidation.entryCount;
1949
+ };
1604
1950
  var validateNode = (state, node, depth) => {
1605
1951
  if (isLeafNode(node)) {
1606
1952
  return validateLeafNode(state, node, depth);
1607
1953
  }
1608
1954
  validateBranchStructure(state, node);
1609
- let leafDepth = null;
1610
- let leafCount = 0;
1611
- let branchCount = 1;
1612
- let entryCount = 0;
1613
- let minKey = null;
1614
- let maxKey = null;
1615
- let previousChildMax = null;
1955
+ const acc = {
1956
+ leafDepth: null,
1957
+ leafCount: 0,
1958
+ branchCount: 1,
1959
+ entryCount: 0,
1960
+ minKey: null,
1961
+ maxKey: null,
1962
+ previousChildMax: null
1963
+ };
1616
1964
  for (let childIndex = node.childOffset; childIndex < node.children.length; childIndex += 1) {
1617
1965
  const childValidation = validateBranchChild(state, node, childIndex, depth);
1618
- if (leafDepth !== null && childValidation.leafDepth !== null && childValidation.leafDepth !== leafDepth) {
1619
- throw new BTreeInvariantError("Leaf depth mismatch detected in tree.");
1620
- }
1621
- if (leafDepth === null && childValidation.leafDepth !== null) {
1622
- leafDepth = childValidation.leafDepth;
1623
- }
1624
- if (previousChildMax !== null && compareNodeKeys(
1625
- state.compareKeys,
1626
- previousChildMax.key,
1627
- previousChildMax.sequence,
1628
- childValidation.minKey.key,
1629
- childValidation.minKey.sequence
1630
- ) >= 0) {
1631
- throw new BTreeInvariantError("Branch child key ranges are not strictly ordered.");
1632
- }
1633
- if (minKey === null) minKey = childValidation.minKey;
1634
- maxKey = childValidation.maxKey;
1635
- previousChildMax = childValidation.maxKey;
1636
- leafCount += childValidation.leafCount;
1637
- branchCount += childValidation.branchCount;
1638
- entryCount += childValidation.entryCount;
1966
+ mergeChildValidation(state, acc, childValidation);
1639
1967
  }
1640
- return { minKey, maxKey, leafDepth, leafCount, branchCount, entryCount };
1968
+ return {
1969
+ minKey: acc.minKey,
1970
+ maxKey: acc.maxKey,
1971
+ leafDepth: acc.leafDepth,
1972
+ leafCount: acc.leafCount,
1973
+ branchCount: acc.branchCount,
1974
+ entryCount: acc.entryCount
1975
+ };
1641
1976
  };
1642
1977
  var assertInvariants = (state) => {
1643
1978
  const validation = validateNode(state, state.root, 0);
1644
1979
  if (validation.entryCount !== state.entryCount) {
1645
- throw new BTreeInvariantError("Index entry count mismatch between tree traversal and tracked state.");
1980
+ throw new BTreeInvariantError(
1981
+ "Index entry count mismatch between tree traversal and tracked state."
1982
+ );
1646
1983
  }
1647
1984
  validateLeafLinks(state, validation.leafCount);
1648
1985
  };
@@ -1698,7 +2035,7 @@ var InMemoryBTree = class _InMemoryBTree {
1698
2035
  remove(key) {
1699
2036
  const entry = removeFirstMatchingEntry(this.state, key);
1700
2037
  if (entry === null) return null;
1701
- return toPublicEntry(entry);
2038
+ return freezeEntry(entry);
1702
2039
  }
1703
2040
  removeById(entryId) {
1704
2041
  if (this.state.entryKeys === null) {
@@ -1706,7 +2043,7 @@ var InMemoryBTree = class _InMemoryBTree {
1706
2043
  }
1707
2044
  const entry = removeEntryById(this.state, entryId);
1708
2045
  if (entry === null) return null;
1709
- return toPublicEntry(entry);
2046
+ return freezeEntry(entry);
1710
2047
  }
1711
2048
  peekById(entryId) {
1712
2049
  if (this.state.entryKeys === null) {
@@ -1714,7 +2051,7 @@ var InMemoryBTree = class _InMemoryBTree {
1714
2051
  }
1715
2052
  const entry = peekEntryById(this.state, entryId);
1716
2053
  if (entry === null) return null;
1717
- return toPublicEntry(entry);
2054
+ return freezeEntry(entry);
1718
2055
  }
1719
2056
  updateById(entryId, value) {
1720
2057
  if (this.state.entryKeys === null) {
@@ -1722,30 +2059,30 @@ var InMemoryBTree = class _InMemoryBTree {
1722
2059
  }
1723
2060
  const entry = updateEntryById(this.state, entryId, value);
1724
2061
  if (entry === null) return null;
1725
- return toPublicEntry(entry);
2062
+ return freezeEntry(entry);
1726
2063
  }
1727
2064
  popFirst() {
1728
2065
  const entry = popFirstEntry(this.state);
1729
2066
  if (entry === null) return null;
1730
- return toPublicEntry(entry);
2067
+ return freezeEntry(entry);
1731
2068
  }
1732
2069
  peekFirst() {
1733
2070
  if (this.state.entryCount === 0) {
1734
2071
  return null;
1735
2072
  }
1736
- return toPublicEntry(leafEntryAt(this.state.leftmostLeaf, 0));
2073
+ return freezeEntry(leafEntryAt(this.state.leftmostLeaf, 0));
1737
2074
  }
1738
2075
  peekLast() {
1739
2076
  if (this.state.entryCount === 0) {
1740
2077
  return null;
1741
2078
  }
1742
2079
  const leaf = this.state.rightmostLeaf;
1743
- return toPublicEntry(leafEntryAt(leaf, leafEntryCount(leaf) - 1));
2080
+ return freezeEntry(leafEntryAt(leaf, leafEntryCount(leaf) - 1));
1744
2081
  }
1745
2082
  popLast() {
1746
2083
  const entry = popLastEntry(this.state);
1747
2084
  if (entry === null) return null;
1748
- return toPublicEntry(entry);
2085
+ return freezeEntry(entry);
1749
2086
  }
1750
2087
  clear() {
1751
2088
  const emptyLeaf = createLeafNode([], null);
@@ -1755,16 +2092,9 @@ var InMemoryBTree = class _InMemoryBTree {
1755
2092
  this.state.entryCount = 0;
1756
2093
  this.state._cursor.leaf = emptyLeaf;
1757
2094
  this.state._cursor.index = 0;
1758
- if (this.state.entryKeys !== null) {
1759
- this.state.entryKeys.clear();
1760
- }
2095
+ this.state.entryKeys?.clear();
1761
2096
  if (this.state.autoScale) {
1762
- const tier = computeAutoScaleTier(0);
1763
- this.state.maxLeafEntries = tier.maxLeaf;
1764
- this.state.maxBranchChildren = tier.maxBranch;
1765
- this.state.minLeafEntries = minOccupancy(tier.maxLeaf);
1766
- this.state.minBranchChildren = minOccupancy(tier.maxBranch);
1767
- this.state._nextAutoScaleThreshold = computeNextAutoScaleThreshold(0);
2097
+ resetAutoScaleToTier0(this.state);
1768
2098
  }
1769
2099
  }
1770
2100
  get(key) {
@@ -1778,65 +2108,39 @@ var InMemoryBTree = class _InMemoryBTree {
1778
2108
  findFirst(key) {
1779
2109
  const found = findFirstMatchingUserKey(this.state, key);
1780
2110
  if (found === null) return null;
1781
- return toPublicEntry(leafEntryAt(found.leaf, found.index));
2111
+ return freezeEntry(leafEntryAt(found.leaf, found.index));
1782
2112
  }
1783
- /**
1784
- * Returns the last entry whose key matches `key`, or `null` if not found.
1785
- * Useful when `duplicateKeys` is `'allow'` and multiple entries share the same key.
1786
- */
1787
2113
  findLast(key) {
1788
2114
  const found = findLastMatchingUserKey(this.state, key);
1789
2115
  if (found === null) return null;
1790
- return toPublicEntry(leafEntryAt(found.leaf, found.index));
2116
+ return freezeEntry(leafEntryAt(found.leaf, found.index));
1791
2117
  }
1792
- /**
1793
- * Returns the smallest key in the tree that is strictly greater than `key`,
1794
- * or `null` if no such key exists.
1795
- */
1796
2118
  nextHigherKey(key) {
1797
2119
  return findNextHigherKey(this.state, key);
1798
2120
  }
1799
- /**
1800
- * Returns the largest key in the tree that is strictly less than `key`,
1801
- * or `null` if no such key exists.
1802
- */
1803
2121
  nextLowerKey(key) {
1804
2122
  return findNextLowerKey(this.state, key);
1805
2123
  }
1806
- /**
1807
- * Returns the entry for `key` if it exists; otherwise returns the entry with
1808
- * the largest key strictly less than `key`. Returns `null` when the tree is
1809
- * empty or every key is greater than `key`.
1810
- */
1811
2124
  getPairOrNextLower(key) {
1812
2125
  const found = findPairOrNextLower(this.state, key);
1813
2126
  if (found === null) return null;
1814
- return toPublicEntry(leafEntryAt(found.leaf, found.index));
2127
+ return freezeEntry(leafEntryAt(found.leaf, found.index));
1815
2128
  }
1816
- /**
1817
- * Returns the number of entries whose keys fall within [`startKey`, `endKey`].
1818
- * Pass `options` to make either bound exclusive.
1819
- */
1820
2129
  count(startKey, endKey, options) {
1821
2130
  return countRangeEntries(this.state, startKey, endKey, options);
1822
2131
  }
1823
- /**
1824
- * Deletes all entries whose keys fall within [`startKey`, `endKey`].
1825
- * Pass `options` to make either bound exclusive.
1826
- * @returns The number of entries deleted.
1827
- */
1828
2132
  deleteRange(startKey, endKey, options) {
1829
2133
  return deleteRangeEntries(this.state, startKey, endKey, options);
1830
2134
  }
1831
2135
  range(startKey, endKey, options) {
1832
- return rangeQueryEntries(this.state, startKey, endKey, options).map(toPublicEntry);
2136
+ return rangeQueryPublicEntries(this.state, startKey, endKey, options);
1833
2137
  }
1834
2138
  *entries() {
1835
2139
  let leaf = this.state.leftmostLeaf;
1836
2140
  while (leaf !== null) {
1837
2141
  const count = leafEntryCount(leaf);
1838
2142
  for (let i = 0; i < count; i += 1) {
1839
- yield toPublicEntry(leafEntryAt(leaf, i));
2143
+ yield freezeEntry(leafEntryAt(leaf, i));
1840
2144
  }
1841
2145
  leaf = leaf.next;
1842
2146
  }
@@ -1846,7 +2150,7 @@ var InMemoryBTree = class _InMemoryBTree {
1846
2150
  while (leaf !== null) {
1847
2151
  const count = leafEntryCount(leaf);
1848
2152
  for (let i = count - 1; i >= 0; i -= 1) {
1849
- yield toPublicEntry(leafEntryAt(leaf, i));
2153
+ yield freezeEntry(leafEntryAt(leaf, i));
1850
2154
  }
1851
2155
  leaf = leaf.prev;
1852
2156
  }
@@ -1864,55 +2168,26 @@ var InMemoryBTree = class _InMemoryBTree {
1864
2168
  [Symbol.iterator]() {
1865
2169
  return this.entries();
1866
2170
  }
2171
+ forEachRange(startKey, endKey, callback, options) {
2172
+ forEachRangeEntries(this.state, startKey, endKey, callback, options);
2173
+ }
1867
2174
  forEach(callback, thisArg) {
1868
- let leaf = this.state.leftmostLeaf;
1869
- while (leaf !== null) {
1870
- const count = leafEntryCount(leaf);
1871
- for (let i = 0; i < count; i += 1) {
1872
- callback.call(thisArg, toPublicEntry(leafEntryAt(leaf, i)));
1873
- }
1874
- leaf = leaf.next;
1875
- }
2175
+ forEachEntry(this.state, callback, thisArg);
1876
2176
  }
1877
2177
  snapshot() {
1878
- const result = new Array(this.state.entryCount);
1879
- let leaf = this.state.leftmostLeaf;
1880
- let writeIdx = 0;
1881
- while (leaf !== null) {
1882
- const count = leafEntryCount(leaf);
1883
- for (let i = 0; i < count; i += 1) {
1884
- result[writeIdx++] = toPublicEntry(leafEntryAt(leaf, i));
1885
- }
1886
- leaf = leaf.next;
1887
- }
1888
- return result;
2178
+ return snapshotEntries(this.state);
1889
2179
  }
1890
- /**
1891
- * Returns a structurally independent `InMemoryBTree` with identical
1892
- * configuration and entries. The tree structure (nodes, links, entry IDs)
1893
- * is fully independent, but stored key and value references are shared
1894
- * with the source tree.
1895
- * Note: `EntryId` values are reassigned in the clone — IDs from the source tree are not valid for the clone.
1896
- */
1897
2180
  clone() {
1898
- const cloned = new _InMemoryBTree(buildConfigFromState(this.state));
2181
+ const cloned = new _InMemoryBTree(
2182
+ buildConfigFromState(this.state)
2183
+ );
1899
2184
  applyAutoScaleCapacitySnapshot(
1900
2185
  cloned.state,
1901
2186
  this.state.maxLeafEntries,
1902
2187
  this.state.maxBranchChildren
1903
2188
  );
1904
2189
  if (this.state.entryCount > 0) {
1905
- const pairs = new Array(this.state.entryCount);
1906
- let leaf = this.state.leftmostLeaf;
1907
- let writeIdx = 0;
1908
- while (leaf !== null) {
1909
- const count = leafEntryCount(leaf);
1910
- for (let i = 0; i < count; i += 1) {
1911
- pairs[writeIdx++] = leafEntryAt(leaf, i);
1912
- }
1913
- leaf = leaf.next;
1914
- }
1915
- cloned.putMany(pairs);
2190
+ cloned.putMany(collectInternalEntries(this.state));
1916
2191
  }
1917
2192
  return cloned;
1918
2193
  }
@@ -1921,26 +2196,19 @@ var InMemoryBTree = class _InMemoryBTree {
1921
2196
  }
1922
2197
  static fromJSON(json, compareKeys) {
1923
2198
  validateBTreeJSON(json);
1924
- const strict = json.config.duplicateKeys !== "allow";
1925
- for (let i = 1; i < json.entries.length; i += 1) {
1926
- const cmp = compareKeys(json.entries[i - 1][0], json.entries[i][0]);
1927
- if (cmp > 0) {
1928
- throw new BTreeValidationError("fromJSON: entries not sorted.");
1929
- }
1930
- if (strict && cmp === 0) {
1931
- throw new BTreeValidationError(
1932
- 'fromJSON: duplicate keys require duplicateKeys "allow".'
1933
- );
1934
- }
1935
- }
1936
- const tree = new _InMemoryBTree(buildConfigFromJSON(json, compareKeys));
2199
+ validateBTreeJSONSortOrder(json, compareKeys);
2200
+ const tree = new _InMemoryBTree(
2201
+ buildConfigFromJSON(json, compareKeys)
2202
+ );
1937
2203
  applyAutoScaleCapacitySnapshot(
1938
2204
  tree.state,
1939
2205
  json.config.maxLeafEntries,
1940
2206
  json.config.maxBranchChildren
1941
2207
  );
1942
2208
  if (json.entries.length > 0) {
1943
- const pairs = new Array(json.entries.length);
2209
+ const pairs = new Array(
2210
+ json.entries.length
2211
+ );
1944
2212
  for (let i = 0; i < json.entries.length; i += 1) {
1945
2213
  pairs[i] = { key: json.entries[i][0], value: json.entries[i][1] };
1946
2214
  }