@liveblocks/core 3.23.0 → 3.23.1-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.0";
9
+ var PKG_VERSION = "3.23.1-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,10 @@ function* nodeStreamToCompactNodes(nodes) {
1087
1105
  }
1088
1106
  }
1089
1107
 
1108
+ // src/internal.ts
1109
+ var kInternal = /* @__PURE__ */ Symbol();
1110
+ var kStorageUpdateSource = /* @__PURE__ */ Symbol();
1111
+
1090
1112
  // src/lib/position.ts
1091
1113
  var MIN_CODE = 32;
1092
1114
  var MAX_CODE = 126;
@@ -1284,6 +1306,10 @@ ${parentKey}`;
1284
1306
  get size() {
1285
1307
  return this.#byOpId.size;
1286
1308
  }
1309
+ /** The still-unacknowledged op with the given opId, if any. */
1310
+ get(opId) {
1311
+ return this.#byOpId.get(opId);
1312
+ }
1287
1313
  /**
1288
1314
  * Mark the given Op as still unacknowledged.
1289
1315
  */
@@ -1383,8 +1409,8 @@ function createManagedPool(options) {
1383
1409
  deleteNode: (id) => void nodes.delete(id),
1384
1410
  generateId: () => `${getCurrentConnectionId()}:${clock++}`,
1385
1411
  generateOpId: () => `${getCurrentConnectionId()}:${opClock++}`,
1386
- dispatch(ops, reverse, storageUpdates) {
1387
- _optionalChain([onDispatch, 'optionalCall', _23 => _23(ops, reverse, storageUpdates)]);
1412
+ dispatch(ops, reverse, storageUpdates, options2) {
1413
+ _optionalChain([onDispatch, 'optionalCall', _23 => _23(ops, reverse, storageUpdates, options2)]);
1388
1414
  },
1389
1415
  assertStorageIsWritable: () => {
1390
1416
  if (!isStorageWritable()) {
@@ -1407,10 +1433,17 @@ function Orphaned(oldKey, oldPos = asPos(oldKey)) {
1407
1433
  return Object.freeze({ type: "Orphaned", oldKey, oldPos });
1408
1434
  }
1409
1435
  var AbstractCrdt = class {
1410
- // ^^^^^^^^^^^^ TODO: Make this an interface
1411
1436
  #pool;
1412
1437
  #id;
1413
1438
  #parent = NoParent;
1439
+ constructor() {
1440
+ Object.defineProperty(this, kInternal, {
1441
+ value: {
1442
+ getId: () => this.#id
1443
+ },
1444
+ enumerable: false
1445
+ });
1446
+ }
1414
1447
  /** @internal */
1415
1448
  _getParentKeyOrThrow() {
1416
1449
  switch (this.parent.type) {
@@ -4877,9 +4910,6 @@ var ManagedSocket = class {
4877
4910
  }
4878
4911
  };
4879
4912
 
4880
- // src/internal.ts
4881
- var kInternal = /* @__PURE__ */ Symbol();
4882
-
4883
4913
  // src/lib/IncrementalJsonParser.ts
4884
4914
  var EMPTY_OBJECT = Object.freeze({});
4885
4915
  var NULL_KEYWORD_CHARS = Array.from(new Set("null"));
@@ -9179,6 +9209,1116 @@ var LiveObject = (_class2 = class _LiveObject extends AbstractCrdt {
9179
9209
  }
9180
9210
  }, _class2.__initStatic(), _class2);
9181
9211
 
9212
+ // src/crdts/liveTextOps.ts
9213
+ function attributesEqual(left, right) {
9214
+ if (left === right) {
9215
+ return true;
9216
+ }
9217
+ if (left === void 0 || right === void 0) {
9218
+ return false;
9219
+ }
9220
+ const leftKeys = Object.keys(left);
9221
+ const rightKeys = Object.keys(right);
9222
+ if (leftKeys.length !== rightKeys.length) {
9223
+ return false;
9224
+ }
9225
+ for (const key of leftKeys) {
9226
+ if (left[key] !== right[key]) {
9227
+ return false;
9228
+ }
9229
+ }
9230
+ return true;
9231
+ }
9232
+ function cloneAttributes(attributes) {
9233
+ return attributes === void 0 ? void 0 : freeze({ ...attributes });
9234
+ }
9235
+ function normalizeSegments(segments) {
9236
+ const normalized = [];
9237
+ for (const segment of segments) {
9238
+ if (segment.text.length === 0) {
9239
+ continue;
9240
+ }
9241
+ const last = normalized.at(-1);
9242
+ const attributes = cloneAttributes(segment.attributes);
9243
+ if (last !== void 0 && attributesEqual(last.attributes, attributes)) {
9244
+ last.text += segment.text;
9245
+ } else {
9246
+ normalized.push({ text: segment.text, attributes });
9247
+ }
9248
+ }
9249
+ return normalized;
9250
+ }
9251
+ function dataToSegments(data) {
9252
+ return normalizeSegments(
9253
+ data.map(([text, attributes]) => ({
9254
+ text,
9255
+ attributes
9256
+ }))
9257
+ );
9258
+ }
9259
+ function segmentsToData(segments) {
9260
+ return segments.map(
9261
+ (segment) => segment.attributes === void 0 ? [segment.text] : [segment.text, { ...segment.attributes }]
9262
+ );
9263
+ }
9264
+ function textLength(segments) {
9265
+ return segments.reduce((sum, segment) => sum + segment.text.length, 0);
9266
+ }
9267
+ function splitSegmentsAt(segments, index) {
9268
+ const result = [];
9269
+ let offset = 0;
9270
+ for (const segment of segments) {
9271
+ const end = offset + segment.text.length;
9272
+ if (index > offset && index < end) {
9273
+ const before2 = segment.text.slice(0, index - offset);
9274
+ const after2 = segment.text.slice(index - offset);
9275
+ result.push({ text: before2, attributes: segment.attributes });
9276
+ result.push({ text: after2, attributes: segment.attributes });
9277
+ } else {
9278
+ result.push({ text: segment.text, attributes: segment.attributes });
9279
+ }
9280
+ offset = end;
9281
+ }
9282
+ return result;
9283
+ }
9284
+ function clipRange(index, length, contentLength) {
9285
+ const clippedIndex = Math.max(0, Math.min(index, contentLength));
9286
+ const clippedEnd = Math.max(
9287
+ clippedIndex,
9288
+ Math.min(index + length, contentLength)
9289
+ );
9290
+ return { index: clippedIndex, length: clippedEnd - clippedIndex };
9291
+ }
9292
+ function applyInsert(segments, index, text, attributes) {
9293
+ if (text.length === 0) {
9294
+ return normalizeSegments(segments);
9295
+ }
9296
+ const split = splitSegmentsAt(segments, index);
9297
+ const result = [];
9298
+ let offset = 0;
9299
+ let inserted = false;
9300
+ for (const segment of split) {
9301
+ if (!inserted && offset === index) {
9302
+ result.push({ text, attributes });
9303
+ inserted = true;
9304
+ }
9305
+ result.push(segment);
9306
+ offset += segment.text.length;
9307
+ }
9308
+ if (!inserted) {
9309
+ result.push({ text, attributes });
9310
+ }
9311
+ return normalizeSegments(result);
9312
+ }
9313
+ function extractDeletedSegments(segments, index, length) {
9314
+ const split = splitSegmentsAt(
9315
+ splitSegmentsAt(segments, index),
9316
+ index + length
9317
+ );
9318
+ const deleted = [];
9319
+ let offset = 0;
9320
+ for (const segment of split) {
9321
+ const end = offset + segment.text.length;
9322
+ if (offset >= index && end <= index + length) {
9323
+ deleted.push({
9324
+ text: segment.text,
9325
+ attributes: segment.attributes
9326
+ });
9327
+ }
9328
+ offset = end;
9329
+ }
9330
+ return normalizeSegments(deleted);
9331
+ }
9332
+ function applyDelete(segments, index, length) {
9333
+ const deletedSegments = extractDeletedSegments(segments, index, length);
9334
+ const split = splitSegmentsAt(
9335
+ splitSegmentsAt(segments, index),
9336
+ index + length
9337
+ );
9338
+ const result = [];
9339
+ let offset = 0;
9340
+ let deletedText = "";
9341
+ for (const segment of split) {
9342
+ const end = offset + segment.text.length;
9343
+ if (offset >= index && end <= index + length) {
9344
+ deletedText += segment.text;
9345
+ } else {
9346
+ result.push(segment);
9347
+ }
9348
+ offset = end;
9349
+ }
9350
+ return {
9351
+ segments: normalizeSegments(result),
9352
+ deletedText,
9353
+ deletedSegments
9354
+ };
9355
+ }
9356
+ function applyFormat(segments, index, length, attributes) {
9357
+ const split = splitSegmentsAt(
9358
+ splitSegmentsAt(segments, index),
9359
+ index + length
9360
+ );
9361
+ const result = [];
9362
+ let offset = 0;
9363
+ for (const segment of split) {
9364
+ const end = offset + segment.text.length;
9365
+ if (offset >= index && end <= index + length) {
9366
+ const nextAttributes = {
9367
+ ..._nullishCoalesce(segment.attributes, () => ( {}))
9368
+ };
9369
+ for (const [key, value] of Object.entries(attributes)) {
9370
+ if (value === null) {
9371
+ delete nextAttributes[key];
9372
+ } else {
9373
+ nextAttributes[key] = value;
9374
+ }
9375
+ }
9376
+ result.push({
9377
+ text: segment.text,
9378
+ attributes: Object.keys(nextAttributes).length === 0 ? void 0 : freeze(nextAttributes)
9379
+ });
9380
+ } else {
9381
+ result.push(segment);
9382
+ }
9383
+ offset = end;
9384
+ }
9385
+ return normalizeSegments(result);
9386
+ }
9387
+ function formatReverseOperations(segments, index, length, patch) {
9388
+ const split = splitSegmentsAt(
9389
+ splitSegmentsAt(segments, index),
9390
+ index + length
9391
+ );
9392
+ const result = [];
9393
+ let offset = 0;
9394
+ for (const segment of split) {
9395
+ const end = offset + segment.text.length;
9396
+ if (offset >= index && end <= index + length) {
9397
+ const attributes = {};
9398
+ const current = _nullishCoalesce(segment.attributes, () => ( {}));
9399
+ for (const key of Object.keys(patch)) {
9400
+ const value = Object.hasOwn(current, key) ? current[key] : void 0;
9401
+ attributes[key] = _nullishCoalesce(value, () => ( null));
9402
+ }
9403
+ result.push({
9404
+ type: "format",
9405
+ index: offset,
9406
+ length: segment.text.length,
9407
+ attributes
9408
+ });
9409
+ }
9410
+ offset = end;
9411
+ }
9412
+ return result;
9413
+ }
9414
+ function mapIndexThroughOperation(index, op) {
9415
+ if (op.type === "insert") {
9416
+ return op.index <= index ? index + op.text.length : index;
9417
+ } else if (op.type === "delete") {
9418
+ if (op.index >= index) {
9419
+ return index;
9420
+ }
9421
+ return Math.max(op.index, index - op.length);
9422
+ } else {
9423
+ return index;
9424
+ }
9425
+ }
9426
+ function mapTextIndexThroughOperations(index, ops) {
9427
+ let mapped = index;
9428
+ for (const op of ops) {
9429
+ mapped = mapIndexThroughOperation(mapped, op);
9430
+ }
9431
+ return mapped;
9432
+ }
9433
+ function inverseMapIndexThroughOperation(index, op) {
9434
+ if (op.type === "insert") {
9435
+ if (index <= op.index) {
9436
+ return index;
9437
+ }
9438
+ return Math.max(op.index, index - op.text.length);
9439
+ } else if (op.type === "delete") {
9440
+ return op.index <= index ? index + op.length : index;
9441
+ } else {
9442
+ return index;
9443
+ }
9444
+ }
9445
+ function inverseMapTextIndexThroughOperations(index, ops) {
9446
+ let mapped = index;
9447
+ for (let i = ops.length - 1; i >= 0; i--) {
9448
+ mapped = inverseMapIndexThroughOperation(mapped, ops[i]);
9449
+ }
9450
+ return mapped;
9451
+ }
9452
+ function oppositeOrder(order) {
9453
+ return order === "before" ? "after" : "before";
9454
+ }
9455
+ function mapIndexOverDelete(index, deleteIndex, deleteLength) {
9456
+ if (deleteIndex >= index) {
9457
+ return index;
9458
+ }
9459
+ return Math.max(deleteIndex, index - deleteLength);
9460
+ }
9461
+ function transformInsert(op, over, order) {
9462
+ if (over.type === "insert") {
9463
+ const shifts = over.index < op.index || over.index === op.index && order === "after";
9464
+ return [shifts ? { ...op, index: op.index + over.text.length } : { ...op }];
9465
+ } else if (over.type === "delete") {
9466
+ return [
9467
+ { ...op, index: mapIndexOverDelete(op.index, over.index, over.length) }
9468
+ ];
9469
+ } else {
9470
+ return [{ ...op }];
9471
+ }
9472
+ }
9473
+ function transformDelete(op, over) {
9474
+ const start = op.index;
9475
+ const end = op.index + op.length;
9476
+ if (over.type === "insert") {
9477
+ const at = over.index;
9478
+ const len = over.text.length;
9479
+ if (at <= start) {
9480
+ return [{ ...op, index: start + len }];
9481
+ }
9482
+ if (at >= end) {
9483
+ return [{ ...op }];
9484
+ }
9485
+ return [
9486
+ { type: "delete", index: start, length: at - start },
9487
+ { type: "delete", index: start + len, length: end - at }
9488
+ ];
9489
+ } else if (over.type === "delete") {
9490
+ const newStart = mapIndexOverDelete(start, over.index, over.length);
9491
+ const newEnd = mapIndexOverDelete(end, over.index, over.length);
9492
+ return newEnd - newStart > 0 ? [{ type: "delete", index: newStart, length: newEnd - newStart }] : [];
9493
+ } else {
9494
+ return [{ ...op }];
9495
+ }
9496
+ }
9497
+ function transformFormat(op, over, order) {
9498
+ const start = op.index;
9499
+ const end = op.index + op.length;
9500
+ if (over.type === "insert") {
9501
+ const at = over.index;
9502
+ const len = over.text.length;
9503
+ if (at <= start) {
9504
+ return [{ ...op, index: start + len }];
9505
+ }
9506
+ if (at >= end) {
9507
+ return [{ ...op }];
9508
+ }
9509
+ return [
9510
+ {
9511
+ type: "format",
9512
+ index: start,
9513
+ length: at - start,
9514
+ attributes: op.attributes
9515
+ },
9516
+ {
9517
+ type: "format",
9518
+ index: at + len,
9519
+ length: end - at,
9520
+ attributes: op.attributes
9521
+ }
9522
+ ];
9523
+ } else if (over.type === "delete") {
9524
+ const newStart = mapIndexOverDelete(start, over.index, over.length);
9525
+ const newEnd = mapIndexOverDelete(end, over.index, over.length);
9526
+ return newEnd - newStart > 0 ? [
9527
+ {
9528
+ type: "format",
9529
+ index: newStart,
9530
+ length: newEnd - newStart,
9531
+ attributes: op.attributes
9532
+ }
9533
+ ] : [];
9534
+ } else {
9535
+ if (order === "after") {
9536
+ return [{ ...op }];
9537
+ }
9538
+ const overlapStart = Math.max(start, over.index);
9539
+ const overlapEnd = Math.min(end, over.index + over.length);
9540
+ if (overlapStart >= overlapEnd) {
9541
+ return [{ ...op }];
9542
+ }
9543
+ const hasConflict = Object.keys(op.attributes).some(
9544
+ (key) => Object.hasOwn(over.attributes, key)
9545
+ );
9546
+ if (!hasConflict) {
9547
+ return [{ ...op }];
9548
+ }
9549
+ const reduced = {};
9550
+ for (const [key, value] of Object.entries(op.attributes)) {
9551
+ if (!Object.hasOwn(over.attributes, key)) {
9552
+ reduced[key] = value;
9553
+ }
9554
+ }
9555
+ const pieces = [];
9556
+ if (start < overlapStart) {
9557
+ pieces.push({
9558
+ type: "format",
9559
+ index: start,
9560
+ length: overlapStart - start,
9561
+ attributes: op.attributes
9562
+ });
9563
+ }
9564
+ if (Object.keys(reduced).length > 0) {
9565
+ pieces.push({
9566
+ type: "format",
9567
+ index: overlapStart,
9568
+ length: overlapEnd - overlapStart,
9569
+ attributes: reduced
9570
+ });
9571
+ }
9572
+ if (overlapEnd < end) {
9573
+ pieces.push({
9574
+ type: "format",
9575
+ index: overlapEnd,
9576
+ length: end - overlapEnd,
9577
+ attributes: op.attributes
9578
+ });
9579
+ }
9580
+ return pieces;
9581
+ }
9582
+ }
9583
+ function transformSingle(op, over, order) {
9584
+ switch (op.type) {
9585
+ case "insert":
9586
+ return transformInsert(op, over, order);
9587
+ case "delete":
9588
+ return transformDelete(op, over);
9589
+ case "format":
9590
+ return transformFormat(op, over, order);
9591
+ }
9592
+ }
9593
+ function transformTextOperationsX(a, b, order) {
9594
+ if (a.length === 0 || b.length === 0) {
9595
+ return [[...a], [...b]];
9596
+ }
9597
+ if (a.length === 1 && b.length === 1) {
9598
+ return [
9599
+ transformSingle(a[0], b[0], order),
9600
+ transformSingle(b[0], a[0], oppositeOrder(order))
9601
+ ];
9602
+ }
9603
+ if (a.length > 1) {
9604
+ const [headA1, b1] = transformTextOperationsX([a[0]], b, order);
9605
+ const [restA1, b2] = transformTextOperationsX(a.slice(1), b1, order);
9606
+ return [[...headA1, ...restA1], b2];
9607
+ }
9608
+ const [a1, headB1] = transformTextOperationsX(a, [b[0]], order);
9609
+ const [a2, restB1] = transformTextOperationsX(a1, b.slice(1), order);
9610
+ return [a2, [...headB1, ...restB1]];
9611
+ }
9612
+ function transformTextOperations(ops, over, order) {
9613
+ return transformTextOperationsX(ops, over, order)[0];
9614
+ }
9615
+ function textOperationsEqual(a, b) {
9616
+ return a === b || stableStringify(a) === stableStringify(b);
9617
+ }
9618
+ function applyTextOperationsToSegments(segments, ops) {
9619
+ let next = [...segments];
9620
+ for (const op of ops) {
9621
+ if (op.type === "insert") {
9622
+ const index = Math.max(0, Math.min(op.index, textLength(next)));
9623
+ next = applyInsert(next, index, op.text, op.attributes);
9624
+ } else if (op.type === "delete") {
9625
+ const index = Math.max(0, Math.min(op.index, textLength(next)));
9626
+ const clipped = clipRange(index, op.length, textLength(next));
9627
+ next = applyDelete(next, clipped.index, clipped.length).segments;
9628
+ } else {
9629
+ const index = Math.max(0, Math.min(op.index, textLength(next)));
9630
+ const clipped = clipRange(index, op.length, textLength(next));
9631
+ next = applyFormat(next, clipped.index, clipped.length, op.attributes);
9632
+ }
9633
+ }
9634
+ return next;
9635
+ }
9636
+ function applyLiveTextOperations(data, ops) {
9637
+ return segmentsToData(
9638
+ applyTextOperationsToSegments(dataToSegments(data), ops)
9639
+ );
9640
+ }
9641
+ function invertTextOperations(segments, ops) {
9642
+ let shadow = [...segments];
9643
+ const reverse = [];
9644
+ for (const op of ops) {
9645
+ if (op.type === "insert") {
9646
+ shadow = applyInsert(shadow, op.index, op.text, op.attributes);
9647
+ reverse.unshift({
9648
+ type: "delete",
9649
+ index: op.index,
9650
+ length: op.text.length
9651
+ });
9652
+ } else if (op.type === "delete") {
9653
+ const deletedSegments = extractDeletedSegments(
9654
+ shadow,
9655
+ op.index,
9656
+ op.length
9657
+ );
9658
+ shadow = applyDelete(shadow, op.index, op.length).segments;
9659
+ const inserts = [];
9660
+ let insertIndex = op.index;
9661
+ for (const segment of deletedSegments) {
9662
+ inserts.push({
9663
+ type: "insert",
9664
+ index: insertIndex,
9665
+ text: segment.text,
9666
+ attributes: segment.attributes
9667
+ });
9668
+ insertIndex += segment.text.length;
9669
+ }
9670
+ for (let index = inserts.length - 1; index >= 0; index--) {
9671
+ reverse.unshift(inserts[index]);
9672
+ }
9673
+ } else {
9674
+ const inverse = formatReverseOperations(
9675
+ shadow,
9676
+ op.index,
9677
+ op.length,
9678
+ op.attributes
9679
+ );
9680
+ shadow = applyFormat(shadow, op.index, op.length, op.attributes);
9681
+ reverse.unshift(...inverse.reverse());
9682
+ }
9683
+ }
9684
+ return reverse;
9685
+ }
9686
+
9687
+ // src/crdts/LiveText.ts
9688
+ var ACCEPTED_OPS_HISTORY_LIMIT = 1e3;
9689
+ var LiveText = class _LiveText extends AbstractCrdt {
9690
+ /** The local document: #confirmed ⊕ #inFlightOps ⊕ #queuedOps. */
9691
+ #segments;
9692
+ /** The server-confirmed document (only authoritative ops applied). */
9693
+ #confirmed;
9694
+ #version;
9695
+ /** The op currently awaiting server acknowledgement (at most one). */
9696
+ #inFlightOpId;
9697
+ /** Its ops, continuously re-expressed against current server state. */
9698
+ #inFlightOps = [];
9699
+ /** Local edits made while an op is in flight; sent after the ack. */
9700
+ #queuedOps = [];
9701
+ #acceptedOps = [];
9702
+ /**
9703
+ * Creates a new LiveText document.
9704
+ *
9705
+ * @param textOrData Initial plain text, or an array of `[text]` /
9706
+ * `[text, attributes]` segments. Defaults to an empty document.
9707
+ *
9708
+ * @example
9709
+ * new LiveText();
9710
+ * new LiveText("Hello world");
9711
+ * new LiveText([["Hello ", { bold: true }], ["world"]]);
9712
+ */
9713
+ constructor(textOrData = "", version = 0) {
9714
+ super();
9715
+ this.#segments = typeof textOrData === "string" ? textOrData.length === 0 ? [] : [{ text: textOrData }] : dataToSegments(textOrData);
9716
+ this.#confirmed = [...this.#segments];
9717
+ this.#version = version;
9718
+ Object.assign(this[kInternal], {
9719
+ encodeIndex: (localIndex) => this.#encodeIndex(localIndex),
9720
+ decodeIndex: (index, fromVersion) => this.#decodeIndex(index, fromVersion)
9721
+ });
9722
+ }
9723
+ get version() {
9724
+ return this.#version;
9725
+ }
9726
+ get length() {
9727
+ return textLength(this.#segments);
9728
+ }
9729
+ /** @internal */
9730
+ static _deserialize([id, item], _parentToChildren, pool) {
9731
+ const text = new _LiveText(item.data, item.version);
9732
+ text._attach(id, pool);
9733
+ return text;
9734
+ }
9735
+ /** @internal */
9736
+ _toOps(parentId, parentKey) {
9737
+ if (this._id === void 0) {
9738
+ throw new Error("Cannot serialize LiveText if it is not attached");
9739
+ }
9740
+ return [
9741
+ {
9742
+ type: OpCode.CREATE_TEXT,
9743
+ id: this._id,
9744
+ parentId,
9745
+ parentKey,
9746
+ data: this.toJSON(),
9747
+ version: this.#version
9748
+ }
9749
+ ];
9750
+ }
9751
+ /** @internal */
9752
+ _serialize() {
9753
+ if (this.parent.type !== "HasParent") {
9754
+ throw new Error("Cannot serialize LiveText if parent is missing");
9755
+ }
9756
+ return {
9757
+ type: CrdtType.TEXT,
9758
+ parentId: nn(this.parent.node._id, "Parent node expected to have ID"),
9759
+ parentKey: this.parent.key,
9760
+ data: this.toJSON(),
9761
+ version: this.#version
9762
+ };
9763
+ }
9764
+ /** @internal */
9765
+ _attachChild(_op) {
9766
+ throw new Error("LiveText cannot contain child nodes");
9767
+ }
9768
+ /** @internal */
9769
+ _detachChild(_crdt) {
9770
+ throw new Error("LiveText cannot contain child nodes");
9771
+ }
9772
+ /** @internal */
9773
+ _apply(op, isLocal) {
9774
+ if (op.type !== OpCode.UPDATE_TEXT) {
9775
+ return super._apply(op, isLocal);
9776
+ }
9777
+ if (isLocal) {
9778
+ return this.#applyLocal(op);
9779
+ }
9780
+ if (op.opId !== void 0 && op.opId === this.#inFlightOpId) {
9781
+ return this.#applyAck(op);
9782
+ }
9783
+ if (op.opId !== void 0 && this.#acceptedOps.some((entry) => entry.opId === op.opId)) {
9784
+ this.#version = Math.max(this.#version, _nullishCoalesce(op.version, () => ( op.baseVersion + 1)));
9785
+ return { modified: false };
9786
+ }
9787
+ return this.#applyRemote(op);
9788
+ }
9789
+ /**
9790
+ * Inserts text at the given index.
9791
+ *
9792
+ * @param index Character index at which to insert. Values outside the
9793
+ * document range are clipped.
9794
+ * @param text Text to insert.
9795
+ * @param attributes Optional inline attributes for the inserted text.
9796
+ *
9797
+ * @example
9798
+ * const text = new LiveText("Hello");
9799
+ * text.insert(5, " world");
9800
+ * text.insert(0, "Say: ", { italic: true });
9801
+ */
9802
+ insert(index, text, attributes) {
9803
+ const clippedIndex = Math.max(0, Math.min(index, this.length));
9804
+ this.#dispatch([{ type: "insert", index: clippedIndex, text, attributes }]);
9805
+ }
9806
+ /**
9807
+ * Deletes `length` characters starting at `index`.
9808
+ *
9809
+ * @example
9810
+ * const text = new LiveText("Hello world");
9811
+ * text.delete(5, 6); // "Hello"
9812
+ */
9813
+ delete(index, length) {
9814
+ const clipped = clipRange(index, length, this.length);
9815
+ if (clipped.length === 0) {
9816
+ return;
9817
+ }
9818
+ this.#dispatch([
9819
+ { type: "delete", index: clipped.index, length: clipped.length }
9820
+ ]);
9821
+ }
9822
+ /**
9823
+ * Replaces a range of text with new text.
9824
+ *
9825
+ * @example
9826
+ * const text = new LiveText("Hello world");
9827
+ * text.replace(0, 5, "Hi"); // "Hi world"
9828
+ */
9829
+ replace(index, length, text, attributes) {
9830
+ const clipped = clipRange(index, length, this.length);
9831
+ const ops = [];
9832
+ if (clipped.length > 0) {
9833
+ ops.push({
9834
+ type: "delete",
9835
+ index: clipped.index,
9836
+ length: clipped.length
9837
+ });
9838
+ }
9839
+ if (text.length > 0) {
9840
+ ops.push({ type: "insert", index: clipped.index, text, attributes });
9841
+ }
9842
+ this.#dispatch(ops);
9843
+ }
9844
+ /**
9845
+ * Encode a local-document index (an offset into this LiveText's current
9846
+ * #segments, which CodeMirror or any consumer mirrors as its document)
9847
+ * into server-confirmed coordinates suitable for broadcasting to peers via
9848
+ * presence or any other side channel.
9849
+ *
9850
+ * The returned index is in this LiveText's current #confirmed coordinates
9851
+ * — that is, with this client's local pending ops inverse-mapped out.
9852
+ * Pair it with the current {@link LiveText.version} when sending so the
9853
+ * receiver can call {@link PrivateLiveTextApi.decodeIndex} to land the
9854
+ * position in their own local document coordinates regardless of their
9855
+ * private pending ops.
9856
+ *
9857
+ * Index ambiguity at boundaries is resolved by an inverse-of-forward
9858
+ * convention: a position at or before a local insertion is reported as
9859
+ * the position right before the insertion in #confirmed; a position past
9860
+ * the insertion shifts left by the insertion's length. Positions inside
9861
+ * an own-pending insertion collapse to the insertion point.
9862
+ */
9863
+ #encodeIndex(localIndex) {
9864
+ let mapped = Math.max(0, Math.min(localIndex, this.length));
9865
+ mapped = inverseMapTextIndexThroughOperations(mapped, this.#queuedOps);
9866
+ mapped = inverseMapTextIndexThroughOperations(mapped, this.#inFlightOps);
9867
+ return mapped;
9868
+ }
9869
+ /**
9870
+ * Decode an `(index, fromVersion)` pair produced by
9871
+ * {@link PrivateLiveTextApi.encodeIndex} — typically on a peer — into an
9872
+ * offset in this LiveText's current local document (an index suitable for
9873
+ * placing a CodeMirror marker, an annotation anchor, or anything else that
9874
+ * lives over #segments).
9875
+ *
9876
+ * Composes the accepted ops applied since `fromVersion` (drawn from
9877
+ * #acceptedOps in locally-applied form) with this client's own local
9878
+ * pending ops, in that order. The result is in current #segments
9879
+ * coordinates.
9880
+ *
9881
+ * Returns `null` when the position cannot be decoded against the current
9882
+ * state:
9883
+ * - `fromVersion` is greater than this LiveText's current version: the
9884
+ * peer is ahead of us. The caller should park the message and retry
9885
+ * after more accepted ops arrive.
9886
+ * - `fromVersion` falls outside the retained accepted-ops history. This
9887
+ * only happens after very long-lived disconnections; the caller can
9888
+ * fall back to using the raw index and letting subsequent local
9889
+ * transactions map it (with bounded drift).
9890
+ */
9891
+ #decodeIndex(index, fromVersion) {
9892
+ if (fromVersion > this.#version) {
9893
+ return null;
9894
+ }
9895
+ if (fromVersion < this.#version) {
9896
+ const oldest = _optionalChain([this, 'access', _238 => _238.#acceptedOps, 'access', _239 => _239[0], 'optionalAccess', _240 => _240.version]);
9897
+ if (oldest === void 0 || oldest > fromVersion + 1) {
9898
+ return null;
9899
+ }
9900
+ }
9901
+ let mapped = index;
9902
+ for (const entry of this.#acceptedOps) {
9903
+ if (entry.version <= fromVersion) continue;
9904
+ if (entry.version > this.#version) break;
9905
+ if (entry.ops.length === 0) continue;
9906
+ mapped = mapTextIndexThroughOperations(mapped, entry.ops);
9907
+ }
9908
+ mapped = mapTextIndexThroughOperations(mapped, this.#inFlightOps);
9909
+ mapped = mapTextIndexThroughOperations(mapped, this.#queuedOps);
9910
+ return Math.max(0, Math.min(mapped, this.length));
9911
+ }
9912
+ /**
9913
+ * Applies or removes inline attributes on a range of text.
9914
+ *
9915
+ * Set an attribute to `null` to remove it from the range.
9916
+ *
9917
+ * @example
9918
+ * const text = new LiveText("Hello world");
9919
+ * text.format(0, 5, { bold: true });
9920
+ * text.format(0, 5, { bold: null });
9921
+ */
9922
+ format(index, length, attributes) {
9923
+ const clipped = clipRange(index, length, this.length);
9924
+ if (clipped.length === 0) {
9925
+ return;
9926
+ }
9927
+ this.#dispatch([
9928
+ {
9929
+ type: "format",
9930
+ index: clipped.index,
9931
+ length: clipped.length,
9932
+ attributes
9933
+ }
9934
+ ]);
9935
+ }
9936
+ /** Local edits made through the public API. */
9937
+ #dispatch(ops) {
9938
+ if (ops.length === 0) {
9939
+ return;
9940
+ }
9941
+ _optionalChain([this, 'access', _241 => _241._pool, 'optionalAccess', _242 => _242.assertStorageIsWritable, 'call', _243 => _243()]);
9942
+ const attached = this._pool !== void 0 && this._id !== void 0;
9943
+ const reverse = attached ? this.#invertOperations(ops) : [];
9944
+ const changes = this.#applyOperationsLocally(ops);
9945
+ if (!attached) {
9946
+ return;
9947
+ }
9948
+ const pool = nn(this._pool);
9949
+ const id = nn(this._id);
9950
+ const updates = /* @__PURE__ */ new Map([
9951
+ [
9952
+ id,
9953
+ {
9954
+ type: "LiveText",
9955
+ node: this,
9956
+ version: this.#version,
9957
+ updates: changes
9958
+ }
9959
+ ]
9960
+ ]);
9961
+ if (this.#inFlightOpId === void 0) {
9962
+ const opId = pool.generateOpId();
9963
+ this.#inFlightOpId = opId;
9964
+ this.#inFlightOps = [...ops];
9965
+ pool.dispatch(
9966
+ [
9967
+ {
9968
+ type: OpCode.UPDATE_TEXT,
9969
+ id,
9970
+ opId,
9971
+ baseVersion: this.#version,
9972
+ ops: [...ops]
9973
+ }
9974
+ ],
9975
+ reverse,
9976
+ updates
9977
+ );
9978
+ } else {
9979
+ this.#queuedOps.push(...ops);
9980
+ pool.dispatch([], reverse, updates, { clearRedoStack: true });
9981
+ }
9982
+ }
9983
+ /**
9984
+ * A local replay of an existing wire op: an undo/redo frame, or an
9985
+ * unacknowledged op re-sent after a reconnect.
9986
+ */
9987
+ #applyLocal(op) {
9988
+ const mutableOp = op;
9989
+ if (op.opId !== void 0 && op.opId === this.#inFlightOpId) {
9990
+ this.#inFlightOps = [...this.#inFlightOps, ...this.#queuedOps];
9991
+ this.#queuedOps = [];
9992
+ mutableOp.baseVersion = this.#version;
9993
+ mutableOp.ops = [...this.#inFlightOps];
9994
+ return { modified: false };
9995
+ }
9996
+ let ops = op.ops;
9997
+ for (const entry of this.#acceptedOps) {
9998
+ if (entry.version > op.baseVersion && entry.ops.length > 0) {
9999
+ ops = transformTextOperations(ops, entry.ops, "after");
10000
+ }
10001
+ }
10002
+ const reverse = this.#invertOperations(ops);
10003
+ const changes = this.#applyOperationsLocally(ops);
10004
+ if (this.#inFlightOpId === void 0 && ops.length > 0) {
10005
+ this.#inFlightOpId = nn(op.opId, "Local ops must have an opId");
10006
+ this.#inFlightOps = [...ops];
10007
+ mutableOp.baseVersion = this.#version;
10008
+ mutableOp.ops = [...ops];
10009
+ } else {
10010
+ this.#queuedOps.push(...ops);
10011
+ mutableOp.baseVersion = this.#version;
10012
+ mutableOp.ops = [];
10013
+ }
10014
+ if (changes.length === 0) {
10015
+ return { modified: false };
10016
+ }
10017
+ return {
10018
+ reverse,
10019
+ modified: {
10020
+ type: "LiveText",
10021
+ node: this,
10022
+ version: this.#version,
10023
+ updates: changes
10024
+ }
10025
+ };
10026
+ }
10027
+ /** Server acknowledgement of our in-flight op. */
10028
+ #applyAck(op) {
10029
+ const ackedVersion = _nullishCoalesce(op.version, () => ( Math.max(this.#version, op.baseVersion + 1)));
10030
+ const predicted = this.#inFlightOps;
10031
+ const opId = this.#inFlightOpId;
10032
+ this.#confirmed = applyTextOperationsToSegments(this.#confirmed, op.ops);
10033
+ this.#inFlightOpId = void 0;
10034
+ this.#inFlightOps = [];
10035
+ let appliedOps = [];
10036
+ let result = { modified: false };
10037
+ if (!textOperationsEqual(op.ops, predicted)) {
10038
+ error2(
10039
+ "LiveText: acknowledgement did not match the local prediction; resynchronizing"
10040
+ );
10041
+ const rebuilt = this.#rebuildLocalFromConfirmed();
10042
+ appliedOps = rebuilt.appliedOps;
10043
+ if (rebuilt.changes.length > 0) {
10044
+ result = {
10045
+ reverse: [],
10046
+ modified: {
10047
+ type: "LiveText",
10048
+ node: this,
10049
+ version: ackedVersion,
10050
+ updates: rebuilt.changes
10051
+ }
10052
+ };
10053
+ }
10054
+ }
10055
+ this.#version = Math.max(this.#version, ackedVersion);
10056
+ this.#recordAccepted(ackedVersion, appliedOps, opId);
10057
+ this.#flushQueued();
10058
+ return result;
10059
+ }
10060
+ /** An accepted op from another client (or a server-fabricated fix op). */
10061
+ #applyRemote(op) {
10062
+ const version = _nullishCoalesce(op.version, () => ( this.#version + 1));
10063
+ this.#confirmed = applyTextOperationsToSegments(this.#confirmed, op.ops);
10064
+ const [overInFlight, inFlight] = transformTextOperationsX(
10065
+ op.ops,
10066
+ this.#inFlightOps,
10067
+ "before"
10068
+ );
10069
+ const [applied, queued] = transformTextOperationsX(
10070
+ overInFlight,
10071
+ this.#queuedOps,
10072
+ "before"
10073
+ );
10074
+ this.#inFlightOps = inFlight;
10075
+ this.#queuedOps = queued;
10076
+ this.#recordAccepted(version, applied, op.opId);
10077
+ if (applied.length === 0) {
10078
+ this.#version = Math.max(this.#version, version);
10079
+ return { modified: false };
10080
+ }
10081
+ const reverse = this.#invertOperations(applied);
10082
+ const changes = this.#applyOperationsLocally(applied);
10083
+ this.#version = Math.max(this.#version, version);
10084
+ return {
10085
+ reverse,
10086
+ modified: {
10087
+ type: "LiveText",
10088
+ node: this,
10089
+ version: this.#version,
10090
+ updates: changes
10091
+ }
10092
+ };
10093
+ }
10094
+ /** Send the queued ops as the next in-flight op (after an ack). */
10095
+ #flushQueued() {
10096
+ if (this.#queuedOps.length === 0 || this._pool === void 0 || this._id === void 0) {
10097
+ return;
10098
+ }
10099
+ const opId = this._pool.generateOpId();
10100
+ this.#inFlightOpId = opId;
10101
+ this.#inFlightOps = this.#queuedOps;
10102
+ this.#queuedOps = [];
10103
+ this._pool.dispatch(
10104
+ [
10105
+ {
10106
+ type: OpCode.UPDATE_TEXT,
10107
+ id: this._id,
10108
+ opId,
10109
+ baseVersion: this.#version,
10110
+ ops: [...this.#inFlightOps]
10111
+ }
10112
+ ],
10113
+ [],
10114
+ /* @__PURE__ */ new Map(),
10115
+ // The local content was already applied (and made undoable) when the
10116
+ // edits happened; this is purely an outbound flush.
10117
+ { clearRedoStack: false }
10118
+ );
10119
+ }
10120
+ /**
10121
+ * Rebuild the local document as confirmed ⊕ queued ops, returning the
10122
+ * coarse delta that was applied. Only used by defensive recovery paths.
10123
+ */
10124
+ #rebuildLocalFromConfirmed() {
10125
+ const before2 = this.#segments;
10126
+ const after2 = applyTextOperationsToSegments(this.#confirmed, [
10127
+ ...this.#inFlightOps,
10128
+ ...this.#queuedOps
10129
+ ]);
10130
+ if (stableStringify(segmentsToData(before2)) === stableStringify(segmentsToData(after2))) {
10131
+ this.#segments = after2;
10132
+ return { appliedOps: [], changes: [] };
10133
+ }
10134
+ const beforeText = before2.map((segment) => segment.text).join("");
10135
+ this.#segments = after2;
10136
+ this.invalidate();
10137
+ const appliedOps = [];
10138
+ const changes = [];
10139
+ if (beforeText.length > 0) {
10140
+ appliedOps.push({ type: "delete", index: 0, length: beforeText.length });
10141
+ changes.push({
10142
+ type: "delete",
10143
+ index: 0,
10144
+ length: beforeText.length,
10145
+ deletedText: beforeText
10146
+ });
10147
+ }
10148
+ let index = 0;
10149
+ for (const segment of after2) {
10150
+ appliedOps.push({
10151
+ type: "insert",
10152
+ index,
10153
+ text: segment.text,
10154
+ attributes: segment.attributes
10155
+ });
10156
+ changes.push({
10157
+ type: "insert",
10158
+ index,
10159
+ text: segment.text,
10160
+ attributes: segment.attributes
10161
+ });
10162
+ index += segment.text.length;
10163
+ }
10164
+ return { appliedOps, changes };
10165
+ }
10166
+ /**
10167
+ * Reconcile this node against an authoritative storage snapshot (e.g.
10168
+ * after a reconnect). The confirmed state and version are replaced by the
10169
+ * snapshot's; pending (in-flight + queued) ops are preserved on top and
10170
+ * will be re-sent by the offline-ops replay.
10171
+ *
10172
+ * @internal
10173
+ */
10174
+ _resyncText(data, version) {
10175
+ this.#confirmed = dataToSegments(data);
10176
+ this.#version = version;
10177
+ this.#acceptedOps = [];
10178
+ const rebuilt = this.#rebuildLocalFromConfirmed();
10179
+ if (rebuilt.changes.length === 0) {
10180
+ return void 0;
10181
+ }
10182
+ return {
10183
+ type: "LiveText",
10184
+ node: this,
10185
+ version: this.#version,
10186
+ updates: rebuilt.changes
10187
+ };
10188
+ }
10189
+ /**
10190
+ * Called when the server rejected one of our ops. Drops all pending state
10191
+ * for this node (edits queued behind a rejected op cannot be trusted
10192
+ * either); the room follows up with a storage resync.
10193
+ *
10194
+ * @internal
10195
+ */
10196
+ _rejectPendingOp(opId) {
10197
+ if (opId !== this.#inFlightOpId) {
10198
+ return;
10199
+ }
10200
+ this.#inFlightOpId = void 0;
10201
+ this.#inFlightOps = [];
10202
+ this.#queuedOps = [];
10203
+ }
10204
+ #recordAccepted(version, ops, opId) {
10205
+ if (this.#acceptedOps.some((entry) => entry.version === version)) {
10206
+ return;
10207
+ }
10208
+ this.#acceptedOps.push({ version, opId, ops: [...ops] });
10209
+ this.#acceptedOps.sort((left, right) => left.version - right.version);
10210
+ if (this.#acceptedOps.length > ACCEPTED_OPS_HISTORY_LIMIT) {
10211
+ this.#acceptedOps.splice(
10212
+ 0,
10213
+ this.#acceptedOps.length - ACCEPTED_OPS_HISTORY_LIMIT
10214
+ );
10215
+ }
10216
+ }
10217
+ #applyOperationsLocally(ops) {
10218
+ const changes = [];
10219
+ for (const op of ops) {
10220
+ if (op.type === "insert") {
10221
+ this.#segments = applyInsert(
10222
+ this.#segments,
10223
+ op.index,
10224
+ op.text,
10225
+ op.attributes
10226
+ );
10227
+ changes.push({
10228
+ type: "insert",
10229
+ index: op.index,
10230
+ text: op.text,
10231
+ attributes: op.attributes
10232
+ });
10233
+ } else if (op.type === "delete") {
10234
+ const result = applyDelete(this.#segments, op.index, op.length);
10235
+ this.#segments = result.segments;
10236
+ changes.push({
10237
+ type: "delete",
10238
+ index: op.index,
10239
+ length: op.length,
10240
+ deletedText: result.deletedText
10241
+ });
10242
+ } else {
10243
+ this.#segments = applyFormat(
10244
+ this.#segments,
10245
+ op.index,
10246
+ op.length,
10247
+ op.attributes
10248
+ );
10249
+ changes.push({
10250
+ type: "format",
10251
+ index: op.index,
10252
+ length: op.length,
10253
+ attributes: op.attributes
10254
+ });
10255
+ }
10256
+ }
10257
+ this.invalidate();
10258
+ return changes;
10259
+ }
10260
+ #invertOperations(ops) {
10261
+ return [
10262
+ {
10263
+ type: OpCode.UPDATE_TEXT,
10264
+ id: nn(this._id),
10265
+ baseVersion: this.#version,
10266
+ ops: invertTextOperations(this.#segments, ops)
10267
+ }
10268
+ ];
10269
+ }
10270
+ /** Returns the plain text content without attributes. Equivalent to joining the text from each segment in {@link LiveText.toJSON}. */
10271
+ toString() {
10272
+ return this.#segments.map((segment) => segment.text).join("");
10273
+ }
10274
+ /**
10275
+ * Returns a JSON-compatible snapshot of the document as a {@link LiveTextData}
10276
+ * array.
10277
+ *
10278
+ * @example
10279
+ * new LiveText([["Hello ", { bold: true }], ["world"]]).toJSON();
10280
+ * // [["Hello ", { bold: true }], ["world"]]
10281
+ */
10282
+ toJSON() {
10283
+ return super.toJSON();
10284
+ }
10285
+ /** @internal */
10286
+ _toJSON() {
10287
+ return segmentsToData(this.#segments);
10288
+ }
10289
+ /** @internal */
10290
+ toTreeNode(key) {
10291
+ return super.toTreeNode(key);
10292
+ }
10293
+ /** @internal */
10294
+ _toTreeNode(key) {
10295
+ const nodeId = _nullishCoalesce(this._id, () => ( nanoid()));
10296
+ const payload = this.toJSON().map(
10297
+ (segment, index) => ({
10298
+ type: "Json",
10299
+ id: `${nodeId}:${index}`,
10300
+ key: String(index),
10301
+ payload: segment
10302
+ })
10303
+ );
10304
+ payload.push({
10305
+ type: "Json",
10306
+ id: `${nodeId}:version`,
10307
+ key: "version",
10308
+ payload: this.version
10309
+ });
10310
+ return {
10311
+ type: "LiveText",
10312
+ id: nodeId,
10313
+ key,
10314
+ payload
10315
+ };
10316
+ }
10317
+ clone() {
10318
+ return new _LiveText(this.toJSON(), this.#version);
10319
+ }
10320
+ };
10321
+
9182
10322
  // src/crdts/liveblocks-helpers.ts
9183
10323
  function creationOpToLiveNode(op) {
9184
10324
  return lsonToLiveNode(creationOpToLson(op));
@@ -9195,6 +10335,8 @@ function creationOpToLson(op) {
9195
10335
  return new LiveMap();
9196
10336
  case OpCode.CREATE_LIST:
9197
10337
  return new LiveList([]);
10338
+ case OpCode.CREATE_TEXT:
10339
+ return new LiveText(op.data, op.version);
9198
10340
  default:
9199
10341
  return assertNever(op, "Unknown creation Op");
9200
10342
  }
@@ -9227,6 +10369,8 @@ function deserialize(node, parentToChildren, pool) {
9227
10369
  return LiveMap._deserialize(node, parentToChildren, pool);
9228
10370
  } else if (isRegisterStorageNode(node)) {
9229
10371
  return LiveRegister._deserialize(node, parentToChildren, pool);
10372
+ } else if (isTextStorageNode(node)) {
10373
+ return LiveText._deserialize(node, parentToChildren, pool);
9230
10374
  } else if (isFileStorageNode(node)) {
9231
10375
  return LiveFile._deserialize(node, parentToChildren, pool);
9232
10376
  } else {
@@ -9242,6 +10386,8 @@ function deserializeToLson(node, parentToChildren, pool) {
9242
10386
  return LiveMap._deserialize(node, parentToChildren, pool);
9243
10387
  } else if (isRegisterStorageNode(node)) {
9244
10388
  return node[1].data;
10389
+ } else if (isTextStorageNode(node)) {
10390
+ return LiveText._deserialize(node, parentToChildren, pool);
9245
10391
  } else if (isFileStorageNode(node)) {
9246
10392
  return LiveFile._deserialize(node, parentToChildren, pool);
9247
10393
  } else {
@@ -9249,7 +10395,7 @@ function deserializeToLson(node, parentToChildren, pool) {
9249
10395
  }
9250
10396
  }
9251
10397
  function isLiveStructure(value) {
9252
- return isLiveList(value) || isLiveMap(value) || isLiveObject(value) || isLiveFile(value);
10398
+ return isLiveList(value) || isLiveMap(value) || isLiveObject(value) || isLiveText(value) || isLiveFile(value);
9253
10399
  }
9254
10400
  function isLiveNode(value) {
9255
10401
  return isLiveStructure(value) || isLiveRegister(value);
@@ -9263,6 +10409,9 @@ function isLiveMap(value) {
9263
10409
  function isLiveObject(value) {
9264
10410
  return value instanceof LiveObject;
9265
10411
  }
10412
+ function isLiveText(value) {
10413
+ return value instanceof LiveText;
10414
+ }
9266
10415
  function isLiveFile(value) {
9267
10416
  return value instanceof LiveFile;
9268
10417
  }
@@ -9275,14 +10424,14 @@ function cloneLson(value) {
9275
10424
  function liveNodeToLson(obj) {
9276
10425
  if (obj instanceof LiveRegister) {
9277
10426
  return obj.data;
9278
- } else if (obj instanceof LiveList || obj instanceof LiveMap || obj instanceof LiveObject || obj instanceof LiveFile) {
10427
+ } else if (obj instanceof LiveList || obj instanceof LiveMap || obj instanceof LiveObject || obj instanceof LiveText || obj instanceof LiveFile) {
9279
10428
  return obj;
9280
10429
  } else {
9281
10430
  return assertNever(obj, "Unknown AbstractCrdt");
9282
10431
  }
9283
10432
  }
9284
10433
  function lsonToLiveNode(value) {
9285
- if (value instanceof LiveObject || value instanceof LiveMap || value instanceof LiveList || value instanceof LiveFile) {
10434
+ if (value instanceof LiveObject || value instanceof LiveMap || value instanceof LiveList || value instanceof LiveText || value instanceof LiveFile) {
9286
10435
  return value;
9287
10436
  } else {
9288
10437
  return new LiveRegister(value);
@@ -9344,7 +10493,26 @@ function isJsonEq(a, b) {
9344
10493
  }
9345
10494
  return true;
9346
10495
  }
9347
- function diffNodeMap(prev, next) {
10496
+ function liveTextDataToReplaceOps(before2, after2) {
10497
+ const ops = [];
10498
+ const beforeLength = before2.reduce(
10499
+ (length, [text]) => length + text.length,
10500
+ 0
10501
+ );
10502
+ if (beforeLength > 0) {
10503
+ ops.push({ type: "delete", index: 0, length: beforeLength });
10504
+ }
10505
+ let index = 0;
10506
+ for (const [text, attributes] of after2) {
10507
+ if (text.length === 0) {
10508
+ continue;
10509
+ }
10510
+ ops.push({ type: "insert", index, text, attributes });
10511
+ index += text.length;
10512
+ }
10513
+ return ops;
10514
+ }
10515
+ function diffNodeMap(prev, next, options) {
9348
10516
  const ops = [];
9349
10517
  const idsToRecreate = /* @__PURE__ */ new Set();
9350
10518
  next.forEach((nextCrdt, id) => {
@@ -9436,6 +10604,16 @@ function diffNodeMap(prev, next) {
9436
10604
  parentKey: crdt.parentKey
9437
10605
  });
9438
10606
  break;
10607
+ case CrdtType.TEXT:
10608
+ ops.push({
10609
+ type: OpCode.CREATE_TEXT,
10610
+ id,
10611
+ parentId: crdt.parentId,
10612
+ parentKey: crdt.parentKey,
10613
+ data: crdt.data,
10614
+ version: crdt.version
10615
+ });
10616
+ break;
9439
10617
  }
9440
10618
  }
9441
10619
  next.forEach((crdt, id) => {
@@ -9462,6 +10640,17 @@ function diffNodeMap(prev, next) {
9462
10640
  }
9463
10641
  }
9464
10642
  }
10643
+ if (_optionalChain([options, 'optionalAccess', _244 => _244.includeLiveTextUpdates]) === true && crdt.type === CrdtType.TEXT && currentCrdt.type === CrdtType.TEXT && !isJsonEq(crdt.data, currentCrdt.data)) {
10644
+ ops.push({
10645
+ type: OpCode.UPDATE_TEXT,
10646
+ id,
10647
+ // A restore is a new edit in the current timeline. The version from
10648
+ // the historic snapshot describes its old timeline and must not move
10649
+ // this node's current version backwards.
10650
+ baseVersion: currentCrdt.version,
10651
+ ops: liveTextDataToReplaceOps(currentCrdt.data, crdt.data)
10652
+ });
10653
+ }
9465
10654
  if (crdt.parentKey !== currentCrdt.parentKey) {
9466
10655
  ops.push({
9467
10656
  type: OpCode.SET_PARENT_KEY,
@@ -9502,19 +10691,43 @@ function mergeListStorageUpdates(first, second) {
9502
10691
  updates: updates.concat(second.updates)
9503
10692
  };
9504
10693
  }
10694
+ function mergeTextStorageUpdates(first, second) {
10695
+ return {
10696
+ ...second,
10697
+ updates: first.updates.concat(second.updates)
10698
+ };
10699
+ }
9505
10700
  function mergeStorageUpdates(first, second) {
9506
10701
  if (first === void 0) {
9507
10702
  return second;
9508
10703
  }
10704
+ let merged;
9509
10705
  if (first.type === "LiveObject" && second.type === "LiveObject") {
9510
- return mergeObjectStorageUpdates(first, second);
10706
+ merged = mergeObjectStorageUpdates(first, second);
9511
10707
  } else if (first.type === "LiveMap" && second.type === "LiveMap") {
9512
- return mergeMapStorageUpdates(first, second);
10708
+ merged = mergeMapStorageUpdates(first, second);
9513
10709
  } else if (first.type === "LiveList" && second.type === "LiveList") {
9514
- return mergeListStorageUpdates(first, second);
10710
+ merged = mergeListStorageUpdates(first, second);
10711
+ } else if (first.type === "LiveText" && second.type === "LiveText") {
10712
+ merged = mergeTextStorageUpdates(first, second);
9515
10713
  } else {
10714
+ merged = second;
10715
+ }
10716
+ const sa = first[kStorageUpdateSource];
10717
+ const sb = second[kStorageUpdateSource];
10718
+ if (sa !== void 0 || sb !== void 0) {
10719
+ if (_optionalChain([sa, 'optionalAccess', _245 => _245.origin]) === "remote" || _optionalChain([sb, 'optionalAccess', _246 => _246.origin]) === "remote") {
10720
+ merged[kStorageUpdateSource] = { origin: "remote" };
10721
+ } else if (_optionalChain([sa, 'optionalAccess', _247 => _247.via]) === "history" || _optionalChain([sb, 'optionalAccess', _248 => _248.via]) === "history") {
10722
+ const historySource = _optionalChain([sb, 'optionalAccess', _249 => _249.via]) === "history" ? sb : _optionalChain([sa, 'optionalAccess', _250 => _250.via]) === "history" ? sa : void 0;
10723
+ if (_optionalChain([historySource, 'optionalAccess', _251 => _251.via]) === "history") {
10724
+ merged[kStorageUpdateSource] = historySource;
10725
+ }
10726
+ } else {
10727
+ merged[kStorageUpdateSource] = { origin: "local", via: "mutation" };
10728
+ }
9516
10729
  }
9517
- return second;
10730
+ return merged;
9518
10731
  }
9519
10732
 
9520
10733
  // src/devtools/bridge.ts
@@ -9530,7 +10743,7 @@ function sendToPanel(message, options) {
9530
10743
  ...message,
9531
10744
  source: "liveblocks-devtools-client"
9532
10745
  };
9533
- if (!(_optionalChain([options, 'optionalAccess', _238 => _238.force]) || _bridgeActive)) {
10746
+ if (!(_optionalChain([options, 'optionalAccess', _252 => _252.force]) || _bridgeActive)) {
9534
10747
  return;
9535
10748
  }
9536
10749
  window.postMessage(fullMsg, "*");
@@ -9538,7 +10751,7 @@ function sendToPanel(message, options) {
9538
10751
  var eventSource = makeEventSource();
9539
10752
  if (process.env.NODE_ENV !== "production" && typeof window !== "undefined") {
9540
10753
  window.addEventListener("message", (event) => {
9541
- if (event.source === window && _optionalChain([event, 'access', _239 => _239.data, 'optionalAccess', _240 => _240.source]) === "liveblocks-devtools-panel") {
10754
+ if (event.source === window && _optionalChain([event, 'access', _253 => _253.data, 'optionalAccess', _254 => _254.source]) === "liveblocks-devtools-panel") {
9542
10755
  eventSource.notify(event.data);
9543
10756
  } else {
9544
10757
  }
@@ -9680,7 +10893,7 @@ function fullSync(room) {
9680
10893
  msg: "room::sync::full",
9681
10894
  roomId: room.id,
9682
10895
  status: room.getStatus(),
9683
- storage: _nullishCoalesce(_optionalChain([root, 'optionalAccess', _241 => _241.toTreeNode, 'call', _242 => _242("root"), 'access', _243 => _243.payload]), () => ( null)),
10896
+ storage: _nullishCoalesce(_optionalChain([root, 'optionalAccess', _255 => _255.toTreeNode, 'call', _256 => _256("root"), 'access', _257 => _257.payload]), () => ( null)),
9684
10897
  me,
9685
10898
  others
9686
10899
  });
@@ -10367,15 +11580,15 @@ function installBackgroundTabSpy() {
10367
11580
  const doc = typeof document !== "undefined" ? document : void 0;
10368
11581
  const inBackgroundSince = { current: null };
10369
11582
  function onVisibilityChange() {
10370
- if (_optionalChain([doc, 'optionalAccess', _244 => _244.visibilityState]) === "hidden") {
11583
+ if (_optionalChain([doc, 'optionalAccess', _258 => _258.visibilityState]) === "hidden") {
10371
11584
  inBackgroundSince.current = _nullishCoalesce(inBackgroundSince.current, () => ( Date.now()));
10372
11585
  } else {
10373
11586
  inBackgroundSince.current = null;
10374
11587
  }
10375
11588
  }
10376
- _optionalChain([doc, 'optionalAccess', _245 => _245.addEventListener, 'call', _246 => _246("visibilitychange", onVisibilityChange)]);
11589
+ _optionalChain([doc, 'optionalAccess', _259 => _259.addEventListener, 'call', _260 => _260("visibilitychange", onVisibilityChange)]);
10377
11590
  const unsub = () => {
10378
- _optionalChain([doc, 'optionalAccess', _247 => _247.removeEventListener, 'call', _248 => _248("visibilitychange", onVisibilityChange)]);
11591
+ _optionalChain([doc, 'optionalAccess', _261 => _261.removeEventListener, 'call', _262 => _262("visibilitychange", onVisibilityChange)]);
10379
11592
  };
10380
11593
  return [inBackgroundSince, unsub];
10381
11594
  }
@@ -10399,7 +11612,7 @@ function makeNodeMapBuffer() {
10399
11612
  function topLevelKeysOf(nodes) {
10400
11613
  const keys2 = /* @__PURE__ */ new Set();
10401
11614
  const root = nodes.get("root");
10402
- for (const key in _optionalChain([root, 'optionalAccess', _249 => _249.data])) {
11615
+ for (const key in _optionalChain([root, 'optionalAccess', _263 => _263.data])) {
10403
11616
  keys2.add(key);
10404
11617
  }
10405
11618
  for (const node of nodes.values()) {
@@ -10471,6 +11684,8 @@ function createRoom(options, config) {
10471
11684
  activeBatch: null,
10472
11685
  unacknowledgedOps
10473
11686
  };
11687
+ let nextHistoryItemId = 0;
11688
+ let historyDisabled = 0;
10474
11689
  const nodeMapBuffer = makeNodeMapBuffer();
10475
11690
  const stopwatch = config.enableDebugLogging ? makeStopWatch() : void 0;
10476
11691
  let lastTokenKey;
@@ -10555,7 +11770,10 @@ function createRoom(options, config) {
10555
11770
  }
10556
11771
  }
10557
11772
  });
10558
- function onDispatch(ops, reverse, storageUpdates) {
11773
+ function onDispatch(ops, reverse, storageUpdates, options2) {
11774
+ for (const value of storageUpdates.values()) {
11775
+ value[kStorageUpdateSource] = { origin: "local", via: "mutation" };
11776
+ }
10559
11777
  if (context.activeBatch) {
10560
11778
  for (const op of ops) {
10561
11779
  context.activeBatch.ops.push(op);
@@ -10570,19 +11788,24 @@ function createRoom(options, config) {
10570
11788
  );
10571
11789
  }
10572
11790
  context.activeBatch.reverseOps.pushLeft(reverse);
11791
+ if (_optionalChain([options2, 'optionalAccess', _264 => _264.clearRedoStack])) {
11792
+ context.activeBatch.clearRedoStack = true;
11793
+ }
10573
11794
  } else {
10574
11795
  if (reverse.length > 0) {
10575
11796
  addToUndoStack(reverse);
10576
11797
  }
11798
+ if (_nullishCoalesce(_optionalChain([options2, 'optionalAccess', _265 => _265.clearRedoStack]), () => ( ops.length > 0))) {
11799
+ clearRedoStack();
11800
+ }
10577
11801
  if (ops.length > 0) {
10578
- context.redoStack.length = 0;
10579
11802
  dispatchOps(ops);
10580
11803
  }
10581
11804
  notify({ storageUpdates });
10582
11805
  }
10583
11806
  }
10584
11807
  function isStorageWritable() {
10585
- const permissionMatrix = _optionalChain([context, 'access', _250 => _250.dynamicSessionInfoSig, 'access', _251 => _251.get, 'call', _252 => _252(), 'optionalAccess', _253 => _253.permissionMatrix]);
11808
+ const permissionMatrix = _optionalChain([context, 'access', _266 => _266.dynamicSessionInfoSig, 'access', _267 => _267.get, 'call', _268 => _268(), 'optionalAccess', _269 => _269.permissionMatrix]);
10586
11809
  return permissionMatrix !== void 0 ? hasPermissionAccess(permissionMatrix, "storage", "write") : true;
10587
11810
  }
10588
11811
  const eventHub = {
@@ -10595,6 +11818,7 @@ function createRoom(options, config) {
10595
11818
  others: makeEventSource(),
10596
11819
  storageBatch: makeEventSource(),
10597
11820
  history: makeEventSource(),
11821
+ privateHistory: makeEventSource(),
10598
11822
  storageDidLoad: makeEventSource(),
10599
11823
  storageStatus: makeEventSource(),
10600
11824
  ydoc: makeEventSource(),
@@ -10682,12 +11906,12 @@ function createRoom(options, config) {
10682
11906
  self,
10683
11907
  (me) => me !== null ? userToTreeNode("Me", me) : null
10684
11908
  );
10685
- function diffCurrentStorageAgainst(target) {
11909
+ function diffCurrentStorageAgainst(target, options2) {
10686
11910
  const current = /* @__PURE__ */ new Map();
10687
11911
  for (const [id, crdt] of context.pool.nodes) {
10688
11912
  current.set(id, crdt._serialize());
10689
11913
  }
10690
- return diffNodeMap(current, target);
11914
+ return diffNodeMap(current, target, options2);
10691
11915
  }
10692
11916
  function createOrUpdateRootFromMessage(nodes) {
10693
11917
  if (nodes.size === 0) {
@@ -10695,6 +11919,23 @@ function createRoom(options, config) {
10695
11919
  }
10696
11920
  if (context.root !== void 0) {
10697
11921
  const result = applyRemoteOps(diffCurrentStorageAgainst(nodes));
11922
+ for (const [id, crdt] of nodes) {
11923
+ if (crdt.type === CrdtType.TEXT) {
11924
+ const node = context.pool.nodes.get(id);
11925
+ if (node !== void 0 && isLiveText(node)) {
11926
+ const update = node._resyncText(crdt.data, crdt.version);
11927
+ if (update !== void 0) {
11928
+ result.updates.storageUpdates.set(
11929
+ id,
11930
+ mergeStorageUpdates(
11931
+ result.updates.storageUpdates.get(id),
11932
+ update
11933
+ )
11934
+ );
11935
+ }
11936
+ }
11937
+ }
11938
+ }
10698
11939
  notify(result.updates);
10699
11940
  } else {
10700
11941
  context.root = LiveObject._fromItems(
@@ -10702,7 +11943,7 @@ function createRoom(options, config) {
10702
11943
  context.pool
10703
11944
  );
10704
11945
  }
10705
- const canWrite = _nullishCoalesce(_optionalChain([self, 'access', _254 => _254.get, 'call', _255 => _255(), 'optionalAccess', _256 => _256.canWrite]), () => ( true));
11946
+ const canWrite = _nullishCoalesce(_optionalChain([self, 'access', _270 => _270.get, 'call', _271 => _271(), 'optionalAccess', _272 => _272.canWrite]), () => ( true));
10706
11947
  const serverTopLevelKeys = topLevelKeysOf(nodes);
10707
11948
  const root = context.root;
10708
11949
  disableHistory(() => {
@@ -10719,12 +11960,23 @@ function createRoom(options, config) {
10719
11960
  }
10720
11961
  });
10721
11962
  }
11963
+ function notifyPrivateHistory(event) {
11964
+ if (historyDisabled > 0) return;
11965
+ eventHub.privateHistory.notify(event);
11966
+ }
11967
+ function clearRedoStack() {
11968
+ if (context.redoStack.length === 0) return;
11969
+ const ids = context.redoStack.map((item) => item.id);
11970
+ context.redoStack.length = 0;
11971
+ notifyPrivateHistory({ action: "discard", ids });
11972
+ }
10722
11973
  function reconcileStorageWithNodes(nodes) {
10723
11974
  if (context.root === void 0) {
10724
11975
  throw new Error("Cannot reconcile storage before it is loaded");
10725
11976
  }
10726
11977
  const ops = diffCurrentStorageAgainst(
10727
- new Map(nodes)
11978
+ new Map(nodes),
11979
+ { includeLiveTextUpdates: true }
10728
11980
  );
10729
11981
  if (ops.length === 0) {
10730
11982
  return;
@@ -10743,9 +11995,14 @@ function createRoom(options, config) {
10743
11995
  }
10744
11996
  function _addToRealUndoStack(frames) {
10745
11997
  if (context.undoStack.length >= 50) {
10746
- context.undoStack.shift();
11998
+ const evicted = context.undoStack.shift();
11999
+ if (evicted !== void 0) {
12000
+ notifyPrivateHistory({ action: "discard", ids: [evicted.id] });
12001
+ }
10747
12002
  }
10748
- context.undoStack.push(frames);
12003
+ const id = nextHistoryItemId++;
12004
+ context.undoStack.push({ id, frames });
12005
+ notifyPrivateHistory({ action: "push", id });
10749
12006
  onHistoryChange();
10750
12007
  }
10751
12008
  function addToUndoStack(frames) {
@@ -10783,19 +12040,55 @@ function createRoom(options, config) {
10783
12040
  "Internal. Tried to get connection id but connection was never open"
10784
12041
  );
10785
12042
  }
10786
- function applyLocalOps(frames) {
12043
+ function applyLocalOps(frames, localStorageUpdateSource = { origin: "local", via: "mutation" }) {
10787
12044
  const [pframes, ops] = partition(
10788
12045
  frames,
10789
12046
  (f) => f.type === "presence"
10790
12047
  );
10791
- const opsWithOpIds = ops.map(
12048
+ const restoredTextIds = /* @__PURE__ */ new Map();
12049
+ for (const op of ops) {
12050
+ if (op.type === OpCode.CREATE_TEXT && op.opId === void 0 && context.pool.nodes.get(op.id) === void 0 && !restoredTextIds.has(op.id)) {
12051
+ restoredTextIds.set(op.id, context.pool.generateId());
12052
+ }
12053
+ }
12054
+ const remappedOps = restoredTextIds.size === 0 ? ops : ops.map((op) => {
12055
+ if (op.opId !== void 0) {
12056
+ return op;
12057
+ }
12058
+ const id = restoredTextIds.get(op.id);
12059
+ if (isCreateOp(op)) {
12060
+ const parentId = restoredTextIds.get(op.parentId);
12061
+ const deletedId = op.deletedId === void 0 ? void 0 : restoredTextIds.get(op.deletedId);
12062
+ if (id === void 0 && parentId === void 0 && deletedId === void 0) {
12063
+ return op;
12064
+ }
12065
+ if (op.type === OpCode.CREATE_TEXT && id !== void 0) {
12066
+ return {
12067
+ ...op,
12068
+ id,
12069
+ version: 0,
12070
+ ...parentId === void 0 ? {} : { parentId },
12071
+ ...deletedId === void 0 ? {} : { deletedId }
12072
+ };
12073
+ }
12074
+ return {
12075
+ ...op,
12076
+ ...id === void 0 ? {} : { id },
12077
+ ...parentId === void 0 ? {} : { parentId },
12078
+ ...deletedId === void 0 ? {} : { deletedId }
12079
+ };
12080
+ }
12081
+ return id === void 0 ? op : { ...op, id };
12082
+ });
12083
+ const opsWithOpIds = remappedOps.map(
10792
12084
  (op) => op.opId === void 0 ? { ...op, opId: context.pool.generateOpId() } : op
10793
12085
  );
10794
12086
  const { reverse, updates } = applyOps(
10795
12087
  pframes,
10796
12088
  opsWithOpIds,
10797
12089
  /* isLocal */
10798
- true
12090
+ true,
12091
+ localStorageUpdateSource
10799
12092
  );
10800
12093
  return { opsToEmit: opsWithOpIds, reverse, updates };
10801
12094
  }
@@ -10807,7 +12100,7 @@ function createRoom(options, config) {
10807
12100
  false
10808
12101
  );
10809
12102
  }
10810
- function applyOps(pframes, ops, isLocal) {
12103
+ function applyOps(pframes, ops, isLocal, localStorageUpdateSource = { origin: "local", via: "mutation" }) {
10811
12104
  const output = {
10812
12105
  reverse: new Deque(),
10813
12106
  storageUpdates: /* @__PURE__ */ new Map(),
@@ -10845,6 +12138,7 @@ function createRoom(options, config) {
10845
12138
  }
10846
12139
  const applyOpResult = applyOp(op, source);
10847
12140
  if (applyOpResult.modified) {
12141
+ applyOpResult.modified[kStorageUpdateSource] = source === 1 /* THEIRS */ ? { origin: "remote" } : localStorageUpdateSource;
10848
12142
  const nodeId = applyOpResult.modified.node._id;
10849
12143
  if (!(nodeId && createdNodeIds.has(nodeId))) {
10850
12144
  output.storageUpdates.set(
@@ -10856,7 +12150,7 @@ function createRoom(options, config) {
10856
12150
  );
10857
12151
  output.reverse.pushLeft(applyOpResult.reverse);
10858
12152
  }
10859
- if (op.type === OpCode.CREATE_LIST || op.type === OpCode.CREATE_MAP || op.type === OpCode.CREATE_OBJECT || op.type === OpCode.CREATE_FILE) {
12153
+ 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
12154
  createdNodeIds.add(op.id);
10861
12155
  }
10862
12156
  }
@@ -10876,6 +12170,7 @@ function createRoom(options, config) {
10876
12170
  switch (op.type) {
10877
12171
  case OpCode.DELETE_OBJECT_KEY:
10878
12172
  case OpCode.UPDATE_OBJECT:
12173
+ case OpCode.UPDATE_TEXT:
10879
12174
  case OpCode.DELETE_CRDT: {
10880
12175
  const node = context.pool.nodes.get(op.id);
10881
12176
  if (node === void 0) {
@@ -10900,6 +12195,7 @@ function createRoom(options, config) {
10900
12195
  case OpCode.CREATE_OBJECT:
10901
12196
  case OpCode.CREATE_LIST:
10902
12197
  case OpCode.CREATE_MAP:
12198
+ case OpCode.CREATE_TEXT:
10903
12199
  case OpCode.CREATE_FILE:
10904
12200
  case OpCode.CREATE_REGISTER: {
10905
12201
  if (op.parentId === void 0) {
@@ -10935,7 +12231,7 @@ function createRoom(options, config) {
10935
12231
  }
10936
12232
  context.myPresence.patch(patch);
10937
12233
  if (context.activeBatch) {
10938
- if (_optionalChain([options2, 'optionalAccess', _257 => _257.addToHistory])) {
12234
+ if (_optionalChain([options2, 'optionalAccess', _273 => _273.addToHistory])) {
10939
12235
  context.activeBatch.reverseOps.pushLeft({
10940
12236
  type: "presence",
10941
12237
  data: oldValues
@@ -10944,7 +12240,7 @@ function createRoom(options, config) {
10944
12240
  context.activeBatch.updates.presence = true;
10945
12241
  } else {
10946
12242
  flushNowOrSoon();
10947
- if (_optionalChain([options2, 'optionalAccess', _258 => _258.addToHistory])) {
12243
+ if (_optionalChain([options2, 'optionalAccess', _274 => _274.addToHistory])) {
10948
12244
  addToUndoStack([{ type: "presence", data: oldValues }]);
10949
12245
  }
10950
12246
  notify({ presence: true });
@@ -11123,11 +12419,11 @@ function createRoom(options, config) {
11123
12419
  break;
11124
12420
  }
11125
12421
  case ServerMsgCode.STORAGE_CHUNK:
11126
- _optionalChain([stopwatch, 'optionalAccess', _259 => _259.lap, 'call', _260 => _260()]);
12422
+ _optionalChain([stopwatch, 'optionalAccess', _275 => _275.lap, 'call', _276 => _276()]);
11127
12423
  nodeMapBuffer.append(compactNodesToNodeStream(message.nodes));
11128
12424
  break;
11129
12425
  case ServerMsgCode.STORAGE_STREAM_END: {
11130
- const timing = _optionalChain([stopwatch, 'optionalAccess', _261 => _261.stop, 'call', _262 => _262()]);
12426
+ const timing = _optionalChain([stopwatch, 'optionalAccess', _277 => _277.stop, 'call', _278 => _278()]);
11131
12427
  if (timing) {
11132
12428
  const ms = (v) => `${v.toFixed(1)}ms`;
11133
12429
  const rest = timing.laps.slice(1);
@@ -11154,16 +12450,37 @@ function createRoom(options, config) {
11154
12450
  }
11155
12451
  break;
11156
12452
  }
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.
12453
+ // Receiving a RejectedOps message means the server refused some of
12454
+ // our ops, so our optimistic local state is out of sync with the
12455
+ // server. For LiveText ops this is a normal (if rare) situation
12456
+ // e.g. a client that was offline long enough to fall outside the
12457
+ // server's retained history window — and we can recover: drop the
12458
+ // rejected pending state and re-fetch the authoritative storage
12459
+ // snapshot. For other ops (e.g. permission rejections), rolling back
12460
+ // particular Ops is hard/impossible, so we keep the old behavior of
12461
+ // accepting the out-of-sync reality and surfacing an error.
11161
12462
  case ServerMsgCode.REJECT_STORAGE_OP: {
11162
12463
  errorWithTitle(
11163
12464
  "Storage mutation rejection error",
11164
12465
  message.reason
11165
12466
  );
11166
- if (process.env.NODE_ENV !== "production") {
12467
+ let needsStorageResync = false;
12468
+ for (const opId of message.opIds) {
12469
+ const rejectedOp = context.unacknowledgedOps.get(opId);
12470
+ context.unacknowledgedOps.delete(opId);
12471
+ context.buffer.storageOperations = context.buffer.storageOperations.filter((op) => op.opId !== opId);
12472
+ if (rejectedOp !== void 0 && rejectedOp.type === OpCode.UPDATE_TEXT) {
12473
+ const node = context.pool.nodes.get(rejectedOp.id);
12474
+ if (node !== void 0 && isLiveText(node)) {
12475
+ node._rejectPendingOp(opId);
12476
+ needsStorageResync = true;
12477
+ }
12478
+ }
12479
+ }
12480
+ if (needsStorageResync) {
12481
+ refreshStorage();
12482
+ flushNowOrSoon();
12483
+ } else if (process.env.NODE_ENV !== "production") {
11167
12484
  throw new Error(
11168
12485
  `Storage mutations rejected by server: ${message.reason}`
11169
12486
  );
@@ -11262,11 +12579,11 @@ function createRoom(options, config) {
11262
12579
  } else if (pendingFeedsRequests.has(requestId)) {
11263
12580
  const pending = pendingFeedsRequests.get(requestId);
11264
12581
  pendingFeedsRequests.delete(requestId);
11265
- _optionalChain([pending, 'optionalAccess', _263 => _263.reject, 'call', _264 => _264(err)]);
12582
+ _optionalChain([pending, 'optionalAccess', _279 => _279.reject, 'call', _280 => _280(err)]);
11266
12583
  } else if (pendingFeedMessagesRequests.has(requestId)) {
11267
12584
  const pending = pendingFeedMessagesRequests.get(requestId);
11268
12585
  pendingFeedMessagesRequests.delete(requestId);
11269
- _optionalChain([pending, 'optionalAccess', _265 => _265.reject, 'call', _266 => _266(err)]);
12586
+ _optionalChain([pending, 'optionalAccess', _281 => _281.reject, 'call', _282 => _282(err)]);
11270
12587
  }
11271
12588
  eventHub.feeds.notify(message);
11272
12589
  break;
@@ -11420,10 +12737,10 @@ function createRoom(options, config) {
11420
12737
  timeoutId,
11421
12738
  kind,
11422
12739
  feedId,
11423
- messageId: _optionalChain([options2, 'optionalAccess', _267 => _267.messageId]),
11424
- expectedClientMessageId: _optionalChain([options2, 'optionalAccess', _268 => _268.expectedClientMessageId])
12740
+ messageId: _optionalChain([options2, 'optionalAccess', _283 => _283.messageId]),
12741
+ expectedClientMessageId: _optionalChain([options2, 'optionalAccess', _284 => _284.expectedClientMessageId])
11425
12742
  });
11426
- if (kind === "add-message" && _optionalChain([options2, 'optionalAccess', _269 => _269.expectedClientMessageId]) === void 0) {
12743
+ if (kind === "add-message" && _optionalChain([options2, 'optionalAccess', _285 => _285.expectedClientMessageId]) === void 0) {
11427
12744
  const q = _nullishCoalesce(pendingAddMessageFifoByFeed.get(feedId), () => ( []));
11428
12745
  q.push(requestId);
11429
12746
  pendingAddMessageFifoByFeed.set(feedId, q);
@@ -11474,10 +12791,10 @@ function createRoom(options, config) {
11474
12791
  }
11475
12792
  if (!matched) {
11476
12793
  const q = pendingAddMessageFifoByFeed.get(message.feedId);
11477
- const headId = _optionalChain([q, 'optionalAccess', _270 => _270[0]]);
12794
+ const headId = _optionalChain([q, 'optionalAccess', _286 => _286[0]]);
11478
12795
  if (headId !== void 0) {
11479
12796
  const pending = pendingFeedMutations.get(headId);
11480
- if (_optionalChain([pending, 'optionalAccess', _271 => _271.kind]) === "add-message" && pending.expectedClientMessageId === void 0) {
12797
+ if (_optionalChain([pending, 'optionalAccess', _287 => _287.kind]) === "add-message" && pending.expectedClientMessageId === void 0) {
11481
12798
  settleFeedMutation(headId, "ok");
11482
12799
  }
11483
12800
  }
@@ -11513,7 +12830,7 @@ function createRoom(options, config) {
11513
12830
  const unacknowledgedOps2 = [...context.unacknowledgedOps.values()];
11514
12831
  createOrUpdateRootFromMessage(nodes);
11515
12832
  applyAndSendOfflineOps(unacknowledgedOps2);
11516
- _optionalChain([_resolveStoragePromise, 'optionalCall', _272 => _272()]);
12833
+ _optionalChain([_resolveStoragePromise, 'optionalCall', _288 => _288()]);
11517
12834
  notifyStorageStatus();
11518
12835
  eventHub.storageDidLoad.notify();
11519
12836
  }
@@ -11522,7 +12839,7 @@ function createRoom(options, config) {
11522
12839
  if (!messages.some((msg) => msg.type === ClientMsgCode.FETCH_STORAGE)) {
11523
12840
  messages.push({ type: ClientMsgCode.FETCH_STORAGE });
11524
12841
  nodeMapBuffer.take();
11525
- _optionalChain([stopwatch, 'optionalAccess', _273 => _273.start, 'call', _274 => _274()]);
12842
+ _optionalChain([stopwatch, 'optionalAccess', _289 => _289.start, 'call', _290 => _290()]);
11526
12843
  }
11527
12844
  }
11528
12845
  function startLoadingStorage() {
@@ -11576,10 +12893,10 @@ function createRoom(options, config) {
11576
12893
  const message = {
11577
12894
  type: ClientMsgCode.FETCH_FEEDS,
11578
12895
  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])
12896
+ cursor: _optionalChain([options2, 'optionalAccess', _291 => _291.cursor]),
12897
+ since: _optionalChain([options2, 'optionalAccess', _292 => _292.since]),
12898
+ limit: _optionalChain([options2, 'optionalAccess', _293 => _293.limit]),
12899
+ metadata: _optionalChain([options2, 'optionalAccess', _294 => _294.metadata])
11583
12900
  };
11584
12901
  context.buffer.messages.push(message);
11585
12902
  flushNowOrSoon();
@@ -11599,9 +12916,9 @@ function createRoom(options, config) {
11599
12916
  type: ClientMsgCode.FETCH_FEED_MESSAGES,
11600
12917
  requestId,
11601
12918
  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])
12919
+ cursor: _optionalChain([options2, 'optionalAccess', _295 => _295.cursor]),
12920
+ since: _optionalChain([options2, 'optionalAccess', _296 => _296.since]),
12921
+ limit: _optionalChain([options2, 'optionalAccess', _297 => _297.limit])
11605
12922
  };
11606
12923
  context.buffer.messages.push(message);
11607
12924
  flushNowOrSoon();
@@ -11620,8 +12937,8 @@ function createRoom(options, config) {
11620
12937
  type: ClientMsgCode.ADD_FEED,
11621
12938
  requestId,
11622
12939
  feedId,
11623
- metadata: _optionalChain([options2, 'optionalAccess', _282 => _282.metadata]),
11624
- createdAt: _optionalChain([options2, 'optionalAccess', _283 => _283.createdAt])
12940
+ metadata: _optionalChain([options2, 'optionalAccess', _298 => _298.metadata]),
12941
+ createdAt: _optionalChain([options2, 'optionalAccess', _299 => _299.createdAt])
11625
12942
  };
11626
12943
  context.buffer.messages.push(message);
11627
12944
  flushNowOrSoon();
@@ -11655,15 +12972,15 @@ function createRoom(options, config) {
11655
12972
  function addFeedMessage(feedId, data, options2) {
11656
12973
  const requestId = nanoid();
11657
12974
  const promise = registerFeedMutation(requestId, "add-message", feedId, {
11658
- expectedClientMessageId: _optionalChain([options2, 'optionalAccess', _284 => _284.id])
12975
+ expectedClientMessageId: _optionalChain([options2, 'optionalAccess', _300 => _300.id])
11659
12976
  });
11660
12977
  const message = {
11661
12978
  type: ClientMsgCode.ADD_FEED_MESSAGE,
11662
12979
  requestId,
11663
12980
  feedId,
11664
12981
  data,
11665
- id: _optionalChain([options2, 'optionalAccess', _285 => _285.id]),
11666
- createdAt: _optionalChain([options2, 'optionalAccess', _286 => _286.createdAt])
12982
+ id: _optionalChain([options2, 'optionalAccess', _301 => _301.id]),
12983
+ createdAt: _optionalChain([options2, 'optionalAccess', _302 => _302.createdAt])
11667
12984
  };
11668
12985
  context.buffer.messages.push(message);
11669
12986
  flushNowOrSoon();
@@ -11680,7 +12997,7 @@ function createRoom(options, config) {
11680
12997
  feedId,
11681
12998
  messageId,
11682
12999
  data,
11683
- updatedAt: _optionalChain([options2, 'optionalAccess', _287 => _287.updatedAt])
13000
+ updatedAt: _optionalChain([options2, 'optionalAccess', _303 => _303.updatedAt])
11684
13001
  };
11685
13002
  context.buffer.messages.push(message);
11686
13003
  flushNowOrSoon();
@@ -11705,14 +13022,19 @@ function createRoom(options, config) {
11705
13022
  if (context.activeBatch) {
11706
13023
  throw new Error("undo is not allowed during a batch");
11707
13024
  }
11708
- const frames = context.undoStack.pop();
11709
- if (frames === void 0) {
13025
+ const item = context.undoStack.pop();
13026
+ if (item === void 0) {
11710
13027
  return;
11711
13028
  }
11712
13029
  context.pausedHistory = null;
11713
- const result = applyLocalOps(frames);
13030
+ const result = applyLocalOps(item.frames, {
13031
+ origin: "local",
13032
+ via: "history",
13033
+ action: "undo"
13034
+ });
13035
+ context.redoStack.push({ id: item.id, frames: result.reverse });
13036
+ notifyPrivateHistory({ action: "undo", id: item.id });
11714
13037
  notify(result.updates);
11715
- context.redoStack.push(result.reverse);
11716
13038
  onHistoryChange();
11717
13039
  for (const op of result.opsToEmit) {
11718
13040
  context.buffer.storageOperations.push(op);
@@ -11723,14 +13045,19 @@ function createRoom(options, config) {
11723
13045
  if (context.activeBatch) {
11724
13046
  throw new Error("redo is not allowed during a batch");
11725
13047
  }
11726
- const frames = context.redoStack.pop();
11727
- if (frames === void 0) {
13048
+ const item = context.redoStack.pop();
13049
+ if (item === void 0) {
11728
13050
  return;
11729
13051
  }
11730
13052
  context.pausedHistory = null;
11731
- const result = applyLocalOps(frames);
13053
+ const result = applyLocalOps(item.frames, {
13054
+ origin: "local",
13055
+ via: "history",
13056
+ action: "redo"
13057
+ });
13058
+ context.undoStack.push({ id: item.id, frames: result.reverse });
13059
+ notifyPrivateHistory({ action: "redo", id: item.id });
11732
13060
  notify(result.updates);
11733
- context.undoStack.push(result.reverse);
11734
13061
  onHistoryChange();
11735
13062
  for (const op of result.opsToEmit) {
11736
13063
  context.buffer.storageOperations.push(op);
@@ -11740,6 +13067,8 @@ function createRoom(options, config) {
11740
13067
  function clear() {
11741
13068
  context.undoStack.length = 0;
11742
13069
  context.redoStack.length = 0;
13070
+ notifyPrivateHistory({ action: "clear" });
13071
+ onHistoryChange();
11743
13072
  }
11744
13073
  function batch2(callback) {
11745
13074
  if (context.activeBatch) {
@@ -11767,8 +13096,8 @@ function createRoom(options, config) {
11767
13096
  if (currentBatch.scheduleHistoryResume) {
11768
13097
  commitPausedHistoryToUndoStack();
11769
13098
  }
11770
- if (currentBatch.ops.length > 0) {
11771
- context.redoStack.length = 0;
13099
+ if (currentBatch.ops.length > 0 || currentBatch.clearRedoStack) {
13100
+ clearRedoStack();
11772
13101
  }
11773
13102
  if (currentBatch.ops.length > 0) {
11774
13103
  dispatchOps(currentBatch.ops);
@@ -11797,7 +13126,6 @@ function createRoom(options, config) {
11797
13126
  }
11798
13127
  commitPausedHistoryToUndoStack();
11799
13128
  }
11800
- let historyDisabled = 0;
11801
13129
  function disableHistory(fn) {
11802
13130
  const origUndo = context.undoStack;
11803
13131
  const origRedo = context.redoStack;
@@ -11887,8 +13215,8 @@ function createRoom(options, config) {
11887
13215
  async function getThreads(options2) {
11888
13216
  return httpClient.getThreads({
11889
13217
  roomId,
11890
- query: _optionalChain([options2, 'optionalAccess', _288 => _288.query]),
11891
- cursor: _optionalChain([options2, 'optionalAccess', _289 => _289.cursor])
13218
+ query: _optionalChain([options2, 'optionalAccess', _304 => _304.query]),
13219
+ cursor: _optionalChain([options2, 'optionalAccess', _305 => _305.cursor])
11892
13220
  });
11893
13221
  }
11894
13222
  async function getThread(threadId) {
@@ -12021,7 +13349,7 @@ function createRoom(options, config) {
12021
13349
  function getSubscriptionSettings(options2) {
12022
13350
  return httpClient.getSubscriptionSettings({
12023
13351
  roomId,
12024
- signal: _optionalChain([options2, 'optionalAccess', _290 => _290.signal])
13352
+ signal: _optionalChain([options2, 'optionalAccess', _306 => _306.signal])
12025
13353
  });
12026
13354
  }
12027
13355
  function updateSubscriptionSettings(settings) {
@@ -12043,30 +13371,45 @@ function createRoom(options, config) {
12043
13371
  {
12044
13372
  [kInternal]: {
12045
13373
  get presenceBuffer() {
12046
- return deepClone(_nullishCoalesce(_optionalChain([context, 'access', _291 => _291.buffer, 'access', _292 => _292.presenceUpdates, 'optionalAccess', _293 => _293.data]), () => ( null)));
13374
+ return deepClone(_nullishCoalesce(_optionalChain([context, 'access', _307 => _307.buffer, 'access', _308 => _308.presenceUpdates, 'optionalAccess', _309 => _309.data]), () => ( null)));
12047
13375
  },
12048
13376
  // prettier-ignore
12049
13377
  get undoStack() {
12050
- return deepClone(context.undoStack);
13378
+ return structuredClone(
13379
+ context.undoStack.map((item) => ({
13380
+ id: item.id,
13381
+ frames: item.frames
13382
+ }))
13383
+ );
13384
+ },
13385
+ // prettier-ignore
13386
+ get redoStack() {
13387
+ return structuredClone(
13388
+ context.redoStack.map((item) => ({
13389
+ id: item.id,
13390
+ frames: item.frames
13391
+ }))
13392
+ );
12051
13393
  },
12052
13394
  // prettier-ignore
12053
13395
  get nodeCount() {
12054
13396
  return context.pool.nodes.size;
12055
13397
  },
12056
13398
  // prettier-ignore
13399
+ history: eventHub.privateHistory.observable,
12057
13400
  getYjsProvider() {
12058
13401
  return context.yjsProvider;
12059
13402
  },
12060
13403
  setYjsProvider(newProvider) {
12061
- _optionalChain([context, 'access', _294 => _294.yjsProvider, 'optionalAccess', _295 => _295.off, 'call', _296 => _296("status", yjsStatusDidChange)]);
13404
+ _optionalChain([context, 'access', _310 => _310.yjsProvider, 'optionalAccess', _311 => _311.off, 'call', _312 => _312("status", yjsStatusDidChange)]);
12062
13405
  context.yjsProvider = newProvider;
12063
- _optionalChain([newProvider, 'optionalAccess', _297 => _297.on, 'call', _298 => _298("status", yjsStatusDidChange)]);
13406
+ _optionalChain([newProvider, 'optionalAccess', _313 => _313.on, 'call', _314 => _314("status", yjsStatusDidChange)]);
12064
13407
  context.yjsProviderDidChange.notify();
12065
13408
  },
12066
13409
  yjsProviderDidChange: context.yjsProviderDidChange.observable,
12067
13410
  // send metadata when using a text editor
12068
13411
  reportTextEditor,
12069
- getPermissionMatrix: () => _optionalChain([context, 'access', _299 => _299.dynamicSessionInfoSig, 'access', _300 => _300.get, 'call', _301 => _301(), 'optionalAccess', _302 => _302.permissionMatrix]),
13412
+ getPermissionMatrix: () => _optionalChain([context, 'access', _315 => _315.dynamicSessionInfoSig, 'access', _316 => _316.get, 'call', _317 => _317(), 'optionalAccess', _318 => _318.permissionMatrix]),
12070
13413
  // create a text mention when using a text editor
12071
13414
  createTextMention,
12072
13415
  // delete a text mention when using a text editor
@@ -12129,7 +13472,7 @@ ${dumpPool(
12129
13472
  source.dispose();
12130
13473
  }
12131
13474
  eventHub.roomWillDestroy.notify();
12132
- _optionalChain([context, 'access', _303 => _303.yjsProvider, 'optionalAccess', _304 => _304.off, 'call', _305 => _305("status", yjsStatusDidChange)]);
13475
+ _optionalChain([context, 'access', _319 => _319.yjsProvider, 'optionalAccess', _320 => _320.off, 'call', _321 => _321("status", yjsStatusDidChange)]);
12133
13476
  syncSourceForStorage.destroy();
12134
13477
  syncSourceForYjs.destroy();
12135
13478
  uninstallBgTabSpy();
@@ -12293,7 +13636,7 @@ function makeClassicSubscribeFn(roomId, events, errorEvents) {
12293
13636
  }
12294
13637
  if (isLiveNode(first)) {
12295
13638
  const node = first;
12296
- if (_optionalChain([options, 'optionalAccess', _306 => _306.isDeep])) {
13639
+ if (_optionalChain([options, 'optionalAccess', _322 => _322.isDeep])) {
12297
13640
  const storageCallback = second;
12298
13641
  return subscribeToLiveStructureDeeply(node, storageCallback);
12299
13642
  } else {
@@ -12383,8 +13726,8 @@ function createClient(options) {
12383
13726
  const authManager = createAuthManager(options, (token) => {
12384
13727
  currentUserId.set(() => token.uid);
12385
13728
  });
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)]);
13729
+ const fetchPolyfill = _optionalChain([clientOptions, 'access', _323 => _323.polyfills, 'optionalAccess', _324 => _324.fetch]) || /* istanbul ignore next */
13730
+ _optionalChain([globalThis, 'access', _325 => _325.fetch, 'optionalAccess', _326 => _326.bind, 'call', _327 => _327(globalThis)]);
12388
13731
  const httpClient = createApiClient({
12389
13732
  baseUrl,
12390
13733
  fetchPolyfill,
@@ -12401,7 +13744,7 @@ function createClient(options) {
12401
13744
  delegates: {
12402
13745
  createSocket: makeCreateSocketDelegateForAi(
12403
13746
  baseUrl,
12404
- _optionalChain([clientOptions, 'access', _312 => _312.polyfills, 'optionalAccess', _313 => _313.WebSocket])
13747
+ _optionalChain([clientOptions, 'access', _328 => _328.polyfills, 'optionalAccess', _329 => _329.WebSocket])
12405
13748
  ),
12406
13749
  authenticate: async () => {
12407
13750
  const resp = await authManager.getAuthValue({
@@ -12472,7 +13815,7 @@ function createClient(options) {
12472
13815
  createSocket: makeCreateSocketDelegateForRoom(
12473
13816
  roomId,
12474
13817
  baseUrl,
12475
- _optionalChain([clientOptions, 'access', _314 => _314.polyfills, 'optionalAccess', _315 => _315.WebSocket])
13818
+ _optionalChain([clientOptions, 'access', _330 => _330.polyfills, 'optionalAccess', _331 => _331.WebSocket])
12476
13819
  ),
12477
13820
  authenticate: makeAuthDelegateForRoom(roomId, authManager)
12478
13821
  })),
@@ -12494,7 +13837,7 @@ function createClient(options) {
12494
13837
  const shouldConnect = _nullishCoalesce(options2.autoConnect, () => ( true));
12495
13838
  if (shouldConnect) {
12496
13839
  if (typeof atob === "undefined") {
12497
- if (_optionalChain([clientOptions, 'access', _316 => _316.polyfills, 'optionalAccess', _317 => _317.atob]) === void 0) {
13840
+ if (_optionalChain([clientOptions, 'access', _332 => _332.polyfills, 'optionalAccess', _333 => _333.atob]) === void 0) {
12498
13841
  throw new Error(
12499
13842
  "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
13843
  );
@@ -12506,7 +13849,7 @@ function createClient(options) {
12506
13849
  return leaseRoom(newRoomDetails);
12507
13850
  }
12508
13851
  function getRoom(roomId) {
12509
- const room = _optionalChain([roomsById, 'access', _318 => _318.get, 'call', _319 => _319(roomId), 'optionalAccess', _320 => _320.room]);
13852
+ const room = _optionalChain([roomsById, 'access', _334 => _334.get, 'call', _335 => _335(roomId), 'optionalAccess', _336 => _336.room]);
12510
13853
  return room ? room : null;
12511
13854
  }
12512
13855
  function logout() {
@@ -12522,7 +13865,7 @@ function createClient(options) {
12522
13865
  const batchedResolveUsers = new Batch(
12523
13866
  async (batchedUserIds) => {
12524
13867
  const userIds = batchedUserIds.flat();
12525
- const users = await _optionalChain([resolveUsers, 'optionalCall', _321 => _321({ userIds })]);
13868
+ const users = await _optionalChain([resolveUsers, 'optionalCall', _337 => _337({ userIds })]);
12526
13869
  warnOnceIf(
12527
13870
  !resolveUsers,
12528
13871
  "Set the resolveUsers option in createClient to specify user info."
@@ -12539,7 +13882,7 @@ function createClient(options) {
12539
13882
  const batchedResolveRoomsInfo = new Batch(
12540
13883
  async (batchedRoomIds) => {
12541
13884
  const roomIds = batchedRoomIds.flat();
12542
- const roomsInfo = await _optionalChain([resolveRoomsInfo, 'optionalCall', _322 => _322({ roomIds })]);
13885
+ const roomsInfo = await _optionalChain([resolveRoomsInfo, 'optionalCall', _338 => _338({ roomIds })]);
12543
13886
  warnOnceIf(
12544
13887
  !resolveRoomsInfo,
12545
13888
  "Set the resolveRoomsInfo option in createClient to specify room info."
@@ -12556,7 +13899,7 @@ function createClient(options) {
12556
13899
  const batchedResolveGroupsInfo = new Batch(
12557
13900
  async (batchedGroupIds) => {
12558
13901
  const groupIds = batchedGroupIds.flat();
12559
- const groupsInfo = await _optionalChain([resolveGroupsInfo, 'optionalCall', _323 => _323({ groupIds })]);
13902
+ const groupsInfo = await _optionalChain([resolveGroupsInfo, 'optionalCall', _339 => _339({ groupIds })]);
12560
13903
  warnOnceIf(
12561
13904
  !resolveGroupsInfo,
12562
13905
  "Set the resolveGroupsInfo option in createClient to specify group info."
@@ -12615,7 +13958,7 @@ function createClient(options) {
12615
13958
  }
12616
13959
  };
12617
13960
  const win = typeof window !== "undefined" ? window : void 0;
12618
- _optionalChain([win, 'optionalAccess', _324 => _324.addEventListener, 'call', _325 => _325("beforeunload", maybePreventClose)]);
13961
+ _optionalChain([win, 'optionalAccess', _340 => _340.addEventListener, 'call', _341 => _341("beforeunload", maybePreventClose)]);
12619
13962
  }
12620
13963
  async function getNotificationSettings(options2) {
12621
13964
  const plainSettings = await httpClient.getNotificationSettings(options2);
@@ -12743,7 +14086,7 @@ var commentBodyElementsTypes = {
12743
14086
  mention: "inline"
12744
14087
  };
12745
14088
  function traverseCommentBody(body, elementOrVisitor, possiblyVisitor) {
12746
- if (!body || !_optionalChain([body, 'optionalAccess', _326 => _326.content])) {
14089
+ if (!body || !_optionalChain([body, 'optionalAccess', _342 => _342.content])) {
12747
14090
  return;
12748
14091
  }
12749
14092
  const element = typeof elementOrVisitor === "string" ? elementOrVisitor : void 0;
@@ -12753,13 +14096,13 @@ function traverseCommentBody(body, elementOrVisitor, possiblyVisitor) {
12753
14096
  for (const block of body.content) {
12754
14097
  if (type === "all" || type === "block") {
12755
14098
  if (guard(block)) {
12756
- _optionalChain([visitor, 'optionalCall', _327 => _327(block)]);
14099
+ _optionalChain([visitor, 'optionalCall', _343 => _343(block)]);
12757
14100
  }
12758
14101
  }
12759
14102
  if (type === "all" || type === "inline") {
12760
14103
  for (const inline of block.children) {
12761
14104
  if (guard(inline)) {
12762
- _optionalChain([visitor, 'optionalCall', _328 => _328(inline)]);
14105
+ _optionalChain([visitor, 'optionalCall', _344 => _344(inline)]);
12763
14106
  }
12764
14107
  }
12765
14108
  }
@@ -12929,7 +14272,7 @@ var stringifyCommentBodyPlainElements = {
12929
14272
  text: ({ element }) => element.text,
12930
14273
  link: ({ element }) => _nullishCoalesce(element.text, () => ( element.url)),
12931
14274
  mention: ({ element, user, group }) => {
12932
- return `@${_nullishCoalesce(_nullishCoalesce(_optionalChain([user, 'optionalAccess', _329 => _329.name]), () => ( _optionalChain([group, 'optionalAccess', _330 => _330.name]))), () => ( element.id))}`;
14275
+ return `@${_nullishCoalesce(_nullishCoalesce(_optionalChain([user, 'optionalAccess', _345 => _345.name]), () => ( _optionalChain([group, 'optionalAccess', _346 => _346.name]))), () => ( element.id))}`;
12933
14276
  }
12934
14277
  };
12935
14278
  var stringifyCommentBodyHtmlElements = {
@@ -12959,7 +14302,7 @@ var stringifyCommentBodyHtmlElements = {
12959
14302
  return html`<a href="${href}" target="_blank" rel="noopener noreferrer">${element.text ? html`${element.text}` : element.url}</a>`;
12960
14303
  },
12961
14304
  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>`;
