@liveblocks/core 3.19.4-test1 → 3.20.0-pre1

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.4-test1";
9
+ var PKG_VERSION = "3.20.0-pre1";
10
10
  var PKG_FORMAT = "esm";
11
11
 
12
12
  // src/dupe-detection.ts
@@ -2715,6 +2715,7 @@ var HttpClient = class {
2715
2715
  };
2716
2716
 
2717
2717
  // src/lib/fsm.ts
2718
+ var IGNORE = /* @__PURE__ */ Symbol("fsm.ignore");
2718
2719
  function distance(state1, state2) {
2719
2720
  if (state1 === state2) {
2720
2721
  return [0, 0];
@@ -2887,7 +2888,7 @@ var FSM = class {
2887
2888
  this.#eventHub = {
2888
2889
  didReceiveEvent: makeEventSource(),
2889
2890
  willTransition: makeEventSource(),
2890
- didIgnoreEvent: makeEventSource(),
2891
+ didIgnoreUnexpectedEvent: makeEventSource(),
2891
2892
  willExitState: makeEventSource(),
2892
2893
  didEnterState: makeEventSource(),
2893
2894
  didExitState: makeEventSource()
@@ -2895,7 +2896,7 @@ var FSM = class {
2895
2896
  this.events = {
2896
2897
  didReceiveEvent: this.#eventHub.didReceiveEvent.observable,
2897
2898
  willTransition: this.#eventHub.willTransition.observable,
2898
- didIgnoreEvent: this.#eventHub.didIgnoreEvent.observable,
2899
+ didIgnoreUnexpectedEvent: this.#eventHub.didIgnoreUnexpectedEvent.observable,
2899
2900
  willExitState: this.#eventHub.willExitState.observable,
2900
2901
  didEnterState: this.#eventHub.didEnterState.observable,
2901
2902
  didExitState: this.#eventHub.didExitState.observable
@@ -3018,9 +3019,14 @@ var FSM = class {
3018
3019
  * `context` params to conditionally decide which next state to transition
3019
3020
  * to.
3020
3021
  *
3021
- * If you set it to `null`, then the transition will be explicitly forbidden
3022
- * and throw an error. If you don't define a target for a transition, then
3023
- * such events will get ignored.
3022
+ * If you don't define a target for a transition, the event is treated
3023
+ * as unhandled in this state: `didIgnoreUnexpectedEvent` fires and the
3024
+ * state does not change.
3025
+ *
3026
+ * To declare an event as an intentional silent no-op in this state, use
3027
+ * the {@link IGNORE} sentinel — either statically (`{ EVENT: IGNORE }`)
3028
+ * or as a return value from a target function. IGNORE'd events do not
3029
+ * fire `didIgnoreUnexpectedEvent`.
3024
3030
  */
3025
3031
  addTransitions(nameOrPattern, mapping) {
3026
3032
  if (this.#runningState !== 0 /* NOT_STARTED_YET */) {
@@ -3040,7 +3046,12 @@ var FSM = class {
3040
3046
  }
3041
3047
  const target = target_;
3042
3048
  this.#knownEventTypes.add(type);
3043
- if (target !== void 0) {
3049
+ if (target === void 0) {
3050
+ continue;
3051
+ }
3052
+ if (target === IGNORE) {
3053
+ map.set(type, IGNORE);
3054
+ } else {
3044
3055
  const targetFn = typeof target === "function" ? target : () => target;
3045
3056
  map.set(type, targetFn);
3046
3057
  }
@@ -3139,12 +3150,14 @@ var FSM = class {
3139
3150
  if (this.#runningState === 2 /* STOPPED */) {
3140
3151
  return;
3141
3152
  }
3142
- const targetFn = this.#getTargetFn(event.type);
3143
- if (targetFn !== void 0) {
3144
- return this.#transition(event, targetFn);
3145
- } else {
3146
- this.#eventHub.didIgnoreEvent.notify(event);
3153
+ const entry = this.#getTargetFn(event.type);
3154
+ if (entry === IGNORE) {
3155
+ return;
3156
+ }
3157
+ if (entry !== void 0) {
3158
+ return this.#transition(event, entry);
3147
3159
  }
3160
+ this.#eventHub.didIgnoreUnexpectedEvent.notify(event);
3148
3161
  }
3149
3162
  #transition(event, target) {
3150
3163
  this.#eventHub.didReceiveEvent.notify(event);
@@ -3153,8 +3166,7 @@ var FSM = class {
3153
3166
  const nextTarget = targetFn(event, this.#currentContext.current);
3154
3167
  let nextState;
3155
3168
  let effects = void 0;
3156
- if (nextTarget === null) {
3157
- this.#eventHub.didIgnoreEvent.notify(event);
3169
+ if (nextTarget === IGNORE) {
3158
3170
  return;
3159
3171
  }
3160
3172
  if (typeof nextTarget === "string") {
@@ -3373,8 +3385,8 @@ function enableTracing(machine) {
3373
3385
  machine.events.didExitState.subscribe(
3374
3386
  ({ state, durationMs }) => log2(`Exited ${state} after ${durationMs.toFixed(0)}ms`)
3375
3387
  ),
3376
- machine.events.didIgnoreEvent.subscribe(
3377
- (e) => log2("Ignored event", e.type, e, "(current state won't handle it)")
3388
+ machine.events.didIgnoreUnexpectedEvent.subscribe(
3389
+ (e) => log2("Ignored unexpected event", e.type, e, "(no transition declared)")
3378
3390
  )
3379
3391
  ];
3380
3392
  return () => {
@@ -3486,7 +3498,12 @@ function createConnectionStateMachine(delegates, options) {
3486
3498
  );
3487
3499
  const onSocketError = (event) => machine.send({ type: "EXPLICIT_SOCKET_ERROR", event });
3488
3500
  const onSocketClose = (event) => machine.send({ type: "EXPLICIT_SOCKET_CLOSE", event });
3489
- const onSocketMessage = (event) => event.data === "pong" ? machine.send({ type: "PONG" }) : onMessage.notify(event);
3501
+ const onSocketMessage = (event) => {
3502
+ machine.send({ type: "ALIVE" });
3503
+ if (event.data !== "pong") {
3504
+ onMessage.notify(event);
3505
+ }
3506
+ };
3490
3507
  function teardownSocket(socket) {
3491
3508
  if (socket) {
3492
3509
  socket.removeEventListener("error", onSocketError);
@@ -3656,7 +3673,13 @@ function createConnectionStateMachine(delegates, options) {
3656
3673
  effect: [increaseBackoffDelay, logPrematureErrorOrCloseEvent(err)]
3657
3674
  };
3658
3675
  }
3659
- );
3676
+ ).addTransitions("@connecting.busy", {
3677
+ // The socket message listener is attached during @connecting.busy (see
3678
+ // onEnterAsync above), so server frames (most notably the actor-id
3679
+ // handshake) can fire onSocketMessage and emit a ALIVE before we
3680
+ // reach @ok.*. That's fine. Heartbeat only matters in @ok.*.
3681
+ ALIVE: IGNORE
3682
+ });
3660
3683
  const sendHeartbeat = {
3661
3684
  target: "@ok.awaiting-pong",
3662
3685
  effect: (ctx) => {
@@ -3671,7 +3694,8 @@ function createConnectionStateMachine(delegates, options) {
3671
3694
  machine.addTimedTransition("@ok.connected", HEARTBEAT_INTERVAL, maybeHeartbeat).addTransitions("@ok.connected", {
3672
3695
  NAVIGATOR_OFFLINE: maybeHeartbeat,
3673
3696
  // Don't take the browser's word for it when it says it's offline. Do a ping/pong to make sure.
3674
- WINDOW_GOT_FOCUS: sendHeartbeat
3697
+ WINDOW_GOT_FOCUS: sendHeartbeat,
3698
+ ALIVE: IGNORE
3675
3699
  });
3676
3700
  machine.addTransitions("@idle.zombie", {
3677
3701
  WINDOW_GOT_FOCUS: "@connecting.backoff"
@@ -3692,7 +3716,7 @@ function createConnectionStateMachine(delegates, options) {
3692
3716
  clearTimeout(timerID);
3693
3717
  onMessage.pause();
3694
3718
  };
3695
- }).addTransitions("@ok.awaiting-pong", { PONG: "@ok.connected" }).addTimedTransition("@ok.awaiting-pong", PONG_TIMEOUT, {
3719
+ }).addTransitions("@ok.awaiting-pong", { ALIVE: "@ok.connected" }).addTimedTransition("@ok.awaiting-pong", PONG_TIMEOUT, {
3696
3720
  target: "@connecting.busy",
3697
3721
  // Log implicit connection loss and drop the current open socket
3698
3722
  effect: log(
@@ -3705,7 +3729,7 @@ function createConnectionStateMachine(delegates, options) {
3705
3729
  // not. When still OPEN, don't transition.
3706
3730
  EXPLICIT_SOCKET_ERROR: (_, context) => {
3707
3731
  if (context.socket?.readyState === 1) {
3708
- return null;
3732
+ return IGNORE;
3709
3733
  }
3710
3734
  return {
3711
3735
  target: "@connecting.backoff",
@@ -5473,9 +5497,7 @@ var OpCode = Object.freeze({
5473
5497
  DELETE_CRDT: 5,
5474
5498
  DELETE_OBJECT_KEY: 6,
5475
5499
  CREATE_MAP: 7,
5476
- CREATE_REGISTER: 8,
5477
- CREATE_TEXT: 9,
5478
- UPDATE_TEXT: 10
5500
+ CREATE_REGISTER: 8
5479
5501
  });
5480
5502
  function isIgnoredOp(op) {
5481
5503
  return op.type === OpCode.DELETE_CRDT && op.id === "ACK";
@@ -5486,8 +5508,7 @@ var CrdtType = Object.freeze({
5486
5508
  OBJECT: 0,
5487
5509
  LIST: 1,
5488
5510
  MAP: 2,
5489
- REGISTER: 3,
5490
- TEXT: 4
5511
+ REGISTER: 3
5491
5512
  });
5492
5513
  function isRootStorageNode(node) {
5493
5514
  return node[0] === "root";
@@ -5504,9 +5525,6 @@ function isMapStorageNode(node) {
5504
5525
  function isRegisterStorageNode(node) {
5505
5526
  return node[1].type === CrdtType.REGISTER;
5506
5527
  }
5507
- function isTextStorageNode(node) {
5508
- return node[1].type === CrdtType.TEXT;
5509
- }
5510
5528
  function isCompactRootNode(node) {
5511
5529
  return node[0] === "root";
5512
5530
  }
@@ -5529,9 +5547,6 @@ function* compactNodesToNodeStream(compactNodes) {
5529
5547
  case CrdtType.REGISTER:
5530
5548
  yield [cnode[0], { type: CrdtType.REGISTER, parentId: cnode[2], parentKey: cnode[3], data: cnode[4] }];
5531
5549
  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;
5535
5550
  default:
5536
5551
  }
5537
5552
  }
@@ -5560,17 +5575,6 @@ function* nodeStreamToCompactNodes(nodes) {
5560
5575
  const id = node[0];
5561
5576
  const crdt = node[1];
5562
5577
  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
- ];
5574
5578
  } else {
5575
5579
  }
5576
5580
  }
@@ -6119,9 +6123,9 @@ var LiveList = class _LiveList extends AbstractCrdt {
6119
6123
  ops.push(op);
6120
6124
  for (const item of this.#items) {
6121
6125
  const parentKey2 = item._getParentKeyOrThrow();
6122
- const childOps = HACK_addIntentAndDeletedIdToOperation(
6126
+ const childOps = addIntentToOpTree(
6123
6127
  item._toOps(this._id, parentKey2),
6124
- void 0
6128
+ "set"
6125
6129
  );
6126
6130
  for (const childOp of childOps) {
6127
6131
  ops.push(childOp);
@@ -6435,8 +6439,9 @@ var LiveList = class _LiveList extends AbstractCrdt {
6435
6439
  existingItem._detach();
6436
6440
  this.#items.remove(existingItem);
6437
6441
  this.#items.add(child);
6438
- const reverse = HACK_addIntentAndDeletedIdToOperation(
6442
+ const reverse = addIntentToOpTree(
6439
6443
  existingItem._toOps(nn(this._id), key),
6444
+ "set",
6440
6445
  op.id
6441
6446
  );
6442
6447
  const delta = [setDelta(indexOfItemWithSameKey, child)];
@@ -6679,8 +6684,7 @@ var LiveList = class _LiveList extends AbstractCrdt {
6679
6684
  * @param element The element to add to the end of the LiveList.
6680
6685
  */
6681
6686
  push(element) {
6682
- this._pool?.assertStorageIsWritable();
6683
- return this.insert(element, this.length);
6687
+ return this.#injectAt(element, this.length, "push");
6684
6688
  }
6685
6689
  /**
6686
6690
  * Inserts one element at a specified index.
@@ -6688,6 +6692,15 @@ var LiveList = class _LiveList extends AbstractCrdt {
6688
6692
  * @param index The index at which you want to insert the element.
6689
6693
  */
6690
6694
  insert(element, index) {
6695
+ return this.#injectAt(element, index, "insert");
6696
+ }
6697
+ /**
6698
+ * Shared implementation of `insert` and `push`. A `"push"` intent leaves the
6699
+ * client-computed position untouched (so optimistic rendering is unchanged),
6700
+ * but tags the Op so the server appends it to the true end of the list
6701
+ * instead of resolving its position against the client's stale view.
6702
+ */
6703
+ #injectAt(element, index, intent) {
6691
6704
  this._pool?.assertStorageIsWritable();
6692
6705
  if (index < 0 || index > this.#items.length) {
6693
6706
  throw new Error(
@@ -6703,8 +6716,9 @@ var LiveList = class _LiveList extends AbstractCrdt {
6703
6716
  if (this._pool && this._id) {
6704
6717
  const id = this._pool.generateId();
6705
6718
  value._attach(id, this._pool);
6719
+ const ops = value._toOpsWithOpId(this._id, position, this._pool);
6706
6720
  this._pool.dispatch(
6707
- value._toOpsWithOpId(this._id, position, this._pool),
6721
+ intent === "push" ? addIntentToOpTree(ops, "push") : ops,
6708
6722
  [{ type: OpCode.DELETE_CRDT, id }],
6709
6723
  /* @__PURE__ */ new Map([
6710
6724
  [this._id, makeUpdate(this, [insertDelta(index, value)])]
@@ -6862,13 +6876,15 @@ var LiveList = class _LiveList extends AbstractCrdt {
6862
6876
  value._attach(id, this._pool);
6863
6877
  const storageUpdates = /* @__PURE__ */ new Map();
6864
6878
  storageUpdates.set(this._id, makeUpdate(this, [setDelta(index, value)]));
6865
- const ops = HACK_addIntentAndDeletedIdToOperation(
6879
+ const ops = addIntentToOpTree(
6866
6880
  value._toOpsWithOpId(this._id, position, this._pool),
6881
+ "set",
6867
6882
  existingId
6868
6883
  );
6869
6884
  this.#unacknowledgedSets.set(position, nn(ops[0].opId));
6870
- const reverseOps = HACK_addIntentAndDeletedIdToOperation(
6885
+ const reverseOps = addIntentToOpTree(
6871
6886
  existingItem._toOps(this._id, position),
6887
+ "set",
6872
6888
  id
6873
6889
  );
6874
6890
  this._pool.dispatch(ops, reverseOps, storageUpdates);
@@ -7069,15 +7085,11 @@ function moveDelta(previousIndex, index, item) {
7069
7085
  previousIndex
7070
7086
  };
7071
7087
  }
7072
- function HACK_addIntentAndDeletedIdToOperation(ops, deletedId) {
7088
+ function addIntentToOpTree(ops, intent, deletedId) {
7073
7089
  return ops.map((op, index) => {
7074
7090
  if (index === 0) {
7075
7091
  const firstOp = op;
7076
- return {
7077
- ...firstOp,
7078
- intent: "set",
7079
- deletedId
7080
- };
7092
+ return { ...firstOp, intent, deletedId };
7081
7093
  } else {
7082
7094
  return op;
7083
7095
  }
@@ -8227,581 +8239,6 @@ var LiveObject = class _LiveObject extends AbstractCrdt {
8227
8239
  }
8228
8240
  };
8229
8241
 
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
-
8805
8242
  // src/crdts/liveblocks-helpers.ts
8806
8243
  function creationOpToLiveNode(op) {
8807
8244
  return lsonToLiveNode(creationOpToLson(op));
@@ -8816,8 +8253,6 @@ function creationOpToLson(op) {
8816
8253
  return new LiveMap();
8817
8254
  case OpCode.CREATE_LIST:
8818
8255
  return new LiveList([]);
8819
- case OpCode.CREATE_TEXT:
8820
- return new LiveText(op.data, op.version);
8821
8256
  default:
8822
8257
  return assertNever(op, "Unknown creation Op");
8823
8258
  }
@@ -8840,8 +8275,6 @@ function deserialize(node, parentToChildren, pool) {
8840
8275
  return LiveMap._deserialize(node, parentToChildren, pool);
8841
8276
  } else if (isRegisterStorageNode(node)) {
8842
8277
  return LiveRegister._deserialize(node, parentToChildren, pool);
8843
- } else if (isTextStorageNode(node)) {
8844
- return LiveText._deserialize(node, parentToChildren, pool);
8845
8278
  } else {
8846
8279
  throw new Error("Unexpected CRDT type");
8847
8280
  }
@@ -8855,14 +8288,12 @@ function deserializeToLson(node, parentToChildren, pool) {
8855
8288
  return LiveMap._deserialize(node, parentToChildren, pool);
8856
8289
  } else if (isRegisterStorageNode(node)) {
8857
8290
  return node[1].data;
8858
- } else if (isTextStorageNode(node)) {
8859
- return LiveText._deserialize(node, parentToChildren, pool);
8860
8291
  } else {
8861
8292
  throw new Error("Unexpected CRDT type");
8862
8293
  }
8863
8294
  }
8864
8295
  function isLiveStructure(value) {
8865
- return isLiveList(value) || isLiveMap(value) || isLiveObject(value) || isLiveText(value);
8296
+ return isLiveList(value) || isLiveMap(value) || isLiveObject(value);
8866
8297
  }
8867
8298
  function isLiveNode(value) {
8868
8299
  return isLiveStructure(value) || isLiveRegister(value);
@@ -8876,9 +8307,6 @@ function isLiveMap(value) {
8876
8307
  function isLiveObject(value) {
8877
8308
  return value instanceof LiveObject;
8878
8309
  }
8879
- function isLiveText(value) {
8880
- return value instanceof LiveText;
8881
- }
8882
8310
  function isLiveRegister(value) {
8883
8311
  return value instanceof LiveRegister;
8884
8312
  }
@@ -8888,14 +8316,14 @@ function cloneLson(value) {
8888
8316
  function liveNodeToLson(obj) {
8889
8317
  if (obj instanceof LiveRegister) {
8890
8318
  return obj.data;
8891
- } else if (obj instanceof LiveList || obj instanceof LiveMap || obj instanceof LiveObject || obj instanceof LiveText) {
8319
+ } else if (obj instanceof LiveList || obj instanceof LiveMap || obj instanceof LiveObject) {
8892
8320
  return obj;
8893
8321
  } else {
8894
8322
  return assertNever(obj, "Unknown AbstractCrdt");
8895
8323
  }
8896
8324
  }
8897
8325
  function lsonToLiveNode(value) {
8898
- if (value instanceof LiveObject || value instanceof LiveMap || value instanceof LiveList || value instanceof LiveText) {
8326
+ if (value instanceof LiveObject || value instanceof LiveMap || value instanceof LiveList) {
8899
8327
  return value;
8900
8328
  } else {
8901
8329
  return new LiveRegister(value);
@@ -8920,38 +8348,6 @@ function getTreesDiffOperations(currentItems, newItems) {
8920
8348
  });
8921
8349
  }
8922
8350
  }
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
- }
8955
8351
  if (crdt.parentKey !== currentCrdt.parentKey) {
8956
8352
  ops.push({
8957
8353
  type: OpCode.SET_PARENT_KEY,
@@ -9000,16 +8396,6 @@ function getTreesDiffOperations(currentItems, newItems) {
9000
8396
  parentKey: crdt.parentKey
9001
8397
  });
9002
8398
  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;
9013
8399
  }
9014
8400
  }
9015
8401
  });
@@ -9042,12 +8428,6 @@ function mergeListStorageUpdates(first, second) {
9042
8428
  updates: updates.concat(second.updates)
9043
8429
  };
9044
8430
  }
9045
- function mergeTextStorageUpdates(first, second) {
9046
- return {
9047
- ...second,
9048
- updates: first.updates.concat(second.updates)
9049
- };
9050
- }
9051
8431
  function mergeStorageUpdates(first, second) {
9052
8432
  if (first === void 0) {
9053
8433
  return second;
@@ -9058,8 +8438,6 @@ function mergeStorageUpdates(first, second) {
9058
8438
  return mergeMapStorageUpdates(first, second);
9059
8439
  } else if (first.type === "LiveList" && second.type === "LiveList") {
9060
8440
  return mergeListStorageUpdates(first, second);
9061
- } else if (first.type === "LiveText" && second.type === "LiveText") {
9062
- return mergeTextStorageUpdates(first, second);
9063
8441
  } else {
9064
8442
  }
9065
8443
  return second;
@@ -10341,7 +9719,7 @@ function createRoom(options, config) {
10341
9719
  );
10342
9720
  output.reverse.pushLeft(applyOpResult.reverse);
10343
9721
  }
10344
- if (op.type === OpCode.CREATE_LIST || op.type === OpCode.CREATE_MAP || op.type === OpCode.CREATE_OBJECT || op.type === OpCode.CREATE_TEXT) {
9722
+ if (op.type === OpCode.CREATE_LIST || op.type === OpCode.CREATE_MAP || op.type === OpCode.CREATE_OBJECT) {
10345
9723
  createdNodeIds.add(op.id);
10346
9724
  }
10347
9725
  }
@@ -10361,7 +9739,6 @@ function createRoom(options, config) {
10361
9739
  switch (op.type) {
10362
9740
  case OpCode.DELETE_OBJECT_KEY:
10363
9741
  case OpCode.UPDATE_OBJECT:
10364
- case OpCode.UPDATE_TEXT:
10365
9742
  case OpCode.DELETE_CRDT: {
10366
9743
  const node = context.pool.nodes.get(op.id);
10367
9744
  if (node === void 0) {
@@ -10386,7 +9763,6 @@ function createRoom(options, config) {
10386
9763
  case OpCode.CREATE_OBJECT:
10387
9764
  case OpCode.CREATE_LIST:
10388
9765
  case OpCode.CREATE_MAP:
10389
- case OpCode.CREATE_TEXT:
10390
9766
  case OpCode.CREATE_REGISTER: {
10391
9767
  if (op.parentId === void 0) {
10392
9768
  return { modified: false };
@@ -12541,12 +11917,6 @@ function toPlainLson(lson) {
12541
11917
  liveblocksType: "LiveList",
12542
11918
  data: [...lson].map((item) => toPlainLson(item))
12543
11919
  };
12544
- } else if (lson instanceof LiveText) {
12545
- return {
12546
- liveblocksType: "LiveText",
12547
- data: lson.toDelta(),
12548
- version: lson.version
12549
- };
12550
11920
  } else {
12551
11921
  return lson;
12552
11922
  }
@@ -12728,7 +12098,6 @@ export {
12728
12098
  LiveList,
12729
12099
  LiveMap,
12730
12100
  LiveObject,
12731
- LiveText,
12732
12101
  LiveblocksError,
12733
12102
  MENTION_CHARACTER,
12734
12103
  MutableSignal,
@@ -12740,7 +12109,6 @@ export {
12740
12109
  SortedList,
12741
12110
  TextEditorType,
12742
12111
  WebsocketCloseCodes,
12743
- applyLiveTextOperations,
12744
12112
  asPos,
12745
12113
  assert,
12746
12114
  assertNever,
@@ -12796,7 +12164,6 @@ export {
12796
12164
  isRegisterStorageNode,
12797
12165
  isRootStorageNode,
12798
12166
  isStartsWithOperator,
12799
- isTextStorageNode,
12800
12167
  isUrl,
12801
12168
  kInternal,
12802
12169
  keys,