@liveblocks/client 0.13.3 → 0.15.0-alpha.2
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 +13 -7
- package/lib/cjs/AbstractCrdt.js +2 -5
- package/lib/cjs/LiveList.d.ts +16 -6
- package/lib/cjs/LiveList.js +148 -17
- package/lib/cjs/LiveMap.d.ts +12 -4
- package/lib/cjs/LiveMap.js +57 -7
- package/lib/cjs/LiveObject.d.ts +22 -8
- package/lib/cjs/LiveObject.js +109 -24
- package/lib/cjs/LiveRegister.d.ts +13 -5
- package/lib/cjs/LiveRegister.js +23 -4
- package/lib/cjs/immutable.d.ts +1 -0
- package/lib/cjs/immutable.js +64 -6
- package/lib/cjs/index.d.ts +1 -0
- package/lib/cjs/index.js +8 -1
- package/lib/cjs/live.d.ts +7 -0
- package/lib/cjs/room.d.ts +9 -1
- package/lib/cjs/room.js +131 -70
- package/lib/cjs/types.d.ts +25 -0
- package/lib/cjs/utils.d.ts +4 -1
- package/lib/cjs/utils.js +101 -1
- package/lib/esm/AbstractCrdt.d.ts +13 -7
- package/lib/esm/AbstractCrdt.js +2 -5
- package/lib/esm/LiveList.d.ts +16 -6
- package/lib/esm/LiveList.js +149 -18
- package/lib/esm/LiveMap.d.ts +12 -4
- package/lib/esm/LiveMap.js +57 -7
- package/lib/esm/LiveObject.d.ts +22 -8
- package/lib/esm/LiveObject.js +109 -24
- package/lib/esm/LiveRegister.d.ts +13 -5
- package/lib/esm/LiveRegister.js +24 -5
- package/lib/esm/immutable.d.ts +1 -0
- package/lib/esm/immutable.js +63 -6
- package/lib/esm/index.d.ts +1 -0
- package/lib/esm/index.js +1 -0
- package/lib/esm/live.d.ts +7 -0
- package/lib/esm/room.d.ts +9 -1
- package/lib/esm/room.js +132 -71
- package/lib/esm/types.d.ts +25 -0
- package/lib/esm/utils.d.ts +4 -1
- package/lib/esm/utils.js +99 -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/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, mergeStorageUpdates, } from "./utils";
|
|
11
11
|
import auth, { parseToken } from "./authentication";
|
|
12
12
|
import { ClientMessageType, ServerMessageType, OpType, } from "./live";
|
|
13
13
|
import { LiveMap } from "./LiveMap";
|
|
@@ -110,18 +110,23 @@ export function makeStateMachine(state, context, mockedEffects) {
|
|
|
110
110
|
};
|
|
111
111
|
return genericSubscribe(cb);
|
|
112
112
|
}
|
|
113
|
-
function
|
|
114
|
-
|
|
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
|
+
}
|
|
115
123
|
for (const key in state.defaultStorageRoot) {
|
|
116
124
|
if (state.root.get(key) == null) {
|
|
117
125
|
state.root.set(key, state.defaultStorageRoot[key]);
|
|
118
126
|
}
|
|
119
127
|
}
|
|
120
128
|
}
|
|
121
|
-
function
|
|
122
|
-
if (items.length === 0) {
|
|
123
|
-
throw new Error("Internal error: cannot load storage without items");
|
|
124
|
-
}
|
|
129
|
+
function buildRootAndParentToChildren(items) {
|
|
125
130
|
const parentToChildren = new Map();
|
|
126
131
|
let root = null;
|
|
127
132
|
for (const tuple of items) {
|
|
@@ -142,6 +147,23 @@ export function makeStateMachine(state, context, mockedEffects) {
|
|
|
142
147
|
if (root == null) {
|
|
143
148
|
throw new Error("Root can't be null");
|
|
144
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);
|
|
145
167
|
return LiveObject._deserialize(root, parentToChildren, {
|
|
146
168
|
addItem,
|
|
147
169
|
deleteItem,
|
|
@@ -171,22 +193,22 @@ export function makeStateMachine(state, context, mockedEffects) {
|
|
|
171
193
|
state.undoStack.push(historyItem);
|
|
172
194
|
}
|
|
173
195
|
}
|
|
174
|
-
function storageDispatch(ops, reverse,
|
|
196
|
+
function storageDispatch(ops, reverse, storageUpdates) {
|
|
175
197
|
if (state.isBatching) {
|
|
176
198
|
state.batch.ops.push(...ops);
|
|
177
|
-
|
|
178
|
-
state.batch.updates.
|
|
179
|
-
}
|
|
199
|
+
storageUpdates.forEach((value, key) => {
|
|
200
|
+
state.batch.updates.storageUpdates.set(key, mergeStorageUpdates(state.batch.updates.storageUpdates.get(key), value));
|
|
201
|
+
});
|
|
180
202
|
state.batch.reverseOps.push(...reverse);
|
|
181
203
|
}
|
|
182
204
|
else {
|
|
183
205
|
addToUndoStack(reverse);
|
|
184
206
|
state.redoStack = [];
|
|
185
207
|
dispatch(ops);
|
|
186
|
-
notify({
|
|
208
|
+
notify({ storageUpdates: storageUpdates });
|
|
187
209
|
}
|
|
188
210
|
}
|
|
189
|
-
function notify({
|
|
211
|
+
function notify({ storageUpdates = new Map(), presence = false, others = [], }) {
|
|
190
212
|
if (others.length > 0) {
|
|
191
213
|
state.others = makeOthers(state.users);
|
|
192
214
|
for (const event of others) {
|
|
@@ -200,28 +222,9 @@ export function makeStateMachine(state, context, mockedEffects) {
|
|
|
200
222
|
listener(state.me);
|
|
201
223
|
}
|
|
202
224
|
}
|
|
203
|
-
if (
|
|
225
|
+
if (storageUpdates.size > 0) {
|
|
204
226
|
for (const subscriber of state.listeners.storage) {
|
|
205
|
-
subscriber(Array.from(
|
|
206
|
-
if (m instanceof LiveObject) {
|
|
207
|
-
return {
|
|
208
|
-
type: "LiveObject",
|
|
209
|
-
node: m,
|
|
210
|
-
};
|
|
211
|
-
}
|
|
212
|
-
else if (m instanceof LiveList) {
|
|
213
|
-
return {
|
|
214
|
-
type: "LiveList",
|
|
215
|
-
node: m,
|
|
216
|
-
};
|
|
217
|
-
}
|
|
218
|
-
else {
|
|
219
|
-
return {
|
|
220
|
-
type: "LiveMap",
|
|
221
|
-
node: m,
|
|
222
|
-
};
|
|
223
|
-
}
|
|
224
|
-
}));
|
|
227
|
+
subscriber(Array.from(storageUpdates.values()));
|
|
225
228
|
}
|
|
226
229
|
}
|
|
227
230
|
}
|
|
@@ -230,7 +233,10 @@ export function makeStateMachine(state, context, mockedEffects) {
|
|
|
230
233
|
state.connection.state === "connecting") {
|
|
231
234
|
return state.connection.id;
|
|
232
235
|
}
|
|
233
|
-
|
|
236
|
+
else if (state.lastConnectionId !== null) {
|
|
237
|
+
return state.lastConnectionId;
|
|
238
|
+
}
|
|
239
|
+
throw new Error("Internal. Tried to get connection id but connection was never open");
|
|
234
240
|
}
|
|
235
241
|
function generateId() {
|
|
236
242
|
return `${getConnectionId()}:${state.clock++}`;
|
|
@@ -238,10 +244,13 @@ export function makeStateMachine(state, context, mockedEffects) {
|
|
|
238
244
|
function generateOpId() {
|
|
239
245
|
return `${getConnectionId()}:${state.opClock++}`;
|
|
240
246
|
}
|
|
241
|
-
function apply(item) {
|
|
247
|
+
function apply(item, isLocal) {
|
|
242
248
|
const result = {
|
|
243
249
|
reverse: [],
|
|
244
|
-
updates: {
|
|
250
|
+
updates: {
|
|
251
|
+
storageUpdates: new Map(),
|
|
252
|
+
presence: false,
|
|
253
|
+
},
|
|
245
254
|
};
|
|
246
255
|
for (const op of item) {
|
|
247
256
|
if (op.type === "presence") {
|
|
@@ -265,16 +274,23 @@ export function makeStateMachine(state, context, mockedEffects) {
|
|
|
265
274
|
result.updates.presence = true;
|
|
266
275
|
}
|
|
267
276
|
else {
|
|
268
|
-
|
|
277
|
+
// Ops applied after undo/redo don't have an opId.
|
|
278
|
+
if (isLocal && !op.opId) {
|
|
279
|
+
op.opId = generateOpId();
|
|
280
|
+
}
|
|
281
|
+
const applyOpResult = applyOp(op, isLocal);
|
|
269
282
|
if (applyOpResult.modified) {
|
|
270
|
-
result.updates.
|
|
283
|
+
result.updates.storageUpdates.set(applyOpResult.modified.node._id, mergeStorageUpdates(result.updates.storageUpdates.get(applyOpResult.modified.node._id), applyOpResult.modified));
|
|
271
284
|
result.reverse.unshift(...applyOpResult.reverse);
|
|
272
285
|
}
|
|
273
286
|
}
|
|
274
287
|
}
|
|
275
288
|
return result;
|
|
276
289
|
}
|
|
277
|
-
function applyOp(op) {
|
|
290
|
+
function applyOp(op, isLocal) {
|
|
291
|
+
if (op.opId) {
|
|
292
|
+
state.offlineOperations.delete(op.opId);
|
|
293
|
+
}
|
|
278
294
|
switch (op.type) {
|
|
279
295
|
case OpType.DeleteObjectKey:
|
|
280
296
|
case OpType.UpdateObject:
|
|
@@ -283,7 +299,7 @@ export function makeStateMachine(state, context, mockedEffects) {
|
|
|
283
299
|
if (item == null) {
|
|
284
300
|
return { modified: false };
|
|
285
301
|
}
|
|
286
|
-
return item._apply(op);
|
|
302
|
+
return item._apply(op, isLocal);
|
|
287
303
|
}
|
|
288
304
|
case OpType.SetParentKey: {
|
|
289
305
|
const item = state.items.get(op.id);
|
|
@@ -292,17 +308,12 @@ export function makeStateMachine(state, context, mockedEffects) {
|
|
|
292
308
|
}
|
|
293
309
|
if (item._parent instanceof LiveList) {
|
|
294
310
|
const previousKey = item._parentKey;
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
parentKey: previousKey,
|
|
302
|
-
},
|
|
303
|
-
],
|
|
304
|
-
modified: item._parent,
|
|
305
|
-
};
|
|
311
|
+
if (previousKey === op.parentKey) {
|
|
312
|
+
return { modified: false };
|
|
313
|
+
}
|
|
314
|
+
else {
|
|
315
|
+
return item._parent._setChildKey(op.parentKey, item, previousKey);
|
|
316
|
+
}
|
|
306
317
|
}
|
|
307
318
|
return { modified: false };
|
|
308
319
|
}
|
|
@@ -311,28 +322,28 @@ export function makeStateMachine(state, context, mockedEffects) {
|
|
|
311
322
|
if (parent == null || getItem(op.id) != null) {
|
|
312
323
|
return { modified: false };
|
|
313
324
|
}
|
|
314
|
-
return parent._attachChild(op.id, op.parentKey, new LiveObject(op.data));
|
|
325
|
+
return parent._attachChild(op.id, op.parentKey, new LiveObject(op.data), isLocal);
|
|
315
326
|
}
|
|
316
327
|
case OpType.CreateList: {
|
|
317
328
|
const parent = state.items.get(op.parentId);
|
|
318
329
|
if (parent == null || getItem(op.id) != null) {
|
|
319
330
|
return { modified: false };
|
|
320
331
|
}
|
|
321
|
-
return parent._attachChild(op.id, op.parentKey, new LiveList());
|
|
332
|
+
return parent._attachChild(op.id, op.parentKey, new LiveList(), isLocal);
|
|
322
333
|
}
|
|
323
334
|
case OpType.CreateRegister: {
|
|
324
335
|
const parent = state.items.get(op.parentId);
|
|
325
336
|
if (parent == null || getItem(op.id) != null) {
|
|
326
337
|
return { modified: false };
|
|
327
338
|
}
|
|
328
|
-
return parent._attachChild(op.id, op.parentKey, new LiveRegister(op.data));
|
|
339
|
+
return parent._attachChild(op.id, op.parentKey, new LiveRegister(op.data), isLocal);
|
|
329
340
|
}
|
|
330
341
|
case OpType.CreateMap: {
|
|
331
342
|
const parent = state.items.get(op.parentId);
|
|
332
343
|
if (parent == null || getItem(op.id) != null) {
|
|
333
344
|
return { modified: false };
|
|
334
345
|
}
|
|
335
|
-
return parent._attachChild(op.id, op.parentKey, new LiveMap());
|
|
346
|
+
return parent._attachChild(op.id, op.parentKey, new LiveMap(), isLocal);
|
|
336
347
|
}
|
|
337
348
|
}
|
|
338
349
|
return { modified: false };
|
|
@@ -525,7 +536,7 @@ See v0.13 release notes for more information.
|
|
|
525
536
|
subMessages.push(message);
|
|
526
537
|
}
|
|
527
538
|
const updates = {
|
|
528
|
-
|
|
539
|
+
storageUpdates: new Map(),
|
|
529
540
|
others: [],
|
|
530
541
|
};
|
|
531
542
|
for (const subMessage of subMessages) {
|
|
@@ -554,15 +565,16 @@ See v0.13 release notes for more information.
|
|
|
554
565
|
break;
|
|
555
566
|
}
|
|
556
567
|
case ServerMessageType.InitialStorageState: {
|
|
557
|
-
|
|
568
|
+
createOrUpdateRootFromMessage(subMessage);
|
|
569
|
+
applyAndSendOfflineOps();
|
|
558
570
|
_getInitialStateResolver === null || _getInitialStateResolver === void 0 ? void 0 : _getInitialStateResolver();
|
|
559
571
|
break;
|
|
560
572
|
}
|
|
561
573
|
case ServerMessageType.UpdateStorage: {
|
|
562
|
-
const applyResult = apply(subMessage.ops);
|
|
563
|
-
|
|
564
|
-
updates.
|
|
565
|
-
}
|
|
574
|
+
const applyResult = apply(subMessage.ops, false);
|
|
575
|
+
applyResult.updates.storageUpdates.forEach((value, key) => {
|
|
576
|
+
updates.storageUpdates.set(key, mergeStorageUpdates(updates.storageUpdates.get(key), value));
|
|
577
|
+
});
|
|
566
578
|
break;
|
|
567
579
|
}
|
|
568
580
|
}
|
|
@@ -629,6 +641,10 @@ See v0.13 release notes for more information.
|
|
|
629
641
|
if (state.connection.state === "connecting") {
|
|
630
642
|
updateConnection(Object.assign(Object.assign({}, state.connection), { state: "open" }));
|
|
631
643
|
state.numberOfRetry = 0;
|
|
644
|
+
state.lastConnectionId = state.connection.id;
|
|
645
|
+
if (state.root) {
|
|
646
|
+
state.buffer.messages.push({ type: ClientMessageType.FetchStorage });
|
|
647
|
+
}
|
|
632
648
|
tryFlushing();
|
|
633
649
|
}
|
|
634
650
|
else {
|
|
@@ -668,11 +684,29 @@ See v0.13 release notes for more information.
|
|
|
668
684
|
clearInterval(state.intervalHandles.heartbeat);
|
|
669
685
|
connect();
|
|
670
686
|
}
|
|
671
|
-
function
|
|
672
|
-
if (state.
|
|
687
|
+
function applyAndSendOfflineOps() {
|
|
688
|
+
if (state.offlineOperations.size === 0) {
|
|
673
689
|
return;
|
|
674
690
|
}
|
|
675
|
-
|
|
691
|
+
const messages = [];
|
|
692
|
+
const ops = Array.from(state.offlineOperations.values());
|
|
693
|
+
const result = apply(ops, true);
|
|
694
|
+
messages.push({
|
|
695
|
+
type: ClientMessageType.UpdateStorage,
|
|
696
|
+
ops: ops,
|
|
697
|
+
});
|
|
698
|
+
notify(result.updates);
|
|
699
|
+
effects.send(messages);
|
|
700
|
+
}
|
|
701
|
+
function tryFlushing() {
|
|
702
|
+
const storageOps = state.buffer.storageOperations;
|
|
703
|
+
if (storageOps.length > 0) {
|
|
704
|
+
storageOps.forEach((op) => {
|
|
705
|
+
state.offlineOperations.set(op.opId, op);
|
|
706
|
+
});
|
|
707
|
+
}
|
|
708
|
+
if (state.socket == null || state.socket.readyState !== WebSocket.OPEN) {
|
|
709
|
+
state.buffer.storageOperations = [];
|
|
676
710
|
return;
|
|
677
711
|
}
|
|
678
712
|
const now = Date.now();
|
|
@@ -792,7 +826,7 @@ See v0.13 release notes for more information.
|
|
|
792
826
|
return;
|
|
793
827
|
}
|
|
794
828
|
state.isHistoryPaused = false;
|
|
795
|
-
const result = apply(historyItem);
|
|
829
|
+
const result = apply(historyItem, true);
|
|
796
830
|
notify(result.updates);
|
|
797
831
|
state.redoStack.push(result.reverse);
|
|
798
832
|
for (const op of historyItem) {
|
|
@@ -811,7 +845,7 @@ See v0.13 release notes for more information.
|
|
|
811
845
|
return;
|
|
812
846
|
}
|
|
813
847
|
state.isHistoryPaused = false;
|
|
814
|
-
const result = apply(historyItem);
|
|
848
|
+
const result = apply(historyItem, true);
|
|
815
849
|
notify(result.updates);
|
|
816
850
|
state.undoStack.push(result.reverse);
|
|
817
851
|
for (const op of historyItem) {
|
|
@@ -834,8 +868,11 @@ See v0.13 release notes for more information.
|
|
|
834
868
|
if (state.batch.reverseOps.length > 0) {
|
|
835
869
|
addToUndoStack(state.batch.reverseOps);
|
|
836
870
|
}
|
|
837
|
-
|
|
838
|
-
|
|
871
|
+
if (state.batch.ops.length > 0) {
|
|
872
|
+
// Only clear the redo stack if something has changed during a batch
|
|
873
|
+
// Clear the redo stack because batch is always called from a local operation
|
|
874
|
+
state.redoStack = [];
|
|
875
|
+
}
|
|
839
876
|
if (state.batch.ops.length > 0) {
|
|
840
877
|
dispatch(state.batch.ops);
|
|
841
878
|
}
|
|
@@ -845,7 +882,7 @@ See v0.13 release notes for more information.
|
|
|
845
882
|
reverseOps: [],
|
|
846
883
|
updates: {
|
|
847
884
|
others: [],
|
|
848
|
-
|
|
885
|
+
storageUpdates: new Map(),
|
|
849
886
|
presence: false,
|
|
850
887
|
},
|
|
851
888
|
};
|
|
@@ -863,6 +900,16 @@ See v0.13 release notes for more information.
|
|
|
863
900
|
}
|
|
864
901
|
state.pausedHistory = [];
|
|
865
902
|
}
|
|
903
|
+
function simulateSocketClose() {
|
|
904
|
+
if (state.socket) {
|
|
905
|
+
state.socket.close();
|
|
906
|
+
}
|
|
907
|
+
}
|
|
908
|
+
function simulateSendCloseEvent(event) {
|
|
909
|
+
if (state.socket) {
|
|
910
|
+
onClose(event);
|
|
911
|
+
}
|
|
912
|
+
}
|
|
866
913
|
return {
|
|
867
914
|
// Internal
|
|
868
915
|
onOpen,
|
|
@@ -871,6 +918,9 @@ See v0.13 release notes for more information.
|
|
|
871
918
|
authenticationSuccess,
|
|
872
919
|
heartbeat,
|
|
873
920
|
onNavigatorOnline,
|
|
921
|
+
// Internal dev tools
|
|
922
|
+
simulateSocketClose,
|
|
923
|
+
simulateSendCloseEvent,
|
|
874
924
|
// onWakeUp,
|
|
875
925
|
onVisibilityChange,
|
|
876
926
|
getUndoStack: () => state.undoStack,
|
|
@@ -902,6 +952,7 @@ See v0.13 release notes for more information.
|
|
|
902
952
|
export function defaultState(me, defaultStorageRoot) {
|
|
903
953
|
return {
|
|
904
954
|
connection: { state: "closed" },
|
|
955
|
+
lastConnectionId: null,
|
|
905
956
|
socket: null,
|
|
906
957
|
listeners: {
|
|
907
958
|
event: [],
|
|
@@ -943,9 +994,14 @@ export function defaultState(me, defaultStorageRoot) {
|
|
|
943
994
|
isBatching: false,
|
|
944
995
|
batch: {
|
|
945
996
|
ops: [],
|
|
946
|
-
updates: {
|
|
997
|
+
updates: {
|
|
998
|
+
storageUpdates: new Map(),
|
|
999
|
+
presence: false,
|
|
1000
|
+
others: [],
|
|
1001
|
+
},
|
|
947
1002
|
reverseOps: [],
|
|
948
1003
|
},
|
|
1004
|
+
offlineOperations: new Map(),
|
|
949
1005
|
};
|
|
950
1006
|
}
|
|
951
1007
|
export function createRoom(name, options) {
|
|
@@ -991,6 +1047,11 @@ export function createRoom(name, options) {
|
|
|
991
1047
|
pause: machine.pauseHistory,
|
|
992
1048
|
resume: machine.resumeHistory,
|
|
993
1049
|
},
|
|
1050
|
+
// @ts-ignore
|
|
1051
|
+
internalDevTools: {
|
|
1052
|
+
closeWebsocket: machine.simulateSocketClose,
|
|
1053
|
+
sendCloseEvent: machine.simulateSendCloseEvent,
|
|
1054
|
+
},
|
|
994
1055
|
};
|
|
995
1056
|
return {
|
|
996
1057
|
connect: machine.connect,
|
package/lib/esm/types.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { AbstractCrdt } from "./AbstractCrdt";
|
|
1
2
|
import type { LiveList } from "./LiveList";
|
|
2
3
|
import type { LiveMap } from "./LiveMap";
|
|
3
4
|
import type { LiveObject } from "./LiveObject";
|
|
@@ -16,17 +17,41 @@ export declare type RoomEventCallbackMap = {
|
|
|
16
17
|
error: ErrorCallback;
|
|
17
18
|
connection: ConnectionCallback;
|
|
18
19
|
};
|
|
20
|
+
export declare type UpdateDelta = {
|
|
21
|
+
type: "update";
|
|
22
|
+
} | {
|
|
23
|
+
type: "delete";
|
|
24
|
+
};
|
|
19
25
|
export declare type LiveMapUpdates<TKey extends string = string, TValue = any> = {
|
|
20
26
|
type: "LiveMap";
|
|
21
27
|
node: LiveMap<TKey, TValue>;
|
|
28
|
+
updates: Record<TKey, UpdateDelta>;
|
|
22
29
|
};
|
|
30
|
+
export declare type LiveObjectUpdateDelta<T> = Partial<{
|
|
31
|
+
[Property in keyof T]: UpdateDelta;
|
|
32
|
+
}>;
|
|
23
33
|
export declare type LiveObjectUpdates<TData = any> = {
|
|
24
34
|
type: "LiveObject";
|
|
25
35
|
node: LiveObject<TData>;
|
|
36
|
+
updates: LiveObjectUpdateDelta<TData>;
|
|
37
|
+
};
|
|
38
|
+
export declare type LiveListUpdateDelta = {
|
|
39
|
+
index: number;
|
|
40
|
+
item: AbstractCrdt;
|
|
41
|
+
type: "insert";
|
|
42
|
+
} | {
|
|
43
|
+
index: number;
|
|
44
|
+
type: "delete";
|
|
45
|
+
} | {
|
|
46
|
+
index: number;
|
|
47
|
+
previousIndex: number;
|
|
48
|
+
item: AbstractCrdt;
|
|
49
|
+
type: "move";
|
|
26
50
|
};
|
|
27
51
|
export declare type LiveListUpdates<TItem = any> = {
|
|
28
52
|
type: "LiveList";
|
|
29
53
|
node: LiveList<TItem>;
|
|
54
|
+
updates: LiveListUpdateDelta[];
|
|
30
55
|
};
|
|
31
56
|
export declare type BroadcastOptions = {
|
|
32
57
|
/**
|
package/lib/esm/utils.d.ts
CHANGED
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
import { AbstractCrdt, Doc } from "./AbstractCrdt";
|
|
2
|
-
import { SerializedCrdtWithId } from "./live";
|
|
2
|
+
import { SerializedCrdtWithId, Op, SerializedCrdt } from "./live";
|
|
3
|
+
import { StorageUpdate } from "./types";
|
|
3
4
|
export declare function remove<T>(array: T[], item: T): void;
|
|
4
5
|
export declare function isSameNodeOrChildOf(node: AbstractCrdt, parent: AbstractCrdt): boolean;
|
|
5
6
|
export declare function deserialize(entry: SerializedCrdtWithId, parentToChildren: Map<string, SerializedCrdtWithId[]>, doc: Doc): AbstractCrdt;
|
|
6
7
|
export declare function isCrdt(obj: any): obj is AbstractCrdt;
|
|
7
8
|
export declare function selfOrRegisterValue(obj: AbstractCrdt): any;
|
|
8
9
|
export declare function selfOrRegister(obj: any): AbstractCrdt;
|
|
10
|
+
export declare function getTreesDiffOperations(currentItems: Map<string, SerializedCrdt>, newItems: Map<string, SerializedCrdt>): Op[];
|
|
11
|
+
export declare function mergeStorageUpdates(first: StorageUpdate | undefined, second: StorageUpdate): StorageUpdate;
|
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,101 @@ 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
|
+
}
|
|
141
|
+
export function mergeStorageUpdates(first, second) {
|
|
142
|
+
if (!first) {
|
|
143
|
+
return second;
|
|
144
|
+
}
|
|
145
|
+
if (second.type === "LiveObject") {
|
|
146
|
+
const updates = first.updates;
|
|
147
|
+
for (const [key, value] of Object.entries(second.updates)) {
|
|
148
|
+
updates[key] = value;
|
|
149
|
+
}
|
|
150
|
+
return Object.assign(Object.assign({}, second), { updates: updates });
|
|
151
|
+
}
|
|
152
|
+
else if (second.type === "LiveMap") {
|
|
153
|
+
const updates = first.updates;
|
|
154
|
+
for (const [key, value] of Object.entries(second.updates)) {
|
|
155
|
+
updates[key] = value;
|
|
156
|
+
}
|
|
157
|
+
return Object.assign(Object.assign({}, second), { updates: updates });
|
|
158
|
+
}
|
|
159
|
+
else if (second.type === "LiveList") {
|
|
160
|
+
const updates = first.updates;
|
|
161
|
+
return Object.assign(Object.assign({}, second), { updates: updates.concat(second.updates) });
|
|
162
|
+
}
|
|
163
|
+
return second;
|
|
164
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@liveblocks/client",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.15.0-alpha.2",
|
|
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
|
+
}
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
import { LiveList } from "../LiveList";
|
|
2
|
-
import { LiveObject } from "../LiveObject";
|
|
3
|
-
import { StorageUpdate } from "../types";
|
|
4
|
-
export declare function liveObjectToJson(liveObject: LiveObject<any>): any;
|
|
5
|
-
export declare function patchLiveList<T>(liveList: LiveList<T>, prev: Array<T>, next: Array<T>): void;
|
|
6
|
-
export declare function patchLiveObject<T extends Record<string, any>>(root: LiveObject<T>, prev: T, next: T): void;
|
|
7
|
-
export declare function patchImmutableObject<T>(state: T, updates: StorageUpdate[]): T;
|