14305
+ return html`<span data-mention>@${_optionalChain([user, 'optionalAccess', _347 => _347.name]) ? html`${_optionalChain([user, 'optionalAccess', _348 => _348.name])}` : _optionalChain([group, 'optionalAccess', _349 => _349.name]) ? html`${_optionalChain([group, 'optionalAccess', _350 => _350.name])}` : element.id}</span>`;
12963
14306
  }
12964
14307
  };
12965
14308
  var stringifyCommentBodyMarkdownElements = {
@@ -12989,20 +14332,20 @@ var stringifyCommentBodyMarkdownElements = {
12989
14332
  return markdown`[${_nullishCoalesce(element.text, () => ( element.url))}](${href})`;
12990
14333
  },
12991
14334
  mention: ({ element, user, group }) => {
12992
- return markdown`@${_nullishCoalesce(_nullishCoalesce(_optionalChain([user, 'optionalAccess', _335 => _335.name]), () => ( _optionalChain([group, 'optionalAccess', _336 => _336.name]))), () => ( element.id))}`;
14335
+ return markdown`@${_nullishCoalesce(_nullishCoalesce(_optionalChain([user, 'optionalAccess', _351 => _351.name]), () => ( _optionalChain([group, 'optionalAccess', _352 => _352.name]))), () => ( element.id))}`;
12993
14336
  }
12994
14337
  };
