@liveblocks/core 3.19.2 → 3.19.4-test1

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.js CHANGED
@@ -6,7 +6,7 @@ var __export = (target, all) => {
6
6
 
7
7
  // src/version.ts
8
8
  var PKG_NAME = "@liveblocks/core";
9
- var PKG_VERSION = "3.19.2";
9
+ var PKG_VERSION = "3.19.4-test1";
10
10
  var PKG_FORMAT = "esm";
11
11
 
12
12
  // src/dupe-detection.ts
@@ -5473,7 +5473,9 @@ var OpCode = Object.freeze({
5473
5473
  DELETE_CRDT: 5,
5474
5474
  DELETE_OBJECT_KEY: 6,
5475
5475
  CREATE_MAP: 7,
5476
- CREATE_REGISTER: 8
5476
+ CREATE_REGISTER: 8,
5477
+ CREATE_TEXT: 9,
5478
+ UPDATE_TEXT: 10
5477
5479
  });
5478
5480
  function isIgnoredOp(op) {
5479
5481
  return op.type === OpCode.DELETE_CRDT && op.id === "ACK";
@@ -5484,7 +5486,8 @@ var CrdtType = Object.freeze({
5484
5486
  OBJECT: 0,
5485
5487
  LIST: 1,
5486
5488
  MAP: 2,
5487
- REGISTER: 3
5489
+ REGISTER: 3,
5490
+ TEXT: 4
5488
5491
  });
5489
5492
  function isRootStorageNode(node) {
5490
5493
  return node[0] === "root";
@@ -5501,6 +5504,9 @@ function isMapStorageNode(node) {
5501
5504
  function isRegisterStorageNode(node) {
5502
5505
  return node[1].type === CrdtType.REGISTER;
5503
5506
  }
5507
+ function isTextStorageNode(node) {
5508
+ return node[1].type === CrdtType.TEXT;
5509
+ }
5504
5510
  function isCompactRootNode(node) {
5505
5511
  return node[0] === "root";
5506
5512
  }
@@ -5523,6 +5529,9 @@ function* compactNodesToNodeStream(compactNodes) {
5523
5529
  case CrdtType.REGISTER:
5524
5530
  yield [cnode[0], { type: CrdtType.REGISTER, parentId: cnode[2], parentKey: cnode[3], data: cnode[4] }];
5525
5531
  break;
5532
+ case CrdtType.TEXT:
5533
+ yield [cnode[0], { type: CrdtType.TEXT, parentId: cnode[2], parentKey: cnode[3], data: cnode[4], version: cnode[5] }];
5534
+ break;
5526
5535
  default:
5527
5536
  }
5528
5537
  }
@@ -5551,6 +5560,17 @@ function* nodeStreamToCompactNodes(nodes) {
5551
5560
  const id = node[0];
5552
5561
  const crdt = node[1];
5553
5562
  yield [id, CrdtType.REGISTER, crdt.parentId, crdt.parentKey, crdt.data];
5563
+ } else if (isTextStorageNode(node)) {
5564
+ const id = node[0];
5565
+ const crdt = node[1];
5566
+ yield [
5567
+ id,
5568
+ CrdtType.TEXT,
5569
+ crdt.parentId,
5570
+ crdt.parentKey,
5571
+ crdt.data,
5572
+ crdt.version
5573
+ ];
5554
5574
  } else {
5555
5575
  }
5556
5576
  }
@@ -8207,6 +8227,581 @@ var LiveObject = class _LiveObject extends AbstractCrdt {
8207
8227
  }
8208
8228
  };
8209
8229
 
8230
+ // src/crdts/liveTextOps.ts
8231
+ function attributesEqual(left, right) {
8232
+ if (left === right) {
8233
+ return true;
8234
+ }
8235
+ if (left === void 0 || right === void 0) {
8236
+ return false;
8237
+ }
8238
+ const leftKeys = Object.keys(left);
8239
+ const rightKeys = Object.keys(right);
8240
+ if (leftKeys.length !== rightKeys.length) {
8241
+ return false;
8242
+ }
8243
+ for (const key of leftKeys) {
8244
+ if (left[key] !== right[key]) {
8245
+ return false;
8246
+ }
8247
+ }
8248
+ return true;
8249
+ }
8250
+ function cloneAttributes(attributes) {
8251
+ return attributes === void 0 ? void 0 : freeze({ ...attributes });
8252
+ }
8253
+ function normalizeSegments(segments) {
8254
+ const normalized = [];
8255
+ for (const segment of segments) {
8256
+ if (segment.text.length === 0) {
8257
+ continue;
8258
+ }
8259
+ const last = normalized.at(-1);
8260
+ const attributes = cloneAttributes(segment.attributes);
8261
+ if (last !== void 0 && attributesEqual(last.attributes, attributes)) {
8262
+ last.text += segment.text;
8263
+ } else {
8264
+ normalized.push({ text: segment.text, attributes });
8265
+ }
8266
+ }
8267
+ return normalized;
8268
+ }
8269
+ function deltaToSegments(delta) {
8270
+ return normalizeSegments(
8271
+ delta.map((item) => ({
8272
+ text: item.text,
8273
+ attributes: item.attributes
8274
+ }))
8275
+ );
8276
+ }
8277
+ function segmentsToDelta(segments) {
8278
+ return segments.map(
8279
+ (segment) => segment.attributes === void 0 ? { text: segment.text } : { text: segment.text, attributes: { ...segment.attributes } }
8280
+ );
8281
+ }
8282
+ function textLength(segments) {
8283
+ return segments.reduce((sum, segment) => sum + segment.text.length, 0);
8284
+ }
8285
+ function splitSegmentsAt(segments, index) {
8286
+ const result = [];
8287
+ let offset = 0;
8288
+ for (const segment of segments) {
8289
+ const end = offset + segment.text.length;
8290
+ if (index > offset && index < end) {
8291
+ const before2 = segment.text.slice(0, index - offset);
8292
+ const after2 = segment.text.slice(index - offset);
8293
+ result.push({ text: before2, attributes: segment.attributes });
8294
+ result.push({ text: after2, attributes: segment.attributes });
8295
+ } else {
8296
+ result.push({ text: segment.text, attributes: segment.attributes });
8297
+ }
8298
+ offset = end;
8299
+ }
8300
+ return result;
8301
+ }
8302
+ function clipRange(index, length, contentLength) {
8303
+ const clippedIndex = Math.max(0, Math.min(index, contentLength));
8304
+ const clippedEnd = Math.max(
8305
+ clippedIndex,
8306
+ Math.min(index + length, contentLength)
8307
+ );
8308
+ return { index: clippedIndex, length: clippedEnd - clippedIndex };
8309
+ }
8310
+ function applyInsert(segments, index, text, attributes) {
8311
+ if (text.length === 0) {
8312
+ return normalizeSegments(segments);
8313
+ }
8314
+ const split = splitSegmentsAt(segments, index);
8315
+ const result = [];
8316
+ let offset = 0;
8317
+ let inserted = false;
8318
+ for (const segment of split) {
8319
+ if (!inserted && offset === index) {
8320
+ result.push({ text, attributes });
8321
+ inserted = true;
8322
+ }
8323
+ result.push(segment);
8324
+ offset += segment.text.length;
8325
+ }
8326
+ if (!inserted) {
8327
+ result.push({ text, attributes });
8328
+ }
8329
+ return normalizeSegments(result);
8330
+ }
8331
+ function extractDeletedSegments(segments, index, length) {
8332
+ const split = splitSegmentsAt(
8333
+ splitSegmentsAt(segments, index),
8334
+ index + length
8335
+ );
8336
+ const deleted = [];
8337
+ let offset = 0;
8338
+ for (const segment of split) {
8339
+ const end = offset + segment.text.length;
8340
+ if (offset >= index && end <= index + length) {
8341
+ deleted.push({
8342
+ text: segment.text,
8343
+ attributes: segment.attributes
8344
+ });
8345
+ }
8346
+ offset = end;
8347
+ }
8348
+ return normalizeSegments(deleted);
8349
+ }
8350
+ function applyDelete(segments, index, length) {
8351
+ const deletedSegments = extractDeletedSegments(segments, index, length);
8352
+ const split = splitSegmentsAt(
8353
+ splitSegmentsAt(segments, index),
8354
+ index + length
8355
+ );
8356
+ const result = [];
8357
+ let offset = 0;
8358
+ let deletedText = "";
8359
+ for (const segment of split) {
8360
+ const end = offset + segment.text.length;
8361
+ if (offset >= index && end <= index + length) {
8362
+ deletedText += segment.text;
8363
+ } else {
8364
+ result.push(segment);
8365
+ }
8366
+ offset = end;
8367
+ }
8368
+ return {
8369
+ segments: normalizeSegments(result),
8370
+ deletedText,
8371
+ deletedSegments
8372
+ };
8373
+ }
8374
+ function applyFormat(segments, index, length, attributes) {
8375
+ const split = splitSegmentsAt(
8376
+ splitSegmentsAt(segments, index),
8377
+ index + length
8378
+ );
8379
+ const result = [];
8380
+ let offset = 0;
8381
+ for (const segment of split) {
8382
+ const end = offset + segment.text.length;
8383
+ if (offset >= index && end <= index + length) {
8384
+ const nextAttributes = {
8385
+ ...segment.attributes ?? {}
8386
+ };
8387
+ for (const [key, value] of Object.entries(attributes)) {
8388
+ if (value === null) {
8389
+ delete nextAttributes[key];
8390
+ } else {
8391
+ nextAttributes[key] = value;
8392
+ }
8393
+ }
8394
+ result.push({
8395
+ text: segment.text,
8396
+ attributes: Object.keys(nextAttributes).length === 0 ? void 0 : freeze(nextAttributes)
8397
+ });
8398
+ } else {
8399
+ result.push(segment);
8400
+ }
8401
+ offset = end;
8402
+ }
8403
+ return normalizeSegments(result);
8404
+ }
8405
+ function formatReverseOperations(segments, index, length, patch) {
8406
+ const split = splitSegmentsAt(
8407
+ splitSegmentsAt(segments, index),
8408
+ index + length
8409
+ );
8410
+ const result = [];
8411
+ let offset = 0;
8412
+ for (const segment of split) {
8413
+ const end = offset + segment.text.length;
8414
+ if (offset >= index && end <= index + length) {
8415
+ const attributes = {};
8416
+ for (const key of Object.keys(patch)) {
8417
+ attributes[key] = segment.attributes?.[key] ?? null;
8418
+ }
8419
+ result.push({
8420
+ type: "format",
8421
+ index: offset,
8422
+ length: segment.text.length,
8423
+ attributes
8424
+ });
8425
+ }
8426
+ offset = end;
8427
+ }
8428
+ return result;
8429
+ }
8430
+ function mapIndexThroughOperation(index, op) {
8431
+ if (op.type === "insert") {
8432
+ return op.index <= index ? index + op.text.length : index;
8433
+ } else if (op.type === "delete") {
8434
+ if (op.index >= index) {
8435
+ return index;
8436
+ }
8437
+ return Math.max(op.index, index - op.length);
8438
+ } else {
8439
+ return index;
8440
+ }
8441
+ }
8442
+ function mapTextIndexThroughOperations(index, ops) {
8443
+ let mapped = index;
8444
+ for (const op of ops) {
8445
+ mapped = mapIndexThroughOperation(mapped, op);
8446
+ }
8447
+ return mapped;
8448
+ }
8449
+ function rebaseTextOperations(ops, acceptedOps) {
8450
+ return ops.map((op) => {
8451
+ if (op.type === "insert") {
8452
+ return {
8453
+ ...op,
8454
+ index: mapTextIndexThroughOperations(op.index, acceptedOps)
8455
+ };
8456
+ } else if (op.type === "delete" || op.type === "format") {
8457
+ const start = mapTextIndexThroughOperations(op.index, acceptedOps);
8458
+ const end = mapTextIndexThroughOperations(
8459
+ op.index + op.length,
8460
+ acceptedOps
8461
+ );
8462
+ return { ...op, index: start, length: Math.max(0, end - start) };
8463
+ } else {
8464
+ return op;
8465
+ }
8466
+ });
8467
+ }
8468
+ function applyTextOperationsToSegments(segments, ops) {
8469
+ let next = [...segments];
8470
+ for (const op of ops) {
8471
+ if (op.type === "insert") {
8472
+ const index = Math.max(0, Math.min(op.index, textLength(next)));
8473
+ next = applyInsert(next, index, op.text, op.attributes);
8474
+ } else if (op.type === "delete") {
8475
+ const index = Math.max(0, Math.min(op.index, textLength(next)));
8476
+ const clipped = clipRange(index, op.length, textLength(next));
8477
+ next = applyDelete(next, clipped.index, clipped.length).segments;
8478
+ } else {
8479
+ const index = Math.max(0, Math.min(op.index, textLength(next)));
8480
+ const clipped = clipRange(index, op.length, textLength(next));
8481
+ next = applyFormat(next, clipped.index, clipped.length, op.attributes);
8482
+ }
8483
+ }
8484
+ return next;
8485
+ }
8486
+ function applyLiveTextOperations(delta, ops) {
8487
+ return segmentsToDelta(applyTextOperationsToSegments(deltaToSegments(delta), ops));
8488
+ }
8489
+ function invertTextOperations(segments, ops) {
8490
+ let shadow = [...segments];
8491
+ const reverse = [];
8492
+ for (const op of ops) {
8493
+ if (op.type === "insert") {
8494
+ shadow = applyInsert(shadow, op.index, op.text, op.attributes);
8495
+ reverse.unshift({
8496
+ type: "delete",
8497
+ index: op.index,
8498
+ length: op.text.length
8499
+ });
8500
+ } else if (op.type === "delete") {
8501
+ const deletedSegments = extractDeletedSegments(
8502
+ shadow,
8503
+ op.index,
8504
+ op.length
8505
+ );
8506
+ shadow = applyDelete(shadow, op.index, op.length).segments;
8507
+ const inserts = [];
8508
+ let insertIndex = op.index;
8509
+ for (const segment of deletedSegments) {
8510
+ inserts.push({
8511
+ type: "insert",
8512
+ index: insertIndex,
8513
+ text: segment.text,
8514
+ attributes: segment.attributes
8515
+ });
8516
+ insertIndex += segment.text.length;
8517
+ }
8518
+ for (let index = inserts.length - 1; index >= 0; index--) {
8519
+ reverse.unshift(inserts[index]);
8520
+ }
8521
+ } else {
8522
+ const inverse = formatReverseOperations(
8523
+ shadow,
8524
+ op.index,
8525
+ op.length,
8526
+ op.attributes
8527
+ );
8528
+ shadow = applyFormat(shadow, op.index, op.length, op.attributes);
8529
+ reverse.unshift(...inverse.reverse());
8530
+ }
8531
+ }
8532
+ return reverse;
8533
+ }
8534
+
8535
+ // src/crdts/LiveText.ts
8536
+ var LiveText = class _LiveText extends AbstractCrdt {
8537
+ #segments;
8538
+ #version;
8539
+ #pendingOps;
8540
+ constructor(textOrDelta = "", version = 0) {
8541
+ super();
8542
+ this.#segments = typeof textOrDelta === "string" ? textOrDelta.length === 0 ? [] : [{ text: textOrDelta }] : deltaToSegments(textOrDelta);
8543
+ this.#version = version;
8544
+ this.#pendingOps = /* @__PURE__ */ new Map();
8545
+ }
8546
+ get version() {
8547
+ return this.#version;
8548
+ }
8549
+ get length() {
8550
+ return this.toString().length;
8551
+ }
8552
+ /** @internal */
8553
+ static _deserialize([id, item], _parentToChildren, pool) {
8554
+ const text = new _LiveText(item.data, item.version);
8555
+ text._attach(id, pool);
8556
+ return text;
8557
+ }
8558
+ /** @internal */
8559
+ _toOps(parentId, parentKey) {
8560
+ if (this._id === void 0) {
8561
+ throw new Error("Cannot serialize LiveText if it is not attached");
8562
+ }
8563
+ return [
8564
+ {
8565
+ type: OpCode.CREATE_TEXT,
8566
+ id: this._id,
8567
+ parentId,
8568
+ parentKey,
8569
+ data: this.toDelta(),
8570
+ version: this.#version
8571
+ }
8572
+ ];
8573
+ }
8574
+ /** @internal */
8575
+ _serialize() {
8576
+ if (this.parent.type !== "HasParent") {
8577
+ throw new Error("Cannot serialize LiveText if parent is missing");
8578
+ }
8579
+ return {
8580
+ type: CrdtType.TEXT,
8581
+ parentId: nn(this.parent.node._id, "Parent node expected to have ID"),
8582
+ parentKey: this.parent.key,
8583
+ data: this.toDelta(),
8584
+ version: this.#version
8585
+ };
8586
+ }
8587
+ /** @internal */
8588
+ _attachChild(_op) {
8589
+ throw new Error("LiveText cannot contain child nodes");
8590
+ }
8591
+ /** @internal */
8592
+ _detachChild(_crdt) {
8593
+ throw new Error("LiveText cannot contain child nodes");
8594
+ }
8595
+ /** @internal */
8596
+ _apply(op, isLocal) {
8597
+ if (op.type !== OpCode.UPDATE_TEXT) {
8598
+ return super._apply(op, isLocal);
8599
+ }
8600
+ if (isLocal) {
8601
+ this.#pendingOps.set(nn(op.opId), op.ops);
8602
+ return this.#applyOperations(op.ops, op.version ?? this.#version);
8603
+ }
8604
+ if (op.opId !== void 0) {
8605
+ const pending2 = this.#pendingOps.get(op.opId);
8606
+ this.#pendingOps.delete(op.opId);
8607
+ const otherPending = Array.from(this.#pendingOps.values()).flat();
8608
+ if (pending2 !== void 0 && otherPending.length > 0) {
8609
+ this.#segments = applyTextOperationsToSegments(
8610
+ this.#segments,
8611
+ invertTextOperations(this.#segments, pending2)
8612
+ );
8613
+ const ops2 = rebaseTextOperations(op.ops, otherPending);
8614
+ return this.#applyOperations(
8615
+ ops2,
8616
+ op.version ?? Math.max(this.#version, op.baseVersion + 1)
8617
+ );
8618
+ }
8619
+ this.#version = op.version ?? Math.max(this.#version, op.baseVersion + 1);
8620
+ return { modified: false };
8621
+ }
8622
+ const pending = Array.from(this.#pendingOps.values()).flat();
8623
+ const ops = pending.length > 0 ? rebaseTextOperations(op.ops, pending) : op.ops;
8624
+ return this.#applyOperations(ops, op.version ?? this.#version + 1);
8625
+ }
8626
+ insert(index, text, attributes) {
8627
+ const clippedIndex = Math.max(0, Math.min(index, this.length));
8628
+ this.#dispatch([{ type: "insert", index: clippedIndex, text, attributes }]);
8629
+ }
8630
+ delete(index, length) {
8631
+ const clipped = clipRange(index, length, this.length);
8632
+ if (clipped.length === 0) {
8633
+ return;
8634
+ }
8635
+ this.#dispatch([
8636
+ { type: "delete", index: clipped.index, length: clipped.length }
8637
+ ]);
8638
+ }
8639
+ replace(index, length, text, attributes) {
8640
+ const clipped = clipRange(index, length, this.length);
8641
+ const ops = [];
8642
+ if (clipped.length > 0) {
8643
+ ops.push({
8644
+ type: "delete",
8645
+ index: clipped.index,
8646
+ length: clipped.length
8647
+ });
8648
+ }
8649
+ if (text.length > 0) {
8650
+ ops.push({ type: "insert", index: clipped.index, text, attributes });
8651
+ }
8652
+ this.#dispatch(ops);
8653
+ }
8654
+ format(index, length, attributes) {
8655
+ const clipped = clipRange(index, length, this.length);
8656
+ if (clipped.length === 0) {
8657
+ return;
8658
+ }
8659
+ this.#dispatch([
8660
+ {
8661
+ type: "format",
8662
+ index: clipped.index,
8663
+ length: clipped.length,
8664
+ attributes
8665
+ }
8666
+ ]);
8667
+ }
8668
+ #dispatch(ops) {
8669
+ if (ops.length === 0) {
8670
+ return;
8671
+ }
8672
+ this._pool?.assertStorageIsWritable();
8673
+ const baseVersion = this.#version;
8674
+ const reverse = this._pool !== void 0 && this._id !== void 0 ? this.#invertOperations(ops) : [];
8675
+ const changes = this.#applyOperationsLocally(ops);
8676
+ if (this._pool !== void 0 && this._id !== void 0) {
8677
+ const opId = this._pool.generateOpId();
8678
+ this.#pendingOps.set(opId, ops);
8679
+ this._pool.dispatch(
8680
+ [
8681
+ {
8682
+ type: OpCode.UPDATE_TEXT,
8683
+ id: this._id,
8684
+ opId,
8685
+ baseVersion,
8686
+ ops: [...ops]
8687
+ }
8688
+ ],
8689
+ reverse,
8690
+ /* @__PURE__ */ new Map([
8691
+ [
8692
+ this._id,
8693
+ {
8694
+ type: "LiveText",
8695
+ node: this,
8696
+ version: this.#version,
8697
+ updates: changes
8698
+ }
8699
+ ]
8700
+ ])
8701
+ );
8702
+ }
8703
+ }
8704
+ #applyOperations(ops, version) {
8705
+ const reverse = this.#invertOperations(ops);
8706
+ const changes = this.#applyOperationsLocally(ops);
8707
+ this.#version = Math.max(this.#version, version);
8708
+ return {
8709
+ reverse,
8710
+ modified: {
8711
+ type: "LiveText",
8712
+ node: this,
8713
+ version: this.#version,
8714
+ updates: changes
8715
+ }
8716
+ };
8717
+ }
8718
+ #applyOperationsLocally(ops) {
8719
+ const changes = [];
8720
+ for (const op of ops) {
8721
+ if (op.type === "insert") {
8722
+ this.#segments = applyInsert(
8723
+ this.#segments,
8724
+ op.index,
8725
+ op.text,
8726
+ op.attributes
8727
+ );
8728
+ changes.push({
8729
+ type: "insert",
8730
+ index: op.index,
8731
+ text: op.text,
8732
+ attributes: op.attributes
8733
+ });
8734
+ } else if (op.type === "delete") {
8735
+ const result = applyDelete(this.#segments, op.index, op.length);
8736
+ this.#segments = result.segments;
8737
+ changes.push({
8738
+ type: "delete",
8739
+ index: op.index,
8740
+ length: op.length,
8741
+ deletedText: result.deletedText
8742
+ });
8743
+ } else {
8744
+ this.#segments = applyFormat(
8745
+ this.#segments,
8746
+ op.index,
8747
+ op.length,
8748
+ op.attributes
8749
+ );
8750
+ changes.push({
8751
+ type: "format",
8752
+ index: op.index,
8753
+ length: op.length,
8754
+ attributes: op.attributes
8755
+ });
8756
+ }
8757
+ }
8758
+ this.invalidate();
8759
+ return changes;
8760
+ }
8761
+ #invertOperations(ops) {
8762
+ return [
8763
+ {
8764
+ type: OpCode.UPDATE_TEXT,
8765
+ id: nn(this._id),
8766
+ baseVersion: this.#version,
8767
+ ops: invertTextOperations(this.#segments, ops)
8768
+ }
8769
+ ];
8770
+ }
8771
+ toString() {
8772
+ return this.#segments.map((segment) => segment.text).join("");
8773
+ }
8774
+ toDelta() {
8775
+ return segmentsToDelta(this.#segments);
8776
+ }
8777
+ toJSON() {
8778
+ return super.toJSON();
8779
+ }
8780
+ /** @internal */
8781
+ _toJSON() {
8782
+ return this.toDelta();
8783
+ }
8784
+ /** @internal */
8785
+ _toTreeNode(key) {
8786
+ return {
8787
+ type: "LiveText",
8788
+ id: this._id ?? nanoid(),
8789
+ key,
8790
+ payload: [
8791
+ {
8792
+ type: "Json",
8793
+ id: `${this._id ?? nanoid()}:text`,
8794
+ key: "text",
8795
+ payload: this.toString()
8796
+ }
8797
+ ]
8798
+ };
8799
+ }
8800
+ clone() {
8801
+ return new _LiveText(this.toDelta(), this.#version);
8802
+ }
8803
+ };
8804
+
8210
8805
  // src/crdts/liveblocks-helpers.ts
8211
8806
  function creationOpToLiveNode(op) {
8212
8807
  return lsonToLiveNode(creationOpToLson(op));
@@ -8221,6 +8816,8 @@ function creationOpToLson(op) {
8221
8816
  return new LiveMap();
8222
8817
  case OpCode.CREATE_LIST:
8223
8818
  return new LiveList([]);
8819
+ case OpCode.CREATE_TEXT:
8820
+ return new LiveText(op.data, op.version);
8224
8821
  default:
8225
8822
  return assertNever(op, "Unknown creation Op");
8226
8823
  }
@@ -8243,6 +8840,8 @@ function deserialize(node, parentToChildren, pool) {
8243
8840
  return LiveMap._deserialize(node, parentToChildren, pool);
8244
8841
  } else if (isRegisterStorageNode(node)) {
8245
8842
  return LiveRegister._deserialize(node, parentToChildren, pool);
8843
+ } else if (isTextStorageNode(node)) {
8844
+ return LiveText._deserialize(node, parentToChildren, pool);
8246
8845
  } else {
8247
8846
  throw new Error("Unexpected CRDT type");
8248
8847
  }
@@ -8256,12 +8855,14 @@ function deserializeToLson(node, parentToChildren, pool) {
8256
8855
  return LiveMap._deserialize(node, parentToChildren, pool);
8257
8856
  } else if (isRegisterStorageNode(node)) {
8258
8857
  return node[1].data;
8858
+ } else if (isTextStorageNode(node)) {
8859
+ return LiveText._deserialize(node, parentToChildren, pool);
8259
8860
  } else {
8260
8861
  throw new Error("Unexpected CRDT type");
8261
8862
  }
8262
8863
  }
8263
8864
  function isLiveStructure(value) {
8264
- return isLiveList(value) || isLiveMap(value) || isLiveObject(value);
8865
+ return isLiveList(value) || isLiveMap(value) || isLiveObject(value) || isLiveText(value);
8265
8866
  }
8266
8867
  function isLiveNode(value) {
8267
8868
  return isLiveStructure(value) || isLiveRegister(value);
@@ -8275,6 +8876,9 @@ function isLiveMap(value) {
8275
8876
  function isLiveObject(value) {
8276
8877
  return value instanceof LiveObject;
8277
8878
  }
8879
+ function isLiveText(value) {
8880
+ return value instanceof LiveText;
8881
+ }
8278
8882
  function isLiveRegister(value) {
8279
8883
  return value instanceof LiveRegister;
8280
8884
  }
@@ -8284,14 +8888,14 @@ function cloneLson(value) {
8284
8888
  function liveNodeToLson(obj) {
8285
8889
  if (obj instanceof LiveRegister) {
8286
8890
  return obj.data;
8287
- } else if (obj instanceof LiveList || obj instanceof LiveMap || obj instanceof LiveObject) {
8891
+ } else if (obj instanceof LiveList || obj instanceof LiveMap || obj instanceof LiveObject || obj instanceof LiveText) {
8288
8892
  return obj;
8289
8893
  } else {
8290
8894
  return assertNever(obj, "Unknown AbstractCrdt");
8291
8895
  }
8292
8896
  }
8293
8897
  function lsonToLiveNode(value) {
8294
- if (value instanceof LiveObject || value instanceof LiveMap || value instanceof LiveList) {
8898
+ if (value instanceof LiveObject || value instanceof LiveMap || value instanceof LiveList || value instanceof LiveText) {
8295
8899
  return value;
8296
8900
  } else {
8297
8901
  return new LiveRegister(value);
@@ -8316,6 +8920,38 @@ function getTreesDiffOperations(currentItems, newItems) {
8316
8920
  });
8317
8921
  }
8318
8922
  }
8923
+ if (crdt.type === CrdtType.TEXT) {
8924
+ if (currentCrdt.type !== CrdtType.TEXT || stringifyOrLog(crdt.data) !== stringifyOrLog(currentCrdt.data) || crdt.version !== currentCrdt.version) {
8925
+ ops.push({
8926
+ type: OpCode.UPDATE_TEXT,
8927
+ id,
8928
+ baseVersion: currentCrdt.type === CrdtType.TEXT ? currentCrdt.version : 0,
8929
+ version: crdt.version,
8930
+ ops: [
8931
+ {
8932
+ type: "delete",
8933
+ index: 0,
8934
+ length: currentCrdt.type === CrdtType.TEXT ? currentCrdt.data.reduce(
8935
+ (sum, item) => sum + item.text.length,
8936
+ 0
8937
+ ) : 0
8938
+ },
8939
+ ...crdt.data.map(
8940
+ (item, index, items) => item.attributes === void 0 ? {
8941
+ type: "insert",
8942
+ index: items.slice(0, index).reduce((sum, item2) => sum + item2.text.length, 0),
8943
+ text: item.text
8944
+ } : {
8945
+ type: "insert",
8946
+ index: items.slice(0, index).reduce((sum, item2) => sum + item2.text.length, 0),
8947
+ text: item.text,
8948
+ attributes: item.attributes
8949
+ }
8950
+ )
8951
+ ]
8952
+ });
8953
+ }
8954
+ }
8319
8955
  if (crdt.parentKey !== currentCrdt.parentKey) {
8320
8956
  ops.push({
8321
8957
  type: OpCode.SET_PARENT_KEY,
@@ -8364,6 +9000,16 @@ function getTreesDiffOperations(currentItems, newItems) {
8364
9000
  parentKey: crdt.parentKey
8365
9001
  });
8366
9002
  break;
9003
+ case CrdtType.TEXT:
9004
+ ops.push({
9005
+ type: OpCode.CREATE_TEXT,
9006
+ id,
9007
+ parentId: crdt.parentId,
9008
+ parentKey: crdt.parentKey,
9009
+ data: crdt.data,
9010
+ version: crdt.version
9011
+ });
9012
+ break;
8367
9013
  }
8368
9014
  }
8369
9015
  });
@@ -8396,6 +9042,12 @@ function mergeListStorageUpdates(first, second) {
8396
9042
  updates: updates.concat(second.updates)
8397
9043
  };
8398
9044
  }
9045
+ function mergeTextStorageUpdates(first, second) {
9046
+ return {
9047
+ ...second,
9048
+ updates: first.updates.concat(second.updates)
9049
+ };
9050
+ }
8399
9051
  function mergeStorageUpdates(first, second) {
8400
9052
  if (first === void 0) {
8401
9053
  return second;
@@ -8406,6 +9058,8 @@ function mergeStorageUpdates(first, second) {
8406
9058
  return mergeMapStorageUpdates(first, second);
8407
9059
  } else if (first.type === "LiveList" && second.type === "LiveList") {
8408
9060
  return mergeListStorageUpdates(first, second);
9061
+ } else if (first.type === "LiveText" && second.type === "LiveText") {
9062
+ return mergeTextStorageUpdates(first, second);
8409
9063
  } else {
8410
9064
  }
8411
9065
  return second;
@@ -9687,7 +10341,7 @@ function createRoom(options, config) {
9687
10341
  );
9688
10342
  output.reverse.pushLeft(applyOpResult.reverse);
9689
10343
  }
9690
- if (op.type === OpCode.CREATE_LIST || op.type === OpCode.CREATE_MAP || op.type === OpCode.CREATE_OBJECT) {
10344
+ if (op.type === OpCode.CREATE_LIST || op.type === OpCode.CREATE_MAP || op.type === OpCode.CREATE_OBJECT || op.type === OpCode.CREATE_TEXT) {
9691
10345
  createdNodeIds.add(op.id);
9692
10346
  }
9693
10347
  }
@@ -9707,6 +10361,7 @@ function createRoom(options, config) {
9707
10361
  switch (op.type) {
9708
10362
  case OpCode.DELETE_OBJECT_KEY:
9709
10363
  case OpCode.UPDATE_OBJECT:
10364
+ case OpCode.UPDATE_TEXT:
9710
10365
  case OpCode.DELETE_CRDT: {
9711
10366
  const node = context.pool.nodes.get(op.id);
9712
10367
  if (node === void 0) {
@@ -9731,6 +10386,7 @@ function createRoom(options, config) {
9731
10386
  case OpCode.CREATE_OBJECT:
9732
10387
  case OpCode.CREATE_LIST:
9733
10388
  case OpCode.CREATE_MAP:
10389
+ case OpCode.CREATE_TEXT:
9734
10390
  case OpCode.CREATE_REGISTER: {
9735
10391
  if (op.parentId === void 0) {
9736
10392
  return { modified: false };
@@ -11885,6 +12541,12 @@ function toPlainLson(lson) {
11885
12541
  liveblocksType: "LiveList",
11886
12542
  data: [...lson].map((item) => toPlainLson(item))
11887
12543
  };
12544
+ } else if (lson instanceof LiveText) {
12545
+ return {
12546
+ liveblocksType: "LiveText",
12547
+ data: lson.toDelta(),
12548
+ version: lson.version
12549
+ };
11888
12550
  } else {
11889
12551
  return lson;
11890
12552
  }
@@ -12066,6 +12728,7 @@ export {
12066
12728
  LiveList,
12067
12729
  LiveMap,
12068
12730
  LiveObject,
12731
+ LiveText,
12069
12732
  LiveblocksError,
12070
12733
  MENTION_CHARACTER,
12071
12734
  MutableSignal,
@@ -12077,6 +12740,7 @@ export {
12077
12740
  SortedList,
12078
12741
  TextEditorType,
12079
12742
  WebsocketCloseCodes,
12743
+ applyLiveTextOperations,
12080
12744
  asPos,
12081
12745
  assert,
12082
12746
  assertNever,
@@ -12132,6 +12796,7 @@ export {
12132
12796
  isRegisterStorageNode,
12133
12797
  isRootStorageNode,
12134
12798
  isStartsWithOperator,
12799
+ isTextStorageNode,
12135
12800
  isUrl,
12136
12801
  kInternal,
12137
12802
  keys,