@sovereignbase/convergent-replicated-list 1.3.5 → 1.3.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -112,9 +112,19 @@ function rebuildLiveProjection(crListReplica) {
112
112
  while (stack.length > 0) {
113
113
  const frame = stack[stack.length - 1];
114
114
  if (!frame.siblings) {
115
- frame.siblings = crListReplica.childrenMap.get(
115
+ const childrenMapSibs = crListReplica.childrenMap.get(
116
116
  frame.predecessorIdentifier
117
117
  );
118
+ const runNextEntry = crListReplica.runNext?.get(
119
+ frame.predecessorIdentifier
120
+ );
121
+ if (childrenMapSibs && runNextEntry) {
122
+ frame.siblings = [...childrenMapSibs, runNextEntry];
123
+ } else if (runNextEntry) {
124
+ frame.siblings = [runNextEntry];
125
+ } else {
126
+ frame.siblings = childrenMapSibs;
127
+ }
118
128
  if (!frame.siblings) {
119
129
  void stack.pop();
120
130
  continue;
@@ -198,9 +208,21 @@ function attachEntryToIndexes(crListReplica, linkedListEntry, deltaBuf) {
198
208
  function detachEntryFromIndexes(crListReplica, linkedListEntry) {
199
209
  void crListReplica.parentMap.delete(linkedListEntry.uuidv7);
200
210
  const siblings = crListReplica.childrenMap.get(linkedListEntry.predecessor);
201
- if (!siblings) return;
202
- const index = siblings.indexOf(linkedListEntry);
203
- if (index !== -1) void siblings.splice(index, 1);
211
+ if (siblings) {
212
+ const index = siblings.indexOf(linkedListEntry);
213
+ if (index !== -1) void siblings.splice(index, 1);
214
+ }
215
+ if (crListReplica.runNext) {
216
+ if (crListReplica.runNext.get(linkedListEntry.predecessor) === linkedListEntry)
217
+ crListReplica.runNext.delete(linkedListEntry.predecessor);
218
+ const runSuccessor = crListReplica.runNext.get(linkedListEntry.uuidv7);
219
+ if (runSuccessor) {
220
+ crListReplica.runNext.delete(linkedListEntry.uuidv7);
221
+ const sibs = crListReplica.childrenMap.get(linkedListEntry.uuidv7);
222
+ if (sibs) sibs.push(runSuccessor);
223
+ else crListReplica.childrenMap.set(linkedListEntry.uuidv7, [runSuccessor]);
224
+ }
225
+ }
204
226
  }
205
227
 
206
228
  // src/.helpers/deleteLiveEntry/index.ts
@@ -363,6 +385,13 @@ function trySpliceSiblingInsert(crListReplica, insertedEntries, reparentedEntrie
363
385
  return true;
364
386
  }
365
387
 
388
+ // src/.helpers/deriveRunUuid/index.ts
389
+ function deriveRunUuid(startUuid, offset) {
390
+ const randA = parseInt(startUuid.slice(15, 18), 16);
391
+ const newRandA = randA + offset & 4095;
392
+ return startUuid.slice(0, 15) + newRandA.toString(16).padStart(3, "0") + startUuid.slice(18);
393
+ }
394
+
366
395
  // src/core/crud/create/index.ts
367
396
  import { isUuidV7 as isUuidV72, prototype } from "@sovereignbase/utils";
368
397
  function __create(snapshot) {
@@ -436,6 +465,8 @@ function __update(listIndex, listValues, crListReplica, mode) {
436
465
  if (listValues.length === 0) return false;
437
466
  const change = {};
438
467
  const delta = { values: [], tombstones: [] };
468
+ let displacedEntry;
469
+ const batchEntries = [];
439
470
  for (const listValue of listValues) {
440
471
  const v7 = uuidv7();
441
472
  const linkedListEntry = {
@@ -484,15 +515,27 @@ function __update(listIndex, listValues, crListReplica, mode) {
484
515
  linkedListEntry,
485
516
  entryToOverwrite.next
486
517
  );
487
- if (entryToOverwrite.next) {
488
- if (entryToOverwrite.next.predecessor === entryToOverwrite.uuidv7) {
489
- void moveEntryToPredecessor(
490
- crListReplica,
491
- entryToOverwrite.next,
492
- linkedListEntry.uuidv7,
493
- delta
494
- );
518
+ if (entryToOverwrite.next?.predecessor === entryToOverwrite.uuidv7) {
519
+ const overwriteNext = entryToOverwrite.next;
520
+ const owSibs = crListReplica.childrenMap.get(
521
+ overwriteNext.predecessor
522
+ );
523
+ if (owSibs) {
524
+ const i = owSibs.indexOf(overwriteNext);
525
+ if (i !== -1) owSibs.splice(i, 1);
495
526
  }
527
+ overwriteNext.predecessor = linkedListEntry.uuidv7;
528
+ const newSibs = crListReplica.childrenMap.get(linkedListEntry.uuidv7);
529
+ if (newSibs) newSibs.push(overwriteNext);
530
+ else
531
+ crListReplica.childrenMap.set(linkedListEntry.uuidv7, [
532
+ overwriteNext
533
+ ]);
534
+ delta.values?.push({
535
+ uuidv7: overwriteNext.uuidv7,
536
+ value: overwriteNext.value,
537
+ predecessor: overwriteNext.predecessor
538
+ });
496
539
  }
497
540
  void attachEntryToIndexes(crListReplica, linkedListEntry, delta);
498
541
  void crListReplica.tombstones.add(entryToOverwrite.uuidv7);
@@ -526,20 +569,24 @@ function __update(listIndex, listValues, crListReplica, mode) {
526
569
  linkedListEntry.index = actualIndex + 1;
527
570
  linkedListEntry.predecessor = crListReplica.cursor.uuidv7;
528
571
  void linkEntryBetween(crListReplica.cursor, linkedListEntry, next);
529
- if (next) {
530
- if (next.predecessor === crListReplica.cursor.uuidv7) {
531
- void moveEntryToPredecessor(
532
- crListReplica,
533
- next,
534
- linkedListEntry.uuidv7,
535
- delta
536
- );
572
+ if (next && next.predecessor === crListReplica.cursor.uuidv7) {
573
+ if (!displacedEntry) {
574
+ displacedEntry = next;
575
+ const sibs = crListReplica.childrenMap.get(next.predecessor);
576
+ if (sibs) {
577
+ const i = sibs.indexOf(next);
578
+ if (i !== -1) sibs.splice(i, 1);
579
+ }
537
580
  }
581
+ displacedEntry.predecessor = linkedListEntry.uuidv7;
538
582
  }
539
583
  void attachEntryToIndexes(crListReplica, linkedListEntry, delta);
540
584
  crListReplica.cursor = linkedListEntry;
541
585
  crListReplica.cursorIndex = linkedListEntry.index;
542
- if (next) crListReplica.index = /* @__PURE__ */ new Map();
586
+ if (next) {
587
+ if (crListReplica.index) crListReplica.index.clear();
588
+ else crListReplica.index = /* @__PURE__ */ new Map();
589
+ }
543
590
  void crListReplica.index?.set(linkedListEntry.index, linkedListEntry);
544
591
  change[linkedListEntry.index] = linkedListEntry.value;
545
592
  break;
@@ -563,17 +610,23 @@ function __update(listIndex, listValues, crListReplica, mode) {
563
610
  linkedListEntry.predecessor = prev?.uuidv7 ?? "\0";
564
611
  void linkEntryBetween(prev, linkedListEntry, crListReplica.cursor);
565
612
  if (crListReplica.cursor.predecessor === linkedListEntry.predecessor) {
566
- void moveEntryToPredecessor(
567
- crListReplica,
568
- crListReplica.cursor,
569
- linkedListEntry.uuidv7,
570
- delta
571
- );
613
+ if (!displacedEntry) {
614
+ displacedEntry = crListReplica.cursor;
615
+ const sibs = crListReplica.childrenMap.get(
616
+ crListReplica.cursor.predecessor
617
+ );
618
+ if (sibs) {
619
+ const i = sibs.indexOf(crListReplica.cursor);
620
+ if (i !== -1) sibs.splice(i, 1);
621
+ }
622
+ }
623
+ displacedEntry.predecessor = linkedListEntry.uuidv7;
572
624
  }
573
625
  void attachEntryToIndexes(crListReplica, linkedListEntry, delta);
574
626
  crListReplica.cursor = linkedListEntry;
575
627
  crListReplica.cursorIndex = actualIndex;
576
- crListReplica.index = /* @__PURE__ */ new Map();
628
+ if (crListReplica.index) crListReplica.index.clear();
629
+ else crListReplica.index = /* @__PURE__ */ new Map();
577
630
  void crListReplica.index?.set(linkedListEntry.index, linkedListEntry);
578
631
  change[actualIndex] = linkedListEntry.value;
579
632
  mode = "after";
@@ -581,9 +634,49 @@ function __update(listIndex, listValues, crListReplica, mode) {
581
634
  break;
582
635
  }
583
636
  }
637
+ batchEntries.push(linkedListEntry);
584
638
  crListReplica.size = crListReplica.parentMap.size;
585
639
  listIndex++;
586
640
  }
641
+ if (batchEntries.length > 1) {
642
+ for (let k = 0; k + 1 < batchEntries.length; k++) {
643
+ const curr = batchEntries[k];
644
+ const next = batchEntries[k + 1];
645
+ if (next.predecessor !== curr.uuidv7) continue;
646
+ crListReplica.childrenMap.delete(curr.uuidv7);
647
+ if (!crListReplica.runNext) crListReplica.runNext = /* @__PURE__ */ new Map();
648
+ crListReplica.runNext.set(curr.uuidv7, next);
649
+ }
650
+ }
651
+ if (batchEntries.length > 1 && delta.values && delta.values.length >= batchEntries.length) {
652
+ const first = batchEntries[0];
653
+ const last = batchEntries[batchEntries.length - 1];
654
+ if (deriveRunUuid(first.uuidv7, batchEntries.length - 1) === last.uuidv7) {
655
+ delta.values.splice(
656
+ delta.values.length - batchEntries.length,
657
+ batchEntries.length,
658
+ {
659
+ uuidv7: first.uuidv7,
660
+ value: first.value,
661
+ predecessor: first.predecessor,
662
+ tail: batchEntries.slice(1).map((e) => e.value)
663
+ }
664
+ );
665
+ }
666
+ }
667
+ if (displacedEntry) {
668
+ const sibs = crListReplica.childrenMap.get(displacedEntry.predecessor);
669
+ if (sibs) sibs.push(displacedEntry);
670
+ else
671
+ crListReplica.childrenMap.set(displacedEntry.predecessor, [
672
+ displacedEntry
673
+ ]);
674
+ delta.values?.push({
675
+ uuidv7: displacedEntry.uuidv7,
676
+ value: displacedEntry.value,
677
+ predecessor: displacedEntry.predecessor
678
+ });
679
+ }
587
680
  return { change, delta };
588
681
  }
589
682
 
@@ -660,9 +753,20 @@ function __merge(crListReplica, crListDelta) {
660
753
  const change = {};
661
754
  let tailTombstoneMovedCursor = false;
662
755
  let needsRelink = false;
663
- if (Object.hasOwn(crListDelta, "values") && Array.isArray(crListDelta.values) && crListDelta.values.length === 1 && (!Object.hasOwn(crListDelta, "tombstones") || Array.isArray(crListDelta.tombstones) && crListDelta.tombstones.length === 0)) {
756
+ const expandedValues = Object.hasOwn(crListDelta, "values") && Array.isArray(crListDelta.values) ? crListDelta.values.flatMap((entry) => {
757
+ if (!entry || !entry.tail || entry.tail.length === 0) return [entry];
758
+ return [
759
+ entry,
760
+ ...entry.tail.map((value, i) => ({
761
+ uuidv7: deriveRunUuid(entry.uuidv7, i + 1),
762
+ value,
763
+ predecessor: deriveRunUuid(entry.uuidv7, i)
764
+ }))
765
+ ];
766
+ }) : void 0;
767
+ if (expandedValues?.length === 1 && (!Object.hasOwn(crListDelta, "tombstones") || Array.isArray(crListDelta.tombstones) && crListDelta.tombstones.length === 0)) {
664
768
  const linkedListEntry = materializeSnapshotEntry(
665
- crListDelta.values[0],
769
+ expandedValues[0],
666
770
  crListReplica
667
771
  );
668
772
  if (!linkedListEntry) return false;
@@ -696,7 +800,7 @@ function __merge(crListReplica, crListDelta) {
696
800
  }
697
801
  }
698
802
  }
699
- if (!Object.hasOwn(crListDelta, "values") || !Array.isArray(crListDelta.values) || crListDelta.values.length === 0 && tailTombstoneMovedCursor) {
803
+ if (!expandedValues || expandedValues.length === 0 && tailTombstoneMovedCursor) {
700
804
  if (newTombsIndices.length === 0) return false;
701
805
  if (newTombsIndices.length === 1 && tailTombstoneMovedCursor) {
702
806
  if (crListReplica.cursor) {
@@ -717,7 +821,7 @@ function __merge(crListReplica, crListDelta) {
717
821
  }
718
822
  return change;
719
823
  }
720
- for (const valueEntry of crListDelta.values) {
824
+ for (const valueEntry of expandedValues) {
721
825
  if (valueEntry === null || valueEntry === void 0) continue;
722
826
  const existingEntry = crListReplica.parentMap.get(valueEntry.uuidv7);
723
827
  if (existingEntry) {
@@ -924,32 +1028,32 @@ var CRList = class {
924
1028
  return this.state.size;
925
1029
  }
926
1030
  /**
927
- * Inserts a value before an index.
1031
+ * Inserts values before an index.
928
1032
  *
929
- * If `beforeIndex` is omitted, the value is inserted at the start of the list.
1033
+ * If `beforeIndex` is omitted, values are inserted at the start of the list.
930
1034
  *
931
- * @param value - The value to insert.
1035
+ * @param values - Values to insert.
932
1036
  * @param beforeIndex - The index to insert before.
933
1037
  */
934
- prepend(value, beforeIndex) {
935
- const result = __update(beforeIndex ?? 0, [value], this.state, "before");
1038
+ prepend(values, beforeIndex) {
1039
+ const result = __update(beforeIndex ?? 0, values, this.state, "before");
936
1040
  if (!result) return;
937
1041
  const { delta, change } = result;
938
1042
  if (delta) void dispatchCRListEvent(this.eventTarget, "delta", delta);
939
1043
  if (change) void dispatchCRListEvent(this.eventTarget, "change", change);
940
1044
  }
941
1045
  /**
942
- * Inserts a value after an index.
1046
+ * Inserts values after an index.
943
1047
  *
944
- * If `afterIndex` is omitted, the value is appended at the end of the list.
1048
+ * If `afterIndex` is omitted, values are appended at the end of the list.
945
1049
  *
946
- * @param value - The value to insert.
1050
+ * @param values - Values to insert.
947
1051
  * @param afterIndex - The index to insert after.
948
1052
  */
949
- append(value, afterIndex) {
1053
+ append(values, afterIndex) {
950
1054
  const result = __update(
951
1055
  afterIndex ?? this.state.size,
952
- [value],
1056
+ values,
953
1057
  this.state,
954
1058
  "after"
955
1059
  );
@@ -958,6 +1062,19 @@ var CRList = class {
958
1062
  if (delta) void dispatchCRListEvent(this.eventTarget, "delta", delta);
959
1063
  if (change) void dispatchCRListEvent(this.eventTarget, "change", change);
960
1064
  }
1065
+ /**
1066
+ * Overwrites entries starting at an index.
1067
+ *
1068
+ * @param index - The index to start overwriting at.
1069
+ * @param values - Values to write.
1070
+ */
1071
+ update(index, values) {
1072
+ const result = __update(index, values, this.state, "overwrite");
1073
+ if (!result) return;
1074
+ const { delta, change } = result;
1075
+ if (delta) void dispatchCRListEvent(this.eventTarget, "delta", delta);
1076
+ if (change) void dispatchCRListEvent(this.eventTarget, "change", change);
1077
+ }
961
1078
  /**
962
1079
  * Removes one or more entries starting at an index.
963
1080
  *