@liveblocks/client 0.13.2 → 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/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 +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/live.d.ts +7 -0
- package/lib/cjs/room.d.ts +8 -0
- package/lib/cjs/room.js +97 -22
- 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 +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/live.d.ts +7 -0
- package/lib/esm/room.d.ts +8 -0
- package/lib/esm/room.js +98 -23
- package/lib/esm/utils.d.ts +2 -1
- package/lib/esm/utils.js +75 -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/LiveObject.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { AbstractCrdt, Doc, ApplyResult } from "./AbstractCrdt";
|
|
2
|
-
import { Op, SerializedCrdtWithId } from "./live";
|
|
2
|
+
import { Op, SerializedCrdt, SerializedCrdtWithId } from "./live";
|
|
3
3
|
/**
|
|
4
4
|
* The LiveObject class is similar to a JavaScript object that is synchronized on all clients.
|
|
5
5
|
* Keys should be a string, and values should be serializable to JSON.
|
|
@@ -11,13 +11,15 @@ export declare class LiveObject<T extends Record<string, any> = Record<string, a
|
|
|
11
11
|
/**
|
|
12
12
|
* INTERNAL
|
|
13
13
|
*/
|
|
14
|
-
_serialize(parentId?: string, parentKey?: string): Op[];
|
|
14
|
+
_serialize(parentId?: string, parentKey?: string, doc?: Doc): Op[];
|
|
15
15
|
/**
|
|
16
16
|
* INTERNAL
|
|
17
17
|
*/
|
|
18
|
-
static _deserialize([id, item]: SerializedCrdtWithId, parentToChildren: Map<string, SerializedCrdtWithId[]>, doc: Doc): LiveObject<
|
|
19
|
-
|
|
20
|
-
|
|
18
|
+
static _deserialize([id, item]: SerializedCrdtWithId, parentToChildren: Map<string, SerializedCrdtWithId[]>, doc: Doc): LiveObject<Record<string, any>>;
|
|
19
|
+
/**
|
|
20
|
+
* INTERNAL
|
|
21
|
+
*/
|
|
22
|
+
static _deserializeChildren(object: LiveObject, parentToChildren: Map<string, SerializedCrdtWithId[]>, doc: Doc): LiveObject<Record<string, any>>;
|
|
21
23
|
/**
|
|
22
24
|
* INTERNAL
|
|
23
25
|
*/
|
|
@@ -25,11 +27,15 @@ export declare class LiveObject<T extends Record<string, any> = Record<string, a
|
|
|
25
27
|
/**
|
|
26
28
|
* INTERNAL
|
|
27
29
|
*/
|
|
28
|
-
_attachChild(id: string, key: keyof T, child: AbstractCrdt): ApplyResult;
|
|
30
|
+
_attachChild(id: string, key: keyof T, child: AbstractCrdt, isLocal: boolean): ApplyResult;
|
|
29
31
|
/**
|
|
30
32
|
* INTERNAL
|
|
31
33
|
*/
|
|
32
34
|
_detachChild(child: AbstractCrdt): void;
|
|
35
|
+
/**
|
|
36
|
+
* INTERNAL
|
|
37
|
+
*/
|
|
38
|
+
_detachChildren(): void;
|
|
33
39
|
/**
|
|
34
40
|
* INTERNAL
|
|
35
41
|
*/
|
|
@@ -37,7 +43,11 @@ export declare class LiveObject<T extends Record<string, any> = Record<string, a
|
|
|
37
43
|
/**
|
|
38
44
|
* INTERNAL
|
|
39
45
|
*/
|
|
40
|
-
_apply(op: Op): ApplyResult;
|
|
46
|
+
_apply(op: Op, isLocal: boolean): ApplyResult;
|
|
47
|
+
/**
|
|
48
|
+
* INTERNAL
|
|
49
|
+
*/
|
|
50
|
+
_toSerializedCrdt(): SerializedCrdt;
|
|
41
51
|
/**
|
|
42
52
|
* Transform the LiveObject into a javascript object
|
|
43
53
|
*/
|
package/lib/esm/LiveObject.js
CHANGED
|
@@ -35,13 +35,14 @@ export class LiveObject extends AbstractCrdt {
|
|
|
35
35
|
/**
|
|
36
36
|
* INTERNAL
|
|
37
37
|
*/
|
|
38
|
-
_serialize(parentId, parentKey) {
|
|
38
|
+
_serialize(parentId, parentKey, doc) {
|
|
39
39
|
if (this._id == null) {
|
|
40
40
|
throw new Error("Cannot serialize item is not attached");
|
|
41
41
|
}
|
|
42
42
|
const ops = [];
|
|
43
43
|
const op = {
|
|
44
44
|
id: this._id,
|
|
45
|
+
opId: doc === null || doc === void 0 ? void 0 : doc.generateOpId(),
|
|
45
46
|
type: OpType.CreateObject,
|
|
46
47
|
parentId,
|
|
47
48
|
parentKey,
|
|
@@ -50,7 +51,7 @@ export class LiveObject extends AbstractCrdt {
|
|
|
50
51
|
ops.push(op);
|
|
51
52
|
for (const [key, value] of __classPrivateFieldGet(this, _LiveObject_map, "f")) {
|
|
52
53
|
if (value instanceof AbstractCrdt) {
|
|
53
|
-
ops.push(...value._serialize(this._id, key));
|
|
54
|
+
ops.push(...value._serialize(this._id, key, doc));
|
|
54
55
|
}
|
|
55
56
|
else {
|
|
56
57
|
op.data[key] = value;
|
|
@@ -67,7 +68,13 @@ export class LiveObject extends AbstractCrdt {
|
|
|
67
68
|
}
|
|
68
69
|
const object = new LiveObject(item.data);
|
|
69
70
|
object._attach(id, doc);
|
|
70
|
-
|
|
71
|
+
return this._deserializeChildren(object, parentToChildren, doc);
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* INTERNAL
|
|
75
|
+
*/
|
|
76
|
+
static _deserializeChildren(object, parentToChildren, doc) {
|
|
77
|
+
const children = parentToChildren.get(object._id);
|
|
71
78
|
if (children == null) {
|
|
72
79
|
return object;
|
|
73
80
|
}
|
|
@@ -96,7 +103,7 @@ export class LiveObject extends AbstractCrdt {
|
|
|
96
103
|
/**
|
|
97
104
|
* INTERNAL
|
|
98
105
|
*/
|
|
99
|
-
_attachChild(id, key, child) {
|
|
106
|
+
_attachChild(id, key, child, isLocal) {
|
|
100
107
|
if (this._doc == null) {
|
|
101
108
|
throw new Error("Can't attach child if doc is not present");
|
|
102
109
|
}
|
|
@@ -138,6 +145,15 @@ export class LiveObject extends AbstractCrdt {
|
|
|
138
145
|
child._detach();
|
|
139
146
|
}
|
|
140
147
|
}
|
|
148
|
+
/**
|
|
149
|
+
* INTERNAL
|
|
150
|
+
*/
|
|
151
|
+
_detachChildren() {
|
|
152
|
+
for (const [key, value] of __classPrivateFieldGet(this, _LiveObject_map, "f")) {
|
|
153
|
+
__classPrivateFieldGet(this, _LiveObject_map, "f").delete(key);
|
|
154
|
+
value._detach();
|
|
155
|
+
}
|
|
156
|
+
}
|
|
141
157
|
/**
|
|
142
158
|
* INTERNAL
|
|
143
159
|
*/
|
|
@@ -152,14 +168,26 @@ export class LiveObject extends AbstractCrdt {
|
|
|
152
168
|
/**
|
|
153
169
|
* INTERNAL
|
|
154
170
|
*/
|
|
155
|
-
_apply(op) {
|
|
171
|
+
_apply(op, isLocal) {
|
|
156
172
|
if (op.type === OpType.UpdateObject) {
|
|
157
|
-
return __classPrivateFieldGet(this, _LiveObject_instances, "m", _LiveObject_applyUpdate).call(this, op);
|
|
173
|
+
return __classPrivateFieldGet(this, _LiveObject_instances, "m", _LiveObject_applyUpdate).call(this, op, isLocal);
|
|
158
174
|
}
|
|
159
175
|
else if (op.type === OpType.DeleteObjectKey) {
|
|
160
176
|
return __classPrivateFieldGet(this, _LiveObject_instances, "m", _LiveObject_applyDeleteObjectKey).call(this, op);
|
|
161
177
|
}
|
|
162
|
-
return super._apply(op);
|
|
178
|
+
return super._apply(op, isLocal);
|
|
179
|
+
}
|
|
180
|
+
/**
|
|
181
|
+
* INTERNAL
|
|
182
|
+
*/
|
|
183
|
+
_toSerializedCrdt() {
|
|
184
|
+
var _a;
|
|
185
|
+
return {
|
|
186
|
+
type: CrdtType.Object,
|
|
187
|
+
parentId: (_a = this._parent) === null || _a === void 0 ? void 0 : _a._id,
|
|
188
|
+
parentKey: this._parentKey,
|
|
189
|
+
data: this.toObject(),
|
|
190
|
+
};
|
|
163
191
|
}
|
|
164
192
|
/**
|
|
165
193
|
* Transform the LiveObject into a javascript object
|
|
@@ -215,7 +243,14 @@ export class LiveObject extends AbstractCrdt {
|
|
|
215
243
|
];
|
|
216
244
|
}
|
|
217
245
|
__classPrivateFieldGet(this, _LiveObject_map, "f").delete(keyAsString);
|
|
218
|
-
this._doc.dispatch([
|
|
246
|
+
this._doc.dispatch([
|
|
247
|
+
{
|
|
248
|
+
type: OpType.DeleteObjectKey,
|
|
249
|
+
key: keyAsString,
|
|
250
|
+
id: this._id,
|
|
251
|
+
opId: this._doc.generateOpId(),
|
|
252
|
+
},
|
|
253
|
+
], reverse, [this]);
|
|
219
254
|
}
|
|
220
255
|
/**
|
|
221
256
|
* Adds or updates multiple properties at once with an object.
|
|
@@ -262,7 +297,7 @@ export class LiveObject extends AbstractCrdt {
|
|
|
262
297
|
if (newValue instanceof AbstractCrdt) {
|
|
263
298
|
newValue._setParentLink(this, key);
|
|
264
299
|
newValue._attach(this._doc.generateId(), this._doc);
|
|
265
|
-
ops.push(...newValue._serialize(this._id, key));
|
|
300
|
+
ops.push(...newValue._serialize(this._id, key, this._doc));
|
|
266
301
|
}
|
|
267
302
|
else {
|
|
268
303
|
updatedProps[key] = newValue;
|
|
@@ -283,7 +318,7 @@ export class LiveObject extends AbstractCrdt {
|
|
|
283
318
|
this._doc.dispatch(ops, reverseOps, [this]);
|
|
284
319
|
}
|
|
285
320
|
}
|
|
286
|
-
_LiveObject_map = new WeakMap(), _LiveObject_propToLastUpdate = new WeakMap(), _LiveObject_instances = new WeakSet(), _LiveObject_applyUpdate = function _LiveObject_applyUpdate(op) {
|
|
321
|
+
_LiveObject_map = new WeakMap(), _LiveObject_propToLastUpdate = new WeakMap(), _LiveObject_instances = new WeakSet(), _LiveObject_applyUpdate = function _LiveObject_applyUpdate(op, isLocal) {
|
|
287
322
|
let isModified = false;
|
|
288
323
|
const reverse = [];
|
|
289
324
|
const reverseUpdate = {
|
|
@@ -305,11 +340,6 @@ _LiveObject_map = new WeakMap(), _LiveObject_propToLastUpdate = new WeakMap(), _
|
|
|
305
340
|
reverse.push({ type: OpType.DeleteObjectKey, id: this._id, key });
|
|
306
341
|
}
|
|
307
342
|
}
|
|
308
|
-
let isLocal = false;
|
|
309
|
-
if (op.opId == null) {
|
|
310
|
-
isLocal = true;
|
|
311
|
-
op.opId = this._doc.generateOpId();
|
|
312
|
-
}
|
|
313
343
|
for (const key in op.data) {
|
|
314
344
|
if (isLocal) {
|
|
315
345
|
__classPrivateFieldGet(this, _LiveObject_propToLastUpdate, "f").set(key, op.opId);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { AbstractCrdt, Doc, ApplyResult } from "./AbstractCrdt";
|
|
2
|
-
import { SerializedCrdtWithId, Op } from "./live";
|
|
2
|
+
import { SerializedCrdtWithId, Op, SerializedCrdt } from "./live";
|
|
3
3
|
/**
|
|
4
4
|
* INTERNAL
|
|
5
5
|
*/
|
|
@@ -14,8 +14,12 @@ export declare class LiveRegister<TValue = any> extends AbstractCrdt {
|
|
|
14
14
|
/**
|
|
15
15
|
* INTERNAL
|
|
16
16
|
*/
|
|
17
|
-
_serialize(parentId: string, parentKey: string): Op[];
|
|
18
|
-
|
|
17
|
+
_serialize(parentId: string, parentKey: string, doc?: Doc): Op[];
|
|
18
|
+
/**
|
|
19
|
+
* INTERNAL
|
|
20
|
+
*/
|
|
21
|
+
_toSerializedCrdt(): SerializedCrdt;
|
|
22
|
+
_attachChild(id: string, key: string, crdt: AbstractCrdt, isLocal: boolean): ApplyResult;
|
|
19
23
|
_detachChild(crdt: AbstractCrdt): void;
|
|
20
|
-
_apply(op: Op): ApplyResult;
|
|
24
|
+
_apply(op: Op, isLocal: boolean): ApplyResult;
|
|
21
25
|
}
|
package/lib/esm/LiveRegister.js
CHANGED
|
@@ -11,7 +11,7 @@ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (
|
|
|
11
11
|
};
|
|
12
12
|
var _LiveRegister_data;
|
|
13
13
|
import { AbstractCrdt } from "./AbstractCrdt";
|
|
14
|
-
import { CrdtType, OpType } from "./live";
|
|
14
|
+
import { CrdtType, OpType, } from "./live";
|
|
15
15
|
/**
|
|
16
16
|
* INTERNAL
|
|
17
17
|
*/
|
|
@@ -38,13 +38,14 @@ export class LiveRegister extends AbstractCrdt {
|
|
|
38
38
|
/**
|
|
39
39
|
* INTERNAL
|
|
40
40
|
*/
|
|
41
|
-
_serialize(parentId, parentKey) {
|
|
41
|
+
_serialize(parentId, parentKey, doc) {
|
|
42
42
|
if (this._id == null || parentId == null || parentKey == null) {
|
|
43
43
|
throw new Error("Cannot serialize register if parentId or parentKey is undefined");
|
|
44
44
|
}
|
|
45
45
|
return [
|
|
46
46
|
{
|
|
47
47
|
type: OpType.CreateRegister,
|
|
48
|
+
opId: doc === null || doc === void 0 ? void 0 : doc.generateOpId(),
|
|
48
49
|
id: this._id,
|
|
49
50
|
parentId,
|
|
50
51
|
parentKey,
|
|
@@ -52,14 +53,26 @@ export class LiveRegister extends AbstractCrdt {
|
|
|
52
53
|
},
|
|
53
54
|
];
|
|
54
55
|
}
|
|
55
|
-
|
|
56
|
+
/**
|
|
57
|
+
* INTERNAL
|
|
58
|
+
*/
|
|
59
|
+
_toSerializedCrdt() {
|
|
60
|
+
var _a;
|
|
61
|
+
return {
|
|
62
|
+
type: CrdtType.Register,
|
|
63
|
+
parentId: (_a = this._parent) === null || _a === void 0 ? void 0 : _a._id,
|
|
64
|
+
parentKey: this._parentKey,
|
|
65
|
+
data: this.data,
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
_attachChild(id, key, crdt, isLocal) {
|
|
56
69
|
throw new Error("Method not implemented.");
|
|
57
70
|
}
|
|
58
71
|
_detachChild(crdt) {
|
|
59
72
|
throw new Error("Method not implemented.");
|
|
60
73
|
}
|
|
61
|
-
_apply(op) {
|
|
62
|
-
return super._apply(op);
|
|
74
|
+
_apply(op, isLocal) {
|
|
75
|
+
return super._apply(op, isLocal);
|
|
63
76
|
}
|
|
64
77
|
}
|
|
65
78
|
_LiveRegister_data = new WeakMap();
|
package/lib/esm/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/esm/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/esm/room.js
CHANGED
|
@@ -7,7 +7,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
7
7
|
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
8
|
});
|
|
9
9
|
};
|
|
10
|
-
import { isSameNodeOrChildOf, remove } from "./utils";
|
|
10
|
+
import { getTreesDiffOperations, isSameNodeOrChildOf, remove } from "./utils";
|
|
11
11
|
import auth, { parseToken } from "./authentication";
|
|
12
12
|
import { ClientMessageType, ServerMessageType, OpType, } from "./live";
|
|
13
13
|
import { LiveMap } from "./LiveMap";
|
|
@@ -107,18 +107,23 @@ export function makeStateMachine(state, context, mockedEffects) {
|
|
|
107
107
|
};
|
|
108
108
|
return genericSubscribe(cb);
|
|
109
109
|
}
|
|
110
|
-
function
|
|
111
|
-
|
|
110
|
+
function createOrUpdateRootFromMessage(message) {
|
|
111
|
+
if (message.items.length === 0) {
|
|
112
|
+
throw new Error("Internal error: cannot load storage without items");
|
|
113
|
+
}
|
|
114
|
+
if (state.root) {
|
|
115
|
+
updateRoot(message.items);
|
|
116
|
+
}
|
|
117
|
+
else {
|
|
118
|
+
state.root = load(message.items);
|
|
119
|
+
}
|
|
112
120
|
for (const key in state.defaultStorageRoot) {
|
|
113
121
|
if (state.root.get(key) == null) {
|
|
114
122
|
state.root.set(key, state.defaultStorageRoot[key]);
|
|
115
123
|
}
|
|
116
124
|
}
|
|
117
125
|
}
|
|
118
|
-
function
|
|
119
|
-
if (items.length === 0) {
|
|
120
|
-
throw new Error("Internal error: cannot load storage without items");
|
|
121
|
-
}
|
|
126
|
+
function buildRootAndParentToChildren(items) {
|
|
122
127
|
const parentToChildren = new Map();
|
|
123
128
|
let root = null;
|
|
124
129
|
for (const tuple of items) {
|
|
@@ -139,6 +144,23 @@ export function makeStateMachine(state, context, mockedEffects) {
|
|
|
139
144
|
if (root == null) {
|
|
140
145
|
throw new Error("Root can't be null");
|
|
141
146
|
}
|
|
147
|
+
return [root, parentToChildren];
|
|
148
|
+
}
|
|
149
|
+
function updateRoot(items) {
|
|
150
|
+
if (!state.root) {
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
const currentItems = new Map();
|
|
154
|
+
state.items.forEach((liveCrdt, id) => {
|
|
155
|
+
currentItems.set(id, liveCrdt._toSerializedCrdt());
|
|
156
|
+
});
|
|
157
|
+
// Get operations that represent the diff between 2 states.
|
|
158
|
+
const ops = getTreesDiffOperations(currentItems, new Map(items));
|
|
159
|
+
const result = apply(ops, false);
|
|
160
|
+
notify(result.updates);
|
|
161
|
+
}
|
|
162
|
+
function load(items) {
|
|
163
|
+
const [root, parentToChildren] = buildRootAndParentToChildren(items);
|
|
142
164
|
return LiveObject._deserialize(root, parentToChildren, {
|
|
143
165
|
addItem,
|
|
144
166
|
deleteItem,
|
|
@@ -227,7 +249,10 @@ export function makeStateMachine(state, context, mockedEffects) {
|
|
|
227
249
|
state.connection.state === "connecting") {
|
|
228
250
|
return state.connection.id;
|
|
229
251
|
}
|
|
230
|
-
|
|
252
|
+
else if (state.lastConnectionId !== null) {
|
|
253
|
+
return state.lastConnectionId;
|
|
254
|
+
}
|
|
255
|
+
throw new Error("Internal. Tried to get connection id but connection was never open");
|
|
231
256
|
}
|
|
232
257
|
function generateId() {
|
|
233
258
|
return `${getConnectionId()}:${state.clock++}`;
|
|
@@ -235,7 +260,7 @@ export function makeStateMachine(state, context, mockedEffects) {
|
|
|
235
260
|
function generateOpId() {
|
|
236
261
|
return `${getConnectionId()}:${state.opClock++}`;
|
|
237
262
|
}
|
|
238
|
-
function apply(item) {
|
|
263
|
+
function apply(item, isLocal) {
|
|
239
264
|
const result = {
|
|
240
265
|
reverse: [],
|
|
241
266
|
updates: { nodes: new Set(), presence: false },
|
|
@@ -262,7 +287,11 @@ export function makeStateMachine(state, context, mockedEffects) {
|
|
|
262
287
|
result.updates.presence = true;
|
|
263
288
|
}
|
|
264
289
|
else {
|
|
265
|
-
|
|
290
|
+
// Ops applied after undo/redo don't have an opId.
|
|
291
|
+
if (isLocal && !op.opId) {
|
|
292
|
+
op.opId = generateOpId();
|
|
293
|
+
}
|
|
294
|
+
const applyOpResult = applyOp(op, isLocal);
|
|
266
295
|
if (applyOpResult.modified) {
|
|
267
296
|
result.updates.nodes.add(applyOpResult.modified);
|
|
268
297
|
result.reverse.unshift(...applyOpResult.reverse);
|
|
@@ -271,7 +300,10 @@ export function makeStateMachine(state, context, mockedEffects) {
|
|
|
271
300
|
}
|
|
272
301
|
return result;
|
|
273
302
|
}
|
|
274
|
-
function applyOp(op) {
|
|
303
|
+
function applyOp(op, isLocal) {
|
|
304
|
+
if (op.opId) {
|
|
305
|
+
state.offlineOperations.delete(op.opId);
|
|
306
|
+
}
|
|
275
307
|
switch (op.type) {
|
|
276
308
|
case OpType.DeleteObjectKey:
|
|
277
309
|
case OpType.UpdateObject:
|
|
@@ -280,7 +312,7 @@ export function makeStateMachine(state, context, mockedEffects) {
|
|
|
280
312
|
if (item == null) {
|
|
281
313
|
return { modified: false };
|
|
282
314
|
}
|
|
283
|
-
return item._apply(op);
|
|
315
|
+
return item._apply(op, isLocal);
|
|
284
316
|
}
|
|
285
317
|
case OpType.SetParentKey: {
|
|
286
318
|
const item = state.items.get(op.id);
|
|
@@ -308,28 +340,28 @@ export function makeStateMachine(state, context, mockedEffects) {
|
|
|
308
340
|
if (parent == null || getItem(op.id) != null) {
|
|
309
341
|
return { modified: false };
|
|
310
342
|
}
|
|
311
|
-
return parent._attachChild(op.id, op.parentKey, new LiveObject(op.data));
|
|
343
|
+
return parent._attachChild(op.id, op.parentKey, new LiveObject(op.data), isLocal);
|
|
312
344
|
}
|
|
313
345
|
case OpType.CreateList: {
|
|
314
346
|
const parent = state.items.get(op.parentId);
|
|
315
347
|
if (parent == null || getItem(op.id) != null) {
|
|
316
348
|
return { modified: false };
|
|
317
349
|
}
|
|
318
|
-
return parent._attachChild(op.id, op.parentKey, new LiveList());
|
|
350
|
+
return parent._attachChild(op.id, op.parentKey, new LiveList(), isLocal);
|
|
319
351
|
}
|
|
320
352
|
case OpType.CreateRegister: {
|
|
321
353
|
const parent = state.items.get(op.parentId);
|
|
322
354
|
if (parent == null || getItem(op.id) != null) {
|
|
323
355
|
return { modified: false };
|
|
324
356
|
}
|
|
325
|
-
return parent._attachChild(op.id, op.parentKey, new LiveRegister(op.data));
|
|
357
|
+
return parent._attachChild(op.id, op.parentKey, new LiveRegister(op.data), isLocal);
|
|
326
358
|
}
|
|
327
359
|
case OpType.CreateMap: {
|
|
328
360
|
const parent = state.items.get(op.parentId);
|
|
329
361
|
if (parent == null || getItem(op.id) != null) {
|
|
330
362
|
return { modified: false };
|
|
331
363
|
}
|
|
332
|
-
return parent._attachChild(op.id, op.parentKey, new LiveMap());
|
|
364
|
+
return parent._attachChild(op.id, op.parentKey, new LiveMap(), isLocal);
|
|
333
365
|
}
|
|
334
366
|
}
|
|
335
367
|
return { modified: false };
|
|
@@ -549,12 +581,13 @@ See v0.13 release notes for more information.
|
|
|
549
581
|
break;
|
|
550
582
|
}
|
|
551
583
|
case ServerMessageType.InitialStorageState: {
|
|
552
|
-
|
|
584
|
+
createOrUpdateRootFromMessage(subMessage);
|
|
585
|
+
applyAndSendOfflineOps();
|
|
553
586
|
_getInitialStateResolver === null || _getInitialStateResolver === void 0 ? void 0 : _getInitialStateResolver();
|
|
554
587
|
break;
|
|
555
588
|
}
|
|
556
589
|
case ServerMessageType.UpdateStorage: {
|
|
557
|
-
const applyResult = apply(subMessage.ops);
|
|
590
|
+
const applyResult = apply(subMessage.ops, false);
|
|
558
591
|
for (const node of applyResult.updates.nodes) {
|
|
559
592
|
updates.nodes.add(node);
|
|
560
593
|
}
|
|
@@ -618,6 +651,10 @@ See v0.13 release notes for more information.
|
|
|
618
651
|
if (state.connection.state === "connecting") {
|
|
619
652
|
updateConnection(Object.assign(Object.assign({}, state.connection), { state: "open" }));
|
|
620
653
|
state.numberOfRetry = 0;
|
|
654
|
+
state.lastConnectionId = state.connection.id;
|
|
655
|
+
if (state.root) {
|
|
656
|
+
state.buffer.messages.push({ type: ClientMessageType.FetchStorage });
|
|
657
|
+
}
|
|
621
658
|
tryFlushing();
|
|
622
659
|
}
|
|
623
660
|
else {
|
|
@@ -657,11 +694,29 @@ See v0.13 release notes for more information.
|
|
|
657
694
|
clearInterval(state.intervalHandles.heartbeat);
|
|
658
695
|
connect();
|
|
659
696
|
}
|
|
660
|
-
function
|
|
661
|
-
if (state.
|
|
697
|
+
function applyAndSendOfflineOps() {
|
|
698
|
+
if (state.offlineOperations.size === 0) {
|
|
662
699
|
return;
|
|
663
700
|
}
|
|
664
|
-
|
|
701
|
+
const messages = [];
|
|
702
|
+
const ops = Array.from(state.offlineOperations.values());
|
|
703
|
+
const result = apply(ops, true);
|
|
704
|
+
messages.push({
|
|
705
|
+
type: ClientMessageType.UpdateStorage,
|
|
706
|
+
ops: ops,
|
|
707
|
+
});
|
|
708
|
+
notify(result.updates);
|
|
709
|
+
effects.send(messages);
|
|
710
|
+
}
|
|
711
|
+
function tryFlushing() {
|
|
712
|
+
const storageOps = state.buffer.storageOperations;
|
|
713
|
+
if (storageOps.length > 0) {
|
|
714
|
+
storageOps.forEach((op) => {
|
|
715
|
+
state.offlineOperations.set(op.opId, op);
|
|
716
|
+
});
|
|
717
|
+
}
|
|
718
|
+
if (state.socket == null || state.socket.readyState !== WebSocket.OPEN) {
|
|
719
|
+
state.buffer.storageOperations = [];
|
|
665
720
|
return;
|
|
666
721
|
}
|
|
667
722
|
const now = Date.now();
|
|
@@ -779,7 +834,7 @@ See v0.13 release notes for more information.
|
|
|
779
834
|
return;
|
|
780
835
|
}
|
|
781
836
|
state.isHistoryPaused = false;
|
|
782
|
-
const result = apply(historyItem);
|
|
837
|
+
const result = apply(historyItem, true);
|
|
783
838
|
notify(result.updates);
|
|
784
839
|
state.redoStack.push(result.reverse);
|
|
785
840
|
for (const op of historyItem) {
|
|
@@ -798,7 +853,7 @@ See v0.13 release notes for more information.
|
|
|
798
853
|
return;
|
|
799
854
|
}
|
|
800
855
|
state.isHistoryPaused = false;
|
|
801
|
-
const result = apply(historyItem);
|
|
856
|
+
const result = apply(historyItem, true);
|
|
802
857
|
notify(result.updates);
|
|
803
858
|
state.undoStack.push(result.reverse);
|
|
804
859
|
for (const op of historyItem) {
|
|
@@ -850,6 +905,16 @@ See v0.13 release notes for more information.
|
|
|
850
905
|
}
|
|
851
906
|
state.pausedHistory = [];
|
|
852
907
|
}
|
|
908
|
+
function simulateSocketClose() {
|
|
909
|
+
if (state.socket) {
|
|
910
|
+
state.socket.close();
|
|
911
|
+
}
|
|
912
|
+
}
|
|
913
|
+
function simulateSendCloseEvent(event) {
|
|
914
|
+
if (state.socket) {
|
|
915
|
+
onClose(event);
|
|
916
|
+
}
|
|
917
|
+
}
|
|
853
918
|
return {
|
|
854
919
|
// Internal
|
|
855
920
|
onOpen,
|
|
@@ -858,6 +923,9 @@ See v0.13 release notes for more information.
|
|
|
858
923
|
authenticationSuccess,
|
|
859
924
|
heartbeat,
|
|
860
925
|
onNavigatorOnline,
|
|
926
|
+
// Internal dev tools
|
|
927
|
+
simulateSocketClose,
|
|
928
|
+
simulateSendCloseEvent,
|
|
861
929
|
// onWakeUp,
|
|
862
930
|
onVisibilityChange,
|
|
863
931
|
getUndoStack: () => state.undoStack,
|
|
@@ -889,6 +957,7 @@ See v0.13 release notes for more information.
|
|
|
889
957
|
export function defaultState(me, defaultStorageRoot) {
|
|
890
958
|
return {
|
|
891
959
|
connection: { state: "closed" },
|
|
960
|
+
lastConnectionId: null,
|
|
892
961
|
socket: null,
|
|
893
962
|
listeners: {
|
|
894
963
|
event: [],
|
|
@@ -933,6 +1002,7 @@ export function defaultState(me, defaultStorageRoot) {
|
|
|
933
1002
|
updates: { nodes: new Set(), presence: false, others: [] },
|
|
934
1003
|
reverseOps: [],
|
|
935
1004
|
},
|
|
1005
|
+
offlineOperations: new Map(),
|
|
936
1006
|
};
|
|
937
1007
|
}
|
|
938
1008
|
export function createRoom(name, options) {
|
|
@@ -978,6 +1048,11 @@ export function createRoom(name, options) {
|
|
|
978
1048
|
pause: machine.pauseHistory,
|
|
979
1049
|
resume: machine.resumeHistory,
|
|
980
1050
|
},
|
|
1051
|
+
// @ts-ignore
|
|
1052
|
+
internalDevTools: {
|
|
1053
|
+
closeWebsocket: machine.simulateSocketClose,
|
|
1054
|
+
sendCloseEvent: machine.simulateSendCloseEvent,
|
|
1055
|
+
},
|
|
981
1056
|
};
|
|
982
1057
|
return {
|
|
983
1058
|
connect: machine.connect,
|
package/lib/esm/utils.d.ts
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { AbstractCrdt, Doc } from "./AbstractCrdt";
|
|
2
|
-
import { SerializedCrdtWithId } from "./live";
|
|
2
|
+
import { SerializedCrdtWithId, Op, SerializedCrdt } from "./live";
|
|
3
3
|
export declare function remove<T>(array: T[], item: T): void;
|
|
4
4
|
export declare function isSameNodeOrChildOf(node: AbstractCrdt, parent: AbstractCrdt): boolean;
|
|
5
5
|
export declare function deserialize(entry: SerializedCrdtWithId, parentToChildren: Map<string, SerializedCrdtWithId[]>, doc: Doc): AbstractCrdt;
|
|
6
6
|
export declare function isCrdt(obj: any): obj is AbstractCrdt;
|
|
7
7
|
export declare function selfOrRegisterValue(obj: AbstractCrdt): any;
|
|
8
8
|
export declare function selfOrRegister(obj: any): AbstractCrdt;
|
|
9
|
+
export declare function getTreesDiffOperations(currentItems: Map<string, SerializedCrdt>, newItems: Map<string, SerializedCrdt>): Op[];
|