@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/cjs/room.js
CHANGED
|
@@ -132,18 +132,23 @@ function makeStateMachine(state, context, mockedEffects) {
|
|
|
132
132
|
};
|
|
133
133
|
return genericSubscribe(cb);
|
|
134
134
|
}
|
|
135
|
-
function
|
|
136
|
-
|
|
135
|
+
function createOrUpdateRootFromMessage(message) {
|
|
136
|
+
if (message.items.length === 0) {
|
|
137
|
+
throw new Error("Internal error: cannot load storage without items");
|
|
138
|
+
}
|
|
139
|
+
if (state.root) {
|
|
140
|
+
updateRoot(message.items);
|
|
141
|
+
}
|
|
142
|
+
else {
|
|
143
|
+
state.root = load(message.items);
|
|
144
|
+
}
|
|
137
145
|
for (const key in state.defaultStorageRoot) {
|
|
138
146
|
if (state.root.get(key) == null) {
|
|
139
147
|
state.root.set(key, state.defaultStorageRoot[key]);
|
|
140
148
|
}
|
|
141
149
|
}
|
|
142
150
|
}
|
|
143
|
-
function
|
|
144
|
-
if (items.length === 0) {
|
|
145
|
-
throw new Error("Internal error: cannot load storage without items");
|
|
146
|
-
}
|
|
151
|
+
function buildRootAndParentToChildren(items) {
|
|
147
152
|
const parentToChildren = new Map();
|
|
148
153
|
let root = null;
|
|
149
154
|
for (const tuple of items) {
|
|
@@ -164,6 +169,23 @@ function makeStateMachine(state, context, mockedEffects) {
|
|
|
164
169
|
if (root == null) {
|
|
165
170
|
throw new Error("Root can't be null");
|
|
166
171
|
}
|
|
172
|
+
return [root, parentToChildren];
|
|
173
|
+
}
|
|
174
|
+
function updateRoot(items) {
|
|
175
|
+
if (!state.root) {
|
|
176
|
+
return;
|
|
177
|
+
}
|
|
178
|
+
const currentItems = new Map();
|
|
179
|
+
state.items.forEach((liveCrdt, id) => {
|
|
180
|
+
currentItems.set(id, liveCrdt._toSerializedCrdt());
|
|
181
|
+
});
|
|
182
|
+
// Get operations that represent the diff between 2 states.
|
|
183
|
+
const ops = (0, utils_1.getTreesDiffOperations)(currentItems, new Map(items));
|
|
184
|
+
const result = apply(ops, false);
|
|
185
|
+
notify(result.updates);
|
|
186
|
+
}
|
|
187
|
+
function load(items) {
|
|
188
|
+
const [root, parentToChildren] = buildRootAndParentToChildren(items);
|
|
167
189
|
return LiveObject_1.LiveObject._deserialize(root, parentToChildren, {
|
|
168
190
|
addItem,
|
|
169
191
|
deleteItem,
|
|
@@ -193,22 +215,22 @@ function makeStateMachine(state, context, mockedEffects) {
|
|
|
193
215
|
state.undoStack.push(historyItem);
|
|
194
216
|
}
|
|
195
217
|
}
|
|
196
|
-
function storageDispatch(ops, reverse,
|
|
218
|
+
function storageDispatch(ops, reverse, storageUpdates) {
|
|
197
219
|
if (state.isBatching) {
|
|
198
220
|
state.batch.ops.push(...ops);
|
|
199
|
-
|
|
200
|
-
state.batch.updates.
|
|
201
|
-
}
|
|
221
|
+
storageUpdates.forEach((value, key) => {
|
|
222
|
+
state.batch.updates.storageUpdates.set(key, (0, utils_1.mergeStorageUpdates)(state.batch.updates.storageUpdates.get(key), value));
|
|
223
|
+
});
|
|
202
224
|
state.batch.reverseOps.push(...reverse);
|
|
203
225
|
}
|
|
204
226
|
else {
|
|
205
227
|
addToUndoStack(reverse);
|
|
206
228
|
state.redoStack = [];
|
|
207
229
|
dispatch(ops);
|
|
208
|
-
notify({
|
|
230
|
+
notify({ storageUpdates: storageUpdates });
|
|
209
231
|
}
|
|
210
232
|
}
|
|
211
|
-
function notify({
|
|
233
|
+
function notify({ storageUpdates = new Map(), presence = false, others = [], }) {
|
|
212
234
|
if (others.length > 0) {
|
|
213
235
|
state.others = makeOthers(state.users);
|
|
214
236
|
for (const event of others) {
|
|
@@ -222,28 +244,9 @@ function makeStateMachine(state, context, mockedEffects) {
|
|
|
222
244
|
listener(state.me);
|
|
223
245
|
}
|
|
224
246
|
}
|
|
225
|
-
if (
|
|
247
|
+
if (storageUpdates.size > 0) {
|
|
226
248
|
for (const subscriber of state.listeners.storage) {
|
|
227
|
-
subscriber(Array.from(
|
|
228
|
-
if (m instanceof LiveObject_1.LiveObject) {
|
|
229
|
-
return {
|
|
230
|
-
type: "LiveObject",
|
|
231
|
-
node: m,
|
|
232
|
-
};
|
|
233
|
-
}
|
|
234
|
-
else if (m instanceof LiveList_1.LiveList) {
|
|
235
|
-
return {
|
|
236
|
-
type: "LiveList",
|
|
237
|
-
node: m,
|
|
238
|
-
};
|
|
239
|
-
}
|
|
240
|
-
else {
|
|
241
|
-
return {
|
|
242
|
-
type: "LiveMap",
|
|
243
|
-
node: m,
|
|
244
|
-
};
|
|
245
|
-
}
|
|
246
|
-
}));
|
|
249
|
+
subscriber(Array.from(storageUpdates.values()));
|
|
247
250
|
}
|
|
248
251
|
}
|
|
249
252
|
}
|
|
@@ -252,7 +255,10 @@ function makeStateMachine(state, context, mockedEffects) {
|
|
|
252
255
|
state.connection.state === "connecting") {
|
|
253
256
|
return state.connection.id;
|
|
254
257
|
}
|
|
255
|
-
|
|
258
|
+
else if (state.lastConnectionId !== null) {
|
|
259
|
+
return state.lastConnectionId;
|
|
260
|
+
}
|
|
261
|
+
throw new Error("Internal. Tried to get connection id but connection was never open");
|
|
256
262
|
}
|
|
257
263
|
function generateId() {
|
|
258
264
|
return `${getConnectionId()}:${state.clock++}`;
|
|
@@ -260,10 +266,13 @@ function makeStateMachine(state, context, mockedEffects) {
|
|
|
260
266
|
function generateOpId() {
|
|
261
267
|
return `${getConnectionId()}:${state.opClock++}`;
|
|
262
268
|
}
|
|
263
|
-
function apply(item) {
|
|
269
|
+
function apply(item, isLocal) {
|
|
264
270
|
const result = {
|
|
265
271
|
reverse: [],
|
|
266
|
-
updates: {
|
|
272
|
+
updates: {
|
|
273
|
+
storageUpdates: new Map(),
|
|
274
|
+
presence: false,
|
|
275
|
+
},
|
|
267
276
|
};
|
|
268
277
|
for (const op of item) {
|
|
269
278
|
if (op.type === "presence") {
|
|
@@ -287,16 +296,23 @@ function makeStateMachine(state, context, mockedEffects) {
|
|
|
287
296
|
result.updates.presence = true;
|
|
288
297
|
}
|
|
289
298
|
else {
|
|
290
|
-
|
|
299
|
+
// Ops applied after undo/redo don't have an opId.
|
|
300
|
+
if (isLocal && !op.opId) {
|
|
301
|
+
op.opId = generateOpId();
|
|
302
|
+
}
|
|
303
|
+
const applyOpResult = applyOp(op, isLocal);
|
|
291
304
|
if (applyOpResult.modified) {
|
|
292
|
-
result.updates.
|
|
305
|
+
result.updates.storageUpdates.set(applyOpResult.modified.node._id, (0, utils_1.mergeStorageUpdates)(result.updates.storageUpdates.get(applyOpResult.modified.node._id), applyOpResult.modified));
|
|
293
306
|
result.reverse.unshift(...applyOpResult.reverse);
|
|
294
307
|
}
|
|
295
308
|
}
|
|
296
309
|
}
|
|
297
310
|
return result;
|
|
298
311
|
}
|
|
299
|
-
function applyOp(op) {
|
|
312
|
+
function applyOp(op, isLocal) {
|
|
313
|
+
if (op.opId) {
|
|
314
|
+
state.offlineOperations.delete(op.opId);
|
|
315
|
+
}
|
|
300
316
|
switch (op.type) {
|
|
301
317
|
case live_1.OpType.DeleteObjectKey:
|
|
302
318
|
case live_1.OpType.UpdateObject:
|
|
@@ -305,7 +321,7 @@ function makeStateMachine(state, context, mockedEffects) {
|
|
|
305
321
|
if (item == null) {
|
|
306
322
|
return { modified: false };
|
|
307
323
|
}
|
|
308
|
-
return item._apply(op);
|
|
324
|
+
return item._apply(op, isLocal);
|
|
309
325
|
}
|
|
310
326
|
case live_1.OpType.SetParentKey: {
|
|
311
327
|
const item = state.items.get(op.id);
|
|
@@ -314,17 +330,12 @@ function makeStateMachine(state, context, mockedEffects) {
|
|
|
314
330
|
}
|
|
315
331
|
if (item._parent instanceof LiveList_1.LiveList) {
|
|
316
332
|
const previousKey = item._parentKey;
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
parentKey: previousKey,
|
|
324
|
-
},
|
|
325
|
-
],
|
|
326
|
-
modified: item._parent,
|
|
327
|
-
};
|
|
333
|
+
if (previousKey === op.parentKey) {
|
|
334
|
+
return { modified: false };
|
|
335
|
+
}
|
|
336
|
+
else {
|
|
337
|
+
return item._parent._setChildKey(op.parentKey, item, previousKey);
|
|
338
|
+
}
|
|
328
339
|
}
|
|
329
340
|
return { modified: false };
|
|
330
341
|
}
|
|
@@ -333,28 +344,28 @@ function makeStateMachine(state, context, mockedEffects) {
|
|
|
333
344
|
if (parent == null || getItem(op.id) != null) {
|
|
334
345
|
return { modified: false };
|
|
335
346
|
}
|
|
336
|
-
return parent._attachChild(op.id, op.parentKey, new LiveObject_1.LiveObject(op.data));
|
|
347
|
+
return parent._attachChild(op.id, op.parentKey, new LiveObject_1.LiveObject(op.data), isLocal);
|
|
337
348
|
}
|
|
338
349
|
case live_1.OpType.CreateList: {
|
|
339
350
|
const parent = state.items.get(op.parentId);
|
|
340
351
|
if (parent == null || getItem(op.id) != null) {
|
|
341
352
|
return { modified: false };
|
|
342
353
|
}
|
|
343
|
-
return parent._attachChild(op.id, op.parentKey, new LiveList_1.LiveList());
|
|
354
|
+
return parent._attachChild(op.id, op.parentKey, new LiveList_1.LiveList(), isLocal);
|
|
344
355
|
}
|
|
345
356
|
case live_1.OpType.CreateRegister: {
|
|
346
357
|
const parent = state.items.get(op.parentId);
|
|
347
358
|
if (parent == null || getItem(op.id) != null) {
|
|
348
359
|
return { modified: false };
|
|
349
360
|
}
|
|
350
|
-
return parent._attachChild(op.id, op.parentKey, new LiveRegister_1.LiveRegister(op.data));
|
|
361
|
+
return parent._attachChild(op.id, op.parentKey, new LiveRegister_1.LiveRegister(op.data), isLocal);
|
|
351
362
|
}
|
|
352
363
|
case live_1.OpType.CreateMap: {
|
|
353
364
|
const parent = state.items.get(op.parentId);
|
|
354
365
|
if (parent == null || getItem(op.id) != null) {
|
|
355
366
|
return { modified: false };
|
|
356
367
|
}
|
|
357
|
-
return parent._attachChild(op.id, op.parentKey, new LiveMap_1.LiveMap());
|
|
368
|
+
return parent._attachChild(op.id, op.parentKey, new LiveMap_1.LiveMap(), isLocal);
|
|
358
369
|
}
|
|
359
370
|
}
|
|
360
371
|
return { modified: false };
|
|
@@ -547,7 +558,7 @@ See v0.13 release notes for more information.
|
|
|
547
558
|
subMessages.push(message);
|
|
548
559
|
}
|
|
549
560
|
const updates = {
|
|
550
|
-
|
|
561
|
+
storageUpdates: new Map(),
|
|
551
562
|
others: [],
|
|
552
563
|
};
|
|
553
564
|
for (const subMessage of subMessages) {
|
|
@@ -576,15 +587,16 @@ See v0.13 release notes for more information.
|
|
|
576
587
|
break;
|
|
577
588
|
}
|
|
578
589
|
case live_1.ServerMessageType.InitialStorageState: {
|
|
579
|
-
|
|
590
|
+
createOrUpdateRootFromMessage(subMessage);
|
|
591
|
+
applyAndSendOfflineOps();
|
|
580
592
|
_getInitialStateResolver === null || _getInitialStateResolver === void 0 ? void 0 : _getInitialStateResolver();
|
|
581
593
|
break;
|
|
582
594
|
}
|
|
583
595
|
case live_1.ServerMessageType.UpdateStorage: {
|
|
584
|
-
const applyResult = apply(subMessage.ops);
|
|
585
|
-
|
|
586
|
-
updates.
|
|
587
|
-
}
|
|
596
|
+
const applyResult = apply(subMessage.ops, false);
|
|
597
|
+
applyResult.updates.storageUpdates.forEach((value, key) => {
|
|
598
|
+
updates.storageUpdates.set(key, (0, utils_1.mergeStorageUpdates)(updates.storageUpdates.get(key), value));
|
|
599
|
+
});
|
|
588
600
|
break;
|
|
589
601
|
}
|
|
590
602
|
}
|
|
@@ -651,6 +663,10 @@ See v0.13 release notes for more information.
|
|
|
651
663
|
if (state.connection.state === "connecting") {
|
|
652
664
|
updateConnection(Object.assign(Object.assign({}, state.connection), { state: "open" }));
|
|
653
665
|
state.numberOfRetry = 0;
|
|
666
|
+
state.lastConnectionId = state.connection.id;
|
|
667
|
+
if (state.root) {
|
|
668
|
+
state.buffer.messages.push({ type: live_1.ClientMessageType.FetchStorage });
|
|
669
|
+
}
|
|
654
670
|
tryFlushing();
|
|
655
671
|
}
|
|
656
672
|
else {
|
|
@@ -690,11 +706,29 @@ See v0.13 release notes for more information.
|
|
|
690
706
|
clearInterval(state.intervalHandles.heartbeat);
|
|
691
707
|
connect();
|
|
692
708
|
}
|
|
693
|
-
function
|
|
694
|
-
if (state.
|
|
709
|
+
function applyAndSendOfflineOps() {
|
|
710
|
+
if (state.offlineOperations.size === 0) {
|
|
695
711
|
return;
|
|
696
712
|
}
|
|
697
|
-
|
|
713
|
+
const messages = [];
|
|
714
|
+
const ops = Array.from(state.offlineOperations.values());
|
|
715
|
+
const result = apply(ops, true);
|
|
716
|
+
messages.push({
|
|
717
|
+
type: live_1.ClientMessageType.UpdateStorage,
|
|
718
|
+
ops: ops,
|
|
719
|
+
});
|
|
720
|
+
notify(result.updates);
|
|
721
|
+
effects.send(messages);
|
|
722
|
+
}
|
|
723
|
+
function tryFlushing() {
|
|
724
|
+
const storageOps = state.buffer.storageOperations;
|
|
725
|
+
if (storageOps.length > 0) {
|
|
726
|
+
storageOps.forEach((op) => {
|
|
727
|
+
state.offlineOperations.set(op.opId, op);
|
|
728
|
+
});
|
|
729
|
+
}
|
|
730
|
+
if (state.socket == null || state.socket.readyState !== WebSocket.OPEN) {
|
|
731
|
+
state.buffer.storageOperations = [];
|
|
698
732
|
return;
|
|
699
733
|
}
|
|
700
734
|
const now = Date.now();
|
|
@@ -814,7 +848,7 @@ See v0.13 release notes for more information.
|
|
|
814
848
|
return;
|
|
815
849
|
}
|
|
816
850
|
state.isHistoryPaused = false;
|
|
817
|
-
const result = apply(historyItem);
|
|
851
|
+
const result = apply(historyItem, true);
|
|
818
852
|
notify(result.updates);
|
|
819
853
|
state.redoStack.push(result.reverse);
|
|
820
854
|
for (const op of historyItem) {
|
|
@@ -833,7 +867,7 @@ See v0.13 release notes for more information.
|
|
|
833
867
|
return;
|
|
834
868
|
}
|
|
835
869
|
state.isHistoryPaused = false;
|
|
836
|
-
const result = apply(historyItem);
|
|
870
|
+
const result = apply(historyItem, true);
|
|
837
871
|
notify(result.updates);
|
|
838
872
|
state.undoStack.push(result.reverse);
|
|
839
873
|
for (const op of historyItem) {
|
|
@@ -856,8 +890,11 @@ See v0.13 release notes for more information.
|
|
|
856
890
|
if (state.batch.reverseOps.length > 0) {
|
|
857
891
|
addToUndoStack(state.batch.reverseOps);
|
|
858
892
|
}
|
|
859
|
-
|
|
860
|
-
|
|
893
|
+
if (state.batch.ops.length > 0) {
|
|
894
|
+
// Only clear the redo stack if something has changed during a batch
|
|
895
|
+
// Clear the redo stack because batch is always called from a local operation
|
|
896
|
+
state.redoStack = [];
|
|
897
|
+
}
|
|
861
898
|
if (state.batch.ops.length > 0) {
|
|
862
899
|
dispatch(state.batch.ops);
|
|
863
900
|
}
|
|
@@ -867,7 +904,7 @@ See v0.13 release notes for more information.
|
|
|
867
904
|
reverseOps: [],
|
|
868
905
|
updates: {
|
|
869
906
|
others: [],
|
|
870
|
-
|
|
907
|
+
storageUpdates: new Map(),
|
|
871
908
|
presence: false,
|
|
872
909
|
},
|
|
873
910
|
};
|
|
@@ -885,6 +922,16 @@ See v0.13 release notes for more information.
|
|
|
885
922
|
}
|
|
886
923
|
state.pausedHistory = [];
|
|
887
924
|
}
|
|
925
|
+
function simulateSocketClose() {
|
|
926
|
+
if (state.socket) {
|
|
927
|
+
state.socket.close();
|
|
928
|
+
}
|
|
929
|
+
}
|
|
930
|
+
function simulateSendCloseEvent(event) {
|
|
931
|
+
if (state.socket) {
|
|
932
|
+
onClose(event);
|
|
933
|
+
}
|
|
934
|
+
}
|
|
888
935
|
return {
|
|
889
936
|
// Internal
|
|
890
937
|
onOpen,
|
|
@@ -893,6 +940,9 @@ See v0.13 release notes for more information.
|
|
|
893
940
|
authenticationSuccess,
|
|
894
941
|
heartbeat,
|
|
895
942
|
onNavigatorOnline,
|
|
943
|
+
// Internal dev tools
|
|
944
|
+
simulateSocketClose,
|
|
945
|
+
simulateSendCloseEvent,
|
|
896
946
|
// onWakeUp,
|
|
897
947
|
onVisibilityChange,
|
|
898
948
|
getUndoStack: () => state.undoStack,
|
|
@@ -925,6 +975,7 @@ exports.makeStateMachine = makeStateMachine;
|
|
|
925
975
|
function defaultState(me, defaultStorageRoot) {
|
|
926
976
|
return {
|
|
927
977
|
connection: { state: "closed" },
|
|
978
|
+
lastConnectionId: null,
|
|
928
979
|
socket: null,
|
|
929
980
|
listeners: {
|
|
930
981
|
event: [],
|
|
@@ -966,9 +1017,14 @@ function defaultState(me, defaultStorageRoot) {
|
|
|
966
1017
|
isBatching: false,
|
|
967
1018
|
batch: {
|
|
968
1019
|
ops: [],
|
|
969
|
-
updates: {
|
|
1020
|
+
updates: {
|
|
1021
|
+
storageUpdates: new Map(),
|
|
1022
|
+
presence: false,
|
|
1023
|
+
others: [],
|
|
1024
|
+
},
|
|
970
1025
|
reverseOps: [],
|
|
971
1026
|
},
|
|
1027
|
+
offlineOperations: new Map(),
|
|
972
1028
|
};
|
|
973
1029
|
}
|
|
974
1030
|
exports.defaultState = defaultState;
|
|
@@ -1015,6 +1071,11 @@ function createRoom(name, options) {
|
|
|
1015
1071
|
pause: machine.pauseHistory,
|
|
1016
1072
|
resume: machine.resumeHistory,
|
|
1017
1073
|
},
|
|
1074
|
+
// @ts-ignore
|
|
1075
|
+
internalDevTools: {
|
|
1076
|
+
closeWebsocket: machine.simulateSocketClose,
|
|
1077
|
+
sendCloseEvent: machine.simulateSendCloseEvent,
|
|
1078
|
+
},
|
|
1018
1079
|
};
|
|
1019
1080
|
return {
|
|
1020
1081
|
connect: machine.connect,
|
package/lib/cjs/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/cjs/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/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.mergeStorageUpdates = 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,103 @@ 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;
|
|
151
|
+
function mergeStorageUpdates(first, second) {
|
|
152
|
+
if (!first) {
|
|
153
|
+
return second;
|
|
154
|
+
}
|
|
155
|
+
if (second.type === "LiveObject") {
|
|
156
|
+
const updates = first.updates;
|
|
157
|
+
for (const [key, value] of Object.entries(second.updates)) {
|
|
158
|
+
updates[key] = value;
|
|
159
|
+
}
|
|
160
|
+
return Object.assign(Object.assign({}, second), { updates: updates });
|
|
161
|
+
}
|
|
162
|
+
else if (second.type === "LiveMap") {
|
|
163
|
+
const updates = first.updates;
|
|
164
|
+
for (const [key, value] of Object.entries(second.updates)) {
|
|
165
|
+
updates[key] = value;
|
|
166
|
+
}
|
|
167
|
+
return Object.assign(Object.assign({}, second), { updates: updates });
|
|
168
|
+
}
|
|
169
|
+
else if (second.type === "LiveList") {
|
|
170
|
+
const updates = first.updates;
|
|
171
|
+
return Object.assign(Object.assign({}, second), { updates: updates.concat(second.updates) });
|
|
172
|
+
}
|
|
173
|
+
return second;
|
|
174
|
+
}
|
|
175
|
+
exports.mergeStorageUpdates = mergeStorageUpdates;
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import { Op } from "./live";
|
|
1
|
+
import { Op, SerializedCrdt } from "./live";
|
|
2
|
+
import { StorageUpdate } from "./types";
|
|
2
3
|
export declare type ApplyResult = {
|
|
3
4
|
reverse: Op[];
|
|
4
|
-
modified:
|
|
5
|
+
modified: StorageUpdate;
|
|
5
6
|
} | {
|
|
6
7
|
modified: false;
|
|
7
8
|
};
|
|
@@ -10,7 +11,7 @@ export interface Doc {
|
|
|
10
11
|
generateOpId: () => string;
|
|
11
12
|
addItem: (id: string, item: AbstractCrdt) => void;
|
|
12
13
|
deleteItem: (id: string) => void;
|
|
13
|
-
dispatch: (ops: Op[], reverseOps: Op[],
|
|
14
|
+
dispatch: (ops: Op[], reverseOps: Op[], storageUpdates: Map<string, StorageUpdate>) => void;
|
|
14
15
|
}
|
|
15
16
|
export declare abstract class AbstractCrdt {
|
|
16
17
|
#private;
|
|
@@ -33,7 +34,7 @@ export declare abstract class AbstractCrdt {
|
|
|
33
34
|
/**
|
|
34
35
|
* INTERNAL
|
|
35
36
|
*/
|
|
36
|
-
_apply(op: Op): ApplyResult;
|
|
37
|
+
_apply(op: Op, isLocal: boolean): ApplyResult;
|
|
37
38
|
/**
|
|
38
39
|
* INTERNAL
|
|
39
40
|
*/
|
|
@@ -45,7 +46,7 @@ export declare abstract class AbstractCrdt {
|
|
|
45
46
|
/**
|
|
46
47
|
* INTERNAL
|
|
47
48
|
*/
|
|
48
|
-
abstract _attachChild(id: string, key: string, crdt: AbstractCrdt): ApplyResult;
|
|
49
|
+
abstract _attachChild(id: string, key: string, crdt: AbstractCrdt, isLocal: boolean): ApplyResult;
|
|
49
50
|
/**
|
|
50
51
|
* INTERNAL
|
|
51
52
|
*/
|
|
@@ -53,9 +54,14 @@ export declare abstract class AbstractCrdt {
|
|
|
53
54
|
/**
|
|
54
55
|
* INTERNAL
|
|
55
56
|
*/
|
|
56
|
-
abstract _detachChild(crdt: AbstractCrdt):
|
|
57
|
+
abstract _detachChild(crdt: AbstractCrdt): ApplyResult;
|
|
57
58
|
/**
|
|
58
59
|
* INTERNAL
|
|
59
60
|
*/
|
|
60
|
-
abstract _serialize(parentId: string, parentKey: string): Op[];
|
|
61
|
+
abstract _serialize(parentId: string, parentKey: string, doc?: Doc): Op[];
|
|
62
|
+
/**
|
|
63
|
+
* INTERNAL
|
|
64
|
+
*/
|
|
65
|
+
abstract _toSerializedCrdt(): SerializedCrdt;
|
|
66
|
+
abstract _getType(): string;
|
|
61
67
|
}
|
package/lib/esm/AbstractCrdt.js
CHANGED
|
@@ -45,14 +45,11 @@ 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
|
-
|
|
53
|
-
const reverse = this._serialize(this._parent._id, this._parentKey);
|
|
54
|
-
this._parent._detachChild(this);
|
|
55
|
-
return { modified: parent, reverse };
|
|
52
|
+
return this._parent._detachChild(this);
|
|
56
53
|
}
|
|
57
54
|
return { modified: false };
|
|
58
55
|
}
|