@liveblocks/core 3.14.0-pre3 → 3.14.0-pre5

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
@@ -6,7 +6,7 @@ var __export = (target, all) => {
6
6
 
7
7
  // src/version.ts
8
8
  var PKG_NAME = "@liveblocks/core";
9
- var PKG_VERSION = "3.14.0-pre3";
9
+ var PKG_VERSION = "3.14.0-pre5";
10
10
  var PKG_FORMAT = "esm";
11
11
 
12
12
  // src/dupe-detection.ts
@@ -567,6 +567,9 @@ var SortedList = class _SortedList {
567
567
  this.#lt = lt;
568
568
  this.#data = alreadySortedList;
569
569
  }
570
+ /**
571
+ * Creates an empty SortedList with the given "less than" function.
572
+ */
570
573
  static with(lt) {
571
574
  return _SortedList.fromAlreadySorted([], lt);
572
575
  }
@@ -588,10 +591,12 @@ var SortedList = class _SortedList {
588
591
  }
589
592
  /**
590
593
  * Adds a new item to the sorted list, such that it remains sorted.
594
+ * Returns the index where the item was inserted.
591
595
  */
592
596
  add(value) {
593
597
  const idx = bisectRight(this.#data, value, this.#lt);
594
598
  this.#data.splice(idx, 0, value);
599
+ return idx;
595
600
  }
596
601
  /**
597
602
  * Removes all values from the sorted list, making it empty again.
@@ -636,6 +641,60 @@ var SortedList = class _SortedList {
636
641
  }
637
642
  return false;
638
643
  }
644
+ /**
645
+ * Removes the item at the given index.
646
+ * Returns the removed item, or undefined if index is out of bounds.
647
+ */
648
+ removeAt(index) {
649
+ if (index < 0 || index >= this.#data.length) {
650
+ return void 0;
651
+ }
652
+ const [removed] = this.#data.splice(index, 1);
653
+ return removed;
654
+ }
655
+ /**
656
+ * Repositions an item to maintain sorted order after its sort key has
657
+ * been mutated in-place. For example:
658
+ *
659
+ * const item = sorted.at(3);
660
+ * item.updatedAt = new Date(); // mutate the item's sort key in-place
661
+ * sorted.reposition(item); // restore sorted order
662
+ *
663
+ * Returns the new index of the item. Throws if the item is not in the list.
664
+ *
665
+ * Semantically equivalent to remove(value) + add(value), but optimized
666
+ * to avoid array shifting when the item only moves a short distance.
667
+ */
668
+ reposition(value) {
669
+ const oldIdx = this.#data.indexOf(value);
670
+ if (oldIdx < 0) {
671
+ throw new Error("Cannot reposition item that is not in the list");
672
+ }
673
+ const prev = this.#data[oldIdx - 1];
674
+ const next = this.#data[oldIdx + 1];
675
+ const validLeft = prev === void 0 || this.#lt(prev, value);
676
+ const validRight = next === void 0 || this.#lt(value, next);
677
+ if (validLeft && validRight) {
678
+ return oldIdx;
679
+ }
680
+ let newIdx = oldIdx;
681
+ while (newIdx > 0 && this.#lt(value, this.#data[newIdx - 1])) {
682
+ this.#data[newIdx] = this.#data[newIdx - 1];
683
+ newIdx--;
684
+ }
685
+ if (newIdx < oldIdx) {
686
+ this.#data[newIdx] = value;
687
+ return newIdx;
688
+ }
689
+ while (newIdx < this.#data.length - 1 && !this.#lt(value, this.#data[newIdx + 1])) {
690
+ this.#data[newIdx] = this.#data[newIdx + 1];
691
+ newIdx++;
692
+ }
693
+ if (newIdx !== oldIdx) {
694
+ this.#data[newIdx] = value;
695
+ }
696
+ return newIdx;
697
+ }
639
698
  at(index) {
640
699
  return this.#data[index];
641
700
  }
@@ -5898,16 +5957,68 @@ function before(pos) {
5898
5957
  }
5899
5958
  return ONE;
5900
5959
  }
5960
+ var VIEWPORT_START = 2;
5961
+ var VIEWPORT_STEP = 3;
5901
5962
  function after(pos) {
5902
- for (let i = 0; i <= pos.length - 1; i++) {
5963
+ for (let i = 0; i < pos.length; i++) {
5903
5964
  const code = pos.charCodeAt(i);
5904
- if (code >= MAX_CODE) {
5905
- continue;
5965
+ if (code < MIN_CODE || code > MAX_CODE) {
5966
+ return pos + ONE;
5906
5967
  }
5907
- return pos.substring(0, i) + String.fromCharCode(code + 1);
5968
+ }
5969
+ while (pos.length > 1 && pos.charCodeAt(pos.length - 1) === MIN_CODE) {
5970
+ pos = pos.slice(0, -1);
5971
+ }
5972
+ if (pos.length === 0 || pos === ZERO) {
5973
+ return ONE;
5974
+ }
5975
+ let viewport = VIEWPORT_START;
5976
+ if (pos.length > VIEWPORT_START) {
5977
+ viewport = VIEWPORT_START + Math.ceil((pos.length - VIEWPORT_START) / VIEWPORT_STEP) * VIEWPORT_STEP;
5978
+ }
5979
+ const result = incrementWithinViewport(pos, viewport);
5980
+ if (result !== null) {
5981
+ return result;
5982
+ }
5983
+ viewport += VIEWPORT_STEP;
5984
+ const extendedResult = incrementWithinViewport(pos, viewport);
5985
+ if (extendedResult !== null) {
5986
+ return extendedResult;
5908
5987
  }
5909
5988
  return pos + ONE;
5910
5989
  }
5990
+ function incrementWithinViewport(pos, viewport) {
5991
+ const digits = [];
5992
+ for (let i = 0; i < viewport; i++) {
5993
+ if (i < pos.length) {
5994
+ digits.push(pos.charCodeAt(i) - MIN_CODE);
5995
+ } else {
5996
+ digits.push(0);
5997
+ }
5998
+ }
5999
+ let carry = 1;
6000
+ for (let i = viewport - 1; i >= 0 && carry; i--) {
6001
+ const sum = digits[i] + carry;
6002
+ if (sum >= NUM_DIGITS) {
6003
+ digits[i] = 0;
6004
+ carry = 1;
6005
+ } else {
6006
+ digits[i] = sum;
6007
+ carry = 0;
6008
+ }
6009
+ }
6010
+ if (carry) {
6011
+ return null;
6012
+ }
6013
+ let result = "";
6014
+ for (const d of digits) {
6015
+ result += String.fromCharCode(d + MIN_CODE);
6016
+ }
6017
+ while (result.length > 1 && result.charCodeAt(result.length - 1) === MIN_CODE) {
6018
+ result = result.slice(0, -1);
6019
+ }
6020
+ return result;
6021
+ }
5911
6022
  function between(lo, hi) {
5912
6023
  if (lo < hi) {
5913
6024
  return _between(lo, hi);
@@ -6368,29 +6479,27 @@ var LiveRegister = class _LiveRegister extends AbstractCrdt {
6368
6479
  };
6369
6480
 
6370
6481
  // src/crdts/LiveList.ts
6371
- function compareNodePosition(itemA, itemB) {
6372
- const posA = itemA._parentPos;
6373
- const posB = itemB._parentPos;
6374
- return posA === posB ? 0 : posA < posB ? -1 : 1;
6482
+ function childNodeLt(a, b) {
6483
+ return a._parentPos < b._parentPos;
6375
6484
  }
6376
6485
  var LiveList = class _LiveList extends AbstractCrdt {
6377
- // TODO: Naive array at first, find a better data structure. Maybe an Order statistics tree?
6378
6486
  #items;
6379
6487
  #implicitlyDeletedItems;
6380
6488
  #unacknowledgedSets;
6381
6489
  constructor(items) {
6382
6490
  super();
6383
- this.#items = [];
6384
6491
  this.#implicitlyDeletedItems = /* @__PURE__ */ new WeakSet();
6385
6492
  this.#unacknowledgedSets = /* @__PURE__ */ new Map();
6386
- let position = void 0;
6493
+ const nodes = [];
6494
+ let lastPos;
6387
6495
  for (const item of items) {
6388
- const newPosition = makePosition(position);
6496
+ const pos = makePosition(lastPos);
6389
6497
  const node = lsonToLiveNode(item);
6390
- node._setParentLink(this, newPosition);
6391
- this.#items.push(node);
6392
- position = newPosition;
6498
+ node._setParentLink(this, pos);
6499
+ nodes.push(node);
6500
+ lastPos = pos;
6393
6501
  }
6502
+ this.#items = SortedList.fromAlreadySorted(nodes, childNodeLt);
6394
6503
  }
6395
6504
  /** @internal */
6396
6505
  static _deserialize([id, _], parentToChildren, pool) {
@@ -6404,7 +6513,7 @@ var LiveList = class _LiveList extends AbstractCrdt {
6404
6513
  const crdt = node[1];
6405
6514
  const child = deserialize(node, parentToChildren, pool);
6406
6515
  child._setParentLink(list, crdt.parentKey);
6407
- list._insertAndSort(child);
6516
+ list.#insert(child);
6408
6517
  }
6409
6518
  return list;
6410
6519
  }
@@ -6440,19 +6549,33 @@ var LiveList = class _LiveList extends AbstractCrdt {
6440
6549
  return ops;
6441
6550
  }
6442
6551
  /**
6443
- * @internal
6444
- *
6445
- * Adds a new item into the sorted list, in the correct position.
6552
+ * Inserts a new child into the list in the correct location (binary search
6553
+ * finds correct position efficiently). Returns the insertion index.
6446
6554
  */
6447
- _insertAndSort(item) {
6448
- this.#items.push(item);
6449
- this._sortItems();
6555
+ #insert(childNode) {
6556
+ const index = this.#items.add(childNode);
6557
+ this.invalidate();
6558
+ return index;
6450
6559
  }
6451
- /** @internal */
6452
- _sortItems() {
6453
- this.#items.sort(compareNodePosition);
6560
+ /**
6561
+ * Updates an item's position and repositions it in the sorted list.
6562
+ * Encapsulates the remove -> mutate -> add cycle needed when changing sort keys.
6563
+ *
6564
+ * IMPORTANT: Item must exist in this list. List count remains unchanged.
6565
+ */
6566
+ #updateItemPosition(item, newKey) {
6567
+ item._setParentLink(this, newKey);
6568
+ this.#items.reposition(item);
6454
6569
  this.invalidate();
6455
6570
  }
6571
+ /**
6572
+ * Updates an item's position by index. Safer than #updateItemPosition when you have
6573
+ * an index, as it ensures the item exists and is from this list.
6574
+ */
6575
+ #updateItemPositionAt(index, newKey) {
6576
+ const item = nn(this.#items.at(index));
6577
+ this.#updateItemPosition(item, newKey);
6578
+ }
6456
6579
  /** @internal */
6457
6580
  _indexOfPosition(position) {
6458
6581
  return this.#items.findIndex(
@@ -6484,10 +6607,12 @@ var LiveList = class _LiveList extends AbstractCrdt {
6484
6607
  const deletedId = op.deletedId;
6485
6608
  const indexOfItemWithSamePosition = this._indexOfPosition(key);
6486
6609
  if (indexOfItemWithSamePosition !== -1) {
6487
- const itemWithSamePosition = this.#items[indexOfItemWithSamePosition];
6610
+ const itemWithSamePosition = nn(
6611
+ this.#items.removeAt(indexOfItemWithSamePosition)
6612
+ );
6488
6613
  if (itemWithSamePosition._id === deletedId) {
6489
6614
  itemWithSamePosition._detach();
6490
- this.#items[indexOfItemWithSamePosition] = child;
6615
+ this.#items.add(child);
6491
6616
  return {
6492
6617
  modified: makeUpdate(this, [
6493
6618
  setDelta(indexOfItemWithSamePosition, child)
@@ -6496,7 +6621,8 @@ var LiveList = class _LiveList extends AbstractCrdt {
6496
6621
  };
6497
6622
  } else {
6498
6623
  this.#implicitlyDeletedItems.add(itemWithSamePosition);
6499
- this.#items[indexOfItemWithSamePosition] = child;
6624
+ this.#items.remove(itemWithSamePosition);
6625
+ this.#items.add(child);
6500
6626
  const delta = [
6501
6627
  setDelta(indexOfItemWithSamePosition, child)
6502
6628
  ];
@@ -6519,7 +6645,7 @@ var LiveList = class _LiveList extends AbstractCrdt {
6519
6645
  if (deleteDelta2) {
6520
6646
  updates.push(deleteDelta2);
6521
6647
  }
6522
- this._insertAndSort(child);
6648
+ this.#insert(child);
6523
6649
  updates.push(insertDelta(this._indexOfPosition(key), child));
6524
6650
  return {
6525
6651
  reverse: [],
@@ -6554,16 +6680,15 @@ var LiveList = class _LiveList extends AbstractCrdt {
6554
6680
  };
6555
6681
  }
6556
6682
  if (indexOfItemWithSamePosition !== -1) {
6557
- this.#implicitlyDeletedItems.add(
6558
- this.#items[indexOfItemWithSamePosition]
6683
+ const itemAtPosition = nn(
6684
+ this.#items.removeAt(indexOfItemWithSamePosition)
6559
6685
  );
6560
- const [prevNode] = this.#items.splice(indexOfItemWithSamePosition, 1);
6561
- delta.push(deleteDelta(indexOfItemWithSamePosition, prevNode));
6686
+ this.#implicitlyDeletedItems.add(itemAtPosition);
6687
+ delta.push(deleteDelta(indexOfItemWithSamePosition, itemAtPosition));
6562
6688
  }
6563
- const prevIndex = this.#items.indexOf(existingItem);
6564
- existingItem._setParentLink(this, op.parentKey);
6565
- this._sortItems();
6566
- const newIndex = this.#items.indexOf(existingItem);
6689
+ const prevIndex = this.#items.findIndex((item) => item === existingItem);
6690
+ this.#updateItemPosition(existingItem, op.parentKey);
6691
+ const newIndex = this.#items.findIndex((item) => item === existingItem);
6567
6692
  if (newIndex !== prevIndex) {
6568
6693
  delta.push(moveDelta(prevIndex, newIndex, existingItem));
6569
6694
  }
@@ -6576,8 +6701,7 @@ var LiveList = class _LiveList extends AbstractCrdt {
6576
6701
  if (orphan && this.#implicitlyDeletedItems.has(orphan)) {
6577
6702
  orphan._setParentLink(this, op.parentKey);
6578
6703
  this.#implicitlyDeletedItems.delete(orphan);
6579
- this._insertAndSort(orphan);
6580
- const recreatedItemIndex = this.#items.indexOf(orphan);
6704
+ const recreatedItemIndex = this.#insert(orphan);
6581
6705
  return {
6582
6706
  modified: makeUpdate(this, [
6583
6707
  // If there is an item at this position, update is a set, else it's an insert
@@ -6588,7 +6712,7 @@ var LiveList = class _LiveList extends AbstractCrdt {
6588
6712
  };
6589
6713
  } else {
6590
6714
  if (indexOfItemWithSamePosition !== -1) {
6591
- this.#items.splice(indexOfItemWithSamePosition, 1);
6715
+ nn(this.#items.removeAt(indexOfItemWithSamePosition));
6592
6716
  }
6593
6717
  const { newItem, newIndex } = this.#createAttachItemAndSort(
6594
6718
  op,
@@ -6647,12 +6771,13 @@ var LiveList = class _LiveList extends AbstractCrdt {
6647
6771
  modified: false
6648
6772
  };
6649
6773
  } else {
6650
- const oldPositionIndex = this.#items.indexOf(existingItem);
6774
+ const oldPositionIndex = this.#items.findIndex(
6775
+ (item) => item === existingItem
6776
+ );
6651
6777
  if (itemIndexAtPosition !== -1) {
6652
6778
  this.#shiftItemPosition(itemIndexAtPosition, key);
6653
6779
  }
6654
- existingItem._setParentLink(this, key);
6655
- this._sortItems();
6780
+ this.#updateItemPosition(existingItem, key);
6656
6781
  const newIndex = this._indexOfPosition(key);
6657
6782
  if (newIndex === oldPositionIndex) {
6658
6783
  return { modified: false };
@@ -6669,7 +6794,7 @@ var LiveList = class _LiveList extends AbstractCrdt {
6669
6794
  if (orphan && this.#implicitlyDeletedItems.has(orphan)) {
6670
6795
  orphan._setParentLink(this, key);
6671
6796
  this.#implicitlyDeletedItems.delete(orphan);
6672
- this._insertAndSort(orphan);
6797
+ this.#insert(orphan);
6673
6798
  const newIndex = this._indexOfPosition(key);
6674
6799
  return {
6675
6800
  modified: makeUpdate(this, [insertDelta(newIndex, orphan)]),
@@ -6698,12 +6823,12 @@ var LiveList = class _LiveList extends AbstractCrdt {
6698
6823
  const existingItemIndex = this._indexOfPosition(key);
6699
6824
  let newKey = key;
6700
6825
  if (existingItemIndex !== -1) {
6701
- const before2 = this.#items[existingItemIndex]?._parentPos;
6702
- const after2 = this.#items[existingItemIndex + 1]?._parentPos;
6826
+ const before2 = this.#items.at(existingItemIndex)?._parentPos;
6827
+ const after2 = this.#items.at(existingItemIndex + 1)?._parentPos;
6703
6828
  newKey = makePosition(before2, after2);
6704
6829
  child._setParentLink(this, newKey);
6705
6830
  }
6706
- this._insertAndSort(child);
6831
+ this.#insert(child);
6707
6832
  const newIndex = this._indexOfPosition(newKey);
6708
6833
  return {
6709
6834
  modified: makeUpdate(this, [insertDelta(newIndex, child)]),
@@ -6722,9 +6847,10 @@ var LiveList = class _LiveList extends AbstractCrdt {
6722
6847
  child._setParentLink(this, key);
6723
6848
  const newKey = key;
6724
6849
  if (indexOfItemWithSameKey !== -1) {
6725
- const existingItem = this.#items[indexOfItemWithSameKey];
6850
+ const existingItem = this.#items.at(indexOfItemWithSameKey);
6726
6851
  existingItem._detach();
6727
- this.#items[indexOfItemWithSameKey] = child;
6852
+ this.#items.remove(existingItem);
6853
+ this.#items.add(child);
6728
6854
  const reverse = HACK_addIntentAndDeletedIdToOperation(
6729
6855
  existingItem._toOps(nn(this._id), key),
6730
6856
  op.id
@@ -6741,7 +6867,7 @@ var LiveList = class _LiveList extends AbstractCrdt {
6741
6867
  reverse
6742
6868
  };
6743
6869
  } else {
6744
- this._insertAndSort(child);
6870
+ this.#insert(child);
6745
6871
  this.#detachItemAssociatedToSetOperation(op.deletedId);
6746
6872
  const newIndex = this._indexOfPosition(newKey);
6747
6873
  return {
@@ -6783,13 +6909,14 @@ var LiveList = class _LiveList extends AbstractCrdt {
6783
6909
  if (child) {
6784
6910
  const parentKey = nn(child._parentKey);
6785
6911
  const reverse = child._toOps(nn(this._id), parentKey);
6786
- const indexToDelete = this.#items.indexOf(child);
6912
+ const indexToDelete = this.#items.findIndex((item) => item === child);
6787
6913
  if (indexToDelete === -1) {
6788
6914
  return {
6789
6915
  modified: false
6790
6916
  };
6791
6917
  }
6792
- const [previousNode] = this.#items.splice(indexToDelete, 1);
6918
+ const previousNode = this.#items.at(indexToDelete);
6919
+ this.#items.remove(child);
6793
6920
  this.invalidate();
6794
6921
  child._detach();
6795
6922
  return {
@@ -6803,8 +6930,7 @@ var LiveList = class _LiveList extends AbstractCrdt {
6803
6930
  if (this.#implicitlyDeletedItems.has(child)) {
6804
6931
  this.#implicitlyDeletedItems.delete(child);
6805
6932
  child._setParentLink(this, newKey);
6806
- this._insertAndSort(child);
6807
- const newIndex = this.#items.indexOf(child);
6933
+ const newIndex = this.#insert(child);
6808
6934
  return {
6809
6935
  modified: makeUpdate(this, [insertDelta(newIndex, child)]),
6810
6936
  reverse: []
@@ -6818,10 +6944,9 @@ var LiveList = class _LiveList extends AbstractCrdt {
6818
6944
  }
6819
6945
  const existingItemIndex = this._indexOfPosition(newKey);
6820
6946
  if (existingItemIndex === -1) {
6821
- const previousIndex = this.#items.indexOf(child);
6822
- child._setParentLink(this, newKey);
6823
- this._sortItems();
6824
- const newIndex = this.#items.indexOf(child);
6947
+ const previousIndex = this.#items.findIndex((item) => item === child);
6948
+ this.#updateItemPosition(child, newKey);
6949
+ const newIndex = this.#items.findIndex((item) => item === child);
6825
6950
  if (newIndex === previousIndex) {
6826
6951
  return {
6827
6952
  modified: false
@@ -6832,14 +6957,13 @@ var LiveList = class _LiveList extends AbstractCrdt {
6832
6957
  reverse: []
6833
6958
  };
6834
6959
  } else {
6835
- this.#items[existingItemIndex]._setParentLink(
6836
- this,
6837
- makePosition(newKey, this.#items[existingItemIndex + 1]?._parentPos)
6960
+ this.#updateItemPositionAt(
6961
+ existingItemIndex,
6962
+ makePosition(newKey, this.#items.at(existingItemIndex + 1)?._parentPos)
6838
6963
  );
6839
- const previousIndex = this.#items.indexOf(child);
6840
- child._setParentLink(this, newKey);
6841
- this._sortItems();
6842
- const newIndex = this.#items.indexOf(child);
6964
+ const previousIndex = this.#items.findIndex((item) => item === child);
6965
+ this.#updateItemPosition(child, newKey);
6966
+ const newIndex = this.#items.findIndex((item) => item === child);
6843
6967
  if (newIndex === previousIndex) {
6844
6968
  return {
6845
6969
  modified: false
@@ -6857,14 +6981,18 @@ var LiveList = class _LiveList extends AbstractCrdt {
6857
6981
  const existingItemIndex = this._indexOfPosition(newKey);
6858
6982
  this.#implicitlyDeletedItems.delete(child);
6859
6983
  if (existingItemIndex !== -1) {
6860
- this.#items[existingItemIndex]._setParentLink(
6984
+ const existingItem = this.#items.at(existingItemIndex);
6985
+ existingItem._setParentLink(
6861
6986
  this,
6862
- makePosition(newKey, this.#items[existingItemIndex + 1]?._parentPos)
6987
+ makePosition(
6988
+ newKey,
6989
+ this.#items.at(existingItemIndex + 1)?._parentPos
6990
+ )
6863
6991
  );
6992
+ this.#items.reposition(existingItem);
6864
6993
  }
6865
6994
  child._setParentLink(this, newKey);
6866
- this._insertAndSort(child);
6867
- const newIndex = this.#items.indexOf(child);
6995
+ const newIndex = this.#insert(child);
6868
6996
  return {
6869
6997
  modified: makeUpdate(this, [insertDelta(newIndex, child)]),
6870
6998
  reverse: []
@@ -6875,17 +7003,19 @@ var LiveList = class _LiveList extends AbstractCrdt {
6875
7003
  modified: false
6876
7004
  };
6877
7005
  }
6878
- const previousIndex = this.#items.indexOf(child);
7006
+ const previousIndex = this.#items.findIndex((item) => item === child);
6879
7007
  const existingItemIndex = this._indexOfPosition(newKey);
6880
7008
  if (existingItemIndex !== -1) {
6881
- this.#items[existingItemIndex]._setParentLink(
6882
- this,
6883
- makePosition(newKey, this.#items[existingItemIndex + 1]?._parentPos)
7009
+ this.#updateItemPositionAt(
7010
+ existingItemIndex,
7011
+ makePosition(
7012
+ newKey,
7013
+ this.#items.at(existingItemIndex + 1)?._parentPos
7014
+ )
6884
7015
  );
6885
7016
  }
6886
- child._setParentLink(this, newKey);
6887
- this._sortItems();
6888
- const newIndex = this.#items.indexOf(child);
7017
+ this.#updateItemPosition(child, newKey);
7018
+ const newIndex = this.#items.findIndex((item) => item === child);
6889
7019
  if (previousIndex === newIndex) {
6890
7020
  return {
6891
7021
  modified: false
@@ -6902,18 +7032,17 @@ var LiveList = class _LiveList extends AbstractCrdt {
6902
7032
  }
6903
7033
  #applySetChildKeyUndoRedo(newKey, child) {
6904
7034
  const previousKey = nn(child._parentKey);
6905
- const previousIndex = this.#items.indexOf(child);
7035
+ const previousIndex = this.#items.findIndex((item) => item === child);
6906
7036
  const existingItemIndex = this._indexOfPosition(newKey);
6907
7037
  let actualNewKey = newKey;
6908
7038
  if (existingItemIndex !== -1) {
6909
7039
  actualNewKey = makePosition(
6910
7040
  newKey,
6911
- this.#items[existingItemIndex + 1]?._parentPos
7041
+ this.#items.at(existingItemIndex + 1)?._parentPos
6912
7042
  );
6913
7043
  }
6914
- child._setParentLink(this, actualNewKey);
6915
- this._sortItems();
6916
- const newIndex = this.#items.indexOf(child);
7044
+ this.#updateItemPosition(child, actualNewKey);
7045
+ const newIndex = this.#items.findIndex((item) => item === child);
6917
7046
  if (previousIndex === newIndex) {
6918
7047
  return {
6919
7048
  modified: false
@@ -6981,12 +7110,12 @@ var LiveList = class _LiveList extends AbstractCrdt {
6981
7110
  `Cannot insert list item at index "${index}". index should be between 0 and ${this.#items.length}`
6982
7111
  );
6983
7112
  }
6984
- const before2 = this.#items[index - 1] ? this.#items[index - 1]._parentPos : void 0;
6985
- const after2 = this.#items[index] ? this.#items[index]._parentPos : void 0;
7113
+ const before2 = this.#items.at(index - 1)?._parentPos;
7114
+ const after2 = this.#items.at(index)?._parentPos;
6986
7115
  const position = makePosition(before2, after2);
6987
7116
  const value = lsonToLiveNode(element);
6988
7117
  value._setParentLink(this, position);
6989
- this._insertAndSort(value);
7118
+ this.#insert(value);
6990
7119
  if (this._pool && this._id) {
6991
7120
  const id = this._pool.generateId();
6992
7121
  value._attach(id, this._pool);
@@ -7023,17 +7152,16 @@ var LiveList = class _LiveList extends AbstractCrdt {
7023
7152
  let beforePosition = null;
7024
7153
  let afterPosition = null;
7025
7154
  if (index < targetIndex) {
7026
- afterPosition = targetIndex === this.#items.length - 1 ? void 0 : this.#items[targetIndex + 1]._parentPos;
7027
- beforePosition = this.#items[targetIndex]._parentPos;
7155
+ afterPosition = targetIndex === this.#items.length - 1 ? void 0 : this.#items.at(targetIndex + 1)?._parentPos;
7156
+ beforePosition = this.#items.at(targetIndex)._parentPos;
7028
7157
  } else {
7029
- afterPosition = this.#items[targetIndex]._parentPos;
7030
- beforePosition = targetIndex === 0 ? void 0 : this.#items[targetIndex - 1]._parentPos;
7158
+ afterPosition = this.#items.at(targetIndex)._parentPos;
7159
+ beforePosition = targetIndex === 0 ? void 0 : this.#items.at(targetIndex - 1)?._parentPos;
7031
7160
  }
7032
7161
  const position = makePosition(beforePosition, afterPosition);
7033
- const item = this.#items[index];
7162
+ const item = this.#items.at(index);
7034
7163
  const previousPosition = item._getParentKeyOrThrow();
7035
- item._setParentLink(this, position);
7036
- this._sortItems();
7164
+ this.#updateItemPositionAt(index, position);
7037
7165
  if (this._pool && this._id) {
7038
7166
  const storageUpdates = /* @__PURE__ */ new Map([
7039
7167
  [this._id, makeUpdate(this, [moveDelta(index, targetIndex, item)])]
@@ -7069,9 +7197,9 @@ var LiveList = class _LiveList extends AbstractCrdt {
7069
7197
  `Cannot delete list item at index "${index}". index should be between 0 and ${this.#items.length - 1}`
7070
7198
  );
7071
7199
  }
7072
- const item = this.#items[index];
7200
+ const item = this.#items.at(index);
7073
7201
  item._detach();
7074
- const [prev] = this.#items.splice(index, 1);
7202
+ this.#items.remove(item);
7075
7203
  this.invalidate();
7076
7204
  if (this._pool) {
7077
7205
  const childRecordId = item._id;
@@ -7079,7 +7207,7 @@ var LiveList = class _LiveList extends AbstractCrdt {
7079
7207
  const storageUpdates = /* @__PURE__ */ new Map();
7080
7208
  storageUpdates.set(
7081
7209
  nn(this._id),
7082
- makeUpdate(this, [deleteDelta(index, prev)])
7210
+ makeUpdate(this, [deleteDelta(index, item)])
7083
7211
  );
7084
7212
  this._pool.dispatch(
7085
7213
  [
@@ -7116,7 +7244,7 @@ var LiveList = class _LiveList extends AbstractCrdt {
7116
7244
  updateDelta.push(deleteDelta(0, item));
7117
7245
  }
7118
7246
  }
7119
- this.#items = [];
7247
+ this.#items.clear();
7120
7248
  this.invalidate();
7121
7249
  const storageUpdates = /* @__PURE__ */ new Map();
7122
7250
  storageUpdates.set(nn(this._id), makeUpdate(this, updateDelta));
@@ -7125,7 +7253,7 @@ var LiveList = class _LiveList extends AbstractCrdt {
7125
7253
  for (const item of this.#items) {
7126
7254
  item._detach();
7127
7255
  }
7128
- this.#items = [];
7256
+ this.#items.clear();
7129
7257
  this.invalidate();
7130
7258
  }
7131
7259
  }
@@ -7136,13 +7264,14 @@ var LiveList = class _LiveList extends AbstractCrdt {
7136
7264
  `Cannot set list item at index "${index}". index should be between 0 and ${this.#items.length - 1}`
7137
7265
  );
7138
7266
  }
7139
- const existingItem = this.#items[index];
7267
+ const existingItem = this.#items.at(index);
7140
7268
  const position = existingItem._getParentKeyOrThrow();
7141
7269
  const existingId = existingItem._id;
7142
7270
  existingItem._detach();
7143
7271
  const value = lsonToLiveNode(item);
7144
7272
  value._setParentLink(this, position);
7145
- this.#items[index] = value;
7273
+ this.#items.remove(existingItem);
7274
+ this.#items.add(value);
7146
7275
  this.invalidate();
7147
7276
  if (this._pool && this._id) {
7148
7277
  const id = this._pool.generateId();
@@ -7165,11 +7294,7 @@ var LiveList = class _LiveList extends AbstractCrdt {
7165
7294
  * Returns an Array of all the elements in the LiveList.
7166
7295
  */
7167
7296
  toArray() {
7168
- return this.#items.map(
7169
- (entry) => liveNodeToLson(entry)
7170
- // ^^^^^^^^
7171
- // FIXME! This isn't safe.
7172
- );
7297
+ return Array.from(this.#items, (entry) => liveNodeToLson(entry));
7173
7298
  }
7174
7299
  /**
7175
7300
  * Tests whether all elements pass the test implemented by the provided function.
@@ -7219,7 +7344,8 @@ var LiveList = class _LiveList extends AbstractCrdt {
7219
7344
  if (index < 0 || index >= this.#items.length) {
7220
7345
  return void 0;
7221
7346
  }
7222
- return liveNodeToLson(this.#items[index]);
7347
+ const item = this.#items.at(index);
7348
+ return item ? liveNodeToLson(item) : void 0;
7223
7349
  }
7224
7350
  /**
7225
7351
  * Returns the first index at which a given element can be found in the LiveList, or -1 if it is not present.
@@ -7245,14 +7371,20 @@ var LiveList = class _LiveList extends AbstractCrdt {
7245
7371
  * @returns An array with each element being the result of the callback function.
7246
7372
  */
7247
7373
  map(callback) {
7248
- return this.#items.map(
7249
- (entry, i) => callback(
7250
- liveNodeToLson(entry),
7251
- // ^^^^^^^^
7252
- // FIXME! This isn't safe.
7253
- i
7254
- )
7255
- );
7374
+ const result = [];
7375
+ let i = 0;
7376
+ for (const entry of this.#items) {
7377
+ result.push(
7378
+ callback(
7379
+ liveNodeToLson(entry),
7380
+ // ^^^^^^^^
7381
+ // FIXME! This isn't safe.
7382
+ i
7383
+ )
7384
+ );
7385
+ i++;
7386
+ }
7387
+ return result;
7256
7388
  }
7257
7389
  /**
7258
7390
  * Tests whether at least one element in the LiveList passes the test implemented by the provided function.
@@ -7269,26 +7401,30 @@ var LiveList = class _LiveList extends AbstractCrdt {
7269
7401
  const newItem = creationOpToLiveNode(op);
7270
7402
  newItem._attach(op.id, nn(this._pool));
7271
7403
  newItem._setParentLink(this, key);
7272
- this._insertAndSort(newItem);
7404
+ this.#insert(newItem);
7273
7405
  const newIndex = this._indexOfPosition(key);
7274
7406
  return { newItem, newIndex };
7275
7407
  }
7276
7408
  #shiftItemPosition(index, key) {
7277
7409
  const shiftedPosition = makePosition(
7278
7410
  key,
7279
- this.#items.length > index + 1 ? this.#items[index + 1]?._parentPos : void 0
7411
+ this.#items.length > index + 1 ? this.#items.at(index + 1)?._parentPos : void 0
7280
7412
  );
7281
- this.#items[index]._setParentLink(this, shiftedPosition);
7413
+ this.#updateItemPositionAt(index, shiftedPosition);
7282
7414
  }
7283
7415
  /** @internal */
7284
7416
  _toTreeNode(key) {
7417
+ const payload = [];
7418
+ let index = 0;
7419
+ for (const item of this.#items) {
7420
+ payload.push(item.toTreeNode(index.toString()));
7421
+ index++;
7422
+ }
7285
7423
  return {
7286
7424
  type: "LiveList",
7287
7425
  id: this._id ?? nanoid(),
7288
7426
  key,
7289
- payload: this.#items.map(
7290
- (item, index) => item.toTreeNode(index.toString())
7291
- )
7427
+ payload
7292
7428
  };
7293
7429
  }
7294
7430
  toImmutable() {
@@ -7296,11 +7432,13 @@ var LiveList = class _LiveList extends AbstractCrdt {
7296
7432
  }
7297
7433
  /** @internal */
7298
7434
  _toImmutable() {
7299
- const result = this.#items.map((node) => node.toImmutable());
7435
+ const result = Array.from(this.#items, (node) => node.toImmutable());
7300
7436
  return process.env.NODE_ENV === "production" ? result : Object.freeze(result);
7301
7437
  }
7302
7438
  clone() {
7303
- return new _LiveList(this.#items.map((item) => item.clone()));
7439
+ return new _LiveList(
7440
+ Array.from(this.#items, (item) => item.clone())
7441
+ );
7304
7442
  }
7305
7443
  };
7306
7444
  var LiveListIterator = class {
@@ -9241,8 +9379,17 @@ function createRoom(options, config) {
9241
9379
  (me) => me !== null ? userToTreeNode("Me", me) : null
9242
9380
  );
9243
9381
  function createOrUpdateRootFromMessage(nodes) {
9382
+ if (nodes.size === 0) {
9383
+ throw new Error("Internal error: cannot load storage without items");
9384
+ }
9244
9385
  if (context.root !== void 0) {
9245
- updateRoot(nodes);
9386
+ const currentItems = /* @__PURE__ */ new Map();
9387
+ for (const [id, crdt] of context.pool.nodes) {
9388
+ currentItems.set(id, crdt._serialize());
9389
+ }
9390
+ const ops = getTreesDiffOperations(currentItems, nodes);
9391
+ const result = applyRemoteOps(ops);
9392
+ notify(result.updates);
9246
9393
  } else {
9247
9394
  context.root = LiveObject._fromItems(
9248
9395
  nodes,
@@ -9264,21 +9411,6 @@ function createRoom(options, config) {
9264
9411
  }
9265
9412
  context.undoStack.length = stackSizeBefore;
9266
9413
  }
9267
- function updateRoot(nodes) {
9268
- if (nodes.size === 0) {
9269
- throw new Error("Internal error: cannot load storage without items");
9270
- }
9271
- if (context.root === void 0) {
9272
- return;
9273
- }
9274
- const currentItems = /* @__PURE__ */ new Map();
9275
- for (const [id, crdt] of context.pool.nodes) {
9276
- currentItems.set(id, crdt._serialize());
9277
- }
9278
- const ops = getTreesDiffOperations(currentItems, nodes);
9279
- const result = applyRemoteOps(ops);
9280
- notify(result.updates);
9281
- }
9282
9414
  function _addToRealUndoStack(frames) {
9283
9415
  if (context.undoStack.length >= 50) {
9284
9416
  context.undoStack.shift();