@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.
package/dist/index.cjs CHANGED
@@ -77,11 +77,12 @@ var createBranchNode = (children, parent) => {
77
77
  const child = children[i];
78
78
  child.parent = branch;
79
79
  child.indexInParent = i;
80
- const target = { key: void 0, sequence: 0 };
80
+ const target = {
81
+ key: void 0,
82
+ sequence: 0
83
+ };
81
84
  if (!writeMinKeyTo(child, target)) {
82
- throw new BTreeInvariantError(
83
- "branch child has no min key"
84
- );
85
+ throw new BTreeInvariantError("branch child has no min key");
85
86
  }
86
87
  keys.push(target);
87
88
  }
@@ -135,7 +136,14 @@ var leafInsertAt = (leaf, logicalIndex, entry) => {
135
136
  leaf.entryOffset -= 1;
136
137
  leaf.entries[phys - 1] = entry;
137
138
  } else {
138
- leaf.entries.splice(phys, 0, entry);
139
+ const len = leaf.entries.length;
140
+ if (phys >= len) {
141
+ leaf.entries.push(entry);
142
+ } else {
143
+ leaf.entries.push(leaf.entries[len - 1]);
144
+ leaf.entries.copyWithin(phys + 1, phys, len);
145
+ leaf.entries[phys] = entry;
146
+ }
139
147
  }
140
148
  };
