@liveblocks/core 3.23.1 → 3.23.2-exp1

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.23.1";
9
+ var PKG_VERSION = "3.23.2-exp1";
10
10
  var PKG_FORMAT = "cjs";
11
11
 
12
12
  // src/dupe-detection.ts
@@ -988,14 +988,15 @@ var OpCode = Object.freeze({
988
988
  DELETE_OBJECT_KEY: 6,
989
989
  CREATE_MAP: 7,
990
990
  CREATE_REGISTER: 8,
991
- // TODO: 9 and 10 are used by LiveText, wait until it's merged.
991
+ CREATE_TEXT: 9,
992
+ UPDATE_TEXT: 10,
992
993
  CREATE_FILE: 11
993
994
  });
994
995
  function isIgnoredOp(op) {
995
996
  return op.type === OpCode.DELETE_CRDT && op.id === "ACK";
996
997
  }
997
998
  function isCreateOp(op) {
998
- return op.type === OpCode.CREATE_OBJECT || op.type === OpCode.CREATE_REGISTER || op.type === OpCode.CREATE_FILE || op.type === OpCode.CREATE_MAP || op.type === OpCode.CREATE_LIST;
999
+ return op.type === OpCode.CREATE_OBJECT || op.type === OpCode.CREATE_REGISTER || op.type === OpCode.CREATE_FILE || op.type === OpCode.CREATE_MAP || op.type === OpCode.CREATE_LIST || op.type === OpCode.CREATE_TEXT;
999
1000
  }
1000
1001
 
1001
1002
  // src/protocol/StorageNode.ts
@@ -1004,7 +1005,7 @@ var CrdtType = Object.freeze({
1004
1005
  LIST: 1,
1005
1006
  MAP: 2,
1006
1007
  REGISTER: 3,
1007
- // TODO: 4 is used by LiveText, wait until it's merged.
1008
+ TEXT: 4,
1008
1009
  FILE: 5
1009
1010
  });
