@liveblocks/client 0.13.0-beta.1 → 0.14.0-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +8 -8
- package/lib/cjs/AbstractCrdt.d.ts +8 -4
- package/lib/cjs/AbstractCrdt.js +2 -2
- package/lib/cjs/LiveList.d.ts +8 -4
- package/lib/cjs/LiveList.js +36 -9
- package/lib/cjs/LiveMap.d.ts +7 -3
- package/lib/cjs/LiveMap.js +23 -5
- package/lib/cjs/LiveObject.d.ts +22 -7
- package/lib/cjs/LiveObject.js +82 -14
- package/lib/cjs/LiveRegister.d.ts +8 -4
- package/lib/cjs/LiveRegister.js +17 -4
- package/lib/cjs/live.d.ts +7 -0
- package/lib/cjs/room.d.ts +8 -0
- package/lib/cjs/room.js +104 -24
- package/lib/cjs/types.d.ts +47 -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 +8 -4
- package/lib/esm/LiveList.js +37 -10
- package/lib/esm/LiveMap.d.ts +7 -3
- package/lib/esm/LiveMap.js +23 -5
- package/lib/esm/LiveObject.d.ts +22 -7
- package/lib/esm/LiveObject.js +82 -14
- package/lib/esm/LiveRegister.d.ts +8 -4
- package/lib/esm/LiveRegister.js +18 -5
- package/lib/esm/live.d.ts +7 -0
- package/lib/esm/room.d.ts +8 -0
- package/lib/esm/room.js +105 -25
- package/lib/esm/types.d.ts +47 -1
- package/lib/esm/utils.d.ts +2 -1
- package/lib/esm/utils.js +75 -1
- package/package.json +2 -2
package/lib/cjs/live.d.ts
CHANGED
|
@@ -122,6 +122,7 @@ export declare type UpdateObjectOp = {
|
|
|
122
122
|
};
|
|
123
123
|
};
|
|
124
124
|
export declare type CreateObjectOp = {
|
|
125
|
+
opId?: string;
|
|
125
126
|
id: string;
|
|
126
127
|
type: OpType.CreateObject;
|
|
127
128
|
parentId?: string;
|
|
@@ -131,18 +132,21 @@ export declare type CreateObjectOp = {
|
|
|
131
132
|
};
|
|
132
133
|
};
|
|
133
134
|
export declare type CreateListOp = {
|
|
135
|
+
opId?: string;
|
|
134
136
|
id: string;
|
|
135
137
|
type: OpType.CreateList;
|
|
136
138
|
parentId: string;
|
|
137
139
|
parentKey: string;
|
|
138
140
|
};
|
|
139
141
|
export declare type CreateMapOp = {
|
|
142
|
+
opId?: string;
|
|
140
143
|
id: string;
|
|
141
144
|
type: OpType.CreateMap;
|
|
142
145
|
parentId: string;
|
|
143
146
|
parentKey: string;
|
|
144
147
|
};
|
|
145
148
|
export declare type CreateRegisterOp = {
|
|
149
|
+
opId?: string;
|
|
146
150
|
id: string;
|
|
147
151
|
type: OpType.CreateRegister;
|
|
148
152
|
parentId: string;
|
|
@@ -150,15 +154,18 @@ export declare type CreateRegisterOp = {
|
|
|
150
154
|
data: any;
|
|
151
155
|
};
|
|
152
156
|
export declare type DeleteCrdtOp = {
|
|
157
|
+
opId?: string;
|
|
153
158
|
id: string;
|
|
154
159
|
type: OpType.DeleteCrdt;
|
|
155
160
|
};
|
|
156
161
|
export declare type SetParentKeyOp = {
|
|
162
|
+
opId?: string;
|
|
157
163
|
id: string;
|
|
158
164
|
type: OpType.SetParentKey;
|
|
159
165
|
parentKey: string;
|
|
160
166
|
};
|
|
161
167
|
export declare type DeleteObjectKeyOp = {
|
|
168
|
+
opId?: string;
|
|
162
169
|
id: string;
|
|
163
170
|
type: OpType.DeleteObjectKey;
|
|
164
171
|
key: string;
|
package/lib/cjs/room.d.ts
CHANGED
|
@@ -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;
|
package/lib/cjs/room.js
CHANGED
|
@@ -129,18 +129,23 @@ function makeStateMachine(state, context, mockedEffects) {
|
|
|
129
129
|
};
|
|
130
130
|
return genericSubscribe(cb);
|
|
131
131
|
}
|
|
132
|
-
function
|
|
133
|
-
|
|
132
|
+
function createOrUpdateRootFromMessage(message) {
|
|
133
|
+
if (message.items.length === 0) {
|
|
134
|
+
throw new Error("Internal error: cannot load storage without items");
|
|
135
|
+
}
|
|
136
|
+
if (state.root) {
|
|
137
|
+
updateRoot(message.items);
|
|
138
|
+
}
|
|
139
|
+
else {
|
|
140
|
+
state.root = load(message.items);
|
|
141
|
+
}
|
|
134
142
|
for (const key in state.defaultStorageRoot) {
|
|
135
143
|
if (state.root.get(key) == null) {
|
|
136
144
|
state.root.set(key, state.defaultStorageRoot[key]);
|
|
137
145
|
}
|
|
138
146
|
}
|
|
139
147
|
}
|
|
140
|
-
function
|
|
141
|
-
if (items.length === 0) {
|
|
142
|
-
throw new Error("Internal error: cannot load storage without items");
|
|
143
|
-
}
|
|
148
|
+
function buildRootAndParentToChildren(items) {
|
|
144
149
|
const parentToChildren = new Map();
|
|
145
150
|
let root = null;
|
|
146
151
|
for (const tuple of items) {
|
|
@@ -161,6 +166,23 @@ function makeStateMachine(state, context, mockedEffects) {
|
|
|
161
166
|
if (root == null) {
|
|
162
167
|
throw new Error("Root can't be null");
|
|
163
168
|
}
|
|
169
|
+
return [root, parentToChildren];
|
|
170
|
+
}
|
|
171
|
+
function updateRoot(items) {
|
|
172
|
+
if (!state.root) {
|
|
173
|
+
return;
|
|
174
|
+
}
|
|
175
|
+
const currentItems = new Map();
|
|
176
|
+
state.items.forEach((liveCrdt, id) => {
|
|
177
|
+
currentItems.set(id, liveCrdt._toSerializedCrdt());
|
|
178
|
+
});
|
|
179
|
+
// Get operations that represent the diff between 2 states.
|
|
180
|
+
const ops = (0, utils_1.getTreesDiffOperations)(currentItems, new Map(items));
|
|
181
|
+
const result = apply(ops, false);
|
|
182
|
+
notify(result.updates);
|
|
183
|
+
}
|
|
184
|
+
function load(items) {
|
|
185
|
+
const [root, parentToChildren] = buildRootAndParentToChildren(items);
|
|
164
186
|
return LiveObject_1.LiveObject._deserialize(root, parentToChildren, {
|
|
165
187
|
addItem,
|
|
166
188
|
deleteItem,
|
|
@@ -249,7 +271,10 @@ function makeStateMachine(state, context, mockedEffects) {
|
|
|
249
271
|
state.connection.state === "connecting") {
|
|
250
272
|
return state.connection.id;
|
|
251
273
|
}
|
|
252
|
-
|
|
274
|
+
else if (state.lastConnectionId !== null) {
|
|
275
|
+
return state.lastConnectionId;
|
|
276
|
+
}
|
|
277
|
+
throw new Error("Internal. Tried to get connection id but connection was never open");
|
|
253
278
|
}
|
|
254
279
|
function generateId() {
|
|
255
280
|
return `${getConnectionId()}:${state.clock++}`;
|
|
@@ -257,7 +282,7 @@ function makeStateMachine(state, context, mockedEffects) {
|
|
|
257
282
|
function generateOpId() {
|
|
258
283
|
return `${getConnectionId()}:${state.opClock++}`;
|
|
259
284
|
}
|
|
260
|
-
function apply(item) {
|
|
285
|
+
function apply(item, isLocal) {
|
|
261
286
|
const result = {
|
|
262
287
|
reverse: [],
|
|
263
288
|
updates: { nodes: new Set(), presence: false },
|
|
@@ -284,7 +309,11 @@ function makeStateMachine(state, context, mockedEffects) {
|
|
|
284
309
|
result.updates.presence = true;
|
|
285
310
|
}
|
|
286
311
|
else {
|
|
287
|
-
|
|
312
|
+
// Ops applied after undo/redo don't have an opId.
|
|
313
|
+
if (isLocal && !op.opId) {
|
|
314
|
+
op.opId = generateOpId();
|
|
315
|
+
}
|
|
316
|
+
const applyOpResult = applyOp(op, isLocal);
|
|
288
317
|
if (applyOpResult.modified) {
|
|
289
318
|
result.updates.nodes.add(applyOpResult.modified);
|
|
290
319
|
result.reverse.unshift(...applyOpResult.reverse);
|
|
@@ -293,7 +322,10 @@ function makeStateMachine(state, context, mockedEffects) {
|
|
|
293
322
|
}
|
|
294
323
|
return result;
|
|
295
324
|
}
|
|
296
|
-
function applyOp(op) {
|
|
325
|
+
function applyOp(op, isLocal) {
|
|
326
|
+
if (op.opId) {
|
|
327
|
+
state.offlineOperations.delete(op.opId);
|
|
328
|
+
}
|
|
297
329
|
switch (op.type) {
|
|
298
330
|
case live_1.OpType.DeleteObjectKey:
|
|
299
331
|
case live_1.OpType.UpdateObject:
|
|
@@ -302,7 +334,7 @@ function makeStateMachine(state, context, mockedEffects) {
|
|
|
302
334
|
if (item == null) {
|
|
303
335
|
return { modified: false };
|
|
304
336
|
}
|
|
305
|
-
return item._apply(op);
|
|
337
|
+
return item._apply(op, isLocal);
|
|
306
338
|
}
|
|
307
339
|
case live_1.OpType.SetParentKey: {
|
|
308
340
|
const item = state.items.get(op.id);
|
|
@@ -330,28 +362,28 @@ function makeStateMachine(state, context, mockedEffects) {
|
|
|
330
362
|
if (parent == null || getItem(op.id) != null) {
|
|
331
363
|
return { modified: false };
|
|
332
364
|
}
|
|
333
|
-
return parent._attachChild(op.id, op.parentKey, new LiveObject_1.LiveObject(op.data));
|
|
365
|
+
return parent._attachChild(op.id, op.parentKey, new LiveObject_1.LiveObject(op.data), isLocal);
|
|
334
366
|
}
|
|
335
367
|
case live_1.OpType.CreateList: {
|
|
336
368
|
const parent = state.items.get(op.parentId);
|
|
337
369
|
if (parent == null || getItem(op.id) != null) {
|
|
338
370
|
return { modified: false };
|
|
339
371
|
}
|
|
340
|
-
return parent._attachChild(op.id, op.parentKey, new LiveList_1.LiveList());
|
|
372
|
+
return parent._attachChild(op.id, op.parentKey, new LiveList_1.LiveList(), isLocal);
|
|
341
373
|
}
|
|
342
374
|
case live_1.OpType.CreateRegister: {
|
|
343
375
|
const parent = state.items.get(op.parentId);
|
|
344
376
|
if (parent == null || getItem(op.id) != null) {
|
|
345
377
|
return { modified: false };
|
|
346
378
|
}
|
|
347
|
-
return parent._attachChild(op.id, op.parentKey, new LiveRegister_1.LiveRegister(op.data));
|
|
379
|
+
return parent._attachChild(op.id, op.parentKey, new LiveRegister_1.LiveRegister(op.data), isLocal);
|
|
348
380
|
}
|
|
349
381
|
case live_1.OpType.CreateMap: {
|
|
350
382
|
const parent = state.items.get(op.parentId);
|
|
351
383
|
if (parent == null || getItem(op.id) != null) {
|
|
352
384
|
return { modified: false };
|
|
353
385
|
}
|
|
354
|
-
return parent._attachChild(op.id, op.parentKey, new LiveMap_1.LiveMap());
|
|
386
|
+
return parent._attachChild(op.id, op.parentKey, new LiveMap_1.LiveMap(), isLocal);
|
|
355
387
|
}
|
|
356
388
|
}
|
|
357
389
|
return { modified: false };
|
|
@@ -571,12 +603,13 @@ See v0.13 release notes for more information.
|
|
|
571
603
|
break;
|
|
572
604
|
}
|
|
573
605
|
case live_1.ServerMessageType.InitialStorageState: {
|
|
574
|
-
|
|
606
|
+
createOrUpdateRootFromMessage(subMessage);
|
|
607
|
+
applyAndSendOfflineOps();
|
|
575
608
|
_getInitialStateResolver === null || _getInitialStateResolver === void 0 ? void 0 : _getInitialStateResolver();
|
|
576
609
|
break;
|
|
577
610
|
}
|
|
578
611
|
case live_1.ServerMessageType.UpdateStorage: {
|
|
579
|
-
const applyResult = apply(subMessage.ops);
|
|
612
|
+
const applyResult = apply(subMessage.ops, false);
|
|
580
613
|
for (const node of applyResult.updates.nodes) {
|
|
581
614
|
updates.nodes.add(node);
|
|
582
615
|
}
|
|
@@ -609,6 +642,7 @@ See v0.13 release notes for more information.
|
|
|
609
642
|
updateConnection({ state: "failed" });
|
|
610
643
|
const error = new LiveblocksError(event.reason, event.code);
|
|
611
644
|
for (const listener of state.listeners.error) {
|
|
645
|
+
console.error(`Liveblocks WebSocket connection closed. Reason: ${error.message} (code: ${error.code})`);
|
|
612
646
|
listener(error);
|
|
613
647
|
}
|
|
614
648
|
}
|
|
@@ -639,6 +673,10 @@ See v0.13 release notes for more information.
|
|
|
639
673
|
if (state.connection.state === "connecting") {
|
|
640
674
|
updateConnection(Object.assign(Object.assign({}, state.connection), { state: "open" }));
|
|
641
675
|
state.numberOfRetry = 0;
|
|
676
|
+
state.lastConnectionId = state.connection.id;
|
|
677
|
+
if (state.root) {
|
|
678
|
+
state.buffer.messages.push({ type: live_1.ClientMessageType.FetchStorage });
|
|
679
|
+
}
|
|
642
680
|
tryFlushing();
|
|
643
681
|
}
|
|
644
682
|
else {
|
|
@@ -678,11 +716,29 @@ See v0.13 release notes for more information.
|
|
|
678
716
|
clearInterval(state.intervalHandles.heartbeat);
|
|
679
717
|
connect();
|
|
680
718
|
}
|
|
681
|
-
function
|
|
682
|
-
if (state.
|
|
719
|
+
function applyAndSendOfflineOps() {
|
|
720
|
+
if (state.offlineOperations.size === 0) {
|
|
683
721
|
return;
|
|
684
722
|
}
|
|
685
|
-
|
|
723
|
+
const messages = [];
|
|
724
|
+
const ops = Array.from(state.offlineOperations.values());
|
|
725
|
+
const result = apply(ops, true);
|
|
726
|
+
messages.push({
|
|
727
|
+
type: live_1.ClientMessageType.UpdateStorage,
|
|
728
|
+
ops: ops,
|
|
729
|
+
});
|
|
730
|
+
notify(result.updates);
|
|
731
|
+
effects.send(messages);
|
|
732
|
+
}
|
|
733
|
+
function tryFlushing() {
|
|
734
|
+
const storageOps = state.buffer.storageOperations;
|
|
735
|
+
if (storageOps.length > 0) {
|
|
736
|
+
storageOps.forEach((op) => {
|
|
737
|
+
state.offlineOperations.set(op.opId, op);
|
|
738
|
+
});
|
|
739
|
+
}
|
|
740
|
+
if (state.socket == null || state.socket.readyState !== WebSocket.OPEN) {
|
|
741
|
+
state.buffer.storageOperations = [];
|
|
686
742
|
return;
|
|
687
743
|
}
|
|
688
744
|
const now = Date.now();
|
|
@@ -800,7 +856,7 @@ See v0.13 release notes for more information.
|
|
|
800
856
|
return;
|
|
801
857
|
}
|
|
802
858
|
state.isHistoryPaused = false;
|
|
803
|
-
const result = apply(historyItem);
|
|
859
|
+
const result = apply(historyItem, true);
|
|
804
860
|
notify(result.updates);
|
|
805
861
|
state.redoStack.push(result.reverse);
|
|
806
862
|
for (const op of historyItem) {
|
|
@@ -819,7 +875,7 @@ See v0.13 release notes for more information.
|
|
|
819
875
|
return;
|
|
820
876
|
}
|
|
821
877
|
state.isHistoryPaused = false;
|
|
822
|
-
const result = apply(historyItem);
|
|
878
|
+
const result = apply(historyItem, true);
|
|
823
879
|
notify(result.updates);
|
|
824
880
|
state.undoStack.push(result.reverse);
|
|
825
881
|
for (const op of historyItem) {
|
|
@@ -839,10 +895,14 @@ See v0.13 release notes for more information.
|
|
|
839
895
|
}
|
|
840
896
|
finally {
|
|
841
897
|
state.isBatching = false;
|
|
842
|
-
|
|
898
|
+
if (state.batch.reverseOps.length > 0) {
|
|
899
|
+
addToUndoStack(state.batch.reverseOps);
|
|
900
|
+
}
|
|
843
901
|
// Clear the redo stack because batch is always called from a local operation
|
|
844
902
|
state.redoStack = [];
|
|
845
|
-
|
|
903
|
+
if (state.batch.ops.length > 0) {
|
|
904
|
+
dispatch(state.batch.ops);
|
|
905
|
+
}
|
|
846
906
|
notify(state.batch.updates);
|
|
847
907
|
state.batch = {
|
|
848
908
|
ops: [],
|
|
@@ -867,6 +927,16 @@ See v0.13 release notes for more information.
|
|
|
867
927
|
}
|
|
868
928
|
state.pausedHistory = [];
|
|
869
929
|
}
|
|
930
|
+
function simulateSocketClose() {
|
|
931
|
+
if (state.socket) {
|
|
932
|
+
state.socket.close();
|
|
933
|
+
}
|
|
934
|
+
}
|
|
935
|
+
function simulateSendCloseEvent(event) {
|
|
936
|
+
if (state.socket) {
|
|
937
|
+
onClose(event);
|
|
938
|
+
}
|
|
939
|
+
}
|
|
870
940
|
return {
|
|
871
941
|
// Internal
|
|
872
942
|
onOpen,
|
|
@@ -875,6 +945,9 @@ See v0.13 release notes for more information.
|
|
|
875
945
|
authenticationSuccess,
|
|
876
946
|
heartbeat,
|
|
877
947
|
onNavigatorOnline,
|
|
948
|
+
// Internal dev tools
|
|
949
|
+
simulateSocketClose,
|
|
950
|
+
simulateSendCloseEvent,
|
|
878
951
|
// onWakeUp,
|
|
879
952
|
onVisibilityChange,
|
|
880
953
|
getUndoStack: () => state.undoStack,
|
|
@@ -907,6 +980,7 @@ exports.makeStateMachine = makeStateMachine;
|
|
|
907
980
|
function defaultState(me, defaultStorageRoot) {
|
|
908
981
|
return {
|
|
909
982
|
connection: { state: "closed" },
|
|
983
|
+
lastConnectionId: null,
|
|
910
984
|
socket: null,
|
|
911
985
|
listeners: {
|
|
912
986
|
event: [],
|
|
@@ -951,6 +1025,7 @@ function defaultState(me, defaultStorageRoot) {
|
|
|
951
1025
|
updates: { nodes: new Set(), presence: false, others: [] },
|
|
952
1026
|
reverseOps: [],
|
|
953
1027
|
},
|
|
1028
|
+
offlineOperations: new Map(),
|
|
954
1029
|
};
|
|
955
1030
|
}
|
|
956
1031
|
exports.defaultState = defaultState;
|
|
@@ -997,6 +1072,11 @@ function createRoom(name, options) {
|
|
|
997
1072
|
pause: machine.pauseHistory,
|
|
998
1073
|
resume: machine.resumeHistory,
|
|
999
1074
|
},
|
|
1075
|
+
// @ts-ignore
|
|
1076
|
+
internalDevTools: {
|
|
1077
|
+
closeWebsocket: machine.simulateSocketClose,
|
|
1078
|
+
sendCloseEvent: machine.simulateSendCloseEvent,
|
|
1079
|
+
},
|
|
1000
1080
|
};
|
|
1001
1081
|
return {
|
|
1002
1082
|
connect: machine.connect,
|
package/lib/cjs/types.d.ts
CHANGED
|
@@ -277,25 +277,57 @@ export declare type Room = {
|
|
|
277
277
|
}): () => void;
|
|
278
278
|
};
|
|
279
279
|
/**
|
|
280
|
-
* Room's history contains
|
|
280
|
+
* Room's history contains functions that let you undo and redo operation made on by the current client on the presence and storage.
|
|
281
281
|
*/
|
|
282
282
|
history: {
|
|
283
283
|
/**
|
|
284
284
|
* Undoes the last operation executed by the current client.
|
|
285
285
|
* It does not impact operations made by other clients.
|
|
286
|
+
*
|
|
287
|
+
* @example
|
|
288
|
+
* room.updatePresence({ selectedId: "xxx" }, { addToHistory: true });
|
|
289
|
+
* room.updatePresence({ selectedId: "yyy" }, { addToHistory: true });
|
|
290
|
+
* room.history.undo();
|
|
291
|
+
* // room.getPresence() equals { selectedId: "xxx" }
|
|
286
292
|
*/
|
|
287
293
|
undo: () => void;
|
|
288
294
|
/**
|
|
289
295
|
* Redoes the last operation executed by the current client.
|
|
290
296
|
* It does not impact operations made by other clients.
|
|
297
|
+
*
|
|
298
|
+
* @example
|
|
299
|
+
* room.updatePresence({ selectedId: "xxx" }, { addToHistory: true });
|
|
300
|
+
* room.updatePresence({ selectedId: "yyy" }, { addToHistory: true });
|
|
301
|
+
* room.history.undo();
|
|
302
|
+
* // room.getPresence() equals { selectedId: "xxx" }
|
|
303
|
+
* room.history.redo();
|
|
304
|
+
* // room.getPresence() equals { selectedId: "yyy" }
|
|
291
305
|
*/
|
|
292
306
|
redo: () => void;
|
|
293
307
|
/**
|
|
294
308
|
* All future modifications made on the Room will be merged together to create a single history item until resume is called.
|
|
309
|
+
*
|
|
310
|
+
* @example
|
|
311
|
+
* room.updatePresence({ cursor: { x: 0, y: 0 } }, { addToHistory: true });
|
|
312
|
+
* room.history.pause();
|
|
313
|
+
* room.updatePresence({ cursor: { x: 1, y: 1 } }, { addToHistory: true });
|
|
314
|
+
* room.updatePresence({ cursor: { x: 2, y: 2 } }, { addToHistory: true });
|
|
315
|
+
* room.history.resume();
|
|
316
|
+
* room.history.undo();
|
|
317
|
+
* // room.getPresence() equals { cursor: { x: 0, y: 0 } }
|
|
295
318
|
*/
|
|
296
319
|
pause: () => void;
|
|
297
320
|
/**
|
|
298
321
|
* Resumes history. Modifications made on the Room are not merged into a single history item anymore.
|
|
322
|
+
*
|
|
323
|
+
* @example
|
|
324
|
+
* room.updatePresence({ cursor: { x: 0, y: 0 } }, { addToHistory: true });
|
|
325
|
+
* room.history.pause();
|
|
326
|
+
* room.updatePresence({ cursor: { x: 1, y: 1 } }, { addToHistory: true });
|
|
327
|
+
* room.updatePresence({ cursor: { x: 2, y: 2 } }, { addToHistory: true });
|
|
328
|
+
* room.history.resume();
|
|
329
|
+
* room.history.undo();
|
|
330
|
+
* // room.getPresence() equals { cursor: { x: 0, y: 0 } }
|
|
299
331
|
*/
|
|
300
332
|
resume: () => void;
|
|
301
333
|
};
|
|
@@ -392,6 +424,13 @@ export declare type Room = {
|
|
|
392
424
|
* });
|
|
393
425
|
*/
|
|
394
426
|
broadcastEvent: (event: any) => void;
|
|
427
|
+
/**
|
|
428
|
+
* Get the room's storage asynchronously.
|
|
429
|
+
* The storage's root is a {@link LiveObject}.
|
|
430
|
+
*
|
|
431
|
+
* @example
|
|
432
|
+
* const { root } = await room.getStorage();
|
|
433
|
+
*/
|
|
395
434
|
getStorage: <TRoot>() => Promise<{
|
|
396
435
|
root: LiveObject<TRoot>;
|
|
397
436
|
}>;
|
|
@@ -400,6 +439,13 @@ export declare type Room = {
|
|
|
400
439
|
* All the modifications are sent to other clients in a single message.
|
|
401
440
|
* All the subscribers are called only after the batch is over.
|
|
402
441
|
* All the modifications are merged in a single history item (undo/redo).
|
|
442
|
+
*
|
|
443
|
+
* @example
|
|
444
|
+
* const { root } = await room.getStorage();
|
|
445
|
+
* room.batch(() => {
|
|
446
|
+
* root.set("x", 0);
|
|
447
|
+
* room.updatePresence({ cursor: { x: 100, y: 100 }});
|
|
448
|
+
* });
|
|
403
449
|
*/
|
|
404
450
|
batch: (fn: () => void) => void;
|
|
405
451
|
};
|
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
|
*/
|