141
149
  var leafCompact = (leaf) => {
@@ -164,7 +172,11 @@ var branchInsertAt = (branch, logicalIndex, child, key) => {
164
172
  const phys = branch.childOffset + logicalIndex;
165
173
  const count = branch.children.length - branch.childOffset;
166
174
  if (branch.childOffset > 0 && logicalIndex < count >>> 1) {
167
- branch.children.copyWithin(branch.childOffset - 1, branch.childOffset, phys);
175
+ branch.children.copyWithin(
176
+ branch.childOffset - 1,
177
+ branch.childOffset,
178
+ phys
179
+ );
168
180
  branch.keys.copyWithin(branch.childOffset - 1, branch.childOffset, phys);
169
181
  branch.childOffset -= 1;
170
182
  branch.children[phys - 1] = child;
@@ -185,8 +197,16 @@ var branchRemoveAt = (branch, physIndex) => {
185
197
  const logicalIndex = physIndex - branch.childOffset;
186
198
  const count = branch.children.length - branch.childOffset;
187
199
  if (logicalIndex < count - 1 - logicalIndex) {
188
- branch.children.copyWithin(branch.childOffset + 1, branch.childOffset, physIndex);
189
- branch.keys.copyWithin(branch.childOffset + 1, branch.childOffset, physIndex);
200
+ branch.children.copyWithin(
201
+ branch.childOffset + 1,
202
+ branch.childOffset,
203
+ physIndex
204
+ );
205
+ branch.keys.copyWithin(
206
+ branch.childOffset + 1,
207
+ branch.childOffset,
208
+ physIndex
209
+ );
190
210
  branch.childOffset += 1;
191
211
  for (let i = branch.childOffset; i <= physIndex; i += 1) {
192
212
  branch.children[i].indexInParent = i;
@@ -216,17 +236,21 @@ var normalizeDuplicateKeyPolicy = (value) => {
216
236
  return "replace";
217
237
  }
218
238
  if (value !== "allow" && value !== "reject" && value !== "replace") {
219
- throw new BTreeValidationError(
220
- `Invalid duplicateKeys option.`
221
- );
239
+ throw new BTreeValidationError(`Invalid duplicateKeys option.`);
222
240
  }
223
241
  return value;
224
242
  };
225
- var toPublicEntry = (entry) => ({
226
- entryId: entry.entryId,
227
- key: entry.key,
228
- value: entry.value
229
- });
243
+ var normalizeDeleteRebalancePolicy = (value) => {
244
+ if (value === void 0) {
245
+ return "standard";
246
+ }
247
+ if (value !== "standard" && value !== "lazy") {
248
+ throw new BTreeValidationError(`Invalid deleteRebalancePolicy option.`);
249
+ }
250
+ return value;
251
+ };
252
+ var freezeEntry = (entry) => Object.freeze(entry);
253
+ var createEntry = (key, entryId, value) => Object.freeze({ key, entryId, value });
230
254
  var isLeafNode = (node) => {
231
255
  return node.kind === NODE_LEAF;
232
256
  };
@@ -376,7 +400,11 @@ var hasKeyEntry = (state, key) => {
376
400
  var findNextHigherKey = (state, key) => {
377
401
  if (state.entryCount === 0) return null;
378
402
  const compare = state.compareKeys;
379
- let leaf = findLeafForKey(state, key, Number.MAX_SAFE_INTEGER);
403
+ let leaf = findLeafForKey(
404
+ state,
405
+ key,
406
+ Number.MAX_SAFE_INTEGER
407
+ );
380
408
  let idx = upperBoundInLeaf(state, leaf, key, Number.MAX_SAFE_INTEGER);
381
409
  while (leaf !== null) {
382
410
  if (idx < leafEntryCount(leaf)) {
@@ -479,55 +507,57 @@ var countRangeEntries = (state, startKey, endKey, options) => {
479
507
  cursorIndex = 0;
480
508
  continue;
481
509
  }
482
- const lastEntry = leafEntryAt(cursorLeaf, leafCount - 1);
483
- const cmpLast = compare(lastEntry.key, endKey);
484
- if (upperExclusive ? cmpLast < 0 : cmpLast <= 0) {
510
+ const lastKey = leafEntryAt(cursorLeaf, leafCount - 1).key;
511
+ if (isLastEntryInRange(lastKey, endKey, compare, upperExclusive)) {
485
512
  count += leafCount - cursorIndex;
486
513
  cursorLeaf = cursorLeaf.next;
487
514
  cursorIndex = 0;
488
515
  continue;
489
516
  }
490
- const endSeq = upperExclusive ? 0 : Number.MAX_SAFE_INTEGER;
491
- const endBound = upperExclusive ? lowerBoundInLeaf(state, cursorLeaf, endKey, endSeq) : upperBoundInLeaf(state, cursorLeaf, endKey, endSeq);
492
- const limit = endBound < leafCount ? endBound : leafCount;
493
- count += limit - cursorIndex;
517
+ count += findBoundaryEnd(state, cursorLeaf, endKey, upperExclusive, leafCount) - cursorIndex;
494
518
  return count;
495
519
  }
496
520
  return count;
497
521
  };
522
+ var isLastEntryInRange = (lastKey, endKey, compare, upperExclusive) => {
523
+ const cmp = compare(lastKey, endKey);
524
+ return upperExclusive ? cmp < 0 : cmp <= 0;
525
+ };
526
+ var findBoundaryEnd = (state, leaf, endKey, upperExclusive, leafCount) => {
527
+ const endSeq = upperExclusive ? 0 : Number.MAX_SAFE_INTEGER;
528
+ const endBound = upperExclusive ? lowerBoundInLeaf(state, leaf, endKey, endSeq) : upperBoundInLeaf(state, leaf, endKey, endSeq);
529
+ return endBound < leafCount ? endBound : leafCount;
530
+ };
498
531
  var RANGE_PREALLOC_THRESHOLD = 200;
499
- var allocateRangeOutput = (state, cursorLeaf, cursorIndex, compare, upperExclusive, startKey, endKey, options) => {
500
- const firstLeafCount = leafEntryCount(cursorLeaf);
501
- const firstLeafRemainder = firstLeafCount - cursorIndex;
502
- if (firstLeafRemainder >= RANGE_PREALLOC_THRESHOLD && cursorLeaf.next !== null) {
503
- const lastEntry = leafEntryAt(cursorLeaf, firstLeafCount - 1);
504
- const cmpLast = compare(lastEntry.key, endKey);
505
- if (upperExclusive ? cmpLast < 0 : cmpLast <= 0) {
506
- const total = countRangeEntries(state, startKey, endKey, options);
507
- return new Array(total);
532
+ var allocateRangeOutput = (state, cursor, startKey, endKey, options) => {
533
+ const firstLeafCount = leafEntryCount(cursor.leaf);
534
+ const firstLeafRemainder = firstLeafCount - cursor.index;
535
+ if (firstLeafRemainder >= RANGE_PREALLOC_THRESHOLD && cursor.leaf.next !== null) {
536
+ const lastKey = leafEntryAt(cursor.leaf, firstLeafCount - 1).key;
537
+ if (isLastEntryInRange(lastKey, endKey, cursor.compare, cursor.upperExclusive)) {
538
+ return new Array(
539
+ countRangeEntries(state, startKey, endKey, options)
540
+ );
508
541
  }
509
542
  }
510
543
  return [];
511
544
  };
512
- var appendLeafSlice = (leaf, from, to, output, useIndexed, writeIdx) => {
545
+ var appendLeafSlicePublic = (leaf, from, to, output, useIndexed, writeIdx) => {
513
546
  if (useIndexed) {
514
547
  for (let i = from; i < to; i += 1) {
515
- output[writeIdx++] = leafEntryAt(leaf, i);
548
+ output[writeIdx++] = freezeEntry(leafEntryAt(leaf, i));
516
549
  }
517
550
  } else {
518
551
  for (let i = from; i < to; i += 1) {
519
- output.push(leafEntryAt(leaf, i));
552
+ output.push(freezeEntry(leafEntryAt(leaf, i)));
520
553
  }
521
554
  }
522
555
  return writeIdx;
523
556
  };
524
- var rangeQueryEntries = (state, startKey, endKey, options) => {
525
- const cursor = initRangeCursor(state, startKey, endKey, options);
526
- if (cursor === null) return [];
557
+ var collectPublicEntries = (state, cursor, endKey, output) => {
558
+ const { compare, upperExclusive } = cursor;
527
559
  let cursorLeaf = cursor.leaf;
528
560
  let cursorIndex = cursor.index;
529
- const { compare, upperExclusive } = cursor;
530
- const output = allocateRangeOutput(state, cursorLeaf, cursorIndex, compare, upperExclusive, startKey, endKey, options);
531
561
  let writeIdx = 0;
532
562
  const useIndexed = output.length > 0;
533
563
  while (cursorLeaf !== null) {
@@ -537,24 +567,82 @@ var rangeQueryEntries = (state, startKey, endKey, options) => {
537
567
  cursorIndex = 0;
538
568
  continue;
539
569
  }
540
- const lastEntry = leafEntryAt(cursorLeaf, leafCount - 1);
541
- const cmpLast = compare(lastEntry.key, endKey);
542
- if (upperExclusive ? cmpLast < 0 : cmpLast <= 0) {
543
- writeIdx = appendLeafSlice(cursorLeaf, cursorIndex, leafCount, output, useIndexed, writeIdx);
570
+ const lastKey = leafEntryAt(cursorLeaf, leafCount - 1).key;
571
+ if (isLastEntryInRange(lastKey, endKey, compare, upperExclusive)) {
572
+ writeIdx = appendLeafSlicePublic(
573
+ cursorLeaf,
574
+ cursorIndex,
575
+ leafCount,
576
+ output,
577
+ useIndexed,
578
+ writeIdx
579
+ );
544
580
  cursorLeaf = cursorLeaf.next;
545
581
  cursorIndex = 0;
546
582
  continue;
547
583
  }
548
- const endSeq = upperExclusive ? 0 : Number.MAX_SAFE_INTEGER;
549
- const endBound = upperExclusive ? lowerBoundInLeaf(state, cursorLeaf, endKey, endSeq) : upperBoundInLeaf(state, cursorLeaf, endKey, endSeq);
550
- const limit = endBound < leafCount ? endBound : leafCount;
551
- appendLeafSlice(cursorLeaf, cursorIndex, limit, output, useIndexed, writeIdx);
552
- return output;
584
+ const limit = findBoundaryEnd(
585
+ state,
586
+ cursorLeaf,
587
+ endKey,
588
+ upperExclusive,
589
+ leafCount
590
+ );
591
+ appendLeafSlicePublic(
592
+ cursorLeaf,
593
+ cursorIndex,
594
+ limit,
595
+ output,
596
+ useIndexed,
597
+ writeIdx
598
+ );
599
+ return;
553
600
  }
601
+ };
602
+ var rangeQueryPublicEntries = (state, startKey, endKey, options) => {
603
+ const cursor = initRangeCursor(state, startKey, endKey, options);
604
+ if (cursor === null) return [];
605
+ const output = allocateRangeOutput(state, cursor, startKey, endKey, options);
606
+ collectPublicEntries(state, cursor, endKey, output);
554
607
  return output;
555
608
  };
609
+ var forEachRangeEntries = (state, startKey, endKey, callback, options) => {
610
+ const cursor = initRangeCursor(state, startKey, endKey, options);
611
+ if (cursor === null) return;
612
+ let cursorLeaf = cursor.leaf;
613
+ let cursorIndex = cursor.index;
614
+ const { compare, upperExclusive } = cursor;
615
+ while (cursorLeaf !== null) {
616
+ const leafCount = leafEntryCount(cursorLeaf);
617
+ if (cursorIndex >= leafCount) {
618
+ cursorLeaf = cursorLeaf.next;
619
+ cursorIndex = 0;
620
+ continue;
621
+ }
622
+ const lastKey = leafEntryAt(cursorLeaf, leafCount - 1).key;
623
+ if (isLastEntryInRange(lastKey, endKey, compare, upperExclusive)) {
624
+ for (let i = cursorIndex; i < leafCount; i += 1) {
625
+ callback(freezeEntry(leafEntryAt(cursorLeaf, i)));
626
+ }
627
+ cursorLeaf = cursorLeaf.next;
628
+ cursorIndex = 0;
629
+ continue;
630
+ }
631
+ const limit = findBoundaryEnd(
632
+ state,
633
+ cursorLeaf,
634
+ endKey,
635
+ upperExclusive,
636
+ leafCount
637
+ );
638
+ for (let i = cursorIndex; i < limit; i += 1) {
639
+ callback(freezeEntry(leafEntryAt(cursorLeaf, i)));
640
+ }
641
+ return;
642
+ }
643
+ };
556
644
 
557
- // src/btree/rebalance.ts
645
+ // src/btree/rebalance-branch.ts
558
646
  var updateMinKeyInAncestors = (node) => {
559
647
  let current = node;
560
648
  while (current.parent !== null) {
@@ -570,12 +658,9 @@ var requireParent = (node) => {
570
658
  }
571
659
  return node.parent;
572
660
  };
573
- var requireLeafNode = (node) => {
574
- if (!isLeafNode(node)) throw new BTreeInvariantError("expected leaf, got branch");
575
- return node;
576
- };
577
661
  var requireBranchNode = (node) => {
578
- if (isLeafNode(node)) throw new BTreeInvariantError("expected branch, got leaf");
662
+ if (isLeafNode(node))
663
+ throw new BTreeInvariantError("expected branch, got leaf");
579
664
  return node;
580
665
  };
581
666
  var removeChildFromBranch = (branch, childIndex) => {
@@ -584,27 +669,18 @@ var removeChildFromBranch = (branch, childIndex) => {
584
669
  }
585
670
  branchRemoveAt(branch, childIndex);
586
671
  };
587
- var detachLeafFromChain = (state, leaf) => {
588
- if (leaf.prev !== null) {
589
- leaf.prev.next = leaf.next;
590
- } else if (leaf.next !== null) {
591
- state.leftmostLeaf = leaf.next;
592
- }
593
- if (leaf.next !== null) {
594
- leaf.next.prev = leaf.prev;
595
- } else if (leaf.prev !== null) {
596
- state.rightmostLeaf = leaf.prev;
597
- }
598
- leaf.prev = null;
599
- leaf.next = null;
600
- };
601
672
  var borrowFromLeftBranch = (branch, leftSibling, branchIndex) => {
602
673
  const borrowedChild = leftSibling.children.pop();
603
- if (borrowedChild === void 0) throw new BTreeInvariantError("left branch borrow failed");
674
+ if (borrowedChild === void 0)
675
+ throw new BTreeInvariantError("left branch borrow failed");
604
676
  leftSibling.keys.pop();
605
677
  borrowedChild.parent = branch;
606
- const borrowedMinKey = { key: void 0, sequence: 0 };
607
- if (!writeMinKeyTo(borrowedChild, borrowedMinKey)) throw new BTreeInvariantError("borrowed child has no min key");
678
+ const borrowedMinKey = {
679
+ key: void 0,
680
+ sequence: 0
681
+ };
682
+ if (!writeMinKeyTo(borrowedChild, borrowedMinKey))
683
+ throw new BTreeInvariantError("borrowed child has no min key");
608
684
  if (branch.childOffset > 0) {
609
685
  branch.childOffset -= 1;
610
686
  branch.children[branch.childOffset] = borrowedChild;
@@ -613,15 +689,20 @@ var borrowFromLeftBranch = (branch, leftSibling, branchIndex) => {
613
689
  } else {
614
690
  branch.children.unshift(borrowedChild);
615
691
  branch.keys.unshift(borrowedMinKey);
616
- for (let i = 0; i < branch.children.length; i += 1) branch.children[i].indexInParent = i;
692
+ for (let i = 0; i < branch.children.length; i += 1)
693
+ branch.children[i].indexInParent = i;
617
694
  }
618
695
  const parent = requireParent(branch);
619
- parent.keys[branchIndex] = { key: borrowedMinKey.key, sequence: borrowedMinKey.sequence };
696
+ parent.keys[branchIndex] = {
697
+ key: borrowedMinKey.key,
698
+ sequence: borrowedMinKey.sequence
699
+ };
620
700
  updateMinKeyInAncestors(branch);
621
701
  };
622
702
  var borrowFromRightBranch = (branch, rightSibling, branchIndex) => {
623
703
  const shiftIdx = rightSibling.childOffset;
624
- if (shiftIdx >= rightSibling.children.length) throw new BTreeInvariantError("right branch borrow failed");
704
+ if (shiftIdx >= rightSibling.children.length)
705
+ throw new BTreeInvariantError("right branch borrow failed");
625
706
  const borrowedChild = rightSibling.children[shiftIdx];
626
707
  rightSibling.childOffset += 1;
627
708
  if (rightSibling.childOffset >= rightSibling.children.length >>> 1) {
@@ -629,8 +710,12 @@ var borrowFromRightBranch = (branch, rightSibling, branchIndex) => {
629
710
  }
630
711
  branch.children.push(borrowedChild);
631
712
  borrowedChild.parent = branch;
632
- const borrowedMinKey = { key: void 0, sequence: 0 };
633
- if (!writeMinKeyTo(borrowedChild, borrowedMinKey)) throw new BTreeInvariantError("borrowed child has no min key");
713
+ const borrowedMinKey = {
714
+ key: void 0,
715
+ sequence: 0
716
+ };
717
+ if (!writeMinKeyTo(borrowedChild, borrowedMinKey))
718
+ throw new BTreeInvariantError("borrowed child has no min key");
634
719
  branch.keys.push(borrowedMinKey);
635
720
  borrowedChild.indexInParent = branch.children.length - 1;
636
721
  const parent = requireParent(branch);
@@ -683,7 +768,10 @@ var rebalanceAfterBranchRemoval = (state, branch) => {
683
768
  const parent = branch.parent;
684
769
  if (parent === null) throw new BTreeInvariantError("branch has no parent");
685
770
  const branchIndex = branch.indexInParent;
686
- const { left: leftSibling, right: rightSibling } = findBranchSiblings(parent, branchIndex);
771
+ const { left: leftSibling, right: rightSibling } = findBranchSiblings(
772
+ parent,
773
+ branchIndex
774
+ );
687
775
  if (rightSibling !== null && branchChildCount(rightSibling) > state.minBranchChildren) {
688
776
  borrowFromRightBranch(branch, rightSibling, branchIndex);
689
777
  return;
@@ -702,44 +790,68 @@ var rebalanceAfterBranchRemoval = (state, branch) => {
702
790
  }
703
791
  throw new BTreeInvariantError("no branch siblings to rebalance");
704
792
  };
793
+
794
+ // src/btree/rebalance.ts
795
+ var requireLeafNode = (node) => {
796
+ if (!isLeafNode(node))
797
+ throw new BTreeInvariantError("expected leaf, got branch");
798
+ return node;
799
+ };
800
+ var detachLeafFromChain = (state, leaf) => {
801
+ if (leaf.prev !== null) {
802
+ leaf.prev.next = leaf.next;
803
+ } else if (leaf.next !== null) {
804
+ state.leftmostLeaf = leaf.next;
805
+ }
806
+ if (leaf.next !== null) {
807
+ leaf.next.prev = leaf.prev;
808
+ } else if (leaf.prev !== null) {
809
+ state.rightmostLeaf = leaf.prev;
810
+ }
811
+ leaf.prev = null;
812
+ leaf.next = null;
813
+ };
705
814
  var mergeLeafEntries = (target, source) => {
706
815
  if (target.entryOffset > 0) {
707
816
  target.entries.copyWithin(0, target.entryOffset);
708
817
  target.entries.length = target.entries.length - target.entryOffset;
709
818
  target.entryOffset = 0;
710
819
  }
711
- const src = source.entries;
712
- for (let i = source.entryOffset; i < src.length; i += 1) target.entries.push(src[i]);
820
+ target.entries.push(...source.entries.slice(source.entryOffset));
713
821
  };
714
- var rebalanceAfterLeafRemoval = (state, leaf) => {
715
- if (leaf === state.root) {
716
- if (state.entryCount === 0) {
717
- state.leftmostLeaf = leaf;
718
- state.rightmostLeaf = leaf;
719
- }
720
- return;
822
+ var applyLazyThreshold = (min) => Math.max(1, Math.ceil(min / 4));
823
+ var leafRebalanceThreshold = (state) => {
824
+ if (state.deleteRebalancePolicy === "lazy") {
825
+ return applyLazyThreshold(state.minLeafEntries);
721
826
  }
722
- if (leafEntryCount(leaf) >= state.minLeafEntries) return;
723
- const parent = leaf.parent;
724
- if (parent === null) throw new BTreeInvariantError("Leaf node has no parent during rebalance.");
725
- const leafIndex = leaf.indexInParent;
726
- const leftSibling = leafIndex > parent.childOffset ? requireLeafNode(parent.children[leafIndex - 1]) : null;
727
- const rightSibling = leafIndex + 1 < parent.children.length ? requireLeafNode(parent.children[leafIndex + 1]) : null;
827
+ return state.minLeafEntries;
828
+ };
829
+ var findLeafSiblings = (parent, leafIndex) => {
830
+ const left = leafIndex > parent.childOffset ? requireLeafNode(parent.children[leafIndex - 1]) : null;
831
+ const right = leafIndex + 1 < parent.children.length ? requireLeafNode(parent.children[leafIndex + 1]) : null;
832
+ return { left, right };
833
+ };
834
+ var tryBorrowFromLeafSibling = (state, leaf, parent, leafIndex, leftSibling, rightSibling) => {
728
835
  if (rightSibling !== null && leafEntryCount(rightSibling) > state.minLeafEntries) {
729
836
  const borrowed = leafShiftEntry(rightSibling);
730
- if (borrowed === void 0) throw new BTreeInvariantError("right leaf borrow failed");
837
+ if (borrowed === void 0)
838
+ throw new BTreeInvariantError("right leaf borrow failed");
731
839
  leaf.entries.push(borrowed);
732
840
  writeMinKeyTo(rightSibling, parent.keys[leafIndex + 1]);
733
- return;
841
+ return true;
734
842
  }
735
843
  if (leftSibling !== null && leafEntryCount(leftSibling) > state.minLeafEntries) {
736
844
  const borrowed = leftSibling.entries.pop();
737
- if (borrowed === void 0) throw new BTreeInvariantError("left leaf borrow failed");
845
+ if (borrowed === void 0)
846
+ throw new BTreeInvariantError("left leaf borrow failed");
738
847
  leafUnshiftEntry(leaf, borrowed);
739
848
  parent.keys[leafIndex] = { key: borrowed.key, sequence: borrowed.entryId };
740
849
  updateMinKeyInAncestors(leaf);
741
- return;
850
+ return true;
742
851
  }
852
+ return false;
853
+ };
854
+ var mergeLeafWithSibling = (state, leaf, parent, leafIndex, leftSibling, rightSibling) => {
743
855
  if (leftSibling !== null) {
744
856
  mergeLeafEntries(leftSibling, leaf);
745
857
  detachLeafFromChain(state, leaf);
@@ -756,6 +868,24 @@ var rebalanceAfterLeafRemoval = (state, leaf) => {
756
868
  }
757
869
  throw new BTreeInvariantError("no leaf siblings to rebalance");
758
870
  };
871
+ var rebalanceAfterLeafRemoval = (state, leaf) => {
872
+ if (leaf === state.root) {
873
+ if (state.entryCount === 0) {
874
+ state.leftmostLeaf = leaf;
875
+ state.rightmostLeaf = leaf;
876
+ }
877
+ return;
878
+ }
879
+ if (leafEntryCount(leaf) >= leafRebalanceThreshold(state)) return;
880
+ const parent = leaf.parent;
881
+ if (parent === null)
882
+ throw new BTreeInvariantError("Leaf node has no parent during rebalance.");
883
+ const leafIndex = leaf.indexInParent;
884
+ const { left, right } = findLeafSiblings(parent, leafIndex);
885
+ if (tryBorrowFromLeafSibling(state, leaf, parent, leafIndex, left, right))
886
+ return;
887
+ mergeLeafWithSibling(state, leaf, parent, leafIndex, left, right);
888
+ };
759
889
 
760
890
  // src/btree/deleteRange.ts
761
891
  var navigateToStart = (state, startKey, lowerExclusive) => {
@@ -768,16 +898,16 @@ var navigateToStart = (state, startKey, lowerExclusive) => {
768
898
  }
769
899
  return { leaf, idx };
770
900
  };
771
- var findRemoveEnd = (state, leaf, idx, endKey, upperExclusive) => {
901
+ var findRemoveEnd = (state, leaf, endKey, upperExclusive) => {
772
902
  const count = leafEntryCount(leaf);
773
- let removeEnd = idx;
774
- while (removeEnd < count) {
775
- const e = leafEntryAt(leaf, removeEnd);
776
- const cmpEnd = state.compareKeys(e.key, endKey);
777
- if (upperExclusive ? cmpEnd >= 0 : cmpEnd > 0) break;
778
- removeEnd += 1;
903
+ const lastEntry = leafEntryAt(leaf, count - 1);
904
+ const cmpLast = state.compareKeys(lastEntry.key, endKey);
905
+ if (upperExclusive ? cmpLast < 0 : cmpLast <= 0) {
906
+ return count;
779
907
  }
780
- return removeEnd;
908
+ const endSeq = upperExclusive ? 0 : Number.MAX_SAFE_INTEGER;
909
+ const endBound = upperExclusive ? lowerBoundInLeaf(state, leaf, endKey, endSeq) : upperBoundInLeaf(state, leaf, endKey, endSeq);
910
+ return endBound < count ? endBound : count;
781
911
  };
782
912
  var spliceLeafAndRebalance = (state, leaf, idx, removeCount) => {
783
913
  if (state.entryKeys !== null) {
@@ -794,10 +924,12 @@ var spliceLeafAndRebalance = (state, leaf, idx, removeCount) => {
794
924
  updateMinKeyInAncestors(leaf);
795
925
  }
796
926
  const countAfterSplice = leafEntryCount(leaf);
927
+ const rebalThreshold = leafRebalanceThreshold(state);
797
928
  let safetyGuard = state.minLeafEntries + 4;
798
- while (safetyGuard > 0 && leaf !== state.root && leafEntryCount(leaf) < state.minLeafEntries) {
929
+ while (safetyGuard > 0 && leaf !== state.root && leafEntryCount(leaf) < rebalThreshold) {
799
930
  rebalanceAfterLeafRemoval(state, leaf);
800
- if (leaf.parent !== null && leaf.parent.children[leaf.indexInParent] !== leaf) break;
931
+ if (leaf.parent !== null && leaf.parent.children[leaf.indexInParent] !== leaf)
932
+ break;
801
933
  safetyGuard -= 1;
802
934
  }
803
935
  if (leafEmptied && leafEntryCount(leaf) > 0 && leaf.parent !== null && leaf.parent.children[leaf.indexInParent] === leaf) {
@@ -825,10 +957,15 @@ var deleteRangeEntries = (state, startKey, endKey, options) => {
825
957
  }
826
958
  if (idx >= leafEntryCount(leaf)) break;
827
959
  const count = leafEntryCount(leaf);
828
- const removeEnd = findRemoveEnd(state, leaf, idx, endKey, upperExclusive);
960
+ const removeEnd = findRemoveEnd(state, leaf, endKey, upperExclusive);
829
961
  const removeCount = removeEnd - idx;
830
962
  if (removeCount === 0) break;
831
- const countAfterSplice = spliceLeafAndRebalance(state, leaf, idx, removeCount);
963
+ const countAfterSplice = spliceLeafAndRebalance(
964
+ state,
965
+ leaf,
966
+ idx,
967
+ removeCount
968
+ );
832
969
  deleted += removeCount;
833
970
  if (removeEnd < count) break;
834
971
  if (!isLeafStillValid(state, leaf)) {
@@ -874,25 +1011,42 @@ var computeNextAutoScaleThreshold = (entryCount) => {
874
1011
  }
875
1012
  return Number.MAX_SAFE_INTEGER;
876
1013
  };
877
- var createInitialState = (config) => {
878
- if (typeof config.compareKeys !== "function") {
879
- throw new BTreeValidationError("compareKeys must be a function.");
880
- }
881
- const autoScale = config.autoScale === true;
1014
+ var resolveInitialCapacity = (config, autoScale) => {
882
1015
  if (autoScale && (config.maxLeafEntries !== void 0 || config.maxBranchChildren !== void 0)) {
883
- throw new BTreeValidationError("autoScale conflicts with explicit capacity.");
1016
+ throw new BTreeValidationError(
1017
+ "autoScale conflicts with explicit capacity."
1018
+ );
884
1019
  }
885
- let maxLeafEntries;
886
- let maxBranchChildren;
887
1020
  if (autoScale) {
888
1021
  const tier = computeAutoScaleTier(0);
889
- maxLeafEntries = tier.maxLeaf;
890
- maxBranchChildren = tier.maxBranch;
891
- } else {
892
- maxLeafEntries = normalizeNodeCapacity(config.maxLeafEntries, "maxLeafEntries", DEFAULT_MAX_LEAF_ENTRIES);
893
- maxBranchChildren = normalizeNodeCapacity(config.maxBranchChildren, "maxBranchChildren", DEFAULT_MAX_BRANCH_CHILDREN);
1022
+ return { maxLeafEntries: tier.maxLeaf, maxBranchChildren: tier.maxBranch };
894
1023
  }
1024
+ return {
1025
+ maxLeafEntries: normalizeNodeCapacity(
1026
+ config.maxLeafEntries,
1027
+ "maxLeafEntries",
1028
+ DEFAULT_MAX_LEAF_ENTRIES
1029
+ ),
1030
+ maxBranchChildren: normalizeNodeCapacity(
1031
+ config.maxBranchChildren,
1032
+ "maxBranchChildren",
1033
+ DEFAULT_MAX_BRANCH_CHILDREN
1034
+ )
1035
+ };
1036
+ };
1037
+ var createInitialState = (config) => {
1038
+ if (typeof config.compareKeys !== "function") {
1039
+ throw new BTreeValidationError("compareKeys must be a function.");
1040
+ }
1041
+ const autoScale = config.autoScale === true;
1042
+ const { maxLeafEntries, maxBranchChildren } = resolveInitialCapacity(
1043
+ config,
1044
+ autoScale
1045
+ );
895
1046
  const duplicateKeys = normalizeDuplicateKeyPolicy(config.duplicateKeys);
1047
+ const deleteRebalancePolicy = normalizeDeleteRebalancePolicy(
1048
+ config.deleteRebalancePolicy
1049
+ );
896
1050
  const emptyLeaf = createLeafNode([], null);
897
1051
  return {
898
1052
  compareKeys: config.compareKeys,
@@ -908,6 +1062,7 @@ var createInitialState = (config) => {
908
1062
  nextSequence: 0,
909
1063
  entryKeys: config.enableEntryIdLookup === true ? /* @__PURE__ */ new Map() : null,
910
1064
  autoScale,
1065
+ deleteRebalancePolicy,
911
1066
  _nextAutoScaleThreshold: autoScale ? computeNextAutoScaleThreshold(0) : Number.MAX_SAFE_INTEGER,
912
1067
  _cursor: { leaf: emptyLeaf, index: 0 }
913
1068
  };
@@ -923,7 +1078,9 @@ var maybeAutoScale = (state) => {
923
1078
  state.maxBranchChildren = maxBranch;
924
1079
  state.minBranchChildren = minOccupancy(maxBranch);
925
1080
  }
926
- state._nextAutoScaleThreshold = computeNextAutoScaleThreshold(state.entryCount);
1081
+ state._nextAutoScaleThreshold = computeNextAutoScaleThreshold(
1082
+ state.entryCount
1083
+ );
927
1084
  };
928
1085
  var applyAutoScaleCapacitySnapshot = (state, maxLeafEntries, maxBranchChildren) => {
929
1086
  if (!state.autoScale) {
@@ -950,6 +1107,14 @@ var applyAutoScaleCapacitySnapshot = (state, maxLeafEntries, maxBranchChildren)
950
1107
  state.minLeafEntries = minOccupancy(normalizedLeaf);
951
1108
  state.minBranchChildren = minOccupancy(normalizedBranch);
952
1109
  };
1110
+ var resetAutoScaleToTier0 = (state) => {
1111
+ const tier = computeAutoScaleTier(0);
1112
+ state.maxLeafEntries = tier.maxLeaf;
1113
+ state.maxBranchChildren = tier.maxBranch;
1114
+ state.minLeafEntries = minOccupancy(tier.maxLeaf);
1115
+ state.minBranchChildren = minOccupancy(tier.maxBranch);
1116
+ state._nextAutoScaleThreshold = computeNextAutoScaleThreshold(0);
1117
+ };
953
1118
 
954
1119
  // src/btree/bulkLoad.ts
955
1120
  var computeChunkBoundaries = (total, max, min) => {
@@ -970,7 +1135,11 @@ var computeChunkBoundaries = (total, max, min) => {
970
1135
  return boundaries;
971
1136
  };
972
1137
  var buildLeaves = (state, entries, ids, baseSequence) => {
973
- const boundaries = computeChunkBoundaries(entries.length, state.maxLeafEntries, state.minLeafEntries);
1138
+ const boundaries = computeChunkBoundaries(
1139
+ entries.length,
1140
+ state.maxLeafEntries,
1141
+ state.minLeafEntries
1142
+ );
974
1143
  const leaves = new Array(boundaries.length);
975
1144
  let chunkStart = 0;
976
1145
  for (let c = 0; c < boundaries.length; c += 1) {
@@ -978,7 +1147,11 @@ var buildLeaves = (state, entries, ids, baseSequence) => {
978
1147
  const chunk = new Array(chunkEnd - chunkStart);
979
1148
  for (let i = chunkStart; i < chunkEnd; i += 1) {
980
1149
  const seq = baseSequence + i;
981
- chunk[i - chunkStart] = { key: entries[i].key, entryId: seq, value: entries[i].value };
1150
+ chunk[i - chunkStart] = createEntry(
1151
+ entries[i].key,
1152
+ seq,
1153
+ entries[i].value
1154
+ );
982
1155
  ids[i] = seq;
983
1156
  if (state.entryKeys !== null) {
984
1157
  state.entryKeys.set(seq, entries[i].key);
@@ -1012,11 +1185,18 @@ var bulkLoadEntries = (state, entries) => {
1012
1185
  } else {
1013
1186
  let currentLevel = leaves;
1014
1187
  while (currentLevel.length > 1) {
1015
- const bounds = computeChunkBoundaries(currentLevel.length, state.maxBranchChildren, state.minBranchChildren);
1188
+ const bounds = computeChunkBoundaries(
1189
+ currentLevel.length,
1190
+ state.maxBranchChildren,
1191
+ state.minBranchChildren
1192
+ );
1016
1193
  const nextLevel = new Array(bounds.length);
1017
1194
  let start = 0;
1018
1195
  for (let b = 0; b < bounds.length; b += 1) {
1019
- nextLevel[b] = createBranchNode(currentLevel.slice(start, bounds[b]), null);
1196
+ nextLevel[b] = createBranchNode(
1197
+ currentLevel.slice(start, bounds[b]),
1198
+ null
1199
+ );
1020
1200
  start = bounds[b];
1021
1201
  }
1022
1202
  currentLevel = nextLevel;
@@ -1027,9 +1207,12 @@ var bulkLoadEntries = (state, entries) => {
1027
1207
  return ids;
1028
1208
  };
1029
1209
 
1030
- // src/btree/mutations.ts
1210
+ // src/btree/split.ts
1031
1211
  var insertChildAfter = (state, parent, existingChild, childToInsert) => {
1032
- const newChildMinKey = { key: void 0, sequence: 0 };
1212
+ const newChildMinKey = {
1213
+ key: void 0,
1214
+ sequence: 0
1215
+ };
1033
1216
  if (!writeMinKeyTo(childToInsert, newChildMinKey)) {
1034
1217
  throw new BTreeInvariantError("inserted child has no min key");
1035
1218
  }
@@ -1067,7 +1250,10 @@ var splitLeaf = (state, leaf) => {
1067
1250
  var splitBranch = (state, branch) => {
1068
1251
  branchCompact(branch);
1069
1252
  const splitAt = Math.ceil(branch.children.length / 2);
1070
- const sibling = createBranchNode(branch.children.splice(splitAt), branch.parent);
1253
+ const sibling = createBranchNode(
1254
+ branch.children.splice(splitAt),
1255
+ branch.parent
1256
+ );
1071
1257
  branch.keys.splice(splitAt);
1072
1258
  if (branch.parent === null) {
1073
1259
  state.root = createBranchNode([branch, sibling], null);
@@ -1075,42 +1261,125 @@ var splitBranch = (state, branch) => {
1075
1261
  }
1076
1262
  insertChildAfter(state, branch.parent, branch, sibling);
1077
1263
  };
1264
+
1265
+ // src/btree/entry-lookup.ts
1266
+ var findLeafEntryBySequence = (state, userKey, sequence) => {
1267
+ const targetLeaf = findLeafForKey(state, userKey, sequence);
1268
+ const index = lowerBoundInLeaf(state, targetLeaf, userKey, sequence);
1269
+ if (index >= leafEntryCount(targetLeaf)) return null;
1270
+ const entry = leafEntryAt(targetLeaf, index);
1271
+ if (entry.entryId !== sequence) return null;
1272
+ return { leaf: targetLeaf, index };
1273
+ };
1274
+ var peekEntryById = (state, entryId) => {
1275
+ if (state.entryKeys === null) {
1276
+ throw new BTreeInvariantError(
1277
+ "entryKeys lookup map is not enabled on this tree."
1278
+ );
1279
+ }
1280
+ const userKey = state.entryKeys.get(entryId);
1281
+ if (userKey === void 0) return null;
1282
+ const found = findLeafEntryBySequence(state, userKey, entryId);
1283
+ if (found === null) return null;
1284
+ const entry = leafEntryAt(found.leaf, found.index);
1285
+ return entry;
1286
+ };
1287
+ var updateEntryById = (state, entryId, newValue) => {
1288
+ if (state.entryKeys === null) {
1289
+ throw new BTreeInvariantError(
1290
+ "entryKeys lookup map is not enabled on this tree."
1291
+ );
1292
+ }
1293
+ const userKey = state.entryKeys.get(entryId);
1294
+ if (userKey === void 0) return null;
1295
+ const found = findLeafEntryBySequence(state, userKey, entryId);
1296
+ if (found === null) return null;
1297
+ const entry = leafEntryAt(found.leaf, found.index);
1298
+ const updated = createEntry(entry.key, entry.entryId, newValue);
1299
+ found.leaf.entries[found.leaf.entryOffset + found.index] = updated;
1300
+ return updated;
1301
+ };
1302
+ var removeEntryById = (state, entryId) => {
1303
+ if (state.entryKeys === null) {
1304
+ throw new BTreeInvariantError(
1305
+ "entryKeys lookup map is not enabled on this tree."
1306
+ );
1307
+ }
1308
+ const userKey = state.entryKeys.get(entryId);
1309
+ if (userKey === void 0) return null;
1310
+ const found = findLeafEntryBySequence(state, userKey, entryId);
1311
+ if (found === null) return null;
1312
+ const entry = leafEntryAt(found.leaf, found.index);
1313
+ leafRemoveAt(found.leaf, found.index);
1314
+ state.entryCount -= 1;
1315
+ state.entryKeys.delete(entryId);
1316
+ if (found.index === 0 && leafEntryCount(found.leaf) > 0 && found.leaf.parent !== null) {
1317
+ updateMinKeyInAncestors(found.leaf);
1318
+ }
1319
+ if (found.leaf !== state.root && leafEntryCount(found.leaf) < state.minLeafEntries) {
1320
+ rebalanceAfterLeafRemoval(state, found.leaf);
1321
+ }
1322
+ return entry;
1323
+ };
1324
+
1325
+ // src/btree/mutations.ts
1326
+ var findDuplicateEntry = (state, targetLeaf, key, insertAt) => {
1327
+ if (state.duplicateKeys === "allow") return null;
1328
+ if (insertAt > 0) {
1329
+ const candidate = leafEntryAt(targetLeaf, insertAt - 1);
1330
+ if (state.compareKeys(candidate.key, key) === 0) {
1331
+ return {
1332
+ leaf: targetLeaf,
1333
+ physIndex: targetLeaf.entryOffset + insertAt - 1,
1334
+ entry: candidate
1335
+ };
1336
+ }
1337
+ } else if (targetLeaf.prev !== null && leafEntryCount(targetLeaf.prev) > 0) {
1338
+ const prevLeaf = targetLeaf.prev;
1339
+ const prevCount = leafEntryCount(prevLeaf);
1340
+ const candidate = leafEntryAt(prevLeaf, prevCount - 1);
1341
+ if (state.compareKeys(candidate.key, key) === 0) {
1342
+ return {
1343
+ leaf: prevLeaf,
1344
+ physIndex: prevLeaf.entryOffset + prevCount - 1,
1345
+ entry: candidate
1346
+ };
1347
+ }
1348
+ }
1349
+ return null;
1350
+ };
1078
1351
  var putEntryIntoLeaf = (state, targetLeaf, key, value) => {
1079
1352
  const sequence = state.nextSequence;
1080
1353
  const insertAt = upperBoundInLeaf(state, targetLeaf, key, sequence);
1081
- if (state.duplicateKeys !== "allow") {
1082
- let existingEntry = null;
1083
- if (insertAt > 0) {
1084
- const candidate = leafEntryAt(targetLeaf, insertAt - 1);
1085
- if (state.compareKeys(candidate.key, key) === 0) {
1086
- existingEntry = candidate;
1087
- }
1088
- } else if (targetLeaf.prev !== null && leafEntryCount(targetLeaf.prev) > 0) {
1089
- const prevLeaf = targetLeaf.prev;
1090
- const candidate = leafEntryAt(prevLeaf, leafEntryCount(prevLeaf) - 1);
1091
- if (state.compareKeys(candidate.key, key) === 0) {
1092
- existingEntry = candidate;
1093
- }
1094
- }
1095
- if (existingEntry !== null) {
1096
- if (state.duplicateKeys === "reject") {
1097
- throw new BTreeValidationError("Duplicate key rejected.");
1098
- }
1099
- existingEntry.value = value;
1100
- return existingEntry.entryId;
1354
+ const dup = findDuplicateEntry(state, targetLeaf, key, insertAt);
1355
+ if (dup !== null) {
1356
+ if (state.duplicateKeys === "reject") {
1357
+ throw new BTreeValidationError("Duplicate key rejected.");
1101
1358
  }
1359
+ dup.leaf.entries[dup.physIndex] = createEntry(
1360
+ dup.entry.key,
1361
+ dup.entry.entryId,
1362
+ value
1363
+ );
1364
+ return dup.entry.entryId;
1102
1365
  }
1103
1366
  if (state.nextSequence >= Number.MAX_SAFE_INTEGER) {
1104
1367
  throw new BTreeValidationError("Sequence overflow.");
1105
1368
  }
1106
1369
  state.nextSequence += 1;
1107
- leafInsertAt(targetLeaf, insertAt, { key, entryId: sequence, value });
1370
+ leafInsertAt(
1371
+ targetLeaf,
1372
+ insertAt,
1373
+ createEntry(key, sequence, value)
1374
+ );
1108
1375
  state.entryCount += 1;
1109
1376
  if (state.entryKeys !== null) {
1110
1377
  state.entryKeys.set(sequence, key);
1111
1378
  }
1112
- if (insertAt === 0 && targetLeaf.parent !== null) updateMinKeyInAncestors(targetLeaf);
1113
- if (leafEntryCount(targetLeaf) > state.maxLeafEntries) splitLeaf(state, targetLeaf);
1379
+ if (insertAt === 0 && targetLeaf.parent !== null)
1380
+ updateMinKeyInAncestors(targetLeaf);
1381
+ if (leafEntryCount(targetLeaf) > state.maxLeafEntries)
1382
+ splitLeaf(state, targetLeaf);
1114
1383
  maybeAutoScale(state);
1115
1384
  return sequence;
1116
1385
  };
@@ -1121,7 +1390,8 @@ var putEntry = (state, key, value) => {
1121
1390
  var popFirstEntry = (state) => {
1122
1391
  if (state.entryCount === 0) return null;
1123
1392
  const firstEntry = leafShiftEntry(state.leftmostLeaf);
1124
- if (firstEntry === void 0) throw new BTreeInvariantError("leftmost leaf empty but count > 0");
1393
+ if (firstEntry === void 0)
1394
+ throw new BTreeInvariantError("leftmost leaf empty but count > 0");
1125
1395
  state.entryCount -= 1;
1126
1396
  if (state.entryKeys !== null) {
1127
1397
  state.entryKeys.delete(firstEntry.entryId);
@@ -1137,7 +1407,8 @@ var popFirstEntry = (state) => {
1137
1407
  var popLastEntry = (state) => {
1138
1408
  if (state.entryCount === 0) return null;
1139
1409
  const lastEntry = leafPopEntry(state.rightmostLeaf);
1140
- if (lastEntry === void 0) throw new BTreeInvariantError("rightmost leaf empty but count > 0");
1410
+ if (lastEntry === void 0)
1411
+ throw new BTreeInvariantError("rightmost leaf empty but count > 0");
1141
1412
  state.entryCount -= 1;
1142
1413
  if (state.entryKeys !== null) {
1143
1414
  state.entryKeys.delete(lastEntry.entryId);
@@ -1156,64 +1427,14 @@ var removeFirstMatchingEntry = (state, key) => {
1156
1427
  leafRemoveAt(targetLeaf, removeAt);
1157
1428
  state.entryCount -= 1;
1158
1429
  if (state.entryKeys !== null) {
1159
- state.entryKeys.delete(targetEntry.entryId);
1160
- }
1161
- if (removeAt === 0 && leafEntryCount(targetLeaf) > 0 && targetLeaf.parent !== null) updateMinKeyInAncestors(targetLeaf);
1162
- if (targetLeaf !== state.root && leafEntryCount(targetLeaf) < state.minLeafEntries) {
1163
- rebalanceAfterLeafRemoval(state, targetLeaf);
1164
- }
1165
- return targetEntry;
1166
- };
1167
- var findLeafEntryBySequence = (state, userKey, sequence) => {
1168
- const targetLeaf = findLeafForKey(state, userKey, sequence);
1169
- const index = lowerBoundInLeaf(state, targetLeaf, userKey, sequence);
1170
- if (index >= leafEntryCount(targetLeaf)) return null;
1171
- const entry = leafEntryAt(targetLeaf, index);
1172
- if (entry.entryId !== sequence) return null;
1173
- return { leaf: targetLeaf, index };
1174
- };
1175
- var removeEntryById = (state, entryId) => {
1176
- if (state.entryKeys === null) {
1177
- throw new BTreeInvariantError("entryKeys lookup map is not enabled on this tree.");
1178
- }
1179
- const userKey = state.entryKeys.get(entryId);
1180
- if (userKey === void 0) return null;
1181
- const found = findLeafEntryBySequence(state, userKey, entryId);
1182
- if (found === null) return null;
1183
- const entry = leafEntryAt(found.leaf, found.index);
1184
- leafRemoveAt(found.leaf, found.index);
1185
- state.entryCount -= 1;
1186
- state.entryKeys.delete(entryId);
1187
- if (found.index === 0 && leafEntryCount(found.leaf) > 0 && found.leaf.parent !== null) {
1188
- updateMinKeyInAncestors(found.leaf);
1189
- }
1190
- if (found.leaf !== state.root && leafEntryCount(found.leaf) < state.minLeafEntries) {
1191
- rebalanceAfterLeafRemoval(state, found.leaf);
1192
- }
1193
- return entry;
1194
- };
1195
- var peekEntryById = (state, entryId) => {
1196
- if (state.entryKeys === null) {
1197
- throw new BTreeInvariantError("entryKeys lookup map is not enabled on this tree.");
1198
- }
1199
- const userKey = state.entryKeys.get(entryId);
1200
- if (userKey === void 0) return null;
1201
- const found = findLeafEntryBySequence(state, userKey, entryId);
1202
- if (found === null) return null;
1203
- const entry = leafEntryAt(found.leaf, found.index);
1204
- return entry;
1205
- };
1206
- var updateEntryById = (state, entryId, newValue) => {
1207
- if (state.entryKeys === null) {
1208
- throw new BTreeInvariantError("entryKeys lookup map is not enabled on this tree.");
1430
+ state.entryKeys.delete(targetEntry.entryId);
1209
1431
  }
1210
- const userKey = state.entryKeys.get(entryId);
1211
- if (userKey === void 0) return null;
1212
- const found = findLeafEntryBySequence(state, userKey, entryId);
1213
- if (found === null) return null;
1214
- const entry = leafEntryAt(found.leaf, found.index);
1215
- entry.value = newValue;
1216
- return entry;
1432
+ if (removeAt === 0 && leafEntryCount(targetLeaf) > 0 && targetLeaf.parent !== null)
1433
+ updateMinKeyInAncestors(targetLeaf);
1434
+ if (targetLeaf !== state.root && leafEntryCount(targetLeaf) < state.minLeafEntries) {
1435
+ rebalanceAfterLeafRemoval(state, targetLeaf);
1436
+ }
1437
+ return targetEntry;
1217
1438
  };
1218
1439
  var putManyEntries = (state, entries) => {
1219
1440
  if (entries.length === 0) return [];
@@ -1221,7 +1442,9 @@ var putManyEntries = (state, entries) => {
1221
1442
  for (let i = 1; i < entries.length; i += 1) {
1222
1443
  const cmp = state.compareKeys(entries[i - 1].key, entries[i].key);
1223
1444
  if (cmp > 0) {
1224
- throw new BTreeValidationError("putMany: entries not in ascending order.");
1445
+ throw new BTreeValidationError(
1446
+ "putMany: entries not in ascending order."
1447
+ );
1225
1448
  }
1226
1449
  if (strictlyAscending && cmp === 0) {
1227
1450
  throw new BTreeValidationError(
@@ -1234,7 +1457,12 @@ var putManyEntries = (state, entries) => {
1234
1457
  let hintLeaf = findLeafForKey(state, entries[0].key, state.nextSequence);
1235
1458
  for (let i = 0; i < entries.length; i += 1) {
1236
1459
  const entry = entries[i];
1237
- const targetLeaf = findLeafFromHint(state, hintLeaf, entry.key, state.nextSequence);
1460
+ const targetLeaf = findLeafFromHint(
1461
+ state,
1462
+ hintLeaf,
1463
+ entry.key,
1464
+ state.nextSequence
1465
+ );
1238
1466
  ids[i] = putEntryIntoLeaf(state, targetLeaf, entry.key, entry.value);
1239
1467
  hintLeaf = targetLeaf;
1240
1468
  }
@@ -1249,7 +1477,8 @@ var buildConfigFromState = (state) => {
1249
1477
  compareKeys: state.compareKeys,
1250
1478
  duplicateKeys: state.duplicateKeys,
1251
1479
  enableEntryIdLookup: state.entryKeys !== null,
1252
- autoScale: state.autoScale
1480
+ autoScale: state.autoScale,
1481
+ deleteRebalancePolicy: state.deleteRebalancePolicy
1253
1482
  };
1254
1483
  if (!state.autoScale) {
1255
1484
  config.maxLeafEntries = state.maxLeafEntries;
@@ -1269,17 +1498,17 @@ var serializeToJSON = (state) => {
1269
1498
  }
1270
1499
  leaf = leaf.next;
1271
1500
  }
1272
- return {
1273
- version: 1,
1274
- config: {
1275
- maxLeafEntries: state.maxLeafEntries,
1276
- maxBranchChildren: state.maxBranchChildren,
1277
- duplicateKeys: state.duplicateKeys,
1278
- enableEntryIdLookup: state.entryKeys !== null,
1279
- autoScale: state.autoScale
1280
- },
1281
- entries
1501
+ const config = {
1502
+ maxLeafEntries: state.maxLeafEntries,
1503
+ maxBranchChildren: state.maxBranchChildren,
1504
+ duplicateKeys: state.duplicateKeys,
1505
+ enableEntryIdLookup: state.entryKeys !== null,
1506
+ autoScale: state.autoScale
1282
1507
  };
1508
+ if (state.deleteRebalancePolicy !== "standard") {
1509
+ config.deleteRebalancePolicy = state.deleteRebalancePolicy;
1510
+ }
1511
+ return { version: 1, config, entries };
1283
1512
  };
1284
1513
  var MAX_SERIALIZED_ENTRIES = 1e6;
1285
1514
  var validateStructure = (json) => {
@@ -1342,13 +1571,28 @@ var validateBTreeJSON = (json) => {
1342
1571
  validateStructure(json);
1343
1572
  validateConfig(json.config);
1344
1573
  };
1574
+ var validateBTreeJSONSortOrder = (json, compareKeys) => {
1575
+ const strict = json.config.duplicateKeys !== "allow";
1576
+ for (let i = 1; i < json.entries.length; i += 1) {
1577
+ const cmp = compareKeys(json.entries[i - 1][0], json.entries[i][0]);
1578
+ if (cmp > 0) {
1579
+ throw new BTreeValidationError("fromJSON: entries not sorted.");
1580
+ }
1581
+ if (strict && cmp === 0) {
1582
+ throw new BTreeValidationError(
1583
+ 'fromJSON: duplicate keys require duplicateKeys "allow".'
1584
+ );
1585
+ }
1586
+ }
1587
+ };
1345
1588
  var buildConfigFromJSON = (json, compareKeys) => {
1346
1589
  const cfg = json.config;
1347
1590
  const config = {
1348
1591
  compareKeys,
1349
1592
  duplicateKeys: cfg.duplicateKeys,
1350
1593
  enableEntryIdLookup: cfg.enableEntryIdLookup,
1351
- autoScale: cfg.autoScale
1594
+ autoScale: cfg.autoScale,
1595
+ deleteRebalancePolicy: cfg.deleteRebalancePolicy
1352
1596
  };
1353
1597
  if (!cfg.autoScale) {
1354
1598
  config.maxLeafEntries = cfg.maxLeafEntries;
@@ -1357,6 +1601,44 @@ var buildConfigFromJSON = (json, compareKeys) => {
1357
1601
  return config;
1358
1602
  };
1359
1603
 
1604
+ // src/btree/traversal.ts
1605
+ var snapshotEntries = (state) => {
1606
+ const result = new Array(state.entryCount);
1607
+ let leaf = state.leftmostLeaf;
1608
+ let writeIdx = 0;
1609
+ while (leaf !== null) {
1610
+ const count = leafEntryCount(leaf);
1611
+ for (let i = 0; i < count; i += 1) {
1612
+ result[writeIdx++] = freezeEntry(leafEntryAt(leaf, i));
1613
+ }
1614
+ leaf = leaf.next;
1615
+ }
1616
+ return result;
1617
+ };
1618
+ var collectInternalEntries = (state) => {
1619
+ const result = new Array(state.entryCount);
1620
+ let leaf = state.leftmostLeaf;
1621
+ let writeIdx = 0;
1622
+ while (leaf !== null) {
1623
+ const count = leafEntryCount(leaf);
1624
+ for (let i = 0; i < count; i += 1) {
1625
+ result[writeIdx++] = leafEntryAt(leaf, i);
1626
+ }
1627
+ leaf = leaf.next;
1628
+ }
1629
+ return result;
1630
+ };
1631
+ var forEachEntry = (state, callback, thisArg) => {
1632
+ let leaf = state.leftmostLeaf;
1633
+ while (leaf !== null) {
1634
+ const count = leafEntryCount(leaf);
1635
+ for (let i = 0; i < count; i += 1) {
1636
+ callback.call(thisArg, freezeEntry(leafEntryAt(leaf, i)));
1637
+ }
1638
+ leaf = leaf.next;
1639
+ }
1640
+ };
1641
+
1360
1642
  // src/btree/integrity-helpers.ts
1361
1643
  var nodeMinKey = (node) => {
1362
1644
  if (isLeafNode(node)) {
@@ -1365,7 +1647,10 @@ var nodeMinKey = (node) => {
1365
1647
  return { key: e.key, sequence: e.entryId };
1366
1648
  }
1367
1649
  if (node.childOffset >= node.keys.length) return null;
1368
- return { key: node.keys[node.childOffset].key, sequence: node.keys[node.childOffset].sequence };
1650
+ return {
1651
+ key: node.keys[node.childOffset].key,
1652
+ sequence: node.keys[node.childOffset].sequence
1653
+ };
1369
1654
  };
1370
1655
  var compareNodeKeys = (comparator, leftKey, leftSeq, rightKey, rightSeq) => {
1371
1656
  const cmp = comparator(leftKey, rightKey);
@@ -1387,9 +1672,7 @@ var getNodeMaxKey = (node) => {
1387
1672
  };
1388
1673
  var validateComparatorResult = (result) => {
1389
1674
  if (!Number.isFinite(result)) {
1390
- throw new BTreeValidationError(
1391
- "compareKeys must return a finite number."
1392
- );
1675
+ throw new BTreeValidationError("compareKeys must return a finite number.");
1393
1676
  }
1394
1677
  return result;
1395
1678
  };
@@ -1449,6 +1732,34 @@ var assertTransitivityAsInvariant = (state, first, second, third) => {
1449
1732
  throw error;
1450
1733
  }
1451
1734
  };
1735
+ var validateAdjacentLeafOrdering = (state, previous, cursor) => {
1736
+ const prevMax = getNodeMaxKey(previous);
1737
+ const currentMin = nodeMinKey(cursor);
1738
+ if (prevMax === null || currentMin === null) {
1739
+ throw new BTreeInvariantError(
1740
+ "Non-empty tree leaf chain contains empty leaf node."
1741
+ );
1742
+ }
1743
+ if (compareNodeKeys(
1744
+ state.compareKeys,
1745
+ prevMax.key,
1746
+ prevMax.sequence,
1747
+ currentMin.key,
1748
+ currentMin.sequence
1749
+ ) > 0) {
1750
+ throw new BTreeInvariantError("Adjacent leaf key ranges are out of order.");
1751
+ }
1752
+ const prevCount = leafEntryCount(previous);
1753
+ const curCount = leafEntryCount(cursor);
1754
+ if (state.duplicateKeys !== "allow" && prevCount > 0 && curCount > 0 && state.compareKeys(
1755
+ leafEntryAt(previous, prevCount - 1).key,
1756
+ leafEntryAt(cursor, 0).key
1757
+ ) === 0) {
1758
+ throw new BTreeInvariantError(
1759
+ "Duplicate user key detected across adjacent leaves with uniqueness policy."
1760
+ );
1761
+ }
1762
+ };
1452
1763
  var validateLeafChainStep = (state, cursor, previous, visited) => {
1453
1764
  if (!isLeafNode(cursor)) {
1454
1765
  throw new BTreeInvariantError("Leaf linkage cursor reached non-leaf node.");
@@ -1460,22 +1771,7 @@ var validateLeafChainStep = (state, cursor, previous, visited) => {
1460
1771
  throw new BTreeInvariantError("Leaf prev pointer mismatch.");
1461
1772
  }
1462
1773
  if (previous !== null && isLeafNode(previous)) {
1463
- const prevMax = getNodeMaxKey(previous);
1464
- const currentMin = nodeMinKey(cursor);
1465
- if (prevMax === null || currentMin === null) {
1466
- throw new BTreeInvariantError("Non-empty tree leaf chain contains empty leaf node.");
1467
- }
1468
- if (compareNodeKeys(state.compareKeys, prevMax.key, prevMax.sequence, currentMin.key, currentMin.sequence) > 0) {
1469
- throw new BTreeInvariantError("Adjacent leaf key ranges are out of order.");
1470
- }
1471
- const prevCount = leafEntryCount(previous);
1472
- const curCount = leafEntryCount(cursor);
1473
- if (state.duplicateKeys !== "allow" && prevCount > 0 && curCount > 0 && state.compareKeys(
1474
- leafEntryAt(previous, prevCount - 1).key,
1475
- leafEntryAt(cursor, 0).key
1476
- ) === 0) {
1477
- throw new BTreeInvariantError("Duplicate user key detected across adjacent leaves with uniqueness policy.");
1478
- }
1774
+ validateAdjacentLeafOrdering(state, previous, cursor);
1479
1775
  }
1480
1776
  };
1481
1777
  var validateLeafLinks = (state, expectedLeafCount) => {
@@ -1484,7 +1780,9 @@ var validateLeafLinks = (state, expectedLeafCount) => {
1484
1780
  throw new BTreeInvariantError("Empty tree root must be a leaf node.");
1485
1781
  }
1486
1782
  if (state.leftmostLeaf !== state.root || state.rightmostLeaf !== state.root) {
1487
- throw new BTreeInvariantError("Empty tree leaf pointers must reference root leaf.");
1783
+ throw new BTreeInvariantError(
1784
+ "Empty tree leaf pointers must reference root leaf."
1785
+ );
1488
1786
  }
1489
1787
  return;
1490
1788
  }
@@ -1509,7 +1807,9 @@ var validateLeafLinks = (state, expectedLeafCount) => {
1509
1807
  throw new BTreeInvariantError("Rightmost leaf pointer mismatch.");
1510
1808
  }
1511
1809
  if (leafCount !== expectedLeafCount) {
1512
- throw new BTreeInvariantError("Leaf chain count mismatch with tree traversal count.");
1810
+ throw new BTreeInvariantError(
1811
+ "Leaf chain count mismatch with tree traversal count."
1812
+ );
1513
1813
  }
1514
1814
  };
1515
1815
 
@@ -1536,7 +1836,9 @@ var validateLeafNodeOrdering = (state, node) => {
1536
1836
  leafEntryAt(node, index - 1).key,
1537
1837
  leafEntryAt(node, index).key
1538
1838
  ) === 0) {
1539
- throw new BTreeInvariantError("Duplicate user key detected in tree with uniqueness policy.");
1839
+ throw new BTreeInvariantError(
1840
+ "Duplicate user key detected in tree with uniqueness policy."
1841
+ );
1540
1842
  }
1541
1843
  }
1542
1844
  }
@@ -1544,12 +1846,7 @@ var validateLeafNodeOrdering = (state, node) => {
1544
1846
  const first = leafEntryAt(node, index - 2);
1545
1847
  const second = leafEntryAt(node, index - 1);
1546
1848
  const third = leafEntryAt(node, index);
1547
- assertTransitivityAsInvariant(
1548
- state,
1549
- first.key,
1550
- second.key,
1551
- third.key
1552
- );
1849
+ assertTransitivityAsInvariant(state, first.key, second.key, third.key);
1553
1850
  }
1554
1851
  if (count > state.maxLeafEntries) {
1555
1852
  throw new BTreeInvariantError("Leaf node exceeds maximum occupancy.");
@@ -1558,9 +1855,14 @@ var validateLeafNodeOrdering = (state, node) => {
1558
1855
  var validateLeafNode = (state, node, depth) => {
1559
1856
  validateLeafNodeOrdering(state, node);
1560
1857
  const count = leafEntryCount(node);
1561
- const baseMinLeaf = state.autoScale ? Math.ceil(computeAutoScaleTier(0).maxLeaf / 2) : state.minLeafEntries;
1858
+ let baseMinLeaf = state.autoScale ? Math.ceil(computeAutoScaleTier(0).maxLeaf / 2) : state.minLeafEntries;
1859
+ if (state.deleteRebalancePolicy === "lazy") {
1860
+ baseMinLeaf = applyLazyThreshold(baseMinLeaf);
1861
+ }
1562
1862
  if (node !== state.root && count < baseMinLeaf) {
1563
- throw new BTreeInvariantError("Non-root leaf node violates minimum occupancy.");
1863
+ throw new BTreeInvariantError(
1864
+ "Non-root leaf node violates minimum occupancy."
1865
+ );
1564
1866
  }
1565
1867
  const first = count === 0 ? null : leafEntryAt(node, 0);
1566
1868
  const last = count === 0 ? null : leafEntryAt(node, count - 1);
@@ -1582,76 +1884,110 @@ var validateBranchStructure = (state, node) => {
1582
1884
  }
1583
1885
  const baseMinBranch = state.autoScale ? Math.ceil(computeAutoScaleTier(0).maxBranch / 2) : state.minBranchChildren;
1584
1886
  if (node !== state.root && liveCount < baseMinBranch) {
1585
- throw new BTreeInvariantError("Non-root branch node violates minimum occupancy.");
1887
+ throw new BTreeInvariantError(
1888
+ "Non-root branch node violates minimum occupancy."
1889
+ );
1586
1890
  }
1587
1891
  if (liveCount > state.maxBranchChildren) {
1588
1892
  throw new BTreeInvariantError("Branch node exceeds maximum occupancy.");
1589
1893
  }
1590
1894
  if (node.keys.length !== node.children.length) {
1591
- throw new BTreeInvariantError("Branch keys array length does not match children array length.");
1895
+ throw new BTreeInvariantError(
1896
+ "Branch keys array length does not match children array length."
1897
+ );
1592
1898
  }
1593
1899
  };
1594
1900
  var validateBranchChild = (state, node, childIndex, depth) => {
1595
1901
  const child = node.children[childIndex];
1596
1902
  if (child.parent !== node) {
1597
- throw new BTreeInvariantError("Child-parent pointer mismatch in branch node.");
1903
+ throw new BTreeInvariantError(
1904
+ "Child-parent pointer mismatch in branch node."
1905
+ );
1598
1906
  }
1599
1907
  if (child.indexInParent !== childIndex) {
1600
- throw new BTreeInvariantError("Child indexInParent does not match actual position in parent.");
1908
+ throw new BTreeInvariantError(
1909
+ "Child indexInParent does not match actual position in parent."
1910
+ );
1601
1911
  }
1602
1912
  const childValidation = validateNode(state, child, depth + 1);
1603
1913
  if (childValidation.minKey === null || childValidation.maxKey === null) {
1604
- throw new BTreeInvariantError("Branch child must not be empty in non-root branch tree.");
1914
+ throw new BTreeInvariantError(
1915
+ "Branch child must not be empty in non-root branch tree."
1916
+ );
1605
1917
  }
1606
1918
  const cachedMinKey = node.keys[childIndex];
1607
1919
  const actualMinKey = nodeMinKey(child);
1608
- if (actualMinKey === null || compareNodeKeys(state.compareKeys, cachedMinKey.key, cachedMinKey.sequence, actualMinKey.key, actualMinKey.sequence) !== 0) {
1609
- throw new BTreeInvariantError("Branch cached key does not match actual child minimum key.");
1920
+ if (actualMinKey === null || compareNodeKeys(
1921
+ state.compareKeys,
1922
+ cachedMinKey.key,
1923
+ cachedMinKey.sequence,
1924
+ actualMinKey.key,
1925
+ actualMinKey.sequence
1926
+ ) !== 0) {
1927
+ throw new BTreeInvariantError(
1928
+ "Branch cached key does not match actual child minimum key."
1929
+ );
1610
1930
  }
1611
1931
  return childValidation;
1612
1932
  };
1933
+ var mergeChildValidation = (state, accumulated, childValidation) => {
1934
+ if (accumulated.leafDepth !== null && childValidation.leafDepth !== null && childValidation.leafDepth !== accumulated.leafDepth) {
1935
+ throw new BTreeInvariantError("Leaf depth mismatch detected in tree.");
1936
+ }
1937
+ if (accumulated.leafDepth === null && childValidation.leafDepth !== null) {
1938
+ accumulated.leafDepth = childValidation.leafDepth;
1939
+ }
1940
+ if (accumulated.previousChildMax !== null && compareNodeKeys(
1941
+ state.compareKeys,
1942
+ accumulated.previousChildMax.key,
1943
+ accumulated.previousChildMax.sequence,
1944
+ childValidation.minKey.key,
1945
+ childValidation.minKey.sequence
1946
+ ) >= 0) {
1947
+ throw new BTreeInvariantError(
1948
+ "Branch child key ranges are not strictly ordered."
1949
+ );
1950
+ }
1951
+ if (accumulated.minKey === null) accumulated.minKey = childValidation.minKey;
1952
+ accumulated.maxKey = childValidation.maxKey;
1953
+ accumulated.previousChildMax = childValidation.maxKey;
1954
+ accumulated.leafCount += childValidation.leafCount;
1955
+ accumulated.branchCount += childValidation.branchCount;
1956
+ accumulated.entryCount += childValidation.entryCount;
1957
+ };
1613
1958
  var validateNode = (state, node, depth) => {
1614
1959
  if (isLeafNode(node)) {
1615
1960
  return validateLeafNode(state, node, depth);
1616
1961
  }
1617
1962
  validateBranchStructure(state, node);
1618
- let leafDepth = null;
1619
- let leafCount = 0;
1620
- let branchCount = 1;
1621
- let entryCount = 0;
1622
- let minKey = null;
1623
- let maxKey = null;
1624
- let previousChildMax = null;
1963
+ const acc = {
1964
+ leafDepth: null,
1965
+ leafCount: 0,
1966
+ branchCount: 1,
1967
+ entryCount: 0,
1968
+ minKey: null,
1969
+ maxKey: null,
1970
+ previousChildMax: null
1971
+ };
1625
1972
  for (let childIndex = node.childOffset; childIndex < node.children.length; childIndex += 1) {
1626
1973
  const childValidation = validateBranchChild(state, node, childIndex, depth);
1627
- if (leafDepth !== null && childValidation.leafDepth !== null && childValidation.leafDepth !== leafDepth) {
1628
- throw new BTreeInvariantError("Leaf depth mismatch detected in tree.");
1629
- }
1630
- if (leafDepth === null && childValidation.leafDepth !== null) {
1631
- leafDepth = childValidation.leafDepth;
1632
- }
1633
- if (previousChildMax !== null && compareNodeKeys(
1634
- state.compareKeys,
1635
- previousChildMax.key,
1636
- previousChildMax.sequence,
1637
- childValidation.minKey.key,
1638
- childValidation.minKey.sequence
1639
- ) >= 0) {
1640
- throw new BTreeInvariantError("Branch child key ranges are not strictly ordered.");
1641
- }
1642
- if (minKey === null) minKey = childValidation.minKey;
1643
- maxKey = childValidation.maxKey;
1644
- previousChildMax = childValidation.maxKey;
1645
- leafCount += childValidation.leafCount;
1646
- branchCount += childValidation.branchCount;
1647
- entryCount += childValidation.entryCount;
1974
+ mergeChildValidation(state, acc, childValidation);
1648
1975
  }
1649
- return { minKey, maxKey, leafDepth, leafCount, branchCount, entryCount };
1976
+ return {
1977
+ minKey: acc.minKey,
1978
+ maxKey: acc.maxKey,
1979
+ leafDepth: acc.leafDepth,
1980
+ leafCount: acc.leafCount,
1981
+ branchCount: acc.branchCount,
1982
+ entryCount: acc.entryCount
1983
+ };
1650
1984
  };
1651
1985
  var assertInvariants = (state) => {
1652
1986
  const validation = validateNode(state, state.root, 0);
1653
1987
  if (validation.entryCount !== state.entryCount) {
1654
- throw new BTreeInvariantError("Index entry count mismatch between tree traversal and tracked state.");
1988
+ throw new BTreeInvariantError(
1989
+ "Index entry count mismatch between tree traversal and tracked state."
1990
+ );
1655
1991
  }
1656
1992
  validateLeafLinks(state, validation.leafCount);
1657
1993
  };
@@ -1707,7 +2043,7 @@ var InMemoryBTree = class _InMemoryBTree {
1707
2043
  remove(key) {
1708
2044
  const entry = removeFirstMatchingEntry(this.state, key);
1709
2045
  if (entry === null) return null;
1710
- return toPublicEntry(entry);
2046
+ return freezeEntry(entry);
1711
2047
  }
1712
2048
  removeById(entryId) {
1713
2049
  if (this.state.entryKeys === null) {
@@ -1715,7 +2051,7 @@ var InMemoryBTree = class _InMemoryBTree {
1715
2051
  }
1716
2052
  const entry = removeEntryById(this.state, entryId);
1717
2053
  if (entry === null) return null;
1718
- return toPublicEntry(entry);
2054
+ return freezeEntry(entry);
1719
2055
  }
1720
2056
  peekById(entryId) {
1721
2057
  if (this.state.entryKeys === null) {
@@ -1723,7 +2059,7 @@ var InMemoryBTree = class _InMemoryBTree {
1723
2059
  }
1724
2060
  const entry = peekEntryById(this.state, entryId);
1725
2061
  if (entry === null) return null;
1726
- return toPublicEntry(entry);
2062
+ return freezeEntry(entry);
1727
2063
  }
1728
2064
  updateById(entryId, value) {
1729
2065
  if (this.state.entryKeys === null) {
@@ -1731,30 +2067,30 @@ var InMemoryBTree = class _InMemoryBTree {
1731
2067
  }
1732
2068
  const entry = updateEntryById(this.state, entryId, value);
1733
2069
  if (entry === null) return null;
1734
- return toPublicEntry(entry);
2070
+ return freezeEntry(entry);
1735
2071
  }
1736
2072
  popFirst() {
1737
2073
  const entry = popFirstEntry(this.state);
1738
2074
  if (entry === null) return null;
1739
- return toPublicEntry(entry);
2075
+ return freezeEntry(entry);
1740
2076
  }
1741
2077
  peekFirst() {
1742
2078
  if (this.state.entryCount === 0) {
1743
2079
  return null;
1744
2080
  }
1745
- return toPublicEntry(leafEntryAt(this.state.leftmostLeaf, 0));
2081
+ return freezeEntry(leafEntryAt(this.state.leftmostLeaf, 0));
1746
2082
  }
1747
2083
  peekLast() {
1748
2084
  if (this.state.entryCount === 0) {
1749
2085
  return null;
1750
2086
  }
1751
2087
  const leaf = this.state.rightmostLeaf;
1752
- return toPublicEntry(leafEntryAt(leaf, leafEntryCount(leaf) - 1));
2088
+ return freezeEntry(leafEntryAt(leaf, leafEntryCount(leaf) - 1));
1753
2089
  }
1754
2090
  popLast() {
1755
2091
  const entry = popLastEntry(this.state);
1756
2092
  if (entry === null) return null;
1757
- return toPublicEntry(entry);
2093
+ return freezeEntry(entry);
1758
2094
  }
1759
2095
  clear() {
1760
2096
  const emptyLeaf = createLeafNode([], null);
@@ -1764,16 +2100,9 @@ var InMemoryBTree = class _InMemoryBTree {
1764
2100
  this.state.entryCount = 0;
1765
2101
  this.state._cursor.leaf = emptyLeaf;
1766
2102
  this.state._cursor.index = 0;
1767
- if (this.state.entryKeys !== null) {
1768
- this.state.entryKeys.clear();
1769
- }
2103
+ this.state.entryKeys?.clear();
1770
2104
  if (this.state.autoScale) {
1771
- const tier = computeAutoScaleTier(0);
1772
- this.state.maxLeafEntries = tier.maxLeaf;
1773
- this.state.maxBranchChildren = tier.maxBranch;
1774
- this.state.minLeafEntries = minOccupancy(tier.maxLeaf);
1775
- this.state.minBranchChildren = minOccupancy(tier.maxBranch);
1776
- this.state._nextAutoScaleThreshold = computeNextAutoScaleThreshold(0);
2105
+ resetAutoScaleToTier0(this.state);
1777
2106
  }
1778
2107
  }
1779
2108
  get(key) {
@@ -1787,65 +2116,39 @@ var InMemoryBTree = class _InMemoryBTree {
1787
2116
  findFirst(key) {
1788
2117
  const found = findFirstMatchingUserKey(this.state, key);
1789
2118
  if (found === null) return null;
1790
- return toPublicEntry(leafEntryAt(found.leaf, found.index));
2119
+ return freezeEntry(leafEntryAt(found.leaf, found.index));
1791
2120
  }
1792
- /**
1793
- * Returns the last entry whose key matches `key`, or `null` if not found.
1794
- * Useful when `duplicateKeys` is `'allow'` and multiple entries share the same key.
1795
- */
1796
2121
  findLast(key) {
1797
2122
  const found = findLastMatchingUserKey(this.state, key);
1798
2123
  if (found === null) return null;
1799
- return toPublicEntry(leafEntryAt(found.leaf, found.index));
2124
+ return freezeEntry(leafEntryAt(found.leaf, found.index));
1800
2125
  }
1801
- /**
1802
- * Returns the smallest key in the tree that is strictly greater than `key`,
1803
- * or `null` if no such key exists.
1804
- */
1805
2126
  nextHigherKey(key) {
1806
2127
  return findNextHigherKey(this.state, key);
1807
2128
  }
1808
- /**
1809
- * Returns the largest key in the tree that is strictly less than `key`,
1810
- * or `null` if no such key exists.
1811
- */
1812
2129
  nextLowerKey(key) {
1813
2130
  return findNextLowerKey(this.state, key);
1814
2131
  }
1815
- /**
1816
- * Returns the entry for `key` if it exists; otherwise returns the entry with
1817
- * the largest key strictly less than `key`. Returns `null` when the tree is
1818
- * empty or every key is greater than `key`.
1819
- */
1820
2132
  getPairOrNextLower(key) {
1821
2133
  const found = findPairOrNextLower(this.state, key);
1822
2134
  if (found === null) return null;
1823
- return toPublicEntry(leafEntryAt(found.leaf, found.index));
2135
+ return freezeEntry(leafEntryAt(found.leaf, found.index));
1824
2136
  }
1825
- /**
1826
- * Returns the number of entries whose keys fall within [`startKey`, `endKey`].
1827
- * Pass `options` to make either bound exclusive.
1828
- */
1829
2137
  count(startKey, endKey, options) {
1830
2138
  return countRangeEntries(this.state, startKey, endKey, options);
1831
2139
  }
1832
- /**
1833
- * Deletes all entries whose keys fall within [`startKey`, `endKey`].
1834
- * Pass `options` to make either bound exclusive.
1835
- * @returns The number of entries deleted.
1836
- */
1837
2140
  deleteRange(startKey, endKey, options) {
1838
2141
  return deleteRangeEntries(this.state, startKey, endKey, options);
1839
2142
  }
1840
2143
  range(startKey, endKey, options) {
1841
- return rangeQueryEntries(this.state, startKey, endKey, options).map(toPublicEntry);
2144
+ return rangeQueryPublicEntries(this.state, startKey, endKey, options);
1842
2145
  }
1843
2146
  *entries() {
1844
2147
  let leaf = this.state.leftmostLeaf;
1845
2148
  while (leaf !== null) {
1846
2149
  const count = leafEntryCount(leaf);
1847
2150
  for (let i = 0; i < count; i += 1) {
1848
- yield toPublicEntry(leafEntryAt(leaf, i));
2151
+ yield freezeEntry(leafEntryAt(leaf, i));
1849
2152
  }
1850
2153
  leaf = leaf.next;
1851
2154
  }
@@ -1855,7 +2158,7 @@ var InMemoryBTree = class _InMemoryBTree {
1855
2158
  while (leaf !== null) {
1856
2159
  const count = leafEntryCount(leaf);
1857
2160
  for (let i = count - 1; i >= 0; i -= 1) {
1858
- yield toPublicEntry(leafEntryAt(leaf, i));
2161
+ yield freezeEntry(leafEntryAt(leaf, i));
1859
2162
  }
1860
2163
  leaf = leaf.prev;
1861
2164
  }
@@ -1873,55 +2176,26 @@ var InMemoryBTree = class _InMemoryBTree {
1873
2176
  [Symbol.iterator]() {
1874
2177
  return this.entries();
1875
2178
  }
2179
+ forEachRange(startKey, endKey, callback, options) {
2180
+ forEachRangeEntries(this.state, startKey, endKey, callback, options);
2181
+ }
1876
2182
  forEach(callback, thisArg) {
1877
- let leaf = this.state.leftmostLeaf;
1878
- while (leaf !== null) {
1879
- const count = leafEntryCount(leaf);
1880
- for (let i = 0; i < count; i += 1) {
1881
- callback.call(thisArg, toPublicEntry(leafEntryAt(leaf, i)));
1882
- }
1883
- leaf = leaf.next;
1884
- }
2183
+ forEachEntry(this.state, callback, thisArg);
1885
2184
  }
1886
2185
  snapshot() {
1887
- const result = new Array(this.state.entryCount);
1888
- let leaf = this.state.leftmostLeaf;
1889
- let writeIdx = 0;
1890
- while (leaf !== null) {
1891
- const count = leafEntryCount(leaf);
1892
- for (let i = 0; i < count; i += 1) {
1893
- result[writeIdx++] = toPublicEntry(leafEntryAt(leaf, i));
1894
- }
1895
- leaf = leaf.next;
1896
- }
1897
- return result;
2186
+ return snapshotEntries(this.state);
1898
2187
  }
1899
- /**
1900
- * Returns a structurally independent `InMemoryBTree` with identical
1901
- * configuration and entries. The tree structure (nodes, links, entry IDs)
1902
- * is fully independent, but stored key and value references are shared
1903
- * with the source tree.
1904
- * Note: `EntryId` values are reassigned in the clone — IDs from the source tree are not valid for the clone.
1905
- */
1906
2188
  clone() {
1907
- const cloned = new _InMemoryBTree(buildConfigFromState(this.state));
2189
+ const cloned = new _InMemoryBTree(
2190
+ buildConfigFromState(this.state)
2191
+ );
1908
2192
  applyAutoScaleCapacitySnapshot(
1909
2193
  cloned.state,
1910
2194
  this.state.maxLeafEntries,
1911
2195
  this.state.maxBranchChildren
1912
2196
  );
1913
2197
  if (this.state.entryCount > 0) {
1914
- const pairs = new Array(this.state.entryCount);
1915
- let leaf = this.state.leftmostLeaf;
1916
- let writeIdx = 0;
1917
- while (leaf !== null) {
1918
- const count = leafEntryCount(leaf);
1919
- for (let i = 0; i < count; i += 1) {
1920
- pairs[writeIdx++] = leafEntryAt(leaf, i);
1921
- }
1922
- leaf = leaf.next;
1923
- }
1924
- cloned.putMany(pairs);
2198
+ cloned.putMany(collectInternalEntries(this.state));
1925
2199
  }
1926
2200
  return cloned;
1927
2201
  }
@@ -1930,26 +2204,19 @@ var InMemoryBTree = class _InMemoryBTree {
1930
2204
  }
1931
2205
  static fromJSON(json, compareKeys) {
1932
2206
  validateBTreeJSON(json);
1933
- const strict = json.config.duplicateKeys !== "allow";
1934
- for (let i = 1; i < json.entries.length; i += 1) {
1935
- const cmp = compareKeys(json.entries[i - 1][0], json.entries[i][0]);
1936
- if (cmp > 0) {
1937
- throw new BTreeValidationError("fromJSON: entries not sorted.");
1938
- }
1939
- if (strict && cmp === 0) {
1940
- throw new BTreeValidationError(
1941
- 'fromJSON: duplicate keys require duplicateKeys "allow".'
1942
- );
1943
- }
1944
- }
1945
- const tree = new _InMemoryBTree(buildConfigFromJSON(json, compareKeys));
2207
+ validateBTreeJSONSortOrder(json, compareKeys);
2208
+ const tree = new _InMemoryBTree(
2209
+ buildConfigFromJSON(json, compareKeys)
2210
+ );
1946
2211
  applyAutoScaleCapacitySnapshot(
1947
2212
  tree.state,
1948
2213
  json.config.maxLeafEntries,
1949
2214
  json.config.maxBranchChildren
1950
2215
  );
1951
2216
  if (json.entries.length > 0) {
1952
- const pairs = new Array(json.entries.length);
2217
+ const pairs = new Array(
2218
+ json.entries.length
2219
+ );
1953
2220
  for (let i = 0; i < json.entries.length; i += 1) {
1954
2221
  pairs[i] = { key: json.entries[i][0], value: json.entries[i][1] };
1955
2222
  }
@@ -1993,13 +2260,17 @@ var assertNeverMutation = (mutation) => {
1993
2260
  var validatePutManyEntries = (entries) => {
1994
2261
  for (const entry of entries) {
1995
2262
  if (typeof entry !== "object" || entry === null || !("key" in entry) || !("value" in entry)) {
1996
- throw new BTreeConcurrencyError("Malformed putMany mutation: each entry must have key and value.");
2263
+ throw new BTreeConcurrencyError(
2264
+ "Malformed putMany mutation: each entry must have key and value."
2265
+ );
1997
2266
  }
1998
2267
  }
1999
2268
  };
2000
2269
  var validateInitMutation = (m, expectedConfigFingerprint) => {
2001
2270
  if (typeof m.configFingerprint !== "string") {
2002
- throw new BTreeConcurrencyError("Malformed init mutation: missing configFingerprint.");
2271
+ throw new BTreeConcurrencyError(
2272
+ "Malformed init mutation: missing configFingerprint."
2273
+ );
2003
2274
  }
2004
2275
  if (expectedConfigFingerprint !== void 0 && m.configFingerprint !== expectedConfigFingerprint) {
2005
2276
  throw new BTreeConcurrencyError(
@@ -2007,45 +2278,70 @@ var validateInitMutation = (m, expectedConfigFingerprint) => {
2007
2278
  );
2008
2279
  }
2009
2280
  };
2010
- var validateSingleMutation = (m, expectedConfigFingerprint) => {
2281
+ var validateMutationFields = (m) => {
2011
2282
  switch (m.type) {
2012
- case "init":
2013
- validateInitMutation(m, expectedConfigFingerprint);
2014
- break;
2015
2283
  case "put":
2016
2284
  if (!("key" in m) || !("value" in m)) {
2017
- throw new BTreeConcurrencyError("Malformed put mutation: missing key or value.");
2285
+ throw new BTreeConcurrencyError(
2286
+ "Malformed put mutation: missing key or value."
2287
+ );
2018
2288
  }
2019
2289
  break;
2020
2290
  case "remove":
2021
2291
  if (!("key" in m)) {
2022
- throw new BTreeConcurrencyError("Malformed remove mutation: missing key.");
2292
+ throw new BTreeConcurrencyError(
2293
+ "Malformed remove mutation: missing key."
2294
+ );
2023
2295
  }
2024
2296
  break;
2025
2297
  case "removeById":
2026
2298
  if (!("entryId" in m)) {
2027
- throw new BTreeConcurrencyError("Malformed removeById mutation: missing entryId.");
2299
+ throw new BTreeConcurrencyError(
2300
+ "Malformed removeById mutation: missing entryId."
2301
+ );
2028
2302
  }
2029
2303
  break;
2030
2304
  case "updateById":
2031
2305
  if (!("entryId" in m) || !("value" in m)) {
2032
- throw new BTreeConcurrencyError("Malformed updateById mutation: missing entryId or value.");
2306
+ throw new BTreeConcurrencyError(
2307
+ "Malformed updateById mutation: missing entryId or value."
2308
+ );
2033
2309
  }
2034
2310
  break;
2035
- case "popFirst":
2036
- case "popLast":
2037
- break;
2038
2311
  case "putMany":
2039
2312
  if (!("entries" in m) || !Array.isArray(m.entries)) {
2040
- throw new BTreeConcurrencyError("Malformed putMany mutation: missing entries array.");
2313
+ throw new BTreeConcurrencyError(
2314
+ "Malformed putMany mutation: missing entries array."
2315
+ );
2041
2316
  }
2042
2317
  validatePutManyEntries(m.entries);
2043
2318
  break;
2044
2319
  case "deleteRange":
2045
2320
  if (!("startKey" in m) || !("endKey" in m)) {
2046
- throw new BTreeConcurrencyError("Malformed deleteRange mutation: missing startKey or endKey.");
2321
+ throw new BTreeConcurrencyError(
2322
+ "Malformed deleteRange mutation: missing startKey or endKey."
2323
+ );
2047
2324
  }
2048
2325
  break;
2326
+ default:
2327
+ break;
2328
+ }
2329
+ };
2330
+ var validateSingleMutation = (m, expectedConfigFingerprint) => {
2331
+ switch (m.type) {
2332
+ case "init":
2333
+ validateInitMutation(m, expectedConfigFingerprint);
2334
+ break;
2335
+ case "put":
2336
+ case "remove":
2337
+ case "removeById":
2338
+ case "updateById":
2339
+ case "putMany":
2340
+ case "deleteRange":
2341
+ validateMutationFields(m);
2342
+ break;
2343
+ case "popFirst":
2344
+ case "popLast":
2049
2345
  case "clear":
2050
2346
  break;
2051
2347
  default:
@@ -2057,7 +2353,9 @@ var validateSingleMutation = (m, expectedConfigFingerprint) => {
2057
2353
  var validateMutationBatch = (mutations, expectedConfigFingerprint) => {
2058
2354
  for (const mutation of mutations) {
2059
2355
  if (typeof mutation !== "object" || mutation === null) {
2060
- throw new BTreeConcurrencyError("Malformed mutation: expected an object.");
2356
+ throw new BTreeConcurrencyError(
2357
+ "Malformed mutation: expected an object."
2358
+ );
2061
2359
  }
2062
2360
  validateSingleMutation(
2063
2361
  mutation,
@@ -2092,9 +2390,7 @@ var normalizeReadMode = (value) => {
2092
2390
  return "strong";
2093
2391
  }
2094
2392
  if (value !== "strong" && value !== "local") {
2095
- throw new BTreeConcurrencyError(
2096
- `readMode: must be 'strong' or 'local'.`
2097
- );
2393
+ throw new BTreeConcurrencyError(`readMode: must be 'strong' or 'local'.`);
2098
2394
  }
2099
2395
  return value;
2100
2396
  };
@@ -2144,7 +2440,11 @@ var applyMutationLocal = (tree, mutation, onInit) => {
2144
2440
  case "popLast":
2145
2441
  return tree.popLast();
2146
2442
  case "deleteRange":
2147
- return tree.deleteRange(mutation.startKey, mutation.endKey, mutation.options);
2443
+ return tree.deleteRange(
2444
+ mutation.startKey,
2445
+ mutation.endKey,
2446
+ mutation.options
2447
+ );
2148
2448
  case "clear":
2149
2449
  tree.clear();
2150
2450
  return null;
@@ -2191,7 +2491,9 @@ var createPutManyEvaluator = (entries, duplicateKeys, compareKeys) => {
2191
2491
  for (let i = 1; i < entries.length; i += 1) {
2192
2492
  const cmp = compareKeys(entries[i - 1].key, entries[i].key);
2193
2493
  if (cmp > 0) {
2194
- throw new BTreeValidationError("putMany: entries not in ascending order.");
2494
+ throw new BTreeValidationError(
2495
+ "putMany: entries not in ascending order."
2496
+ );
2195
2497
  }
2196
2498
  if (strictlyAscending && cmp === 0) {
2197
2499
  throw new BTreeValidationError(
@@ -2222,59 +2524,48 @@ var createClearEvaluator = () => {
2222
2524
  return () => ({ type: "clear" });
2223
2525
  };
2224
2526
 
2225
- // src/concurrency/ConcurrentInMemoryBTree.ts
2226
- var ConcurrentInMemoryBTree = class {
2227
- constructor(config) {
2228
- this.store = config.store;
2229
- this.compareKeys = config.compareKeys;
2230
- this.maxRetries = normalizeMaxRetries(config.maxRetries);
2231
- this.maxSyncMutationsPerBatch = normalizeMaxSyncMutationsPerBatch(
2232
- config.maxSyncMutationsPerBatch
2527
+ // src/concurrency/syncLogValidation.ts
2528
+ var validateSyncLog = (log, maxSyncMutationsPerBatch) => {
2529
+ if (typeof log.version !== "bigint") {
2530
+ throw new BTreeConcurrencyError("Store contract: version must be bigint.");
2531
+ }
2532
+ if (!Array.isArray(log.mutations)) {
2533
+ throw new BTreeConcurrencyError(
2534
+ "Store contract: mutations must be an array."
2233
2535
  );
2234
- this.duplicateKeys = config.duplicateKeys ?? "replace";
2235
- this.readMode = normalizeReadMode(config.readMode);
2236
- this.configFingerprint = computeConfigFingerprint(config);
2237
- this.tree = new InMemoryBTree({
2238
- compareKeys: config.compareKeys,
2239
- maxLeafEntries: config.maxLeafEntries,
2240
- maxBranchChildren: config.maxBranchChildren,
2241
- duplicateKeys: config.duplicateKeys,
2242
- enableEntryIdLookup: config.enableEntryIdLookup,
2243
- autoScale: config.autoScale
2244
- });
2536
+ }
2537
+ if (log.mutations.length > maxSyncMutationsPerBatch) {
2538
+ throw new BTreeConcurrencyError(
2539
+ `Sync batch exceeded limit (${String(maxSyncMutationsPerBatch)}).`
2540
+ );
2541
+ }
2542
+ };
2543
+
2544
+ // src/concurrency/coordinator.ts
2545
+ var Coordinator = class {
2546
+ constructor(tree, store, maxRetries, maxSyncMutationsPerBatch, configFingerprint, readMode) {
2547
+ this.tree = tree;
2548
+ this.store = store;
2549
+ this.maxRetries = maxRetries;
2550
+ this.maxSyncMutationsPerBatch = maxSyncMutationsPerBatch;
2551
+ this.configFingerprint = configFingerprint;
2552
+ this.readMode = readMode;
2245
2553
  this.currentVersion = 0n;
2246
2554
  this.operationQueue = Promise.resolve();
2247
2555
  this.initSeen = false;
2248
2556
  this.corrupted = false;
2249
2557
  }
2250
- async sync() {
2251
- await this.runExclusive(async () => {
2252
- await this.syncUnlocked();
2253
- });
2254
- }
2255
2558
  async syncUnlocked() {
2256
2559
  const log = await this.store.getLogEntriesSince(this.currentVersion);
2257
- if (typeof log.version !== "bigint") {
2258
- throw new BTreeConcurrencyError("Store contract: version must be bigint.");
2259
- }
2260
- if (!Array.isArray(log.mutations)) {
2261
- throw new BTreeConcurrencyError("Store contract: mutations must be an array.");
2262
- }
2263
- if (log.mutations.length > this.maxSyncMutationsPerBatch) {
2264
- throw new BTreeConcurrencyError(
2265
- `Sync batch exceeded limit (${String(this.maxSyncMutationsPerBatch)}).`
2266
- );
2267
- }
2268
- if (log.version <= this.currentVersion) {
2269
- return;
2270
- }
2560
+ validateSyncLog(log, this.maxSyncMutationsPerBatch);
2561
+ if (log.version <= this.currentVersion) return;
2271
2562
  validateMutationBatch(log.mutations, this.configFingerprint);
2272
2563
  try {
2273
- for (const mutation of log.mutations) {
2274
- applyMutationLocal(this.tree, mutation, () => {
2275
- this.initSeen = true;
2276
- });
2277
- }
2564
+ const markInit = () => {
2565
+ this.initSeen = true;
2566
+ };
2567
+ for (const mutation of log.mutations)
2568
+ applyMutationLocal(this.tree, mutation, markInit);
2278
2569
  this.currentVersion = log.version;
2279
2570
  } catch (error) {
2280
2571
  this.corrupted = true;
@@ -2286,11 +2577,10 @@ var ConcurrentInMemoryBTree = class {
2286
2577
  }
2287
2578
  runExclusive(operation) {
2288
2579
  const run = async () => {
2289
- if (this.corrupted) {
2580
+ if (this.corrupted)
2290
2581
  throw new BTreeConcurrencyError(
2291
2582
  "Instance is permanently corrupted. Discard and create a new instance."
2292
2583
  );
2293
- }
2294
2584
  return operation();
2295
2585
  };
2296
2586
  const result = this.operationQueue.then(run, run);
@@ -2302,40 +2592,38 @@ var ConcurrentInMemoryBTree = class {
2302
2592
  }
2303
2593
  readOp(fn) {
2304
2594
  return this.runExclusive(async () => {
2305
- if (this.readMode === "strong") {
2306
- await this.syncUnlocked();
2307
- }
2595
+ if (this.readMode === "strong") await this.syncUnlocked();
2308
2596
  return fn(this.tree);
2309
2597
  });
2310
2598
  }
2311
- async appendMutationAndApplyUnlocked(evaluate) {
2599
+ async appendAndApply(evaluate) {
2312
2600
  for (let attempt = 0; attempt < this.maxRetries; attempt += 1) {
2313
2601
  await this.syncUnlocked();
2314
2602
  const mutation = evaluate(this.tree);
2315
- if (mutation === null) {
2316
- return null;
2317
- }
2603
+ if (mutation === null) return null;
2318
2604
  const expectedVersion = this.currentVersion;
2319
- const mutations = this.initSeen ? [mutation] : [{ type: "init", configFingerprint: this.configFingerprint }, mutation];
2605
+ const mutations = this.initSeen ? [mutation] : [
2606
+ { type: "init", configFingerprint: this.configFingerprint },
2607
+ mutation
2608
+ ];
2320
2609
  const appendResult = await this.store.append(expectedVersion, mutations);
2321
2610
  assertAppendVersionContract(expectedVersion, appendResult);
2322
2611
  if (appendResult.applied) {
2323
2612
  try {
2613
+ const markInit = () => {
2614
+ this.initSeen = true;
2615
+ };
2324
2616
  for (const m of mutations) {
2325
2617
  if (m === mutation) break;
2326
- applyMutationLocal(this.tree, m, () => {
2327
- this.initSeen = true;
2328
- });
2618
+ applyMutationLocal(this.tree, m, markInit);
2329
2619
  }
2330
- const localResult = applyMutationLocal(
2620
+ const result = applyMutationLocal(
2331
2621
  this.tree,
2332
2622
  mutation,
2333
- () => {
2334
- this.initSeen = true;
2335
- }
2623
+ markInit
2336
2624
  );
2337
2625
  this.currentVersion = appendResult.version;
2338
- return localResult;
2626
+ return result;
2339
2627
  } catch (error) {
2340
2628
  this.corrupted = true;
2341
2629
  const cause = error instanceof Error ? error.message : String(error);
@@ -2349,137 +2637,159 @@ var ConcurrentInMemoryBTree = class {
2349
2637
  `Mutation failed after ${String(this.maxRetries)} retries.`
2350
2638
  );
2351
2639
  }
2352
- async put(key, value) {
2353
- return this.runExclusive(async () => {
2354
- return this.appendMutationAndApplyUnlocked(
2355
- createPutEvaluator(this.duplicateKeys, key, value)
2356
- );
2640
+ writeOp(evaluator) {
2641
+ return this.runExclusive(async () => this.appendAndApply(evaluator));
2642
+ }
2643
+ };
2644
+
2645
+ // src/concurrency/ConcurrentInMemoryBTree.ts
2646
+ var ConcurrentInMemoryBTree = class {
2647
+ constructor(config) {
2648
+ this.compareKeys = config.compareKeys;
2649
+ this.duplicateKeys = config.duplicateKeys ?? "replace";
2650
+ const tree = new InMemoryBTree({
2651
+ compareKeys: config.compareKeys,
2652
+ maxLeafEntries: config.maxLeafEntries,
2653
+ maxBranchChildren: config.maxBranchChildren,
2654
+ duplicateKeys: config.duplicateKeys,
2655
+ enableEntryIdLookup: config.enableEntryIdLookup,
2656
+ autoScale: config.autoScale,
2657
+ deleteRebalancePolicy: config.deleteRebalancePolicy
2357
2658
  });
2659
+ this.coord = new Coordinator(
2660
+ tree,
2661
+ config.store,
2662
+ normalizeMaxRetries(config.maxRetries),
2663
+ normalizeMaxSyncMutationsPerBatch(config.maxSyncMutationsPerBatch),
2664
+ computeConfigFingerprint(config),
2665
+ normalizeReadMode(config.readMode)
2666
+ );
2358
2667
  }
2359
- async remove(key) {
2360
- return this.runExclusive(async () => {
2361
- return this.appendMutationAndApplyUnlocked(createRemoveEvaluator(key));
2668
+ async sync() {
2669
+ await this.coord.runExclusive(async () => {
2670
+ await this.coord.syncUnlocked();
2362
2671
  });
2363
2672
  }
2364
- async removeById(entryId) {
2365
- return this.runExclusive(async () => {
2366
- return this.appendMutationAndApplyUnlocked(createRemoveByIdEvaluator(entryId));
2673
+ async syncThenRead(fn) {
2674
+ return this.coord.runExclusive(async () => {
2675
+ await this.coord.syncUnlocked();
2676
+ return fn(this.coord.tree);
2367
2677
  });
2368
2678
  }
2679
+ async put(key, value) {
2680
+ return this.coord.writeOp(
2681
+ createPutEvaluator(this.duplicateKeys, key, value)
2682
+ );
2683
+ }
2684
+ async remove(key) {
2685
+ return this.coord.writeOp(createRemoveEvaluator(key));
2686
+ }
2687
+ async removeById(entryId) {
2688
+ return this.coord.writeOp(createRemoveByIdEvaluator(entryId));
2689
+ }
2369
2690
  async updateById(entryId, value) {
2370
- return this.runExclusive(async () => {
2371
- return this.appendMutationAndApplyUnlocked(createUpdateByIdEvaluator(entryId, value));
2372
- });
2691
+ return this.coord.writeOp(createUpdateByIdEvaluator(entryId, value));
2373
2692
  }
2374
2693
  async popFirst() {
2375
- return this.runExclusive(async () => {
2376
- return this.appendMutationAndApplyUnlocked(createPopFirstEvaluator());
2377
- });
2694
+ return this.coord.writeOp(createPopFirstEvaluator());
2378
2695
  }
2379
2696
  async popLast() {
2380
- return this.runExclusive(async () => {
2381
- return this.appendMutationAndApplyUnlocked(createPopLastEvaluator());
2382
- });
2697
+ return this.coord.writeOp(createPopLastEvaluator());
2383
2698
  }
2384
2699
  async putMany(entries) {
2385
- if (entries.length === 0) {
2386
- return [];
2387
- }
2388
- return this.runExclusive(async () => {
2389
- return this.appendMutationAndApplyUnlocked(
2390
- createPutManyEvaluator(entries, this.duplicateKeys, this.compareKeys)
2391
- );
2392
- });
2700
+ if (entries.length === 0) return [];
2701
+ return this.coord.writeOp(
2702
+ createPutManyEvaluator(entries, this.duplicateKeys, this.compareKeys)
2703
+ );
2393
2704
  }
2394
2705
  async deleteRange(startKey, endKey, options) {
2395
- return this.runExclusive(async () => {
2396
- const result = await this.appendMutationAndApplyUnlocked(
2397
- createDeleteRangeEvaluator(startKey, endKey, options)
2398
- );
2399
- return result ?? 0;
2400
- });
2706
+ const result = await this.coord.writeOp(
2707
+ createDeleteRangeEvaluator(startKey, endKey, options)
2708
+ );
2709
+ return result ?? 0;
2401
2710
  }
2402
2711
  async clear() {
2403
- await this.runExclusive(async () => {
2404
- await this.appendMutationAndApplyUnlocked(createClearEvaluator());
2405
- });
2712
+ await this.coord.writeOp(createClearEvaluator());
2406
2713
  }
2407
2714
  async get(key) {
2408
- return this.readOp((tree) => tree.get(key));
2715
+ return this.coord.readOp((t) => t.get(key));
2409
2716
  }
2410
2717
  async hasKey(key) {
2411
- return this.readOp((tree) => tree.hasKey(key));
2718
+ return this.coord.readOp((t) => t.hasKey(key));
2412
2719
  }
2413
2720
  async findFirst(key) {
2414
- return this.readOp((tree) => tree.findFirst(key));
2721
+ return this.coord.readOp((t) => t.findFirst(key));
2415
2722
  }
2416
2723
  async findLast(key) {
2417
- return this.readOp((tree) => tree.findLast(key));
2724
+ return this.coord.readOp((t) => t.findLast(key));
2418
2725
  }
2419
2726
  async range(startKey, endKey, options) {
2420
- return this.readOp((tree) => tree.range(startKey, endKey, options));
2727
+ return this.coord.readOp((t) => t.range(startKey, endKey, options));
2421
2728
  }
2422
2729
  async snapshot() {
2423
- return this.readOp((tree) => tree.snapshot());
2730
+ return this.coord.readOp((t) => t.snapshot());
2424
2731
  }
2425
2732
  async size() {
2426
- return this.readOp((tree) => tree.size());
2733
+ return this.coord.readOp((t) => t.size());
2427
2734
  }
2428
2735
  async assertInvariants() {
2429
- await this.readOp((tree) => tree.assertInvariants());
2736
+ await this.coord.readOp((t) => t.assertInvariants());
2430
2737
  }
2431
2738
  async getStats() {
2432
- return this.readOp((tree) => tree.getStats());
2739
+ return this.coord.readOp((t) => t.getStats());
2433
2740
  }
2434
2741
  async peekFirst() {
2435
- return this.readOp((tree) => tree.peekFirst());
2742
+ return this.coord.readOp((t) => t.peekFirst());
2436
2743
  }
2437
2744
  async peekLast() {
2438
- return this.readOp((tree) => tree.peekLast());
2745
+ return this.coord.readOp((t) => t.peekLast());
2439
2746
  }
2440
2747
  async peekById(entryId) {
2441
- return this.readOp((tree) => tree.peekById(entryId));
2748
+ return this.coord.readOp((t) => t.peekById(entryId));
2442
2749
  }
2443
2750
  async count(startKey, endKey, options) {
2444
- return this.readOp((tree) => tree.count(startKey, endKey, options));
2751
+ return this.coord.readOp((t) => t.count(startKey, endKey, options));
2445
2752
  }
2446
2753
  async nextHigherKey(key) {
2447
- return this.readOp((tree) => tree.nextHigherKey(key));
2754
+ return this.coord.readOp((t) => t.nextHigherKey(key));
2448
2755
  }
2449
2756
  async nextLowerKey(key) {
2450
- return this.readOp((tree) => tree.nextLowerKey(key));
2757
+ return this.coord.readOp((t) => t.nextLowerKey(key));
2451
2758
  }
2452
2759
  async getPairOrNextLower(key) {
2453
- return this.readOp((tree) => tree.getPairOrNextLower(key));
2760
+ return this.coord.readOp((t) => t.getPairOrNextLower(key));
2454
2761
  }
2455
2762
  async entries() {
2456
- return this.readOp((tree) => Array.from(tree.entries()));
2763
+ return this.coord.readOp((t) => Array.from(t.entries()));
2457
2764
  }
2458
2765
  async entriesReversed() {
2459
- return this.readOp((tree) => Array.from(tree.entriesReversed()));
2766
+ return this.coord.readOp((t) => Array.from(t.entriesReversed()));
2460
2767
  }
2461
2768
  async keys() {
2462
- return this.readOp((tree) => Array.from(tree.keys()));
2769
+ return this.coord.readOp((t) => Array.from(t.keys()));
2463
2770
  }
2464
2771
  async values() {
2465
- return this.readOp((tree) => Array.from(tree.values()));
2772
+ return this.coord.readOp((t) => Array.from(t.values()));
2466
2773
  }
2467
2774
  async forEach(callback) {
2468
- await this.readOp((tree) => {
2469
- tree.forEach(callback);
2775
+ await this.coord.readOp((t) => {
2776
+ t.forEach(callback);
2777
+ });
2778
+ }
2779
+ async forEachRange(startKey, endKey, callback, options) {
2780
+ await this.coord.readOp((t) => {
2781
+ t.forEachRange(startKey, endKey, callback, options);
2470
2782
  });
2471
2783
  }
2472
2784
  async *[Symbol.asyncIterator]() {
2473
2785
  const all = await this.entries();
2474
- for (const entry of all) {
2475
- yield entry;
2476
- }
2786
+ for (const entry of all) yield entry;
2477
2787
  }
2478
2788
  async clone() {
2479
- return this.readOp((tree) => tree.clone());
2789
+ return this.coord.readOp((t) => t.clone());
2480
2790
  }
2481
2791
  async toJSON() {
2482
- return this.readOp((tree) => tree.toJSON());
2792
+ return this.coord.readOp((t) => t.toJSON());
2483
2793
  }
2484
2794
  static fromJSON(json, compareKeys) {
2485
2795
  return InMemoryBTree.fromJSON(json, compareKeys);