serializable-bptree 8.3.5 → 8.4.1

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/README.md CHANGED
@@ -84,6 +84,7 @@ Additionally, this library supports asynchronous operations and rule-based query
84
84
  - **Async/Sync Support**: Provides both synchronous and asynchronous APIs.
85
85
  - **Query Optimization**: Rule-based optimizer to choose the best index for complex queries.
86
86
  - **TypeScript**: Fully typed for a better developer experience.
87
+ - **Auto Rollback**: Automatically cleans up internal memory buffers on commit failures, preventing memory leaks without manual intervention.
87
88
 
88
89
  ## How to use
89
90
 
@@ -413,11 +413,7 @@ var MVCCTransaction = class {
413
413
  */
414
414
  rollback() {
415
415
  const { created, updated, deleted } = this.getResultEntries();
416
- this.writeBuffer.clear();
417
- this.deleteBuffer.clear();
418
- this.createdKeys.clear();
419
- this.deletedValues.clear();
420
- this.originallyExisted.clear();
416
+ this._cleanupAll();
421
417
  this.committed = true;
422
418
  if (this.root !== this) {
423
419
  this.root.activeTransactions.delete(this);
@@ -453,6 +449,19 @@ var MVCCTransaction = class {
453
449
  }
454
450
  return Array.from(conflicts);
455
451
  }
452
+ /**
453
+ * Cleans up all buffers and history.
454
+ * This method is called by the commit method.
455
+ */
456
+ _cleanupAll() {
457
+ this.writeBuffer.clear();
458
+ this.deleteBuffer.clear();
459
+ this.createdKeys.clear();
460
+ this.deletedValues.clear();
461
+ this.originallyExisted.clear();
462
+ this.keyVersions.clear();
463
+ this.bufferHistory.clear();
464
+ }
456
465
  /**
457
466
  * Cleans up both deletedCache and versionIndex based on minActiveVersion.
458
467
  * Root transactions call this after commit to reclaim memory.
@@ -644,6 +653,7 @@ var SyncMVCCTransaction = class _SyncMVCCTransaction extends MVCCTransaction {
644
653
  if (this.parent) {
645
654
  const failure = this.parent._merge(this);
646
655
  if (failure) {
656
+ this.rollback();
647
657
  return {
648
658
  label,
649
659
  success: false,
@@ -654,11 +664,13 @@ var SyncMVCCTransaction = class _SyncMVCCTransaction extends MVCCTransaction {
654
664
  deleted
655
665
  };
656
666
  }
667
+ this._cleanupAll();
657
668
  this.committed = true;
658
669
  } else {
659
670
  if (this.writeBuffer.size > 0 || this.deleteBuffer.size > 0) {
660
671
  const failure = this._merge(this);
661
672
  if (failure) {
673
+ this.rollback();
662
674
  return {
663
675
  label,
664
676
  success: false,
@@ -669,13 +681,7 @@ var SyncMVCCTransaction = class _SyncMVCCTransaction extends MVCCTransaction {
669
681
  deleted: []
670
682
  };
671
683
  }
672
- this.writeBuffer.clear();
673
- this.deleteBuffer.clear();
674
- this.createdKeys.clear();
675
- this.deletedValues.clear();
676
- this.originallyExisted.clear();
677
- this.keyVersions.clear();
678
- this.bufferHistory.clear();
684
+ this._cleanupAll();
679
685
  this.localVersion = 0;
680
686
  this.snapshotVersion = this.version;
681
687
  }
@@ -1309,6 +1315,7 @@ var AsyncMVCCTransaction = class _AsyncMVCCTransaction extends MVCCTransaction {
1309
1315
  if (this.parent) {
1310
1316
  const failure = await this.parent._merge(this);
1311
1317
  if (failure) {
1318
+ this.rollback();
1312
1319
  return {
1313
1320
  label,
1314
1321
  success: false,
@@ -1319,11 +1326,13 @@ var AsyncMVCCTransaction = class _AsyncMVCCTransaction extends MVCCTransaction {
1319
1326
  deleted
1320
1327
  };
1321
1328
  }
1329
+ this._cleanupAll();
1322
1330
  this.committed = true;
1323
1331
  } else {
1324
1332
  if (this.writeBuffer.size > 0 || this.deleteBuffer.size > 0) {
1325
1333
  const failure = await this._merge(this);
1326
1334
  if (failure) {
1335
+ this.rollback();
1327
1336
  return {
1328
1337
  label,
1329
1338
  success: false,
@@ -1334,13 +1343,7 @@ var AsyncMVCCTransaction = class _AsyncMVCCTransaction extends MVCCTransaction {
1334
1343
  deleted: []
1335
1344
  };
1336
1345
  }
1337
- this.writeBuffer.clear();
1338
- this.deleteBuffer.clear();
1339
- this.createdKeys.clear();
1340
- this.deletedValues.clear();
1341
- this.originallyExisted.clear();
1342
- this.keyVersions.clear();
1343
- this.bufferHistory.clear();
1346
+ this._cleanupAll();
1344
1347
  this.localVersion = 0;
1345
1348
  this.snapshotVersion = this.version;
1346
1349
  }
@@ -1924,30 +1927,22 @@ var BPTreeTransaction = class _BPTreeTransaction {
1924
1927
  */
1925
1928
  _insertValueIntoLeaf(leaf, key, value) {
1926
1929
  if (leaf.values.length) {
1927
- for (let i = 0, len = leaf.values.length; i < len; i++) {
1928
- const nValue = leaf.values[i];
1929
- if (this.comparator.isSame(value, nValue)) {
1930
- if (leaf.keys[i].includes(key)) {
1931
- return false;
1932
- }
1933
- leaf.keys[i].push(key);
1934
- return true;
1935
- } else if (this.comparator.isLower(value, nValue)) {
1936
- leaf.values.splice(i, 0, value);
1937
- leaf.keys.splice(i, 0, [key]);
1938
- return true;
1939
- } else if (i + 1 === leaf.values.length) {
1940
- leaf.values.push(value);
1941
- leaf.keys.push([key]);
1942
- return true;
1930
+ const { index, found } = this._binarySearchValues(leaf.values, value);
1931
+ if (found) {
1932
+ if (leaf.keys[index].includes(key)) {
1933
+ return false;
1943
1934
  }
1935
+ leaf.keys[index].push(key);
1936
+ return true;
1944
1937
  }
1938
+ leaf.values.splice(index, 0, value);
1939
+ leaf.keys.splice(index, 0, [key]);
1940
+ return true;
1945
1941
  } else {
1946
1942
  leaf.values = [value];
1947
1943
  leaf.keys = [[key]];
1948
1944
  return true;
1949
1945
  }
1950
- return false;
1951
1946
  }
1952
1947
  _cloneNode(node) {
1953
1948
  return JSON.parse(JSON.stringify(node));
@@ -2240,12 +2235,8 @@ var BPTreeSyncTransaction = class extends BPTreeTransaction {
2240
2235
  const midValue = parentNode.values[mid];
2241
2236
  parentNode.values = parentNode.values.slice(0, mid);
2242
2237
  parentNode.keys = parentNode.keys.slice(0, mid + 1);
2243
- for (const k of parentNode.keys) {
2244
- const n = this._cloneNode(this.getNode(k));
2245
- n.parent = parentNode.id;
2246
- this._updateNode(n);
2247
- }
2248
- for (const k of newSiblingNodeRecursive.keys) {
2238
+ for (let i = 0, len = newSiblingNodeRecursive.keys.length; i < len; i++) {
2239
+ const k = newSiblingNodeRecursive.keys[i];
2249
2240
  const n = this._cloneNode(this.getNode(k));
2250
2241
  n.parent = newSiblingNodeRecursive.id;
2251
2242
  this._updateNode(n);
@@ -2327,7 +2318,7 @@ var BPTreeSyncTransaction = class extends BPTreeTransaction {
2327
2318
  for (let i = 0; i < len; i++) {
2328
2319
  const nValue = node.values[i];
2329
2320
  const keys = node.keys[i];
2330
- for (let j = 0, kLen = keys.length; j < kLen; j++) {
2321
+ for (let j = 0, len2 = keys.length; j < len2; j++) {
2331
2322
  yield [keys[j], nValue];
2332
2323
  }
2333
2324
  }
@@ -2406,7 +2397,7 @@ var BPTreeSyncTransaction = class extends BPTreeTransaction {
2406
2397
  while (true) {
2407
2398
  for (let i = 0, len = node.values.length; i < len; i++) {
2408
2399
  const keys = node.keys[i];
2409
- for (let j = 0, kLen = keys.length; j < kLen; j++) {
2400
+ for (let j = 0, len2 = keys.length; j < len2; j++) {
2410
2401
  if (keys[j] === key) {
2411
2402
  return node.values[i];
2412
2403
  }
@@ -2515,8 +2506,16 @@ var BPTreeSyncTransaction = class extends BPTreeTransaction {
2515
2506
  const sorted = [...entries].sort((a, b) => this.comparator.asc(a[1], b[1]));
2516
2507
  let currentLeaf = null;
2517
2508
  let modified = false;
2518
- for (const [key, value] of sorted) {
2519
- const targetLeaf = this.locateLeaf(value);
2509
+ let cachedLeafId = null;
2510
+ let cachedLeafMaxValue = null;
2511
+ for (let i = 0, len = sorted.length; i < len; i++) {
2512
+ const [key, value] = sorted[i];
2513
+ let targetLeaf;
2514
+ if (cachedLeafId !== null && cachedLeafMaxValue !== null && currentLeaf !== null && (this.comparator.isLower(value, cachedLeafMaxValue) || this.comparator.isSame(value, cachedLeafMaxValue))) {
2515
+ targetLeaf = currentLeaf;
2516
+ } else {
2517
+ targetLeaf = this.locateLeaf(value);
2518
+ }
2520
2519
  if (currentLeaf !== null && currentLeaf.id === targetLeaf.id) {
2521
2520
  } else {
2522
2521
  if (currentLeaf !== null && modified) {
@@ -2525,8 +2524,10 @@ var BPTreeSyncTransaction = class extends BPTreeTransaction {
2525
2524
  currentLeaf = this._cloneNode(targetLeaf);
2526
2525
  modified = false;
2527
2526
  }
2527
+ cachedLeafId = currentLeaf.id;
2528
2528
  const changed = this._insertValueIntoLeaf(currentLeaf, key, value);
2529
2529
  modified = modified || changed;
2530
+ cachedLeafMaxValue = currentLeaf.values[currentLeaf.values.length - 1];
2530
2531
  if (currentLeaf.values.length === this.order) {
2531
2532
  this._updateNode(currentLeaf);
2532
2533
  let after = this._createNode(
@@ -2547,6 +2548,8 @@ var BPTreeSyncTransaction = class extends BPTreeTransaction {
2547
2548
  this._updateNode(after);
2548
2549
  this._insertInParent(currentLeaf, after.values[0], after);
2549
2550
  currentLeaf = null;
2551
+ cachedLeafId = null;
2552
+ cachedLeafMaxValue = null;
2550
2553
  modified = false;
2551
2554
  }
2552
2555
  }
@@ -2554,6 +2557,85 @@ var BPTreeSyncTransaction = class extends BPTreeTransaction {
2554
2557
  this._updateNode(currentLeaf);
2555
2558
  }
2556
2559
  }
2560
+ bulkLoad(entries) {
2561
+ if (entries.length === 0) return;
2562
+ const root = this.getNode(this.rootId);
2563
+ if (!root.leaf || root.values.length > 0) {
2564
+ throw new Error("bulkLoad can only be called on an empty tree. Use batchInsert for non-empty trees.");
2565
+ }
2566
+ const sorted = [...entries].sort((a, b) => this.comparator.asc(a[1], b[1]));
2567
+ const grouped = [];
2568
+ for (let i = 0, len = sorted.length; i < len; i++) {
2569
+ const [key, value] = sorted[i];
2570
+ const last = grouped[grouped.length - 1];
2571
+ if (last && this.comparator.isSame(last.value, value)) {
2572
+ if (!last.keys.includes(key)) {
2573
+ last.keys.push(key);
2574
+ }
2575
+ } else {
2576
+ grouped.push({ keys: [key], value });
2577
+ }
2578
+ }
2579
+ this._deleteNode(root);
2580
+ const maxLeafSize = this.order - 1;
2581
+ const leaves = [];
2582
+ for (let i = 0, len = grouped.length; i < len; i += maxLeafSize) {
2583
+ const chunk = grouped.slice(i, i + maxLeafSize);
2584
+ const leafKeys = chunk.map((g) => g.keys);
2585
+ const leafValues = chunk.map((g) => g.value);
2586
+ const leaf = this._createNode(
2587
+ true,
2588
+ leafKeys,
2589
+ leafValues,
2590
+ null,
2591
+ null,
2592
+ null
2593
+ );
2594
+ leaves.push(leaf);
2595
+ }
2596
+ for (let i = 0, len = leaves.length; i < len; i++) {
2597
+ if (i > 0) {
2598
+ leaves[i].prev = leaves[i - 1].id;
2599
+ }
2600
+ if (i < len - 1) {
2601
+ leaves[i].next = leaves[i + 1].id;
2602
+ }
2603
+ this._updateNode(leaves[i]);
2604
+ }
2605
+ let currentLevel = leaves;
2606
+ while (currentLevel.length > 1) {
2607
+ const nextLevel = [];
2608
+ for (let i = 0, len = currentLevel.length; i < len; i += this.order) {
2609
+ const children = currentLevel.slice(i, i + this.order);
2610
+ const childIds = children.map((c) => c.id);
2611
+ const separators = [];
2612
+ for (let j = 1, len2 = children.length; j < len2; j++) {
2613
+ separators.push(children[j].values[0]);
2614
+ }
2615
+ const internalNode = this._createNode(
2616
+ false,
2617
+ childIds,
2618
+ separators,
2619
+ null,
2620
+ null,
2621
+ null
2622
+ );
2623
+ for (let j = 0, len2 = children.length; j < len2; j++) {
2624
+ const child = children[j];
2625
+ child.parent = internalNode.id;
2626
+ this._updateNode(child);
2627
+ }
2628
+ nextLevel.push(internalNode);
2629
+ }
2630
+ currentLevel = nextLevel;
2631
+ }
2632
+ const newRoot = currentLevel[0];
2633
+ this._writeHead({
2634
+ root: newRoot.id,
2635
+ order: this.order,
2636
+ data: this.strategy.head.data
2637
+ });
2638
+ }
2557
2639
  _deleteEntry(node, key) {
2558
2640
  if (!node.leaf) {
2559
2641
  let keyIndex = -1;
@@ -2657,7 +2739,8 @@ var BPTreeSyncTransaction = class extends BPTreeTransaction {
2657
2739
  siblingNode.values.push(...node.values);
2658
2740
  if (!siblingNode.leaf) {
2659
2741
  const keys = siblingNode.keys;
2660
- for (const key2 of keys) {
2742
+ for (let i = 0, len = keys.length; i < len; i++) {
2743
+ const key2 = keys[i];
2661
2744
  const node2 = this._cloneNode(this.getNode(key2));
2662
2745
  node2.parent = siblingNode.id;
2663
2746
  this._updateNode(node2);
@@ -2725,21 +2808,24 @@ var BPTreeSyncTransaction = class extends BPTreeTransaction {
2725
2808
  this._updateNode(siblingNode);
2726
2809
  }
2727
2810
  if (!siblingNode.leaf) {
2728
- for (const key2 of siblingNode.keys) {
2811
+ for (let i = 0, len = siblingNode.keys.length; i < len; i++) {
2812
+ const key2 = siblingNode.keys[i];
2729
2813
  const n = this._cloneNode(this.getNode(key2));
2730
2814
  n.parent = siblingNode.id;
2731
2815
  this._updateNode(n);
2732
2816
  }
2733
2817
  }
2734
2818
  if (!node.leaf) {
2735
- for (const key2 of node.keys) {
2819
+ for (let i = 0, len = node.keys.length; i < len; i++) {
2820
+ const key2 = node.keys[i];
2736
2821
  const n = this._cloneNode(this.getNode(key2));
2737
2822
  n.parent = node.id;
2738
2823
  this._updateNode(n);
2739
2824
  }
2740
2825
  }
2741
2826
  if (!parentNode.leaf) {
2742
- for (const key2 of parentNode.keys) {
2827
+ for (let i = 0, len = parentNode.keys.length; i < len; i++) {
2828
+ const key2 = parentNode.keys[i];
2743
2829
  const n = this._cloneNode(this.getNode(key2));
2744
2830
  n.parent = parentNode.id;
2745
2831
  this._updateNode(n);
@@ -2816,8 +2902,12 @@ var BPTreeSyncTransaction = class extends BPTreeTransaction {
2816
2902
  result = this.rootTx.commit(label);
2817
2903
  if (result.success) {
2818
2904
  this.rootTx.rootId = this.rootId;
2905
+ } else {
2906
+ this.mvcc.rollback();
2819
2907
  }
2820
2908
  }
2909
+ } else {
2910
+ this.mvcc.rollback();
2821
2911
  }
2822
2912
  return result;
2823
2913
  }
@@ -2917,6 +3007,14 @@ var BPTreeSync = class extends BPTreeSyncTransaction {
2917
3007
  throw new Error(`Transaction failed: ${result.error || "Commit failed due to conflict"}`);
2918
3008
  }
2919
3009
  }
3010
+ bulkLoad(entries) {
3011
+ const tx = this.createTransaction();
3012
+ tx.bulkLoad(entries);
3013
+ const result = tx.commit();
3014
+ if (!result.success) {
3015
+ throw new Error(`Transaction failed: ${result.error || "Commit failed due to conflict"}`);
3016
+ }
3017
+ }
2920
3018
  };
2921
3019
 
2922
3020
  // node_modules/ryoiki/dist/esm/index.mjs
@@ -3334,12 +3432,8 @@ var BPTreeAsyncTransaction = class extends BPTreeTransaction {
3334
3432
  const midValue = parentNode.values[mid];
3335
3433
  parentNode.values = parentNode.values.slice(0, mid);
3336
3434
  parentNode.keys = parentNode.keys.slice(0, mid + 1);
3337
- for (const k of parentNode.keys) {
3338
- const n = this._cloneNode(await this.getNode(k));
3339
- n.parent = parentNode.id;
3340
- await this._updateNode(n);
3341
- }
3342
- for (const k of newSiblingNodeRecursive.keys) {
3435
+ for (let i = 0, len = newSiblingNodeRecursive.keys.length; i < len; i++) {
3436
+ const k = newSiblingNodeRecursive.keys[i];
3343
3437
  const n = this._cloneNode(await this.getNode(k));
3344
3438
  n.parent = newSiblingNodeRecursive.id;
3345
3439
  await this._updateNode(n);
@@ -3431,7 +3525,7 @@ var BPTreeAsyncTransaction = class extends BPTreeTransaction {
3431
3525
  for (let i = 0; i < len; i++) {
3432
3526
  const nValue = node.values[i];
3433
3527
  const keys = node.keys[i];
3434
- for (let j = 0, kLen = keys.length; j < kLen; j++) {
3528
+ for (let j = 0, len2 = keys.length; j < len2; j++) {
3435
3529
  yield [keys[j], nValue];
3436
3530
  }
3437
3531
  }
@@ -3509,7 +3603,7 @@ var BPTreeAsyncTransaction = class extends BPTreeTransaction {
3509
3603
  while (true) {
3510
3604
  for (let i = 0, len = node.values.length; i < len; i++) {
3511
3605
  const keys = node.keys[i];
3512
- for (let j = 0, kLen = keys.length; j < kLen; j++) {
3606
+ for (let j = 0, len2 = keys.length; j < len2; j++) {
3513
3607
  if (keys[j] === key) {
3514
3608
  return node.values[i];
3515
3609
  }
@@ -3621,8 +3715,16 @@ var BPTreeAsyncTransaction = class extends BPTreeTransaction {
3621
3715
  const sorted = [...entries].sort((a, b) => this.comparator.asc(a[1], b[1]));
3622
3716
  let currentLeaf = null;
3623
3717
  let modified = false;
3624
- for (const [key, value] of sorted) {
3625
- const targetLeaf = await this.locateLeaf(value);
3718
+ let cachedLeafId = null;
3719
+ let cachedLeafMaxValue = null;
3720
+ for (let i = 0, len = sorted.length; i < len; i++) {
3721
+ const [key, value] = sorted[i];
3722
+ let targetLeaf;
3723
+ if (cachedLeafId !== null && cachedLeafMaxValue !== null && currentLeaf !== null && (this.comparator.isLower(value, cachedLeafMaxValue) || this.comparator.isSame(value, cachedLeafMaxValue))) {
3724
+ targetLeaf = currentLeaf;
3725
+ } else {
3726
+ targetLeaf = await this.locateLeaf(value);
3727
+ }
3626
3728
  if (currentLeaf !== null && currentLeaf.id === targetLeaf.id) {
3627
3729
  } else {
3628
3730
  if (currentLeaf !== null && modified) {
@@ -3631,8 +3733,10 @@ var BPTreeAsyncTransaction = class extends BPTreeTransaction {
3631
3733
  currentLeaf = this._cloneNode(targetLeaf);
3632
3734
  modified = false;
3633
3735
  }
3736
+ cachedLeafId = currentLeaf.id;
3634
3737
  const changed = this._insertValueIntoLeaf(currentLeaf, key, value);
3635
3738
  modified = modified || changed;
3739
+ cachedLeafMaxValue = currentLeaf.values[currentLeaf.values.length - 1];
3636
3740
  if (currentLeaf.values.length === this.order) {
3637
3741
  await this._updateNode(currentLeaf);
3638
3742
  let after = await this._createNode(
@@ -3653,6 +3757,8 @@ var BPTreeAsyncTransaction = class extends BPTreeTransaction {
3653
3757
  await this._updateNode(after);
3654
3758
  await this._insertInParent(currentLeaf, after.values[0], after);
3655
3759
  currentLeaf = null;
3760
+ cachedLeafId = null;
3761
+ cachedLeafMaxValue = null;
3656
3762
  modified = false;
3657
3763
  }
3658
3764
  }
@@ -3661,6 +3767,87 @@ var BPTreeAsyncTransaction = class extends BPTreeTransaction {
3661
3767
  }
3662
3768
  });
3663
3769
  }
3770
+ async bulkLoad(entries) {
3771
+ if (entries.length === 0) return;
3772
+ return this.writeLock(0, async () => {
3773
+ const root = await this.getNode(this.rootId);
3774
+ if (!root.leaf || root.values.length > 0) {
3775
+ throw new Error("bulkLoad can only be called on an empty tree. Use batchInsert for non-empty trees.");
3776
+ }
3777
+ const sorted = [...entries].sort((a, b) => this.comparator.asc(a[1], b[1]));
3778
+ const grouped = [];
3779
+ for (let i = 0, len = sorted.length; i < len; i++) {
3780
+ const [key, value] = sorted[i];
3781
+ const last = grouped[grouped.length - 1];
3782
+ if (last && this.comparator.isSame(last.value, value)) {
3783
+ if (!last.keys.includes(key)) {
3784
+ last.keys.push(key);
3785
+ }
3786
+ } else {
3787
+ grouped.push({ keys: [key], value });
3788
+ }
3789
+ }
3790
+ await this._deleteNode(root);
3791
+ const maxLeafSize = this.order - 1;
3792
+ const leaves = [];
3793
+ for (let i = 0, len = grouped.length; i < len; i += maxLeafSize) {
3794
+ const chunk = grouped.slice(i, i + maxLeafSize);
3795
+ const leafKeys = chunk.map((g) => g.keys);
3796
+ const leafValues = chunk.map((g) => g.value);
3797
+ const leaf = await this._createNode(
3798
+ true,
3799
+ leafKeys,
3800
+ leafValues,
3801
+ null,
3802
+ null,
3803
+ null
3804
+ );
3805
+ leaves.push(leaf);
3806
+ }
3807
+ for (let i = 0, len = leaves.length; i < len; i++) {
3808
+ if (i > 0) {
3809
+ leaves[i].prev = leaves[i - 1].id;
3810
+ }
3811
+ if (i < len - 1) {
3812
+ leaves[i].next = leaves[i + 1].id;
3813
+ }
3814
+ await this._updateNode(leaves[i]);
3815
+ }
3816
+ let currentLevel = leaves;
3817
+ while (currentLevel.length > 1) {
3818
+ const nextLevel = [];
3819
+ for (let i = 0, len = currentLevel.length; i < len; i += this.order) {
3820
+ const children = currentLevel.slice(i, i + this.order);
3821
+ const childIds = children.map((c) => c.id);
3822
+ const separators = [];
3823
+ for (let j = 1, len2 = children.length; j < len2; j++) {
3824
+ separators.push(children[j].values[0]);
3825
+ }
3826
+ const internalNode = await this._createNode(
3827
+ false,
3828
+ childIds,
3829
+ separators,
3830
+ null,
3831
+ null,
3832
+ null
3833
+ );
3834
+ for (let j = 0, len2 = children.length; j < len2; j++) {
3835
+ const child = children[j];
3836
+ child.parent = internalNode.id;
3837
+ await this._updateNode(child);
3838
+ }
3839
+ nextLevel.push(internalNode);
3840
+ }
3841
+ currentLevel = nextLevel;
3842
+ }
3843
+ const newRoot = currentLevel[0];
3844
+ await this._writeHead({
3845
+ root: newRoot.id,
3846
+ order: this.order,
3847
+ data: this.strategy.head.data
3848
+ });
3849
+ });
3850
+ }
3664
3851
  async _deleteEntry(node, key) {
3665
3852
  if (!node.leaf) {
3666
3853
  let keyIndex = -1;
@@ -3764,7 +3951,8 @@ var BPTreeAsyncTransaction = class extends BPTreeTransaction {
3764
3951
  siblingNode.values.push(...node.values);
3765
3952
  if (!siblingNode.leaf) {
3766
3953
  const keys = siblingNode.keys;
3767
- for (const key2 of keys) {
3954
+ for (let i = 0, len = keys.length; i < len; i++) {
3955
+ const key2 = keys[i];
3768
3956
  const node2 = this._cloneNode(await this.getNode(key2));
3769
3957
  node2.parent = siblingNode.id;
3770
3958
  await this._updateNode(node2);
@@ -3832,21 +4020,24 @@ var BPTreeAsyncTransaction = class extends BPTreeTransaction {
3832
4020
  await this._updateNode(siblingNode);
3833
4021
  }
3834
4022
  if (!siblingNode.leaf) {
3835
- for (const key2 of siblingNode.keys) {
4023
+ for (let i = 0, len = siblingNode.keys.length; i < len; i++) {
4024
+ const key2 = siblingNode.keys[i];
3836
4025
  const n = this._cloneNode(await this.getNode(key2));
3837
4026
  n.parent = siblingNode.id;
3838
4027
  await this._updateNode(n);
3839
4028
  }
3840
4029
  }
3841
4030
  if (!node.leaf) {
3842
- for (const key2 of node.keys) {
4031
+ for (let i = 0, len = node.keys.length; i < len; i++) {
4032
+ const key2 = node.keys[i];
3843
4033
  const n = this._cloneNode(await this.getNode(key2));
3844
4034
  n.parent = node.id;
3845
4035
  await this._updateNode(n);
3846
4036
  }
3847
4037
  }
3848
4038
  if (!parentNode.leaf) {
3849
- for (const key2 of parentNode.keys) {
4039
+ for (let i = 0, len = parentNode.keys.length; i < len; i++) {
4040
+ const key2 = parentNode.keys[i];
3850
4041
  const n = this._cloneNode(await this.getNode(key2));
3851
4042
  n.parent = parentNode.id;
3852
4043
  await this._updateNode(n);
@@ -3925,8 +4116,12 @@ var BPTreeAsyncTransaction = class extends BPTreeTransaction {
3925
4116
  result = await this.rootTx.commit(label);
3926
4117
  if (result.success) {
3927
4118
  this.rootTx.rootId = this.rootId;
4119
+ } else {
4120
+ this.mvcc.rollback();
3928
4121
  }
3929
4122
  }
4123
+ } else {
4124
+ this.mvcc.rollback();
3930
4125
  }
3931
4126
  return result;
3932
4127
  }
@@ -4032,6 +4227,16 @@ var BPTreeAsync = class extends BPTreeAsyncTransaction {
4032
4227
  }
4033
4228
  });
4034
4229
  }
4230
+ async bulkLoad(entries) {
4231
+ return this.writeLock(1, async () => {
4232
+ const tx = await this.createTransaction();
4233
+ await tx.bulkLoad(entries);
4234
+ const result = await tx.commit();
4235
+ if (!result.success) {
4236
+ throw new Error(`Transaction failed: ${result.error || "Commit failed due to conflict"}`);
4237
+ }
4238
+ });
4239
+ }
4035
4240
  };
4036
4241
 
4037
4242
  // src/base/SerializeStrategy.ts
@@ -377,11 +377,7 @@ var MVCCTransaction = class {
377
377
  */
378
378
  rollback() {
379
379
  const { created, updated, deleted } = this.getResultEntries();
380
- this.writeBuffer.clear();
381
- this.deleteBuffer.clear();
382
- this.createdKeys.clear();
383
- this.deletedValues.clear();
384
- this.originallyExisted.clear();
380
+ this._cleanupAll();
385
381
  this.committed = true;
386
382
  if (this.root !== this) {
387
383
  this.root.activeTransactions.delete(this);
@@ -417,6 +413,19 @@ var MVCCTransaction = class {
417
413
  }
418
414
  return Array.from(conflicts);
419
415
  }
416
+ /**
417
+ * Cleans up all buffers and history.
418
+ * This method is called by the commit method.
419
+ */
420
+ _cleanupAll() {
421
+ this.writeBuffer.clear();
422
+ this.deleteBuffer.clear();
423
+ this.createdKeys.clear();
424
+ this.deletedValues.clear();
425
+ this.originallyExisted.clear();
426
+ this.keyVersions.clear();
427
+ this.bufferHistory.clear();
428
+ }
420
429
  /**
421
430
  * Cleans up both deletedCache and versionIndex based on minActiveVersion.
422
431
  * Root transactions call this after commit to reclaim memory.
@@ -608,6 +617,7 @@ var SyncMVCCTransaction = class _SyncMVCCTransaction extends MVCCTransaction {
608
617
  if (this.parent) {
609
618
  const failure = this.parent._merge(this);
610
619
  if (failure) {
620
+ this.rollback();
611
621
  return {
612
622
  label,
613
623
  success: false,
@@ -618,11 +628,13 @@ var SyncMVCCTransaction = class _SyncMVCCTransaction extends MVCCTransaction {
618
628
  deleted
619
629
  };
620
630
  }
631
+ this._cleanupAll();
621
632
  this.committed = true;
622
633
  } else {
623
634
  if (this.writeBuffer.size > 0 || this.deleteBuffer.size > 0) {
624
635
  const failure = this._merge(this);
625
636
  if (failure) {
637
+ this.rollback();
626
638
  return {
627
639
  label,
628
640
  success: false,
@@ -633,13 +645,7 @@ var SyncMVCCTransaction = class _SyncMVCCTransaction extends MVCCTransaction {
633
645
  deleted: []
634
646
  };
635
647
  }
636
- this.writeBuffer.clear();
637
- this.deleteBuffer.clear();
638
- this.createdKeys.clear();
639
- this.deletedValues.clear();
640
- this.originallyExisted.clear();
641
- this.keyVersions.clear();
642
- this.bufferHistory.clear();
648
+ this._cleanupAll();
643
649
  this.localVersion = 0;
644
650
  this.snapshotVersion = this.version;
645
651
  }
@@ -1273,6 +1279,7 @@ var AsyncMVCCTransaction = class _AsyncMVCCTransaction extends MVCCTransaction {
1273
1279
  if (this.parent) {
1274
1280
  const failure = await this.parent._merge(this);
1275
1281
  if (failure) {
1282
+ this.rollback();
1276
1283
  return {
1277
1284
  label,
1278
1285
  success: false,
@@ -1283,11 +1290,13 @@ var AsyncMVCCTransaction = class _AsyncMVCCTransaction extends MVCCTransaction {
1283
1290
  deleted
1284
1291
  };
1285
1292
  }
1293
+ this._cleanupAll();
1286
1294
  this.committed = true;
1287
1295
  } else {
1288
1296
  if (this.writeBuffer.size > 0 || this.deleteBuffer.size > 0) {
1289
1297
  const failure = await this._merge(this);
1290
1298
  if (failure) {
1299
+ this.rollback();
1291
1300
  return {
1292
1301
  label,
1293
1302
  success: false,
@@ -1298,13 +1307,7 @@ var AsyncMVCCTransaction = class _AsyncMVCCTransaction extends MVCCTransaction {
1298
1307
  deleted: []
1299
1308
  };
1300
1309
  }
1301
- this.writeBuffer.clear();
1302
- this.deleteBuffer.clear();
1303
- this.createdKeys.clear();
1304
- this.deletedValues.clear();
1305
- this.originallyExisted.clear();
1306
- this.keyVersions.clear();
1307
- this.bufferHistory.clear();
1310
+ this._cleanupAll();
1308
1311
  this.localVersion = 0;
1309
1312
  this.snapshotVersion = this.version;
1310
1313
  }
@@ -1888,30 +1891,22 @@ var BPTreeTransaction = class _BPTreeTransaction {
1888
1891
  */
1889
1892
  _insertValueIntoLeaf(leaf, key, value) {
1890
1893
  if (leaf.values.length) {
1891
- for (let i = 0, len = leaf.values.length; i < len; i++) {
1892
- const nValue = leaf.values[i];
1893
- if (this.comparator.isSame(value, nValue)) {
1894
- if (leaf.keys[i].includes(key)) {
1895
- return false;
1896
- }
1897
- leaf.keys[i].push(key);
1898
- return true;
1899
- } else if (this.comparator.isLower(value, nValue)) {
1900
- leaf.values.splice(i, 0, value);
1901
- leaf.keys.splice(i, 0, [key]);
1902
- return true;
1903
- } else if (i + 1 === leaf.values.length) {
1904
- leaf.values.push(value);
1905
- leaf.keys.push([key]);
1906
- return true;
1894
+ const { index, found } = this._binarySearchValues(leaf.values, value);
1895
+ if (found) {
1896
+ if (leaf.keys[index].includes(key)) {
1897
+ return false;
1907
1898
  }
1899
+ leaf.keys[index].push(key);
1900
+ return true;
1908
1901
  }
1902
+ leaf.values.splice(index, 0, value);
1903
+ leaf.keys.splice(index, 0, [key]);
1904
+ return true;
1909
1905
  } else {
1910
1906
  leaf.values = [value];
1911
1907
  leaf.keys = [[key]];
1912
1908
  return true;
1913
1909
  }
1914
- return false;
1915
1910
  }
1916
1911
  _cloneNode(node) {
1917
1912
  return JSON.parse(JSON.stringify(node));
@@ -2204,12 +2199,8 @@ var BPTreeSyncTransaction = class extends BPTreeTransaction {
2204
2199
  const midValue = parentNode.values[mid];
2205
2200
  parentNode.values = parentNode.values.slice(0, mid);
2206
2201
  parentNode.keys = parentNode.keys.slice(0, mid + 1);
2207
- for (const k of parentNode.keys) {
2208
- const n = this._cloneNode(this.getNode(k));
2209
- n.parent = parentNode.id;
2210
- this._updateNode(n);
2211
- }
2212
- for (const k of newSiblingNodeRecursive.keys) {
2202
+ for (let i = 0, len = newSiblingNodeRecursive.keys.length; i < len; i++) {
2203
+ const k = newSiblingNodeRecursive.keys[i];
2213
2204
  const n = this._cloneNode(this.getNode(k));
2214
2205
  n.parent = newSiblingNodeRecursive.id;
2215
2206
  this._updateNode(n);
@@ -2291,7 +2282,7 @@ var BPTreeSyncTransaction = class extends BPTreeTransaction {
2291
2282
  for (let i = 0; i < len; i++) {
2292
2283
  const nValue = node.values[i];
2293
2284
  const keys = node.keys[i];
2294
- for (let j = 0, kLen = keys.length; j < kLen; j++) {
2285
+ for (let j = 0, len2 = keys.length; j < len2; j++) {
2295
2286
  yield [keys[j], nValue];
2296
2287
  }
2297
2288
  }
@@ -2370,7 +2361,7 @@ var BPTreeSyncTransaction = class extends BPTreeTransaction {
2370
2361
  while (true) {
2371
2362
  for (let i = 0, len = node.values.length; i < len; i++) {
2372
2363
  const keys = node.keys[i];
2373
- for (let j = 0, kLen = keys.length; j < kLen; j++) {
2364
+ for (let j = 0, len2 = keys.length; j < len2; j++) {
2374
2365
  if (keys[j] === key) {
2375
2366
  return node.values[i];
2376
2367
  }
@@ -2479,8 +2470,16 @@ var BPTreeSyncTransaction = class extends BPTreeTransaction {
2479
2470
  const sorted = [...entries].sort((a, b) => this.comparator.asc(a[1], b[1]));
2480
2471
  let currentLeaf = null;
2481
2472
  let modified = false;
2482
- for (const [key, value] of sorted) {
2483
- const targetLeaf = this.locateLeaf(value);
2473
+ let cachedLeafId = null;
2474
+ let cachedLeafMaxValue = null;
2475
+ for (let i = 0, len = sorted.length; i < len; i++) {
2476
+ const [key, value] = sorted[i];
2477
+ let targetLeaf;
2478
+ if (cachedLeafId !== null && cachedLeafMaxValue !== null && currentLeaf !== null && (this.comparator.isLower(value, cachedLeafMaxValue) || this.comparator.isSame(value, cachedLeafMaxValue))) {
2479
+ targetLeaf = currentLeaf;
2480
+ } else {
2481
+ targetLeaf = this.locateLeaf(value);
2482
+ }
2484
2483
  if (currentLeaf !== null && currentLeaf.id === targetLeaf.id) {
2485
2484
  } else {
2486
2485
  if (currentLeaf !== null && modified) {
@@ -2489,8 +2488,10 @@ var BPTreeSyncTransaction = class extends BPTreeTransaction {
2489
2488
  currentLeaf = this._cloneNode(targetLeaf);
2490
2489
  modified = false;
2491
2490
  }
2491
+ cachedLeafId = currentLeaf.id;
2492
2492
  const changed = this._insertValueIntoLeaf(currentLeaf, key, value);
2493
2493
  modified = modified || changed;
2494
+ cachedLeafMaxValue = currentLeaf.values[currentLeaf.values.length - 1];
2494
2495
  if (currentLeaf.values.length === this.order) {
2495
2496
  this._updateNode(currentLeaf);
2496
2497
  let after = this._createNode(
@@ -2511,6 +2512,8 @@ var BPTreeSyncTransaction = class extends BPTreeTransaction {
2511
2512
  this._updateNode(after);
2512
2513
  this._insertInParent(currentLeaf, after.values[0], after);
2513
2514
  currentLeaf = null;
2515
+ cachedLeafId = null;
2516
+ cachedLeafMaxValue = null;
2514
2517
  modified = false;
2515
2518
  }
2516
2519
  }
@@ -2518,6 +2521,85 @@ var BPTreeSyncTransaction = class extends BPTreeTransaction {
2518
2521
  this._updateNode(currentLeaf);
2519
2522
  }
2520
2523
  }
2524
+ bulkLoad(entries) {
2525
+ if (entries.length === 0) return;
2526
+ const root = this.getNode(this.rootId);
2527
+ if (!root.leaf || root.values.length > 0) {
2528
+ throw new Error("bulkLoad can only be called on an empty tree. Use batchInsert for non-empty trees.");
2529
+ }
2530
+ const sorted = [...entries].sort((a, b) => this.comparator.asc(a[1], b[1]));
2531
+ const grouped = [];
2532
+ for (let i = 0, len = sorted.length; i < len; i++) {
2533
+ const [key, value] = sorted[i];
2534
+ const last = grouped[grouped.length - 1];
2535
+ if (last && this.comparator.isSame(last.value, value)) {
2536
+ if (!last.keys.includes(key)) {
2537
+ last.keys.push(key);
2538
+ }
2539
+ } else {
2540
+ grouped.push({ keys: [key], value });
2541
+ }
2542
+ }
2543
+ this._deleteNode(root);
2544
+ const maxLeafSize = this.order - 1;
2545
+ const leaves = [];
2546
+ for (let i = 0, len = grouped.length; i < len; i += maxLeafSize) {
2547
+ const chunk = grouped.slice(i, i + maxLeafSize);
2548
+ const leafKeys = chunk.map((g) => g.keys);
2549
+ const leafValues = chunk.map((g) => g.value);
2550
+ const leaf = this._createNode(
2551
+ true,
2552
+ leafKeys,
2553
+ leafValues,
2554
+ null,
2555
+ null,
2556
+ null
2557
+ );
2558
+ leaves.push(leaf);
2559
+ }
2560
+ for (let i = 0, len = leaves.length; i < len; i++) {
2561
+ if (i > 0) {
2562
+ leaves[i].prev = leaves[i - 1].id;
2563
+ }
2564
+ if (i < len - 1) {
2565
+ leaves[i].next = leaves[i + 1].id;
2566
+ }
2567
+ this._updateNode(leaves[i]);
2568
+ }
2569
+ let currentLevel = leaves;
2570
+ while (currentLevel.length > 1) {
2571
+ const nextLevel = [];
2572
+ for (let i = 0, len = currentLevel.length; i < len; i += this.order) {
2573
+ const children = currentLevel.slice(i, i + this.order);
2574
+ const childIds = children.map((c) => c.id);
2575
+ const separators = [];
2576
+ for (let j = 1, len2 = children.length; j < len2; j++) {
2577
+ separators.push(children[j].values[0]);
2578
+ }
2579
+ const internalNode = this._createNode(
2580
+ false,
2581
+ childIds,
2582
+ separators,
2583
+ null,
2584
+ null,
2585
+ null
2586
+ );
2587
+ for (let j = 0, len2 = children.length; j < len2; j++) {
2588
+ const child = children[j];
2589
+ child.parent = internalNode.id;
2590
+ this._updateNode(child);
2591
+ }
2592
+ nextLevel.push(internalNode);
2593
+ }
2594
+ currentLevel = nextLevel;
2595
+ }
2596
+ const newRoot = currentLevel[0];
2597
+ this._writeHead({
2598
+ root: newRoot.id,
2599
+ order: this.order,
2600
+ data: this.strategy.head.data
2601
+ });
2602
+ }
2521
2603
  _deleteEntry(node, key) {
2522
2604
  if (!node.leaf) {
2523
2605
  let keyIndex = -1;
@@ -2621,7 +2703,8 @@ var BPTreeSyncTransaction = class extends BPTreeTransaction {
2621
2703
  siblingNode.values.push(...node.values);
2622
2704
  if (!siblingNode.leaf) {
2623
2705
  const keys = siblingNode.keys;
2624
- for (const key2 of keys) {
2706
+ for (let i = 0, len = keys.length; i < len; i++) {
2707
+ const key2 = keys[i];
2625
2708
  const node2 = this._cloneNode(this.getNode(key2));
2626
2709
  node2.parent = siblingNode.id;
2627
2710
  this._updateNode(node2);
@@ -2689,21 +2772,24 @@ var BPTreeSyncTransaction = class extends BPTreeTransaction {
2689
2772
  this._updateNode(siblingNode);
2690
2773
  }
2691
2774
  if (!siblingNode.leaf) {
2692
- for (const key2 of siblingNode.keys) {
2775
+ for (let i = 0, len = siblingNode.keys.length; i < len; i++) {
2776
+ const key2 = siblingNode.keys[i];
2693
2777
  const n = this._cloneNode(this.getNode(key2));
2694
2778
  n.parent = siblingNode.id;
2695
2779
  this._updateNode(n);
2696
2780
  }
2697
2781
  }
2698
2782
  if (!node.leaf) {
2699
- for (const key2 of node.keys) {
2783
+ for (let i = 0, len = node.keys.length; i < len; i++) {
2784
+ const key2 = node.keys[i];
2700
2785
  const n = this._cloneNode(this.getNode(key2));
2701
2786
  n.parent = node.id;
2702
2787
  this._updateNode(n);
2703
2788
  }
2704
2789
  }
2705
2790
  if (!parentNode.leaf) {
2706
- for (const key2 of parentNode.keys) {
2791
+ for (let i = 0, len = parentNode.keys.length; i < len; i++) {
2792
+ const key2 = parentNode.keys[i];
2707
2793
  const n = this._cloneNode(this.getNode(key2));
2708
2794
  n.parent = parentNode.id;
2709
2795
  this._updateNode(n);
@@ -2780,8 +2866,12 @@ var BPTreeSyncTransaction = class extends BPTreeTransaction {
2780
2866
  result = this.rootTx.commit(label);
2781
2867
  if (result.success) {
2782
2868
  this.rootTx.rootId = this.rootId;
2869
+ } else {
2870
+ this.mvcc.rollback();
2783
2871
  }
2784
2872
  }
2873
+ } else {
2874
+ this.mvcc.rollback();
2785
2875
  }
2786
2876
  return result;
2787
2877
  }
@@ -2881,6 +2971,14 @@ var BPTreeSync = class extends BPTreeSyncTransaction {
2881
2971
  throw new Error(`Transaction failed: ${result.error || "Commit failed due to conflict"}`);
2882
2972
  }
2883
2973
  }
2974
+ bulkLoad(entries) {
2975
+ const tx = this.createTransaction();
2976
+ tx.bulkLoad(entries);
2977
+ const result = tx.commit();
2978
+ if (!result.success) {
2979
+ throw new Error(`Transaction failed: ${result.error || "Commit failed due to conflict"}`);
2980
+ }
2981
+ }
2884
2982
  };
2885
2983
 
2886
2984
  // node_modules/ryoiki/dist/esm/index.mjs
@@ -3298,12 +3396,8 @@ var BPTreeAsyncTransaction = class extends BPTreeTransaction {
3298
3396
  const midValue = parentNode.values[mid];
3299
3397
  parentNode.values = parentNode.values.slice(0, mid);
3300
3398
  parentNode.keys = parentNode.keys.slice(0, mid + 1);
3301
- for (const k of parentNode.keys) {
3302
- const n = this._cloneNode(await this.getNode(k));
3303
- n.parent = parentNode.id;
3304
- await this._updateNode(n);
3305
- }
3306
- for (const k of newSiblingNodeRecursive.keys) {
3399
+ for (let i = 0, len = newSiblingNodeRecursive.keys.length; i < len; i++) {
3400
+ const k = newSiblingNodeRecursive.keys[i];
3307
3401
  const n = this._cloneNode(await this.getNode(k));
3308
3402
  n.parent = newSiblingNodeRecursive.id;
3309
3403
  await this._updateNode(n);
@@ -3395,7 +3489,7 @@ var BPTreeAsyncTransaction = class extends BPTreeTransaction {
3395
3489
  for (let i = 0; i < len; i++) {
3396
3490
  const nValue = node.values[i];
3397
3491
  const keys = node.keys[i];
3398
- for (let j = 0, kLen = keys.length; j < kLen; j++) {
3492
+ for (let j = 0, len2 = keys.length; j < len2; j++) {
3399
3493
  yield [keys[j], nValue];
3400
3494
  }
3401
3495
  }
@@ -3473,7 +3567,7 @@ var BPTreeAsyncTransaction = class extends BPTreeTransaction {
3473
3567
  while (true) {
3474
3568
  for (let i = 0, len = node.values.length; i < len; i++) {
3475
3569
  const keys = node.keys[i];
3476
- for (let j = 0, kLen = keys.length; j < kLen; j++) {
3570
+ for (let j = 0, len2 = keys.length; j < len2; j++) {
3477
3571
  if (keys[j] === key) {
3478
3572
  return node.values[i];
3479
3573
  }
@@ -3585,8 +3679,16 @@ var BPTreeAsyncTransaction = class extends BPTreeTransaction {
3585
3679
  const sorted = [...entries].sort((a, b) => this.comparator.asc(a[1], b[1]));
3586
3680
  let currentLeaf = null;
3587
3681
  let modified = false;
3588
- for (const [key, value] of sorted) {
3589
- const targetLeaf = await this.locateLeaf(value);
3682
+ let cachedLeafId = null;
3683
+ let cachedLeafMaxValue = null;
3684
+ for (let i = 0, len = sorted.length; i < len; i++) {
3685
+ const [key, value] = sorted[i];
3686
+ let targetLeaf;
3687
+ if (cachedLeafId !== null && cachedLeafMaxValue !== null && currentLeaf !== null && (this.comparator.isLower(value, cachedLeafMaxValue) || this.comparator.isSame(value, cachedLeafMaxValue))) {
3688
+ targetLeaf = currentLeaf;
3689
+ } else {
3690
+ targetLeaf = await this.locateLeaf(value);
3691
+ }
3590
3692
  if (currentLeaf !== null && currentLeaf.id === targetLeaf.id) {
3591
3693
  } else {
3592
3694
  if (currentLeaf !== null && modified) {
@@ -3595,8 +3697,10 @@ var BPTreeAsyncTransaction = class extends BPTreeTransaction {
3595
3697
  currentLeaf = this._cloneNode(targetLeaf);
3596
3698
  modified = false;
3597
3699
  }
3700
+ cachedLeafId = currentLeaf.id;
3598
3701
  const changed = this._insertValueIntoLeaf(currentLeaf, key, value);
3599
3702
  modified = modified || changed;
3703
+ cachedLeafMaxValue = currentLeaf.values[currentLeaf.values.length - 1];
3600
3704
  if (currentLeaf.values.length === this.order) {
3601
3705
  await this._updateNode(currentLeaf);
3602
3706
  let after = await this._createNode(
@@ -3617,6 +3721,8 @@ var BPTreeAsyncTransaction = class extends BPTreeTransaction {
3617
3721
  await this._updateNode(after);
3618
3722
  await this._insertInParent(currentLeaf, after.values[0], after);
3619
3723
  currentLeaf = null;
3724
+ cachedLeafId = null;
3725
+ cachedLeafMaxValue = null;
3620
3726
  modified = false;
3621
3727
  }
3622
3728
  }
@@ -3625,6 +3731,87 @@ var BPTreeAsyncTransaction = class extends BPTreeTransaction {
3625
3731
  }
3626
3732
  });
3627
3733
  }
3734
+ async bulkLoad(entries) {
3735
+ if (entries.length === 0) return;
3736
+ return this.writeLock(0, async () => {
3737
+ const root = await this.getNode(this.rootId);
3738
+ if (!root.leaf || root.values.length > 0) {
3739
+ throw new Error("bulkLoad can only be called on an empty tree. Use batchInsert for non-empty trees.");
3740
+ }
3741
+ const sorted = [...entries].sort((a, b) => this.comparator.asc(a[1], b[1]));
3742
+ const grouped = [];
3743
+ for (let i = 0, len = sorted.length; i < len; i++) {
3744
+ const [key, value] = sorted[i];
3745
+ const last = grouped[grouped.length - 1];
3746
+ if (last && this.comparator.isSame(last.value, value)) {
3747
+ if (!last.keys.includes(key)) {
3748
+ last.keys.push(key);
3749
+ }
3750
+ } else {
3751
+ grouped.push({ keys: [key], value });
3752
+ }
3753
+ }
3754
+ await this._deleteNode(root);
3755
+ const maxLeafSize = this.order - 1;
3756
+ const leaves = [];
3757
+ for (let i = 0, len = grouped.length; i < len; i += maxLeafSize) {
3758
+ const chunk = grouped.slice(i, i + maxLeafSize);
3759
+ const leafKeys = chunk.map((g) => g.keys);
3760
+ const leafValues = chunk.map((g) => g.value);
3761
+ const leaf = await this._createNode(
3762
+ true,
3763
+ leafKeys,
3764
+ leafValues,
3765
+ null,
3766
+ null,
3767
+ null
3768
+ );
3769
+ leaves.push(leaf);
3770
+ }
3771
+ for (let i = 0, len = leaves.length; i < len; i++) {
3772
+ if (i > 0) {
3773
+ leaves[i].prev = leaves[i - 1].id;
3774
+ }
3775
+ if (i < len - 1) {
3776
+ leaves[i].next = leaves[i + 1].id;
3777
+ }
3778
+ await this._updateNode(leaves[i]);
3779
+ }
3780
+ let currentLevel = leaves;
3781
+ while (currentLevel.length > 1) {
3782
+ const nextLevel = [];
3783
+ for (let i = 0, len = currentLevel.length; i < len; i += this.order) {
3784
+ const children = currentLevel.slice(i, i + this.order);
3785
+ const childIds = children.map((c) => c.id);
3786
+ const separators = [];
3787
+ for (let j = 1, len2 = children.length; j < len2; j++) {
3788
+ separators.push(children[j].values[0]);
3789
+ }
3790
+ const internalNode = await this._createNode(
3791
+ false,
3792
+ childIds,
3793
+ separators,
3794
+ null,
3795
+ null,
3796
+ null
3797
+ );
3798
+ for (let j = 0, len2 = children.length; j < len2; j++) {
3799
+ const child = children[j];
3800
+ child.parent = internalNode.id;
3801
+ await this._updateNode(child);
3802
+ }
3803
+ nextLevel.push(internalNode);
3804
+ }
3805
+ currentLevel = nextLevel;
3806
+ }
3807
+ const newRoot = currentLevel[0];
3808
+ await this._writeHead({
3809
+ root: newRoot.id,
3810
+ order: this.order,
3811
+ data: this.strategy.head.data
3812
+ });
3813
+ });
3814
+ }
3628
3815
  async _deleteEntry(node, key) {
3629
3816
  if (!node.leaf) {
3630
3817
  let keyIndex = -1;
@@ -3728,7 +3915,8 @@ var BPTreeAsyncTransaction = class extends BPTreeTransaction {
3728
3915
  siblingNode.values.push(...node.values);
3729
3916
  if (!siblingNode.leaf) {
3730
3917
  const keys = siblingNode.keys;
3731
- for (const key2 of keys) {
3918
+ for (let i = 0, len = keys.length; i < len; i++) {
3919
+ const key2 = keys[i];
3732
3920
  const node2 = this._cloneNode(await this.getNode(key2));
3733
3921
  node2.parent = siblingNode.id;
3734
3922
  await this._updateNode(node2);
@@ -3796,21 +3984,24 @@ var BPTreeAsyncTransaction = class extends BPTreeTransaction {
3796
3984
  await this._updateNode(siblingNode);
3797
3985
  }
3798
3986
  if (!siblingNode.leaf) {
3799
- for (const key2 of siblingNode.keys) {
3987
+ for (let i = 0, len = siblingNode.keys.length; i < len; i++) {
3988
+ const key2 = siblingNode.keys[i];
3800
3989
  const n = this._cloneNode(await this.getNode(key2));
3801
3990
  n.parent = siblingNode.id;
3802
3991
  await this._updateNode(n);
3803
3992
  }
3804
3993
  }
3805
3994
  if (!node.leaf) {
3806
- for (const key2 of node.keys) {
3995
+ for (let i = 0, len = node.keys.length; i < len; i++) {
3996
+ const key2 = node.keys[i];
3807
3997
  const n = this._cloneNode(await this.getNode(key2));
3808
3998
  n.parent = node.id;
3809
3999
  await this._updateNode(n);
3810
4000
  }
3811
4001
  }
3812
4002
  if (!parentNode.leaf) {
3813
- for (const key2 of parentNode.keys) {
4003
+ for (let i = 0, len = parentNode.keys.length; i < len; i++) {
4004
+ const key2 = parentNode.keys[i];
3814
4005
  const n = this._cloneNode(await this.getNode(key2));
3815
4006
  n.parent = parentNode.id;
3816
4007
  await this._updateNode(n);
@@ -3889,8 +4080,12 @@ var BPTreeAsyncTransaction = class extends BPTreeTransaction {
3889
4080
  result = await this.rootTx.commit(label);
3890
4081
  if (result.success) {
3891
4082
  this.rootTx.rootId = this.rootId;
4083
+ } else {
4084
+ this.mvcc.rollback();
3892
4085
  }
3893
4086
  }
4087
+ } else {
4088
+ this.mvcc.rollback();
3894
4089
  }
3895
4090
  return result;
3896
4091
  }
@@ -3996,6 +4191,16 @@ var BPTreeAsync = class extends BPTreeAsyncTransaction {
3996
4191
  }
3997
4192
  });
3998
4193
  }
4194
+ async bulkLoad(entries) {
4195
+ return this.writeLock(1, async () => {
4196
+ const tx = await this.createTransaction();
4197
+ await tx.bulkLoad(entries);
4198
+ const result = await tx.commit();
4199
+ if (!result.success) {
4200
+ throw new Error(`Transaction failed: ${result.error || "Commit failed due to conflict"}`);
4201
+ }
4202
+ });
4203
+ }
3999
4204
  };
4000
4205
 
4001
4206
  // src/base/SerializeStrategy.ts
@@ -12,4 +12,5 @@ export declare class BPTreeAsync<K, V> extends BPTreeAsyncTransaction<K, V> {
12
12
  insert(key: K, value: V): Promise<void>;
13
13
  delete(key: K, value?: V): Promise<void>;
14
14
  batchInsert(entries: [K, V][]): Promise<void>;
15
+ bulkLoad(entries: [K, V][]): Promise<void>;
15
16
  }
@@ -12,4 +12,5 @@ export declare class BPTreeSync<K, V> extends BPTreeSyncTransaction<K, V> {
12
12
  insert(key: K, value: V): void;
13
13
  delete(key: K, value?: V): void;
14
14
  batchInsert(entries: [K, V][]): void;
15
+ bulkLoad(entries: [K, V][]): void;
15
16
  }
@@ -173,6 +173,18 @@ export declare abstract class BPTreeTransaction<K, V> {
173
173
  * @param entries Array of [key, value] pairs to insert.
174
174
  */
175
175
  abstract batchInsert(entries: [K, V][]): Deferred<void>;
176
+ /**
177
+ * Builds a B+Tree from scratch using bulk loading (bottom-up construction).
178
+ * This is significantly faster than batchInsert for initial tree construction,
179
+ * as it avoids top-down traversal and creates nodes directly.
180
+ *
181
+ * **Precondition**: The tree must be empty. If the tree already contains data,
182
+ * an error will be thrown. Use batchInsert for adding data to an existing tree.
183
+ *
184
+ * @param entries Array of [key, value] pairs to bulk load.
185
+ * @throws Error if the tree is not empty.
186
+ */
187
+ abstract bulkLoad(entries: [K, V][]): Deferred<void>;
176
188
  /**
177
189
  * Deletes the pair that matches the key and value.
178
190
  * @param key The key of the pair. This key must be unique.
@@ -42,6 +42,7 @@ export declare class BPTreeAsyncTransaction<K, V> extends BPTreeTransaction<K, V
42
42
  where(condition: BPTreeCondition<V>, options?: BPTreeSearchOption<K>): Promise<BPTreePair<K, V>>;
43
43
  insert(key: K, value: V): Promise<void>;
44
44
  batchInsert(entries: [K, V][]): Promise<void>;
45
+ bulkLoad(entries: [K, V][]): Promise<void>;
45
46
  protected _deleteEntry(node: BPTreeUnknownNode<K, V>, key: BPTreeNodeKey<K>): Promise<BPTreeUnknownNode<K, V>>;
46
47
  delete(key: K, value?: V): Promise<void>;
47
48
  getHeadData(): Promise<SerializableData>;
@@ -39,6 +39,7 @@ export declare class BPTreeSyncTransaction<K, V> extends BPTreeTransaction<K, V>
39
39
  where(condition: BPTreeCondition<V>, options?: BPTreeSearchOption<K>): BPTreePair<K, V>;
40
40
  insert(key: K, value: V): void;
41
41
  batchInsert(entries: [K, V][]): void;
42
+ bulkLoad(entries: [K, V][]): void;
42
43
  protected _deleteEntry(node: BPTreeUnknownNode<K, V>, key: BPTreeNodeKey<K>): BPTreeUnknownNode<K, V>;
43
44
  delete(key: K, value?: V): void;
44
45
  getHeadData(): SerializableData;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "serializable-bptree",
3
- "version": "8.3.5",
3
+ "version": "8.4.1",
4
4
  "description": "Store the B+tree flexibly, not only in-memory.",
5
5
  "types": "./dist/types/index.d.ts",
6
6
  "main": "./dist/cjs/index.cjs",
@@ -44,7 +44,7 @@
44
44
  "typescript": "^5.9.3"
45
45
  },
46
46
  "dependencies": {
47
- "mvcc-api": "^1.3.5",
47
+ "mvcc-api": "^1.3.6",
48
48
  "ryoiki": "^1.2.0"
49
49
  }
50
50
  }