@sovereignbase/convergent-replicated-list 1.3.4 → 1.3.6

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
@@ -294,8 +294,17 @@ function trySpliceReplacement(crListReplica, insertedEntries, reparentedEntries,
294
294
  } else if (crListReplica.childrenMap.get(inserted.uuidv7)?.length) {
295
295
  return false;
296
296
  }
297
- if (predecessor?.next !== next) return false;
298
- if (!predecessor && crListReplica.cursor !== next) return false;
297
+ if (predecessor) {
298
+ if (predecessor.next !== next) return false;
299
+ } else {
300
+ let reachable = 0;
301
+ let current2 = next;
302
+ while (current2) {
303
+ reachable++;
304
+ current2 = current2.next;
305
+ }
306
+ if (reachable !== crListReplica.parentMap.size - 1) return false;
307
+ }
299
308
  const expectedIndex = predecessor ? predecessor.index + 1 : 0;
300
309
  void linkEntryBetween(predecessor, inserted, next);
301
310
  let current = inserted;
@@ -312,6 +321,48 @@ function trySpliceReplacement(crListReplica, insertedEntries, reparentedEntries,
312
321
  return true;
313
322
  }
314
323
 
324
+ // src/.helpers/trySpliceSiblingInsert/index.ts
325
+ function trySpliceSiblingInsert(crListReplica, insertedEntries, reparentedEntries, tombstoneCount) {
326
+ if (tombstoneCount !== 0 || insertedEntries.length !== 1 || reparentedEntries.length !== 0)
327
+ return false;
328
+ const inserted = insertedEntries[0];
329
+ if (inserted.predecessor === "\0") return false;
330
+ if (crListReplica.childrenMap.get(inserted.uuidv7)?.length) return false;
331
+ const predecessor = crListReplica.parentMap.get(inserted.predecessor);
332
+ const siblings = crListReplica.childrenMap.get(inserted.predecessor);
333
+ if (!predecessor || !siblings || siblings.length < 2) return false;
334
+ void siblings.sort((a, b) => a.uuidv7 > b.uuidv7 ? 1 : -1);
335
+ const siblingIndex = siblings.indexOf(inserted);
336
+ if (siblingIndex === -1) return false;
337
+ const lastSibling = siblings[siblings.length - 1];
338
+ if (lastSibling !== inserted && lastSibling.next) return false;
339
+ const previousSibling = siblings[siblingIndex - 1];
340
+ const nextSibling = siblings[siblingIndex + 1];
341
+ if (previousSibling?.uuidv7) {
342
+ if (crListReplica.childrenMap.get(previousSibling.uuidv7)?.length)
343
+ return false;
344
+ if (previousSibling.next !== nextSibling) return false;
345
+ } else if (predecessor.next !== nextSibling) {
346
+ return false;
347
+ }
348
+ const prev = previousSibling ?? predecessor;
349
+ const next = nextSibling;
350
+ if (next && next.prev !== prev) return false;
351
+ void linkEntryBetween(prev, inserted, next);
352
+ let current = inserted;
353
+ let index = prev.index + 1;
354
+ while (current) {
355
+ current.index = index;
356
+ index++;
357
+ current = current.next;
358
+ }
359
+ crListReplica.index = /* @__PURE__ */ new Map([[inserted.index, inserted]]);
360
+ crListReplica.cursor = inserted;
361
+ crListReplica.cursorIndex = inserted.index;
362
+ crListReplica.size = crListReplica.parentMap.size;
363
+ return true;
364
+ }
365
+
315
366
  // src/core/crud/create/index.ts
316
367
  import { isUuidV7 as isUuidV72, prototype } from "@sovereignbase/utils";
