document-dataply 0.0.13 → 0.0.14-alpha.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/dist/cjs/index.js CHANGED
@@ -66,7 +66,7 @@ var require_cjs = __commonJS({
66
66
  AsyncMVCCStrategy: () => AsyncMVCCStrategy2,
67
67
  AsyncMVCCTransaction: () => AsyncMVCCTransaction2,
68
68
  BPTreeAsync: () => BPTreeAsync2,
69
- BPTreeAsyncTransaction: () => BPTreeAsyncTransaction2,
69
+ BPTreeAsyncTransaction: () => BPTreeAsyncTransaction,
70
70
  BPTreeSync: () => BPTreeSync,
71
71
  BPTreeSyncTransaction: () => BPTreeSyncTransaction,
72
72
  BitmapPageManager: () => BitmapPageManager,
@@ -1985,30 +1985,22 @@ var require_cjs = __commonJS({
1985
1985
  */
1986
1986
  _insertValueIntoLeaf(leaf, key, value) {
1987
1987
  if (leaf.values.length) {
1988
- for (let i = 0, len = leaf.values.length; i < len; i++) {
1989
- const nValue = leaf.values[i];
1990
- if (this.comparator.isSame(value, nValue)) {
1991
- if (leaf.keys[i].includes(key)) {
1992
- return false;
1993
- }
1994
- leaf.keys[i].push(key);
1995
- return true;
1996
- } else if (this.comparator.isLower(value, nValue)) {
1997
- leaf.values.splice(i, 0, value);
1998
- leaf.keys.splice(i, 0, [key]);
1999
- return true;
2000
- } else if (i + 1 === leaf.values.length) {
2001
- leaf.values.push(value);
2002
- leaf.keys.push([key]);
2003
- return true;
1988
+ const { index, found } = this._binarySearchValues(leaf.values, value);
1989
+ if (found) {
1990
+ if (leaf.keys[index].includes(key)) {
1991
+ return false;
2004
1992
  }
1993
+ leaf.keys[index].push(key);
1994
+ return true;
2005
1995
  }
1996
+ leaf.values.splice(index, 0, value);
1997
+ leaf.keys.splice(index, 0, [key]);
1998
+ return true;
2006
1999
  } else {
2007
2000
  leaf.values = [value];
2008
2001
  leaf.keys = [[key]];
2009
2002
  return true;
2010
2003
  }
2011
- return false;
2012
2004
  }
2013
2005
  _cloneNode(node) {
2014
2006
  return JSON.parse(JSON.stringify(node));
@@ -2299,12 +2291,8 @@ var require_cjs = __commonJS({
2299
2291
  const midValue = parentNode.values[mid];
2300
2292
  parentNode.values = parentNode.values.slice(0, mid);
2301
2293
  parentNode.keys = parentNode.keys.slice(0, mid + 1);
2302
- for (const k2 of parentNode.keys) {
2303
- const n = this._cloneNode(this.getNode(k2));
2304
- n.parent = parentNode.id;
2305
- this._updateNode(n);
2306
- }
2307
- for (const k2 of newSiblingNodeRecursive.keys) {
2294
+ for (let i = 0, len = newSiblingNodeRecursive.keys.length; i < len; i++) {
2295
+ const k2 = newSiblingNodeRecursive.keys[i];
2308
2296
  const n = this._cloneNode(this.getNode(k2));
2309
2297
  n.parent = newSiblingNodeRecursive.id;
2310
2298
  this._updateNode(n);
@@ -2386,7 +2374,7 @@ var require_cjs = __commonJS({
2386
2374
  for (let i = 0; i < len; i++) {
2387
2375
  const nValue = node.values[i];
2388
2376
  const keys = node.keys[i];
2389
- for (let j = 0, kLen = keys.length; j < kLen; j++) {
2377
+ for (let j = 0, len2 = keys.length; j < len2; j++) {
2390
2378
  yield [keys[j], nValue];
2391
2379
  }
2392
2380
  }
@@ -2465,7 +2453,7 @@ var require_cjs = __commonJS({
2465
2453
  while (true) {
2466
2454
  for (let i = 0, len = node.values.length; i < len; i++) {
2467
2455
  const keys = node.keys[i];
2468
- for (let j = 0, kLen = keys.length; j < kLen; j++) {
2456
+ for (let j = 0, len2 = keys.length; j < len2; j++) {
2469
2457
  if (keys[j] === key) {
2470
2458
  return node.values[i];
2471
2459
  }
@@ -2574,8 +2562,16 @@ var require_cjs = __commonJS({
2574
2562
  const sorted = [...entries].sort((a, b2) => this.comparator.asc(a[1], b2[1]));
2575
2563
  let currentLeaf = null;
2576
2564
  let modified = false;
2577
- for (const [key, value] of sorted) {
2578
- const targetLeaf = this.locateLeaf(value);
2565
+ let cachedLeafId = null;
2566
+ let cachedLeafMaxValue = null;
2567
+ for (let i = 0, len = sorted.length; i < len; i++) {
2568
+ const [key, value] = sorted[i];
2569
+ let targetLeaf;
2570
+ if (cachedLeafId !== null && cachedLeafMaxValue !== null && currentLeaf !== null && (this.comparator.isLower(value, cachedLeafMaxValue) || this.comparator.isSame(value, cachedLeafMaxValue))) {
2571
+ targetLeaf = currentLeaf;
2572
+ } else {
2573
+ targetLeaf = this.locateLeaf(value);
2574
+ }
2579
2575
  if (currentLeaf !== null && currentLeaf.id === targetLeaf.id) {
2580
2576
  } else {
2581
2577
  if (currentLeaf !== null && modified) {
@@ -2584,8 +2580,10 @@ var require_cjs = __commonJS({
2584
2580
  currentLeaf = this._cloneNode(targetLeaf);
2585
2581
  modified = false;
2586
2582
  }
2583
+ cachedLeafId = currentLeaf.id;
2587
2584
  const changed = this._insertValueIntoLeaf(currentLeaf, key, value);
2588
2585
  modified = modified || changed;
2586
+ cachedLeafMaxValue = currentLeaf.values[currentLeaf.values.length - 1];
2589
2587
  if (currentLeaf.values.length === this.order) {
2590
2588
  this._updateNode(currentLeaf);
2591
2589
  let after = this._createNode(
@@ -2606,6 +2604,8 @@ var require_cjs = __commonJS({
2606
2604
  this._updateNode(after);
2607
2605
  this._insertInParent(currentLeaf, after.values[0], after);
2608
2606
  currentLeaf = null;
2607
+ cachedLeafId = null;
2608
+ cachedLeafMaxValue = null;
2609
2609
  modified = false;
2610
2610
  }
2611
2611
  }
@@ -2613,6 +2613,85 @@ var require_cjs = __commonJS({
2613
2613
  this._updateNode(currentLeaf);
2614
2614
  }
2615
2615
  }
2616
+ bulkLoad(entries) {
2617
+ if (entries.length === 0) return;
2618
+ const root = this.getNode(this.rootId);
2619
+ if (!root.leaf || root.values.length > 0) {
2620
+ throw new Error("bulkLoad can only be called on an empty tree. Use batchInsert for non-empty trees.");
2621
+ }
2622
+ const sorted = [...entries].sort((a, b2) => this.comparator.asc(a[1], b2[1]));
2623
+ const grouped = [];
2624
+ for (let i = 0, len = sorted.length; i < len; i++) {
2625
+ const [key, value] = sorted[i];
2626
+ const last = grouped[grouped.length - 1];
2627
+ if (last && this.comparator.isSame(last.value, value)) {
2628
+ if (!last.keys.includes(key)) {
2629
+ last.keys.push(key);
2630
+ }
2631
+ } else {
2632
+ grouped.push({ keys: [key], value });
2633
+ }
2634
+ }
2635
+ this._deleteNode(root);
2636
+ const maxLeafSize = this.order - 1;
2637
+ const leaves = [];
2638
+ for (let i = 0, len = grouped.length; i < len; i += maxLeafSize) {
2639
+ const chunk = grouped.slice(i, i + maxLeafSize);
2640
+ const leafKeys = chunk.map((g2) => g2.keys);
2641
+ const leafValues = chunk.map((g2) => g2.value);
2642
+ const leaf = this._createNode(
2643
+ true,
2644
+ leafKeys,
2645
+ leafValues,
2646
+ null,
2647
+ null,
2648
+ null
2649
+ );
2650
+ leaves.push(leaf);
2651
+ }
2652
+ for (let i = 0, len = leaves.length; i < len; i++) {
2653
+ if (i > 0) {
2654
+ leaves[i].prev = leaves[i - 1].id;
2655
+ }
2656
+ if (i < len - 1) {
2657
+ leaves[i].next = leaves[i + 1].id;
2658
+ }
2659
+ this._updateNode(leaves[i]);
2660
+ }
2661
+ let currentLevel = leaves;
2662
+ while (currentLevel.length > 1) {
2663
+ const nextLevel = [];
2664
+ for (let i = 0, len = currentLevel.length; i < len; i += this.order) {
2665
+ const children = currentLevel.slice(i, i + this.order);
2666
+ const childIds = children.map((c) => c.id);
2667
+ const separators = [];
2668
+ for (let j = 1, len2 = children.length; j < len2; j++) {
2669
+ separators.push(children[j].values[0]);
2670
+ }
2671
+ const internalNode = this._createNode(
2672
+ false,
2673
+ childIds,
2674
+ separators,
2675
+ null,
2676
+ null,
2677
+ null
2678
+ );
2679
+ for (let j = 0, len2 = children.length; j < len2; j++) {
2680
+ const child = children[j];
2681
+ child.parent = internalNode.id;
2682
+ this._updateNode(child);
2683
+ }
2684
+ nextLevel.push(internalNode);
2685
+ }
2686
+ currentLevel = nextLevel;
2687
+ }
2688
+ const newRoot = currentLevel[0];
2689
+ this._writeHead({
2690
+ root: newRoot.id,
2691
+ order: this.order,
2692
+ data: this.strategy.head.data
2693
+ });
2694
+ }
2616
2695
  _deleteEntry(node, key) {
2617
2696
  if (!node.leaf) {
2618
2697
  let keyIndex = -1;
@@ -2716,7 +2795,8 @@ var require_cjs = __commonJS({
2716
2795
  siblingNode.values.push(...node.values);
2717
2796
  if (!siblingNode.leaf) {
2718
2797
  const keys = siblingNode.keys;
2719
- for (const key2 of keys) {
2798
+ for (let i = 0, len = keys.length; i < len; i++) {
2799
+ const key2 = keys[i];
2720
2800
  const node2 = this._cloneNode(this.getNode(key2));
2721
2801
  node2.parent = siblingNode.id;
2722
2802
  this._updateNode(node2);
@@ -2784,21 +2864,24 @@ var require_cjs = __commonJS({
2784
2864
  this._updateNode(siblingNode);
2785
2865
  }
2786
2866
  if (!siblingNode.leaf) {
2787
- for (const key2 of siblingNode.keys) {
2867
+ for (let i = 0, len = siblingNode.keys.length; i < len; i++) {
2868
+ const key2 = siblingNode.keys[i];
2788
2869
  const n = this._cloneNode(this.getNode(key2));
2789
2870
  n.parent = siblingNode.id;
2790
2871
  this._updateNode(n);
2791
2872
  }
2792
2873
  }
2793
2874
  if (!node.leaf) {
2794
- for (const key2 of node.keys) {
2875
+ for (let i = 0, len = node.keys.length; i < len; i++) {
2876
+ const key2 = node.keys[i];
2795
2877
  const n = this._cloneNode(this.getNode(key2));
2796
2878
  n.parent = node.id;
2797
2879
  this._updateNode(n);
2798
2880
  }
2799
2881
  }
2800
2882
  if (!parentNode.leaf) {
2801
- for (const key2 of parentNode.keys) {
2883
+ for (let i = 0, len = parentNode.keys.length; i < len; i++) {
2884
+ const key2 = parentNode.keys[i];
2802
2885
  const n = this._cloneNode(this.getNode(key2));
2803
2886
  n.parent = parentNode.id;
2804
2887
  this._updateNode(n);
@@ -2972,6 +3055,14 @@ var require_cjs = __commonJS({
2972
3055
  throw new Error(`Transaction failed: ${result.error || "Commit failed due to conflict"}`);
2973
3056
  }
2974
3057
  }
3058
+ bulkLoad(entries) {
3059
+ const tx = this.createTransaction();
3060
+ tx.bulkLoad(entries);
3061
+ const result = tx.commit();
3062
+ if (!result.success) {
3063
+ throw new Error(`Transaction failed: ${result.error || "Commit failed due to conflict"}`);
3064
+ }
3065
+ }
2975
3066
  };
2976
3067
  var Ryoiki2 = class _Ryoiki2 {
2977
3068
  readings;
@@ -3229,7 +3320,7 @@ var require_cjs = __commonJS({
3229
3320
  this._free(this.writings, lockId);
3230
3321
  }
3231
3322
  };
3232
- var BPTreeAsyncTransaction2 = class extends BPTreeTransaction {
3323
+ var BPTreeAsyncTransaction = class extends BPTreeTransaction {
3233
3324
  lock;
3234
3325
  constructor(rootTx, mvccRoot, mvcc, strategy, comparator, option) {
3235
3326
  super(
@@ -3385,12 +3476,8 @@ var require_cjs = __commonJS({
3385
3476
  const midValue = parentNode.values[mid];
3386
3477
  parentNode.values = parentNode.values.slice(0, mid);
3387
3478
  parentNode.keys = parentNode.keys.slice(0, mid + 1);
3388
- for (const k2 of parentNode.keys) {
3389
- const n = this._cloneNode(await this.getNode(k2));
3390
- n.parent = parentNode.id;
3391
- await this._updateNode(n);
3392
- }
3393
- for (const k2 of newSiblingNodeRecursive.keys) {
3479
+ for (let i = 0, len = newSiblingNodeRecursive.keys.length; i < len; i++) {
3480
+ const k2 = newSiblingNodeRecursive.keys[i];
3394
3481
  const n = this._cloneNode(await this.getNode(k2));
3395
3482
  n.parent = newSiblingNodeRecursive.id;
3396
3483
  await this._updateNode(n);
@@ -3482,7 +3569,7 @@ var require_cjs = __commonJS({
3482
3569
  for (let i = 0; i < len; i++) {
3483
3570
  const nValue = node.values[i];
3484
3571
  const keys = node.keys[i];
3485
- for (let j = 0, kLen = keys.length; j < kLen; j++) {
3572
+ for (let j = 0, len2 = keys.length; j < len2; j++) {
3486
3573
  yield [keys[j], nValue];
3487
3574
  }
3488
3575
  }
@@ -3560,7 +3647,7 @@ var require_cjs = __commonJS({
3560
3647
  while (true) {
3561
3648
  for (let i = 0, len = node.values.length; i < len; i++) {
3562
3649
  const keys = node.keys[i];
3563
- for (let j = 0, kLen = keys.length; j < kLen; j++) {
3650
+ for (let j = 0, len2 = keys.length; j < len2; j++) {
3564
3651
  if (keys[j] === key) {
3565
3652
  return node.values[i];
3566
3653
  }
@@ -3672,8 +3759,16 @@ var require_cjs = __commonJS({
3672
3759
  const sorted = [...entries].sort((a, b2) => this.comparator.asc(a[1], b2[1]));
3673
3760
  let currentLeaf = null;
3674
3761
  let modified = false;
3675
- for (const [key, value] of sorted) {
3676
- const targetLeaf = await this.locateLeaf(value);
3762
+ let cachedLeafId = null;
3763
+ let cachedLeafMaxValue = null;
3764
+ for (let i = 0, len = sorted.length; i < len; i++) {
3765
+ const [key, value] = sorted[i];
3766
+ let targetLeaf;
3767
+ if (cachedLeafId !== null && cachedLeafMaxValue !== null && currentLeaf !== null && (this.comparator.isLower(value, cachedLeafMaxValue) || this.comparator.isSame(value, cachedLeafMaxValue))) {
3768
+ targetLeaf = currentLeaf;
3769
+ } else {
3770
+ targetLeaf = await this.locateLeaf(value);
3771
+ }
3677
3772
  if (currentLeaf !== null && currentLeaf.id === targetLeaf.id) {
3678
3773
  } else {
3679
3774
  if (currentLeaf !== null && modified) {
@@ -3682,8 +3777,10 @@ var require_cjs = __commonJS({
3682
3777
  currentLeaf = this._cloneNode(targetLeaf);
3683
3778
  modified = false;
3684
3779
  }
3780
+ cachedLeafId = currentLeaf.id;
3685
3781
  const changed = this._insertValueIntoLeaf(currentLeaf, key, value);
3686
3782
  modified = modified || changed;
3783
+ cachedLeafMaxValue = currentLeaf.values[currentLeaf.values.length - 1];
3687
3784
  if (currentLeaf.values.length === this.order) {
3688
3785
  await this._updateNode(currentLeaf);
3689
3786
  let after = await this._createNode(
@@ -3704,6 +3801,8 @@ var require_cjs = __commonJS({
3704
3801
  await this._updateNode(after);
3705
3802
  await this._insertInParent(currentLeaf, after.values[0], after);
3706
3803
  currentLeaf = null;
3804
+ cachedLeafId = null;
3805
+ cachedLeafMaxValue = null;
3707
3806
  modified = false;
3708
3807
  }
3709
3808
  }
@@ -3712,6 +3811,87 @@ var require_cjs = __commonJS({
3712
3811
  }
3713
3812
  });
3714
3813
  }
3814
+ async bulkLoad(entries) {
3815
+ if (entries.length === 0) return;
3816
+ return this.writeLock(0, async () => {
3817
+ const root = await this.getNode(this.rootId);
3818
+ if (!root.leaf || root.values.length > 0) {
3819
+ throw new Error("bulkLoad can only be called on an empty tree. Use batchInsert for non-empty trees.");
3820
+ }
3821
+ const sorted = [...entries].sort((a, b2) => this.comparator.asc(a[1], b2[1]));
3822
+ const grouped = [];
3823
+ for (let i = 0, len = sorted.length; i < len; i++) {
3824
+ const [key, value] = sorted[i];
3825
+ const last = grouped[grouped.length - 1];
3826
+ if (last && this.comparator.isSame(last.value, value)) {
3827
+ if (!last.keys.includes(key)) {
3828
+ last.keys.push(key);
3829
+ }
3830
+ } else {
3831
+ grouped.push({ keys: [key], value });
3832
+ }
3833
+ }
3834
+ await this._deleteNode(root);
3835
+ const maxLeafSize = this.order - 1;
3836
+ const leaves = [];
3837
+ for (let i = 0, len = grouped.length; i < len; i += maxLeafSize) {
3838
+ const chunk = grouped.slice(i, i + maxLeafSize);
3839
+ const leafKeys = chunk.map((g2) => g2.keys);
3840
+ const leafValues = chunk.map((g2) => g2.value);
3841
+ const leaf = await this._createNode(
3842
+ true,
3843
+ leafKeys,
3844
+ leafValues,
3845
+ null,
3846
+ null,
3847
+ null
3848
+ );
3849
+ leaves.push(leaf);
3850
+ }
3851
+ for (let i = 0, len = leaves.length; i < len; i++) {
3852
+ if (i > 0) {
3853
+ leaves[i].prev = leaves[i - 1].id;
3854
+ }
3855
+ if (i < len - 1) {
3856
+ leaves[i].next = leaves[i + 1].id;
3857
+ }
3858
+ await this._updateNode(leaves[i]);
3859
+ }
3860
+ let currentLevel = leaves;
3861
+ while (currentLevel.length > 1) {
3862
+ const nextLevel = [];
3863
+ for (let i = 0, len = currentLevel.length; i < len; i += this.order) {
3864
+ const children = currentLevel.slice(i, i + this.order);
3865
+ const childIds = children.map((c) => c.id);
3866
+ const separators = [];
3867
+ for (let j = 1, len2 = children.length; j < len2; j++) {
3868
+ separators.push(children[j].values[0]);
3869
+ }
3870
+ const internalNode = await this._createNode(
3871
+ false,
3872
+ childIds,
3873
+ separators,
3874
+ null,
3875
+ null,
3876
+ null
3877
+ );
3878
+ for (let j = 0, len2 = children.length; j < len2; j++) {
3879
+ const child = children[j];
3880
+ child.parent = internalNode.id;
3881
+ await this._updateNode(child);
3882
+ }
3883
+ nextLevel.push(internalNode);
3884
+ }
3885
+ currentLevel = nextLevel;
3886
+ }
3887
+ const newRoot = currentLevel[0];
3888
+ await this._writeHead({
3889
+ root: newRoot.id,
3890
+ order: this.order,
3891
+ data: this.strategy.head.data
3892
+ });
3893
+ });
3894
+ }
3715
3895
  async _deleteEntry(node, key) {
3716
3896
  if (!node.leaf) {
3717
3897
  let keyIndex = -1;
@@ -3815,7 +3995,8 @@ var require_cjs = __commonJS({
3815
3995
  siblingNode.values.push(...node.values);
3816
3996
  if (!siblingNode.leaf) {
3817
3997
  const keys = siblingNode.keys;
3818
- for (const key2 of keys) {
3998
+ for (let i = 0, len = keys.length; i < len; i++) {
3999
+ const key2 = keys[i];
3819
4000
  const node2 = this._cloneNode(await this.getNode(key2));
3820
4001
  node2.parent = siblingNode.id;
3821
4002
  await this._updateNode(node2);
@@ -3883,21 +4064,24 @@ var require_cjs = __commonJS({
3883
4064
  await this._updateNode(siblingNode);
3884
4065
  }
3885
4066
  if (!siblingNode.leaf) {
3886
- for (const key2 of siblingNode.keys) {
4067
+ for (let i = 0, len = siblingNode.keys.length; i < len; i++) {
4068
+ const key2 = siblingNode.keys[i];
3887
4069
  const n = this._cloneNode(await this.getNode(key2));
3888
4070
  n.parent = siblingNode.id;
3889
4071
  await this._updateNode(n);
3890
4072
  }
3891
4073
  }
3892
4074
  if (!node.leaf) {
3893
- for (const key2 of node.keys) {
4075
+ for (let i = 0, len = node.keys.length; i < len; i++) {
4076
+ const key2 = node.keys[i];
3894
4077
  const n = this._cloneNode(await this.getNode(key2));
3895
4078
  n.parent = node.id;
3896
4079
  await this._updateNode(n);
3897
4080
  }
3898
4081
  }
3899
4082
  if (!parentNode.leaf) {
3900
- for (const key2 of parentNode.keys) {
4083
+ for (let i = 0, len = parentNode.keys.length; i < len; i++) {
4084
+ const key2 = parentNode.keys[i];
3901
4085
  const n = this._cloneNode(await this.getNode(key2));
3902
4086
  n.parent = parentNode.id;
3903
4087
  await this._updateNode(n);
@@ -4018,7 +4202,7 @@ var require_cjs = __commonJS({
4018
4202
  }
4019
4203
  }
4020
4204
  };
4021
- var BPTreeAsync2 = class extends BPTreeAsyncTransaction2 {
4205
+ var BPTreeAsync2 = class extends BPTreeAsyncTransaction {
4022
4206
  constructor(strategy, comparator, option) {
4023
4207
  const mvccRoot = new AsyncMVCCTransaction(new BPTreeMVCCStrategyAsync(strategy), {
4024
4208
  cacheCapacity: option?.capacity ?? void 0
@@ -4038,7 +4222,7 @@ var require_cjs = __commonJS({
4038
4222
  */
4039
4223
  async createTransaction() {
4040
4224
  const nestedTx = this.mvcc.createNested();
4041
- const tx = new BPTreeAsyncTransaction2(
4225
+ const tx = new BPTreeAsyncTransaction(
4042
4226
  this,
4043
4227
  this.mvcc,
4044
4228
  nestedTx,
@@ -4079,6 +4263,16 @@ var require_cjs = __commonJS({
4079
4263
  }
4080
4264
  });
4081
4265
  }
4266
+ async bulkLoad(entries) {
4267
+ return this.writeLock(1, async () => {
4268
+ const tx = await this.createTransaction();
4269
+ await tx.bulkLoad(entries);
4270
+ const result = await tx.commit();
4271
+ if (!result.success) {
4272
+ throw new Error(`Transaction failed: ${result.error || "Commit failed due to conflict"}`);
4273
+ }
4274
+ });
4275
+ }
4082
4276
  };
4083
4277
  var SerializeStrategy = class {
4084
4278
  order;
@@ -8065,12 +8259,14 @@ var require_cjs = __commonJS({
8065
8259
  buffer;
8066
8260
  view;
8067
8261
  totalWrittenPages = 0;
8262
+ logger;
8068
8263
  /**
8069
8264
  * Constructor
8070
8265
  * @param walFilePath WAL file path
8071
8266
  * @param pageSize Page size
8267
+ * @param logger Logger instance
8072
8268
  */
8073
- constructor(walFilePath, pageSize) {
8269
+ constructor(walFilePath, pageSize, logger) {
8074
8270
  if ((pageSize & pageSize - 1) !== 0) {
8075
8271
  throw new Error("Page size must be a power of 2");
8076
8272
  }
@@ -8079,6 +8275,7 @@ var require_cjs = __commonJS({
8079
8275
  this.entrySize = 4 + pageSize;
8080
8276
  this.buffer = new Uint8Array(this.entrySize);
8081
8277
  this.view = new DataView(this.buffer.buffer);
8278
+ this.logger = logger;
8082
8279
  }
8083
8280
  /**
8084
8281
  * Opens the log file.
@@ -8106,11 +8303,11 @@ var require_cjs = __commonJS({
8106
8303
  try {
8107
8304
  const manager = new PageManagerFactory().getManager(data);
8108
8305
  if (!manager.verifyChecksum(data)) {
8109
- console.warn(`[WALManager] Checksum verification failed for PageID ${pageId} during recovery. Ignoring changes.`);
8306
+ this.logger.warn(`Checksum verification failed for PageID ${pageId} during recovery. Ignoring changes.`);
8110
8307
  continue;
8111
8308
  }
8112
8309
  } catch (e) {
8113
- console.warn(`[WALManager] Failed to verify checksum for PageID ${pageId} during recovery: ${e}. Ignoring changes.`);
8310
+ this.logger.warn(`Failed to verify checksum for PageID ${pageId} during recovery: ${e}. Ignoring changes.`);
8114
8311
  continue;
8115
8312
  }
8116
8313
  promises.push(writePage(pageId, data));
@@ -8423,20 +8620,22 @@ var require_cjs = __commonJS({
8423
8620
  * @param pageCacheCapacity 페이지 캐시 크기
8424
8621
  * @param options 데이터플라이 옵션
8425
8622
  */
8426
- constructor(fileHandle, pageSize, pageCacheCapacity, options) {
8623
+ constructor(fileHandle, pageSize, pageCacheCapacity, options, logger, walLogger) {
8427
8624
  this.fileHandle = fileHandle;
8428
8625
  this.pageSize = pageSize;
8429
8626
  this.pageCacheCapacity = pageCacheCapacity;
8430
8627
  this.options = options;
8431
8628
  const walPath = options.wal;
8432
- this.walManager = walPath ? new WALManager(walPath, pageSize) : null;
8629
+ this.walManager = walPath && walLogger ? new WALManager(walPath, pageSize, walLogger) : null;
8433
8630
  this.pageManagerFactory = new PageManagerFactory();
8434
8631
  this.pageStrategy = new PageMVCCStrategy(fileHandle, pageSize, pageCacheCapacity);
8632
+ this.logger = logger;
8435
8633
  }
8436
8634
  pageFactory = new PageManagerFactory();
8437
8635
  walManager;
8438
8636
  pageManagerFactory;
8439
8637
  pageStrategy;
8638
+ logger;
8440
8639
  /** 글로벌 동기화(체크포인트/커밋)를 위한 Mutex */
8441
8640
  lockPromise = Promise.resolve();
8442
8641
  /**
@@ -8461,7 +8660,9 @@ var require_cjs = __commonJS({
8461
8660
  * Performs WAL recovery if necessary.
8462
8661
  */
8463
8662
  async init() {
8663
+ this.logger.info("Initializing");
8464
8664
  if (this.walManager) {
8665
+ this.logger.debug("WALManager found, starting recovery");
8465
8666
  await this.walManager.recover(async (pageId, data) => {
8466
8667
  await this.pageStrategy.write(pageId, data);
8467
8668
  });
@@ -8633,6 +8834,7 @@ var require_cjs = __commonJS({
8633
8834
  * @returns Created or reused page ID
8634
8835
  */
8635
8836
  async appendNewPage(pageType = PageManager.CONSTANT.PAGE_TYPE_EMPTY, tx) {
8837
+ this.logger.debug(`Appending new page of type ${pageType}`);
8636
8838
  await tx.__acquireWriteLock(0);
8637
8839
  const metadata = await this.getMetadata(tx);
8638
8840
  const metadataManager = this.pageFactory.getManager(metadata);
@@ -8736,6 +8938,7 @@ var require_cjs = __commonJS({
8736
8938
  * @param tx Transaction
8737
8939
  */
8738
8940
  async freeChain(startPageId, tx) {
8941
+ this.logger.debug(`Freeing chain starting at page ${startPageId}`);
8739
8942
  let currentPageId = startPageId;
8740
8943
  const visited = /* @__PURE__ */ new Set();
8741
8944
  while (currentPageId !== -1 && currentPageId !== 0) {
@@ -8775,6 +8978,7 @@ var require_cjs = __commonJS({
8775
8978
  */
8776
8979
  async commitToWAL(dirtyPages) {
8777
8980
  if (this.walManager) {
8981
+ this.logger.debug(`Committing ${dirtyPages.size} pages to WAL`);
8778
8982
  await this.walManager.prepareCommit(dirtyPages);
8779
8983
  await this.walManager.finalizeCommit(true);
8780
8984
  }
@@ -8786,6 +8990,7 @@ var require_cjs = __commonJS({
8786
8990
  * 3. WAL 로그 파일 비우기 (Clear/Truncate)
8787
8991
  */
8788
8992
  async checkpoint() {
8993
+ this.logger.info("Starting checkpoint");
8789
8994
  await this.runGlobalLock(async () => {
8790
8995
  await this.pageStrategy.flush();
8791
8996
  await this.pageStrategy.sync();
@@ -8798,6 +9003,7 @@ var require_cjs = __commonJS({
8798
9003
  * Closes the page file system.
8799
9004
  */
8800
9005
  async close() {
9006
+ this.logger.info("Closing");
8801
9007
  await this.checkpoint();
8802
9008
  if (this.walManager) {
8803
9009
  this.walManager.close();
@@ -9029,7 +9235,7 @@ var require_cjs = __commonJS({
9029
9235
  }
9030
9236
  };
9031
9237
  var RowTableEngine = class {
9032
- constructor(pfs, txContext, options) {
9238
+ constructor(pfs, txContext, options, logger) {
9033
9239
  this.pfs = pfs;
9034
9240
  this.txContext = txContext;
9035
9241
  this.options = options;
@@ -9039,6 +9245,7 @@ var require_cjs = __commonJS({
9039
9245
  this.overflowPageManager = this.factory.getManagerFromType(OverflowPageManager.CONSTANT.PAGE_TYPE_OVERFLOW);
9040
9246
  this.rowManager = new Row();
9041
9247
  this.keyManager = new KeyManager();
9248
+ this.logger = logger;
9042
9249
  this.ridBuffer = new Uint8Array(Row.CONSTANT.SIZE_RID);
9043
9250
  this.pageIdBuffer = new Uint8Array(DataPageManager.CONSTANT.SIZE_PAGE_ID);
9044
9251
  this.maxBodySize = this.pfs.pageSize - DataPageManager.CONSTANT.SIZE_PAGE_HEADER;
@@ -9067,6 +9274,7 @@ var require_cjs = __commonJS({
9067
9274
  maxBodySize;
9068
9275
  ridBuffer;
9069
9276
  pageIdBuffer;
9277
+ logger;
9070
9278
  initialized = false;
9071
9279
  /**
9072
9280
  * Retrieves the BPTree transaction associated with the given transaction.
@@ -9101,6 +9309,7 @@ var require_cjs = __commonJS({
9101
9309
  */
9102
9310
  async init() {
9103
9311
  if (!this.initialized) {
9312
+ this.logger.info("Initializing B+ Tree");
9104
9313
  await this.bptree.init();
9105
9314
  this.initialized = true;
9106
9315
  }
@@ -9148,6 +9357,7 @@ var require_cjs = __commonJS({
9148
9357
  * @returns Metadata
9149
9358
  */
9150
9359
  async getMetadata(tx) {
9360
+ this.logger.debug("Getting metadata");
9151
9361
  if (!this.initialized) {
9152
9362
  throw new Error("RowTableEngine instance is not initialized");
9153
9363
  }
@@ -9173,6 +9383,7 @@ var require_cjs = __commonJS({
9173
9383
  * @returns Array of PKs of the inserted data
9174
9384
  */
9175
9385
  async insert(dataList, incrementRowCount, overflowForcly, tx) {
9386
+ this.logger.debug(`Inserting ${dataList.length} rows (overflowForcly: ${overflowForcly})`);
9176
9387
  if (dataList.length === 0) {
9177
9388
  return [];
9178
9389
  }
@@ -9274,6 +9485,7 @@ var require_cjs = __commonJS({
9274
9485
  * @param tx Transaction
9275
9486
  */
9276
9487
  async update(pk, data, tx) {
9488
+ this.logger.debug(`Updating row with PK: ${pk}`);
9277
9489
  await tx.__acquireWriteLock(0);
9278
9490
  const rid = await this.getRidByPK(pk, tx);
9279
9491
  if (rid === null) {
@@ -9359,6 +9571,7 @@ var require_cjs = __commonJS({
9359
9571
  * @param tx Transaction
9360
9572
  */
9361
9573
  async delete(pk, decrementRowCount, tx) {
9574
+ this.logger.debug(`Deleting row with PK: ${pk}`);
9362
9575
  await tx.__acquireWriteLock(0);
9363
9576
  const rid = await this.getRidByPK(pk, tx);
9364
9577
  if (rid === null) {
@@ -9418,6 +9631,7 @@ var require_cjs = __commonJS({
9418
9631
  * @returns Raw data of the row
9419
9632
  */
9420
9633
  async selectByPK(pk, tx) {
9634
+ this.logger.debug(`Selecting row by PK: ${pk}`);
9421
9635
  const rid = await this.getRidByPK(pk, tx);
9422
9636
  if (rid === null) {
9423
9637
  return null;
@@ -9432,6 +9646,7 @@ var require_cjs = __commonJS({
9432
9646
  * @returns Array of raw data of the rows in the same order as input PKs
9433
9647
  */
9434
9648
  async selectMany(pks, tx) {
9649
+ this.logger.debug(`Selecting many rows (${pks.length} PKs)`);
9435
9650
  const collections = await this.collectItemsByPage(pks, tx);
9436
9651
  return this.fetchRowsByRids(collections, pks.length, tx);
9437
9652
  }
@@ -9810,25 +10025,99 @@ var require_cjs = __commonJS({
9810
10025
  return this.storage.run(tx, callback);
9811
10026
  }
9812
10027
  };
10028
+ var Colors = {
10029
+ reset: "\x1B[0m",
10030
+ debug: "\x1B[36m",
10031
+ // Cyan
10032
+ info: "\x1B[32m",
10033
+ // Green
10034
+ warn: "\x1B[33m",
10035
+ // Yellow
10036
+ error: "\x1B[31m",
10037
+ // Red
10038
+ white: "\x1B[37m",
10039
+ // White
10040
+ grey: "\x1B[90m"
10041
+ // Grey
10042
+ };
10043
+ var LoggerManager = class {
10044
+ level;
10045
+ constructor(level = 2) {
10046
+ this.level = level;
10047
+ }
10048
+ setLevel(level) {
10049
+ this.level = level;
10050
+ }
10051
+ shouldLog(level) {
10052
+ if (this.level === 0) return false;
10053
+ return level >= this.level;
10054
+ }
10055
+ create(moduleName) {
10056
+ return new Logger(this, moduleName);
10057
+ }
10058
+ };
10059
+ var Logger = class {
10060
+ constructor(parent, moduleName) {
10061
+ this.parent = parent;
10062
+ this.moduleName = moduleName;
10063
+ }
10064
+ debug(message, ...args) {
10065
+ if (this.parent.shouldLog(
10066
+ 1
10067
+ /* Debug */
10068
+ )) {
10069
+ console.debug(`${Colors.debug}[DEBUG] [${this.moduleName}]${Colors.reset} ${Colors.white}${message}${Colors.reset}`, ...args);
10070
+ }
10071
+ }
10072
+ info(message, ...args) {
10073
+ if (this.parent.shouldLog(
10074
+ 2
10075
+ /* Info */
10076
+ )) {
10077
+ console.info(`${Colors.info}[INFO] [${this.moduleName}]${Colors.reset} ${Colors.white}${message}${Colors.reset}`, ...args);
10078
+ }
10079
+ }
10080
+ warn(message, ...args) {
10081
+ if (this.parent.shouldLog(
10082
+ 3
10083
+ /* Warning */
10084
+ )) {
10085
+ console.warn(`${Colors.warn}[WARN] [${this.moduleName}]${Colors.reset} ${Colors.white}${message}${Colors.reset}`, ...args);
10086
+ }
10087
+ }
10088
+ error(message, ...args) {
10089
+ if (this.parent.shouldLog(
10090
+ 4
10091
+ /* Error */
10092
+ )) {
10093
+ console.error(`${Colors.error}[ERROR] [${this.moduleName}]${Colors.reset} ${Colors.white}${message}${Colors.reset}`, ...args);
10094
+ }
10095
+ }
10096
+ };
9813
10097
  var DataplyAPI2 = class {
9814
10098
  constructor(file, options) {
9815
10099
  this.file = file;
9816
10100
  this.hook = useHookall(this);
9817
10101
  this.options = this.verboseOptions(options);
10102
+ this.loggerManager = new LoggerManager(this.options.logLevel);
10103
+ this.logger = this.loggerManager.create("DataplyAPI");
9818
10104
  this.isNewlyCreated = !import_node_fs3.default.existsSync(file);
9819
10105
  this.fileHandle = this.createOrOpen(file, this.options);
9820
10106
  this.pfs = new PageFileSystem(
9821
10107
  this.fileHandle,
9822
10108
  this.options.pageSize,
9823
10109
  this.options.pageCacheCapacity,
9824
- this.options
10110
+ this.options,
10111
+ this.loggerManager.create("PageFileSystem"),
10112
+ options.wal ? this.loggerManager.create("WALManager") : void 0
9825
10113
  );
9826
10114
  this.textCodec = new TextCodec();
9827
10115
  this.txContext = new TransactionContext();
9828
10116
  this.lockManager = new LockManager();
9829
- this.rowTableEngine = new RowTableEngine(this.pfs, this.txContext, this.options);
10117
+ this.rowTableEngine = new RowTableEngine(this.pfs, this.txContext, this.options, this.loggerManager.create("RowTableEngine"));
9830
10118
  this.initialized = false;
9831
10119
  this.txIdCounter = 0;
10120
+ this.logger.debug(`DataplyAPI instance created with file: ${file}`);
9832
10121
  }
9833
10122
  /**
9834
10123
  * These are not the same options that were used when the database was created.
@@ -9850,6 +10139,10 @@ var require_cjs = __commonJS({
9850
10139
  txContext;
9851
10140
  /** Hook */
9852
10141
  hook;
10142
+ /** Base Logger */
10143
+ loggerManager;
10144
+ /** Logger module for DataplyAPI */
10145
+ logger;
9853
10146
  /** Whether the database was initialized via `init()` */
9854
10147
  initialized;
9855
10148
  /** Whether the database was created this time. */
@@ -9864,6 +10157,7 @@ var require_cjs = __commonJS({
9864
10157
  * @returns Whether the page file is a valid Dataply file
9865
10158
  */
9866
10159
  verifyFormat(fileHandle) {
10160
+ this.logger.debug(`Verifying format for file handle: ${fileHandle}`);
9867
10161
  const size = MetadataPageManager.CONSTANT.OFFSET_MAGIC_STRING + MetadataPageManager.CONSTANT.MAGIC_STRING.length;
9868
10162
  const metadataPage = new Uint8Array(size);
9869
10163
  import_node_fs3.default.readSync(fileHandle, metadataPage, 0, size, 0);
@@ -9883,7 +10177,9 @@ var require_cjs = __commonJS({
9883
10177
  pageCacheCapacity: 1e4,
9884
10178
  pagePreallocationCount: 1e3,
9885
10179
  wal: null,
9886
- walCheckpointThreshold: 1e3
10180
+ walCheckpointThreshold: 1e3,
10181
+ logLevel: 2
10182
+ /* Info */
9887
10183
  }, options);
9888
10184
  }
9889
10185
  /**
@@ -9894,6 +10190,7 @@ var require_cjs = __commonJS({
9894
10190
  * @param fileHandle File handle
9895
10191
  */
9896
10192
  initializeFile(file, fileHandle, options) {
10193
+ this.logger.info(`Initializing new dataply file: ${file}`);
9897
10194
  const metadataPageManager = new MetadataPageManager();
9898
10195
  const bitmapPageManager = new BitmapPageManager();
9899
10196
  const dataPageManager = new DataPageManager();
@@ -9943,6 +10240,7 @@ var require_cjs = __commonJS({
9943
10240
  * @returns File handle
9944
10241
  */
9945
10242
  createOrOpen(file, options) {
10243
+ this.logger.info(`Opening dataply file: ${file}`);
9946
10244
  let fileHandle;
9947
10245
  if (options.pageCacheCapacity < 100) {
9948
10246
  throw new Error("Page cache capacity must be at least 100");
@@ -9978,6 +10276,7 @@ var require_cjs = __commonJS({
9978
10276
  * If not called, the dataply instance cannot be used.
9979
10277
  */
9980
10278
  async init() {
10279
+ this.logger.info("Initializing DataplyAPI");
9981
10280
  if (this.initialized) {
9982
10281
  return;
9983
10282
  }
@@ -9997,6 +10296,7 @@ var require_cjs = __commonJS({
9997
10296
  * @returns Transaction object
9998
10297
  */
9999
10298
  createTransaction() {
10299
+ this.logger.debug(`Creating transaction: ${this.txIdCounter + 1}`);
10000
10300
  return new Transaction4(
10001
10301
  ++this.txIdCounter,
10002
10302
  this.txContext,
@@ -10021,6 +10321,7 @@ var require_cjs = __commonJS({
10021
10321
  * @returns A release function
10022
10322
  */
10023
10323
  acquireWriteLock() {
10324
+ this.logger.debug("Acquiring write lock");
10024
10325
  const previous = this.writeQueue;
10025
10326
  let release;
10026
10327
  this.writeQueue = new Promise((resolve) => {
@@ -10038,6 +10339,7 @@ var require_cjs = __commonJS({
10038
10339
  * @returns The result of the callback.
10039
10340
  */
10040
10341
  async runWithDefaultWrite(callback, tx) {
10342
+ this.logger.debug("Running with default write transaction");
10041
10343
  if (!tx) {
10042
10344
  const release = await this.acquireWriteLock();
10043
10345
  const internalTx = this.createTransaction();
@@ -10061,6 +10363,7 @@ var require_cjs = __commonJS({
10061
10363
  return result;
10062
10364
  }
10063
10365
  async runWithDefault(callback, tx) {
10366
+ this.logger.debug("Running with default transaction");
10064
10367
  const isInternalTx = !tx;
10065
10368
  if (!tx) {
10066
10369
  tx = this.createTransaction();
@@ -10088,6 +10391,7 @@ var require_cjs = __commonJS({
10088
10391
  * @returns An AsyncGenerator that yields values from the callback.
10089
10392
  */
10090
10393
  async *streamWithDefault(callback, tx) {
10394
+ this.logger.debug("Streaming with default transaction");
10091
10395
  const isInternalTx = !tx;
10092
10396
  if (!tx) {
10093
10397
  tx = this.createTransaction();
@@ -10115,6 +10419,7 @@ var require_cjs = __commonJS({
10115
10419
  * @returns Metadata of the dataply.
10116
10420
  */
10117
10421
  async getMetadata(tx) {
10422
+ this.logger.debug("Getting metadata");
10118
10423
  if (!this.initialized) {
10119
10424
  throw new Error("Dataply instance is not initialized");
10120
10425
  }
@@ -10128,6 +10433,7 @@ var require_cjs = __commonJS({
10128
10433
  * @returns PK of the added data
10129
10434
  */
10130
10435
  async insert(data, incrementRowCount, tx) {
10436
+ this.logger.debug("Inserting data");
10131
10437
  if (!this.initialized) {
10132
10438
  throw new Error("Dataply instance is not initialized");
10133
10439
  }
@@ -10148,6 +10454,7 @@ var require_cjs = __commonJS({
10148
10454
  * @returns PK of the added data
10149
10455
  */
10150
10456
  async insertAsOverflow(data, incrementRowCount, tx) {
10457
+ this.logger.debug("Inserting data as overflow");
10151
10458
  if (!this.initialized) {
10152
10459
  throw new Error("Dataply instance is not initialized");
10153
10460
  }
@@ -10169,6 +10476,7 @@ var require_cjs = __commonJS({
10169
10476
  * @returns Array of PKs of the added data
10170
10477
  */
10171
10478
  async insertBatch(dataList, incrementRowCount, tx) {
10479
+ this.logger.debug(`Inserting batch data: ${dataList.length} items`);
10172
10480
  if (!this.initialized) {
10173
10481
  throw new Error("Dataply instance is not initialized");
10174
10482
  }
@@ -10187,6 +10495,7 @@ var require_cjs = __commonJS({
10187
10495
  * @param tx Transaction
10188
10496
  */
10189
10497
  async update(pk, data, tx) {
10498
+ this.logger.debug(`Updating data for PK: ${pk}`);
10190
10499
  if (!this.initialized) {
10191
10500
  throw new Error("Dataply instance is not initialized");
10192
10501
  }
@@ -10204,6 +10513,7 @@ var require_cjs = __commonJS({
10204
10513
  * @param tx Transaction
10205
10514
  */
10206
10515
  async delete(pk, decrementRowCount, tx) {
10516
+ this.logger.debug(`Deleting data for PK: ${pk}`);
10207
10517
  if (!this.initialized) {
10208
10518
  throw new Error("Dataply instance is not initialized");
10209
10519
  }
@@ -10213,6 +10523,7 @@ var require_cjs = __commonJS({
10213
10523
  }, tx);
10214
10524
  }
10215
10525
  async select(pk, asRaw = false, tx) {
10526
+ this.logger.debug(`Selecting data for PK: ${pk}`);
10216
10527
  if (!this.initialized) {
10217
10528
  throw new Error("Dataply instance is not initialized");
10218
10529
  }
@@ -10224,6 +10535,7 @@ var require_cjs = __commonJS({
10224
10535
  }, tx);
10225
10536
  }
10226
10537
  async selectMany(pks, asRaw = false, tx) {
10538
+ this.logger.debug(`Selecting many data: ${pks.length} keys`);
10227
10539
  if (!this.initialized) {
10228
10540
  throw new Error("Dataply instance is not initialized");
10229
10541
  }
@@ -10240,6 +10552,7 @@ var require_cjs = __commonJS({
10240
10552
  * Closes the dataply file.
10241
10553
  */
10242
10554
  async close() {
10555
+ this.logger.info("Closing DataplyAPI");
10243
10556
  if (!this.initialized) {
10244
10557
  throw new Error("Dataply instance is not initialized");
10245
10558
  }
@@ -10860,9 +11173,10 @@ var BinaryHeap = class {
10860
11173
 
10861
11174
  // src/core/QueryManager.ts
10862
11175
  var QueryManager = class {
10863
- constructor(api, optimizer) {
11176
+ constructor(api, optimizer, logger) {
10864
11177
  this.api = api;
10865
11178
  this.optimizer = optimizer;
11179
+ this.logger = logger;
10866
11180
  }
10867
11181
  operatorConverters = {
10868
11182
  equal: "primaryEqual",
@@ -11174,18 +11488,23 @@ var QueryManager = class {
11174
11488
  async countDocuments(query, tx) {
11175
11489
  return this.api.runWithDefault(async (tx2) => {
11176
11490
  const pks = await this.getKeys(query);
11491
+ this.logger.debug(`Counted ${pks.length} documents matching query`);
11177
11492
  return pks.length;
11178
11493
  }, tx);
11179
11494
  }
11180
11495
  selectDocuments(query, options = {}, tx) {
11181
11496
  for (const field of Object.keys(query)) {
11182
11497
  if (!this.api.indexedFields.has(field)) {
11183
- throw new Error(`Query field "${field}" is not indexed. Available indexed fields: ${Array.from(this.api.indexedFields).join(", ")}`);
11498
+ const errorMsg = `Query field "${field}" is not indexed. Available indexed fields: ${Array.from(this.api.indexedFields).join(", ")}`;
11499
+ this.logger.error(errorMsg);
11500
+ throw new Error(errorMsg);
11184
11501
  }
11185
11502
  }
11186
11503
  const orderBy = options.orderBy;
11187
11504
  if (orderBy !== void 0 && !this.api.indexedFields.has(orderBy)) {
11188
- throw new Error(`orderBy field "${orderBy}" is not indexed. Available indexed fields: ${Array.from(this.api.indexedFields).join(", ")}`);
11505
+ const errorMsg = `orderBy field "${orderBy}" is not indexed. Available indexed fields: ${Array.from(this.api.indexedFields).join(", ")}`;
11506
+ this.logger.error(errorMsg);
11507
+ throw new Error(errorMsg);
11189
11508
  }
11190
11509
  const {
11191
11510
  limit = Infinity,
@@ -11282,6 +11601,7 @@ var QueryManager = class {
11282
11601
  others,
11283
11602
  tx2
11284
11603
  )) {
11604
+ self.logger.debug(`Yielding sorted document: ${doc._id}`);
11285
11605
  if (skippedCount < offset) {
11286
11606
  skippedCount++;
11287
11607
  continue;
@@ -11381,8 +11701,9 @@ var DocumentSerializeStrategyAsync = class extends import_dataply2.SerializeStra
11381
11701
 
11382
11702
  // src/core/IndexManager.ts
11383
11703
  var IndexManager = class {
11384
- constructor(api) {
11704
+ constructor(api, logger) {
11385
11705
  this.api = api;
11706
+ this.logger = logger;
11386
11707
  this.trees = /* @__PURE__ */ new Map();
11387
11708
  this.indexedFields = /* @__PURE__ */ new Set(["_id"]);
11388
11709
  }
@@ -11458,6 +11779,7 @@ var IndexManager = class {
11458
11779
  this.fieldToIndices.get(field).push(indexName);
11459
11780
  }
11460
11781
  }
11782
+ const perIndexCapacity = Math.floor(this.api.options.pageCacheCapacity / Object.keys(this.api.indices).length);
11461
11783
  for (const indexName of targetIndices.keys()) {
11462
11784
  if (metadata.indices[indexName]) {
11463
11785
  const tree = new import_dataply3.BPTreeAsync(
@@ -11467,7 +11789,10 @@ var IndexManager = class {
11467
11789
  this.api.txContext,
11468
11790
  indexName
11469
11791
  ),
11470
- this.api.comparator
11792
+ this.api.comparator,
11793
+ {
11794
+ capacity: perIndexCapacity
11795
+ }
11471
11796
  );
11472
11797
  await tree.init();
11473
11798
  this.trees.set(indexName, tree);
@@ -11480,6 +11805,7 @@ var IndexManager = class {
11480
11805
  * Register an index. If called before init(), queues it.
11481
11806
  */
11482
11807
  async registerIndex(name, option, tx) {
11808
+ this.logger.debug(`Registering index "${name}" (type: ${option.type})`);
11483
11809
  if (!this.api.isDocInitialized) {
11484
11810
  this.pendingCreateIndices.set(name, option);
11485
11811
  return;
@@ -11509,6 +11835,7 @@ var IndexManager = class {
11509
11835
  }
11510
11836
  this.fieldToIndices.get(field).push(name);
11511
11837
  }
11838
+ const perIndexCapacity = Math.floor(this.api.options.pageCacheCapacity / Object.keys(this.api.indices).length);
11512
11839
  const tree = new import_dataply3.BPTreeAsync(
11513
11840
  new DocumentSerializeStrategyAsync(
11514
11841
  this.api.rowTableEngine.order,
@@ -11516,7 +11843,10 @@ var IndexManager = class {
11516
11843
  this.api.txContext,
11517
11844
  name
11518
11845
  ),
11519
- this.api.comparator
11846
+ this.api.comparator,
11847
+ {
11848
+ capacity: perIndexCapacity
11849
+ }
11520
11850
  );
11521
11851
  await tree.init();
11522
11852
  this.trees.set(name, tree);
@@ -11570,6 +11900,7 @@ var IndexManager = class {
11570
11900
  * Backfill indices for newly created indices after data was inserted.
11571
11901
  */
11572
11902
  async backfillIndices(tx) {
11903
+ this.logger.debug(`Starting backfill for fields: ${this.pendingBackfillFields.join(", ")}`);
11573
11904
  return this.api.runWithDefaultWrite(async (tx2) => {
11574
11905
  if (this.pendingBackfillFields.length === 0) {
11575
11906
  return 0;
@@ -11579,16 +11910,14 @@ var IndexManager = class {
11579
11910
  if (metadata.lastId === 0) {
11580
11911
  return 0;
11581
11912
  }
11582
- let indexTxMap = {};
11913
+ const bulkData = {};
11583
11914
  for (const indexName of backfillTargets) {
11584
11915
  const tree = this.trees.get(indexName);
11585
11916
  if (tree && indexName !== "_id") {
11586
- indexTxMap[indexName] = await tree.createTransaction();
11917
+ bulkData[indexName] = [];
11587
11918
  }
11588
11919
  }
11589
11920
  let backfilledCount = 0;
11590
- let chunkCount = 0;
11591
- const CHUNK_SIZE = 1e3;
11592
11921
  const idTree = this.trees.get("_id");
11593
11922
  if (!idTree) {
11594
11923
  throw new Error("ID tree not found");
@@ -11602,63 +11931,45 @@ var IndexManager = class {
11602
11931
  const flatDoc = this.api.flattenDocument(doc);
11603
11932
  for (let i = 0, len = backfillTargets.length; i < len; i++) {
11604
11933
  const indexName = backfillTargets[i];
11605
- if (!(indexName in indexTxMap)) continue;
11934
+ if (!(indexName in bulkData)) continue;
11606
11935
  const config = this.registeredIndices.get(indexName);
11607
11936
  if (!config) continue;
11608
- const btx = indexTxMap[indexName];
11609
11937
  if (config.type === "fts") {
11610
11938
  const primaryField = this.getPrimaryField(config);
11611
11939
  const v2 = flatDoc[primaryField];
11612
11940
  if (v2 === void 0 || typeof v2 !== "string") continue;
11613
11941
  const ftsConfig = this.getFtsConfig(config);
11614
11942
  const tokens = ftsConfig ? tokenize(v2, ftsConfig) : [v2];
11615
- const batchInsertData = [];
11616
- for (let i2 = 0, len2 = tokens.length; i2 < len2; i2++) {
11617
- const token = tokens[i2];
11943
+ for (let j = 0, tLen = tokens.length; j < tLen; j++) {
11944
+ const token = tokens[j];
11618
11945
  const keyToInsert = this.getTokenKey(k2, token);
11619
11946
  const entry = { k: k2, v: token };
11620
- batchInsertData.push([keyToInsert, entry]);
11947
+ bulkData[indexName].push([keyToInsert, entry]);
11621
11948
  }
11622
- await btx.batchInsert(batchInsertData);
11623
11949
  } else {
11624
11950
  const indexVal = this.getIndexValue(config, flatDoc);
11625
11951
  if (indexVal === void 0) continue;
11626
11952
  const entry = { k: k2, v: indexVal };
11627
- const batchInsertData = [[k2, entry]];
11628
- await btx.batchInsert(batchInsertData);
11953
+ bulkData[indexName].push([k2, entry]);
11629
11954
  }
11630
11955
  }
11631
11956
  backfilledCount++;
11632
- chunkCount++;
11633
- if (chunkCount >= CHUNK_SIZE) {
11634
- try {
11635
- for (const btx of Object.values(indexTxMap)) {
11636
- await btx.commit();
11637
- }
11638
- } catch (err) {
11639
- for (const btx of Object.values(indexTxMap)) {
11640
- await btx.rollback();
11641
- }
11642
- throw err;
11643
- }
11644
- for (const indexName of backfillTargets) {
11645
- const tree = this.trees.get(indexName);
11646
- if (tree && indexName !== "_id") {
11647
- indexTxMap[indexName] = await tree.createTransaction();
11648
- }
11649
- }
11650
- chunkCount = 0;
11651
- }
11652
11957
  }
11653
- if (chunkCount > 0) {
11958
+ for (const indexName of backfillTargets) {
11959
+ const tree = this.trees.get(indexName);
11960
+ if (!tree || indexName === "_id") continue;
11961
+ const entries = bulkData[indexName];
11962
+ if (!entries || entries.length === 0) continue;
11963
+ const treeTx = await tree.createTransaction();
11654
11964
  try {
11655
- for (const btx of Object.values(indexTxMap)) {
11656
- await btx.commit();
11965
+ await treeTx.bulkLoad(entries);
11966
+ const res = await treeTx.commit();
11967
+ if (!res.success) {
11968
+ await treeTx.rollback();
11969
+ throw res.error;
11657
11970
  }
11658
11971
  } catch (err) {
11659
- for (const btx of Object.values(indexTxMap)) {
11660
- await btx.rollback();
11661
- }
11972
+ await treeTx.rollback();
11662
11973
  throw err;
11663
11974
  }
11664
11975
  }
@@ -11666,6 +11977,95 @@ var IndexManager = class {
11666
11977
  return backfilledCount;
11667
11978
  }, tx);
11668
11979
  }
11980
+ /**
11981
+ * Rebuild specified indices by clearing existing tree data and rebuilding via bulkLoad.
11982
+ * If no index names are provided, all indices (except _id) are rebuilt.
11983
+ */
11984
+ async rebuildIndices(indexNames, tx) {
11985
+ const targets = indexNames ?? [...this.registeredIndices.keys()].filter((n) => n !== "_id");
11986
+ for (const name of targets) {
11987
+ if (name === "_id") {
11988
+ throw new Error('Cannot rebuild the "_id" index.');
11989
+ }
11990
+ if (!this.registeredIndices.has(name)) {
11991
+ throw new Error(`Index "${name}" does not exist.`);
11992
+ }
11993
+ }
11994
+ if (targets.length === 0) return 0;
11995
+ this.logger.debug(`Starting rebuild for indices: ${targets.join(", ")}`);
11996
+ return this.api.runWithDefaultWrite(async (tx2) => {
11997
+ const metadata = await this.api.getDocumentInnerMetadata(tx2);
11998
+ if (metadata.lastId === 0) return 0;
11999
+ const bulkData = {};
12000
+ for (const indexName of targets) {
12001
+ bulkData[indexName] = [];
12002
+ }
12003
+ const idTree = this.trees.get("_id");
12004
+ if (!idTree) throw new Error("ID tree not found");
12005
+ let docCount = 0;
12006
+ const stream = idTree.whereStream({ primaryGte: { v: 0 } });
12007
+ for await (const [k2] of stream) {
12008
+ const doc = await this.api.getDocument(k2, tx2);
12009
+ if (!doc) continue;
12010
+ const flatDoc = this.api.flattenDocument(doc);
12011
+ for (let i = 0, len = targets.length; i < len; i++) {
12012
+ const indexName = targets[i];
12013
+ const config = this.registeredIndices.get(indexName);
12014
+ if (!config) continue;
12015
+ if (config.type === "fts") {
12016
+ const primaryField = this.getPrimaryField(config);
12017
+ const v2 = flatDoc[primaryField];
12018
+ if (v2 === void 0 || typeof v2 !== "string") continue;
12019
+ const ftsConfig = this.getFtsConfig(config);
12020
+ const tokens = ftsConfig ? tokenize(v2, ftsConfig) : [v2];
12021
+ for (let j = 0, tLen = tokens.length; j < tLen; j++) {
12022
+ const token = tokens[j];
12023
+ const keyToInsert = this.getTokenKey(k2, token);
12024
+ bulkData[indexName].push([keyToInsert, { k: k2, v: token }]);
12025
+ }
12026
+ } else {
12027
+ const indexVal = this.getIndexValue(config, flatDoc);
12028
+ if (indexVal === void 0) continue;
12029
+ bulkData[indexName].push([k2, { k: k2, v: indexVal }]);
12030
+ }
12031
+ }
12032
+ docCount++;
12033
+ }
12034
+ const perIndexCapacity = Math.floor(this.api.options.pageCacheCapacity / Object.keys(this.api.indices).length);
12035
+ for (const indexName of targets) {
12036
+ metadata.indices[indexName][0] = -1;
12037
+ await this.api.updateDocumentInnerMetadata(metadata, tx2);
12038
+ const tree = new import_dataply3.BPTreeAsync(
12039
+ new DocumentSerializeStrategyAsync(
12040
+ this.api.rowTableEngine.order,
12041
+ this.api,
12042
+ this.api.txContext,
12043
+ indexName
12044
+ ),
12045
+ this.api.comparator,
12046
+ { capacity: perIndexCapacity }
12047
+ );
12048
+ await tree.init();
12049
+ this.trees.set(indexName, tree);
12050
+ const entries = bulkData[indexName];
12051
+ if (entries.length > 0) {
12052
+ const treeTx = await tree.createTransaction();
12053
+ try {
12054
+ await treeTx.bulkLoad(entries);
12055
+ const res = await treeTx.commit();
12056
+ if (!res.success) {
12057
+ await treeTx.rollback();
12058
+ throw res.error;
12059
+ }
12060
+ } catch (err) {
12061
+ await treeTx.rollback();
12062
+ throw err;
12063
+ }
12064
+ }
12065
+ }
12066
+ return docCount;
12067
+ }, tx);
12068
+ }
11669
12069
  /**
11670
12070
  * Convert CreateIndexOption to IndexMetaConfig for metadata storage.
11671
12071
  */
@@ -11835,8 +12235,17 @@ var DeadlineChunker = class {
11835
12235
 
11836
12236
  // src/core/MutationManager.ts
11837
12237
  var MutationManager = class {
11838
- constructor(api) {
12238
+ constructor(api, logger) {
11839
12239
  this.api = api;
12240
+ this.logger = logger;
12241
+ }
12242
+ async isTreeEmpty(tree) {
12243
+ try {
12244
+ const root = await tree.getNode(tree.rootId);
12245
+ return root.leaf && root.values.length === 0;
12246
+ } catch {
12247
+ return true;
12248
+ }
11840
12249
  }
11841
12250
  async insertDocumentInternal(document, tx) {
11842
12251
  const metadata = await this.api.getDocumentInnerMetadata(tx);
@@ -11937,20 +12346,29 @@ var MutationManager = class {
11937
12346
  batchInsertData.push([item.pk, { k: item.pk, v: indexVal }]);
11938
12347
  }
11939
12348
  }
11940
- const initMaxSize = 5e4;
11941
- const initChunkSize = Math.min(
11942
- initMaxSize,
11943
- Math.max(initMaxSize, Math.floor(batchInsertData.length / 100 * 5))
11944
- );
11945
- const chunker = new DeadlineChunker(initChunkSize);
11946
- await chunker.processInChunks(batchInsertData, async (chunk) => {
11947
- const [error] = await catchPromise(treeTx.batchInsert(chunk));
12349
+ const isEmptyTree = await this.isTreeEmpty(tree);
12350
+ if (isEmptyTree) {
12351
+ const [error] = await catchPromise(treeTx.bulkLoad(batchInsertData));
11948
12352
  if (error) {
11949
12353
  throw error;
11950
12354
  }
11951
- });
12355
+ } else {
12356
+ const initMaxSize = 5e4;
12357
+ const initChunkSize = Math.min(
12358
+ initMaxSize,
12359
+ Math.max(initMaxSize, Math.floor(batchInsertData.length / 100 * 5))
12360
+ );
12361
+ const chunker = new DeadlineChunker(initChunkSize);
12362
+ await chunker.processInChunks(batchInsertData, async (chunk) => {
12363
+ const [error] = await catchPromise(treeTx.batchInsert(chunk));
12364
+ if (error) {
12365
+ throw error;
12366
+ }
12367
+ });
12368
+ }
11952
12369
  const res = await treeTx.commit();
11953
12370
  if (!res.success) {
12371
+ await treeTx.rollback();
11954
12372
  throw res.error;
11955
12373
  }
11956
12374
  }
@@ -12087,8 +12505,9 @@ var MutationManager = class {
12087
12505
 
12088
12506
  // src/core/MetadataManager.ts
12089
12507
  var MetadataManager = class {
12090
- constructor(api) {
12508
+ constructor(api, logger) {
12091
12509
  this.api = api;
12510
+ this.logger = logger;
12092
12511
  }
12093
12512
  async getDocumentMetadata(tx) {
12094
12513
  const metadata = await this.api.getMetadata(tx);
@@ -12116,6 +12535,7 @@ var MetadataManager = class {
12116
12535
  return JSON.parse(row);
12117
12536
  }
12118
12537
  async updateDocumentInnerMetadata(metadata, tx) {
12538
+ this.logger.debug(`Updating document inner metadata: version ${metadata.version}`);
12119
12539
  await this.api.update(1, JSON.stringify(metadata), tx);
12120
12540
  }
12121
12541
  async migration(version, callback, tx) {
@@ -12125,6 +12545,7 @@ var MetadataManager = class {
12125
12545
  if (currentVersion < version) {
12126
12546
  await callback(tx2);
12127
12547
  const freshMetadata = await this.getDocumentInnerMetadata(tx2);
12548
+ this.logger.info(`Migration applied successfully to schemeVersion ${version}`);
12128
12549
  freshMetadata.schemeVersion = version;
12129
12550
  freshMetadata.updatedAt = Date.now();
12130
12551
  await this.updateDocumentInnerMetadata(freshMetadata, tx2);
@@ -12961,9 +13382,10 @@ var BuiltinAnalysisProviders = [
12961
13382
 
12962
13383
  // src/core/AnalysisManager.ts
12963
13384
  var AnalysisManager = class {
12964
- constructor(api, schedule, sampleSize) {
13385
+ constructor(api, schedule, sampleSize, logger) {
12965
13386
  this.api = api;
12966
13387
  this.sampleSize = sampleSize;
13388
+ this.logger = logger;
12967
13389
  this.cron = new E(schedule, async () => {
12968
13390
  if (this.flushing) return;
12969
13391
  await this.api.flushAnalysis();
@@ -13092,6 +13514,7 @@ var AnalysisManager = class {
13092
13514
  if (this.flushing) {
13093
13515
  throw new Error("Cannot flush analysis while analysis is already flushing.");
13094
13516
  }
13517
+ this.logger.debug("Starting analysis data flush");
13095
13518
  this.flushing = true;
13096
13519
  for (const [name, provider] of this.providers) {
13097
13520
  if (provider instanceof IntervalAnalysisProvider) {
@@ -13099,6 +13522,7 @@ var AnalysisManager = class {
13099
13522
  }
13100
13523
  }
13101
13524
  this.flushing = false;
13525
+ this.logger.debug("Finished analysis data flush");
13102
13526
  }
13103
13527
  /**
13104
13528
  * Get the analysis header row.
@@ -13226,32 +13650,39 @@ var DocumentDataplyAPI = class extends import_dataply4.DataplyAPI {
13226
13650
  constructor(file, options) {
13227
13651
  super(file, options);
13228
13652
  this.optimizer = new Optimizer(this);
13229
- this.queryManager = new QueryManager(this, this.optimizer);
13230
- this.indexManager = new IndexManager(this);
13231
- this.mutationManager = new MutationManager(this);
13232
- this.metadataManager = new MetadataManager(this);
13653
+ this.queryManager = new QueryManager(this, this.optimizer, this.loggerManager.create("document-dataply:query"));
13654
+ this.indexManager = new IndexManager(this, this.loggerManager.create("document-dataply:index"));
13655
+ this.mutationManager = new MutationManager(this, this.loggerManager.create("document-dataply:mutation"));
13656
+ this.metadataManager = new MetadataManager(this, this.loggerManager.create("document-dataply:metadata"));
13233
13657
  this.documentFormatter = new DocumentFormatter();
13234
13658
  this.analysisManager = new AnalysisManager(
13235
13659
  this,
13236
13660
  options.analysisSchedule ?? "* */1 * * *",
13237
- options.analysisSampleSize ?? 1e3
13661
+ options.analysisSampleSize ?? 1e3,
13662
+ this.loggerManager.create("document-dataply:analysis")
13238
13663
  );
13239
13664
  this.hook.onceAfter("close", async () => {
13665
+ this.logger.info("DocumentDataplyAPI closing");
13240
13666
  this.analysisManager.close();
13241
13667
  });
13242
13668
  this.hook.onceAfter("init", async (tx, isNewlyCreated) => {
13669
+ this.logger.info(`Initializing document database. New creation: ${isNewlyCreated}`);
13243
13670
  if (isNewlyCreated) {
13244
13671
  await this.initializeDocumentFile(tx);
13672
+ this.logger.debug("Initialized document file format");
13245
13673
  }
13246
13674
  if (!await this.verifyDocumentFile(tx)) {
13675
+ this.logger.error("Document metadata verification failed");
13247
13676
  throw new Error("Document metadata verification failed");
13248
13677
  }
13249
13678
  const metadata = await this.getDocumentInnerMetadata(tx);
13250
13679
  await this.indexManager.initializeIndices(metadata, isNewlyCreated, tx);
13680
+ this.logger.debug(`Indices initialized. Total indices: ${Object.keys(metadata.indices).length}`);
13251
13681
  this.analysisManager.registerBuiltinProviders();
13252
13682
  await this.analysisManager.initializeProviders(tx);
13253
13683
  this.analysisManager.triggerCron();
13254
13684
  this._initialized = true;
13685
+ this.logger.info("Document database fully initialized");
13255
13686
  return tx;
13256
13687
  });
13257
13688
  }
@@ -13309,6 +13740,16 @@ var DocumentDataplyAPI = class extends import_dataply4.DataplyAPI {
13309
13740
  async backfillIndices(tx) {
13310
13741
  return this.indexManager.backfillIndices(tx);
13311
13742
  }
13743
+ /**
13744
+ * Rebuild specified indices by clearing existing tree data and rebuilding via bulkLoad.
13745
+ * If no index names are provided, all indices (except _id) are rebuilt.
13746
+ * @param indexNames Optional array of index names to rebuild
13747
+ * @param tx Optional transaction
13748
+ * @returns The number of documents processed
13749
+ */
13750
+ async rebuildIndices(indexNames, tx) {
13751
+ return this.indexManager.rebuildIndices(indexNames, tx);
13752
+ }
13312
13753
  /**
13313
13754
  * Flush all interval analysis providers, forcing statistics to be recalculated.
13314
13755
  * Call this after bulk inserts or periodically to keep statistics fresh.
@@ -13533,6 +13974,16 @@ var DocumentDataply = class _DocumentDataply {
13533
13974
  await this.api.dropIndex(name, tx);
13534
13975
  return this;
13535
13976
  }
13977
+ /**
13978
+ * Rebuild specified indices by clearing existing data and reconstructing via bulkLoad.
13979
+ * If no index names are provided, rebuilds all indices except '_id'.
13980
+ * @param indexNames Optional array of index names to rebuild
13981
+ * @param tx Optional transaction
13982
+ * @returns The number of documents processed
13983
+ */
13984
+ async rebuildIndices(indexNames, tx) {
13985
+ return this.api.rebuildIndices(indexNames, tx);
13986
+ }
13536
13987
  /**
13537
13988
  * Initialize the document database
13538
13989
  */