@liveblocks/core 3.20.0-perm1 → 3.20.0-perm2

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.cjs 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.20.0-perm1";
9
+ var PKG_VERSION = "3.20.0-perm2";
10
10
  var PKG_FORMAT = "cjs";
11
11
 
12
12
  // src/dupe-detection.ts
@@ -701,6 +701,20 @@ var SortedList = class _SortedList {
701
701
  get length() {
702
702
  return this.#data.length;
703
703
  }
704
+ /**
705
+ * Whether the given value is present, by identity. O(log n) plus the length
706
+ * of any run of items that share its sort key (normally 1). Bisects on the
707
+ * value's own key, so it only finds values sitting at their sorted position,
708
+ * which is true for any item currently in the list.
709
+ */
710
+ includes(value) {
711
+ for (let i = bisectRight(this.#data, value, this.#lt) - 1; i >= 0 && !this.#lt(this.#data[i], value); i--) {
712
+ if (this.#data[i] === value) {
713
+ return true;
714
+ }
715
+ }
716
+ return false;
717
+ }
704
718
  *filter(predicate) {
705
719
  for (const item of this.#data) {
706
720
  if (predicate(item)) {
@@ -5723,6 +5737,9 @@ var OpCode = Object.freeze({
5723
5737
  function isIgnoredOp(op) {
5724
5738
  return op.type === OpCode.DELETE_CRDT && op.id === "ACK";
5725
5739
  }
5740
+ function isCreateOp(op) {
5741
+ return op.type === OpCode.CREATE_OBJECT || op.type === OpCode.CREATE_REGISTER || op.type === OpCode.CREATE_MAP || op.type === OpCode.CREATE_LIST;
5742
+ }
5726
5743
 
5727
5744
  // src/protocol/StorageNode.ts
5728
5745
  var CrdtType = Object.freeze({
@@ -5980,12 +5997,112 @@ function asPos(str) {
5980
5997
  return isPos(str) ? str : convertToPos(str);
5981
5998
  }
5982
5999
 
6000
+ // src/crdts/UnacknowledgedOps.ts
6001
+ var UnacknowledgedOps = class {
6002
+ // opId -> op
6003
+ #byOpId = /* @__PURE__ */ new Map();
6004
+ // position -> (opId -> Create op)
6005
+ #createOpsByPosition = /* @__PURE__ */ new Map();
6006
+ // parentId -> (opId -> Create op)
6007
+ #createOpsByParent = /* @__PURE__ */ new Map();
6008
+ // opIds of pending ops that were in flight when a connection died, so the
6009
+ // server may already have processed them. See isPossiblyStored().
6010
+ #possiblyStoredOpIds = /* @__PURE__ */ new Set();
6011
+ #posKey(parentId, parentKey) {
6012
+ return `${parentId}
6013
+ ${parentKey}`;
6014
+ }
6015
+ get size() {
6016
+ return this.#byOpId.size;
6017
+ }
6018
+ /**
6019
+ * Mark the given Op as still unacknowledged.
6020
+ */
6021
+ add(op) {
6022
+ this.#byOpId.set(op.opId, op);
6023
+ if (isCreateOp(op)) {
6024
+ const posKey = this.#posKey(op.parentId, op.parentKey);
6025
+ let atPosition = this.#createOpsByPosition.get(posKey);
6026
+ if (atPosition === void 0) {
6027
+ atPosition = /* @__PURE__ */ new Map();
6028
+ this.#createOpsByPosition.set(posKey, atPosition);
6029
+ }
6030
+ atPosition.set(op.opId, op);
6031
+ let inParent = this.#createOpsByParent.get(op.parentId);
6032
+ if (inParent === void 0) {
6033
+ inParent = /* @__PURE__ */ new Map();
6034
+ this.#createOpsByParent.set(op.parentId, inParent);
6035
+ }
6036
+ inParent.set(op.opId, op);
6037
+ }
6038
+ }
6039
+ /**
6040
+ * Drop the op with the given opId from the set, because the server has
6041
+ * acknowledged it (confirmed our own op, or signalled it was seen but
6042
+ * ignored).
6043
+ */
6044
+ delete(opId) {
6045
+ const op = this.#byOpId.get(opId);
6046
+ if (op === void 0) {
6047
+ return;
6048
+ }
6049
+ this.#byOpId.delete(opId);
6050
+ this.#possiblyStoredOpIds.delete(opId);
6051
+ if (isCreateOp(op)) {
6052
+ const posKey = this.#posKey(op.parentId, op.parentKey);
6053
+ const atPosition = this.#createOpsByPosition.get(posKey);
6054
+ _optionalChain([atPosition, 'optionalAccess', _111 => _111.delete, 'call', _112 => _112(opId)]);
6055
+ if (atPosition !== void 0 && atPosition.size === 0) {
6056
+ this.#createOpsByPosition.delete(posKey);
6057
+ }
6058
+ const inParent = this.#createOpsByParent.get(op.parentId);
6059
+ _optionalChain([inParent, 'optionalAccess', _113 => _113.delete, 'call', _114 => _114(opId)]);
6060
+ if (inParent !== void 0 && inParent.size === 0) {
6061
+ this.#createOpsByParent.delete(op.parentId);
6062
+ }
6063
+ }
6064
+ }
6065
+ /**
6066
+ * The still-unacknowledged Create ops with the given `parentId` and
6067
+ * `parentKey` (targeting one exact position), in dispatch order. O(1) lookup.
6068
+ * Empty if none.
6069
+ */
6070
+ getByParentIdAndKey(parentId, parentKey) {
6071
+ return _nullishCoalesce(_optionalChain([this, 'access', _115 => _115.#createOpsByPosition, 'access', _116 => _116.get, 'call', _117 => _117(this.#posKey(parentId, parentKey)), 'optionalAccess', _118 => _118.values, 'call', _119 => _119()]), () => ( []));
6072
+ }
6073
+ /**
6074
+ * The still-unacknowledged Create ops with the given `parentId` (across all
6075
+ * positions), in dispatch order. O(1) lookup. Empty if none.
6076
+ */
6077
+ getByParentId(parentId) {
6078
+ return _nullishCoalesce(_optionalChain([this, 'access', _120 => _120.#createOpsByParent, 'access', _121 => _121.get, 'call', _122 => _122(parentId), 'optionalAccess', _123 => _123.values, 'call', _124 => _124()]), () => ( []));
6079
+ }
6080
+ /** All still-unacknowledged ops, in dispatch order. */
6081
+ values() {
6082
+ return this.#byOpId.values();
6083
+ }
6084
+ isPossiblyStored(opId) {
6085
+ return this.#possiblyStoredOpIds.has(opId);
6086
+ }
6087
+ /**
6088
+ * Mark every currently pending op as possibly stored on the server. Called
6089
+ * when the connection dies: all of these ops were in flight, and their
6090
+ * (possibly lost) acks would have been the only way to know their fate.
6091
+ */
6092
+ markAllAsPossiblyStored() {
6093
+ for (const opId of this.#byOpId.keys()) {
6094
+ this.#possiblyStoredOpIds.add(opId);
6095
+ }
6096
+ }
6097
+ };
6098
+
5983
6099
  // src/crdts/AbstractCrdt.ts
5984
6100
  function createManagedPool(roomId, options) {
5985
6101
  const {
5986
6102
  getCurrentConnectionId,
5987
6103
  onDispatch,
5988
- isStorageWritable = () => true
6104
+ isStorageWritable = () => true,
6105
+ unacknowledgedOps = new UnacknowledgedOps()
5989
6106
  } = options;
5990
6107
  let clock = 0;
5991
6108
  let opClock = 0;
@@ -5999,7 +6116,7 @@ function createManagedPool(roomId, options) {
5999
6116
  generateId: () => `${getCurrentConnectionId()}:${clock++}`,
6000
6117
  generateOpId: () => `${getCurrentConnectionId()}:${opClock++}`,
6001
6118
  dispatch(ops, reverse, storageUpdates) {
6002
- _optionalChain([onDispatch, 'optionalCall', _111 => _111(ops, reverse, storageUpdates)]);
6119
+ _optionalChain([onDispatch, 'optionalCall', _125 => _125(ops, reverse, storageUpdates)]);
6003
6120
  },
6004
6121
  assertStorageIsWritable: () => {
6005
6122
  if (!isStorageWritable()) {
@@ -6007,7 +6124,8 @@ function createManagedPool(roomId, options) {
6007
6124
  "Cannot write to storage with a read only user, please ensure the user has write permissions"
6008
6125
  );
6009
6126
  }
6010
- }
6127
+ },
6128
+ unacknowledgedOps
6011
6129
  };
6012
6130
  }
6013
6131
  function crdtAsLiveNode(value) {
@@ -6289,11 +6407,9 @@ function childNodeLt(a, b) {
6289
6407
  var LiveList = class _LiveList extends AbstractCrdt {
6290
6408
  #items;
6291
6409
  #implicitlyDeletedItems;
6292
- #unacknowledgedSets;
6293
6410
  constructor(items) {
6294
6411
  super();
6295
6412
  this.#implicitlyDeletedItems = /* @__PURE__ */ new WeakSet();
6296
- this.#unacknowledgedSets = /* @__PURE__ */ new Map();
6297
6413
  const nodes = [];
6298
6414
  let lastPos;
6299
6415
  for (const item of items) {
@@ -6323,12 +6439,13 @@ var LiveList = class _LiveList extends AbstractCrdt {
6323
6439
  }
6324
6440
  /**
6325
6441
  * @internal
6326
- * This function assumes that the resulting ops will be sent to the server if they have an 'opId'
6327
- * so we mutate _unacknowledgedSets to avoid potential flickering
6328
- * https://github.com/liveblocks/liveblocks/pull/1177
6442
+ * Serializes this list (and its children) into Create ops. Each child's
6443
+ * create is tagged with the "set" intent (in the loop below) so that a list
6444
+ * created and immediately mutated doesn't transiently re-show its initial
6445
+ * items (flicker, https://github.com/liveblocks/liveblocks/pull/1177).
6329
6446
  *
6330
- * This is quite unintuitive and should disappear as soon as
6331
- * we introduce an explicit LiveList.Set operation
6447
+ * This is quite unintuitive and should disappear as soon as we introduce an
6448
+ * explicit LiveList.Set operation.
6332
6449
  */
6333
6450
  _toOps(parentId, parentKey) {
6334
6451
  if (this._id === void 0) {
@@ -6344,9 +6461,9 @@ var LiveList = class _LiveList extends AbstractCrdt {
6344
6461
  ops.push(op);
6345
6462
  for (const item of this.#items) {
6346
6463
  const parentKey2 = item._getParentKeyOrThrow();
6347
- const childOps = HACK_addIntentAndDeletedIdToOperation(
6464
+ const childOps = addIntentToRootOp(
6348
6465
  item._toOps(this._id, parentKey2),
6349
- void 0
6466
+ "set"
6350
6467
  );
6351
6468
  for (const childOp of childOps) {
6352
6469
  ops.push(childOp);
@@ -6388,6 +6505,28 @@ var LiveList = class _LiveList extends AbstractCrdt {
6388
6505
  (item) => item._getParentKeyOrThrow() === position
6389
6506
  );
6390
6507
  }
6508
+ /**
6509
+ * The opId of this list's still-unacknowledged "set" op at the given position,
6510
+ * or undefined if none. Derived from the room's unacknowledgedOps (the single
6511
+ * source of truth) rather than tracked in a per-instance map. The pool's
6512
+ * position index already scopes to this list's (parentId, position); the last
6513
+ * match wins, matching the original last-write-wins map semantics.
6514
+ */
6515
+ #unacknowledgedSetOpIdAt(position) {
6516
+ if (this._pool === void 0 || this._id === void 0) {
6517
+ return void 0;
6518
+ }
6519
+ let opId;
6520
+ for (const op of this._pool.unacknowledgedOps.getByParentIdAndKey(
6521
+ this._id,
6522
+ position
6523
+ )) {
6524
+ if (op.intent === "set") {
6525
+ opId = op.opId;
6526
+ }
6527
+ }
6528
+ return opId;
6529
+ }
6391
6530
  /** @internal */
6392
6531
  _attach(id, pool) {
6393
6532
  super._attach(id, pool);
@@ -6468,13 +6607,9 @@ var LiveList = class _LiveList extends AbstractCrdt {
6468
6607
  if (deletedDelta) {
6469
6608
  delta.push(deletedDelta);
6470
6609
  }
6471
- const unacknowledgedOpId = this.#unacknowledgedSets.get(op.parentKey);
6472
- if (unacknowledgedOpId !== void 0) {
6473
- if (unacknowledgedOpId !== op.opId) {
6474
- return delta.length === 0 ? { modified: false } : { modified: makeUpdate(this, delta), reverse: [] };
6475
- } else {
6476
- this.#unacknowledgedSets.delete(op.parentKey);
6477
- }
6610
+ const unacknowledgedOpId = this.#unacknowledgedSetOpIdAt(op.parentKey);
6611
+ if (unacknowledgedOpId !== void 0 && unacknowledgedOpId !== op.opId) {
6612
+ return delta.length === 0 ? { modified: false } : { modified: makeUpdate(this, delta), reverse: [] };
6478
6613
  }
6479
6614
  const indexOfItemWithSamePosition = this._indexOfPosition(op.parentKey);
6480
6615
  const existingItem = this.#items.find((item) => item._id === op.id);
@@ -6565,11 +6700,92 @@ var LiveList = class _LiveList extends AbstractCrdt {
6565
6700
  this.#shiftItemPosition(existingItemIndex, key);
6566
6701
  }
6567
6702
  const { newItem, newIndex } = this.#createAttachItemAndSort(op, key);
6703
+ const bumpDeltas = this.#bumpUnackedPushesAbove(key);
6568
6704
  return {
6569
- modified: makeUpdate(this, [insertDelta(newIndex, newItem)]),
6705
+ modified: makeUpdate(this, [
6706
+ insertDelta(newIndex, newItem),
6707
+ ...bumpDeltas
6708
+ ]),
6570
6709
  reverse: []
6571
6710
  };
6572
6711
  }
6712
+ /**
6713
+ * This list's own still-unacknowledged pushed items (their `intent: "push"`
6714
+ * Create op is still pending in the room's unacknowledgedOps). Derived from
6715
+ * the single source of truth, so an item drops out the instant its op is
6716
+ * acked, with no per-instance membership to leak. Yielded in push order.
6717
+ *
6718
+ * Excludes ops that may already be stored on the server (they were in
6719
+ * flight when a connection died, so their fate is unknown): the bump
6720
+ * prediction assumes the server has not processed the op yet, which is only
6721
+ * guaranteed for ops sent on the current connection. For these excluded
6722
+ * ops, the server's (re-)ack states the authoritative position; predicting
6723
+ * locally could produce a wrong position that no ack would correct.
6724
+ *
6725
+ * Restricted to items currently in `#items`: a pushed node whose op is still
6726
+ * pending may have been pulled out of the list (e.g. implicitly deleted by a
6727
+ * remote set, or removed by an undo) while still living in the pool, and such
6728
+ * a node must not be repositioned.
6729
+ */
6730
+ *#unackedPushNodes() {
6731
+ if (this._pool === void 0 || this._id === void 0) {
6732
+ return;
6733
+ }
6734
+ for (const op of this._pool.unacknowledgedOps.getByParentId(this._id)) {
6735
+ if (op.intent !== "push") {
6736
+ continue;
6737
+ }
6738
+ if (this._pool.unacknowledgedOps.isPossiblyStored(op.opId)) {
6739
+ continue;
6740
+ }
6741
+ const node = this._pool.getNode(op.id);
6742
+ if (node !== void 0 && this.#items.includes(node)) {
6743
+ yield node;
6744
+ }
6745
+ }
6746
+ }
6747
+ /**
6748
+ * Optimistic no-flip for pushed items. When a remote op lands at or below my
6749
+ * still-unacked pushed items, those items must end up *after* it: FIFO plus
6750
+ * the room's serial processing guarantee the remote was processed first, so
6751
+ * my unacked pushes belong behind it. Re-chain the whole unacked-push block,
6752
+ * in push order, to sit after the highest confirmed sibling, so it keeps
6753
+ * rendering as a contiguous tail instead of getting interleaved. Local-only;
6754
+ * the real acks overwrite these keys with the (identical) server keys.
6755
+ */
6756
+ #bumpUnackedPushesAbove(remoteKey) {
6757
+ const pending = new Set(this.#unackedPushNodes());
6758
+ if (pending.size === 0) {
6759
+ return [];
6760
+ }
6761
+ let minPending;
6762
+ for (const node of pending) {
6763
+ const pos = node._parentPos;
6764
+ if (minPending === void 0 || pos < minPending) {
6765
+ minPending = pos;
6766
+ }
6767
+ }
6768
+ if (remoteKey < nn(minPending)) {
6769
+ return [];
6770
+ }
6771
+ let base;
6772
+ for (const item of this.#items) {
6773
+ if (!pending.has(item)) {
6774
+ base = item._parentPos;
6775
+ }
6776
+ }
6777
+ const deltas = [];
6778
+ for (const node of pending) {
6779
+ const previousIndex = this.#items.findIndex((item) => item === node);
6780
+ base = makePosition(base);
6781
+ this.#updateItemPosition(node, base);
6782
+ const index = this.#items.findIndex((item) => item === node);
6783
+ if (index !== previousIndex) {
6784
+ deltas.push(moveDelta(previousIndex, index, node));
6785
+ }
6786
+ }
6787
+ return deltas;
6788
+ }
6573
6789
  #applyInsertAck(op) {
6574
6790
  const existingItem = this.#items.find((item) => item._id === op.id);
6575
6791
  const key = asPos(op.parentKey);
@@ -6624,7 +6840,7 @@ var LiveList = class _LiveList extends AbstractCrdt {
6624
6840
  #applyInsertUndoRedo(op) {
6625
6841
  const { id, parentKey: key } = op;
6626
6842
  const child = creationOpToLiveNode(op);
6627
- if (_optionalChain([this, 'access', _112 => _112._pool, 'optionalAccess', _113 => _113.getNode, 'call', _114 => _114(id)]) !== void 0) {
6843
+ if (_optionalChain([this, 'access', _126 => _126._pool, 'optionalAccess', _127 => _127.getNode, 'call', _128 => _128(id)]) !== void 0) {
6628
6844
  return { modified: false };
6629
6845
  }
6630
6846
  child._attach(id, nn(this._pool));
@@ -6632,8 +6848,8 @@ var LiveList = class _LiveList extends AbstractCrdt {
6632
6848
  const existingItemIndex = this._indexOfPosition(key);
6633
6849
  let newKey = key;
6634
6850
  if (existingItemIndex !== -1) {
6635
- const before2 = _optionalChain([this, 'access', _115 => _115.#items, 'access', _116 => _116.at, 'call', _117 => _117(existingItemIndex), 'optionalAccess', _118 => _118._parentPos]);
6636
- const after2 = _optionalChain([this, 'access', _119 => _119.#items, 'access', _120 => _120.at, 'call', _121 => _121(existingItemIndex + 1), 'optionalAccess', _122 => _122._parentPos]);
6851
+ const before2 = _optionalChain([this, 'access', _129 => _129.#items, 'access', _130 => _130.at, 'call', _131 => _131(existingItemIndex), 'optionalAccess', _132 => _132._parentPos]);
6852
+ const after2 = _optionalChain([this, 'access', _133 => _133.#items, 'access', _134 => _134.at, 'call', _135 => _135(existingItemIndex + 1), 'optionalAccess', _136 => _136._parentPos]);
6637
6853
  newKey = makePosition(before2, after2);
6638
6854
  child._setParentLink(this, newKey);
6639
6855
  }
@@ -6647,10 +6863,9 @@ var LiveList = class _LiveList extends AbstractCrdt {
6647
6863
  #applySetUndoRedo(op) {
6648
6864
  const { id, parentKey: key } = op;
6649
6865
  const child = creationOpToLiveNode(op);
6650
- if (_optionalChain([this, 'access', _123 => _123._pool, 'optionalAccess', _124 => _124.getNode, 'call', _125 => _125(id)]) !== void 0) {
6866
+ if (_optionalChain([this, 'access', _137 => _137._pool, 'optionalAccess', _138 => _138.getNode, 'call', _139 => _139(id)]) !== void 0) {
6651
6867
  return { modified: false };
6652
6868
  }
6653
- this.#unacknowledgedSets.set(key, nn(op.opId));
6654
6869
  const indexOfItemWithSameKey = this._indexOfPosition(key);
6655
6870
  child._attach(id, nn(this._pool));
6656
6871
  child._setParentLink(this, key);
@@ -6660,8 +6875,9 @@ var LiveList = class _LiveList extends AbstractCrdt {
6660
6875
  existingItem._detach();
6661
6876
  this.#items.remove(existingItem);
6662
6877
  this.#items.add(child);
6663
- const reverse = HACK_addIntentAndDeletedIdToOperation(
6878
+ const reverse = addIntentToRootOp(
6664
6879
  existingItem._toOps(nn(this._id), key),
6880
+ "set",
6665
6881
  op.id
6666
6882
  );
6667
6883
  const delta = [setDelta(indexOfItemWithSameKey, child)];
@@ -6768,7 +6984,7 @@ var LiveList = class _LiveList extends AbstractCrdt {
6768
6984
  } else {
6769
6985
  this.#updateItemPositionAt(
6770
6986
  existingItemIndex,
6771
- makePosition(newKey, _optionalChain([this, 'access', _126 => _126.#items, 'access', _127 => _127.at, 'call', _128 => _128(existingItemIndex + 1), 'optionalAccess', _129 => _129._parentPos]))
6987
+ makePosition(newKey, _optionalChain([this, 'access', _140 => _140.#items, 'access', _141 => _141.at, 'call', _142 => _142(existingItemIndex + 1), 'optionalAccess', _143 => _143._parentPos]))
6772
6988
  );
6773
6989
  const previousIndex = this.#items.findIndex((item) => item === child);
6774
6990
  this.#updateItemPosition(child, newKey);
@@ -6795,7 +7011,7 @@ var LiveList = class _LiveList extends AbstractCrdt {
6795
7011
  this,
6796
7012
  makePosition(
6797
7013
  newKey,
6798
- _optionalChain([this, 'access', _130 => _130.#items, 'access', _131 => _131.at, 'call', _132 => _132(existingItemIndex + 1), 'optionalAccess', _133 => _133._parentPos])
7014
+ _optionalChain([this, 'access', _144 => _144.#items, 'access', _145 => _145.at, 'call', _146 => _146(existingItemIndex + 1), 'optionalAccess', _147 => _147._parentPos])
6799
7015
  )
6800
7016
  );
6801
7017
  this.#items.reposition(existingItem);
@@ -6819,7 +7035,7 @@ var LiveList = class _LiveList extends AbstractCrdt {
6819
7035
  existingItemIndex,
6820
7036
  makePosition(
6821
7037
  newKey,
6822
- _optionalChain([this, 'access', _134 => _134.#items, 'access', _135 => _135.at, 'call', _136 => _136(existingItemIndex + 1), 'optionalAccess', _137 => _137._parentPos])
7038
+ _optionalChain([this, 'access', _148 => _148.#items, 'access', _149 => _149.at, 'call', _150 => _150(existingItemIndex + 1), 'optionalAccess', _151 => _151._parentPos])
6823
7039
  )
6824
7040
  );
6825
7041
  }
@@ -6847,7 +7063,7 @@ var LiveList = class _LiveList extends AbstractCrdt {
6847
7063
  if (existingItemIndex !== -1) {
6848
7064
  actualNewKey = makePosition(
6849
7065
  newKey,
6850
- _optionalChain([this, 'access', _138 => _138.#items, 'access', _139 => _139.at, 'call', _140 => _140(existingItemIndex + 1), 'optionalAccess', _141 => _141._parentPos])
7066
+ _optionalChain([this, 'access', _152 => _152.#items, 'access', _153 => _153.at, 'call', _154 => _154(existingItemIndex + 1), 'optionalAccess', _155 => _155._parentPos])
6851
7067
  );
6852
7068
  }
6853
7069
  this.#updateItemPosition(child, actualNewKey);
@@ -6904,8 +7120,7 @@ var LiveList = class _LiveList extends AbstractCrdt {
6904
7120
  * @param element The element to add to the end of the LiveList.
6905
7121
  */
6906
7122
  push(element) {
6907
- _optionalChain([this, 'access', _142 => _142._pool, 'optionalAccess', _143 => _143.assertStorageIsWritable, 'call', _144 => _144()]);
6908
- return this.insert(element, this.length);
7123
+ return this.#injectAt(element, this.length, "push");
6909
7124
  }
6910
7125
  /**
6911
7126
  * Inserts one element at a specified index.
@@ -6913,14 +7128,23 @@ var LiveList = class _LiveList extends AbstractCrdt {
6913
7128
  * @param index The index at which you want to insert the element.
6914
7129
  */
6915
7130
  insert(element, index) {
6916
- _optionalChain([this, 'access', _145 => _145._pool, 'optionalAccess', _146 => _146.assertStorageIsWritable, 'call', _147 => _147()]);
7131
+ return this.#injectAt(element, index, "insert");
7132
+ }
7133
+ /**
7134
+ * Shared implementation of `insert` and `push`. A `"push"` intent leaves the
7135
+ * client-computed position untouched (so optimistic rendering is unchanged),
7136
+ * but tags the Op so the server appends it to the true end of the list
7137
+ * instead of resolving its position against the client's stale view.
7138
+ */
7139
+ #injectAt(element, index, intent) {
7140
+ _optionalChain([this, 'access', _156 => _156._pool, 'optionalAccess', _157 => _157.assertStorageIsWritable, 'call', _158 => _158()]);
6917
7141
  if (index < 0 || index > this.#items.length) {
6918
7142
  throw new Error(
6919
7143
  `Cannot insert list item at index "${index}". index should be between 0 and ${this.#items.length}`
6920
7144
  );
6921
7145
  }
6922
- const before2 = _optionalChain([this, 'access', _148 => _148.#items, 'access', _149 => _149.at, 'call', _150 => _150(index - 1), 'optionalAccess', _151 => _151._parentPos]);
6923
- const after2 = _optionalChain([this, 'access', _152 => _152.#items, 'access', _153 => _153.at, 'call', _154 => _154(index), 'optionalAccess', _155 => _155._parentPos]);
7146
+ const before2 = _optionalChain([this, 'access', _159 => _159.#items, 'access', _160 => _160.at, 'call', _161 => _161(index - 1), 'optionalAccess', _162 => _162._parentPos]);
7147
+ const after2 = _optionalChain([this, 'access', _163 => _163.#items, 'access', _164 => _164.at, 'call', _165 => _165(index), 'optionalAccess', _166 => _166._parentPos]);
6924
7148
  const position = makePosition(before2, after2);
6925
7149
  const value = lsonToLiveNode(element);
6926
7150
  value._setParentLink(this, position);
@@ -6928,8 +7152,9 @@ var LiveList = class _LiveList extends AbstractCrdt {
6928
7152
  if (this._pool && this._id) {
6929
7153
  const id = this._pool.generateId();
6930
7154
  value._attach(id, this._pool);
7155
+ const ops = value._toOpsWithOpId(this._id, position, this._pool);
6931
7156
  this._pool.dispatch(
6932
- value._toOpsWithOpId(this._id, position, this._pool),
7157
+ intent === "push" ? addIntentToRootOp(ops, "push") : ops,
6933
7158
  [{ type: OpCode.DELETE_CRDT, id }],
6934
7159
  /* @__PURE__ */ new Map([
6935
7160
  [this._id, makeUpdate(this, [insertDelta(index, value)])]
@@ -6943,7 +7168,7 @@ var LiveList = class _LiveList extends AbstractCrdt {
6943
7168
  * @param targetIndex The index where the element should be after moving.
6944
7169
  */
6945
7170
  move(index, targetIndex) {
6946
- _optionalChain([this, 'access', _156 => _156._pool, 'optionalAccess', _157 => _157.assertStorageIsWritable, 'call', _158 => _158()]);
7171
+ _optionalChain([this, 'access', _167 => _167._pool, 'optionalAccess', _168 => _168.assertStorageIsWritable, 'call', _169 => _169()]);
6947
7172
  if (targetIndex < 0) {
6948
7173
  throw new Error("targetIndex cannot be less than 0");
6949
7174
  }
@@ -6961,11 +7186,11 @@ var LiveList = class _LiveList extends AbstractCrdt {
6961
7186
  let beforePosition = null;
6962
7187
  let afterPosition = null;
6963
7188
  if (index < targetIndex) {
6964
- afterPosition = targetIndex === this.#items.length - 1 ? void 0 : _optionalChain([this, 'access', _159 => _159.#items, 'access', _160 => _160.at, 'call', _161 => _161(targetIndex + 1), 'optionalAccess', _162 => _162._parentPos]);
7189
+ afterPosition = targetIndex === this.#items.length - 1 ? void 0 : _optionalChain([this, 'access', _170 => _170.#items, 'access', _171 => _171.at, 'call', _172 => _172(targetIndex + 1), 'optionalAccess', _173 => _173._parentPos]);
6965
7190
  beforePosition = this.#items.at(targetIndex)._parentPos;
6966
7191
  } else {
6967
7192
  afterPosition = this.#items.at(targetIndex)._parentPos;
6968
- beforePosition = targetIndex === 0 ? void 0 : _optionalChain([this, 'access', _163 => _163.#items, 'access', _164 => _164.at, 'call', _165 => _165(targetIndex - 1), 'optionalAccess', _166 => _166._parentPos]);
7193
+ beforePosition = targetIndex === 0 ? void 0 : _optionalChain([this, 'access', _174 => _174.#items, 'access', _175 => _175.at, 'call', _176 => _176(targetIndex - 1), 'optionalAccess', _177 => _177._parentPos]);
6969
7194
  }
6970
7195
  const position = makePosition(beforePosition, afterPosition);
6971
7196
  const item = this.#items.at(index);
@@ -7000,7 +7225,7 @@ var LiveList = class _LiveList extends AbstractCrdt {
7000
7225
  * @param index The index of the element to delete
7001
7226
  */
7002
7227
  delete(index) {
7003
- _optionalChain([this, 'access', _167 => _167._pool, 'optionalAccess', _168 => _168.assertStorageIsWritable, 'call', _169 => _169()]);
7228
+ _optionalChain([this, 'access', _178 => _178._pool, 'optionalAccess', _179 => _179.assertStorageIsWritable, 'call', _180 => _180()]);
7004
7229
  if (index < 0 || index >= this.#items.length) {
7005
7230
  throw new Error(
7006
7231
  `Cannot delete list item at index "${index}". index should be between 0 and ${this.#items.length - 1}`
@@ -7033,7 +7258,7 @@ var LiveList = class _LiveList extends AbstractCrdt {
7033
7258
  }
7034
7259
  }
7035
7260
  clear() {
7036
- _optionalChain([this, 'access', _170 => _170._pool, 'optionalAccess', _171 => _171.assertStorageIsWritable, 'call', _172 => _172()]);
7261
+ _optionalChain([this, 'access', _181 => _181._pool, 'optionalAccess', _182 => _182.assertStorageIsWritable, 'call', _183 => _183()]);
7037
7262
  if (this._pool) {
7038
7263
  const ops = [];
7039
7264
  const reverseOps = [];
@@ -7067,7 +7292,7 @@ var LiveList = class _LiveList extends AbstractCrdt {
7067
7292
  }
7068
7293
  }
7069
7294
  set(index, item) {
7070
- _optionalChain([this, 'access', _173 => _173._pool, 'optionalAccess', _174 => _174.assertStorageIsWritable, 'call', _175 => _175()]);
7295
+ _optionalChain([this, 'access', _184 => _184._pool, 'optionalAccess', _185 => _185.assertStorageIsWritable, 'call', _186 => _186()]);
7071
7296
  if (index < 0 || index >= this.#items.length) {
7072
7297
  throw new Error(
7073
7298
  `Cannot set list item at index "${index}". index should be between 0 and ${this.#items.length - 1}`
@@ -7087,13 +7312,14 @@ var LiveList = class _LiveList extends AbstractCrdt {
7087
7312
  value._attach(id, this._pool);
7088
7313
  const storageUpdates = /* @__PURE__ */ new Map();
7089
7314
  storageUpdates.set(this._id, makeUpdate(this, [setDelta(index, value)]));
7090
- const ops = HACK_addIntentAndDeletedIdToOperation(
7315
+ const ops = addIntentToRootOp(
7091
7316
  value._toOpsWithOpId(this._id, position, this._pool),
7317
+ "set",
7092
7318
  existingId
7093
7319
  );
7094
- this.#unacknowledgedSets.set(position, nn(ops[0].opId));
7095
- const reverseOps = HACK_addIntentAndDeletedIdToOperation(
7320
+ const reverseOps = addIntentToRootOp(
7096
7321
  existingItem._toOps(this._id, position),
7322
+ "set",
7097
7323
  id
7098
7324
  );
7099
7325
  this._pool.dispatch(ops, reverseOps, storageUpdates);
@@ -7225,7 +7451,7 @@ var LiveList = class _LiveList extends AbstractCrdt {
7225
7451
  #shiftItemPosition(index, key) {
7226
7452
  const shiftedPosition = makePosition(
7227
7453
  key,
7228
- this.#items.length > index + 1 ? _optionalChain([this, 'access', _176 => _176.#items, 'access', _177 => _177.at, 'call', _178 => _178(index + 1), 'optionalAccess', _179 => _179._parentPos]) : void 0
7454
+ this.#items.length > index + 1 ? _optionalChain([this, 'access', _187 => _187.#items, 'access', _188 => _188.at, 'call', _189 => _189(index + 1), 'optionalAccess', _190 => _190._parentPos]) : void 0
7229
7455
  );
7230
7456
  this.#updateItemPositionAt(index, shiftedPosition);
7231
7457
  }
@@ -7294,15 +7520,11 @@ function moveDelta(previousIndex, index, item) {
7294
7520
  previousIndex
7295
7521
  };
7296
7522
  }
7297
- function HACK_addIntentAndDeletedIdToOperation(ops, deletedId) {
7523
+ function addIntentToRootOp(ops, intent, deletedId) {
7298
7524
  return ops.map((op, index) => {
7299
7525
  if (index === 0) {
7300
7526
  const firstOp = op;
7301
- return {
7302
- ...firstOp,
7303
- intent: "set",
7304
- deletedId
7305
- };
7527
+ return { ...firstOp, intent, deletedId };
7306
7528
  } else {
7307
7529
  return op;
7308
7530
  }
@@ -7478,7 +7700,7 @@ var LiveMap = class _LiveMap extends AbstractCrdt {
7478
7700
  * @param value The value of the element to add. Should be serializable to JSON.
7479
7701
  */
7480
7702
  set(key, value) {
7481
- _optionalChain([this, 'access', _180 => _180._pool, 'optionalAccess', _181 => _181.assertStorageIsWritable, 'call', _182 => _182()]);
7703
+ _optionalChain([this, 'access', _191 => _191._pool, 'optionalAccess', _192 => _192.assertStorageIsWritable, 'call', _193 => _193()]);
7482
7704
  const oldValue = this.#map.get(key);
7483
7705
  if (oldValue) {
7484
7706
  oldValue._detach();
@@ -7524,7 +7746,7 @@ var LiveMap = class _LiveMap extends AbstractCrdt {
7524
7746
  * @returns true if an element existed and has been removed, or false if the element does not exist.
7525
7747
  */
7526
7748
  delete(key) {
7527
- _optionalChain([this, 'access', _183 => _183._pool, 'optionalAccess', _184 => _184.assertStorageIsWritable, 'call', _185 => _185()]);
7749
+ _optionalChain([this, 'access', _194 => _194._pool, 'optionalAccess', _195 => _195.assertStorageIsWritable, 'call', _196 => _196()]);
7528
7750
  const item = this.#map.get(key);
7529
7751
  if (item === void 0) {
7530
7752
  return false;
@@ -7954,6 +8176,7 @@ var LiveObject = (_class2 = class _LiveObject extends AbstractCrdt {
7954
8176
  const id = nn(this._id);
7955
8177
  const parentKey = nn(child._parentKey);
7956
8178
  const reverse = child._toOps(id, parentKey);
8179
+ const deletedItem = liveNodeToLson(child);
7957
8180
  for (const [key, value] of this.#synced) {
7958
8181
  if (value === child) {
7959
8182
  this.#synced.delete(key);
@@ -7965,7 +8188,7 @@ var LiveObject = (_class2 = class _LiveObject extends AbstractCrdt {
7965
8188
  node: this,
7966
8189
  type: "LiveObject",
7967
8190
  updates: {
7968
- [parentKey]: { type: "delete" }
8191
+ [parentKey]: { type: "delete", deletedItem }
7969
8192
  }
7970
8193
  };
7971
8194
  return { modified: storageUpdate, reverse };
@@ -8135,20 +8358,20 @@ var LiveObject = (_class2 = class _LiveObject extends AbstractCrdt {
8135
8358
  * Caveat: this method will not add changes to the undo/redo stack.
8136
8359
  */
8137
8360
  setLocal(key, value) {
8138
- _optionalChain([this, 'access', _186 => _186._pool, 'optionalAccess', _187 => _187.assertStorageIsWritable, 'call', _188 => _188()]);
8361
+ _optionalChain([this, 'access', _197 => _197._pool, 'optionalAccess', _198 => _198.assertStorageIsWritable, 'call', _199 => _199()]);
8139
8362
  const deleteResult = this.#prepareDelete(key);
8140
8363
  this.#local.set(key, value);
8141
8364
  this.invalidate();
8142
8365
  if (this._pool !== void 0 && this._id !== void 0) {
8143
- const ops = _nullishCoalesce(_optionalChain([deleteResult, 'optionalAccess', _189 => _189[0]]), () => ( []));
8144
- const reverse = _nullishCoalesce(_optionalChain([deleteResult, 'optionalAccess', _190 => _190[1]]), () => ( []));
8145
- const storageUpdates = _nullishCoalesce(_optionalChain([deleteResult, 'optionalAccess', _191 => _191[2]]), () => ( /* @__PURE__ */ new Map()));
8366
+ const ops = _nullishCoalesce(_optionalChain([deleteResult, 'optionalAccess', _200 => _200[0]]), () => ( []));
8367
+ const reverse = _nullishCoalesce(_optionalChain([deleteResult, 'optionalAccess', _201 => _201[1]]), () => ( []));
8368
+ const storageUpdates = _nullishCoalesce(_optionalChain([deleteResult, 'optionalAccess', _202 => _202[2]]), () => ( /* @__PURE__ */ new Map()));
8146
8369
  const existing = storageUpdates.get(this._id);
8147
8370
  storageUpdates.set(this._id, {
8148
8371
  node: this,
8149
8372
  type: "LiveObject",
8150
8373
  updates: {
8151
- ..._optionalChain([existing, 'optionalAccess', _192 => _192.updates]),
8374
+ ..._optionalChain([existing, 'optionalAccess', _203 => _203.updates]),
8152
8375
  [key]: { type: "update" }
8153
8376
  }
8154
8377
  });
@@ -8168,7 +8391,7 @@ var LiveObject = (_class2 = class _LiveObject extends AbstractCrdt {
8168
8391
  * #synced or pool/id are unavailable. Does NOT dispatch.
8169
8392
  */
8170
8393
  #prepareDelete(key) {
8171
- _optionalChain([this, 'access', _193 => _193._pool, 'optionalAccess', _194 => _194.assertStorageIsWritable, 'call', _195 => _195()]);
8394
+ _optionalChain([this, 'access', _204 => _204._pool, 'optionalAccess', _205 => _205.assertStorageIsWritable, 'call', _206 => _206()]);
8172
8395
  const k = key;
8173
8396
  if (this.#local.has(k) && !this.#synced.has(k)) {
8174
8397
  const oldValue2 = this.#local.get(k);
@@ -8244,7 +8467,7 @@ var LiveObject = (_class2 = class _LiveObject extends AbstractCrdt {
8244
8467
  const result = this.#prepareDelete(key);
8245
8468
  if (result) {
8246
8469
  const [ops, reverse, storageUpdates] = result;
8247
- _optionalChain([this, 'access', _196 => _196._pool, 'optionalAccess', _197 => _197.dispatch, 'call', _198 => _198(ops, reverse, storageUpdates)]);
8470
+ _optionalChain([this, 'access', _207 => _207._pool, 'optionalAccess', _208 => _208.dispatch, 'call', _209 => _209(ops, reverse, storageUpdates)]);
8248
8471
  }
8249
8472
  }
8250
8473
  /**
@@ -8252,7 +8475,7 @@ var LiveObject = (_class2 = class _LiveObject extends AbstractCrdt {
8252
8475
  * @param patch The object used to overrides properties
8253
8476
  */
8254
8477
  update(patch) {
8255
- _optionalChain([this, 'access', _199 => _199._pool, 'optionalAccess', _200 => _200.assertStorageIsWritable, 'call', _201 => _201()]);
8478
+ _optionalChain([this, 'access', _210 => _210._pool, 'optionalAccess', _211 => _211.assertStorageIsWritable, 'call', _212 => _212()]);
8256
8479
  if (_LiveObject.detectLargeObjects) {
8257
8480
  const data = {};
8258
8481
  for (const [key, value] of this.#synced) {
@@ -8542,6 +8765,60 @@ function lsonToLiveNode(value) {
8542
8765
  return new LiveRegister(value);
8543
8766
  }
8544
8767
  }
8768
+ function dumpPool(pool) {
8769
+ const rows = Array.from(pool.nodes.values(), (node) => {
8770
+ const parent = node.parent;
8771
+ const parentId = parent.type === "HasParent" ? _nullishCoalesce(parent.node._id, () => ( "?")) : parent.type === "Orphaned" ? "<orphaned>" : "-";
8772
+ let value;
8773
+ if (node instanceof LiveRegister) {
8774
+ value = stringifyOrLog(node.data);
8775
+ } else if (node instanceof LiveList) {
8776
+ value = "<LiveList>";
8777
+ } else if (node instanceof LiveMap) {
8778
+ value = "<LiveMap>";
8779
+ } else {
8780
+ value = "<LiveObject>";
8781
+ }
8782
+ return { id: nn(node._id), parentId, key: _nullishCoalesce(node._parentKey, () => ( "")), value };
8783
+ });
8784
+ rows.sort((a, b) => {
8785
+ if (a.parentId !== b.parentId) return a.parentId < b.parentId ? -1 : 1;
8786
+ if (a.key !== b.key) return a.key < b.key ? -1 : 1;
8787
+ return 0;
8788
+ });
8789
+ return rows.map(
8790
+ (r) => ` ${r.id} parent=${r.parentId} key=${r.key || "\u2014"} ${r.value}`
8791
+ ).join("\n");
8792
+ }
8793
+ function isJsonEq(a, b) {
8794
+ if (a === b) {
8795
+ return true;
8796
+ }
8797
+ if (typeof a !== "object" || a === null || typeof b !== "object" || b === null) {
8798
+ return false;
8799
+ }
8800
+ if (Array.isArray(a) || Array.isArray(b)) {
8801
+ if (!Array.isArray(a) || !Array.isArray(b) || a.length !== b.length) {
8802
+ return false;
8803
+ }
8804
+ for (let i = 0; i < a.length; i++) {
8805
+ if (!isJsonEq(a[i], b[i])) {
8806
+ return false;
8807
+ }
8808
+ }
8809
+ return true;
8810
+ }
8811
+ const aKeys = Object.keys(a);
8812
+ if (aKeys.length !== Object.keys(b).length) {
8813
+ return false;
8814
+ }
8815
+ for (const key of aKeys) {
8816
+ if (!isJsonEq(a[key], b[key])) {
8817
+ return false;
8818
+ }
8819
+ }
8820
+ return true;
8821
+ }
8545
8822
  function getTreesDiffOperations(currentItems, newItems) {
8546
8823
  const ops = [];
8547
8824
  currentItems.forEach((_, id) => {
@@ -8553,12 +8830,28 @@ function getTreesDiffOperations(currentItems, newItems) {
8553
8830
  const currentCrdt = currentItems.get(id);
8554
8831
  if (currentCrdt) {
8555
8832
  if (crdt.type === CrdtType.OBJECT) {
8556
- if (currentCrdt.type !== CrdtType.OBJECT || stringifyOrLog(crdt.data) !== stringifyOrLog(currentCrdt.data)) {
8557
- ops.push({
8558
- type: OpCode.UPDATE_OBJECT,
8559
- id,
8560
- data: crdt.data
8561
- });
8833
+ if (currentCrdt.type !== CrdtType.OBJECT) {
8834
+ ops.push({ type: OpCode.UPDATE_OBJECT, id, data: crdt.data });
8835
+ } else {
8836
+ const changed = /* @__PURE__ */ new Map();
8837
+ for (const key of Object.keys(crdt.data)) {
8838
+ const value = crdt.data[key];
8839
+ if (value !== void 0 && !isJsonEq(value, currentCrdt.data[key])) {
8840
+ changed.set(key, value);
8841
+ }
8842
+ }
8843
+ if (changed.size > 0) {
8844
+ ops.push({
8845
+ type: OpCode.UPDATE_OBJECT,
8846
+ id,
8847
+ data: Object.fromEntries(changed)
8848
+ });
8849
+ }
8850
+ for (const key of Object.keys(currentCrdt.data)) {
8851
+ if (!(key in crdt.data)) {
8852
+ ops.push({ type: OpCode.DELETE_OBJECT_KEY, id, key });
8853
+ }
8854
+ }
8562
8855
  }
8563
8856
  }
8564
8857
  if (crdt.parentKey !== currentCrdt.parentKey) {
@@ -8669,7 +8962,7 @@ function sendToPanel(message, options) {
8669
8962
  ...message,
8670
8963
  source: "liveblocks-devtools-client"
8671
8964
  };
8672
- if (!(_optionalChain([options, 'optionalAccess', _202 => _202.force]) || _bridgeActive)) {
8965
+ if (!(_optionalChain([options, 'optionalAccess', _213 => _213.force]) || _bridgeActive)) {
8673
8966
  return;
8674
8967
  }
8675
8968
  window.postMessage(fullMsg, "*");
@@ -8677,7 +8970,7 @@ function sendToPanel(message, options) {
8677
8970
  var eventSource = makeEventSource();
8678
8971
  if (process.env.NODE_ENV !== "production" && typeof window !== "undefined") {
8679
8972
  window.addEventListener("message", (event) => {
8680
- if (event.source === window && _optionalChain([event, 'access', _203 => _203.data, 'optionalAccess', _204 => _204.source]) === "liveblocks-devtools-panel") {
8973
+ if (event.source === window && _optionalChain([event, 'access', _214 => _214.data, 'optionalAccess', _215 => _215.source]) === "liveblocks-devtools-panel") {
8681
8974
  eventSource.notify(event.data);
8682
8975
  } else {
8683
8976
  }
@@ -8819,7 +9112,7 @@ function fullSync(room) {
8819
9112
  msg: "room::sync::full",
8820
9113
  roomId: room.id,
8821
9114
  status: room.getStatus(),
8822
- storage: _nullishCoalesce(_optionalChain([root, 'optionalAccess', _205 => _205.toTreeNode, 'call', _206 => _206("root"), 'access', _207 => _207.payload]), () => ( null)),
9115
+ storage: _nullishCoalesce(_optionalChain([root, 'optionalAccess', _216 => _216.toTreeNode, 'call', _217 => _217("root"), 'access', _218 => _218.payload]), () => ( null)),
8823
9116
  me,
8824
9117
  others
8825
9118
  });
@@ -9498,15 +9791,15 @@ function installBackgroundTabSpy() {
9498
9791
  const doc = typeof document !== "undefined" ? document : void 0;
9499
9792
  const inBackgroundSince = { current: null };
9500
9793
  function onVisibilityChange() {
9501
- if (_optionalChain([doc, 'optionalAccess', _208 => _208.visibilityState]) === "hidden") {
9794
+ if (_optionalChain([doc, 'optionalAccess', _219 => _219.visibilityState]) === "hidden") {
9502
9795
  inBackgroundSince.current = _nullishCoalesce(inBackgroundSince.current, () => ( Date.now()));
9503
9796
  } else {
9504
9797
  inBackgroundSince.current = null;
9505
9798
  }
9506
9799
  }
9507
- _optionalChain([doc, 'optionalAccess', _209 => _209.addEventListener, 'call', _210 => _210("visibilitychange", onVisibilityChange)]);
9800
+ _optionalChain([doc, 'optionalAccess', _220 => _220.addEventListener, 'call', _221 => _221("visibilitychange", onVisibilityChange)]);
9508
9801
  const unsub = () => {
9509
- _optionalChain([doc, 'optionalAccess', _211 => _211.removeEventListener, 'call', _212 => _212("visibilitychange", onVisibilityChange)]);
9802
+ _optionalChain([doc, 'optionalAccess', _222 => _222.removeEventListener, 'call', _223 => _223("visibilitychange", onVisibilityChange)]);
9510
9803
  };
9511
9804
  return [inBackgroundSince, unsub];
9512
9805
  }
@@ -9551,6 +9844,7 @@ function createRoom(options, config) {
9551
9844
  delegates,
9552
9845
  config.enableDebugLogging
9553
9846
  );
9847
+ const unacknowledgedOps = new UnacknowledgedOps();
9554
9848
  const context = {
9555
9849
  buffer: {
9556
9850
  flushTimerID: void 0,
@@ -9578,14 +9872,15 @@ function createRoom(options, config) {
9578
9872
  pool: createManagedPool(roomId, {
9579
9873
  getCurrentConnectionId,
9580
9874
  onDispatch,
9581
- isStorageWritable
9875
+ isStorageWritable,
9876
+ unacknowledgedOps
9582
9877
  }),
9583
9878
  root: void 0,
9584
9879
  undoStack: [],
9585
9880
  redoStack: [],
9586
9881
  pausedHistory: null,
9587
9882
  activeBatch: null,
9588
- unacknowledgedOps: /* @__PURE__ */ new Map()
9883
+ unacknowledgedOps
9589
9884
  };
9590
9885
  const nodeMapBuffer = makeNodeMapBuffer();
9591
9886
  const stopwatch = config.enableDebugLogging ? makeStopWatch() : void 0;
@@ -9652,6 +9947,7 @@ function createRoom(options, config) {
9652
9947
  }
9653
9948
  function onDidDisconnect() {
9654
9949
  clearTimeout(context.buffer.flushTimerID);
9950
+ context.unacknowledgedOps.markAllAsPossiblyStored();
9655
9951
  }
9656
9952
  managedSocket.events.onMessage.subscribe(handleServerMessage);
9657
9953
  managedSocket.events.statusDidChange.subscribe(onStatusDidChange);
@@ -9697,7 +9993,7 @@ function createRoom(options, config) {
9697
9993
  }
9698
9994
  }
9699
9995
  function isStorageWritable() {
9700
- const scopes = _optionalChain([context, 'access', _213 => _213.dynamicSessionInfoSig, 'access', _214 => _214.get, 'call', _215 => _215(), 'optionalAccess', _216 => _216.scopes]);
9996
+ const scopes = _optionalChain([context, 'access', _224 => _224.dynamicSessionInfoSig, 'access', _225 => _225.get, 'call', _226 => _226(), 'optionalAccess', _227 => _227.scopes]);
9701
9997
  return scopes !== void 0 ? hasPermissionCapability(scopes, "storage", "write") : true;
9702
9998
  }
9703
9999
  const eventHub = {
@@ -9809,7 +10105,7 @@ function createRoom(options, config) {
9809
10105
  context.pool
9810
10106
  );
9811
10107
  }
9812
- const canWrite = _nullishCoalesce(_optionalChain([self, 'access', _217 => _217.get, 'call', _218 => _218(), 'optionalAccess', _219 => _219.canWrite]), () => ( true));
10108
+ const canWrite = _nullishCoalesce(_optionalChain([self, 'access', _228 => _228.get, 'call', _229 => _229(), 'optionalAccess', _230 => _230.canWrite]), () => ( true));
9813
10109
  const root = context.root;
9814
10110
  disableHistory(() => {
9815
10111
  for (const key in context.initialStorage) {
@@ -10014,7 +10310,7 @@ function createRoom(options, config) {
10014
10310
  }
10015
10311
  context.myPresence.patch(patch);
10016
10312
  if (context.activeBatch) {
10017
- if (_optionalChain([options2, 'optionalAccess', _220 => _220.addToHistory])) {
10313
+ if (_optionalChain([options2, 'optionalAccess', _231 => _231.addToHistory])) {
10018
10314
  context.activeBatch.reverseOps.pushLeft({
10019
10315
  type: "presence",
10020
10316
  data: oldValues
@@ -10023,7 +10319,7 @@ function createRoom(options, config) {
10023
10319
  context.activeBatch.updates.presence = true;
10024
10320
  } else {
10025
10321
  flushNowOrSoon();
10026
- if (_optionalChain([options2, 'optionalAccess', _221 => _221.addToHistory])) {
10322
+ if (_optionalChain([options2, 'optionalAccess', _232 => _232.addToHistory])) {
10027
10323
  addToUndoStack([{ type: "presence", data: oldValues }]);
10028
10324
  }
10029
10325
  notify({ presence: true });
@@ -10132,12 +10428,11 @@ function createRoom(options, config) {
10132
10428
  }
10133
10429
  }
10134
10430
  function applyAndSendOfflineOps(unackedOps) {
10135
- if (unackedOps.size === 0) {
10431
+ if (unackedOps.length === 0) {
10136
10432
  return;
10137
10433
  }
10138
10434
  const messages = [];
10139
- const inOps = Array.from(unackedOps.values());
10140
- const result = applyLocalOps(inOps);
10435
+ const result = applyLocalOps(unackedOps);
10141
10436
  messages.push({
10142
10437
  type: ClientMsgCode.UPDATE_STORAGE,
10143
10438
  ops: result.opsToEmit
@@ -10201,11 +10496,11 @@ function createRoom(options, config) {
10201
10496
  break;
10202
10497
  }
10203
10498
  case ServerMsgCode.STORAGE_CHUNK:
10204
- _optionalChain([stopwatch, 'optionalAccess', _222 => _222.lap, 'call', _223 => _223()]);
10499
+ _optionalChain([stopwatch, 'optionalAccess', _233 => _233.lap, 'call', _234 => _234()]);
10205
10500
  nodeMapBuffer.append(compactNodesToNodeStream(message.nodes));
10206
10501
  break;
10207
10502
  case ServerMsgCode.STORAGE_STREAM_END: {
10208
- const timing = _optionalChain([stopwatch, 'optionalAccess', _224 => _224.stop, 'call', _225 => _225()]);
10503
+ const timing = _optionalChain([stopwatch, 'optionalAccess', _235 => _235.stop, 'call', _236 => _236()]);
10209
10504
  if (timing) {
10210
10505
  const ms = (v) => `${v.toFixed(1)}ms`;
10211
10506
  const rest = timing.laps.slice(1);
@@ -10340,11 +10635,11 @@ function createRoom(options, config) {
10340
10635
  } else if (pendingFeedsRequests.has(requestId)) {
10341
10636
  const pending = pendingFeedsRequests.get(requestId);
10342
10637
  pendingFeedsRequests.delete(requestId);
10343
- _optionalChain([pending, 'optionalAccess', _226 => _226.reject, 'call', _227 => _227(err)]);
10638
+ _optionalChain([pending, 'optionalAccess', _237 => _237.reject, 'call', _238 => _238(err)]);
10344
10639
  } else if (pendingFeedMessagesRequests.has(requestId)) {
10345
10640
  const pending = pendingFeedMessagesRequests.get(requestId);
10346
10641
  pendingFeedMessagesRequests.delete(requestId);
10347
- _optionalChain([pending, 'optionalAccess', _228 => _228.reject, 'call', _229 => _229(err)]);
10642
+ _optionalChain([pending, 'optionalAccess', _239 => _239.reject, 'call', _240 => _240(err)]);
10348
10643
  }
10349
10644
  eventHub.feeds.notify(message);
10350
10645
  break;
@@ -10361,7 +10656,7 @@ function createRoom(options, config) {
10361
10656
  const storageOps = context.buffer.storageOperations;
10362
10657
  if (storageOps.length > 0) {
10363
10658
  for (const op of storageOps) {
10364
- context.unacknowledgedOps.set(op.opId, op);
10659
+ context.unacknowledgedOps.add(op);
10365
10660
  }
10366
10661
  notifyStorageStatus();
10367
10662
  }
@@ -10498,10 +10793,10 @@ function createRoom(options, config) {
10498
10793
  timeoutId,
10499
10794
  kind,
10500
10795
  feedId,
10501
- messageId: _optionalChain([options2, 'optionalAccess', _230 => _230.messageId]),
10502
- expectedClientMessageId: _optionalChain([options2, 'optionalAccess', _231 => _231.expectedClientMessageId])
10796
+ messageId: _optionalChain([options2, 'optionalAccess', _241 => _241.messageId]),
10797
+ expectedClientMessageId: _optionalChain([options2, 'optionalAccess', _242 => _242.expectedClientMessageId])
10503
10798
  });
10504
- if (kind === "add-message" && _optionalChain([options2, 'optionalAccess', _232 => _232.expectedClientMessageId]) === void 0) {
10799
+ if (kind === "add-message" && _optionalChain([options2, 'optionalAccess', _243 => _243.expectedClientMessageId]) === void 0) {
10505
10800
  const q = _nullishCoalesce(pendingAddMessageFifoByFeed.get(feedId), () => ( []));
10506
10801
  q.push(requestId);
10507
10802
  pendingAddMessageFifoByFeed.set(feedId, q);
@@ -10552,10 +10847,10 @@ function createRoom(options, config) {
10552
10847
  }
10553
10848
  if (!matched) {
10554
10849
  const q = pendingAddMessageFifoByFeed.get(message.feedId);
10555
- const headId = _optionalChain([q, 'optionalAccess', _233 => _233[0]]);
10850
+ const headId = _optionalChain([q, 'optionalAccess', _244 => _244[0]]);
10556
10851
  if (headId !== void 0) {
10557
10852
  const pending = pendingFeedMutations.get(headId);
10558
- if (_optionalChain([pending, 'optionalAccess', _234 => _234.kind]) === "add-message" && pending.expectedClientMessageId === void 0) {
10853
+ if (_optionalChain([pending, 'optionalAccess', _245 => _245.kind]) === "add-message" && pending.expectedClientMessageId === void 0) {
10559
10854
  settleFeedMutation(headId, "ok");
10560
10855
  }
10561
10856
  }
@@ -10588,10 +10883,10 @@ function createRoom(options, config) {
10588
10883
  }
10589
10884
  }
10590
10885
  function processInitialStorage(nodes) {
10591
- const unacknowledgedOps = new Map(context.unacknowledgedOps);
10886
+ const unacknowledgedOps2 = [...context.unacknowledgedOps.values()];
10592
10887
  createOrUpdateRootFromMessage(nodes);
10593
- applyAndSendOfflineOps(unacknowledgedOps);
10594
- _optionalChain([_resolveStoragePromise, 'optionalCall', _235 => _235()]);
10888
+ applyAndSendOfflineOps(unacknowledgedOps2);
10889
+ _optionalChain([_resolveStoragePromise, 'optionalCall', _246 => _246()]);
10595
10890
  notifyStorageStatus();
10596
10891
  eventHub.storageDidLoad.notify();
10597
10892
  }
@@ -10609,7 +10904,7 @@ function createRoom(options, config) {
10609
10904
  } else if (!messages.some((msg) => msg.type === ClientMsgCode.FETCH_STORAGE)) {
10610
10905
  messages.push({ type: ClientMsgCode.FETCH_STORAGE });
10611
10906
  nodeMapBuffer.take();
10612
- _optionalChain([stopwatch, 'optionalAccess', _236 => _236.start, 'call', _237 => _237()]);
10907
+ _optionalChain([stopwatch, 'optionalAccess', _247 => _247.start, 'call', _248 => _248()]);
10613
10908
  }
10614
10909
  if (options2.flush) {
10615
10910
  flushNowOrSoon();
@@ -10665,10 +10960,10 @@ function createRoom(options, config) {
10665
10960
  const message = {
10666
10961
  type: ClientMsgCode.FETCH_FEEDS,
10667
10962
  requestId,
10668
- cursor: _optionalChain([options2, 'optionalAccess', _238 => _238.cursor]),
10669
- since: _optionalChain([options2, 'optionalAccess', _239 => _239.since]),
10670
- limit: _optionalChain([options2, 'optionalAccess', _240 => _240.limit]),
10671
- metadata: _optionalChain([options2, 'optionalAccess', _241 => _241.metadata])
10963
+ cursor: _optionalChain([options2, 'optionalAccess', _249 => _249.cursor]),
10964
+ since: _optionalChain([options2, 'optionalAccess', _250 => _250.since]),
10965
+ limit: _optionalChain([options2, 'optionalAccess', _251 => _251.limit]),
10966
+ metadata: _optionalChain([options2, 'optionalAccess', _252 => _252.metadata])
10672
10967
  };
10673
10968
  context.buffer.messages.push(message);
10674
10969
  flushNowOrSoon();
@@ -10688,9 +10983,9 @@ function createRoom(options, config) {
10688
10983
  type: ClientMsgCode.FETCH_FEED_MESSAGES,
10689
10984
  requestId,
10690
10985
  feedId,
10691
- cursor: _optionalChain([options2, 'optionalAccess', _242 => _242.cursor]),
10692
- since: _optionalChain([options2, 'optionalAccess', _243 => _243.since]),
10693
- limit: _optionalChain([options2, 'optionalAccess', _244 => _244.limit])
10986
+ cursor: _optionalChain([options2, 'optionalAccess', _253 => _253.cursor]),
10987
+ since: _optionalChain([options2, 'optionalAccess', _254 => _254.since]),
10988
+ limit: _optionalChain([options2, 'optionalAccess', _255 => _255.limit])
10694
10989
  };
10695
10990
  context.buffer.messages.push(message);
10696
10991
  flushNowOrSoon();
@@ -10709,8 +11004,8 @@ function createRoom(options, config) {
10709
11004
  type: ClientMsgCode.ADD_FEED,
10710
11005
  requestId,
10711
11006
  feedId,
10712
- metadata: _optionalChain([options2, 'optionalAccess', _245 => _245.metadata]),
10713
- createdAt: _optionalChain([options2, 'optionalAccess', _246 => _246.createdAt])
11007
+ metadata: _optionalChain([options2, 'optionalAccess', _256 => _256.metadata]),
11008
+ createdAt: _optionalChain([options2, 'optionalAccess', _257 => _257.createdAt])
10714
11009
  };
10715
11010
  context.buffer.messages.push(message);
10716
11011
  flushNowOrSoon();
@@ -10744,15 +11039,15 @@ function createRoom(options, config) {
10744
11039
  function addFeedMessage(feedId, data, options2) {
10745
11040
  const requestId = nanoid();
10746
11041
  const promise = registerFeedMutation(requestId, "add-message", feedId, {
10747
- expectedClientMessageId: _optionalChain([options2, 'optionalAccess', _247 => _247.id])
11042
+ expectedClientMessageId: _optionalChain([options2, 'optionalAccess', _258 => _258.id])
10748
11043
  });
10749
11044
  const message = {
10750
11045
  type: ClientMsgCode.ADD_FEED_MESSAGE,
10751
11046
  requestId,
10752
11047
  feedId,
10753
11048
  data,
10754
- id: _optionalChain([options2, 'optionalAccess', _248 => _248.id]),
10755
- createdAt: _optionalChain([options2, 'optionalAccess', _249 => _249.createdAt])
11049
+ id: _optionalChain([options2, 'optionalAccess', _259 => _259.id]),
11050
+ createdAt: _optionalChain([options2, 'optionalAccess', _260 => _260.createdAt])
10756
11051
  };
10757
11052
  context.buffer.messages.push(message);
10758
11053
  flushNowOrSoon();
@@ -10769,7 +11064,7 @@ function createRoom(options, config) {
10769
11064
  feedId,
10770
11065
  messageId,
10771
11066
  data,
10772
- updatedAt: _optionalChain([options2, 'optionalAccess', _250 => _250.updatedAt])
11067
+ updatedAt: _optionalChain([options2, 'optionalAccess', _261 => _261.updatedAt])
10773
11068
  };
10774
11069
  context.buffer.messages.push(message);
10775
11070
  flushNowOrSoon();
@@ -10976,8 +11271,8 @@ function createRoom(options, config) {
10976
11271
  async function getThreads(options2) {
10977
11272
  return httpClient.getThreads({
10978
11273
  roomId,
10979
- query: _optionalChain([options2, 'optionalAccess', _251 => _251.query]),
10980
- cursor: _optionalChain([options2, 'optionalAccess', _252 => _252.cursor])
11274
+ query: _optionalChain([options2, 'optionalAccess', _262 => _262.query]),
11275
+ cursor: _optionalChain([options2, 'optionalAccess', _263 => _263.cursor])
10981
11276
  });
10982
11277
  }
10983
11278
  async function getThread(threadId) {
@@ -11099,7 +11394,7 @@ function createRoom(options, config) {
11099
11394
  function getSubscriptionSettings(options2) {
11100
11395
  return httpClient.getSubscriptionSettings({
11101
11396
  roomId,
11102
- signal: _optionalChain([options2, 'optionalAccess', _253 => _253.signal])
11397
+ signal: _optionalChain([options2, 'optionalAccess', _264 => _264.signal])
11103
11398
  });
11104
11399
  }
11105
11400
  function updateSubscriptionSettings(settings) {
@@ -11121,7 +11416,7 @@ function createRoom(options, config) {
11121
11416
  {
11122
11417
  [kInternal]: {
11123
11418
  get presenceBuffer() {
11124
- return deepClone(_nullishCoalesce(_optionalChain([context, 'access', _254 => _254.buffer, 'access', _255 => _255.presenceUpdates, 'optionalAccess', _256 => _256.data]), () => ( null)));
11419
+ return deepClone(_nullishCoalesce(_optionalChain([context, 'access', _265 => _265.buffer, 'access', _266 => _266.presenceUpdates, 'optionalAccess', _267 => _267.data]), () => ( null)));
11125
11420
  },
11126
11421
  // prettier-ignore
11127
11422
  get undoStack() {
@@ -11136,9 +11431,9 @@ function createRoom(options, config) {
11136
11431
  return context.yjsProvider;
11137
11432
  },
11138
11433
  setYjsProvider(newProvider) {
11139
- _optionalChain([context, 'access', _257 => _257.yjsProvider, 'optionalAccess', _258 => _258.off, 'call', _259 => _259("status", yjsStatusDidChange)]);
11434
+ _optionalChain([context, 'access', _268 => _268.yjsProvider, 'optionalAccess', _269 => _269.off, 'call', _270 => _270("status", yjsStatusDidChange)]);
11140
11435
  context.yjsProvider = newProvider;
11141
- _optionalChain([newProvider, 'optionalAccess', _260 => _260.on, 'call', _261 => _261("status", yjsStatusDidChange)]);
11436
+ _optionalChain([newProvider, 'optionalAccess', _271 => _271.on, 'call', _272 => _272("status", yjsStatusDidChange)]);
11142
11437
  context.yjsProviderDidChange.notify();
11143
11438
  },
11144
11439
  yjsProviderDidChange: context.yjsProviderDidChange.observable,
@@ -11179,6 +11474,11 @@ function createRoom(options, config) {
11179
11474
  connect: () => managedSocket.connect(),
11180
11475
  reconnect: () => managedSocket.reconnect(),
11181
11476
  disconnect: () => managedSocket.disconnect(),
11477
+ _dump: () => {
11478
+ const n = context.pool.nodes.size;
11479
+ return `Room "${roomId}" (${n} node${n === 1 ? "" : "s"}):
11480
+ ${dumpPool(context.pool)}`;
11481
+ },
11182
11482
  destroy: () => {
11183
11483
  pendingFeedsRequests.forEach(
11184
11484
  (request) => request.reject(new Error("Room destroyed"))
@@ -11191,7 +11491,7 @@ function createRoom(options, config) {
11191
11491
  source.dispose();
11192
11492
  }
11193
11493
  eventHub.roomWillDestroy.notify();
11194
- _optionalChain([context, 'access', _262 => _262.yjsProvider, 'optionalAccess', _263 => _263.off, 'call', _264 => _264("status", yjsStatusDidChange)]);
11494
+ _optionalChain([context, 'access', _273 => _273.yjsProvider, 'optionalAccess', _274 => _274.off, 'call', _275 => _275("status", yjsStatusDidChange)]);
11195
11495
  syncSourceForStorage.destroy();
11196
11496
  syncSourceForYjs.destroy();
11197
11497
  uninstallBgTabSpy();
@@ -11351,7 +11651,7 @@ function makeClassicSubscribeFn(roomId, events, errorEvents) {
11351
11651
  }
11352
11652
  if (isLiveNode(first)) {
11353
11653
  const node = first;
11354
- if (_optionalChain([options, 'optionalAccess', _265 => _265.isDeep])) {
11654
+ if (_optionalChain([options, 'optionalAccess', _276 => _276.isDeep])) {
11355
11655
  const storageCallback = second;
11356
11656
  return subscribeToLiveStructureDeeply(node, storageCallback);
11357
11657
  } else {
@@ -11441,8 +11741,8 @@ function createClient(options) {
11441
11741
  const authManager = createAuthManager(options, (token) => {
11442
11742
  currentUserId.set(() => token.uid);
11443
11743
  });
11444
- const fetchPolyfill = _optionalChain([clientOptions, 'access', _266 => _266.polyfills, 'optionalAccess', _267 => _267.fetch]) || /* istanbul ignore next */
11445
- _optionalChain([globalThis, 'access', _268 => _268.fetch, 'optionalAccess', _269 => _269.bind, 'call', _270 => _270(globalThis)]);
11744
+ const fetchPolyfill = _optionalChain([clientOptions, 'access', _277 => _277.polyfills, 'optionalAccess', _278 => _278.fetch]) || /* istanbul ignore next */
11745
+ _optionalChain([globalThis, 'access', _279 => _279.fetch, 'optionalAccess', _280 => _280.bind, 'call', _281 => _281(globalThis)]);
11446
11746
  const httpClient = createApiClient({
11447
11747
  baseUrl,
11448
11748
  fetchPolyfill,
@@ -11460,7 +11760,7 @@ function createClient(options) {
11460
11760
  delegates: {
11461
11761
  createSocket: makeCreateSocketDelegateForAi(
11462
11762
  baseUrl,
11463
- _optionalChain([clientOptions, 'access', _271 => _271.polyfills, 'optionalAccess', _272 => _272.WebSocket])
11763
+ _optionalChain([clientOptions, 'access', _282 => _282.polyfills, 'optionalAccess', _283 => _283.WebSocket])
11464
11764
  ),
11465
11765
  authenticate: async () => {
11466
11766
  const resp = await authManager.getAuthValue({
@@ -11532,7 +11832,7 @@ function createClient(options) {
11532
11832
  createSocket: makeCreateSocketDelegateForRoom(
11533
11833
  roomId,
11534
11834
  baseUrl,
11535
- _optionalChain([clientOptions, 'access', _273 => _273.polyfills, 'optionalAccess', _274 => _274.WebSocket])
11835
+ _optionalChain([clientOptions, 'access', _284 => _284.polyfills, 'optionalAccess', _285 => _285.WebSocket])
11536
11836
  ),
11537
11837
  authenticate: makeAuthDelegateForRoom(roomId, authManager)
11538
11838
  })),
@@ -11555,7 +11855,7 @@ function createClient(options) {
11555
11855
  const shouldConnect = _nullishCoalesce(options2.autoConnect, () => ( true));
11556
11856
  if (shouldConnect) {
11557
11857
  if (typeof atob === "undefined") {
11558
- if (_optionalChain([clientOptions, 'access', _275 => _275.polyfills, 'optionalAccess', _276 => _276.atob]) === void 0) {
11858
+ if (_optionalChain([clientOptions, 'access', _286 => _286.polyfills, 'optionalAccess', _287 => _287.atob]) === void 0) {
11559
11859
  throw new Error(
11560
11860
  "You need to polyfill atob to use the client in your environment. Please follow the instructions at https://liveblocks.io/docs/errors/liveblocks-client/atob-polyfill"
11561
11861
  );
@@ -11567,7 +11867,7 @@ function createClient(options) {
11567
11867
  return leaseRoom(newRoomDetails);
11568
11868
  }
11569
11869
  function getRoom(roomId) {
11570
- const room = _optionalChain([roomsById, 'access', _277 => _277.get, 'call', _278 => _278(roomId), 'optionalAccess', _279 => _279.room]);
11870
+ const room = _optionalChain([roomsById, 'access', _288 => _288.get, 'call', _289 => _289(roomId), 'optionalAccess', _290 => _290.room]);
11571
11871
  return room ? room : null;
11572
11872
  }
11573
11873
  function logout() {
@@ -11583,7 +11883,7 @@ function createClient(options) {
11583
11883
  const batchedResolveUsers = new Batch(
11584
11884
  async (batchedUserIds) => {
11585
11885
  const userIds = batchedUserIds.flat();
11586
- const users = await _optionalChain([resolveUsers, 'optionalCall', _280 => _280({ userIds })]);
11886
+ const users = await _optionalChain([resolveUsers, 'optionalCall', _291 => _291({ userIds })]);
11587
11887
  warnOnceIf(
11588
11888
  !resolveUsers,
11589
11889
  "Set the resolveUsers option in createClient to specify user info."
@@ -11600,7 +11900,7 @@ function createClient(options) {
11600
11900
  const batchedResolveRoomsInfo = new Batch(
11601
11901
  async (batchedRoomIds) => {
11602
11902
  const roomIds = batchedRoomIds.flat();
11603
- const roomsInfo = await _optionalChain([resolveRoomsInfo, 'optionalCall', _281 => _281({ roomIds })]);
11903
+ const roomsInfo = await _optionalChain([resolveRoomsInfo, 'optionalCall', _292 => _292({ roomIds })]);
11604
11904
  warnOnceIf(
11605
11905
  !resolveRoomsInfo,
11606
11906
  "Set the resolveRoomsInfo option in createClient to specify room info."
@@ -11617,7 +11917,7 @@ function createClient(options) {
11617
11917
  const batchedResolveGroupsInfo = new Batch(
11618
11918
  async (batchedGroupIds) => {
11619
11919
  const groupIds = batchedGroupIds.flat();
11620
- const groupsInfo = await _optionalChain([resolveGroupsInfo, 'optionalCall', _282 => _282({ groupIds })]);
11920
+ const groupsInfo = await _optionalChain([resolveGroupsInfo, 'optionalCall', _293 => _293({ groupIds })]);
11621
11921
  warnOnceIf(
11622
11922
  !resolveGroupsInfo,
11623
11923
  "Set the resolveGroupsInfo option in createClient to specify group info."
@@ -11676,7 +11976,7 @@ function createClient(options) {
11676
11976
  }
11677
11977
  };
11678
11978
  const win = typeof window !== "undefined" ? window : void 0;
11679
- _optionalChain([win, 'optionalAccess', _283 => _283.addEventListener, 'call', _284 => _284("beforeunload", maybePreventClose)]);
11979
+ _optionalChain([win, 'optionalAccess', _294 => _294.addEventListener, 'call', _295 => _295("beforeunload", maybePreventClose)]);
11680
11980
  }
11681
11981
  async function getNotificationSettings(options2) {
11682
11982
  const plainSettings = await httpClient.getNotificationSettings(options2);
@@ -11692,6 +11992,7 @@ function createClient(options) {
11692
11992
  {
11693
11993
  enterRoom,
11694
11994
  getRoom,
11995
+ _dump: () => Array.from(roomsById.values(), ({ room }) => room._dump()).join("\n\n"),
11695
11996
  logout,
11696
11997
  // Public inbox notifications API
11697
11998
  getInboxNotifications: httpClient.getInboxNotifications,
@@ -11803,7 +12104,7 @@ var commentBodyElementsTypes = {
11803
12104
  mention: "inline"
11804
12105
  };
11805
12106
  function traverseCommentBody(body, elementOrVisitor, possiblyVisitor) {
11806
- if (!body || !_optionalChain([body, 'optionalAccess', _285 => _285.content])) {
12107
+ if (!body || !_optionalChain([body, 'optionalAccess', _296 => _296.content])) {
11807
12108
  return;
11808
12109
  }
11809
12110
  const element = typeof elementOrVisitor === "string" ? elementOrVisitor : void 0;
@@ -11813,13 +12114,13 @@ function traverseCommentBody(body, elementOrVisitor, possiblyVisitor) {
11813
12114
  for (const block of body.content) {
11814
12115
  if (type === "all" || type === "block") {
11815
12116
  if (guard(block)) {
11816
- _optionalChain([visitor, 'optionalCall', _286 => _286(block)]);
12117
+ _optionalChain([visitor, 'optionalCall', _297 => _297(block)]);
11817
12118
  }
11818
12119
  }
11819
12120
  if (type === "all" || type === "inline") {
11820
12121
  for (const inline of block.children) {
11821
12122
  if (guard(inline)) {
11822
- _optionalChain([visitor, 'optionalCall', _287 => _287(inline)]);
12123
+ _optionalChain([visitor, 'optionalCall', _298 => _298(inline)]);
11823
12124
  }
11824
12125
  }
11825
12126
  }
@@ -11989,7 +12290,7 @@ var stringifyCommentBodyPlainElements = {
11989
12290
  text: ({ element }) => element.text,
11990
12291
  link: ({ element }) => _nullishCoalesce(element.text, () => ( element.url)),
11991
12292
  mention: ({ element, user, group }) => {
11992
- return `@${_nullishCoalesce(_nullishCoalesce(_optionalChain([user, 'optionalAccess', _288 => _288.name]), () => ( _optionalChain([group, 'optionalAccess', _289 => _289.name]))), () => ( element.id))}`;
12293
+ return `@${_nullishCoalesce(_nullishCoalesce(_optionalChain([user, 'optionalAccess', _299 => _299.name]), () => ( _optionalChain([group, 'optionalAccess', _300 => _300.name]))), () => ( element.id))}`;
11993
12294
  }
11994
12295
  };
11995
12296
  var stringifyCommentBodyHtmlElements = {
@@ -12019,7 +12320,7 @@ var stringifyCommentBodyHtmlElements = {
12019
12320
  return html`<a href="${href}" target="_blank" rel="noopener noreferrer">${element.text ? html`${element.text}` : element.url}</a>`;
12020
12321
  },
12021
12322
  mention: ({ element, user, group }) => {
12022
- return html`<span data-mention>@${_optionalChain([user, 'optionalAccess', _290 => _290.name]) ? html`${_optionalChain([user, 'optionalAccess', _291 => _291.name])}` : _optionalChain([group, 'optionalAccess', _292 => _292.name]) ? html`${_optionalChain([group, 'optionalAccess', _293 => _293.name])}` : element.id}</span>`;
12323
+ return html`<span data-mention>@${_optionalChain([user, 'optionalAccess', _301 => _301.name]) ? html`${_optionalChain([user, 'optionalAccess', _302 => _302.name])}` : _optionalChain([group, 'optionalAccess', _303 => _303.name]) ? html`${_optionalChain([group, 'optionalAccess', _304 => _304.name])}` : element.id}</span>`;
12023
12324
  }
12024
12325
  };
12025
12326
  var stringifyCommentBodyMarkdownElements = {
@@ -12049,20 +12350,20 @@ var stringifyCommentBodyMarkdownElements = {
12049
12350
  return markdown`[${_nullishCoalesce(element.text, () => ( element.url))}](${href})`;
12050
12351
  },
12051
12352
  mention: ({ element, user, group }) => {
12052
- return markdown`@${_nullishCoalesce(_nullishCoalesce(_optionalChain([user, 'optionalAccess', _294 => _294.name]), () => ( _optionalChain([group, 'optionalAccess', _295 => _295.name]))), () => ( element.id))}`;
12353
+ return markdown`@${_nullishCoalesce(_nullishCoalesce(_optionalChain([user, 'optionalAccess', _305 => _305.name]), () => ( _optionalChain([group, 'optionalAccess', _306 => _306.name]))), () => ( element.id))}`;
12053
12354
  }
12054
12355
  };
12055
12356
  async function stringifyCommentBody(body, options) {
12056
- const format = _nullishCoalesce(_optionalChain([options, 'optionalAccess', _296 => _296.format]), () => ( "plain"));
12057
- const separator = _nullishCoalesce(_optionalChain([options, 'optionalAccess', _297 => _297.separator]), () => ( (format === "markdown" ? "\n\n" : "\n")));
12357
+ const format = _nullishCoalesce(_optionalChain([options, 'optionalAccess', _307 => _307.format]), () => ( "plain"));
12358
+ const separator = _nullishCoalesce(_optionalChain([options, 'optionalAccess', _308 => _308.separator]), () => ( (format === "markdown" ? "\n\n" : "\n")));
12058
12359
  const elements = {
12059
12360
  ...format === "html" ? stringifyCommentBodyHtmlElements : format === "markdown" ? stringifyCommentBodyMarkdownElements : stringifyCommentBodyPlainElements,
12060
- ..._optionalChain([options, 'optionalAccess', _298 => _298.elements])
12361
+ ..._optionalChain([options, 'optionalAccess', _309 => _309.elements])
12061
12362
  };
12062
12363
  const { users: resolvedUsers, groups: resolvedGroupsInfo } = await resolveMentionsInCommentBody(
12063
12364
  body,
12064
- _optionalChain([options, 'optionalAccess', _299 => _299.resolveUsers]),
12065
- _optionalChain([options, 'optionalAccess', _300 => _300.resolveGroupsInfo])
12365
+ _optionalChain([options, 'optionalAccess', _310 => _310.resolveUsers]),
12366
+ _optionalChain([options, 'optionalAccess', _311 => _311.resolveGroupsInfo])
12066
12367
  );
12067
12368
  const blocks = body.content.flatMap((block, blockIndex) => {
12068
12369
  switch (block.type) {
@@ -12197,9 +12498,9 @@ function makePoller(callback, intervalMs, options) {
12197
12498
  const startTime = performance.now();
12198
12499
  const doc = typeof document !== "undefined" ? document : void 0;
12199
12500
  const win = typeof window !== "undefined" ? window : void 0;
12200
- const maxStaleTimeMs = _nullishCoalesce(_optionalChain([options, 'optionalAccess', _301 => _301.maxStaleTimeMs]), () => ( Number.POSITIVE_INFINITY));
12501
+ const maxStaleTimeMs = _nullishCoalesce(_optionalChain([options, 'optionalAccess', _312 => _312.maxStaleTimeMs]), () => ( Number.POSITIVE_INFINITY));
12201
12502
  const context = {
12202
- inForeground: _optionalChain([doc, 'optionalAccess', _302 => _302.visibilityState]) !== "hidden",
12503
+ inForeground: _optionalChain([doc, 'optionalAccess', _313 => _313.visibilityState]) !== "hidden",
12203
12504
  lastSuccessfulPollAt: startTime,
12204
12505
  count: 0,
12205
12506
  backoff: 0
@@ -12280,11 +12581,11 @@ function makePoller(callback, intervalMs, options) {
12280
12581
  pollNowIfStale();
12281
12582
  }
12282
12583
  function onVisibilityChange() {
12283
- setInForeground(_optionalChain([doc, 'optionalAccess', _303 => _303.visibilityState]) !== "hidden");
12584
+ setInForeground(_optionalChain([doc, 'optionalAccess', _314 => _314.visibilityState]) !== "hidden");
12284
12585
  }
12285
- _optionalChain([doc, 'optionalAccess', _304 => _304.addEventListener, 'call', _305 => _305("visibilitychange", onVisibilityChange)]);
12286
- _optionalChain([win, 'optionalAccess', _306 => _306.addEventListener, 'call', _307 => _307("online", onVisibilityChange)]);
12287
- _optionalChain([win, 'optionalAccess', _308 => _308.addEventListener, 'call', _309 => _309("focus", pollNowIfStale)]);
12586
+ _optionalChain([doc, 'optionalAccess', _315 => _315.addEventListener, 'call', _316 => _316("visibilitychange", onVisibilityChange)]);
12587
+ _optionalChain([win, 'optionalAccess', _317 => _317.addEventListener, 'call', _318 => _318("online", onVisibilityChange)]);
12588
+ _optionalChain([win, 'optionalAccess', _319 => _319.addEventListener, 'call', _320 => _320("focus", pollNowIfStale)]);
12288
12589
  fsm.start();
12289
12590
  return {
12290
12591
  inc,
@@ -12428,5 +12729,6 @@ detectDupes(PKG_NAME, PKG_VERSION, PKG_FORMAT);
12428
12729
 
12429
12730
 
12430
12731
 
12431
- exports.ClientMsgCode = ClientMsgCode; exports.CrdtType = CrdtType; exports.DefaultMap = DefaultMap; exports.Deque = Deque; exports.DerivedSignal = DerivedSignal; exports.FeedRequestErrorCode = FeedRequestErrorCode; exports.HttpError = HttpError; exports.LiveList = LiveList; exports.LiveMap = LiveMap; exports.LiveObject = LiveObject; exports.LiveblocksError = LiveblocksError; exports.MENTION_CHARACTER = MENTION_CHARACTER; exports.MutableSignal = MutableSignal; exports.OpCode = OpCode; exports.Permission = Permission; exports.Promise_withResolvers = Promise_withResolvers; exports.ServerMsgCode = ServerMsgCode; exports.Signal = Signal; exports.SortedList = SortedList; exports.TextEditorType = TextEditorType; exports.WebsocketCloseCodes = WebsocketCloseCodes; exports.asPos = asPos; exports.assert = assert; exports.assertNever = assertNever; exports.autoRetry = autoRetry; exports.b64decode = b64decode; exports.batch = batch; exports.checkBounds = checkBounds; exports.chunk = chunk; exports.cloneLson = cloneLson; exports.compactNodesToNodeStream = compactNodesToNodeStream; exports.compactObject = compactObject; exports.console = fancy_console_exports; exports.convertToCommentData = convertToCommentData; exports.convertToCommentUserReaction = convertToCommentUserReaction; exports.convertToGroupData = convertToGroupData; exports.convertToInboxNotificationData = convertToInboxNotificationData; exports.convertToSubscriptionData = convertToSubscriptionData; exports.convertToThreadData = convertToThreadData; exports.convertToUserSubscriptionData = convertToUserSubscriptionData; exports.createClient = createClient; exports.createCommentAttachmentId = createCommentAttachmentId; exports.createCommentId = createCommentId; exports.createInboxNotificationId = createInboxNotificationId; exports.createManagedPool = createManagedPool; exports.createNotificationSettings = createNotificationSettings; exports.createThreadId = createThreadId; exports.defineAiTool = defineAiTool; exports.deprecate = deprecate; exports.deprecateIf = deprecateIf; exports.detectDupes = detectDupes; exports.entries = entries; exports.errorIf = errorIf; exports.findLastIndex = findLastIndex; exports.freeze = freeze; exports.generateUrl = generateUrl; exports.getMentionsFromCommentBody = getMentionsFromCommentBody; exports.getRoomPermissionConflicts = getRoomPermissionConflicts; exports.getSubscriptionKey = getSubscriptionKey; exports.hasPermissionCapability = hasPermissionCapability; exports.hasPermissionCapabilityAccess = hasPermissionCapabilityAccess; exports.html = html; exports.htmlSafe = htmlSafe; exports.isCommentBodyLink = isCommentBodyLink; exports.isCommentBodyMention = isCommentBodyMention; exports.isCommentBodyText = isCommentBodyText; exports.isJsonArray = isJsonArray; exports.isJsonObject = isJsonObject; exports.isJsonScalar = isJsonScalar; exports.isListStorageNode = isListStorageNode; exports.isLiveNode = isLiveNode; exports.isMapStorageNode = isMapStorageNode; exports.isNotificationChannelEnabled = isNotificationChannelEnabled; exports.isNumberOperator = isNumberOperator; exports.isObjectStorageNode = isObjectStorageNode; exports.isPlainObject = isPlainObject; exports.isRegisterStorageNode = isRegisterStorageNode; exports.isRootStorageNode = isRootStorageNode; exports.isStartsWithOperator = isStartsWithOperator; exports.isUrl = isUrl; exports.kInternal = kInternal; exports.keys = keys; exports.makeAbortController = makeAbortController; exports.makeEventSource = makeEventSource; exports.makePoller = makePoller; exports.makePosition = makePosition; exports.mapValues = mapValues; exports.memoizeOnSuccess = memoizeOnSuccess; exports.nanoid = nanoid; exports.nn = nn; exports.nodeStreamToCompactNodes = nodeStreamToCompactNodes; exports.normalizeRoomAccessesInput = normalizeRoomAccessesInput; exports.normalizeRoomAccessesUpdateInput = normalizeRoomAccessesUpdateInput; exports.normalizeRoomPermissionInput = normalizeRoomPermissionInput; exports.objectToQuery = objectToQuery; exports.patchNotificationSettings = patchNotificationSettings; exports.permissionCapabilitiesFromScopes = permissionCapabilitiesFromScopes; exports.raise = raise; exports.resolveMentionsInCommentBody = resolveMentionsInCommentBody; exports.sanitizeUrl = sanitizeUrl; exports.shallow = shallow; exports.shallow2 = shallow2; exports.stableStringify = stableStringify; exports.stringifyCommentBody = stringifyCommentBody; exports.throwUsageError = throwUsageError; exports.toPlainLson = toPlainLson; exports.tryParseJson = tryParseJson; exports.url = url; exports.urljoin = urljoin; exports.wait = wait; exports.warnOnce = warnOnce; exports.warnOnceIf = warnOnceIf; exports.withTimeout = withTimeout;
12732
+
12733
+ exports.ClientMsgCode = ClientMsgCode; exports.CrdtType = CrdtType; exports.DefaultMap = DefaultMap; exports.Deque = Deque; exports.DerivedSignal = DerivedSignal; exports.FeedRequestErrorCode = FeedRequestErrorCode; exports.HttpError = HttpError; exports.LiveList = LiveList; exports.LiveMap = LiveMap; exports.LiveObject = LiveObject; exports.LiveblocksError = LiveblocksError; exports.MENTION_CHARACTER = MENTION_CHARACTER; exports.MutableSignal = MutableSignal; exports.OpCode = OpCode; exports.Permission = Permission; exports.Promise_withResolvers = Promise_withResolvers; exports.ServerMsgCode = ServerMsgCode; exports.Signal = Signal; exports.SortedList = SortedList; exports.TextEditorType = TextEditorType; exports.WebsocketCloseCodes = WebsocketCloseCodes; exports.asPos = asPos; exports.assert = assert; exports.assertNever = assertNever; exports.autoRetry = autoRetry; exports.b64decode = b64decode; exports.batch = batch; exports.checkBounds = checkBounds; exports.chunk = chunk; exports.cloneLson = cloneLson; exports.compactNodesToNodeStream = compactNodesToNodeStream; exports.compactObject = compactObject; exports.console = fancy_console_exports; exports.convertToCommentData = convertToCommentData; exports.convertToCommentUserReaction = convertToCommentUserReaction; exports.convertToGroupData = convertToGroupData; exports.convertToInboxNotificationData = convertToInboxNotificationData; exports.convertToSubscriptionData = convertToSubscriptionData; exports.convertToThreadData = convertToThreadData; exports.convertToUserSubscriptionData = convertToUserSubscriptionData; exports.createClient = createClient; exports.createCommentAttachmentId = createCommentAttachmentId; exports.createCommentId = createCommentId; exports.createInboxNotificationId = createInboxNotificationId; exports.createManagedPool = createManagedPool; exports.createNotificationSettings = createNotificationSettings; exports.createThreadId = createThreadId; exports.deepLiveify = deepLiveify; exports.defineAiTool = defineAiTool; exports.deprecate = deprecate; exports.deprecateIf = deprecateIf; exports.detectDupes = detectDupes; exports.entries = entries; exports.errorIf = errorIf; exports.findLastIndex = findLastIndex; exports.freeze = freeze; exports.generateUrl = generateUrl; exports.getMentionsFromCommentBody = getMentionsFromCommentBody; exports.getRoomPermissionConflicts = getRoomPermissionConflicts; exports.getSubscriptionKey = getSubscriptionKey; exports.hasPermissionCapability = hasPermissionCapability; exports.hasPermissionCapabilityAccess = hasPermissionCapabilityAccess; exports.html = html; exports.htmlSafe = htmlSafe; exports.isCommentBodyLink = isCommentBodyLink; exports.isCommentBodyMention = isCommentBodyMention; exports.isCommentBodyText = isCommentBodyText; exports.isJsonArray = isJsonArray; exports.isJsonObject = isJsonObject; exports.isJsonScalar = isJsonScalar; exports.isListStorageNode = isListStorageNode; exports.isLiveNode = isLiveNode; exports.isMapStorageNode = isMapStorageNode; exports.isNotificationChannelEnabled = isNotificationChannelEnabled; exports.isNumberOperator = isNumberOperator; exports.isObjectStorageNode = isObjectStorageNode; exports.isPlainObject = isPlainObject; exports.isRegisterStorageNode = isRegisterStorageNode; exports.isRootStorageNode = isRootStorageNode; exports.isStartsWithOperator = isStartsWithOperator; exports.isUrl = isUrl; exports.kInternal = kInternal; exports.keys = keys; exports.makeAbortController = makeAbortController; exports.makeEventSource = makeEventSource; exports.makePoller = makePoller; exports.makePosition = makePosition; exports.mapValues = mapValues; exports.memoizeOnSuccess = memoizeOnSuccess; exports.nanoid = nanoid; exports.nn = nn; exports.nodeStreamToCompactNodes = nodeStreamToCompactNodes; exports.normalizeRoomAccessesInput = normalizeRoomAccessesInput; exports.normalizeRoomAccessesUpdateInput = normalizeRoomAccessesUpdateInput; exports.normalizeRoomPermissionInput = normalizeRoomPermissionInput; exports.objectToQuery = objectToQuery; exports.patchNotificationSettings = patchNotificationSettings; exports.permissionCapabilitiesFromScopes = permissionCapabilitiesFromScopes; exports.raise = raise; exports.resolveMentionsInCommentBody = resolveMentionsInCommentBody; exports.sanitizeUrl = sanitizeUrl; exports.shallow = shallow; exports.shallow2 = shallow2; exports.stableStringify = stableStringify; exports.stringifyCommentBody = stringifyCommentBody; exports.throwUsageError = throwUsageError; exports.toPlainLson = toPlainLson; exports.tryParseJson = tryParseJson; exports.url = url; exports.urljoin = urljoin; exports.wait = wait; exports.warnOnce = warnOnce; exports.warnOnceIf = warnOnceIf; exports.withTimeout = withTimeout;
12432
12734
  //# sourceMappingURL=index.cjs.map