@liveblocks/core 3.19.5-pre1 → 3.20.0-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.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.5-pre1";
9
+ var PKG_VERSION = "3.20.0-exp1";
10
10
  var PKG_FORMAT = "esm";
11
11
 
12
12
  // src/dupe-detection.ts
@@ -5511,7 +5511,9 @@ var OpCode = Object.freeze({
5511
5511
  DELETE_CRDT: 5,
5512
5512
  DELETE_OBJECT_KEY: 6,
5513
5513
  CREATE_MAP: 7,
5514
- CREATE_REGISTER: 8
5514
+ CREATE_REGISTER: 8,
5515
+ CREATE_TEXT: 9,
5516
+ UPDATE_TEXT: 10
5515
5517
  });
5516
5518
  function isIgnoredOp(op) {
5517
5519
  return op.type === OpCode.DELETE_CRDT && op.id === "ACK";
@@ -5525,7 +5527,8 @@ var CrdtType = Object.freeze({
5525
5527
  OBJECT: 0,
5526
5528
  LIST: 1,
5527
5529
  MAP: 2,
5528
- REGISTER: 3
5530
+ REGISTER: 3,
5531
+ TEXT: 4
5529
5532
  });
5530
5533
  function isRootStorageNode(node) {
5531
5534
  return node[0] === "root";
@@ -5542,6 +5545,9 @@ function isMapStorageNode(node) {
5542
5545
  function isRegisterStorageNode(node) {
5543
5546
  return node[1].type === CrdtType.REGISTER;
5544
5547
  }
5548
+ function isTextStorageNode(node) {
5549
+ return node[1].type === CrdtType.TEXT;
5550
+ }
5545
5551
  function isCompactRootNode(node) {
5546
5552
  return node[0] === "root";
5547
5553
  }
@@ -5564,6 +5570,9 @@ function* compactNodesToNodeStream(compactNodes) {
5564
5570
  case CrdtType.REGISTER:
5565
5571
  yield [cnode[0], { type: CrdtType.REGISTER, parentId: cnode[2], parentKey: cnode[3], data: cnode[4] }];
5566
5572
  break;
5573
+ case CrdtType.TEXT:
5574
+ yield [cnode[0], { type: CrdtType.TEXT, parentId: cnode[2], parentKey: cnode[3], data: cnode[4], version: cnode[5] }];
5575
+ break;
5567
5576
  default:
5568
5577
  }
5569
5578
  }
@@ -5592,6 +5601,17 @@ function* nodeStreamToCompactNodes(nodes) {
5592
5601
  const id = node[0];
5593
5602
  const crdt = node[1];
5594
5603
  yield [id, CrdtType.REGISTER, crdt.parentId, crdt.parentKey, crdt.data];
5604
+ } else if (isTextStorageNode(node)) {
5605
+ const id = node[0];
5606
+ const crdt = node[1];
5607
+ yield [
5608
+ id,
5609
+ CrdtType.TEXT,
5610
+ crdt.parentId,
5611
+ crdt.parentKey,
5612
+ crdt.data,
5613
+ crdt.version
5614
+ ];
5595
5615
  } else {
5596
5616
  }
5597
5617
  }
@@ -8427,6 +8447,581 @@ var LiveObject = class _LiveObject extends AbstractCrdt {
8427
8447
  }
8428
8448
  };
8429
8449
 
