@liveblocks/core 3.20.0-perm1 → 3.20.0-perm3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -6,7 +6,7 @@ var __export = (target, all) => {
6
6
 
7
7
  // src/version.ts
8
8
  var PKG_NAME = "@liveblocks/core";
9
- var PKG_VERSION = "3.20.0-perm1";
9
+ var PKG_VERSION = "3.20.0-perm3";
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)) {
@@ -5297,29 +5311,16 @@ function hasPermissionCapabilityAccess(capabilities, resource, requiredAccess) {
5297
5311
 
5298
5312
  // src/permissions.ts
5299
5313
  var VALID_PERMISSIONS = new Set(Object.values(Permission));
5300
- var DEFAULT_PERMISSIONS = [
5301
- Permission.RoomRead,
5302
- Permission.RoomWrite
5303
- ];
5304
5314
  var ROOM_PERMISSION_OBJECT_KEYS = /* @__PURE__ */ new Set([
5305
5315
  "default",
5306
5316
  ...ROOM_PERMISSION_RESOURCES
5307
5317
  ]);
5308
- var RESOURCE_SPECIFIC_PERMISSIONS_BY_RESOURCE = {
5309
- presence: Object.values(RESOURCE_PERMISSIONS.presence).flat(),
5310
- storage: Object.values(RESOURCE_PERMISSIONS.storage).flat(),
5311
- comments: Object.values(RESOURCE_PERMISSIONS.comments).flat(),
5312
- feeds: Object.values(RESOURCE_PERMISSIONS.feeds).flat()
5313
- };
5314
- var RESOURCE_SPECIFIC_PERMISSIONS = ROOM_PERMISSION_RESOURCES.flatMap(
5315
- (resource) => RESOURCE_SPECIFIC_PERMISSIONS_BY_RESOURCE[resource]
5316
- );
5317
- function permissionForAccessLevel(resource, access) {
5318
+ function permissionForAccessLevel(resource, access, field = resource) {
5318
5319
  const levels = RESOURCE_PERMISSIONS[resource];
5319
5320
  const permissions = levels[access];
5320
5321
  if (permissions === void 0 || permissions.length === 0) {
5321
5322
  throw new Error(
5322
- `Invalid permission level for ${resource}: ${String(access)}`
5323
+ `Invalid permission level for ${field}: ${_nullishCoalesce(JSON.stringify(access), () => ( String(access)))}`
5323
5324
  );
5324
5325
  }
5325
5326
  return permissions[0];
@@ -5379,7 +5380,11 @@ function normalizeRoomPermissionObject(objectInput) {
5379
5380
  const permissions = [];
5380
5381
  if (objectInput.default !== void 0) {
5381
5382
  permissions.push(
5382
- permissionForAccessLevel(DEFAULT_PERMISSION_RESOURCE, objectInput.default)
5383
+ permissionForAccessLevel(
5384
+ DEFAULT_PERMISSION_RESOURCE,
5385
+ objectInput.default,
5386
+ "default"
5387
+ )
5383
5388
  );
5384
5389
  }
5385
5390
  for (const resource of ROOM_PERMISSION_RESOURCES) {
@@ -5415,17 +5420,37 @@ function normalizeRoomAccessesUpdateInput(input) {
5415
5420
  ])
5416
5421
  );
5417
5422
  }
5418
- function getRoomPermissionConflicts(permission) {
5419
- if (DEFAULT_PERMISSIONS.includes(permission)) {
5420
- return [...DEFAULT_PERMISSIONS, ...RESOURCE_SPECIFIC_PERMISSIONS];
5423
+ function mergePermissionCapabilities(sources) {
5424
+ return {
5425
+ creation: strongestCapabilityAccess(sources, "creation"),
5426
+ presence: strongestCapabilityAccess(sources, "presence"),
5427
+ storage: strongestCapabilityAccess(sources, "storage"),
5428
+ comments: strongestCapabilityAccess(sources, "comments"),
5429
+ feeds: strongestCapabilityAccess(sources, "feeds"),
5430
+ personal: "write"
5431
+ };
5432
+ }
5433
+ function permissionCapabilitiesToScopes(capabilities) {
5434
+ const scopes = [];
5435
+ const baseAccess = capabilities.creation;
5436
+ if (baseAccess !== "none") {
5437
+ scopes.push(
5438
+ permissionForAccessLevel(DEFAULT_PERMISSION_RESOURCE, baseAccess)
5439
+ );
5421
5440
  }
5422
- for (const resource of ROOM_PERMISSION_RESOURCES) {
5423
- const permissions = RESOURCE_SPECIFIC_PERMISSIONS_BY_RESOURCE[resource];
5424
- if (permissions.includes(permission)) {
5425
- return permissions;
5441
+ for (const capability of ROOM_PERMISSION_RESOURCES) {
5442
+ const access = capabilities[capability];
5443
+ if (access !== baseAccess) {
5444
+ scopes.push(permissionForAccessLevel(capability, access));
5426
5445
  }
5427
5446
  }
5428
- return [];
5447
+ return scopes;
5448
+ }
5449
+ function strongestCapabilityAccess(sources, resource) {
5450
+ return sources.reduce(
5451
+ (strongest, source) => strongestAccess(strongest, source[resource]),
5452
+ "none"
5453
+ );
5429
5454
  }
5430
5455
  function strongestAccess(left, right) {
5431
5456
  return ACCESS_RANKS[right] > ACCESS_RANKS[left] ? right : left;
@@ -5723,6 +5748,9 @@ var OpCode = Object.freeze({
5723
5748
  function isIgnoredOp(op) {
5724
5749
  return op.type === OpCode.DELETE_CRDT && op.id === "ACK";
5725
5750
  }
5751
+ function isCreateOp(op) {
5752
+ return op.type === OpCode.CREATE_OBJECT || op.type === OpCode.CREATE_REGISTER || op.type === OpCode.CREATE_MAP || op.type === OpCode.CREATE_LIST;
5753
+ }
5726
5754
 
5727
5755
  // src/protocol/StorageNode.ts
5728
5756
  var CrdtType = Object.freeze({
@@ -5980,12 +6008,112 @@ function asPos(str) {
5980
6008
  return isPos(str) ? str : convertToPos(str);
5981
6009
  }
5982
6010
 
6011
+ // src/crdts/UnacknowledgedOps.ts
6012
+ var UnacknowledgedOps = class {
6013
+ // opId -> op
6014
+ #byOpId = /* @__PURE__ */ new Map();
6015
+ // position -> (opId -> Create op)
6016
+ #createOpsByPosition = /* @__PURE__ */ new Map();
6017
+ // parentId -> (opId -> Create op)
6018
+ #createOpsByParent = /* @__PURE__ */ new Map();
6019
+ // opIds of pending ops that were in flight when a connection died, so the
6020
+ // server may already have processed them. See isPossiblyStored().
6021
+ #possiblyStoredOpIds = /* @__PURE__ */ new Set();
6022
+ #posKey(parentId, parentKey) {
6023
+ return `${parentId}
6024
+ ${parentKey}`;
6025
+ }
6026
+ get size() {
6027
+ return this.#byOpId.size;
6028
+ }
6029
+ /**
6030
+ * Mark the given Op as still unacknowledged.
6031
+ */
6032
+ add(op) {
6033
+ this.#byOpId.set(op.opId, op);
6034
+ if (isCreateOp(op)) {
6035
+ const posKey = this.#posKey(op.parentId, op.parentKey);
6036
+ let atPosition = this.#createOpsByPosition.get(posKey);
6037
+ if (atPosition === void 0) {
6038
+ atPosition = /* @__PURE__ */ new Map();
6039
+ this.#createOpsByPosition.set(posKey, atPosition);
6040
+ }
6041
+ atPosition.set(op.opId, op);
6042
+ let inParent = this.#createOpsByParent.get(op.parentId);
6043
+ if (inParent === void 0) {
6044
+ inParent = /* @__PURE__ */ new Map();
6045
+ this.#createOpsByParent.set(op.parentId, inParent);
6046
+ }
6047
+ inParent.set(op.opId, op);
6048
+ }
6049
+ }
6050
+ /**
6051
+ * Drop the op with the given opId from the set, because the server has
6052
+ * acknowledged it (confirmed our own op, or signalled it was seen but
6053
+ * ignored).
6054
+ */
6055
+ delete(opId) {
6056
+ const op = this.#byOpId.get(opId);
6057
+ if (op === void 0) {
6058
+ return;
6059
+ }
6060
+ this.#byOpId.delete(opId);
6061
+ this.#possiblyStoredOpIds.delete(opId);
6062
+ if (isCreateOp(op)) {
6063
+ const posKey = this.#posKey(op.parentId, op.parentKey);
6064
+ const atPosition = this.#createOpsByPosition.get(posKey);
6065
+ _optionalChain([atPosition, 'optionalAccess', _111 => _111.delete, 'call', _112 => _112(opId)]);
6066
+ if (atPosition !== void 0 && atPosition.size === 0) {
6067
+ this.#createOpsByPosition.delete(posKey);
6068
+ }
6069
+ const inParent = this.#createOpsByParent.get(op.parentId);
6070
+ _optionalChain([inParent, 'optionalAccess', _113 => _113.delete, 'call', _114 => _114(opId)]);
6071
+ if (inParent !== void 0 && inParent.size === 0) {
6072
+ this.#createOpsByParent.delete(op.parentId);
6073
+ }
6074
+ }
6075
+ }
6076
+ /**
6077
+ * The still-unacknowledged Create ops with the given `parentId` and
6078
+ * `parentKey` (targeting one exact position), in dispatch order. O(1) lookup.
6079
+ * Empty if none.
6080
+ */
6081
+ getByParentIdAndKey(parentId, parentKey) {
6082
+ 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()]), () => ( []));
6083
+ }
6084
+ /**
6085
+ * The still-unacknowledged Create ops with the given `parentId` (across all
6086
+ * positions), in dispatch order. O(1) lookup. Empty if none.
6087
+ */
6088
+ getByParentId(parentId) {
6089
+ return _nullishCoalesce(_optionalChain([this, 'access', _120 => _120.#createOpsByParent, 'access', _121 => _121.get, 'call', _122 => _122(parentId), 'optionalAccess', _123 => _123.values, 'call', _124 => _124()]), () => ( []));
6090
+ }
6091
+ /** All still-unacknowledged ops, in dispatch order. */
6092
+ values() {
6093
+ return this.#byOpId.values();
6094
+ }
6095
+ isPossiblyStored(opId) {
6096
+ return this.#possiblyStoredOpIds.has(opId);
6097
+ }
6098
+ /**
6099
+ * Mark every currently pending op as possibly stored on the server. Called
6100
+ * when the connection dies: all of these ops were in flight, and their
6101
+ * (possibly lost) acks would have been the only way to know their fate.
6102
+ */
6103
+ markAllAsPossiblyStored() {
6104
+ for (const opId of this.#byOpId.keys()) {
6105
+ this.#possiblyStoredOpIds.add(opId);
6106
+ }
6107
+ }
6108
+ };
6109
+
5983
6110
  // src/crdts/AbstractCrdt.ts
5984
6111
  function createManagedPool(roomId, options) {
5985
6112
  const {
5986
6113
  getCurrentConnectionId,
5987
6114
  onDispatch,
5988
- isStorageWritable = () => true
6115
+ isStorageWritable = () => true,
6116
+ unacknowledgedOps = new UnacknowledgedOps()
5989
6117
  } = options;
5990
6118
  let clock = 0;
5991
6119
  let opClock = 0;
@@ -5999,7 +6127,7 @@ function createManagedPool(roomId, options) {
5999
6127
  generateId: () => `${getCurrentConnectionId()}:${clock++}`,
6000
6128
  generateOpId: () => `${getCurrentConnectionId()}:${opClock++}`,
6001
6129
  dispatch(ops, reverse, storageUpdates) {
6002
- _optionalChain([onDispatch, 'optionalCall', _111 => _111(ops, reverse, storageUpdates)]);
6130
+ _optionalChain([onDispatch, 'optionalCall', _125 => _125(ops, reverse, storageUpdates)]);
6003
6131
  },
6004
6132
  assertStorageIsWritable: () => {
6005
6133
  if (!isStorageWritable()) {
@@ -6007,7 +6135,8 @@ function createManagedPool(roomId, options) {
6007
6135
  "Cannot write to storage with a read only user, please ensure the user has write permissions"
6008
6136
  );
6009
6137
  }
6010
- }
6138
+ },
6139
+ unacknowledgedOps
6011
6140
  };
6012
6141
  }
6013
6142
  function crdtAsLiveNode(value) {
@@ -6289,11 +6418,9 @@ function childNodeLt(a, b) {
6289
6418
  var LiveList = class _LiveList extends AbstractCrdt {
6290
6419
  #items;
6291
6420
  #implicitlyDeletedItems;
6292
- #unacknowledgedSets;
6293
6421
  constructor(items) {
6294
6422
  super();
6295
6423
  this.#implicitlyDeletedItems = /* @__PURE__ */ new WeakSet();
6296
- this.#unacknowledgedSets = /* @__PURE__ */ new Map();
6297
6424
  const nodes = [];
6298
6425
  let lastPos;
6299
6426
  for (const item of items) {
@@ -6323,12 +6450,13 @@ var LiveList = class _LiveList extends AbstractCrdt {
6323
6450
  }
6324
6451
  /**
6325
6452
  * @internal
6326
- * This function assumes that the resulting ops will be sent to the server if they have an 'opId'
6327
- * so we mutate _unacknowledgedSets to avoid potential flickering
6328
- * https://github.com/liveblocks/liveblocks/pull/1177
6453
+ * Serializes this list (and its children) into Create ops. Each child's
6454
+ * create is tagged with the "set" intent (in the loop below) so that a list
6455
+ * created and immediately mutated doesn't transiently re-show its initial
6456
+ * items (flicker, https://github.com/liveblocks/liveblocks/pull/1177).
6329
6457
  *
6330
- * This is quite unintuitive and should disappear as soon as
6331
- * we introduce an explicit LiveList.Set operation
6458
+ * This is quite unintuitive and should disappear as soon as we introduce an
6459
+ * explicit LiveList.Set operation.
6332
6460
  */
6333
6461
  _toOps(parentId, parentKey) {
6334
6462
  if (this._id === void 0) {
@@ -6344,9 +6472,9 @@ var LiveList = class _LiveList extends AbstractCrdt {
6344
6472
  ops.push(op);
6345
6473
  for (const item of this.#items) {
6346
6474
  const parentKey2 = item._getParentKeyOrThrow();
6347
- const childOps = HACK_addIntentAndDeletedIdToOperation(
6475
+ const childOps = addIntentToRootOp(
6348
6476
  item._toOps(this._id, parentKey2),
6349
- void 0
6477
+ "set"
6350
6478
  );
6351
6479
  for (const childOp of childOps) {
6352
6480
  ops.push(childOp);
@@ -6388,6 +6516,28 @@ var LiveList = class _LiveList extends AbstractCrdt {
6388
6516
  (item) => item._getParentKeyOrThrow() === position
6389
6517
  );
6390
6518
  }
6519
+ /**
6520
+ * The opId of this list's still-unacknowledged "set" op at the given position,
6521
+ * or undefined if none. Derived from the room's unacknowledgedOps (the single
6522
+ * source of truth) rather than tracked in a per-instance map. The pool's
6523
+ * position index already scopes to this list's (parentId, position); the last
6524
+ * match wins, matching the original last-write-wins map semantics.
6525
+ */
6526
+ #unacknowledgedSetOpIdAt(position) {
6527
+ if (this._pool === void 0 || this._id === void 0) {
6528
+ return void 0;
6529
+ }
6530
+ let opId;
6531
+ for (const op of this._pool.unacknowledgedOps.getByParentIdAndKey(
6532
+ this._id,
6533
+ position
6534
+ )) {
6535
+ if (op.intent === "set") {
6536
+ opId = op.opId;
6537
+ }
6538
+ }
6539
+ return opId;
6540
+ }
6391
6541
  /** @internal */
6392
6542
  _attach(id, pool) {
6393
6543
  super._attach(id, pool);
@@ -6468,13 +6618,9 @@ var LiveList = class _LiveList extends AbstractCrdt {
6468
6618
  if (deletedDelta) {
6469
6619
  delta.push(deletedDelta);
6470
6620
  }
6471
- const unacknowledgedOpId = this.#unacknowledgedSets.get(op.parentKey);
6472
- if (unacknowledgedOpId !== void 0) {
6473
- if (unacknowledgedOpId !== op.opId) {
6474
- return delta.length === 0 ? { modified: false } : { modified: makeUpdate(this, delta), reverse: [] };
6475
- } else {
6476
- this.#unacknowledgedSets.delete(op.parentKey);
6477
- }
6621
+ const unacknowledgedOpId = this.#unacknowledgedSetOpIdAt(op.parentKey);
6622
+ if (unacknowledgedOpId !== void 0 && unacknowledgedOpId !== op.opId) {
6623
+ return delta.length === 0 ? { modified: false } : { modified: makeUpdate(this, delta), reverse: [] };
6478
6624
  }
6479
6625
  const indexOfItemWithSamePosition = this._indexOfPosition(op.parentKey);
6480
6626
  const existingItem = this.#items.find((item) => item._id === op.id);
@@ -6565,11 +6711,92 @@ var LiveList = class _LiveList extends AbstractCrdt {
6565
6711
  this.#shiftItemPosition(existingItemIndex, key);
6566
6712
  }
6567
6713
  const { newItem, newIndex } = this.#createAttachItemAndSort(op, key);
6714
+ const bumpDeltas = this.#bumpUnackedPushesAbove(key);
6568
6715
  return {
6569
- modified: makeUpdate(this, [insertDelta(newIndex, newItem)]),
6716
+ modified: makeUpdate(this, [
6717
+ insertDelta(newIndex, newItem),
6718
+ ...bumpDeltas
6719
+ ]),
6570
6720
  reverse: []
6571
6721
  };
6572
6722
  }
6723
+ /**
6724
+ * This list's own still-unacknowledged pushed items (their `intent: "push"`
6725
+ * Create op is still pending in the room's unacknowledgedOps). Derived from
6726
+ * the single source of truth, so an item drops out the instant its op is
6727
+ * acked, with no per-instance membership to leak. Yielded in push order.
6728
+ *
6729
+ * Excludes ops that may already be stored on the server (they were in
6730
+ * flight when a connection died, so their fate is unknown): the bump
6731
+ * prediction assumes the server has not processed the op yet, which is only
6732
+ * guaranteed for ops sent on the current connection. For these excluded
6733
+ * ops, the server's (re-)ack states the authoritative position; predicting
6734
+ * locally could produce a wrong position that no ack would correct.
6735
+ *
6736
+ * Restricted to items currently in `#items`: a pushed node whose op is still
6737
+ * pending may have been pulled out of the list (e.g. implicitly deleted by a
6738
+ * remote set, or removed by an undo) while still living in the pool, and such
6739
+ * a node must not be repositioned.
6740
+ */
6741
+ *#unackedPushNodes() {
6742
+ if (this._pool === void 0 || this._id === void 0) {
6743
+ return;
6744
+ }
6745
+ for (const op of this._pool.unacknowledgedOps.getByParentId(this._id)) {
6746
+ if (op.intent !== "push") {
6747
+ continue;
6748
+ }
6749
+ if (this._pool.unacknowledgedOps.isPossiblyStored(op.opId)) {
6750
+ continue;
6751
+ }
6752
+ const node = this._pool.getNode(op.id);
6753
+ if (node !== void 0 && this.#items.includes(node)) {
6754
+ yield node;
6755
+ }
6756
+ }
6757
+ }
6758
+ /**
6759
+ * Optimistic no-flip for pushed items. When a remote op lands at or below my
6760
+ * still-unacked pushed items, those items must end up *after* it: FIFO plus
6761
+ * the room's serial processing guarantee the remote was processed first, so
6762
+ * my unacked pushes belong behind it. Re-chain the whole unacked-push block,
6763
+ * in push order, to sit after the highest confirmed sibling, so it keeps
6764
+ * rendering as a contiguous tail instead of getting interleaved. Local-only;
6765
+ * the real acks overwrite these keys with the (identical) server keys.
6766
+ */
6767
+ #bumpUnackedPushesAbove(remoteKey) {
6768
+ const pending = new Set(this.#unackedPushNodes());
6769
+ if (pending.size === 0) {
6770
+ return [];
6771
+ }
6772
+ let minPending;
6773
+ for (const node of pending) {
6774
+ const pos = node._parentPos;
6775
+ if (minPending === void 0 || pos < minPending) {
6776
+ minPending = pos;
6777
+ }
6778
+ }
6779
+ if (remoteKey < nn(minPending)) {
6780
+ return [];
6781
+ }
6782
+ let base;
6783
+ for (const item of this.#items) {
6784
+ if (!pending.has(item)) {
6785
+ base = item._parentPos;
6786
+ }
6787
+ }
6788
+ const deltas = [];
6789
+ for (const node of pending) {
6790
+ const previousIndex = this.#items.findIndex((item) => item === node);
6791
+ base = makePosition(base);
6792
+ this.#updateItemPosition(node, base);
6793
+ const index = this.#items.findIndex((item) => item === node);
6794
+ if (index !== previousIndex) {
6795
+ deltas.push(moveDelta(previousIndex, index, node));
6796
+ }
6797
+ }
6798
+ return deltas;
6799
+ }
6573
6800
  #applyInsertAck(op) {
6574
6801
  const existingItem = this.#items.find((item) => item._id === op.id);
6575
6802
  const key = asPos(op.parentKey);
@@ -6624,7 +6851,7 @@ var LiveList = class _LiveList extends AbstractCrdt {
6624
6851
  #applyInsertUndoRedo(op) {
6625
6852
  const { id, parentKey: key } = op;
6626
6853
  const child = creationOpToLiveNode(op);
6627
- if (_optionalChain([this, 'access', _112 => _112._pool, 'optionalAccess', _113 => _113.getNode, 'call', _114 => _114(id)]) !== void 0) {
6854
+ if (_optionalChain([this, 'access', _126 => _126._pool, 'optionalAccess', _127 => _127.getNode, 'call', _128 => _128(id)]) !== void 0) {
6628
6855
  return { modified: false };
6629
6856
  }
6630
6857
  child._attach(id, nn(this._pool));
@@ -6632,8 +6859,8 @@ var LiveList = class _LiveList extends AbstractCrdt {
6632
6859
  const existingItemIndex = this._indexOfPosition(key);
6633
6860
  let newKey = key;
6634
6861
  if (existingItemIndex !== -1) {
6635
- const before2 = _optionalChain([this, 'access', _115 => _115.#items, 'access', _116 => _116.at, 'call', _117 => _117(existingItemIndex), 'optionalAccess', _118 => _118._parentPos]);
6636
- const after2 = _optionalChain([this, 'access', _119 => _119.#items, 'access', _120 => _120.at, 'call', _121 => _121(existingItemIndex + 1), 'optionalAccess', _122 => _122._parentPos]);
6862
+ const before2 = _optionalChain([this, 'access', _129 => _129.#items, 'access', _130 => _130.at, 'call', _131 => _131(existingItemIndex), 'optionalAccess', _132 => _132._parentPos]);
6863
+ const after2 = _optionalChain([this, 'access', _133 => _133.#items, 'access', _134 => _134.at, 'call', _135 => _135(existingItemIndex + 1), 'optionalAccess', _136 => _136._parentPos]);
6637
6864
  newKey = makePosition(before2, after2);
6638
6865
  child._setParentLink(this, newKey);
6639
6866
  }
@@ -6647,10 +6874,9 @@ var LiveList = class _LiveList extends AbstractCrdt {
6647
6874
  #applySetUndoRedo(op) {
6648
6875
  const { id, parentKey: key } = op;
6649
6876
  const child = creationOpToLiveNode(op);
6650
- if (_optionalChain([this, 'access', _123 => _123._pool, 'optionalAccess', _124 => _124.getNode, 'call', _125 => _125(id)]) !== void 0) {
6877
+ if (_optionalChain([this, 'access', _137 => _137._pool, 'optionalAccess', _138 => _138.getNode, 'call', _139 => _139(id)]) !== void 0) {
6651
6878
  return { modified: false };
6652
6879
  }
6653
- this.#unacknowledgedSets.set(key, nn(op.opId));
6654
6880
  const indexOfItemWithSameKey = this._indexOfPosition(key);
6655
6881
  child._attach(id, nn(this._pool));
6656
6882
  child._setParentLink(this, key);
@@ -6660,8 +6886,9 @@ var LiveList = class _LiveList extends AbstractCrdt {
6660
6886
  existingItem._detach();
6661
6887
  this.#items.remove(existingItem);
6662
6888
  this.#items.add(child);
6663
- const reverse = HACK_addIntentAndDeletedIdToOperation(
6889
+ const reverse = addIntentToRootOp(
6664
6890
  existingItem._toOps(nn(this._id), key),
6891
+ "set",
6665
6892
  op.id
6666
6893
  );
6667
6894
  const delta = [setDelta(indexOfItemWithSameKey, child)];
@@ -6768,7 +6995,7 @@ var LiveList = class _LiveList extends AbstractCrdt {
6768
6995
  } else {
6769
6996
  this.#updateItemPositionAt(
6770
6997
  existingItemIndex,
6771
- makePosition(newKey, _optionalChain([this, 'access', _126 => _126.#items, 'access', _127 => _127.at, 'call', _128 => _128(existingItemIndex + 1), 'optionalAccess', _129 => _129._parentPos]))
6998
+ makePosition(newKey, _optionalChain([this, 'access', _140 => _140.#items, 'access', _141 => _141.at, 'call', _142 => _142(existingItemIndex + 1), 'optionalAccess', _143 => _143._parentPos]))
6772
6999
  );
6773
7000
  const previousIndex = this.#items.findIndex((item) => item === child);
6774
7001
  this.#updateItemPosition(child, newKey);
@@ -6795,7 +7022,7 @@ var LiveList = class _LiveList extends AbstractCrdt {
6795
7022
  this,
6796
7023
  makePosition(
6797
7024
  newKey,
6798
- _optionalChain([this, 'access', _130 => _130.#items, 'access', _131 => _131.at, 'call', _132 => _132(existingItemIndex + 1), 'optionalAccess', _133 => _133._parentPos])
7025
+ _optionalChain([this, 'access', _144 => _144.#items, 'access', _145 => _145.at, 'call', _146 => _146(existingItemIndex + 1), 'optionalAccess', _147 => _147._parentPos])
6799
7026
  )
6800
7027
  );
6801
7028
  this.#items.reposition(existingItem);
@@ -6819,7 +7046,7 @@ var LiveList = class _LiveList extends AbstractCrdt {
6819
7046
  existingItemIndex,
6820
7047
  makePosition(
6821
7048
  newKey,
6822
- _optionalChain([this, 'access', _134 => _134.#items, 'access', _135 => _135.at, 'call', _136 => _136(existingItemIndex + 1), 'optionalAccess', _137 => _137._parentPos])
7049
+ _optionalChain([this, 'access', _148 => _148.#items, 'access', _149 => _149.at, 'call', _150 => _150(existingItemIndex + 1), 'optionalAccess', _151 => _151._parentPos])
6823
7050
  )
6824
7051
  );
6825
7052
  }
@@ -6847,7 +7074,7 @@ var LiveList = class _LiveList extends AbstractCrdt {
6847
7074
  if (existingItemIndex !== -1) {
6848
7075
  actualNewKey = makePosition(
6849
7076
  newKey,
6850
- _optionalChain([this, 'access', _138 => _138.#items, 'access', _139 => _139.at, 'call', _140 => _140(existingItemIndex + 1), 'optionalAccess', _141 => _141._parentPos])
7077
+ _optionalChain([this, 'access', _152 => _152.#items, 'access', _153 => _153.at, 'call', _154 => _154(existingItemIndex + 1), 'optionalAccess', _155 => _155._parentPos])
6851
7078
  );
6852
7079
  }
6853
7080
  this.#updateItemPosition(child, actualNewKey);
@@ -6904,8 +7131,7 @@ var LiveList = class _LiveList extends AbstractCrdt {
6904
7131
  * @param element The element to add to the end of the LiveList.
6905
7132
  */
6906
7133
  push(element) {
6907
- _optionalChain([this, 'access', _142 => _142._pool, 'optionalAccess', _143 => _143.assertStorageIsWritable, 'call', _144 => _144()]);
6908
- return this.insert(element, this.length);
7134
+ return this.#injectAt(element, this.length, "push");
6909
7135
  }
6910
7136
  /**
6911
7137
  * Inserts one element at a specified index.
@@ -6913,14 +7139,23 @@ var LiveList = class _LiveList extends AbstractCrdt {
6913
7139
  * @param index The index at which you want to insert the element.
6914
7140
  */
6915
7141
  insert(element, index) {
6916
- _optionalChain([this, 'access', _145 => _145._pool, 'optionalAccess', _146 => _146.assertStorageIsWritable, 'call', _147 => _147()]);
7142
+ return this.#injectAt(element, index, "insert");
7143
+ }
7144
+ /**
7145
+ * Shared implementation of `insert` and `push`. A `"push"` intent leaves the
7146
+ * client-computed position untouched (so optimistic rendering is unchanged),
7147
+ * but tags the Op so the server appends it to the true end of the list
7148
+ * instead of resolving its position against the client's stale view.
7149
+ */
7150
+ #injectAt(element, index, intent) {
7151
+ _optionalChain([this, 'access', _156 => _156._pool, 'optionalAccess', _157 => _157.assertStorageIsWritable, 'call', _158 => _158()]);
6917
7152
  if (index < 0 || index > this.#items.length) {
6918
7153
  throw new Error(
6919
7154
  `Cannot insert list item at index "${index}". index should be between 0 and ${this.#items.length}`
6920
7155
  );
6921
7156
  }
6922
- const before2 = _optionalChain([this, 'access', _148 => _148.#items, 'access', _149 => _149.at, 'call', _150 => _150(index - 1), 'optionalAccess', _151 => _151._parentPos]);
6923
- const after2 = _optionalChain([this, 'access', _152 => _152.#items, 'access', _153 => _153.at, 'call', _154 => _154(index), 'optionalAccess', _155 => _155._parentPos]);
7157
+ const before2 = _optionalChain([this, 'access', _159 => _159.#items, 'access', _160 => _160.at, 'call', _161 => _161(index - 1), 'optionalAccess', _162 => _162._parentPos]);
7158
+ const after2 = _optionalChain([this, 'access', _163 => _163.#items, 'access', _164 => _164.at, 'call', _165 => _165(index), 'optionalAccess', _166 => _166._parentPos]);
6924
7159
  const position = makePosition(before2, after2);
6925
7160
  const value = lsonToLiveNode(element);
6926
7161
  value._setParentLink(this, position);
@@ -6928,8 +7163,9 @@ var LiveList = class _LiveList extends AbstractCrdt {
6928
7163
  if (this._pool && this._id) {
6929
7164
  const id = this._pool.generateId();
6930
7165
  value._attach(id, this._pool);
7166
+ const ops = value._toOpsWithOpId(this._id, position, this._pool);
6931
7167
  this._pool.dispatch(
6932
- value._toOpsWithOpId(this._id, position, this._pool),
7168
+ intent === "push" ? addIntentToRootOp(ops, "push") : ops,
6933
7169
  [{ type: OpCode.DELETE_CRDT, id }],
6934
7170
  /* @__PURE__ */ new Map([
6935
7171
  [this._id, makeUpdate(this, [insertDelta(index, value)])]
@@ -6943,7 +7179,7 @@ var LiveList = class _LiveList extends AbstractCrdt {
6943
7179
  * @param targetIndex The index where the element should be after moving.
6944
7180
  */
6945
7181
  move(index, targetIndex) {
6946
- _optionalChain([this, 'access', _156 => _156._pool, 'optionalAccess', _157 => _157.assertStorageIsWritable, 'call', _158 => _158()]);
7182
+ _optionalChain([this, 'access', _167 => _167._pool, 'optionalAccess', _168 => _168.assertStorageIsWritable, 'call', _169 => _169()]);
6947
7183
  if (targetIndex < 0) {
6948
7184
  throw new Error("targetIndex cannot be less than 0");
6949
7185
  }
@@ -6961,11 +7197,11 @@ var LiveList = class _LiveList extends AbstractCrdt {
6961
7197
  let beforePosition = null;
6962
7198
  let afterPosition = null;
6963
7199
  if (index < targetIndex) {
6964
- afterPosition = targetIndex === this.#items.length - 1 ? void 0 : _optionalChain([this, 'access', _159 => _159.#items, 'access', _160 => _160.at, 'call', _161 => _161(targetIndex + 1), 'optionalAccess', _162 => _162._parentPos]);
7200
+ afterPosition = targetIndex === this.#items.length - 1 ? void 0 : _optionalChain([this, 'access', _170 => _170.#items, 'access', _171 => _171.at, 'call', _172 => _172(targetIndex + 1), 'optionalAccess', _173 => _173._parentPos]);
6965
7201
  beforePosition = this.#items.at(targetIndex)._parentPos;
6966
7202
  } else {
6967
7203
  afterPosition = this.#items.at(targetIndex)._parentPos;
6968
- beforePosition = targetIndex === 0 ? void 0 : _optionalChain([this, 'access', _163 => _163.#items, 'access', _164 => _164.at, 'call', _165 => _165(targetIndex - 1), 'optionalAccess', _166 => _166._parentPos]);
7204
+ beforePosition = targetIndex === 0 ? void 0 : _optionalChain([this, 'access', _174 => _174.#items, 'access', _175 => _175.at, 'call', _176 => _176(targetIndex - 1), 'optionalAccess', _177 => _177._parentPos]);
6969
7205
  }
6970
7206
  const position = makePosition(beforePosition, afterPosition);
6971
7207
  const item = this.#items.at(index);
@@ -7000,7 +7236,7 @@ var LiveList = class _LiveList extends AbstractCrdt {
7000
7236
  * @param index The index of the element to delete
7001
7237
  */
7002
7238
  delete(index) {
7003
- _optionalChain([this, 'access', _167 => _167._pool, 'optionalAccess', _168 => _168.assertStorageIsWritable, 'call', _169 => _169()]);
7239
+ _optionalChain([this, 'access', _178 => _178._pool, 'optionalAccess', _179 => _179.assertStorageIsWritable, 'call', _180 => _180()]);
7004
7240
  if (index < 0 || index >= this.#items.length) {
7005
7241
  throw new Error(
7006
7242
  `Cannot delete list item at index "${index}". index should be between 0 and ${this.#items.length - 1}`
@@ -7033,7 +7269,7 @@ var LiveList = class _LiveList extends AbstractCrdt {
7033
7269
  }
7034
7270
  }
7035
7271
  clear() {
7036
- _optionalChain([this, 'access', _170 => _170._pool, 'optionalAccess', _171 => _171.assertStorageIsWritable, 'call', _172 => _172()]);
7272
+ _optionalChain([this, 'access', _181 => _181._pool, 'optionalAccess', _182 => _182.assertStorageIsWritable, 'call', _183 => _183()]);
7037
7273
  if (this._pool) {
7038
7274
  const ops = [];
7039
7275
  const reverseOps = [];
@@ -7067,7 +7303,7 @@ var LiveList = class _LiveList extends AbstractCrdt {
7067
7303
  }
7068
7304
  }
7069
7305
  set(index, item) {
7070
- _optionalChain([this, 'access', _173 => _173._pool, 'optionalAccess', _174 => _174.assertStorageIsWritable, 'call', _175 => _175()]);
7306
+ _optionalChain([this, 'access', _184 => _184._pool, 'optionalAccess', _185 => _185.assertStorageIsWritable, 'call', _186 => _186()]);
7071
7307
  if (index < 0 || index >= this.#items.length) {
7072
7308
  throw new Error(
7073
7309
  `Cannot set list item at index "${index}". index should be between 0 and ${this.#items.length - 1}`
@@ -7087,13 +7323,14 @@ var LiveList = class _LiveList extends AbstractCrdt {
7087
7323
  value._attach(id, this._pool);
7088
7324
  const storageUpdates = /* @__PURE__ */ new Map();
7089
7325
  storageUpdates.set(this._id, makeUpdate(this, [setDelta(index, value)]));
7090
- const ops = HACK_addIntentAndDeletedIdToOperation(
7326
+ const ops = addIntentToRootOp(
7091
7327
  value._toOpsWithOpId(this._id, position, this._pool),
7328
+ "set",
7092
7329
  existingId
7093
7330
  );
7094
- this.#unacknowledgedSets.set(position, nn(ops[0].opId));
7095
- const reverseOps = HACK_addIntentAndDeletedIdToOperation(
7331
+ const reverseOps = addIntentToRootOp(
7096
7332
  existingItem._toOps(this._id, position),
7333
+ "set",
7097
7334
  id
7098
7335
  );
7099
7336
  this._pool.dispatch(ops, reverseOps, storageUpdates);
@@ -7225,7 +7462,7 @@ var LiveList = class _LiveList extends AbstractCrdt {
7225
7462
  #shiftItemPosition(index, key) {
7226
7463
  const shiftedPosition = makePosition(
7227
7464
  key,
7228
- this.#items.length > index + 1 ? _optionalChain([this, 'access', _176 => _176.#items, 'access', _177 => _177.at, 'call', _178 => _178(index + 1), 'optionalAccess', _179 => _179._parentPos]) : void 0
7465
+ this.#items.length > index + 1 ? _optionalChain([this, 'access', _187 => _187.#items, 'access', _188 => _188.at, 'call', _189 => _189(index + 1), 'optionalAccess', _190 => _190._parentPos]) : void 0
7229
7466
  );
7230
7467
  this.#updateItemPositionAt(index, shiftedPosition);
7231
7468
  }
@@ -7294,15 +7531,11 @@ function moveDelta(previousIndex, index, item) {
7294
7531
  previousIndex
7295
7532
  };
7296
7533
  }
7297
- function HACK_addIntentAndDeletedIdToOperation(ops, deletedId) {
7534
+ function addIntentToRootOp(ops, intent, deletedId) {
7298
7535
  return ops.map((op, index) => {
7299
7536
  if (index === 0) {
7300
7537
  const firstOp = op;
7301
- return {
7302
- ...firstOp,
7303
- intent: "set",
7304
- deletedId
7305
- };
7538
+ return { ...firstOp, intent, deletedId };
7306
7539
  } else {
7307
7540
  return op;
7308
7541
  }
@@ -7478,7 +7711,7 @@ var LiveMap = class _LiveMap extends AbstractCrdt {
7478
7711
  * @param value The value of the element to add. Should be serializable to JSON.
7479
7712
  */
7480
7713
  set(key, value) {
7481
- _optionalChain([this, 'access', _180 => _180._pool, 'optionalAccess', _181 => _181.assertStorageIsWritable, 'call', _182 => _182()]);
7714
+ _optionalChain([this, 'access', _191 => _191._pool, 'optionalAccess', _192 => _192.assertStorageIsWritable, 'call', _193 => _193()]);
7482
7715
  const oldValue = this.#map.get(key);
7483
7716
  if (oldValue) {
7484
7717
  oldValue._detach();
@@ -7524,7 +7757,7 @@ var LiveMap = class _LiveMap extends AbstractCrdt {
7524
7757
  * @returns true if an element existed and has been removed, or false if the element does not exist.
7525
7758
  */
7526
7759
  delete(key) {
7527
- _optionalChain([this, 'access', _183 => _183._pool, 'optionalAccess', _184 => _184.assertStorageIsWritable, 'call', _185 => _185()]);
7760
+ _optionalChain([this, 'access', _194 => _194._pool, 'optionalAccess', _195 => _195.assertStorageIsWritable, 'call', _196 => _196()]);
7528
7761
  const item = this.#map.get(key);
7529
7762
  if (item === void 0) {
7530
7763
  return false;
@@ -7954,6 +8187,7 @@ var LiveObject = (_class2 = class _LiveObject extends AbstractCrdt {
7954
8187
  const id = nn(this._id);
7955
8188
  const parentKey = nn(child._parentKey);
7956
8189
  const reverse = child._toOps(id, parentKey);
8190
+ const deletedItem = liveNodeToLson(child);
7957
8191
  for (const [key, value] of this.#synced) {
7958
8192
  if (value === child) {
7959
8193
  this.#synced.delete(key);
@@ -7965,7 +8199,7 @@ var LiveObject = (_class2 = class _LiveObject extends AbstractCrdt {
7965
8199
  node: this,
7966
8200
  type: "LiveObject",
7967
8201
  updates: {
7968
- [parentKey]: { type: "delete" }
8202
+ [parentKey]: { type: "delete", deletedItem }
7969
8203
  }
7970
8204
  };
7971
8205
  return { modified: storageUpdate, reverse };
@@ -8135,20 +8369,20 @@ var LiveObject = (_class2 = class _LiveObject extends AbstractCrdt {
8135
8369
  * Caveat: this method will not add changes to the undo/redo stack.
8136
8370
  */
8137
8371
  setLocal(key, value) {
8138
- _optionalChain([this, 'access', _186 => _186._pool, 'optionalAccess', _187 => _187.assertStorageIsWritable, 'call', _188 => _188()]);
8372
+ _optionalChain([this, 'access', _197 => _197._pool, 'optionalAccess', _198 => _198.assertStorageIsWritable, 'call', _199 => _199()]);
8139
8373
  const deleteResult = this.#prepareDelete(key);
8140
8374
  this.#local.set(key, value);
8141
8375
  this.invalidate();
8142
8376
  if (this._pool !== void 0 && this._id !== void 0) {
8143
- const ops = _nullishCoalesce(_optionalChain([deleteResult, 'optionalAccess', _189 => _189[0]]), () => ( []));
8144
- const reverse = _nullishCoalesce(_optionalChain([deleteResult, 'optionalAccess', _190 => _190[1]]), () => ( []));
8145
- const storageUpdates = _nullishCoalesce(_optionalChain([deleteResult, 'optionalAccess', _191 => _191[2]]), () => ( /* @__PURE__ */ new Map()));
8377
+ const ops = _nullishCoalesce(_optionalChain([deleteResult, 'optionalAccess', _200 => _200[0]]), () => ( []));
8378
+ const reverse = _nullishCoalesce(_optionalChain([deleteResult, 'optionalAccess', _201 => _201[1]]), () => ( []));
8379
+ const storageUpdates = _nullishCoalesce(_optionalChain([deleteResult, 'optionalAccess', _202 => _202[2]]), () => ( /* @__PURE__ */ new Map()));
8146
8380
  const existing = storageUpdates.get(this._id);
8147
8381
  storageUpdates.set(this._id, {
8148
8382
  node: this,
8149
8383
  type: "LiveObject",
8150
8384
  updates: {
8151
- ..._optionalChain([existing, 'optionalAccess', _192 => _192.updates]),
8385
+ ..._optionalChain([existing, 'optionalAccess', _203 => _203.updates]),
8152
8386
  [key]: { type: "update" }
8153
8387
  }
8154
8388
  });
@@ -8168,7 +8402,7 @@ var LiveObject = (_class2 = class _LiveObject extends AbstractCrdt {
8168
8402
  * #synced or pool/id are unavailable. Does NOT dispatch.
8169
8403
  */
8170
8404
  #prepareDelete(key) {
8171
- _optionalChain([this, 'access', _193 => _193._pool, 'optionalAccess', _194 => _194.assertStorageIsWritable, 'call', _195 => _195()]);
8405
+ _optionalChain([this, 'access', _204 => _204._pool, 'optionalAccess', _205 => _205.assertStorageIsWritable, 'call', _206 => _206()]);
8172
8406
  const k = key;
8173
8407
  if (this.#local.has(k) && !this.#synced.has(k)) {
8174
8408
  const oldValue2 = this.#local.get(k);
@@ -8244,7 +8478,7 @@ var LiveObject = (_class2 = class _LiveObject extends AbstractCrdt {
8244
8478
  const result = this.#prepareDelete(key);
8245
8479
  if (result) {
8246
8480
  const [ops, reverse, storageUpdates] = result;
8247
- _optionalChain([this, 'access', _196 => _196._pool, 'optionalAccess', _197 => _197.dispatch, 'call', _198 => _198(ops, reverse, storageUpdates)]);
8481
+ _optionalChain([this, 'access', _207 => _207._pool, 'optionalAccess', _208 => _208.dispatch, 'call', _209 => _209(ops, reverse, storageUpdates)]);
8248
8482
  }
8249
8483
  }
8250
8484
  /**
@@ -8252,7 +8486,7 @@ var LiveObject = (_class2 = class _LiveObject extends AbstractCrdt {
8252
8486
  * @param patch The object used to overrides properties
8253
8487
  */
8254
8488
  update(patch) {
8255
- _optionalChain([this, 'access', _199 => _199._pool, 'optionalAccess', _200 => _200.assertStorageIsWritable, 'call', _201 => _201()]);
8489
+ _optionalChain([this, 'access', _210 => _210._pool, 'optionalAccess', _211 => _211.assertStorageIsWritable, 'call', _212 => _212()]);
8256
8490
  if (_LiveObject.detectLargeObjects) {
8257
8491
  const data = {};
8258
8492
  for (const [key, value] of this.#synced) {
@@ -8542,6 +8776,60 @@ function lsonToLiveNode(value) {
8542
8776
  return new LiveRegister(value);
8543
8777
  }
8544
8778
  }
8779
+ function dumpPool(pool) {
8780
+ const rows = Array.from(pool.nodes.values(), (node) => {
8781
+ const parent = node.parent;
8782
+ const parentId = parent.type === "HasParent" ? _nullishCoalesce(parent.node._id, () => ( "?")) : parent.type === "Orphaned" ? "<orphaned>" : "-";
8783
+ let value;
8784
+ if (node instanceof LiveRegister) {
8785
+ value = stringifyOrLog(node.data);
8786
+ } else if (node instanceof LiveList) {
8787
+ value = "<LiveList>";
8788
+ } else if (node instanceof LiveMap) {
8789
+ value = "<LiveMap>";
8790
+ } else {
8791
+ value = "<LiveObject>";
8792
+ }
8793
+ return { id: nn(node._id), parentId, key: _nullishCoalesce(node._parentKey, () => ( "")), value };
8794
+ });
8795
+ rows.sort((a, b) => {
8796
+ if (a.parentId !== b.parentId) return a.parentId < b.parentId ? -1 : 1;
8797
+ if (a.key !== b.key) return a.key < b.key ? -1 : 1;
8798
+ return 0;
8799
+ });
8800
+ return rows.map(
8801
+ (r) => ` ${r.id} parent=${r.parentId} key=${r.key || "\u2014"} ${r.value}`
8802
+ ).join("\n");
8803
+ }
8804
+ function isJsonEq(a, b) {
8805
+ if (a === b) {
8806
+ return true;
8807
+ }
8808
+ if (typeof a !== "object" || a === null || typeof b !== "object" || b === null) {
8809
+ return false;
8810
+ }
8811
+ if (Array.isArray(a) || Array.isArray(b)) {
8812
+ if (!Array.isArray(a) || !Array.isArray(b) || a.length !== b.length) {
8813
+ return false;
8814
+ }
8815
+ for (let i = 0; i < a.length; i++) {
8816
+ if (!isJsonEq(a[i], b[i])) {
8817
+ return false;
8818
+ }
8819
+ }
8820
+ return true;
8821
+ }
8822
+ const aKeys = Object.keys(a);
8823
+ if (aKeys.length !== Object.keys(b).length) {
8824
+ return false;
8825
+ }
8826
+ for (const key of aKeys) {
8827
+ if (!isJsonEq(a[key], b[key])) {
8828
+ return false;
8829
+ }
8830
+ }
8831
+ return true;
8832
+ }
8545
8833
  function getTreesDiffOperations(currentItems, newItems) {
8546
8834
  const ops = [];
8547
8835
  currentItems.forEach((_, id) => {
@@ -8553,12 +8841,28 @@ function getTreesDiffOperations(currentItems, newItems) {
8553
8841
  const currentCrdt = currentItems.get(id);
8554
8842
  if (currentCrdt) {
8555
8843
  if (crdt.type === CrdtType.OBJECT) {
8556
- if (currentCrdt.type !== CrdtType.OBJECT || stringifyOrLog(crdt.data) !== stringifyOrLog(currentCrdt.data)) {
8557
- ops.push({
8558
- type: OpCode.UPDATE_OBJECT,
8559
- id,
8560
- data: crdt.data
8561
- });
8844
+ if (currentCrdt.type !== CrdtType.OBJECT) {
8845
+ ops.push({ type: OpCode.UPDATE_OBJECT, id, data: crdt.data });
8846
+ } else {
8847
+ const changed = /* @__PURE__ */ new Map();
8848
+ for (const key of Object.keys(crdt.data)) {
8849
+ const value = crdt.data[key];
8850
+ if (value !== void 0 && !isJsonEq(value, currentCrdt.data[key])) {
8851
+ changed.set(key, value);
8852
+ }
8853
+ }
8854
+ if (changed.size > 0) {
8855
+ ops.push({
8856
+ type: OpCode.UPDATE_OBJECT,
8857
+ id,
8858
+ data: Object.fromEntries(changed)
8859
+ });
8860
+ }
8861
+ for (const key of Object.keys(currentCrdt.data)) {
8862
+ if (!(key in crdt.data)) {
8863
+ ops.push({ type: OpCode.DELETE_OBJECT_KEY, id, key });
8864
+ }
8865
+ }
8562
8866
  }
8563
8867
  }
8564
8868
  if (crdt.parentKey !== currentCrdt.parentKey) {
@@ -8669,7 +8973,7 @@ function sendToPanel(message, options) {
8669
8973
  ...message,
8670
8974
  source: "liveblocks-devtools-client"
8671
8975
  };
8672
- if (!(_optionalChain([options, 'optionalAccess', _202 => _202.force]) || _bridgeActive)) {
8976
+ if (!(_optionalChain([options, 'optionalAccess', _213 => _213.force]) || _bridgeActive)) {
8673
8977
  return;
8674
8978
  }
8675
8979
  window.postMessage(fullMsg, "*");
@@ -8677,7 +8981,7 @@ function sendToPanel(message, options) {
8677
8981
  var eventSource = makeEventSource();
8678
8982
  if (process.env.NODE_ENV !== "production" && typeof window !== "undefined") {
8679
8983
  window.addEventListener("message", (event) => {
8680
- if (event.source === window && _optionalChain([event, 'access', _203 => _203.data, 'optionalAccess', _204 => _204.source]) === "liveblocks-devtools-panel") {
8984
+ if (event.source === window && _optionalChain([event, 'access', _214 => _214.data, 'optionalAccess', _215 => _215.source]) === "liveblocks-devtools-panel") {
8681
8985
  eventSource.notify(event.data);
8682
8986
  } else {
8683
8987
  }
@@ -8819,7 +9123,7 @@ function fullSync(room) {
8819
9123
  msg: "room::sync::full",
8820
9124
  roomId: room.id,
8821
9125
  status: room.getStatus(),
8822
- storage: _nullishCoalesce(_optionalChain([root, 'optionalAccess', _205 => _205.toTreeNode, 'call', _206 => _206("root"), 'access', _207 => _207.payload]), () => ( null)),
9126
+ storage: _nullishCoalesce(_optionalChain([root, 'optionalAccess', _216 => _216.toTreeNode, 'call', _217 => _217("root"), 'access', _218 => _218.payload]), () => ( null)),
8823
9127
  me,
8824
9128
  others
8825
9129
  });
@@ -9498,15 +9802,15 @@ function installBackgroundTabSpy() {
9498
9802
  const doc = typeof document !== "undefined" ? document : void 0;
9499
9803
  const inBackgroundSince = { current: null };
9500
9804
  function onVisibilityChange() {
9501
- if (_optionalChain([doc, 'optionalAccess', _208 => _208.visibilityState]) === "hidden") {
9805
+ if (_optionalChain([doc, 'optionalAccess', _219 => _219.visibilityState]) === "hidden") {
9502
9806
  inBackgroundSince.current = _nullishCoalesce(inBackgroundSince.current, () => ( Date.now()));
9503
9807
  } else {
9504
9808
  inBackgroundSince.current = null;
9505
9809
  }
9506
9810
  }
9507
- _optionalChain([doc, 'optionalAccess', _209 => _209.addEventListener, 'call', _210 => _210("visibilitychange", onVisibilityChange)]);
9811
+ _optionalChain([doc, 'optionalAccess', _220 => _220.addEventListener, 'call', _221 => _221("visibilitychange", onVisibilityChange)]);
9508
9812
  const unsub = () => {
9509
- _optionalChain([doc, 'optionalAccess', _211 => _211.removeEventListener, 'call', _212 => _212("visibilitychange", onVisibilityChange)]);
9813
+ _optionalChain([doc, 'optionalAccess', _222 => _222.removeEventListener, 'call', _223 => _223("visibilitychange", onVisibilityChange)]);
9510
9814
  };
9511
9815
  return [inBackgroundSince, unsub];
9512
9816
  }
@@ -9551,6 +9855,7 @@ function createRoom(options, config) {
9551
9855
  delegates,
9552
9856
  config.enableDebugLogging
9553
9857
  );
9858
+ const unacknowledgedOps = new UnacknowledgedOps();
9554
9859
  const context = {
9555
9860
  buffer: {
9556
9861
  flushTimerID: void 0,
@@ -9578,14 +9883,15 @@ function createRoom(options, config) {
9578
9883
  pool: createManagedPool(roomId, {
9579
9884
  getCurrentConnectionId,
9580
9885
  onDispatch,
9581
- isStorageWritable
9886
+ isStorageWritable,
9887
+ unacknowledgedOps
9582
9888
  }),
9583
9889
  root: void 0,
9584
9890
  undoStack: [],
9585
9891
  redoStack: [],
9586
9892
  pausedHistory: null,
9587
9893
  activeBatch: null,
9588
- unacknowledgedOps: /* @__PURE__ */ new Map()
9894
+ unacknowledgedOps
9589
9895
  };
9590
9896
  const nodeMapBuffer = makeNodeMapBuffer();
9591
9897
  const stopwatch = config.enableDebugLogging ? makeStopWatch() : void 0;
@@ -9652,6 +9958,7 @@ function createRoom(options, config) {
9652
9958
  }
9653
9959
  function onDidDisconnect() {
9654
9960
  clearTimeout(context.buffer.flushTimerID);
9961
+ context.unacknowledgedOps.markAllAsPossiblyStored();
9655
9962
  }
9656
9963
  managedSocket.events.onMessage.subscribe(handleServerMessage);
9657
9964
  managedSocket.events.statusDidChange.subscribe(onStatusDidChange);
@@ -9697,7 +10004,7 @@ function createRoom(options, config) {
9697
10004
  }
9698
10005
  }
9699
10006
  function isStorageWritable() {
9700
- const scopes = _optionalChain([context, 'access', _213 => _213.dynamicSessionInfoSig, 'access', _214 => _214.get, 'call', _215 => _215(), 'optionalAccess', _216 => _216.scopes]);
10007
+ const scopes = _optionalChain([context, 'access', _224 => _224.dynamicSessionInfoSig, 'access', _225 => _225.get, 'call', _226 => _226(), 'optionalAccess', _227 => _227.scopes]);
9701
10008
  return scopes !== void 0 ? hasPermissionCapability(scopes, "storage", "write") : true;
9702
10009
  }
9703
10010
  const eventHub = {
@@ -9809,7 +10116,7 @@ function createRoom(options, config) {
9809
10116
  context.pool
9810
10117
  );
9811
10118
  }
9812
- const canWrite = _nullishCoalesce(_optionalChain([self, 'access', _217 => _217.get, 'call', _218 => _218(), 'optionalAccess', _219 => _219.canWrite]), () => ( true));
10119
+ const canWrite = _nullishCoalesce(_optionalChain([self, 'access', _228 => _228.get, 'call', _229 => _229(), 'optionalAccess', _230 => _230.canWrite]), () => ( true));
9813
10120
  const root = context.root;
9814
10121
  disableHistory(() => {
9815
10122
  for (const key in context.initialStorage) {
@@ -10014,7 +10321,7 @@ function createRoom(options, config) {
10014
10321
  }
10015
10322
  context.myPresence.patch(patch);
10016
10323
  if (context.activeBatch) {
10017
- if (_optionalChain([options2, 'optionalAccess', _220 => _220.addToHistory])) {
10324
+ if (_optionalChain([options2, 'optionalAccess', _231 => _231.addToHistory])) {
10018
10325
  context.activeBatch.reverseOps.pushLeft({
10019
10326
  type: "presence",
10020
10327
  data: oldValues
@@ -10023,7 +10330,7 @@ function createRoom(options, config) {
10023
10330
  context.activeBatch.updates.presence = true;
10024
10331
  } else {
10025
10332
  flushNowOrSoon();
10026
- if (_optionalChain([options2, 'optionalAccess', _221 => _221.addToHistory])) {
10333
+ if (_optionalChain([options2, 'optionalAccess', _232 => _232.addToHistory])) {
10027
10334
  addToUndoStack([{ type: "presence", data: oldValues }]);
10028
10335
  }
10029
10336
  notify({ presence: true });
@@ -10132,12 +10439,11 @@ function createRoom(options, config) {
10132
10439
  }
10133
10440
  }
10134
10441
  function applyAndSendOfflineOps(unackedOps) {
10135
- if (unackedOps.size === 0) {
10442
+ if (unackedOps.length === 0) {
10136
10443
  return;
10137
10444
  }
10138
10445
  const messages = [];
10139
- const inOps = Array.from(unackedOps.values());
10140
- const result = applyLocalOps(inOps);
10446
+ const result = applyLocalOps(unackedOps);
10141
10447
  messages.push({
10142
10448
  type: ClientMsgCode.UPDATE_STORAGE,
10143
10449
  ops: result.opsToEmit
@@ -10201,11 +10507,11 @@ function createRoom(options, config) {
10201
10507
  break;
10202
10508
  }
10203
10509
  case ServerMsgCode.STORAGE_CHUNK:
10204
- _optionalChain([stopwatch, 'optionalAccess', _222 => _222.lap, 'call', _223 => _223()]);
10510
+ _optionalChain([stopwatch, 'optionalAccess', _233 => _233.lap, 'call', _234 => _234()]);
10205
10511
  nodeMapBuffer.append(compactNodesToNodeStream(message.nodes));
10206
10512
  break;
10207
10513
  case ServerMsgCode.STORAGE_STREAM_END: {
10208
- const timing = _optionalChain([stopwatch, 'optionalAccess', _224 => _224.stop, 'call', _225 => _225()]);
10514
+ const timing = _optionalChain([stopwatch, 'optionalAccess', _235 => _235.stop, 'call', _236 => _236()]);
10209
10515
  if (timing) {
10210
10516
  const ms = (v) => `${v.toFixed(1)}ms`;
10211
10517
  const rest = timing.laps.slice(1);
@@ -10340,11 +10646,11 @@ function createRoom(options, config) {
10340
10646
  } else if (pendingFeedsRequests.has(requestId)) {
10341
10647
  const pending = pendingFeedsRequests.get(requestId);
10342
10648
  pendingFeedsRequests.delete(requestId);
10343
- _optionalChain([pending, 'optionalAccess', _226 => _226.reject, 'call', _227 => _227(err)]);
10649
+ _optionalChain([pending, 'optionalAccess', _237 => _237.reject, 'call', _238 => _238(err)]);
10344
10650
  } else if (pendingFeedMessagesRequests.has(requestId)) {
10345
10651
  const pending = pendingFeedMessagesRequests.get(requestId);
10346
10652
  pendingFeedMessagesRequests.delete(requestId);
10347
- _optionalChain([pending, 'optionalAccess', _228 => _228.reject, 'call', _229 => _229(err)]);
10653
+ _optionalChain([pending, 'optionalAccess', _239 => _239.reject, 'call', _240 => _240(err)]);
10348
10654
  }
10349
10655
  eventHub.feeds.notify(message);
10350
10656
  break;
@@ -10361,7 +10667,7 @@ function createRoom(options, config) {
10361
10667
  const storageOps = context.buffer.storageOperations;
10362
10668
  if (storageOps.length > 0) {
10363
10669
  for (const op of storageOps) {
10364
- context.unacknowledgedOps.set(op.opId, op);
10670
+ context.unacknowledgedOps.add(op);
10365
10671
  }
10366
10672
  notifyStorageStatus();
10367
10673
  }
@@ -10498,10 +10804,10 @@ function createRoom(options, config) {
10498
10804
  timeoutId,
10499
10805
  kind,
10500
10806
  feedId,
10501
- messageId: _optionalChain([options2, 'optionalAccess', _230 => _230.messageId]),
10502
- expectedClientMessageId: _optionalChain([options2, 'optionalAccess', _231 => _231.expectedClientMessageId])
10807
+ messageId: _optionalChain([options2, 'optionalAccess', _241 => _241.messageId]),
10808
+ expectedClientMessageId: _optionalChain([options2, 'optionalAccess', _242 => _242.expectedClientMessageId])
10503
10809
  });
10504
- if (kind === "add-message" && _optionalChain([options2, 'optionalAccess', _232 => _232.expectedClientMessageId]) === void 0) {
10810
+ if (kind === "add-message" && _optionalChain([options2, 'optionalAccess', _243 => _243.expectedClientMessageId]) === void 0) {
10505
10811
  const q = _nullishCoalesce(pendingAddMessageFifoByFeed.get(feedId), () => ( []));
10506
10812
  q.push(requestId);
10507
10813
  pendingAddMessageFifoByFeed.set(feedId, q);
@@ -10552,10 +10858,10 @@ function createRoom(options, config) {
10552
10858
  }
10553
10859
  if (!matched) {
10554
10860
  const q = pendingAddMessageFifoByFeed.get(message.feedId);
10555
- const headId = _optionalChain([q, 'optionalAccess', _233 => _233[0]]);
10861
+ const headId = _optionalChain([q, 'optionalAccess', _244 => _244[0]]);
10556
10862
  if (headId !== void 0) {
10557
10863
  const pending = pendingFeedMutations.get(headId);
10558
- if (_optionalChain([pending, 'optionalAccess', _234 => _234.kind]) === "add-message" && pending.expectedClientMessageId === void 0) {
10864
+ if (_optionalChain([pending, 'optionalAccess', _245 => _245.kind]) === "add-message" && pending.expectedClientMessageId === void 0) {
10559
10865
  settleFeedMutation(headId, "ok");
10560
10866
  }
10561
10867
  }
@@ -10588,10 +10894,10 @@ function createRoom(options, config) {
10588
10894
  }
10589
10895
  }
10590
10896
  function processInitialStorage(nodes) {
10591
- const unacknowledgedOps = new Map(context.unacknowledgedOps);
10897
+ const unacknowledgedOps2 = [...context.unacknowledgedOps.values()];
10592
10898
  createOrUpdateRootFromMessage(nodes);
10593
- applyAndSendOfflineOps(unacknowledgedOps);
10594
- _optionalChain([_resolveStoragePromise, 'optionalCall', _235 => _235()]);
10899
+ applyAndSendOfflineOps(unacknowledgedOps2);
10900
+ _optionalChain([_resolveStoragePromise, 'optionalCall', _246 => _246()]);
10595
10901
  notifyStorageStatus();
10596
10902
  eventHub.storageDidLoad.notify();
10597
10903
  }
@@ -10609,7 +10915,7 @@ function createRoom(options, config) {
10609
10915
  } else if (!messages.some((msg) => msg.type === ClientMsgCode.FETCH_STORAGE)) {
10610
10916
  messages.push({ type: ClientMsgCode.FETCH_STORAGE });
10611
10917
  nodeMapBuffer.take();
10612
- _optionalChain([stopwatch, 'optionalAccess', _236 => _236.start, 'call', _237 => _237()]);
10918
+ _optionalChain([stopwatch, 'optionalAccess', _247 => _247.start, 'call', _248 => _248()]);
10613
10919
  }
10614
10920
  if (options2.flush) {
10615
10921
  flushNowOrSoon();
@@ -10665,10 +10971,10 @@ function createRoom(options, config) {
10665
10971
  const message = {
10666
10972
  type: ClientMsgCode.FETCH_FEEDS,
10667
10973
  requestId,
10668
- cursor: _optionalChain([options2, 'optionalAccess', _238 => _238.cursor]),
10669
- since: _optionalChain([options2, 'optionalAccess', _239 => _239.since]),
10670
- limit: _optionalChain([options2, 'optionalAccess', _240 => _240.limit]),
10671
- metadata: _optionalChain([options2, 'optionalAccess', _241 => _241.metadata])
10974
+ cursor: _optionalChain([options2, 'optionalAccess', _249 => _249.cursor]),
10975
+ since: _optionalChain([options2, 'optionalAccess', _250 => _250.since]),
10976
+ limit: _optionalChain([options2, 'optionalAccess', _251 => _251.limit]),
10977
+ metadata: _optionalChain([options2, 'optionalAccess', _252 => _252.metadata])
10672
10978
  };
10673
10979
  context.buffer.messages.push(message);
10674
10980
  flushNowOrSoon();
@@ -10688,9 +10994,9 @@ function createRoom(options, config) {
10688
10994
  type: ClientMsgCode.FETCH_FEED_MESSAGES,
10689
10995
  requestId,
10690
10996
  feedId,
10691
- cursor: _optionalChain([options2, 'optionalAccess', _242 => _242.cursor]),
10692
- since: _optionalChain([options2, 'optionalAccess', _243 => _243.since]),
10693
- limit: _optionalChain([options2, 'optionalAccess', _244 => _244.limit])
10997
+ cursor: _optionalChain([options2, 'optionalAccess', _253 => _253.cursor]),
10998
+ since: _optionalChain([options2, 'optionalAccess', _254 => _254.since]),
10999
+ limit: _optionalChain([options2, 'optionalAccess', _255 => _255.limit])
10694
11000
  };
10695
11001
  context.buffer.messages.push(message);
10696
11002
  flushNowOrSoon();
@@ -10709,8 +11015,8 @@ function createRoom(options, config) {
10709
11015
  type: ClientMsgCode.ADD_FEED,
10710
11016
  requestId,
10711
11017
  feedId,
10712
- metadata: _optionalChain([options2, 'optionalAccess', _245 => _245.metadata]),
10713
- createdAt: _optionalChain([options2, 'optionalAccess', _246 => _246.createdAt])
11018
+ metadata: _optionalChain([options2, 'optionalAccess', _256 => _256.metadata]),
11019
+ createdAt: _optionalChain([options2, 'optionalAccess', _257 => _257.createdAt])
10714
11020
  };
10715
11021
  context.buffer.messages.push(message);
10716
11022
  flushNowOrSoon();
@@ -10744,15 +11050,15 @@ function createRoom(options, config) {
10744
11050
  function addFeedMessage(feedId, data, options2) {
10745
11051
  const requestId = nanoid();
10746
11052
  const promise = registerFeedMutation(requestId, "add-message", feedId, {
10747
- expectedClientMessageId: _optionalChain([options2, 'optionalAccess', _247 => _247.id])
11053
+ expectedClientMessageId: _optionalChain([options2, 'optionalAccess', _258 => _258.id])
10748
11054
  });
10749
11055
  const message = {
10750
11056
  type: ClientMsgCode.ADD_FEED_MESSAGE,
10751
11057
  requestId,
10752
11058
  feedId,
10753
11059
  data,
10754
- id: _optionalChain([options2, 'optionalAccess', _248 => _248.id]),
10755
- createdAt: _optionalChain([options2, 'optionalAccess', _249 => _249.createdAt])
11060
+ id: _optionalChain([options2, 'optionalAccess', _259 => _259.id]),
11061
+ createdAt: _optionalChain([options2, 'optionalAccess', _260 => _260.createdAt])
10756
11062
  };
10757
11063
  context.buffer.messages.push(message);
10758
11064
  flushNowOrSoon();
@@ -10769,7 +11075,7 @@ function createRoom(options, config) {
10769
11075
  feedId,
10770
11076
  messageId,
10771
11077
  data,
10772
- updatedAt: _optionalChain([options2, 'optionalAccess', _250 => _250.updatedAt])
11078
+ updatedAt: _optionalChain([options2, 'optionalAccess', _261 => _261.updatedAt])
10773
11079
  };
10774
11080
  context.buffer.messages.push(message);
10775
11081
  flushNowOrSoon();
@@ -10976,8 +11282,8 @@ function createRoom(options, config) {
10976
11282
  async function getThreads(options2) {
10977
11283
  return httpClient.getThreads({
10978
11284
  roomId,
10979
- query: _optionalChain([options2, 'optionalAccess', _251 => _251.query]),
10980
- cursor: _optionalChain([options2, 'optionalAccess', _252 => _252.cursor])
11285
+ query: _optionalChain([options2, 'optionalAccess', _262 => _262.query]),
11286
+ cursor: _optionalChain([options2, 'optionalAccess', _263 => _263.cursor])
10981
11287
  });
10982
11288
  }
10983
11289
  async function getThread(threadId) {
@@ -11099,7 +11405,7 @@ function createRoom(options, config) {
11099
11405
  function getSubscriptionSettings(options2) {
11100
11406
  return httpClient.getSubscriptionSettings({
11101
11407
  roomId,
11102
- signal: _optionalChain([options2, 'optionalAccess', _253 => _253.signal])
11408
+ signal: _optionalChain([options2, 'optionalAccess', _264 => _264.signal])
11103
11409
  });
11104
11410
  }
11105
11411
  function updateSubscriptionSettings(settings) {
@@ -11121,7 +11427,7 @@ function createRoom(options, config) {
11121
11427
  {
11122
11428
  [kInternal]: {
11123
11429
  get presenceBuffer() {
11124
- return deepClone(_nullishCoalesce(_optionalChain([context, 'access', _254 => _254.buffer, 'access', _255 => _255.presenceUpdates, 'optionalAccess', _256 => _256.data]), () => ( null)));
11430
+ return deepClone(_nullishCoalesce(_optionalChain([context, 'access', _265 => _265.buffer, 'access', _266 => _266.presenceUpdates, 'optionalAccess', _267 => _267.data]), () => ( null)));
11125
11431
  },
11126
11432
  // prettier-ignore
11127
11433
  get undoStack() {
@@ -11136,9 +11442,9 @@ function createRoom(options, config) {
11136
11442
  return context.yjsProvider;
11137
11443
  },
11138
11444
  setYjsProvider(newProvider) {
11139
- _optionalChain([context, 'access', _257 => _257.yjsProvider, 'optionalAccess', _258 => _258.off, 'call', _259 => _259("status", yjsStatusDidChange)]);
11445
+ _optionalChain([context, 'access', _268 => _268.yjsProvider, 'optionalAccess', _269 => _269.off, 'call', _270 => _270("status", yjsStatusDidChange)]);
11140
11446
  context.yjsProvider = newProvider;
11141
- _optionalChain([newProvider, 'optionalAccess', _260 => _260.on, 'call', _261 => _261("status", yjsStatusDidChange)]);
11447
+ _optionalChain([newProvider, 'optionalAccess', _271 => _271.on, 'call', _272 => _272("status", yjsStatusDidChange)]);
11142
11448
  context.yjsProviderDidChange.notify();
11143
11449
  },
11144
11450
  yjsProviderDidChange: context.yjsProviderDidChange.observable,
@@ -11179,6 +11485,11 @@ function createRoom(options, config) {
11179
11485
  connect: () => managedSocket.connect(),
11180
11486
  reconnect: () => managedSocket.reconnect(),
11181
11487
  disconnect: () => managedSocket.disconnect(),
11488
+ _dump: () => {
11489
+ const n = context.pool.nodes.size;
11490
+ return `Room "${roomId}" (${n} node${n === 1 ? "" : "s"}):
11491
+ ${dumpPool(context.pool)}`;
11492
+ },
11182
11493
  destroy: () => {
11183
11494
  pendingFeedsRequests.forEach(
11184
11495
  (request) => request.reject(new Error("Room destroyed"))
@@ -11191,7 +11502,7 @@ function createRoom(options, config) {
11191
11502
  source.dispose();
11192
11503
  }
11193
11504
  eventHub.roomWillDestroy.notify();
11194
- _optionalChain([context, 'access', _262 => _262.yjsProvider, 'optionalAccess', _263 => _263.off, 'call', _264 => _264("status", yjsStatusDidChange)]);
11505
+ _optionalChain([context, 'access', _273 => _273.yjsProvider, 'optionalAccess', _274 => _274.off, 'call', _275 => _275("status", yjsStatusDidChange)]);
11195
11506
  syncSourceForStorage.destroy();
11196
11507
  syncSourceForYjs.destroy();
11197
11508
  uninstallBgTabSpy();
@@ -11351,7 +11662,7 @@ function makeClassicSubscribeFn(roomId, events, errorEvents) {
11351
11662
  }
11352
11663
  if (isLiveNode(first)) {
11353
11664
  const node = first;
11354
- if (_optionalChain([options, 'optionalAccess', _265 => _265.isDeep])) {
11665
+ if (_optionalChain([options, 'optionalAccess', _276 => _276.isDeep])) {
11355
11666
  const storageCallback = second;
11356
11667
  return subscribeToLiveStructureDeeply(node, storageCallback);
11357
11668
  } else {
@@ -11441,8 +11752,8 @@ function createClient(options) {
11441
11752
  const authManager = createAuthManager(options, (token) => {
11442
11753
  currentUserId.set(() => token.uid);
11443
11754
  });
11444
- const fetchPolyfill = _optionalChain([clientOptions, 'access', _266 => _266.polyfills, 'optionalAccess', _267 => _267.fetch]) || /* istanbul ignore next */
11445
- _optionalChain([globalThis, 'access', _268 => _268.fetch, 'optionalAccess', _269 => _269.bind, 'call', _270 => _270(globalThis)]);
11755
+ const fetchPolyfill = _optionalChain([clientOptions, 'access', _277 => _277.polyfills, 'optionalAccess', _278 => _278.fetch]) || /* istanbul ignore next */
11756
+ _optionalChain([globalThis, 'access', _279 => _279.fetch, 'optionalAccess', _280 => _280.bind, 'call', _281 => _281(globalThis)]);
11446
11757
  const httpClient = createApiClient({
11447
11758
  baseUrl,
11448
11759
  fetchPolyfill,
@@ -11460,7 +11771,7 @@ function createClient(options) {
11460
11771
  delegates: {
11461
11772
  createSocket: makeCreateSocketDelegateForAi(
11462
11773
  baseUrl,
11463
- _optionalChain([clientOptions, 'access', _271 => _271.polyfills, 'optionalAccess', _272 => _272.WebSocket])
11774
+ _optionalChain([clientOptions, 'access', _282 => _282.polyfills, 'optionalAccess', _283 => _283.WebSocket])
11464
11775
  ),
11465
11776
  authenticate: async () => {
11466
11777
  const resp = await authManager.getAuthValue({
@@ -11532,7 +11843,7 @@ function createClient(options) {
11532
11843
  createSocket: makeCreateSocketDelegateForRoom(
11533
11844
  roomId,
11534
11845
  baseUrl,
11535
- _optionalChain([clientOptions, 'access', _273 => _273.polyfills, 'optionalAccess', _274 => _274.WebSocket])
11846
+ _optionalChain([clientOptions, 'access', _284 => _284.polyfills, 'optionalAccess', _285 => _285.WebSocket])
11536
11847
  ),
11537
11848
  authenticate: makeAuthDelegateForRoom(roomId, authManager)
11538
11849
  })),
@@ -11555,7 +11866,7 @@ function createClient(options) {
11555
11866
  const shouldConnect = _nullishCoalesce(options2.autoConnect, () => ( true));
11556
11867
  if (shouldConnect) {
11557
11868
  if (typeof atob === "undefined") {
11558
- if (_optionalChain([clientOptions, 'access', _275 => _275.polyfills, 'optionalAccess', _276 => _276.atob]) === void 0) {
11869
+ if (_optionalChain([clientOptions, 'access', _286 => _286.polyfills, 'optionalAccess', _287 => _287.atob]) === void 0) {
11559
11870
  throw new Error(
11560
11871
  "You need to polyfill atob to use the client in your environment. Please follow the instructions at https://liveblocks.io/docs/errors/liveblocks-client/atob-polyfill"
11561
11872
  );
@@ -11567,7 +11878,7 @@ function createClient(options) {
11567
11878
  return leaseRoom(newRoomDetails);
11568
11879
  }
11569
11880
  function getRoom(roomId) {
11570
- const room = _optionalChain([roomsById, 'access', _277 => _277.get, 'call', _278 => _278(roomId), 'optionalAccess', _279 => _279.room]);
11881
+ const room = _optionalChain([roomsById, 'access', _288 => _288.get, 'call', _289 => _289(roomId), 'optionalAccess', _290 => _290.room]);
11571
11882
  return room ? room : null;
11572
11883
  }
11573
11884
  function logout() {
@@ -11583,7 +11894,7 @@ function createClient(options) {
11583
11894
  const batchedResolveUsers = new Batch(
11584
11895
  async (batchedUserIds) => {
11585
11896
  const userIds = batchedUserIds.flat();
11586
- const users = await _optionalChain([resolveUsers, 'optionalCall', _280 => _280({ userIds })]);
11897
+ const users = await _optionalChain([resolveUsers, 'optionalCall', _291 => _291({ userIds })]);
11587
11898
  warnOnceIf(
11588
11899
  !resolveUsers,
11589
11900
  "Set the resolveUsers option in createClient to specify user info."
@@ -11600,7 +11911,7 @@ function createClient(options) {
11600
11911
  const batchedResolveRoomsInfo = new Batch(
11601
11912
  async (batchedRoomIds) => {
11602
11913
  const roomIds = batchedRoomIds.flat();
11603
- const roomsInfo = await _optionalChain([resolveRoomsInfo, 'optionalCall', _281 => _281({ roomIds })]);
11914
+ const roomsInfo = await _optionalChain([resolveRoomsInfo, 'optionalCall', _292 => _292({ roomIds })]);
11604
11915
  warnOnceIf(
11605
11916
  !resolveRoomsInfo,
11606
11917
  "Set the resolveRoomsInfo option in createClient to specify room info."
@@ -11617,7 +11928,7 @@ function createClient(options) {
11617
11928
  const batchedResolveGroupsInfo = new Batch(
11618
11929
  async (batchedGroupIds) => {
11619
11930
  const groupIds = batchedGroupIds.flat();
11620
- const groupsInfo = await _optionalChain([resolveGroupsInfo, 'optionalCall', _282 => _282({ groupIds })]);
11931
+ const groupsInfo = await _optionalChain([resolveGroupsInfo, 'optionalCall', _293 => _293({ groupIds })]);
11621
11932
  warnOnceIf(
11622
11933
  !resolveGroupsInfo,
11623
11934
  "Set the resolveGroupsInfo option in createClient to specify group info."
@@ -11676,7 +11987,7 @@ function createClient(options) {
11676
11987
  }
11677
11988
  };
11678
11989
  const win = typeof window !== "undefined" ? window : void 0;
11679
- _optionalChain([win, 'optionalAccess', _283 => _283.addEventListener, 'call', _284 => _284("beforeunload", maybePreventClose)]);
11990
+ _optionalChain([win, 'optionalAccess', _294 => _294.addEventListener, 'call', _295 => _295("beforeunload", maybePreventClose)]);
11680
11991
  }
11681
11992
  async function getNotificationSettings(options2) {
11682
11993
  const plainSettings = await httpClient.getNotificationSettings(options2);
@@ -11692,6 +12003,7 @@ function createClient(options) {
11692
12003
  {
11693
12004
  enterRoom,
11694
12005
  getRoom,
12006
+ _dump: () => Array.from(roomsById.values(), ({ room }) => room._dump()).join("\n\n"),
11695
12007
  logout,
11696
12008
  // Public inbox notifications API
11697
12009
  getInboxNotifications: httpClient.getInboxNotifications,
@@ -11803,7 +12115,7 @@ var commentBodyElementsTypes = {
11803
12115
  mention: "inline"
11804
12116
  };
11805
12117
  function traverseCommentBody(body, elementOrVisitor, possiblyVisitor) {
11806
- if (!body || !_optionalChain([body, 'optionalAccess', _285 => _285.content])) {
12118
+ if (!body || !_optionalChain([body, 'optionalAccess', _296 => _296.content])) {
11807
12119
  return;
11808
12120
  }
11809
12121
  const element = typeof elementOrVisitor === "string" ? elementOrVisitor : void 0;
@@ -11813,13 +12125,13 @@ function traverseCommentBody(body, elementOrVisitor, possiblyVisitor) {
11813
12125
  for (const block of body.content) {
11814
12126
  if (type === "all" || type === "block") {
11815
12127
  if (guard(block)) {
11816
- _optionalChain([visitor, 'optionalCall', _286 => _286(block)]);
12128
+ _optionalChain([visitor, 'optionalCall', _297 => _297(block)]);
11817
12129
  }
11818
12130
  }
11819
12131
  if (type === "all" || type === "inline") {
11820
12132
  for (const inline of block.children) {
11821
12133
  if (guard(inline)) {
11822
- _optionalChain([visitor, 'optionalCall', _287 => _287(inline)]);
12134
+ _optionalChain([visitor, 'optionalCall', _298 => _298(inline)]);
11823
12135
  }
11824
12136
  }
11825
12137
  }
@@ -11989,7 +12301,7 @@ var stringifyCommentBodyPlainElements = {
11989
12301
  text: ({ element }) => element.text,
11990
12302
  link: ({ element }) => _nullishCoalesce(element.text, () => ( element.url)),
11991
12303
  mention: ({ element, user, group }) => {
11992
- return `@${_nullishCoalesce(_nullishCoalesce(_optionalChain([user, 'optionalAccess', _288 => _288.name]), () => ( _optionalChain([group, 'optionalAccess', _289 => _289.name]))), () => ( element.id))}`;
12304
+ return `@${_nullishCoalesce(_nullishCoalesce(_optionalChain([user, 'optionalAccess', _299 => _299.name]), () => ( _optionalChain([group, 'optionalAccess', _300 => _300.name]))), () => ( element.id))}`;
11993
12305
  }
11994
12306
  };
11995
12307
  var stringifyCommentBodyHtmlElements = {
@@ -12019,7 +12331,7 @@ var stringifyCommentBodyHtmlElements = {
12019
12331
  return html`<a href="${href}" target="_blank" rel="noopener noreferrer">${element.text ? html`${element.text}` : element.url}</a>`;
12020
12332
  },
12021
12333
  mention: ({ element, user, group }) => {
12022
- return html`<span data-mention>@${_optionalChain([user, 'optionalAccess', _290 => _290.name]) ? html`${_optionalChain([user, 'optionalAccess', _291 => _291.name])}` : _optionalChain([group, 'optionalAccess', _292 => _292.name]) ? html`${_optionalChain([group, 'optionalAccess', _293 => _293.name])}` : element.id}</span>`;
12334
+ return html`<span data-mention>@${_optionalChain([user, 'optionalAccess', _301 => _301.name]) ? html`${_optionalChain([user, 'optionalAccess', _302 => _302.name])}` : _optionalChain([group, 'optionalAccess', _303 => _303.name]) ? html`${_optionalChain([group, 'optionalAccess', _304 => _304.name])}` : element.id}</span>`;
12023
12335
  }
12024
12336
  };
12025
12337
  var stringifyCommentBodyMarkdownElements = {
@@ -12049,20 +12361,20 @@ var stringifyCommentBodyMarkdownElements = {
12049
12361
  return markdown`[${_nullishCoalesce(element.text, () => ( element.url))}](${href})`;
12050
12362
  },
12051
12363
  mention: ({ element, user, group }) => {
12052
- return markdown`@${_nullishCoalesce(_nullishCoalesce(_optionalChain([user, 'optionalAccess', _294 => _294.name]), () => ( _optionalChain([group, 'optionalAccess', _295 => _295.name]))), () => ( element.id))}`;
12364
+ return markdown`@${_nullishCoalesce(_nullishCoalesce(_optionalChain([user, 'optionalAccess', _305 => _305.name]), () => ( _optionalChain([group, 'optionalAccess', _306 => _306.name]))), () => ( element.id))}`;
12053
12365
  }
12054
12366
  };
12055
12367
  async function stringifyCommentBody(body, options) {
12056
- const format = _nullishCoalesce(_optionalChain([options, 'optionalAccess', _296 => _296.format]), () => ( "plain"));
12057
- const separator = _nullishCoalesce(_optionalChain([options, 'optionalAccess', _297 => _297.separator]), () => ( (format === "markdown" ? "\n\n" : "\n")));
12368
+ const format = _nullishCoalesce(_optionalChain([options, 'optionalAccess', _307 => _307.format]), () => ( "plain"));
12369
+ const separator = _nullishCoalesce(_optionalChain([options, 'optionalAccess', _308 => _308.separator]), () => ( (format === "markdown" ? "\n\n" : "\n")));
12058
12370
  const elements = {
12059
12371
  ...format === "html" ? stringifyCommentBodyHtmlElements : format === "markdown" ? stringifyCommentBodyMarkdownElements : stringifyCommentBodyPlainElements,
12060
- ..._optionalChain([options, 'optionalAccess', _298 => _298.elements])
12372
+ ..._optionalChain([options, 'optionalAccess', _309 => _309.elements])
12061
12373
  };
12062
12374
  const { users: resolvedUsers, groups: resolvedGroupsInfo } = await resolveMentionsInCommentBody(
12063
12375
  body,
12064
- _optionalChain([options, 'optionalAccess', _299 => _299.resolveUsers]),
12065
- _optionalChain([options, 'optionalAccess', _300 => _300.resolveGroupsInfo])
12376
+ _optionalChain([options, 'optionalAccess', _310 => _310.resolveUsers]),
12377
+ _optionalChain([options, 'optionalAccess', _311 => _311.resolveGroupsInfo])
12066
12378
  );
12067
12379
  const blocks = body.content.flatMap((block, blockIndex) => {
12068
12380
  switch (block.type) {
@@ -12197,9 +12509,9 @@ function makePoller(callback, intervalMs, options) {
12197
12509
  const startTime = performance.now();
12198
12510
  const doc = typeof document !== "undefined" ? document : void 0;
12199
12511
  const win = typeof window !== "undefined" ? window : void 0;
12200
- const maxStaleTimeMs = _nullishCoalesce(_optionalChain([options, 'optionalAccess', _301 => _301.maxStaleTimeMs]), () => ( Number.POSITIVE_INFINITY));
12512
+ const maxStaleTimeMs = _nullishCoalesce(_optionalChain([options, 'optionalAccess', _312 => _312.maxStaleTimeMs]), () => ( Number.POSITIVE_INFINITY));
12201
12513
  const context = {
12202
- inForeground: _optionalChain([doc, 'optionalAccess', _302 => _302.visibilityState]) !== "hidden",
12514
+ inForeground: _optionalChain([doc, 'optionalAccess', _313 => _313.visibilityState]) !== "hidden",
12203
12515
  lastSuccessfulPollAt: startTime,
12204
12516
  count: 0,
12205
12517
  backoff: 0
@@ -12280,11 +12592,11 @@ function makePoller(callback, intervalMs, options) {
12280
12592
  pollNowIfStale();
12281
12593
  }
12282
12594
  function onVisibilityChange() {
12283
- setInForeground(_optionalChain([doc, 'optionalAccess', _303 => _303.visibilityState]) !== "hidden");
12595
+ setInForeground(_optionalChain([doc, 'optionalAccess', _314 => _314.visibilityState]) !== "hidden");
12284
12596
  }
12285
- _optionalChain([doc, 'optionalAccess', _304 => _304.addEventListener, 'call', _305 => _305("visibilitychange", onVisibilityChange)]);
12286
- _optionalChain([win, 'optionalAccess', _306 => _306.addEventListener, 'call', _307 => _307("online", onVisibilityChange)]);
12287
- _optionalChain([win, 'optionalAccess', _308 => _308.addEventListener, 'call', _309 => _309("focus", pollNowIfStale)]);
12597
+ _optionalChain([doc, 'optionalAccess', _315 => _315.addEventListener, 'call', _316 => _316("visibilitychange", onVisibilityChange)]);
12598
+ _optionalChain([win, 'optionalAccess', _317 => _317.addEventListener, 'call', _318 => _318("online", onVisibilityChange)]);
12599
+ _optionalChain([win, 'optionalAccess', _319 => _319.addEventListener, 'call', _320 => _320("focus", pollNowIfStale)]);
12288
12600
  fsm.start();
12289
12601
  return {
12290
12602
  inc,
@@ -12428,5 +12740,7 @@ detectDupes(PKG_NAME, PKG_VERSION, PKG_FORMAT);
12428
12740
 
12429
12741
 
12430
12742
 
12431
- exports.ClientMsgCode = ClientMsgCode; exports.CrdtType = CrdtType; exports.DefaultMap = DefaultMap; exports.Deque = Deque; exports.DerivedSignal = DerivedSignal; exports.FeedRequestErrorCode = FeedRequestErrorCode; exports.HttpError = HttpError; exports.LiveList = LiveList; exports.LiveMap = LiveMap; exports.LiveObject = LiveObject; exports.LiveblocksError = LiveblocksError; exports.MENTION_CHARACTER = MENTION_CHARACTER; exports.MutableSignal = MutableSignal; exports.OpCode = OpCode; exports.Permission = Permission; exports.Promise_withResolvers = Promise_withResolvers; exports.ServerMsgCode = ServerMsgCode; exports.Signal = Signal; exports.SortedList = SortedList; exports.TextEditorType = TextEditorType; exports.WebsocketCloseCodes = WebsocketCloseCodes; exports.asPos = asPos; exports.assert = assert; exports.assertNever = assertNever; exports.autoRetry = autoRetry; exports.b64decode = b64decode; exports.batch = batch; exports.checkBounds = checkBounds; exports.chunk = chunk; exports.cloneLson = cloneLson; exports.compactNodesToNodeStream = compactNodesToNodeStream; exports.compactObject = compactObject; exports.console = fancy_console_exports; exports.convertToCommentData = convertToCommentData; exports.convertToCommentUserReaction = convertToCommentUserReaction; exports.convertToGroupData = convertToGroupData; exports.convertToInboxNotificationData = convertToInboxNotificationData; exports.convertToSubscriptionData = convertToSubscriptionData; exports.convertToThreadData = convertToThreadData; exports.convertToUserSubscriptionData = convertToUserSubscriptionData; exports.createClient = createClient; exports.createCommentAttachmentId = createCommentAttachmentId; exports.createCommentId = createCommentId; exports.createInboxNotificationId = createInboxNotificationId; exports.createManagedPool = createManagedPool; exports.createNotificationSettings = createNotificationSettings; exports.createThreadId = createThreadId; exports.defineAiTool = defineAiTool; exports.deprecate = deprecate; exports.deprecateIf = deprecateIf; exports.detectDupes = detectDupes; exports.entries = entries; exports.errorIf = errorIf; exports.findLastIndex = findLastIndex; exports.freeze = freeze; exports.generateUrl = generateUrl; exports.getMentionsFromCommentBody = getMentionsFromCommentBody; exports.getRoomPermissionConflicts = getRoomPermissionConflicts; exports.getSubscriptionKey = getSubscriptionKey; exports.hasPermissionCapability = hasPermissionCapability; exports.hasPermissionCapabilityAccess = hasPermissionCapabilityAccess; exports.html = html; exports.htmlSafe = htmlSafe; exports.isCommentBodyLink = isCommentBodyLink; exports.isCommentBodyMention = isCommentBodyMention; exports.isCommentBodyText = isCommentBodyText; exports.isJsonArray = isJsonArray; exports.isJsonObject = isJsonObject; exports.isJsonScalar = isJsonScalar; exports.isListStorageNode = isListStorageNode; exports.isLiveNode = isLiveNode; exports.isMapStorageNode = isMapStorageNode; exports.isNotificationChannelEnabled = isNotificationChannelEnabled; exports.isNumberOperator = isNumberOperator; exports.isObjectStorageNode = isObjectStorageNode; exports.isPlainObject = isPlainObject; exports.isRegisterStorageNode = isRegisterStorageNode; exports.isRootStorageNode = isRootStorageNode; exports.isStartsWithOperator = isStartsWithOperator; exports.isUrl = isUrl; exports.kInternal = kInternal; exports.keys = keys; exports.makeAbortController = makeAbortController; exports.makeEventSource = makeEventSource; exports.makePoller = makePoller; exports.makePosition = makePosition; exports.mapValues = mapValues; exports.memoizeOnSuccess = memoizeOnSuccess; exports.nanoid = nanoid; exports.nn = nn; exports.nodeStreamToCompactNodes = nodeStreamToCompactNodes; exports.normalizeRoomAccessesInput = normalizeRoomAccessesInput; exports.normalizeRoomAccessesUpdateInput = normalizeRoomAccessesUpdateInput; exports.normalizeRoomPermissionInput = normalizeRoomPermissionInput; exports.objectToQuery = objectToQuery; exports.patchNotificationSettings = patchNotificationSettings; exports.permissionCapabilitiesFromScopes = permissionCapabilitiesFromScopes; exports.raise = raise; exports.resolveMentionsInCommentBody = resolveMentionsInCommentBody; exports.sanitizeUrl = sanitizeUrl; exports.shallow = shallow; exports.shallow2 = shallow2; exports.stableStringify = stableStringify; exports.stringifyCommentBody = stringifyCommentBody; exports.throwUsageError = throwUsageError; exports.toPlainLson = toPlainLson; exports.tryParseJson = tryParseJson; exports.url = url; exports.urljoin = urljoin; exports.wait = wait; exports.warnOnce = warnOnce; exports.warnOnceIf = warnOnceIf; exports.withTimeout = withTimeout;
12743
+
12744
+
12745
+ exports.ClientMsgCode = ClientMsgCode; exports.CrdtType = CrdtType; exports.DefaultMap = DefaultMap; exports.Deque = Deque; exports.DerivedSignal = DerivedSignal; exports.FeedRequestErrorCode = FeedRequestErrorCode; exports.HttpError = HttpError; exports.LiveList = LiveList; exports.LiveMap = LiveMap; exports.LiveObject = LiveObject; exports.LiveblocksError = LiveblocksError; exports.MENTION_CHARACTER = MENTION_CHARACTER; exports.MutableSignal = MutableSignal; exports.OpCode = OpCode; exports.Permission = Permission; exports.Promise_withResolvers = Promise_withResolvers; exports.ServerMsgCode = ServerMsgCode; exports.Signal = Signal; exports.SortedList = SortedList; exports.TextEditorType = TextEditorType; exports.WebsocketCloseCodes = WebsocketCloseCodes; exports.asPos = asPos; exports.assert = assert; exports.assertNever = assertNever; exports.autoRetry = autoRetry; exports.b64decode = b64decode; exports.batch = batch; exports.checkBounds = checkBounds; exports.chunk = chunk; exports.cloneLson = cloneLson; exports.compactNodesToNodeStream = compactNodesToNodeStream; exports.compactObject = compactObject; exports.console = fancy_console_exports; exports.convertToCommentData = convertToCommentData; exports.convertToCommentUserReaction = convertToCommentUserReaction; exports.convertToGroupData = convertToGroupData; exports.convertToInboxNotificationData = convertToInboxNotificationData; exports.convertToSubscriptionData = convertToSubscriptionData; exports.convertToThreadData = convertToThreadData; exports.convertToUserSubscriptionData = convertToUserSubscriptionData; exports.createClient = createClient; exports.createCommentAttachmentId = createCommentAttachmentId; exports.createCommentId = createCommentId; exports.createInboxNotificationId = createInboxNotificationId; exports.createManagedPool = createManagedPool; exports.createNotificationSettings = createNotificationSettings; exports.createThreadId = createThreadId; exports.deepLiveify = deepLiveify; exports.defineAiTool = defineAiTool; exports.deprecate = deprecate; exports.deprecateIf = deprecateIf; exports.detectDupes = detectDupes; exports.entries = entries; exports.errorIf = errorIf; exports.findLastIndex = findLastIndex; exports.freeze = freeze; exports.generateUrl = generateUrl; exports.getMentionsFromCommentBody = getMentionsFromCommentBody; exports.getSubscriptionKey = getSubscriptionKey; exports.hasPermissionCapability = hasPermissionCapability; exports.hasPermissionCapabilityAccess = hasPermissionCapabilityAccess; exports.html = html; exports.htmlSafe = htmlSafe; exports.isCommentBodyLink = isCommentBodyLink; exports.isCommentBodyMention = isCommentBodyMention; exports.isCommentBodyText = isCommentBodyText; exports.isJsonArray = isJsonArray; exports.isJsonObject = isJsonObject; exports.isJsonScalar = isJsonScalar; exports.isListStorageNode = isListStorageNode; exports.isLiveNode = isLiveNode; exports.isMapStorageNode = isMapStorageNode; exports.isNotificationChannelEnabled = isNotificationChannelEnabled; exports.isNumberOperator = isNumberOperator; exports.isObjectStorageNode = isObjectStorageNode; exports.isPlainObject = isPlainObject; exports.isRegisterStorageNode = isRegisterStorageNode; exports.isRootStorageNode = isRootStorageNode; exports.isStartsWithOperator = isStartsWithOperator; exports.isUrl = isUrl; exports.kInternal = kInternal; exports.keys = keys; exports.makeAbortController = makeAbortController; exports.makeEventSource = makeEventSource; exports.makePoller = makePoller; exports.makePosition = makePosition; exports.mapValues = mapValues; exports.memoizeOnSuccess = memoizeOnSuccess; exports.mergePermissionCapabilities = mergePermissionCapabilities; exports.nanoid = nanoid; exports.nn = nn; exports.nodeStreamToCompactNodes = nodeStreamToCompactNodes; exports.normalizeRoomAccessesInput = normalizeRoomAccessesInput; exports.normalizeRoomAccessesUpdateInput = normalizeRoomAccessesUpdateInput; exports.normalizeRoomPermissionInput = normalizeRoomPermissionInput; exports.objectToQuery = objectToQuery; exports.patchNotificationSettings = patchNotificationSettings; exports.permissionCapabilitiesFromScopes = permissionCapabilitiesFromScopes; exports.permissionCapabilitiesToScopes = permissionCapabilitiesToScopes; exports.raise = raise; exports.resolveMentionsInCommentBody = resolveMentionsInCommentBody; exports.sanitizeUrl = sanitizeUrl; exports.shallow = shallow; exports.shallow2 = shallow2; exports.stableStringify = stableStringify; exports.stringifyCommentBody = stringifyCommentBody; exports.throwUsageError = throwUsageError; exports.toPlainLson = toPlainLson; exports.tryParseJson = tryParseJson; exports.url = url; exports.urljoin = urljoin; exports.wait = wait; exports.warnOnce = warnOnce; exports.warnOnceIf = warnOnceIf; exports.withTimeout = withTimeout;
12432
12746
  //# sourceMappingURL=index.cjs.map