@liveblocks/client 0.16.4 → 0.16.5
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/index.js +815 -1540
- package/index.mjs +778 -1295
- package/internal.d.ts +24 -5
- package/internal.js +146 -337
- package/internal.mjs +127 -299
- package/package.json +8 -7
- package/shared.d.ts +15 -3
- package/shared.js +1275 -2380
- package/shared.mjs +1183 -1887
package/shared.mjs
CHANGED
|
@@ -1,1947 +1,1243 @@
|
|
|
1
|
-
var ServerMessageType;
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
OpType[OpType["CreateList"] = 2] = "CreateList";
|
|
30
|
-
OpType[OpType["UpdateObject"] = 3] = "UpdateObject";
|
|
31
|
-
OpType[OpType["CreateObject"] = 4] = "CreateObject";
|
|
32
|
-
OpType[OpType["DeleteCrdt"] = 5] = "DeleteCrdt";
|
|
33
|
-
OpType[OpType["DeleteObjectKey"] = 6] = "DeleteObjectKey";
|
|
34
|
-
OpType[OpType["CreateMap"] = 7] = "CreateMap";
|
|
35
|
-
OpType[OpType["CreateRegister"] = 8] = "CreateRegister";
|
|
36
|
-
})(OpType || (OpType = {}));
|
|
37
|
-
var WebsocketCloseCodes;
|
|
38
|
-
(function (WebsocketCloseCodes) {
|
|
39
|
-
WebsocketCloseCodes[WebsocketCloseCodes["CLOSE_ABNORMAL"] = 1006] = "CLOSE_ABNORMAL";
|
|
40
|
-
WebsocketCloseCodes[WebsocketCloseCodes["INVALID_MESSAGE_FORMAT"] = 4000] = "INVALID_MESSAGE_FORMAT";
|
|
41
|
-
WebsocketCloseCodes[WebsocketCloseCodes["NOT_ALLOWED"] = 4001] = "NOT_ALLOWED";
|
|
42
|
-
WebsocketCloseCodes[WebsocketCloseCodes["MAX_NUMBER_OF_MESSAGES_PER_SECONDS"] = 4002] = "MAX_NUMBER_OF_MESSAGES_PER_SECONDS";
|
|
43
|
-
WebsocketCloseCodes[WebsocketCloseCodes["MAX_NUMBER_OF_CONCURRENT_CONNECTIONS"] = 4003] = "MAX_NUMBER_OF_CONCURRENT_CONNECTIONS";
|
|
44
|
-
WebsocketCloseCodes[WebsocketCloseCodes["MAX_NUMBER_OF_MESSAGES_PER_DAY_PER_APP"] = 4004] = "MAX_NUMBER_OF_MESSAGES_PER_DAY_PER_APP";
|
|
45
|
-
WebsocketCloseCodes[WebsocketCloseCodes["MAX_NUMBER_OF_CONCURRENT_CONNECTIONS_PER_ROOM"] = 4005] = "MAX_NUMBER_OF_CONCURRENT_CONNECTIONS_PER_ROOM";
|
|
46
|
-
WebsocketCloseCodes[WebsocketCloseCodes["CLOSE_WITHOUT_RETRY"] = 4999] = "CLOSE_WITHOUT_RETRY";
|
|
47
|
-
})(WebsocketCloseCodes || (WebsocketCloseCodes = {}));
|
|
1
|
+
var ServerMessageType, ClientMessageType, CrdtType, OpType, WebsocketCloseCodes;
|
|
2
|
+
|
|
3
|
+
!function(ServerMessageType) {
|
|
4
|
+
ServerMessageType[ServerMessageType.UpdatePresence = 100] = "UpdatePresence", ServerMessageType[ServerMessageType.UserJoined = 101] = "UserJoined",
|
|
5
|
+
ServerMessageType[ServerMessageType.UserLeft = 102] = "UserLeft", ServerMessageType[ServerMessageType.Event = 103] = "Event",
|
|
6
|
+
ServerMessageType[ServerMessageType.RoomState = 104] = "RoomState", ServerMessageType[ServerMessageType.InitialStorageState = 200] = "InitialStorageState",
|
|
7
|
+
ServerMessageType[ServerMessageType.UpdateStorage = 201] = "UpdateStorage";
|
|
8
|
+
}(ServerMessageType || (ServerMessageType = {})), function(ClientMessageType) {
|
|
9
|
+
ClientMessageType[ClientMessageType.UpdatePresence = 100] = "UpdatePresence", ClientMessageType[ClientMessageType.ClientEvent = 103] = "ClientEvent",
|
|
10
|
+
ClientMessageType[ClientMessageType.FetchStorage = 200] = "FetchStorage", ClientMessageType[ClientMessageType.UpdateStorage = 201] = "UpdateStorage";
|
|
11
|
+
}(ClientMessageType || (ClientMessageType = {})), function(CrdtType) {
|
|
12
|
+
CrdtType[CrdtType.Object = 0] = "Object", CrdtType[CrdtType.List = 1] = "List",
|
|
13
|
+
CrdtType[CrdtType.Map = 2] = "Map", CrdtType[CrdtType.Register = 3] = "Register";
|
|
14
|
+
}(CrdtType || (CrdtType = {})), function(OpType) {
|
|
15
|
+
OpType[OpType.Init = 0] = "Init", OpType[OpType.SetParentKey = 1] = "SetParentKey",
|
|
16
|
+
OpType[OpType.CreateList = 2] = "CreateList", OpType[OpType.UpdateObject = 3] = "UpdateObject",
|
|
17
|
+
OpType[OpType.CreateObject = 4] = "CreateObject", OpType[OpType.DeleteCrdt = 5] = "DeleteCrdt",
|
|
18
|
+
OpType[OpType.DeleteObjectKey = 6] = "DeleteObjectKey", OpType[OpType.CreateMap = 7] = "CreateMap",
|
|
19
|
+
OpType[OpType.CreateRegister = 8] = "CreateRegister";
|
|
20
|
+
}(OpType || (OpType = {})), function(WebsocketCloseCodes) {
|
|
21
|
+
WebsocketCloseCodes[WebsocketCloseCodes.CLOSE_ABNORMAL = 1006] = "CLOSE_ABNORMAL",
|
|
22
|
+
WebsocketCloseCodes[WebsocketCloseCodes.INVALID_MESSAGE_FORMAT = 4e3] = "INVALID_MESSAGE_FORMAT",
|
|
23
|
+
WebsocketCloseCodes[WebsocketCloseCodes.NOT_ALLOWED = 4001] = "NOT_ALLOWED", WebsocketCloseCodes[WebsocketCloseCodes.MAX_NUMBER_OF_MESSAGES_PER_SECONDS = 4002] = "MAX_NUMBER_OF_MESSAGES_PER_SECONDS",
|
|
24
|
+
WebsocketCloseCodes[WebsocketCloseCodes.MAX_NUMBER_OF_CONCURRENT_CONNECTIONS = 4003] = "MAX_NUMBER_OF_CONCURRENT_CONNECTIONS",
|
|
25
|
+
WebsocketCloseCodes[WebsocketCloseCodes.MAX_NUMBER_OF_MESSAGES_PER_DAY_PER_APP = 4004] = "MAX_NUMBER_OF_MESSAGES_PER_DAY_PER_APP",
|
|
26
|
+
WebsocketCloseCodes[WebsocketCloseCodes.MAX_NUMBER_OF_CONCURRENT_CONNECTIONS_PER_ROOM = 4005] = "MAX_NUMBER_OF_CONCURRENT_CONNECTIONS_PER_ROOM",
|
|
27
|
+
WebsocketCloseCodes[WebsocketCloseCodes.CLOSE_WITHOUT_RETRY = 4999] = "CLOSE_WITHOUT_RETRY";
|
|
28
|
+
}(WebsocketCloseCodes || (WebsocketCloseCodes = {}));
|
|
48
29
|
|
|
49
30
|
class AbstractCrdt {
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
case OpType.DeleteCrdt: {
|
|
83
|
-
if (this._parent != null && this._parentKey != null) {
|
|
84
|
-
return this._parent._detachChild(this);
|
|
85
|
-
}
|
|
86
|
-
return { modified: false };
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
return { modified: false };
|
|
90
|
-
}
|
|
91
|
-
/**
|
|
92
|
-
* @internal
|
|
93
|
-
*/
|
|
94
|
-
_setParentLink(parent, key) {
|
|
95
|
-
if (this.__parent != null && this.__parent !== parent) {
|
|
96
|
-
throw new Error("Cannot attach parent if it already exist");
|
|
97
|
-
}
|
|
98
|
-
this.__parentKey = key;
|
|
99
|
-
this.__parent = parent;
|
|
100
|
-
}
|
|
101
|
-
/**
|
|
102
|
-
* @internal
|
|
103
|
-
*/
|
|
104
|
-
_attach(id, doc) {
|
|
105
|
-
if (this.__id || this.__doc) {
|
|
106
|
-
throw new Error("Cannot attach if CRDT is already attached");
|
|
107
|
-
}
|
|
108
|
-
doc.addItem(id, this);
|
|
109
|
-
this.__id = id;
|
|
110
|
-
this.__doc = doc;
|
|
111
|
-
}
|
|
112
|
-
/**
|
|
113
|
-
* @internal
|
|
114
|
-
*/
|
|
115
|
-
_detach() {
|
|
116
|
-
if (this.__doc && this.__id) {
|
|
117
|
-
this.__doc.deleteItem(this.__id);
|
|
118
|
-
}
|
|
119
|
-
this.__parent = undefined;
|
|
120
|
-
this.__doc = undefined;
|
|
121
|
-
}
|
|
31
|
+
get _doc() {
|
|
32
|
+
return this.__doc;
|
|
33
|
+
}
|
|
34
|
+
get roomId() {
|
|
35
|
+
return this.__doc ? this.__doc.roomId : null;
|
|
36
|
+
}
|
|
37
|
+
get _id() {
|
|
38
|
+
return this.__id;
|
|
39
|
+
}
|
|
40
|
+
get _parent() {
|
|
41
|
+
return this.__parent;
|
|
42
|
+
}
|
|
43
|
+
get _parentKey() {
|
|
44
|
+
return this.__parentKey;
|
|
45
|
+
}
|
|
46
|
+
_apply(op, _isLocal) {
|
|
47
|
+
return op.type === OpType.DeleteCrdt && null != this._parent && null != this._parentKey ? this._parent._detachChild(this) : {
|
|
48
|
+
modified: !1
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
_setParentLink(parent, key) {
|
|
52
|
+
if (null != this.__parent && this.__parent !== parent) throw new Error("Cannot attach parent if it already exist");
|
|
53
|
+
this.__parentKey = key, this.__parent = parent;
|
|
54
|
+
}
|
|
55
|
+
_attach(id, doc) {
|
|
56
|
+
if (this.__id || this.__doc) throw new Error("Cannot attach if CRDT is already attached");
|
|
57
|
+
doc.addItem(id, this), this.__id = id, this.__doc = doc;
|
|
58
|
+
}
|
|
59
|
+
_detach() {
|
|
60
|
+
this.__doc && this.__id && this.__doc.deleteItem(this.__id), this.__parent = void 0,
|
|
61
|
+
this.__doc = void 0;
|
|
62
|
+
}
|
|
122
63
|
}
|
|
123
64
|
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
}
|
|
131
|
-
// Insert at the end
|
|
132
|
-
if (before != null && after == null) {
|
|
133
|
-
return getNextPosition(before);
|
|
134
|
-
}
|
|
135
|
-
// Insert at the start
|
|
136
|
-
if (before == null && after != null) {
|
|
137
|
-
return getPreviousPosition(after);
|
|
138
|
-
}
|
|
139
|
-
return pos(makePositionFromCodes(posCodes(before), posCodes(after)));
|
|
65
|
+
function parseJson(rawMessage) {
|
|
66
|
+
try {
|
|
67
|
+
return JSON.parse(rawMessage);
|
|
68
|
+
} catch (e) {
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
140
71
|
}
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
for (let i = 0; i < afterCodes.length; i++) {
|
|
145
|
-
const code = afterCodes[i];
|
|
146
|
-
if (code <= min + 1) {
|
|
147
|
-
result.push(min);
|
|
148
|
-
if (afterCodes.length - 1 === i) {
|
|
149
|
-
result.push(max);
|
|
150
|
-
break;
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
else {
|
|
154
|
-
result.push(code - 1);
|
|
155
|
-
break;
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
return pos(result);
|
|
72
|
+
|
|
73
|
+
function isJsonArray(data) {
|
|
74
|
+
return Array.isArray(data);
|
|
159
75
|
}
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
76
|
+
|
|
77
|
+
function isJsonObject(data) {
|
|
78
|
+
return null !== data && "object" == typeof data && !isJsonArray(data);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
class LiveRegister extends AbstractCrdt {
|
|
82
|
+
constructor(data) {
|
|
83
|
+
super(), this._data = data;
|
|
84
|
+
}
|
|
85
|
+
get data() {
|
|
86
|
+
return this._data;
|
|
87
|
+
}
|
|
88
|
+
static _deserialize([id, item], _parentToChildren, doc) {
|
|
89
|
+
if (item.type !== CrdtType.Register) throw new Error(`Tried to deserialize a map but item type is "${item.type}"`);
|
|
90
|
+
const register = new LiveRegister(item.data);
|
|
91
|
+
return register._attach(id, doc), register;
|
|
92
|
+
}
|
|
93
|
+
_serialize(parentId, parentKey, doc, intent) {
|
|
94
|
+
if (null == this._id || null == parentId || null == parentKey) throw new Error("Cannot serialize register if parentId or parentKey is undefined");
|
|
95
|
+
return [ {
|
|
96
|
+
type: OpType.CreateRegister,
|
|
97
|
+
opId: null == doc ? void 0 : doc.generateOpId(),
|
|
98
|
+
id: this._id,
|
|
99
|
+
intent: intent,
|
|
100
|
+
parentId: parentId,
|
|
101
|
+
parentKey: parentKey,
|
|
102
|
+
data: this.data
|
|
103
|
+
} ];
|
|
104
|
+
}
|
|
105
|
+
_toSerializedCrdt() {
|
|
106
|
+
var _a;
|
|
107
|
+
return {
|
|
108
|
+
type: CrdtType.Register,
|
|
109
|
+
parentId: null === (_a = this._parent) || void 0 === _a ? void 0 : _a._id,
|
|
110
|
+
parentKey: this._parentKey,
|
|
111
|
+
data: this.data
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
_attachChild(_op, _isLocal) {
|
|
115
|
+
throw new Error("Method not implemented.");
|
|
116
|
+
}
|
|
117
|
+
_detachChild(_crdt) {
|
|
118
|
+
throw new Error("Method not implemented.");
|
|
119
|
+
}
|
|
120
|
+
_apply(op, isLocal) {
|
|
121
|
+
return super._apply(op, isLocal);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
function makePosition(before, after) {
|
|
126
|
+
return null == before && null == after ? pos([ 33 ]) : null != before && null == after ? function(before) {
|
|
127
|
+
const result = [], beforeCodes = posCodes(before);
|
|
128
|
+
for (let i = 0; i < beforeCodes.length; i++) {
|
|
129
|
+
const code = beforeCodes[i];
|
|
130
|
+
if (126 !== code) {
|
|
131
|
+
result.push(code + 1);
|
|
132
|
+
break;
|
|
133
|
+
}
|
|
134
|
+
if (result.push(code), beforeCodes.length - 1 === i) {
|
|
135
|
+
result.push(33);
|
|
136
|
+
break;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
return pos(result);
|
|
140
|
+
}(before) : null == before && null != after ? function(after) {
|
|
141
|
+
const result = [], afterCodes = posCodes(after);
|
|
142
|
+
for (let i = 0; i < afterCodes.length; i++) {
|
|
143
|
+
const code = afterCodes[i];
|
|
144
|
+
if (!(code <= 33)) {
|
|
145
|
+
result.push(code - 1);
|
|
146
|
+
break;
|
|
147
|
+
}
|
|
148
|
+
if (result.push(32), afterCodes.length - 1 === i) {
|
|
149
|
+
result.push(126);
|
|
150
|
+
break;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
return pos(result);
|
|
154
|
+
}(after) : pos(makePositionFromCodes(posCodes(before), posCodes(after)));
|
|
178
155
|
}
|
|
156
|
+
|
|
179
157
|
function makePositionFromCodes(before, after) {
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
const mid = (afterDigit + beforeDigit) >> 1;
|
|
199
|
-
result.push(mid);
|
|
200
|
-
break;
|
|
201
|
-
}
|
|
202
|
-
return result;
|
|
158
|
+
let index = 0;
|
|
159
|
+
const result = [];
|
|
160
|
+
for (;;) {
|
|
161
|
+
const beforeDigit = before[index] || 32, afterDigit = after[index] || 126;
|
|
162
|
+
if (beforeDigit > afterDigit) throw new Error(`Impossible to generate position between ${before} and ${after}`);
|
|
163
|
+
if (beforeDigit === afterDigit) {
|
|
164
|
+
result.push(beforeDigit), index++;
|
|
165
|
+
continue;
|
|
166
|
+
}
|
|
167
|
+
if (afterDigit - beforeDigit == 1) {
|
|
168
|
+
result.push(beforeDigit), result.push(...makePositionFromCodes(before.slice(index + 1), []));
|
|
169
|
+
break;
|
|
170
|
+
}
|
|
171
|
+
const mid = afterDigit + beforeDigit >> 1;
|
|
172
|
+
result.push(mid);
|
|
173
|
+
break;
|
|
174
|
+
}
|
|
175
|
+
return result;
|
|
203
176
|
}
|
|
177
|
+
|
|
204
178
|
function posCodes(str) {
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
}
|
|
209
|
-
return codes;
|
|
179
|
+
const codes = [];
|
|
180
|
+
for (let i = 0; i < str.length; i++) codes.push(str.charCodeAt(i));
|
|
181
|
+
return codes;
|
|
210
182
|
}
|
|
183
|
+
|
|
211
184
|
function pos(codes) {
|
|
212
|
-
|
|
213
|
-
}
|
|
214
|
-
function compare(posA, posB) {
|
|
215
|
-
const aCodes = posCodes(posA);
|
|
216
|
-
const bCodes = posCodes(posB);
|
|
217
|
-
const maxLength = Math.max(aCodes.length, bCodes.length);
|
|
218
|
-
for (let i = 0; i < maxLength; i++) {
|
|
219
|
-
const a = aCodes[i] == null ? min : aCodes[i];
|
|
220
|
-
const b = bCodes[i] == null ? min : bCodes[i];
|
|
221
|
-
if (a === b) {
|
|
222
|
-
continue;
|
|
223
|
-
}
|
|
224
|
-
else {
|
|
225
|
-
return a - b;
|
|
226
|
-
}
|
|
227
|
-
}
|
|
228
|
-
throw new Error(`Impossible to compare similar position "${posA}" and "${posB}"`);
|
|
185
|
+
return String.fromCharCode(...codes);
|
|
229
186
|
}
|
|
230
187
|
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
}
|
|
239
|
-
get data() {
|
|
240
|
-
return this._data;
|
|
241
|
-
}
|
|
242
|
-
/**
|
|
243
|
-
* INTERNAL
|
|
244
|
-
*/
|
|
245
|
-
static _deserialize([id, item], _parentToChildren, doc) {
|
|
246
|
-
if (item.type !== CrdtType.Register) {
|
|
247
|
-
throw new Error(`Tried to deserialize a map but item type is "${item.type}"`);
|
|
248
|
-
}
|
|
249
|
-
const register = new LiveRegister(item.data);
|
|
250
|
-
register._attach(id, doc);
|
|
251
|
-
return register;
|
|
252
|
-
}
|
|
253
|
-
/**
|
|
254
|
-
* INTERNAL
|
|
255
|
-
*/
|
|
256
|
-
_serialize(parentId, parentKey, doc, intent) {
|
|
257
|
-
if (this._id == null || parentId == null || parentKey == null) {
|
|
258
|
-
throw new Error("Cannot serialize register if parentId or parentKey is undefined");
|
|
259
|
-
}
|
|
260
|
-
return [
|
|
261
|
-
{
|
|
262
|
-
type: OpType.CreateRegister,
|
|
263
|
-
opId: doc === null || doc === void 0 ? void 0 : doc.generateOpId(),
|
|
264
|
-
id: this._id,
|
|
265
|
-
intent,
|
|
266
|
-
parentId,
|
|
267
|
-
parentKey,
|
|
268
|
-
data: this.data,
|
|
269
|
-
},
|
|
270
|
-
];
|
|
271
|
-
}
|
|
272
|
-
/**
|
|
273
|
-
* INTERNAL
|
|
274
|
-
*/
|
|
275
|
-
_toSerializedCrdt() {
|
|
276
|
-
var _a;
|
|
277
|
-
return {
|
|
278
|
-
type: CrdtType.Register,
|
|
279
|
-
parentId: (_a = this._parent) === null || _a === void 0 ? void 0 : _a._id,
|
|
280
|
-
parentKey: this._parentKey,
|
|
281
|
-
data: this.data,
|
|
282
|
-
};
|
|
283
|
-
}
|
|
284
|
-
_attachChild(_op, _isLocal) {
|
|
285
|
-
throw new Error("Method not implemented.");
|
|
286
|
-
}
|
|
287
|
-
_detachChild(_crdt) {
|
|
288
|
-
throw new Error("Method not implemented.");
|
|
289
|
-
}
|
|
290
|
-
_apply(op, isLocal) {
|
|
291
|
-
return super._apply(op, isLocal);
|
|
292
|
-
}
|
|
188
|
+
function compare(posA, posB) {
|
|
189
|
+
const aCodes = posCodes(posA), bCodes = posCodes(posB), maxLength = Math.max(aCodes.length, bCodes.length);
|
|
190
|
+
for (let i = 0; i < maxLength; i++) {
|
|
191
|
+
const a = null == aCodes[i] ? 32 : aCodes[i], b = null == bCodes[i] ? 32 : bCodes[i];
|
|
192
|
+
if (a !== b) return a - b;
|
|
193
|
+
}
|
|
194
|
+
throw new Error(`Impossible to compare similar position "${posA}" and "${posB}"`);
|
|
293
195
|
}
|
|
294
196
|
|
|
295
|
-
/**
|
|
296
|
-
* The LiveList class represents an ordered collection of items that is synchronized across clients.
|
|
297
|
-
*/
|
|
298
197
|
class LiveList extends AbstractCrdt {
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
}
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
{
|
|
623
|
-
index: newIndex,
|
|
624
|
-
previousIndex: index,
|
|
625
|
-
item: item[0],
|
|
626
|
-
type: "move",
|
|
627
|
-
},
|
|
628
|
-
],
|
|
629
|
-
});
|
|
630
|
-
this._doc.dispatch([
|
|
631
|
-
{
|
|
632
|
-
type: OpType.SetParentKey,
|
|
633
|
-
id: item[0]._id,
|
|
634
|
-
opId: this._doc.generateOpId(),
|
|
635
|
-
parentKey: position,
|
|
636
|
-
},
|
|
637
|
-
], [
|
|
638
|
-
{
|
|
639
|
-
type: OpType.SetParentKey,
|
|
640
|
-
id: item[0]._id,
|
|
641
|
-
parentKey: previousPosition,
|
|
642
|
-
},
|
|
643
|
-
], storageUpdates);
|
|
644
|
-
}
|
|
645
|
-
}
|
|
646
|
-
/**
|
|
647
|
-
* Deletes an element at the specified index
|
|
648
|
-
* @param index The index of the element to delete
|
|
649
|
-
*/
|
|
650
|
-
delete(index) {
|
|
651
|
-
if (index < 0 || index >= this._items.length) {
|
|
652
|
-
throw new Error(`Cannot delete list item at index "${index}". index should be between 0 and ${this._items.length - 1}`);
|
|
653
|
-
}
|
|
654
|
-
const item = this._items[index];
|
|
655
|
-
item[0]._detach();
|
|
656
|
-
this._items.splice(index, 1);
|
|
657
|
-
if (this._doc) {
|
|
658
|
-
const childRecordId = item[0]._id;
|
|
659
|
-
if (childRecordId) {
|
|
660
|
-
const storageUpdates = new Map();
|
|
661
|
-
storageUpdates.set(this._id, {
|
|
662
|
-
node: this,
|
|
663
|
-
type: "LiveList",
|
|
664
|
-
updates: [{ index: index, type: "delete" }],
|
|
665
|
-
});
|
|
666
|
-
this._doc.dispatch([
|
|
667
|
-
{
|
|
668
|
-
id: childRecordId,
|
|
669
|
-
opId: this._doc.generateOpId(),
|
|
670
|
-
type: OpType.DeleteCrdt,
|
|
671
|
-
},
|
|
672
|
-
], item[0]._serialize(this._id, item[1]), storageUpdates);
|
|
673
|
-
}
|
|
674
|
-
}
|
|
675
|
-
}
|
|
676
|
-
clear() {
|
|
677
|
-
if (this._doc) {
|
|
678
|
-
const ops = [];
|
|
679
|
-
const reverseOps = [];
|
|
680
|
-
const updateDelta = [];
|
|
681
|
-
let i = 0;
|
|
682
|
-
for (const item of this._items) {
|
|
683
|
-
item[0]._detach();
|
|
684
|
-
const childId = item[0]._id;
|
|
685
|
-
if (childId) {
|
|
686
|
-
ops.push({ id: childId, type: OpType.DeleteCrdt });
|
|
687
|
-
reverseOps.push(...item[0]._serialize(this._id, item[1]));
|
|
688
|
-
updateDelta.push({ index: i, type: "delete" });
|
|
689
|
-
}
|
|
690
|
-
i++;
|
|
691
|
-
}
|
|
692
|
-
this._items = [];
|
|
693
|
-
const storageUpdates = new Map();
|
|
694
|
-
storageUpdates.set(this._id, {
|
|
695
|
-
node: this,
|
|
696
|
-
type: "LiveList",
|
|
697
|
-
updates: updateDelta,
|
|
698
|
-
});
|
|
699
|
-
this._doc.dispatch(ops, reverseOps, storageUpdates);
|
|
700
|
-
}
|
|
701
|
-
else {
|
|
702
|
-
for (const item of this._items) {
|
|
703
|
-
item[0]._detach();
|
|
704
|
-
}
|
|
705
|
-
this._items = [];
|
|
706
|
-
}
|
|
707
|
-
}
|
|
708
|
-
set(index, item) {
|
|
709
|
-
if (index < 0 || index >= this._items.length) {
|
|
710
|
-
throw new Error(`Cannot set list item at index "${index}". index should be between 0 and ${this._items.length - 1}`);
|
|
711
|
-
}
|
|
712
|
-
const [existingItem, position] = this._items[index];
|
|
713
|
-
existingItem._detach();
|
|
714
|
-
const value = selfOrRegister(item);
|
|
715
|
-
value._setParentLink(this, position);
|
|
716
|
-
this._items[index][0] = value;
|
|
717
|
-
if (this._doc && this._id) {
|
|
718
|
-
const id = this._doc.generateId();
|
|
719
|
-
value._attach(id, this._doc);
|
|
720
|
-
const storageUpdates = new Map();
|
|
721
|
-
storageUpdates.set(this._id, {
|
|
722
|
-
node: this,
|
|
723
|
-
type: "LiveList",
|
|
724
|
-
updates: [
|
|
725
|
-
{
|
|
726
|
-
index,
|
|
727
|
-
item: value instanceof LiveRegister ? value.data : value,
|
|
728
|
-
type: "set",
|
|
729
|
-
},
|
|
730
|
-
],
|
|
731
|
-
});
|
|
732
|
-
this._doc.dispatch(value._serialize(this._id, position, this._doc, "set"), existingItem._serialize(this._id, position, undefined, "set"), storageUpdates);
|
|
733
|
-
}
|
|
734
|
-
}
|
|
735
|
-
/**
|
|
736
|
-
* Returns an Array of all the elements in the LiveList.
|
|
737
|
-
*/
|
|
738
|
-
toArray() {
|
|
739
|
-
return this._items.map((entry) => selfOrRegisterValue(entry[0]));
|
|
740
|
-
}
|
|
741
|
-
/**
|
|
742
|
-
* Tests whether all elements pass the test implemented by the provided function.
|
|
743
|
-
* @param predicate Function to test for each element, taking two arguments (the element and its index).
|
|
744
|
-
* @returns true if the predicate function returns a truthy value for every element. Otherwise, false.
|
|
745
|
-
*/
|
|
746
|
-
every(predicate) {
|
|
747
|
-
return this.toArray().every(predicate);
|
|
748
|
-
}
|
|
749
|
-
/**
|
|
750
|
-
* Creates an array with all elements that pass the test implemented by the provided function.
|
|
751
|
-
* @param predicate Function to test each element of the LiveList. Return a value that coerces to true to keep the element, or to false otherwise.
|
|
752
|
-
* @returns An array with the elements that pass the test.
|
|
753
|
-
*/
|
|
754
|
-
filter(predicate) {
|
|
755
|
-
return this.toArray().filter(predicate);
|
|
756
|
-
}
|
|
757
|
-
/**
|
|
758
|
-
* Returns the first element that satisfies the provided testing function.
|
|
759
|
-
* @param predicate Function to execute on each value.
|
|
760
|
-
* @returns The value of the first element in the LiveList that satisfies the provided testing function. Otherwise, undefined is returned.
|
|
761
|
-
*/
|
|
762
|
-
find(predicate) {
|
|
763
|
-
return this.toArray().find(predicate);
|
|
764
|
-
}
|
|
765
|
-
/**
|
|
766
|
-
* Returns the index of the first element in the LiveList that satisfies the provided testing function.
|
|
767
|
-
* @param predicate Function to execute on each value until the function returns true, indicating that the satisfying element was found.
|
|
768
|
-
* @returns The index of the first element in the LiveList that passes the test. Otherwise, -1.
|
|
769
|
-
*/
|
|
770
|
-
findIndex(predicate) {
|
|
771
|
-
return this.toArray().findIndex(predicate);
|
|
772
|
-
}
|
|
773
|
-
/**
|
|
774
|
-
* Executes a provided function once for each element.
|
|
775
|
-
* @param callbackfn Function to execute on each element.
|
|
776
|
-
*/
|
|
777
|
-
forEach(callbackfn) {
|
|
778
|
-
return this.toArray().forEach(callbackfn);
|
|
779
|
-
}
|
|
780
|
-
/**
|
|
781
|
-
* Get the element at the specified index.
|
|
782
|
-
* @param index The index on the element to get.
|
|
783
|
-
* @returns The element at the specified index or undefined.
|
|
784
|
-
*/
|
|
785
|
-
get(index) {
|
|
786
|
-
if (index < 0 || index >= this._items.length) {
|
|
787
|
-
return undefined;
|
|
788
|
-
}
|
|
789
|
-
return selfOrRegisterValue(this._items[index][0]);
|
|
790
|
-
}
|
|
791
|
-
/**
|
|
792
|
-
* Returns the first index at which a given element can be found in the LiveList, or -1 if it is not present.
|
|
793
|
-
* @param searchElement Element to locate.
|
|
794
|
-
* @param fromIndex The index to start the search at.
|
|
795
|
-
* @returns The first index of the element in the LiveList; -1 if not found.
|
|
796
|
-
*/
|
|
797
|
-
indexOf(searchElement, fromIndex) {
|
|
798
|
-
return this.toArray().indexOf(searchElement, fromIndex);
|
|
799
|
-
}
|
|
800
|
-
/**
|
|
801
|
-
* Returns the last index at which a given element can be found in the LiveList, or -1 if it is not present. The LiveLsit is searched backwards, starting at fromIndex.
|
|
802
|
-
* @param searchElement Element to locate.
|
|
803
|
-
* @param fromIndex The index at which to start searching backwards.
|
|
804
|
-
* @returns
|
|
805
|
-
*/
|
|
806
|
-
lastIndexOf(searchElement, fromIndex) {
|
|
807
|
-
return this.toArray().lastIndexOf(searchElement, fromIndex);
|
|
808
|
-
}
|
|
809
|
-
/**
|
|
810
|
-
* Creates an array populated with the results of calling a provided function on every element.
|
|
811
|
-
* @param callback Function that is called for every element.
|
|
812
|
-
* @returns An array with each element being the result of the callback function.
|
|
813
|
-
*/
|
|
814
|
-
map(callback) {
|
|
815
|
-
return this._items.map((entry, i) => callback(selfOrRegisterValue(entry[0]), i));
|
|
816
|
-
}
|
|
817
|
-
/**
|
|
818
|
-
* Tests whether at least one element in the LiveList passes the test implemented by the provided function.
|
|
819
|
-
* @param predicate Function to test for each element.
|
|
820
|
-
* @returns true if the callback function returns a truthy value for at least one element. Otherwise, false.
|
|
821
|
-
*/
|
|
822
|
-
some(predicate) {
|
|
823
|
-
return this.toArray().some(predicate);
|
|
824
|
-
}
|
|
825
|
-
[Symbol.iterator]() {
|
|
826
|
-
return new LiveListIterator(this._items);
|
|
827
|
-
}
|
|
198
|
+
constructor(items = []) {
|
|
199
|
+
let position;
|
|
200
|
+
super(), this._items = [];
|
|
201
|
+
for (let i = 0; i < items.length; i++) {
|
|
202
|
+
const newPosition = makePosition(position), item = selfOrRegister(items[i]);
|
|
203
|
+
this._items.push([ item, newPosition ]), position = newPosition;
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
static _deserialize([id], parentToChildren, doc) {
|
|
207
|
+
const list = new LiveList([]);
|
|
208
|
+
list._attach(id, doc);
|
|
209
|
+
const children = parentToChildren.get(id);
|
|
210
|
+
if (null == children) return list;
|
|
211
|
+
for (const entry of children) {
|
|
212
|
+
const child = deserialize(entry, parentToChildren, doc);
|
|
213
|
+
child._setParentLink(list, entry[1].parentKey), list._items.push([ child, entry[1].parentKey ]),
|
|
214
|
+
list._items.sort(((itemA, itemB) => compare(itemA[1], itemB[1])));
|
|
215
|
+
}
|
|
216
|
+
return list;
|
|
217
|
+
}
|
|
218
|
+
_serialize(parentId, parentKey, doc, intent) {
|
|
219
|
+
if (null == this._id) throw new Error("Cannot serialize item is not attached");
|
|
220
|
+
if (null == parentId || null == parentKey) throw new Error("Cannot serialize list if parentId or parentKey is undefined");
|
|
221
|
+
const ops = [], op = {
|
|
222
|
+
id: this._id,
|
|
223
|
+
opId: null == doc ? void 0 : doc.generateOpId(),
|
|
224
|
+
intent: intent,
|
|
225
|
+
type: OpType.CreateList,
|
|
226
|
+
parentId: parentId,
|
|
227
|
+
parentKey: parentKey
|
|
228
|
+
};
|
|
229
|
+
ops.push(op);
|
|
230
|
+
for (const [value, key] of this._items) ops.push(...value._serialize(this._id, key, doc));
|
|
231
|
+
return ops;
|
|
232
|
+
}
|
|
233
|
+
_indexOfPosition(position) {
|
|
234
|
+
return this._items.findIndex((item => item[1] === position));
|
|
235
|
+
}
|
|
236
|
+
_attach(id, doc) {
|
|
237
|
+
super._attach(id, doc);
|
|
238
|
+
for (const [item] of this._items) item._attach(doc.generateId(), doc);
|
|
239
|
+
}
|
|
240
|
+
_detach() {
|
|
241
|
+
super._detach();
|
|
242
|
+
for (const [value] of this._items) value._detach();
|
|
243
|
+
}
|
|
244
|
+
_attachChild(op, isLocal) {
|
|
245
|
+
var _a;
|
|
246
|
+
if (null == this._doc) throw new Error("Can't attach child if doc is not present");
|
|
247
|
+
const {id: id, parentKey: parentKey, intent: intent} = op, key = parentKey, child = creationOpToLiveStructure(op);
|
|
248
|
+
if (void 0 !== this._doc.getItem(id)) return {
|
|
249
|
+
modified: !1
|
|
250
|
+
};
|
|
251
|
+
child._attach(id, this._doc), child._setParentLink(this, key);
|
|
252
|
+
const index = this._items.findIndex((entry => entry[1] === key));
|
|
253
|
+
let newKey = key;
|
|
254
|
+
if (-1 !== index) {
|
|
255
|
+
if ("set" === intent) {
|
|
256
|
+
const existingItem = this._items[index][0];
|
|
257
|
+
existingItem._detach();
|
|
258
|
+
const storageUpdate = {
|
|
259
|
+
node: this,
|
|
260
|
+
type: "LiveList",
|
|
261
|
+
updates: [ {
|
|
262
|
+
index: index,
|
|
263
|
+
type: "set",
|
|
264
|
+
item: child instanceof LiveRegister ? child.data : child
|
|
265
|
+
} ]
|
|
266
|
+
};
|
|
267
|
+
return this._items[index][0] = child, {
|
|
268
|
+
modified: storageUpdate,
|
|
269
|
+
reverse: existingItem._serialize(this._id, key, this._doc, "set")
|
|
270
|
+
};
|
|
271
|
+
}
|
|
272
|
+
if (isLocal) {
|
|
273
|
+
const before = this._items[index] ? this._items[index][1] : void 0, after = this._items[index + 1] ? this._items[index + 1][1] : void 0;
|
|
274
|
+
newKey = makePosition(before, after), child._setParentLink(this, newKey);
|
|
275
|
+
} else this._items[index][1] = makePosition(key, null === (_a = this._items[index + 1]) || void 0 === _a ? void 0 : _a[1]);
|
|
276
|
+
}
|
|
277
|
+
this._items.push([ child, newKey ]), this._items.sort(((itemA, itemB) => compare(itemA[1], itemB[1])));
|
|
278
|
+
const newIndex = this._items.findIndex((entry => entry[1] === newKey));
|
|
279
|
+
return {
|
|
280
|
+
reverse: [ {
|
|
281
|
+
type: OpType.DeleteCrdt,
|
|
282
|
+
id: id
|
|
283
|
+
} ],
|
|
284
|
+
modified: {
|
|
285
|
+
node: this,
|
|
286
|
+
type: "LiveList",
|
|
287
|
+
updates: [ {
|
|
288
|
+
index: newIndex,
|
|
289
|
+
type: "insert",
|
|
290
|
+
item: child instanceof LiveRegister ? child.data : child
|
|
291
|
+
} ]
|
|
292
|
+
}
|
|
293
|
+
};
|
|
294
|
+
}
|
|
295
|
+
_detachChild(child) {
|
|
296
|
+
if (child) {
|
|
297
|
+
const reverse = child._serialize(this._id, child._parentKey, this._doc), indexToDelete = this._items.findIndex((item => item[0] === child));
|
|
298
|
+
this._items.splice(indexToDelete, 1), child._detach();
|
|
299
|
+
return {
|
|
300
|
+
modified: {
|
|
301
|
+
node: this,
|
|
302
|
+
type: "LiveList",
|
|
303
|
+
updates: [ {
|
|
304
|
+
index: indexToDelete,
|
|
305
|
+
type: "delete"
|
|
306
|
+
} ]
|
|
307
|
+
},
|
|
308
|
+
reverse: reverse
|
|
309
|
+
};
|
|
310
|
+
}
|
|
311
|
+
return {
|
|
312
|
+
modified: !1
|
|
313
|
+
};
|
|
314
|
+
}
|
|
315
|
+
_setChildKey(key, child, previousKey) {
|
|
316
|
+
var _a;
|
|
317
|
+
child._setParentLink(this, key);
|
|
318
|
+
const previousIndex = this._items.findIndex((entry => entry[0]._id === child._id)), index = this._items.findIndex((entry => entry[1] === key));
|
|
319
|
+
-1 !== index && (this._items[index][1] = makePosition(key, null === (_a = this._items[index + 1]) || void 0 === _a ? void 0 : _a[1]));
|
|
320
|
+
const item = this._items.find((item => item[0] === child));
|
|
321
|
+
item && (item[1] = key), this._items.sort(((itemA, itemB) => compare(itemA[1], itemB[1])));
|
|
322
|
+
const newIndex = this._items.findIndex((entry => entry[0]._id === child._id));
|
|
323
|
+
return {
|
|
324
|
+
modified: {
|
|
325
|
+
node: this,
|
|
326
|
+
type: "LiveList",
|
|
327
|
+
updates: newIndex === previousIndex ? [] : [ {
|
|
328
|
+
index: newIndex,
|
|
329
|
+
item: child instanceof LiveRegister ? child.data : child,
|
|
330
|
+
previousIndex: previousIndex,
|
|
331
|
+
type: "move"
|
|
332
|
+
} ]
|
|
333
|
+
},
|
|
334
|
+
reverse: [ {
|
|
335
|
+
type: OpType.SetParentKey,
|
|
336
|
+
id: null == item ? void 0 : item[0]._id,
|
|
337
|
+
parentKey: previousKey
|
|
338
|
+
} ]
|
|
339
|
+
};
|
|
340
|
+
}
|
|
341
|
+
_apply(op, isLocal) {
|
|
342
|
+
return super._apply(op, isLocal);
|
|
343
|
+
}
|
|
344
|
+
_toSerializedCrdt() {
|
|
345
|
+
var _a;
|
|
346
|
+
return {
|
|
347
|
+
type: CrdtType.List,
|
|
348
|
+
parentId: null === (_a = this._parent) || void 0 === _a ? void 0 : _a._id,
|
|
349
|
+
parentKey: this._parentKey
|
|
350
|
+
};
|
|
351
|
+
}
|
|
352
|
+
get length() {
|
|
353
|
+
return this._items.length;
|
|
354
|
+
}
|
|
355
|
+
push(element) {
|
|
356
|
+
return this.insert(element, this.length);
|
|
357
|
+
}
|
|
358
|
+
insert(element, index) {
|
|
359
|
+
if (index < 0 || index > this._items.length) throw new Error(`Cannot insert list item at index "${index}". index should be between 0 and ${this._items.length}`);
|
|
360
|
+
const position = makePosition(this._items[index - 1] ? this._items[index - 1][1] : void 0, this._items[index] ? this._items[index][1] : void 0), value = selfOrRegister(element);
|
|
361
|
+
value._setParentLink(this, position), this._items.push([ value, position ]), this._items.sort(((itemA, itemB) => compare(itemA[1], itemB[1])));
|
|
362
|
+
const newIndex = this._items.findIndex((entry => entry[1] === position));
|
|
363
|
+
if (this._doc && this._id) {
|
|
364
|
+
const id = this._doc.generateId();
|
|
365
|
+
value._attach(id, this._doc);
|
|
366
|
+
const storageUpdates = new Map;
|
|
367
|
+
storageUpdates.set(this._id, {
|
|
368
|
+
node: this,
|
|
369
|
+
type: "LiveList",
|
|
370
|
+
updates: [ {
|
|
371
|
+
index: newIndex,
|
|
372
|
+
item: value instanceof LiveRegister ? value.data : value,
|
|
373
|
+
type: "insert"
|
|
374
|
+
} ]
|
|
375
|
+
}), this._doc.dispatch(value._serialize(this._id, position, this._doc), [ {
|
|
376
|
+
type: OpType.DeleteCrdt,
|
|
377
|
+
id: id
|
|
378
|
+
} ], storageUpdates);
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
move(index, targetIndex) {
|
|
382
|
+
if (targetIndex < 0) throw new Error("targetIndex cannot be less than 0");
|
|
383
|
+
if (targetIndex >= this._items.length) throw new Error("targetIndex cannot be greater or equal than the list length");
|
|
384
|
+
if (index < 0) throw new Error("index cannot be less than 0");
|
|
385
|
+
if (index >= this._items.length) throw new Error("index cannot be greater or equal than the list length");
|
|
386
|
+
let beforePosition = null, afterPosition = null;
|
|
387
|
+
index < targetIndex ? (afterPosition = targetIndex === this._items.length - 1 ? void 0 : this._items[targetIndex + 1][1],
|
|
388
|
+
beforePosition = this._items[targetIndex][1]) : (afterPosition = this._items[targetIndex][1],
|
|
389
|
+
beforePosition = 0 === targetIndex ? void 0 : this._items[targetIndex - 1][1]);
|
|
390
|
+
const position = makePosition(beforePosition, afterPosition), item = this._items[index], previousPosition = item[1];
|
|
391
|
+
item[1] = position, item[0]._setParentLink(this, position), this._items.sort(((itemA, itemB) => compare(itemA[1], itemB[1])));
|
|
392
|
+
const newIndex = this._items.findIndex((entry => entry[1] === position));
|
|
393
|
+
if (this._doc && this._id) {
|
|
394
|
+
const storageUpdates = new Map;
|
|
395
|
+
storageUpdates.set(this._id, {
|
|
396
|
+
node: this,
|
|
397
|
+
type: "LiveList",
|
|
398
|
+
updates: [ {
|
|
399
|
+
index: newIndex,
|
|
400
|
+
previousIndex: index,
|
|
401
|
+
item: item[0],
|
|
402
|
+
type: "move"
|
|
403
|
+
} ]
|
|
404
|
+
}), this._doc.dispatch([ {
|
|
405
|
+
type: OpType.SetParentKey,
|
|
406
|
+
id: item[0]._id,
|
|
407
|
+
opId: this._doc.generateOpId(),
|
|
408
|
+
parentKey: position
|
|
409
|
+
} ], [ {
|
|
410
|
+
type: OpType.SetParentKey,
|
|
411
|
+
id: item[0]._id,
|
|
412
|
+
parentKey: previousPosition
|
|
413
|
+
} ], storageUpdates);
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
delete(index) {
|
|
417
|
+
if (index < 0 || index >= this._items.length) throw new Error(`Cannot delete list item at index "${index}". index should be between 0 and ${this._items.length - 1}`);
|
|
418
|
+
const item = this._items[index];
|
|
419
|
+
if (item[0]._detach(), this._items.splice(index, 1), this._doc) {
|
|
420
|
+
const childRecordId = item[0]._id;
|
|
421
|
+
if (childRecordId) {
|
|
422
|
+
const storageUpdates = new Map;
|
|
423
|
+
storageUpdates.set(this._id, {
|
|
424
|
+
node: this,
|
|
425
|
+
type: "LiveList",
|
|
426
|
+
updates: [ {
|
|
427
|
+
index: index,
|
|
428
|
+
type: "delete"
|
|
429
|
+
} ]
|
|
430
|
+
}), this._doc.dispatch([ {
|
|
431
|
+
id: childRecordId,
|
|
432
|
+
opId: this._doc.generateOpId(),
|
|
433
|
+
type: OpType.DeleteCrdt
|
|
434
|
+
} ], item[0]._serialize(this._id, item[1]), storageUpdates);
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
clear() {
|
|
439
|
+
if (this._doc) {
|
|
440
|
+
const ops = [], reverseOps = [], updateDelta = [];
|
|
441
|
+
let i = 0;
|
|
442
|
+
for (const item of this._items) {
|
|
443
|
+
item[0]._detach();
|
|
444
|
+
const childId = item[0]._id;
|
|
445
|
+
childId && (ops.push({
|
|
446
|
+
id: childId,
|
|
447
|
+
type: OpType.DeleteCrdt
|
|
448
|
+
}), reverseOps.push(...item[0]._serialize(this._id, item[1])), updateDelta.push({
|
|
449
|
+
index: i,
|
|
450
|
+
type: "delete"
|
|
451
|
+
})), i++;
|
|
452
|
+
}
|
|
453
|
+
this._items = [];
|
|
454
|
+
const storageUpdates = new Map;
|
|
455
|
+
storageUpdates.set(this._id, {
|
|
456
|
+
node: this,
|
|
457
|
+
type: "LiveList",
|
|
458
|
+
updates: updateDelta
|
|
459
|
+
}), this._doc.dispatch(ops, reverseOps, storageUpdates);
|
|
460
|
+
} else {
|
|
461
|
+
for (const item of this._items) item[0]._detach();
|
|
462
|
+
this._items = [];
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
set(index, item) {
|
|
466
|
+
if (index < 0 || index >= this._items.length) throw new Error(`Cannot set list item at index "${index}". index should be between 0 and ${this._items.length - 1}`);
|
|
467
|
+
const [existingItem, position] = this._items[index];
|
|
468
|
+
existingItem._detach();
|
|
469
|
+
const value = selfOrRegister(item);
|
|
470
|
+
if (value._setParentLink(this, position), this._items[index][0] = value, this._doc && this._id) {
|
|
471
|
+
const id = this._doc.generateId();
|
|
472
|
+
value._attach(id, this._doc);
|
|
473
|
+
const storageUpdates = new Map;
|
|
474
|
+
storageUpdates.set(this._id, {
|
|
475
|
+
node: this,
|
|
476
|
+
type: "LiveList",
|
|
477
|
+
updates: [ {
|
|
478
|
+
index: index,
|
|
479
|
+
item: value instanceof LiveRegister ? value.data : value,
|
|
480
|
+
type: "set"
|
|
481
|
+
} ]
|
|
482
|
+
}), this._doc.dispatch(value._serialize(this._id, position, this._doc, "set"), existingItem._serialize(this._id, position, void 0, "set"), storageUpdates);
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
toArray() {
|
|
486
|
+
return this._items.map((entry => selfOrRegisterValue(entry[0])));
|
|
487
|
+
}
|
|
488
|
+
every(predicate) {
|
|
489
|
+
return this.toArray().every(predicate);
|
|
490
|
+
}
|
|
491
|
+
filter(predicate) {
|
|
492
|
+
return this.toArray().filter(predicate);
|
|
493
|
+
}
|
|
494
|
+
find(predicate) {
|
|
495
|
+
return this.toArray().find(predicate);
|
|
496
|
+
}
|
|
497
|
+
findIndex(predicate) {
|
|
498
|
+
return this.toArray().findIndex(predicate);
|
|
499
|
+
}
|
|
500
|
+
forEach(callbackfn) {
|
|
501
|
+
return this.toArray().forEach(callbackfn);
|
|
502
|
+
}
|
|
503
|
+
get(index) {
|
|
504
|
+
if (!(index < 0 || index >= this._items.length)) return selfOrRegisterValue(this._items[index][0]);
|
|
505
|
+
}
|
|
506
|
+
indexOf(searchElement, fromIndex) {
|
|
507
|
+
return this.toArray().indexOf(searchElement, fromIndex);
|
|
508
|
+
}
|
|
509
|
+
lastIndexOf(searchElement, fromIndex) {
|
|
510
|
+
return this.toArray().lastIndexOf(searchElement, fromIndex);
|
|
511
|
+
}
|
|
512
|
+
map(callback) {
|
|
513
|
+
return this._items.map(((entry, i) => callback(selfOrRegisterValue(entry[0]), i)));
|
|
514
|
+
}
|
|
515
|
+
some(predicate) {
|
|
516
|
+
return this.toArray().some(predicate);
|
|
517
|
+
}
|
|
518
|
+
[Symbol.iterator]() {
|
|
519
|
+
return new LiveListIterator(this._items);
|
|
520
|
+
}
|
|
828
521
|
}
|
|
522
|
+
|
|
829
523
|
class LiveListIterator {
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
value: selfOrRegisterValue(result.value[0]),
|
|
846
|
-
};
|
|
847
|
-
}
|
|
524
|
+
constructor(items) {
|
|
525
|
+
this._innerIterator = items[Symbol.iterator]();
|
|
526
|
+
}
|
|
527
|
+
[Symbol.iterator]() {
|
|
528
|
+
return this;
|
|
529
|
+
}
|
|
530
|
+
next() {
|
|
531
|
+
const result = this._innerIterator.next();
|
|
532
|
+
return result.done ? {
|
|
533
|
+
done: !0,
|
|
534
|
+
value: void 0
|
|
535
|
+
} : {
|
|
536
|
+
value: selfOrRegisterValue(result.value[0])
|
|
537
|
+
};
|
|
538
|
+
}
|
|
848
539
|
}
|
|
849
540
|
|
|
850
|
-
|
|
851
|
-
* The LiveMap class is similar to a JavaScript Map that is synchronized on all clients.
|
|
852
|
-
* Keys should be a string, and values should be serializable to JSON.
|
|
853
|
-
* If multiple clients update the same property simultaneously, the last modification received by the Liveblocks servers is the winner.
|
|
854
|
-
*/
|
|
855
|
-
class LiveMap extends AbstractCrdt {
|
|
856
|
-
constructor(entries) {
|
|
857
|
-
super();
|
|
858
|
-
if (entries) {
|
|
859
|
-
const mappedEntries = [];
|
|
860
|
-
for (const entry of entries) {
|
|
861
|
-
const value = selfOrRegister(entry[1]);
|
|
862
|
-
value._setParentLink(this, entry[0]);
|
|
863
|
-
mappedEntries.push([entry[0], value]);
|
|
864
|
-
}
|
|
865
|
-
this._map = new Map(mappedEntries);
|
|
866
|
-
}
|
|
867
|
-
else {
|
|
868
|
-
this._map = new Map();
|
|
869
|
-
}
|
|
870
|
-
}
|
|
871
|
-
/**
|
|
872
|
-
* @internal
|
|
873
|
-
*/
|
|
874
|
-
_serialize(parentId, parentKey, doc, intent) {
|
|
875
|
-
if (this._id == null) {
|
|
876
|
-
throw new Error("Cannot serialize item is not attached");
|
|
877
|
-
}
|
|
878
|
-
if (parentId == null || parentKey == null) {
|
|
879
|
-
throw new Error("Cannot serialize map if parentId or parentKey is undefined");
|
|
880
|
-
}
|
|
881
|
-
const ops = [];
|
|
882
|
-
const op = {
|
|
883
|
-
id: this._id,
|
|
884
|
-
opId: doc === null || doc === void 0 ? void 0 : doc.generateOpId(),
|
|
885
|
-
type: OpType.CreateMap,
|
|
886
|
-
intent,
|
|
887
|
-
parentId,
|
|
888
|
-
parentKey,
|
|
889
|
-
};
|
|
890
|
-
ops.push(op);
|
|
891
|
-
for (const [key, value] of this._map) {
|
|
892
|
-
ops.push(...value._serialize(this._id, key, doc));
|
|
893
|
-
}
|
|
894
|
-
return ops;
|
|
895
|
-
}
|
|
896
|
-
/**
|
|
897
|
-
* @internal
|
|
898
|
-
*/
|
|
899
|
-
static _deserialize([id, item], parentToChildren, doc) {
|
|
900
|
-
if (item.type !== CrdtType.Map) {
|
|
901
|
-
throw new Error(`Tried to deserialize a map but item type is "${item.type}"`);
|
|
902
|
-
}
|
|
903
|
-
const map = new LiveMap();
|
|
904
|
-
map._attach(id, doc);
|
|
905
|
-
const children = parentToChildren.get(id);
|
|
906
|
-
if (children == null) {
|
|
907
|
-
return map;
|
|
908
|
-
}
|
|
909
|
-
for (const entry of children) {
|
|
910
|
-
const crdt = entry[1];
|
|
911
|
-
if (crdt.parentKey == null) {
|
|
912
|
-
throw new Error("Tried to deserialize a crdt but it does not have a parentKey and is not the root");
|
|
913
|
-
}
|
|
914
|
-
const child = deserialize(entry, parentToChildren, doc);
|
|
915
|
-
child._setParentLink(map, crdt.parentKey);
|
|
916
|
-
map._map.set(crdt.parentKey, child);
|
|
917
|
-
}
|
|
918
|
-
return map;
|
|
919
|
-
}
|
|
920
|
-
/**
|
|
921
|
-
* @internal
|
|
922
|
-
*/
|
|
923
|
-
_attach(id, doc) {
|
|
924
|
-
super._attach(id, doc);
|
|
925
|
-
for (const [_key, value] of this._map) {
|
|
926
|
-
if (isCrdt(value)) {
|
|
927
|
-
value._attach(doc.generateId(), doc);
|
|
928
|
-
}
|
|
929
|
-
}
|
|
930
|
-
}
|
|
931
|
-
/**
|
|
932
|
-
* @internal
|
|
933
|
-
*/
|
|
934
|
-
_attachChild(op, _isLocal) {
|
|
935
|
-
if (this._doc == null) {
|
|
936
|
-
throw new Error("Can't attach child if doc is not present");
|
|
937
|
-
}
|
|
938
|
-
const { id, parentKey } = op;
|
|
939
|
-
const key = parentKey;
|
|
940
|
-
const child = creationOpToLiveStructure(op);
|
|
941
|
-
if (this._doc.getItem(id) !== undefined) {
|
|
942
|
-
return { modified: false };
|
|
943
|
-
}
|
|
944
|
-
const previousValue = this._map.get(key);
|
|
945
|
-
let reverse;
|
|
946
|
-
if (previousValue) {
|
|
947
|
-
reverse = previousValue._serialize(this._id, key);
|
|
948
|
-
previousValue._detach();
|
|
949
|
-
}
|
|
950
|
-
else {
|
|
951
|
-
reverse = [{ type: OpType.DeleteCrdt, id }];
|
|
952
|
-
}
|
|
953
|
-
child._setParentLink(this, key);
|
|
954
|
-
child._attach(id, this._doc);
|
|
955
|
-
this._map.set(key, child);
|
|
956
|
-
return {
|
|
957
|
-
modified: {
|
|
958
|
-
node: this,
|
|
959
|
-
type: "LiveMap",
|
|
960
|
-
updates: { [key]: { type: "update" } },
|
|
961
|
-
},
|
|
962
|
-
reverse,
|
|
963
|
-
};
|
|
964
|
-
}
|
|
965
|
-
/**
|
|
966
|
-
* @internal
|
|
967
|
-
*/
|
|
968
|
-
_detach() {
|
|
969
|
-
super._detach();
|
|
970
|
-
for (const item of this._map.values()) {
|
|
971
|
-
item._detach();
|
|
972
|
-
}
|
|
973
|
-
}
|
|
974
|
-
/**
|
|
975
|
-
* @internal
|
|
976
|
-
*/
|
|
977
|
-
_detachChild(child) {
|
|
978
|
-
const reverse = child._serialize(this._id, child._parentKey, this._doc);
|
|
979
|
-
for (const [key, value] of this._map) {
|
|
980
|
-
if (value === child) {
|
|
981
|
-
this._map.delete(key);
|
|
982
|
-
}
|
|
983
|
-
}
|
|
984
|
-
child._detach();
|
|
985
|
-
const storageUpdate = {
|
|
986
|
-
node: this,
|
|
987
|
-
type: "LiveMap",
|
|
988
|
-
updates: { [child._parentKey]: { type: "delete" } },
|
|
989
|
-
};
|
|
990
|
-
return { modified: storageUpdate, reverse };
|
|
991
|
-
}
|
|
992
|
-
/**
|
|
993
|
-
* @internal
|
|
994
|
-
*/
|
|
995
|
-
_toSerializedCrdt() {
|
|
996
|
-
var _a;
|
|
997
|
-
return {
|
|
998
|
-
type: CrdtType.Map,
|
|
999
|
-
parentId: (_a = this._parent) === null || _a === void 0 ? void 0 : _a._id,
|
|
1000
|
-
parentKey: this._parentKey,
|
|
1001
|
-
};
|
|
1002
|
-
}
|
|
1003
|
-
/**
|
|
1004
|
-
* Returns a specified element from the LiveMap.
|
|
1005
|
-
* @param key The key of the element to return.
|
|
1006
|
-
* @returns The element associated with the specified key, or undefined if the key can't be found in the LiveMap.
|
|
1007
|
-
*/
|
|
1008
|
-
get(key) {
|
|
1009
|
-
const value = this._map.get(key);
|
|
1010
|
-
if (value == undefined) {
|
|
1011
|
-
return undefined;
|
|
1012
|
-
}
|
|
1013
|
-
return selfOrRegisterValue(value);
|
|
1014
|
-
}
|
|
1015
|
-
/**
|
|
1016
|
-
* Adds or updates an element with a specified key and a value.
|
|
1017
|
-
* @param key The key of the element to add. Should be a string.
|
|
1018
|
-
* @param value The value of the element to add. Should be serializable to JSON.
|
|
1019
|
-
*/
|
|
1020
|
-
set(key, value) {
|
|
1021
|
-
const oldValue = this._map.get(key);
|
|
1022
|
-
if (oldValue) {
|
|
1023
|
-
oldValue._detach();
|
|
1024
|
-
}
|
|
1025
|
-
const item = selfOrRegister(value);
|
|
1026
|
-
item._setParentLink(this, key);
|
|
1027
|
-
this._map.set(key, item);
|
|
1028
|
-
if (this._doc && this._id) {
|
|
1029
|
-
const id = this._doc.generateId();
|
|
1030
|
-
item._attach(id, this._doc);
|
|
1031
|
-
const storageUpdates = new Map();
|
|
1032
|
-
storageUpdates.set(this._id, {
|
|
1033
|
-
node: this,
|
|
1034
|
-
type: "LiveMap",
|
|
1035
|
-
updates: { [key]: { type: "update" } },
|
|
1036
|
-
});
|
|
1037
|
-
this._doc.dispatch(item._serialize(this._id, key, this._doc), oldValue
|
|
1038
|
-
? oldValue._serialize(this._id, key)
|
|
1039
|
-
: [{ type: OpType.DeleteCrdt, id }], storageUpdates);
|
|
1040
|
-
}
|
|
1041
|
-
}
|
|
1042
|
-
/**
|
|
1043
|
-
* Returns the number of elements in the LiveMap.
|
|
1044
|
-
*/
|
|
1045
|
-
get size() {
|
|
1046
|
-
return this._map.size;
|
|
1047
|
-
}
|
|
1048
|
-
/**
|
|
1049
|
-
* Returns a boolean indicating whether an element with the specified key exists or not.
|
|
1050
|
-
* @param key The key of the element to test for presence.
|
|
1051
|
-
*/
|
|
1052
|
-
has(key) {
|
|
1053
|
-
return this._map.has(key);
|
|
1054
|
-
}
|
|
1055
|
-
/**
|
|
1056
|
-
* Removes the specified element by key.
|
|
1057
|
-
* @param key The key of the element to remove.
|
|
1058
|
-
* @returns true if an element existed and has been removed, or false if the element does not exist.
|
|
1059
|
-
*/
|
|
1060
|
-
delete(key) {
|
|
1061
|
-
const item = this._map.get(key);
|
|
1062
|
-
if (item == null) {
|
|
1063
|
-
return false;
|
|
1064
|
-
}
|
|
1065
|
-
item._detach();
|
|
1066
|
-
this._map.delete(key);
|
|
1067
|
-
if (this._doc && item._id) {
|
|
1068
|
-
const storageUpdates = new Map();
|
|
1069
|
-
storageUpdates.set(this._id, {
|
|
1070
|
-
node: this,
|
|
1071
|
-
type: "LiveMap",
|
|
1072
|
-
updates: { [key]: { type: "delete" } },
|
|
1073
|
-
});
|
|
1074
|
-
this._doc.dispatch([
|
|
1075
|
-
{
|
|
1076
|
-
type: OpType.DeleteCrdt,
|
|
1077
|
-
id: item._id,
|
|
1078
|
-
opId: this._doc.generateOpId(),
|
|
1079
|
-
},
|
|
1080
|
-
], item._serialize(this._id, key), storageUpdates);
|
|
1081
|
-
}
|
|
1082
|
-
return true;
|
|
1083
|
-
}
|
|
1084
|
-
/**
|
|
1085
|
-
* Returns a new Iterator object that contains the [key, value] pairs for each element.
|
|
1086
|
-
*/
|
|
1087
|
-
entries() {
|
|
1088
|
-
const innerIterator = this._map.entries();
|
|
1089
|
-
return {
|
|
1090
|
-
[Symbol.iterator]: function () {
|
|
1091
|
-
return this;
|
|
1092
|
-
},
|
|
1093
|
-
next() {
|
|
1094
|
-
const iteratorValue = innerIterator.next();
|
|
1095
|
-
if (iteratorValue.done) {
|
|
1096
|
-
return {
|
|
1097
|
-
done: true,
|
|
1098
|
-
value: undefined,
|
|
1099
|
-
};
|
|
1100
|
-
}
|
|
1101
|
-
const entry = iteratorValue.value;
|
|
1102
|
-
return {
|
|
1103
|
-
value: [entry[0], selfOrRegisterValue(iteratorValue.value[1])],
|
|
1104
|
-
};
|
|
1105
|
-
},
|
|
1106
|
-
};
|
|
1107
|
-
}
|
|
1108
|
-
/**
|
|
1109
|
-
* Same function object as the initial value of the entries method.
|
|
1110
|
-
*/
|
|
1111
|
-
[Symbol.iterator]() {
|
|
1112
|
-
return this.entries();
|
|
1113
|
-
}
|
|
1114
|
-
/**
|
|
1115
|
-
* Returns a new Iterator object that contains the keys for each element.
|
|
1116
|
-
*/
|
|
1117
|
-
keys() {
|
|
1118
|
-
return this._map.keys();
|
|
1119
|
-
}
|
|
1120
|
-
/**
|
|
1121
|
-
* Returns a new Iterator object that contains the values for each element.
|
|
1122
|
-
*/
|
|
1123
|
-
values() {
|
|
1124
|
-
const innerIterator = this._map.values();
|
|
1125
|
-
return {
|
|
1126
|
-
[Symbol.iterator]: function () {
|
|
1127
|
-
return this;
|
|
1128
|
-
},
|
|
1129
|
-
next() {
|
|
1130
|
-
const iteratorValue = innerIterator.next();
|
|
1131
|
-
if (iteratorValue.done) {
|
|
1132
|
-
return {
|
|
1133
|
-
done: true,
|
|
1134
|
-
value: undefined,
|
|
1135
|
-
};
|
|
1136
|
-
}
|
|
1137
|
-
return {
|
|
1138
|
-
value: selfOrRegisterValue(iteratorValue.value),
|
|
1139
|
-
};
|
|
1140
|
-
},
|
|
1141
|
-
};
|
|
1142
|
-
}
|
|
1143
|
-
/**
|
|
1144
|
-
* Executes a provided function once per each key/value pair in the Map object, in insertion order.
|
|
1145
|
-
* @param callback Function to execute for each entry in the map.
|
|
1146
|
-
*/
|
|
1147
|
-
forEach(callback) {
|
|
1148
|
-
for (const entry of this) {
|
|
1149
|
-
callback(entry[1], entry[0], this);
|
|
1150
|
-
}
|
|
1151
|
-
}
|
|
1152
|
-
}
|
|
541
|
+
const _emittedDeprecationWarnings = new Set;
|
|
1153
542
|
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
*/
|
|
1158
|
-
function parseJson(rawMessage) {
|
|
1159
|
-
try {
|
|
1160
|
-
// eslint-disable-next-line no-restricted-syntax
|
|
1161
|
-
return JSON.parse(rawMessage);
|
|
1162
|
-
}
|
|
1163
|
-
catch (e) {
|
|
1164
|
-
return undefined;
|
|
1165
|
-
}
|
|
543
|
+
function deprecate(message, key = message) {
|
|
544
|
+
"production" !== process.env.NODE_ENV && (_emittedDeprecationWarnings.has(key) || (_emittedDeprecationWarnings.add(key),
|
|
545
|
+
console.error(`DEPRECATION WARNING: ${message}`)));
|
|
1166
546
|
}
|
|
1167
|
-
|
|
1168
|
-
|
|
547
|
+
|
|
548
|
+
function deprecateIf(condition, message, key = message) {
|
|
549
|
+
"production" !== process.env.NODE_ENV && condition && deprecate(message, key);
|
|
1169
550
|
}
|
|
1170
|
-
|
|
1171
|
-
|
|
551
|
+
|
|
552
|
+
function throwUsageError(message) {
|
|
553
|
+
if ("production" !== process.env.NODE_ENV) {
|
|
554
|
+
const usageError = new Error(message);
|
|
555
|
+
throw usageError.name = "Usage error", usageError;
|
|
556
|
+
}
|
|
1172
557
|
}
|
|
1173
558
|
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
// matter how often it gets called.
|
|
1177
|
-
const _emittedDeprecationWarnings = new Set();
|
|
1178
|
-
/**
|
|
1179
|
-
* Displays a deprecation warning in the dev console. Only in dev mode, and
|
|
1180
|
-
* only once per message/key. In production, this is a no-op.
|
|
1181
|
-
*/
|
|
1182
|
-
function deprecate(message, key = message) {
|
|
1183
|
-
if (process.env.NODE_ENV !== "production") {
|
|
1184
|
-
if (!_emittedDeprecationWarnings.has(key)) {
|
|
1185
|
-
_emittedDeprecationWarnings.add(key);
|
|
1186
|
-
console.error(`DEPRECATION WARNING: ${message}`);
|
|
1187
|
-
}
|
|
1188
|
-
}
|
|
559
|
+
function errorIf(condition, message) {
|
|
560
|
+
"production" !== process.env.NODE_ENV && condition && throwUsageError(message);
|
|
1189
561
|
}
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
562
|
+
|
|
563
|
+
class LiveMap extends AbstractCrdt {
|
|
564
|
+
constructor(entries) {
|
|
565
|
+
if (super(), deprecateIf(null === entries, "Support for calling `new LiveMap(null)` will be removed in @liveblocks/client 0.18. Please call as `new LiveMap()`, or `new LiveMap([])`."),
|
|
566
|
+
entries) {
|
|
567
|
+
const mappedEntries = [];
|
|
568
|
+
for (const entry of entries) {
|
|
569
|
+
const value = selfOrRegister(entry[1]);
|
|
570
|
+
value._setParentLink(this, entry[0]), mappedEntries.push([ entry[0], value ]);
|
|
571
|
+
}
|
|
572
|
+
this._map = new Map(mappedEntries);
|
|
573
|
+
} else this._map = new Map;
|
|
574
|
+
}
|
|
575
|
+
_serialize(parentId, parentKey, doc, intent) {
|
|
576
|
+
if (null == this._id) throw new Error("Cannot serialize item is not attached");
|
|
577
|
+
if (null == parentId || null == parentKey) throw new Error("Cannot serialize map if parentId or parentKey is undefined");
|
|
578
|
+
const ops = [], op = {
|
|
579
|
+
id: this._id,
|
|
580
|
+
opId: null == doc ? void 0 : doc.generateOpId(),
|
|
581
|
+
type: OpType.CreateMap,
|
|
582
|
+
intent: intent,
|
|
583
|
+
parentId: parentId,
|
|
584
|
+
parentKey: parentKey
|
|
585
|
+
};
|
|
586
|
+
ops.push(op);
|
|
587
|
+
for (const [key, value] of this._map) ops.push(...value._serialize(this._id, key, doc));
|
|
588
|
+
return ops;
|
|
589
|
+
}
|
|
590
|
+
static _deserialize([id, item], parentToChildren, doc) {
|
|
591
|
+
if (item.type !== CrdtType.Map) throw new Error(`Tried to deserialize a map but item type is "${item.type}"`);
|
|
592
|
+
const map = new LiveMap;
|
|
593
|
+
map._attach(id, doc);
|
|
594
|
+
const children = parentToChildren.get(id);
|
|
595
|
+
if (null == children) return map;
|
|
596
|
+
for (const entry of children) {
|
|
597
|
+
const crdt = entry[1];
|
|
598
|
+
if (null == crdt.parentKey) throw new Error("Tried to deserialize a crdt but it does not have a parentKey and is not the root");
|
|
599
|
+
const child = deserialize(entry, parentToChildren, doc);
|
|
600
|
+
child._setParentLink(map, crdt.parentKey), map._map.set(crdt.parentKey, child);
|
|
601
|
+
}
|
|
602
|
+
return map;
|
|
603
|
+
}
|
|
604
|
+
_attach(id, doc) {
|
|
605
|
+
super._attach(id, doc);
|
|
606
|
+
for (const [_key, value] of this._map) isCrdt(value) && value._attach(doc.generateId(), doc);
|
|
607
|
+
}
|
|
608
|
+
_attachChild(op, _isLocal) {
|
|
609
|
+
if (null == this._doc) throw new Error("Can't attach child if doc is not present");
|
|
610
|
+
const {id: id, parentKey: parentKey} = op, key = parentKey, child = creationOpToLiveStructure(op);
|
|
611
|
+
if (void 0 !== this._doc.getItem(id)) return {
|
|
612
|
+
modified: !1
|
|
613
|
+
};
|
|
614
|
+
const previousValue = this._map.get(key);
|
|
615
|
+
let reverse;
|
|
616
|
+
return previousValue ? (reverse = previousValue._serialize(this._id, key), previousValue._detach()) : reverse = [ {
|
|
617
|
+
type: OpType.DeleteCrdt,
|
|
618
|
+
id: id
|
|
619
|
+
} ], child._setParentLink(this, key), child._attach(id, this._doc), this._map.set(key, child),
|
|
620
|
+
{
|
|
621
|
+
modified: {
|
|
622
|
+
node: this,
|
|
623
|
+
type: "LiveMap",
|
|
624
|
+
updates: {
|
|
625
|
+
[key]: {
|
|
626
|
+
type: "update"
|
|
627
|
+
}
|
|
628
|
+
}
|
|
629
|
+
},
|
|
630
|
+
reverse: reverse
|
|
631
|
+
};
|
|
632
|
+
}
|
|
633
|
+
_detach() {
|
|
634
|
+
super._detach();
|
|
635
|
+
for (const item of this._map.values()) item._detach();
|
|
636
|
+
}
|
|
637
|
+
_detachChild(child) {
|
|
638
|
+
const reverse = child._serialize(this._id, child._parentKey, this._doc);
|
|
639
|
+
for (const [key, value] of this._map) value === child && this._map.delete(key);
|
|
640
|
+
child._detach();
|
|
641
|
+
return {
|
|
642
|
+
modified: {
|
|
643
|
+
node: this,
|
|
644
|
+
type: "LiveMap",
|
|
645
|
+
updates: {
|
|
646
|
+
[child._parentKey]: {
|
|
647
|
+
type: "delete"
|
|
648
|
+
}
|
|
649
|
+
}
|
|
650
|
+
},
|
|
651
|
+
reverse: reverse
|
|
652
|
+
};
|
|
653
|
+
}
|
|
654
|
+
_toSerializedCrdt() {
|
|
655
|
+
var _a;
|
|
656
|
+
return {
|
|
657
|
+
type: CrdtType.Map,
|
|
658
|
+
parentId: null === (_a = this._parent) || void 0 === _a ? void 0 : _a._id,
|
|
659
|
+
parentKey: this._parentKey
|
|
660
|
+
};
|
|
661
|
+
}
|
|
662
|
+
get(key) {
|
|
663
|
+
const value = this._map.get(key);
|
|
664
|
+
if (null != value) return selfOrRegisterValue(value);
|
|
665
|
+
}
|
|
666
|
+
set(key, value) {
|
|
667
|
+
const oldValue = this._map.get(key);
|
|
668
|
+
oldValue && oldValue._detach();
|
|
669
|
+
const item = selfOrRegister(value);
|
|
670
|
+
if (item._setParentLink(this, key), this._map.set(key, item), this._doc && this._id) {
|
|
671
|
+
const id = this._doc.generateId();
|
|
672
|
+
item._attach(id, this._doc);
|
|
673
|
+
const storageUpdates = new Map;
|
|
674
|
+
storageUpdates.set(this._id, {
|
|
675
|
+
node: this,
|
|
676
|
+
type: "LiveMap",
|
|
677
|
+
updates: {
|
|
678
|
+
[key]: {
|
|
679
|
+
type: "update"
|
|
680
|
+
}
|
|
681
|
+
}
|
|
682
|
+
}), this._doc.dispatch(item._serialize(this._id, key, this._doc), oldValue ? oldValue._serialize(this._id, key) : [ {
|
|
683
|
+
type: OpType.DeleteCrdt,
|
|
684
|
+
id: id
|
|
685
|
+
} ], storageUpdates);
|
|
686
|
+
}
|
|
687
|
+
}
|
|
688
|
+
get size() {
|
|
689
|
+
return this._map.size;
|
|
690
|
+
}
|
|
691
|
+
has(key) {
|
|
692
|
+
return this._map.has(key);
|
|
693
|
+
}
|
|
694
|
+
delete(key) {
|
|
695
|
+
const item = this._map.get(key);
|
|
696
|
+
if (null == item) return !1;
|
|
697
|
+
if (item._detach(), this._map.delete(key), this._doc && item._id) {
|
|
698
|
+
const storageUpdates = new Map;
|
|
699
|
+
storageUpdates.set(this._id, {
|
|
700
|
+
node: this,
|
|
701
|
+
type: "LiveMap",
|
|
702
|
+
updates: {
|
|
703
|
+
[key]: {
|
|
704
|
+
type: "delete"
|
|
705
|
+
}
|
|
706
|
+
}
|
|
707
|
+
}), this._doc.dispatch([ {
|
|
708
|
+
type: OpType.DeleteCrdt,
|
|
709
|
+
id: item._id,
|
|
710
|
+
opId: this._doc.generateOpId()
|
|
711
|
+
} ], item._serialize(this._id, key), storageUpdates);
|
|
712
|
+
}
|
|
713
|
+
return !0;
|
|
714
|
+
}
|
|
715
|
+
entries() {
|
|
716
|
+
const innerIterator = this._map.entries();
|
|
717
|
+
return {
|
|
718
|
+
[Symbol.iterator]: function() {
|
|
719
|
+
return this;
|
|
720
|
+
},
|
|
721
|
+
next() {
|
|
722
|
+
const iteratorValue = innerIterator.next();
|
|
723
|
+
if (iteratorValue.done) return {
|
|
724
|
+
done: !0,
|
|
725
|
+
value: void 0
|
|
726
|
+
};
|
|
727
|
+
return {
|
|
728
|
+
value: [ iteratorValue.value[0], selfOrRegisterValue(iteratorValue.value[1]) ]
|
|
729
|
+
};
|
|
730
|
+
}
|
|
731
|
+
};
|
|
732
|
+
}
|
|
733
|
+
[Symbol.iterator]() {
|
|
734
|
+
return this.entries();
|
|
735
|
+
}
|
|
736
|
+
keys() {
|
|
737
|
+
return this._map.keys();
|
|
738
|
+
}
|
|
739
|
+
values() {
|
|
740
|
+
const innerIterator = this._map.values();
|
|
741
|
+
return {
|
|
742
|
+
[Symbol.iterator]: function() {
|
|
743
|
+
return this;
|
|
744
|
+
},
|
|
745
|
+
next() {
|
|
746
|
+
const iteratorValue = innerIterator.next();
|
|
747
|
+
return iteratorValue.done ? {
|
|
748
|
+
done: !0,
|
|
749
|
+
value: void 0
|
|
750
|
+
} : {
|
|
751
|
+
value: selfOrRegisterValue(iteratorValue.value)
|
|
752
|
+
};
|
|
753
|
+
}
|
|
754
|
+
};
|
|
755
|
+
}
|
|
756
|
+
forEach(callback) {
|
|
757
|
+
for (const entry of this) callback(entry[1], entry[0], this);
|
|
758
|
+
}
|
|
1201
759
|
}
|
|
760
|
+
|
|
1202
761
|
function remove(array, item) {
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
}
|
|
1208
|
-
}
|
|
762
|
+
for (let i = 0; i < array.length; i++) if (array[i] === item) {
|
|
763
|
+
array.splice(i, 1);
|
|
764
|
+
break;
|
|
765
|
+
}
|
|
1209
766
|
}
|
|
1210
|
-
|
|
1211
|
-
* Removes null and undefined values from the array, and reflects this in the
|
|
1212
|
-
* output type.
|
|
1213
|
-
*/
|
|
767
|
+
|
|
1214
768
|
function compact(items) {
|
|
1215
|
-
|
|
769
|
+
return items.filter((item => null != item));
|
|
1216
770
|
}
|
|
771
|
+
|
|
1217
772
|
function creationOpToLiveStructure(op) {
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
773
|
+
switch (op.type) {
|
|
774
|
+
case OpType.CreateRegister:
|
|
775
|
+
return new LiveRegister(op.data);
|
|
776
|
+
|
|
777
|
+
case OpType.CreateObject:
|
|
778
|
+
return new LiveObject(op.data);
|
|
779
|
+
|
|
780
|
+
case OpType.CreateMap:
|
|
781
|
+
return new LiveMap;
|
|
782
|
+
|
|
783
|
+
case OpType.CreateList:
|
|
784
|
+
return new LiveList;
|
|
785
|
+
}
|
|
1228
786
|
}
|
|
787
|
+
|
|
1229
788
|
function isSameNodeOrChildOf(node, parent) {
|
|
1230
|
-
|
|
1231
|
-
return true;
|
|
1232
|
-
}
|
|
1233
|
-
if (node._parent) {
|
|
1234
|
-
return isSameNodeOrChildOf(node._parent, parent);
|
|
1235
|
-
}
|
|
1236
|
-
return false;
|
|
789
|
+
return node === parent || !!node._parent && isSameNodeOrChildOf(node._parent, parent);
|
|
1237
790
|
}
|
|
791
|
+
|
|
1238
792
|
function deserialize(entry, parentToChildren, doc) {
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
}
|
|
793
|
+
switch (entry[1].type) {
|
|
794
|
+
case CrdtType.Object:
|
|
795
|
+
return LiveObject._deserialize(entry, parentToChildren, doc);
|
|
796
|
+
|
|
797
|
+
case CrdtType.List:
|
|
798
|
+
return LiveList._deserialize(entry, parentToChildren, doc);
|
|
799
|
+
|
|
800
|
+
case CrdtType.Map:
|
|
801
|
+
return LiveMap._deserialize(entry, parentToChildren, doc);
|
|
802
|
+
|
|
803
|
+
case CrdtType.Register:
|
|
804
|
+
return LiveRegister._deserialize(entry, parentToChildren, doc);
|
|
805
|
+
|
|
806
|
+
default:
|
|
807
|
+
throw new Error("Unexpected CRDT type");
|
|
808
|
+
}
|
|
1256
809
|
}
|
|
810
|
+
|
|
1257
811
|
function isCrdt(obj) {
|
|
1258
|
-
|
|
1259
|
-
obj instanceof LiveMap ||
|
|
1260
|
-
obj instanceof LiveList ||
|
|
1261
|
-
obj instanceof LiveRegister);
|
|
812
|
+
return obj instanceof LiveObject || obj instanceof LiveMap || obj instanceof LiveList || obj instanceof LiveRegister;
|
|
1262
813
|
}
|
|
814
|
+
|
|
1263
815
|
function selfOrRegisterValue(obj) {
|
|
1264
|
-
|
|
1265
|
-
return obj.data;
|
|
1266
|
-
}
|
|
1267
|
-
return obj;
|
|
816
|
+
return obj instanceof LiveRegister ? obj.data : obj;
|
|
1268
817
|
}
|
|
818
|
+
|
|
1269
819
|
function selfOrRegister(obj) {
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
return obj;
|
|
1274
|
-
}
|
|
1275
|
-
else if (obj instanceof LiveRegister) {
|
|
1276
|
-
throw new Error("Internal error. LiveRegister should not be created from selfOrRegister");
|
|
1277
|
-
}
|
|
1278
|
-
else {
|
|
1279
|
-
// By now, we've checked that obj isn't a Live storage instance.
|
|
1280
|
-
// Technically what remains here can still be a (1) live data scalar, or
|
|
1281
|
-
// a (2) list of Lson values, or (3) an object with Lson values.
|
|
1282
|
-
//
|
|
1283
|
-
// Of these, (1) is fine, because a live data scalar is also a legal Json
|
|
1284
|
-
// scalar.
|
|
1285
|
-
//
|
|
1286
|
-
// But (2) and (3) are only technically fine if those only contain Json
|
|
1287
|
-
// values. Technically, these can still contain nested Live storage
|
|
1288
|
-
// instances, and we should probably assert that they don't at runtime.
|
|
1289
|
-
//
|
|
1290
|
-
// TypeScript understands this and doesn't let us use `obj` until we do :)
|
|
1291
|
-
//
|
|
1292
|
-
return new LiveRegister(obj);
|
|
1293
|
-
// ^^^^^^^
|
|
1294
|
-
// TODO: Better to assert than to force-cast here!
|
|
1295
|
-
}
|
|
820
|
+
if (obj instanceof LiveObject || obj instanceof LiveMap || obj instanceof LiveList) return obj;
|
|
821
|
+
if (obj instanceof LiveRegister) throw new Error("Internal error. LiveRegister should not be created from selfOrRegister");
|
|
822
|
+
return new LiveRegister(obj);
|
|
1296
823
|
}
|
|
824
|
+
|
|
1297
825
|
function getTreesDiffOperations(currentItems, newItems) {
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
parentId: crdt.parentId,
|
|
1354
|
-
parentKey: crdt.parentKey,
|
|
1355
|
-
data: crdt.data,
|
|
1356
|
-
});
|
|
1357
|
-
break;
|
|
1358
|
-
case CrdtType.Map:
|
|
1359
|
-
ops.push({
|
|
1360
|
-
type: OpType.CreateMap,
|
|
1361
|
-
id: id,
|
|
1362
|
-
parentId: crdt.parentId,
|
|
1363
|
-
parentKey: crdt.parentKey,
|
|
1364
|
-
});
|
|
1365
|
-
break;
|
|
1366
|
-
}
|
|
1367
|
-
}
|
|
1368
|
-
});
|
|
1369
|
-
return ops;
|
|
1370
|
-
}
|
|
1371
|
-
function mergeObjectStorageUpdates(first, second) {
|
|
1372
|
-
const updates = first.updates;
|
|
1373
|
-
for (const [key, value] of entries(second.updates)) {
|
|
1374
|
-
updates[key] = value;
|
|
1375
|
-
}
|
|
1376
|
-
return Object.assign(Object.assign({}, second), { updates: updates });
|
|
1377
|
-
}
|
|
1378
|
-
function mergeMapStorageUpdates(first, second) {
|
|
1379
|
-
const updates = first.updates;
|
|
1380
|
-
for (const [key, value] of entries(second.updates)) {
|
|
1381
|
-
updates[key] = value;
|
|
1382
|
-
}
|
|
1383
|
-
return Object.assign(Object.assign({}, second), { updates: updates });
|
|
1384
|
-
}
|
|
1385
|
-
function mergeListStorageUpdates(first, second) {
|
|
1386
|
-
const updates = first.updates;
|
|
1387
|
-
return Object.assign(Object.assign({}, second), { updates: updates.concat(second.updates) });
|
|
826
|
+
const ops = [];
|
|
827
|
+
return currentItems.forEach(((_, id) => {
|
|
828
|
+
newItems.get(id) || ops.push({
|
|
829
|
+
type: OpType.DeleteCrdt,
|
|
830
|
+
id: id
|
|
831
|
+
});
|
|
832
|
+
})), newItems.forEach(((crdt, id) => {
|
|
833
|
+
const currentCrdt = currentItems.get(id);
|
|
834
|
+
if (currentCrdt) crdt.type === CrdtType.Object && JSON.stringify(crdt.data) !== JSON.stringify(currentCrdt.data) && ops.push({
|
|
835
|
+
type: OpType.UpdateObject,
|
|
836
|
+
id: id,
|
|
837
|
+
data: crdt.data
|
|
838
|
+
}), crdt.parentKey !== currentCrdt.parentKey && ops.push({
|
|
839
|
+
type: OpType.SetParentKey,
|
|
840
|
+
id: id,
|
|
841
|
+
parentKey: crdt.parentKey
|
|
842
|
+
}); else switch (crdt.type) {
|
|
843
|
+
case CrdtType.Register:
|
|
844
|
+
ops.push({
|
|
845
|
+
type: OpType.CreateRegister,
|
|
846
|
+
id: id,
|
|
847
|
+
parentId: crdt.parentId,
|
|
848
|
+
parentKey: crdt.parentKey,
|
|
849
|
+
data: crdt.data
|
|
850
|
+
});
|
|
851
|
+
break;
|
|
852
|
+
|
|
853
|
+
case CrdtType.List:
|
|
854
|
+
ops.push({
|
|
855
|
+
type: OpType.CreateList,
|
|
856
|
+
id: id,
|
|
857
|
+
parentId: crdt.parentId,
|
|
858
|
+
parentKey: crdt.parentKey
|
|
859
|
+
});
|
|
860
|
+
break;
|
|
861
|
+
|
|
862
|
+
case CrdtType.Object:
|
|
863
|
+
ops.push({
|
|
864
|
+
type: OpType.CreateObject,
|
|
865
|
+
id: id,
|
|
866
|
+
parentId: crdt.parentId,
|
|
867
|
+
parentKey: crdt.parentKey,
|
|
868
|
+
data: crdt.data
|
|
869
|
+
});
|
|
870
|
+
break;
|
|
871
|
+
|
|
872
|
+
case CrdtType.Map:
|
|
873
|
+
ops.push({
|
|
874
|
+
type: OpType.CreateMap,
|
|
875
|
+
id: id,
|
|
876
|
+
parentId: crdt.parentId,
|
|
877
|
+
parentKey: crdt.parentKey
|
|
878
|
+
});
|
|
879
|
+
}
|
|
880
|
+
})), ops;
|
|
1388
881
|
}
|
|
1389
|
-
|
|
882
|
+
|
|
1390
883
|
function mergeStorageUpdates(first, second) {
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
884
|
+
return first ? "LiveObject" === first.type && "LiveObject" === second.type ? function(first, second) {
|
|
885
|
+
const updates = first.updates;
|
|
886
|
+
for (const [key, value] of entries(second.updates)) updates[key] = value;
|
|
887
|
+
return Object.assign(Object.assign({}, second), {
|
|
888
|
+
updates: updates
|
|
889
|
+
});
|
|
890
|
+
}(first, second) : "LiveMap" === first.type && "LiveMap" === second.type ? function(first, second) {
|
|
891
|
+
const updates = first.updates;
|
|
892
|
+
for (const [key, value] of entries(second.updates)) updates[key] = value;
|
|
893
|
+
return Object.assign(Object.assign({}, second), {
|
|
894
|
+
updates: updates
|
|
895
|
+
});
|
|
896
|
+
}(first, second) : "LiveList" === first.type && "LiveList" === second.type ? function(first, second) {
|
|
897
|
+
const updates = first.updates;
|
|
898
|
+
return Object.assign(Object.assign({}, second), {
|
|
899
|
+
updates: updates.concat(second.updates)
|
|
900
|
+
});
|
|
901
|
+
}(first, second) : second : second;
|
|
1405
902
|
}
|
|
903
|
+
|
|
1406
904
|
function isPlain(value) {
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
}
|
|
1416
|
-
function isPlainObject(value) {
|
|
1417
|
-
if (typeof value !== "object" || value === null)
|
|
1418
|
-
return false;
|
|
1419
|
-
const proto = Object.getPrototypeOf(value);
|
|
1420
|
-
if (proto === null)
|
|
1421
|
-
return true;
|
|
1422
|
-
let baseProto = proto;
|
|
1423
|
-
while (Object.getPrototypeOf(baseProto) !== null) {
|
|
1424
|
-
baseProto = Object.getPrototypeOf(baseProto);
|
|
1425
|
-
}
|
|
1426
|
-
return proto === baseProto;
|
|
905
|
+
const type = typeof value;
|
|
906
|
+
return "undefined" === type || null === value || "string" === type || "boolean" === type || "number" === type || Array.isArray(value) || function(value) {
|
|
907
|
+
if ("object" != typeof value || null === value) return !1;
|
|
908
|
+
const proto = Object.getPrototypeOf(value);
|
|
909
|
+
if (null === proto) return !0;
|
|
910
|
+
let baseProto = proto;
|
|
911
|
+
for (;null !== Object.getPrototypeOf(baseProto); ) baseProto = Object.getPrototypeOf(baseProto);
|
|
912
|
+
return proto === baseProto;
|
|
913
|
+
}(value);
|
|
1427
914
|
}
|
|
915
|
+
|
|
1428
916
|
function findNonSerializableValue(value, path = "") {
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
if (typeof nestedValue === "object") {
|
|
1447
|
-
const nonSerializableNestedValue = findNonSerializableValue(nestedValue, nestedPath);
|
|
1448
|
-
if (nonSerializableNestedValue) {
|
|
1449
|
-
return nonSerializableNestedValue;
|
|
1450
|
-
}
|
|
1451
|
-
}
|
|
1452
|
-
}
|
|
1453
|
-
return false;
|
|
917
|
+
if (!isPlain) return {
|
|
918
|
+
path: path || "root",
|
|
919
|
+
value: value
|
|
920
|
+
};
|
|
921
|
+
if ("object" != typeof value || null === value) return !1;
|
|
922
|
+
for (const [key, nestedValue] of Object.entries(value)) {
|
|
923
|
+
const nestedPath = path ? path + "." + key : key;
|
|
924
|
+
if (!isPlain(nestedValue)) return {
|
|
925
|
+
path: nestedPath,
|
|
926
|
+
value: nestedValue
|
|
927
|
+
};
|
|
928
|
+
if ("object" == typeof nestedValue) {
|
|
929
|
+
const nonSerializableNestedValue = findNonSerializableValue(nestedValue, nestedPath);
|
|
930
|
+
if (nonSerializableNestedValue) return nonSerializableNestedValue;
|
|
931
|
+
}
|
|
932
|
+
}
|
|
933
|
+
return !1;
|
|
1454
934
|
}
|
|
935
|
+
|
|
1455
936
|
function isTokenValid(token) {
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
if (data === undefined ||
|
|
1462
|
-
!isJsonObject(data) ||
|
|
1463
|
-
typeof data.exp !== "number") {
|
|
1464
|
-
return false;
|
|
1465
|
-
}
|
|
1466
|
-
const now = Date.now();
|
|
1467
|
-
if (now / 1000 > data.exp - 300) {
|
|
1468
|
-
return false;
|
|
1469
|
-
}
|
|
1470
|
-
return true;
|
|
937
|
+
const tokenParts = token.split(".");
|
|
938
|
+
if (3 !== tokenParts.length) return !1;
|
|
939
|
+
const data = parseJson(atob(tokenParts[1]));
|
|
940
|
+
if (void 0 === data || !isJsonObject(data) || "number" != typeof data.exp) return !1;
|
|
941
|
+
return !(Date.now() / 1e3 > data.exp - 300);
|
|
1471
942
|
}
|
|
1472
|
-
|
|
1473
|
-
* Drop-in replacement for Object.entries() that retains better types.
|
|
1474
|
-
*/
|
|
943
|
+
|
|
1475
944
|
function entries(obj) {
|
|
1476
|
-
|
|
945
|
+
return Object.entries(obj);
|
|
1477
946
|
}
|
|
1478
947
|
|
|
1479
|
-
/**
|
|
1480
|
-
* The LiveObject class is similar to a JavaScript object that is synchronized on all clients.
|
|
1481
|
-
* Keys should be a string, and values should be serializable to JSON.
|
|
1482
|
-
* If multiple clients update the same property simultaneously, the last modification received by the Liveblocks servers is the winner.
|
|
1483
|
-
*/
|
|
1484
948
|
class LiveObject extends AbstractCrdt {
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
}
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
|
|
1637
|
-
|
|
1638
|
-
|
|
1639
|
-
|
|
1640
|
-
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
|
|
1660
|
-
|
|
1661
|
-
|
|
1662
|
-
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
|
|
1676
|
-
|
|
1677
|
-
|
|
1678
|
-
|
|
1679
|
-
|
|
1680
|
-
|
|
1681
|
-
}
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
|
-
|
|
1690
|
-
|
|
1691
|
-
|
|
1692
|
-
|
|
1693
|
-
|
|
1694
|
-
|
|
1695
|
-
|
|
1696
|
-
|
|
1697
|
-
|
|
1698
|
-
|
|
1699
|
-
|
|
1700
|
-
|
|
1701
|
-
|
|
1702
|
-
|
|
1703
|
-
|
|
1704
|
-
|
|
1705
|
-
|
|
1706
|
-
|
|
1707
|
-
|
|
1708
|
-
|
|
1709
|
-
|
|
1710
|
-
|
|
1711
|
-
|
|
1712
|
-
|
|
1713
|
-
|
|
1714
|
-
|
|
1715
|
-
|
|
1716
|
-
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
|
|
1720
|
-
|
|
1721
|
-
|
|
1722
|
-
|
|
1723
|
-
|
|
1724
|
-
|
|
1725
|
-
|
|
1726
|
-
|
|
1727
|
-
|
|
1728
|
-
|
|
1729
|
-
|
|
1730
|
-
|
|
1731
|
-
|
|
1732
|
-
|
|
1733
|
-
|
|
1734
|
-
|
|
1735
|
-
|
|
1736
|
-
|
|
1737
|
-
|
|
1738
|
-
|
|
1739
|
-
|
|
1740
|
-
|
|
1741
|
-
|
|
1742
|
-
|
|
1743
|
-
|
|
1744
|
-
|
|
1745
|
-
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
-
|
|
1749
|
-
|
|
1750
|
-
|
|
1751
|
-
|
|
1752
|
-
|
|
1753
|
-
|
|
1754
|
-
|
|
1755
|
-
|
|
1756
|
-
|
|
1757
|
-
|
|
1758
|
-
|
|
1759
|
-
|
|
1760
|
-
|
|
1761
|
-
|
|
1762
|
-
|
|
1763
|
-
|
|
1764
|
-
|
|
1765
|
-
|
|
1766
|
-
|
|
1767
|
-
|
|
1768
|
-
|
|
1769
|
-
|
|
1770
|
-
|
|
1771
|
-
|
|
1772
|
-
|
|
1773
|
-
|
|
1774
|
-
|
|
1775
|
-
|
|
1776
|
-
|
|
1777
|
-
oldValue._detach();
|
|
1778
|
-
}
|
|
1779
|
-
else if (oldValue !== undefined) {
|
|
1780
|
-
reverse = [
|
|
1781
|
-
{
|
|
1782
|
-
type: OpType.UpdateObject,
|
|
1783
|
-
id: this._id,
|
|
1784
|
-
data: { [key]: oldValue },
|
|
1785
|
-
},
|
|
1786
|
-
];
|
|
1787
|
-
}
|
|
1788
|
-
this._map.delete(key);
|
|
1789
|
-
return {
|
|
1790
|
-
modified: {
|
|
1791
|
-
node: this,
|
|
1792
|
-
type: "LiveObject",
|
|
1793
|
-
updates: { [op.key]: { type: "delete" } },
|
|
1794
|
-
},
|
|
1795
|
-
reverse,
|
|
1796
|
-
};
|
|
1797
|
-
}
|
|
1798
|
-
/**
|
|
1799
|
-
* Transform the LiveObject into a javascript object
|
|
1800
|
-
*/
|
|
1801
|
-
toObject() {
|
|
1802
|
-
return Object.fromEntries(this._map);
|
|
1803
|
-
}
|
|
1804
|
-
/**
|
|
1805
|
-
* Adds or updates a property with a specified key and a value.
|
|
1806
|
-
* @param key The key of the property to add
|
|
1807
|
-
* @param value The value of the property to add
|
|
1808
|
-
*/
|
|
1809
|
-
set(key, value) {
|
|
1810
|
-
// TODO: Find out why typescript complains
|
|
1811
|
-
this.update({ [key]: value });
|
|
1812
|
-
}
|
|
1813
|
-
/**
|
|
1814
|
-
* Returns a specified property from the LiveObject.
|
|
1815
|
-
* @param key The key of the property to get
|
|
1816
|
-
*/
|
|
1817
|
-
get(key) {
|
|
1818
|
-
return this._map.get(key);
|
|
1819
|
-
}
|
|
1820
|
-
/**
|
|
1821
|
-
* Deletes a key from the LiveObject
|
|
1822
|
-
* @param key The key of the property to delete
|
|
1823
|
-
*/
|
|
1824
|
-
delete(key) {
|
|
1825
|
-
const keyAsString = key;
|
|
1826
|
-
const oldValue = this._map.get(keyAsString);
|
|
1827
|
-
if (oldValue === undefined) {
|
|
1828
|
-
return;
|
|
1829
|
-
}
|
|
1830
|
-
if (this._doc == null || this._id == null) {
|
|
1831
|
-
if (oldValue instanceof AbstractCrdt) {
|
|
1832
|
-
oldValue._detach();
|
|
1833
|
-
}
|
|
1834
|
-
this._map.delete(keyAsString);
|
|
1835
|
-
return;
|
|
1836
|
-
}
|
|
1837
|
-
let reverse;
|
|
1838
|
-
if (oldValue instanceof AbstractCrdt) {
|
|
1839
|
-
oldValue._detach();
|
|
1840
|
-
reverse = oldValue._serialize(this._id, keyAsString);
|
|
1841
|
-
}
|
|
1842
|
-
else {
|
|
1843
|
-
reverse = [
|
|
1844
|
-
{
|
|
1845
|
-
type: OpType.UpdateObject,
|
|
1846
|
-
data: { [keyAsString]: oldValue },
|
|
1847
|
-
id: this._id,
|
|
1848
|
-
},
|
|
1849
|
-
];
|
|
1850
|
-
}
|
|
1851
|
-
this._map.delete(keyAsString);
|
|
1852
|
-
const storageUpdates = new Map();
|
|
1853
|
-
storageUpdates.set(this._id, {
|
|
1854
|
-
node: this,
|
|
1855
|
-
type: "LiveObject",
|
|
1856
|
-
updates: { [key]: { type: "delete" } },
|
|
1857
|
-
});
|
|
1858
|
-
this._doc.dispatch([
|
|
1859
|
-
{
|
|
1860
|
-
type: OpType.DeleteObjectKey,
|
|
1861
|
-
key: keyAsString,
|
|
1862
|
-
id: this._id,
|
|
1863
|
-
opId: this._doc.generateOpId(),
|
|
1864
|
-
},
|
|
1865
|
-
], reverse, storageUpdates);
|
|
1866
|
-
}
|
|
1867
|
-
/**
|
|
1868
|
-
* Adds or updates multiple properties at once with an object.
|
|
1869
|
-
* @param overrides The object used to overrides properties
|
|
1870
|
-
*/
|
|
1871
|
-
update(overrides) {
|
|
1872
|
-
if (this._doc == null || this._id == null) {
|
|
1873
|
-
for (const key in overrides) {
|
|
1874
|
-
const oldValue = this._map.get(key);
|
|
1875
|
-
if (oldValue instanceof AbstractCrdt) {
|
|
1876
|
-
oldValue._detach();
|
|
1877
|
-
}
|
|
1878
|
-
const newValue = overrides[key];
|
|
1879
|
-
if (newValue instanceof AbstractCrdt) {
|
|
1880
|
-
newValue._setParentLink(this, key);
|
|
1881
|
-
}
|
|
1882
|
-
this._map.set(key, newValue);
|
|
1883
|
-
}
|
|
1884
|
-
return;
|
|
1885
|
-
}
|
|
1886
|
-
const ops = [];
|
|
1887
|
-
const reverseOps = [];
|
|
1888
|
-
const opId = this._doc.generateOpId();
|
|
1889
|
-
const updatedProps = {};
|
|
1890
|
-
const reverseUpdateOp = {
|
|
1891
|
-
id: this._id,
|
|
1892
|
-
type: OpType.UpdateObject,
|
|
1893
|
-
data: {},
|
|
1894
|
-
};
|
|
1895
|
-
const updateDelta = {};
|
|
1896
|
-
for (const key in overrides) {
|
|
1897
|
-
const oldValue = this._map.get(key);
|
|
1898
|
-
if (oldValue instanceof AbstractCrdt) {
|
|
1899
|
-
reverseOps.push(...oldValue._serialize(this._id, key));
|
|
1900
|
-
oldValue._detach();
|
|
1901
|
-
}
|
|
1902
|
-
else if (oldValue === undefined) {
|
|
1903
|
-
reverseOps.push({ type: OpType.DeleteObjectKey, id: this._id, key });
|
|
1904
|
-
}
|
|
1905
|
-
else {
|
|
1906
|
-
reverseUpdateOp.data[key] = oldValue;
|
|
1907
|
-
}
|
|
1908
|
-
const newValue = overrides[key];
|
|
1909
|
-
if (newValue instanceof AbstractCrdt) {
|
|
1910
|
-
newValue._setParentLink(this, key);
|
|
1911
|
-
newValue._attach(this._doc.generateId(), this._doc);
|
|
1912
|
-
const newAttachChildOps = newValue._serialize(this._id, key, this._doc);
|
|
1913
|
-
const createCrdtOp = newAttachChildOps.find((op) => op.parentId === this._id);
|
|
1914
|
-
if (createCrdtOp) {
|
|
1915
|
-
this._propToLastUpdate.set(key, createCrdtOp.opId);
|
|
1916
|
-
}
|
|
1917
|
-
ops.push(...newAttachChildOps);
|
|
1918
|
-
}
|
|
1919
|
-
else {
|
|
1920
|
-
updatedProps[key] = newValue;
|
|
1921
|
-
this._propToLastUpdate.set(key, opId);
|
|
1922
|
-
}
|
|
1923
|
-
this._map.set(key, newValue);
|
|
1924
|
-
updateDelta[key] = { type: "update" };
|
|
1925
|
-
}
|
|
1926
|
-
if (Object.keys(reverseUpdateOp.data).length !== 0) {
|
|
1927
|
-
reverseOps.unshift(reverseUpdateOp);
|
|
1928
|
-
}
|
|
1929
|
-
if (Object.keys(updatedProps).length !== 0) {
|
|
1930
|
-
ops.unshift({
|
|
1931
|
-
opId,
|
|
1932
|
-
id: this._id,
|
|
1933
|
-
type: OpType.UpdateObject,
|
|
1934
|
-
data: updatedProps,
|
|
1935
|
-
});
|
|
1936
|
-
}
|
|
1937
|
-
const storageUpdates = new Map();
|
|
1938
|
-
storageUpdates.set(this._id, {
|
|
1939
|
-
node: this,
|
|
1940
|
-
type: "LiveObject",
|
|
1941
|
-
updates: updateDelta,
|
|
1942
|
-
});
|
|
1943
|
-
this._doc.dispatch(ops, reverseOps, storageUpdates);
|
|
1944
|
-
}
|
|
949
|
+
constructor(obj = {}) {
|
|
950
|
+
super(), this._propToLastUpdate = new Map;
|
|
951
|
+
for (const key in obj) {
|
|
952
|
+
const value = obj[key];
|
|
953
|
+
value instanceof AbstractCrdt && value._setParentLink(this, key);
|
|
954
|
+
}
|
|
955
|
+
this._map = new Map(Object.entries(obj));
|
|
956
|
+
}
|
|
957
|
+
_serialize(parentId, parentKey, doc, intent) {
|
|
958
|
+
if (null == this._id) throw new Error("Cannot serialize item is not attached");
|
|
959
|
+
const ops = [], op = {
|
|
960
|
+
id: this._id,
|
|
961
|
+
opId: null == doc ? void 0 : doc.generateOpId(),
|
|
962
|
+
intent: intent,
|
|
963
|
+
type: OpType.CreateObject,
|
|
964
|
+
parentId: parentId,
|
|
965
|
+
parentKey: parentKey,
|
|
966
|
+
data: {}
|
|
967
|
+
};
|
|
968
|
+
ops.push(op);
|
|
969
|
+
for (const [key, value] of this._map) value instanceof AbstractCrdt ? ops.push(...value._serialize(this._id, key, doc)) : op.data[key] = value;
|
|
970
|
+
return ops;
|
|
971
|
+
}
|
|
972
|
+
static _deserialize([id, item], parentToChildren, doc) {
|
|
973
|
+
if (item.type !== CrdtType.Object) throw new Error(`Tried to deserialize a record but item type is "${item.type}"`);
|
|
974
|
+
const liveObj = new LiveObject(item.data);
|
|
975
|
+
return liveObj._attach(id, doc), this._deserializeChildren(liveObj, parentToChildren, doc);
|
|
976
|
+
}
|
|
977
|
+
static _deserializeChildren(liveObj, parentToChildren, doc) {
|
|
978
|
+
const children = parentToChildren.get(liveObj._id);
|
|
979
|
+
if (null == children) return liveObj;
|
|
980
|
+
for (const entry of children) {
|
|
981
|
+
const crdt = entry[1];
|
|
982
|
+
if (null == crdt.parentKey) throw new Error("Tried to deserialize a crdt but it does not have a parentKey and is not the root");
|
|
983
|
+
const child = deserialize(entry, parentToChildren, doc);
|
|
984
|
+
child._setParentLink(liveObj, crdt.parentKey), liveObj._map.set(crdt.parentKey, child);
|
|
985
|
+
}
|
|
986
|
+
return liveObj;
|
|
987
|
+
}
|
|
988
|
+
_attach(id, doc) {
|
|
989
|
+
super._attach(id, doc);
|
|
990
|
+
for (const [_key, value] of this._map) value instanceof AbstractCrdt && value._attach(doc.generateId(), doc);
|
|
991
|
+
}
|
|
992
|
+
_attachChild(op, isLocal) {
|
|
993
|
+
if (null == this._doc) throw new Error("Can't attach child if doc is not present");
|
|
994
|
+
const {id: id, parentKey: parentKey, opId: opId} = op, key = parentKey, child = creationOpToLiveStructure(op);
|
|
995
|
+
if (void 0 !== this._doc.getItem(id)) return this._propToLastUpdate.get(key) === opId && this._propToLastUpdate.delete(key),
|
|
996
|
+
{
|
|
997
|
+
modified: !1
|
|
998
|
+
};
|
|
999
|
+
if (isLocal) this._propToLastUpdate.set(key, opId); else if (void 0 !== this._propToLastUpdate.get(key)) return this._propToLastUpdate.get(key) === opId ? (this._propToLastUpdate.delete(key),
|
|
1000
|
+
{
|
|
1001
|
+
modified: !1
|
|
1002
|
+
}) : {
|
|
1003
|
+
modified: !1
|
|
1004
|
+
};
|
|
1005
|
+
const previousValue = this._map.get(key);
|
|
1006
|
+
let reverse;
|
|
1007
|
+
return isCrdt(previousValue) ? (reverse = previousValue._serialize(this._id, key),
|
|
1008
|
+
previousValue._detach()) : reverse = void 0 === previousValue ? [ {
|
|
1009
|
+
type: OpType.DeleteObjectKey,
|
|
1010
|
+
id: this._id,
|
|
1011
|
+
key: key
|
|
1012
|
+
} ] : [ {
|
|
1013
|
+
type: OpType.UpdateObject,
|
|
1014
|
+
id: this._id,
|
|
1015
|
+
data: {
|
|
1016
|
+
[key]: previousValue
|
|
1017
|
+
}
|
|
1018
|
+
} ], this._map.set(key, child), child._setParentLink(this, key), child._attach(id, this._doc),
|
|
1019
|
+
{
|
|
1020
|
+
reverse: reverse,
|
|
1021
|
+
modified: {
|
|
1022
|
+
node: this,
|
|
1023
|
+
type: "LiveObject",
|
|
1024
|
+
updates: {
|
|
1025
|
+
[key]: {
|
|
1026
|
+
type: "update"
|
|
1027
|
+
}
|
|
1028
|
+
}
|
|
1029
|
+
}
|
|
1030
|
+
};
|
|
1031
|
+
}
|
|
1032
|
+
_detachChild(child) {
|
|
1033
|
+
if (child) {
|
|
1034
|
+
const reverse = child._serialize(this._id, child._parentKey, this._doc);
|
|
1035
|
+
for (const [key, value] of this._map) value === child && this._map.delete(key);
|
|
1036
|
+
child._detach();
|
|
1037
|
+
return {
|
|
1038
|
+
modified: {
|
|
1039
|
+
node: this,
|
|
1040
|
+
type: "LiveObject",
|
|
1041
|
+
updates: {
|
|
1042
|
+
[child._parentKey]: {
|
|
1043
|
+
type: "delete"
|
|
1044
|
+
}
|
|
1045
|
+
}
|
|
1046
|
+
},
|
|
1047
|
+
reverse: reverse
|
|
1048
|
+
};
|
|
1049
|
+
}
|
|
1050
|
+
return {
|
|
1051
|
+
modified: !1
|
|
1052
|
+
};
|
|
1053
|
+
}
|
|
1054
|
+
_detachChildren() {
|
|
1055
|
+
for (const [key, value] of this._map) this._map.delete(key), value._detach();
|
|
1056
|
+
}
|
|
1057
|
+
_detach() {
|
|
1058
|
+
super._detach();
|
|
1059
|
+
for (const value of this._map.values()) isCrdt(value) && value._detach();
|
|
1060
|
+
}
|
|
1061
|
+
_apply(op, isLocal) {
|
|
1062
|
+
return op.type === OpType.UpdateObject ? this._applyUpdate(op, isLocal) : op.type === OpType.DeleteObjectKey ? this._applyDeleteObjectKey(op) : super._apply(op, isLocal);
|
|
1063
|
+
}
|
|
1064
|
+
_toSerializedCrdt() {
|
|
1065
|
+
var _a;
|
|
1066
|
+
const data = {};
|
|
1067
|
+
for (const [key, value] of this._map) value instanceof AbstractCrdt == !1 && (data[key] = value);
|
|
1068
|
+
return {
|
|
1069
|
+
type: CrdtType.Object,
|
|
1070
|
+
parentId: null === (_a = this._parent) || void 0 === _a ? void 0 : _a._id,
|
|
1071
|
+
parentKey: this._parentKey,
|
|
1072
|
+
data: data
|
|
1073
|
+
};
|
|
1074
|
+
}
|
|
1075
|
+
_applyUpdate(op, isLocal) {
|
|
1076
|
+
let isModified = !1;
|
|
1077
|
+
const reverse = [], reverseUpdate = {
|
|
1078
|
+
type: OpType.UpdateObject,
|
|
1079
|
+
id: this._id,
|
|
1080
|
+
data: {}
|
|
1081
|
+
};
|
|
1082
|
+
reverse.push(reverseUpdate);
|
|
1083
|
+
for (const key in op.data) {
|
|
1084
|
+
const oldValue = this._map.get(key);
|
|
1085
|
+
oldValue instanceof AbstractCrdt ? (reverse.push(...oldValue._serialize(this._id, key)),
|
|
1086
|
+
oldValue._detach()) : void 0 !== oldValue ? reverseUpdate.data[key] = oldValue : void 0 === oldValue && reverse.push({
|
|
1087
|
+
type: OpType.DeleteObjectKey,
|
|
1088
|
+
id: this._id,
|
|
1089
|
+
key: key
|
|
1090
|
+
});
|
|
1091
|
+
}
|
|
1092
|
+
const updateDelta = {};
|
|
1093
|
+
for (const key in op.data) {
|
|
1094
|
+
if (isLocal) this._propToLastUpdate.set(key, op.opId); else {
|
|
1095
|
+
if (null != this._propToLastUpdate.get(key)) {
|
|
1096
|
+
if (this._propToLastUpdate.get(key) === op.opId) {
|
|
1097
|
+
this._propToLastUpdate.delete(key);
|
|
1098
|
+
continue;
|
|
1099
|
+
}
|
|
1100
|
+
continue;
|
|
1101
|
+
}
|
|
1102
|
+
isModified = !0;
|
|
1103
|
+
}
|
|
1104
|
+
const oldValue = this._map.get(key);
|
|
1105
|
+
isCrdt(oldValue) && oldValue._detach(), isModified = !0, updateDelta[key] = {
|
|
1106
|
+
type: "update"
|
|
1107
|
+
}, this._map.set(key, op.data[key]);
|
|
1108
|
+
}
|
|
1109
|
+
return 0 !== Object.keys(reverseUpdate.data).length && reverse.unshift(reverseUpdate),
|
|
1110
|
+
isModified ? {
|
|
1111
|
+
modified: {
|
|
1112
|
+
node: this,
|
|
1113
|
+
type: "LiveObject",
|
|
1114
|
+
updates: updateDelta
|
|
1115
|
+
},
|
|
1116
|
+
reverse: reverse
|
|
1117
|
+
} : {
|
|
1118
|
+
modified: !1
|
|
1119
|
+
};
|
|
1120
|
+
}
|
|
1121
|
+
_applyDeleteObjectKey(op) {
|
|
1122
|
+
const key = op.key;
|
|
1123
|
+
if (!1 === this._map.has(key)) return {
|
|
1124
|
+
modified: !1
|
|
1125
|
+
};
|
|
1126
|
+
if (void 0 !== this._propToLastUpdate.get(key)) return {
|
|
1127
|
+
modified: !1
|
|
1128
|
+
};
|
|
1129
|
+
const oldValue = this._map.get(key);
|
|
1130
|
+
let reverse = [];
|
|
1131
|
+
return isCrdt(oldValue) ? (reverse = oldValue._serialize(this._id, op.key), oldValue._detach()) : void 0 !== oldValue && (reverse = [ {
|
|
1132
|
+
type: OpType.UpdateObject,
|
|
1133
|
+
id: this._id,
|
|
1134
|
+
data: {
|
|
1135
|
+
[key]: oldValue
|
|
1136
|
+
}
|
|
1137
|
+
} ]), this._map.delete(key), {
|
|
1138
|
+
modified: {
|
|
1139
|
+
node: this,
|
|
1140
|
+
type: "LiveObject",
|
|
1141
|
+
updates: {
|
|
1142
|
+
[op.key]: {
|
|
1143
|
+
type: "delete"
|
|
1144
|
+
}
|
|
1145
|
+
}
|
|
1146
|
+
},
|
|
1147
|
+
reverse: reverse
|
|
1148
|
+
};
|
|
1149
|
+
}
|
|
1150
|
+
toObject() {
|
|
1151
|
+
return function(iterable) {
|
|
1152
|
+
const obj = {};
|
|
1153
|
+
for (const [key, val] of iterable) obj[key] = val;
|
|
1154
|
+
return obj;
|
|
1155
|
+
}(this._map);
|
|
1156
|
+
}
|
|
1157
|
+
set(key, value) {
|
|
1158
|
+
this.update({
|
|
1159
|
+
[key]: value
|
|
1160
|
+
});
|
|
1161
|
+
}
|
|
1162
|
+
get(key) {
|
|
1163
|
+
return this._map.get(key);
|
|
1164
|
+
}
|
|
1165
|
+
delete(key) {
|
|
1166
|
+
const keyAsString = key, oldValue = this._map.get(keyAsString);
|
|
1167
|
+
if (void 0 === oldValue) return;
|
|
1168
|
+
if (null == this._doc || null == this._id) return oldValue instanceof AbstractCrdt && oldValue._detach(),
|
|
1169
|
+
void this._map.delete(keyAsString);
|
|
1170
|
+
let reverse;
|
|
1171
|
+
oldValue instanceof AbstractCrdt ? (oldValue._detach(), reverse = oldValue._serialize(this._id, keyAsString)) : reverse = [ {
|
|
1172
|
+
type: OpType.UpdateObject,
|
|
1173
|
+
data: {
|
|
1174
|
+
[keyAsString]: oldValue
|
|
1175
|
+
},
|
|
1176
|
+
id: this._id
|
|
1177
|
+
} ], this._map.delete(keyAsString);
|
|
1178
|
+
const storageUpdates = new Map;
|
|
1179
|
+
storageUpdates.set(this._id, {
|
|
1180
|
+
node: this,
|
|
1181
|
+
type: "LiveObject",
|
|
1182
|
+
updates: {
|
|
1183
|
+
[key]: {
|
|
1184
|
+
type: "delete"
|
|
1185
|
+
}
|
|
1186
|
+
}
|
|
1187
|
+
}), this._doc.dispatch([ {
|
|
1188
|
+
type: OpType.DeleteObjectKey,
|
|
1189
|
+
key: keyAsString,
|
|
1190
|
+
id: this._id,
|
|
1191
|
+
opId: this._doc.generateOpId()
|
|
1192
|
+
} ], reverse, storageUpdates);
|
|
1193
|
+
}
|
|
1194
|
+
update(overrides) {
|
|
1195
|
+
if (null == this._doc || null == this._id) {
|
|
1196
|
+
for (const key in overrides) {
|
|
1197
|
+
const oldValue = this._map.get(key);
|
|
1198
|
+
oldValue instanceof AbstractCrdt && oldValue._detach();
|
|
1199
|
+
const newValue = overrides[key];
|
|
1200
|
+
newValue instanceof AbstractCrdt && newValue._setParentLink(this, key), this._map.set(key, newValue);
|
|
1201
|
+
}
|
|
1202
|
+
return;
|
|
1203
|
+
}
|
|
1204
|
+
const ops = [], reverseOps = [], opId = this._doc.generateOpId(), updatedProps = {}, reverseUpdateOp = {
|
|
1205
|
+
id: this._id,
|
|
1206
|
+
type: OpType.UpdateObject,
|
|
1207
|
+
data: {}
|
|
1208
|
+
}, updateDelta = {};
|
|
1209
|
+
for (const key in overrides) {
|
|
1210
|
+
const oldValue = this._map.get(key);
|
|
1211
|
+
oldValue instanceof AbstractCrdt ? (reverseOps.push(...oldValue._serialize(this._id, key)),
|
|
1212
|
+
oldValue._detach()) : void 0 === oldValue ? reverseOps.push({
|
|
1213
|
+
type: OpType.DeleteObjectKey,
|
|
1214
|
+
id: this._id,
|
|
1215
|
+
key: key
|
|
1216
|
+
}) : reverseUpdateOp.data[key] = oldValue;
|
|
1217
|
+
const newValue = overrides[key];
|
|
1218
|
+
if (newValue instanceof AbstractCrdt) {
|
|
1219
|
+
newValue._setParentLink(this, key), newValue._attach(this._doc.generateId(), this._doc);
|
|
1220
|
+
const newAttachChildOps = newValue._serialize(this._id, key, this._doc), createCrdtOp = newAttachChildOps.find((op => op.parentId === this._id));
|
|
1221
|
+
createCrdtOp && this._propToLastUpdate.set(key, createCrdtOp.opId), ops.push(...newAttachChildOps);
|
|
1222
|
+
} else updatedProps[key] = newValue, this._propToLastUpdate.set(key, opId);
|
|
1223
|
+
this._map.set(key, newValue), updateDelta[key] = {
|
|
1224
|
+
type: "update"
|
|
1225
|
+
};
|
|
1226
|
+
}
|
|
1227
|
+
0 !== Object.keys(reverseUpdateOp.data).length && reverseOps.unshift(reverseUpdateOp),
|
|
1228
|
+
0 !== Object.keys(updatedProps).length && ops.unshift({
|
|
1229
|
+
opId: opId,
|
|
1230
|
+
id: this._id,
|
|
1231
|
+
type: OpType.UpdateObject,
|
|
1232
|
+
data: updatedProps
|
|
1233
|
+
});
|
|
1234
|
+
const storageUpdates = new Map;
|
|
1235
|
+
storageUpdates.set(this._id, {
|
|
1236
|
+
node: this,
|
|
1237
|
+
type: "LiveObject",
|
|
1238
|
+
updates: updateDelta
|
|
1239
|
+
}), this._doc.dispatch(ops, reverseOps, storageUpdates);
|
|
1240
|
+
}
|
|
1945
1241
|
}
|
|
1946
1242
|
|
|
1947
|
-
export { AbstractCrdt as A, ClientMessageType as C, LiveObject as L, OpType as O, ServerMessageType as S, WebsocketCloseCodes as W, isSameNodeOrChildOf as a, LiveList as b, isJsonArray as c, compact as d, isJsonObject as e, deprecateIf as f, getTreesDiffOperations as g, LiveMap as h, isTokenValid as i, LiveRegister as j, findNonSerializableValue as k, CrdtType as l, mergeStorageUpdates as m, deprecate as n, parseJson as p, remove as r };
|
|
1243
|
+
export { AbstractCrdt as A, ClientMessageType as C, LiveObject as L, OpType as O, ServerMessageType as S, WebsocketCloseCodes as W, isSameNodeOrChildOf as a, LiveList as b, isJsonArray as c, compact as d, isJsonObject as e, deprecateIf as f, getTreesDiffOperations as g, LiveMap as h, isTokenValid as i, LiveRegister as j, findNonSerializableValue as k, CrdtType as l, mergeStorageUpdates as m, deprecate as n, errorIf as o, parseJson as p, remove as r, throwUsageError as t };
|