@frostpillar/frostpillar-btree 0.2.6 → 0.2.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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,68 @@ 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");
701
809
  leaf.entries.push(borrowed);
702
810
  writeMinKeyTo(rightSibling, parent.keys[leafIndex + 1]);
703
- return;
811
+ return true;
704
812
  }
705
813
  if (leftSibling !== null && leafEntryCount(leftSibling) > state.minLeafEntries) {
706
814
  const borrowed = leftSibling.entries.pop();
707
- if (borrowed === void 0) throw new BTreeInvariantError("left leaf borrow failed");
815
+ if (borrowed === void 0)
816
+ throw new BTreeInvariantError("left leaf borrow failed");
708
817
  leafUnshiftEntry(leaf, borrowed);
709
818
  parent.keys[leafIndex] = { key: borrowed.key, sequence: borrowed.entryId };
710
819
  updateMinKeyInAncestors(leaf);
711
- return;
820
+ return true;
712
821
  }
822
+ return false;
823
+ };
824
+ var mergeLeafWithSibling = (state, leaf, parent, leafIndex, leftSibling, rightSibling) => {
713
825
  if (leftSibling !== null) {
714
826
  mergeLeafEntries(leftSibling, leaf);
715
827
  detachLeafFromChain(state, leaf);
@@ -726,6 +838,24 @@ var rebalanceAfterLeafRemoval = (state, leaf) => {
726
838
  }
727
839
  throw new BTreeInvariantError("no leaf siblings to rebalance");
728
840
  };
841
+ var rebalanceAfterLeafRemoval = (state, leaf) => {
842
+ if (leaf === state.root) {
843
+ if (state.entryCount === 0) {
844
+ state.leftmostLeaf = leaf;
845
+ state.rightmostLeaf = leaf;
846
+ }
847
+ return;
848
+ }
849
+ if (leafEntryCount(leaf) >= leafRebalanceThreshold(state)) return;
850
+ const parent = leaf.parent;
851
+ if (parent === null)
852
+ throw new BTreeInvariantError("Leaf node has no parent during rebalance.");
853
+ const leafIndex = leaf.indexInParent;
854
+ const { left, right } = findLeafSiblings(parent, leafIndex);
855
+ if (tryBorrowFromLeafSibling(state, leaf, parent, leafIndex, left, right))
856
+ return;
857
+ mergeLeafWithSibling(state, leaf, parent, leafIndex, left, right);
858
+ };
729
859
 
730
860
  // src/btree/deleteRange.ts
731
861
  var navigateToStart = (state, startKey, lowerExclusive) => {
@@ -738,16 +868,16 @@ var navigateToStart = (state, startKey, lowerExclusive) => {
738
868
  }
739
869
  return { leaf, idx };
740
870
  };
741
- var findRemoveEnd = (state, leaf, idx, endKey, upperExclusive) => {
871
+ var findRemoveEnd = (state, leaf, endKey, upperExclusive) => {
742
872
  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;
873
+ const lastEntry = leafEntryAt(leaf, count - 1);
874
+ const cmpLast = state.compareKeys(lastEntry.key, endKey);
875
+ if (upperExclusive ? cmpLast < 0 : cmpLast <= 0) {
876
+ return count;
749
877
  }
750
- return removeEnd;
878
+ const endSeq = upperExclusive ? 0 : Number.MAX_SAFE_INTEGER;
879
+ const endBound = upperExclusive ? lowerBoundInLeaf(state, leaf, endKey, endSeq) : upperBoundInLeaf(state, leaf, endKey, endSeq);
880
+ return endBound < count ? endBound : count;
751
881
  };
752
882
  var spliceLeafAndRebalance = (state, leaf, idx, removeCount) => {
753
883
  if (state.entryKeys !== null) {
@@ -764,10 +894,12 @@ var spliceLeafAndRebalance = (state, leaf, idx, removeCount) => {
764
894
  updateMinKeyInAncestors(leaf);
765
895
  }
766
896
  const countAfterSplice = leafEntryCount(leaf);
897
+ const rebalThreshold = leafRebalanceThreshold(state);
767
898
  let safetyGuard = state.minLeafEntries + 4;
768
- while (safetyGuard > 0 && leaf !== state.root && leafEntryCount(leaf) < state.minLeafEntries) {
899
+ while (safetyGuard > 0 && leaf !== state.root && leafEntryCount(leaf) < rebalThreshold) {
769
900
  rebalanceAfterLeafRemoval(state, leaf);
770
- if (leaf.parent !== null && leaf.parent.children[leaf.indexInParent] !== leaf) break;
901
+ if (leaf.parent !== null && leaf.parent.children[leaf.indexInParent] !== leaf)
902
+ break;
771
903
  safetyGuard -= 1;
772
904
  }
773
905
  if (leafEmptied && leafEntryCount(leaf) > 0 && leaf.parent !== null && leaf.parent.children[leaf.indexInParent] === leaf) {
@@ -795,10 +927,15 @@ var deleteRangeEntries = (state, startKey, endKey, options) => {
795
927
  }
796
928
  if (idx >= leafEntryCount(leaf)) break;
797
929
  const count = leafEntryCount(leaf);
798
- const removeEnd = findRemoveEnd(state, leaf, idx, endKey, upperExclusive);
930
+ const removeEnd = findRemoveEnd(state, leaf, endKey, upperExclusive);
799
931
  const removeCount = removeEnd - idx;
800
932
  if (removeCount === 0) break;
801
- const countAfterSplice = spliceLeafAndRebalance(state, leaf, idx, removeCount);
933
+ const countAfterSplice = spliceLeafAndRebalance(
934
+ state,
935
+ leaf,
936
+ idx,
937
+ removeCount
938
+ );
802
939
  deleted += removeCount;
803
940
  if (removeEnd < count) break;
804
941
  if (!isLeafStillValid(state, leaf)) {
@@ -844,25 +981,42 @@ var computeNextAutoScaleThreshold = (entryCount) => {
844
981
  }
845
982
  return Number.MAX_SAFE_INTEGER;
846
983
  };
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;
984
+ var resolveInitialCapacity = (config, autoScale) => {
852
985
  if (autoScale && (config.maxLeafEntries !== void 0 || config.maxBranchChildren !== void 0)) {
853
- throw new BTreeValidationError("autoScale conflicts with explicit capacity.");
986
+ throw new BTreeValidationError(
987
+ "autoScale conflicts with explicit capacity."
988
+ );
854
989
  }
855
- let maxLeafEntries;
856
- let maxBranchChildren;
857
990
  if (autoScale) {
858
991
  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);
992
+ return { maxLeafEntries: tier.maxLeaf, maxBranchChildren: tier.maxBranch };
864
993
  }
994
+ return {
995
+ maxLeafEntries: normalizeNodeCapacity(
996
+ config.maxLeafEntries,
997
+ "maxLeafEntries",
998
+ DEFAULT_MAX_LEAF_ENTRIES
999
+ ),
1000
+ maxBranchChildren: normalizeNodeCapacity(
1001
+ config.maxBranchChildren,
1002
+ "maxBranchChildren",
1003
+ DEFAULT_MAX_BRANCH_CHILDREN
1004
+ )
1005
+ };
1006
+ };
1007
+ var createInitialState = (config) => {
1008
+ if (typeof config.compareKeys !== "function") {
1009
+ throw new BTreeValidationError("compareKeys must be a function.");
1010
+ }
1011
+ const autoScale = config.autoScale === true;
1012
+ const { maxLeafEntries, maxBranchChildren } = resolveInitialCapacity(
1013
+ config,
1014
+ autoScale
1015
+ );
865
1016
  const duplicateKeys = normalizeDuplicateKeyPolicy(config.duplicateKeys);
1017
+ const deleteRebalancePolicy = normalizeDeleteRebalancePolicy(
1018
+ config.deleteRebalancePolicy
1019
+ );
866
1020
  const emptyLeaf = createLeafNode([], null);
867
1021
  return {
868
1022
  compareKeys: config.compareKeys,
@@ -878,6 +1032,7 @@ var createInitialState = (config) => {
878
1032
  nextSequence: 0,
879
1033
  entryKeys: config.enableEntryIdLookup === true ? /* @__PURE__ */ new Map() : null,
880
1034
  autoScale,
1035
+ deleteRebalancePolicy,
881
1036
  _nextAutoScaleThreshold: autoScale ? computeNextAutoScaleThreshold(0) : Number.MAX_SAFE_INTEGER,
882
1037
  _cursor: { leaf: emptyLeaf, index: 0 }
883
1038
  };
@@ -893,7 +1048,9 @@ var maybeAutoScale = (state) => {
893
1048
  state.maxBranchChildren = maxBranch;
894
1049
  state.minBranchChildren = minOccupancy(maxBranch);
895
1050
  }
896
- state._nextAutoScaleThreshold = computeNextAutoScaleThreshold(state.entryCount);
1051
+ state._nextAutoScaleThreshold = computeNextAutoScaleThreshold(
1052
+ state.entryCount
1053
+ );
897
1054
  };
898
1055
  var applyAutoScaleCapacitySnapshot = (state, maxLeafEntries, maxBranchChildren) => {
899
1056
  if (!state.autoScale) {
@@ -920,6 +1077,14 @@ var applyAutoScaleCapacitySnapshot = (state, maxLeafEntries, maxBranchChildren)
920
1077
  state.minLeafEntries = minOccupancy(normalizedLeaf);
921
1078
  state.minBranchChildren = minOccupancy(normalizedBranch);
922
1079
  };
1080
+ var resetAutoScaleToTier0 = (state) => {
1081
+ const tier = computeAutoScaleTier(0);
1082
+ state.maxLeafEntries = tier.maxLeaf;
1083
+ state.maxBranchChildren = tier.maxBranch;
1084
+ state.minLeafEntries = minOccupancy(tier.maxLeaf);
1085
+ state.minBranchChildren = minOccupancy(tier.maxBranch);
1086
+ state._nextAutoScaleThreshold = computeNextAutoScaleThreshold(0);
1087
+ };
923
1088
 
924
1089
  // src/btree/bulkLoad.ts
925
1090
  var computeChunkBoundaries = (total, max, min) => {
@@ -940,7 +1105,11 @@ var computeChunkBoundaries = (total, max, min) => {
940
1105
  return boundaries;
941
1106
  };
942
1107
  var buildLeaves = (state, entries, ids, baseSequence) => {
943
- const boundaries = computeChunkBoundaries(entries.length, state.maxLeafEntries, state.minLeafEntries);
1108
+ const boundaries = computeChunkBoundaries(
1109
+ entries.length,
1110
+ state.maxLeafEntries,
1111
+ state.minLeafEntries
1112
+ );
944
1113
  const leaves = new Array(boundaries.length);
945
1114
  let chunkStart = 0;
946
1115
  for (let c = 0; c < boundaries.length; c += 1) {
@@ -948,7 +1117,11 @@ var buildLeaves = (state, entries, ids, baseSequence) => {
948
1117
  const chunk = new Array(chunkEnd - chunkStart);
949
1118
  for (let i = chunkStart; i < chunkEnd; i += 1) {
950
1119
  const seq = baseSequence + i;
951
- chunk[i - chunkStart] = { key: entries[i].key, entryId: seq, value: entries[i].value };
1120
+ chunk[i - chunkStart] = createEntry(
1121
+ entries[i].key,
1122
+ seq,
1123
+ entries[i].value
1124
+ );
952
1125
  ids[i] = seq;
953
1126
  if (state.entryKeys !== null) {
954
1127
  state.entryKeys.set(seq, entries[i].key);
@@ -982,11 +1155,18 @@ var bulkLoadEntries = (state, entries) => {
982
1155
  } else {
983
1156
  let currentLevel = leaves;
984
1157
  while (currentLevel.length > 1) {
985
- const bounds = computeChunkBoundaries(currentLevel.length, state.maxBranchChildren, state.minBranchChildren);
1158
+ const bounds = computeChunkBoundaries(
1159
+ currentLevel.length,
1160
+ state.maxBranchChildren,
1161
+ state.minBranchChildren
1162
+ );
986
1163
  const nextLevel = new Array(bounds.length);
987
1164
  let start = 0;
988
1165
  for (let b = 0; b < bounds.length; b += 1) {
989
- nextLevel[b] = createBranchNode(currentLevel.slice(start, bounds[b]), null);
1166
+ nextLevel[b] = createBranchNode(
1167
+ currentLevel.slice(start, bounds[b]),
1168
+ null
1169
+ );
990
1170
  start = bounds[b];
991
1171
  }
992
1172
  currentLevel = nextLevel;
@@ -997,9 +1177,12 @@ var bulkLoadEntries = (state, entries) => {
997
1177
  return ids;
998
1178
  };
999
1179
 
1000
- // src/btree/mutations.ts
1180
+ // src/btree/split.ts
1001
1181
  var insertChildAfter = (state, parent, existingChild, childToInsert) => {
1002
- const newChildMinKey = { key: void 0, sequence: 0 };
1182
+ const newChildMinKey = {
1183
+ key: void 0,
1184
+ sequence: 0
1185
+ };
1003
1186
  if (!writeMinKeyTo(childToInsert, newChildMinKey)) {
1004
1187
  throw new BTreeInvariantError("inserted child has no min key");
1005
1188
  }
@@ -1037,7 +1220,10 @@ var splitLeaf = (state, leaf) => {
1037
1220
  var splitBranch = (state, branch) => {
1038
1221
  branchCompact(branch);
1039
1222
  const splitAt = Math.ceil(branch.children.length / 2);
1040
- const sibling = createBranchNode(branch.children.splice(splitAt), branch.parent);
1223
+ const sibling = createBranchNode(
1224
+ branch.children.splice(splitAt),
1225
+ branch.parent
1226
+ );
1041
1227
  branch.keys.splice(splitAt);
1042
1228
  if (branch.parent === null) {
1043
1229
  state.root = createBranchNode([branch, sibling], null);
@@ -1045,42 +1231,125 @@ var splitBranch = (state, branch) => {
1045
1231
  }
1046
1232
  insertChildAfter(state, branch.parent, branch, sibling);
1047
1233
  };
1234
+
1235
+ // src/btree/entry-lookup.ts
1236
+ var findLeafEntryBySequence = (state, userKey, sequence) => {
1237
+ const targetLeaf = findLeafForKey(state, userKey, sequence);
1238
+ const index = lowerBoundInLeaf(state, targetLeaf, userKey, sequence);
1239
+ if (index >= leafEntryCount(targetLeaf)) return null;
1240
+ const entry = leafEntryAt(targetLeaf, index);
1241
+ if (entry.entryId !== sequence) return null;
1242
+ return { leaf: targetLeaf, index };
1243
+ };
1244
+ var peekEntryById = (state, entryId) => {
1245
+ if (state.entryKeys === null) {
1246
+ throw new BTreeInvariantError(
1247
+ "entryKeys lookup map is not enabled on this tree."
1248
+ );
1249
+ }
1250
+ const userKey = state.entryKeys.get(entryId);
1251
+ if (userKey === void 0) return null;
1252
+ const found = findLeafEntryBySequence(state, userKey, entryId);
1253
+ if (found === null) return null;
1254
+ const entry = leafEntryAt(found.leaf, found.index);
1255
+ return entry;
1256
+ };
1257
+ var updateEntryById = (state, entryId, newValue) => {
1258
+ if (state.entryKeys === null) {
1259
+ throw new BTreeInvariantError(
1260
+ "entryKeys lookup map is not enabled on this tree."
1261
+ );
1262
+ }
1263
+ const userKey = state.entryKeys.get(entryId);
1264
+ if (userKey === void 0) return null;
1265
+ const found = findLeafEntryBySequence(state, userKey, entryId);
1266
+ if (found === null) return null;
1267
+ const entry = leafEntryAt(found.leaf, found.index);
1268
+ const updated = createEntry(entry.key, entry.entryId, newValue);
1269
+ found.leaf.entries[found.leaf.entryOffset + found.index] = updated;
1270
+ return updated;
1271
+ };
1272
+ var removeEntryById = (state, entryId) => {
1273
+ if (state.entryKeys === null) {
1274
+ throw new BTreeInvariantError(
1275
+ "entryKeys lookup map is not enabled on this tree."
1276
+ );
1277
+ }
1278
+ const userKey = state.entryKeys.get(entryId);
1279
+ if (userKey === void 0) return null;
1280
+ const found = findLeafEntryBySequence(state, userKey, entryId);
1281
+ if (found === null) return null;
1282
+ const entry = leafEntryAt(found.leaf, found.index);
1283
+ leafRemoveAt(found.leaf, found.index);
1284
+ state.entryCount -= 1;
1285
+ state.entryKeys.delete(entryId);
1286
+ if (found.index === 0 && leafEntryCount(found.leaf) > 0 && found.leaf.parent !== null) {
1287
+ updateMinKeyInAncestors(found.leaf);
1288
+ }
1289
+ if (found.leaf !== state.root && leafEntryCount(found.leaf) < state.minLeafEntries) {
1290
+ rebalanceAfterLeafRemoval(state, found.leaf);
1291
+ }
1292
+ return entry;
1293
+ };
1294
+
1295
+ // src/btree/mutations.ts
1296
+ var findDuplicateEntry = (state, targetLeaf, key, insertAt) => {
1297
+ if (state.duplicateKeys === "allow") return null;
1298
+ if (insertAt > 0) {
1299
+ const candidate = leafEntryAt(targetLeaf, insertAt - 1);
1300
+ if (state.compareKeys(candidate.key, key) === 0) {
1301
+ return {
1302
+ leaf: targetLeaf,
1303
+ physIndex: targetLeaf.entryOffset + insertAt - 1,
1304
+ entry: candidate
1305
+ };
1306
+ }
1307
+ } else if (targetLeaf.prev !== null && leafEntryCount(targetLeaf.prev) > 0) {
1308
+ const prevLeaf = targetLeaf.prev;
1309
+ const prevCount = leafEntryCount(prevLeaf);
1310
+ const candidate = leafEntryAt(prevLeaf, prevCount - 1);
1311
+ if (state.compareKeys(candidate.key, key) === 0) {
1312
+ return {
1313
+ leaf: prevLeaf,
1314
+ physIndex: prevLeaf.entryOffset + prevCount - 1,
1315
+ entry: candidate
1316
+ };
1317
+ }
1318
+ }
1319
+ return null;
1320
+ };
1048
1321
  var putEntryIntoLeaf = (state, targetLeaf, key, value) => {
1049
1322
  const sequence = state.nextSequence;
1050
1323
  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
- }
1324
+ const dup = findDuplicateEntry(state, targetLeaf, key, insertAt);
1325
+ if (dup !== null) {
1326
+ if (state.duplicateKeys === "reject") {
1327
+ throw new BTreeValidationError("Duplicate key rejected.");
1328
+ }
1329
+ dup.leaf.entries[dup.physIndex] = createEntry(
1330
+ dup.entry.key,
1331
+ dup.entry.entryId,
1332
+ value
1333
+ );
1334
+ return dup.entry.entryId;
1072
1335
  }
1073
1336
  if (state.nextSequence >= Number.MAX_SAFE_INTEGER) {
1074
1337
  throw new BTreeValidationError("Sequence overflow.");
1075
1338
  }
1076
1339
  state.nextSequence += 1;
1077
- leafInsertAt(targetLeaf, insertAt, { key, entryId: sequence, value });
1340
+ leafInsertAt(
1341
+ targetLeaf,
1342
+ insertAt,
1343
+ createEntry(key, sequence, value)
1344
+ );
1078
1345
  state.entryCount += 1;
1079
1346
  if (state.entryKeys !== null) {
1080
1347
  state.entryKeys.set(sequence, key);
1081
1348
  }
1082
- if (insertAt === 0 && targetLeaf.parent !== null) updateMinKeyInAncestors(targetLeaf);
1083
- if (leafEntryCount(targetLeaf) > state.maxLeafEntries) splitLeaf(state, targetLeaf);
1349
+ if (insertAt === 0 && targetLeaf.parent !== null)
1350
+ updateMinKeyInAncestors(targetLeaf);
1351
+ if (leafEntryCount(targetLeaf) > state.maxLeafEntries)
1352
+ splitLeaf(state, targetLeaf);
1084
1353
  maybeAutoScale(state);
1085
1354
  return sequence;
1086
1355
  };
@@ -1091,7 +1360,8 @@ var putEntry = (state, key, value) => {
1091
1360
  var popFirstEntry = (state) => {
1092
1361
  if (state.entryCount === 0) return null;
1093
1362
  const firstEntry = leafShiftEntry(state.leftmostLeaf);
1094
- if (firstEntry === void 0) throw new BTreeInvariantError("leftmost leaf empty but count > 0");
1363
+ if (firstEntry === void 0)
1364
+ throw new BTreeInvariantError("leftmost leaf empty but count > 0");
1095
1365
  state.entryCount -= 1;
1096
1366
  if (state.entryKeys !== null) {
1097
1367
  state.entryKeys.delete(firstEntry.entryId);
@@ -1107,7 +1377,8 @@ var popFirstEntry = (state) => {
1107
1377
  var popLastEntry = (state) => {
1108
1378
  if (state.entryCount === 0) return null;
1109
1379
  const lastEntry = leafPopEntry(state.rightmostLeaf);
1110
- if (lastEntry === void 0) throw new BTreeInvariantError("rightmost leaf empty but count > 0");
1380
+ if (lastEntry === void 0)
1381
+ throw new BTreeInvariantError("rightmost leaf empty but count > 0");
1111
1382
  state.entryCount -= 1;
1112
1383
  if (state.entryKeys !== null) {
1113
1384
  state.entryKeys.delete(lastEntry.entryId);
@@ -1128,70 +1399,22 @@ var removeFirstMatchingEntry = (state, key) => {
1128
1399
  if (state.entryKeys !== null) {
1129
1400
  state.entryKeys.delete(targetEntry.entryId);
1130
1401
  }
1131
- if (removeAt === 0 && leafEntryCount(targetLeaf) > 0 && targetLeaf.parent !== null) updateMinKeyInAncestors(targetLeaf);
1402
+ if (removeAt === 0 && leafEntryCount(targetLeaf) > 0 && targetLeaf.parent !== null)
1403
+ updateMinKeyInAncestors(targetLeaf);
1132
1404
  if (targetLeaf !== state.root && leafEntryCount(targetLeaf) < state.minLeafEntries) {
1133
1405
  rebalanceAfterLeafRemoval(state, targetLeaf);
1134
1406
  }
1135
1407
  return targetEntry;
1136
1408
  };
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
1409
  var putManyEntries = (state, entries) => {
1189
1410
  if (entries.length === 0) return [];
1190
1411
  const strictlyAscending = state.duplicateKeys !== "allow";
1191
1412
  for (let i = 1; i < entries.length; i += 1) {
1192
1413
  const cmp = state.compareKeys(entries[i - 1].key, entries[i].key);
1193
1414
  if (cmp > 0) {
1194
- throw new BTreeValidationError("putMany: entries not in ascending order.");
1415
+ throw new BTreeValidationError(
1416
+ "putMany: entries not in ascending order."
1417
+ );
1195
1418
  }
1196
1419
  if (strictlyAscending && cmp === 0) {
1197
1420
  throw new BTreeValidationError(
@@ -1204,7 +1427,12 @@ var putManyEntries = (state, entries) => {
1204
1427
  let hintLeaf = findLeafForKey(state, entries[0].key, state.nextSequence);
1205
1428
  for (let i = 0; i < entries.length; i += 1) {
1206
1429
  const entry = entries[i];
1207
- const targetLeaf = findLeafFromHint(state, hintLeaf, entry.key, state.nextSequence);
1430
+ const targetLeaf = findLeafFromHint(
1431
+ state,
1432
+ hintLeaf,
1433
+ entry.key,
1434
+ state.nextSequence
1435
+ );
1208
1436
  ids[i] = putEntryIntoLeaf(state, targetLeaf, entry.key, entry.value);
1209
1437
  hintLeaf = targetLeaf;
1210
1438
  }
@@ -1219,7 +1447,8 @@ var buildConfigFromState = (state) => {
1219
1447
  compareKeys: state.compareKeys,
1220
1448
  duplicateKeys: state.duplicateKeys,
1221
1449
  enableEntryIdLookup: state.entryKeys !== null,
1222
- autoScale: state.autoScale
1450
+ autoScale: state.autoScale,
1451
+ deleteRebalancePolicy: state.deleteRebalancePolicy
1223
1452
  };
1224
1453
  if (!state.autoScale) {
1225
1454
  config.maxLeafEntries = state.maxLeafEntries;
@@ -1239,17 +1468,17 @@ var serializeToJSON = (state) => {
1239
1468
  }
1240
1469
  leaf = leaf.next;
1241
1470
  }
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
1471
+ const config = {
1472
+ maxLeafEntries: state.maxLeafEntries,
1473
+ maxBranchChildren: state.maxBranchChildren,
1474
+ duplicateKeys: state.duplicateKeys,
1475
+ enableEntryIdLookup: state.entryKeys !== null,
1476
+ autoScale: state.autoScale
1252
1477
  };
1478
+ if (state.deleteRebalancePolicy !== "standard") {
1479
+ config.deleteRebalancePolicy = state.deleteRebalancePolicy;
1480
+ }
1481
+ return { version: 1, config, entries };
1253
1482
  };
1254
1483
  var MAX_SERIALIZED_ENTRIES = 1e6;
1255
1484
  var validateStructure = (json) => {
@@ -1312,13 +1541,28 @@ var validateBTreeJSON = (json) => {
1312
1541
  validateStructure(json);
1313
1542
  validateConfig(json.config);
1314
1543
  };
1544
+ var validateBTreeJSONSortOrder = (json, compareKeys) => {
1545
+ const strict = json.config.duplicateKeys !== "allow";
1546
+ for (let i = 1; i < json.entries.length; i += 1) {
1547
+ const cmp = compareKeys(json.entries[i - 1][0], json.entries[i][0]);
1548
+ if (cmp > 0) {
1549
+ throw new BTreeValidationError("fromJSON: entries not sorted.");
1550
+ }
1551
+ if (strict && cmp === 0) {
1552
+ throw new BTreeValidationError(
1553
+ 'fromJSON: duplicate keys require duplicateKeys "allow".'
1554
+ );
1555
+ }
1556
+ }
1557
+ };
1315
1558
  var buildConfigFromJSON = (json, compareKeys) => {
1316
1559
  const cfg = json.config;
1317
1560
  const config = {
1318
1561
  compareKeys,
1319
1562
  duplicateKeys: cfg.duplicateKeys,
1320
1563
  enableEntryIdLookup: cfg.enableEntryIdLookup,
1321
- autoScale: cfg.autoScale
1564
+ autoScale: cfg.autoScale,
1565
+ deleteRebalancePolicy: cfg.deleteRebalancePolicy
1322
1566
  };
1323
1567
  if (!cfg.autoScale) {
1324
1568
  config.maxLeafEntries = cfg.maxLeafEntries;
@@ -1327,6 +1571,44 @@ var buildConfigFromJSON = (json, compareKeys) => {
1327
1571
  return config;
1328
1572
  };
1329
1573
 
1574
+ // src/btree/traversal.ts
1575
+ var snapshotEntries = (state) => {
1576
+ const result = new Array(state.entryCount);
1577
+ let leaf = state.leftmostLeaf;
1578
+ let writeIdx = 0;
1579
+ while (leaf !== null) {
1580
+ const count = leafEntryCount(leaf);
1581
+ for (let i = 0; i < count; i += 1) {
1582
+ result[writeIdx++] = freezeEntry(leafEntryAt(leaf, i));
1583
+ }
1584
+ leaf = leaf.next;
1585
+ }
1586
+ return result;
1587
+ };
1588
+ var collectInternalEntries = (state) => {
1589
+ const result = new Array(state.entryCount);
1590
+ let leaf = state.leftmostLeaf;
1591
+ let writeIdx = 0;
1592
+ while (leaf !== null) {
1593
+ const count = leafEntryCount(leaf);
1594
+ for (let i = 0; i < count; i += 1) {
1595
+ result[writeIdx++] = leafEntryAt(leaf, i);
1596
+ }
1597
+ leaf = leaf.next;
1598
+ }
1599
+ return result;
1600
+ };
1601
+ var forEachEntry = (state, callback, thisArg) => {
1602
+ let leaf = state.leftmostLeaf;
1603
+ while (leaf !== null) {
1604
+ const count = leafEntryCount(leaf);
1605
+ for (let i = 0; i < count; i += 1) {
1606
+ callback.call(thisArg, freezeEntry(leafEntryAt(leaf, i)));
1607
+ }
1608
+ leaf = leaf.next;
1609
+ }
1610
+ };
1611
+
1330
1612
  // src/btree/integrity-helpers.ts
1331
1613
  var nodeMinKey = (node) => {
1332
1614
  if (isLeafNode(node)) {
@@ -1335,7 +1617,10 @@ var nodeMinKey = (node) => {
1335
1617
  return { key: e.key, sequence: e.entryId };
1336
1618
  }
1337
1619
  if (node.childOffset >= node.keys.length) return null;
1338
- return { key: node.keys[node.childOffset].key, sequence: node.keys[node.childOffset].sequence };
1620
+ return {
1621
+ key: node.keys[node.childOffset].key,
1622
+ sequence: node.keys[node.childOffset].sequence
1623
+ };
1339
1624
  };
1340
1625
  var compareNodeKeys = (comparator, leftKey, leftSeq, rightKey, rightSeq) => {
1341
1626
  const cmp = comparator(leftKey, rightKey);
@@ -1357,9 +1642,7 @@ var getNodeMaxKey = (node) => {
1357
1642
  };
1358
1643
  var validateComparatorResult = (result) => {
1359
1644
  if (!Number.isFinite(result)) {
1360
- throw new BTreeValidationError(
1361
- "compareKeys must return a finite number."
1362
- );
1645
+ throw new BTreeValidationError("compareKeys must return a finite number.");
1363
1646
  }
1364
1647
  return result;
1365
1648
  };
@@ -1419,6 +1702,34 @@ var assertTransitivityAsInvariant = (state, first, second, third) => {
1419
1702
  throw error;
1420
1703
  }
1421
1704
  };
1705
+ var validateAdjacentLeafOrdering = (state, previous, cursor) => {
1706
+ const prevMax = getNodeMaxKey(previous);
1707
+ const currentMin = nodeMinKey(cursor);
1708
+ if (prevMax === null || currentMin === null) {
1709
+ throw new BTreeInvariantError(
1710
+ "Non-empty tree leaf chain contains empty leaf node."
1711
+ );
1712
+ }
1713
+ if (compareNodeKeys(
1714
+ state.compareKeys,
1715
+ prevMax.key,
1716
+ prevMax.sequence,
1717
+ currentMin.key,
1718
+ currentMin.sequence
1719
+ ) > 0) {
1720
+ throw new BTreeInvariantError("Adjacent leaf key ranges are out of order.");
1721
+ }
1722
+ const prevCount = leafEntryCount(previous);
1723
+ const curCount = leafEntryCount(cursor);
1724
+ if (state.duplicateKeys !== "allow" && prevCount > 0 && curCount > 0 && state.compareKeys(
1725
+ leafEntryAt(previous, prevCount - 1).key,
1726
+ leafEntryAt(cursor, 0).key
1727
+ ) === 0) {
1728
+ throw new BTreeInvariantError(
1729
+ "Duplicate user key detected across adjacent leaves with uniqueness policy."
1730
+ );
1731
+ }
1732
+ };
1422
1733
  var validateLeafChainStep = (state, cursor, previous, visited) => {
1423
1734
  if (!isLeafNode(cursor)) {
1424
1735
  throw new BTreeInvariantError("Leaf linkage cursor reached non-leaf node.");
@@ -1430,22 +1741,7 @@ var validateLeafChainStep = (state, cursor, previous, visited) => {
1430
1741
  throw new BTreeInvariantError("Leaf prev pointer mismatch.");
1431
1742
  }
1432
1743
  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
- }
1744
+ validateAdjacentLeafOrdering(state, previous, cursor);
1449
1745
  }
1450
1746
  };
1451
1747
  var validateLeafLinks = (state, expectedLeafCount) => {
@@ -1454,7 +1750,9 @@ var validateLeafLinks = (state, expectedLeafCount) => {
1454
1750
  throw new BTreeInvariantError("Empty tree root must be a leaf node.");
1455
1751
  }
1456
1752
  if (state.leftmostLeaf !== state.root || state.rightmostLeaf !== state.root) {
1457
- throw new BTreeInvariantError("Empty tree leaf pointers must reference root leaf.");
1753
+ throw new BTreeInvariantError(
1754
+ "Empty tree leaf pointers must reference root leaf."
1755
+ );
1458
1756
  }
1459
1757
  return;
1460
1758
  }
@@ -1479,7 +1777,9 @@ var validateLeafLinks = (state, expectedLeafCount) => {
1479
1777
  throw new BTreeInvariantError("Rightmost leaf pointer mismatch.");
1480
1778
  }
1481
1779
  if (leafCount !== expectedLeafCount) {
1482
- throw new BTreeInvariantError("Leaf chain count mismatch with tree traversal count.");
1780
+ throw new BTreeInvariantError(
1781
+ "Leaf chain count mismatch with tree traversal count."
1782
+ );
1483
1783
  }
1484
1784
  };
1485
1785
 
@@ -1506,7 +1806,9 @@ var validateLeafNodeOrdering = (state, node) => {
1506
1806
  leafEntryAt(node, index - 1).key,
1507
1807
  leafEntryAt(node, index).key
1508
1808
  ) === 0) {
1509
- throw new BTreeInvariantError("Duplicate user key detected in tree with uniqueness policy.");
1809
+ throw new BTreeInvariantError(
1810
+ "Duplicate user key detected in tree with uniqueness policy."
1811
+ );
1510
1812
  }
1511
1813
  }
1512
1814
  }
@@ -1514,12 +1816,7 @@ var validateLeafNodeOrdering = (state, node) => {
1514
1816
  const first = leafEntryAt(node, index - 2);
1515
1817
  const second = leafEntryAt(node, index - 1);
1516
1818
  const third = leafEntryAt(node, index);
1517
- assertTransitivityAsInvariant(
1518
- state,
1519
- first.key,
1520
- second.key,
1521
- third.key
1522
- );
1819
+ assertTransitivityAsInvariant(state, first.key, second.key, third.key);
1523
1820
  }
1524
1821
  if (count > state.maxLeafEntries) {
1525
1822
  throw new BTreeInvariantError("Leaf node exceeds maximum occupancy.");
@@ -1528,9 +1825,14 @@ var validateLeafNodeOrdering = (state, node) => {
1528
1825
  var validateLeafNode = (state, node, depth) => {
1529
1826
  validateLeafNodeOrdering(state, node);
1530
1827
  const count = leafEntryCount(node);
1531
- const baseMinLeaf = state.autoScale ? Math.ceil(computeAutoScaleTier(0).maxLeaf / 2) : state.minLeafEntries;
1828
+ let baseMinLeaf = state.autoScale ? Math.ceil(computeAutoScaleTier(0).maxLeaf / 2) : state.minLeafEntries;
1829
+ if (state.deleteRebalancePolicy === "lazy") {
1830
+ baseMinLeaf = applyLazyThreshold(baseMinLeaf);
1831
+ }
1532
1832
  if (node !== state.root && count < baseMinLeaf) {
1533
- throw new BTreeInvariantError("Non-root leaf node violates minimum occupancy.");
1833
+ throw new BTreeInvariantError(
1834
+ "Non-root leaf node violates minimum occupancy."
1835
+ );
1534
1836
  }
1535
1837
  const first = count === 0 ? null : leafEntryAt(node, 0);
1536
1838
  const last = count === 0 ? null : leafEntryAt(node, count - 1);
@@ -1552,76 +1854,110 @@ var validateBranchStructure = (state, node) => {
1552
1854
  }
1553
1855
  const baseMinBranch = state.autoScale ? Math.ceil(computeAutoScaleTier(0).maxBranch / 2) : state.minBranchChildren;
1554
1856
  if (node !== state.root && liveCount < baseMinBranch) {
1555
- throw new BTreeInvariantError("Non-root branch node violates minimum occupancy.");
1857
+ throw new BTreeInvariantError(
1858
+ "Non-root branch node violates minimum occupancy."
1859
+ );
1556
1860
  }
1557
1861
  if (liveCount > state.maxBranchChildren) {
1558
1862
  throw new BTreeInvariantError("Branch node exceeds maximum occupancy.");
1559
1863
  }
1560
1864
  if (node.keys.length !== node.children.length) {
1561
- throw new BTreeInvariantError("Branch keys array length does not match children array length.");
1865
+ throw new BTreeInvariantError(
1866
+ "Branch keys array length does not match children array length."
1867
+ );
1562
1868
  }
1563
1869
  };
1564
1870
  var validateBranchChild = (state, node, childIndex, depth) => {
1565
1871
  const child = node.children[childIndex];
1566
1872
  if (child.parent !== node) {
1567
- throw new BTreeInvariantError("Child-parent pointer mismatch in branch node.");
1873
+ throw new BTreeInvariantError(
1874
+ "Child-parent pointer mismatch in branch node."
1875
+ );
1568
1876
  }
1569
1877
  if (child.indexInParent !== childIndex) {
1570
- throw new BTreeInvariantError("Child indexInParent does not match actual position in parent.");
1878
+ throw new BTreeInvariantError(
1879
+ "Child indexInParent does not match actual position in parent."
1880
+ );
1571
1881
  }
1572
1882
  const childValidation = validateNode(state, child, depth + 1);
1573
1883
  if (childValidation.minKey === null || childValidation.maxKey === null) {
1574
- throw new BTreeInvariantError("Branch child must not be empty in non-root branch tree.");
1884
+ throw new BTreeInvariantError(
1885
+ "Branch child must not be empty in non-root branch tree."
1886
+ );
1575
1887
  }
1576
1888
  const cachedMinKey = node.keys[childIndex];
1577
1889
  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.");
1890
+ if (actualMinKey === null || compareNodeKeys(
1891
+ state.compareKeys,
1892
+ cachedMinKey.key,
1893
+ cachedMinKey.sequence,
1894
+ actualMinKey.key,
1895
+ actualMinKey.sequence
1896
+ ) !== 0) {
1897
+ throw new BTreeInvariantError(
1898
+ "Branch cached key does not match actual child minimum key."
1899
+ );
1580
1900
  }
1581
1901
  return childValidation;
1582
1902
  };
1903
+ var mergeChildValidation = (state, accumulated, childValidation) => {
1904
+ if (accumulated.leafDepth !== null && childValidation.leafDepth !== null && childValidation.leafDepth !== accumulated.leafDepth) {
1905
+ throw new BTreeInvariantError("Leaf depth mismatch detected in tree.");
1906
+ }
1907
+ if (accumulated.leafDepth === null && childValidation.leafDepth !== null) {
1908
+ accumulated.leafDepth = childValidation.leafDepth;
1909
+ }
1910
+ if (accumulated.previousChildMax !== null && compareNodeKeys(
1911
+ state.compareKeys,
1912
+ accumulated.previousChildMax.key,
1913
+ accumulated.previousChildMax.sequence,
1914
+ childValidation.minKey.key,
1915
+ childValidation.minKey.sequence
1916
+ ) >= 0) {
1917
+ throw new BTreeInvariantError(
1918
+ "Branch child key ranges are not strictly ordered."
1919
+ );
1920
+ }
1921
+ if (accumulated.minKey === null) accumulated.minKey = childValidation.minKey;
1922
+ accumulated.maxKey = childValidation.maxKey;
1923
+ accumulated.previousChildMax = childValidation.maxKey;
1924
+ accumulated.leafCount += childValidation.leafCount;
1925
+ accumulated.branchCount += childValidation.branchCount;
1926
+ accumulated.entryCount += childValidation.entryCount;
1927
+ };
1583
1928
  var validateNode = (state, node, depth) => {
1584
1929
  if (isLeafNode(node)) {
1585
1930
  return validateLeafNode(state, node, depth);
1586
1931
  }
1587
1932
  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;
1933
+ const acc = {
1934
+ leafDepth: null,
1935
+ leafCount: 0,
1936
+ branchCount: 1,
1937
+ entryCount: 0,
1938
+ minKey: null,
1939
+ maxKey: null,
1940
+ previousChildMax: null
1941
+ };
1595
1942
  for (let childIndex = node.childOffset; childIndex < node.children.length; childIndex += 1) {
1596
1943
  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;
1944
+ mergeChildValidation(state, acc, childValidation);
1618
1945
  }
1619
- return { minKey, maxKey, leafDepth, leafCount, branchCount, entryCount };
1946
+ return {
1947
+ minKey: acc.minKey,
1948
+ maxKey: acc.maxKey,
1949
+ leafDepth: acc.leafDepth,
1950
+ leafCount: acc.leafCount,
1951
+ branchCount: acc.branchCount,
1952
+ entryCount: acc.entryCount
1953
+ };
1620
1954
  };
1621
1955
  var assertInvariants = (state) => {
1622
1956
  const validation = validateNode(state, state.root, 0);
1623
1957
  if (validation.entryCount !== state.entryCount) {
1624
- throw new BTreeInvariantError("Index entry count mismatch between tree traversal and tracked state.");
1958
+ throw new BTreeInvariantError(
1959
+ "Index entry count mismatch between tree traversal and tracked state."
1960
+ );
1625
1961
  }
1626
1962
  validateLeafLinks(state, validation.leafCount);
1627
1963
  };
@@ -1677,7 +2013,7 @@ var InMemoryBTree = class _InMemoryBTree {
1677
2013
  remove(key) {
1678
2014
  const entry = removeFirstMatchingEntry(this.state, key);
1679
2015
  if (entry === null) return null;
1680
- return toPublicEntry(entry);
2016
+ return freezeEntry(entry);
1681
2017
  }
1682
2018
  removeById(entryId) {
1683
2019
  if (this.state.entryKeys === null) {
@@ -1685,7 +2021,7 @@ var InMemoryBTree = class _InMemoryBTree {
1685
2021
  }
1686
2022
  const entry = removeEntryById(this.state, entryId);
1687
2023
  if (entry === null) return null;
1688
- return toPublicEntry(entry);
2024
+ return freezeEntry(entry);
1689
2025
  }
1690
2026
  peekById(entryId) {
1691
2027
  if (this.state.entryKeys === null) {
@@ -1693,7 +2029,7 @@ var InMemoryBTree = class _InMemoryBTree {
1693
2029
  }
1694
2030
  const entry = peekEntryById(this.state, entryId);
1695
2031
  if (entry === null) return null;
1696
- return toPublicEntry(entry);
2032
+ return freezeEntry(entry);
1697
2033
  }
1698
2034
  updateById(entryId, value) {
1699
2035
  if (this.state.entryKeys === null) {
@@ -1701,30 +2037,30 @@ var InMemoryBTree = class _InMemoryBTree {
1701
2037
  }
1702
2038
  const entry = updateEntryById(this.state, entryId, value);
1703
2039
  if (entry === null) return null;
1704
- return toPublicEntry(entry);
2040
+ return freezeEntry(entry);
1705
2041
  }
1706
2042
  popFirst() {
1707
2043
  const entry = popFirstEntry(this.state);
1708
2044
  if (entry === null) return null;
1709
- return toPublicEntry(entry);
2045
+ return freezeEntry(entry);
1710
2046
  }
1711
2047
  peekFirst() {
1712
2048
  if (this.state.entryCount === 0) {
1713
2049
  return null;
1714
2050
  }
1715
- return toPublicEntry(leafEntryAt(this.state.leftmostLeaf, 0));
2051
+ return freezeEntry(leafEntryAt(this.state.leftmostLeaf, 0));
1716
2052
  }
1717
2053
  peekLast() {
1718
2054
  if (this.state.entryCount === 0) {
1719
2055
  return null;
1720
2056
  }
1721
2057
  const leaf = this.state.rightmostLeaf;
1722
- return toPublicEntry(leafEntryAt(leaf, leafEntryCount(leaf) - 1));
2058
+ return freezeEntry(leafEntryAt(leaf, leafEntryCount(leaf) - 1));
1723
2059
  }
1724
2060
  popLast() {
1725
2061
  const entry = popLastEntry(this.state);
1726
2062
  if (entry === null) return null;
1727
- return toPublicEntry(entry);
2063
+ return freezeEntry(entry);
1728
2064
  }
1729
2065
  clear() {
1730
2066
  const emptyLeaf = createLeafNode([], null);
@@ -1734,16 +2070,9 @@ var InMemoryBTree = class _InMemoryBTree {
1734
2070
  this.state.entryCount = 0;
1735
2071
  this.state._cursor.leaf = emptyLeaf;
1736
2072
  this.state._cursor.index = 0;
1737
- if (this.state.entryKeys !== null) {
1738
- this.state.entryKeys.clear();
1739
- }
2073
+ this.state.entryKeys?.clear();
1740
2074
  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);
2075
+ resetAutoScaleToTier0(this.state);
1747
2076
  }
1748
2077
  }
1749
2078
  get(key) {
@@ -1757,65 +2086,39 @@ var InMemoryBTree = class _InMemoryBTree {
1757
2086
  findFirst(key) {
1758
2087
  const found = findFirstMatchingUserKey(this.state, key);
1759
2088
  if (found === null) return null;
1760
- return toPublicEntry(leafEntryAt(found.leaf, found.index));
2089
+ return freezeEntry(leafEntryAt(found.leaf, found.index));
1761
2090
  }
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
2091
  findLast(key) {
1767
2092
  const found = findLastMatchingUserKey(this.state, key);
1768
2093
  if (found === null) return null;
1769
- return toPublicEntry(leafEntryAt(found.leaf, found.index));
2094
+ return freezeEntry(leafEntryAt(found.leaf, found.index));
1770
2095
  }
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
2096
  nextHigherKey(key) {
1776
2097
  return findNextHigherKey(this.state, key);
1777
2098
  }
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
2099
  nextLowerKey(key) {
1783
2100
  return findNextLowerKey(this.state, key);
1784
2101
  }
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
2102
  getPairOrNextLower(key) {
1791
2103
  const found = findPairOrNextLower(this.state, key);
1792
2104
  if (found === null) return null;
1793
- return toPublicEntry(leafEntryAt(found.leaf, found.index));
2105
+ return freezeEntry(leafEntryAt(found.leaf, found.index));
1794
2106
  }
1795
- /**
1796
- * Returns the number of entries whose keys fall within [`startKey`, `endKey`].
1797
- * Pass `options` to make either bound exclusive.
1798
- */
1799
2107
  count(startKey, endKey, options) {
1800
2108
  return countRangeEntries(this.state, startKey, endKey, options);
1801
2109
  }
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
2110
  deleteRange(startKey, endKey, options) {
1808
2111
  return deleteRangeEntries(this.state, startKey, endKey, options);
1809
2112
  }
1810
2113
  range(startKey, endKey, options) {
1811
- return rangeQueryEntries(this.state, startKey, endKey, options).map(toPublicEntry);
2114
+ return rangeQueryPublicEntries(this.state, startKey, endKey, options);
1812
2115
  }
1813
2116
  *entries() {
1814
2117
  let leaf = this.state.leftmostLeaf;
1815
2118
  while (leaf !== null) {
1816
2119
  const count = leafEntryCount(leaf);
1817
2120
  for (let i = 0; i < count; i += 1) {
1818
- yield toPublicEntry(leafEntryAt(leaf, i));
2121
+ yield freezeEntry(leafEntryAt(leaf, i));
1819
2122
  }
1820
2123
  leaf = leaf.next;
1821
2124
  }
@@ -1825,7 +2128,7 @@ var InMemoryBTree = class _InMemoryBTree {
1825
2128
  while (leaf !== null) {
1826
2129
  const count = leafEntryCount(leaf);
1827
2130
  for (let i = count - 1; i >= 0; i -= 1) {
1828
- yield toPublicEntry(leafEntryAt(leaf, i));
2131
+ yield freezeEntry(leafEntryAt(leaf, i));
1829
2132
  }
1830
2133
  leaf = leaf.prev;
1831
2134
  }
@@ -1843,55 +2146,26 @@ var InMemoryBTree = class _InMemoryBTree {
1843
2146
  [Symbol.iterator]() {
1844
2147
  return this.entries();
1845
2148
  }
2149
+ forEachRange(startKey, endKey, callback, options) {
2150
+ forEachRangeEntries(this.state, startKey, endKey, callback, options);
2151
+ }
1846
2152
  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
- }
2153
+ forEachEntry(this.state, callback, thisArg);
1855
2154
  }
1856
2155
  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;
2156
+ return snapshotEntries(this.state);
1868
2157
  }
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
2158
  clone() {
1877
- const cloned = new _InMemoryBTree(buildConfigFromState(this.state));
2159
+ const cloned = new _InMemoryBTree(
2160
+ buildConfigFromState(this.state)
2161
+ );
1878
2162
  applyAutoScaleCapacitySnapshot(
1879
2163
  cloned.state,
1880
2164
  this.state.maxLeafEntries,
1881
2165
  this.state.maxBranchChildren
1882
2166
  );
1883
2167
  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);
2168
+ cloned.putMany(collectInternalEntries(this.state));
1895
2169
  }
1896
2170
  return cloned;
1897
2171
  }
@@ -1900,26 +2174,19 @@ var InMemoryBTree = class _InMemoryBTree {
1900
2174
  }
1901
2175
  static fromJSON(json, compareKeys) {
1902
2176
  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));
2177
+ validateBTreeJSONSortOrder(json, compareKeys);
2178
+ const tree = new _InMemoryBTree(
2179
+ buildConfigFromJSON(json, compareKeys)
2180
+ );
1916
2181
  applyAutoScaleCapacitySnapshot(
1917
2182
  tree.state,
1918
2183
  json.config.maxLeafEntries,
1919
2184
  json.config.maxBranchChildren
1920
2185
  );
1921
2186
  if (json.entries.length > 0) {
1922
- const pairs = new Array(json.entries.length);
2187
+ const pairs = new Array(
2188
+ json.entries.length
2189
+ );
1923
2190
  for (let i = 0; i < json.entries.length; i += 1) {
1924
2191
  pairs[i] = { key: json.entries[i][0], value: json.entries[i][1] };
1925
2192
  }