@liveblocks/client 0.13.2 → 0.14.0-beta.1
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/lib/cjs/AbstractCrdt.d.ts +8 -4
- package/lib/cjs/AbstractCrdt.js +2 -2
- package/lib/cjs/LiveList.d.ts +8 -4
- package/lib/cjs/LiveList.js +36 -9
- package/lib/cjs/LiveMap.d.ts +7 -3
- package/lib/cjs/LiveMap.js +23 -5
- package/lib/cjs/LiveObject.d.ts +17 -7
- package/lib/cjs/LiveObject.js +45 -15
- package/lib/cjs/LiveRegister.d.ts +8 -4
- package/lib/cjs/LiveRegister.js +17 -4
- package/lib/cjs/live.d.ts +7 -0
- package/lib/cjs/room.d.ts +8 -0
- package/lib/cjs/room.js +97 -22
- package/lib/cjs/utils.d.ts +2 -1
- package/lib/cjs/utils.js +76 -1
- package/lib/esm/AbstractCrdt.d.ts +8 -4
- package/lib/esm/AbstractCrdt.js +2 -2
- package/lib/esm/LiveList.d.ts +8 -4
- package/lib/esm/LiveList.js +37 -10
- package/lib/esm/LiveMap.d.ts +7 -3
- package/lib/esm/LiveMap.js +23 -5
- package/lib/esm/LiveObject.d.ts +17 -7
- package/lib/esm/LiveObject.js +45 -15
- package/lib/esm/LiveRegister.d.ts +8 -4
- package/lib/esm/LiveRegister.js +18 -5
- package/lib/esm/live.d.ts +7 -0
- package/lib/esm/room.d.ts +8 -0
- package/lib/esm/room.js +98 -23
- package/lib/esm/utils.d.ts +2 -1
- package/lib/esm/utils.js +75 -1
- package/package.json +2 -2
- package/lib/cjs/immutable/index.d.ts +0 -7
- package/lib/cjs/immutable/index.js +0 -214
- package/lib/esm/immutable/index.d.ts +0 -7
- package/lib/esm/immutable/index.js +0 -207
package/lib/cjs/room.js
CHANGED
|
@@ -129,18 +129,23 @@ function makeStateMachine(state, context, mockedEffects) {
|
|
|
129
129
|
};
|
|
130
130
|
return genericSubscribe(cb);
|
|
131
131
|
}
|
|
132
|
-
function
|
|
133
|
-
|
|
132
|
+
function createOrUpdateRootFromMessage(message) {
|
|
133
|
+
if (message.items.length === 0) {
|
|
134
|
+
throw new Error("Internal error: cannot load storage without items");
|
|
135
|
+
}
|
|
136
|
+
if (state.root) {
|
|
137
|
+
updateRoot(message.items);
|
|
138
|
+
}
|
|
139
|
+
else {
|
|
140
|
+
state.root = load(message.items);
|
|
141
|
+
}
|
|
134
142
|
for (const key in state.defaultStorageRoot) {
|
|
135
143
|
if (state.root.get(key) == null) {
|
|
136
144
|
state.root.set(key, state.defaultStorageRoot[key]);
|
|
137
145
|
}
|
|
138
146
|
}
|
|
139
147
|
}
|
|
140
|
-
function
|
|
141
|
-
if (items.length === 0) {
|
|
142
|
-
throw new Error("Internal error: cannot load storage without items");
|
|
143
|
-
}
|
|
148
|
+
function buildRootAndParentToChildren(items) {
|
|
144
149
|
const parentToChildren = new Map();
|
|
145
150
|
let root = null;
|
|
146
151
|
for (const tuple of items) {
|
|
@@ -161,6 +166,23 @@ function makeStateMachine(state, context, mockedEffects) {
|
|
|
161
166
|
if (root == null) {
|
|
162
167
|
throw new Error("Root can't be null");
|
|
163
168
|
}
|
|
169
|
+
return [root, parentToChildren];
|
|
170
|
+
}
|
|
171
|
+
function updateRoot(items) {
|
|
172
|
+
if (!state.root) {
|
|
173
|
+
return;
|
|
174
|
+
}
|
|
175
|
+
const currentItems = new Map();
|
|
176
|
+
state.items.forEach((liveCrdt, id) => {
|
|
177
|
+
currentItems.set(id, liveCrdt._toSerializedCrdt());
|
|
178
|
+
});
|
|
179
|
+
// Get operations that represent the diff between 2 states.
|
|
180
|
+
const ops = (0, utils_1.getTreesDiffOperations)(currentItems, new Map(items));
|
|
181
|
+
const result = apply(ops, false);
|
|
182
|
+
notify(result.updates);
|
|
183
|
+
}
|
|
184
|
+
function load(items) {
|
|
185
|
+
const [root, parentToChildren] = buildRootAndParentToChildren(items);
|
|
164
186
|
return LiveObject_1.LiveObject._deserialize(root, parentToChildren, {
|
|
165
187
|
addItem,
|
|
166
188
|
deleteItem,
|
|
@@ -249,7 +271,10 @@ function makeStateMachine(state, context, mockedEffects) {
|
|
|
249
271
|
state.connection.state === "connecting") {
|
|
250
272
|
return state.connection.id;
|
|
251
273
|
}
|
|
252
|
-
|
|
274
|
+
else if (state.lastConnectionId !== null) {
|
|
275
|
+
return state.lastConnectionId;
|
|
276
|
+
}
|
|
277
|
+
throw new Error("Internal. Tried to get connection id but connection was never open");
|
|
253
278
|
}
|
|
254
279
|
function generateId() {
|
|
255
280
|
return `${getConnectionId()}:${state.clock++}`;
|
|
@@ -257,7 +282,7 @@ function makeStateMachine(state, context, mockedEffects) {
|
|
|
257
282
|
function generateOpId() {
|
|
258
283
|
return `${getConnectionId()}:${state.opClock++}`;
|
|
259
284
|
}
|
|
260
|
-
function apply(item) {
|
|
285
|
+
function apply(item, isLocal) {
|
|
261
286
|
const result = {
|
|
262
287
|
reverse: [],
|
|
263
288
|
updates: { nodes: new Set(), presence: false },
|
|
@@ -284,7 +309,11 @@ function makeStateMachine(state, context, mockedEffects) {
|
|
|
284
309
|
result.updates.presence = true;
|
|
285
310
|
}
|
|
286
311
|
else {
|
|
287
|
-
|
|
312
|
+
// Ops applied after undo/redo don't have an opId.
|
|
313
|
+
if (isLocal && !op.opId) {
|
|
314
|
+
op.opId = generateOpId();
|
|
315
|
+
}
|
|
316
|
+
const applyOpResult = applyOp(op, isLocal);
|
|
288
317
|
if (applyOpResult.modified) {
|
|
289
318
|
result.updates.nodes.add(applyOpResult.modified);
|
|
290
319
|
result.reverse.unshift(...applyOpResult.reverse);
|
|
@@ -293,7 +322,10 @@ function makeStateMachine(state, context, mockedEffects) {
|
|
|
293
322
|
}
|
|
294
323
|
return result;
|
|
295
324
|
}
|
|
296
|
-
function applyOp(op) {
|
|
325
|
+
function applyOp(op, isLocal) {
|
|
326
|
+
if (op.opId) {
|
|
327
|
+
state.offlineOperations.delete(op.opId);
|
|
328
|
+
}
|
|
297
329
|
switch (op.type) {
|
|
298
330
|
case live_1.OpType.DeleteObjectKey:
|
|
299
331
|
case live_1.OpType.UpdateObject:
|
|
@@ -302,7 +334,7 @@ function makeStateMachine(state, context, mockedEffects) {
|
|
|
302
334
|
if (item == null) {
|
|
303
335
|
return { modified: false };
|
|
304
336
|
}
|
|
305
|
-
return item._apply(op);
|
|
337
|
+
return item._apply(op, isLocal);
|
|
306
338
|
}
|
|
307
339
|
case live_1.OpType.SetParentKey: {
|
|
308
340
|
const item = state.items.get(op.id);
|
|
@@ -330,28 +362,28 @@ function makeStateMachine(state, context, mockedEffects) {
|
|
|
330
362
|
if (parent == null || getItem(op.id) != null) {
|
|
331
363
|
return { modified: false };
|
|
332
364
|
}
|
|
333
|
-
return parent._attachChild(op.id, op.parentKey, new LiveObject_1.LiveObject(op.data));
|
|
365
|
+
return parent._attachChild(op.id, op.parentKey, new LiveObject_1.LiveObject(op.data), isLocal);
|
|
334
366
|
}
|
|
335
367
|
case live_1.OpType.CreateList: {
|
|
336
368
|
const parent = state.items.get(op.parentId);
|
|
337
369
|
if (parent == null || getItem(op.id) != null) {
|
|
338
370
|
return { modified: false };
|
|
339
371
|
}
|
|
340
|
-
return parent._attachChild(op.id, op.parentKey, new LiveList_1.LiveList());
|
|
372
|
+
return parent._attachChild(op.id, op.parentKey, new LiveList_1.LiveList(), isLocal);
|
|
341
373
|
}
|
|
342
374
|
case live_1.OpType.CreateRegister: {
|
|
343
375
|
const parent = state.items.get(op.parentId);
|
|
344
376
|
if (parent == null || getItem(op.id) != null) {
|
|
345
377
|
return { modified: false };
|
|
346
378
|
}
|
|
347
|
-
return parent._attachChild(op.id, op.parentKey, new LiveRegister_1.LiveRegister(op.data));
|
|
379
|
+
return parent._attachChild(op.id, op.parentKey, new LiveRegister_1.LiveRegister(op.data), isLocal);
|
|
348
380
|
}
|
|
349
381
|
case live_1.OpType.CreateMap: {
|
|
350
382
|
const parent = state.items.get(op.parentId);
|
|
351
383
|
if (parent == null || getItem(op.id) != null) {
|
|
352
384
|
return { modified: false };
|
|
353
385
|
}
|
|
354
|
-
return parent._attachChild(op.id, op.parentKey, new LiveMap_1.LiveMap());
|
|
386
|
+
return parent._attachChild(op.id, op.parentKey, new LiveMap_1.LiveMap(), isLocal);
|
|
355
387
|
}
|
|
356
388
|
}
|
|
357
389
|
return { modified: false };
|
|
@@ -571,12 +603,13 @@ See v0.13 release notes for more information.
|
|
|
571
603
|
break;
|
|
572
604
|
}
|
|
573
605
|
case live_1.ServerMessageType.InitialStorageState: {
|
|
574
|
-
|
|
606
|
+
createOrUpdateRootFromMessage(subMessage);
|
|
607
|
+
applyAndSendOfflineOps();
|
|
575
608
|
_getInitialStateResolver === null || _getInitialStateResolver === void 0 ? void 0 : _getInitialStateResolver();
|
|
576
609
|
break;
|
|
577
610
|
}
|
|
578
611
|
case live_1.ServerMessageType.UpdateStorage: {
|
|
579
|
-
const applyResult = apply(subMessage.ops);
|
|
612
|
+
const applyResult = apply(subMessage.ops, false);
|
|
580
613
|
for (const node of applyResult.updates.nodes) {
|
|
581
614
|
updates.nodes.add(node);
|
|
582
615
|
}
|
|
@@ -640,6 +673,10 @@ See v0.13 release notes for more information.
|
|
|
640
673
|
if (state.connection.state === "connecting") {
|
|
641
674
|
updateConnection(Object.assign(Object.assign({}, state.connection), { state: "open" }));
|
|
642
675
|
state.numberOfRetry = 0;
|
|
676
|
+
state.lastConnectionId = state.connection.id;
|
|
677
|
+
if (state.root) {
|
|
678
|
+
state.buffer.messages.push({ type: live_1.ClientMessageType.FetchStorage });
|
|
679
|
+
}
|
|
643
680
|
tryFlushing();
|
|
644
681
|
}
|
|
645
682
|
else {
|
|
@@ -679,11 +716,29 @@ See v0.13 release notes for more information.
|
|
|
679
716
|
clearInterval(state.intervalHandles.heartbeat);
|
|
680
717
|
connect();
|
|
681
718
|
}
|
|
682
|
-
function
|
|
683
|
-
if (state.
|
|
719
|
+
function applyAndSendOfflineOps() {
|
|
720
|
+
if (state.offlineOperations.size === 0) {
|
|
684
721
|
return;
|
|
685
722
|
}
|
|
686
|
-
|
|
723
|
+
const messages = [];
|
|
724
|
+
const ops = Array.from(state.offlineOperations.values());
|
|
725
|
+
const result = apply(ops, true);
|
|
726
|
+
messages.push({
|
|
727
|
+
type: live_1.ClientMessageType.UpdateStorage,
|
|
728
|
+
ops: ops,
|
|
729
|
+
});
|
|
730
|
+
notify(result.updates);
|
|
731
|
+
effects.send(messages);
|
|
732
|
+
}
|
|
733
|
+
function tryFlushing() {
|
|
734
|
+
const storageOps = state.buffer.storageOperations;
|
|
735
|
+
if (storageOps.length > 0) {
|
|
736
|
+
storageOps.forEach((op) => {
|
|
737
|
+
state.offlineOperations.set(op.opId, op);
|
|
738
|
+
});
|
|
739
|
+
}
|
|
740
|
+
if (state.socket == null || state.socket.readyState !== WebSocket.OPEN) {
|
|
741
|
+
state.buffer.storageOperations = [];
|
|
687
742
|
return;
|
|
688
743
|
}
|
|
689
744
|
const now = Date.now();
|
|
@@ -801,7 +856,7 @@ See v0.13 release notes for more information.
|
|
|
801
856
|
return;
|
|
802
857
|
}
|
|
803
858
|
state.isHistoryPaused = false;
|
|
804
|
-
const result = apply(historyItem);
|
|
859
|
+
const result = apply(historyItem, true);
|
|
805
860
|
notify(result.updates);
|
|
806
861
|
state.redoStack.push(result.reverse);
|
|
807
862
|
for (const op of historyItem) {
|
|
@@ -820,7 +875,7 @@ See v0.13 release notes for more information.
|
|
|
820
875
|
return;
|
|
821
876
|
}
|
|
822
877
|
state.isHistoryPaused = false;
|
|
823
|
-
const result = apply(historyItem);
|
|
878
|
+
const result = apply(historyItem, true);
|
|
824
879
|
notify(result.updates);
|
|
825
880
|
state.undoStack.push(result.reverse);
|
|
826
881
|
for (const op of historyItem) {
|
|
@@ -872,6 +927,16 @@ See v0.13 release notes for more information.
|
|
|
872
927
|
}
|
|
873
928
|
state.pausedHistory = [];
|
|
874
929
|
}
|
|
930
|
+
function simulateSocketClose() {
|
|
931
|
+
if (state.socket) {
|
|
932
|
+
state.socket.close();
|
|
933
|
+
}
|
|
934
|
+
}
|
|
935
|
+
function simulateSendCloseEvent(event) {
|
|
936
|
+
if (state.socket) {
|
|
937
|
+
onClose(event);
|
|
938
|
+
}
|
|
939
|
+
}
|
|
875
940
|
return {
|
|
876
941
|
// Internal
|
|
877
942
|
onOpen,
|
|
@@ -880,6 +945,9 @@ See v0.13 release notes for more information.
|
|
|
880
945
|
authenticationSuccess,
|
|
881
946
|
heartbeat,
|
|
882
947
|
onNavigatorOnline,
|
|
948
|
+
// Internal dev tools
|
|
949
|
+
simulateSocketClose,
|
|
950
|
+
simulateSendCloseEvent,
|
|
883
951
|
// onWakeUp,
|
|
884
952
|
onVisibilityChange,
|
|
885
953
|
getUndoStack: () => state.undoStack,
|
|
@@ -912,6 +980,7 @@ exports.makeStateMachine = makeStateMachine;
|
|
|
912
980
|
function defaultState(me, defaultStorageRoot) {
|
|
913
981
|
return {
|
|
914
982
|
connection: { state: "closed" },
|
|
983
|
+
lastConnectionId: null,
|
|
915
984
|
socket: null,
|
|
916
985
|
listeners: {
|
|
917
986
|
event: [],
|
|
@@ -956,6 +1025,7 @@ function defaultState(me, defaultStorageRoot) {
|
|
|
956
1025
|
updates: { nodes: new Set(), presence: false, others: [] },
|
|
957
1026
|
reverseOps: [],
|
|
958
1027
|
},
|
|
1028
|
+
offlineOperations: new Map(),
|
|
959
1029
|
};
|
|
960
1030
|
}
|
|
961
1031
|
exports.defaultState = defaultState;
|
|
@@ -1002,6 +1072,11 @@ function createRoom(name, options) {
|
|
|
1002
1072
|
pause: machine.pauseHistory,
|
|
1003
1073
|
resume: machine.resumeHistory,
|
|
1004
1074
|
},
|
|
1075
|
+
// @ts-ignore
|
|
1076
|
+
internalDevTools: {
|
|
1077
|
+
closeWebsocket: machine.simulateSocketClose,
|
|
1078
|
+
sendCloseEvent: machine.simulateSendCloseEvent,
|
|
1079
|
+
},
|
|
1005
1080
|
};
|
|
1006
1081
|
return {
|
|
1007
1082
|
connect: machine.connect,
|
package/lib/cjs/utils.d.ts
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { AbstractCrdt, Doc } from "./AbstractCrdt";
|
|
2
|
-
import { SerializedCrdtWithId } from "./live";
|
|
2
|
+
import { SerializedCrdtWithId, Op, SerializedCrdt } from "./live";
|
|
3
3
|
export declare function remove<T>(array: T[], item: T): void;
|
|
4
4
|
export declare function isSameNodeOrChildOf(node: AbstractCrdt, parent: AbstractCrdt): boolean;
|
|
5
5
|
export declare function deserialize(entry: SerializedCrdtWithId, parentToChildren: Map<string, SerializedCrdtWithId[]>, doc: Doc): AbstractCrdt;
|
|
6
6
|
export declare function isCrdt(obj: any): obj is AbstractCrdt;
|
|
7
7
|
export declare function selfOrRegisterValue(obj: AbstractCrdt): any;
|
|
8
8
|
export declare function selfOrRegister(obj: any): AbstractCrdt;
|
|
9
|
+
export declare function getTreesDiffOperations(currentItems: Map<string, SerializedCrdt>, newItems: Map<string, SerializedCrdt>): Op[];
|
package/lib/cjs/utils.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.selfOrRegister = exports.selfOrRegisterValue = exports.isCrdt = exports.deserialize = exports.isSameNodeOrChildOf = exports.remove = void 0;
|
|
3
|
+
exports.getTreesDiffOperations = exports.selfOrRegister = exports.selfOrRegisterValue = exports.isCrdt = exports.deserialize = exports.isSameNodeOrChildOf = exports.remove = void 0;
|
|
4
4
|
const live_1 = require("./live");
|
|
5
5
|
const LiveList_1 = require("./LiveList");
|
|
6
6
|
const LiveMap_1 = require("./LiveMap");
|
|
@@ -73,3 +73,78 @@ function selfOrRegister(obj) {
|
|
|
73
73
|
}
|
|
74
74
|
}
|
|
75
75
|
exports.selfOrRegister = selfOrRegister;
|
|
76
|
+
function getTreesDiffOperations(currentItems, newItems) {
|
|
77
|
+
const ops = [];
|
|
78
|
+
currentItems.forEach((_, id) => {
|
|
79
|
+
if (!newItems.get(id)) {
|
|
80
|
+
// Delete crdt
|
|
81
|
+
ops.push({
|
|
82
|
+
type: live_1.OpType.DeleteCrdt,
|
|
83
|
+
id: id,
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
});
|
|
87
|
+
newItems.forEach((crdt, id) => {
|
|
88
|
+
const currentCrdt = currentItems.get(id);
|
|
89
|
+
if (currentCrdt) {
|
|
90
|
+
if (crdt.type === live_1.CrdtType.Object) {
|
|
91
|
+
if (JSON.stringify(crdt.data) !==
|
|
92
|
+
JSON.stringify(currentCrdt.data)) {
|
|
93
|
+
ops.push({
|
|
94
|
+
type: live_1.OpType.UpdateObject,
|
|
95
|
+
id: id,
|
|
96
|
+
data: crdt.data,
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
if (crdt.parentKey !== currentCrdt.parentKey) {
|
|
101
|
+
ops.push({
|
|
102
|
+
type: live_1.OpType.SetParentKey,
|
|
103
|
+
id: id,
|
|
104
|
+
parentKey: crdt.parentKey,
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
else {
|
|
109
|
+
// new Crdt
|
|
110
|
+
switch (crdt.type) {
|
|
111
|
+
case live_1.CrdtType.Register:
|
|
112
|
+
ops.push({
|
|
113
|
+
type: live_1.OpType.CreateRegister,
|
|
114
|
+
id: id,
|
|
115
|
+
parentId: crdt.parentId,
|
|
116
|
+
parentKey: crdt.parentKey,
|
|
117
|
+
data: crdt.data,
|
|
118
|
+
});
|
|
119
|
+
break;
|
|
120
|
+
case live_1.CrdtType.List:
|
|
121
|
+
ops.push({
|
|
122
|
+
type: live_1.OpType.CreateList,
|
|
123
|
+
id: id,
|
|
124
|
+
parentId: crdt.parentId,
|
|
125
|
+
parentKey: crdt.parentKey,
|
|
126
|
+
});
|
|
127
|
+
break;
|
|
128
|
+
case live_1.CrdtType.Object:
|
|
129
|
+
ops.push({
|
|
130
|
+
type: live_1.OpType.CreateObject,
|
|
131
|
+
id: id,
|
|
132
|
+
parentId: crdt.parentId,
|
|
133
|
+
parentKey: crdt.parentKey,
|
|
134
|
+
data: crdt.data,
|
|
135
|
+
});
|
|
136
|
+
break;
|
|
137
|
+
case live_1.CrdtType.Map:
|
|
138
|
+
ops.push({
|
|
139
|
+
type: live_1.OpType.CreateMap,
|
|
140
|
+
id: id,
|
|
141
|
+
parentId: crdt.parentId,
|
|
142
|
+
parentKey: crdt.parentKey,
|
|
143
|
+
});
|
|
144
|
+
break;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
});
|
|
148
|
+
return ops;
|
|
149
|
+
}
|
|
150
|
+
exports.getTreesDiffOperations = getTreesDiffOperations;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Op } from "./live";
|
|
1
|
+
import { Op, SerializedCrdt } from "./live";
|
|
2
2
|
export declare type ApplyResult = {
|
|
3
3
|
reverse: Op[];
|
|
4
4
|
modified: AbstractCrdt;
|
|
@@ -33,7 +33,7 @@ export declare abstract class AbstractCrdt {
|
|
|
33
33
|
/**
|
|
34
34
|
* INTERNAL
|
|
35
35
|
*/
|
|
36
|
-
_apply(op: Op): ApplyResult;
|
|
36
|
+
_apply(op: Op, isLocal: boolean): ApplyResult;
|
|
37
37
|
/**
|
|
38
38
|
* INTERNAL
|
|
39
39
|
*/
|
|
@@ -45,7 +45,7 @@ export declare abstract class AbstractCrdt {
|
|
|
45
45
|
/**
|
|
46
46
|
* INTERNAL
|
|
47
47
|
*/
|
|
48
|
-
abstract _attachChild(id: string, key: string, crdt: AbstractCrdt): ApplyResult;
|
|
48
|
+
abstract _attachChild(id: string, key: string, crdt: AbstractCrdt, isLocal: boolean): ApplyResult;
|
|
49
49
|
/**
|
|
50
50
|
* INTERNAL
|
|
51
51
|
*/
|
|
@@ -57,5 +57,9 @@ export declare abstract class AbstractCrdt {
|
|
|
57
57
|
/**
|
|
58
58
|
* INTERNAL
|
|
59
59
|
*/
|
|
60
|
-
abstract _serialize(parentId: string, parentKey: string): Op[];
|
|
60
|
+
abstract _serialize(parentId: string, parentKey: string, doc?: Doc): Op[];
|
|
61
|
+
/**
|
|
62
|
+
* INTERNAL
|
|
63
|
+
*/
|
|
64
|
+
abstract _toSerializedCrdt(): SerializedCrdt;
|
|
61
65
|
}
|
package/lib/esm/AbstractCrdt.js
CHANGED
|
@@ -45,12 +45,12 @@ export class AbstractCrdt {
|
|
|
45
45
|
/**
|
|
46
46
|
* INTERNAL
|
|
47
47
|
*/
|
|
48
|
-
_apply(op) {
|
|
48
|
+
_apply(op, isLocal) {
|
|
49
49
|
switch (op.type) {
|
|
50
50
|
case OpType.DeleteCrdt: {
|
|
51
51
|
if (this._parent != null && this._parentKey != null) {
|
|
52
52
|
const parent = this._parent;
|
|
53
|
-
const reverse = this._serialize(this._parent._id, this._parentKey);
|
|
53
|
+
const reverse = this._serialize(this._parent._id, this._parentKey, __classPrivateFieldGet(this, _AbstractCrdt_doc, "f"));
|
|
54
54
|
this._parent._detachChild(this);
|
|
55
55
|
return { modified: parent, reverse };
|
|
56
56
|
}
|
package/lib/esm/LiveList.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { AbstractCrdt, Doc, ApplyResult } from "./AbstractCrdt";
|
|
2
|
-
import { SerializedList, SerializedCrdtWithId, Op } from "./live";
|
|
2
|
+
import { SerializedList, SerializedCrdtWithId, Op, SerializedCrdt } from "./live";
|
|
3
3
|
/**
|
|
4
4
|
* The LiveList class represents an ordered collection of items that is synchorinized across clients.
|
|
5
5
|
*/
|
|
@@ -13,7 +13,7 @@ export declare class LiveList<T> extends AbstractCrdt {
|
|
|
13
13
|
/**
|
|
14
14
|
* INTERNAL
|
|
15
15
|
*/
|
|
16
|
-
_serialize(parentId?: string, parentKey?: string): Op[];
|
|
16
|
+
_serialize(parentId?: string, parentKey?: string, doc?: Doc): Op[];
|
|
17
17
|
/**
|
|
18
18
|
* INTERNAL
|
|
19
19
|
*/
|
|
@@ -25,7 +25,7 @@ export declare class LiveList<T> extends AbstractCrdt {
|
|
|
25
25
|
/**
|
|
26
26
|
* INTERNAL
|
|
27
27
|
*/
|
|
28
|
-
_attachChild(id: string, key: string, child: AbstractCrdt): ApplyResult;
|
|
28
|
+
_attachChild(id: string, key: string, child: AbstractCrdt, isLocal: boolean): ApplyResult;
|
|
29
29
|
/**
|
|
30
30
|
* INTERNAL
|
|
31
31
|
*/
|
|
@@ -37,7 +37,11 @@ export declare class LiveList<T> extends AbstractCrdt {
|
|
|
37
37
|
/**
|
|
38
38
|
* INTERNAL
|
|
39
39
|
*/
|
|
40
|
-
_apply(op: Op): ApplyResult;
|
|
40
|
+
_apply(op: Op, isLocal: boolean): ApplyResult;
|
|
41
|
+
/**
|
|
42
|
+
* INTERNAL
|
|
43
|
+
*/
|
|
44
|
+
_toSerializedCrdt(): SerializedCrdt;
|
|
41
45
|
/**
|
|
42
46
|
* Returns the number of elements.
|
|
43
47
|
*/
|
package/lib/esm/LiveList.js
CHANGED
|
@@ -12,7 +12,7 @@ var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (
|
|
|
12
12
|
var _LiveList_items, _LiveListIterator_innerIterator;
|
|
13
13
|
import { AbstractCrdt } from "./AbstractCrdt";
|
|
14
14
|
import { deserialize, selfOrRegister, selfOrRegisterValue } from "./utils";
|
|
15
|
-
import { OpType, } from "./live";
|
|
15
|
+
import { OpType, CrdtType, } from "./live";
|
|
16
16
|
import { makePosition, compare } from "./position";
|
|
17
17
|
/**
|
|
18
18
|
* The LiveList class represents an ordered collection of items that is synchorinized across clients.
|
|
@@ -51,7 +51,7 @@ export class LiveList extends AbstractCrdt {
|
|
|
51
51
|
/**
|
|
52
52
|
* INTERNAL
|
|
53
53
|
*/
|
|
54
|
-
_serialize(parentId, parentKey) {
|
|
54
|
+
_serialize(parentId, parentKey, doc) {
|
|
55
55
|
if (this._id == null) {
|
|
56
56
|
throw new Error("Cannot serialize item is not attached");
|
|
57
57
|
}
|
|
@@ -61,13 +61,14 @@ export class LiveList extends AbstractCrdt {
|
|
|
61
61
|
const ops = [];
|
|
62
62
|
const op = {
|
|
63
63
|
id: this._id,
|
|
64
|
+
opId: doc === null || doc === void 0 ? void 0 : doc.generateOpId(),
|
|
64
65
|
type: OpType.CreateList,
|
|
65
66
|
parentId,
|
|
66
67
|
parentKey,
|
|
67
68
|
};
|
|
68
69
|
ops.push(op);
|
|
69
70
|
for (const [value, key] of __classPrivateFieldGet(this, _LiveList_items, "f")) {
|
|
70
|
-
ops.push(...value._serialize(this._id, key));
|
|
71
|
+
ops.push(...value._serialize(this._id, key, doc));
|
|
71
72
|
}
|
|
72
73
|
return ops;
|
|
73
74
|
}
|
|
@@ -92,7 +93,7 @@ export class LiveList extends AbstractCrdt {
|
|
|
92
93
|
/**
|
|
93
94
|
* INTERNAL
|
|
94
95
|
*/
|
|
95
|
-
_attachChild(id, key, child) {
|
|
96
|
+
_attachChild(id, key, child, isLocal) {
|
|
96
97
|
var _a;
|
|
97
98
|
if (this._doc == null) {
|
|
98
99
|
throw new Error("Can't attach child if doc is not present");
|
|
@@ -100,11 +101,24 @@ export class LiveList extends AbstractCrdt {
|
|
|
100
101
|
child._attach(id, this._doc);
|
|
101
102
|
child._setParentLink(this, key);
|
|
102
103
|
const index = __classPrivateFieldGet(this, _LiveList_items, "f").findIndex((entry) => entry[1] === key);
|
|
103
|
-
|
|
104
|
+
let newKey = key;
|
|
105
|
+
// If there is a conflict
|
|
104
106
|
if (index !== -1) {
|
|
105
|
-
|
|
107
|
+
if (isLocal) {
|
|
108
|
+
// If change is local => assign a temporary position to newly attached child
|
|
109
|
+
let before = __classPrivateFieldGet(this, _LiveList_items, "f")[index] ? __classPrivateFieldGet(this, _LiveList_items, "f")[index][1] : undefined;
|
|
110
|
+
let after = __classPrivateFieldGet(this, _LiveList_items, "f")[index + 1]
|
|
111
|
+
? __classPrivateFieldGet(this, _LiveList_items, "f")[index + 1][1]
|
|
112
|
+
: undefined;
|
|
113
|
+
newKey = makePosition(before, after);
|
|
114
|
+
child._setParentLink(this, newKey);
|
|
115
|
+
}
|
|
116
|
+
else {
|
|
117
|
+
// If change is remote => assign a temporary position to existing child until we get the fix from the backend
|
|
118
|
+
__classPrivateFieldGet(this, _LiveList_items, "f")[index][1] = makePosition(key, (_a = __classPrivateFieldGet(this, _LiveList_items, "f")[index + 1]) === null || _a === void 0 ? void 0 : _a[1]);
|
|
119
|
+
}
|
|
106
120
|
}
|
|
107
|
-
__classPrivateFieldGet(this, _LiveList_items, "f").push([child,
|
|
121
|
+
__classPrivateFieldGet(this, _LiveList_items, "f").push([child, newKey]);
|
|
108
122
|
__classPrivateFieldGet(this, _LiveList_items, "f").sort((itemA, itemB) => compare(itemA[1], itemB[1]));
|
|
109
123
|
return { reverse: [{ type: OpType.DeleteCrdt, id }], modified: this };
|
|
110
124
|
}
|
|
@@ -138,8 +152,19 @@ export class LiveList extends AbstractCrdt {
|
|
|
138
152
|
/**
|
|
139
153
|
* INTERNAL
|
|
140
154
|
*/
|
|
141
|
-
_apply(op) {
|
|
142
|
-
return super._apply(op);
|
|
155
|
+
_apply(op, isLocal) {
|
|
156
|
+
return super._apply(op, isLocal);
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* INTERNAL
|
|
160
|
+
*/
|
|
161
|
+
_toSerializedCrdt() {
|
|
162
|
+
var _a;
|
|
163
|
+
return {
|
|
164
|
+
type: CrdtType.List,
|
|
165
|
+
parentId: (_a = this._parent) === null || _a === void 0 ? void 0 : _a._id,
|
|
166
|
+
parentKey: this._parentKey,
|
|
167
|
+
};
|
|
143
168
|
}
|
|
144
169
|
/**
|
|
145
170
|
* Returns the number of elements.
|
|
@@ -173,7 +198,7 @@ export class LiveList extends AbstractCrdt {
|
|
|
173
198
|
if (this._doc && this._id) {
|
|
174
199
|
const id = this._doc.generateId();
|
|
175
200
|
value._attach(id, this._doc);
|
|
176
|
-
this._doc.dispatch(value._serialize(this._id, position), [{ type: OpType.DeleteCrdt, id }], [this]);
|
|
201
|
+
this._doc.dispatch(value._serialize(this._id, position, this._doc), [{ type: OpType.DeleteCrdt, id }], [this]);
|
|
177
202
|
}
|
|
178
203
|
}
|
|
179
204
|
/**
|
|
@@ -219,6 +244,7 @@ export class LiveList extends AbstractCrdt {
|
|
|
219
244
|
{
|
|
220
245
|
type: OpType.SetParentKey,
|
|
221
246
|
id: item[0]._id,
|
|
247
|
+
opId: this._doc.generateOpId(),
|
|
222
248
|
parentKey: position,
|
|
223
249
|
},
|
|
224
250
|
], [
|
|
@@ -247,6 +273,7 @@ export class LiveList extends AbstractCrdt {
|
|
|
247
273
|
this._doc.dispatch([
|
|
248
274
|
{
|
|
249
275
|
id: childRecordId,
|
|
276
|
+
opId: this._doc.generateOpId(),
|
|
250
277
|
type: OpType.DeleteCrdt,
|
|
251
278
|
},
|
|
252
279
|
], item[0]._serialize(this._id, item[1]), [this]);
|
package/lib/esm/LiveMap.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { AbstractCrdt, Doc, ApplyResult } from "./AbstractCrdt";
|
|
2
|
-
import { Op, SerializedCrdtWithId } from "./live";
|
|
2
|
+
import { Op, SerializedCrdtWithId, SerializedCrdt } from "./live";
|
|
3
3
|
/**
|
|
4
4
|
* The LiveMap class is similar to a JavaScript Map that is synchronized on all clients.
|
|
5
5
|
* Keys should be a string, and values should be serializable to JSON.
|
|
@@ -11,7 +11,7 @@ export declare class LiveMap<TKey extends string, TValue> extends AbstractCrdt {
|
|
|
11
11
|
/**
|
|
12
12
|
* INTERNAL
|
|
13
13
|
*/
|
|
14
|
-
_serialize(parentId?: string, parentKey?: string): Op[];
|
|
14
|
+
_serialize(parentId?: string, parentKey?: string, doc?: Doc): Op[];
|
|
15
15
|
/**
|
|
16
16
|
* INTERNAL
|
|
17
17
|
*/
|
|
@@ -23,7 +23,7 @@ export declare class LiveMap<TKey extends string, TValue> extends AbstractCrdt {
|
|
|
23
23
|
/**
|
|
24
24
|
* INTERNAL
|
|
25
25
|
*/
|
|
26
|
-
_attachChild(id: string, key: TKey, child: AbstractCrdt): ApplyResult;
|
|
26
|
+
_attachChild(id: string, key: TKey, child: AbstractCrdt, isLocal: boolean): ApplyResult;
|
|
27
27
|
/**
|
|
28
28
|
* INTERNAL
|
|
29
29
|
*/
|
|
@@ -32,6 +32,10 @@ export declare class LiveMap<TKey extends string, TValue> extends AbstractCrdt {
|
|
|
32
32
|
* INTERNAL
|
|
33
33
|
*/
|
|
34
34
|
_detachChild(child: AbstractCrdt): void;
|
|
35
|
+
/**
|
|
36
|
+
* INTERNAL
|
|
37
|
+
*/
|
|
38
|
+
_toSerializedCrdt(): SerializedCrdt;
|
|
35
39
|
/**
|
|
36
40
|
* Returns a specified element from the LiveMap.
|
|
37
41
|
* @param key The key of the element to return.
|
package/lib/esm/LiveMap.js
CHANGED
|
@@ -38,7 +38,7 @@ export class LiveMap extends AbstractCrdt {
|
|
|
38
38
|
/**
|
|
39
39
|
* INTERNAL
|
|
40
40
|
*/
|
|
41
|
-
_serialize(parentId, parentKey) {
|
|
41
|
+
_serialize(parentId, parentKey, doc) {
|
|
42
42
|
if (this._id == null) {
|
|
43
43
|
throw new Error("Cannot serialize item is not attached");
|
|
44
44
|
}
|
|
@@ -48,13 +48,14 @@ export class LiveMap extends AbstractCrdt {
|
|
|
48
48
|
const ops = [];
|
|
49
49
|
const op = {
|
|
50
50
|
id: this._id,
|
|
51
|
+
opId: doc === null || doc === void 0 ? void 0 : doc.generateOpId(),
|
|
51
52
|
type: OpType.CreateMap,
|
|
52
53
|
parentId,
|
|
53
54
|
parentKey,
|
|
54
55
|
};
|
|
55
56
|
ops.push(op);
|
|
56
57
|
for (const [key, value] of __classPrivateFieldGet(this, _LiveMap_map, "f")) {
|
|
57
|
-
ops.push(...value._serialize(this._id, key));
|
|
58
|
+
ops.push(...value._serialize(this._id, key, doc));
|
|
58
59
|
}
|
|
59
60
|
return ops;
|
|
60
61
|
}
|
|
@@ -96,7 +97,7 @@ export class LiveMap extends AbstractCrdt {
|
|
|
96
97
|
/**
|
|
97
98
|
* INTERNAL
|
|
98
99
|
*/
|
|
99
|
-
_attachChild(id, key, child) {
|
|
100
|
+
_attachChild(id, key, child, isLocal) {
|
|
100
101
|
if (this._doc == null) {
|
|
101
102
|
throw new Error("Can't attach child if doc is not present");
|
|
102
103
|
}
|
|
@@ -134,6 +135,17 @@ export class LiveMap extends AbstractCrdt {
|
|
|
134
135
|
}
|
|
135
136
|
child._detach();
|
|
136
137
|
}
|
|
138
|
+
/**
|
|
139
|
+
* INTERNAL
|
|
140
|
+
*/
|
|
141
|
+
_toSerializedCrdt() {
|
|
142
|
+
var _a;
|
|
143
|
+
return {
|
|
144
|
+
type: CrdtType.Map,
|
|
145
|
+
parentId: (_a = this._parent) === null || _a === void 0 ? void 0 : _a._id,
|
|
146
|
+
parentKey: this._parentKey,
|
|
147
|
+
};
|
|
148
|
+
}
|
|
137
149
|
/**
|
|
138
150
|
* Returns a specified element from the LiveMap.
|
|
139
151
|
* @param key The key of the element to return.
|
|
@@ -162,7 +174,7 @@ export class LiveMap extends AbstractCrdt {
|
|
|
162
174
|
if (this._doc && this._id) {
|
|
163
175
|
const id = this._doc.generateId();
|
|
164
176
|
item._attach(id, this._doc);
|
|
165
|
-
this._doc.dispatch(item._serialize(this._id, key), oldValue
|
|
177
|
+
this._doc.dispatch(item._serialize(this._id, key, this._doc), oldValue
|
|
166
178
|
? oldValue._serialize(this._id, key)
|
|
167
179
|
: [{ type: OpType.DeleteCrdt, id }], [this]);
|
|
168
180
|
}
|
|
@@ -192,7 +204,13 @@ export class LiveMap extends AbstractCrdt {
|
|
|
192
204
|
}
|
|
193
205
|
item._detach();
|
|
194
206
|
if (this._doc && item._id) {
|
|
195
|
-
this._doc.dispatch([
|
|
207
|
+
this._doc.dispatch([
|
|
208
|
+
{
|
|
209
|
+
type: OpType.DeleteCrdt,
|
|
210
|
+
id: item._id,
|
|
211
|
+
opId: this._doc.generateOpId(),
|
|
212
|
+
},
|
|
213
|
+
], item._serialize(this._id, key), [this]);
|
|
196
214
|
}
|
|
197
215
|
__classPrivateFieldGet(this, _LiveMap_map, "f").delete(key);
|
|
198
216
|
return true;
|