@liveblocks/core 3.19.2 → 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.2";
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)) {
@@ -2715,6 +2729,7 @@ var HttpClient = class {
2715
2729
  };
2716
2730
 
2717
2731
  // src/lib/fsm.ts
2732
+ var IGNORE = /* @__PURE__ */ Symbol("fsm.ignore");
2718
2733
  function distance(state1, state2) {
2719
2734
  if (state1 === state2) {
2720
2735
  return [0, 0];
@@ -2887,7 +2902,7 @@ var FSM = class {
2887
2902
  this.#eventHub = {
2888
2903
  didReceiveEvent: makeEventSource(),
2889
2904
  willTransition: makeEventSource(),
2890
- didIgnoreEvent: makeEventSource(),
2905
+ didIgnoreUnexpectedEvent: makeEventSource(),
2891
2906
  willExitState: makeEventSource(),
2892
2907
  didEnterState: makeEventSource(),
2893
2908
  didExitState: makeEventSource()
@@ -2895,7 +2910,7 @@ var FSM = class {
2895
2910
  this.events = {
2896
2911
  didReceiveEvent: this.#eventHub.didReceiveEvent.observable,
2897
2912
  willTransition: this.#eventHub.willTransition.observable,
2898
- didIgnoreEvent: this.#eventHub.didIgnoreEvent.observable,
2913
+ didIgnoreUnexpectedEvent: this.#eventHub.didIgnoreUnexpectedEvent.observable,
2899
2914
  willExitState: this.#eventHub.willExitState.observable,
2900
2915
  didEnterState: this.#eventHub.didEnterState.observable,
2901
2916
  didExitState: this.#eventHub.didExitState.observable
@@ -3018,9 +3033,14 @@ var FSM = class {
3018
3033
  * `context` params to conditionally decide which next state to transition
3019
3034
  * to.
3020
3035
  *
3021
- * If you set it to `null`, then the transition will be explicitly forbidden
3022
- * and throw an error. If you don't define a target for a transition, then
3023
- * such events will get ignored.
3036
+ * If you don't define a target for a transition, the event is treated
3037
+ * as unhandled in this state: `didIgnoreUnexpectedEvent` fires and the
3038
+ * state does not change.
3039
+ *
3040
+ * To declare an event as an intentional silent no-op in this state, use
3041
+ * the {@link IGNORE} sentinel — either statically (`{ EVENT: IGNORE }`)
3042
+ * or as a return value from a target function. IGNORE'd events do not
3043
+ * fire `didIgnoreUnexpectedEvent`.
3024
3044
  */
3025
3045
  addTransitions(nameOrPattern, mapping) {
3026
3046
  if (this.#runningState !== 0 /* NOT_STARTED_YET */) {
@@ -3040,7 +3060,12 @@ var FSM = class {
3040
3060
  }
3041
3061
  const target = target_;
3042
3062
  this.#knownEventTypes.add(type);
3043
- if (target !== void 0) {
3063
+ if (target === void 0) {
3064
+ continue;
3065
+ }
3066
+ if (target === IGNORE) {
3067
+ map.set(type, IGNORE);
3068
+ } else {
3044
3069
  const targetFn = typeof target === "function" ? target : () => target;
3045
3070
  map.set(type, targetFn);
3046
3071
  }
@@ -3139,12 +3164,14 @@ var FSM = class {
3139
3164
  if (this.#runningState === 2 /* STOPPED */) {
3140
3165
  return;
3141
3166
  }
3142
- const targetFn = this.#getTargetFn(event.type);
3143
- if (targetFn !== void 0) {
3144
- return this.#transition(event, targetFn);
3145
- } else {
3146
- this.#eventHub.didIgnoreEvent.notify(event);
3167
+ const entry = this.#getTargetFn(event.type);
3168
+ if (entry === IGNORE) {
3169
+ return;
3147
3170
  }
3171
+ if (entry !== void 0) {
3172
+ return this.#transition(event, entry);
3173
+ }
3174
+ this.#eventHub.didIgnoreUnexpectedEvent.notify(event);
3148
3175
  }
3149
3176
  #transition(event, target) {
3150
3177
  this.#eventHub.didReceiveEvent.notify(event);
@@ -3153,8 +3180,7 @@ var FSM = class {
3153
3180
  const nextTarget = targetFn(event, this.#currentContext.current);
3154
3181
  let nextState;
3155
3182
  let effects = void 0;
3156
- if (nextTarget === null) {
3157
- this.#eventHub.didIgnoreEvent.notify(event);
3183
+ if (nextTarget === IGNORE) {
3158
3184
  return;
3159
3185
  }
3160
3186
  if (typeof nextTarget === "string") {
@@ -3373,8 +3399,8 @@ function enableTracing(machine) {
3373
3399
  machine.events.didExitState.subscribe(
3374
3400
  ({ state, durationMs }) => log2(`Exited ${state} after ${durationMs.toFixed(0)}ms`)
3375
3401
  ),
3376
- machine.events.didIgnoreEvent.subscribe(
3377
- (e) => log2("Ignored event", e.type, e, "(current state won't handle it)")
3402
+ machine.events.didIgnoreUnexpectedEvent.subscribe(
3403
+ (e) => log2("Ignored unexpected event", e.type, e, "(no transition declared)")
3378
3404
  )
3379
3405
  ];
3380
3406
  return () => {
@@ -3486,7 +3512,12 @@ function createConnectionStateMachine(delegates, options) {
3486
3512
  );
3487
3513
  const onSocketError = (event) => machine.send({ type: "EXPLICIT_SOCKET_ERROR", event });
3488
3514
  const onSocketClose = (event) => machine.send({ type: "EXPLICIT_SOCKET_CLOSE", event });
3489
- const onSocketMessage = (event) => event.data === "pong" ? machine.send({ type: "PONG" }) : onMessage.notify(event);
3515
+ const onSocketMessage = (event) => {
3516
+ machine.send({ type: "ALIVE" });
3517
+ if (event.data !== "pong") {
3518
+ onMessage.notify(event);
3519
+ }
3520
+ };
3490
3521
  function teardownSocket(socket) {
3491
3522
  if (socket) {
3492
3523
  socket.removeEventListener("error", onSocketError);
@@ -3656,7 +3687,13 @@ function createConnectionStateMachine(delegates, options) {
3656
3687
  effect: [increaseBackoffDelay, logPrematureErrorOrCloseEvent(err)]
3657
3688
  };
3658
3689
  }
3659
- );
3690
+ ).addTransitions("@connecting.busy", {
3691
+ // The socket message listener is attached during @connecting.busy (see
3692
+ // onEnterAsync above), so server frames (most notably the actor-id
3693
+ // handshake) can fire onSocketMessage and emit a ALIVE before we
3694
+ // reach @ok.*. That's fine. Heartbeat only matters in @ok.*.
3695
+ ALIVE: IGNORE
3696
+ });
3660
3697
  const sendHeartbeat = {
3661
3698
  target: "@ok.awaiting-pong",
3662
3699
  effect: (ctx) => {
@@ -3671,7 +3708,8 @@ function createConnectionStateMachine(delegates, options) {
3671
3708
  machine.addTimedTransition("@ok.connected", HEARTBEAT_INTERVAL, maybeHeartbeat).addTransitions("@ok.connected", {
3672
3709
  NAVIGATOR_OFFLINE: maybeHeartbeat,
3673
3710
  // Don't take the browser's word for it when it says it's offline. Do a ping/pong to make sure.
3674
- WINDOW_GOT_FOCUS: sendHeartbeat
3711
+ WINDOW_GOT_FOCUS: sendHeartbeat,
3712
+ ALIVE: IGNORE
3675
3713
  });
3676
3714
  machine.addTransitions("@idle.zombie", {
3677
3715
  WINDOW_GOT_FOCUS: "@connecting.backoff"
@@ -3692,7 +3730,7 @@ function createConnectionStateMachine(delegates, options) {
3692
3730
  clearTimeout(timerID);
3693
3731
  onMessage.pause();
3694
3732
  };
3695
- }).addTransitions("@ok.awaiting-pong", { PONG: "@ok.connected" }).addTimedTransition("@ok.awaiting-pong", PONG_TIMEOUT, {
3733
+ }).addTransitions("@ok.awaiting-pong", { ALIVE: "@ok.connected" }).addTimedTransition("@ok.awaiting-pong", PONG_TIMEOUT, {
3696
3734
  target: "@connecting.busy",
3697
3735
  // Log implicit connection loss and drop the current open socket
3698
3736
  effect: log(
@@ -3705,7 +3743,7 @@ function createConnectionStateMachine(delegates, options) {
3705
3743
  // not. When still OPEN, don't transition.
3706
3744
  EXPLICIT_SOCKET_ERROR: (_, context) => {
3707
3745
  if (_optionalChain([context, 'access', _48 => _48.socket, 'optionalAccess', _49 => _49.readyState]) === 1) {
3708
- return null;
3746
+ return IGNORE;
3709
3747
  }
3710
3748
  return {
3711
3749
  target: "@connecting.backoff",
@@ -5478,6 +5516,9 @@ var OpCode = Object.freeze({
5478
5516
  function isIgnoredOp(op) {
5479
5517
  return op.type === OpCode.DELETE_CRDT && op.id === "ACK";
5480
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
+ }
5481
5522
 
5482
5523
  // src/protocol/StorageNode.ts
5483
5524
  var CrdtType = Object.freeze({
@@ -5735,12 +5776,95 @@ function asPos(str) {
5735
5776
  return isPos(str) ? str : convertToPos(str);
5736
5777
  }
5737
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
+
5738
5861
  // src/crdts/AbstractCrdt.ts
5739
5862
  function createManagedPool(roomId, options) {
5740
5863
  const {
5741
5864
  getCurrentConnectionId,
5742
5865
  onDispatch,
5743
- isStorageWritable = () => true
5866
+ isStorageWritable = () => true,
5867
+ unacknowledgedOps = new UnacknowledgedOps()
5744
5868
  } = options;
5745
5869
  let clock = 0;
5746
5870
  let opClock = 0;
@@ -5754,7 +5878,7 @@ function createManagedPool(roomId, options) {
5754
5878
  generateId: () => `${getCurrentConnectionId()}:${clock++}`,
5755
5879
  generateOpId: () => `${getCurrentConnectionId()}:${opClock++}`,
5756
5880
  dispatch(ops, reverse, storageUpdates) {
5757
- _optionalChain([onDispatch, 'optionalCall', _111 => _111(ops, reverse, storageUpdates)]);
5881
+ _optionalChain([onDispatch, 'optionalCall', _125 => _125(ops, reverse, storageUpdates)]);
5758
5882
  },
5759
5883
  assertStorageIsWritable: () => {
5760
5884
  if (!isStorageWritable()) {
@@ -5762,7 +5886,8 @@ function createManagedPool(roomId, options) {
5762
5886
  "Cannot write to storage with a read only user, please ensure the user has write permissions"
5763
5887
  );
5764
5888
  }
5765
- }
5889
+ },
5890
+ unacknowledgedOps
5766
5891
  };
5767
5892
  }
5768
5893
  function crdtAsLiveNode(value) {
@@ -6044,11 +6169,9 @@ function childNodeLt(a, b) {
6044
6169
  var LiveList = class _LiveList extends AbstractCrdt {
6045
6170
  #items;
6046
6171
  #implicitlyDeletedItems;
6047
- #unacknowledgedSets;
6048
6172
  constructor(items) {
6049
6173
  super();
6050
6174
  this.#implicitlyDeletedItems = /* @__PURE__ */ new WeakSet();
6051
- this.#unacknowledgedSets = /* @__PURE__ */ new Map();
6052
6175
  const nodes = [];
6053
6176
  let lastPos;
6054
6177
  for (const item of items) {
@@ -6078,12 +6201,13 @@ var LiveList = class _LiveList extends AbstractCrdt {
6078
6201
  }
6079
6202
  /**
6080
6203
  * @internal
6081
- * This function assumes that the resulting ops will be sent to the server if they have an 'opId'
6082
- * so we mutate _unacknowledgedSets to avoid potential flickering
6083
- * 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).
6084
6208
  *
6085
- * This is quite unintuitive and should disappear as soon as
6086
- * 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.
6087
6211
  */
6088
6212
  _toOps(parentId, parentKey) {
6089
6213
  if (this._id === void 0) {
@@ -6099,9 +6223,9 @@ var LiveList = class _LiveList extends AbstractCrdt {
6099
6223
  ops.push(op);
6100
6224
  for (const item of this.#items) {
6101
6225
  const parentKey2 = item._getParentKeyOrThrow();
6102
- const childOps = HACK_addIntentAndDeletedIdToOperation(
6226
+ const childOps = addIntentToRootOp(
6103
6227
  item._toOps(this._id, parentKey2),
6104
- void 0
6228
+ "set"
6105
6229
  );
6106
6230
  for (const childOp of childOps) {
6107
6231
  ops.push(childOp);
@@ -6143,6 +6267,28 @@ var LiveList = class _LiveList extends AbstractCrdt {
6143
6267
  (item) => item._getParentKeyOrThrow() === position
6144
6268
  );
6145
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
+ }
6146
6292
  /** @internal */
6147
6293
  _attach(id, pool) {
6148
6294
  super._attach(id, pool);
@@ -6223,13 +6369,9 @@ var LiveList = class _LiveList extends AbstractCrdt {
6223
6369
  if (deletedDelta) {
6224
6370
  delta.push(deletedDelta);
6225
6371
  }
6226
- const unacknowledgedOpId = this.#unacknowledgedSets.get(op.parentKey);
6227
- if (unacknowledgedOpId !== void 0) {
6228
- if (unacknowledgedOpId !== op.opId) {
6229
- return delta.length === 0 ? { modified: false } : { modified: makeUpdate(this, delta), reverse: [] };
6230
- } else {
6231
- this.#unacknowledgedSets.delete(op.parentKey);
6232
- }
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: [] };
6233
6375
  }
6234
6376
  const indexOfItemWithSamePosition = this._indexOfPosition(op.parentKey);
6235
6377
  const existingItem = this.#items.find((item) => item._id === op.id);
@@ -6310,7 +6452,7 @@ var LiveList = class _LiveList extends AbstractCrdt {
6310
6452
  }
6311
6453
  return result.modified.updates[0];
6312
6454
  }
6313
- #applyRemoteInsert(op) {
6455
+ #applyRemoteInsert(op, fromSnapshot) {
6314
6456
  if (this._pool === void 0) {
6315
6457
  throw new Error("Can't attach child if managed pool is not present");
6316
6458
  }
@@ -6320,11 +6462,82 @@ var LiveList = class _LiveList extends AbstractCrdt {
6320
6462
  this.#shiftItemPosition(existingItemIndex, key);
6321
6463
  }
6322
6464
  const { newItem, newIndex } = this.#createAttachItemAndSort(op, key);
6465
+ const bumpDeltas = fromSnapshot ? [] : this.#bumpUnackedPushesAbove(key);
6323
6466
  return {
6324
- modified: makeUpdate(this, [insertDelta(newIndex, newItem)]),
6467
+ modified: makeUpdate(this, [
6468
+ insertDelta(newIndex, newItem),
6469
+ ...bumpDeltas
6470
+ ]),
6325
6471
  reverse: []
6326
6472
  };
6327
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
+ }
6328
6541
  #applyInsertAck(op) {
6329
6542
  const existingItem = this.#items.find((item) => item._id === op.id);
6330
6543
  const key = asPos(op.parentKey);
@@ -6379,7 +6592,7 @@ var LiveList = class _LiveList extends AbstractCrdt {
6379
6592
  #applyInsertUndoRedo(op) {
6380
6593
  const { id, parentKey: key } = op;
6381
6594
  const child = creationOpToLiveNode(op);
6382
- 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) {
6383
6596
  return { modified: false };
6384
6597
  }
6385
6598
  child._attach(id, nn(this._pool));
@@ -6387,8 +6600,8 @@ var LiveList = class _LiveList extends AbstractCrdt {
6387
6600
  const existingItemIndex = this._indexOfPosition(key);
6388
6601
  let newKey = key;
6389
6602
  if (existingItemIndex !== -1) {
6390
- const before2 = _optionalChain([this, 'access', _115 => _115.#items, 'access', _116 => _116.at, 'call', _117 => _117(existingItemIndex), 'optionalAccess', _118 => _118._parentPos]);
6391
- 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]);
6392
6605
  newKey = makePosition(before2, after2);
6393
6606
  child._setParentLink(this, newKey);
6394
6607
  }
@@ -6402,10 +6615,9 @@ var LiveList = class _LiveList extends AbstractCrdt {
6402
6615
  #applySetUndoRedo(op) {
6403
6616
  const { id, parentKey: key } = op;
6404
6617
  const child = creationOpToLiveNode(op);
6405
- 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) {
6406
6619
  return { modified: false };
6407
6620
  }
6408
- this.#unacknowledgedSets.set(key, nn(op.opId));
6409
6621
  const indexOfItemWithSameKey = this._indexOfPosition(key);
6410
6622
  child._attach(id, nn(this._pool));
6411
6623
  child._setParentLink(this, key);
@@ -6415,8 +6627,9 @@ var LiveList = class _LiveList extends AbstractCrdt {
6415
6627
  existingItem._detach();
6416
6628
  this.#items.remove(existingItem);
6417
6629
  this.#items.add(child);
6418
- const reverse = HACK_addIntentAndDeletedIdToOperation(
6630
+ const reverse = addIntentToRootOp(
6419
6631
  existingItem._toOps(nn(this._id), key),
6632
+ "set",
6420
6633
  op.id
6421
6634
  );
6422
6635
  const delta = [setDelta(indexOfItemWithSameKey, child)];
@@ -6441,7 +6654,7 @@ var LiveList = class _LiveList extends AbstractCrdt {
6441
6654
  }
6442
6655
  }
6443
6656
  /** @internal */
6444
- _attachChild(op, source) {
6657
+ _attachChild(op, source, fromSnapshot = false) {
6445
6658
  if (this._pool === void 0) {
6446
6659
  throw new Error("Can't attach child if managed pool is not present");
6447
6660
  }
@@ -6456,7 +6669,7 @@ var LiveList = class _LiveList extends AbstractCrdt {
6456
6669
  }
6457
6670
  } else {
6458
6671
  if (source === 1 /* THEIRS */) {
6459
- result = this.#applyRemoteInsert(op);
6672
+ result = this.#applyRemoteInsert(op, fromSnapshot);
6460
6673
  } else if (source === 2 /* OURS */) {
6461
6674
  result = this.#applyInsertAck(op);
6462
6675
  } else {
@@ -6523,7 +6736,7 @@ var LiveList = class _LiveList extends AbstractCrdt {
6523
6736
  } else {
6524
6737
  this.#updateItemPositionAt(
6525
6738
  existingItemIndex,
6526
- 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]))
6527
6740
  );
6528
6741
  const previousIndex = this.#items.findIndex((item) => item === child);
6529
6742
  this.#updateItemPosition(child, newKey);
@@ -6550,7 +6763,7 @@ var LiveList = class _LiveList extends AbstractCrdt {
6550
6763
  this,
6551
6764
  makePosition(
6552
6765
  newKey,
6553
- _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])
6554
6767
  )
6555
6768
  );
6556
6769
  this.#items.reposition(existingItem);
@@ -6574,7 +6787,7 @@ var LiveList = class _LiveList extends AbstractCrdt {
6574
6787
  existingItemIndex,
6575
6788
  makePosition(
6576
6789
  newKey,
6577
- _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])
6578
6791
  )
6579
6792
  );
6580
6793
  }
@@ -6602,7 +6815,7 @@ var LiveList = class _LiveList extends AbstractCrdt {
6602
6815
  if (existingItemIndex !== -1) {
6603
6816
  actualNewKey = makePosition(
6604
6817
  newKey,
6605
- _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])
6606
6819
  );
6607
6820
  }
6608
6821
  this.#updateItemPosition(child, actualNewKey);
@@ -6659,8 +6872,7 @@ var LiveList = class _LiveList extends AbstractCrdt {
6659
6872
  * @param element The element to add to the end of the LiveList.
6660
6873
  */
6661
6874
  push(element) {
6662
- _optionalChain([this, 'access', _142 => _142._pool, 'optionalAccess', _143 => _143.assertStorageIsWritable, 'call', _144 => _144()]);
6663
- return this.insert(element, this.length);
6875
+ return this.#injectAt(element, this.length, "push");
6664
6876
  }
6665
6877
  /**
6666
6878
  * Inserts one element at a specified index.
@@ -6668,14 +6880,23 @@ var LiveList = class _LiveList extends AbstractCrdt {
6668
6880
  * @param index The index at which you want to insert the element.
6669
6881
  */
6670
6882
  insert(element, index) {
6671
- _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()]);
6672
6893
  if (index < 0 || index > this.#items.length) {
6673
6894
  throw new Error(
6674
6895
  `Cannot insert list item at index "${index}". index should be between 0 and ${this.#items.length}`
6675
6896
  );
6676
6897
  }
6677
- const before2 = _optionalChain([this, 'access', _148 => _148.#items, 'access', _149 => _149.at, 'call', _150 => _150(index - 1), 'optionalAccess', _151 => _151._parentPos]);
6678
- 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]);
6679
6900
  const position = makePosition(before2, after2);
6680
6901
  const value = lsonToLiveNode(element);
6681
6902
  value._setParentLink(this, position);
@@ -6683,8 +6904,9 @@ var LiveList = class _LiveList extends AbstractCrdt {
6683
6904
  if (this._pool && this._id) {
6684
6905
  const id = this._pool.generateId();
6685
6906
  value._attach(id, this._pool);
6907
+ const ops = value._toOpsWithOpId(this._id, position, this._pool);
6686
6908
  this._pool.dispatch(
6687
- value._toOpsWithOpId(this._id, position, this._pool),
6909
+ intent === "push" ? addIntentToRootOp(ops, "push") : ops,
6688
6910
  [{ type: OpCode.DELETE_CRDT, id }],
6689
6911
  /* @__PURE__ */ new Map([
6690
6912
  [this._id, makeUpdate(this, [insertDelta(index, value)])]
@@ -6698,7 +6920,7 @@ var LiveList = class _LiveList extends AbstractCrdt {
6698
6920
  * @param targetIndex The index where the element should be after moving.
6699
6921
  */
6700
6922
  move(index, targetIndex) {
6701
- _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()]);
6702
6924
  if (targetIndex < 0) {
6703
6925
  throw new Error("targetIndex cannot be less than 0");
6704
6926
  }
@@ -6716,11 +6938,11 @@ var LiveList = class _LiveList extends AbstractCrdt {
6716
6938
  let beforePosition = null;
6717
6939
  let afterPosition = null;
6718
6940
  if (index < targetIndex) {
6719
- 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]);
6720
6942
  beforePosition = this.#items.at(targetIndex)._parentPos;
6721
6943
  } else {
6722
6944
  afterPosition = this.#items.at(targetIndex)._parentPos;
6723
- 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]);
6724
6946
  }
6725
6947
  const position = makePosition(beforePosition, afterPosition);
6726
6948
  const item = this.#items.at(index);
@@ -6755,7 +6977,7 @@ var LiveList = class _LiveList extends AbstractCrdt {
6755
6977
  * @param index The index of the element to delete
6756
6978
  */
6757
6979
  delete(index) {
6758
- _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()]);
6759
6981
  if (index < 0 || index >= this.#items.length) {
6760
6982
  throw new Error(
6761
6983
  `Cannot delete list item at index "${index}". index should be between 0 and ${this.#items.length - 1}`
@@ -6788,7 +7010,7 @@ var LiveList = class _LiveList extends AbstractCrdt {
6788
7010
  }
6789
7011
  }
6790
7012
  clear() {
6791
- _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()]);
6792
7014
  if (this._pool) {
6793
7015
  const ops = [];
6794
7016
  const reverseOps = [];
@@ -6822,7 +7044,7 @@ var LiveList = class _LiveList extends AbstractCrdt {
6822
7044
  }
6823
7045
  }
6824
7046
  set(index, item) {
6825
- _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()]);
6826
7048
  if (index < 0 || index >= this.#items.length) {
6827
7049
  throw new Error(
6828
7050
  `Cannot set list item at index "${index}". index should be between 0 and ${this.#items.length - 1}`
@@ -6842,13 +7064,14 @@ var LiveList = class _LiveList extends AbstractCrdt {
6842
7064
  value._attach(id, this._pool);
6843
7065
  const storageUpdates = /* @__PURE__ */ new Map();
6844
7066
  storageUpdates.set(this._id, makeUpdate(this, [setDelta(index, value)]));
6845
- const ops = HACK_addIntentAndDeletedIdToOperation(
7067
+ const ops = addIntentToRootOp(
6846
7068
  value._toOpsWithOpId(this._id, position, this._pool),
7069
+ "set",
6847
7070
  existingId
6848
7071
  );
6849
- this.#unacknowledgedSets.set(position, nn(ops[0].opId));
6850
- const reverseOps = HACK_addIntentAndDeletedIdToOperation(
7072
+ const reverseOps = addIntentToRootOp(
6851
7073
  existingItem._toOps(this._id, position),
7074
+ "set",
6852
7075
  id
6853
7076
  );
6854
7077
  this._pool.dispatch(ops, reverseOps, storageUpdates);
@@ -6980,7 +7203,7 @@ var LiveList = class _LiveList extends AbstractCrdt {
6980
7203
  #shiftItemPosition(index, key) {
6981
7204
  const shiftedPosition = makePosition(
6982
7205
  key,
6983
- 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
6984
7207
  );
6985
7208
  this.#updateItemPositionAt(index, shiftedPosition);
6986
7209
  }
@@ -7049,15 +7272,11 @@ function moveDelta(previousIndex, index, item) {
7049
7272
  previousIndex
7050
7273
  };
7051
7274
  }
7052
- function HACK_addIntentAndDeletedIdToOperation(ops, deletedId) {
7275
+ function addIntentToRootOp(ops, intent, deletedId) {
7053
7276
  return ops.map((op, index) => {
7054
7277
  if (index === 0) {
7055
7278
  const firstOp = op;
7056
- return {
7057
- ...firstOp,
7058
- intent: "set",
7059
- deletedId
7060
- };
7279
+ return { ...firstOp, intent, deletedId };
7061
7280
  } else {
7062
7281
  return op;
7063
7282
  }
@@ -7233,7 +7452,7 @@ var LiveMap = class _LiveMap extends AbstractCrdt {
7233
7452
  * @param value The value of the element to add. Should be serializable to JSON.
7234
7453
  */
7235
7454
  set(key, value) {
7236
- _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()]);
7237
7456
  const oldValue = this.#map.get(key);
7238
7457
  if (oldValue) {
7239
7458
  oldValue._detach();
@@ -7279,7 +7498,7 @@ var LiveMap = class _LiveMap extends AbstractCrdt {
7279
7498
  * @returns true if an element existed and has been removed, or false if the element does not exist.
7280
7499
  */
7281
7500
  delete(key) {
7282
- _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()]);
7283
7502
  const item = this.#map.get(key);
7284
7503
  if (item === void 0) {
7285
7504
  return false;
@@ -7890,20 +8109,20 @@ var LiveObject = (_class2 = class _LiveObject extends AbstractCrdt {
7890
8109
  * Caveat: this method will not add changes to the undo/redo stack.
7891
8110
  */
7892
8111
  setLocal(key, value) {
7893
- _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()]);
7894
8113
  const deleteResult = this.#prepareDelete(key);
7895
8114
  this.#local.set(key, value);
7896
8115
  this.invalidate();
7897
8116
  if (this._pool !== void 0 && this._id !== void 0) {
7898
- const ops = _nullishCoalesce(_optionalChain([deleteResult, 'optionalAccess', _189 => _189[0]]), () => ( []));
7899
- const reverse = _nullishCoalesce(_optionalChain([deleteResult, 'optionalAccess', _190 => _190[1]]), () => ( []));
7900
- 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()));
7901
8120
  const existing = storageUpdates.get(this._id);
7902
8121
  storageUpdates.set(this._id, {
7903
8122
  node: this,
7904
8123
  type: "LiveObject",
7905
8124
  updates: {
7906
- ..._optionalChain([existing, 'optionalAccess', _192 => _192.updates]),
8125
+ ..._optionalChain([existing, 'optionalAccess', _203 => _203.updates]),
7907
8126
  [key]: { type: "update" }
7908
8127
  }
7909
8128
  });
@@ -7923,7 +8142,7 @@ var LiveObject = (_class2 = class _LiveObject extends AbstractCrdt {
7923
8142
  * #synced or pool/id are unavailable. Does NOT dispatch.
7924
8143
  */
7925
8144
  #prepareDelete(key) {
7926
- _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()]);
7927
8146
  const k = key;
7928
8147
  if (this.#local.has(k) && !this.#synced.has(k)) {
7929
8148
  const oldValue2 = this.#local.get(k);
@@ -7999,7 +8218,7 @@ var LiveObject = (_class2 = class _LiveObject extends AbstractCrdt {
7999
8218
  const result = this.#prepareDelete(key);
8000
8219
  if (result) {
8001
8220
  const [ops, reverse, storageUpdates] = result;
8002
- _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)]);
8003
8222
  }
8004
8223
  }
8005
8224
  /**
@@ -8007,7 +8226,7 @@ var LiveObject = (_class2 = class _LiveObject extends AbstractCrdt {
8007
8226
  * @param patch The object used to overrides properties
8008
8227
  */
8009
8228
  update(patch) {
8010
- _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()]);
8011
8230
  if (_LiveObject.detectLargeObjects) {
8012
8231
  const data = {};
8013
8232
  for (const [key, value] of this.#synced) {
@@ -8297,6 +8516,31 @@ function lsonToLiveNode(value) {
8297
8516
  return new LiveRegister(value);
8298
8517
  }
8299
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
+ }
8300
8544
  function getTreesDiffOperations(currentItems, newItems) {
8301
8545
  const ops = [];
8302
8546
  currentItems.forEach((_, id) => {
@@ -8424,7 +8668,7 @@ function sendToPanel(message, options) {
8424
8668
  ...message,
8425
8669
  source: "liveblocks-devtools-client"
8426
8670
  };
8427
- if (!(_optionalChain([options, 'optionalAccess', _202 => _202.force]) || _bridgeActive)) {
8671
+ if (!(_optionalChain([options, 'optionalAccess', _213 => _213.force]) || _bridgeActive)) {
8428
8672
  return;
8429
8673
  }
8430
8674
  window.postMessage(fullMsg, "*");
@@ -8432,7 +8676,7 @@ function sendToPanel(message, options) {
8432
8676
  var eventSource = makeEventSource();
8433
8677
  if (process.env.NODE_ENV !== "production" && typeof window !== "undefined") {
8434
8678
  window.addEventListener("message", (event) => {
8435
- 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") {
8436
8680
  eventSource.notify(event.data);
8437
8681
  } else {
8438
8682
  }
@@ -8574,7 +8818,7 @@ function fullSync(room) {
8574
8818
  msg: "room::sync::full",
8575
8819
  roomId: room.id,
8576
8820
  status: room.getStatus(),
8577
- 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)),
8578
8822
  me,
8579
8823
  others
8580
8824
  });
@@ -9253,15 +9497,15 @@ function installBackgroundTabSpy() {
9253
9497
  const doc = typeof document !== "undefined" ? document : void 0;
9254
9498
  const inBackgroundSince = { current: null };
9255
9499
  function onVisibilityChange() {
9256
- if (_optionalChain([doc, 'optionalAccess', _208 => _208.visibilityState]) === "hidden") {
9500
+ if (_optionalChain([doc, 'optionalAccess', _219 => _219.visibilityState]) === "hidden") {
9257
9501
  inBackgroundSince.current = _nullishCoalesce(inBackgroundSince.current, () => ( Date.now()));
9258
9502
  } else {
9259
9503
  inBackgroundSince.current = null;
9260
9504
  }
9261
9505
  }
9262
- _optionalChain([doc, 'optionalAccess', _209 => _209.addEventListener, 'call', _210 => _210("visibilitychange", onVisibilityChange)]);
9506
+ _optionalChain([doc, 'optionalAccess', _220 => _220.addEventListener, 'call', _221 => _221("visibilitychange", onVisibilityChange)]);
9263
9507
  const unsub = () => {
9264
- _optionalChain([doc, 'optionalAccess', _211 => _211.removeEventListener, 'call', _212 => _212("visibilitychange", onVisibilityChange)]);
9508
+ _optionalChain([doc, 'optionalAccess', _222 => _222.removeEventListener, 'call', _223 => _223("visibilitychange", onVisibilityChange)]);
9265
9509
  };
9266
9510
  return [inBackgroundSince, unsub];
9267
9511
  }
@@ -9306,6 +9550,7 @@ function createRoom(options, config) {
9306
9550
  delegates,
9307
9551
  config.enableDebugLogging
9308
9552
  );
9553
+ const unacknowledgedOps = new UnacknowledgedOps();
9309
9554
  const context = {
9310
9555
  buffer: {
9311
9556
  flushTimerID: void 0,
@@ -9333,14 +9578,15 @@ function createRoom(options, config) {
9333
9578
  pool: createManagedPool(roomId, {
9334
9579
  getCurrentConnectionId,
9335
9580
  onDispatch,
9336
- isStorageWritable
9581
+ isStorageWritable,
9582
+ unacknowledgedOps
9337
9583
  }),
9338
9584
  root: void 0,
9339
9585
  undoStack: [],
9340
9586
  redoStack: [],
9341
9587
  pausedHistory: null,
9342
9588
  activeBatch: null,
9343
- unacknowledgedOps: /* @__PURE__ */ new Map()
9589
+ unacknowledgedOps
9344
9590
  };
9345
9591
  const nodeMapBuffer = makeNodeMapBuffer();
9346
9592
  const stopwatch = config.enableDebugLogging ? makeStopWatch() : void 0;
@@ -9452,7 +9698,7 @@ function createRoom(options, config) {
9452
9698
  }
9453
9699
  }
9454
9700
  function isStorageWritable() {
9455
- 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]);
9456
9702
  return scopes !== void 0 ? canWriteStorage(scopes) : true;
9457
9703
  }
9458
9704
  const eventHub = {
@@ -9548,7 +9794,11 @@ function createRoom(options, config) {
9548
9794
  currentItems.set(id, crdt._serialize());
9549
9795
  }
9550
9796
  const ops = getTreesDiffOperations(currentItems, nodes);
9551
- const result = applyRemoteOps(ops);
9797
+ const result = applyRemoteOps(
9798
+ ops,
9799
+ /* fromSnapshot */
9800
+ true
9801
+ );
9552
9802
  notify(result.updates);
9553
9803
  } else {
9554
9804
  context.root = LiveObject._fromItems(
@@ -9556,7 +9806,7 @@ function createRoom(options, config) {
9556
9806
  context.pool
9557
9807
  );
9558
9808
  }
9559
- 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));
9560
9810
  const root = context.root;
9561
9811
  disableHistory(() => {
9562
9812
  for (const key in context.initialStorage) {
@@ -9630,15 +9880,16 @@ function createRoom(options, config) {
9630
9880
  );
9631
9881
  return { opsToEmit: opsWithOpIds, reverse, updates };
9632
9882
  }
9633
- function applyRemoteOps(ops) {
9883
+ function applyRemoteOps(ops, fromSnapshot = false) {
9634
9884
  return applyOps(
9635
9885
  [],
9636
9886
  ops,
9637
9887
  /* isLocal */
9638
- false
9888
+ false,
9889
+ fromSnapshot
9639
9890
  );
9640
9891
  }
9641
- function applyOps(pframes, ops, isLocal) {
9892
+ function applyOps(pframes, ops, isLocal, fromSnapshot = false) {
9642
9893
  const output = {
9643
9894
  reverse: new Deque(),
9644
9895
  storageUpdates: /* @__PURE__ */ new Map(),
@@ -9674,7 +9925,7 @@ function createRoom(options, config) {
9674
9925
  } else {
9675
9926
  source = 1 /* THEIRS */;
9676
9927
  }
9677
- const applyOpResult = applyOp(op, source);
9928
+ const applyOpResult = applyOp(op, source, fromSnapshot);
9678
9929
  if (applyOpResult.modified) {
9679
9930
  const nodeId = applyOpResult.modified.node._id;
9680
9931
  if (!(nodeId && createdNodeIds.has(nodeId))) {
@@ -9700,7 +9951,7 @@ function createRoom(options, config) {
9700
9951
  }
9701
9952
  };
9702
9953
  }
9703
- function applyOp(op, source) {
9954
+ function applyOp(op, source, fromSnapshot = false) {
9704
9955
  if (isIgnoredOp(op)) {
9705
9956
  return { modified: false };
9706
9957
  }
@@ -9739,7 +9990,7 @@ function createRoom(options, config) {
9739
9990
  if (parentNode === void 0) {
9740
9991
  return { modified: false };
9741
9992
  }
9742
- return parentNode._attachChild(op, source);
9993
+ return parentNode._attachChild(op, source, fromSnapshot);
9743
9994
  }
9744
9995
  }
9745
9996
  }
@@ -9761,7 +10012,7 @@ function createRoom(options, config) {
9761
10012
  }
9762
10013
  context.myPresence.patch(patch);
9763
10014
  if (context.activeBatch) {
9764
- if (_optionalChain([options2, 'optionalAccess', _220 => _220.addToHistory])) {
10015
+ if (_optionalChain([options2, 'optionalAccess', _231 => _231.addToHistory])) {
9765
10016
  context.activeBatch.reverseOps.pushLeft({
9766
10017
  type: "presence",
9767
10018
  data: oldValues
@@ -9770,7 +10021,7 @@ function createRoom(options, config) {
9770
10021
  context.activeBatch.updates.presence = true;
9771
10022
  } else {
9772
10023
  flushNowOrSoon();
9773
- if (_optionalChain([options2, 'optionalAccess', _221 => _221.addToHistory])) {
10024
+ if (_optionalChain([options2, 'optionalAccess', _232 => _232.addToHistory])) {
9774
10025
  addToUndoStack([{ type: "presence", data: oldValues }]);
9775
10026
  }
9776
10027
  notify({ presence: true });
@@ -9879,12 +10130,11 @@ function createRoom(options, config) {
9879
10130
  }
9880
10131
  }
9881
10132
  function applyAndSendOfflineOps(unackedOps) {
9882
- if (unackedOps.size === 0) {
10133
+ if (unackedOps.length === 0) {
9883
10134
  return;
9884
10135
  }
9885
10136
  const messages = [];
9886
- const inOps = Array.from(unackedOps.values());
9887
- const result = applyLocalOps(inOps);
10137
+ const result = applyLocalOps(unackedOps);
9888
10138
  messages.push({
9889
10139
  type: ClientMsgCode.UPDATE_STORAGE,
9890
10140
  ops: result.opsToEmit
@@ -9948,11 +10198,11 @@ function createRoom(options, config) {
9948
10198
  break;
9949
10199
  }
9950
10200
  case ServerMsgCode.STORAGE_CHUNK:
9951
- _optionalChain([stopwatch, 'optionalAccess', _222 => _222.lap, 'call', _223 => _223()]);
10201
+ _optionalChain([stopwatch, 'optionalAccess', _233 => _233.lap, 'call', _234 => _234()]);
9952
10202
  nodeMapBuffer.append(compactNodesToNodeStream(message.nodes));
9953
10203
  break;
9954
10204
  case ServerMsgCode.STORAGE_STREAM_END: {
9955
- const timing = _optionalChain([stopwatch, 'optionalAccess', _224 => _224.stop, 'call', _225 => _225()]);
10205
+ const timing = _optionalChain([stopwatch, 'optionalAccess', _235 => _235.stop, 'call', _236 => _236()]);
9956
10206
  if (timing) {
9957
10207
  const ms = (v) => `${v.toFixed(1)}ms`;
9958
10208
  const rest = timing.laps.slice(1);
@@ -10087,11 +10337,11 @@ function createRoom(options, config) {
10087
10337
  } else if (pendingFeedsRequests.has(requestId)) {
10088
10338
  const pending = pendingFeedsRequests.get(requestId);
10089
10339
  pendingFeedsRequests.delete(requestId);
10090
- _optionalChain([pending, 'optionalAccess', _226 => _226.reject, 'call', _227 => _227(err)]);
10340
+ _optionalChain([pending, 'optionalAccess', _237 => _237.reject, 'call', _238 => _238(err)]);
10091
10341
  } else if (pendingFeedMessagesRequests.has(requestId)) {
10092
10342
  const pending = pendingFeedMessagesRequests.get(requestId);
10093
10343
  pendingFeedMessagesRequests.delete(requestId);
10094
- _optionalChain([pending, 'optionalAccess', _228 => _228.reject, 'call', _229 => _229(err)]);
10344
+ _optionalChain([pending, 'optionalAccess', _239 => _239.reject, 'call', _240 => _240(err)]);
10095
10345
  }
10096
10346
  eventHub.feeds.notify(message);
10097
10347
  break;
@@ -10108,7 +10358,7 @@ function createRoom(options, config) {
10108
10358
  const storageOps = context.buffer.storageOperations;
10109
10359
  if (storageOps.length > 0) {
10110
10360
  for (const op of storageOps) {
10111
- context.unacknowledgedOps.set(op.opId, op);
10361
+ context.unacknowledgedOps.add(op);
10112
10362
  }
10113
10363
  notifyStorageStatus();
10114
10364
  }
@@ -10245,10 +10495,10 @@ function createRoom(options, config) {
10245
10495
  timeoutId,
10246
10496
  kind,
10247
10497
  feedId,
10248
- messageId: _optionalChain([options2, 'optionalAccess', _230 => _230.messageId]),
10249
- expectedClientMessageId: _optionalChain([options2, 'optionalAccess', _231 => _231.expectedClientMessageId])
10498
+ messageId: _optionalChain([options2, 'optionalAccess', _241 => _241.messageId]),
10499
+ expectedClientMessageId: _optionalChain([options2, 'optionalAccess', _242 => _242.expectedClientMessageId])
10250
10500
  });
10251
- 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) {
10252
10502
  const q = _nullishCoalesce(pendingAddMessageFifoByFeed.get(feedId), () => ( []));
10253
10503
  q.push(requestId);
10254
10504
  pendingAddMessageFifoByFeed.set(feedId, q);
@@ -10299,10 +10549,10 @@ function createRoom(options, config) {
10299
10549
  }
10300
10550
  if (!matched) {
10301
10551
  const q = pendingAddMessageFifoByFeed.get(message.feedId);
10302
- const headId = _optionalChain([q, 'optionalAccess', _233 => _233[0]]);
10552
+ const headId = _optionalChain([q, 'optionalAccess', _244 => _244[0]]);
10303
10553
  if (headId !== void 0) {
10304
10554
  const pending = pendingFeedMutations.get(headId);
10305
- 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) {
10306
10556
  settleFeedMutation(headId, "ok");
10307
10557
  }
10308
10558
  }
@@ -10335,10 +10585,10 @@ function createRoom(options, config) {
10335
10585
  }
10336
10586
  }
10337
10587
  function processInitialStorage(nodes) {
10338
- const unacknowledgedOps = new Map(context.unacknowledgedOps);
10588
+ const unacknowledgedOps2 = [...context.unacknowledgedOps.values()];
10339
10589
  createOrUpdateRootFromMessage(nodes);
10340
- applyAndSendOfflineOps(unacknowledgedOps);
10341
- _optionalChain([_resolveStoragePromise, 'optionalCall', _235 => _235()]);
10590
+ applyAndSendOfflineOps(unacknowledgedOps2);
10591
+ _optionalChain([_resolveStoragePromise, 'optionalCall', _246 => _246()]);
10342
10592
  notifyStorageStatus();
10343
10593
  eventHub.storageDidLoad.notify();
10344
10594
  }
@@ -10356,7 +10606,7 @@ function createRoom(options, config) {
10356
10606
  } else if (!messages.some((msg) => msg.type === ClientMsgCode.FETCH_STORAGE)) {
10357
10607
  messages.push({ type: ClientMsgCode.FETCH_STORAGE });
10358
10608
  nodeMapBuffer.take();
10359
- _optionalChain([stopwatch, 'optionalAccess', _236 => _236.start, 'call', _237 => _237()]);
10609
+ _optionalChain([stopwatch, 'optionalAccess', _247 => _247.start, 'call', _248 => _248()]);
10360
10610
  }
10361
10611
  if (options2.flush) {
10362
10612
  flushNowOrSoon();
@@ -10412,10 +10662,10 @@ function createRoom(options, config) {
10412
10662
  const message = {
10413
10663
  type: ClientMsgCode.FETCH_FEEDS,
10414
10664
  requestId,
10415
- cursor: _optionalChain([options2, 'optionalAccess', _238 => _238.cursor]),
10416
- since: _optionalChain([options2, 'optionalAccess', _239 => _239.since]),
10417
- limit: _optionalChain([options2, 'optionalAccess', _240 => _240.limit]),
10418
- 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])
10419
10669
  };
10420
10670
  context.buffer.messages.push(message);
10421
10671
  flushNowOrSoon();
@@ -10435,9 +10685,9 @@ function createRoom(options, config) {
10435
10685
  type: ClientMsgCode.FETCH_FEED_MESSAGES,
10436
10686
  requestId,
10437
10687
  feedId,
10438
- cursor: _optionalChain([options2, 'optionalAccess', _242 => _242.cursor]),
10439
- since: _optionalChain([options2, 'optionalAccess', _243 => _243.since]),
10440
- 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])
10441
10691
  };
10442
10692
  context.buffer.messages.push(message);
10443
10693
  flushNowOrSoon();
@@ -10456,8 +10706,8 @@ function createRoom(options, config) {
10456
10706
  type: ClientMsgCode.ADD_FEED,
10457
10707
  requestId,
10458
10708
  feedId,
10459
- metadata: _optionalChain([options2, 'optionalAccess', _245 => _245.metadata]),
10460
- createdAt: _optionalChain([options2, 'optionalAccess', _246 => _246.createdAt])
10709
+ metadata: _optionalChain([options2, 'optionalAccess', _256 => _256.metadata]),
10710
+ createdAt: _optionalChain([options2, 'optionalAccess', _257 => _257.createdAt])
10461
10711
  };
10462
10712
  context.buffer.messages.push(message);
10463
10713
  flushNowOrSoon();
@@ -10491,15 +10741,15 @@ function createRoom(options, config) {
10491
10741
  function addFeedMessage(feedId, data, options2) {
10492
10742
  const requestId = nanoid();
10493
10743
  const promise = registerFeedMutation(requestId, "add-message", feedId, {
10494
- expectedClientMessageId: _optionalChain([options2, 'optionalAccess', _247 => _247.id])
10744
+ expectedClientMessageId: _optionalChain([options2, 'optionalAccess', _258 => _258.id])
10495
10745
  });
10496
10746
  const message = {
10497
10747
  type: ClientMsgCode.ADD_FEED_MESSAGE,
10498
10748
  requestId,
10499
10749
  feedId,
10500
10750
  data,
10501
- id: _optionalChain([options2, 'optionalAccess', _248 => _248.id]),
10502
- createdAt: _optionalChain([options2, 'optionalAccess', _249 => _249.createdAt])
10751
+ id: _optionalChain([options2, 'optionalAccess', _259 => _259.id]),
10752
+ createdAt: _optionalChain([options2, 'optionalAccess', _260 => _260.createdAt])
10503
10753
  };
10504
10754
  context.buffer.messages.push(message);
10505
10755
  flushNowOrSoon();
@@ -10516,7 +10766,7 @@ function createRoom(options, config) {
10516
10766
  feedId,
10517
10767
  messageId,
10518
10768
  data,
10519
- updatedAt: _optionalChain([options2, 'optionalAccess', _250 => _250.updatedAt])
10769
+ updatedAt: _optionalChain([options2, 'optionalAccess', _261 => _261.updatedAt])
10520
10770
  };
10521
10771
  context.buffer.messages.push(message);
10522
10772
  flushNowOrSoon();
@@ -10723,8 +10973,8 @@ function createRoom(options, config) {
10723
10973
  async function getThreads(options2) {
10724
10974
  return httpClient.getThreads({
10725
10975
  roomId,
10726
- query: _optionalChain([options2, 'optionalAccess', _251 => _251.query]),
10727
- cursor: _optionalChain([options2, 'optionalAccess', _252 => _252.cursor])
10976
+ query: _optionalChain([options2, 'optionalAccess', _262 => _262.query]),
10977
+ cursor: _optionalChain([options2, 'optionalAccess', _263 => _263.cursor])
10728
10978
  });
10729
10979
  }
10730
10980
  async function getThread(threadId) {
@@ -10846,7 +11096,7 @@ function createRoom(options, config) {
10846
11096
  function getSubscriptionSettings(options2) {
10847
11097
  return httpClient.getSubscriptionSettings({
10848
11098
  roomId,
10849
- signal: _optionalChain([options2, 'optionalAccess', _253 => _253.signal])
11099
+ signal: _optionalChain([options2, 'optionalAccess', _264 => _264.signal])
10850
11100
  });
10851
11101
  }
10852
11102
  function updateSubscriptionSettings(settings) {
@@ -10868,7 +11118,7 @@ function createRoom(options, config) {
10868
11118
  {
10869
11119
  [kInternal]: {
10870
11120
  get presenceBuffer() {
10871
- 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)));
10872
11122
  },
10873
11123
  // prettier-ignore
10874
11124
  get undoStack() {
@@ -10883,9 +11133,9 @@ function createRoom(options, config) {
10883
11133
  return context.yjsProvider;
10884
11134
  },
10885
11135
  setYjsProvider(newProvider) {
10886
- _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)]);
10887
11137
  context.yjsProvider = newProvider;
10888
- _optionalChain([newProvider, 'optionalAccess', _260 => _260.on, 'call', _261 => _261("status", yjsStatusDidChange)]);
11138
+ _optionalChain([newProvider, 'optionalAccess', _271 => _271.on, 'call', _272 => _272("status", yjsStatusDidChange)]);
10889
11139
  context.yjsProviderDidChange.notify();
10890
11140
  },
10891
11141
  yjsProviderDidChange: context.yjsProviderDidChange.observable,
@@ -10926,6 +11176,11 @@ function createRoom(options, config) {
10926
11176
  connect: () => managedSocket.connect(),
10927
11177
  reconnect: () => managedSocket.reconnect(),
10928
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
+ },
10929
11184
  destroy: () => {
10930
11185
  pendingFeedsRequests.forEach(
10931
11186
  (request) => request.reject(new Error("Room destroyed"))
@@ -10938,7 +11193,7 @@ function createRoom(options, config) {
10938
11193
  source.dispose();
10939
11194
  }
10940
11195
  eventHub.roomWillDestroy.notify();
10941
- _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)]);
10942
11197
  syncSourceForStorage.destroy();
10943
11198
  syncSourceForYjs.destroy();
10944
11199
  uninstallBgTabSpy();
@@ -11098,7 +11353,7 @@ function makeClassicSubscribeFn(roomId, events, errorEvents) {
11098
11353
  }
11099
11354
  if (isLiveNode(first)) {
11100
11355
  const node = first;
11101
- if (_optionalChain([options, 'optionalAccess', _265 => _265.isDeep])) {
11356
+ if (_optionalChain([options, 'optionalAccess', _276 => _276.isDeep])) {
11102
11357
  const storageCallback = second;
11103
11358
  return subscribeToLiveStructureDeeply(node, storageCallback);
11104
11359
  } else {
@@ -11184,8 +11439,8 @@ function createClient(options) {
11184
11439
  const authManager = createAuthManager(options, (token) => {
11185
11440
  currentUserId.set(() => token.uid);
11186
11441
  });
11187
- const fetchPolyfill = _optionalChain([clientOptions, 'access', _266 => _266.polyfills, 'optionalAccess', _267 => _267.fetch]) || /* istanbul ignore next */
11188
- _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)]);
11189
11444
  const httpClient = createApiClient({
11190
11445
  baseUrl,
11191
11446
  fetchPolyfill,
@@ -11203,7 +11458,7 @@ function createClient(options) {
11203
11458
  delegates: {
11204
11459
  createSocket: makeCreateSocketDelegateForAi(
11205
11460
  baseUrl,
11206
- _optionalChain([clientOptions, 'access', _271 => _271.polyfills, 'optionalAccess', _272 => _272.WebSocket])
11461
+ _optionalChain([clientOptions, 'access', _282 => _282.polyfills, 'optionalAccess', _283 => _283.WebSocket])
11207
11462
  ),
11208
11463
  authenticate: async () => {
11209
11464
  const resp = await authManager.getAuthValue({
@@ -11273,7 +11528,7 @@ function createClient(options) {
11273
11528
  createSocket: makeCreateSocketDelegateForRoom(
11274
11529
  roomId,
11275
11530
  baseUrl,
11276
- _optionalChain([clientOptions, 'access', _273 => _273.polyfills, 'optionalAccess', _274 => _274.WebSocket])
11531
+ _optionalChain([clientOptions, 'access', _284 => _284.polyfills, 'optionalAccess', _285 => _285.WebSocket])
11277
11532
  ),
11278
11533
  authenticate: makeAuthDelegateForRoom(roomId, authManager)
11279
11534
  })),
@@ -11296,7 +11551,7 @@ function createClient(options) {
11296
11551
  const shouldConnect = _nullishCoalesce(options2.autoConnect, () => ( true));
11297
11552
  if (shouldConnect) {
11298
11553
  if (typeof atob === "undefined") {
11299
- 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) {
11300
11555
  throw new Error(
11301
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"
11302
11557
  );
@@ -11308,7 +11563,7 @@ function createClient(options) {
11308
11563
  return leaseRoom(newRoomDetails);
11309
11564
  }
11310
11565
  function getRoom(roomId) {
11311
- 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]);
11312
11567
  return room ? room : null;
11313
11568
  }
11314
11569
  function logout() {
@@ -11324,7 +11579,7 @@ function createClient(options) {
11324
11579
  const batchedResolveUsers = new Batch(
11325
11580
  async (batchedUserIds) => {
11326
11581
  const userIds = batchedUserIds.flat();
11327
- const users = await _optionalChain([resolveUsers, 'optionalCall', _280 => _280({ userIds })]);
11582
+ const users = await _optionalChain([resolveUsers, 'optionalCall', _291 => _291({ userIds })]);
11328
11583
  warnOnceIf(
11329
11584
  !resolveUsers,
11330
11585
  "Set the resolveUsers option in createClient to specify user info."
@@ -11341,7 +11596,7 @@ function createClient(options) {
11341
11596
  const batchedResolveRoomsInfo = new Batch(
11342
11597
  async (batchedRoomIds) => {
11343
11598
  const roomIds = batchedRoomIds.flat();
11344
- const roomsInfo = await _optionalChain([resolveRoomsInfo, 'optionalCall', _281 => _281({ roomIds })]);
11599
+ const roomsInfo = await _optionalChain([resolveRoomsInfo, 'optionalCall', _292 => _292({ roomIds })]);
11345
11600
  warnOnceIf(
11346
11601
  !resolveRoomsInfo,
11347
11602
  "Set the resolveRoomsInfo option in createClient to specify room info."
@@ -11358,7 +11613,7 @@ function createClient(options) {
11358
11613
  const batchedResolveGroupsInfo = new Batch(
11359
11614
  async (batchedGroupIds) => {
11360
11615
  const groupIds = batchedGroupIds.flat();
11361
- const groupsInfo = await _optionalChain([resolveGroupsInfo, 'optionalCall', _282 => _282({ groupIds })]);
11616
+ const groupsInfo = await _optionalChain([resolveGroupsInfo, 'optionalCall', _293 => _293({ groupIds })]);
11362
11617
  warnOnceIf(
11363
11618
  !resolveGroupsInfo,
11364
11619
  "Set the resolveGroupsInfo option in createClient to specify group info."
@@ -11417,7 +11672,7 @@ function createClient(options) {
11417
11672
  }
11418
11673
  };
11419
11674
  const win = typeof window !== "undefined" ? window : void 0;
11420
- _optionalChain([win, 'optionalAccess', _283 => _283.addEventListener, 'call', _284 => _284("beforeunload", maybePreventClose)]);
11675
+ _optionalChain([win, 'optionalAccess', _294 => _294.addEventListener, 'call', _295 => _295("beforeunload", maybePreventClose)]);
11421
11676
  }
11422
11677
  async function getNotificationSettings(options2) {
11423
11678
  const plainSettings = await httpClient.getNotificationSettings(options2);
@@ -11433,6 +11688,7 @@ function createClient(options) {
11433
11688
  {
11434
11689
  enterRoom,
11435
11690
  getRoom,
11691
+ _dump: () => Array.from(roomsById.values(), ({ room }) => room._dump()).join("\n\n"),
11436
11692
  logout,
11437
11693
  // Public inbox notifications API
11438
11694
  getInboxNotifications: httpClient.getInboxNotifications,
@@ -11544,7 +11800,7 @@ var commentBodyElementsTypes = {
11544
11800
  mention: "inline"
11545
11801
  };
11546
11802
  function traverseCommentBody(body, elementOrVisitor, possiblyVisitor) {
11547
- if (!body || !_optionalChain([body, 'optionalAccess', _285 => _285.content])) {
11803
+ if (!body || !_optionalChain([body, 'optionalAccess', _296 => _296.content])) {
11548
11804
  return;
11549
11805
  }
11550
11806
  const element = typeof elementOrVisitor === "string" ? elementOrVisitor : void 0;
@@ -11554,13 +11810,13 @@ function traverseCommentBody(body, elementOrVisitor, possiblyVisitor) {
11554
11810
  for (const block of body.content) {
11555
11811
  if (type === "all" || type === "block") {
11556
11812
  if (guard(block)) {
11557
- _optionalChain([visitor, 'optionalCall', _286 => _286(block)]);
11813
+ _optionalChain([visitor, 'optionalCall', _297 => _297(block)]);
11558
11814
  }
11559
11815
  }
11560
11816
  if (type === "all" || type === "inline") {
11561
11817
  for (const inline of block.children) {
11562
11818
  if (guard(inline)) {
11563
- _optionalChain([visitor, 'optionalCall', _287 => _287(inline)]);
11819
+ _optionalChain([visitor, 'optionalCall', _298 => _298(inline)]);
11564
11820
  }
11565
11821
  }
11566
11822
  }
@@ -11730,7 +11986,7 @@ var stringifyCommentBodyPlainElements = {
11730
11986
  text: ({ element }) => element.text,
11731
11987
  link: ({ element }) => _nullishCoalesce(element.text, () => ( element.url)),
11732
11988
  mention: ({ element, user, group }) => {
11733
- 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))}`;
11734
11990
  }
11735
11991
  };
11736
11992
  var stringifyCommentBodyHtmlElements = {
@@ -11760,7 +12016,7 @@ var stringifyCommentBodyHtmlElements = {
11760
12016
  return html`<a href="${href}" target="_blank" rel="noopener noreferrer">${element.text ? html`${element.text}` : element.url}</a>`;
11761
12017
  },
11762
12018
  mention: ({ element, user, group }) => {
11763
- 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>`;
11764
12020
  }
11765
12021
  };
11766
12022
  var stringifyCommentBodyMarkdownElements = {
@@ -11790,20 +12046,20 @@ var stringifyCommentBodyMarkdownElements = {
11790
12046
  return markdown`[${_nullishCoalesce(element.text, () => ( element.url))}](${href})`;
11791
12047
  },
11792
12048
  mention: ({ element, user, group }) => {
11793
- 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))}`;
11794
12050
  }
11795
12051
  };
11796
12052
  async function stringifyCommentBody(body, options) {
11797
- const format = _nullishCoalesce(_optionalChain([options, 'optionalAccess', _296 => _296.format]), () => ( "plain"));
11798
- 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")));
11799
12055
  const elements = {
11800
12056
  ...format === "html" ? stringifyCommentBodyHtmlElements : format === "markdown" ? stringifyCommentBodyMarkdownElements : stringifyCommentBodyPlainElements,
11801
- ..._optionalChain([options, 'optionalAccess', _298 => _298.elements])
12057
+ ..._optionalChain([options, 'optionalAccess', _309 => _309.elements])
11802
12058
  };
11803
12059
  const { users: resolvedUsers, groups: resolvedGroupsInfo } = await resolveMentionsInCommentBody(
11804
12060
  body,
11805
- _optionalChain([options, 'optionalAccess', _299 => _299.resolveUsers]),
11806
- _optionalChain([options, 'optionalAccess', _300 => _300.resolveGroupsInfo])
12061
+ _optionalChain([options, 'optionalAccess', _310 => _310.resolveUsers]),
12062
+ _optionalChain([options, 'optionalAccess', _311 => _311.resolveGroupsInfo])
11807
12063
  );
11808
12064
  const blocks = body.content.flatMap((block, blockIndex) => {
11809
12065
  switch (block.type) {
@@ -11938,9 +12194,9 @@ function makePoller(callback, intervalMs, options) {
11938
12194
  const startTime = performance.now();
11939
12195
  const doc = typeof document !== "undefined" ? document : void 0;
11940
12196
  const win = typeof window !== "undefined" ? window : void 0;
11941
- 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));
11942
12198
  const context = {
11943
- inForeground: _optionalChain([doc, 'optionalAccess', _302 => _302.visibilityState]) !== "hidden",
12199
+ inForeground: _optionalChain([doc, 'optionalAccess', _313 => _313.visibilityState]) !== "hidden",
11944
12200
  lastSuccessfulPollAt: startTime,
11945
12201
  count: 0,
11946
12202
  backoff: 0
@@ -12021,11 +12277,11 @@ function makePoller(callback, intervalMs, options) {
12021
12277
  pollNowIfStale();
12022
12278
  }
12023
12279
  function onVisibilityChange() {
12024
- setInForeground(_optionalChain([doc, 'optionalAccess', _303 => _303.visibilityState]) !== "hidden");
12280
+ setInForeground(_optionalChain([doc, 'optionalAccess', _314 => _314.visibilityState]) !== "hidden");
12025
12281
  }
12026
- _optionalChain([doc, 'optionalAccess', _304 => _304.addEventListener, 'call', _305 => _305("visibilitychange", onVisibilityChange)]);
12027
- _optionalChain([win, 'optionalAccess', _306 => _306.addEventListener, 'call', _307 => _307("online", onVisibilityChange)]);
12028
- _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)]);
12029
12285
  fsm.start();
12030
12286
  return {
12031
12287
  inc,