@liveblocks/core 3.19.3 → 3.19.4-rc1

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.19.3";
9
+ var PKG_VERSION = "3.19.4-rc1";
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)) {
@@ -5502,6 +5516,9 @@ var OpCode = Object.freeze({
5502
5516
  function isIgnoredOp(op) {
5503
5517
  return op.type === OpCode.DELETE_CRDT && op.id === "ACK";
5504
5518
  }
5519
+ function isCreateOp(op) {
5520
+ return op.type === OpCode.CREATE_OBJECT || op.type === OpCode.CREATE_REGISTER || op.type === OpCode.CREATE_MAP || op.type === OpCode.CREATE_LIST;
5521
+ }
5505
5522
 
5506
5523
  // src/protocol/StorageNode.ts
5507
5524
  var CrdtType = Object.freeze({
@@ -5759,12 +5776,95 @@ function asPos(str) {
5759
5776
  return isPos(str) ? str : convertToPos(str);
5760
5777
  }
5761
5778
 
5779
+ // src/crdts/UnacknowledgedOps.ts
5780
+ var UnacknowledgedOps = class {
5781
+ // opId -> op
5782
+ #byOpId = /* @__PURE__ */ new Map();
5783
+ // position -> (opId -> Create op)
5784
+ #createOpsByPosition = /* @__PURE__ */ new Map();
5785
+ // parentId -> (opId -> Create op)
5786
+ #createOpsByParent = /* @__PURE__ */ new Map();
5787
+ #posKey(parentId, parentKey) {
5788
+ return `${parentId}
5789
+ ${parentKey}`;
5790
+ }
5791
+ get size() {
5792
+ return this.#byOpId.size;
5793
+ }
5794
+ /**
5795
+ * Mark the given Op as still unacknowledged.
5796
+ */
5797
+ add(op) {
5798
+ this.#byOpId.set(op.opId, op);
5799
+ if (isCreateOp(op)) {
5800
+ const posKey = this.#posKey(op.parentId, op.parentKey);
5801
+ let atPosition = this.#createOpsByPosition.get(posKey);
5802
+ if (atPosition === void 0) {
5803
+ atPosition = /* @__PURE__ */ new Map();
5804
+ this.#createOpsByPosition.set(posKey, atPosition);
5805
+ }
5806
+ atPosition.set(op.opId, op);
5807
+ let inParent = this.#createOpsByParent.get(op.parentId);
5808
+ if (inParent === void 0) {
5809
+ inParent = /* @__PURE__ */ new Map();
5810
+ this.#createOpsByParent.set(op.parentId, inParent);
5811
+ }
5812
+ inParent.set(op.opId, op);
5813
+ }
5814
+ }
5815
+ /**
5816
+ * Drop the op with the given opId from the set, because the server has
5817
+ * acknowledged it (confirmed our own op, or signalled it was seen but
5818
+ * ignored).
5819
+ */
5820
+ delete(opId) {
5821
+ const op = this.#byOpId.get(opId);
5822
+ if (op === void 0) {
5823
+ return;
5824
+ }
5825
+ this.#byOpId.delete(opId);
5826
+ if (isCreateOp(op)) {
5827
+ const posKey = this.#posKey(op.parentId, op.parentKey);
5828
+ const atPosition = this.#createOpsByPosition.get(posKey);
5829
+ _optionalChain([atPosition, 'optionalAccess', _111 => _111.delete, 'call', _112 => _112(opId)]);
5830
+ if (atPosition !== void 0 && atPosition.size === 0) {
5831
+ this.#createOpsByPosition.delete(posKey);
5832
+ }
5833
+ const inParent = this.#createOpsByParent.get(op.parentId);
5834
+ _optionalChain([inParent, 'optionalAccess', _113 => _113.delete, 'call', _114 => _114(opId)]);
5835
+ if (inParent !== void 0 && inParent.size === 0) {
5836
+ this.#createOpsByParent.delete(op.parentId);
5837
+ }
5838
+ }
5839
+ }
5840
+ /**
5841
+ * The still-unacknowledged Create ops with the given `parentId` and
5842
+ * `parentKey` (targeting one exact position), in dispatch order. O(1) lookup.
5843
+ * Empty if none.
5844
+ */
5845
+ getByParentIdAndKey(parentId, parentKey) {
5846
+ 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()]), () => ( []));
5847
+ }
5848
+ /**
5849
+ * The still-unacknowledged Create ops with the given `parentId` (across all
5850
+ * positions), in dispatch order. O(1) lookup. Empty if none.
5851
+ */
5852
+ getByParentId(parentId) {
5853
+ return _nullishCoalesce(_optionalChain([this, 'access', _120 => _120.#createOpsByParent, 'access', _121 => _121.get, 'call', _122 => _122(parentId), 'optionalAccess', _123 => _123.values, 'call', _124 => _124()]), () => ( []));
5854
+ }
5855
+ /** All still-unacknowledged ops, in dispatch order. */
5856
+ values() {
5857
+ return this.#byOpId.values();
5858
+ }
5859
+ };
5860
+
5762
5861
  // src/crdts/AbstractCrdt.ts
5763
5862
  function createManagedPool(roomId, options) {
5764
5863
  const {
5765
5864
  getCurrentConnectionId,
5766
5865
  onDispatch,
5767
- isStorageWritable = () => true
5866
+ isStorageWritable = () => true,
5867
+ unacknowledgedOps = new UnacknowledgedOps()
5768
5868
  } = options;
5769
5869
  let clock = 0;
5770
5870
  let opClock = 0;
@@ -5778,7 +5878,7 @@ function createManagedPool(roomId, options) {
5778
5878
  generateId: () => `${getCurrentConnectionId()}:${clock++}`,
5779
5879
  generateOpId: () => `${getCurrentConnectionId()}:${opClock++}`,
5780
5880
  dispatch(ops, reverse, storageUpdates) {
5781
- _optionalChain([onDispatch, 'optionalCall', _111 => _111(ops, reverse, storageUpdates)]);
5881
+ _optionalChain([onDispatch, 'optionalCall', _125 => _125(ops, reverse, storageUpdates)]);
5782
5882
  },
5783
5883
  assertStorageIsWritable: () => {
5784
5884
  if (!isStorageWritable()) {
@@ -5786,7 +5886,8 @@ function createManagedPool(roomId, options) {
5786
5886
  "Cannot write to storage with a read only user, please ensure the user has write permissions"
5787
5887
  );
5788
5888
  }
5789
- }
5889
+ },
5890
+ unacknowledgedOps
5790
5891
  };
5791
5892
  }
5792
5893
  function crdtAsLiveNode(value) {
@@ -6068,11 +6169,9 @@ function childNodeLt(a, b) {
6068
6169
  var LiveList = class _LiveList extends AbstractCrdt {
6069
6170
  #items;
6070
6171
  #implicitlyDeletedItems;
6071
- #unacknowledgedSets;
6072
6172
  constructor(items) {
6073
6173
  super();
6074
6174
  this.#implicitlyDeletedItems = /* @__PURE__ */ new WeakSet();
6075
- this.#unacknowledgedSets = /* @__PURE__ */ new Map();
6076
6175
  const nodes = [];
6077
6176
  let lastPos;
6078
6177
  for (const item of items) {
@@ -6102,12 +6201,13 @@ var LiveList = class _LiveList extends AbstractCrdt {
6102
6201
  }
6103
6202
  /**
6104
6203
  * @internal
6105
- * This function assumes that the resulting ops will be sent to the server if they have an 'opId'
6106
- * so we mutate _unacknowledgedSets to avoid potential flickering
6107
- * https://github.com/liveblocks/liveblocks/pull/1177
6204
+ * Serializes this list (and its children) into Create ops. Each child's
6205
+ * create is tagged with the "set" intent (in the loop below) so that a list
6206
+ * created and immediately mutated doesn't transiently re-show its initial
6207
+ * items (flicker, https://github.com/liveblocks/liveblocks/pull/1177).
6108
6208
  *
6109
- * This is quite unintuitive and should disappear as soon as
6110
- * we introduce an explicit LiveList.Set operation
6209
+ * This is quite unintuitive and should disappear as soon as we introduce an
6210
+ * explicit LiveList.Set operation.
6111
6211
  */
6112
6212
  _toOps(parentId, parentKey) {
6113
6213
  if (this._id === void 0) {
@@ -6123,9 +6223,9 @@ var LiveList = class _LiveList extends AbstractCrdt {
6123
6223
  ops.push(op);
6124
6224
  for (const item of this.#items) {
6125
6225
  const parentKey2 = item._getParentKeyOrThrow();
6126
- const childOps = HACK_addIntentAndDeletedIdToOperation(
6226
+ const childOps = addIntentToRootOp(
6127
6227
  item._toOps(this._id, parentKey2),
6128
- void 0
6228
+ "set"
6129
6229
  );
6130
6230
  for (const childOp of childOps) {
6131
6231
  ops.push(childOp);
@@ -6167,6 +6267,28 @@ var LiveList = class _LiveList extends AbstractCrdt {
6167
6267
  (item) => item._getParentKeyOrThrow() === position
6168
6268
  );
6169
6269
  }
6270
+ /**
6271
+ * The opId of this list's still-unacknowledged "set" op at the given position,
6272
+ * or undefined if none. Derived from the room's unacknowledgedOps (the single
6273
+ * source of truth) rather than tracked in a per-instance map. The pool's
6274
+ * position index already scopes to this list's (parentId, position); the last
6275
+ * match wins, matching the original last-write-wins map semantics.
6276
+ */
6277
+ #unacknowledgedSetOpIdAt(position) {
6278
+ if (this._pool === void 0 || this._id === void 0) {
6279
+ return void 0;
6280
+ }
6281
+ let opId;
6282
+ for (const op of this._pool.unacknowledgedOps.getByParentIdAndKey(
6283
+ this._id,
6284
+ position
6285
+ )) {
6286
+ if (op.intent === "set") {
6287
+ opId = op.opId;
6288
+ }
6289
+ }
6290
+ return opId;
6291
+ }
6170
6292
  /** @internal */
6171
6293
  _attach(id, pool) {
6172
6294
  super._attach(id, pool);
@@ -6247,13 +6369,9 @@ var LiveList = class _LiveList extends AbstractCrdt {
6247
6369
  if (deletedDelta) {
6248
6370
  delta.push(deletedDelta);
6249
6371
  }
6250
- const unacknowledgedOpId = this.#unacknowledgedSets.get(op.parentKey);
6251
- if (unacknowledgedOpId !== void 0) {
6252
- if (unacknowledgedOpId !== op.opId) {
6253
- return delta.length === 0 ? { modified: false } : { modified: makeUpdate(this, delta), reverse: [] };
6254
- } else {
6255
- this.#unacknowledgedSets.delete(op.parentKey);
6256
- }
6372
+ const unacknowledgedOpId = this.#unacknowledgedSetOpIdAt(op.parentKey);
6373
+ if (unacknowledgedOpId !== void 0 && unacknowledgedOpId !== op.opId) {
6374
+ return delta.length === 0 ? { modified: false } : { modified: makeUpdate(this, delta), reverse: [] };
6257
6375
  }
6258
6376
  const indexOfItemWithSamePosition = this._indexOfPosition(op.parentKey);
6259
6377
  const existingItem = this.#items.find((item) => item._id === op.id);
@@ -6334,7 +6452,7 @@ var LiveList = class _LiveList extends AbstractCrdt {
6334
6452
  }
6335
6453
  return result.modified.updates[0];
6336
6454
  }
6337
- #applyRemoteInsert(op) {
6455
+ #applyRemoteInsert(op, fromSnapshot) {
6338
6456
  if (this._pool === void 0) {
6339
6457
  throw new Error("Can't attach child if managed pool is not present");
6340
6458
  }
@@ -6344,11 +6462,82 @@ var LiveList = class _LiveList extends AbstractCrdt {
6344
6462
  this.#shiftItemPosition(existingItemIndex, key);
6345
6463
  }
6346
6464
  const { newItem, newIndex } = this.#createAttachItemAndSort(op, key);
6465
+ const bumpDeltas = fromSnapshot ? [] : this.#bumpUnackedPushesAbove(key);
6347
6466
  return {
6348
- modified: makeUpdate(this, [insertDelta(newIndex, newItem)]),
6467
+ modified: makeUpdate(this, [
6468
+ insertDelta(newIndex, newItem),
6469
+ ...bumpDeltas
6470
+ ]),
6349
6471
  reverse: []
6350
6472
  };
6351
6473
  }
6474
+ /**
6475
+ * This list's own still-unacknowledged pushed items (their `intent: "push"`
6476
+ * Create op is still pending in the room's unacknowledgedOps). Derived from
6477
+ * the single source of truth, so an item drops out the instant its op is
6478
+ * acked, with no per-instance membership to leak. Yielded in push order.
6479
+ *
6480
+ * Restricted to items currently in `#items`: a pushed node whose op is still
6481
+ * pending may have been pulled out of the list (e.g. implicitly deleted by a
6482
+ * remote set, or removed by an undo) while still living in the pool, and such
6483
+ * a node must not be repositioned.
6484
+ */
6485
+ *#unackedPushNodes() {
6486
+ if (this._pool === void 0 || this._id === void 0) {
6487
+ return;
6488
+ }
6489
+ for (const op of this._pool.unacknowledgedOps.getByParentId(this._id)) {
6490
+ if (op.intent !== "push") {
6491
+ continue;
6492
+ }
6493
+ const node = this._pool.getNode(op.id);
6494
+ if (node !== void 0 && this.#items.includes(node)) {
6495
+ yield node;
6496
+ }
6497
+ }
6498
+ }
6499
+ /**
6500
+ * Optimistic no-flip for pushed items. When a remote op lands at or below my
6501
+ * still-unacked pushed items, those items must end up *after* it: FIFO plus
6502
+ * the room's serial processing guarantee the remote was processed first, so
6503
+ * my unacked pushes belong behind it. Re-chain the whole unacked-push block,
6504
+ * in push order, to sit after the highest confirmed sibling, so it keeps
6505
+ * rendering as a contiguous tail instead of getting interleaved. Local-only;
6506
+ * the real acks overwrite these keys with the (identical) server keys.
6507
+ */
6508
+ #bumpUnackedPushesAbove(remoteKey) {
6509
+ const pending = new Set(this.#unackedPushNodes());
6510
+ if (pending.size === 0) {
6511
+ return [];
6512
+ }
6513
+ let minPending;
6514
+ for (const node of pending) {
6515
+ const pos = node._parentPos;
6516
+ if (minPending === void 0 || pos < minPending) {
6517
+ minPending = pos;
6518
+ }
6519
+ }
6520
+ if (remoteKey < nn(minPending)) {
6521
+ return [];
6522
+ }
6523
+ let base;
6524
+ for (const item of this.#items) {
6525
+ if (!pending.has(item)) {
6526
+ base = item._parentPos;
6527
+ }
6528
+ }
6529
+ const deltas = [];
6530
+ for (const node of pending) {
6531
+ const previousIndex = this.#items.findIndex((item) => item === node);
6532
+ base = makePosition(base);
6533
+ this.#updateItemPosition(node, base);
6534
+ const index = this.#items.findIndex((item) => item === node);
6535
+ if (index !== previousIndex) {
6536
+ deltas.push(moveDelta(previousIndex, index, node));
6537
+ }
6538
+ }
6539
+ return deltas;
6540
+ }
6352
6541
  #applyInsertAck(op) {
6353
6542
  const existingItem = this.#items.find((item) => item._id === op.id);
6354
6543
  const key = asPos(op.parentKey);
@@ -6403,7 +6592,7 @@ var LiveList = class _LiveList extends AbstractCrdt {
6403
6592
  #applyInsertUndoRedo(op) {
6404
6593
  const { id, parentKey: key } = op;
6405
6594
  const child = creationOpToLiveNode(op);
6406
- if (_optionalChain([this, 'access', _112 => _112._pool, 'optionalAccess', _113 => _113.getNode, 'call', _114 => _114(id)]) !== void 0) {
6595
+ if (_optionalChain([this, 'access', _126 => _126._pool, 'optionalAccess', _127 => _127.getNode, 'call', _128 => _128(id)]) !== void 0) {
6407
6596
  return { modified: false };
6408
6597
  }
6409
6598
  child._attach(id, nn(this._pool));
@@ -6411,8 +6600,8 @@ var LiveList = class _LiveList extends AbstractCrdt {
6411
6600
  const existingItemIndex = this._indexOfPosition(key);
6412
6601
  let newKey = key;
6413
6602
  if (existingItemIndex !== -1) {
6414
- const before2 = _optionalChain([this, 'access', _115 => _115.#items, 'access', _116 => _116.at, 'call', _117 => _117(existingItemIndex), 'optionalAccess', _118 => _118._parentPos]);
6415
- const after2 = _optionalChain([this, 'access', _119 => _119.#items, 'access', _120 => _120.at, 'call', _121 => _121(existingItemIndex + 1), 'optionalAccess', _122 => _122._parentPos]);
6603
+ const before2 = _optionalChain([this, 'access', _129 => _129.#items, 'access', _130 => _130.at, 'call', _131 => _131(existingItemIndex), 'optionalAccess', _132 => _132._parentPos]);
6604
+ const after2 = _optionalChain([this, 'access', _133 => _133.#items, 'access', _134 => _134.at, 'call', _135 => _135(existingItemIndex + 1), 'optionalAccess', _136 => _136._parentPos]);
6416
6605
  newKey = makePosition(before2, after2);
6417
6606
  child._setParentLink(this, newKey);
6418
6607
  }
@@ -6426,10 +6615,9 @@ var LiveList = class _LiveList extends AbstractCrdt {
6426
6615
  #applySetUndoRedo(op) {
6427
6616
  const { id, parentKey: key } = op;
6428
6617
  const child = creationOpToLiveNode(op);
6429
- if (_optionalChain([this, 'access', _123 => _123._pool, 'optionalAccess', _124 => _124.getNode, 'call', _125 => _125(id)]) !== void 0) {
6618
+ if (_optionalChain([this, 'access', _137 => _137._pool, 'optionalAccess', _138 => _138.getNode, 'call', _139 => _139(id)]) !== void 0) {
6430
6619
  return { modified: false };
6431
6620
  }
6432
- this.#unacknowledgedSets.set(key, nn(op.opId));
6433
6621
  const indexOfItemWithSameKey = this._indexOfPosition(key);
6434
6622
  child._attach(id, nn(this._pool));
6435
6623
  child._setParentLink(this, key);
@@ -6439,8 +6627,9 @@ var LiveList = class _LiveList extends AbstractCrdt {
6439
6627
  existingItem._detach();
6440
6628
  this.#items.remove(existingItem);
6441
6629
  this.#items.add(child);
6442
- const reverse = HACK_addIntentAndDeletedIdToOperation(
6630
+ const reverse = addIntentToRootOp(
6443
6631
  existingItem._toOps(nn(this._id), key),
6632
+ "set",
6444
6633
  op.id
6445
6634
  );
6446
6635
  const delta = [setDelta(indexOfItemWithSameKey, child)];
@@ -6465,7 +6654,7 @@ var LiveList = class _LiveList extends AbstractCrdt {
6465
6654
  }
6466
6655
  }
6467
6656
  /** @internal */
6468
- _attachChild(op, source) {
6657
+ _attachChild(op, source, fromSnapshot = false) {
6469
6658
  if (this._pool === void 0) {
6470
6659
  throw new Error("Can't attach child if managed pool is not present");
6471
6660
  }
@@ -6480,7 +6669,7 @@ var LiveList = class _LiveList extends AbstractCrdt {
6480
6669
  }
6481
6670
  } else {
6482
6671
  if (source === 1 /* THEIRS */) {
6483
- result = this.#applyRemoteInsert(op);
6672
+ result = this.#applyRemoteInsert(op, fromSnapshot);
6484
6673
  } else if (source === 2 /* OURS */) {
6485
6674
  result = this.#applyInsertAck(op);
6486
6675
  } else {
@@ -6547,7 +6736,7 @@ var LiveList = class _LiveList extends AbstractCrdt {
6547
6736
  } else {
6548
6737
  this.#updateItemPositionAt(
6549
6738
  existingItemIndex,
6550
- makePosition(newKey, _optionalChain([this, 'access', _126 => _126.#items, 'access', _127 => _127.at, 'call', _128 => _128(existingItemIndex + 1), 'optionalAccess', _129 => _129._parentPos]))
6739
+ makePosition(newKey, _optionalChain([this, 'access', _140 => _140.#items, 'access', _141 => _141.at, 'call', _142 => _142(existingItemIndex + 1), 'optionalAccess', _143 => _143._parentPos]))
6551
6740
  );
6552
6741
  const previousIndex = this.#items.findIndex((item) => item === child);
6553
6742
  this.#updateItemPosition(child, newKey);
@@ -6574,7 +6763,7 @@ var LiveList = class _LiveList extends AbstractCrdt {
6574
6763
  this,
6575
6764
  makePosition(
6576
6765
  newKey,
6577
- _optionalChain([this, 'access', _130 => _130.#items, 'access', _131 => _131.at, 'call', _132 => _132(existingItemIndex + 1), 'optionalAccess', _133 => _133._parentPos])
6766
+ _optionalChain([this, 'access', _144 => _144.#items, 'access', _145 => _145.at, 'call', _146 => _146(existingItemIndex + 1), 'optionalAccess', _147 => _147._parentPos])
6578
6767
  )
6579
6768
  );
6580
6769
  this.#items.reposition(existingItem);
@@ -6598,7 +6787,7 @@ var LiveList = class _LiveList extends AbstractCrdt {
6598
6787
  existingItemIndex,
6599
6788
  makePosition(
6600
6789
  newKey,
6601
- _optionalChain([this, 'access', _134 => _134.#items, 'access', _135 => _135.at, 'call', _136 => _136(existingItemIndex + 1), 'optionalAccess', _137 => _137._parentPos])
6790
+ _optionalChain([this, 'access', _148 => _148.#items, 'access', _149 => _149.at, 'call', _150 => _150(existingItemIndex + 1), 'optionalAccess', _151 => _151._parentPos])
6602
6791
  )
6603
6792
  );
6604
6793
  }
@@ -6626,7 +6815,7 @@ var LiveList = class _LiveList extends AbstractCrdt {
6626
6815
  if (existingItemIndex !== -1) {
6627
6816
  actualNewKey = makePosition(
6628
6817
  newKey,
6629
- _optionalChain([this, 'access', _138 => _138.#items, 'access', _139 => _139.at, 'call', _140 => _140(existingItemIndex + 1), 'optionalAccess', _141 => _141._parentPos])
6818
+ _optionalChain([this, 'access', _152 => _152.#items, 'access', _153 => _153.at, 'call', _154 => _154(existingItemIndex + 1), 'optionalAccess', _155 => _155._parentPos])
6630
6819
  );
6631
6820
  }
6632
6821
  this.#updateItemPosition(child, actualNewKey);
@@ -6683,8 +6872,7 @@ var LiveList = class _LiveList extends AbstractCrdt {
6683
6872
  * @param element The element to add to the end of the LiveList.
6684
6873
  */
6685
6874
  push(element) {
6686
- _optionalChain([this, 'access', _142 => _142._pool, 'optionalAccess', _143 => _143.assertStorageIsWritable, 'call', _144 => _144()]);
6687
- return this.insert(element, this.length);
6875
+ return this.#injectAt(element, this.length, "push");
6688
6876
  }
6689
6877
  /**
6690
6878
  * Inserts one element at a specified index.
@@ -6692,14 +6880,23 @@ var LiveList = class _LiveList extends AbstractCrdt {
6692
6880
  * @param index The index at which you want to insert the element.
6693
6881
  */
6694
6882
  insert(element, index) {
6695
- _optionalChain([this, 'access', _145 => _145._pool, 'optionalAccess', _146 => _146.assertStorageIsWritable, 'call', _147 => _147()]);
6883
+ return this.#injectAt(element, index, "insert");
6884
+ }
6885
+ /**
6886
+ * Shared implementation of `insert` and `push`. A `"push"` intent leaves the
6887
+ * client-computed position untouched (so optimistic rendering is unchanged),
6888
+ * but tags the Op so the server appends it to the true end of the list
6889
+ * instead of resolving its position against the client's stale view.
6890
+ */
6891
+ #injectAt(element, index, intent) {
6892
+ _optionalChain([this, 'access', _156 => _156._pool, 'optionalAccess', _157 => _157.assertStorageIsWritable, 'call', _158 => _158()]);
6696
6893
  if (index < 0 || index > this.#items.length) {
6697
6894
  throw new Error(
6698
6895
  `Cannot insert list item at index "${index}". index should be between 0 and ${this.#items.length}`
6699
6896
  );
6700
6897
  }
6701
- const before2 = _optionalChain([this, 'access', _148 => _148.#items, 'access', _149 => _149.at, 'call', _150 => _150(index - 1), 'optionalAccess', _151 => _151._parentPos]);
6702
- const after2 = _optionalChain([this, 'access', _152 => _152.#items, 'access', _153 => _153.at, 'call', _154 => _154(index), 'optionalAccess', _155 => _155._parentPos]);
6898
+ const before2 = _optionalChain([this, 'access', _159 => _159.#items, 'access', _160 => _160.at, 'call', _161 => _161(index - 1), 'optionalAccess', _162 => _162._parentPos]);
6899
+ const after2 = _optionalChain([this, 'access', _163 => _163.#items, 'access', _164 => _164.at, 'call', _165 => _165(index), 'optionalAccess', _166 => _166._parentPos]);
6703
6900
  const position = makePosition(before2, after2);
6704
6901
  const value = lsonToLiveNode(element);
6705
6902
  value._setParentLink(this, position);
@@ -6707,8 +6904,9 @@ var LiveList = class _LiveList extends AbstractCrdt {
6707
6904
  if (this._pool && this._id) {
6708
6905
  const id = this._pool.generateId();
6709
6906
  value._attach(id, this._pool);
6907
+ const ops = value._toOpsWithOpId(this._id, position, this._pool);
6710
6908
  this._pool.dispatch(
6711
- value._toOpsWithOpId(this._id, position, this._pool),
6909
+ intent === "push" ? addIntentToRootOp(ops, "push") : ops,
6712
6910
  [{ type: OpCode.DELETE_CRDT, id }],
6713
6911
  /* @__PURE__ */ new Map([
6714
6912
  [this._id, makeUpdate(this, [insertDelta(index, value)])]
@@ -6722,7 +6920,7 @@ var LiveList = class _LiveList extends AbstractCrdt {
6722
6920
  * @param targetIndex The index where the element should be after moving.
6723
6921
  */
6724
6922
  move(index, targetIndex) {
6725
- _optionalChain([this, 'access', _156 => _156._pool, 'optionalAccess', _157 => _157.assertStorageIsWritable, 'call', _158 => _158()]);
6923
+ _optionalChain([this, 'access', _167 => _167._pool, 'optionalAccess', _168 => _168.assertStorageIsWritable, 'call', _169 => _169()]);
6726
6924
  if (targetIndex < 0) {
6727
6925
  throw new Error("targetIndex cannot be less than 0");
6728
6926
  }
@@ -6740,11 +6938,11 @@ var LiveList = class _LiveList extends AbstractCrdt {
6740
6938
  let beforePosition = null;
6741
6939
  let afterPosition = null;
6742
6940
  if (index < targetIndex) {
6743
- 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]);
6941
+ 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]);
6744
6942
  beforePosition = this.#items.at(targetIndex)._parentPos;
6745
6943
  } else {
6746
6944
  afterPosition = this.#items.at(targetIndex)._parentPos;
6747
- beforePosition = targetIndex === 0 ? void 0 : _optionalChain([this, 'access', _163 => _163.#items, 'access', _164 => _164.at, 'call', _165 => _165(targetIndex - 1), 'optionalAccess', _166 => _166._parentPos]);
6945
+ beforePosition = targetIndex === 0 ? void 0 : _optionalChain([this, 'access', _174 => _174.#items, 'access', _175 => _175.at, 'call', _176 => _176(targetIndex - 1), 'optionalAccess', _177 => _177._parentPos]);
6748
6946
  }
6749
6947
  const position = makePosition(beforePosition, afterPosition);
6750
6948
  const item = this.#items.at(index);
@@ -6779,7 +6977,7 @@ var LiveList = class _LiveList extends AbstractCrdt {
6779
6977
  * @param index The index of the element to delete
6780
6978
  */
6781
6979
  delete(index) {
6782
- _optionalChain([this, 'access', _167 => _167._pool, 'optionalAccess', _168 => _168.assertStorageIsWritable, 'call', _169 => _169()]);
6980
+ _optionalChain([this, 'access', _178 => _178._pool, 'optionalAccess', _179 => _179.assertStorageIsWritable, 'call', _180 => _180()]);
6783
6981
  if (index < 0 || index >= this.#items.length) {
6784
6982
  throw new Error(
6785
6983
  `Cannot delete list item at index "${index}". index should be between 0 and ${this.#items.length - 1}`
@@ -6812,7 +7010,7 @@ var LiveList = class _LiveList extends AbstractCrdt {
6812
7010
  }
6813
7011
  }
6814
7012
  clear() {
6815
- _optionalChain([this, 'access', _170 => _170._pool, 'optionalAccess', _171 => _171.assertStorageIsWritable, 'call', _172 => _172()]);
7013
+ _optionalChain([this, 'access', _181 => _181._pool, 'optionalAccess', _182 => _182.assertStorageIsWritable, 'call', _183 => _183()]);
6816
7014
  if (this._pool) {
6817
7015
  const ops = [];
6818
7016
  const reverseOps = [];
@@ -6846,7 +7044,7 @@ var LiveList = class _LiveList extends AbstractCrdt {
6846
7044
  }
6847
7045
  }
6848
7046
  set(index, item) {
6849
- _optionalChain([this, 'access', _173 => _173._pool, 'optionalAccess', _174 => _174.assertStorageIsWritable, 'call', _175 => _175()]);
7047
+ _optionalChain([this, 'access', _184 => _184._pool, 'optionalAccess', _185 => _185.assertStorageIsWritable, 'call', _186 => _186()]);
6850
7048
  if (index < 0 || index >= this.#items.length) {
6851
7049
  throw new Error(
6852
7050
  `Cannot set list item at index "${index}". index should be between 0 and ${this.#items.length - 1}`
@@ -6866,13 +7064,14 @@ var LiveList = class _LiveList extends AbstractCrdt {
6866
7064
  value._attach(id, this._pool);
6867
7065
  const storageUpdates = /* @__PURE__ */ new Map();
6868
7066
  storageUpdates.set(this._id, makeUpdate(this, [setDelta(index, value)]));
6869
- const ops = HACK_addIntentAndDeletedIdToOperation(
7067
+ const ops = addIntentToRootOp(
6870
7068
  value._toOpsWithOpId(this._id, position, this._pool),
7069
+ "set",
6871
7070
  existingId
6872
7071
  );
6873
- this.#unacknowledgedSets.set(position, nn(ops[0].opId));
6874
- const reverseOps = HACK_addIntentAndDeletedIdToOperation(
7072
+ const reverseOps = addIntentToRootOp(
6875
7073
  existingItem._toOps(this._id, position),
7074
+ "set",
6876
7075
  id
6877
7076
  );
6878
7077
  this._pool.dispatch(ops, reverseOps, storageUpdates);
@@ -7004,7 +7203,7 @@ var LiveList = class _LiveList extends AbstractCrdt {
7004
7203
  #shiftItemPosition(index, key) {
7005
7204
  const shiftedPosition = makePosition(
7006
7205
  key,
7007
- 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
7206
+ 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
7008
7207
  );
7009
7208
  this.#updateItemPositionAt(index, shiftedPosition);
7010
7209
  }
@@ -7073,15 +7272,11 @@ function moveDelta(previousIndex, index, item) {
7073
7272
  previousIndex
7074
7273
  };
7075
7274
  }
7076
- function HACK_addIntentAndDeletedIdToOperation(ops, deletedId) {
7275
+ function addIntentToRootOp(ops, intent, deletedId) {
7077
7276
  return ops.map((op, index) => {
7078
7277
  if (index === 0) {
7079
7278
  const firstOp = op;
7080
- return {
7081
- ...firstOp,
7082
- intent: "set",
7083
- deletedId
7084
- };
7279
+ return { ...firstOp, intent, deletedId };
7085
7280
  } else {
7086
7281
  return op;
7087
7282
  }
@@ -7257,7 +7452,7 @@ var LiveMap = class _LiveMap extends AbstractCrdt {
7257
7452
  * @param value The value of the element to add. Should be serializable to JSON.
7258
7453
  */
7259
7454
  set(key, value) {
7260
- _optionalChain([this, 'access', _180 => _180._pool, 'optionalAccess', _181 => _181.assertStorageIsWritable, 'call', _182 => _182()]);
7455
+ _optionalChain([this, 'access', _191 => _191._pool, 'optionalAccess', _192 => _192.assertStorageIsWritable, 'call', _193 => _193()]);
7261
7456
  const oldValue = this.#map.get(key);
7262
7457
  if (oldValue) {
7263
7458
  oldValue._detach();
@@ -7303,7 +7498,7 @@ var LiveMap = class _LiveMap extends AbstractCrdt {
7303
7498
  * @returns true if an element existed and has been removed, or false if the element does not exist.
7304
7499
  */
7305
7500
  delete(key) {
7306
- _optionalChain([this, 'access', _183 => _183._pool, 'optionalAccess', _184 => _184.assertStorageIsWritable, 'call', _185 => _185()]);
7501
+ _optionalChain([this, 'access', _194 => _194._pool, 'optionalAccess', _195 => _195.assertStorageIsWritable, 'call', _196 => _196()]);
7307
7502
  const item = this.#map.get(key);
7308
7503
  if (item === void 0) {
7309
7504
  return false;
@@ -7914,20 +8109,20 @@ var LiveObject = (_class2 = class _LiveObject extends AbstractCrdt {
7914
8109
  * Caveat: this method will not add changes to the undo/redo stack.
7915
8110
  */
7916
8111
  setLocal(key, value) {
7917
- _optionalChain([this, 'access', _186 => _186._pool, 'optionalAccess', _187 => _187.assertStorageIsWritable, 'call', _188 => _188()]);
8112
+ _optionalChain([this, 'access', _197 => _197._pool, 'optionalAccess', _198 => _198.assertStorageIsWritable, 'call', _199 => _199()]);
7918
8113
  const deleteResult = this.#prepareDelete(key);
7919
8114
  this.#local.set(key, value);
7920
8115
  this.invalidate();
7921
8116
  if (this._pool !== void 0 && this._id !== void 0) {
7922
- const ops = _nullishCoalesce(_optionalChain([deleteResult, 'optionalAccess', _189 => _189[0]]), () => ( []));
7923
- const reverse = _nullishCoalesce(_optionalChain([deleteResult, 'optionalAccess', _190 => _190[1]]), () => ( []));
7924
- const storageUpdates = _nullishCoalesce(_optionalChain([deleteResult, 'optionalAccess', _191 => _191[2]]), () => ( /* @__PURE__ */ new Map()));
8117
+ const ops = _nullishCoalesce(_optionalChain([deleteResult, 'optionalAccess', _200 => _200[0]]), () => ( []));
8118
+ const reverse = _nullishCoalesce(_optionalChain([deleteResult, 'optionalAccess', _201 => _201[1]]), () => ( []));
8119
+ const storageUpdates = _nullishCoalesce(_optionalChain([deleteResult, 'optionalAccess', _202 => _202[2]]), () => ( /* @__PURE__ */ new Map()));
7925
8120
  const existing = storageUpdates.get(this._id);
7926
8121
  storageUpdates.set(this._id, {
7927
8122
  node: this,
7928
8123
  type: "LiveObject",
7929
8124
  updates: {
7930
- ..._optionalChain([existing, 'optionalAccess', _192 => _192.updates]),
8125
+ ..._optionalChain([existing, 'optionalAccess', _203 => _203.updates]),
7931
8126
  [key]: { type: "update" }
7932
8127
  }
7933
8128
  });
@@ -7947,7 +8142,7 @@ var LiveObject = (_class2 = class _LiveObject extends AbstractCrdt {
7947
8142
  * #synced or pool/id are unavailable. Does NOT dispatch.
7948
8143
  */
7949
8144
  #prepareDelete(key) {
7950
- _optionalChain([this, 'access', _193 => _193._pool, 'optionalAccess', _194 => _194.assertStorageIsWritable, 'call', _195 => _195()]);
8145
+ _optionalChain([this, 'access', _204 => _204._pool, 'optionalAccess', _205 => _205.assertStorageIsWritable, 'call', _206 => _206()]);
7951
8146
  const k = key;
7952
8147
  if (this.#local.has(k) && !this.#synced.has(k)) {
7953
8148
  const oldValue2 = this.#local.get(k);
@@ -8023,7 +8218,7 @@ var LiveObject = (_class2 = class _LiveObject extends AbstractCrdt {
8023
8218
  const result = this.#prepareDelete(key);
8024
8219
  if (result) {
8025
8220
  const [ops, reverse, storageUpdates] = result;
8026
- _optionalChain([this, 'access', _196 => _196._pool, 'optionalAccess', _197 => _197.dispatch, 'call', _198 => _198(ops, reverse, storageUpdates)]);
8221
+ _optionalChain([this, 'access', _207 => _207._pool, 'optionalAccess', _208 => _208.dispatch, 'call', _209 => _209(ops, reverse, storageUpdates)]);
8027
8222
  }
8028
8223
  }
8029
8224
  /**
@@ -8031,7 +8226,7 @@ var LiveObject = (_class2 = class _LiveObject extends AbstractCrdt {
8031
8226
  * @param patch The object used to overrides properties
8032
8227
  */
8033
8228
  update(patch) {
8034
- _optionalChain([this, 'access', _199 => _199._pool, 'optionalAccess', _200 => _200.assertStorageIsWritable, 'call', _201 => _201()]);
8229
+ _optionalChain([this, 'access', _210 => _210._pool, 'optionalAccess', _211 => _211.assertStorageIsWritable, 'call', _212 => _212()]);
8035
8230
  if (_LiveObject.detectLargeObjects) {
8036
8231
  const data = {};
8037
8232
  for (const [key, value] of this.#synced) {
@@ -8321,6 +8516,31 @@ function lsonToLiveNode(value) {
8321
8516
  return new LiveRegister(value);
8322
8517
  }
8323
8518
  }
8519
+ function dumpPool(pool) {
8520
+ const rows = Array.from(pool.nodes.values(), (node) => {
8521
+ const parent = node.parent;
8522
+ const parentId = parent.type === "HasParent" ? _nullishCoalesce(parent.node._id, () => ( "?")) : parent.type === "Orphaned" ? "<orphaned>" : "-";
8523
+ let value;
8524
+ if (node instanceof LiveRegister) {
8525
+ value = stringifyOrLog(node.data);
8526
+ } else if (node instanceof LiveList) {
8527
+ value = "<LiveList>";
8528
+ } else if (node instanceof LiveMap) {
8529
+ value = "<LiveMap>";
8530
+ } else {
8531
+ value = "<LiveObject>";
8532
+ }
8533
+ return { id: nn(node._id), parentId, key: _nullishCoalesce(node._parentKey, () => ( "")), value };
8534
+ });
8535
+ rows.sort((a, b) => {
8536
+ if (a.parentId !== b.parentId) return a.parentId < b.parentId ? -1 : 1;
8537
+ if (a.key !== b.key) return a.key < b.key ? -1 : 1;
8538
+ return 0;
8539
+ });
8540
+ return rows.map(
8541
+ (r) => ` ${r.id} parent=${r.parentId} key=${r.key || "\u2014"} ${r.value}`
8542
+ ).join("\n");
8543
+ }
8324
8544
  function getTreesDiffOperations(currentItems, newItems) {
8325
8545
  const ops = [];
8326
8546
  currentItems.forEach((_, id) => {
@@ -8448,7 +8668,7 @@ function sendToPanel(message, options) {
8448
8668
  ...message,
8449
8669
  source: "liveblocks-devtools-client"
8450
8670
  };
8451
- if (!(_optionalChain([options, 'optionalAccess', _202 => _202.force]) || _bridgeActive)) {
8671
+ if (!(_optionalChain([options, 'optionalAccess', _213 => _213.force]) || _bridgeActive)) {
8452
8672
  return;
8453
8673
  }
8454
8674
  window.postMessage(fullMsg, "*");
@@ -8456,7 +8676,7 @@ function sendToPanel(message, options) {
8456
8676
  var eventSource = makeEventSource();
8457
8677
  if (process.env.NODE_ENV !== "production" && typeof window !== "undefined") {
8458
8678
  window.addEventListener("message", (event) => {
8459
- if (event.source === window && _optionalChain([event, 'access', _203 => _203.data, 'optionalAccess', _204 => _204.source]) === "liveblocks-devtools-panel") {
8679
+ if (event.source === window && _optionalChain([event, 'access', _214 => _214.data, 'optionalAccess', _215 => _215.source]) === "liveblocks-devtools-panel") {
8460
8680
  eventSource.notify(event.data);
8461
8681
  } else {
8462
8682
  }
@@ -8598,7 +8818,7 @@ function fullSync(room) {
8598
8818
  msg: "room::sync::full",
8599
8819
  roomId: room.id,
8600
8820
  status: room.getStatus(),
8601
- storage: _nullishCoalesce(_optionalChain([root, 'optionalAccess', _205 => _205.toTreeNode, 'call', _206 => _206("root"), 'access', _207 => _207.payload]), () => ( null)),
8821
+ storage: _nullishCoalesce(_optionalChain([root, 'optionalAccess', _216 => _216.toTreeNode, 'call', _217 => _217("root"), 'access', _218 => _218.payload]), () => ( null)),
8602
8822
  me,
8603
8823
  others
8604
8824
  });
@@ -9277,15 +9497,15 @@ function installBackgroundTabSpy() {
9277
9497
  const doc = typeof document !== "undefined" ? document : void 0;
9278
9498
  const inBackgroundSince = { current: null };
9279
9499
  function onVisibilityChange() {
9280
- if (_optionalChain([doc, 'optionalAccess', _208 => _208.visibilityState]) === "hidden") {
9500
+ if (_optionalChain([doc, 'optionalAccess', _219 => _219.visibilityState]) === "hidden") {
9281
9501
  inBackgroundSince.current = _nullishCoalesce(inBackgroundSince.current, () => ( Date.now()));
9282
9502
  } else {
9283
9503
  inBackgroundSince.current = null;
9284
9504
  }
9285
9505
  }
9286
- _optionalChain([doc, 'optionalAccess', _209 => _209.addEventListener, 'call', _210 => _210("visibilitychange", onVisibilityChange)]);
9506
+ _optionalChain([doc, 'optionalAccess', _220 => _220.addEventListener, 'call', _221 => _221("visibilitychange", onVisibilityChange)]);
9287
9507
  const unsub = () => {
9288
- _optionalChain([doc, 'optionalAccess', _211 => _211.removeEventListener, 'call', _212 => _212("visibilitychange", onVisibilityChange)]);
9508
+ _optionalChain([doc, 'optionalAccess', _222 => _222.removeEventListener, 'call', _223 => _223("visibilitychange", onVisibilityChange)]);
9289
9509
  };
9290
9510
  return [inBackgroundSince, unsub];
9291
9511
  }
@@ -9330,6 +9550,7 @@ function createRoom(options, config) {
9330
9550
  delegates,
9331
9551
  config.enableDebugLogging
9332
9552
  );
9553
+ const unacknowledgedOps = new UnacknowledgedOps();
9333
9554
  const context = {
9334
9555
  buffer: {
9335
9556
  flushTimerID: void 0,
@@ -9357,14 +9578,15 @@ function createRoom(options, config) {
9357
9578
  pool: createManagedPool(roomId, {
9358
9579
  getCurrentConnectionId,
9359
9580
  onDispatch,
9360
- isStorageWritable
9581
+ isStorageWritable,
9582
+ unacknowledgedOps
9361
9583
  }),
9362
9584
  root: void 0,
9363
9585
  undoStack: [],
9364
9586
  redoStack: [],
9365
9587
  pausedHistory: null,
9366
9588
  activeBatch: null,
9367
- unacknowledgedOps: /* @__PURE__ */ new Map()
9589
+ unacknowledgedOps
9368
9590
  };
9369
9591
  const nodeMapBuffer = makeNodeMapBuffer();
9370
9592
  const stopwatch = config.enableDebugLogging ? makeStopWatch() : void 0;
@@ -9476,7 +9698,7 @@ function createRoom(options, config) {
9476
9698
  }
9477
9699
  }
9478
9700
  function isStorageWritable() {
9479
- const scopes = _optionalChain([context, 'access', _213 => _213.dynamicSessionInfoSig, 'access', _214 => _214.get, 'call', _215 => _215(), 'optionalAccess', _216 => _216.scopes]);
9701
+ const scopes = _optionalChain([context, 'access', _224 => _224.dynamicSessionInfoSig, 'access', _225 => _225.get, 'call', _226 => _226(), 'optionalAccess', _227 => _227.scopes]);
9480
9702
  return scopes !== void 0 ? canWriteStorage(scopes) : true;
9481
9703
  }
9482
9704
  const eventHub = {
@@ -9572,7 +9794,11 @@ function createRoom(options, config) {
9572
9794
  currentItems.set(id, crdt._serialize());
9573
9795
  }
9574
9796
  const ops = getTreesDiffOperations(currentItems, nodes);
9575
- const result = applyRemoteOps(ops);
9797
+ const result = applyRemoteOps(
9798
+ ops,
9799
+ /* fromSnapshot */
9800
+ true
9801
+ );
9576
9802
  notify(result.updates);
9577
9803
  } else {
9578
9804
  context.root = LiveObject._fromItems(
@@ -9580,7 +9806,7 @@ function createRoom(options, config) {
9580
9806
  context.pool
9581
9807
  );
9582
9808
  }
9583
- const canWrite = _nullishCoalesce(_optionalChain([self, 'access', _217 => _217.get, 'call', _218 => _218(), 'optionalAccess', _219 => _219.canWrite]), () => ( true));
9809
+ const canWrite = _nullishCoalesce(_optionalChain([self, 'access', _228 => _228.get, 'call', _229 => _229(), 'optionalAccess', _230 => _230.canWrite]), () => ( true));
9584
9810
  const root = context.root;
9585
9811
  disableHistory(() => {
9586
9812
  for (const key in context.initialStorage) {
@@ -9654,15 +9880,16 @@ function createRoom(options, config) {
9654
9880
  );
9655
9881
  return { opsToEmit: opsWithOpIds, reverse, updates };
9656
9882
  }
9657
- function applyRemoteOps(ops) {
9883
+ function applyRemoteOps(ops, fromSnapshot = false) {
9658
9884
  return applyOps(
9659
9885
  [],
9660
9886
  ops,
9661
9887
  /* isLocal */
9662
- false
9888
+ false,
9889
+ fromSnapshot
9663
9890
  );
9664
9891
  }
9665
- function applyOps(pframes, ops, isLocal) {
9892
+ function applyOps(pframes, ops, isLocal, fromSnapshot = false) {
9666
9893
  const output = {
9667
9894
  reverse: new Deque(),
9668
9895
  storageUpdates: /* @__PURE__ */ new Map(),
@@ -9698,7 +9925,7 @@ function createRoom(options, config) {
9698
9925
  } else {
9699
9926
  source = 1 /* THEIRS */;
9700
9927
  }
9701
- const applyOpResult = applyOp(op, source);
9928
+ const applyOpResult = applyOp(op, source, fromSnapshot);
9702
9929
  if (applyOpResult.modified) {
9703
9930
  const nodeId = applyOpResult.modified.node._id;
9704
9931
  if (!(nodeId && createdNodeIds.has(nodeId))) {
@@ -9724,7 +9951,7 @@ function createRoom(options, config) {
9724
9951
  }
9725
9952
  };
9726
9953
  }
9727
- function applyOp(op, source) {
9954
+ function applyOp(op, source, fromSnapshot = false) {
9728
9955
  if (isIgnoredOp(op)) {
9729
9956
  return { modified: false };
9730
9957
  }
@@ -9763,7 +9990,7 @@ function createRoom(options, config) {
9763
9990
  if (parentNode === void 0) {
9764
9991
  return { modified: false };
9765
9992
  }
9766
- return parentNode._attachChild(op, source);
9993
+ return parentNode._attachChild(op, source, fromSnapshot);
9767
9994
  }
9768
9995
  }
9769
9996
  }
@@ -9785,7 +10012,7 @@ function createRoom(options, config) {
9785
10012
  }
9786
10013
  context.myPresence.patch(patch);
9787
10014
  if (context.activeBatch) {
9788
- if (_optionalChain([options2, 'optionalAccess', _220 => _220.addToHistory])) {
10015
+ if (_optionalChain([options2, 'optionalAccess', _231 => _231.addToHistory])) {
9789
10016
  context.activeBatch.reverseOps.pushLeft({
9790
10017
  type: "presence",
9791
10018
  data: oldValues
@@ -9794,7 +10021,7 @@ function createRoom(options, config) {
9794
10021
  context.activeBatch.updates.presence = true;
9795
10022
  } else {
9796
10023
  flushNowOrSoon();
9797
- if (_optionalChain([options2, 'optionalAccess', _221 => _221.addToHistory])) {
10024
+ if (_optionalChain([options2, 'optionalAccess', _232 => _232.addToHistory])) {
9798
10025
  addToUndoStack([{ type: "presence", data: oldValues }]);
9799
10026
  }
9800
10027
  notify({ presence: true });
@@ -9903,12 +10130,11 @@ function createRoom(options, config) {
9903
10130
  }
9904
10131
  }
9905
10132
  function applyAndSendOfflineOps(unackedOps) {
9906
- if (unackedOps.size === 0) {
10133
+ if (unackedOps.length === 0) {
9907
10134
  return;
9908
10135
  }
9909
10136
  const messages = [];
9910
- const inOps = Array.from(unackedOps.values());
9911
- const result = applyLocalOps(inOps);
10137
+ const result = applyLocalOps(unackedOps);
9912
10138
  messages.push({
9913
10139
  type: ClientMsgCode.UPDATE_STORAGE,
9914
10140
  ops: result.opsToEmit
@@ -9972,11 +10198,11 @@ function createRoom(options, config) {
9972
10198
  break;
9973
10199
  }
9974
10200
  case ServerMsgCode.STORAGE_CHUNK:
9975
- _optionalChain([stopwatch, 'optionalAccess', _222 => _222.lap, 'call', _223 => _223()]);
10201
+ _optionalChain([stopwatch, 'optionalAccess', _233 => _233.lap, 'call', _234 => _234()]);
9976
10202
  nodeMapBuffer.append(compactNodesToNodeStream(message.nodes));
9977
10203
  break;
9978
10204
  case ServerMsgCode.STORAGE_STREAM_END: {
9979
- const timing = _optionalChain([stopwatch, 'optionalAccess', _224 => _224.stop, 'call', _225 => _225()]);
10205
+ const timing = _optionalChain([stopwatch, 'optionalAccess', _235 => _235.stop, 'call', _236 => _236()]);
9980
10206
  if (timing) {
9981
10207
  const ms = (v) => `${v.toFixed(1)}ms`;
9982
10208
  const rest = timing.laps.slice(1);
@@ -10111,11 +10337,11 @@ function createRoom(options, config) {
10111
10337
  } else if (pendingFeedsRequests.has(requestId)) {
10112
10338
  const pending = pendingFeedsRequests.get(requestId);
10113
10339
  pendingFeedsRequests.delete(requestId);
10114
- _optionalChain([pending, 'optionalAccess', _226 => _226.reject, 'call', _227 => _227(err)]);
10340
+ _optionalChain([pending, 'optionalAccess', _237 => _237.reject, 'call', _238 => _238(err)]);
10115
10341
  } else if (pendingFeedMessagesRequests.has(requestId)) {
10116
10342
  const pending = pendingFeedMessagesRequests.get(requestId);
10117
10343
  pendingFeedMessagesRequests.delete(requestId);
10118
- _optionalChain([pending, 'optionalAccess', _228 => _228.reject, 'call', _229 => _229(err)]);
10344
+ _optionalChain([pending, 'optionalAccess', _239 => _239.reject, 'call', _240 => _240(err)]);
10119
10345
  }
10120
10346
  eventHub.feeds.notify(message);
10121
10347
  break;
@@ -10132,7 +10358,7 @@ function createRoom(options, config) {
10132
10358
  const storageOps = context.buffer.storageOperations;
10133
10359
  if (storageOps.length > 0) {
10134
10360
  for (const op of storageOps) {
10135
- context.unacknowledgedOps.set(op.opId, op);
10361
+ context.unacknowledgedOps.add(op);
10136
10362
  }
10137
10363
  notifyStorageStatus();
10138
10364
  }
@@ -10269,10 +10495,10 @@ function createRoom(options, config) {
10269
10495
  timeoutId,
10270
10496
  kind,
10271
10497
  feedId,
10272
- messageId: _optionalChain([options2, 'optionalAccess', _230 => _230.messageId]),
10273
- expectedClientMessageId: _optionalChain([options2, 'optionalAccess', _231 => _231.expectedClientMessageId])
10498
+ messageId: _optionalChain([options2, 'optionalAccess', _241 => _241.messageId]),
10499
+ expectedClientMessageId: _optionalChain([options2, 'optionalAccess', _242 => _242.expectedClientMessageId])
10274
10500
  });
10275
- if (kind === "add-message" && _optionalChain([options2, 'optionalAccess', _232 => _232.expectedClientMessageId]) === void 0) {
10501
+ if (kind === "add-message" && _optionalChain([options2, 'optionalAccess', _243 => _243.expectedClientMessageId]) === void 0) {
10276
10502
  const q = _nullishCoalesce(pendingAddMessageFifoByFeed.get(feedId), () => ( []));
10277
10503
  q.push(requestId);
10278
10504
  pendingAddMessageFifoByFeed.set(feedId, q);
@@ -10323,10 +10549,10 @@ function createRoom(options, config) {
10323
10549
  }
10324
10550
  if (!matched) {
10325
10551
  const q = pendingAddMessageFifoByFeed.get(message.feedId);
10326
- const headId = _optionalChain([q, 'optionalAccess', _233 => _233[0]]);
10552
+ const headId = _optionalChain([q, 'optionalAccess', _244 => _244[0]]);
10327
10553
  if (headId !== void 0) {
10328
10554
  const pending = pendingFeedMutations.get(headId);
10329
- if (_optionalChain([pending, 'optionalAccess', _234 => _234.kind]) === "add-message" && pending.expectedClientMessageId === void 0) {
10555
+ if (_optionalChain([pending, 'optionalAccess', _245 => _245.kind]) === "add-message" && pending.expectedClientMessageId === void 0) {
10330
10556
  settleFeedMutation(headId, "ok");
10331
10557
  }
10332
10558
  }
@@ -10359,10 +10585,10 @@ function createRoom(options, config) {
10359
10585
  }
10360
10586
  }
10361
10587
  function processInitialStorage(nodes) {
10362
- const unacknowledgedOps = new Map(context.unacknowledgedOps);
10588
+ const unacknowledgedOps2 = [...context.unacknowledgedOps.values()];
10363
10589
  createOrUpdateRootFromMessage(nodes);
10364
- applyAndSendOfflineOps(unacknowledgedOps);
10365
- _optionalChain([_resolveStoragePromise, 'optionalCall', _235 => _235()]);
10590
+ applyAndSendOfflineOps(unacknowledgedOps2);
10591
+ _optionalChain([_resolveStoragePromise, 'optionalCall', _246 => _246()]);
10366
10592
  notifyStorageStatus();
10367
10593
  eventHub.storageDidLoad.notify();
10368
10594
  }
@@ -10380,7 +10606,7 @@ function createRoom(options, config) {
10380
10606
  } else if (!messages.some((msg) => msg.type === ClientMsgCode.FETCH_STORAGE)) {
10381
10607
  messages.push({ type: ClientMsgCode.FETCH_STORAGE });
10382
10608
  nodeMapBuffer.take();
10383
- _optionalChain([stopwatch, 'optionalAccess', _236 => _236.start, 'call', _237 => _237()]);
10609
+ _optionalChain([stopwatch, 'optionalAccess', _247 => _247.start, 'call', _248 => _248()]);
10384
10610
  }
10385
10611
  if (options2.flush) {
10386
10612
  flushNowOrSoon();
@@ -10436,10 +10662,10 @@ function createRoom(options, config) {
10436
10662
  const message = {
10437
10663
  type: ClientMsgCode.FETCH_FEEDS,
10438
10664
  requestId,
10439
- cursor: _optionalChain([options2, 'optionalAccess', _238 => _238.cursor]),
10440
- since: _optionalChain([options2, 'optionalAccess', _239 => _239.since]),
10441
- limit: _optionalChain([options2, 'optionalAccess', _240 => _240.limit]),
10442
- metadata: _optionalChain([options2, 'optionalAccess', _241 => _241.metadata])
10665
+ cursor: _optionalChain([options2, 'optionalAccess', _249 => _249.cursor]),
10666
+ since: _optionalChain([options2, 'optionalAccess', _250 => _250.since]),
10667
+ limit: _optionalChain([options2, 'optionalAccess', _251 => _251.limit]),
10668
+ metadata: _optionalChain([options2, 'optionalAccess', _252 => _252.metadata])
10443
10669
  };
10444
10670
  context.buffer.messages.push(message);
10445
10671
  flushNowOrSoon();
@@ -10459,9 +10685,9 @@ function createRoom(options, config) {
10459
10685
  type: ClientMsgCode.FETCH_FEED_MESSAGES,
10460
10686
  requestId,
10461
10687
  feedId,
10462
- cursor: _optionalChain([options2, 'optionalAccess', _242 => _242.cursor]),
10463
- since: _optionalChain([options2, 'optionalAccess', _243 => _243.since]),
10464
- limit: _optionalChain([options2, 'optionalAccess', _244 => _244.limit])
10688
+ cursor: _optionalChain([options2, 'optionalAccess', _253 => _253.cursor]),
10689
+ since: _optionalChain([options2, 'optionalAccess', _254 => _254.since]),
10690
+ limit: _optionalChain([options2, 'optionalAccess', _255 => _255.limit])
10465
10691
  };
10466
10692
  context.buffer.messages.push(message);
10467
10693
  flushNowOrSoon();
@@ -10480,8 +10706,8 @@ function createRoom(options, config) {
10480
10706
  type: ClientMsgCode.ADD_FEED,
10481
10707
  requestId,
10482
10708
  feedId,
10483
- metadata: _optionalChain([options2, 'optionalAccess', _245 => _245.metadata]),
10484
- createdAt: _optionalChain([options2, 'optionalAccess', _246 => _246.createdAt])
10709
+ metadata: _optionalChain([options2, 'optionalAccess', _256 => _256.metadata]),
10710
+ createdAt: _optionalChain([options2, 'optionalAccess', _257 => _257.createdAt])
10485
10711
  };
10486
10712
  context.buffer.messages.push(message);
10487
10713
  flushNowOrSoon();
@@ -10515,15 +10741,15 @@ function createRoom(options, config) {
10515
10741
  function addFeedMessage(feedId, data, options2) {
10516
10742
  const requestId = nanoid();
10517
10743
  const promise = registerFeedMutation(requestId, "add-message", feedId, {
10518
- expectedClientMessageId: _optionalChain([options2, 'optionalAccess', _247 => _247.id])
10744
+ expectedClientMessageId: _optionalChain([options2, 'optionalAccess', _258 => _258.id])
10519
10745
  });
10520
10746
  const message = {
10521
10747
  type: ClientMsgCode.ADD_FEED_MESSAGE,
10522
10748
  requestId,
10523
10749
  feedId,
10524
10750
  data,
10525
- id: _optionalChain([options2, 'optionalAccess', _248 => _248.id]),
10526
- createdAt: _optionalChain([options2, 'optionalAccess', _249 => _249.createdAt])
10751
+ id: _optionalChain([options2, 'optionalAccess', _259 => _259.id]),
10752
+ createdAt: _optionalChain([options2, 'optionalAccess', _260 => _260.createdAt])
10527
10753
  };
10528
10754
  context.buffer.messages.push(message);
10529
10755
  flushNowOrSoon();
@@ -10540,7 +10766,7 @@ function createRoom(options, config) {
10540
10766
  feedId,
10541
10767
  messageId,
10542
10768
  data,
10543
- updatedAt: _optionalChain([options2, 'optionalAccess', _250 => _250.updatedAt])
10769
+ updatedAt: _optionalChain([options2, 'optionalAccess', _261 => _261.updatedAt])
10544
10770
  };
10545
10771
  context.buffer.messages.push(message);
10546
10772
  flushNowOrSoon();
@@ -10747,8 +10973,8 @@ function createRoom(options, config) {
10747
10973
  async function getThreads(options2) {
10748
10974
  return httpClient.getThreads({
10749
10975
  roomId,
10750
- query: _optionalChain([options2, 'optionalAccess', _251 => _251.query]),
10751
- cursor: _optionalChain([options2, 'optionalAccess', _252 => _252.cursor])
10976
+ query: _optionalChain([options2, 'optionalAccess', _262 => _262.query]),
10977
+ cursor: _optionalChain([options2, 'optionalAccess', _263 => _263.cursor])
10752
10978
  });
10753
10979
  }
10754
10980
  async function getThread(threadId) {
@@ -10870,7 +11096,7 @@ function createRoom(options, config) {
10870
11096
  function getSubscriptionSettings(options2) {
10871
11097
  return httpClient.getSubscriptionSettings({
10872
11098
  roomId,
10873
- signal: _optionalChain([options2, 'optionalAccess', _253 => _253.signal])
11099
+ signal: _optionalChain([options2, 'optionalAccess', _264 => _264.signal])
10874
11100
  });
10875
11101
  }
10876
11102
  function updateSubscriptionSettings(settings) {
@@ -10892,7 +11118,7 @@ function createRoom(options, config) {
10892
11118
  {
10893
11119
  [kInternal]: {
10894
11120
  get presenceBuffer() {
10895
- return deepClone(_nullishCoalesce(_optionalChain([context, 'access', _254 => _254.buffer, 'access', _255 => _255.presenceUpdates, 'optionalAccess', _256 => _256.data]), () => ( null)));
11121
+ return deepClone(_nullishCoalesce(_optionalChain([context, 'access', _265 => _265.buffer, 'access', _266 => _266.presenceUpdates, 'optionalAccess', _267 => _267.data]), () => ( null)));
10896
11122
  },
10897
11123
  // prettier-ignore
10898
11124
  get undoStack() {
@@ -10907,9 +11133,9 @@ function createRoom(options, config) {
10907
11133
  return context.yjsProvider;
10908
11134
  },
10909
11135
  setYjsProvider(newProvider) {
10910
- _optionalChain([context, 'access', _257 => _257.yjsProvider, 'optionalAccess', _258 => _258.off, 'call', _259 => _259("status", yjsStatusDidChange)]);
11136
+ _optionalChain([context, 'access', _268 => _268.yjsProvider, 'optionalAccess', _269 => _269.off, 'call', _270 => _270("status", yjsStatusDidChange)]);
10911
11137
  context.yjsProvider = newProvider;
10912
- _optionalChain([newProvider, 'optionalAccess', _260 => _260.on, 'call', _261 => _261("status", yjsStatusDidChange)]);
11138
+ _optionalChain([newProvider, 'optionalAccess', _271 => _271.on, 'call', _272 => _272("status", yjsStatusDidChange)]);
10913
11139
  context.yjsProviderDidChange.notify();
10914
11140
  },
10915
11141
  yjsProviderDidChange: context.yjsProviderDidChange.observable,
@@ -10950,6 +11176,11 @@ function createRoom(options, config) {
10950
11176
  connect: () => managedSocket.connect(),
10951
11177
  reconnect: () => managedSocket.reconnect(),
10952
11178
  disconnect: () => managedSocket.disconnect(),
11179
+ _dump: () => {
11180
+ const n = context.pool.nodes.size;
11181
+ return `Room "${roomId}" (${n} node${n === 1 ? "" : "s"}):
11182
+ ${dumpPool(context.pool)}`;
11183
+ },
10953
11184
  destroy: () => {
10954
11185
  pendingFeedsRequests.forEach(
10955
11186
  (request) => request.reject(new Error("Room destroyed"))
@@ -10962,7 +11193,7 @@ function createRoom(options, config) {
10962
11193
  source.dispose();
10963
11194
  }
10964
11195
  eventHub.roomWillDestroy.notify();
10965
- _optionalChain([context, 'access', _262 => _262.yjsProvider, 'optionalAccess', _263 => _263.off, 'call', _264 => _264("status", yjsStatusDidChange)]);
11196
+ _optionalChain([context, 'access', _273 => _273.yjsProvider, 'optionalAccess', _274 => _274.off, 'call', _275 => _275("status", yjsStatusDidChange)]);
10966
11197
  syncSourceForStorage.destroy();
10967
11198
  syncSourceForYjs.destroy();
10968
11199
  uninstallBgTabSpy();
@@ -11122,7 +11353,7 @@ function makeClassicSubscribeFn(roomId, events, errorEvents) {
11122
11353
  }
11123
11354
  if (isLiveNode(first)) {
11124
11355
  const node = first;
11125
- if (_optionalChain([options, 'optionalAccess', _265 => _265.isDeep])) {
11356
+ if (_optionalChain([options, 'optionalAccess', _276 => _276.isDeep])) {
11126
11357
  const storageCallback = second;
11127
11358
  return subscribeToLiveStructureDeeply(node, storageCallback);
11128
11359
  } else {
@@ -11208,8 +11439,8 @@ function createClient(options) {
11208
11439
  const authManager = createAuthManager(options, (token) => {
11209
11440
  currentUserId.set(() => token.uid);
11210
11441
  });
11211
- const fetchPolyfill = _optionalChain([clientOptions, 'access', _266 => _266.polyfills, 'optionalAccess', _267 => _267.fetch]) || /* istanbul ignore next */
11212
- _optionalChain([globalThis, 'access', _268 => _268.fetch, 'optionalAccess', _269 => _269.bind, 'call', _270 => _270(globalThis)]);
11442
+ const fetchPolyfill = _optionalChain([clientOptions, 'access', _277 => _277.polyfills, 'optionalAccess', _278 => _278.fetch]) || /* istanbul ignore next */
11443
+ _optionalChain([globalThis, 'access', _279 => _279.fetch, 'optionalAccess', _280 => _280.bind, 'call', _281 => _281(globalThis)]);
11213
11444
  const httpClient = createApiClient({
11214
11445
  baseUrl,
11215
11446
  fetchPolyfill,
@@ -11227,7 +11458,7 @@ function createClient(options) {
11227
11458
  delegates: {
11228
11459
  createSocket: makeCreateSocketDelegateForAi(
11229
11460
  baseUrl,
11230
- _optionalChain([clientOptions, 'access', _271 => _271.polyfills, 'optionalAccess', _272 => _272.WebSocket])
11461
+ _optionalChain([clientOptions, 'access', _282 => _282.polyfills, 'optionalAccess', _283 => _283.WebSocket])
11231
11462
  ),
11232
11463
  authenticate: async () => {
11233
11464
  const resp = await authManager.getAuthValue({
@@ -11297,7 +11528,7 @@ function createClient(options) {
11297
11528
  createSocket: makeCreateSocketDelegateForRoom(
11298
11529
  roomId,
11299
11530
  baseUrl,
11300
- _optionalChain([clientOptions, 'access', _273 => _273.polyfills, 'optionalAccess', _274 => _274.WebSocket])
11531
+ _optionalChain([clientOptions, 'access', _284 => _284.polyfills, 'optionalAccess', _285 => _285.WebSocket])
11301
11532
  ),
11302
11533
  authenticate: makeAuthDelegateForRoom(roomId, authManager)
11303
11534
  })),
@@ -11320,7 +11551,7 @@ function createClient(options) {
11320
11551
  const shouldConnect = _nullishCoalesce(options2.autoConnect, () => ( true));
11321
11552
  if (shouldConnect) {
11322
11553
  if (typeof atob === "undefined") {
11323
- if (_optionalChain([clientOptions, 'access', _275 => _275.polyfills, 'optionalAccess', _276 => _276.atob]) === void 0) {
11554
+ if (_optionalChain([clientOptions, 'access', _286 => _286.polyfills, 'optionalAccess', _287 => _287.atob]) === void 0) {
11324
11555
  throw new Error(
11325
11556
  "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"
11326
11557
  );
@@ -11332,7 +11563,7 @@ function createClient(options) {
11332
11563
  return leaseRoom(newRoomDetails);
11333
11564
  }
11334
11565
  function getRoom(roomId) {
11335
- const room = _optionalChain([roomsById, 'access', _277 => _277.get, 'call', _278 => _278(roomId), 'optionalAccess', _279 => _279.room]);
11566
+ const room = _optionalChain([roomsById, 'access', _288 => _288.get, 'call', _289 => _289(roomId), 'optionalAccess', _290 => _290.room]);
11336
11567
  return room ? room : null;
11337
11568
  }
11338
11569
  function logout() {
@@ -11348,7 +11579,7 @@ function createClient(options) {
11348
11579
  const batchedResolveUsers = new Batch(
11349
11580
  async (batchedUserIds) => {
11350
11581
  const userIds = batchedUserIds.flat();
11351
- const users = await _optionalChain([resolveUsers, 'optionalCall', _280 => _280({ userIds })]);
11582
+ const users = await _optionalChain([resolveUsers, 'optionalCall', _291 => _291({ userIds })]);
11352
11583
  warnOnceIf(
11353
11584
  !resolveUsers,
11354
11585
  "Set the resolveUsers option in createClient to specify user info."
@@ -11365,7 +11596,7 @@ function createClient(options) {
11365
11596
  const batchedResolveRoomsInfo = new Batch(
11366
11597
  async (batchedRoomIds) => {
11367
11598
  const roomIds = batchedRoomIds.flat();
11368
- const roomsInfo = await _optionalChain([resolveRoomsInfo, 'optionalCall', _281 => _281({ roomIds })]);
11599
+ const roomsInfo = await _optionalChain([resolveRoomsInfo, 'optionalCall', _292 => _292({ roomIds })]);
11369
11600
  warnOnceIf(
11370
11601
  !resolveRoomsInfo,
11371
11602
  "Set the resolveRoomsInfo option in createClient to specify room info."
@@ -11382,7 +11613,7 @@ function createClient(options) {
11382
11613
  const batchedResolveGroupsInfo = new Batch(
11383
11614
  async (batchedGroupIds) => {
11384
11615
  const groupIds = batchedGroupIds.flat();
11385
- const groupsInfo = await _optionalChain([resolveGroupsInfo, 'optionalCall', _282 => _282({ groupIds })]);
11616
+ const groupsInfo = await _optionalChain([resolveGroupsInfo, 'optionalCall', _293 => _293({ groupIds })]);
11386
11617
  warnOnceIf(
11387
11618
  !resolveGroupsInfo,
11388
11619
  "Set the resolveGroupsInfo option in createClient to specify group info."
@@ -11441,7 +11672,7 @@ function createClient(options) {
11441
11672
  }
11442
11673
  };
11443
11674
  const win = typeof window !== "undefined" ? window : void 0;
11444
- _optionalChain([win, 'optionalAccess', _283 => _283.addEventListener, 'call', _284 => _284("beforeunload", maybePreventClose)]);
11675
+ _optionalChain([win, 'optionalAccess', _294 => _294.addEventListener, 'call', _295 => _295("beforeunload", maybePreventClose)]);
11445
11676
  }
11446
11677
  async function getNotificationSettings(options2) {
11447
11678
  const plainSettings = await httpClient.getNotificationSettings(options2);
@@ -11457,6 +11688,7 @@ function createClient(options) {
11457
11688
  {
11458
11689
  enterRoom,
11459
11690
  getRoom,
11691
+ _dump: () => Array.from(roomsById.values(), ({ room }) => room._dump()).join("\n\n"),
11460
11692
  logout,
11461
11693
  // Public inbox notifications API
11462
11694
  getInboxNotifications: httpClient.getInboxNotifications,
@@ -11568,7 +11800,7 @@ var commentBodyElementsTypes = {
11568
11800
  mention: "inline"
11569
11801
  };
11570
11802
  function traverseCommentBody(body, elementOrVisitor, possiblyVisitor) {
11571
- if (!body || !_optionalChain([body, 'optionalAccess', _285 => _285.content])) {
11803
+ if (!body || !_optionalChain([body, 'optionalAccess', _296 => _296.content])) {
11572
11804
  return;
11573
11805
  }
11574
11806
  const element = typeof elementOrVisitor === "string" ? elementOrVisitor : void 0;
@@ -11578,13 +11810,13 @@ function traverseCommentBody(body, elementOrVisitor, possiblyVisitor) {
11578
11810
  for (const block of body.content) {
11579
11811
  if (type === "all" || type === "block") {
11580
11812
  if (guard(block)) {
11581
- _optionalChain([visitor, 'optionalCall', _286 => _286(block)]);
11813
+ _optionalChain([visitor, 'optionalCall', _297 => _297(block)]);
11582
11814
  }
11583
11815
  }
11584
11816
  if (type === "all" || type === "inline") {
11585
11817
  for (const inline of block.children) {
11586
11818
  if (guard(inline)) {
11587
- _optionalChain([visitor, 'optionalCall', _287 => _287(inline)]);
11819
+ _optionalChain([visitor, 'optionalCall', _298 => _298(inline)]);
11588
11820
  }
11589
11821
  }
11590
11822
  }
@@ -11754,7 +11986,7 @@ var stringifyCommentBodyPlainElements = {
11754
11986
  text: ({ element }) => element.text,
11755
11987
  link: ({ element }) => _nullishCoalesce(element.text, () => ( element.url)),
11756
11988
  mention: ({ element, user, group }) => {
11757
- return `@${_nullishCoalesce(_nullishCoalesce(_optionalChain([user, 'optionalAccess', _288 => _288.name]), () => ( _optionalChain([group, 'optionalAccess', _289 => _289.name]))), () => ( element.id))}`;
11989
+ return `@${_nullishCoalesce(_nullishCoalesce(_optionalChain([user, 'optionalAccess', _299 => _299.name]), () => ( _optionalChain([group, 'optionalAccess', _300 => _300.name]))), () => ( element.id))}`;
11758
11990
  }
11759
11991
  };
11760
11992
  var stringifyCommentBodyHtmlElements = {
@@ -11784,7 +12016,7 @@ var stringifyCommentBodyHtmlElements = {
11784
12016
  return html`<a href="${href}" target="_blank" rel="noopener noreferrer">${element.text ? html`${element.text}` : element.url}</a>`;
11785
12017
  },
11786
12018
  mention: ({ element, user, group }) => {
11787
- 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>`;
12019
+ 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>`;
11788
12020
  }
11789
12021
  };
11790
12022
  var stringifyCommentBodyMarkdownElements = {
@@ -11814,20 +12046,20 @@ var stringifyCommentBodyMarkdownElements = {
11814
12046
  return markdown`[${_nullishCoalesce(element.text, () => ( element.url))}](${href})`;
11815
12047
  },
11816
12048
  mention: ({ element, user, group }) => {
11817
- return markdown`@${_nullishCoalesce(_nullishCoalesce(_optionalChain([user, 'optionalAccess', _294 => _294.name]), () => ( _optionalChain([group, 'optionalAccess', _295 => _295.name]))), () => ( element.id))}`;
12049
+ return markdown`@${_nullishCoalesce(_nullishCoalesce(_optionalChain([user, 'optionalAccess', _305 => _305.name]), () => ( _optionalChain([group, 'optionalAccess', _306 => _306.name]))), () => ( element.id))}`;
11818
12050
  }
11819
12051
  };
11820
12052
  async function stringifyCommentBody(body, options) {
11821
- const format = _nullishCoalesce(_optionalChain([options, 'optionalAccess', _296 => _296.format]), () => ( "plain"));
11822
- const separator = _nullishCoalesce(_optionalChain([options, 'optionalAccess', _297 => _297.separator]), () => ( (format === "markdown" ? "\n\n" : "\n")));
12053
+ const format = _nullishCoalesce(_optionalChain([options, 'optionalAccess', _307 => _307.format]), () => ( "plain"));
12054
+ const separator = _nullishCoalesce(_optionalChain([options, 'optionalAccess', _308 => _308.separator]), () => ( (format === "markdown" ? "\n\n" : "\n")));
11823
12055
  const elements = {
11824
12056
  ...format === "html" ? stringifyCommentBodyHtmlElements : format === "markdown" ? stringifyCommentBodyMarkdownElements : stringifyCommentBodyPlainElements,
11825
- ..._optionalChain([options, 'optionalAccess', _298 => _298.elements])
12057
+ ..._optionalChain([options, 'optionalAccess', _309 => _309.elements])
11826
12058
  };
11827
12059
  const { users: resolvedUsers, groups: resolvedGroupsInfo } = await resolveMentionsInCommentBody(
11828
12060
  body,
11829
- _optionalChain([options, 'optionalAccess', _299 => _299.resolveUsers]),
11830
- _optionalChain([options, 'optionalAccess', _300 => _300.resolveGroupsInfo])
12061
+ _optionalChain([options, 'optionalAccess', _310 => _310.resolveUsers]),
12062
+ _optionalChain([options, 'optionalAccess', _311 => _311.resolveGroupsInfo])
11831
12063
  );
11832
12064
  const blocks = body.content.flatMap((block, blockIndex) => {
11833
12065
  switch (block.type) {
@@ -11962,9 +12194,9 @@ function makePoller(callback, intervalMs, options) {
11962
12194
  const startTime = performance.now();
11963
12195
  const doc = typeof document !== "undefined" ? document : void 0;
11964
12196
  const win = typeof window !== "undefined" ? window : void 0;
11965
- const maxStaleTimeMs = _nullishCoalesce(_optionalChain([options, 'optionalAccess', _301 => _301.maxStaleTimeMs]), () => ( Number.POSITIVE_INFINITY));
12197
+ const maxStaleTimeMs = _nullishCoalesce(_optionalChain([options, 'optionalAccess', _312 => _312.maxStaleTimeMs]), () => ( Number.POSITIVE_INFINITY));
11966
12198
  const context = {
11967
- inForeground: _optionalChain([doc, 'optionalAccess', _302 => _302.visibilityState]) !== "hidden",
12199
+ inForeground: _optionalChain([doc, 'optionalAccess', _313 => _313.visibilityState]) !== "hidden",
11968
12200
  lastSuccessfulPollAt: startTime,
11969
12201
  count: 0,
11970
12202
  backoff: 0
@@ -12045,11 +12277,11 @@ function makePoller(callback, intervalMs, options) {
12045
12277
  pollNowIfStale();
12046
12278
  }
12047
12279
  function onVisibilityChange() {
12048
- setInForeground(_optionalChain([doc, 'optionalAccess', _303 => _303.visibilityState]) !== "hidden");
12280
+ setInForeground(_optionalChain([doc, 'optionalAccess', _314 => _314.visibilityState]) !== "hidden");
12049
12281
  }
12050
- _optionalChain([doc, 'optionalAccess', _304 => _304.addEventListener, 'call', _305 => _305("visibilitychange", onVisibilityChange)]);
12051
- _optionalChain([win, 'optionalAccess', _306 => _306.addEventListener, 'call', _307 => _307("online", onVisibilityChange)]);
12052
- _optionalChain([win, 'optionalAccess', _308 => _308.addEventListener, 'call', _309 => _309("focus", pollNowIfStale)]);
12282
+ _optionalChain([doc, 'optionalAccess', _315 => _315.addEventListener, 'call', _316 => _316("visibilitychange", onVisibilityChange)]);
12283
+ _optionalChain([win, 'optionalAccess', _317 => _317.addEventListener, 'call', _318 => _318("online", onVisibilityChange)]);
12284
+ _optionalChain([win, 'optionalAccess', _319 => _319.addEventListener, 'call', _320 => _320("focus", pollNowIfStale)]);
12053
12285
  fsm.start();
12054
12286
  return {
12055
12287
  inc,