1010
1011
  function isRootStorageNode(node) {
@@ -1022,6 +1023,9 @@ function isMapStorageNode(node) {
1022
1023
  function isRegisterStorageNode(node) {
1023
1024
  return node[1].type === CrdtType.REGISTER;
1024
1025
  }
1026
+ function isTextStorageNode(node) {
1027
+ return node[1].type === CrdtType.TEXT;
1028
+ }
1025
1029
  function isFileStorageNode(node) {
1026
1030
  return node[1].type === CrdtType.FILE;
1027
1031
  }
@@ -1047,6 +1051,9 @@ function* compactNodesToNodeStream(compactNodes) {
1047
1051
  case CrdtType.REGISTER:
1048
1052
  yield [cnode[0], { type: CrdtType.REGISTER, parentId: cnode[2], parentKey: cnode[3], data: cnode[4] }];
1049
1053
  break;
1054
+ case CrdtType.TEXT:
1055
+ yield [cnode[0], { type: CrdtType.TEXT, parentId: cnode[2], parentKey: cnode[3], data: cnode[4], version: cnode[5] }];
1056
+ break;
1050
1057
  case CrdtType.FILE:
1051
1058
  yield [cnode[0], { type: CrdtType.FILE, parentId: cnode[2], parentKey: cnode[3], data: cnode[4] }];
1052
1059
  break;
@@ -1078,6 +1085,17 @@ function* nodeStreamToCompactNodes(nodes) {
1078
1085
  const id = node[0];
1079
1086
  const crdt = node[1];
1080
1087
  yield [id, CrdtType.REGISTER, crdt.parentId, crdt.parentKey, crdt.data];
1088
+ } else if (isTextStorageNode(node)) {
1089
+ const id = node[0];
1090
+ const crdt = node[1];
1091
+ yield [
1092
+ id,
1093
+ CrdtType.TEXT,
1094
+ crdt.parentId,
1095
+ crdt.parentKey,
1096
+ crdt.data,
1097
+ crdt.version
1098
+ ];
1081
1099
  } else if (isFileStorageNode(node)) {
1082
1100
  const id = node[0];
1083
1101
  const crdt = node[1];
@@ -1087,6 +1105,9 @@ function* nodeStreamToCompactNodes(nodes) {
1087
1105
  }
1088
1106
  }
1089
1107
 
1108
+ // src/internal.ts
1109
+ var kInternal = /* @__PURE__ */ Symbol();
1110
+
1090
1111
  // src/lib/position.ts
1091
1112
  var MIN_CODE = 32;
1092
1113
  var MAX_CODE = 126;
@@ -1266,6 +1287,18 @@ function asPos(str) {
1266
1287
  return isPos(str) ? str : convertToPos(str);
1267
1288
  }
1268
1289
 
1290
+ // src/crdts/StorageUpdates.ts
1291
+ var REMOTE = freeze({ origin: "remote" });
1292
+ var LOCAL_EDIT = freeze({ origin: "local", via: "edit" });
1293
+ var LOCAL_UNDO = freeze({ origin: "local", via: "undo" });
1294
+ var LOCAL_REDO = freeze({ origin: "local", via: "redo" });
1295
+ function toUpdateSource(source) {
1296
+ return source.origin === "remote" ? source : (
1297
+ // Removes `optimistic` field, which is not public
1298
+ { origin: "local", via: source.via }
1299
+ );
1300
+ }
1301
+
1269
1302
  // src/crdts/UnacknowledgedOps.ts
1270
1303
  var UnacknowledgedOps = class {
1271
1304
  // opId -> op
@@ -1284,6 +1317,10 @@ ${parentKey}`;
1284
1317
  get size() {
1285
1318
  return this.#byOpId.size;
1286
1319
  }
1320
+ /** The still-unacknowledged op with the given opId, if any. */
1321
+ get(opId) {
1322
+ return this.#byOpId.get(opId);
1323
+ }
1287
1324
  /**
1288
1325
  * Mark the given Op as still unacknowledged.
1289
1326
  */
@@ -1383,8 +1420,8 @@ function createManagedPool(options) {
1383
1420
  deleteNode: (id) => void nodes.delete(id),
1384
1421
  generateId: () => `${getCurrentConnectionId()}:${clock++}`,
1385
1422
  generateOpId: () => `${getCurrentConnectionId()}:${opClock++}`,
1386
- dispatch(ops, reverse, storageUpdates) {
1387
- _optionalChain([onDispatch, 'optionalCall', _23 => _23(ops, reverse, storageUpdates)]);
1423
+ dispatch(ops, reverse, storageUpdates, options2) {
1424
+ _optionalChain([onDispatch, 'optionalCall', _23 => _23(ops, reverse, storageUpdates, options2)]);
1388
1425
  },
1389
1426
  assertStorageIsWritable: () => {
1390
1427
  if (!isStorageWritable()) {
@@ -1407,10 +1444,17 @@ function Orphaned(oldKey, oldPos = asPos(oldKey)) {
1407
1444
  return Object.freeze({ type: "Orphaned", oldKey, oldPos });
1408
1445
  }
1409
1446
  var AbstractCrdt = class {
1410
- // ^^^^^^^^^^^^ TODO: Make this an interface
1411
1447
  #pool;
1412
1448
  #id;
1413
1449
  #parent = NoParent;
1450
+ constructor() {
1451
+ Object.defineProperty(this, kInternal, {
1452
+ value: {
1453
+ getId: () => this.#id
1454
+ },
1455
+ enumerable: false
1456
+ });
1457
+ }
1414
1458
  /** @internal */
1415
1459
  _getParentKeyOrThrow() {
1416
1460
  switch (this.parent.type) {
@@ -1463,11 +1507,14 @@ var AbstractCrdt = class {
1463
1507
  }
1464
1508
  }
1465
1509
  /** @internal */
1466
- _apply(op, _isLocal) {
1510
+ _apply(op, source) {
1467
1511
  switch (op.type) {
1468
1512
  case OpCode.DELETE_CRDT: {
1469
1513
  if (this.parent.type === "HasParent") {
1470
- return this.parent.node._detachChild(crdtAsLiveNode(this));
1514
+ return this.parent.node._detachChild(
1515
+ crdtAsLiveNode(this),
1516
+ toUpdateSource(source)
1517
+ );
1471
1518
  }
1472
1519
  return { modified: false };
1473
1520
  }
@@ -1657,8 +1704,8 @@ var LiveFile = class _LiveFile extends AbstractCrdt {
1657
1704
  throw new Error("A LiveFile node cannot have children");
1658
1705
  }
1659
1706
  /** @internal */
1660
- _apply(op, isLocal) {
1661
- return super._apply(op, isLocal);
1707
+ _apply(op, source) {
1708
+ return super._apply(op, source);
1662
1709
  }
1663
1710
  /** @internal */
1664
1711
  _toTreeNode(key) {
@@ -4877,9 +4924,6 @@ var ManagedSocket = class {
4877
4924
  }
4878
4925
  };
4879
4926
 
4880
- // src/internal.ts
4881
- var kInternal = /* @__PURE__ */ Symbol();
4882
-
4883
4927
  // src/lib/IncrementalJsonParser.ts
4884
4928
  var EMPTY_OBJECT = Object.freeze({});
4885
4929
  var NULL_KEYWORD_CHARS = Array.from(new Set("null"));
@@ -6881,8 +6925,8 @@ var LiveRegister = class _LiveRegister extends AbstractCrdt {
6881
6925
  throw new Error("Method not implemented.");
6882
6926
  }
6883
6927
  /** @internal */
6884
- _apply(op, isLocal) {
6885
- return super._apply(op, isLocal);
6928
+ _apply(op, source) {
6929
+ return super._apply(op, source);
6886
6930
  }
6887
6931
  /** @internal */
6888
6932
  _toTreeNode(key) {
@@ -7043,7 +7087,7 @@ var LiveList = class _LiveList extends AbstractCrdt {
7043
7087
  item._detach();
7044
7088
  }
7045
7089
  }
7046
- #applySetRemote(op) {
7090
+ #applyRemoteSet(op) {
7047
7091
  if (this._pool === void 0) {
7048
7092
  throw new Error("Can't attach child if managed pool is not present");
7049
7093
  }
@@ -7061,9 +7105,11 @@ var LiveList = class _LiveList extends AbstractCrdt {
7061
7105
  itemWithSamePosition._detach();
7062
7106
  this.#items.add(child);
7063
7107
  return {
7064
- modified: makeUpdate(this, [
7065
- setDelta(indexOfItemWithSamePosition, child)
7066
- ]),
7108
+ modified: makeUpdate(
7109
+ this,
7110
+ [setDelta(indexOfItemWithSamePosition, child)],
7111
+ REMOTE
7112
+ ),
7067
7113
  reverse: []
7068
7114
  };
7069
7115
  } else {
@@ -7074,20 +7120,22 @@ var LiveList = class _LiveList extends AbstractCrdt {
7074
7120
  setDelta(indexOfItemWithSamePosition, child)
7075
7121
  ];
7076
7122
  const deleteDelta2 = this.#detachItemAssociatedToSetOperation(
7077
- op.deletedId
7123
+ op.deletedId,
7124
+ REMOTE
7078
7125
  );
7079
7126
  if (deleteDelta2) {
7080
7127
  delta.push(deleteDelta2);
7081
7128
  }
7082
7129
  return {
7083
- modified: makeUpdate(this, delta),
7130
+ modified: makeUpdate(this, delta, REMOTE),
7084
7131
  reverse: []
7085
7132
  };
7086
7133
  }
7087
7134
  } else {
7088
7135
  const updates = [];
7089
7136
  const deleteDelta2 = this.#detachItemAssociatedToSetOperation(
7090
- op.deletedId
7137
+ op.deletedId,
7138
+ REMOTE
7091
7139
  );
7092
7140
  if (deleteDelta2) {
7093
7141
  updates.push(deleteDelta2);
@@ -7096,29 +7144,32 @@ var LiveList = class _LiveList extends AbstractCrdt {
7096
7144
  updates.push(insertDelta(this._indexOfPosition(key), child));
7097
7145
  return {
7098
7146
  reverse: [],
7099
- modified: makeUpdate(this, updates)
7147
+ modified: makeUpdate(this, updates, REMOTE)
7100
7148
  };
7101
7149
  }
7102
7150
  }
7103
- #applySetAck(op) {
7151
+ #applySetAck(op, source) {
7104
7152
  if (this._pool === void 0) {
7105
7153
  throw new Error("Can't attach child if managed pool is not present");
7106
7154
  }
7107
7155
  const delta = [];
7108
- const deletedDelta = this.#detachItemAssociatedToSetOperation(op.deletedId);
7156
+ const deletedDelta = this.#detachItemAssociatedToSetOperation(
7157
+ op.deletedId,
7158
+ source
7159
+ );
7109
7160
  if (deletedDelta) {
7110
7161
  delta.push(deletedDelta);
7111
7162
  }
7112
7163
  const unacknowledgedOpId = this.#unacknowledgedSetOpIdAt(op.parentKey);
7113
7164
  if (unacknowledgedOpId !== void 0 && unacknowledgedOpId !== op.opId) {
7114
- return delta.length === 0 ? { modified: false } : { modified: makeUpdate(this, delta), reverse: [] };
7165
+ return delta.length === 0 ? { modified: false } : { modified: makeUpdate(this, delta, source), reverse: [] };
7115
7166
  }
7116
7167
  const indexOfItemWithSamePosition = this._indexOfPosition(op.parentKey);
7117
7168
  const existingItem = this.#items.find((item) => item._id === op.id);
7118
7169
  if (existingItem !== void 0) {
7119
7170
  if (existingItem._parentKey === op.parentKey) {
7120
7171
  return {
7121
- modified: delta.length > 0 ? makeUpdate(this, delta) : false,
7172
+ modified: delta.length > 0 ? makeUpdate(this, delta, source) : false,
7122
7173
  reverse: []
7123
7174
  };
7124
7175
  }
@@ -7136,7 +7187,7 @@ var LiveList = class _LiveList extends AbstractCrdt {
7136
7187
  delta.push(moveDelta(prevIndex, newIndex, existingItem));
7137
7188
  }
7138
7189
  return {
7139
- modified: delta.length > 0 ? makeUpdate(this, delta) : false,
7190
+ modified: delta.length > 0 ? makeUpdate(this, delta, source) : false,
7140
7191
  reverse: []
7141
7192
  };
7142
7193
  } else {
@@ -7146,11 +7197,15 @@ var LiveList = class _LiveList extends AbstractCrdt {
7146
7197
  this.#implicitlyDeletedItems.delete(orphan);
7147
7198
  const recreatedItemIndex = this.#insert(orphan);
7148
7199
  return {
7149
- modified: makeUpdate(this, [
7150
- // If there is an item at this position, update is a set, else it's an insert
7151
- indexOfItemWithSamePosition === -1 ? insertDelta(recreatedItemIndex, orphan) : setDelta(recreatedItemIndex, orphan),
7152
- ...delta
7153
- ]),
7200
+ modified: makeUpdate(
7201
+ this,
7202
+ [
7203
+ // If there is an item at this position, update is a set, else it's an insert
7204
+ indexOfItemWithSamePosition === -1 ? insertDelta(recreatedItemIndex, orphan) : setDelta(recreatedItemIndex, orphan),
7205
+ ...delta
7206
+ ],
7207
+ source
7208
+ ),
7154
7209
  reverse: []
7155
7210
  };
7156
7211
  } else {
@@ -7165,11 +7220,15 @@ var LiveList = class _LiveList extends AbstractCrdt {
7165
7220
  op.parentKey
7166
7221
  );
7167
7222
  return {
7168
- modified: makeUpdate(this, [
7169
- // If there is an item at this position, update is a set, else it's an insert
7170
- indexOfItemWithSamePosition === -1 ? insertDelta(newIndex, newItem) : setDelta(newIndex, newItem),
7171
- ...delta
7172
- ]),
7223
+ modified: makeUpdate(
7224
+ this,
7225
+ [
7226
+ // If there is an item at this position, update is a set, else it's an insert
7227
+ indexOfItemWithSamePosition === -1 ? insertDelta(newIndex, newItem) : setDelta(newIndex, newItem),
7228
+ ...delta
7229
+ ],
7230
+ source
7231
+ ),
7173
7232
  reverse: []
7174
7233
  };
7175
7234
  }
@@ -7178,7 +7237,7 @@ var LiveList = class _LiveList extends AbstractCrdt {
7178
7237
  /**
7179
7238
  * Returns the update delta of the deletion or null
7180
7239
  */
7181
- #detachItemAssociatedToSetOperation(deletedId) {
7240
+ #detachItemAssociatedToSetOperation(deletedId, source) {
7182
7241
  if (deletedId === void 0 || this._pool === void 0) {
7183
7242
  return null;
7184
7243
  }
@@ -7186,7 +7245,7 @@ var LiveList = class _LiveList extends AbstractCrdt {
7186
7245
  if (deletedItem === void 0) {
7187
7246
  return null;
7188
7247
  }
7189
- const result = this._detachChild(deletedItem);
7248
+ const result = this._detachChild(deletedItem, source);
7190
7249
  if (result.modified === false) {
7191
7250
  return null;
7192
7251
  }
@@ -7204,10 +7263,11 @@ var LiveList = class _LiveList extends AbstractCrdt {
7204
7263
  const { newItem, newIndex } = this.#createAttachItemAndSort(op, key);
7205
7264
  const bumpDeltas = this.#bumpUnackedPushesAbove(key);
7206
7265
  return {
7207
- modified: makeUpdate(this, [
7208
- insertDelta(newIndex, newItem),
7209
- ...bumpDeltas
7210
- ]),
7266
+ modified: makeUpdate(
7267
+ this,
7268
+ [insertDelta(newIndex, newItem), ...bumpDeltas],
7269
+ REMOTE
7270
+ ),
7211
7271
  reverse: []
7212
7272
  };
7213
7273
  }
@@ -7288,7 +7348,7 @@ var LiveList = class _LiveList extends AbstractCrdt {
7288
7348
  }
7289
7349
  return deltas;
7290
7350
  }
7291
- #applyInsertAck(op) {
7351
+ #applyInsertAck(op, source) {
7292
7352
  const existingItem = this.#items.find((item) => item._id === op.id);
7293
7353
  const key = asPos(op.parentKey);
7294
7354
  const itemIndexAtPosition = this._indexOfPosition(key);
@@ -7310,9 +7370,11 @@ var LiveList = class _LiveList extends AbstractCrdt {
7310
7370
  return { modified: false };
7311
7371
  }
7312
7372
  return {
7313
- modified: makeUpdate(this, [
7314
- moveDelta(oldPositionIndex, newIndex, existingItem)
7315
- ]),
7373
+ modified: makeUpdate(
7374
+ this,
7375
+ [moveDelta(oldPositionIndex, newIndex, existingItem)],
7376
+ source
7377
+ ),
7316
7378
  reverse: []
7317
7379
  };
7318
7380
  }
@@ -7324,7 +7386,7 @@ var LiveList = class _LiveList extends AbstractCrdt {
7324
7386
  this.#insert(orphan);
7325
7387
  const newIndex = this._indexOfPosition(key);
7326
7388
  return {
7327
- modified: makeUpdate(this, [insertDelta(newIndex, orphan)]),
7389
+ modified: makeUpdate(this, [insertDelta(newIndex, orphan)], source),
7328
7390
  reverse: []
7329
7391
  };
7330
7392
  } else {
@@ -7333,13 +7395,13 @@ var LiveList = class _LiveList extends AbstractCrdt {
7333
7395
  }
7334
7396
  const { newItem, newIndex } = this.#createAttachItemAndSort(op, key);
7335
7397
  return {
7336
- modified: makeUpdate(this, [insertDelta(newIndex, newItem)]),
7398
+ modified: makeUpdate(this, [insertDelta(newIndex, newItem)], source),
7337
7399
  reverse: []
7338
7400
  };
7339
7401
  }
7340
7402
  }
7341
7403
  }
7342
- #applyInsertUndoRedo(op) {
7404
+ #applyLocalInsert(op, source) {
7343
7405
  const { id, parentKey: key } = op;
7344
7406
  const child = creationOpToLiveNode(op);
7345
7407
  if (_optionalChain([this, 'access', _151 => _151._pool, 'optionalAccess', _152 => _152.getNode, 'call', _153 => _153(id)]) !== void 0) {
@@ -7358,11 +7420,11 @@ var LiveList = class _LiveList extends AbstractCrdt {
7358
7420
  this.#insert(child);
7359
7421
  const newIndex = this._indexOfPosition(newKey);
7360
7422
  return {
7361
- modified: makeUpdate(this, [insertDelta(newIndex, child)]),
7423
+ modified: makeUpdate(this, [insertDelta(newIndex, child)], source),
7362
7424
  reverse: [{ type: OpCode.DELETE_CRDT, id }]
7363
7425
  };
7364
7426
  }
7365
- #applySetUndoRedo(op) {
7427
+ #applyLocalSet(op, source) {
7366
7428
  const { id, parentKey: key } = op;
7367
7429
  const child = creationOpToLiveNode(op);
7368
7430
  if (_optionalChain([this, 'access', _162 => _162._pool, 'optionalAccess', _163 => _163.getNode, 'call', _164 => _164(id)]) !== void 0) {
@@ -7384,46 +7446,48 @@ var LiveList = class _LiveList extends AbstractCrdt {
7384
7446
  );
7385
7447
  const delta = [setDelta(indexOfItemWithSameKey, child)];
7386
7448
  const deletedDelta = this.#detachItemAssociatedToSetOperation(
7387
- op.deletedId
7449
+ op.deletedId,
7450
+ source
7388
7451
  );
7389
7452
  if (deletedDelta) {
7390
7453
  delta.push(deletedDelta);
7391
7454
  }
7392
7455
  return {
7393
- modified: makeUpdate(this, delta),
7456
+ modified: makeUpdate(this, delta, source),
7394
7457
  reverse
7395
7458
  };
7396
7459
  } else {
7397
7460
  this.#insert(child);
7398
- this.#detachItemAssociatedToSetOperation(op.deletedId);
7461
+ this.#detachItemAssociatedToSetOperation(op.deletedId, source);
7399
7462
  const newIndex = this._indexOfPosition(newKey);
7400
7463
  return {
7401
7464
  reverse: [{ type: OpCode.DELETE_CRDT, id }],
7402
- modified: makeUpdate(this, [insertDelta(newIndex, child)])
7465
+ modified: makeUpdate(this, [insertDelta(newIndex, child)], source)
7403
7466
  };
7404
7467
  }
7405
7468
  }
7406
7469
  /** @internal */
7407
- _attachChild(op, source) {
7470
+ _attachChild(op, opSource) {
7471
+ const source = toUpdateSource(opSource);
7408
7472
  if (this._pool === void 0) {
7409
7473
  throw new Error("Can't attach child if managed pool is not present");
7410
7474
  }
7411
7475
  let result;
7412
7476
  if (op.intent === "set") {
7413
- if (source === 1 /* THEIRS */) {
7414
- result = this.#applySetRemote(op);
7415
- } else if (source === 2 /* OURS */) {
7416
- result = this.#applySetAck(op);
7477
+ if (opSource.origin === "remote") {
7478
+ result = this.#applyRemoteSet(op);
7479
+ } else if (!opSource.optimistic) {
7480
+ result = this.#applySetAck(op, source);
7417
7481
  } else {
7418
- result = this.#applySetUndoRedo(op);
7482
+ result = this.#applyLocalSet(op, source);
7419
7483
  }
7420
7484
  } else {
7421
- if (source === 1 /* THEIRS */) {
7485
+ if (opSource.origin === "remote") {
7422
7486
  result = this.#applyRemoteInsert(op);
7423
- } else if (source === 2 /* OURS */) {
7424
- result = this.#applyInsertAck(op);
7487
+ } else if (!opSource.optimistic) {
7488
+ result = this.#applyInsertAck(op, source);
7425
7489
  } else {
7426
- result = this.#applyInsertUndoRedo(op);
7490
+ result = this.#applyLocalInsert(op, source);
7427
7491
  }
7428
7492
  }
7429
7493
  if (result.modified !== false) {
@@ -7432,7 +7496,7 @@ var LiveList = class _LiveList extends AbstractCrdt {
7432
7496
  return result;
7433
7497
  }
7434
7498
  /** @internal */
7435
- _detachChild(child) {
7499
+ _detachChild(child, source) {
7436
7500
  if (child) {
7437
7501
  const parentKey = nn(child._parentKey);
7438
7502
  const reverse = child._toOps(nn(this._id), parentKey);
@@ -7447,19 +7511,23 @@ var LiveList = class _LiveList extends AbstractCrdt {
7447
7511
  this.invalidate();
7448
7512
  child._detach();
7449
7513
  return {
7450
- modified: makeUpdate(this, [deleteDelta(indexToDelete, previousNode)]),
7514
+ modified: makeUpdate(
7515
+ this,
7516
+ [deleteDelta(indexToDelete, previousNode)],
7517
+ source
7518
+ ),
7451
7519
  reverse
7452
7520
  };
7453
7521
  }
7454
7522
  return { modified: false };
7455
7523
  }
7456
- #applySetChildKeyRemote(newKey, child) {
7524
+ #applyRemoteSetChildKey(newKey, child) {
7457
7525
  if (this.#implicitlyDeletedItems.has(child)) {
7458
7526
  this.#implicitlyDeletedItems.delete(child);
7459
7527
  child._setParentLink(this, newKey);
7460
7528
  const newIndex = this.#insert(child);
7461
7529
  return {
7462
- modified: makeUpdate(this, [insertDelta(newIndex, child)]),
7530
+ modified: makeUpdate(this, [insertDelta(newIndex, child)], REMOTE),
7463
7531
  reverse: []
7464
7532
  };
7465
7533
  }
@@ -7480,7 +7548,11 @@ var LiveList = class _LiveList extends AbstractCrdt {
7480
7548
  };
7481
7549
  }
7482
7550
  return {
7483
- modified: makeUpdate(this, [moveDelta(previousIndex, newIndex, child)]),
7551
+ modified: makeUpdate(
7552
+ this,
7553
+ [moveDelta(previousIndex, newIndex, child)],
7554
+ REMOTE
7555
+ ),
7484
7556
  reverse: []
7485
7557
  };
7486
7558
  } else {
@@ -7497,12 +7569,16 @@ var LiveList = class _LiveList extends AbstractCrdt {
7497
7569
  };
7498
7570
  }
7499
7571
  return {
7500
- modified: makeUpdate(this, [moveDelta(previousIndex, newIndex, child)]),
7572
+ modified: makeUpdate(
7573
+ this,
7574
+ [moveDelta(previousIndex, newIndex, child)],
7575
+ REMOTE
7576
+ ),
7501
7577
  reverse: []
7502
7578
  };
7503
7579
  }
7504
7580
  }
7505
- #applySetChildKeyAck(newKey, child) {
7581
+ #applySetChildKeyAck(newKey, child, source) {
7506
7582
  const previousKey = nn(child._parentKey);
7507
7583
  if (this.#implicitlyDeletedItems.has(child)) {
7508
7584
  const existingItemIndex = this._indexOfPosition(newKey);
@@ -7521,7 +7597,7 @@ var LiveList = class _LiveList extends AbstractCrdt {
7521
7597
  child._setParentLink(this, newKey);
7522
7598
  const newIndex = this.#insert(child);
7523
7599
  return {
7524
- modified: makeUpdate(this, [insertDelta(newIndex, child)]),
7600
+ modified: makeUpdate(this, [insertDelta(newIndex, child)], source),
7525
7601
  reverse: []
7526
7602
  };
7527
7603
  } else {
@@ -7549,15 +7625,17 @@ var LiveList = class _LiveList extends AbstractCrdt {
7549
7625
  };
7550
7626
  } else {
7551
7627
  return {
7552
- modified: makeUpdate(this, [
7553
- moveDelta(previousIndex, newIndex, child)
7554
- ]),
7628
+ modified: makeUpdate(
7629
+ this,
7630
+ [moveDelta(previousIndex, newIndex, child)],
7631
+ source
7632
+ ),
7555
7633
  reverse: []
7556
7634
  };
7557
7635
  }
7558
7636
  }
7559
7637
  }
7560
- #applySetChildKeyUndoRedo(newKey, child) {
7638
+ #applyLocalSetChildKey(newKey, child, source) {
7561
7639
  const previousKey = nn(child._parentKey);
7562
7640
  const previousIndex = this.#items.findIndex((item) => item === child);
7563
7641
  const existingItemIndex = this._indexOfPosition(newKey);
@@ -7576,7 +7654,11 @@ var LiveList = class _LiveList extends AbstractCrdt {
7576
7654
  };
7577
7655
  }
7578
7656
  return {
7579
- modified: makeUpdate(this, [moveDelta(previousIndex, newIndex, child)]),
7657
+ modified: makeUpdate(
7658
+ this,
7659
+ [moveDelta(previousIndex, newIndex, child)],
7660
+ source
7661
+ ),
7580
7662
  reverse: [
7581
7663
  {
7582
7664
  type: OpCode.SET_PARENT_KEY,
@@ -7587,18 +7669,19 @@ var LiveList = class _LiveList extends AbstractCrdt {
7587
7669
  };
7588
7670
  }
7589
7671
  /** @internal */
7590
- _setChildKey(newKey, child, source) {
7591
- if (source === 1 /* THEIRS */) {
7592
- return this.#applySetChildKeyRemote(newKey, child);
7593
- } else if (source === 2 /* OURS */) {
7594
- return this.#applySetChildKeyAck(newKey, child);
7672
+ _setChildKey(newKey, child, opSource) {
7673
+ const source = toUpdateSource(opSource);
7674
+ if (opSource.origin === "remote") {
7675
+ return this.#applyRemoteSetChildKey(newKey, child);
7676
+ } else if (!opSource.optimistic) {
7677
+ return this.#applySetChildKeyAck(newKey, child, source);
7595
7678
  } else {
7596
- return this.#applySetChildKeyUndoRedo(newKey, child);
7679
+ return this.#applyLocalSetChildKey(newKey, child, source);
7597
7680
  }
7598
7681
  }
7599
7682
  /** @internal */
7600
- _apply(op, isLocal) {
7601
- return super._apply(op, isLocal);
7683
+ _apply(op, source) {
7684
+ return super._apply(op, source);
7602
7685
  }
7603
7686
  /** @internal */
7604
7687
  _serialize() {
@@ -7659,7 +7742,7 @@ var LiveList = class _LiveList extends AbstractCrdt {
7659
7742
  intent === "push" ? addIntentToRootOp(ops, "push") : ops,
7660
7743
  [{ type: OpCode.DELETE_CRDT, id }],
7661
7744
  /* @__PURE__ */ new Map([
7662
- [this._id, makeUpdate(this, [insertDelta(index, value)])]
7745
+ [this._id, makeUpdate(this, [insertDelta(index, value)], LOCAL_EDIT)]
7663
7746
  ])
7664
7747
  );
7665
7748
  }
@@ -7700,7 +7783,10 @@ var LiveList = class _LiveList extends AbstractCrdt {
7700
7783
  this.#updateItemPositionAt(index, position);
7701
7784
  if (this._pool && this._id) {
7702
7785
  const storageUpdates = /* @__PURE__ */ new Map([
7703
- [this._id, makeUpdate(this, [moveDelta(index, targetIndex, item)])]
7786
+ [
7787
+ this._id,
7788
+ makeUpdate(this, [moveDelta(index, targetIndex, item)], LOCAL_EDIT)
7789
+ ]
7704
7790
  ]);
7705
7791
  this._pool.dispatch(
7706
7792
  [
@@ -7743,7 +7829,7 @@ var LiveList = class _LiveList extends AbstractCrdt {
7743
7829
  const storageUpdates = /* @__PURE__ */ new Map();
7744
7830
  storageUpdates.set(
7745
7831
  nn(this._id),
7746
- makeUpdate(this, [deleteDelta(index, item)])
7832
+ makeUpdate(this, [deleteDelta(index, item)], LOCAL_EDIT)
7747
7833
  );
7748
7834
  this._pool.dispatch(
7749
7835
  [
@@ -7783,7 +7869,10 @@ var LiveList = class _LiveList extends AbstractCrdt {
7783
7869
  this.#items.clear();
7784
7870
  this.invalidate();
7785
7871
  const storageUpdates = /* @__PURE__ */ new Map();
7786
- storageUpdates.set(nn(this._id), makeUpdate(this, updateDelta));
7872
+ storageUpdates.set(
7873
+ nn(this._id),
7874
+ makeUpdate(this, updateDelta, LOCAL_EDIT)
7875
+ );
7787
7876
  this._pool.dispatch(ops, reverseOps, storageUpdates);
7788
7877
  } else {
7789
7878
  for (const item of this.#items) {
@@ -7813,7 +7902,10 @@ var LiveList = class _LiveList extends AbstractCrdt {
7813
7902
  const id = this._pool.generateId();
7814
7903
  value._attach(id, this._pool);
7815
7904
  const storageUpdates = /* @__PURE__ */ new Map();
7816
- storageUpdates.set(this._id, makeUpdate(this, [setDelta(index, value)]));
7905
+ storageUpdates.set(
7906
+ this._id,
7907
+ makeUpdate(this, [setDelta(index, value)], LOCAL_EDIT)
7908
+ );
7817
7909
  const ops = addIntentToRootOp(
7818
7910
  value._toOpsWithOpId(this._id, position, this._pool),
7819
7911
  "set",
@@ -7986,11 +8078,12 @@ var LiveList = class _LiveList extends AbstractCrdt {
7986
8078
  );
7987
8079
  }
7988
8080
  };
7989
- function makeUpdate(liveList, deltaUpdates) {
8081
+ function makeUpdate(liveList, deltaUpdates, source) {
7990
8082
  return {
7991
8083
  node: liveList,
7992
8084
  type: "LiveList",
7993
- updates: deltaUpdates
8085
+ updates: deltaUpdates,
8086
+ source
7994
8087
  };
7995
8088
  }
7996
8089
  function setDelta(index, item) {
@@ -8109,7 +8202,9 @@ var LiveMap = class _LiveMap extends AbstractCrdt {
8109
8202
  if (this._pool.getNode(id) !== void 0) {
8110
8203
  return { modified: false };
8111
8204
  }
8112
- if (source === 2 /* OURS */) {
8205
+ if (source.origin === "remote") {
8206
+ this.#unacknowledgedSet.delete(key);
8207
+ } else if (!source.optimistic) {
8113
8208
  const lastUpdateOpId = this.#unacknowledgedSet.get(key);
8114
8209
  if (lastUpdateOpId === opId) {
8115
8210
  this.#unacknowledgedSet.delete(key);
@@ -8117,8 +8212,6 @@ var LiveMap = class _LiveMap extends AbstractCrdt {
8117
8212
  } else if (lastUpdateOpId !== void 0) {
8118
8213
  return { modified: false };
8119
8214
  }
8120
- } else if (source === 1 /* THEIRS */) {
8121
- this.#unacknowledgedSet.delete(key);
8122
8215
  }
8123
8216
  const previousValue = this.#map.get(key);
8124
8217
  let reverse;
@@ -8137,7 +8230,8 @@ var LiveMap = class _LiveMap extends AbstractCrdt {
8137
8230
  modified: {
8138
8231
  node: this,
8139
8232
  type: "LiveMap",
8140
- updates: { [key]: { type: "update" } }
8233
+ updates: { [key]: { type: "update" } },
8234
+ source: toUpdateSource(source)
8141
8235
  },
8142
8236
  reverse
8143
8237
  };
@@ -8150,7 +8244,7 @@ var LiveMap = class _LiveMap extends AbstractCrdt {
8150
8244
  }
8151
8245
  }
8152
8246
  /** @internal */
8153
- _detachChild(child) {
8247
+ _detachChild(child, source) {
8154
8248
  const id = nn(this._id);
8155
8249
  const parentKey = nn(child._parentKey);
8156
8250
  const reverse = child._toOps(id, parentKey);
@@ -8169,7 +8263,8 @@ var LiveMap = class _LiveMap extends AbstractCrdt {
8169
8263
  type: "delete",
8170
8264
  deletedItem: liveNodeToLson(child)
8171
8265
  }
8172
- }
8266
+ },
8267
+ source
8173
8268
  };
8174
8269
  return { modified: storageUpdate, reverse };
8175
8270
  }
@@ -8218,7 +8313,8 @@ var LiveMap = class _LiveMap extends AbstractCrdt {
8218
8313
  storageUpdates.set(this._id, {
8219
8314
  node: this,
8220
8315
  type: "LiveMap",
8221
- updates: { [key]: { type: "update" } }
8316
+ updates: { [key]: { type: "update" } },
8317
+ source: LOCAL_EDIT
8222
8318
  });
8223
8319
  const ops = item._toOpsWithOpId(this._id, key, this._pool);
8224
8320
  this.#unacknowledgedSet.set(key, nn(ops[0].opId));
@@ -8267,7 +8363,8 @@ var LiveMap = class _LiveMap extends AbstractCrdt {
8267
8363
  type: "delete",
8268
8364
  deletedItem: liveNodeToLson(item)
8269
8365
  }
8270
- }
8366
+ },
8367
+ source: LOCAL_EDIT
8271
8368
  });
8272
8369
  this._pool.dispatch(
8273
8370
  [
@@ -8632,7 +8729,7 @@ var LiveObject = (_class2 = class _LiveObject extends AbstractCrdt {
8632
8729
  }
8633
8730
  return { modified: false };
8634
8731
  }
8635
- if (source === 0 /* LOCAL */) {
8732
+ if (source.origin === "local" && source.optimistic) {
8636
8733
  this.#unackedOpsByKey.set(key, nn(opId));
8637
8734
  } else if (this.#unackedOpsByKey.get(key) === void 0) {
8638
8735
  } else if (this.#unackedOpsByKey.get(key) === opId) {
@@ -8670,12 +8767,13 @@ var LiveObject = (_class2 = class _LiveObject extends AbstractCrdt {
8670
8767
  modified: {
8671
8768
  node: this,
8672
8769
  type: "LiveObject",
8673
- updates: { [key]: { type: "update" } }
8770
+ updates: { [key]: { type: "update" } },
8771
+ source: toUpdateSource(source)
8674
8772
  }
8675
8773
  };
8676
8774
  }
8677
8775
  /** @internal */
8678
- _detachChild(child) {
8776
+ _detachChild(child, source) {
8679
8777
  if (child) {
8680
8778
  const id = nn(this._id);
8681
8779
  const parentKey = nn(child._parentKey);
@@ -8693,7 +8791,8 @@ var LiveObject = (_class2 = class _LiveObject extends AbstractCrdt {
8693
8791
  type: "LiveObject",
8694
8792
  updates: {
8695
8793
  [parentKey]: { type: "delete", deletedItem }
8696
- }
8794
+ },
8795
+ source
8697
8796
  };
8698
8797
  return { modified: storageUpdate, reverse };
8699
8798
  }
@@ -8709,13 +8808,13 @@ var LiveObject = (_class2 = class _LiveObject extends AbstractCrdt {
8709
8808
  }
8710
8809
  }
8711
8810
  /** @internal */
8712
- _apply(op, isLocal) {
8811
+ _apply(op, source) {
8713
8812
  if (op.type === OpCode.UPDATE_OBJECT) {
8714
- return this.#applyUpdate(op, isLocal);
8813
+ return this.#applyUpdate(op, source);
8715
8814
  } else if (op.type === OpCode.DELETE_OBJECT_KEY) {
8716
- return this.#applyDeleteObjectKey(op, isLocal);
8815
+ return this.#applyDeleteObjectKey(op, source);
8717
8816
  }
8718
- return super._apply(op, isLocal);
8817
+ return super._apply(op, source);
8719
8818
  }
8720
8819
  /** @internal */
8721
8820
  _serialize() {
@@ -8739,7 +8838,7 @@ var LiveObject = (_class2 = class _LiveObject extends AbstractCrdt {
8739
8838
  };
8740
8839
  }
8741
8840
  }
8742
- #applyUpdate(op, isLocal) {
8841
+ #applyUpdate(op, source) {
8743
8842
  let isModified = false;
8744
8843
  const id = nn(this._id);
8745
8844
  const reverse = [];
@@ -8767,7 +8866,7 @@ var LiveObject = (_class2 = class _LiveObject extends AbstractCrdt {
8767
8866
  if (value === void 0) {
8768
8867
  continue;
8769
8868
  }
8770
- if (isLocal) {
8869
+ if (source.origin === "local" && source.optimistic) {
8771
8870
  this.#unackedOpsByKey.set(key, nn(op.opId));
8772
8871
  } else if (this.#unackedOpsByKey.get(key) === void 0) {
8773
8872
  isModified = true;
@@ -8794,18 +8893,19 @@ var LiveObject = (_class2 = class _LiveObject extends AbstractCrdt {
8794
8893
  modified: {
8795
8894
  node: this,
8796
8895
  type: "LiveObject",
8797
- updates: updateDelta
8896
+ updates: updateDelta,
8897
+ source: toUpdateSource(source)
8798
8898
  },
8799
8899
  reverse
8800
8900
  } : { modified: false };
8801
8901
  }
8802
- #applyDeleteObjectKey(op, isLocal) {
8902
+ #applyDeleteObjectKey(op, source) {
8803
8903
  const key = op.key;
8804
8904
  const oldValue = this.#synced.get(key);
8805
8905
  if (oldValue === void 0) {
8806
8906
  return { modified: false };
8807
8907
  }
8808
- if (!isLocal && this.#unackedOpsByKey.get(key) !== void 0) {
8908
+ if (!(source.origin === "local" && source.optimistic) && this.#unackedOpsByKey.get(key) !== void 0) {
8809
8909
  return { modified: false };
8810
8910
  }
8811
8911
  const id = nn(this._id);
@@ -8831,7 +8931,8 @@ var LiveObject = (_class2 = class _LiveObject extends AbstractCrdt {
8831
8931
  type: "LiveObject",
8832
8932
  updates: {
8833
8933
  [op.key]: { type: "delete", deletedItem: oldValue }
8834
- }
8934
+ },
8935
+ source: toUpdateSource(source)
8835
8936
  },
8836
8937
  reverse
8837
8938
  };
@@ -8877,7 +8978,8 @@ var LiveObject = (_class2 = class _LiveObject extends AbstractCrdt {
8877
8978
  updates: {
8878
8979
  ..._optionalChain([existing, 'optionalAccess', _228 => _228.updates]),
8879
8980
  [key]: { type: "update" }
8880
- }
8981
+ },
8982
+ source: LOCAL_EDIT
8881
8983
  });
8882
8984
  this._pool.dispatch(ops, reverse, storageUpdates);
8883
8985
  }
@@ -8911,7 +9013,8 @@ var LiveObject = (_class2 = class _LiveObject extends AbstractCrdt {
8911
9013
  type: "delete",
8912
9014
  deletedItem: oldValue2
8913
9015
  }
8914
- }
9016
+ },
9017
+ source: LOCAL_EDIT
8915
9018
  });
8916
9019
  return [[], [], storageUpdates2];
8917
9020
  }
@@ -8959,7 +9062,8 @@ var LiveObject = (_class2 = class _LiveObject extends AbstractCrdt {
8959
9062
  type: "LiveObject",
8960
9063
  updates: {
8961
9064
  [key]: { type: "delete", deletedItem: oldValue }
8962
- }
9065
+ },
9066
+ source: LOCAL_EDIT
8963
9067
  });
8964
9068
  return [ops, reverse, storageUpdates];
8965
9069
  }
@@ -9097,7 +9201,8 @@ var LiveObject = (_class2 = class _LiveObject extends AbstractCrdt {
9097
9201
  storageUpdates.set(this._id, {
9098
9202
  node: this,
9099
9203
  type: "LiveObject",
9100
- updates: updateDelta
9204
+ updates: updateDelta,
9205
+ source: LOCAL_EDIT
9101
9206
  });
9102
9207
  this._pool.dispatch(ops, reverseOps, storageUpdates);
9103
9208
  }
@@ -9179,181 +9284,1389 @@ var LiveObject = (_class2 = class _LiveObject extends AbstractCrdt {
9179
9284
  }
9180
9285
  }, _class2.__initStatic(), _class2);
9181
9286
 
9182
- // src/crdts/liveblocks-helpers.ts
9183
- function creationOpToLiveNode(op) {
9184
- return lsonToLiveNode(creationOpToLson(op));
9185
- }
9186
- function creationOpToLson(op) {
9187
- switch (op.type) {
9188
- case OpCode.CREATE_FILE:
9189
- return new LiveFile(op.data);
9190
- case OpCode.CREATE_REGISTER:
9191
- return op.data;
9192
- case OpCode.CREATE_OBJECT:
9193
- return new LiveObject(op.data);
9194
- case OpCode.CREATE_MAP:
9195
- return new LiveMap();
9196
- case OpCode.CREATE_LIST:
9197
- return new LiveList([]);
9198
- default:
9199
- return assertNever(op, "Unknown creation Op");
9200
- }
9201
- }
9202
- function isSameNodeOrChildOf(node, parent) {
9203
- if (node === parent) {
9287
+ // src/crdts/liveTextOps.ts
9288
+ function attributesEqual(left, right) {
9289
+ if (left === right) {
9204
9290
  return true;
9205
9291
  }
9206
- if (node.parent.type === "HasParent") {
9207
- return isSameNodeOrChildOf(node.parent.node, parent);
9292
+ if (left === void 0 || right === void 0) {
9293
+ return false;
9208
9294
  }
9209
- return false;
9210
- }
9211
- function liveObjectFromNodeStream(nodes) {
9212
- const pool = createManagedPool({
9213
- getCurrentConnectionId: () => {
9214
- throw new Error(
9215
- "Cannot mutate a historic storage version: it is a read-only snapshot"
9216
- );
9295
+ const leftKeys = Object.keys(left);
9296
+ const rightKeys = Object.keys(right);
9297
+ if (leftKeys.length !== rightKeys.length) {
9298
+ return false;
9299
+ }
9300
+ for (const key of leftKeys) {
9301
+ if (left[key] !== right[key]) {
9302
+ return false;
9217
9303
  }
9218
- });
9219
- return LiveObject._fromItems(nodes, pool);
9220
- }
9221
- function deserialize(node, parentToChildren, pool) {
9222
- if (isObjectStorageNode(node)) {
9223
- return LiveObject._deserialize(node, parentToChildren, pool);
9224
- } else if (isListStorageNode(node)) {
9225
- return LiveList._deserialize(node, parentToChildren, pool);
9226
- } else if (isMapStorageNode(node)) {
9227
- return LiveMap._deserialize(node, parentToChildren, pool);
9228
- } else if (isRegisterStorageNode(node)) {
9229
- return LiveRegister._deserialize(node, parentToChildren, pool);
9230
- } else if (isFileStorageNode(node)) {
9231
- return LiveFile._deserialize(node, parentToChildren, pool);
9232
- } else {
9233
- throw new Error("Unexpected CRDT type");
9234
9304
  }
9305
+ return true;
9235
9306
  }
9236
- function deserializeToLson(node, parentToChildren, pool) {
9237
- if (isObjectStorageNode(node)) {
9238
- return LiveObject._deserialize(node, parentToChildren, pool);
9239
- } else if (isListStorageNode(node)) {
9240
- return LiveList._deserialize(node, parentToChildren, pool);
9241
- } else if (isMapStorageNode(node)) {
9242
- return LiveMap._deserialize(node, parentToChildren, pool);
9243
- } else if (isRegisterStorageNode(node)) {
9244
- return node[1].data;
9245
- } else if (isFileStorageNode(node)) {
9246
- return LiveFile._deserialize(node, parentToChildren, pool);
9247
- } else {
9248
- throw new Error("Unexpected CRDT type");
9307
+ function cloneAttributes(attributes) {
9308
+ return attributes === void 0 ? void 0 : freeze({ ...attributes });
9309
+ }
9310
+ function normalizeSegments(segments) {
9311
+ const normalized = [];
9312
+ for (const segment of segments) {
9313
+ if (segment.text.length === 0) {
9314
+ continue;
9315
+ }
9316
+ const last = normalized.at(-1);
9317
+ const attributes = cloneAttributes(segment.attributes);
9318
+ if (last !== void 0 && attributesEqual(last.attributes, attributes)) {
9319
+ last.text += segment.text;
9320
+ } else {
9321
+ normalized.push({ text: segment.text, attributes });
9322
+ }
9249
9323
  }
9324
+ return normalized;
9250
9325
  }
9251
- function isLiveStructure(value) {
9252
- return isLiveList(value) || isLiveMap(value) || isLiveObject(value) || isLiveFile(value);
9326
+ function dataToSegments(data) {
9327
+ return normalizeSegments(
9328
+ data.map(([text, attributes]) => ({
9329
+ text,
9330
+ attributes
9331
+ }))
9332
+ );
9253
9333
  }
9254
- function isLiveNode(value) {
9255
- return isLiveStructure(value) || isLiveRegister(value);
9334
+ function segmentsToData(segments) {
9335
+ return segments.map(
9336
+ (segment) => segment.attributes === void 0 ? [segment.text] : [segment.text, { ...segment.attributes }]
9337
+ );
9256
9338
  }
9257
- function isLiveList(value) {
9258
- return value instanceof LiveList;
9339
+ function textLength(segments) {
9340
+ return segments.reduce((sum, segment) => sum + segment.text.length, 0);
9259
9341
  }
9260
- function isLiveMap(value) {
9261
- return value instanceof LiveMap;
9342
+ function splitSegmentsAt(segments, index) {
9343
+ const result = [];
9344
+ let offset = 0;
9345
+ for (const segment of segments) {
9346
+ const end = offset + segment.text.length;
9347
+ if (index > offset && index < end) {
9348
+ const before2 = segment.text.slice(0, index - offset);
9349
+ const after2 = segment.text.slice(index - offset);
9350
+ result.push({ text: before2, attributes: segment.attributes });
9351
+ result.push({ text: after2, attributes: segment.attributes });
9352
+ } else {
9353
+ result.push({ text: segment.text, attributes: segment.attributes });
9354
+ }
9355
+ offset = end;
9356
+ }
9357
+ return result;
9262
9358
  }
9263
- function isLiveObject(value) {
9264
- return value instanceof LiveObject;
9359
+ function clipRange(index, length, contentLength) {
9360
+ const clippedIndex = Math.max(0, Math.min(index, contentLength));
9361
+ const clippedEnd = Math.max(
9362
+ clippedIndex,
9363
+ Math.min(index + length, contentLength)
9364
+ );
9365
+ return { index: clippedIndex, length: clippedEnd - clippedIndex };
9265
9366
  }
9266
- function isLiveFile(value) {
9267
- return value instanceof LiveFile;
9367
+ function isInSurrogatePair(text, index) {
9368
+ const previous = text.charCodeAt(index - 1);
9369
+ const next = text.charCodeAt(index);
9370
+ return previous >= 55296 && previous <= 56319 && next >= 56320 && next <= 57343;
9268
9371
  }
9269
- function isLiveRegister(value) {
9270
- return value instanceof LiveRegister;
9372
+ function clipIndexToCodePointBoundary(text, index) {
9373
+ const clippedIndex = Math.max(0, Math.min(index, text.length));
9374
+ return isInSurrogatePair(text, clippedIndex) ? clippedIndex - 1 : clippedIndex;
9271
9375
  }
9272
- function cloneLson(value) {
9273
- return value === void 0 ? void 0 : isLiveStructure(value) ? value.clone() : deepClone(value);
9376
+ function clipRangeToCodePointBoundaries(text, index, length) {
9377
+ const clipped = clipRange(index, length, text.length);
9378
+ if (clipped.length === 0) {
9379
+ return {
9380
+ index: clipIndexToCodePointBoundary(text, clipped.index),
9381
+ length: 0
9382
+ };
9383
+ }
9384
+ const clippedEnd = clipped.index + clipped.length;
9385
+ const normalizedIndex = isInSurrogatePair(text, clipped.index) ? clipped.index - 1 : clipped.index;
9386
+ const normalizedEnd = isInSurrogatePair(text, clippedEnd) ? clippedEnd + 1 : clippedEnd;
9387
+ return {
9388
+ index: normalizedIndex,
9389
+ length: normalizedEnd - normalizedIndex
9390
+ };
9274
9391
  }
9275
- function liveNodeToLson(obj) {
9276
- if (obj instanceof LiveRegister) {
9277
- return obj.data;
9278
- } else if (obj instanceof LiveList || obj instanceof LiveMap || obj instanceof LiveObject || obj instanceof LiveFile) {
9279
- return obj;
9280
- } else {
9281
- return assertNever(obj, "Unknown AbstractCrdt");
9392
+ function applyInsert(segments, index, text, attributes) {
9393
+ if (text.length === 0) {
9394
+ return normalizeSegments(segments);
9395
+ }
9396
+ const split = splitSegmentsAt(segments, index);
9397
+ const result = [];
9398
+ let offset = 0;
9399
+ let inserted = false;
9400
+ for (const segment of split) {
9401
+ if (!inserted && offset === index) {
9402
+ result.push({ text, attributes });
9403
+ inserted = true;
9404
+ }
9405
+ result.push(segment);
9406
+ offset += segment.text.length;
9282
9407
  }
9408
+ if (!inserted) {
9409
+ result.push({ text, attributes });
9410
+ }
9411
+ return normalizeSegments(result);
9283
9412
  }
9284
- function lsonToLiveNode(value) {
9285
- if (value instanceof LiveObject || value instanceof LiveMap || value instanceof LiveList || value instanceof LiveFile) {
9286
- return value;
9287
- } else {
9288
- return new LiveRegister(value);
9413
+ function extractDeletedSegments(segments, index, length) {
9414
+ const split = splitSegmentsAt(
9415
+ splitSegmentsAt(segments, index),
9416
+ index + length
9417
+ );
9418
+ const deleted = [];
9419
+ let offset = 0;
9420
+ for (const segment of split) {
9421
+ const end = offset + segment.text.length;
9422
+ if (offset >= index && end <= index + length) {
9423
+ deleted.push({
9424
+ text: segment.text,
9425
+ attributes: segment.attributes
9426
+ });
9427
+ }
9428
+ offset = end;
9289
9429
  }
9430
+ return normalizeSegments(deleted);
9290
9431
  }
9291
- function dumpPool(pool) {
9292
- const rows = Array.from(pool.nodes.values(), (node) => {
9293
- const parent = node.parent;
9294
- const parentId = parent.type === "HasParent" ? _nullishCoalesce(parent.node._id, () => ( "?")) : parent.type === "Orphaned" ? "<orphaned>" : "-";
9295
- let value;
9296
- if (node instanceof LiveRegister) {
9297
- value = stringifyOrLog(node.data);
9298
- } else if (node instanceof LiveList) {
9299
- value = "<LiveList>";
9300
- } else if (node instanceof LiveMap) {
9301
- value = "<LiveMap>";
9302
- } else if (node instanceof LiveFile) {
9303
- value = stringifyOrLog(node.data);
9432
+ function applyDelete(segments, index, length) {
9433
+ const deletedSegments = extractDeletedSegments(segments, index, length);
9434
+ const split = splitSegmentsAt(
9435
+ splitSegmentsAt(segments, index),
9436
+ index + length
9437
+ );
9438
+ const result = [];
9439
+ let offset = 0;
9440
+ let deletedText = "";
9441
+ for (const segment of split) {
9442
+ const end = offset + segment.text.length;
9443
+ if (offset >= index && end <= index + length) {
9444
+ deletedText += segment.text;
9304
9445
  } else {
9305
- value = "<LiveObject>";
9446
+ result.push(segment);
9306
9447
  }
9307
- return { id: nn(node._id), parentId, key: _nullishCoalesce(node._parentKey, () => ( "")), value };
9308
- });
9309
- rows.sort((a, b) => {
9310
- if (a.parentId !== b.parentId) return a.parentId < b.parentId ? -1 : 1;
9311
- if (a.key !== b.key) return a.key < b.key ? -1 : 1;
9312
- return 0;
9313
- });
9314
- return rows.map(
9315
- (r) => ` ${r.id} parent=${r.parentId} key=${r.key || "\u2014"} ${r.value}`
9316
- ).join("\n");
9317
- }
9318
- function isJsonEq(a, b) {
9319
- if (a === b) {
9320
- return true;
9448
+ offset = end;
9321
9449
  }
9322
- if (typeof a !== "object" || a === null || typeof b !== "object" || b === null) {
9323
- return false;
9324
- }
9325
- if (Array.isArray(a) || Array.isArray(b)) {
9326
- if (!Array.isArray(a) || !Array.isArray(b) || a.length !== b.length) {
9327
- return false;
9328
- }
9329
- for (let i = 0; i < a.length; i++) {
9330
- if (!isJsonEq(a[i], b[i])) {
9331
- return false;
9450
+ return {
9451
+ segments: normalizeSegments(result),
9452
+ deletedText,
9453
+ deletedSegments
9454
+ };
9455
+ }
9456
+ function applyFormat(segments, index, length, attributes) {
9457
+ const split = splitSegmentsAt(
9458
+ splitSegmentsAt(segments, index),
9459
+ index + length
9460
+ );
9461
+ const result = [];
9462
+ let offset = 0;
9463
+ for (const segment of split) {
9464
+ const end = offset + segment.text.length;
9465
+ if (offset >= index && end <= index + length) {
9466
+ const nextAttributes = {
9467
+ ..._nullishCoalesce(segment.attributes, () => ( {}))
9468
+ };
9469
+ for (const [key, value] of Object.entries(attributes)) {
9470
+ if (value === null) {
9471
+ delete nextAttributes[key];
9472
+ } else {
9473
+ nextAttributes[key] = value;
9474
+ }
9332
9475
  }
9476
+ result.push({
9477
+ text: segment.text,
9478
+ attributes: Object.keys(nextAttributes).length === 0 ? void 0 : freeze(nextAttributes)
9479
+ });
9480
+ } else {
9481
+ result.push(segment);
9333
9482
  }
9334
- return true;
9483
+ offset = end;
9335
9484
  }
9336
- const aKeys = Object.keys(a);
9337
- if (aKeys.length !== Object.keys(b).length) {
9338
- return false;
9485
+ return normalizeSegments(result);
9486
+ }
9487
+ function formatReverseOperations(segments, index, length, patch) {
9488
+ const split = splitSegmentsAt(
9489
+ splitSegmentsAt(segments, index),
9490
+ index + length
9491
+ );
9492
+ const result = [];
9493
+ let offset = 0;
9494
+ for (const segment of split) {
9495
+ const end = offset + segment.text.length;
9496
+ if (offset >= index && end <= index + length) {
9497
+ const attributes = {};
9498
+ const current = _nullishCoalesce(segment.attributes, () => ( {}));
9499
+ for (const key of Object.keys(patch)) {
9500
+ const value = Object.hasOwn(current, key) ? current[key] : void 0;
9501
+ attributes[key] = _nullishCoalesce(value, () => ( null));
9502
+ }
9503
+ result.push({
9504
+ type: "format",
9505
+ index: offset,
9506
+ length: segment.text.length,
9507
+ attributes
9508
+ });
9509
+ }
9510
+ offset = end;
9339
9511
  }
9340
- for (const key of aKeys) {
9341
- if (!isJsonEq(a[key], b[key])) {
9342
- return false;
9512
+ return result;
9513
+ }
9514
+ function mapIndexThroughOperation(index, op) {
9515
+ if (op.type === "insert") {
9516
+ return op.index <= index ? index + op.text.length : index;
9517
+ } else if (op.type === "delete") {
9518
+ if (op.index >= index) {
9519
+ return index;
9343
9520
  }
9521
+ return Math.max(op.index, index - op.length);
9522
+ } else {
9523
+ return index;
9344
9524
  }
9345
- return true;
9346
9525
  }
9347
- function diffNodeMap(prev, next) {
9348
- const ops = [];
9349
- const idsToRecreate = /* @__PURE__ */ new Set();
9350
- next.forEach((nextCrdt, id) => {
9351
- const currentCrdt = prev.get(id);
9352
- if (currentCrdt === void 0) {
9353
- return;
9526
+ function mapTextIndexThroughOperations(index, ops) {
9527
+ let mapped = index;
9528
+ for (const op of ops) {
9529
+ mapped = mapIndexThroughOperation(mapped, op);
9530
+ }
9531
+ return mapped;
9532
+ }
9533
+ function inverseMapIndexThroughOperation(index, op) {
9534
+ if (op.type === "insert") {
9535
+ if (index <= op.index) {
9536
+ return index;
9354
9537
  }
9355
- if (currentCrdt.type !== nextCrdt.type || currentCrdt.type === CrdtType.FILE && nextCrdt.type === CrdtType.FILE && (currentCrdt.data.id !== nextCrdt.data.id || currentCrdt.data.name !== nextCrdt.data.name || currentCrdt.data.size !== nextCrdt.data.size || currentCrdt.data.mimeType !== nextCrdt.data.mimeType)) {
9356
- idsToRecreate.add(id);
9538
+ return Math.max(op.index, index - op.text.length);
9539
+ } else if (op.type === "delete") {
9540
+ return op.index <= index ? index + op.length : index;
9541
+ } else {
9542
+ return index;
9543
+ }
9544
+ }
9545
+ function inverseMapTextIndexThroughOperations(index, ops) {
9546
+ let mapped = index;
9547
+ for (let i = ops.length - 1; i >= 0; i--) {
9548
+ mapped = inverseMapIndexThroughOperation(mapped, ops[i]);
9549
+ }
9550
+ return mapped;
9551
+ }
9552
+ function oppositeOrder(order) {
9553
+ return order === "before" ? "after" : "before";
9554
+ }
9555
+ function mapIndexOverDelete(index, deleteIndex, deleteLength) {
9556
+ if (deleteIndex >= index) {
9557
+ return index;
9558
+ }
9559
+ return Math.max(deleteIndex, index - deleteLength);
9560
+ }
9561
+ function transformInsert(op, over, order) {
9562
+ if (over.type === "insert") {
9563
+ const shifts = over.index < op.index || over.index === op.index && order === "after";
9564
+ return [shifts ? { ...op, index: op.index + over.text.length } : { ...op }];
9565
+ } else if (over.type === "delete") {
9566
+ return [
9567
+ { ...op, index: mapIndexOverDelete(op.index, over.index, over.length) }
9568
+ ];
9569
+ } else {
9570
+ return [{ ...op }];
9571
+ }
9572
+ }
9573
+ function transformDelete(op, over) {
9574
+ const start = op.index;
9575
+ const end = op.index + op.length;
9576
+ if (over.type === "insert") {
9577
+ const at = over.index;
9578
+ const len = over.text.length;
9579
+ if (at <= start) {
9580
+ return [{ ...op, index: start + len }];
9581
+ }
9582
+ if (at >= end) {
9583
+ return [{ ...op }];
9584
+ }
9585
+ return [
9586
+ { type: "delete", index: start, length: at - start },
9587
+ { type: "delete", index: start + len, length: end - at }
9588
+ ];
9589
+ } else if (over.type === "delete") {
9590
+ const newStart = mapIndexOverDelete(start, over.index, over.length);
9591
+ const newEnd = mapIndexOverDelete(end, over.index, over.length);
9592
+ return newEnd - newStart > 0 ? [{ type: "delete", index: newStart, length: newEnd - newStart }] : [];
9593
+ } else {
9594
+ return [{ ...op }];
9595
+ }
9596
+ }
9597
+ function transformFormat(op, over, order) {
9598
+ const start = op.index;
9599
+ const end = op.index + op.length;
9600
+ if (over.type === "insert") {
9601
+ const at = over.index;
9602
+ const len = over.text.length;
9603
+ if (at <= start) {
9604
+ return [{ ...op, index: start + len }];
9605
+ }
9606
+ if (at >= end) {
9607
+ return [{ ...op }];
9608
+ }
9609
+ return [
9610
+ {
9611
+ type: "format",
9612
+ index: start,
9613
+ length: at - start,
9614
+ attributes: op.attributes
9615
+ },
9616
+ {
9617
+ type: "format",
9618
+ index: at + len,
9619
+ length: end - at,
9620
+ attributes: op.attributes
9621
+ }
9622
+ ];
9623
+ } else if (over.type === "delete") {
9624
+ const newStart = mapIndexOverDelete(start, over.index, over.length);
9625
+ const newEnd = mapIndexOverDelete(end, over.index, over.length);
9626
+ return newEnd - newStart > 0 ? [
9627
+ {
9628
+ type: "format",
9629
+ index: newStart,
9630
+ length: newEnd - newStart,
9631
+ attributes: op.attributes
9632
+ }
9633
+ ] : [];
9634
+ } else {
9635
+ if (order === "after") {
9636
+ return [{ ...op }];
9637
+ }
9638
+ const overlapStart = Math.max(start, over.index);
9639
+ const overlapEnd = Math.min(end, over.index + over.length);
9640
+ if (overlapStart >= overlapEnd) {
9641
+ return [{ ...op }];
9642
+ }
9643
+ const hasConflict = Object.keys(op.attributes).some(
9644
+ (key) => Object.hasOwn(over.attributes, key)
9645
+ );
9646
+ if (!hasConflict) {
9647
+ return [{ ...op }];
9648
+ }
9649
+ const reduced = {};
9650
+ for (const [key, value] of Object.entries(op.attributes)) {
9651
+ if (!Object.hasOwn(over.attributes, key)) {
9652
+ reduced[key] = value;
9653
+ }
9654
+ }
9655
+ const pieces = [];
9656
+ if (start < overlapStart) {
9657
+ pieces.push({
9658
+ type: "format",
9659
+ index: start,
9660
+ length: overlapStart - start,
9661
+ attributes: op.attributes
9662
+ });
9663
+ }
9664
+ if (Object.keys(reduced).length > 0) {
9665
+ pieces.push({
9666
+ type: "format",
9667
+ index: overlapStart,
9668
+ length: overlapEnd - overlapStart,
9669
+ attributes: reduced
9670
+ });
9671
+ }
9672
+ if (overlapEnd < end) {
9673
+ pieces.push({
9674
+ type: "format",
9675
+ index: overlapEnd,
9676
+ length: end - overlapEnd,
9677
+ attributes: op.attributes
9678
+ });
9679
+ }
9680
+ return pieces;
9681
+ }
9682
+ }
9683
+ function transformSingle(op, over, order) {
9684
+ switch (op.type) {
9685
+ case "insert":
9686
+ return transformInsert(op, over, order);
9687
+ case "delete":
9688
+ return transformDelete(op, over);
9689
+ case "format":
9690
+ return transformFormat(op, over, order);
9691
+ }
9692
+ }
9693
+ function transformTextOperationsX(a, b, order) {
9694
+ if (a.length === 0 || b.length === 0) {
9695
+ return [[...a], [...b]];
9696
+ }
9697
+ if (a.length === 1 && b.length === 1) {
9698
+ return [
9699
+ transformSingle(a[0], b[0], order),
9700
+ transformSingle(b[0], a[0], oppositeOrder(order))
9701
+ ];
9702
+ }
9703
+ if (a.length > 1) {
9704
+ const [headA1, b1] = transformTextOperationsX([a[0]], b, order);
9705
+ const [restA1, b2] = transformTextOperationsX(a.slice(1), b1, order);
9706
+ return [[...headA1, ...restA1], b2];
9707
+ }
9708
+ const [a1, headB1] = transformTextOperationsX(a, [b[0]], order);
9709
+ const [a2, restB1] = transformTextOperationsX(a1, b.slice(1), order);
9710
+ return [a2, [...headB1, ...restB1]];
9711
+ }
9712
+ function transformTextOperations(ops, over, order) {
9713
+ return transformTextOperationsX(ops, over, order)[0];
9714
+ }
9715
+ function textOperationsEqual(a, b) {
9716
+ return a === b || stableStringify(a) === stableStringify(b);
9717
+ }
9718
+ function applyTextOperationsToSegments(segments, ops) {
9719
+ let next = [...segments];
9720
+ for (const op of ops) {
9721
+ if (op.type === "insert") {
9722
+ const index = Math.max(0, Math.min(op.index, textLength(next)));
9723
+ next = applyInsert(next, index, op.text, op.attributes);
9724
+ } else if (op.type === "delete") {
9725
+ const index = Math.max(0, Math.min(op.index, textLength(next)));
9726
+ const clipped = clipRange(index, op.length, textLength(next));
9727
+ next = applyDelete(next, clipped.index, clipped.length).segments;
9728
+ } else {
9729
+ const index = Math.max(0, Math.min(op.index, textLength(next)));
9730
+ const clipped = clipRange(index, op.length, textLength(next));
9731
+ next = applyFormat(next, clipped.index, clipped.length, op.attributes);
9732
+ }
9733
+ }
9734
+ return next;
9735
+ }
9736
+ function applyLiveTextOperations(data, ops) {
9737
+ return segmentsToData(
9738
+ applyTextOperationsToSegments(dataToSegments(data), ops)
9739
+ );
9740
+ }
9741
+ function normalizeLiveTextOperations(data, operations) {
9742
+ let shadow = dataToSegments(data);
9743
+ const normalized = [];
9744
+ for (const operation of operations) {
9745
+ const text = shadow.map((segment) => segment.text).join("");
9746
+ let normalizedOperation;
9747
+ if (operation.type === "insert") {
9748
+ normalizedOperation = {
9749
+ ...operation,
9750
+ index: clipIndexToCodePointBoundary(text, operation.index)
9751
+ };
9752
+ } else {
9753
+ const range = clipRangeToCodePointBoundaries(
9754
+ text,
9755
+ operation.index,
9756
+ operation.length
9757
+ );
9758
+ normalizedOperation = {
9759
+ ...operation,
9760
+ index: range.index,
9761
+ length: range.length
9762
+ };
9763
+ }
9764
+ normalized.push(normalizedOperation);
9765
+ shadow = applyTextOperationsToSegments(shadow, [normalizedOperation]);
9766
+ }
9767
+ return normalized;
9768
+ }
9769
+ function invertTextOperations(segments, ops) {
9770
+ let shadow = [...segments];
9771
+ const reverse = [];
9772
+ for (const op of ops) {
9773
+ if (op.type === "insert") {
9774
+ shadow = applyInsert(shadow, op.index, op.text, op.attributes);
9775
+ reverse.unshift({
9776
+ type: "delete",
9777
+ index: op.index,
9778
+ length: op.text.length
9779
+ });
9780
+ } else if (op.type === "delete") {
9781
+ const deletedSegments = extractDeletedSegments(
9782
+ shadow,
9783
+ op.index,
9784
+ op.length
9785
+ );
9786
+ shadow = applyDelete(shadow, op.index, op.length).segments;
9787
+ const inserts = [];
9788
+ let insertIndex = op.index;
9789
+ for (const segment of deletedSegments) {
9790
+ inserts.push({
9791
+ type: "insert",
9792
+ index: insertIndex,
9793
+ text: segment.text,
9794
+ attributes: segment.attributes
9795
+ });
9796
+ insertIndex += segment.text.length;
9797
+ }
9798
+ for (let index = inserts.length - 1; index >= 0; index--) {
9799
+ reverse.unshift(inserts[index]);
9800
+ }
9801
+ } else {
9802
+ const inverse = formatReverseOperations(
9803
+ shadow,
9804
+ op.index,
9805
+ op.length,
9806
+ op.attributes
9807
+ );
9808
+ shadow = applyFormat(shadow, op.index, op.length, op.attributes);
9809
+ reverse.unshift(...inverse.reverse());
9810
+ }
9811
+ }
9812
+ return reverse;
9813
+ }
9814
+
9815
+ // src/crdts/LiveText.ts
9816
+ var ACCEPTED_OPS_HISTORY_LIMIT = 1e3;
9817
+ var LiveText = class _LiveText extends AbstractCrdt {
9818
+ /** The local document: #confirmed ⊕ #inFlightOps ⊕ #queuedOps. */
9819
+ #segments;
9820
+ /** The server-confirmed document (only authoritative ops applied). */
9821
+ #confirmed;
9822
+ #version;
9823
+ /** The op currently awaiting server acknowledgement (at most one). */
9824
+ #inFlightOpId;
9825
+ /** Its ops, continuously re-expressed against current server state. */
9826
+ #inFlightOps = [];
9827
+ /** Local edits made while an op is in flight; sent after the ack. */
9828
+ #queuedOps = [];
9829
+ #acceptedOps = [];
9830
+ /**
9831
+ * Creates a new LiveText document.
9832
+ *
9833
+ * @param textOrData Initial plain text, or an array of `[text]` /
9834
+ * `[text, attributes]` segments. Defaults to an empty document.
9835
+ *
9836
+ * @example
9837
+ * new LiveText();
9838
+ * new LiveText("Hello world");
9839
+ * new LiveText([["Hello ", { bold: true }], ["world"]]);
9840
+ */
9841
+ constructor(textOrData = "", version = 0) {
9842
+ super();
9843
+ this.#segments = typeof textOrData === "string" ? textOrData.length === 0 ? [] : [{ text: textOrData }] : dataToSegments(textOrData);
9844
+ this.#confirmed = [...this.#segments];
9845
+ this.#version = version;
9846
+ Object.assign(this[kInternal], {
9847
+ encodeIndex: (localIndex) => this.#encodeIndex(localIndex),
9848
+ decodeIndex: (index, fromVersion) => this.#decodeIndex(index, fromVersion)
9849
+ });
9850
+ }
9851
+ get version() {
9852
+ return this.#version;
9853
+ }
9854
+ get length() {
9855
+ return textLength(this.#segments);
9856
+ }
9857
+ /** @internal */
9858
+ static _deserialize([id, item], _parentToChildren, pool) {
9859
+ const text = new _LiveText(item.data, item.version);
9860
+ text._attach(id, pool);
9861
+ return text;
9862
+ }
9863
+ /** @internal */
9864
+ _toOps(parentId, parentKey) {
9865
+ if (this._id === void 0) {
9866
+ throw new Error("Cannot serialize LiveText if it is not attached");
9867
+ }
9868
+ return [
9869
+ {
9870
+ type: OpCode.CREATE_TEXT,
9871
+ id: this._id,
9872
+ parentId,
9873
+ parentKey,
9874
+ data: this.toJSON(),
9875
+ version: this.#version
9876
+ }
9877
+ ];
9878
+ }
9879
+ /** @internal */
9880
+ _serialize() {
9881
+ if (this.parent.type !== "HasParent") {
9882
+ throw new Error("Cannot serialize LiveText if parent is missing");
9883
+ }
9884
+ return {
9885
+ type: CrdtType.TEXT,
9886
+ parentId: nn(this.parent.node._id, "Parent node expected to have ID"),
9887
+ parentKey: this.parent.key,
9888
+ data: this.toJSON(),
9889
+ version: this.#version
9890
+ };
9891
+ }
9892
+ /** @internal */
9893
+ _attachChild(_op) {
9894
+ throw new Error("LiveText cannot contain child nodes");
9895
+ }
9896
+ /** @internal */
9897
+ _detachChild(_crdt) {
9898
+ throw new Error("LiveText cannot contain child nodes");
9899
+ }
9900
+ /** @internal */
9901
+ _apply(op, source) {
9902
+ if (op.type !== OpCode.UPDATE_TEXT) {
9903
+ return super._apply(op, source);
9904
+ }
9905
+ if (source.origin === "local" && source.optimistic) {
9906
+ return this.#applyLocal(op, toUpdateSource(source));
9907
+ }
9908
+ if (op.opId !== void 0 && op.opId === this.#inFlightOpId) {
9909
+ return this.#applyAck(op, toUpdateSource(source));
9910
+ }
9911
+ if (op.opId !== void 0 && this.#acceptedOps.some((entry) => entry.opId === op.opId)) {
9912
+ this.#version = Math.max(this.#version, _nullishCoalesce(op.version, () => ( op.baseVersion + 1)));
9913
+ return { modified: false };
9914
+ }
9915
+ return this.#applyRemote(op, toUpdateSource(source));
9916
+ }
9917
+ /**
9918
+ * Inserts text at the given index.
9919
+ *
9920
+ * @param index Character index at which to insert. Values outside the
9921
+ * document range are clipped.
9922
+ * @param text Text to insert.
9923
+ * @param attributes Optional inline attributes for the inserted text.
9924
+ *
9925
+ * @example
9926
+ * const text = new LiveText("Hello");
9927
+ * text.insert(5, " world");
9928
+ * text.insert(0, "Say: ", { italic: true });
9929
+ */
9930
+ insert(index, text, attributes) {
9931
+ const clippedIndex = clipIndexToCodePointBoundary(this.toString(), index);
9932
+ this.#dispatch([{ type: "insert", index: clippedIndex, text, attributes }]);
9933
+ }
9934
+ /**
9935
+ * Deletes `length` characters starting at `index`.
9936
+ *
9937
+ * @example
9938
+ * const text = new LiveText("Hello world");
9939
+ * text.delete(5, 6); // "Hello"
9940
+ */
9941
+ delete(index, length) {
9942
+ const clipped = clipRangeToCodePointBoundaries(
9943
+ this.toString(),
9944
+ index,
9945
+ length
9946
+ );
9947
+ if (clipped.length === 0) {
9948
+ return;
9949
+ }
9950
+ this.#dispatch([
9951
+ { type: "delete", index: clipped.index, length: clipped.length }
9952
+ ]);
9953
+ }
9954
+ /**
9955
+ * Replaces a range of text with new text.
9956
+ *
9957
+ * @example
9958
+ * const text = new LiveText("Hello world");
9959
+ * text.replace(0, 5, "Hi"); // "Hi world"
9960
+ */
9961
+ replace(index, length, text, attributes) {
9962
+ const clipped = clipRangeToCodePointBoundaries(
9963
+ this.toString(),
9964
+ index,
9965
+ length
9966
+ );
9967
+ const ops = [];
9968
+ if (clipped.length > 0) {
9969
+ ops.push({
9970
+ type: "delete",
9971
+ index: clipped.index,
9972
+ length: clipped.length
9973
+ });
9974
+ }
9975
+ if (text.length > 0) {
9976
+ ops.push({ type: "insert", index: clipped.index, text, attributes });
9977
+ }
9978
+ this.#dispatch(ops);
9979
+ }
9980
+ /**
9981
+ * Encode a local-document index (an offset into this LiveText's current
9982
+ * #segments, which CodeMirror or any consumer mirrors as its document)
9983
+ * into server-confirmed coordinates suitable for broadcasting to peers via
9984
+ * presence or any other side channel.
9985
+ *
9986
+ * The returned index is in this LiveText's current #confirmed coordinates
9987
+ * — that is, with this client's local pending ops inverse-mapped out.
9988
+ * Pair it with the current {@link LiveText.version} when sending so the
9989
+ * receiver can call {@link PrivateLiveTextApi.decodeIndex} to land the
9990
+ * position in their own local document coordinates regardless of their
9991
+ * private pending ops.
9992
+ *
9993
+ * Index ambiguity at boundaries is resolved by an inverse-of-forward
9994
+ * convention: a position at or before a local insertion is reported as
9995
+ * the position right before the insertion in #confirmed; a position past
9996
+ * the insertion shifts left by the insertion's length. Positions inside
9997
+ * an own-pending insertion collapse to the insertion point.
9998
+ */
9999
+ #encodeIndex(localIndex) {
10000
+ let mapped = Math.max(0, Math.min(localIndex, this.length));
10001
+ mapped = inverseMapTextIndexThroughOperations(mapped, this.#queuedOps);
10002
+ mapped = inverseMapTextIndexThroughOperations(mapped, this.#inFlightOps);
10003
+ return mapped;
10004
+ }
10005
+ /**
10006
+ * Decode an `(index, fromVersion)` pair produced by
10007
+ * {@link PrivateLiveTextApi.encodeIndex} — typically on a peer — into an
10008
+ * offset in this LiveText's current local document (an index suitable for
10009
+ * placing a CodeMirror marker, an annotation anchor, or anything else that
10010
+ * lives over #segments).
10011
+ *
10012
+ * Composes the accepted ops applied since `fromVersion` (drawn from
10013
+ * #acceptedOps in locally-applied form) with this client's own local
10014
+ * pending ops, in that order. The result is in current #segments
10015
+ * coordinates.
10016
+ *
10017
+ * Returns `null` when the position cannot be decoded against the current
10018
+ * state:
10019
+ * - `fromVersion` is greater than this LiveText's current version: the
10020
+ * peer is ahead of us. The caller should park the message and retry
10021
+ * after more accepted ops arrive.
10022
+ * - `fromVersion` falls outside the retained accepted-ops history. This
10023
+ * only happens after very long-lived disconnections; the caller can
10024
+ * fall back to using the raw index and letting subsequent local
10025
+ * transactions map it (with bounded drift).
10026
+ */
10027
+ #decodeIndex(index, fromVersion) {
10028
+ if (fromVersion > this.#version) {
10029
+ return null;
10030
+ }
10031
+ if (fromVersion < this.#version) {
10032
+ const oldest = _optionalChain([this, 'access', _238 => _238.#acceptedOps, 'access', _239 => _239[0], 'optionalAccess', _240 => _240.version]);
10033
+ if (oldest === void 0 || oldest > fromVersion + 1) {
10034
+ return null;
10035
+ }
10036
+ }
10037
+ let mapped = index;
10038
+ for (const entry of this.#acceptedOps) {
10039
+ if (entry.version <= fromVersion) continue;
10040
+ if (entry.version > this.#version) break;
10041
+ if (entry.ops.length === 0) continue;
10042
+ mapped = mapTextIndexThroughOperations(mapped, entry.ops);
10043
+ }
10044
+ mapped = mapTextIndexThroughOperations(mapped, this.#inFlightOps);
10045
+ mapped = mapTextIndexThroughOperations(mapped, this.#queuedOps);
10046
+ return Math.max(0, Math.min(mapped, this.length));
10047
+ }
10048
+ /**
10049
+ * Applies or removes inline attributes on a range of text.
10050
+ *
10051
+ * Set an attribute to `null` to remove it from the range.
10052
+ *
10053
+ * @example
10054
+ * const text = new LiveText("Hello world");
10055
+ * text.format(0, 5, { bold: true });
10056
+ * text.format(0, 5, { bold: null });
10057
+ */
10058
+ format(index, length, attributes) {
10059
+ const clipped = clipRangeToCodePointBoundaries(
10060
+ this.toString(),
10061
+ index,
10062
+ length
10063
+ );
10064
+ if (clipped.length === 0) {
10065
+ return;
10066
+ }
10067
+ this.#dispatch([
10068
+ {
10069
+ type: "format",
10070
+ index: clipped.index,
10071
+ length: clipped.length,
10072
+ attributes
10073
+ }
10074
+ ]);
10075
+ }
10076
+ /** Local edits made through the public API. */
10077
+ #dispatch(ops) {
10078
+ if (ops.length === 0) {
10079
+ return;
10080
+ }
10081
+ _optionalChain([this, 'access', _241 => _241._pool, 'optionalAccess', _242 => _242.assertStorageIsWritable, 'call', _243 => _243()]);
10082
+ const attached = this._pool !== void 0 && this._id !== void 0;
10083
+ const reverse = attached ? this.#invertOperations(ops) : [];
10084
+ const changes = this.#applyOperationsLocally(ops);
10085
+ if (!attached) {
10086
+ return;
10087
+ }
10088
+ const pool = nn(this._pool);
10089
+ const id = nn(this._id);
10090
+ const updates = /* @__PURE__ */ new Map([
10091
+ [
10092
+ id,
10093
+ {
10094
+ type: "LiveText",
10095
+ node: this,
10096
+ version: this.#version,
10097
+ updates: changes,
10098
+ source: LOCAL_EDIT
10099
+ }
10100
+ ]
10101
+ ]);
10102
+ if (this.#inFlightOpId === void 0) {
10103
+ const opId = pool.generateOpId();
10104
+ this.#inFlightOpId = opId;
10105
+ this.#inFlightOps = [...ops];
10106
+ pool.dispatch(
10107
+ [
10108
+ {
10109
+ type: OpCode.UPDATE_TEXT,
10110
+ id,
10111
+ opId,
10112
+ baseVersion: this.#version,
10113
+ ops: [...ops]
10114
+ }
10115
+ ],
10116
+ reverse,
10117
+ updates
10118
+ );
10119
+ } else {
10120
+ this.#queuedOps.push(...ops);
10121
+ pool.dispatch([], reverse, updates, { clearRedoStack: true });
10122
+ }
10123
+ }
10124
+ /**
10125
+ * A local replay of an existing wire op: an undo/redo frame, or an
10126
+ * unacknowledged op re-sent after a reconnect.
10127
+ */
10128
+ #applyLocal(op, source) {
10129
+ const mutableOp = op;
10130
+ if (op.opId !== void 0 && op.opId === this.#inFlightOpId) {
10131
+ this.#inFlightOps = [...this.#inFlightOps, ...this.#queuedOps];
10132
+ this.#queuedOps = [];
10133
+ mutableOp.baseVersion = this.#version;
10134
+ mutableOp.ops = [...this.#inFlightOps];
10135
+ return { modified: false };
10136
+ }
10137
+ let ops = op.ops;
10138
+ for (const entry of this.#acceptedOps) {
10139
+ if (entry.version > op.baseVersion && entry.ops.length > 0) {
10140
+ ops = transformTextOperations(ops, entry.ops, "after");
10141
+ }
10142
+ }
10143
+ const reverse = this.#invertOperations(ops);
10144
+ const changes = this.#applyOperationsLocally(ops);
10145
+ if (this.#inFlightOpId === void 0 && ops.length > 0) {
10146
+ this.#inFlightOpId = nn(op.opId, "Local ops must have an opId");
10147
+ this.#inFlightOps = [...ops];
10148
+ mutableOp.baseVersion = this.#version;
10149
+ mutableOp.ops = [...ops];
10150
+ } else {
10151
+ this.#queuedOps.push(...ops);
10152
+ mutableOp.baseVersion = this.#version;
10153
+ mutableOp.ops = [];
10154
+ }
10155
+ if (changes.length === 0) {
10156
+ return { modified: false };
10157
+ }
10158
+ return {
10159
+ reverse,
10160
+ modified: {
10161
+ type: "LiveText",
10162
+ node: this,
10163
+ version: this.#version,
10164
+ updates: changes,
10165
+ source
10166
+ }
10167
+ };
10168
+ }
10169
+ /** Server acknowledgement of our in-flight op. */
10170
+ #applyAck(op, source) {
10171
+ const ackedVersion = _nullishCoalesce(op.version, () => ( Math.max(this.#version, op.baseVersion + 1)));
10172
+ const predicted = this.#inFlightOps;
10173
+ const opId = this.#inFlightOpId;
10174
+ this.#confirmed = applyTextOperationsToSegments(this.#confirmed, op.ops);
10175
+ this.#inFlightOpId = void 0;
10176
+ this.#inFlightOps = [];
10177
+ let appliedOps = [];
10178
+ let result = { modified: false };
10179
+ if (!textOperationsEqual(op.ops, predicted)) {
10180
+ error2(
10181
+ "LiveText: acknowledgement did not match the local prediction; resynchronizing"
10182
+ );
10183
+ const rebuilt = this.#rebuildLocalFromConfirmed();
10184
+ appliedOps = rebuilt.appliedOps;
10185
+ if (rebuilt.changes.length > 0) {
10186
+ result = {
10187
+ reverse: [],
10188
+ modified: {
10189
+ type: "LiveText",
10190
+ node: this,
10191
+ version: ackedVersion,
10192
+ updates: rebuilt.changes,
10193
+ source
10194
+ }
10195
+ };
10196
+ }
10197
+ }
10198
+ this.#version = Math.max(this.#version, ackedVersion);
10199
+ this.#recordAccepted(ackedVersion, appliedOps, opId);
10200
+ this.#flushQueued();
10201
+ return result;
10202
+ }
10203
+ /** An accepted op from another client (or a server-fabricated fix op). */
10204
+ #applyRemote(op, source) {
10205
+ const version = _nullishCoalesce(op.version, () => ( this.#version + 1));
10206
+ this.#confirmed = applyTextOperationsToSegments(this.#confirmed, op.ops);
10207
+ const [overInFlight, inFlight] = transformTextOperationsX(
10208
+ op.ops,
10209
+ this.#inFlightOps,
10210
+ "before"
10211
+ );
10212
+ const [applied, queued] = transformTextOperationsX(
10213
+ overInFlight,
10214
+ this.#queuedOps,
10215
+ "before"
10216
+ );
10217
+ this.#inFlightOps = inFlight;
10218
+ this.#queuedOps = queued;
10219
+ this.#recordAccepted(version, applied, op.opId);
10220
+ if (applied.length === 0) {
10221
+ this.#version = Math.max(this.#version, version);
10222
+ return { modified: false };
10223
+ }
10224
+ const reverse = this.#invertOperations(applied);
10225
+ const changes = this.#applyOperationsLocally(applied);
10226
+ this.#version = Math.max(this.#version, version);
10227
+ return {
10228
+ reverse,
10229
+ modified: {
10230
+ type: "LiveText",
10231
+ node: this,
10232
+ version: this.#version,
10233
+ updates: changes,
10234
+ source
10235
+ }
10236
+ };
10237
+ }
10238
+ /** Send the queued ops as the next in-flight op (after an ack). */
10239
+ #flushQueued() {
10240
+ if (this.#queuedOps.length === 0 || this._pool === void 0 || this._id === void 0) {
10241
+ return;
10242
+ }
10243
+ const opId = this._pool.generateOpId();
10244
+ this.#inFlightOpId = opId;
10245
+ this.#inFlightOps = this.#queuedOps;
10246
+ this.#queuedOps = [];
10247
+ this._pool.dispatch(
10248
+ [
10249
+ {
10250
+ type: OpCode.UPDATE_TEXT,
10251
+ id: this._id,
10252
+ opId,
10253
+ baseVersion: this.#version,
10254
+ ops: [...this.#inFlightOps]
10255
+ }
10256
+ ],
10257
+ [],
10258
+ /* @__PURE__ */ new Map(),
10259
+ // The local content was already applied (and made undoable) when the
10260
+ // edits happened; this is purely an outbound flush.
10261
+ { clearRedoStack: false }
10262
+ );
10263
+ }
10264
+ /**
10265
+ * Rebuild the local document as confirmed ⊕ queued ops, returning the
10266
+ * coarse delta that was applied. Only used by defensive recovery paths.
10267
+ */
10268
+ #rebuildLocalFromConfirmed() {
10269
+ const before2 = this.#segments;
10270
+ const after2 = applyTextOperationsToSegments(this.#confirmed, [
10271
+ ...this.#inFlightOps,
10272
+ ...this.#queuedOps
10273
+ ]);
10274
+ if (stableStringify(segmentsToData(before2)) === stableStringify(segmentsToData(after2))) {
10275
+ this.#segments = after2;
10276
+ return { appliedOps: [], changes: [] };
10277
+ }
10278
+ const beforeText = before2.map((segment) => segment.text).join("");
10279
+ this.#segments = after2;
10280
+ this.invalidate();
10281
+ const appliedOps = [];
10282
+ const changes = [];
10283
+ if (beforeText.length > 0) {
10284
+ appliedOps.push({ type: "delete", index: 0, length: beforeText.length });
10285
+ changes.push({
10286
+ type: "delete",
10287
+ index: 0,
10288
+ length: beforeText.length,
10289
+ deletedText: beforeText
10290
+ });
10291
+ }
10292
+ let index = 0;
10293
+ for (const segment of after2) {
10294
+ appliedOps.push({
10295
+ type: "insert",
10296
+ index,
10297
+ text: segment.text,
10298
+ attributes: segment.attributes
10299
+ });
10300
+ changes.push({
10301
+ type: "insert",
10302
+ index,
10303
+ text: segment.text,
10304
+ attributes: segment.attributes
10305
+ });
10306
+ index += segment.text.length;
10307
+ }
10308
+ return { appliedOps, changes };
10309
+ }
10310
+ /**
10311
+ * Reconcile this node against an authoritative storage snapshot (e.g.
10312
+ * after a reconnect). The confirmed state and version are replaced by the
10313
+ * snapshot's; pending (in-flight + queued) ops are preserved on top and
10314
+ * will be re-sent by the offline-ops replay.
10315
+ *
10316
+ * @internal
10317
+ */
10318
+ _resyncText(data, version, source) {
10319
+ this.#confirmed = dataToSegments(data);
10320
+ this.#version = version;
10321
+ this.#acceptedOps = [];
10322
+ const rebuilt = this.#rebuildLocalFromConfirmed();
10323
+ if (rebuilt.changes.length === 0) {
10324
+ return void 0;
10325
+ }
10326
+ return {
10327
+ type: "LiveText",
10328
+ node: this,
10329
+ version: this.#version,
10330
+ updates: rebuilt.changes,
10331
+ source
10332
+ };
10333
+ }
10334
+ /**
10335
+ * Called when the server rejected one of our ops. Drops all pending state
10336
+ * for this node (edits queued behind a rejected op cannot be trusted
10337
+ * either); the room follows up with a storage resync.
10338
+ *
10339
+ * @internal
10340
+ */
10341
+ _rejectPendingOp(opId) {
10342
+ if (opId !== this.#inFlightOpId) {
10343
+ return;
10344
+ }
10345
+ this.#inFlightOpId = void 0;
10346
+ this.#inFlightOps = [];
10347
+ this.#queuedOps = [];
10348
+ }
10349
+ #recordAccepted(version, ops, opId) {
10350
+ if (this.#acceptedOps.some((entry) => entry.version === version)) {
10351
+ return;
10352
+ }
10353
+ this.#acceptedOps.push({ version, opId, ops: [...ops] });
10354
+ this.#acceptedOps.sort((left, right) => left.version - right.version);
10355
+ if (this.#acceptedOps.length > ACCEPTED_OPS_HISTORY_LIMIT) {
10356
+ this.#acceptedOps.splice(
10357
+ 0,
10358
+ this.#acceptedOps.length - ACCEPTED_OPS_HISTORY_LIMIT
10359
+ );
10360
+ }
10361
+ }
10362
+ #applyOperationsLocally(ops) {
10363
+ const changes = [];
10364
+ for (const op of ops) {
10365
+ if (op.type === "insert") {
10366
+ this.#segments = applyInsert(
10367
+ this.#segments,
10368
+ op.index,
10369
+ op.text,
10370
+ op.attributes
10371
+ );
10372
+ changes.push({
10373
+ type: "insert",
10374
+ index: op.index,
10375
+ text: op.text,
10376
+ attributes: op.attributes
10377
+ });
10378
+ } else if (op.type === "delete") {
10379
+ const result = applyDelete(this.#segments, op.index, op.length);
10380
+ this.#segments = result.segments;
10381
+ changes.push({
10382
+ type: "delete",
10383
+ index: op.index,
10384
+ length: op.length,
10385
+ deletedText: result.deletedText
10386
+ });
10387
+ } else {
10388
+ this.#segments = applyFormat(
10389
+ this.#segments,
10390
+ op.index,
10391
+ op.length,
10392
+ op.attributes
10393
+ );
10394
+ changes.push({
10395
+ type: "format",
10396
+ index: op.index,
10397
+ length: op.length,
10398
+ attributes: op.attributes
10399
+ });
10400
+ }
10401
+ }
10402
+ this.invalidate();
10403
+ return changes;
10404
+ }
10405
+ #invertOperations(ops) {
10406
+ return [
10407
+ {
10408
+ type: OpCode.UPDATE_TEXT,
10409
+ id: nn(this._id),
10410
+ baseVersion: this.#version,
10411
+ ops: invertTextOperations(this.#segments, ops)
10412
+ }
10413
+ ];
10414
+ }
10415
+ /** Returns the plain text content without attributes. Equivalent to joining the text from each segment in {@link LiveText.toJSON}. */
10416
+ toString() {
10417
+ return this.#segments.map((segment) => segment.text).join("");
10418
+ }
10419
+ /**
10420
+ * Returns a JSON-compatible snapshot of the document as a {@link LiveTextData}
10421
+ * array.
10422
+ *
10423
+ * @example
10424
+ * new LiveText([["Hello ", { bold: true }], ["world"]]).toJSON();
10425
+ * // [["Hello ", { bold: true }], ["world"]]
10426
+ */
10427
+ toJSON() {
10428
+ return super.toJSON();
10429
+ }
10430
+ /** @internal */
10431
+ _toJSON() {
10432
+ return segmentsToData(this.#segments);
10433
+ }
10434
+ /** @internal */
10435
+ toTreeNode(key) {
10436
+ return super.toTreeNode(key);
10437
+ }
10438
+ /** @internal */
10439
+ _toTreeNode(key) {
10440
+ const nodeId = _nullishCoalesce(this._id, () => ( nanoid()));
10441
+ const payload = this.toJSON().map(
10442
+ (segment, index) => ({
10443
+ type: "Json",
10444
+ id: `${nodeId}:${index}`,
10445
+ key: String(index),
10446
+ payload: segment
10447
+ })
10448
+ );
10449
+ payload.push({
10450
+ type: "Json",
10451
+ id: `${nodeId}:version`,
10452
+ key: "version",
10453
+ payload: this.version
10454
+ });
10455
+ return {
10456
+ type: "LiveText",
10457
+ id: nodeId,
10458
+ key,
10459
+ payload
10460
+ };
10461
+ }
10462
+ clone() {
10463
+ return new _LiveText(this.toJSON(), this.#version);
10464
+ }
10465
+ };
10466
+
10467
+ // src/crdts/liveblocks-helpers.ts
10468
+ function creationOpToLiveNode(op) {
10469
+ return lsonToLiveNode(creationOpToLson(op));
10470
+ }
10471
+ function creationOpToLson(op) {
10472
+ switch (op.type) {
10473
+ case OpCode.CREATE_FILE:
10474
+ return new LiveFile(op.data);
10475
+ case OpCode.CREATE_REGISTER:
10476
+ return op.data;
10477
+ case OpCode.CREATE_OBJECT:
10478
+ return new LiveObject(op.data);
10479
+ case OpCode.CREATE_MAP:
10480
+ return new LiveMap();
10481
+ case OpCode.CREATE_LIST:
10482
+ return new LiveList([]);
10483
+ case OpCode.CREATE_TEXT:
10484
+ return new LiveText(op.data, op.version);
10485
+ default:
10486
+ return assertNever(op, "Unknown creation Op");
10487
+ }
10488
+ }
10489
+ function isSameNodeOrChildOf(node, parent) {
10490
+ if (node === parent) {
10491
+ return true;
10492
+ }
10493
+ if (node.parent.type === "HasParent") {
10494
+ return isSameNodeOrChildOf(node.parent.node, parent);
10495
+ }
10496
+ return false;
10497
+ }
10498
+ function liveObjectFromNodeStream(nodes) {
10499
+ const pool = createManagedPool({
10500
+ getCurrentConnectionId: () => {
10501
+ throw new Error(
10502
+ "Cannot mutate a historic storage version: it is a read-only snapshot"
10503
+ );
10504
+ }
10505
+ });
10506
+ return LiveObject._fromItems(nodes, pool);
10507
+ }
10508
+ function deserialize(node, parentToChildren, pool) {
10509
+ if (isObjectStorageNode(node)) {
10510
+ return LiveObject._deserialize(node, parentToChildren, pool);
10511
+ } else if (isListStorageNode(node)) {
10512
+ return LiveList._deserialize(node, parentToChildren, pool);
10513
+ } else if (isMapStorageNode(node)) {
10514
+ return LiveMap._deserialize(node, parentToChildren, pool);
10515
+ } else if (isRegisterStorageNode(node)) {
10516
+ return LiveRegister._deserialize(node, parentToChildren, pool);
10517
+ } else if (isTextStorageNode(node)) {
10518
+ return LiveText._deserialize(node, parentToChildren, pool);
10519
+ } else if (isFileStorageNode(node)) {
10520
+ return LiveFile._deserialize(node, parentToChildren, pool);
10521
+ } else {
10522
+ throw new Error("Unexpected CRDT type");
10523
+ }
10524
+ }
10525
+ function deserializeToLson(node, parentToChildren, pool) {
10526
+ if (isObjectStorageNode(node)) {
10527
+ return LiveObject._deserialize(node, parentToChildren, pool);
10528
+ } else if (isListStorageNode(node)) {
10529
+ return LiveList._deserialize(node, parentToChildren, pool);
10530
+ } else if (isMapStorageNode(node)) {
10531
+ return LiveMap._deserialize(node, parentToChildren, pool);
10532
+ } else if (isRegisterStorageNode(node)) {
10533
+ return node[1].data;
10534
+ } else if (isTextStorageNode(node)) {
10535
+ return LiveText._deserialize(node, parentToChildren, pool);
10536
+ } else if (isFileStorageNode(node)) {
10537
+ return LiveFile._deserialize(node, parentToChildren, pool);
10538
+ } else {
10539
+ throw new Error("Unexpected CRDT type");
10540
+ }
10541
+ }
10542
+ function isLiveStructure(value) {
10543
+ return isLiveList(value) || isLiveMap(value) || isLiveObject(value) || isLiveText(value) || isLiveFile(value);
10544
+ }
10545
+ function isLiveNode(value) {
10546
+ return isLiveStructure(value) || isLiveRegister(value);
10547
+ }
10548
+ function isLiveList(value) {
10549
+ return value instanceof LiveList;
10550
+ }
10551
+ function isLiveMap(value) {
10552
+ return value instanceof LiveMap;
10553
+ }
10554
+ function isLiveObject(value) {
10555
+ return value instanceof LiveObject;
10556
+ }
10557
+ function isLiveText(value) {
10558
+ return value instanceof LiveText;
10559
+ }
10560
+ function isLiveFile(value) {
10561
+ return value instanceof LiveFile;
10562
+ }
10563
+ function isLiveRegister(value) {
10564
+ return value instanceof LiveRegister;
10565
+ }
10566
+ function cloneLson(value) {
10567
+ return value === void 0 ? void 0 : isLiveStructure(value) ? value.clone() : deepClone(value);
10568
+ }
10569
+ function liveNodeToLson(obj) {
10570
+ if (obj instanceof LiveRegister) {
10571
+ return obj.data;
10572
+ } else if (obj instanceof LiveList || obj instanceof LiveMap || obj instanceof LiveObject || obj instanceof LiveText || obj instanceof LiveFile) {
10573
+ return obj;
10574
+ } else {
10575
+ return assertNever(obj, "Unknown AbstractCrdt");
10576
+ }
10577
+ }
10578
+ function lsonToLiveNode(value) {
10579
+ if (value instanceof LiveObject || value instanceof LiveMap || value instanceof LiveList || value instanceof LiveText || value instanceof LiveFile) {
10580
+ return value;
10581
+ } else {
10582
+ return new LiveRegister(value);
10583
+ }
10584
+ }
10585
+ function dumpPool(pool) {
10586
+ const rows = Array.from(pool.nodes.values(), (node) => {
10587
+ const parent = node.parent;
10588
+ const parentId = parent.type === "HasParent" ? _nullishCoalesce(parent.node._id, () => ( "?")) : parent.type === "Orphaned" ? "<orphaned>" : "-";
10589
+ let value;
10590
+ if (node instanceof LiveRegister) {
10591
+ value = stringifyOrLog(node.data);
10592
+ } else if (node instanceof LiveList) {
10593
+ value = "<LiveList>";
10594
+ } else if (node instanceof LiveMap) {
10595
+ value = "<LiveMap>";
10596
+ } else if (node instanceof LiveFile) {
10597
+ value = stringifyOrLog(node.data);
10598
+ } else {
10599
+ value = "<LiveObject>";
10600
+ }
10601
+ return { id: nn(node._id), parentId, key: _nullishCoalesce(node._parentKey, () => ( "")), value };
10602
+ });
10603
+ rows.sort((a, b) => {
10604
+ if (a.parentId !== b.parentId) return a.parentId < b.parentId ? -1 : 1;
10605
+ if (a.key !== b.key) return a.key < b.key ? -1 : 1;
10606
+ return 0;
10607
+ });
10608
+ return rows.map(
10609
+ (r) => ` ${r.id} parent=${r.parentId} key=${r.key || "\u2014"} ${r.value}`
10610
+ ).join("\n");
10611
+ }
10612
+ function isJsonEq(a, b) {
10613
+ if (a === b) {
10614
+ return true;
10615
+ }
10616
+ if (typeof a !== "object" || a === null || typeof b !== "object" || b === null) {
10617
+ return false;
10618
+ }
10619
+ if (Array.isArray(a) || Array.isArray(b)) {
10620
+ if (!Array.isArray(a) || !Array.isArray(b) || a.length !== b.length) {
10621
+ return false;
10622
+ }
10623
+ for (let i = 0; i < a.length; i++) {
10624
+ if (!isJsonEq(a[i], b[i])) {
10625
+ return false;
10626
+ }
10627
+ }
10628
+ return true;
10629
+ }
10630
+ const aKeys = Object.keys(a);
10631
+ if (aKeys.length !== Object.keys(b).length) {
10632
+ return false;
10633
+ }
10634
+ for (const key of aKeys) {
10635
+ if (!isJsonEq(a[key], b[key])) {
10636
+ return false;
10637
+ }
10638
+ }
10639
+ return true;
10640
+ }
10641
+ function liveTextDataToReplaceOps(before2, after2) {
10642
+ const ops = [];
10643
+ const beforeLength = before2.reduce(
10644
+ (length, [text]) => length + text.length,
10645
+ 0
10646
+ );
10647
+ if (beforeLength > 0) {
10648
+ ops.push({ type: "delete", index: 0, length: beforeLength });
10649
+ }
10650
+ let index = 0;
10651
+ for (const [text, attributes] of after2) {
10652
+ if (text.length === 0) {
10653
+ continue;
10654
+ }
10655
+ ops.push({ type: "insert", index, text, attributes });
10656
+ index += text.length;
10657
+ }
10658
+ return ops;
10659
+ }
10660
+ function diffNodeMap(prev, next, options) {
10661
+ const ops = [];
10662
+ const idsToRecreate = /* @__PURE__ */ new Set();
10663
+ next.forEach((nextCrdt, id) => {
10664
+ const currentCrdt = prev.get(id);
10665
+ if (currentCrdt === void 0) {
10666
+ return;
10667
+ }
10668
+ if (currentCrdt.type !== nextCrdt.type || currentCrdt.type === CrdtType.FILE && nextCrdt.type === CrdtType.FILE && (currentCrdt.data.id !== nextCrdt.data.id || currentCrdt.data.name !== nextCrdt.data.name || currentCrdt.data.size !== nextCrdt.data.size || currentCrdt.data.mimeType !== nextCrdt.data.mimeType)) {
10669
+ idsToRecreate.add(id);
9357
10670
  }
9358
10671
  });
9359
10672
  let foundDescendant = true;
@@ -9436,6 +10749,16 @@ function diffNodeMap(prev, next) {
9436
10749
  parentKey: crdt.parentKey
9437
10750
  });
9438
10751
  break;
10752
+ case CrdtType.TEXT:
10753
+ ops.push({
10754
+ type: OpCode.CREATE_TEXT,
10755
+ id,
10756
+ parentId: crdt.parentId,
10757
+ parentKey: crdt.parentKey,
10758
+ data: crdt.data,
10759
+ version: crdt.version
10760
+ });
10761
+ break;
9439
10762
  }
9440
10763
  }
9441
10764
  next.forEach((crdt, id) => {
@@ -9462,6 +10785,17 @@ function diffNodeMap(prev, next) {
9462
10785
  }
9463
10786
  }
9464
10787
  }
10788
+ if (_optionalChain([options, 'optionalAccess', _244 => _244.includeLiveTextUpdates]) === true && crdt.type === CrdtType.TEXT && currentCrdt.type === CrdtType.TEXT && !isJsonEq(crdt.data, currentCrdt.data)) {
10789
+ ops.push({
10790
+ type: OpCode.UPDATE_TEXT,
10791
+ id,
10792
+ // A restore is a new edit in the current timeline. The version from
10793
+ // the historic snapshot describes its old timeline and must not move
10794
+ // this node's current version backwards.
10795
+ baseVersion: currentCrdt.version,
10796
+ ops: liveTextDataToReplaceOps(currentCrdt.data, crdt.data)
10797
+ });
10798
+ }
9465
10799
  if (crdt.parentKey !== currentCrdt.parentKey) {
9466
10800
  ops.push({
9467
10801
  type: OpCode.SET_PARENT_KEY,
@@ -9502,19 +10836,36 @@ function mergeListStorageUpdates(first, second) {
9502
10836
  updates: updates.concat(second.updates)
9503
10837
  };
9504
10838
  }
10839
+ function mergeTextStorageUpdates(first, second) {
10840
+ return {
10841
+ ...second,
10842
+ updates: first.updates.concat(second.updates)
10843
+ };
10844
+ }
10845
+ function mergeUpdateSources(first, second) {
10846
+ if (first.origin === "remote" || second.origin === "remote") {
10847
+ return REMOTE;
10848
+ }
10849
+ if (second.via !== "edit") return second;
10850
+ if (first.via !== "edit") return first;
10851
+ return LOCAL_EDIT;
10852
+ }
9505
10853
  function mergeStorageUpdates(first, second) {
9506
10854
  if (first === void 0) {
9507
10855
  return second;
9508
10856
  }
10857
+ const source = mergeUpdateSources(first.source, second.source);
9509
10858
  if (first.type === "LiveObject" && second.type === "LiveObject") {
9510
- return mergeObjectStorageUpdates(first, second);
10859
+ return { ...mergeObjectStorageUpdates(first, second), source };
9511
10860
  } else if (first.type === "LiveMap" && second.type === "LiveMap") {
9512
- return mergeMapStorageUpdates(first, second);
10861
+ return { ...mergeMapStorageUpdates(first, second), source };
9513
10862
  } else if (first.type === "LiveList" && second.type === "LiveList") {
9514
- return mergeListStorageUpdates(first, second);
10863
+ return { ...mergeListStorageUpdates(first, second), source };
10864
+ } else if (first.type === "LiveText" && second.type === "LiveText") {
10865
+ return { ...mergeTextStorageUpdates(first, second), source };
9515
10866
  } else {
10867
+ return { ...second, source };
9516
10868
  }
9517
- return second;
9518
10869
  }
9519
10870
 
9520
10871
  // src/devtools/bridge.ts
@@ -9530,7 +10881,7 @@ function sendToPanel(message, options) {
9530
10881
  ...message,
9531
10882
  source: "liveblocks-devtools-client"
9532
10883
  };
9533
- if (!(_optionalChain([options, 'optionalAccess', _238 => _238.force]) || _bridgeActive)) {
10884
+ if (!(_optionalChain([options, 'optionalAccess', _245 => _245.force]) || _bridgeActive)) {
9534
10885
  return;
9535
10886
  }
9536
10887
  window.postMessage(fullMsg, "*");
@@ -9538,7 +10889,7 @@ function sendToPanel(message, options) {
9538
10889
  var eventSource = makeEventSource();
9539
10890
  if (process.env.NODE_ENV !== "production" && typeof window !== "undefined") {
9540
10891
  window.addEventListener("message", (event) => {
9541
- if (event.source === window && _optionalChain([event, 'access', _239 => _239.data, 'optionalAccess', _240 => _240.source]) === "liveblocks-devtools-panel") {
10892
+ if (event.source === window && _optionalChain([event, 'access', _246 => _246.data, 'optionalAccess', _247 => _247.source]) === "liveblocks-devtools-panel") {
9542
10893
  eventSource.notify(event.data);
9543
10894
  } else {
9544
10895
  }
@@ -9680,7 +11031,7 @@ function fullSync(room) {
9680
11031
  msg: "room::sync::full",
9681
11032
  roomId: room.id,
9682
11033
  status: room.getStatus(),
9683
- storage: _nullishCoalesce(_optionalChain([root, 'optionalAccess', _241 => _241.toTreeNode, 'call', _242 => _242("root"), 'access', _243 => _243.payload]), () => ( null)),
11034
+ storage: _nullishCoalesce(_optionalChain([root, 'optionalAccess', _248 => _248.toTreeNode, 'call', _249 => _249("root"), 'access', _250 => _250.payload]), () => ( null)),
9684
11035
  me,
9685
11036
  others
9686
11037
  });
@@ -10367,15 +11718,15 @@ function installBackgroundTabSpy() {
10367
11718
  const doc = typeof document !== "undefined" ? document : void 0;
10368
11719
  const inBackgroundSince = { current: null };
10369
11720
  function onVisibilityChange() {
10370
- if (_optionalChain([doc, 'optionalAccess', _244 => _244.visibilityState]) === "hidden") {
11721
+ if (_optionalChain([doc, 'optionalAccess', _251 => _251.visibilityState]) === "hidden") {
10371
11722
  inBackgroundSince.current = _nullishCoalesce(inBackgroundSince.current, () => ( Date.now()));
10372
11723
  } else {
10373
11724
  inBackgroundSince.current = null;
10374
11725
  }
10375
11726
  }
10376
- _optionalChain([doc, 'optionalAccess', _245 => _245.addEventListener, 'call', _246 => _246("visibilitychange", onVisibilityChange)]);
11727
+ _optionalChain([doc, 'optionalAccess', _252 => _252.addEventListener, 'call', _253 => _253("visibilitychange", onVisibilityChange)]);
10377
11728
  const unsub = () => {
10378
- _optionalChain([doc, 'optionalAccess', _247 => _247.removeEventListener, 'call', _248 => _248("visibilitychange", onVisibilityChange)]);
11729
+ _optionalChain([doc, 'optionalAccess', _254 => _254.removeEventListener, 'call', _255 => _255("visibilitychange", onVisibilityChange)]);
10379
11730
  };
10380
11731
  return [inBackgroundSince, unsub];
10381
11732
  }
@@ -10399,7 +11750,7 @@ function makeNodeMapBuffer() {
10399
11750
  function topLevelKeysOf(nodes) {
10400
11751
  const keys2 = /* @__PURE__ */ new Set();
10401
11752
  const root = nodes.get("root");
10402
- for (const key in _optionalChain([root, 'optionalAccess', _249 => _249.data])) {
11753
+ for (const key in _optionalChain([root, 'optionalAccess', _256 => _256.data])) {
10403
11754
  keys2.add(key);
10404
11755
  }
10405
11756
  for (const node of nodes.values()) {
@@ -10471,6 +11822,8 @@ function createRoom(options, config) {
10471
11822
  activeBatch: null,
10472
11823
  unacknowledgedOps
10473
11824
  };
11825
+ let nextHistoryItemId = 0;
11826
+ let historyDisabled = 0;
10474
11827
  const nodeMapBuffer = makeNodeMapBuffer();
10475
11828
  const stopwatch = config.enableDebugLogging ? makeStopWatch() : void 0;
10476
11829
  let lastTokenKey;
@@ -10555,7 +11908,7 @@ function createRoom(options, config) {
10555
11908
  }
10556
11909
  }
10557
11910
  });
10558
- function onDispatch(ops, reverse, storageUpdates) {
11911
+ function onDispatch(ops, reverse, storageUpdates, options2) {
10559
11912
  if (context.activeBatch) {
10560
11913
  for (const op of ops) {
10561
11914
  context.activeBatch.ops.push(op);
@@ -10570,19 +11923,24 @@ function createRoom(options, config) {
10570
11923
  );
10571
11924
  }
10572
11925
  context.activeBatch.reverseOps.pushLeft(reverse);
11926
+ if (_optionalChain([options2, 'optionalAccess', _257 => _257.clearRedoStack])) {
11927
+ context.activeBatch.clearRedoStack = true;
11928
+ }
10573
11929
  } else {
10574
11930
  if (reverse.length > 0) {
10575
11931
  addToUndoStack(reverse);
10576
11932
  }
11933
+ if (_nullishCoalesce(_optionalChain([options2, 'optionalAccess', _258 => _258.clearRedoStack]), () => ( ops.length > 0))) {
11934
+ clearRedoStack();
11935
+ }
10577
11936
  if (ops.length > 0) {
10578
- context.redoStack.length = 0;
10579
11937
  dispatchOps(ops);
10580
11938
  }
10581
11939
  notify({ storageUpdates });
10582
11940
  }
10583
11941
  }
10584
11942
  function isStorageWritable() {
10585
- const permissionMatrix = _optionalChain([context, 'access', _250 => _250.dynamicSessionInfoSig, 'access', _251 => _251.get, 'call', _252 => _252(), 'optionalAccess', _253 => _253.permissionMatrix]);
11943
+ const permissionMatrix = _optionalChain([context, 'access', _259 => _259.dynamicSessionInfoSig, 'access', _260 => _260.get, 'call', _261 => _261(), 'optionalAccess', _262 => _262.permissionMatrix]);
10586
11944
  return permissionMatrix !== void 0 ? hasPermissionAccess(permissionMatrix, "storage", "write") : true;
10587
11945
  }
10588
11946
  const eventHub = {
@@ -10595,6 +11953,7 @@ function createRoom(options, config) {
10595
11953
  others: makeEventSource(),
10596
11954
  storageBatch: makeEventSource(),
10597
11955
  history: makeEventSource(),
11956
+ privateHistory: makeEventSource(),
10598
11957
  storageDidLoad: makeEventSource(),
10599
11958
  storageStatus: makeEventSource(),
10600
11959
  ydoc: makeEventSource(),
@@ -10682,12 +12041,12 @@ function createRoom(options, config) {
10682
12041
  self,
10683
12042
  (me) => me !== null ? userToTreeNode("Me", me) : null
10684
12043
  );
10685
- function diffCurrentStorageAgainst(target) {
12044
+ function diffCurrentStorageAgainst(target, options2) {
10686
12045
  const current = /* @__PURE__ */ new Map();
10687
12046
  for (const [id, crdt] of context.pool.nodes) {
10688
12047
  current.set(id, crdt._serialize());
10689
12048
  }
10690
- return diffNodeMap(current, target);
12049
+ return diffNodeMap(current, target, options2);
10691
12050
  }
10692
12051
  function createOrUpdateRootFromMessage(nodes) {
10693
12052
  if (nodes.size === 0) {
@@ -10695,6 +12054,23 @@ function createRoom(options, config) {
10695
12054
  }
10696
12055
  if (context.root !== void 0) {
10697
12056
  const result = applyRemoteOps(diffCurrentStorageAgainst(nodes));
12057
+ for (const [id, crdt] of nodes) {
12058
+ if (crdt.type === CrdtType.TEXT) {
12059
+ const node = context.pool.nodes.get(id);
12060
+ if (node !== void 0 && isLiveText(node)) {
12061
+ const update = node._resyncText(crdt.data, crdt.version, REMOTE);
12062
+ if (update !== void 0) {
12063
+ result.updates.storageUpdates.set(
12064
+ id,
12065
+ mergeStorageUpdates(
12066
+ result.updates.storageUpdates.get(id),
12067
+ update
12068
+ )
12069
+ );
12070
+ }
12071
+ }
12072
+ }
12073
+ }
10698
12074
  notify(result.updates);
10699
12075
  } else {
10700
12076
  context.root = LiveObject._fromItems(
@@ -10702,7 +12078,7 @@ function createRoom(options, config) {
10702
12078
  context.pool
10703
12079
  );
10704
12080
  }
10705
- const canWrite = _nullishCoalesce(_optionalChain([self, 'access', _254 => _254.get, 'call', _255 => _255(), 'optionalAccess', _256 => _256.canWrite]), () => ( true));
12081
+ const canWrite = _nullishCoalesce(_optionalChain([self, 'access', _263 => _263.get, 'call', _264 => _264(), 'optionalAccess', _265 => _265.canWrite]), () => ( true));
10706
12082
  const serverTopLevelKeys = topLevelKeysOf(nodes);
10707
12083
  const root = context.root;
10708
12084
  disableHistory(() => {
@@ -10719,12 +12095,23 @@ function createRoom(options, config) {
10719
12095
  }
10720
12096
  });
10721
12097
  }
12098
+ function notifyPrivateHistory(event) {
12099
+ if (historyDisabled > 0) return;
12100
+ eventHub.privateHistory.notify(event);
12101
+ }
12102
+ function clearRedoStack() {
12103
+ if (context.redoStack.length === 0) return;
12104
+ const ids = context.redoStack.map((item) => item.id);
12105
+ context.redoStack.length = 0;
12106
+ notifyPrivateHistory({ action: "discard", ids });
12107
+ }
10722
12108
  function reconcileStorageWithNodes(nodes) {
10723
12109
  if (context.root === void 0) {
10724
12110
  throw new Error("Cannot reconcile storage before it is loaded");
10725
12111
  }
10726
12112
  const ops = diffCurrentStorageAgainst(
10727
- new Map(nodes)
12113
+ new Map(nodes),
12114
+ { includeLiveTextUpdates: true }
10728
12115
  );
10729
12116
  if (ops.length === 0) {
10730
12117
  return;
@@ -10743,9 +12130,14 @@ function createRoom(options, config) {
10743
12130
  }
10744
12131
  function _addToRealUndoStack(frames) {
10745
12132
  if (context.undoStack.length >= 50) {
10746
- context.undoStack.shift();
12133
+ const evicted = context.undoStack.shift();
12134
+ if (evicted !== void 0) {
12135
+ notifyPrivateHistory({ action: "discard", ids: [evicted.id] });
12136
+ }
10747
12137
  }
10748
- context.undoStack.push(frames);
12138
+ const id = nextHistoryItemId++;
12139
+ context.undoStack.push({ id, frames });
12140
+ notifyPrivateHistory({ action: "push", id });
10749
12141
  onHistoryChange();
10750
12142
  }
10751
12143
  function addToUndoStack(frames) {
@@ -10769,7 +12161,10 @@ function createRoom(options, config) {
10769
12161
  eventHub.myPresence.notify(context.myPresence.get());
10770
12162
  }
10771
12163
  if (storageUpdates !== void 0 && storageUpdates.size > 0) {
10772
- const updates2 = Array.from(storageUpdates.values());
12164
+ const updates2 = Array.from(storageUpdates.values(), (update) => ({
12165
+ ...update,
12166
+ source: toUpdateSource(update.source)
12167
+ }));
10773
12168
  eventHub.storageBatch.notify(updates2);
10774
12169
  }
10775
12170
  notifyStorageStatus();
@@ -10783,19 +12178,69 @@ function createRoom(options, config) {
10783
12178
  "Internal. Tried to get connection id but connection was never open"
10784
12179
  );
10785
12180
  }
10786
- function applyLocalOps(frames) {
12181
+ const viaByOpId = /* @__PURE__ */ new Map();
12182
+ function viaOfAckedOp(opId) {
12183
+ const via = viaByOpId.get(opId);
12184
+ if (via === void 0) {
12185
+ return "edit";
12186
+ }
12187
+ viaByOpId.delete(opId);
12188
+ return via;
12189
+ }
12190
+ function applyLocalOps(frames, localSource = LOCAL_EDIT) {
10787
12191
  const [pframes, ops] = partition(
10788
12192
  frames,
10789
12193
  (f) => f.type === "presence"
10790
12194
  );
10791
- const opsWithOpIds = ops.map(
12195
+ const restoredTextIds = /* @__PURE__ */ new Map();
12196
+ for (const op of ops) {
12197
+ if (op.type === OpCode.CREATE_TEXT && op.opId === void 0 && context.pool.nodes.get(op.id) === void 0 && !restoredTextIds.has(op.id)) {
12198
+ restoredTextIds.set(op.id, context.pool.generateId());
12199
+ }
12200
+ }
12201
+ const remappedOps = restoredTextIds.size === 0 ? ops : ops.map((op) => {
12202
+ if (op.opId !== void 0) {
12203
+ return op;
12204
+ }
12205
+ const id = restoredTextIds.get(op.id);
12206
+ if (isCreateOp(op)) {
12207
+ const parentId = restoredTextIds.get(op.parentId);
12208
+ const deletedId = op.deletedId === void 0 ? void 0 : restoredTextIds.get(op.deletedId);
12209
+ if (id === void 0 && parentId === void 0 && deletedId === void 0) {
12210
+ return op;
12211
+ }
12212
+ if (op.type === OpCode.CREATE_TEXT && id !== void 0) {
12213
+ return {
12214
+ ...op,
12215
+ id,
12216
+ version: 0,
12217
+ ...parentId === void 0 ? {} : { parentId },
12218
+ ...deletedId === void 0 ? {} : { deletedId }
12219
+ };
12220
+ }
12221
+ return {
12222
+ ...op,
12223
+ ...id === void 0 ? {} : { id },
12224
+ ...parentId === void 0 ? {} : { parentId },
12225
+ ...deletedId === void 0 ? {} : { deletedId }
12226
+ };
12227
+ }
12228
+ return id === void 0 ? op : { ...op, id };
12229
+ });
12230
+ const opsWithOpIds = remappedOps.map(
10792
12231
  (op) => op.opId === void 0 ? { ...op, opId: context.pool.generateOpId() } : op
10793
12232
  );
12233
+ if (localSource.via !== "edit") {
12234
+ for (const op of opsWithOpIds) {
12235
+ viaByOpId.set(op.opId, localSource.via);
12236
+ }
12237
+ }
10794
12238
  const { reverse, updates } = applyOps(
10795
12239
  pframes,
10796
12240
  opsWithOpIds,
10797
12241
  /* isLocal */
10798
- true
12242
+ true,
12243
+ localSource
10799
12244
  );
10800
12245
  return { opsToEmit: opsWithOpIds, reverse, updates };
10801
12246
  }
@@ -10807,7 +12252,7 @@ function createRoom(options, config) {
10807
12252
  false
10808
12253
  );
10809
12254
  }
10810
- function applyOps(pframes, ops, isLocal) {
12255
+ function applyOps(pframes, ops, isLocal, localSource = LOCAL_EDIT) {
10811
12256
  const output = {
10812
12257
  reverse: new Deque(),
10813
12258
  storageUpdates: /* @__PURE__ */ new Map(),
@@ -10836,12 +12281,16 @@ function createRoom(options, config) {
10836
12281
  for (const op of ops) {
10837
12282
  let source;
10838
12283
  if (isLocal) {
10839
- source = 0 /* LOCAL */;
12284
+ source = { ...localSource, optimistic: true };
10840
12285
  } else if (op.opId !== void 0) {
10841
12286
  context.unacknowledgedOps.delete(op.opId);
10842
- source = 2 /* OURS */;
12287
+ source = {
12288
+ origin: "local",
12289
+ via: viaOfAckedOp(op.opId),
12290
+ optimistic: false
12291
+ };
10843
12292
  } else {
10844
- source = 1 /* THEIRS */;
12293
+ source = REMOTE;
10845
12294
  }
10846
12295
  const applyOpResult = applyOp(op, source);
10847
12296
  if (applyOpResult.modified) {
@@ -10856,7 +12305,7 @@ function createRoom(options, config) {
10856
12305
  );
10857
12306
  output.reverse.pushLeft(applyOpResult.reverse);
10858
12307
  }
10859
- if (op.type === OpCode.CREATE_LIST || op.type === OpCode.CREATE_MAP || op.type === OpCode.CREATE_OBJECT || op.type === OpCode.CREATE_FILE) {
12308
+ if (op.type === OpCode.CREATE_LIST || op.type === OpCode.CREATE_MAP || op.type === OpCode.CREATE_OBJECT || op.type === OpCode.CREATE_TEXT || op.type === OpCode.CREATE_FILE) {
10860
12309
  createdNodeIds.add(op.id);
10861
12310
  }
10862
12311
  }
@@ -10876,12 +12325,13 @@ function createRoom(options, config) {
10876
12325
  switch (op.type) {
10877
12326
  case OpCode.DELETE_OBJECT_KEY:
10878
12327
  case OpCode.UPDATE_OBJECT:
12328
+ case OpCode.UPDATE_TEXT:
10879
12329
  case OpCode.DELETE_CRDT: {
10880
12330
  const node = context.pool.nodes.get(op.id);
10881
12331
  if (node === void 0) {
10882
12332
  return { modified: false };
10883
12333
  }
10884
- return node._apply(op, source === 0 /* LOCAL */);
12334
+ return node._apply(op, source);
10885
12335
  }
10886
12336
  case OpCode.SET_PARENT_KEY: {
10887
12337
  const node = context.pool.nodes.get(op.id);
@@ -10900,6 +12350,7 @@ function createRoom(options, config) {
10900
12350
  case OpCode.CREATE_OBJECT:
10901
12351
  case OpCode.CREATE_LIST:
10902
12352
  case OpCode.CREATE_MAP:
12353
+ case OpCode.CREATE_TEXT:
10903
12354
  case OpCode.CREATE_FILE:
10904
12355
  case OpCode.CREATE_REGISTER: {
10905
12356
  if (op.parentId === void 0) {
@@ -10935,7 +12386,7 @@ function createRoom(options, config) {
10935
12386
  }
10936
12387
  context.myPresence.patch(patch);
10937
12388
  if (context.activeBatch) {
10938
- if (_optionalChain([options2, 'optionalAccess', _257 => _257.addToHistory])) {
12389
+ if (_optionalChain([options2, 'optionalAccess', _266 => _266.addToHistory])) {
10939
12390
  context.activeBatch.reverseOps.pushLeft({
10940
12391
  type: "presence",
10941
12392
  data: oldValues
@@ -10944,7 +12395,7 @@ function createRoom(options, config) {
10944
12395
  context.activeBatch.updates.presence = true;
10945
12396
  } else {
10946
12397
  flushNowOrSoon();
10947
- if (_optionalChain([options2, 'optionalAccess', _258 => _258.addToHistory])) {
12398
+ if (_optionalChain([options2, 'optionalAccess', _267 => _267.addToHistory])) {
10948
12399
  addToUndoStack([{ type: "presence", data: oldValues }]);
10949
12400
  }
10950
12401
  notify({ presence: true });
@@ -11123,11 +12574,11 @@ function createRoom(options, config) {
11123
12574
  break;
11124
12575
  }
11125
12576
  case ServerMsgCode.STORAGE_CHUNK:
11126
- _optionalChain([stopwatch, 'optionalAccess', _259 => _259.lap, 'call', _260 => _260()]);
12577
+ _optionalChain([stopwatch, 'optionalAccess', _268 => _268.lap, 'call', _269 => _269()]);
11127
12578
  nodeMapBuffer.append(compactNodesToNodeStream(message.nodes));
11128
12579
  break;
11129
12580
  case ServerMsgCode.STORAGE_STREAM_END: {
11130
- const timing = _optionalChain([stopwatch, 'optionalAccess', _261 => _261.stop, 'call', _262 => _262()]);
12581
+ const timing = _optionalChain([stopwatch, 'optionalAccess', _270 => _270.stop, 'call', _271 => _271()]);
11131
12582
  if (timing) {
11132
12583
  const ms = (v) => `${v.toFixed(1)}ms`;
11133
12584
  const rest = timing.laps.slice(1);
@@ -11154,16 +12605,38 @@ function createRoom(options, config) {
11154
12605
  }
11155
12606
  break;
11156
12607
  }
11157
- // Receiving a RejectedOps message in the client means that the server is no
11158
- // longer in sync with the client. Trying to synchronize the client again by
11159
- // rolling back particular Ops may be hard/impossible. It's fine to not try and
11160
- // accept the out-of-sync reality and throw an error.
12608
+ // Receiving a RejectedOps message means the server refused some of
12609
+ // our ops, so our optimistic local state is out of sync with the
12610
+ // server. For LiveText ops this is a normal (if rare) situation
12611
+ // e.g. a client that was offline long enough to fall outside the
12612
+ // server's retained history window — and we can recover: drop the
12613
+ // rejected pending state and re-fetch the authoritative storage
12614
+ // snapshot. For other ops (e.g. permission rejections), rolling back
12615
+ // particular Ops is hard/impossible, so we keep the old behavior of
12616
+ // accepting the out-of-sync reality and surfacing an error.
11161
12617
  case ServerMsgCode.REJECT_STORAGE_OP: {
11162
12618
  errorWithTitle(
11163
12619
  "Storage mutation rejection error",
11164
12620
  message.reason
11165
12621
  );
11166
- if (process.env.NODE_ENV !== "production") {
12622
+ let needsStorageResync = false;
12623
+ for (const opId of message.opIds) {
12624
+ const rejectedOp = context.unacknowledgedOps.get(opId);
12625
+ context.unacknowledgedOps.delete(opId);
12626
+ context.buffer.storageOperations = context.buffer.storageOperations.filter((op) => op.opId !== opId);
12627
+ viaByOpId.delete(opId);
12628
+ if (rejectedOp !== void 0 && rejectedOp.type === OpCode.UPDATE_TEXT) {
12629
+ const node = context.pool.nodes.get(rejectedOp.id);
12630
+ if (node !== void 0 && isLiveText(node)) {
12631
+ node._rejectPendingOp(opId);
12632
+ needsStorageResync = true;
12633
+ }
12634
+ }
12635
+ }
12636
+ if (needsStorageResync) {
12637
+ refreshStorage();
12638
+ flushNowOrSoon();
12639
+ } else if (process.env.NODE_ENV !== "production") {
11167
12640
  throw new Error(
11168
12641
  `Storage mutations rejected by server: ${message.reason}`
11169
12642
  );
@@ -11262,11 +12735,11 @@ function createRoom(options, config) {
11262
12735
  } else if (pendingFeedsRequests.has(requestId)) {
11263
12736
  const pending = pendingFeedsRequests.get(requestId);
11264
12737
  pendingFeedsRequests.delete(requestId);
11265
- _optionalChain([pending, 'optionalAccess', _263 => _263.reject, 'call', _264 => _264(err)]);
12738
+ _optionalChain([pending, 'optionalAccess', _272 => _272.reject, 'call', _273 => _273(err)]);
11266
12739
  } else if (pendingFeedMessagesRequests.has(requestId)) {
11267
12740
  const pending = pendingFeedMessagesRequests.get(requestId);
11268
12741
  pendingFeedMessagesRequests.delete(requestId);
11269
- _optionalChain([pending, 'optionalAccess', _265 => _265.reject, 'call', _266 => _266(err)]);
12742
+ _optionalChain([pending, 'optionalAccess', _274 => _274.reject, 'call', _275 => _275(err)]);
11270
12743
  }
11271
12744
  eventHub.feeds.notify(message);
11272
12745
  break;
@@ -11420,10 +12893,10 @@ function createRoom(options, config) {
11420
12893
  timeoutId,
11421
12894
  kind,
11422
12895
  feedId,
11423
- messageId: _optionalChain([options2, 'optionalAccess', _267 => _267.messageId]),
11424
- expectedClientMessageId: _optionalChain([options2, 'optionalAccess', _268 => _268.expectedClientMessageId])
12896
+ messageId: _optionalChain([options2, 'optionalAccess', _276 => _276.messageId]),
12897
+ expectedClientMessageId: _optionalChain([options2, 'optionalAccess', _277 => _277.expectedClientMessageId])
11425
12898
  });
11426
- if (kind === "add-message" && _optionalChain([options2, 'optionalAccess', _269 => _269.expectedClientMessageId]) === void 0) {
12899
+ if (kind === "add-message" && _optionalChain([options2, 'optionalAccess', _278 => _278.expectedClientMessageId]) === void 0) {
11427
12900
  const q = _nullishCoalesce(pendingAddMessageFifoByFeed.get(feedId), () => ( []));
11428
12901
  q.push(requestId);
11429
12902
  pendingAddMessageFifoByFeed.set(feedId, q);
@@ -11474,10 +12947,10 @@ function createRoom(options, config) {
11474
12947
  }
11475
12948
  if (!matched) {
11476
12949
  const q = pendingAddMessageFifoByFeed.get(message.feedId);
11477
- const headId = _optionalChain([q, 'optionalAccess', _270 => _270[0]]);
12950
+ const headId = _optionalChain([q, 'optionalAccess', _279 => _279[0]]);
11478
12951
  if (headId !== void 0) {
11479
12952
  const pending = pendingFeedMutations.get(headId);
11480
- if (_optionalChain([pending, 'optionalAccess', _271 => _271.kind]) === "add-message" && pending.expectedClientMessageId === void 0) {
12953
+ if (_optionalChain([pending, 'optionalAccess', _280 => _280.kind]) === "add-message" && pending.expectedClientMessageId === void 0) {
11481
12954
  settleFeedMutation(headId, "ok");
11482
12955
  }
11483
12956
  }
@@ -11513,7 +12986,7 @@ function createRoom(options, config) {
11513
12986
  const unacknowledgedOps2 = [...context.unacknowledgedOps.values()];
11514
12987
  createOrUpdateRootFromMessage(nodes);
11515
12988
  applyAndSendOfflineOps(unacknowledgedOps2);
11516
- _optionalChain([_resolveStoragePromise, 'optionalCall', _272 => _272()]);
12989
+ _optionalChain([_resolveStoragePromise, 'optionalCall', _281 => _281()]);
11517
12990
  notifyStorageStatus();
11518
12991
  eventHub.storageDidLoad.notify();
11519
12992
  }
@@ -11522,7 +12995,7 @@ function createRoom(options, config) {
11522
12995
  if (!messages.some((msg) => msg.type === ClientMsgCode.FETCH_STORAGE)) {
11523
12996
  messages.push({ type: ClientMsgCode.FETCH_STORAGE });
11524
12997
  nodeMapBuffer.take();
11525
- _optionalChain([stopwatch, 'optionalAccess', _273 => _273.start, 'call', _274 => _274()]);
12998
+ _optionalChain([stopwatch, 'optionalAccess', _282 => _282.start, 'call', _283 => _283()]);
11526
12999
  }
11527
13000
  }
11528
13001
  function startLoadingStorage() {
@@ -11576,10 +13049,10 @@ function createRoom(options, config) {
11576
13049
  const message = {
11577
13050
  type: ClientMsgCode.FETCH_FEEDS,
11578
13051
  requestId,
11579
- cursor: _optionalChain([options2, 'optionalAccess', _275 => _275.cursor]),
11580
- since: _optionalChain([options2, 'optionalAccess', _276 => _276.since]),
11581
- limit: _optionalChain([options2, 'optionalAccess', _277 => _277.limit]),
11582
- metadata: _optionalChain([options2, 'optionalAccess', _278 => _278.metadata])
13052
+ cursor: _optionalChain([options2, 'optionalAccess', _284 => _284.cursor]),
13053
+ since: _optionalChain([options2, 'optionalAccess', _285 => _285.since]),
13054
+ limit: _optionalChain([options2, 'optionalAccess', _286 => _286.limit]),
13055
+ metadata: _optionalChain([options2, 'optionalAccess', _287 => _287.metadata])
11583
13056
  };
11584
13057
  context.buffer.messages.push(message);
11585
13058
  flushNowOrSoon();
@@ -11599,9 +13072,9 @@ function createRoom(options, config) {
11599
13072
  type: ClientMsgCode.FETCH_FEED_MESSAGES,
11600
13073
  requestId,
11601
13074
  feedId,
11602
- cursor: _optionalChain([options2, 'optionalAccess', _279 => _279.cursor]),
11603
- since: _optionalChain([options2, 'optionalAccess', _280 => _280.since]),
11604
- limit: _optionalChain([options2, 'optionalAccess', _281 => _281.limit])
13075
+ cursor: _optionalChain([options2, 'optionalAccess', _288 => _288.cursor]),
13076
+ since: _optionalChain([options2, 'optionalAccess', _289 => _289.since]),
13077
+ limit: _optionalChain([options2, 'optionalAccess', _290 => _290.limit])
11605
13078
  };
11606
13079
  context.buffer.messages.push(message);
11607
13080
  flushNowOrSoon();
@@ -11620,8 +13093,8 @@ function createRoom(options, config) {
11620
13093
  type: ClientMsgCode.ADD_FEED,
11621
13094
  requestId,
11622
13095
  feedId,
11623
- metadata: _optionalChain([options2, 'optionalAccess', _282 => _282.metadata]),
11624
- createdAt: _optionalChain([options2, 'optionalAccess', _283 => _283.createdAt])
13096
+ metadata: _optionalChain([options2, 'optionalAccess', _291 => _291.metadata]),
13097
+ createdAt: _optionalChain([options2, 'optionalAccess', _292 => _292.createdAt])
11625
13098
  };
11626
13099
  context.buffer.messages.push(message);
11627
13100
  flushNowOrSoon();
@@ -11655,15 +13128,15 @@ function createRoom(options, config) {
11655
13128
  function addFeedMessage(feedId, data, options2) {
11656
13129
  const requestId = nanoid();
11657
13130
  const promise = registerFeedMutation(requestId, "add-message", feedId, {
11658
- expectedClientMessageId: _optionalChain([options2, 'optionalAccess', _284 => _284.id])
13131
+ expectedClientMessageId: _optionalChain([options2, 'optionalAccess', _293 => _293.id])
11659
13132
  });
11660
13133
  const message = {
11661
13134
  type: ClientMsgCode.ADD_FEED_MESSAGE,
11662
13135
  requestId,
11663
13136
  feedId,
11664
13137
  data,
11665
- id: _optionalChain([options2, 'optionalAccess', _285 => _285.id]),
11666
- createdAt: _optionalChain([options2, 'optionalAccess', _286 => _286.createdAt])
13138
+ id: _optionalChain([options2, 'optionalAccess', _294 => _294.id]),
13139
+ createdAt: _optionalChain([options2, 'optionalAccess', _295 => _295.createdAt])
11667
13140
  };
11668
13141
  context.buffer.messages.push(message);
11669
13142
  flushNowOrSoon();
@@ -11680,7 +13153,7 @@ function createRoom(options, config) {
11680
13153
  feedId,
11681
13154
  messageId,
11682
13155
  data,
11683
- updatedAt: _optionalChain([options2, 'optionalAccess', _287 => _287.updatedAt])
13156
+ updatedAt: _optionalChain([options2, 'optionalAccess', _296 => _296.updatedAt])
11684
13157
  };
11685
13158
  context.buffer.messages.push(message);
11686
13159
  flushNowOrSoon();
@@ -11705,14 +13178,15 @@ function createRoom(options, config) {
11705
13178
  if (context.activeBatch) {
11706
13179
  throw new Error("undo is not allowed during a batch");
11707
13180
  }
11708
- const frames = context.undoStack.pop();
11709
- if (frames === void 0) {
13181
+ const item = context.undoStack.pop();
13182
+ if (item === void 0) {
11710
13183
  return;
11711
13184
  }
11712
13185
  context.pausedHistory = null;
11713
- const result = applyLocalOps(frames);
13186
+ const result = applyLocalOps(item.frames, LOCAL_UNDO);
13187
+ context.redoStack.push({ id: item.id, frames: result.reverse });
13188
+ notifyPrivateHistory({ action: "undo", id: item.id });
11714
13189
  notify(result.updates);
11715
- context.redoStack.push(result.reverse);
11716
13190
  onHistoryChange();
11717
13191
  for (const op of result.opsToEmit) {
11718
13192
  context.buffer.storageOperations.push(op);
@@ -11723,14 +13197,15 @@ function createRoom(options, config) {
11723
13197
  if (context.activeBatch) {
11724
13198
  throw new Error("redo is not allowed during a batch");
11725
13199
  }
11726
- const frames = context.redoStack.pop();
11727
- if (frames === void 0) {
13200
+ const item = context.redoStack.pop();
13201
+ if (item === void 0) {
11728
13202
  return;
11729
13203
  }
11730
13204
  context.pausedHistory = null;
11731
- const result = applyLocalOps(frames);
13205
+ const result = applyLocalOps(item.frames, LOCAL_REDO);
13206
+ context.undoStack.push({ id: item.id, frames: result.reverse });
13207
+ notifyPrivateHistory({ action: "redo", id: item.id });
11732
13208
  notify(result.updates);
11733
- context.undoStack.push(result.reverse);
11734
13209
  onHistoryChange();
11735
13210
  for (const op of result.opsToEmit) {
11736
13211
  context.buffer.storageOperations.push(op);
@@ -11740,6 +13215,8 @@ function createRoom(options, config) {
11740
13215
  function clear() {
11741
13216
  context.undoStack.length = 0;
11742
13217
  context.redoStack.length = 0;
13218
+ notifyPrivateHistory({ action: "clear" });
13219
+ onHistoryChange();
11743
13220
  }
11744
13221
  function batch2(callback) {
11745
13222
  if (context.activeBatch) {
@@ -11767,8 +13244,8 @@ function createRoom(options, config) {
11767
13244
  if (currentBatch.scheduleHistoryResume) {
11768
13245
  commitPausedHistoryToUndoStack();
11769
13246
  }
11770
- if (currentBatch.ops.length > 0) {
11771
- context.redoStack.length = 0;
13247
+ if (currentBatch.ops.length > 0 || currentBatch.clearRedoStack) {
13248
+ clearRedoStack();
11772
13249
  }
11773
13250
  if (currentBatch.ops.length > 0) {
11774
13251
  dispatchOps(currentBatch.ops);
@@ -11797,7 +13274,6 @@ function createRoom(options, config) {
11797
13274
  }
11798
13275
  commitPausedHistoryToUndoStack();
11799
13276
  }
11800
- let historyDisabled = 0;
11801
13277
  function disableHistory(fn) {
11802
13278
  const origUndo = context.undoStack;
11803
13279
  const origRedo = context.redoStack;
@@ -11887,8 +13363,8 @@ function createRoom(options, config) {
11887
13363
  async function getThreads(options2) {
11888
13364
  return httpClient.getThreads({
11889
13365
  roomId,
11890
- query: _optionalChain([options2, 'optionalAccess', _288 => _288.query]),
11891
- cursor: _optionalChain([options2, 'optionalAccess', _289 => _289.cursor])
13366
+ query: _optionalChain([options2, 'optionalAccess', _297 => _297.query]),
13367
+ cursor: _optionalChain([options2, 'optionalAccess', _298 => _298.cursor])
11892
13368
  });
11893
13369
  }
11894
13370
  async function getThread(threadId) {
@@ -12021,7 +13497,7 @@ function createRoom(options, config) {
12021
13497
  function getSubscriptionSettings(options2) {
12022
13498
  return httpClient.getSubscriptionSettings({
12023
13499
  roomId,
12024
- signal: _optionalChain([options2, 'optionalAccess', _290 => _290.signal])
13500
+ signal: _optionalChain([options2, 'optionalAccess', _299 => _299.signal])
12025
13501
  });
12026
13502
  }
12027
13503
  function updateSubscriptionSettings(settings) {
@@ -12043,30 +13519,45 @@ function createRoom(options, config) {
12043
13519
  {
12044
13520
  [kInternal]: {
12045
13521
  get presenceBuffer() {
12046
- return deepClone(_nullishCoalesce(_optionalChain([context, 'access', _291 => _291.buffer, 'access', _292 => _292.presenceUpdates, 'optionalAccess', _293 => _293.data]), () => ( null)));
13522
+ return deepClone(_nullishCoalesce(_optionalChain([context, 'access', _300 => _300.buffer, 'access', _301 => _301.presenceUpdates, 'optionalAccess', _302 => _302.data]), () => ( null)));
12047
13523
  },
12048
13524
  // prettier-ignore
12049
13525
  get undoStack() {
12050
- return deepClone(context.undoStack);
13526
+ return structuredClone(
13527
+ context.undoStack.map((item) => ({
13528
+ id: item.id,
13529
+ frames: item.frames
13530
+ }))
13531
+ );
13532
+ },
13533
+ // prettier-ignore
13534
+ get redoStack() {
13535
+ return structuredClone(
13536
+ context.redoStack.map((item) => ({
13537
+ id: item.id,
13538
+ frames: item.frames
13539
+ }))
13540
+ );
12051
13541
  },
12052
13542
  // prettier-ignore
12053
13543
  get nodeCount() {
12054
13544
  return context.pool.nodes.size;
12055
13545
  },
12056
13546
  // prettier-ignore
13547
+ history: eventHub.privateHistory.observable,
12057
13548
  getYjsProvider() {
12058
13549
  return context.yjsProvider;
12059
13550
  },
12060
13551
  setYjsProvider(newProvider) {
12061
- _optionalChain([context, 'access', _294 => _294.yjsProvider, 'optionalAccess', _295 => _295.off, 'call', _296 => _296("status", yjsStatusDidChange)]);
13552
+ _optionalChain([context, 'access', _303 => _303.yjsProvider, 'optionalAccess', _304 => _304.off, 'call', _305 => _305("status", yjsStatusDidChange)]);
12062
13553
  context.yjsProvider = newProvider;
12063
- _optionalChain([newProvider, 'optionalAccess', _297 => _297.on, 'call', _298 => _298("status", yjsStatusDidChange)]);
13554
+ _optionalChain([newProvider, 'optionalAccess', _306 => _306.on, 'call', _307 => _307("status", yjsStatusDidChange)]);
12064
13555
  context.yjsProviderDidChange.notify();
12065
13556
  },
12066
13557
  yjsProviderDidChange: context.yjsProviderDidChange.observable,
12067
13558
  // send metadata when using a text editor
12068
13559
  reportTextEditor,
12069
- getPermissionMatrix: () => _optionalChain([context, 'access', _299 => _299.dynamicSessionInfoSig, 'access', _300 => _300.get, 'call', _301 => _301(), 'optionalAccess', _302 => _302.permissionMatrix]),
13560
+ getPermissionMatrix: () => _optionalChain([context, 'access', _308 => _308.dynamicSessionInfoSig, 'access', _309 => _309.get, 'call', _310 => _310(), 'optionalAccess', _311 => _311.permissionMatrix]),
12070
13561
  // create a text mention when using a text editor
12071
13562
  createTextMention,
12072
13563
  // delete a text mention when using a text editor
@@ -12129,7 +13620,7 @@ ${dumpPool(
12129
13620
  source.dispose();
12130
13621
  }
12131
13622
  eventHub.roomWillDestroy.notify();
12132
- _optionalChain([context, 'access', _303 => _303.yjsProvider, 'optionalAccess', _304 => _304.off, 'call', _305 => _305("status", yjsStatusDidChange)]);
13623
+ _optionalChain([context, 'access', _312 => _312.yjsProvider, 'optionalAccess', _313 => _313.off, 'call', _314 => _314("status", yjsStatusDidChange)]);
12133
13624
  syncSourceForStorage.destroy();
12134
13625
  syncSourceForYjs.destroy();
12135
13626
  uninstallBgTabSpy();
@@ -12293,7 +13784,7 @@ function makeClassicSubscribeFn(roomId, events, errorEvents) {
12293
13784
  }
12294
13785
  if (isLiveNode(first)) {
12295
13786
  const node = first;
12296
- if (_optionalChain([options, 'optionalAccess', _306 => _306.isDeep])) {
13787
+ if (_optionalChain([options, 'optionalAccess', _315 => _315.isDeep])) {
12297
13788
  const storageCallback = second;
12298
13789
  return subscribeToLiveStructureDeeply(node, storageCallback);
12299
13790
  } else {
@@ -12383,8 +13874,8 @@ function createClient(options) {
12383
13874
  const authManager = createAuthManager(options, (token) => {
12384
13875
  currentUserId.set(() => token.uid);
12385
13876
  });
12386
- const fetchPolyfill = _optionalChain([clientOptions, 'access', _307 => _307.polyfills, 'optionalAccess', _308 => _308.fetch]) || /* istanbul ignore next */
12387
- _optionalChain([globalThis, 'access', _309 => _309.fetch, 'optionalAccess', _310 => _310.bind, 'call', _311 => _311(globalThis)]);
13877
+ const fetchPolyfill = _optionalChain([clientOptions, 'access', _316 => _316.polyfills, 'optionalAccess', _317 => _317.fetch]) || /* istanbul ignore next */
13878
+ _optionalChain([globalThis, 'access', _318 => _318.fetch, 'optionalAccess', _319 => _319.bind, 'call', _320 => _320(globalThis)]);
12388
13879
  const httpClient = createApiClient({
12389
13880
  baseUrl,
12390
13881
  fetchPolyfill,
@@ -12401,7 +13892,7 @@ function createClient(options) {
12401
13892
  delegates: {
12402
13893
  createSocket: makeCreateSocketDelegateForAi(
12403
13894
  baseUrl,
12404
- _optionalChain([clientOptions, 'access', _312 => _312.polyfills, 'optionalAccess', _313 => _313.WebSocket])
13895
+ _optionalChain([clientOptions, 'access', _321 => _321.polyfills, 'optionalAccess', _322 => _322.WebSocket])
12405
13896
  ),
12406
13897
  authenticate: async () => {
12407
13898
  const resp = await authManager.getAuthValue({
@@ -12472,7 +13963,7 @@ function createClient(options) {
12472
13963
  createSocket: makeCreateSocketDelegateForRoom(
12473
13964
  roomId,
12474
13965
  baseUrl,
12475
- _optionalChain([clientOptions, 'access', _314 => _314.polyfills, 'optionalAccess', _315 => _315.WebSocket])
13966
+ _optionalChain([clientOptions, 'access', _323 => _323.polyfills, 'optionalAccess', _324 => _324.WebSocket])
12476
13967
  ),
12477
13968
  authenticate: makeAuthDelegateForRoom(roomId, authManager)
12478
13969
  })),
@@ -12494,7 +13985,7 @@ function createClient(options) {
12494
13985
  const shouldConnect = _nullishCoalesce(options2.autoConnect, () => ( true));
12495
13986
  if (shouldConnect) {
12496
13987
  if (typeof atob === "undefined") {
12497
- if (_optionalChain([clientOptions, 'access', _316 => _316.polyfills, 'optionalAccess', _317 => _317.atob]) === void 0) {
13988
+ if (_optionalChain([clientOptions, 'access', _325 => _325.polyfills, 'optionalAccess', _326 => _326.atob]) === void 0) {
12498
13989
  throw new Error(
12499
13990
  "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"
12500
13991
  );
@@ -12506,7 +13997,7 @@ function createClient(options) {
12506
13997
  return leaseRoom(newRoomDetails);
12507
13998
  }
12508
13999
  function getRoom(roomId) {
12509
- const room = _optionalChain([roomsById, 'access', _318 => _318.get, 'call', _319 => _319(roomId), 'optionalAccess', _320 => _320.room]);
14000
+ const room = _optionalChain([roomsById, 'access', _327 => _327.get, 'call', _328 => _328(roomId), 'optionalAccess', _329 => _329.room]);
12510
14001
  return room ? room : null;
12511
14002
  }
12512
14003
  function logout() {
@@ -12522,7 +14013,7 @@ function createClient(options) {
12522
14013
  const batchedResolveUsers = new Batch(
12523
14014
  async (batchedUserIds) => {
12524
14015
  const userIds = batchedUserIds.flat();
12525
- const users = await _optionalChain([resolveUsers, 'optionalCall', _321 => _321({ userIds })]);
14016
+ const users = await _optionalChain([resolveUsers, 'optionalCall', _330 => _330({ userIds })]);
12526
14017
  warnOnceIf(
12527
14018
  !resolveUsers,
12528
14019
  "Set the resolveUsers option in createClient to specify user info."
@@ -12539,7 +14030,7 @@ function createClient(options) {
12539
14030
  const batchedResolveRoomsInfo = new Batch(
12540
14031
  async (batchedRoomIds) => {
12541
14032
  const roomIds = batchedRoomIds.flat();
12542
- const roomsInfo = await _optionalChain([resolveRoomsInfo, 'optionalCall', _322 => _322({ roomIds })]);
14033
+ const roomsInfo = await _optionalChain([resolveRoomsInfo, 'optionalCall', _331 => _331({ roomIds })]);
12543
14034
  warnOnceIf(
12544
14035
  !resolveRoomsInfo,
12545
14036
  "Set the resolveRoomsInfo option in createClient to specify room info."
@@ -12556,7 +14047,7 @@ function createClient(options) {
12556
14047
  const batchedResolveGroupsInfo = new Batch(
12557
14048
  async (batchedGroupIds) => {
12558
14049
  const groupIds = batchedGroupIds.flat();
12559
- const groupsInfo = await _optionalChain([resolveGroupsInfo, 'optionalCall', _323 => _323({ groupIds })]);
14050
+ const groupsInfo = await _optionalChain([resolveGroupsInfo, 'optionalCall', _332 => _332({ groupIds })]);
12560
14051
  warnOnceIf(
12561
14052
  !resolveGroupsInfo,
12562
14053
  "Set the resolveGroupsInfo option in createClient to specify group info."
@@ -12615,7 +14106,7 @@ function createClient(options) {
12615
14106
  }
12616
14107
  };
12617
14108
  const win = typeof window !== "undefined" ? window : void 0;
12618
- _optionalChain([win, 'optionalAccess', _324 => _324.addEventListener, 'call', _325 => _325("beforeunload", maybePreventClose)]);
14109
+ _optionalChain([win, 'optionalAccess', _333 => _333.addEventListener, 'call', _334 => _334("beforeunload", maybePreventClose)]);
12619
14110
  }
12620
14111
  async function getNotificationSettings(options2) {
12621
14112
  const plainSettings = await httpClient.getNotificationSettings(options2);
@@ -12743,7 +14234,7 @@ var commentBodyElementsTypes = {
12743
14234
  mention: "inline"
12744
14235
  };
12745
14236
  function traverseCommentBody(body, elementOrVisitor, possiblyVisitor) {
12746
- if (!body || !_optionalChain([body, 'optionalAccess', _326 => _326.content])) {
14237
+ if (!body || !_optionalChain([body, 'optionalAccess', _335 => _335.content])) {
12747
14238
  return;
12748
14239
  }
12749
14240
  const element = typeof elementOrVisitor === "string" ? elementOrVisitor : void 0;
@@ -12753,13 +14244,13 @@ function traverseCommentBody(body, elementOrVisitor, possiblyVisitor) {
12753
14244
  for (const block of body.content) {
12754
14245
  if (type === "all" || type === "block") {
12755
14246
  if (guard(block)) {
12756
- _optionalChain([visitor, 'optionalCall', _327 => _327(block)]);
14247
+ _optionalChain([visitor, 'optionalCall', _336 => _336(block)]);
12757
14248
  }
12758
14249
  }
12759
14250
  if (type === "all" || type === "inline") {
12760
14251
  for (const inline of block.children) {
12761
14252
  if (guard(inline)) {
12762
- _optionalChain([visitor, 'optionalCall', _328 => _328(inline)]);
14253
+ _optionalChain([visitor, 'optionalCall', _337 => _337(inline)]);
12763
14254
  }
12764
14255
  }
12765
14256
  }
@@ -12929,7 +14420,7 @@ var stringifyCommentBodyPlainElements = {
12929
14420
  text: ({ element }) => element.text,
12930
14421
  link: ({ element }) => _nullishCoalesce(element.text, () => ( element.url)),
12931
14422
  mention: ({ element, user, group }) => {
12932
- return `@${_nullishCoalesce(_nullishCoalesce(_optionalChain([user, 'optionalAccess', _329 => _329.name]), () => ( _optionalChain([group, 'optionalAccess', _330 => _330.name]))), () => ( element.id))}`;
14423
+ return `@${_nullishCoalesce(_nullishCoalesce(_optionalChain([user, 'optionalAccess', _338 => _338.name]), () => ( _optionalChain([group, 'optionalAccess', _339 => _339.name]))), () => ( element.id))}`;
12933
14424
  }
12934
14425
  };
12935
14426
  var stringifyCommentBodyHtmlElements = {
@@ -12959,7 +14450,7 @@ var stringifyCommentBodyHtmlElements = {
12959
14450
  return html`<a href="${href}" target="_blank" rel="noopener noreferrer">${element.text ? html`${element.text}` : element.url}</a>`;
12960
14451
  },
12961
14452
  mention: ({ element, user, group }) => {
12962
- return html`<span data-mention>@${_optionalChain([user, 'optionalAccess', _331 => _331.name]) ? html`${_optionalChain([user, 'optionalAccess', _332 => _332.name])}` : _optionalChain([group, 'optionalAccess', _333 => _333.name]) ? html`${_optionalChain([group, 'optionalAccess', _334 => _334.name])}` : element.id}</span>`;
14453
+ return html`<span data-mention>@${_optionalChain([user, 'optionalAccess', _340 => _340.name]) ? html`${_optionalChain([user, 'optionalAccess', _341 => _341.name])}` : _optionalChain([group, 'optionalAccess', _342 => _342.name]) ? html`${_optionalChain([group, 'optionalAccess', _343 => _343.name])}` : element.id}</span>`;
12963
14454
  }
12964
14455
  };
12965
14456
  var stringifyCommentBodyMarkdownElements = {
@@ -12989,20 +14480,20 @@ var stringifyCommentBodyMarkdownElements = {
12989
14480
  return markdown`[${_nullishCoalesce(element.text, () => ( element.url))}](${href})`;
12990
14481
  },
12991
14482
  mention: ({ element, user, group }) => {
12992
- return markdown`@${_nullishCoalesce(_nullishCoalesce(_optionalChain([user, 'optionalAccess', _335 => _335.name]), () => ( _optionalChain([group, 'optionalAccess', _336 => _336.name]))), () => ( element.id))}`;
14483
+ return markdown`@${_nullishCoalesce(_nullishCoalesce(_optionalChain([user, 'optionalAccess', _344 => _344.name]), () => ( _optionalChain([group, 'optionalAccess', _345 => _345.name]))), () => ( element.id))}`;
12993
14484
  }
12994
14485
  };
12995
14486
  async function stringifyCommentBody(body, options) {
12996
- const format = _nullishCoalesce(_optionalChain([options, 'optionalAccess', _337 => _337.format]), () => ( "plain"));
12997
- const separator = _nullishCoalesce(_optionalChain([options, 'optionalAccess', _338 => _338.separator]), () => ( (format === "markdown" ? "\n\n" : "\n")));
14487
+ const format = _nullishCoalesce(_optionalChain([options, 'optionalAccess', _346 => _346.format]), () => ( "plain"));
14488
+ const separator = _nullishCoalesce(_optionalChain([options, 'optionalAccess', _347 => _347.separator]), () => ( (format === "markdown" ? "\n\n" : "\n")));
12998
14489
  const elements = {
12999
14490
  ...format === "html" ? stringifyCommentBodyHtmlElements : format === "markdown" ? stringifyCommentBodyMarkdownElements : stringifyCommentBodyPlainElements,
13000
- ..._optionalChain([options, 'optionalAccess', _339 => _339.elements])
14491
+ ..._optionalChain([options, 'optionalAccess', _348 => _348.elements])
13001
14492
  };
13002
14493
  const { users: resolvedUsers, groups: resolvedGroupsInfo } = await resolveMentionsInCommentBody(
13003
14494
  body,
13004
- _optionalChain([options, 'optionalAccess', _340 => _340.resolveUsers]),
13005
- _optionalChain([options, 'optionalAccess', _341 => _341.resolveGroupsInfo])
14495
+ _optionalChain([options, 'optionalAccess', _349 => _349.resolveUsers]),
14496
+ _optionalChain([options, 'optionalAccess', _350 => _350.resolveGroupsInfo])
13006
14497
  );
13007
14498
  const blocks = body.content.flatMap((block, blockIndex) => {
13008
14499
  switch (block.type) {
@@ -13084,6 +14575,12 @@ function toPlainLson(lson) {
13084
14575
  liveblocksType: "LiveList",
13085
14576
  data: [...lson].map((item) => toPlainLson(item))
13086
14577
  };
14578
+ } else if (lson instanceof LiveText) {
14579
+ return {
14580
+ liveblocksType: "LiveText",
14581
+ data: lson.toJSON(),
14582
+ version: lson.version
14583
+ };
13087
14584
  } else if (lson instanceof LiveFile) {
13088
14585
  return {
13089
14586
  liveblocksType: "LiveFile",
@@ -13142,9 +14639,9 @@ function makePoller(callback, intervalMs, options) {
13142
14639
  const startTime = performance.now();
13143
14640
  const doc = typeof document !== "undefined" ? document : void 0;
13144
14641
  const win = typeof window !== "undefined" ? window : void 0;
13145
- const maxStaleTimeMs = _nullishCoalesce(_optionalChain([options, 'optionalAccess', _342 => _342.maxStaleTimeMs]), () => ( Number.POSITIVE_INFINITY));
14642
+ const maxStaleTimeMs = _nullishCoalesce(_optionalChain([options, 'optionalAccess', _351 => _351.maxStaleTimeMs]), () => ( Number.POSITIVE_INFINITY));
13146
14643
  const context = {
13147
- inForeground: _optionalChain([doc, 'optionalAccess', _343 => _343.visibilityState]) !== "hidden",
14644
+ inForeground: _optionalChain([doc, 'optionalAccess', _352 => _352.visibilityState]) !== "hidden",
13148
14645
  lastSuccessfulPollAt: startTime,
13149
14646
  count: 0,
13150
14647
  backoff: 0
@@ -13225,11 +14722,11 @@ function makePoller(callback, intervalMs, options) {
13225
14722
  pollNowIfStale();
13226
14723
  }
13227
14724
  function onVisibilityChange() {
13228
- setInForeground(_optionalChain([doc, 'optionalAccess', _344 => _344.visibilityState]) !== "hidden");
14725
+ setInForeground(_optionalChain([doc, 'optionalAccess', _353 => _353.visibilityState]) !== "hidden");
13229
14726
  }
13230
- _optionalChain([doc, 'optionalAccess', _345 => _345.addEventListener, 'call', _346 => _346("visibilitychange", onVisibilityChange)]);
13231
- _optionalChain([win, 'optionalAccess', _347 => _347.addEventListener, 'call', _348 => _348("online", onVisibilityChange)]);
13232
- _optionalChain([win, 'optionalAccess', _349 => _349.addEventListener, 'call', _350 => _350("focus", pollNowIfStale)]);
14727
+ _optionalChain([doc, 'optionalAccess', _354 => _354.addEventListener, 'call', _355 => _355("visibilitychange", onVisibilityChange)]);
14728
+ _optionalChain([win, 'optionalAccess', _356 => _356.addEventListener, 'call', _357 => _357("online", onVisibilityChange)]);
14729
+ _optionalChain([win, 'optionalAccess', _358 => _358.addEventListener, 'call', _359 => _359("focus", pollNowIfStale)]);
13233
14730
  fsm.start();
13234
14731
  return {
13235
14732
  inc,
@@ -13378,5 +14875,10 @@ detectDupes(PKG_NAME, PKG_VERSION, PKG_FORMAT);
13378
14875
 
13379
14876
 
13380
14877
 
13381
- exports.ClientMsgCode = ClientMsgCode; exports.CrdtType = CrdtType; exports.DefaultMap = DefaultMap; exports.Deque = Deque; exports.DerivedSignal = DerivedSignal; exports.FeedRequestErrorCode = FeedRequestErrorCode; exports.HttpError = HttpError; exports.LiveFile = LiveFile; 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.createStorageFileId = createStorageFileId; 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.getLiveFileId = getLiveFileId; exports.getMentionsFromCommentBody = getMentionsFromCommentBody; exports.getSubscriptionKey = getSubscriptionKey; exports.hasPermissionAccess = hasPermissionAccess; exports.html = html; exports.htmlSafe = htmlSafe; exports.isCommentBodyLink = isCommentBodyLink; exports.isCommentBodyMention = isCommentBodyMention; exports.isCommentBodyText = isCommentBodyText; exports.isFileStorageNode = isFileStorageNode; 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.mergeRoomPermissionScopes = mergeRoomPermissionScopes; exports.nanoid = nanoid; exports.nn = nn; exports.nodeStreamToCompactNodes = nodeStreamToCompactNodes; exports.normalizeRoomAccesses = normalizeRoomAccesses; exports.normalizeRoomPermissions = normalizeRoomPermissions; exports.normalizeUpdateRoomAccesses = normalizeUpdateRoomAccesses; exports.objectToQuery = objectToQuery; exports.patchNotificationSettings = patchNotificationSettings; exports.permissionMatrixFromScopes = permissionMatrixFromScopes; 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.validatePermissionsSet = validatePermissionsSet; exports.wait = wait; exports.warnOnce = warnOnce; exports.warnOnceIf = warnOnceIf; exports.withTimeout = withTimeout;
14878
+
14879
+
14880
+
14881
+
14882
+
14883
+ exports.ClientMsgCode = ClientMsgCode; exports.CrdtType = CrdtType; exports.DefaultMap = DefaultMap; exports.Deque = Deque; exports.DerivedSignal = DerivedSignal; exports.FeedRequestErrorCode = FeedRequestErrorCode; exports.HttpError = HttpError; exports.LiveFile = LiveFile; exports.LiveList = LiveList; exports.LiveMap = LiveMap; exports.LiveObject = LiveObject; exports.LiveText = LiveText; 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.applyLiveTextOperations = applyLiveTextOperations; 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.createStorageFileId = createStorageFileId; 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.getLiveFileId = getLiveFileId; exports.getMentionsFromCommentBody = getMentionsFromCommentBody; exports.getSubscriptionKey = getSubscriptionKey; exports.hasPermissionAccess = hasPermissionAccess; exports.html = html; exports.htmlSafe = htmlSafe; exports.isCommentBodyLink = isCommentBodyLink; exports.isCommentBodyMention = isCommentBodyMention; exports.isCommentBodyText = isCommentBodyText; exports.isFileStorageNode = isFileStorageNode; 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.isTextStorageNode = isTextStorageNode; 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.mergeRoomPermissionScopes = mergeRoomPermissionScopes; exports.nanoid = nanoid; exports.nn = nn; exports.nodeStreamToCompactNodes = nodeStreamToCompactNodes; exports.normalizeLiveTextOperations = normalizeLiveTextOperations; exports.normalizeRoomAccesses = normalizeRoomAccesses; exports.normalizeRoomPermissions = normalizeRoomPermissions; exports.normalizeUpdateRoomAccesses = normalizeUpdateRoomAccesses; exports.objectToQuery = objectToQuery; exports.patchNotificationSettings = patchNotificationSettings; exports.permissionMatrixFromScopes = permissionMatrixFromScopes; 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.transformTextOperations = transformTextOperations; exports.tryParseJson = tryParseJson; exports.url = url; exports.urljoin = urljoin; exports.validatePermissionsSet = validatePermissionsSet; exports.wait = wait; exports.warnOnce = warnOnce; exports.warnOnceIf = warnOnceIf; exports.withTimeout = withTimeout;
13382
14884
  //# sourceMappingURL=index.cjs.map