@liveblocks/core 3.19.3 → 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.cjs +762 -121
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +122 -10
- package/dist/index.d.ts +122 -10
- package/dist/index.js +692 -51
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
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.
|
|
9
|
+
var PKG_VERSION = "3.19.4-test1";
|
|
10
10
|
var PKG_FORMAT = "esm";
|
|
11
11
|
|
|
12
12
|
// src/dupe-detection.ts
|
|
@@ -2715,7 +2715,6 @@ var HttpClient = class {
|
|
|
2715
2715
|
};
|
|
2716
2716
|
|
|
2717
2717
|
// src/lib/fsm.ts
|
|
2718
|
-
var IGNORE = /* @__PURE__ */ Symbol("fsm.ignore");
|
|
2719
2718
|
function distance(state1, state2) {
|
|
2720
2719
|
if (state1 === state2) {
|
|
2721
2720
|
return [0, 0];
|
|
@@ -2888,7 +2887,7 @@ var FSM = class {
|
|
|
2888
2887
|
this.#eventHub = {
|
|
2889
2888
|
didReceiveEvent: makeEventSource(),
|
|
2890
2889
|
willTransition: makeEventSource(),
|
|
2891
|
-
|
|
2890
|
+
didIgnoreEvent: makeEventSource(),
|
|
2892
2891
|
willExitState: makeEventSource(),
|
|
2893
2892
|
didEnterState: makeEventSource(),
|
|
2894
2893
|
didExitState: makeEventSource()
|
|
@@ -2896,7 +2895,7 @@ var FSM = class {
|
|
|
2896
2895
|
this.events = {
|
|
2897
2896
|
didReceiveEvent: this.#eventHub.didReceiveEvent.observable,
|
|
2898
2897
|
willTransition: this.#eventHub.willTransition.observable,
|
|
2899
|
-
|
|
2898
|
+
didIgnoreEvent: this.#eventHub.didIgnoreEvent.observable,
|
|
2900
2899
|
willExitState: this.#eventHub.willExitState.observable,
|
|
2901
2900
|
didEnterState: this.#eventHub.didEnterState.observable,
|
|
2902
2901
|
didExitState: this.#eventHub.didExitState.observable
|
|
@@ -3019,14 +3018,9 @@ var FSM = class {
|
|
|
3019
3018
|
* `context` params to conditionally decide which next state to transition
|
|
3020
3019
|
* to.
|
|
3021
3020
|
*
|
|
3022
|
-
* If you
|
|
3023
|
-
*
|
|
3024
|
-
*
|
|
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`.
|
|
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.
|
|
3030
3024
|
*/
|
|
3031
3025
|
addTransitions(nameOrPattern, mapping) {
|
|
3032
3026
|
if (this.#runningState !== 0 /* NOT_STARTED_YET */) {
|
|
@@ -3046,12 +3040,7 @@ var FSM = class {
|
|
|
3046
3040
|
}
|
|
3047
3041
|
const target = target_;
|
|
3048
3042
|
this.#knownEventTypes.add(type);
|
|
3049
|
-
if (target
|
|
3050
|
-
continue;
|
|
3051
|
-
}
|
|
3052
|
-
if (target === IGNORE) {
|
|
3053
|
-
map.set(type, IGNORE);
|
|
3054
|
-
} else {
|
|
3043
|
+
if (target !== void 0) {
|
|
3055
3044
|
const targetFn = typeof target === "function" ? target : () => target;
|
|
3056
3045
|
map.set(type, targetFn);
|
|
3057
3046
|
}
|
|
@@ -3150,14 +3139,12 @@ var FSM = class {
|
|
|
3150
3139
|
if (this.#runningState === 2 /* STOPPED */) {
|
|
3151
3140
|
return;
|
|
3152
3141
|
}
|
|
3153
|
-
const
|
|
3154
|
-
if (
|
|
3155
|
-
return;
|
|
3156
|
-
}
|
|
3157
|
-
|
|
3158
|
-
return this.#transition(event, entry);
|
|
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);
|
|
3159
3147
|
}
|
|
3160
|
-
this.#eventHub.didIgnoreUnexpectedEvent.notify(event);
|
|
3161
3148
|
}
|
|
3162
3149
|
#transition(event, target) {
|
|
3163
3150
|
this.#eventHub.didReceiveEvent.notify(event);
|
|
@@ -3166,7 +3153,8 @@ var FSM = class {
|
|
|
3166
3153
|
const nextTarget = targetFn(event, this.#currentContext.current);
|
|
3167
3154
|
let nextState;
|
|
3168
3155
|
let effects = void 0;
|
|
3169
|
-
if (nextTarget ===
|
|
3156
|
+
if (nextTarget === null) {
|
|
3157
|
+
this.#eventHub.didIgnoreEvent.notify(event);
|
|
3170
3158
|
return;
|
|
3171
3159
|
}
|
|
3172
3160
|
if (typeof nextTarget === "string") {
|
|
@@ -3385,8 +3373,8 @@ function enableTracing(machine) {
|
|
|
3385
3373
|
machine.events.didExitState.subscribe(
|
|
3386
3374
|
({ state, durationMs }) => log2(`Exited ${state} after ${durationMs.toFixed(0)}ms`)
|
|
3387
3375
|
),
|
|
3388
|
-
machine.events.
|
|
3389
|
-
(e) => log2("Ignored
|
|
3376
|
+
machine.events.didIgnoreEvent.subscribe(
|
|
3377
|
+
(e) => log2("Ignored event", e.type, e, "(current state won't handle it)")
|
|
3390
3378
|
)
|
|
3391
3379
|
];
|
|
3392
3380
|
return () => {
|
|
@@ -3498,12 +3486,7 @@ function createConnectionStateMachine(delegates, options) {
|
|
|
3498
3486
|
);
|
|
3499
3487
|
const onSocketError = (event) => machine.send({ type: "EXPLICIT_SOCKET_ERROR", event });
|
|
3500
3488
|
const onSocketClose = (event) => machine.send({ type: "EXPLICIT_SOCKET_CLOSE", event });
|
|
3501
|
-
const onSocketMessage = (event) => {
|
|
3502
|
-
machine.send({ type: "ALIVE" });
|
|
3503
|
-
if (event.data !== "pong") {
|
|
3504
|
-
onMessage.notify(event);
|
|
3505
|
-
}
|
|
3506
|
-
};
|
|
3489
|
+
const onSocketMessage = (event) => event.data === "pong" ? machine.send({ type: "PONG" }) : onMessage.notify(event);
|
|
3507
3490
|
function teardownSocket(socket) {
|
|
3508
3491
|
if (socket) {
|
|
3509
3492
|
socket.removeEventListener("error", onSocketError);
|
|
@@ -3673,13 +3656,7 @@ function createConnectionStateMachine(delegates, options) {
|
|
|
3673
3656
|
effect: [increaseBackoffDelay, logPrematureErrorOrCloseEvent(err)]
|
|
3674
3657
|
};
|
|
3675
3658
|
}
|
|
3676
|
-
)
|
|
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
|
-
});
|
|
3659
|
+
);
|
|
3683
3660
|
const sendHeartbeat = {
|
|
3684
3661
|
target: "@ok.awaiting-pong",
|
|
3685
3662
|
effect: (ctx) => {
|
|
@@ -3694,8 +3671,7 @@ function createConnectionStateMachine(delegates, options) {
|
|
|
3694
3671
|
machine.addTimedTransition("@ok.connected", HEARTBEAT_INTERVAL, maybeHeartbeat).addTransitions("@ok.connected", {
|
|
3695
3672
|
NAVIGATOR_OFFLINE: maybeHeartbeat,
|
|
3696
3673
|
// Don't take the browser's word for it when it says it's offline. Do a ping/pong to make sure.
|
|
3697
|
-
WINDOW_GOT_FOCUS: sendHeartbeat
|
|
3698
|
-
ALIVE: IGNORE
|
|
3674
|
+
WINDOW_GOT_FOCUS: sendHeartbeat
|
|
3699
3675
|
});
|
|
3700
3676
|
machine.addTransitions("@idle.zombie", {
|
|
3701
3677
|
WINDOW_GOT_FOCUS: "@connecting.backoff"
|
|
@@ -3716,7 +3692,7 @@ function createConnectionStateMachine(delegates, options) {
|
|
|
3716
3692
|
clearTimeout(timerID);
|
|
3717
3693
|
onMessage.pause();
|
|
3718
3694
|
};
|
|
3719
|
-
}).addTransitions("@ok.awaiting-pong", {
|
|
3695
|
+
}).addTransitions("@ok.awaiting-pong", { PONG: "@ok.connected" }).addTimedTransition("@ok.awaiting-pong", PONG_TIMEOUT, {
|
|
3720
3696
|
target: "@connecting.busy",
|
|
3721
3697
|
// Log implicit connection loss and drop the current open socket
|
|
3722
3698
|
effect: log(
|
|
@@ -3729,7 +3705,7 @@ function createConnectionStateMachine(delegates, options) {
|
|
|
3729
3705
|
// not. When still OPEN, don't transition.
|
|
3730
3706
|
EXPLICIT_SOCKET_ERROR: (_, context) => {
|
|
3731
3707
|
if (context.socket?.readyState === 1) {
|
|
3732
|
-
return
|
|
3708
|
+
return null;
|
|
3733
3709
|
}
|
|
3734
3710
|
return {
|
|
3735
3711
|
target: "@connecting.backoff",
|
|
@@ -5497,7 +5473,9 @@ var OpCode = Object.freeze({
|
|
|
5497
5473
|
DELETE_CRDT: 5,
|
|
5498
5474
|
DELETE_OBJECT_KEY: 6,
|
|
5499
5475
|
CREATE_MAP: 7,
|
|
5500
|
-
CREATE_REGISTER: 8
|
|
5476
|
+
CREATE_REGISTER: 8,
|
|
5477
|
+
CREATE_TEXT: 9,
|
|
5478
|
+
UPDATE_TEXT: 10
|
|
5501
5479
|
});
|
|
5502
5480
|
function isIgnoredOp(op) {
|
|
5503
5481
|
return op.type === OpCode.DELETE_CRDT && op.id === "ACK";
|
|
@@ -5508,7 +5486,8 @@ var CrdtType = Object.freeze({
|
|
|
5508
5486
|
OBJECT: 0,
|
|
5509
5487
|
LIST: 1,
|
|
5510
5488
|
MAP: 2,
|
|
5511
|
-
REGISTER: 3
|
|
5489
|
+
REGISTER: 3,
|
|
5490
|
+
TEXT: 4
|
|
5512
5491
|
});
|
|
5513
5492
|
function isRootStorageNode(node) {
|
|
5514
5493
|
return node[0] === "root";
|
|
@@ -5525,6 +5504,9 @@ function isMapStorageNode(node) {
|
|
|
5525
5504
|
function isRegisterStorageNode(node) {
|
|
5526
5505
|
return node[1].type === CrdtType.REGISTER;
|
|
5527
5506
|
}
|
|
5507
|
+
function isTextStorageNode(node) {
|
|
5508
|
+
return node[1].type === CrdtType.TEXT;
|
|
5509
|
+
}
|
|
5528
5510
|
function isCompactRootNode(node) {
|
|
5529
5511
|
return node[0] === "root";
|
|
5530
5512
|
}
|
|
@@ -5547,6 +5529,9 @@ function* compactNodesToNodeStream(compactNodes) {
|
|
|
5547
5529
|
case CrdtType.REGISTER:
|
|
5548
5530
|
yield [cnode[0], { type: CrdtType.REGISTER, parentId: cnode[2], parentKey: cnode[3], data: cnode[4] }];
|
|
5549
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;
|
|
5550
5535
|
default:
|
|
5551
5536
|
}
|
|
5552
5537
|
}
|
|
@@ -5575,6 +5560,17 @@ function* nodeStreamToCompactNodes(nodes) {
|
|
|
5575
5560
|
const id = node[0];
|
|
5576
5561
|
const crdt = node[1];
|
|
5577
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
|
+
];
|
|
5578
5574
|
} else {
|
|
5579
5575
|
}
|
|
5580
5576
|
}
|
|
@@ -8231,6 +8227,581 @@ var LiveObject = class _LiveObject extends AbstractCrdt {
|
|
|
8231
8227
|
}
|
|
8232
8228
|
};
|
|
8233
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
|
+
|
|
8234
8805
|
// src/crdts/liveblocks-helpers.ts
|
|
8235
8806
|
function creationOpToLiveNode(op) {
|
|
8236
8807
|
return lsonToLiveNode(creationOpToLson(op));
|
|
@@ -8245,6 +8816,8 @@ function creationOpToLson(op) {
|
|
|
8245
8816
|
return new LiveMap();
|
|
8246
8817
|
case OpCode.CREATE_LIST:
|
|
8247
8818
|
return new LiveList([]);
|
|
8819
|
+
case OpCode.CREATE_TEXT:
|
|
8820
|
+
return new LiveText(op.data, op.version);
|
|
8248
8821
|
default:
|
|
8249
8822
|
return assertNever(op, "Unknown creation Op");
|
|
8250
8823
|
}
|
|
@@ -8267,6 +8840,8 @@ function deserialize(node, parentToChildren, pool) {
|
|
|
8267
8840
|
return LiveMap._deserialize(node, parentToChildren, pool);
|
|
8268
8841
|
} else if (isRegisterStorageNode(node)) {
|
|
8269
8842
|
return LiveRegister._deserialize(node, parentToChildren, pool);
|
|
8843
|
+
} else if (isTextStorageNode(node)) {
|
|
8844
|
+
return LiveText._deserialize(node, parentToChildren, pool);
|
|
8270
8845
|
} else {
|
|
8271
8846
|
throw new Error("Unexpected CRDT type");
|
|
8272
8847
|
}
|
|
@@ -8280,12 +8855,14 @@ function deserializeToLson(node, parentToChildren, pool) {
|
|
|
8280
8855
|
return LiveMap._deserialize(node, parentToChildren, pool);
|
|
8281
8856
|
} else if (isRegisterStorageNode(node)) {
|
|
8282
8857
|
return node[1].data;
|
|
8858
|
+
} else if (isTextStorageNode(node)) {
|
|
8859
|
+
return LiveText._deserialize(node, parentToChildren, pool);
|
|
8283
8860
|
} else {
|
|
8284
8861
|
throw new Error("Unexpected CRDT type");
|
|
8285
8862
|
}
|
|
8286
8863
|
}
|
|
8287
8864
|
function isLiveStructure(value) {
|
|
8288
|
-
return isLiveList(value) || isLiveMap(value) || isLiveObject(value);
|
|
8865
|
+
return isLiveList(value) || isLiveMap(value) || isLiveObject(value) || isLiveText(value);
|
|
8289
8866
|
}
|
|
8290
8867
|
function isLiveNode(value) {
|
|
8291
8868
|
return isLiveStructure(value) || isLiveRegister(value);
|
|
@@ -8299,6 +8876,9 @@ function isLiveMap(value) {
|
|
|
8299
8876
|
function isLiveObject(value) {
|
|
8300
8877
|
return value instanceof LiveObject;
|
|
8301
8878
|
}
|
|
8879
|
+
function isLiveText(value) {
|
|
8880
|
+
return value instanceof LiveText;
|
|
8881
|
+
}
|
|
8302
8882
|
function isLiveRegister(value) {
|
|
8303
8883
|
return value instanceof LiveRegister;
|
|
8304
8884
|
}
|
|
@@ -8308,14 +8888,14 @@ function cloneLson(value) {
|
|
|
8308
8888
|
function liveNodeToLson(obj) {
|
|
8309
8889
|
if (obj instanceof LiveRegister) {
|
|
8310
8890
|
return obj.data;
|
|
8311
|
-
} 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) {
|
|
8312
8892
|
return obj;
|
|
8313
8893
|
} else {
|
|
8314
8894
|
return assertNever(obj, "Unknown AbstractCrdt");
|
|
8315
8895
|
}
|
|
8316
8896
|
}
|
|
8317
8897
|
function lsonToLiveNode(value) {
|
|
8318
|
-
if (value instanceof LiveObject || value instanceof LiveMap || value instanceof LiveList) {
|
|
8898
|
+
if (value instanceof LiveObject || value instanceof LiveMap || value instanceof LiveList || value instanceof LiveText) {
|
|
8319
8899
|
return value;
|
|
8320
8900
|
} else {
|
|
8321
8901
|
return new LiveRegister(value);
|
|
@@ -8340,6 +8920,38 @@ function getTreesDiffOperations(currentItems, newItems) {
|
|
|
8340
8920
|
});
|
|
8341
8921
|
}
|
|
8342
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
|
+
}
|
|
8343
8955
|
if (crdt.parentKey !== currentCrdt.parentKey) {
|
|
8344
8956
|
ops.push({
|
|
8345
8957
|
type: OpCode.SET_PARENT_KEY,
|
|
@@ -8388,6 +9000,16 @@ function getTreesDiffOperations(currentItems, newItems) {
|
|
|
8388
9000
|
parentKey: crdt.parentKey
|
|
8389
9001
|
});
|
|
8390
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;
|
|
8391
9013
|
}
|
|
8392
9014
|
}
|
|
8393
9015
|
});
|
|
@@ -8420,6 +9042,12 @@ function mergeListStorageUpdates(first, second) {
|
|
|
8420
9042
|
updates: updates.concat(second.updates)
|
|
8421
9043
|
};
|
|
8422
9044
|
}
|
|
9045
|
+
function mergeTextStorageUpdates(first, second) {
|
|
9046
|
+
return {
|
|
9047
|
+
...second,
|
|
9048
|
+
updates: first.updates.concat(second.updates)
|
|
9049
|
+
};
|
|
9050
|
+
}
|
|
8423
9051
|
function mergeStorageUpdates(first, second) {
|
|
8424
9052
|
if (first === void 0) {
|
|
8425
9053
|
return second;
|
|
@@ -8430,6 +9058,8 @@ function mergeStorageUpdates(first, second) {
|
|
|
8430
9058
|
return mergeMapStorageUpdates(first, second);
|
|
8431
9059
|
} else if (first.type === "LiveList" && second.type === "LiveList") {
|
|
8432
9060
|
return mergeListStorageUpdates(first, second);
|
|
9061
|
+
} else if (first.type === "LiveText" && second.type === "LiveText") {
|
|
9062
|
+
return mergeTextStorageUpdates(first, second);
|
|
8433
9063
|
} else {
|
|
8434
9064
|
}
|
|
8435
9065
|
return second;
|
|
@@ -9711,7 +10341,7 @@ function createRoom(options, config) {
|
|
|
9711
10341
|
);
|
|
9712
10342
|
output.reverse.pushLeft(applyOpResult.reverse);
|
|
9713
10343
|
}
|
|
9714
|
-
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) {
|
|
9715
10345
|
createdNodeIds.add(op.id);
|
|
9716
10346
|
}
|
|
9717
10347
|
}
|
|
@@ -9731,6 +10361,7 @@ function createRoom(options, config) {
|
|
|
9731
10361
|
switch (op.type) {
|
|
9732
10362
|
case OpCode.DELETE_OBJECT_KEY:
|
|
9733
10363
|
case OpCode.UPDATE_OBJECT:
|
|
10364
|
+
case OpCode.UPDATE_TEXT:
|
|
9734
10365
|
case OpCode.DELETE_CRDT: {
|
|
9735
10366
|
const node = context.pool.nodes.get(op.id);
|
|
9736
10367
|
if (node === void 0) {
|
|
@@ -9755,6 +10386,7 @@ function createRoom(options, config) {
|
|
|
9755
10386
|
case OpCode.CREATE_OBJECT:
|
|
9756
10387
|
case OpCode.CREATE_LIST:
|
|
9757
10388
|
case OpCode.CREATE_MAP:
|
|
10389
|
+
case OpCode.CREATE_TEXT:
|
|
9758
10390
|
case OpCode.CREATE_REGISTER: {
|
|
9759
10391
|
if (op.parentId === void 0) {
|
|
9760
10392
|
return { modified: false };
|
|
@@ -11909,6 +12541,12 @@ function toPlainLson(lson) {
|
|
|
11909
12541
|
liveblocksType: "LiveList",
|
|
11910
12542
|
data: [...lson].map((item) => toPlainLson(item))
|
|
11911
12543
|
};
|
|
12544
|
+
} else if (lson instanceof LiveText) {
|
|
12545
|
+
return {
|
|
12546
|
+
liveblocksType: "LiveText",
|
|
12547
|
+
data: lson.toDelta(),
|
|
12548
|
+
version: lson.version
|
|
12549
|
+
};
|
|
11912
12550
|
} else {
|
|
11913
12551
|
return lson;
|
|
11914
12552
|
}
|
|
@@ -12090,6 +12728,7 @@ export {
|
|
|
12090
12728
|
LiveList,
|
|
12091
12729
|
LiveMap,
|
|
12092
12730
|
LiveObject,
|
|
12731
|
+
LiveText,
|
|
12093
12732
|
LiveblocksError,
|
|
12094
12733
|
MENTION_CHARACTER,
|
|
12095
12734
|
MutableSignal,
|
|
@@ -12101,6 +12740,7 @@ export {
|
|
|
12101
12740
|
SortedList,
|
|
12102
12741
|
TextEditorType,
|
|
12103
12742
|
WebsocketCloseCodes,
|
|
12743
|
+
applyLiveTextOperations,
|
|
12104
12744
|
asPos,
|
|
12105
12745
|
assert,
|
|
12106
12746
|
assertNever,
|
|
@@ -12156,6 +12796,7 @@ export {
|
|
|
12156
12796
|
isRegisterStorageNode,
|
|
12157
12797
|
isRootStorageNode,
|
|
12158
12798
|
isStartsWithOperator,
|
|
12799
|
+
isTextStorageNode,
|
|
12159
12800
|
isUrl,
|
|
12160
12801
|
kInternal,
|
|
12161
12802
|
keys,
|