317
368
  function __create(snapshot) {
@@ -385,6 +436,7 @@ function __update(listIndex, listValues, crListReplica, mode) {
385
436
  if (listValues.length === 0) return false;
386
437
  const change = {};
387
438
  const delta = { values: [], tombstones: [] };
439
+ let displacedEntry;
388
440
  for (const listValue of listValues) {
389
441
  const v7 = uuidv7();
390
442
  const linkedListEntry = {
@@ -433,15 +485,27 @@ function __update(listIndex, listValues, crListReplica, mode) {
433
485
  linkedListEntry,
434
486
  entryToOverwrite.next
435
487
  );
436
- if (entryToOverwrite.next) {
437
- if (entryToOverwrite.next.predecessor === entryToOverwrite.uuidv7) {
438
- void moveEntryToPredecessor(
439
- crListReplica,
440
- entryToOverwrite.next,
441
- linkedListEntry.uuidv7,
442
- delta
443
- );
488
+ if (entryToOverwrite.next?.predecessor === entryToOverwrite.uuidv7) {
489
+ const overwriteNext = entryToOverwrite.next;
490
+ const owSibs = crListReplica.childrenMap.get(
491
+ overwriteNext.predecessor
492
+ );
493
+ if (owSibs) {
494
+ const i = owSibs.indexOf(overwriteNext);
495
+ if (i !== -1) owSibs.splice(i, 1);
444
496
  }
497
+ overwriteNext.predecessor = linkedListEntry.uuidv7;
498
+ const newSibs = crListReplica.childrenMap.get(linkedListEntry.uuidv7);
499
+ if (newSibs) newSibs.push(overwriteNext);
500
+ else
501
+ crListReplica.childrenMap.set(linkedListEntry.uuidv7, [
502
+ overwriteNext
503
+ ]);
504
+ delta.values?.push({
505
+ uuidv7: overwriteNext.uuidv7,
506
+ value: overwriteNext.value,
507
+ predecessor: overwriteNext.predecessor
508
+ });
445
509
  }
446
510
  void attachEntryToIndexes(crListReplica, linkedListEntry, delta);
447
511
  void crListReplica.tombstones.add(entryToOverwrite.uuidv7);
@@ -475,20 +539,24 @@ function __update(listIndex, listValues, crListReplica, mode) {
475
539
  linkedListEntry.index = actualIndex + 1;
476
540
  linkedListEntry.predecessor = crListReplica.cursor.uuidv7;
477
541
  void linkEntryBetween(crListReplica.cursor, linkedListEntry, next);
478
- if (next) {
479
- if (next.predecessor === crListReplica.cursor.uuidv7) {
480
- void moveEntryToPredecessor(
481
- crListReplica,
482
- next,
483
- linkedListEntry.uuidv7,
484
- delta
485
- );
542
+ if (next && next.predecessor === crListReplica.cursor.uuidv7) {
543
+ if (!displacedEntry) {
544
+ displacedEntry = next;
545
+ const sibs = crListReplica.childrenMap.get(next.predecessor);
546
+ if (sibs) {
547
+ const i = sibs.indexOf(next);
548
+ if (i !== -1) sibs.splice(i, 1);
549
+ }
486
550
  }
551
+ displacedEntry.predecessor = linkedListEntry.uuidv7;
487
552
  }
488
553
  void attachEntryToIndexes(crListReplica, linkedListEntry, delta);
489
554
  crListReplica.cursor = linkedListEntry;
490
555
  crListReplica.cursorIndex = linkedListEntry.index;
491
- if (next) crListReplica.index = /* @__PURE__ */ new Map();
556
+ if (next) {
557
+ if (crListReplica.index) crListReplica.index.clear();
558
+ else crListReplica.index = /* @__PURE__ */ new Map();
559
+ }
492
560
  void crListReplica.index?.set(linkedListEntry.index, linkedListEntry);
493
561
  change[linkedListEntry.index] = linkedListEntry.value;
494
562
  break;
@@ -512,17 +580,23 @@ function __update(listIndex, listValues, crListReplica, mode) {
512
580
  linkedListEntry.predecessor = prev?.uuidv7 ?? "\0";
513
581
  void linkEntryBetween(prev, linkedListEntry, crListReplica.cursor);
514
582
  if (crListReplica.cursor.predecessor === linkedListEntry.predecessor) {
515
- void moveEntryToPredecessor(
516
- crListReplica,
517
- crListReplica.cursor,
518
- linkedListEntry.uuidv7,
519
- delta
520
- );
583
+ if (!displacedEntry) {
584
+ displacedEntry = crListReplica.cursor;
585
+ const sibs = crListReplica.childrenMap.get(
586
+ crListReplica.cursor.predecessor
587
+ );
588
+ if (sibs) {
589
+ const i = sibs.indexOf(crListReplica.cursor);
590
+ if (i !== -1) sibs.splice(i, 1);
591
+ }
592
+ }
593
+ displacedEntry.predecessor = linkedListEntry.uuidv7;
521
594
  }
522
595
  void attachEntryToIndexes(crListReplica, linkedListEntry, delta);
523
596
  crListReplica.cursor = linkedListEntry;
524
597
  crListReplica.cursorIndex = actualIndex;
525
- crListReplica.index = /* @__PURE__ */ new Map();
598
+ if (crListReplica.index) crListReplica.index.clear();
599
+ else crListReplica.index = /* @__PURE__ */ new Map();
526
600
  void crListReplica.index?.set(linkedListEntry.index, linkedListEntry);
527
601
  change[actualIndex] = linkedListEntry.value;
528
602
  mode = "after";
@@ -533,6 +607,19 @@ function __update(listIndex, listValues, crListReplica, mode) {
533
607
  crListReplica.size = crListReplica.parentMap.size;
534
608
  listIndex++;
535
609
  }
610
+ if (displacedEntry) {
611
+ const sibs = crListReplica.childrenMap.get(displacedEntry.predecessor);
612
+ if (sibs) sibs.push(displacedEntry);
613
+ else
614
+ crListReplica.childrenMap.set(displacedEntry.predecessor, [
615
+ displacedEntry
616
+ ]);
617
+ delta.values?.push({
618
+ uuidv7: displacedEntry.uuidv7,
619
+ value: displacedEntry.value,
620
+ predecessor: displacedEntry.predecessor
621
+ });
622
+ }
536
623
  return { change, delta };
537
624
  }
538
625
 
@@ -645,7 +732,7 @@ function __merge(crListReplica, crListDelta) {
645
732
  }
646
733
  }
647
734
  }
648
- if (!Object.hasOwn(crListDelta, "values") || !Array.isArray(crListDelta.values)) {
735
+ if (!Object.hasOwn(crListDelta, "values") || !Array.isArray(crListDelta.values) || crListDelta.values.length === 0 && tailTombstoneMovedCursor) {
649
736
  if (newTombsIndices.length === 0) return false;
650
737
  if (newTombsIndices.length === 1 && tailTombstoneMovedCursor) {
651
738
  if (crListReplica.cursor) {
@@ -714,7 +801,12 @@ function __merge(crListReplica, crListDelta) {
714
801
  }
715
802
  }
716
803
  if (needsRelink) {
717
- if (!trySpliceInsertedParent(crListReplica, newVals, reparentedVals) && !trySpliceReplacement(
804
+ if (!trySpliceSiblingInsert(
805
+ crListReplica,
806
+ newVals,
807
+ reparentedVals,
808
+ newTombsIndices.length
809
+ ) && !trySpliceInsertedParent(crListReplica, newVals, reparentedVals) && !trySpliceReplacement(
718
810
  crListReplica,
719
811
  newVals,
720
812
  reparentedVals,
@@ -868,32 +960,32 @@ var CRList = class {
868
960
  return this.state.size;
869
961
  }
870
962
  /**
871
- * Inserts a value before an index.
963
+ * Inserts values before an index.
872
964
  *
873
- * If `beforeIndex` is omitted, the value is inserted at the start of the list.
965
+ * If `beforeIndex` is omitted, values are inserted at the start of the list.
874
966
  *
875
- * @param value - The value to insert.
967
+ * @param values - Values to insert.
876
968
  * @param beforeIndex - The index to insert before.
877
969
  */
878
- prepend(value, beforeIndex) {
879
- const result = __update(beforeIndex ?? 0, [value], this.state, "before");
970
+ prepend(values, beforeIndex) {
971
+ const result = __update(beforeIndex ?? 0, values, this.state, "before");
880
972
  if (!result) return;
881
973
  const { delta, change } = result;
882
974
  if (delta) void dispatchCRListEvent(this.eventTarget, "delta", delta);
883
975
  if (change) void dispatchCRListEvent(this.eventTarget, "change", change);
884
976
  }
885
977
  /**
886
- * Inserts a value after an index.
978
+ * Inserts values after an index.
887
979
  *
888
- * If `afterIndex` is omitted, the value is appended at the end of the list.
980
+ * If `afterIndex` is omitted, values are appended at the end of the list.
889
981
  *
890
- * @param value - The value to insert.
982
+ * @param values - Values to insert.
891
983
  * @param afterIndex - The index to insert after.
892
984
  */
893
- append(value, afterIndex) {
985
+ append(values, afterIndex) {
894
986
  const result = __update(
895
987
  afterIndex ?? this.state.size,
896
- [value],
988
+ values,
897
989
  this.state,
898
990
  "after"
899
991
  );
@@ -903,12 +995,26 @@ var CRList = class {
903
995
  if (change) void dispatchCRListEvent(this.eventTarget, "change", change);
904
996
  }
905
997
  /**
906
- * Removes the entry at an index.
998
+ * Overwrites entries starting at an index.
999
+ *
1000
+ * @param index - The index to start overwriting at.
1001
+ * @param values - Values to write.
1002
+ */
1003
+ update(index, values) {
1004
+ const result = __update(index, values, this.state, "overwrite");
1005
+ if (!result) return;
1006
+ const { delta, change } = result;
1007
+ if (delta) void dispatchCRListEvent(this.eventTarget, "delta", delta);
1008
+ if (change) void dispatchCRListEvent(this.eventTarget, "change", change);
1009
+ }
1010
+ /**
1011
+ * Removes one or more entries starting at an index.
907
1012
  *
908
- * @param index - The index to remove.
1013
+ * @param index - The first index to remove.
1014
+ * @param count - Number of entries to remove. Defaults to `1`.
909
1015
  */
910
- remove(index) {
911
- const result = __delete(this.state, index, index + 1);
1016
+ remove(index, count = 1) {
1017
+ const result = __delete(this.state, index, index + count);
912
1018
  if (!result) return;
913
1019
  const { delta, change } = result;
914
1020
  if (delta) void dispatchCRListEvent(this.eventTarget, "delta", delta);