@frostpillar/frostpillar-btree 0.2.5 → 0.2.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/core.cjs CHANGED
@@ -42,54 +42,10 @@ var BTreeInvariantError = class extends Error {
42
42
  }
43
43
  };
44
44
 
45
- // src/btree/types.ts
46
- var DEFAULT_MAX_LEAF_ENTRIES = 64;
47
- var DEFAULT_MAX_BRANCH_CHILDREN = 64;
48
- var MIN_NODE_CAPACITY = 3;
49
- var MAX_NODE_CAPACITY = 16384;
50
- var NODE_LEAF = 0;
51
- var NODE_BRANCH = 1;
52
- var normalizeDuplicateKeyPolicy = (value) => {
53
- if (value === void 0) {
54
- return "replace";
55
- }
56
- if (value !== "allow" && value !== "reject" && value !== "replace") {
57
- throw new BTreeValidationError(
58
- `Invalid duplicateKeys option.`
59
- );
60
- }
61
- return value;
62
- };
63
- var isLeafNode = (node) => {
64
- return node.kind === NODE_LEAF;
65
- };
66
- var writeMinKeyTo = (node, target) => {
67
- if (node.kind === NODE_LEAF) {
68
- if (node.entryOffset >= node.entries.length) return false;
69
- const e = node.entries[node.entryOffset];
70
- target.key = e.key;
71
- target.sequence = e.entryId;
72
- return true;
73
- }
74
- if (node.childOffset >= node.keys.length) return false;
75
- target.key = node.keys[node.childOffset].key;
76
- target.sequence = node.keys[node.childOffset].sequence;
77
- return true;
78
- };
79
- var normalizeNodeCapacity = (value, field, defaultValue) => {
80
- if (value === void 0) {
81
- return defaultValue;
82
- }
83
- if (!Number.isInteger(value) || value < MIN_NODE_CAPACITY || value > MAX_NODE_CAPACITY) {
84
- throw new BTreeValidationError(
85
- `${field}: integer ${MIN_NODE_CAPACITY}\u2013${MAX_NODE_CAPACITY} required.`
86
- );
87
- }
88
- return value;
89
- };
45
+ // src/btree/node-ops.ts
90
46
  var createLeafNode = (entries, parent) => {
91
47
  return {
92
- kind: NODE_LEAF,
48
+ kind: 0,
93
49
  entries,
94
50
  entryOffset: 0,
95
51
  parent,
@@ -101,7 +57,7 @@ var createLeafNode = (entries, parent) => {
101
57
  var createBranchNode = (children, parent) => {
102
58
  const keys = [];
103
59
  const branch = {
104
- kind: NODE_BRANCH,
60
+ kind: 1,
105
61
  children,
106
62
  keys,
107
63
  childOffset: 0,
@@ -112,11 +68,12 @@ var createBranchNode = (children, parent) => {
112
68
  const child = children[i];
113
69
  child.parent = branch;
114
70
  child.indexInParent = i;
115
- const target = { key: void 0, sequence: 0 };
71
+ const target = {
72
+ key: void 0,
73
+ sequence: 0
74
+ };
116
75
  if (!writeMinKeyTo(child, target)) {
117
- throw new BTreeInvariantError(
118
- "branch child has no min key"
119
- );
76
+ throw new BTreeInvariantError("branch child has no min key");
120
77
  }
121
78
  keys.push(target);
122
79
  }
@@ -170,7 +127,14 @@ var leafInsertAt = (leaf, logicalIndex, entry) => {
170
127
  leaf.entryOffset -= 1;
171
128
  leaf.entries[phys - 1] = entry;
172
129
  } else {
173
- leaf.entries.splice(phys, 0, entry);
130
+ const len = leaf.entries.length;
131
+ if (phys >= len) {
132
+ leaf.entries.push(entry);
133
+ } else {
134
+ leaf.entries.push(leaf.entries[len - 1]);
135
+ leaf.entries.copyWithin(phys + 1, phys, len);
136
+ leaf.entries[phys] = entry;
137
+ }
174
138
  }
175
139
  };
176
140
  var leafCompact = (leaf) => {
@@ -199,7 +163,11 @@ var branchInsertAt = (branch, logicalIndex, child, key) => {
199
163
  const phys = branch.childOffset + logicalIndex;
200
164
  const count = branch.children.length - branch.childOffset;
201
165
  if (branch.childOffset > 0 && logicalIndex < count >>> 1) {
202
- branch.children.copyWithin(branch.childOffset - 1, branch.childOffset, phys);
166
+ branch.children.copyWithin(
167
+ branch.childOffset - 1,
168
+ branch.childOffset,
169
+ phys
170
+ );
203
171
  branch.keys.copyWithin(branch.childOffset - 1, branch.childOffset, phys);
204
172
  branch.childOffset -= 1;
205
173
  branch.children[phys - 1] = child;
@@ -220,8 +188,16 @@ var branchRemoveAt = (branch, physIndex) => {
220
188
  const logicalIndex = physIndex - branch.childOffset;
221
189
  const count = branch.children.length - branch.childOffset;
222
190
  if (logicalIndex < count - 1 - logicalIndex) {
223
- branch.children.copyWithin(branch.childOffset + 1, branch.childOffset, physIndex);
224
- branch.keys.copyWithin(branch.childOffset + 1, branch.childOffset, physIndex);
191
+ branch.children.copyWithin(
192
+ branch.childOffset + 1,
193
+ branch.childOffset,
194
+ physIndex
195
+ );
196
+ branch.keys.copyWithin(
197
+ branch.childOffset + 1,
198
+ branch.childOffset,
199
+ physIndex
200
+ );
225
201
  branch.childOffset += 1;
226
202
  for (let i = branch.childOffset; i <= physIndex; i += 1) {
227
203
  branch.children[i].indexInParent = i;
@@ -240,6 +216,60 @@ var branchRemoveAt = (branch, physIndex) => {
240
216
  }
241
217
  };
242
218
 
219
+ // src/btree/types.ts
220
+ var DEFAULT_MAX_LEAF_ENTRIES = 64;
221
+ var DEFAULT_MAX_BRANCH_CHILDREN = 64;
222
+ var MIN_NODE_CAPACITY = 3;
223
+ var MAX_NODE_CAPACITY = 16384;
224
+ var NODE_LEAF = 0;
225
+ var normalizeDuplicateKeyPolicy = (value) => {
226
+ if (value === void 0) {
227
+ return "replace";
228
+ }
229
+ if (value !== "allow" && value !== "reject" && value !== "replace") {
230
+ throw new BTreeValidationError(`Invalid duplicateKeys option.`);
231
+ }
232
+ return value;
233
+ };
234
+ var normalizeDeleteRebalancePolicy = (value) => {
235
+ if (value === void 0) {
236
+ return "standard";
237
+ }
238
+ if (value !== "standard" && value !== "lazy") {
239
+ throw new BTreeValidationError(`Invalid deleteRebalancePolicy option.`);
240
+ }
241
+ return value;
242
+ };
243
+ var freezeEntry = (entry) => Object.freeze(entry);
244
+ var createEntry = (key, entryId, value) => Object.freeze({ key, entryId, value });
245
+ var isLeafNode = (node) => {
246
+ return node.kind === NODE_LEAF;
247
+ };
248
+ var writeMinKeyTo = (node, target) => {
249
+ if (node.kind === NODE_LEAF) {
250
+ if (node.entryOffset >= node.entries.length) return false;
251
+ const e = node.entries[node.entryOffset];
252
+ target.key = e.key;
253
+ target.sequence = e.entryId;
254
+ return true;
255
+ }
256
+ if (node.childOffset >= node.keys.length) return false;
257
+ target.key = node.keys[node.childOffset].key;
258
+ target.sequence = node.keys[node.childOffset].sequence;
259
+ return true;
260
+ };
261
+ var normalizeNodeCapacity = (value, field, defaultValue) => {
262
+ if (value === void 0) {
263
+ return defaultValue;
264
+ }
265
+ if (!Number.isInteger(value) || value < MIN_NODE_CAPACITY || value > MAX_NODE_CAPACITY) {
266
+ throw new BTreeValidationError(
267
+ `${field}: integer ${MIN_NODE_CAPACITY}\u2013${MAX_NODE_CAPACITY} required.`
268
+ );
269
+ }
270
+ return value;
271
+ };
272
+
243
273
  // src/btree/navigation.ts
244
274
  var selectBranchChild = (compare, branch, userKey, sequence) => {
245
275
  const off = branch.childOffset;
@@ -253,7 +283,7 @@ var selectBranchChild = (compare, branch, userKey, sequence) => {
253
283
  const mid = lower + upper >>> 1;
254
284
  const k = branch.keys[mid];
255
285
  const cmp = compare(k.key, userKey);
256
- if ((cmp !== 0 ? cmp : k.sequence - sequence) <= 0) {
286
+ if ((cmp !== 0 ? cmp : k.sequence < sequence ? -1 : k.sequence > sequence ? 1 : 0) <= 0) {
257
287
  selectedIndex = mid;
258
288
  lower = mid + 1;
259
289
  } else {
@@ -278,7 +308,7 @@ var lowerBoundInLeaf = (state, leaf, userKey, sequence) => {
278
308
  const mid = lower + upper >>> 1;
279
309
  const e = leaf.entries[mid];
280
310
  const cmp = compare(e.key, userKey);
281
- if ((cmp !== 0 ? cmp : e.entryId - sequence) < 0) {
311
+ if ((cmp !== 0 ? cmp : e.entryId < sequence ? -1 : e.entryId > sequence ? 1 : 0) < 0) {
282
312
  lower = mid + 1;
283
313
  } else {
284
314
  upper = mid;
@@ -294,7 +324,7 @@ var upperBoundInLeaf = (state, leaf, userKey, sequence) => {
294
324
  const mid = lower + upper >>> 1;
295
325
  const e = leaf.entries[mid];
296
326
  const cmp = compare(e.key, userKey);
297
- if ((cmp !== 0 ? cmp : e.entryId - sequence) <= 0) {
327
+ if ((cmp !== 0 ? cmp : e.entryId < sequence ? -1 : e.entryId > sequence ? 1 : 0) <= 0) {
298
328
  lower = mid + 1;
299
329
  } else {
300
330
  upper = mid;
@@ -361,7 +391,11 @@ var hasKeyEntry = (state, key) => {
361
391
  var findNextHigherKey = (state, key) => {
362
392
  if (state.entryCount === 0) return null;
363
393
  const compare = state.compareKeys;
364
- let leaf = findLeafForKey(state, key, Number.MAX_SAFE_INTEGER);
394
+ let leaf = findLeafForKey(
395
+ state,
396
+ key,
397
+ Number.MAX_SAFE_INTEGER
398
+ );
365
399
  let idx = upperBoundInLeaf(state, leaf, key, Number.MAX_SAFE_INTEGER);
366
400
  while (leaf !== null) {
367
401
  if (idx < leafEntryCount(leaf)) {
@@ -431,7 +465,175 @@ var findPairOrNextLower = (state, key) => {
431
465
  return null;
432
466
  };
433
467
 
434
- // src/btree/rebalance.ts
468
+ // src/btree/rangeQuery.ts
469
+ function isEmptyRange(compare, startKey, endKey, options) {
470
+ const cmp = compare(startKey, endKey);
471
+ if (cmp > 0) return true;
472
+ const lowerExclusive = options?.lowerBound === "exclusive";
473
+ const upperExclusive = options?.upperBound === "exclusive";
474
+ return lowerExclusive && upperExclusive && cmp === 0;
475
+ }
476
+ var initRangeCursor = (state, startKey, endKey, options) => {
477
+ if (state.entryCount === 0) return null;
478
+ const compare = state.compareKeys;
479
+ if (isEmptyRange(compare, startKey, endKey, options)) return null;
480
+ const lowerExclusive = options?.lowerBound === "exclusive";
481
+ const upperExclusive = options?.upperBound === "exclusive";
482
+ const startSeq = lowerExclusive ? Number.MAX_SAFE_INTEGER : 0;
483
+ const leaf = findLeafForKey(state, startKey, startSeq);
484
+ const index = lowerExclusive ? upperBoundInLeaf(state, leaf, startKey, Number.MAX_SAFE_INTEGER) : lowerBoundInLeaf(state, leaf, startKey, 0);
485
+ return { leaf, index, compare, upperExclusive };
486
+ };
487
+ var countRangeEntries = (state, startKey, endKey, options) => {
488
+ const cursor = initRangeCursor(state, startKey, endKey, options);
489
+ if (cursor === null) return 0;
490
+ let cursorLeaf = cursor.leaf;
491
+ let cursorIndex = cursor.index;
492
+ const { compare, upperExclusive } = cursor;
493
+ let count = 0;
494
+ while (cursorLeaf !== null) {
495
+ const leafCount = leafEntryCount(cursorLeaf);
496
+ if (cursorIndex >= leafCount) {
497
+ cursorLeaf = cursorLeaf.next;
498
+ cursorIndex = 0;
499
+ continue;
500
+ }
501
+ const lastKey = leafEntryAt(cursorLeaf, leafCount - 1).key;
502
+ if (isLastEntryInRange(lastKey, endKey, compare, upperExclusive)) {
503
+ count += leafCount - cursorIndex;
504
+ cursorLeaf = cursorLeaf.next;
505
+ cursorIndex = 0;
506
+ continue;
507
+ }
508
+ count += findBoundaryEnd(state, cursorLeaf, endKey, upperExclusive, leafCount) - cursorIndex;
509
+ return count;
510
+ }
511
+ return count;
512
+ };
513
+ var isLastEntryInRange = (lastKey, endKey, compare, upperExclusive) => {
514
+ const cmp = compare(lastKey, endKey);
515
+ return upperExclusive ? cmp < 0 : cmp <= 0;
516
+ };
517
+ var findBoundaryEnd = (state, leaf, endKey, upperExclusive, leafCount) => {
518
+ const endSeq = upperExclusive ? 0 : Number.MAX_SAFE_INTEGER;
519
+ const endBound = upperExclusive ? lowerBoundInLeaf(state, leaf, endKey, endSeq) : upperBoundInLeaf(state, leaf, endKey, endSeq);
520
+ return endBound < leafCount ? endBound : leafCount;
521
+ };
522
+ var RANGE_PREALLOC_THRESHOLD = 200;
523
+ var allocateRangeOutput = (state, cursor, startKey, endKey, options) => {
524
+ const firstLeafCount = leafEntryCount(cursor.leaf);
525
+ const firstLeafRemainder = firstLeafCount - cursor.index;
526
+ if (firstLeafRemainder >= RANGE_PREALLOC_THRESHOLD && cursor.leaf.next !== null) {
527
+ const lastKey = leafEntryAt(cursor.leaf, firstLeafCount - 1).key;
528
+ if (isLastEntryInRange(lastKey, endKey, cursor.compare, cursor.upperExclusive)) {
529
+ return new Array(
530
+ countRangeEntries(state, startKey, endKey, options)
531
+ );
532
+ }
533
+ }
534
+ return [];
535
+ };
536
+ var appendLeafSlicePublic = (leaf, from, to, output, useIndexed, writeIdx) => {
537
+ if (useIndexed) {
538
+ for (let i = from; i < to; i += 1) {
539
+ output[writeIdx++] = freezeEntry(leafEntryAt(leaf, i));
540
+ }
541
+ } else {
542
+ for (let i = from; i < to; i += 1) {
543
+ output.push(freezeEntry(leafEntryAt(leaf, i)));
544
+ }
545
+ }
546
+ return writeIdx;
547
+ };
548
+ var collectPublicEntries = (state, cursor, endKey, output) => {
549
+ const { compare, upperExclusive } = cursor;
550
+ let cursorLeaf = cursor.leaf;
551
+ let cursorIndex = cursor.index;
552
+ let writeIdx = 0;
553
+ const useIndexed = output.length > 0;
554
+ while (cursorLeaf !== null) {
555
+ const leafCount = leafEntryCount(cursorLeaf);
556
+ if (cursorIndex >= leafCount) {
557
+ cursorLeaf = cursorLeaf.next;
558
+ cursorIndex = 0;
559
+ continue;
560
+ }
561
+ const lastKey = leafEntryAt(cursorLeaf, leafCount - 1).key;
562
+ if (isLastEntryInRange(lastKey, endKey, compare, upperExclusive)) {
563
+ writeIdx = appendLeafSlicePublic(
564
+ cursorLeaf,
565
+ cursorIndex,
566
+ leafCount,
567
+ output,
568
+ useIndexed,
569
+ writeIdx
570
+ );
571
+ cursorLeaf = cursorLeaf.next;
572
+ cursorIndex = 0;
573
+ continue;
574
+ }
575
+ const limit = findBoundaryEnd(
576
+ state,
577
+ cursorLeaf,
578
+ endKey,
579
+ upperExclusive,
580
+ leafCount
581
+ );
582
+ appendLeafSlicePublic(
583
+ cursorLeaf,
584
+ cursorIndex,
585
+ limit,
586
+ output,
587
+ useIndexed,
588
+ writeIdx
589
+ );
590
+ return;
591
+ }
592
+ };
593
+ var rangeQueryPublicEntries = (state, startKey, endKey, options) => {
594
+ const cursor = initRangeCursor(state, startKey, endKey, options);
595
+ if (cursor === null) return [];
596
+ const output = allocateRangeOutput(state, cursor, startKey, endKey, options);
597
+ collectPublicEntries(state, cursor, endKey, output);
598
+ return output;
599
+ };
600
+ var forEachRangeEntries = (state, startKey, endKey, callback, options) => {
601
+ const cursor = initRangeCursor(state, startKey, endKey, options);
602
+ if (cursor === null) return;
603
+ let cursorLeaf = cursor.leaf;
604
+ let cursorIndex = cursor.index;
605
+ const { compare, upperExclusive } = cursor;
606
+ while (cursorLeaf !== null) {
607
+ const leafCount = leafEntryCount(cursorLeaf);
608
+ if (cursorIndex >= leafCount) {
609
+ cursorLeaf = cursorLeaf.next;
610
+ cursorIndex = 0;
611
+ continue;
612
+ }
613
+ const lastKey = leafEntryAt(cursorLeaf, leafCount - 1).key;
614
+ if (isLastEntryInRange(lastKey, endKey, compare, upperExclusive)) {
615
+ for (let i = cursorIndex; i < leafCount; i += 1) {
616
+ callback(freezeEntry(leafEntryAt(cursorLeaf, i)));
617
+ }
618
+ cursorLeaf = cursorLeaf.next;
619
+ cursorIndex = 0;
620
+ continue;
621
+ }
622
+ const limit = findBoundaryEnd(
623
+ state,
624
+ cursorLeaf,
625
+ endKey,
626
+ upperExclusive,
627
+ leafCount
628
+ );
629
+ for (let i = cursorIndex; i < limit; i += 1) {
630
+ callback(freezeEntry(leafEntryAt(cursorLeaf, i)));
631
+ }
632
+ return;
633
+ }
634
+ };
635
+
636
+ // src/btree/rebalance-branch.ts
435
637
  var updateMinKeyInAncestors = (node) => {
436
638
  let current = node;
437
639
  while (current.parent !== null) {
@@ -447,12 +649,9 @@ var requireParent = (node) => {
447
649
  }
448
650
  return node.parent;
449
651
  };
450
- var requireLeafNode = (node) => {
451
- if (!isLeafNode(node)) throw new BTreeInvariantError("expected leaf, got branch");
452
- return node;
453
- };
454
652
  var requireBranchNode = (node) => {
455
- if (isLeafNode(node)) throw new BTreeInvariantError("expected branch, got leaf");
653
+ if (isLeafNode(node))
654
+ throw new BTreeInvariantError("expected branch, got leaf");
456
655
  return node;
457
656
  };
458
657
  var removeChildFromBranch = (branch, childIndex) => {
@@ -461,27 +660,18 @@ var removeChildFromBranch = (branch, childIndex) => {
461
660
  }
462
661
  branchRemoveAt(branch, childIndex);
463
662
  };
464
- var detachLeafFromChain = (state, leaf) => {
465
- if (leaf.prev !== null) {
466
- leaf.prev.next = leaf.next;
467
- } else if (leaf.next !== null) {
468
- state.leftmostLeaf = leaf.next;
469
- }
470
- if (leaf.next !== null) {
471
- leaf.next.prev = leaf.prev;
472
- } else if (leaf.prev !== null) {
473
- state.rightmostLeaf = leaf.prev;
474
- }
475
- leaf.prev = null;
476
- leaf.next = null;
477
- };
478
663
  var borrowFromLeftBranch = (branch, leftSibling, branchIndex) => {
479
664
  const borrowedChild = leftSibling.children.pop();
480
- if (borrowedChild === void 0) throw new BTreeInvariantError("left branch borrow failed");
665
+ if (borrowedChild === void 0)
666
+ throw new BTreeInvariantError("left branch borrow failed");
481
667
  leftSibling.keys.pop();
482
668
  borrowedChild.parent = branch;
483
- const borrowedMinKey = { key: void 0, sequence: 0 };
484
- if (!writeMinKeyTo(borrowedChild, borrowedMinKey)) throw new BTreeInvariantError("borrowed child has no min key");
669
+ const borrowedMinKey = {
670
+ key: void 0,
671
+ sequence: 0
672
+ };
673
+ if (!writeMinKeyTo(borrowedChild, borrowedMinKey))
674
+ throw new BTreeInvariantError("borrowed child has no min key");
485
675
  if (branch.childOffset > 0) {
486
676
  branch.childOffset -= 1;
487
677
  branch.children[branch.childOffset] = borrowedChild;
@@ -490,15 +680,20 @@ var borrowFromLeftBranch = (branch, leftSibling, branchIndex) => {
490
680
  } else {
491
681
  branch.children.unshift(borrowedChild);
492
682
  branch.keys.unshift(borrowedMinKey);
493
- for (let i = 0; i < branch.children.length; i += 1) branch.children[i].indexInParent = i;
683
+ for (let i = 0; i < branch.children.length; i += 1)
684
+ branch.children[i].indexInParent = i;
494
685
  }
495
686
  const parent = requireParent(branch);
496
- parent.keys[branchIndex] = { key: borrowedMinKey.key, sequence: borrowedMinKey.sequence };
687
+ parent.keys[branchIndex] = {
688
+ key: borrowedMinKey.key,
689
+ sequence: borrowedMinKey.sequence
690
+ };
497
691
  updateMinKeyInAncestors(branch);
498
692
  };
499
693
  var borrowFromRightBranch = (branch, rightSibling, branchIndex) => {
500
694
  const shiftIdx = rightSibling.childOffset;
501
- if (shiftIdx >= rightSibling.children.length) throw new BTreeInvariantError("right branch borrow failed");
695
+ if (shiftIdx >= rightSibling.children.length)
696
+ throw new BTreeInvariantError("right branch borrow failed");
502
697
  const borrowedChild = rightSibling.children[shiftIdx];
503
698
  rightSibling.childOffset += 1;
504
699
  if (rightSibling.childOffset >= rightSibling.children.length >>> 1) {
@@ -506,8 +701,12 @@ var borrowFromRightBranch = (branch, rightSibling, branchIndex) => {
506
701
  }
507
702
  branch.children.push(borrowedChild);
508
703
  borrowedChild.parent = branch;
509
- const borrowedMinKey = { key: void 0, sequence: 0 };
510
- if (!writeMinKeyTo(borrowedChild, borrowedMinKey)) throw new BTreeInvariantError("borrowed child has no min key");
704
+ const borrowedMinKey = {
705
+ key: void 0,
706
+ sequence: 0
707
+ };
708
+ if (!writeMinKeyTo(borrowedChild, borrowedMinKey))
709
+ throw new BTreeInvariantError("borrowed child has no min key");
511
710
  branch.keys.push(borrowedMinKey);
512
711
  borrowedChild.indexInParent = branch.children.length - 1;
513
712
  const parent = requireParent(branch);
@@ -560,7 +759,10 @@ var rebalanceAfterBranchRemoval = (state, branch) => {
560
759
  const parent = branch.parent;
561
760
  if (parent === null) throw new BTreeInvariantError("branch has no parent");
562
761
  const branchIndex = branch.indexInParent;
563
- const { left: leftSibling, right: rightSibling } = findBranchSiblings(parent, branchIndex);
762
+ const { left: leftSibling, right: rightSibling } = findBranchSiblings(
763
+ parent,
764
+ branchIndex
765
+ );
564
766
  if (rightSibling !== null && branchChildCount(rightSibling) > state.minBranchChildren) {
565
767
  borrowFromRightBranch(branch, rightSibling, branchIndex);
566
768
  return;
@@ -579,44 +781,68 @@ var rebalanceAfterBranchRemoval = (state, branch) => {
579
781
  }
580
782
  throw new BTreeInvariantError("no branch siblings to rebalance");
581
783
  };
784
+
785
+ // src/btree/rebalance.ts
786
+ var requireLeafNode = (node) => {
787
+ if (!isLeafNode(node))
788
+ throw new BTreeInvariantError("expected leaf, got branch");
789
+ return node;
790
+ };
791
+ var detachLeafFromChain = (state, leaf) => {
792
+ if (leaf.prev !== null) {
793
+ leaf.prev.next = leaf.next;
794
+ } else if (leaf.next !== null) {
795
+ state.leftmostLeaf = leaf.next;
796
+ }
797
+ if (leaf.next !== null) {
798
+ leaf.next.prev = leaf.prev;
799
+ } else if (leaf.prev !== null) {
800
+ state.rightmostLeaf = leaf.prev;
801
+ }
802
+ leaf.prev = null;
803
+ leaf.next = null;
804
+ };
582
805
  var mergeLeafEntries = (target, source) => {
583
806
  if (target.entryOffset > 0) {
584
807
  target.entries.copyWithin(0, target.entryOffset);
585
808
  target.entries.length = target.entries.length - target.entryOffset;
586
809
  target.entryOffset = 0;
587
810
  }
588
- const src = source.entries;
589
- for (let i = source.entryOffset; i < src.length; i += 1) target.entries.push(src[i]);
811
+ target.entries.push(...source.entries.slice(source.entryOffset));
590
812
  };
591
- var rebalanceAfterLeafRemoval = (state, leaf) => {
592
- if (leaf === state.root) {
593
- if (state.entryCount === 0) {
594
- state.leftmostLeaf = leaf;
595
- state.rightmostLeaf = leaf;
596
- }
597
- return;
813
+ var applyLazyThreshold = (min) => Math.max(1, Math.ceil(min / 4));
814
+ var leafRebalanceThreshold = (state) => {
815
+ if (state.deleteRebalancePolicy === "lazy") {
816
+ return applyLazyThreshold(state.minLeafEntries);
598
817
  }
599
- if (leafEntryCount(leaf) >= state.minLeafEntries) return;
600
- const parent = leaf.parent;
601
- if (parent === null) throw new BTreeInvariantError("Leaf node has no parent during rebalance.");
602
- const leafIndex = leaf.indexInParent;
603
- const leftSibling = leafIndex > parent.childOffset ? requireLeafNode(parent.children[leafIndex - 1]) : null;
604
- const rightSibling = leafIndex + 1 < parent.children.length ? requireLeafNode(parent.children[leafIndex + 1]) : null;
818
+ return state.minLeafEntries;
819
+ };
820
+ var findLeafSiblings = (parent, leafIndex) => {
821
+ const left = leafIndex > parent.childOffset ? requireLeafNode(parent.children[leafIndex - 1]) : null;
822
+ const right = leafIndex + 1 < parent.children.length ? requireLeafNode(parent.children[leafIndex + 1]) : null;
823
+ return { left, right };
824
+ };
825
+ var tryBorrowFromLeafSibling = (state, leaf, parent, leafIndex, leftSibling, rightSibling) => {
605
826
  if (rightSibling !== null && leafEntryCount(rightSibling) > state.minLeafEntries) {
606
827
  const borrowed = leafShiftEntry(rightSibling);
607
- if (borrowed === void 0) throw new BTreeInvariantError("right leaf borrow failed");
828
+ if (borrowed === void 0)
829
+ throw new BTreeInvariantError("right leaf borrow failed");
608
830
  leaf.entries.push(borrowed);
609
831
  writeMinKeyTo(rightSibling, parent.keys[leafIndex + 1]);
610
- return;
832
+ return true;
611
833
  }
612
834
  if (leftSibling !== null && leafEntryCount(leftSibling) > state.minLeafEntries) {
613
835
  const borrowed = leftSibling.entries.pop();
614
- if (borrowed === void 0) throw new BTreeInvariantError("left leaf borrow failed");
836
+ if (borrowed === void 0)
837
+ throw new BTreeInvariantError("left leaf borrow failed");
615
838
  leafUnshiftEntry(leaf, borrowed);
616
839
  parent.keys[leafIndex] = { key: borrowed.key, sequence: borrowed.entryId };
617
840
  updateMinKeyInAncestors(leaf);
618
- return;
841
+ return true;
619
842
  }
843
+ return false;
844
+ };
845
+ var mergeLeafWithSibling = (state, leaf, parent, leafIndex, leftSibling, rightSibling) => {
620
846
  if (leftSibling !== null) {
621
847
  mergeLeafEntries(leftSibling, leaf);
622
848
  detachLeafFromChain(state, leaf);
@@ -633,6 +859,24 @@ var rebalanceAfterLeafRemoval = (state, leaf) => {
633
859
  }
634
860
  throw new BTreeInvariantError("no leaf siblings to rebalance");
635
861
  };
862
+ var rebalanceAfterLeafRemoval = (state, leaf) => {
863
+ if (leaf === state.root) {
864
+ if (state.entryCount === 0) {
865
+ state.leftmostLeaf = leaf;
866
+ state.rightmostLeaf = leaf;
867
+ }
868
+ return;
869
+ }
870
+ if (leafEntryCount(leaf) >= leafRebalanceThreshold(state)) return;
871
+ const parent = leaf.parent;
872
+ if (parent === null)
873
+ throw new BTreeInvariantError("Leaf node has no parent during rebalance.");
874
+ const leafIndex = leaf.indexInParent;
875
+ const { left, right } = findLeafSiblings(parent, leafIndex);
876
+ if (tryBorrowFromLeafSibling(state, leaf, parent, leafIndex, left, right))
877
+ return;
878
+ mergeLeafWithSibling(state, leaf, parent, leafIndex, left, right);
879
+ };
636
880
 
637
881
  // src/btree/deleteRange.ts
638
882
  var navigateToStart = (state, startKey, lowerExclusive) => {
@@ -645,27 +889,18 @@ var navigateToStart = (state, startKey, lowerExclusive) => {
645
889
  }
646
890
  return { leaf, idx };
647
891
  };
648
- var findRemoveEnd = (state, leaf, idx, endKey, upperExclusive) => {
892
+ var findRemoveEnd = (state, leaf, endKey, upperExclusive) => {
649
893
  const count = leafEntryCount(leaf);
650
- let removeEnd = idx;
651
- while (removeEnd < count) {
652
- const e = leafEntryAt(leaf, removeEnd);
653
- const cmpEnd = state.compareKeys(e.key, endKey);
654
- if (upperExclusive ? cmpEnd >= 0 : cmpEnd > 0) break;
655
- removeEnd += 1;
656
- }
657
- return removeEnd;
658
- };
659
- var computeTreeHeight = (state) => {
660
- let h = 0;
661
- let n = state.root;
662
- while (!isLeafNode(n)) {
663
- n = n.children[n.childOffset];
664
- h += 1;
894
+ const lastEntry = leafEntryAt(leaf, count - 1);
895
+ const cmpLast = state.compareKeys(lastEntry.key, endKey);
896
+ if (upperExclusive ? cmpLast < 0 : cmpLast <= 0) {
897
+ return count;
665
898
  }
666
- return h;
899
+ const endSeq = upperExclusive ? 0 : Number.MAX_SAFE_INTEGER;
900
+ const endBound = upperExclusive ? lowerBoundInLeaf(state, leaf, endKey, endSeq) : upperBoundInLeaf(state, leaf, endKey, endSeq);
901
+ return endBound < count ? endBound : count;
667
902
  };
668
- var spliceLeafAndRebalance = (state, leaf, idx, removeCount, maxRebalanceDepth) => {
903
+ var spliceLeafAndRebalance = (state, leaf, idx, removeCount) => {
669
904
  if (state.entryKeys !== null) {
670
905
  for (let i = idx; i < idx + removeCount; i += 1) {
671
906
  state.entryKeys.delete(leafEntryAt(leaf, i).entryId);
@@ -680,11 +915,13 @@ var spliceLeafAndRebalance = (state, leaf, idx, removeCount, maxRebalanceDepth)
680
915
  updateMinKeyInAncestors(leaf);
681
916
  }
682
917
  const countAfterSplice = leafEntryCount(leaf);
683
- let maxIter = maxRebalanceDepth;
684
- while (maxIter > 0 && leaf !== state.root && leafEntryCount(leaf) < state.minLeafEntries) {
918
+ const rebalThreshold = leafRebalanceThreshold(state);
919
+ let safetyGuard = state.minLeafEntries + 4;
920
+ while (safetyGuard > 0 && leaf !== state.root && leafEntryCount(leaf) < rebalThreshold) {
685
921
  rebalanceAfterLeafRemoval(state, leaf);
686
- if (leaf.parent !== null && leaf.parent.children[leaf.indexInParent] !== leaf) break;
687
- maxIter -= 1;
922
+ if (leaf.parent !== null && leaf.parent.children[leaf.indexInParent] !== leaf)
923
+ break;
924
+ safetyGuard -= 1;
688
925
  }
689
926
  if (leafEmptied && leafEntryCount(leaf) > 0 && leaf.parent !== null && leaf.parent.children[leaf.indexInParent] === leaf) {
690
927
  updateMinKeyInAncestors(leaf);
@@ -694,12 +931,9 @@ var spliceLeafAndRebalance = (state, leaf, idx, removeCount, maxRebalanceDepth)
694
931
  var isLeafStillValid = (state, leaf) => leaf.parent === null ? leaf === state.root : leaf.parent.children[leaf.indexInParent] === leaf;
695
932
  var deleteRangeEntries = (state, startKey, endKey, options) => {
696
933
  if (state.entryCount === 0) return 0;
697
- const boundCompared = state.compareKeys(startKey, endKey);
698
- if (boundCompared > 0) return 0;
934
+ if (isEmptyRange(state.compareKeys, startKey, endKey, options)) return 0;
699
935
  const lowerExclusive = options?.lowerBound === "exclusive";
700
936
  const upperExclusive = options?.upperBound === "exclusive";
701
- if (lowerExclusive && upperExclusive && boundCompared === 0) return 0;
702
- const treeHeight = computeTreeHeight(state);
703
937
  let deleted = 0;
704
938
  let needsNavigate = true;
705
939
  let leaf = null;
@@ -714,10 +948,15 @@ var deleteRangeEntries = (state, startKey, endKey, options) => {
714
948
  }
715
949
  if (idx >= leafEntryCount(leaf)) break;
716
950
  const count = leafEntryCount(leaf);
717
- const removeEnd = findRemoveEnd(state, leaf, idx, endKey, upperExclusive);
951
+ const removeEnd = findRemoveEnd(state, leaf, endKey, upperExclusive);
718
952
  const removeCount = removeEnd - idx;
719
953
  if (removeCount === 0) break;
720
- const countAfterSplice = spliceLeafAndRebalance(state, leaf, idx, removeCount, treeHeight);
954
+ const countAfterSplice = spliceLeafAndRebalance(
955
+ state,
956
+ leaf,
957
+ idx,
958
+ removeCount
959
+ );
721
960
  deleted += removeCount;
722
961
  if (removeEnd < count) break;
723
962
  if (!isLeafStillValid(state, leaf)) {
@@ -736,6 +975,7 @@ var deleteRangeEntries = (state, startKey, endKey, options) => {
736
975
  };
737
976
 
738
977
  // src/btree/autoScale.ts
978
+ var minOccupancy = (max) => Math.ceil(max / 2);
739
979
  var AUTO_SCALE_TIERS = [
740
980
  { threshold: 0, maxLeaf: 32, maxBranch: 32 },
741
981
  { threshold: 1e3, maxLeaf: 64, maxBranch: 64 },
@@ -762,33 +1002,50 @@ var computeNextAutoScaleThreshold = (entryCount) => {
762
1002
  }
763
1003
  return Number.MAX_SAFE_INTEGER;
764
1004
  };
765
- var createInitialState = (config) => {
766
- if (typeof config.compareKeys !== "function") {
767
- throw new BTreeValidationError("compareKeys must be a function.");
768
- }
769
- const autoScale = config.autoScale === true;
1005
+ var resolveInitialCapacity = (config, autoScale) => {
770
1006
  if (autoScale && (config.maxLeafEntries !== void 0 || config.maxBranchChildren !== void 0)) {
771
- throw new BTreeValidationError("autoScale conflicts with explicit capacity.");
1007
+ throw new BTreeValidationError(
1008
+ "autoScale conflicts with explicit capacity."
1009
+ );
772
1010
  }
773
- let maxLeafEntries;
774
- let maxBranchChildren;
775
1011
  if (autoScale) {
776
1012
  const tier = computeAutoScaleTier(0);
777
- maxLeafEntries = tier.maxLeaf;
778
- maxBranchChildren = tier.maxBranch;
779
- } else {
780
- maxLeafEntries = normalizeNodeCapacity(config.maxLeafEntries, "maxLeafEntries", DEFAULT_MAX_LEAF_ENTRIES);
781
- maxBranchChildren = normalizeNodeCapacity(config.maxBranchChildren, "maxBranchChildren", DEFAULT_MAX_BRANCH_CHILDREN);
1013
+ return { maxLeafEntries: tier.maxLeaf, maxBranchChildren: tier.maxBranch };
782
1014
  }
1015
+ return {
1016
+ maxLeafEntries: normalizeNodeCapacity(
1017
+ config.maxLeafEntries,
1018
+ "maxLeafEntries",
1019
+ DEFAULT_MAX_LEAF_ENTRIES
1020
+ ),
1021
+ maxBranchChildren: normalizeNodeCapacity(
1022
+ config.maxBranchChildren,
1023
+ "maxBranchChildren",
1024
+ DEFAULT_MAX_BRANCH_CHILDREN
1025
+ )
1026
+ };
1027
+ };
1028
+ var createInitialState = (config) => {
1029
+ if (typeof config.compareKeys !== "function") {
1030
+ throw new BTreeValidationError("compareKeys must be a function.");
1031
+ }
1032
+ const autoScale = config.autoScale === true;
1033
+ const { maxLeafEntries, maxBranchChildren } = resolveInitialCapacity(
1034
+ config,
1035
+ autoScale
1036
+ );
783
1037
  const duplicateKeys = normalizeDuplicateKeyPolicy(config.duplicateKeys);
1038
+ const deleteRebalancePolicy = normalizeDeleteRebalancePolicy(
1039
+ config.deleteRebalancePolicy
1040
+ );
784
1041
  const emptyLeaf = createLeafNode([], null);
785
1042
  return {
786
1043
  compareKeys: config.compareKeys,
787
1044
  maxLeafEntries,
788
1045
  maxBranchChildren,
789
1046
  duplicateKeys,
790
- minLeafEntries: Math.ceil(maxLeafEntries / 2),
791
- minBranchChildren: Math.ceil(maxBranchChildren / 2),
1047
+ minLeafEntries: minOccupancy(maxLeafEntries),
1048
+ minBranchChildren: minOccupancy(maxBranchChildren),
792
1049
  root: emptyLeaf,
793
1050
  leftmostLeaf: emptyLeaf,
794
1051
  rightmostLeaf: emptyLeaf,
@@ -796,6 +1053,7 @@ var createInitialState = (config) => {
796
1053
  nextSequence: 0,
797
1054
  entryKeys: config.enableEntryIdLookup === true ? /* @__PURE__ */ new Map() : null,
798
1055
  autoScale,
1056
+ deleteRebalancePolicy,
799
1057
  _nextAutoScaleThreshold: autoScale ? computeNextAutoScaleThreshold(0) : Number.MAX_SAFE_INTEGER,
800
1058
  _cursor: { leaf: emptyLeaf, index: 0 }
801
1059
  };
@@ -805,13 +1063,15 @@ var maybeAutoScale = (state) => {
805
1063
  const { maxLeaf, maxBranch } = computeAutoScaleTier(state.entryCount);
806
1064
  if (maxLeaf > state.maxLeafEntries) {
807
1065
  state.maxLeafEntries = maxLeaf;
808
- state.minLeafEntries = Math.ceil(maxLeaf / 2);
1066
+ state.minLeafEntries = minOccupancy(maxLeaf);
809
1067
  }
810
1068
  if (maxBranch > state.maxBranchChildren) {
811
1069
  state.maxBranchChildren = maxBranch;
812
- state.minBranchChildren = Math.ceil(maxBranch / 2);
1070
+ state.minBranchChildren = minOccupancy(maxBranch);
813
1071
  }
814
- state._nextAutoScaleThreshold = computeNextAutoScaleThreshold(state.entryCount);
1072
+ state._nextAutoScaleThreshold = computeNextAutoScaleThreshold(
1073
+ state.entryCount
1074
+ );
815
1075
  };
816
1076
  var applyAutoScaleCapacitySnapshot = (state, maxLeafEntries, maxBranchChildren) => {
817
1077
  if (!state.autoScale) {
@@ -835,8 +1095,16 @@ var applyAutoScaleCapacitySnapshot = (state, maxLeafEntries, maxBranchChildren)
835
1095
  }
836
1096
  state.maxLeafEntries = normalizedLeaf;
837
1097
  state.maxBranchChildren = normalizedBranch;
838
- state.minLeafEntries = Math.ceil(normalizedLeaf / 2);
839
- state.minBranchChildren = Math.ceil(normalizedBranch / 2);
1098
+ state.minLeafEntries = minOccupancy(normalizedLeaf);
1099
+ state.minBranchChildren = minOccupancy(normalizedBranch);
1100
+ };
1101
+ var resetAutoScaleToTier0 = (state) => {
1102
+ const tier = computeAutoScaleTier(0);
1103
+ state.maxLeafEntries = tier.maxLeaf;
1104
+ state.maxBranchChildren = tier.maxBranch;
1105
+ state.minLeafEntries = minOccupancy(tier.maxLeaf);
1106
+ state.minBranchChildren = minOccupancy(tier.maxBranch);
1107
+ state._nextAutoScaleThreshold = computeNextAutoScaleThreshold(0);
840
1108
  };
841
1109
 
842
1110
  // src/btree/bulkLoad.ts
@@ -858,7 +1126,11 @@ var computeChunkBoundaries = (total, max, min) => {
858
1126
  return boundaries;
859
1127
  };
860
1128
  var buildLeaves = (state, entries, ids, baseSequence) => {
861
- const boundaries = computeChunkBoundaries(entries.length, state.maxLeafEntries, state.minLeafEntries);
1129
+ const boundaries = computeChunkBoundaries(
1130
+ entries.length,
1131
+ state.maxLeafEntries,
1132
+ state.minLeafEntries
1133
+ );
862
1134
  const leaves = new Array(boundaries.length);
863
1135
  let chunkStart = 0;
864
1136
  for (let c = 0; c < boundaries.length; c += 1) {
@@ -866,7 +1138,11 @@ var buildLeaves = (state, entries, ids, baseSequence) => {
866
1138
  const chunk = new Array(chunkEnd - chunkStart);
867
1139
  for (let i = chunkStart; i < chunkEnd; i += 1) {
868
1140
  const seq = baseSequence + i;
869
- chunk[i - chunkStart] = { key: entries[i].key, entryId: seq, value: entries[i].value };
1141
+ chunk[i - chunkStart] = createEntry(
1142
+ entries[i].key,
1143
+ seq,
1144
+ entries[i].value
1145
+ );
870
1146
  ids[i] = seq;
871
1147
  if (state.entryKeys !== null) {
872
1148
  state.entryKeys.set(seq, entries[i].key);
@@ -900,11 +1176,18 @@ var bulkLoadEntries = (state, entries) => {
900
1176
  } else {
901
1177
  let currentLevel = leaves;
902
1178
  while (currentLevel.length > 1) {
903
- const bounds = computeChunkBoundaries(currentLevel.length, state.maxBranchChildren, state.minBranchChildren);
1179
+ const bounds = computeChunkBoundaries(
1180
+ currentLevel.length,
1181
+ state.maxBranchChildren,
1182
+ state.minBranchChildren
1183
+ );
904
1184
  const nextLevel = new Array(bounds.length);
905
1185
  let start = 0;
906
1186
  for (let b = 0; b < bounds.length; b += 1) {
907
- nextLevel[b] = createBranchNode(currentLevel.slice(start, bounds[b]), null);
1187
+ nextLevel[b] = createBranchNode(
1188
+ currentLevel.slice(start, bounds[b]),
1189
+ null
1190
+ );
908
1191
  start = bounds[b];
909
1192
  }
910
1193
  currentLevel = nextLevel;
@@ -915,9 +1198,12 @@ var bulkLoadEntries = (state, entries) => {
915
1198
  return ids;
916
1199
  };
917
1200
 
918
- // src/btree/mutations.ts
1201
+ // src/btree/split.ts
919
1202
  var insertChildAfter = (state, parent, existingChild, childToInsert) => {
920
- const newChildMinKey = { key: void 0, sequence: 0 };
1203
+ const newChildMinKey = {
1204
+ key: void 0,
1205
+ sequence: 0
1206
+ };
921
1207
  if (!writeMinKeyTo(childToInsert, newChildMinKey)) {
922
1208
  throw new BTreeInvariantError("inserted child has no min key");
923
1209
  }
@@ -945,60 +1231,146 @@ var splitLeaf = (state, leaf) => {
945
1231
  } else {
946
1232
  state.rightmostLeaf = sibling;
947
1233
  }
948
- leaf.next = sibling;
949
- if (leaf.parent === null) {
950
- state.root = createBranchNode([leaf, sibling], null);
951
- return;
1234
+ leaf.next = sibling;
1235
+ if (leaf.parent === null) {
1236
+ state.root = createBranchNode([leaf, sibling], null);
1237
+ return;
1238
+ }
1239
+ insertChildAfter(state, leaf.parent, leaf, sibling);
1240
+ };
1241
+ var splitBranch = (state, branch) => {
1242
+ branchCompact(branch);
1243
+ const splitAt = Math.ceil(branch.children.length / 2);
1244
+ const sibling = createBranchNode(
1245
+ branch.children.splice(splitAt),
1246
+ branch.parent
1247
+ );
1248
+ branch.keys.splice(splitAt);
1249
+ if (branch.parent === null) {
1250
+ state.root = createBranchNode([branch, sibling], null);
1251
+ return;
1252
+ }
1253
+ insertChildAfter(state, branch.parent, branch, sibling);
1254
+ };
1255
+
1256
+ // src/btree/entry-lookup.ts
1257
+ var findLeafEntryBySequence = (state, userKey, sequence) => {
1258
+ const targetLeaf = findLeafForKey(state, userKey, sequence);
1259
+ const index = lowerBoundInLeaf(state, targetLeaf, userKey, sequence);
1260
+ if (index >= leafEntryCount(targetLeaf)) return null;
1261
+ const entry = leafEntryAt(targetLeaf, index);
1262
+ if (entry.entryId !== sequence) return null;
1263
+ return { leaf: targetLeaf, index };
1264
+ };
1265
+ var peekEntryById = (state, entryId) => {
1266
+ if (state.entryKeys === null) {
1267
+ throw new BTreeInvariantError(
1268
+ "entryKeys lookup map is not enabled on this tree."
1269
+ );
1270
+ }
1271
+ const userKey = state.entryKeys.get(entryId);
1272
+ if (userKey === void 0) return null;
1273
+ const found = findLeafEntryBySequence(state, userKey, entryId);
1274
+ if (found === null) return null;
1275
+ const entry = leafEntryAt(found.leaf, found.index);
1276
+ return entry;
1277
+ };
1278
+ var updateEntryById = (state, entryId, newValue) => {
1279
+ if (state.entryKeys === null) {
1280
+ throw new BTreeInvariantError(
1281
+ "entryKeys lookup map is not enabled on this tree."
1282
+ );
1283
+ }
1284
+ const userKey = state.entryKeys.get(entryId);
1285
+ if (userKey === void 0) return null;
1286
+ const found = findLeafEntryBySequence(state, userKey, entryId);
1287
+ if (found === null) return null;
1288
+ const entry = leafEntryAt(found.leaf, found.index);
1289
+ const updated = createEntry(entry.key, entry.entryId, newValue);
1290
+ found.leaf.entries[found.leaf.entryOffset + found.index] = updated;
1291
+ return updated;
1292
+ };
1293
+ var removeEntryById = (state, entryId) => {
1294
+ if (state.entryKeys === null) {
1295
+ throw new BTreeInvariantError(
1296
+ "entryKeys lookup map is not enabled on this tree."
1297
+ );
1298
+ }
1299
+ const userKey = state.entryKeys.get(entryId);
1300
+ if (userKey === void 0) return null;
1301
+ const found = findLeafEntryBySequence(state, userKey, entryId);
1302
+ if (found === null) return null;
1303
+ const entry = leafEntryAt(found.leaf, found.index);
1304
+ leafRemoveAt(found.leaf, found.index);
1305
+ state.entryCount -= 1;
1306
+ state.entryKeys.delete(entryId);
1307
+ if (found.index === 0 && leafEntryCount(found.leaf) > 0 && found.leaf.parent !== null) {
1308
+ updateMinKeyInAncestors(found.leaf);
1309
+ }
1310
+ if (found.leaf !== state.root && leafEntryCount(found.leaf) < state.minLeafEntries) {
1311
+ rebalanceAfterLeafRemoval(state, found.leaf);
952
1312
  }
953
- insertChildAfter(state, leaf.parent, leaf, sibling);
1313
+ return entry;
954
1314
  };
955
- var splitBranch = (state, branch) => {
956
- branchCompact(branch);
957
- const splitAt = Math.ceil(branch.children.length / 2);
958
- const sibling = createBranchNode(branch.children.splice(splitAt), branch.parent);
959
- branch.keys.splice(splitAt);
960
- if (branch.parent === null) {
961
- state.root = createBranchNode([branch, sibling], null);
962
- return;
1315
+
1316
+ // src/btree/mutations.ts
1317
+ var findDuplicateEntry = (state, targetLeaf, key, insertAt) => {
1318
+ if (state.duplicateKeys === "allow") return null;
1319
+ if (insertAt > 0) {
1320
+ const candidate = leafEntryAt(targetLeaf, insertAt - 1);
1321
+ if (state.compareKeys(candidate.key, key) === 0) {
1322
+ return {
1323
+ leaf: targetLeaf,
1324
+ physIndex: targetLeaf.entryOffset + insertAt - 1,
1325
+ entry: candidate
1326
+ };
1327
+ }
1328
+ } else if (targetLeaf.prev !== null && leafEntryCount(targetLeaf.prev) > 0) {
1329
+ const prevLeaf = targetLeaf.prev;
1330
+ const prevCount = leafEntryCount(prevLeaf);
1331
+ const candidate = leafEntryAt(prevLeaf, prevCount - 1);
1332
+ if (state.compareKeys(candidate.key, key) === 0) {
1333
+ return {
1334
+ leaf: prevLeaf,
1335
+ physIndex: prevLeaf.entryOffset + prevCount - 1,
1336
+ entry: candidate
1337
+ };
1338
+ }
963
1339
  }
964
- insertChildAfter(state, branch.parent, branch, sibling);
1340
+ return null;
965
1341
  };
966
1342
  var putEntryIntoLeaf = (state, targetLeaf, key, value) => {
967
1343
  const sequence = state.nextSequence;
968
1344
  const insertAt = upperBoundInLeaf(state, targetLeaf, key, sequence);
969
- if (state.duplicateKeys !== "allow") {
970
- let existingEntry = null;
971
- if (insertAt > 0) {
972
- const candidate = leafEntryAt(targetLeaf, insertAt - 1);
973
- if (state.compareKeys(candidate.key, key) === 0) {
974
- existingEntry = candidate;
975
- }
976
- } else if (targetLeaf.prev !== null && leafEntryCount(targetLeaf.prev) > 0) {
977
- const prevLeaf = targetLeaf.prev;
978
- const candidate = leafEntryAt(prevLeaf, leafEntryCount(prevLeaf) - 1);
979
- if (state.compareKeys(candidate.key, key) === 0) {
980
- existingEntry = candidate;
981
- }
982
- }
983
- if (existingEntry !== null) {
984
- if (state.duplicateKeys === "reject") {
985
- throw new BTreeValidationError("Duplicate key rejected.");
986
- }
987
- existingEntry.value = value;
988
- return existingEntry.entryId;
989
- }
1345
+ const dup = findDuplicateEntry(state, targetLeaf, key, insertAt);
1346
+ if (dup !== null) {
1347
+ if (state.duplicateKeys === "reject") {
1348
+ throw new BTreeValidationError("Duplicate key rejected.");
1349
+ }
1350
+ dup.leaf.entries[dup.physIndex] = createEntry(
1351
+ dup.entry.key,
1352
+ dup.entry.entryId,
1353
+ value
1354
+ );
1355
+ return dup.entry.entryId;
990
1356
  }
991
1357
  if (state.nextSequence >= Number.MAX_SAFE_INTEGER) {
992
1358
  throw new BTreeValidationError("Sequence overflow.");
993
1359
  }
994
1360
  state.nextSequence += 1;
995
- leafInsertAt(targetLeaf, insertAt, { key, entryId: sequence, value });
1361
+ leafInsertAt(
1362
+ targetLeaf,
1363
+ insertAt,
1364
+ createEntry(key, sequence, value)
1365
+ );
996
1366
  state.entryCount += 1;
997
1367
  if (state.entryKeys !== null) {
998
1368
  state.entryKeys.set(sequence, key);
999
1369
  }
1000
- if (insertAt === 0 && targetLeaf.parent !== null) updateMinKeyInAncestors(targetLeaf);
1001
- if (leafEntryCount(targetLeaf) > state.maxLeafEntries) splitLeaf(state, targetLeaf);
1370
+ if (insertAt === 0 && targetLeaf.parent !== null)
1371
+ updateMinKeyInAncestors(targetLeaf);
1372
+ if (leafEntryCount(targetLeaf) > state.maxLeafEntries)
1373
+ splitLeaf(state, targetLeaf);
1002
1374
  maybeAutoScale(state);
1003
1375
  return sequence;
1004
1376
  };
@@ -1009,7 +1381,8 @@ var putEntry = (state, key, value) => {
1009
1381
  var popFirstEntry = (state) => {
1010
1382
  if (state.entryCount === 0) return null;
1011
1383
  const firstEntry = leafShiftEntry(state.leftmostLeaf);
1012
- if (firstEntry === void 0) throw new BTreeInvariantError("leftmost leaf empty but count > 0");
1384
+ if (firstEntry === void 0)
1385
+ throw new BTreeInvariantError("leftmost leaf empty but count > 0");
1013
1386
  state.entryCount -= 1;
1014
1387
  if (state.entryKeys !== null) {
1015
1388
  state.entryKeys.delete(firstEntry.entryId);
@@ -1025,7 +1398,8 @@ var popFirstEntry = (state) => {
1025
1398
  var popLastEntry = (state) => {
1026
1399
  if (state.entryCount === 0) return null;
1027
1400
  const lastEntry = leafPopEntry(state.rightmostLeaf);
1028
- if (lastEntry === void 0) throw new BTreeInvariantError("rightmost leaf empty but count > 0");
1401
+ if (lastEntry === void 0)
1402
+ throw new BTreeInvariantError("rightmost leaf empty but count > 0");
1029
1403
  state.entryCount -= 1;
1030
1404
  if (state.entryKeys !== null) {
1031
1405
  state.entryKeys.delete(lastEntry.entryId);
@@ -1046,62 +1420,26 @@ var removeFirstMatchingEntry = (state, key) => {
1046
1420
  if (state.entryKeys !== null) {
1047
1421
  state.entryKeys.delete(targetEntry.entryId);
1048
1422
  }
1049
- if (removeAt === 0 && leafEntryCount(targetLeaf) > 0 && targetLeaf.parent !== null) updateMinKeyInAncestors(targetLeaf);
1423
+ if (removeAt === 0 && leafEntryCount(targetLeaf) > 0 && targetLeaf.parent !== null)
1424
+ updateMinKeyInAncestors(targetLeaf);
1050
1425
  if (targetLeaf !== state.root && leafEntryCount(targetLeaf) < state.minLeafEntries) {
1051
1426
  rebalanceAfterLeafRemoval(state, targetLeaf);
1052
1427
  }
1053
1428
  return targetEntry;
1054
1429
  };
1055
- var findLeafEntryBySequence = (state, userKey, sequence) => {
1056
- const targetLeaf = findLeafForKey(state, userKey, sequence);
1057
- const index = lowerBoundInLeaf(state, targetLeaf, userKey, sequence);
1058
- if (index >= leafEntryCount(targetLeaf)) return null;
1059
- const entry = leafEntryAt(targetLeaf, index);
1060
- if (entry.entryId !== sequence) return null;
1061
- return { leaf: targetLeaf, index };
1062
- };
1063
- var removeEntryById = (state, entryId) => {
1064
- const userKey = state.entryKeys.get(entryId);
1065
- if (userKey === void 0) return null;
1066
- const found = findLeafEntryBySequence(state, userKey, entryId);
1067
- if (found === null) return null;
1068
- const entry = leafEntryAt(found.leaf, found.index);
1069
- leafRemoveAt(found.leaf, found.index);
1070
- state.entryCount -= 1;
1071
- state.entryKeys.delete(entryId);
1072
- if (found.index === 0 && leafEntryCount(found.leaf) > 0 && found.leaf.parent !== null) {
1073
- updateMinKeyInAncestors(found.leaf);
1074
- }
1075
- if (found.leaf !== state.root && leafEntryCount(found.leaf) < state.minLeafEntries) {
1076
- rebalanceAfterLeafRemoval(state, found.leaf);
1077
- }
1078
- return entry;
1079
- };
1080
- var peekEntryById = (state, entryId) => {
1081
- const userKey = state.entryKeys.get(entryId);
1082
- if (userKey === void 0) return null;
1083
- const found = findLeafEntryBySequence(state, userKey, entryId);
1084
- if (found === null) return null;
1085
- const entry = leafEntryAt(found.leaf, found.index);
1086
- return entry;
1087
- };
1088
- var updateEntryById = (state, entryId, newValue) => {
1089
- const userKey = state.entryKeys.get(entryId);
1090
- if (userKey === void 0) return null;
1091
- const found = findLeafEntryBySequence(state, userKey, entryId);
1092
- if (found === null) return null;
1093
- const entry = leafEntryAt(found.leaf, found.index);
1094
- entry.value = newValue;
1095
- return entry;
1096
- };
1097
1430
  var putManyEntries = (state, entries) => {
1098
1431
  if (entries.length === 0) return [];
1099
1432
  const strictlyAscending = state.duplicateKeys !== "allow";
1100
1433
  for (let i = 1; i < entries.length; i += 1) {
1101
1434
  const cmp = state.compareKeys(entries[i - 1].key, entries[i].key);
1102
- if (strictlyAscending ? cmp >= 0 : cmp > 0) {
1435
+ if (cmp > 0) {
1436
+ throw new BTreeValidationError(
1437
+ "putMany: entries not in ascending order."
1438
+ );
1439
+ }
1440
+ if (strictlyAscending && cmp === 0) {
1103
1441
  throw new BTreeValidationError(
1104
- strictlyAscending ? "putMany: not sorted in strict ascending order." : "putMany: not sorted in non-descending order."
1442
+ state.duplicateKeys === "reject" ? "putMany: duplicate key rejected." : "putMany: equal keys not allowed in strict mode."
1105
1443
  );
1106
1444
  }
1107
1445
  }
@@ -1110,7 +1448,12 @@ var putManyEntries = (state, entries) => {
1110
1448
  let hintLeaf = findLeafForKey(state, entries[0].key, state.nextSequence);
1111
1449
  for (let i = 0; i < entries.length; i += 1) {
1112
1450
  const entry = entries[i];
1113
- const targetLeaf = findLeafFromHint(state, hintLeaf, entry.key, state.nextSequence);
1451
+ const targetLeaf = findLeafFromHint(
1452
+ state,
1453
+ hintLeaf,
1454
+ entry.key,
1455
+ state.nextSequence
1456
+ );
1114
1457
  ids[i] = putEntryIntoLeaf(state, targetLeaf, entry.key, entry.value);
1115
1458
  hintLeaf = targetLeaf;
1116
1459
  }
@@ -1119,116 +1462,14 @@ var putManyEntries = (state, entries) => {
1119
1462
  return bulkLoadEntries(state, entries);
1120
1463
  };
1121
1464
 
1122
- // src/btree/rangeQuery.ts
1123
- var initRangeCursor = (state, startKey, endKey, options) => {
1124
- if (state.entryCount === 0) return null;
1125
- const compare = state.compareKeys;
1126
- const boundCompared = compare(startKey, endKey);
1127
- if (boundCompared > 0) return null;
1128
- const lowerExclusive = options?.lowerBound === "exclusive";
1129
- const upperExclusive = options?.upperBound === "exclusive";
1130
- if (lowerExclusive && upperExclusive && boundCompared === 0) return null;
1131
- const startSeq = lowerExclusive ? Number.MAX_SAFE_INTEGER : 0;
1132
- const leaf = findLeafForKey(state, startKey, startSeq);
1133
- const index = lowerExclusive ? upperBoundInLeaf(state, leaf, startKey, Number.MAX_SAFE_INTEGER) : lowerBoundInLeaf(state, leaf, startKey, 0);
1134
- return { leaf, index, compare, upperExclusive };
1135
- };
1136
- var countRangeEntries = (state, startKey, endKey, options) => {
1137
- const cursor = initRangeCursor(state, startKey, endKey, options);
1138
- if (cursor === null) return 0;
1139
- let cursorLeaf = cursor.leaf;
1140
- let cursorIndex = cursor.index;
1141
- const { compare, upperExclusive } = cursor;
1142
- let count = 0;
1143
- while (cursorLeaf !== null) {
1144
- const leafCount = leafEntryCount(cursorLeaf);
1145
- if (cursorIndex >= leafCount) {
1146
- cursorLeaf = cursorLeaf.next;
1147
- cursorIndex = 0;
1148
- continue;
1149
- }
1150
- const lastEntry = leafEntryAt(cursorLeaf, leafCount - 1);
1151
- const cmpLast = compare(lastEntry.key, endKey);
1152
- if (upperExclusive ? cmpLast < 0 : cmpLast <= 0) {
1153
- count += leafCount - cursorIndex;
1154
- cursorLeaf = cursorLeaf.next;
1155
- cursorIndex = 0;
1156
- continue;
1157
- }
1158
- const endSeq = upperExclusive ? 0 : Number.MAX_SAFE_INTEGER;
1159
- const endBound = upperExclusive ? lowerBoundInLeaf(state, cursorLeaf, endKey, endSeq) : upperBoundInLeaf(state, cursorLeaf, endKey, endSeq);
1160
- const limit = endBound < leafCount ? endBound : leafCount;
1161
- count += limit - cursorIndex;
1162
- return count;
1163
- }
1164
- return count;
1165
- };
1166
- var RANGE_PREALLOC_THRESHOLD = 200;
1167
- var allocateRangeOutput = (state, cursorLeaf, cursorIndex, compare, upperExclusive, startKey, endKey, options) => {
1168
- const firstLeafCount = leafEntryCount(cursorLeaf);
1169
- const firstLeafRemainder = firstLeafCount - cursorIndex;
1170
- if (firstLeafRemainder >= RANGE_PREALLOC_THRESHOLD && cursorLeaf.next !== null) {
1171
- const lastEntry = leafEntryAt(cursorLeaf, firstLeafCount - 1);
1172
- const cmpLast = compare(lastEntry.key, endKey);
1173
- if (upperExclusive ? cmpLast < 0 : cmpLast <= 0) {
1174
- const total = countRangeEntries(state, startKey, endKey, options);
1175
- return new Array(total);
1176
- }
1177
- }
1178
- return [];
1179
- };
1180
- var appendLeafSlice = (leaf, from, to, output, useIndexed, writeIdx) => {
1181
- if (useIndexed) {
1182
- for (let i = from; i < to; i += 1) {
1183
- output[writeIdx++] = leafEntryAt(leaf, i);
1184
- }
1185
- } else {
1186
- for (let i = from; i < to; i += 1) {
1187
- output.push(leafEntryAt(leaf, i));
1188
- }
1189
- }
1190
- return writeIdx;
1191
- };
1192
- var rangeQueryEntries = (state, startKey, endKey, options) => {
1193
- const cursor = initRangeCursor(state, startKey, endKey, options);
1194
- if (cursor === null) return [];
1195
- let cursorLeaf = cursor.leaf;
1196
- let cursorIndex = cursor.index;
1197
- const { compare, upperExclusive } = cursor;
1198
- const output = allocateRangeOutput(state, cursorLeaf, cursorIndex, compare, upperExclusive, startKey, endKey, options);
1199
- let writeIdx = 0;
1200
- const useIndexed = output.length > 0;
1201
- while (cursorLeaf !== null) {
1202
- const leafCount = leafEntryCount(cursorLeaf);
1203
- if (cursorIndex >= leafCount) {
1204
- cursorLeaf = cursorLeaf.next;
1205
- cursorIndex = 0;
1206
- continue;
1207
- }
1208
- const lastEntry = leafEntryAt(cursorLeaf, leafCount - 1);
1209
- const cmpLast = compare(lastEntry.key, endKey);
1210
- if (upperExclusive ? cmpLast < 0 : cmpLast <= 0) {
1211
- writeIdx = appendLeafSlice(cursorLeaf, cursorIndex, leafCount, output, useIndexed, writeIdx);
1212
- cursorLeaf = cursorLeaf.next;
1213
- cursorIndex = 0;
1214
- continue;
1215
- }
1216
- const endSeq = upperExclusive ? 0 : Number.MAX_SAFE_INTEGER;
1217
- const endBound = upperExclusive ? lowerBoundInLeaf(state, cursorLeaf, endKey, endSeq) : upperBoundInLeaf(state, cursorLeaf, endKey, endSeq);
1218
- const limit = endBound < leafCount ? endBound : leafCount;
1219
- appendLeafSlice(cursorLeaf, cursorIndex, limit, output, useIndexed, writeIdx);
1220
- return output;
1221
- }
1222
- return output;
1223
- };
1224
-
1225
1465
  // src/btree/serialization.ts
1226
1466
  var buildConfigFromState = (state) => {
1227
1467
  const config = {
1228
1468
  compareKeys: state.compareKeys,
1229
1469
  duplicateKeys: state.duplicateKeys,
1230
1470
  enableEntryIdLookup: state.entryKeys !== null,
1231
- autoScale: state.autoScale
1471
+ autoScale: state.autoScale,
1472
+ deleteRebalancePolicy: state.deleteRebalancePolicy
1232
1473
  };
1233
1474
  if (!state.autoScale) {
1234
1475
  config.maxLeafEntries = state.maxLeafEntries;
@@ -1248,17 +1489,17 @@ var serializeToJSON = (state) => {
1248
1489
  }
1249
1490
  leaf = leaf.next;
1250
1491
  }
1251
- return {
1252
- version: 1,
1253
- config: {
1254
- maxLeafEntries: state.maxLeafEntries,
1255
- maxBranchChildren: state.maxBranchChildren,
1256
- duplicateKeys: state.duplicateKeys,
1257
- enableEntryIdLookup: state.entryKeys !== null,
1258
- autoScale: state.autoScale
1259
- },
1260
- entries
1492
+ const config = {
1493
+ maxLeafEntries: state.maxLeafEntries,
1494
+ maxBranchChildren: state.maxBranchChildren,
1495
+ duplicateKeys: state.duplicateKeys,
1496
+ enableEntryIdLookup: state.entryKeys !== null,
1497
+ autoScale: state.autoScale
1261
1498
  };
1499
+ if (state.deleteRebalancePolicy !== "standard") {
1500
+ config.deleteRebalancePolicy = state.deleteRebalancePolicy;
1501
+ }
1502
+ return { version: 1, config, entries };
1262
1503
  };
1263
1504
  var MAX_SERIALIZED_ENTRIES = 1e6;
1264
1505
  var validateStructure = (json) => {
@@ -1321,13 +1562,28 @@ var validateBTreeJSON = (json) => {
1321
1562
  validateStructure(json);
1322
1563
  validateConfig(json.config);
1323
1564
  };
1565
+ var validateBTreeJSONSortOrder = (json, compareKeys) => {
1566
+ const strict = json.config.duplicateKeys !== "allow";
1567
+ for (let i = 1; i < json.entries.length; i += 1) {
1568
+ const cmp = compareKeys(json.entries[i - 1][0], json.entries[i][0]);
1569
+ if (cmp > 0) {
1570
+ throw new BTreeValidationError("fromJSON: entries not sorted.");
1571
+ }
1572
+ if (strict && cmp === 0) {
1573
+ throw new BTreeValidationError(
1574
+ 'fromJSON: duplicate keys require duplicateKeys "allow".'
1575
+ );
1576
+ }
1577
+ }
1578
+ };
1324
1579
  var buildConfigFromJSON = (json, compareKeys) => {
1325
1580
  const cfg = json.config;
1326
1581
  const config = {
1327
1582
  compareKeys,
1328
1583
  duplicateKeys: cfg.duplicateKeys,
1329
1584
  enableEntryIdLookup: cfg.enableEntryIdLookup,
1330
- autoScale: cfg.autoScale
1585
+ autoScale: cfg.autoScale,
1586
+ deleteRebalancePolicy: cfg.deleteRebalancePolicy
1331
1587
  };
1332
1588
  if (!cfg.autoScale) {
1333
1589
  config.maxLeafEntries = cfg.maxLeafEntries;
@@ -1336,6 +1592,44 @@ var buildConfigFromJSON = (json, compareKeys) => {
1336
1592
  return config;
1337
1593
  };
1338
1594
 
1595
+ // src/btree/traversal.ts
1596
+ var snapshotEntries = (state) => {
1597
+ const result = new Array(state.entryCount);
1598
+ let leaf = state.leftmostLeaf;
1599
+ let writeIdx = 0;
1600
+ while (leaf !== null) {
1601
+ const count = leafEntryCount(leaf);
1602
+ for (let i = 0; i < count; i += 1) {
1603
+ result[writeIdx++] = freezeEntry(leafEntryAt(leaf, i));
1604
+ }
1605
+ leaf = leaf.next;
1606
+ }
1607
+ return result;
1608
+ };
1609
+ var collectInternalEntries = (state) => {
1610
+ const result = new Array(state.entryCount);
1611
+ let leaf = state.leftmostLeaf;
1612
+ let writeIdx = 0;
1613
+ while (leaf !== null) {
1614
+ const count = leafEntryCount(leaf);
1615
+ for (let i = 0; i < count; i += 1) {
1616
+ result[writeIdx++] = leafEntryAt(leaf, i);
1617
+ }
1618
+ leaf = leaf.next;
1619
+ }
1620
+ return result;
1621
+ };
1622
+ var forEachEntry = (state, callback, thisArg) => {
1623
+ let leaf = state.leftmostLeaf;
1624
+ while (leaf !== null) {
1625
+ const count = leafEntryCount(leaf);
1626
+ for (let i = 0; i < count; i += 1) {
1627
+ callback.call(thisArg, freezeEntry(leafEntryAt(leaf, i)));
1628
+ }
1629
+ leaf = leaf.next;
1630
+ }
1631
+ };
1632
+
1339
1633
  // src/btree/integrity-helpers.ts
1340
1634
  var nodeMinKey = (node) => {
1341
1635
  if (isLeafNode(node)) {
@@ -1344,14 +1638,17 @@ var nodeMinKey = (node) => {
1344
1638
  return { key: e.key, sequence: e.entryId };
1345
1639
  }
1346
1640
  if (node.childOffset >= node.keys.length) return null;
1347
- return { key: node.keys[node.childOffset].key, sequence: node.keys[node.childOffset].sequence };
1641
+ return {
1642
+ key: node.keys[node.childOffset].key,
1643
+ sequence: node.keys[node.childOffset].sequence
1644
+ };
1348
1645
  };
1349
1646
  var compareNodeKeys = (comparator, leftKey, leftSeq, rightKey, rightSeq) => {
1350
1647
  const cmp = comparator(leftKey, rightKey);
1351
1648
  if (cmp !== 0) {
1352
1649
  return cmp;
1353
1650
  }
1354
- return leftSeq - rightSeq;
1651
+ return leftSeq < rightSeq ? -1 : leftSeq > rightSeq ? 1 : 0;
1355
1652
  };
1356
1653
  var getNodeMaxKey = (node) => {
1357
1654
  if (isLeafNode(node)) {
@@ -1366,9 +1663,7 @@ var getNodeMaxKey = (node) => {
1366
1663
  };
1367
1664
  var validateComparatorResult = (result) => {
1368
1665
  if (!Number.isFinite(result)) {
1369
- throw new BTreeValidationError(
1370
- "compareKeys must return a finite number."
1371
- );
1666
+ throw new BTreeValidationError("compareKeys must return a finite number.");
1372
1667
  }
1373
1668
  return result;
1374
1669
  };
@@ -1428,6 +1723,34 @@ var assertTransitivityAsInvariant = (state, first, second, third) => {
1428
1723
  throw error;
1429
1724
  }
1430
1725
  };
1726
+ var validateAdjacentLeafOrdering = (state, previous, cursor) => {
1727
+ const prevMax = getNodeMaxKey(previous);
1728
+ const currentMin = nodeMinKey(cursor);
1729
+ if (prevMax === null || currentMin === null) {
1730
+ throw new BTreeInvariantError(
1731
+ "Non-empty tree leaf chain contains empty leaf node."
1732
+ );
1733
+ }
1734
+ if (compareNodeKeys(
1735
+ state.compareKeys,
1736
+ prevMax.key,
1737
+ prevMax.sequence,
1738
+ currentMin.key,
1739
+ currentMin.sequence
1740
+ ) > 0) {
1741
+ throw new BTreeInvariantError("Adjacent leaf key ranges are out of order.");
1742
+ }
1743
+ const prevCount = leafEntryCount(previous);
1744
+ const curCount = leafEntryCount(cursor);
1745
+ if (state.duplicateKeys !== "allow" && prevCount > 0 && curCount > 0 && state.compareKeys(
1746
+ leafEntryAt(previous, prevCount - 1).key,
1747
+ leafEntryAt(cursor, 0).key
1748
+ ) === 0) {
1749
+ throw new BTreeInvariantError(
1750
+ "Duplicate user key detected across adjacent leaves with uniqueness policy."
1751
+ );
1752
+ }
1753
+ };
1431
1754
  var validateLeafChainStep = (state, cursor, previous, visited) => {
1432
1755
  if (!isLeafNode(cursor)) {
1433
1756
  throw new BTreeInvariantError("Leaf linkage cursor reached non-leaf node.");
@@ -1439,22 +1762,7 @@ var validateLeafChainStep = (state, cursor, previous, visited) => {
1439
1762
  throw new BTreeInvariantError("Leaf prev pointer mismatch.");
1440
1763
  }
1441
1764
  if (previous !== null && isLeafNode(previous)) {
1442
- const prevMax = getNodeMaxKey(previous);
1443
- const currentMin = nodeMinKey(cursor);
1444
- if (prevMax === null || currentMin === null) {
1445
- throw new BTreeInvariantError("Non-empty tree leaf chain contains empty leaf node.");
1446
- }
1447
- if (compareNodeKeys(state.compareKeys, prevMax.key, prevMax.sequence, currentMin.key, currentMin.sequence) > 0) {
1448
- throw new BTreeInvariantError("Adjacent leaf key ranges are out of order.");
1449
- }
1450
- const prevCount = leafEntryCount(previous);
1451
- const curCount = leafEntryCount(cursor);
1452
- if (state.duplicateKeys !== "allow" && prevCount > 0 && curCount > 0 && state.compareKeys(
1453
- leafEntryAt(previous, prevCount - 1).key,
1454
- leafEntryAt(cursor, 0).key
1455
- ) === 0) {
1456
- throw new BTreeInvariantError("Duplicate user key detected across adjacent leaves with uniqueness policy.");
1457
- }
1765
+ validateAdjacentLeafOrdering(state, previous, cursor);
1458
1766
  }
1459
1767
  };
1460
1768
  var validateLeafLinks = (state, expectedLeafCount) => {
@@ -1463,7 +1771,9 @@ var validateLeafLinks = (state, expectedLeafCount) => {
1463
1771
  throw new BTreeInvariantError("Empty tree root must be a leaf node.");
1464
1772
  }
1465
1773
  if (state.leftmostLeaf !== state.root || state.rightmostLeaf !== state.root) {
1466
- throw new BTreeInvariantError("Empty tree leaf pointers must reference root leaf.");
1774
+ throw new BTreeInvariantError(
1775
+ "Empty tree leaf pointers must reference root leaf."
1776
+ );
1467
1777
  }
1468
1778
  return;
1469
1779
  }
@@ -1488,7 +1798,9 @@ var validateLeafLinks = (state, expectedLeafCount) => {
1488
1798
  throw new BTreeInvariantError("Rightmost leaf pointer mismatch.");
1489
1799
  }
1490
1800
  if (leafCount !== expectedLeafCount) {
1491
- throw new BTreeInvariantError("Leaf chain count mismatch with tree traversal count.");
1801
+ throw new BTreeInvariantError(
1802
+ "Leaf chain count mismatch with tree traversal count."
1803
+ );
1492
1804
  }
1493
1805
  };
1494
1806
 
@@ -1515,7 +1827,9 @@ var validateLeafNodeOrdering = (state, node) => {
1515
1827
  leafEntryAt(node, index - 1).key,
1516
1828
  leafEntryAt(node, index).key
1517
1829
  ) === 0) {
1518
- throw new BTreeInvariantError("Duplicate user key detected in tree with uniqueness policy.");
1830
+ throw new BTreeInvariantError(
1831
+ "Duplicate user key detected in tree with uniqueness policy."
1832
+ );
1519
1833
  }
1520
1834
  }
1521
1835
  }
@@ -1523,12 +1837,7 @@ var validateLeafNodeOrdering = (state, node) => {
1523
1837
  const first = leafEntryAt(node, index - 2);
1524
1838
  const second = leafEntryAt(node, index - 1);
1525
1839
  const third = leafEntryAt(node, index);
1526
- assertTransitivityAsInvariant(
1527
- state,
1528
- first.key,
1529
- second.key,
1530
- third.key
1531
- );
1840
+ assertTransitivityAsInvariant(state, first.key, second.key, third.key);
1532
1841
  }
1533
1842
  if (count > state.maxLeafEntries) {
1534
1843
  throw new BTreeInvariantError("Leaf node exceeds maximum occupancy.");
@@ -1537,9 +1846,14 @@ var validateLeafNodeOrdering = (state, node) => {
1537
1846
  var validateLeafNode = (state, node, depth) => {
1538
1847
  validateLeafNodeOrdering(state, node);
1539
1848
  const count = leafEntryCount(node);
1540
- const baseMinLeaf = state.autoScale ? Math.ceil(computeAutoScaleTier(0).maxLeaf / 2) : state.minLeafEntries;
1849
+ let baseMinLeaf = state.autoScale ? Math.ceil(computeAutoScaleTier(0).maxLeaf / 2) : state.minLeafEntries;
1850
+ if (state.deleteRebalancePolicy === "lazy") {
1851
+ baseMinLeaf = applyLazyThreshold(baseMinLeaf);
1852
+ }
1541
1853
  if (node !== state.root && count < baseMinLeaf) {
1542
- throw new BTreeInvariantError("Non-root leaf node violates minimum occupancy.");
1854
+ throw new BTreeInvariantError(
1855
+ "Non-root leaf node violates minimum occupancy."
1856
+ );
1543
1857
  }
1544
1858
  const first = count === 0 ? null : leafEntryAt(node, 0);
1545
1859
  const last = count === 0 ? null : leafEntryAt(node, count - 1);
@@ -1561,76 +1875,110 @@ var validateBranchStructure = (state, node) => {
1561
1875
  }
1562
1876
  const baseMinBranch = state.autoScale ? Math.ceil(computeAutoScaleTier(0).maxBranch / 2) : state.minBranchChildren;
1563
1877
  if (node !== state.root && liveCount < baseMinBranch) {
1564
- throw new BTreeInvariantError("Non-root branch node violates minimum occupancy.");
1878
+ throw new BTreeInvariantError(
1879
+ "Non-root branch node violates minimum occupancy."
1880
+ );
1565
1881
  }
1566
1882
  if (liveCount > state.maxBranchChildren) {
1567
1883
  throw new BTreeInvariantError("Branch node exceeds maximum occupancy.");
1568
1884
  }
1569
1885
  if (node.keys.length !== node.children.length) {
1570
- throw new BTreeInvariantError("Branch keys array length does not match children array length.");
1886
+ throw new BTreeInvariantError(
1887
+ "Branch keys array length does not match children array length."
1888
+ );
1571
1889
  }
1572
1890
  };
1573
1891
  var validateBranchChild = (state, node, childIndex, depth) => {
1574
1892
  const child = node.children[childIndex];
1575
1893
  if (child.parent !== node) {
1576
- throw new BTreeInvariantError("Child-parent pointer mismatch in branch node.");
1894
+ throw new BTreeInvariantError(
1895
+ "Child-parent pointer mismatch in branch node."
1896
+ );
1577
1897
  }
1578
1898
  if (child.indexInParent !== childIndex) {
1579
- throw new BTreeInvariantError("Child indexInParent does not match actual position in parent.");
1899
+ throw new BTreeInvariantError(
1900
+ "Child indexInParent does not match actual position in parent."
1901
+ );
1580
1902
  }
1581
1903
  const childValidation = validateNode(state, child, depth + 1);
1582
1904
  if (childValidation.minKey === null || childValidation.maxKey === null) {
1583
- throw new BTreeInvariantError("Branch child must not be empty in non-root branch tree.");
1905
+ throw new BTreeInvariantError(
1906
+ "Branch child must not be empty in non-root branch tree."
1907
+ );
1584
1908
  }
1585
1909
  const cachedMinKey = node.keys[childIndex];
1586
1910
  const actualMinKey = nodeMinKey(child);
1587
- if (actualMinKey === null || compareNodeKeys(state.compareKeys, cachedMinKey.key, cachedMinKey.sequence, actualMinKey.key, actualMinKey.sequence) !== 0) {
1588
- throw new BTreeInvariantError("Branch cached key does not match actual child minimum key.");
1911
+ if (actualMinKey === null || compareNodeKeys(
1912
+ state.compareKeys,
1913
+ cachedMinKey.key,
1914
+ cachedMinKey.sequence,
1915
+ actualMinKey.key,
1916
+ actualMinKey.sequence
1917
+ ) !== 0) {
1918
+ throw new BTreeInvariantError(
1919
+ "Branch cached key does not match actual child minimum key."
1920
+ );
1589
1921
  }
1590
1922
  return childValidation;
1591
1923
  };
1924
+ var mergeChildValidation = (state, accumulated, childValidation) => {
1925
+ if (accumulated.leafDepth !== null && childValidation.leafDepth !== null && childValidation.leafDepth !== accumulated.leafDepth) {
1926
+ throw new BTreeInvariantError("Leaf depth mismatch detected in tree.");
1927
+ }
1928
+ if (accumulated.leafDepth === null && childValidation.leafDepth !== null) {
1929
+ accumulated.leafDepth = childValidation.leafDepth;
1930
+ }
1931
+ if (accumulated.previousChildMax !== null && compareNodeKeys(
1932
+ state.compareKeys,
1933
+ accumulated.previousChildMax.key,
1934
+ accumulated.previousChildMax.sequence,
1935
+ childValidation.minKey.key,
1936
+ childValidation.minKey.sequence
1937
+ ) >= 0) {
1938
+ throw new BTreeInvariantError(
1939
+ "Branch child key ranges are not strictly ordered."
1940
+ );
1941
+ }
1942
+ if (accumulated.minKey === null) accumulated.minKey = childValidation.minKey;
1943
+ accumulated.maxKey = childValidation.maxKey;
1944
+ accumulated.previousChildMax = childValidation.maxKey;
1945
+ accumulated.leafCount += childValidation.leafCount;
1946
+ accumulated.branchCount += childValidation.branchCount;
1947
+ accumulated.entryCount += childValidation.entryCount;
1948
+ };
1592
1949
  var validateNode = (state, node, depth) => {
1593
1950
  if (isLeafNode(node)) {
1594
1951
  return validateLeafNode(state, node, depth);
1595
1952
  }
1596
1953
  validateBranchStructure(state, node);
1597
- let leafDepth = null;
1598
- let leafCount = 0;
1599
- let branchCount = 1;
1600
- let entryCount = 0;
1601
- let minKey = null;
1602
- let maxKey = null;
1603
- let previousChildMax = null;
1954
+ const acc = {
1955
+ leafDepth: null,
1956
+ leafCount: 0,
1957
+ branchCount: 1,
1958
+ entryCount: 0,
1959
+ minKey: null,
1960
+ maxKey: null,
1961
+ previousChildMax: null
1962
+ };
1604
1963
  for (let childIndex = node.childOffset; childIndex < node.children.length; childIndex += 1) {
1605
1964
  const childValidation = validateBranchChild(state, node, childIndex, depth);
1606
- if (leafDepth !== null && childValidation.leafDepth !== null && childValidation.leafDepth !== leafDepth) {
1607
- throw new BTreeInvariantError("Leaf depth mismatch detected in tree.");
1608
- }
1609
- if (leafDepth === null && childValidation.leafDepth !== null) {
1610
- leafDepth = childValidation.leafDepth;
1611
- }
1612
- if (previousChildMax !== null && compareNodeKeys(
1613
- state.compareKeys,
1614
- previousChildMax.key,
1615
- previousChildMax.sequence,
1616
- childValidation.minKey.key,
1617
- childValidation.minKey.sequence
1618
- ) >= 0) {
1619
- throw new BTreeInvariantError("Branch child key ranges are not strictly ordered.");
1620
- }
1621
- if (minKey === null) minKey = childValidation.minKey;
1622
- maxKey = childValidation.maxKey;
1623
- previousChildMax = childValidation.maxKey;
1624
- leafCount += childValidation.leafCount;
1625
- branchCount += childValidation.branchCount;
1626
- entryCount += childValidation.entryCount;
1965
+ mergeChildValidation(state, acc, childValidation);
1627
1966
  }
1628
- return { minKey, maxKey, leafDepth, leafCount, branchCount, entryCount };
1967
+ return {
1968
+ minKey: acc.minKey,
1969
+ maxKey: acc.maxKey,
1970
+ leafDepth: acc.leafDepth,
1971
+ leafCount: acc.leafCount,
1972
+ branchCount: acc.branchCount,
1973
+ entryCount: acc.entryCount
1974
+ };
1629
1975
  };
1630
1976
  var assertInvariants = (state) => {
1631
1977
  const validation = validateNode(state, state.root, 0);
1632
1978
  if (validation.entryCount !== state.entryCount) {
1633
- throw new BTreeInvariantError("Index entry count mismatch between tree traversal and tracked state.");
1979
+ throw new BTreeInvariantError(
1980
+ "Index entry count mismatch between tree traversal and tracked state."
1981
+ );
1634
1982
  }
1635
1983
  validateLeafLinks(state, validation.leafCount);
1636
1984
  };
@@ -1684,44 +2032,56 @@ var InMemoryBTree = class _InMemoryBTree {
1684
2032
  return putManyEntries(this.state, entries);
1685
2033
  }
1686
2034
  remove(key) {
1687
- return removeFirstMatchingEntry(this.state, key);
2035
+ const entry = removeFirstMatchingEntry(this.state, key);
2036
+ if (entry === null) return null;
2037
+ return freezeEntry(entry);
1688
2038
  }
1689
2039
  removeById(entryId) {
1690
2040
  if (this.state.entryKeys === null) {
1691
2041
  throw new BTreeValidationError("Requires enableEntryIdLookup: true.");
1692
2042
  }
1693
- return removeEntryById(this.state, entryId);
2043
+ const entry = removeEntryById(this.state, entryId);
2044
+ if (entry === null) return null;
2045
+ return freezeEntry(entry);
1694
2046
  }
1695
2047
  peekById(entryId) {
1696
2048
  if (this.state.entryKeys === null) {
1697
2049
  throw new BTreeValidationError("Requires enableEntryIdLookup: true.");
1698
2050
  }
1699
- return peekEntryById(this.state, entryId);
2051
+ const entry = peekEntryById(this.state, entryId);
2052
+ if (entry === null) return null;
2053
+ return freezeEntry(entry);
1700
2054
  }
1701
2055
  updateById(entryId, value) {
1702
2056
  if (this.state.entryKeys === null) {
1703
2057
  throw new BTreeValidationError("Requires enableEntryIdLookup: true.");
1704
2058
  }
1705
- return updateEntryById(this.state, entryId, value);
2059
+ const entry = updateEntryById(this.state, entryId, value);
2060
+ if (entry === null) return null;
2061
+ return freezeEntry(entry);
1706
2062
  }
1707
2063
  popFirst() {
1708
- return popFirstEntry(this.state);
2064
+ const entry = popFirstEntry(this.state);
2065
+ if (entry === null) return null;
2066
+ return freezeEntry(entry);
1709
2067
  }
1710
2068
  peekFirst() {
1711
2069
  if (this.state.entryCount === 0) {
1712
2070
  return null;
1713
2071
  }
1714
- return leafEntryAt(this.state.leftmostLeaf, 0);
2072
+ return freezeEntry(leafEntryAt(this.state.leftmostLeaf, 0));
1715
2073
  }
1716
2074
  peekLast() {
1717
2075
  if (this.state.entryCount === 0) {
1718
2076
  return null;
1719
2077
  }
1720
2078
  const leaf = this.state.rightmostLeaf;
1721
- return leafEntryAt(leaf, leafEntryCount(leaf) - 1);
2079
+ return freezeEntry(leafEntryAt(leaf, leafEntryCount(leaf) - 1));
1722
2080
  }
1723
2081
  popLast() {
1724
- return popLastEntry(this.state);
2082
+ const entry = popLastEntry(this.state);
2083
+ if (entry === null) return null;
2084
+ return freezeEntry(entry);
1725
2085
  }
1726
2086
  clear() {
1727
2087
  const emptyLeaf = createLeafNode([], null);
@@ -1731,16 +2091,9 @@ var InMemoryBTree = class _InMemoryBTree {
1731
2091
  this.state.entryCount = 0;
1732
2092
  this.state._cursor.leaf = emptyLeaf;
1733
2093
  this.state._cursor.index = 0;
1734
- if (this.state.entryKeys !== null) {
1735
- this.state.entryKeys.clear();
1736
- }
2094
+ this.state.entryKeys?.clear();
1737
2095
  if (this.state.autoScale) {
1738
- const tier = computeAutoScaleTier(0);
1739
- this.state.maxLeafEntries = tier.maxLeaf;
1740
- this.state.maxBranchChildren = tier.maxBranch;
1741
- this.state.minLeafEntries = Math.ceil(tier.maxLeaf / 2);
1742
- this.state.minBranchChildren = Math.ceil(tier.maxBranch / 2);
1743
- this.state._nextAutoScaleThreshold = computeNextAutoScaleThreshold(0);
2096
+ resetAutoScaleToTier0(this.state);
1744
2097
  }
1745
2098
  }
1746
2099
  get(key) {
@@ -1754,12 +2107,12 @@ var InMemoryBTree = class _InMemoryBTree {
1754
2107
  findFirst(key) {
1755
2108
  const found = findFirstMatchingUserKey(this.state, key);
1756
2109
  if (found === null) return null;
1757
- return leafEntryAt(found.leaf, found.index);
2110
+ return freezeEntry(leafEntryAt(found.leaf, found.index));
1758
2111
  }
1759
2112
  findLast(key) {
1760
2113
  const found = findLastMatchingUserKey(this.state, key);
1761
2114
  if (found === null) return null;
1762
- return leafEntryAt(found.leaf, found.index);
2115
+ return freezeEntry(leafEntryAt(found.leaf, found.index));
1763
2116
  }
1764
2117
  nextHigherKey(key) {
1765
2118
  return findNextHigherKey(this.state, key);
@@ -1770,7 +2123,7 @@ var InMemoryBTree = class _InMemoryBTree {
1770
2123
  getPairOrNextLower(key) {
1771
2124
  const found = findPairOrNextLower(this.state, key);
1772
2125
  if (found === null) return null;
1773
- return leafEntryAt(found.leaf, found.index);
2126
+ return freezeEntry(leafEntryAt(found.leaf, found.index));
1774
2127
  }
1775
2128
  count(startKey, endKey, options) {
1776
2129
  return countRangeEntries(this.state, startKey, endKey, options);
@@ -1779,14 +2132,14 @@ var InMemoryBTree = class _InMemoryBTree {
1779
2132
  return deleteRangeEntries(this.state, startKey, endKey, options);
1780
2133
  }
1781
2134
  range(startKey, endKey, options) {
1782
- return rangeQueryEntries(this.state, startKey, endKey, options);
2135
+ return rangeQueryPublicEntries(this.state, startKey, endKey, options);
1783
2136
  }
1784
2137
  *entries() {
1785
2138
  let leaf = this.state.leftmostLeaf;
1786
2139
  while (leaf !== null) {
1787
2140
  const count = leafEntryCount(leaf);
1788
2141
  for (let i = 0; i < count; i += 1) {
1789
- yield leafEntryAt(leaf, i);
2142
+ yield freezeEntry(leafEntryAt(leaf, i));
1790
2143
  }
1791
2144
  leaf = leaf.next;
1792
2145
  }
@@ -1796,7 +2149,7 @@ var InMemoryBTree = class _InMemoryBTree {
1796
2149
  while (leaf !== null) {
1797
2150
  const count = leafEntryCount(leaf);
1798
2151
  for (let i = count - 1; i >= 0; i -= 1) {
1799
- yield leafEntryAt(leaf, i);
2152
+ yield freezeEntry(leafEntryAt(leaf, i));
1800
2153
  }
1801
2154
  leaf = leaf.prev;
1802
2155
  }
@@ -1814,48 +2167,26 @@ var InMemoryBTree = class _InMemoryBTree {
1814
2167
  [Symbol.iterator]() {
1815
2168
  return this.entries();
1816
2169
  }
2170
+ forEachRange(startKey, endKey, callback, options) {
2171
+ forEachRangeEntries(this.state, startKey, endKey, callback, options);
2172
+ }
1817
2173
  forEach(callback, thisArg) {
1818
- let leaf = this.state.leftmostLeaf;
1819
- while (leaf !== null) {
1820
- const count = leafEntryCount(leaf);
1821
- for (let i = 0; i < count; i += 1) {
1822
- callback.call(thisArg, leafEntryAt(leaf, i));
1823
- }
1824
- leaf = leaf.next;
1825
- }
2174
+ forEachEntry(this.state, callback, thisArg);
1826
2175
  }
1827
2176
  snapshot() {
1828
- const result = new Array(this.state.entryCount);
1829
- let leaf = this.state.leftmostLeaf;
1830
- let writeIdx = 0;
1831
- while (leaf !== null) {
1832
- const count = leafEntryCount(leaf);
1833
- for (let i = 0; i < count; i += 1) {
1834
- result[writeIdx++] = leafEntryAt(leaf, i);
1835
- }
1836
- leaf = leaf.next;
1837
- }
1838
- return result;
2177
+ return snapshotEntries(this.state);
1839
2178
  }
1840
2179
  clone() {
1841
- const cloned = new _InMemoryBTree(buildConfigFromState(this.state));
2180
+ const cloned = new _InMemoryBTree(
2181
+ buildConfigFromState(this.state)
2182
+ );
1842
2183
  applyAutoScaleCapacitySnapshot(
1843
2184
  cloned.state,
1844
2185
  this.state.maxLeafEntries,
1845
2186
  this.state.maxBranchChildren
1846
2187
  );
1847
2188
  if (this.state.entryCount > 0) {
1848
- const pairs = new Array(this.state.entryCount);
1849
- let leaf = this.state.leftmostLeaf;
1850
- let writeIdx = 0;
1851
- while (leaf !== null) {
1852
- const count = leafEntryCount(leaf);
1853
- for (let i = 0; i < count; i += 1) {
1854
- pairs[writeIdx++] = leafEntryAt(leaf, i);
1855
- }
1856
- leaf = leaf.next;
1857
- }
1858
- cloned.putMany(pairs);
2189
+ cloned.putMany(collectInternalEntries(this.state));
1859
2190
  }
1860
2191
  return cloned;
1861
2192
  }
@@ -1864,26 +2195,19 @@ var InMemoryBTree = class _InMemoryBTree {
1864
2195
  }
1865
2196
  static fromJSON(json, compareKeys) {
1866
2197
  validateBTreeJSON(json);
1867
- const strict = json.config.duplicateKeys !== "allow";
1868
- for (let i = 1; i < json.entries.length; i += 1) {
1869
- const cmp = compareKeys(json.entries[i - 1][0], json.entries[i][0]);
1870
- if (cmp > 0) {
1871
- throw new BTreeValidationError("fromJSON: entries not sorted.");
1872
- }
1873
- if (strict && cmp === 0) {
1874
- throw new BTreeValidationError(
1875
- 'fromJSON: duplicate keys require duplicateKeys "allow".'
1876
- );
1877
- }
1878
- }
1879
- const tree = new _InMemoryBTree(buildConfigFromJSON(json, compareKeys));
2198
+ validateBTreeJSONSortOrder(json, compareKeys);
2199
+ const tree = new _InMemoryBTree(
2200
+ buildConfigFromJSON(json, compareKeys)
2201
+ );
1880
2202
  applyAutoScaleCapacitySnapshot(
1881
2203
  tree.state,
1882
2204
  json.config.maxLeafEntries,
1883
2205
  json.config.maxBranchChildren
1884
2206
  );
1885
2207
  if (json.entries.length > 0) {
1886
- const pairs = new Array(json.entries.length);
2208
+ const pairs = new Array(
2209
+ json.entries.length
2210
+ );
1887
2211
  for (let i = 0; i < json.entries.length; i += 1) {
1888
2212
  pairs[i] = { key: json.entries[i][0], value: json.entries[i][1] };
1889
2213
  }