@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/cjs/room.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Others, Presence, ClientOptions, Room, MyPresenceCallback, OthersEventCallback, AuthEndpoint, EventCallback, User, Connection, ErrorCallback, AuthenticationToken, ConnectionCallback, StorageCallback, StorageUpdate } from "./types";
|
|
1
|
+
import { Others, Presence, ClientOptions, Room, MyPresenceCallback, OthersEventCallback, AuthEndpoint, EventCallback, User, Connection, ErrorCallback, AuthenticationToken, ConnectionCallback, StorageCallback, StorageUpdate, BroadcastOptions } from "./types";
|
|
2
2
|
import { ClientMessage, Op } from "./live";
|
|
3
3
|
import { LiveMap } from "./LiveMap";
|
|
4
4
|
import { LiveObject } from "./LiveObject";
|
|
@@ -11,6 +11,7 @@ declare type HistoryItem = Array<Op | {
|
|
|
11
11
|
declare type IdFactory = () => string;
|
|
12
12
|
export declare type State = {
|
|
13
13
|
connection: Connection;
|
|
14
|
+
lastConnectionId: number | null;
|
|
14
15
|
socket: WebSocket | null;
|
|
15
16
|
lastFlushTime: number;
|
|
16
17
|
buffer: {
|
|
@@ -62,6 +63,7 @@ export declare type State = {
|
|
|
62
63
|
nodes: Set<AbstractCrdt>;
|
|
63
64
|
};
|
|
64
65
|
};
|
|
66
|
+
offlineOperations: Map<string, Op>;
|
|
65
67
|
};
|
|
66
68
|
export declare type Effects = {
|
|
67
69
|
authenticate(): void;
|
|
@@ -89,6 +91,12 @@ export declare function makeStateMachine(state: State, context: Context, mockedE
|
|
|
89
91
|
authenticationSuccess: (token: AuthenticationToken, socket: WebSocket) => void;
|
|
90
92
|
heartbeat: () => void;
|
|
91
93
|
onNavigatorOnline: () => void;
|
|
94
|
+
simulateSocketClose: () => void;
|
|
95
|
+
simulateSendCloseEvent: (event: {
|
|
96
|
+
code: number;
|
|
97
|
+
wasClean: boolean;
|
|
98
|
+
reason: any;
|
|
99
|
+
}) => void;
|
|
92
100
|
onVisibilityChange: (visibilityState: VisibilityState) => void;
|
|
93
101
|
getUndoStack: () => HistoryItem[];
|
|
94
102
|
getItemsCount: () => number;
|
|
@@ -118,7 +126,7 @@ export declare function makeStateMachine(state: State, context: Context, mockedE
|
|
|
118
126
|
updatePresence: <T_4 extends Presence>(overrides: Partial<T_4>, options?: {
|
|
119
127
|
addToHistory: boolean;
|
|
120
128
|
} | undefined) => void;
|
|
121
|
-
broadcastEvent: (event: any) => void;
|
|
129
|
+
broadcastEvent: (event: any, options?: BroadcastOptions) => void;
|
|
122
130
|
batch: (callback: () => void) => void;
|
|
123
131
|
undo: () => void;
|
|
124
132
|
redo: () => void;
|
package/lib/cjs/room.js
CHANGED
|
@@ -58,6 +58,9 @@ function makeOthers(presenceMap) {
|
|
|
58
58
|
get count() {
|
|
59
59
|
return array.length;
|
|
60
60
|
},
|
|
61
|
+
[Symbol.iterator]() {
|
|
62
|
+
return array[Symbol.iterator]();
|
|
63
|
+
},
|
|
61
64
|
map(callback) {
|
|
62
65
|
return array.map(callback);
|
|
63
66
|
},
|
|
@@ -129,18 +132,23 @@ function makeStateMachine(state, context, mockedEffects) {
|
|
|
129
132
|
};
|
|
130
133
|
return genericSubscribe(cb);
|
|
131
134
|
}
|
|
132
|
-
function
|
|
133
|
-
|
|
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
|
+
}
|
|
134
145
|
for (const key in state.defaultStorageRoot) {
|
|
135
146
|
if (state.root.get(key) == null) {
|
|
136
147
|
state.root.set(key, state.defaultStorageRoot[key]);
|
|
137
148
|
}
|
|
138
149
|
}
|
|
139
150
|
}
|
|
140
|
-
function
|
|
141
|
-
if (items.length === 0) {
|
|
142
|
-
throw new Error("Internal error: cannot load storage without items");
|
|
143
|
-
}
|
|
151
|
+
function buildRootAndParentToChildren(items) {
|
|
144
152
|
const parentToChildren = new Map();
|
|
145
153
|
let root = null;
|
|
146
154
|
for (const tuple of items) {
|
|
@@ -161,6 +169,23 @@ function makeStateMachine(state, context, mockedEffects) {
|
|
|
161
169
|
if (root == null) {
|
|
162
170
|
throw new Error("Root can't be null");
|
|
163
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);
|
|
164
189
|
return LiveObject_1.LiveObject._deserialize(root, parentToChildren, {
|
|
165
190
|
addItem,
|
|
166
191
|
deleteItem,
|
|
@@ -249,7 +274,10 @@ function makeStateMachine(state, context, mockedEffects) {
|
|
|
249
274
|
state.connection.state === "connecting") {
|
|
250
275
|
return state.connection.id;
|
|
251
276
|
}
|
|
252
|
-
|
|
277
|
+
else if (state.lastConnectionId !== null) {
|
|
278
|
+
return state.lastConnectionId;
|
|
279
|
+
}
|
|
280
|
+
throw new Error("Internal. Tried to get connection id but connection was never open");
|
|
253
281
|
}
|
|
254
282
|
function generateId() {
|
|
255
283
|
return `${getConnectionId()}:${state.clock++}`;
|
|
@@ -257,7 +285,7 @@ function makeStateMachine(state, context, mockedEffects) {
|
|
|
257
285
|
function generateOpId() {
|
|
258
286
|
return `${getConnectionId()}:${state.opClock++}`;
|
|
259
287
|
}
|
|
260
|
-
function apply(item) {
|
|
288
|
+
function apply(item, isLocal) {
|
|
261
289
|
const result = {
|
|
262
290
|
reverse: [],
|
|
263
291
|
updates: { nodes: new Set(), presence: false },
|
|
@@ -284,7 +312,11 @@ function makeStateMachine(state, context, mockedEffects) {
|
|
|
284
312
|
result.updates.presence = true;
|
|
285
313
|
}
|
|
286
314
|
else {
|
|
287
|
-
|
|
315
|
+
// Ops applied after undo/redo don't have an opId.
|
|
316
|
+
if (isLocal && !op.opId) {
|
|
317
|
+
op.opId = generateOpId();
|
|
318
|
+
}
|
|
319
|
+
const applyOpResult = applyOp(op, isLocal);
|
|
288
320
|
if (applyOpResult.modified) {
|
|
289
321
|
result.updates.nodes.add(applyOpResult.modified);
|
|
290
322
|
result.reverse.unshift(...applyOpResult.reverse);
|
|
@@ -293,7 +325,10 @@ function makeStateMachine(state, context, mockedEffects) {
|
|
|
293
325
|
}
|
|
294
326
|
return result;
|
|
295
327
|
}
|
|
296
|
-
function applyOp(op) {
|
|
328
|
+
function applyOp(op, isLocal) {
|
|
329
|
+
if (op.opId) {
|
|
330
|
+
state.offlineOperations.delete(op.opId);
|
|
331
|
+
}
|
|
297
332
|
switch (op.type) {
|
|
298
333
|
case live_1.OpType.DeleteObjectKey:
|
|
299
334
|
case live_1.OpType.UpdateObject:
|
|
@@ -302,7 +337,7 @@ function makeStateMachine(state, context, mockedEffects) {
|
|
|
302
337
|
if (item == null) {
|
|
303
338
|
return { modified: false };
|
|
304
339
|
}
|
|
305
|
-
return item._apply(op);
|
|
340
|
+
return item._apply(op, isLocal);
|
|
306
341
|
}
|
|
307
342
|
case live_1.OpType.SetParentKey: {
|
|
308
343
|
const item = state.items.get(op.id);
|
|
@@ -330,28 +365,28 @@ function makeStateMachine(state, context, mockedEffects) {
|
|
|
330
365
|
if (parent == null || getItem(op.id) != null) {
|
|
331
366
|
return { modified: false };
|
|
332
367
|
}
|
|
333
|
-
return parent._attachChild(op.id, op.parentKey, new LiveObject_1.LiveObject(op.data));
|
|
368
|
+
return parent._attachChild(op.id, op.parentKey, new LiveObject_1.LiveObject(op.data), isLocal);
|
|
334
369
|
}
|
|
335
370
|
case live_1.OpType.CreateList: {
|
|
336
371
|
const parent = state.items.get(op.parentId);
|
|
337
372
|
if (parent == null || getItem(op.id) != null) {
|
|
338
373
|
return { modified: false };
|
|
339
374
|
}
|
|
340
|
-
return parent._attachChild(op.id, op.parentKey, new LiveList_1.LiveList());
|
|
375
|
+
return parent._attachChild(op.id, op.parentKey, new LiveList_1.LiveList(), isLocal);
|
|
341
376
|
}
|
|
342
377
|
case live_1.OpType.CreateRegister: {
|
|
343
378
|
const parent = state.items.get(op.parentId);
|
|
344
379
|
if (parent == null || getItem(op.id) != null) {
|
|
345
380
|
return { modified: false };
|
|
346
381
|
}
|
|
347
|
-
return parent._attachChild(op.id, op.parentKey, new LiveRegister_1.LiveRegister(op.data));
|
|
382
|
+
return parent._attachChild(op.id, op.parentKey, new LiveRegister_1.LiveRegister(op.data), isLocal);
|
|
348
383
|
}
|
|
349
384
|
case live_1.OpType.CreateMap: {
|
|
350
385
|
const parent = state.items.get(op.parentId);
|
|
351
386
|
if (parent == null || getItem(op.id) != null) {
|
|
352
387
|
return { modified: false };
|
|
353
388
|
}
|
|
354
|
-
return parent._attachChild(op.id, op.parentKey, new LiveMap_1.LiveMap());
|
|
389
|
+
return parent._attachChild(op.id, op.parentKey, new LiveMap_1.LiveMap(), isLocal);
|
|
355
390
|
}
|
|
356
391
|
}
|
|
357
392
|
return { modified: false };
|
|
@@ -443,7 +478,9 @@ See v0.13 release notes for more information.
|
|
|
443
478
|
state.socket = socket;
|
|
444
479
|
}
|
|
445
480
|
function authenticationFailure(error) {
|
|
446
|
-
|
|
481
|
+
if (process.env.NODE_ENV !== "production") {
|
|
482
|
+
console.error("Call to authentication endpoint failed", error);
|
|
483
|
+
}
|
|
447
484
|
updateConnection({ state: "unavailable" });
|
|
448
485
|
state.numberOfRetry++;
|
|
449
486
|
state.timeoutHandles.reconnect = effects.scheduleReconnect(getRetryDelay());
|
|
@@ -571,12 +608,13 @@ See v0.13 release notes for more information.
|
|
|
571
608
|
break;
|
|
572
609
|
}
|
|
573
610
|
case live_1.ServerMessageType.InitialStorageState: {
|
|
574
|
-
|
|
611
|
+
createOrUpdateRootFromMessage(subMessage);
|
|
612
|
+
applyAndSendOfflineOps();
|
|
575
613
|
_getInitialStateResolver === null || _getInitialStateResolver === void 0 ? void 0 : _getInitialStateResolver();
|
|
576
614
|
break;
|
|
577
615
|
}
|
|
578
616
|
case live_1.ServerMessageType.UpdateStorage: {
|
|
579
|
-
const applyResult = apply(subMessage.ops);
|
|
617
|
+
const applyResult = apply(subMessage.ops, false);
|
|
580
618
|
for (const node of applyResult.updates.nodes) {
|
|
581
619
|
updates.nodes.add(node);
|
|
582
620
|
}
|
|
@@ -609,13 +647,20 @@ See v0.13 release notes for more information.
|
|
|
609
647
|
updateConnection({ state: "failed" });
|
|
610
648
|
const error = new LiveblocksError(event.reason, event.code);
|
|
611
649
|
for (const listener of state.listeners.error) {
|
|
650
|
+
if (process.env.NODE_ENV !== "production") {
|
|
651
|
+
console.error(`Connection to Liveblocks websocket server closed. Reason: ${error.message} (code: ${error.code})`);
|
|
652
|
+
}
|
|
612
653
|
listener(error);
|
|
613
654
|
}
|
|
614
655
|
}
|
|
615
656
|
else if (event.wasClean === false) {
|
|
616
|
-
updateConnection({ state: "unavailable" });
|
|
617
657
|
state.numberOfRetry++;
|
|
618
|
-
|
|
658
|
+
const delay = getRetryDelay();
|
|
659
|
+
if (process.env.NODE_ENV !== "production") {
|
|
660
|
+
console.warn(`Connection to Liveblocks websocket server closed (code: ${event.code}). Retrying in ${delay}ms.`);
|
|
661
|
+
}
|
|
662
|
+
updateConnection({ state: "unavailable" });
|
|
663
|
+
state.timeoutHandles.reconnect = effects.scheduleReconnect(delay);
|
|
619
664
|
}
|
|
620
665
|
else {
|
|
621
666
|
updateConnection({ state: "closed" });
|
|
@@ -639,6 +684,10 @@ See v0.13 release notes for more information.
|
|
|
639
684
|
if (state.connection.state === "connecting") {
|
|
640
685
|
updateConnection(Object.assign(Object.assign({}, state.connection), { state: "open" }));
|
|
641
686
|
state.numberOfRetry = 0;
|
|
687
|
+
state.lastConnectionId = state.connection.id;
|
|
688
|
+
if (state.root) {
|
|
689
|
+
state.buffer.messages.push({ type: live_1.ClientMessageType.FetchStorage });
|
|
690
|
+
}
|
|
642
691
|
tryFlushing();
|
|
643
692
|
}
|
|
644
693
|
else {
|
|
@@ -678,11 +727,29 @@ See v0.13 release notes for more information.
|
|
|
678
727
|
clearInterval(state.intervalHandles.heartbeat);
|
|
679
728
|
connect();
|
|
680
729
|
}
|
|
681
|
-
function
|
|
682
|
-
if (state.
|
|
730
|
+
function applyAndSendOfflineOps() {
|
|
731
|
+
if (state.offlineOperations.size === 0) {
|
|
683
732
|
return;
|
|
684
733
|
}
|
|
685
|
-
|
|
734
|
+
const messages = [];
|
|
735
|
+
const ops = Array.from(state.offlineOperations.values());
|
|
736
|
+
const result = apply(ops, true);
|
|
737
|
+
messages.push({
|
|
738
|
+
type: live_1.ClientMessageType.UpdateStorage,
|
|
739
|
+
ops: ops,
|
|
740
|
+
});
|
|
741
|
+
notify(result.updates);
|
|
742
|
+
effects.send(messages);
|
|
743
|
+
}
|
|
744
|
+
function tryFlushing() {
|
|
745
|
+
const storageOps = state.buffer.storageOperations;
|
|
746
|
+
if (storageOps.length > 0) {
|
|
747
|
+
storageOps.forEach((op) => {
|
|
748
|
+
state.offlineOperations.set(op.opId, op);
|
|
749
|
+
});
|
|
750
|
+
}
|
|
751
|
+
if (state.socket == null || state.socket.readyState !== WebSocket.OPEN) {
|
|
752
|
+
state.buffer.storageOperations = [];
|
|
686
753
|
return;
|
|
687
754
|
}
|
|
688
755
|
const now = Date.now();
|
|
@@ -757,8 +824,10 @@ See v0.13 release notes for more information.
|
|
|
757
824
|
function getOthers() {
|
|
758
825
|
return state.others;
|
|
759
826
|
}
|
|
760
|
-
function broadcastEvent(event
|
|
761
|
-
|
|
827
|
+
function broadcastEvent(event, options = {
|
|
828
|
+
shouldQueueEventIfNotReady: false,
|
|
829
|
+
}) {
|
|
830
|
+
if (state.socket == null && options.shouldQueueEventIfNotReady == false) {
|
|
762
831
|
return;
|
|
763
832
|
}
|
|
764
833
|
state.buffer.messages.push({
|
|
@@ -800,7 +869,7 @@ See v0.13 release notes for more information.
|
|
|
800
869
|
return;
|
|
801
870
|
}
|
|
802
871
|
state.isHistoryPaused = false;
|
|
803
|
-
const result = apply(historyItem);
|
|
872
|
+
const result = apply(historyItem, true);
|
|
804
873
|
notify(result.updates);
|
|
805
874
|
state.redoStack.push(result.reverse);
|
|
806
875
|
for (const op of historyItem) {
|
|
@@ -819,7 +888,7 @@ See v0.13 release notes for more information.
|
|
|
819
888
|
return;
|
|
820
889
|
}
|
|
821
890
|
state.isHistoryPaused = false;
|
|
822
|
-
const result = apply(historyItem);
|
|
891
|
+
const result = apply(historyItem, true);
|
|
823
892
|
notify(result.updates);
|
|
824
893
|
state.undoStack.push(result.reverse);
|
|
825
894
|
for (const op of historyItem) {
|
|
@@ -871,6 +940,16 @@ See v0.13 release notes for more information.
|
|
|
871
940
|
}
|
|
872
941
|
state.pausedHistory = [];
|
|
873
942
|
}
|
|
943
|
+
function simulateSocketClose() {
|
|
944
|
+
if (state.socket) {
|
|
945
|
+
state.socket.close();
|
|
946
|
+
}
|
|
947
|
+
}
|
|
948
|
+
function simulateSendCloseEvent(event) {
|
|
949
|
+
if (state.socket) {
|
|
950
|
+
onClose(event);
|
|
951
|
+
}
|
|
952
|
+
}
|
|
874
953
|
return {
|
|
875
954
|
// Internal
|
|
876
955
|
onOpen,
|
|
@@ -879,6 +958,9 @@ See v0.13 release notes for more information.
|
|
|
879
958
|
authenticationSuccess,
|
|
880
959
|
heartbeat,
|
|
881
960
|
onNavigatorOnline,
|
|
961
|
+
// Internal dev tools
|
|
962
|
+
simulateSocketClose,
|
|
963
|
+
simulateSendCloseEvent,
|
|
882
964
|
// onWakeUp,
|
|
883
965
|
onVisibilityChange,
|
|
884
966
|
getUndoStack: () => state.undoStack,
|
|
@@ -911,6 +993,7 @@ exports.makeStateMachine = makeStateMachine;
|
|
|
911
993
|
function defaultState(me, defaultStorageRoot) {
|
|
912
994
|
return {
|
|
913
995
|
connection: { state: "closed" },
|
|
996
|
+
lastConnectionId: null,
|
|
914
997
|
socket: null,
|
|
915
998
|
listeners: {
|
|
916
999
|
event: [],
|
|
@@ -955,6 +1038,7 @@ function defaultState(me, defaultStorageRoot) {
|
|
|
955
1038
|
updates: { nodes: new Set(), presence: false, others: [] },
|
|
956
1039
|
reverseOps: [],
|
|
957
1040
|
},
|
|
1041
|
+
offlineOperations: new Map(),
|
|
958
1042
|
};
|
|
959
1043
|
}
|
|
960
1044
|
exports.defaultState = defaultState;
|
|
@@ -1001,6 +1085,11 @@ function createRoom(name, options) {
|
|
|
1001
1085
|
pause: machine.pauseHistory,
|
|
1002
1086
|
resume: machine.resumeHistory,
|
|
1003
1087
|
},
|
|
1088
|
+
// @ts-ignore
|
|
1089
|
+
internalDevTools: {
|
|
1090
|
+
closeWebsocket: machine.simulateSocketClose,
|
|
1091
|
+
sendCloseEvent: machine.simulateSendCloseEvent,
|
|
1092
|
+
},
|
|
1004
1093
|
};
|
|
1005
1094
|
return {
|
|
1006
1095
|
connect: machine.connect,
|
package/lib/cjs/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/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
|
*/
|
|
@@ -64,6 +68,7 @@ export declare class LiveList<T> extends AbstractCrdt {
|
|
|
64
68
|
* @param index The index of the element to delete
|
|
65
69
|
*/
|
|
66
70
|
delete(index: number): void;
|
|
71
|
+
clear(): void;
|
|
67
72
|
/**
|
|
68
73
|
* Returns an Array of all the elements in the LiveList.
|
|
69
74
|
*/
|