@liveblocks/client 0.13.1 → 0.14.0
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 +9 -4
- package/lib/cjs/LiveList.js +58 -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/index.d.ts +1 -1
- package/lib/cjs/live.d.ts +7 -0
- package/lib/cjs/room.d.ts +10 -2
- package/lib/cjs/room.js +116 -27
- package/lib/cjs/types.d.ts +13 -1
- 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 +9 -4
- package/lib/esm/LiveList.js +59 -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/index.d.ts +1 -1
- package/lib/esm/live.d.ts +7 -0
- package/lib/esm/room.d.ts +10 -2
- package/lib/esm/room.js +117 -28
- package/lib/esm/types.d.ts +13 -1
- package/lib/esm/utils.d.ts +2 -1
- package/lib/esm/utils.js +75 -1
- package/package.json +2 -2
package/lib/esm/room.js
CHANGED
|
@@ -7,7 +7,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
7
7
|
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
8
|
});
|
|
9
9
|
};
|
|
10
|
-
import { isSameNodeOrChildOf, remove } from "./utils";
|
|
10
|
+
import { getTreesDiffOperations, isSameNodeOrChildOf, remove } from "./utils";
|
|
11
11
|
import auth, { parseToken } from "./authentication";
|
|
12
12
|
import { ClientMessageType, ServerMessageType, OpType, } from "./live";
|
|
13
13
|
import { LiveMap } from "./LiveMap";
|
|
@@ -36,6 +36,9 @@ function makeOthers(presenceMap) {
|
|
|
36
36
|
get count() {
|
|
37
37
|
return array.length;
|
|
38
38
|
},
|
|
39
|
+
[Symbol.iterator]() {
|
|
40
|
+
return array[Symbol.iterator]();
|
|
41
|
+
},
|
|
39
42
|
map(callback) {
|
|
40
43
|
return array.map(callback);
|
|
41
44
|
},
|
|
@@ -107,18 +110,23 @@ export function makeStateMachine(state, context, mockedEffects) {
|
|
|
107
110
|
};
|
|
108
111
|
return genericSubscribe(cb);
|
|
109
112
|
}
|
|
110
|
-
function
|
|
111
|
-
|
|
113
|
+
function createOrUpdateRootFromMessage(message) {
|
|
114
|
+
if (message.items.length === 0) {
|
|
115
|
+
throw new Error("Internal error: cannot load storage without items");
|
|
116
|
+
}
|
|
117
|
+
if (state.root) {
|
|
118
|
+
updateRoot(message.items);
|
|
119
|
+
}
|
|
120
|
+
else {
|
|
121
|
+
state.root = load(message.items);
|
|
122
|
+
}
|
|
112
123
|
for (const key in state.defaultStorageRoot) {
|
|
113
124
|
if (state.root.get(key) == null) {
|
|
114
125
|
state.root.set(key, state.defaultStorageRoot[key]);
|
|
115
126
|
}
|
|
116
127
|
}
|
|
117
128
|
}
|
|
118
|
-
function
|
|
119
|
-
if (items.length === 0) {
|
|
120
|
-
throw new Error("Internal error: cannot load storage without items");
|
|
121
|
-
}
|
|
129
|
+
function buildRootAndParentToChildren(items) {
|
|
122
130
|
const parentToChildren = new Map();
|
|
123
131
|
let root = null;
|
|
124
132
|
for (const tuple of items) {
|
|
@@ -139,6 +147,23 @@ export function makeStateMachine(state, context, mockedEffects) {
|
|
|
139
147
|
if (root == null) {
|
|
140
148
|
throw new Error("Root can't be null");
|
|
141
149
|
}
|
|
150
|
+
return [root, parentToChildren];
|
|
151
|
+
}
|
|
152
|
+
function updateRoot(items) {
|
|
153
|
+
if (!state.root) {
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
156
|
+
const currentItems = new Map();
|
|
157
|
+
state.items.forEach((liveCrdt, id) => {
|
|
158
|
+
currentItems.set(id, liveCrdt._toSerializedCrdt());
|
|
159
|
+
});
|
|
160
|
+
// Get operations that represent the diff between 2 states.
|
|
161
|
+
const ops = getTreesDiffOperations(currentItems, new Map(items));
|
|
162
|
+
const result = apply(ops, false);
|
|
163
|
+
notify(result.updates);
|
|
164
|
+
}
|
|
165
|
+
function load(items) {
|
|
166
|
+
const [root, parentToChildren] = buildRootAndParentToChildren(items);
|
|
142
167
|
return LiveObject._deserialize(root, parentToChildren, {
|
|
143
168
|
addItem,
|
|
144
169
|
deleteItem,
|
|
@@ -227,7 +252,10 @@ export function makeStateMachine(state, context, mockedEffects) {
|
|
|
227
252
|
state.connection.state === "connecting") {
|
|
228
253
|
return state.connection.id;
|
|
229
254
|
}
|
|
230
|
-
|
|
255
|
+
else if (state.lastConnectionId !== null) {
|
|
256
|
+
return state.lastConnectionId;
|
|
257
|
+
}
|
|
258
|
+
throw new Error("Internal. Tried to get connection id but connection was never open");
|
|
231
259
|
}
|
|
232
260
|
function generateId() {
|
|
233
261
|
return `${getConnectionId()}:${state.clock++}`;
|
|
@@ -235,7 +263,7 @@ export function makeStateMachine(state, context, mockedEffects) {
|
|
|
235
263
|
function generateOpId() {
|
|
236
264
|
return `${getConnectionId()}:${state.opClock++}`;
|
|
237
265
|
}
|
|
238
|
-
function apply(item) {
|
|
266
|
+
function apply(item, isLocal) {
|
|
239
267
|
const result = {
|
|
240
268
|
reverse: [],
|
|
241
269
|
updates: { nodes: new Set(), presence: false },
|
|
@@ -262,7 +290,11 @@ export function makeStateMachine(state, context, mockedEffects) {
|
|
|
262
290
|
result.updates.presence = true;
|
|
263
291
|
}
|
|
264
292
|
else {
|
|
265
|
-
|
|
293
|
+
// Ops applied after undo/redo don't have an opId.
|
|
294
|
+
if (isLocal && !op.opId) {
|
|
295
|
+
op.opId = generateOpId();
|
|
296
|
+
}
|
|
297
|
+
const applyOpResult = applyOp(op, isLocal);
|
|
266
298
|
if (applyOpResult.modified) {
|
|
267
299
|
result.updates.nodes.add(applyOpResult.modified);
|
|
268
300
|
result.reverse.unshift(...applyOpResult.reverse);
|
|
@@ -271,7 +303,10 @@ export function makeStateMachine(state, context, mockedEffects) {
|
|
|
271
303
|
}
|
|
272
304
|
return result;
|
|
273
305
|
}
|
|
274
|
-
function applyOp(op) {
|
|
306
|
+
function applyOp(op, isLocal) {
|
|
307
|
+
if (op.opId) {
|
|
308
|
+
state.offlineOperations.delete(op.opId);
|
|
309
|
+
}
|
|
275
310
|
switch (op.type) {
|
|
276
311
|
case OpType.DeleteObjectKey:
|
|
277
312
|
case OpType.UpdateObject:
|
|
@@ -280,7 +315,7 @@ export function makeStateMachine(state, context, mockedEffects) {
|
|
|
280
315
|
if (item == null) {
|
|
281
316
|
return { modified: false };
|
|
282
317
|
}
|
|
283
|
-
return item._apply(op);
|
|
318
|
+
return item._apply(op, isLocal);
|
|
284
319
|
}
|
|
285
320
|
case OpType.SetParentKey: {
|
|
286
321
|
const item = state.items.get(op.id);
|
|
@@ -308,28 +343,28 @@ export function makeStateMachine(state, context, mockedEffects) {
|
|
|
308
343
|
if (parent == null || getItem(op.id) != null) {
|
|
309
344
|
return { modified: false };
|
|
310
345
|
}
|
|
311
|
-
return parent._attachChild(op.id, op.parentKey, new LiveObject(op.data));
|
|
346
|
+
return parent._attachChild(op.id, op.parentKey, new LiveObject(op.data), isLocal);
|
|
312
347
|
}
|
|
313
348
|
case OpType.CreateList: {
|
|
314
349
|
const parent = state.items.get(op.parentId);
|
|
315
350
|
if (parent == null || getItem(op.id) != null) {
|
|
316
351
|
return { modified: false };
|
|
317
352
|
}
|
|
318
|
-
return parent._attachChild(op.id, op.parentKey, new LiveList());
|
|
353
|
+
return parent._attachChild(op.id, op.parentKey, new LiveList(), isLocal);
|
|
319
354
|
}
|
|
320
355
|
case OpType.CreateRegister: {
|
|
321
356
|
const parent = state.items.get(op.parentId);
|
|
322
357
|
if (parent == null || getItem(op.id) != null) {
|
|
323
358
|
return { modified: false };
|
|
324
359
|
}
|
|
325
|
-
return parent._attachChild(op.id, op.parentKey, new LiveRegister(op.data));
|
|
360
|
+
return parent._attachChild(op.id, op.parentKey, new LiveRegister(op.data), isLocal);
|
|
326
361
|
}
|
|
327
362
|
case OpType.CreateMap: {
|
|
328
363
|
const parent = state.items.get(op.parentId);
|
|
329
364
|
if (parent == null || getItem(op.id) != null) {
|
|
330
365
|
return { modified: false };
|
|
331
366
|
}
|
|
332
|
-
return parent._attachChild(op.id, op.parentKey, new LiveMap());
|
|
367
|
+
return parent._attachChild(op.id, op.parentKey, new LiveMap(), isLocal);
|
|
333
368
|
}
|
|
334
369
|
}
|
|
335
370
|
return { modified: false };
|
|
@@ -421,7 +456,9 @@ See v0.13 release notes for more information.
|
|
|
421
456
|
state.socket = socket;
|
|
422
457
|
}
|
|
423
458
|
function authenticationFailure(error) {
|
|
424
|
-
|
|
459
|
+
if (process.env.NODE_ENV !== "production") {
|
|
460
|
+
console.error("Call to authentication endpoint failed", error);
|
|
461
|
+
}
|
|
425
462
|
updateConnection({ state: "unavailable" });
|
|
426
463
|
state.numberOfRetry++;
|
|
427
464
|
state.timeoutHandles.reconnect = effects.scheduleReconnect(getRetryDelay());
|
|
@@ -549,12 +586,13 @@ See v0.13 release notes for more information.
|
|
|
549
586
|
break;
|
|
550
587
|
}
|
|
551
588
|
case ServerMessageType.InitialStorageState: {
|
|
552
|
-
|
|
589
|
+
createOrUpdateRootFromMessage(subMessage);
|
|
590
|
+
applyAndSendOfflineOps();
|
|
553
591
|
_getInitialStateResolver === null || _getInitialStateResolver === void 0 ? void 0 : _getInitialStateResolver();
|
|
554
592
|
break;
|
|
555
593
|
}
|
|
556
594
|
case ServerMessageType.UpdateStorage: {
|
|
557
|
-
const applyResult = apply(subMessage.ops);
|
|
595
|
+
const applyResult = apply(subMessage.ops, false);
|
|
558
596
|
for (const node of applyResult.updates.nodes) {
|
|
559
597
|
updates.nodes.add(node);
|
|
560
598
|
}
|
|
@@ -587,13 +625,20 @@ See v0.13 release notes for more information.
|
|
|
587
625
|
updateConnection({ state: "failed" });
|
|
588
626
|
const error = new LiveblocksError(event.reason, event.code);
|
|
589
627
|
for (const listener of state.listeners.error) {
|
|
628
|
+
if (process.env.NODE_ENV !== "production") {
|
|
629
|
+
console.error(`Connection to Liveblocks websocket server closed. Reason: ${error.message} (code: ${error.code})`);
|
|
630
|
+
}
|
|
590
631
|
listener(error);
|
|
591
632
|
}
|
|
592
633
|
}
|
|
593
634
|
else if (event.wasClean === false) {
|
|
594
|
-
updateConnection({ state: "unavailable" });
|
|
595
635
|
state.numberOfRetry++;
|
|
596
|
-
|
|
636
|
+
const delay = getRetryDelay();
|
|
637
|
+
if (process.env.NODE_ENV !== "production") {
|
|
638
|
+
console.warn(`Connection to Liveblocks websocket server closed (code: ${event.code}). Retrying in ${delay}ms.`);
|
|
639
|
+
}
|
|
640
|
+
updateConnection({ state: "unavailable" });
|
|
641
|
+
state.timeoutHandles.reconnect = effects.scheduleReconnect(delay);
|
|
597
642
|
}
|
|
598
643
|
else {
|
|
599
644
|
updateConnection({ state: "closed" });
|
|
@@ -617,6 +662,10 @@ See v0.13 release notes for more information.
|
|
|
617
662
|
if (state.connection.state === "connecting") {
|
|
618
663
|
updateConnection(Object.assign(Object.assign({}, state.connection), { state: "open" }));
|
|
619
664
|
state.numberOfRetry = 0;
|
|
665
|
+
state.lastConnectionId = state.connection.id;
|
|
666
|
+
if (state.root) {
|
|
667
|
+
state.buffer.messages.push({ type: ClientMessageType.FetchStorage });
|
|
668
|
+
}
|
|
620
669
|
tryFlushing();
|
|
621
670
|
}
|
|
622
671
|
else {
|
|
@@ -656,11 +705,29 @@ See v0.13 release notes for more information.
|
|
|
656
705
|
clearInterval(state.intervalHandles.heartbeat);
|
|
657
706
|
connect();
|
|
658
707
|
}
|
|
659
|
-
function
|
|
660
|
-
if (state.
|
|
708
|
+
function applyAndSendOfflineOps() {
|
|
709
|
+
if (state.offlineOperations.size === 0) {
|
|
661
710
|
return;
|
|
662
711
|
}
|
|
663
|
-
|
|
712
|
+
const messages = [];
|
|
713
|
+
const ops = Array.from(state.offlineOperations.values());
|
|
714
|
+
const result = apply(ops, true);
|
|
715
|
+
messages.push({
|
|
716
|
+
type: ClientMessageType.UpdateStorage,
|
|
717
|
+
ops: ops,
|
|
718
|
+
});
|
|
719
|
+
notify(result.updates);
|
|
720
|
+
effects.send(messages);
|
|
721
|
+
}
|
|
722
|
+
function tryFlushing() {
|
|
723
|
+
const storageOps = state.buffer.storageOperations;
|
|
724
|
+
if (storageOps.length > 0) {
|
|
725
|
+
storageOps.forEach((op) => {
|
|
726
|
+
state.offlineOperations.set(op.opId, op);
|
|
727
|
+
});
|
|
728
|
+
}
|
|
729
|
+
if (state.socket == null || state.socket.readyState !== WebSocket.OPEN) {
|
|
730
|
+
state.buffer.storageOperations = [];
|
|
664
731
|
return;
|
|
665
732
|
}
|
|
666
733
|
const now = Date.now();
|
|
@@ -735,8 +802,10 @@ See v0.13 release notes for more information.
|
|
|
735
802
|
function getOthers() {
|
|
736
803
|
return state.others;
|
|
737
804
|
}
|
|
738
|
-
function broadcastEvent(event
|
|
739
|
-
|
|
805
|
+
function broadcastEvent(event, options = {
|
|
806
|
+
shouldQueueEventIfNotReady: false,
|
|
807
|
+
}) {
|
|
808
|
+
if (state.socket == null && options.shouldQueueEventIfNotReady == false) {
|
|
740
809
|
return;
|
|
741
810
|
}
|
|
742
811
|
state.buffer.messages.push({
|
|
@@ -778,7 +847,7 @@ See v0.13 release notes for more information.
|
|
|
778
847
|
return;
|
|
779
848
|
}
|
|
780
849
|
state.isHistoryPaused = false;
|
|
781
|
-
const result = apply(historyItem);
|
|
850
|
+
const result = apply(historyItem, true);
|
|
782
851
|
notify(result.updates);
|
|
783
852
|
state.redoStack.push(result.reverse);
|
|
784
853
|
for (const op of historyItem) {
|
|
@@ -797,7 +866,7 @@ See v0.13 release notes for more information.
|
|
|
797
866
|
return;
|
|
798
867
|
}
|
|
799
868
|
state.isHistoryPaused = false;
|
|
800
|
-
const result = apply(historyItem);
|
|
869
|
+
const result = apply(historyItem, true);
|
|
801
870
|
notify(result.updates);
|
|
802
871
|
state.undoStack.push(result.reverse);
|
|
803
872
|
for (const op of historyItem) {
|
|
@@ -849,6 +918,16 @@ See v0.13 release notes for more information.
|
|
|
849
918
|
}
|
|
850
919
|
state.pausedHistory = [];
|
|
851
920
|
}
|
|
921
|
+
function simulateSocketClose() {
|
|
922
|
+
if (state.socket) {
|
|
923
|
+
state.socket.close();
|
|
924
|
+
}
|
|
925
|
+
}
|
|
926
|
+
function simulateSendCloseEvent(event) {
|
|
927
|
+
if (state.socket) {
|
|
928
|
+
onClose(event);
|
|
929
|
+
}
|
|
930
|
+
}
|
|
852
931
|
return {
|
|
853
932
|
// Internal
|
|
854
933
|
onOpen,
|
|
@@ -857,6 +936,9 @@ See v0.13 release notes for more information.
|
|
|
857
936
|
authenticationSuccess,
|
|
858
937
|
heartbeat,
|
|
859
938
|
onNavigatorOnline,
|
|
939
|
+
// Internal dev tools
|
|
940
|
+
simulateSocketClose,
|
|
941
|
+
simulateSendCloseEvent,
|
|
860
942
|
// onWakeUp,
|
|
861
943
|
onVisibilityChange,
|
|
862
944
|
getUndoStack: () => state.undoStack,
|
|
@@ -888,6 +970,7 @@ See v0.13 release notes for more information.
|
|
|
888
970
|
export function defaultState(me, defaultStorageRoot) {
|
|
889
971
|
return {
|
|
890
972
|
connection: { state: "closed" },
|
|
973
|
+
lastConnectionId: null,
|
|
891
974
|
socket: null,
|
|
892
975
|
listeners: {
|
|
893
976
|
event: [],
|
|
@@ -932,6 +1015,7 @@ export function defaultState(me, defaultStorageRoot) {
|
|
|
932
1015
|
updates: { nodes: new Set(), presence: false, others: [] },
|
|
933
1016
|
reverseOps: [],
|
|
934
1017
|
},
|
|
1018
|
+
offlineOperations: new Map(),
|
|
935
1019
|
};
|
|
936
1020
|
}
|
|
937
1021
|
export function createRoom(name, options) {
|
|
@@ -977,6 +1061,11 @@ export function createRoom(name, options) {
|
|
|
977
1061
|
pause: machine.pauseHistory,
|
|
978
1062
|
resume: machine.resumeHistory,
|
|
979
1063
|
},
|
|
1064
|
+
// @ts-ignore
|
|
1065
|
+
internalDevTools: {
|
|
1066
|
+
closeWebsocket: machine.simulateSocketClose,
|
|
1067
|
+
sendCloseEvent: machine.simulateSendCloseEvent,
|
|
1068
|
+
},
|
|
980
1069
|
};
|
|
981
1070
|
return {
|
|
982
1071
|
connect: machine.connect,
|
package/lib/esm/types.d.ts
CHANGED
|
@@ -28,6 +28,14 @@ export declare type LiveListUpdates<TItem = any> = {
|
|
|
28
28
|
type: "LiveList";
|
|
29
29
|
node: LiveList<TItem>;
|
|
30
30
|
};
|
|
31
|
+
export declare type BroadcastOptions = {
|
|
32
|
+
/**
|
|
33
|
+
* Whether or not event is queued if the connection is currently closed.
|
|
34
|
+
*
|
|
35
|
+
* ❗ We are not sure if we want to support this option in the future so it might be deprecated to be replaced by something else
|
|
36
|
+
*/
|
|
37
|
+
shouldQueueEventIfNotReady: boolean;
|
|
38
|
+
};
|
|
31
39
|
export declare type StorageUpdate = LiveMapUpdates | LiveObjectUpdates | LiveListUpdates;
|
|
32
40
|
export declare type StorageCallback = (updates: StorageUpdate[]) => void;
|
|
33
41
|
export declare type Client = {
|
|
@@ -65,6 +73,10 @@ export interface Others<TPresence extends Presence = Presence> {
|
|
|
65
73
|
* Number of other users in the room.
|
|
66
74
|
*/
|
|
67
75
|
readonly count: number;
|
|
76
|
+
/**
|
|
77
|
+
* Returns a new Iterator object that contains the users.
|
|
78
|
+
*/
|
|
79
|
+
[Symbol.iterator](): IterableIterator<User<TPresence>>;
|
|
68
80
|
/**
|
|
69
81
|
* Returns the array of connected users in room.
|
|
70
82
|
*/
|
|
@@ -423,7 +435,7 @@ export declare type Room = {
|
|
|
423
435
|
* }
|
|
424
436
|
* });
|
|
425
437
|
*/
|
|
426
|
-
broadcastEvent: (event: any) => void;
|
|
438
|
+
broadcastEvent: (event: any, options?: BroadcastOptions) => void;
|
|
427
439
|
/**
|
|
428
440
|
* Get the room's storage asynchronously.
|
|
429
441
|
* The storage's root is a {@link LiveObject}.
|
package/lib/esm/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/esm/utils.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { CrdtType, } from "./live";
|
|
1
|
+
import { CrdtType, OpType, } from "./live";
|
|
2
2
|
import { LiveList } from "./LiveList";
|
|
3
3
|
import { LiveMap } from "./LiveMap";
|
|
4
4
|
import { LiveObject } from "./LiveObject";
|
|
@@ -64,3 +64,77 @@ export function selfOrRegister(obj) {
|
|
|
64
64
|
return new LiveRegister(obj);
|
|
65
65
|
}
|
|
66
66
|
}
|
|
67
|
+
export function getTreesDiffOperations(currentItems, newItems) {
|
|
68
|
+
const ops = [];
|
|
69
|
+
currentItems.forEach((_, id) => {
|
|
70
|
+
if (!newItems.get(id)) {
|
|
71
|
+
// Delete crdt
|
|
72
|
+
ops.push({
|
|
73
|
+
type: OpType.DeleteCrdt,
|
|
74
|
+
id: id,
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
});
|
|
78
|
+
newItems.forEach((crdt, id) => {
|
|
79
|
+
const currentCrdt = currentItems.get(id);
|
|
80
|
+
if (currentCrdt) {
|
|
81
|
+
if (crdt.type === CrdtType.Object) {
|
|
82
|
+
if (JSON.stringify(crdt.data) !==
|
|
83
|
+
JSON.stringify(currentCrdt.data)) {
|
|
84
|
+
ops.push({
|
|
85
|
+
type: OpType.UpdateObject,
|
|
86
|
+
id: id,
|
|
87
|
+
data: crdt.data,
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
if (crdt.parentKey !== currentCrdt.parentKey) {
|
|
92
|
+
ops.push({
|
|
93
|
+
type: OpType.SetParentKey,
|
|
94
|
+
id: id,
|
|
95
|
+
parentKey: crdt.parentKey,
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
else {
|
|
100
|
+
// new Crdt
|
|
101
|
+
switch (crdt.type) {
|
|
102
|
+
case CrdtType.Register:
|
|
103
|
+
ops.push({
|
|
104
|
+
type: OpType.CreateRegister,
|
|
105
|
+
id: id,
|
|
106
|
+
parentId: crdt.parentId,
|
|
107
|
+
parentKey: crdt.parentKey,
|
|
108
|
+
data: crdt.data,
|
|
109
|
+
});
|
|
110
|
+
break;
|
|
111
|
+
case CrdtType.List:
|
|
112
|
+
ops.push({
|
|
113
|
+
type: OpType.CreateList,
|
|
114
|
+
id: id,
|
|
115
|
+
parentId: crdt.parentId,
|
|
116
|
+
parentKey: crdt.parentKey,
|
|
117
|
+
});
|
|
118
|
+
break;
|
|
119
|
+
case CrdtType.Object:
|
|
120
|
+
ops.push({
|
|
121
|
+
type: OpType.CreateObject,
|
|
122
|
+
id: id,
|
|
123
|
+
parentId: crdt.parentId,
|
|
124
|
+
parentKey: crdt.parentKey,
|
|
125
|
+
data: crdt.data,
|
|
126
|
+
});
|
|
127
|
+
break;
|
|
128
|
+
case CrdtType.Map:
|
|
129
|
+
ops.push({
|
|
130
|
+
type: OpType.CreateMap,
|
|
131
|
+
id: id,
|
|
132
|
+
parentId: crdt.parentId,
|
|
133
|
+
parentKey: crdt.parentKey,
|
|
134
|
+
});
|
|
135
|
+
break;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
});
|
|
139
|
+
return ops;
|
|
140
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@liveblocks/client",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.14.0",
|
|
4
4
|
"description": "",
|
|
5
5
|
"main": "./lib/cjs/index.js",
|
|
6
6
|
"module": "./lib/esm/index.js",
|
|
@@ -38,4 +38,4 @@
|
|
|
38
38
|
"url": "https://github.com/liveblocks/liveblocks.git",
|
|
39
39
|
"directory": "packages/liveblocks"
|
|
40
40
|
}
|
|
41
|
-
}
|
|
41
|
+
}
|