8450
+ // src/crdts/liveTextOps.ts
8451
+ function attributesEqual(left, right) {
8452
+ if (left === right) {
8453
+ return true;
8454
+ }
8455
+ if (left === void 0 || right === void 0) {
8456
+ return false;
8457
+ }
8458
+ const leftKeys = Object.keys(left);
8459
+ const rightKeys = Object.keys(right);
8460
+ if (leftKeys.length !== rightKeys.length) {
8461
+ return false;
8462
+ }
8463
+ for (const key of leftKeys) {
8464
+ if (left[key] !== right[key]) {
8465
+ return false;
8466
+ }
8467
+ }
8468
+ return true;
8469
+ }
8470
+ function cloneAttributes(attributes) {
8471
+ return attributes === void 0 ? void 0 : freeze({ ...attributes });
8472
+ }
8473
+ function normalizeSegments(segments) {
8474
+ const normalized = [];
8475
+ for (const segment of segments) {
8476
+ if (segment.text.length === 0) {
8477
+ continue;
8478
+ }
8479
+ const last = normalized.at(-1);
8480
+ const attributes = cloneAttributes(segment.attributes);
8481
+ if (last !== void 0 && attributesEqual(last.attributes, attributes)) {
8482
+ last.text += segment.text;
8483
+ } else {
8484
+ normalized.push({ text: segment.text, attributes });
8485
+ }
8486
+ }
8487
+ return normalized;
8488
+ }
8489
+ function deltaToSegments(delta) {
8490
+ return normalizeSegments(
8491
+ delta.map((item) => ({
8492
+ text: item.text,
8493
+ attributes: item.attributes
8494
+ }))
8495
+ );
8496
+ }
8497
+ function segmentsToDelta(segments) {
8498
+ return segments.map(
8499
+ (segment) => segment.attributes === void 0 ? { text: segment.text } : { text: segment.text, attributes: { ...segment.attributes } }
8500
+ );
8501
+ }
8502
+ function textLength(segments) {
8503
+ return segments.reduce((sum, segment) => sum + segment.text.length, 0);
8504
+ }
8505
+ function splitSegmentsAt(segments, index) {
8506
+ const result = [];
8507
+ let offset = 0;
8508
+ for (const segment of segments) {
8509
+ const end = offset + segment.text.length;
8510
+ if (index > offset && index < end) {
8511
+ const before2 = segment.text.slice(0, index - offset);
8512
+ const after2 = segment.text.slice(index - offset);
8513
+ result.push({ text: before2, attributes: segment.attributes });
8514
+ result.push({ text: after2, attributes: segment.attributes });
8515
+ } else {
8516
+ result.push({ text: segment.text, attributes: segment.attributes });
8517
+ }
8518
+ offset = end;
8519
+ }
8520
+ return result;
8521
+ }
8522
+ function clipRange(index, length, contentLength) {
8523
+ const clippedIndex = Math.max(0, Math.min(index, contentLength));
8524
+ const clippedEnd = Math.max(
8525
+ clippedIndex,
8526
+ Math.min(index + length, contentLength)
8527
+ );
8528
+ return { index: clippedIndex, length: clippedEnd - clippedIndex };
8529
+ }
8530
+ function applyInsert(segments, index, text, attributes) {
8531
+ if (text.length === 0) {
8532
+ return normalizeSegments(segments);
8533
+ }
8534
+ const split = splitSegmentsAt(segments, index);
8535
+ const result = [];
8536
+ let offset = 0;
8537
+ let inserted = false;
8538
+ for (const segment of split) {
8539
+ if (!inserted && offset === index) {
8540
+ result.push({ text, attributes });
8541
+ inserted = true;
8542
+ }
8543
+ result.push(segment);
8544
+ offset += segment.text.length;
8545
+ }
8546
+ if (!inserted) {
8547
+ result.push({ text, attributes });
8548
+ }
8549
+ return normalizeSegments(result);
8550
+ }
8551
+ function extractDeletedSegments(segments, index, length) {
8552
+ const split = splitSegmentsAt(
8553
+ splitSegmentsAt(segments, index),
8554
+ index + length
8555
+ );
8556
+ const deleted = [];
8557
+ let offset = 0;
8558
+ for (const segment of split) {
8559
+ const end = offset + segment.text.length;
8560
+ if (offset >= index && end <= index + length) {
8561
+ deleted.push({
8562
+ text: segment.text,
8563
+ attributes: segment.attributes
8564
+ });
8565
+ }
8566
+ offset = end;
8567
+ }
8568
+ return normalizeSegments(deleted);
8569
+ }
8570
+ function applyDelete(segments, index, length) {
8571
+ const deletedSegments = extractDeletedSegments(segments, index, length);
8572
+ const split = splitSegmentsAt(
8573
+ splitSegmentsAt(segments, index),
8574
+ index + length
8575
+ );
8576
+ const result = [];
8577
+ let offset = 0;
8578
+ let deletedText = "";
8579
+ for (const segment of split) {
8580
+ const end = offset + segment.text.length;
8581
+ if (offset >= index && end <= index + length) {
8582
+ deletedText += segment.text;
8583
+ } else {
8584
+ result.push(segment);
8585
+ }
8586
+ offset = end;
8587
+ }
8588
+ return {
8589
+ segments: normalizeSegments(result),
8590
+ deletedText,
8591
+ deletedSegments
8592
+ };
8593
+ }
8594
+ function applyFormat(segments, index, length, attributes) {
8595
+ const split = splitSegmentsAt(
8596
+ splitSegmentsAt(segments, index),
8597
+ index + length
8598
+ );
8599
+ const result = [];
8600
+ let offset = 0;
8601
+ for (const segment of split) {
8602
+ const end = offset + segment.text.length;
8603
+ if (offset >= index && end <= index + length) {
8604
+ const nextAttributes = {
8605
+ ...segment.attributes ?? {}
8606
+ };
8607
+ for (const [key, value] of Object.entries(attributes)) {
8608
+ if (value === null) {
8609
+ delete nextAttributes[key];
8610
+ } else {
8611
+ nextAttributes[key] = value;
8612
+ }
8613
+ }
8614
+ result.push({
8615
+ text: segment.text,
8616
+ attributes: Object.keys(nextAttributes).length === 0 ? void 0 : freeze(nextAttributes)
8617
+ });
8618
+ } else {
8619
+ result.push(segment);
8620
+ }
8621
+ offset = end;
8622
+ }
8623
+ return normalizeSegments(result);
8624
+ }
8625
+ function formatReverseOperations(segments, index, length, patch) {
8626
+ const split = splitSegmentsAt(
8627
+ splitSegmentsAt(segments, index),
8628
+ index + length
8629
+ );
8630
+ const result = [];
8631
+ let offset = 0;
8632
+ for (const segment of split) {
8633
+ const end = offset + segment.text.length;
8634
+ if (offset >= index && end <= index + length) {
8635
+ const attributes = {};
8636
+ for (const key of Object.keys(patch)) {
8637
+ attributes[key] = segment.attributes?.[key] ?? null;
8638
+ }
8639
+ result.push({
8640
+ type: "format",
8641
+ index: offset,
8642
+ length: segment.text.length,
8643
+ attributes
8644
+ });
8645
+ }
8646
+ offset = end;
8647
+ }
8648
+ return result;
8649
+ }
8650
+ function mapIndexThroughOperation(index, op) {
8651
+ if (op.type === "insert") {
8652
+ return op.index <= index ? index + op.text.length : index;
8653
+ } else if (op.type === "delete") {
8654
+ if (op.index >= index) {
8655
+ return index;
8656
+ }
8657
+ return Math.max(op.index, index - op.length);
8658
+ } else {
8659
+ return index;
8660
+ }
8661
+ }
8662
+ function mapTextIndexThroughOperations(index, ops) {
8663
+ let mapped = index;
8664
+ for (const op of ops) {
8665
+ mapped = mapIndexThroughOperation(mapped, op);
8666
+ }
8667
+ return mapped;
8668
+ }
8669
+ function rebaseTextOperations(ops, acceptedOps) {
8670
+ return ops.map((op) => {
8671
+ if (op.type === "insert") {
8672
+ return {
8673
+ ...op,
8674
+ index: mapTextIndexThroughOperations(op.index, acceptedOps)
8675
+ };
8676
+ } else if (op.type === "delete" || op.type === "format") {
8677
+ const start = mapTextIndexThroughOperations(op.index, acceptedOps);
8678
+ const end = mapTextIndexThroughOperations(
8679
+ op.index + op.length,
8680
+ acceptedOps
8681
+ );
8682
+ return { ...op, index: start, length: Math.max(0, end - start) };
8683
+ } else {
8684
+ return op;
8685
+ }
8686
+ });
8687
+ }
8688
+ function applyTextOperationsToSegments(segments, ops) {
8689
+ let next = [...segments];
8690
+ for (const op of ops) {
8691
+ if (op.type === "insert") {
8692
+ const index = Math.max(0, Math.min(op.index, textLength(next)));
8693
+ next = applyInsert(next, index, op.text, op.attributes);
8694
+ } else if (op.type === "delete") {
8695
+ const index = Math.max(0, Math.min(op.index, textLength(next)));
8696
+ const clipped = clipRange(index, op.length, textLength(next));
8697
+ next = applyDelete(next, clipped.index, clipped.length).segments;
8698
+ } else {
8699
+ const index = Math.max(0, Math.min(op.index, textLength(next)));
8700
+ const clipped = clipRange(index, op.length, textLength(next));
8701
+ next = applyFormat(next, clipped.index, clipped.length, op.attributes);
8702
+ }
8703
+ }
8704
+ return next;
8705
+ }
8706
+ function applyLiveTextOperations(delta, ops) {
8707
+ return segmentsToDelta(applyTextOperationsToSegments(deltaToSegments(delta), ops));
8708
+ }
8709
+ function invertTextOperations(segments, ops) {
8710
+ let shadow = [...segments];
8711
+ const reverse = [];
8712
+ for (const op of ops) {
8713
+ if (op.type === "insert") {
8714
+ shadow = applyInsert(shadow, op.index, op.text, op.attributes);
8715
+ reverse.unshift({
8716
+ type: "delete",
8717
+ index: op.index,
8718
+ length: op.text.length
8719
+ });
8720
+ } else if (op.type === "delete") {
8721
+ const deletedSegments = extractDeletedSegments(
8722
+ shadow,
8723
+ op.index,
8724
+ op.length
8725
+ );
8726
+ shadow = applyDelete(shadow, op.index, op.length).segments;
8727
+ const inserts = [];
8728
+ let insertIndex = op.index;
8729
+ for (const segment of deletedSegments) {
8730
+ inserts.push({
8731
+ type: "insert",
8732
+ index: insertIndex,
8733
+ text: segment.text,
8734
+ attributes: segment.attributes
8735
+ });
8736
+ insertIndex += segment.text.length;
8737
+ }
8738
+ for (let index = inserts.length - 1; index >= 0; index--) {
8739
+ reverse.unshift(inserts[index]);
8740
+ }
8741
+ } else {
8742
+ const inverse = formatReverseOperations(
8743
+ shadow,
8744
+ op.index,
8745
+ op.length,
8746
+ op.attributes
8747
+ );
8748
+ shadow = applyFormat(shadow, op.index, op.length, op.attributes);
8749
+ reverse.unshift(...inverse.reverse());
8750
+ }
8751
+ }
8752
+ return reverse;
8753
+ }
8754
+
8755
+ // src/crdts/LiveText.ts
8756
+ var LiveText = class _LiveText extends AbstractCrdt {
8757
+ #segments;
8758
+ #version;
8759
+ #pendingOps;
8760
+ constructor(textOrDelta = "", version = 0) {
8761
+ super();
8762
+ this.#segments = typeof textOrDelta === "string" ? textOrDelta.length === 0 ? [] : [{ text: textOrDelta }] : deltaToSegments(textOrDelta);
8763
+ this.#version = version;
8764
+ this.#pendingOps = /* @__PURE__ */ new Map();
8765
+ }
8766
+ get version() {
8767
+ return this.#version;
8768
+ }
8769
+ get length() {
8770
+ return this.toString().length;
8771
+ }
8772
+ /** @internal */
8773
+ static _deserialize([id, item], _parentToChildren, pool) {
8774
+ const text = new _LiveText(item.data, item.version);
8775
+ text._attach(id, pool);
8776
+ return text;
8777
+ }
8778
+ /** @internal */
8779
+ _toOps(parentId, parentKey) {
8780
+ if (this._id === void 0) {
8781
+ throw new Error("Cannot serialize LiveText if it is not attached");
8782
+ }
8783
+ return [
8784
+ {
8785
+ type: OpCode.CREATE_TEXT,
8786
+ id: this._id,
8787
+ parentId,
8788
+ parentKey,
8789
+ data: this.toDelta(),
8790
+ version: this.#version
8791
+ }
8792
+ ];
8793
+ }
8794
+ /** @internal */
8795
+ _serialize() {
8796
+ if (this.parent.type !== "HasParent") {
8797
+ throw new Error("Cannot serialize LiveText if parent is missing");
8798
+ }
8799
+ return {
8800
+ type: CrdtType.TEXT,
8801
+ parentId: nn(this.parent.node._id, "Parent node expected to have ID"),
8802
+ parentKey: this.parent.key,
8803
+ data: this.toDelta(),
8804
+ version: this.#version
8805
+ };
8806
+ }
8807
+ /** @internal */
8808
+ _attachChild(_op) {
8809
+ throw new Error("LiveText cannot contain child nodes");
8810
+ }
8811
+ /** @internal */
8812
+ _detachChild(_crdt) {
8813
+ throw new Error("LiveText cannot contain child nodes");
8814
+ }
8815
+ /** @internal */
8816
+ _apply(op, isLocal) {
8817
+ if (op.type !== OpCode.UPDATE_TEXT) {
8818
+ return super._apply(op, isLocal);
8819
+ }
8820
+ if (isLocal) {
8821
+ this.#pendingOps.set(nn(op.opId), op.ops);
8822
+ return this.#applyOperations(op.ops, op.version ?? this.#version);
8823
+ }
8824
+ if (op.opId !== void 0) {
8825
+ const pending2 = this.#pendingOps.get(op.opId);
8826
+ this.#pendingOps.delete(op.opId);
8827
+ const otherPending = Array.from(this.#pendingOps.values()).flat();
8828
+ if (pending2 !== void 0 && otherPending.length > 0) {
8829
+ this.#segments = applyTextOperationsToSegments(
8830
+ this.#segments,
8831
+ invertTextOperations(this.#segments, pending2)
8832
+ );
8833
+ const ops2 = rebaseTextOperations(op.ops, otherPending);
8834
+ return this.#applyOperations(
8835
+ ops2,
8836
+ op.version ?? Math.max(this.#version, op.baseVersion + 1)
8837
+ );
8838
+ }
8839
+ this.#version = op.version ?? Math.max(this.#version, op.baseVersion + 1);
8840
+ return { modified: false };
8841
+ }
8842
+ const pending = Array.from(this.#pendingOps.values()).flat();
8843
+ const ops = pending.length > 0 ? rebaseTextOperations(op.ops, pending) : op.ops;
8844
+ return this.#applyOperations(ops, op.version ?? this.#version + 1);
8845
+ }
8846
+ insert(index, text, attributes) {
8847
+ const clippedIndex = Math.max(0, Math.min(index, this.length));
8848
+ this.#dispatch([{ type: "insert", index: clippedIndex, text, attributes }]);
8849
+ }
8850
+ delete(index, length) {
8851
+ const clipped = clipRange(index, length, this.length);
8852
+ if (clipped.length === 0) {
8853
+ return;
8854
+ }
8855
+ this.#dispatch([
8856
+ { type: "delete", index: clipped.index, length: clipped.length }
8857
+ ]);
8858
+ }
8859
+ replace(index, length, text, attributes) {
8860
+ const clipped = clipRange(index, length, this.length);
8861
+ const ops = [];
8862
+ if (clipped.length > 0) {
8863
+ ops.push({
8864
+ type: "delete",
8865
+ index: clipped.index,
8866
+ length: clipped.length
8867
+ });
8868
+ }
8869
+ if (text.length > 0) {
8870
+ ops.push({ type: "insert", index: clipped.index, text, attributes });
8871
+ }
8872
+ this.#dispatch(ops);
8873
+ }
8874
+ format(index, length, attributes) {
8875
+ const clipped = clipRange(index, length, this.length);
8876
+ if (clipped.length === 0) {
8877
+ return;
8878
+ }
8879
+ this.#dispatch([
8880
+ {
8881
+ type: "format",
8882
+ index: clipped.index,
8883
+ length: clipped.length,
8884
+ attributes
8885
+ }
8886
+ ]);
8887
+ }
8888
+ #dispatch(ops) {
8889
+ if (ops.length === 0) {
8890
+ return;
8891
+ }
8892
+ this._pool?.assertStorageIsWritable();
8893
+ const baseVersion = this.#version;
8894
+ const reverse = this._pool !== void 0 && this._id !== void 0 ? this.#invertOperations(ops) : [];
8895
+ const changes = this.#applyOperationsLocally(ops);
8896
+ if (this._pool !== void 0 && this._id !== void 0) {
8897
+ const opId = this._pool.generateOpId();
8898
+ this.#pendingOps.set(opId, ops);
8899
+ this._pool.dispatch(
8900
+ [
8901
+ {
8902
+ type: OpCode.UPDATE_TEXT,
8903
+ id: this._id,
8904
+ opId,
8905
+ baseVersion,
8906
+ ops: [...ops]
8907
+ }
8908
+ ],
8909
+ reverse,
8910
+ /* @__PURE__ */ new Map([
8911
+ [
8912
+ this._id,
8913
+ {
8914
+ type: "LiveText",
8915
+ node: this,
8916
+ version: this.#version,
8917
+ updates: changes
8918
+ }
8919
+ ]
8920
+ ])
8921
+ );
8922
+ }
8923
+ }
8924
+ #applyOperations(ops, version) {
8925
+ const reverse = this.#invertOperations(ops);
8926
+ const changes = this.#applyOperationsLocally(ops);
8927
+ this.#version = Math.max(this.#version, version);
8928
+ return {
8929
+ reverse,
8930
+ modified: {
8931
+ type: "LiveText",
8932
+ node: this,
8933
+ version: this.#version,
8934
+ updates: changes
8935
+ }
8936
+ };
8937
+ }
8938
+ #applyOperationsLocally(ops) {
8939
+ const changes = [];
8940
+ for (const op of ops) {
8941
+ if (op.type === "insert") {
8942
+ this.#segments = applyInsert(
8943
+ this.#segments,
8944
+ op.index,
8945
+ op.text,
8946
+ op.attributes
8947
+ );
8948
+ changes.push({
8949
+ type: "insert",
8950
+ index: op.index,
8951
+ text: op.text,
8952
+ attributes: op.attributes
8953
+ });
8954
+ } else if (op.type === "delete") {
8955
+ const result = applyDelete(this.#segments, op.index, op.length);
8956
+ this.#segments = result.segments;
8957
+ changes.push({
8958
+ type: "delete",
8959
+ index: op.index,
8960
+ length: op.length,
8961
+ deletedText: result.deletedText
8962
+ });
8963
+ } else {
8964
+ this.#segments = applyFormat(
8965
+ this.#segments,
8966
+ op.index,
8967
+ op.length,
8968
+ op.attributes
8969
+ );
8970
+ changes.push({
8971
+ type: "format",
8972
+ index: op.index,
8973
+ length: op.length,
8974
+ attributes: op.attributes
8975
+ });
8976
+ }
8977
+ }
8978
+ this.invalidate();
8979
+ return changes;
8980
+ }
8981
+ #invertOperations(ops) {
8982
+ return [
8983
+ {
8984
+ type: OpCode.UPDATE_TEXT,
8985
+ id: nn(this._id),
8986
+ baseVersion: this.#version,
8987
+ ops: invertTextOperations(this.#segments, ops)
8988
+ }
8989
+ ];
8990
+ }
8991
+ toString() {
8992
+ return this.#segments.map((segment) => segment.text).join("");
8993
+ }
8994
+ toDelta() {
8995
+ return segmentsToDelta(this.#segments);
8996
+ }
8997
+ toJSON() {
8998
+ return super.toJSON();
8999
+ }
9000
+ /** @internal */
9001
+ _toJSON() {
9002
+ return this.toDelta();
9003
+ }
9004
+ /** @internal */
9005
+ _toTreeNode(key) {
9006
+ return {
9007
+ type: "LiveText",
9008
+ id: this._id ?? nanoid(),
9009
+ key,
9010
+ payload: [
9011
+ {
9012
+ type: "Json",
9013
+ id: `${this._id ?? nanoid()}:text`,
9014
+ key: "text",
9015
+ payload: this.toString()
9016
+ }
9017
+ ]
9018
+ };
9019
+ }
9020
+ clone() {
9021
+ return new _LiveText(this.toDelta(), this.#version);
9022
+ }
9023
+ };
9024
+
8430
9025
  // src/crdts/liveblocks-helpers.ts
8431
9026
  function creationOpToLiveNode(op) {
8432
9027
  return lsonToLiveNode(creationOpToLson(op));
@@ -8441,6 +9036,8 @@ function creationOpToLson(op) {
8441
9036
  return new LiveMap();
8442
9037
  case OpCode.CREATE_LIST:
8443
9038
  return new LiveList([]);
9039
+ case OpCode.CREATE_TEXT:
9040
+ return new LiveText(op.data, op.version);
8444
9041
  default:
8445
9042
  return assertNever(op, "Unknown creation Op");
8446
9043
  }
@@ -8463,6 +9060,8 @@ function deserialize(node, parentToChildren, pool) {
8463
9060
  return LiveMap._deserialize(node, parentToChildren, pool);
8464
9061
  } else if (isRegisterStorageNode(node)) {
8465
9062
  return LiveRegister._deserialize(node, parentToChildren, pool);
9063
+ } else if (isTextStorageNode(node)) {
9064
+ return LiveText._deserialize(node, parentToChildren, pool);
8466
9065
  } else {
8467
9066
  throw new Error("Unexpected CRDT type");
8468
9067
  }
@@ -8476,12 +9075,14 @@ function deserializeToLson(node, parentToChildren, pool) {
8476
9075
  return LiveMap._deserialize(node, parentToChildren, pool);
8477
9076
  } else if (isRegisterStorageNode(node)) {
8478
9077
  return node[1].data;
9078
+ } else if (isTextStorageNode(node)) {
9079
+ return LiveText._deserialize(node, parentToChildren, pool);
8479
9080
  } else {
8480
9081
  throw new Error("Unexpected CRDT type");
8481
9082
  }
8482
9083
  }
8483
9084
  function isLiveStructure(value) {
8484
- return isLiveList(value) || isLiveMap(value) || isLiveObject(value);
9085
+ return isLiveList(value) || isLiveMap(value) || isLiveObject(value) || isLiveText(value);
8485
9086
  }
8486
9087
  function isLiveNode(value) {
8487
9088
  return isLiveStructure(value) || isLiveRegister(value);
@@ -8495,6 +9096,9 @@ function isLiveMap(value) {
8495
9096
  function isLiveObject(value) {
8496
9097
  return value instanceof LiveObject;
8497
9098
  }
9099
+ function isLiveText(value) {
9100
+ return value instanceof LiveText;
9101
+ }
8498
9102
  function isLiveRegister(value) {
8499
9103
  return value instanceof LiveRegister;
8500
9104
  }
@@ -8504,14 +9108,14 @@ function cloneLson(value) {
8504
9108
  function liveNodeToLson(obj) {
8505
9109
  if (obj instanceof LiveRegister) {
8506
9110
  return obj.data;
8507
- } else if (obj instanceof LiveList || obj instanceof LiveMap || obj instanceof LiveObject) {
9111
+ } else if (obj instanceof LiveList || obj instanceof LiveMap || obj instanceof LiveObject || obj instanceof LiveText) {
8508
9112
  return obj;
8509
9113
  } else {
8510
9114
  return assertNever(obj, "Unknown AbstractCrdt");
8511
9115
  }
8512
9116
  }
8513
9117
  function lsonToLiveNode(value) {
8514
- if (value instanceof LiveObject || value instanceof LiveMap || value instanceof LiveList) {
9118
+ if (value instanceof LiveObject || value instanceof LiveMap || value instanceof LiveList || value instanceof LiveText) {
8515
9119
  return value;
8516
9120
  } else {
8517
9121
  return new LiveRegister(value);
@@ -8606,6 +9210,38 @@ function getTreesDiffOperations(currentItems, newItems) {
8606
9210
  }
8607
9211
  }
8608
9212
  }
9213
+ if (crdt.type === CrdtType.TEXT) {
9214
+ if (currentCrdt.type !== CrdtType.TEXT || stringifyOrLog(crdt.data) !== stringifyOrLog(currentCrdt.data) || crdt.version !== currentCrdt.version) {
9215
+ ops.push({
9216
+ type: OpCode.UPDATE_TEXT,
9217
+ id,
9218
+ baseVersion: currentCrdt.type === CrdtType.TEXT ? currentCrdt.version : 0,
9219
+ version: crdt.version,
9220
+ ops: [
9221
+ {
9222
+ type: "delete",
9223
+ index: 0,
9224
+ length: currentCrdt.type === CrdtType.TEXT ? currentCrdt.data.reduce(
9225
+ (sum, item) => sum + item.text.length,
9226
+ 0
9227
+ ) : 0
9228
+ },
9229
+ ...crdt.data.map(
9230
+ (item, index, items) => item.attributes === void 0 ? {
9231
+ type: "insert",
9232
+ index: items.slice(0, index).reduce((sum, item2) => sum + item2.text.length, 0),
9233
+ text: item.text
9234
+ } : {
9235
+ type: "insert",
9236
+ index: items.slice(0, index).reduce((sum, item2) => sum + item2.text.length, 0),
9237
+ text: item.text,
9238
+ attributes: item.attributes
9239
+ }
9240
+ )
9241
+ ]
9242
+ });
9243
+ }
9244
+ }
8609
9245
  if (crdt.parentKey !== currentCrdt.parentKey) {
8610
9246
  ops.push({
8611
9247
  type: OpCode.SET_PARENT_KEY,
@@ -8654,6 +9290,16 @@ function getTreesDiffOperations(currentItems, newItems) {
8654
9290
  parentKey: crdt.parentKey
8655
9291
  });
8656
9292
  break;
9293
+ case CrdtType.TEXT:
9294
+ ops.push({
9295
+ type: OpCode.CREATE_TEXT,
9296
+ id,
9297
+ parentId: crdt.parentId,
9298
+ parentKey: crdt.parentKey,
9299
+ data: crdt.data,
9300
+ version: crdt.version
9301
+ });
9302
+ break;
8657
9303
  }
8658
9304
  }
8659
9305
  });
@@ -8686,6 +9332,12 @@ function mergeListStorageUpdates(first, second) {
8686
9332
  updates: updates.concat(second.updates)
8687
9333
  };
8688
9334
  }
9335
+ function mergeTextStorageUpdates(first, second) {
9336
+ return {
9337
+ ...second,
9338
+ updates: first.updates.concat(second.updates)
9339
+ };
9340
+ }
8689
9341
  function mergeStorageUpdates(first, second) {
8690
9342
  if (first === void 0) {
8691
9343
  return second;
@@ -8696,6 +9348,8 @@ function mergeStorageUpdates(first, second) {
8696
9348
  return mergeMapStorageUpdates(first, second);
8697
9349
  } else if (first.type === "LiveList" && second.type === "LiveList") {
8698
9350
  return mergeListStorageUpdates(first, second);
9351
+ } else if (first.type === "LiveText" && second.type === "LiveText") {
9352
+ return mergeTextStorageUpdates(first, second);
8699
9353
  } else {
8700
9354
  }
8701
9355
  return second;
@@ -9984,7 +10638,7 @@ function createRoom(options, config) {
9984
10638
  );
9985
10639
  output.reverse.pushLeft(applyOpResult.reverse);
9986
10640
  }
9987
- if (op.type === OpCode.CREATE_LIST || op.type === OpCode.CREATE_MAP || op.type === OpCode.CREATE_OBJECT) {
10641
+ if (op.type === OpCode.CREATE_LIST || op.type === OpCode.CREATE_MAP || op.type === OpCode.CREATE_OBJECT || op.type === OpCode.CREATE_TEXT) {
9988
10642
  createdNodeIds.add(op.id);
9989
10643
  }
9990
10644
  }
@@ -10004,6 +10658,7 @@ function createRoom(options, config) {
10004
10658
  switch (op.type) {
10005
10659
  case OpCode.DELETE_OBJECT_KEY:
10006
10660
  case OpCode.UPDATE_OBJECT:
10661
+ case OpCode.UPDATE_TEXT:
10007
10662
  case OpCode.DELETE_CRDT: {
10008
10663
  const node = context.pool.nodes.get(op.id);
10009
10664
  if (node === void 0) {
@@ -10028,6 +10683,7 @@ function createRoom(options, config) {
10028
10683
  case OpCode.CREATE_OBJECT:
10029
10684
  case OpCode.CREATE_LIST:
10030
10685
  case OpCode.CREATE_MAP:
10686
+ case OpCode.CREATE_TEXT:
10031
10687
  case OpCode.CREATE_REGISTER: {
10032
10688
  if (op.parentId === void 0) {
10033
10689
  return { modified: false };
@@ -12187,6 +12843,12 @@ function toPlainLson(lson) {
12187
12843
  liveblocksType: "LiveList",
12188
12844
  data: [...lson].map((item) => toPlainLson(item))
12189
12845
  };
12846
+ } else if (lson instanceof LiveText) {
12847
+ return {
12848
+ liveblocksType: "LiveText",
12849
+ data: lson.toDelta(),
12850
+ version: lson.version
12851
+ };
12190
12852
  } else {
12191
12853
  return lson;
12192
12854
  }
@@ -12368,6 +13030,7 @@ export {
12368
13030
  LiveList,
12369
13031
  LiveMap,
12370
13032
  LiveObject,
13033
+ LiveText,
12371
13034
  LiveblocksError,
12372
13035
  MENTION_CHARACTER,
12373
13036
  MutableSignal,
@@ -12379,6 +13042,7 @@ export {
12379
13042
  SortedList,
12380
13043
  TextEditorType,
12381
13044
  WebsocketCloseCodes,
13045
+ applyLiveTextOperations,
12382
13046
  asPos,
12383
13047
  assert,
12384
13048
  assertNever,
@@ -12405,7 +13069,6 @@ export {
12405
13069
  createManagedPool,
12406
13070
  createNotificationSettings,
12407
13071
  createThreadId,
12408
- deepLiveify,
12409
13072
  defineAiTool,
12410
13073
  deprecate,
12411
13074
  deprecateIf,
@@ -12435,6 +13098,7 @@ export {
12435
13098
  isRegisterStorageNode,
12436
13099
  isRootStorageNode,
12437
13100
  isStartsWithOperator,
13101
+ isTextStorageNode,
12438
13102
  isUrl,
12439
13103
  kInternal,
12440
13104
  keys,