12995
14338
  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")));
14339
+ const format = _nullishCoalesce(_optionalChain([options, 'optionalAccess', _353 => _353.format]), () => ( "plain"));
14340
+ const separator = _nullishCoalesce(_optionalChain([options, 'optionalAccess', _354 => _354.separator]), () => ( (format === "markdown" ? "\n\n" : "\n")));
12998
14341
  const elements = {
12999
14342
  ...format === "html" ? stringifyCommentBodyHtmlElements : format === "markdown" ? stringifyCommentBodyMarkdownElements : stringifyCommentBodyPlainElements,
13000
- ..._optionalChain([options, 'optionalAccess', _339 => _339.elements])
14343
+ ..._optionalChain([options, 'optionalAccess', _355 => _355.elements])
13001
14344
  };
13002
14345
  const { users: resolvedUsers, groups: resolvedGroupsInfo } = await resolveMentionsInCommentBody(
13003
14346
  body,
13004
- _optionalChain([options, 'optionalAccess', _340 => _340.resolveUsers]),
13005
- _optionalChain([options, 'optionalAccess', _341 => _341.resolveGroupsInfo])
14347
+ _optionalChain([options, 'optionalAccess', _356 => _356.resolveUsers]),
14348
+ _optionalChain([options, 'optionalAccess', _357 => _357.resolveGroupsInfo])
13006
14349
  );
