@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.cjs +163 -796
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +17 -129
- package/dist/index.d.ts +17 -129
- package/dist/index.js +73 -706
- 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.
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
3022
|
-
*
|
|
3023
|
-
*
|
|
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
|
|
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
|
|
3143
|
-
if (
|
|
3144
|
-
return
|
|
3145
|
-
}
|
|
3146
|
-
|
|
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 ===
|
|
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.
|
|
3377
|
-
(e) => log2("Ignored event", e.type, e, "(
|
|
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) =>
|
|
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", {
|
|
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
|
|
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 =
|
|
6126
|
+
const childOps = addIntentToOpTree(
|
|
6123
6127
|
item._toOps(this._id, parentKey2),
|
|
6124
|
-
|
|
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 =
|
|
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.
|
|
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
|
-
|
|
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 =
|
|
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 =
|
|
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
|
|
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)
|
|
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
|
|
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
|
|
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
|
|
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,
|