13007
14350
  const blocks = body.content.flatMap((block, blockIndex) => {
13008
14351
  switch (block.type) {
@@ -13084,6 +14427,12 @@ function toPlainLson(lson) {
13084
14427
  liveblocksType: "LiveList",
13085
14428
  data: [...lson].map((item) => toPlainLson(item))
13086
14429
  };
14430
+ } else if (lson instanceof LiveText) {
14431
+ return {
14432
+ liveblocksType: "LiveText",
14433
+ data: lson.toJSON(),
14434
+ version: lson.version
14435
+ };
13087
14436
  } else if (lson instanceof LiveFile) {
13088
14437
  return {
13089
14438
  liveblocksType: "LiveFile",
@@ -13142,9 +14491,9 @@ function makePoller(callback, intervalMs, options) {
13142
14491
  const startTime = performance.now();
13143
14492
  const doc = typeof document !== "undefined" ? document : void 0;
13144
14493
  const win = typeof window !== "undefined" ? window : void 0;
13145
- const maxStaleTimeMs = _nullishCoalesce(_optionalChain([options, 'optionalAccess', _342 => _342.maxStaleTimeMs]), () => ( Number.POSITIVE_INFINITY));
14494
+ const maxStaleTimeMs = _nullishCoalesce(_optionalChain([options, 'optionalAccess', _358 => _358.maxStaleTimeMs]), () => ( Number.POSITIVE_INFINITY));
13146
14495
  const context = {
13147
- inForeground: _optionalChain([doc, 'optionalAccess', _343 => _343.visibilityState]) !== "hidden",
14496
+ inForeground: _optionalChain([doc, 'optionalAccess', _359 => _359.visibilityState]) !== "hidden",
13148
14497
  lastSuccessfulPollAt: startTime,
13149
14498
  count: 0,
13150
14499
  backoff: 0
@@ -13225,11 +14574,11 @@ function makePoller(callback, intervalMs, options) {
13225
14574
  pollNowIfStale();
13226
14575
  }
13227
14576
  function onVisibilityChange() {
13228
- setInForeground(_optionalChain([doc, 'optionalAccess', _344 => _344.visibilityState]) !== "hidden");
14577
+ setInForeground(_optionalChain([doc, 'optionalAccess', _360 => _360.visibilityState]) !== "hidden");
13229
14578
  }
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)]);
14579
+ _optionalChain([doc, 'optionalAccess', _361 => _361.addEventListener, 'call', _362 => _362("visibilitychange", onVisibilityChange)]);
14580
+ _optionalChain([win, 'optionalAccess', _363 => _363.addEventListener, 'call', _364 => _364("online", onVisibilityChange)]);
14581
+ _optionalChain([win, 'optionalAccess', _365 => _365.addEventListener, 'call', _366 => _366("focus", pollNowIfStale)]);
13233
14582
  fsm.start();
13234
14583
  return {
13235
14584
  inc,
@@ -13378,5 +14727,10 @@ detectDupes(PKG_NAME, PKG_VERSION, PKG_FORMAT);
13378
14727
 
13379
14728
 
13380
14729
 
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;
14730
+
14731
+
14732
+
14733
+
14734
+
14735
+ 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.kStorageUpdateSource = kStorageUpdateSource; 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.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
14736
  //# sourceMappingURL=index.cjs.map