@liveblocks/client 0.14.4 → 0.15.0-alpha.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/cjs/AbstractCrdt.d.ts +5 -3
- package/lib/cjs/AbstractCrdt.js +1 -4
- package/lib/cjs/LiveList.d.ts +8 -2
- package/lib/cjs/LiveList.js +113 -9
- package/lib/cjs/LiveMap.d.ts +5 -1
- package/lib/cjs/LiveMap.js +35 -3
- package/lib/cjs/LiveObject.d.ts +5 -1
- package/lib/cjs/LiveObject.js +65 -10
- package/lib/cjs/LiveRegister.d.ts +5 -1
- package/lib/cjs/LiveRegister.js +6 -0
- package/lib/cjs/immutable.d.ts +9 -0
- package/lib/cjs/immutable.js +299 -0
- package/lib/cjs/index.d.ts +1 -0
- package/lib/cjs/index.js +8 -1
- package/lib/cjs/room.d.ts +1 -1
- package/lib/cjs/room.js +34 -54
- package/lib/cjs/types.d.ts +25 -4
- package/lib/cjs/utils.d.ts +6 -0
- package/lib/cjs/utils.js +76 -1
- package/lib/esm/AbstractCrdt.d.ts +5 -3
- package/lib/esm/AbstractCrdt.js +1 -4
- package/lib/esm/LiveList.d.ts +8 -2
- package/lib/esm/LiveList.js +113 -9
- package/lib/esm/LiveMap.d.ts +5 -1
- package/lib/esm/LiveMap.js +35 -3
- package/lib/esm/LiveObject.d.ts +5 -1
- package/lib/esm/LiveObject.js +65 -10
- package/lib/esm/LiveRegister.d.ts +5 -1
- package/lib/esm/LiveRegister.js +6 -0
- package/lib/esm/immutable.d.ts +9 -0
- package/lib/esm/immutable.js +290 -0
- package/lib/esm/index.d.ts +1 -0
- package/lib/esm/index.js +1 -0
- package/lib/esm/room.d.ts +1 -1
- package/lib/esm/room.js +35 -55
- package/lib/esm/types.d.ts +25 -4
- package/lib/esm/utils.d.ts +6 -0
- package/lib/esm/utils.js +73 -0
- package/package.json +3 -2
|
@@ -0,0 +1,299 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.patchImmutableObject = exports.patchLiveObject = exports.patchLiveObjectKey = exports.patchLiveList = exports.liveNodeToJson = exports.liveObjectToJson = void 0;
|
|
4
|
+
const LiveList_1 = require("./LiveList");
|
|
5
|
+
const LiveMap_1 = require("./LiveMap");
|
|
6
|
+
const LiveObject_1 = require("./LiveObject");
|
|
7
|
+
const LiveRegister_1 = require("./LiveRegister");
|
|
8
|
+
const utils_1 = require("./utils");
|
|
9
|
+
function liveObjectToJson(liveObject) {
|
|
10
|
+
const result = {};
|
|
11
|
+
const obj = liveObject.toObject();
|
|
12
|
+
for (const key in obj) {
|
|
13
|
+
result[key] = liveNodeToJson(obj[key]);
|
|
14
|
+
}
|
|
15
|
+
return result;
|
|
16
|
+
}
|
|
17
|
+
exports.liveObjectToJson = liveObjectToJson;
|
|
18
|
+
function liveMapToJson(map) {
|
|
19
|
+
const result = {};
|
|
20
|
+
const obj = Object.fromEntries(map);
|
|
21
|
+
for (const key in obj) {
|
|
22
|
+
result[key] = liveNodeToJson(obj[key]);
|
|
23
|
+
}
|
|
24
|
+
return result;
|
|
25
|
+
}
|
|
26
|
+
function liveListToJson(value) {
|
|
27
|
+
return value.toArray().map(liveNodeToJson);
|
|
28
|
+
}
|
|
29
|
+
function liveNodeToJson(value) {
|
|
30
|
+
if (value instanceof LiveObject_1.LiveObject) {
|
|
31
|
+
return liveObjectToJson(value);
|
|
32
|
+
}
|
|
33
|
+
else if (value instanceof LiveList_1.LiveList) {
|
|
34
|
+
return liveListToJson(value);
|
|
35
|
+
}
|
|
36
|
+
else if (value instanceof LiveMap_1.LiveMap) {
|
|
37
|
+
return liveMapToJson(value);
|
|
38
|
+
}
|
|
39
|
+
else if (value instanceof LiveRegister_1.LiveRegister) {
|
|
40
|
+
return value.data;
|
|
41
|
+
}
|
|
42
|
+
return value;
|
|
43
|
+
}
|
|
44
|
+
exports.liveNodeToJson = liveNodeToJson;
|
|
45
|
+
function isPlainObject(obj) {
|
|
46
|
+
return Object.prototype.toString.call(obj) === "[object Object]";
|
|
47
|
+
}
|
|
48
|
+
function anyToCrdt(obj) {
|
|
49
|
+
if (obj == null) {
|
|
50
|
+
return obj;
|
|
51
|
+
}
|
|
52
|
+
if (Array.isArray(obj)) {
|
|
53
|
+
return new LiveList_1.LiveList(obj.map(anyToCrdt));
|
|
54
|
+
}
|
|
55
|
+
if (isPlainObject(obj)) {
|
|
56
|
+
const init = {};
|
|
57
|
+
for (const key in obj) {
|
|
58
|
+
init[key] = anyToCrdt(obj[key]);
|
|
59
|
+
}
|
|
60
|
+
return new LiveObject_1.LiveObject(init);
|
|
61
|
+
}
|
|
62
|
+
return obj;
|
|
63
|
+
}
|
|
64
|
+
function patchLiveList(liveList, prev, next) {
|
|
65
|
+
let i = 0;
|
|
66
|
+
let prevEnd = prev.length - 1;
|
|
67
|
+
let nextEnd = next.length - 1;
|
|
68
|
+
let prevNode = prev[0];
|
|
69
|
+
let nextNode = next[0];
|
|
70
|
+
/**
|
|
71
|
+
* For A,B,C => A,B,C,D
|
|
72
|
+
* i = 3, prevEnd = 2, nextEnd = 3
|
|
73
|
+
*
|
|
74
|
+
* For A,B,C => B,C
|
|
75
|
+
* i = 2, prevEnd = 2, nextEnd = 1
|
|
76
|
+
*
|
|
77
|
+
* For B,C => A,B,C
|
|
78
|
+
* i = 0, pre
|
|
79
|
+
*/
|
|
80
|
+
outer: {
|
|
81
|
+
while (prevNode === nextNode) {
|
|
82
|
+
++i;
|
|
83
|
+
if (i > prevEnd || i > nextEnd) {
|
|
84
|
+
break outer;
|
|
85
|
+
}
|
|
86
|
+
prevNode = prev[i];
|
|
87
|
+
nextNode = next[i];
|
|
88
|
+
}
|
|
89
|
+
prevNode = prev[prevEnd];
|
|
90
|
+
nextNode = next[nextEnd];
|
|
91
|
+
while (prevNode === nextNode) {
|
|
92
|
+
prevEnd--;
|
|
93
|
+
nextEnd--;
|
|
94
|
+
if (i > prevEnd || i > nextEnd) {
|
|
95
|
+
break outer;
|
|
96
|
+
}
|
|
97
|
+
prevNode = prev[prevEnd];
|
|
98
|
+
nextNode = next[nextEnd];
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
if (i > prevEnd) {
|
|
102
|
+
if (i <= nextEnd) {
|
|
103
|
+
while (i <= nextEnd) {
|
|
104
|
+
liveList.insert(anyToCrdt(next[i]), i);
|
|
105
|
+
i++;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
else if (i > nextEnd) {
|
|
110
|
+
let localI = i;
|
|
111
|
+
while (localI <= prevEnd) {
|
|
112
|
+
liveList.delete(i);
|
|
113
|
+
localI++;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
else {
|
|
117
|
+
while (i <= prevEnd && i <= nextEnd) {
|
|
118
|
+
prevNode = prev[i];
|
|
119
|
+
nextNode = next[i];
|
|
120
|
+
const liveListNode = liveList.get(i);
|
|
121
|
+
if (liveListNode instanceof LiveObject_1.LiveObject &&
|
|
122
|
+
isPlainObject(prevNode) &&
|
|
123
|
+
isPlainObject(nextNode)) {
|
|
124
|
+
patchLiveObject(liveListNode, prevNode, nextNode);
|
|
125
|
+
}
|
|
126
|
+
else {
|
|
127
|
+
liveList.delete(i);
|
|
128
|
+
liveList.insert(anyToCrdt(nextNode), i);
|
|
129
|
+
}
|
|
130
|
+
i++;
|
|
131
|
+
}
|
|
132
|
+
while (i <= nextEnd) {
|
|
133
|
+
liveList.insert(anyToCrdt(next[i]), i);
|
|
134
|
+
i++;
|
|
135
|
+
}
|
|
136
|
+
while (i <= prevEnd) {
|
|
137
|
+
liveList.delete(i);
|
|
138
|
+
i++;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
exports.patchLiveList = patchLiveList;
|
|
143
|
+
function patchLiveObjectKey(liveObject, key, prev, next) {
|
|
144
|
+
if (process.env.NODE_ENV !== "production") {
|
|
145
|
+
const nonSerializableValue = (0, utils_1.findNonSerializableValue)(next);
|
|
146
|
+
if (nonSerializableValue) {
|
|
147
|
+
console.error(`New state path: '${nonSerializableValue.path}' value: '${nonSerializableValue.value}' is not serializable.\nOnly serializable value can be synced with Liveblocks.`);
|
|
148
|
+
return;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
const value = liveObject.get(key);
|
|
152
|
+
if (next === undefined) {
|
|
153
|
+
liveObject.delete(key);
|
|
154
|
+
}
|
|
155
|
+
else if (value === undefined) {
|
|
156
|
+
liveObject.set(key, anyToCrdt(next));
|
|
157
|
+
}
|
|
158
|
+
else if (prev === next) {
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
else if (value instanceof LiveList_1.LiveList &&
|
|
162
|
+
Array.isArray(prev) &&
|
|
163
|
+
Array.isArray(next)) {
|
|
164
|
+
patchLiveList(value, prev, next);
|
|
165
|
+
}
|
|
166
|
+
else if (value instanceof LiveObject_1.LiveObject &&
|
|
167
|
+
isPlainObject(prev) &&
|
|
168
|
+
isPlainObject(next)) {
|
|
169
|
+
patchLiveObject(value, prev, next);
|
|
170
|
+
}
|
|
171
|
+
else {
|
|
172
|
+
liveObject.set(key, anyToCrdt(next));
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
exports.patchLiveObjectKey = patchLiveObjectKey;
|
|
176
|
+
function patchLiveObject(root, prev, next) {
|
|
177
|
+
const updates = {};
|
|
178
|
+
for (const key in next) {
|
|
179
|
+
patchLiveObjectKey(root, key, prev[key], next[key]);
|
|
180
|
+
}
|
|
181
|
+
for (const key in prev) {
|
|
182
|
+
if (next[key] === undefined) {
|
|
183
|
+
root.delete(key);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
if (Object.keys(updates).length > 0) {
|
|
187
|
+
root.update(updates);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
exports.patchLiveObject = patchLiveObject;
|
|
191
|
+
function getParentsPath(node) {
|
|
192
|
+
const path = [];
|
|
193
|
+
while (node._parentKey != null && node._parent != null) {
|
|
194
|
+
if (node._parent instanceof LiveList_1.LiveList) {
|
|
195
|
+
path.push(node._parent._indexOfPosition(node._parentKey));
|
|
196
|
+
}
|
|
197
|
+
else {
|
|
198
|
+
path.push(node._parentKey);
|
|
199
|
+
}
|
|
200
|
+
node = node._parent;
|
|
201
|
+
}
|
|
202
|
+
return path;
|
|
203
|
+
}
|
|
204
|
+
function patchImmutableObject(state, updates) {
|
|
205
|
+
return updates.reduce((state, update) => patchImmutableObjectWithUpdate(state, update), state);
|
|
206
|
+
}
|
|
207
|
+
exports.patchImmutableObject = patchImmutableObject;
|
|
208
|
+
function patchImmutableObjectWithUpdate(state, update) {
|
|
209
|
+
const path = getParentsPath(update.node);
|
|
210
|
+
return patchImmutableNode(state, path, update);
|
|
211
|
+
}
|
|
212
|
+
function patchImmutableNode(state, path, update) {
|
|
213
|
+
var _a, _b, _c, _d;
|
|
214
|
+
const pathItem = path.pop();
|
|
215
|
+
if (pathItem === undefined) {
|
|
216
|
+
switch (update.type) {
|
|
217
|
+
case "LiveObject": {
|
|
218
|
+
if (typeof state !== "object") {
|
|
219
|
+
throw new Error("Internal: received update on LiveObject but state was not an object");
|
|
220
|
+
}
|
|
221
|
+
let newState = Object.assign({}, state);
|
|
222
|
+
for (const key in update.updates) {
|
|
223
|
+
if (((_a = update.updates[key]) === null || _a === void 0 ? void 0 : _a.type) === "update") {
|
|
224
|
+
newState[key] = liveNodeToJson(update.node.get(key));
|
|
225
|
+
}
|
|
226
|
+
else if (((_b = update.updates[key]) === null || _b === void 0 ? void 0 : _b.type) === "delete") {
|
|
227
|
+
delete newState[key];
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
return newState;
|
|
231
|
+
}
|
|
232
|
+
case "LiveList": {
|
|
233
|
+
if (Array.isArray(state) === false) {
|
|
234
|
+
throw new Error("Internal: received update on LiveList but state was not an array");
|
|
235
|
+
}
|
|
236
|
+
let newState = state.map((x) => x);
|
|
237
|
+
for (const listUpdate of update.updates) {
|
|
238
|
+
if (listUpdate.type === "insert") {
|
|
239
|
+
if (listUpdate.index === newState.length) {
|
|
240
|
+
newState.push(liveNodeToJson(listUpdate.item));
|
|
241
|
+
}
|
|
242
|
+
else {
|
|
243
|
+
newState = [
|
|
244
|
+
...newState.slice(0, listUpdate.index),
|
|
245
|
+
liveNodeToJson(listUpdate.item),
|
|
246
|
+
...newState.slice(listUpdate.index),
|
|
247
|
+
];
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
else if (listUpdate.type === "delete") {
|
|
251
|
+
newState.splice(listUpdate.index, 1);
|
|
252
|
+
}
|
|
253
|
+
else if (listUpdate.type === "move") {
|
|
254
|
+
if (listUpdate.previousIndex > listUpdate.index) {
|
|
255
|
+
newState = [
|
|
256
|
+
...newState.slice(0, listUpdate.index),
|
|
257
|
+
liveNodeToJson(listUpdate.item),
|
|
258
|
+
...newState.slice(listUpdate.index, listUpdate.previousIndex),
|
|
259
|
+
...newState.slice(listUpdate.previousIndex + 1),
|
|
260
|
+
];
|
|
261
|
+
}
|
|
262
|
+
else {
|
|
263
|
+
newState = [
|
|
264
|
+
...newState.slice(0, listUpdate.previousIndex),
|
|
265
|
+
...newState.slice(listUpdate.previousIndex + 1, listUpdate.index + 1),
|
|
266
|
+
liveNodeToJson(listUpdate.item),
|
|
267
|
+
...newState.slice(listUpdate.index + 1),
|
|
268
|
+
];
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
return newState;
|
|
273
|
+
}
|
|
274
|
+
case "LiveMap": {
|
|
275
|
+
if (typeof state !== "object") {
|
|
276
|
+
throw new Error("Internal: received update on LiveMap but state was not an object");
|
|
277
|
+
}
|
|
278
|
+
let newState = Object.assign({}, state);
|
|
279
|
+
for (const key in update.updates) {
|
|
280
|
+
if (((_c = update.updates[key]) === null || _c === void 0 ? void 0 : _c.type) === "update") {
|
|
281
|
+
newState[key] = liveNodeToJson(update.node.get(key));
|
|
282
|
+
}
|
|
283
|
+
else if (((_d = update.updates[key]) === null || _d === void 0 ? void 0 : _d.type) === "delete") {
|
|
284
|
+
delete newState[key];
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
return newState;
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
if (Array.isArray(state)) {
|
|
292
|
+
const newArray = [...state];
|
|
293
|
+
newArray[pathItem] = patchImmutableNode(state[pathItem], path, update);
|
|
294
|
+
return newArray;
|
|
295
|
+
}
|
|
296
|
+
else {
|
|
297
|
+
return Object.assign(Object.assign({}, state), { [pathItem]: patchImmutableNode(state[pathItem], path, update) });
|
|
298
|
+
}
|
|
299
|
+
}
|
package/lib/cjs/index.d.ts
CHANGED
|
@@ -3,3 +3,4 @@ export { LiveMap } from "./LiveMap";
|
|
|
3
3
|
export { LiveList } from "./LiveList";
|
|
4
4
|
export type { Others, Presence, Room, Client, User, BroadcastOptions, } from "./types";
|
|
5
5
|
export { createClient } from "./client";
|
|
6
|
+
export { liveObjectToJson, liveNodeToJson, patchLiveList, patchImmutableObject, patchLiveObject, patchLiveObjectKey, } from "./immutable";
|
package/lib/cjs/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.createClient = exports.LiveList = exports.LiveMap = exports.LiveObject = void 0;
|
|
3
|
+
exports.patchLiveObjectKey = exports.patchLiveObject = exports.patchImmutableObject = exports.patchLiveList = exports.liveNodeToJson = exports.liveObjectToJson = exports.createClient = exports.LiveList = exports.LiveMap = exports.LiveObject = void 0;
|
|
4
4
|
var LiveObject_1 = require("./LiveObject");
|
|
5
5
|
Object.defineProperty(exports, "LiveObject", { enumerable: true, get: function () { return LiveObject_1.LiveObject; } });
|
|
6
6
|
var LiveMap_1 = require("./LiveMap");
|
|
@@ -9,3 +9,10 @@ var LiveList_1 = require("./LiveList");
|
|
|
9
9
|
Object.defineProperty(exports, "LiveList", { enumerable: true, get: function () { return LiveList_1.LiveList; } });
|
|
10
10
|
var client_1 = require("./client");
|
|
11
11
|
Object.defineProperty(exports, "createClient", { enumerable: true, get: function () { return client_1.createClient; } });
|
|
12
|
+
var immutable_1 = require("./immutable");
|
|
13
|
+
Object.defineProperty(exports, "liveObjectToJson", { enumerable: true, get: function () { return immutable_1.liveObjectToJson; } });
|
|
14
|
+
Object.defineProperty(exports, "liveNodeToJson", { enumerable: true, get: function () { return immutable_1.liveNodeToJson; } });
|
|
15
|
+
Object.defineProperty(exports, "patchLiveList", { enumerable: true, get: function () { return immutable_1.patchLiveList; } });
|
|
16
|
+
Object.defineProperty(exports, "patchImmutableObject", { enumerable: true, get: function () { return immutable_1.patchImmutableObject; } });
|
|
17
|
+
Object.defineProperty(exports, "patchLiveObject", { enumerable: true, get: function () { return immutable_1.patchLiveObject; } });
|
|
18
|
+
Object.defineProperty(exports, "patchLiveObjectKey", { enumerable: true, get: function () { return immutable_1.patchLiveObjectKey; } });
|
package/lib/cjs/room.d.ts
CHANGED
package/lib/cjs/room.js
CHANGED
|
@@ -192,22 +192,22 @@ function makeStateMachine(state, context, mockedEffects) {
|
|
|
192
192
|
state.undoStack.push(historyItem);
|
|
193
193
|
}
|
|
194
194
|
}
|
|
195
|
-
function storageDispatch(ops, reverse,
|
|
195
|
+
function storageDispatch(ops, reverse, storageUpdates) {
|
|
196
196
|
if (state.isBatching) {
|
|
197
197
|
state.batch.ops.push(...ops);
|
|
198
|
-
|
|
199
|
-
state.batch.updates.
|
|
200
|
-
}
|
|
198
|
+
storageUpdates.forEach((value, key) => {
|
|
199
|
+
state.batch.updates.storageUpdates.set(key, (0, utils_1.mergeStorageUpdates)(state.batch.updates.storageUpdates.get(key), value));
|
|
200
|
+
});
|
|
201
201
|
state.batch.reverseOps.push(...reverse);
|
|
202
202
|
}
|
|
203
203
|
else {
|
|
204
204
|
addToUndoStack(reverse);
|
|
205
205
|
state.redoStack = [];
|
|
206
206
|
dispatch(ops);
|
|
207
|
-
notify({
|
|
207
|
+
notify({ storageUpdates: storageUpdates });
|
|
208
208
|
}
|
|
209
209
|
}
|
|
210
|
-
function notify({
|
|
210
|
+
function notify({ storageUpdates = new Map(), presence = false, others = [], }) {
|
|
211
211
|
if (others.length > 0) {
|
|
212
212
|
state.others = makeOthers(state.users);
|
|
213
213
|
for (const event of others) {
|
|
@@ -221,28 +221,9 @@ function makeStateMachine(state, context, mockedEffects) {
|
|
|
221
221
|
listener(state.me);
|
|
222
222
|
}
|
|
223
223
|
}
|
|
224
|
-
if (
|
|
224
|
+
if (storageUpdates.size > 0) {
|
|
225
225
|
for (const subscriber of state.listeners.storage) {
|
|
226
|
-
subscriber(Array.from(
|
|
227
|
-
if (m instanceof LiveObject_1.LiveObject) {
|
|
228
|
-
return {
|
|
229
|
-
type: "LiveObject",
|
|
230
|
-
node: m,
|
|
231
|
-
};
|
|
232
|
-
}
|
|
233
|
-
else if (m instanceof LiveList_1.LiveList) {
|
|
234
|
-
return {
|
|
235
|
-
type: "LiveList",
|
|
236
|
-
node: m,
|
|
237
|
-
};
|
|
238
|
-
}
|
|
239
|
-
else {
|
|
240
|
-
return {
|
|
241
|
-
type: "LiveMap",
|
|
242
|
-
node: m,
|
|
243
|
-
};
|
|
244
|
-
}
|
|
245
|
-
}));
|
|
226
|
+
subscriber(Array.from(storageUpdates.values()));
|
|
246
227
|
}
|
|
247
228
|
}
|
|
248
229
|
}
|
|
@@ -265,7 +246,10 @@ function makeStateMachine(state, context, mockedEffects) {
|
|
|
265
246
|
function apply(item, isLocal) {
|
|
266
247
|
const result = {
|
|
267
248
|
reverse: [],
|
|
268
|
-
updates: {
|
|
249
|
+
updates: {
|
|
250
|
+
storageUpdates: new Map(),
|
|
251
|
+
presence: false,
|
|
252
|
+
},
|
|
269
253
|
};
|
|
270
254
|
for (const op of item) {
|
|
271
255
|
if (op.type === "presence") {
|
|
@@ -295,7 +279,7 @@ function makeStateMachine(state, context, mockedEffects) {
|
|
|
295
279
|
}
|
|
296
280
|
const applyOpResult = applyOp(op, isLocal);
|
|
297
281
|
if (applyOpResult.modified) {
|
|
298
|
-
result.updates.
|
|
282
|
+
result.updates.storageUpdates.set(applyOpResult.modified.node._id, (0, utils_1.mergeStorageUpdates)(result.updates.storageUpdates.get(applyOpResult.modified.node._id), applyOpResult.modified));
|
|
299
283
|
result.reverse.unshift(...applyOpResult.reverse);
|
|
300
284
|
}
|
|
301
285
|
}
|
|
@@ -323,17 +307,12 @@ function makeStateMachine(state, context, mockedEffects) {
|
|
|
323
307
|
}
|
|
324
308
|
if (item._parent instanceof LiveList_1.LiveList) {
|
|
325
309
|
const previousKey = item._parentKey;
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
parentKey: previousKey,
|
|
333
|
-
},
|
|
334
|
-
],
|
|
335
|
-
modified: item._parent,
|
|
336
|
-
};
|
|
310
|
+
if (previousKey === op.parentKey) {
|
|
311
|
+
return { modified: false };
|
|
312
|
+
}
|
|
313
|
+
else {
|
|
314
|
+
return item._parent._setChildKey(op.parentKey, item, previousKey);
|
|
315
|
+
}
|
|
337
316
|
}
|
|
338
317
|
return { modified: false };
|
|
339
318
|
}
|
|
@@ -559,7 +538,7 @@ See v0.13 release notes for more information.
|
|
|
559
538
|
subMessages.push(message);
|
|
560
539
|
}
|
|
561
540
|
const updates = {
|
|
562
|
-
|
|
541
|
+
storageUpdates: new Map(),
|
|
563
542
|
others: [],
|
|
564
543
|
};
|
|
565
544
|
for (const subMessage of subMessages) {
|
|
@@ -595,9 +574,9 @@ See v0.13 release notes for more information.
|
|
|
595
574
|
}
|
|
596
575
|
case live_1.ServerMessageType.UpdateStorage: {
|
|
597
576
|
const applyResult = apply(subMessage.ops, false);
|
|
598
|
-
|
|
599
|
-
updates.
|
|
600
|
-
}
|
|
577
|
+
applyResult.updates.storageUpdates.forEach((value, key) => {
|
|
578
|
+
updates.storageUpdates.set(key, (0, utils_1.mergeStorageUpdates)(updates.storageUpdates.get(key), value));
|
|
579
|
+
});
|
|
601
580
|
break;
|
|
602
581
|
}
|
|
603
582
|
}
|
|
@@ -664,11 +643,6 @@ See v0.13 release notes for more information.
|
|
|
664
643
|
if (state.connection.state === "connecting") {
|
|
665
644
|
updateConnection(Object.assign(Object.assign({}, state.connection), { state: "open" }));
|
|
666
645
|
state.numberOfRetry = 0;
|
|
667
|
-
// Re-broadcast the user presence during a reconnect.
|
|
668
|
-
if (state.lastConnectionId !== undefined) {
|
|
669
|
-
state.buffer.presence = state.me;
|
|
670
|
-
tryFlushing();
|
|
671
|
-
}
|
|
672
646
|
state.lastConnectionId = state.connection.id;
|
|
673
647
|
if (state.root) {
|
|
674
648
|
state.buffer.messages.push({ type: live_1.ClientMessageType.FetchStorage });
|
|
@@ -896,8 +870,11 @@ See v0.13 release notes for more information.
|
|
|
896
870
|
if (state.batch.reverseOps.length > 0) {
|
|
897
871
|
addToUndoStack(state.batch.reverseOps);
|
|
898
872
|
}
|
|
899
|
-
|
|
900
|
-
|
|
873
|
+
if (state.batch.ops.length > 0) {
|
|
874
|
+
// Only clear the redo stack if something has changed during a batch
|
|
875
|
+
// Clear the redo stack because batch is always called from a local operation
|
|
876
|
+
state.redoStack = [];
|
|
877
|
+
}
|
|
901
878
|
if (state.batch.ops.length > 0) {
|
|
902
879
|
dispatch(state.batch.ops);
|
|
903
880
|
}
|
|
@@ -907,7 +884,7 @@ See v0.13 release notes for more information.
|
|
|
907
884
|
reverseOps: [],
|
|
908
885
|
updates: {
|
|
909
886
|
others: [],
|
|
910
|
-
|
|
887
|
+
storageUpdates: new Map(),
|
|
911
888
|
presence: false,
|
|
912
889
|
},
|
|
913
890
|
};
|
|
@@ -1019,7 +996,11 @@ function defaultState(me, defaultStorageRoot) {
|
|
|
1019
996
|
isBatching: false,
|
|
1020
997
|
batch: {
|
|
1021
998
|
ops: [],
|
|
1022
|
-
updates: {
|
|
999
|
+
updates: {
|
|
1000
|
+
storageUpdates: new Map(),
|
|
1001
|
+
presence: false,
|
|
1002
|
+
others: [],
|
|
1003
|
+
},
|
|
1023
1004
|
reverseOps: [],
|
|
1024
1005
|
},
|
|
1025
1006
|
offlineOperations: new Map(),
|
|
@@ -1030,7 +1011,6 @@ function createRoom(options, context) {
|
|
|
1030
1011
|
const state = defaultState(options.defaultPresence, options.defaultStorageRoot);
|
|
1031
1012
|
const machine = makeStateMachine(state, context);
|
|
1032
1013
|
const room = {
|
|
1033
|
-
id: context.room,
|
|
1034
1014
|
/////////////
|
|
1035
1015
|
// Core //
|
|
1036
1016
|
/////////////
|
package/lib/cjs/types.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { AbstractCrdt } from "./AbstractCrdt";
|
|
1
2
|
import type { LiveList } from "./LiveList";
|
|
2
3
|
import type { LiveMap } from "./LiveMap";
|
|
3
4
|
import type { LiveObject } from "./LiveObject";
|
|
@@ -16,17 +17,41 @@ export declare type RoomEventCallbackMap = {
|
|
|
16
17
|
error: ErrorCallback;
|
|
17
18
|
connection: ConnectionCallback;
|
|
18
19
|
};
|
|
20
|
+
export declare type UpdateDelta = {
|
|
21
|
+
type: "update";
|
|
22
|
+
} | {
|
|
23
|
+
type: "delete";
|
|
24
|
+
};
|
|
19
25
|
export declare type LiveMapUpdates<TKey extends string = string, TValue = any> = {
|
|
20
26
|
type: "LiveMap";
|
|
21
27
|
node: LiveMap<TKey, TValue>;
|
|
28
|
+
updates: Record<TKey, UpdateDelta>;
|
|
22
29
|
};
|
|
30
|
+
export declare type LiveObjectUpdateDelta<T> = Partial<{
|
|
31
|
+
[Property in keyof T]: UpdateDelta;
|
|
32
|
+
}>;
|
|
23
33
|
export declare type LiveObjectUpdates<TData = any> = {
|
|
24
34
|
type: "LiveObject";
|
|
25
35
|
node: LiveObject<TData>;
|
|
36
|
+
updates: LiveObjectUpdateDelta<TData>;
|
|
37
|
+
};
|
|
38
|
+
export declare type LiveListUpdateDelta = {
|
|
39
|
+
index: number;
|
|
40
|
+
item: AbstractCrdt;
|
|
41
|
+
type: "insert";
|
|
42
|
+
} | {
|
|
43
|
+
index: number;
|
|
44
|
+
type: "delete";
|
|
45
|
+
} | {
|
|
46
|
+
index: number;
|
|
47
|
+
previousIndex: number;
|
|
48
|
+
item: AbstractCrdt;
|
|
49
|
+
type: "move";
|
|
26
50
|
};
|
|
27
51
|
export declare type LiveListUpdates<TItem = any> = {
|
|
28
52
|
type: "LiveList";
|
|
29
53
|
node: LiveList<TItem>;
|
|
54
|
+
updates: LiveListUpdateDelta[];
|
|
30
55
|
};
|
|
31
56
|
export declare type BroadcastOptions = {
|
|
32
57
|
/**
|
|
@@ -167,10 +192,6 @@ export declare type OthersEvent<T extends Presence = Presence> = {
|
|
|
167
192
|
type: "reset";
|
|
168
193
|
};
|
|
169
194
|
export declare type Room = {
|
|
170
|
-
/**
|
|
171
|
-
* The id of the room.
|
|
172
|
-
*/
|
|
173
|
-
readonly id: string;
|
|
174
195
|
getConnectionState(): ConnectionState;
|
|
175
196
|
subscribe: {
|
|
176
197
|
/**
|
package/lib/cjs/utils.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { AbstractCrdt, Doc } from "./AbstractCrdt";
|
|
2
2
|
import { SerializedCrdtWithId, Op, SerializedCrdt } from "./live";
|
|
3
|
+
import { StorageUpdate } from "./types";
|
|
3
4
|
export declare function remove<T>(array: T[], item: T): void;
|
|
4
5
|
export declare function isSameNodeOrChildOf(node: AbstractCrdt, parent: AbstractCrdt): boolean;
|
|
5
6
|
export declare function deserialize(entry: SerializedCrdtWithId, parentToChildren: Map<string, SerializedCrdtWithId[]>, doc: Doc): AbstractCrdt;
|
|
@@ -7,3 +8,8 @@ export declare function isCrdt(obj: any): obj is AbstractCrdt;
|
|
|
7
8
|
export declare function selfOrRegisterValue(obj: AbstractCrdt): any;
|
|
8
9
|
export declare function selfOrRegister(obj: any): AbstractCrdt;
|
|
9
10
|
export declare function getTreesDiffOperations(currentItems: Map<string, SerializedCrdt>, newItems: Map<string, SerializedCrdt>): Op[];
|
|
11
|
+
export declare function mergeStorageUpdates(first: StorageUpdate | undefined, second: StorageUpdate): StorageUpdate;
|
|
12
|
+
export declare function findNonSerializableValue(value: any, path?: string): {
|
|
13
|
+
path: string;
|
|
14
|
+
value: any;
|
|
15
|
+
} | false;
|
package/lib/cjs/utils.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.getTreesDiffOperations = exports.selfOrRegister = exports.selfOrRegisterValue = exports.isCrdt = exports.deserialize = exports.isSameNodeOrChildOf = exports.remove = void 0;
|
|
3
|
+
exports.findNonSerializableValue = exports.mergeStorageUpdates = exports.getTreesDiffOperations = exports.selfOrRegister = exports.selfOrRegisterValue = exports.isCrdt = exports.deserialize = exports.isSameNodeOrChildOf = exports.remove = void 0;
|
|
4
4
|
const live_1 = require("./live");
|
|
5
5
|
const LiveList_1 = require("./LiveList");
|
|
6
6
|
const LiveMap_1 = require("./LiveMap");
|
|
@@ -148,3 +148,78 @@ function getTreesDiffOperations(currentItems, newItems) {
|
|
|
148
148
|
return ops;
|
|
149
149
|
}
|
|
150
150
|
exports.getTreesDiffOperations = getTreesDiffOperations;
|
|
151
|
+
function mergeStorageUpdates(first, second) {
|
|
152
|
+
if (!first) {
|
|
153
|
+
return second;
|
|
154
|
+
}
|
|
155
|
+
if (second.type === "LiveObject") {
|
|
156
|
+
const updates = first.updates;
|
|
157
|
+
for (const [key, value] of Object.entries(second.updates)) {
|
|
158
|
+
updates[key] = value;
|
|
159
|
+
}
|
|
160
|
+
return Object.assign(Object.assign({}, second), { updates: updates });
|
|
161
|
+
}
|
|
162
|
+
else if (second.type === "LiveMap") {
|
|
163
|
+
const updates = first.updates;
|
|
164
|
+
for (const [key, value] of Object.entries(second.updates)) {
|
|
165
|
+
updates[key] = value;
|
|
166
|
+
}
|
|
167
|
+
return Object.assign(Object.assign({}, second), { updates: updates });
|
|
168
|
+
}
|
|
169
|
+
else if (second.type === "LiveList") {
|
|
170
|
+
const updates = first.updates;
|
|
171
|
+
return Object.assign(Object.assign({}, second), { updates: updates.concat(second.updates) });
|
|
172
|
+
}
|
|
173
|
+
return second;
|
|
174
|
+
}
|
|
175
|
+
exports.mergeStorageUpdates = mergeStorageUpdates;
|
|
176
|
+
function isPlain(value) {
|
|
177
|
+
const type = typeof value;
|
|
178
|
+
return (type === "undefined" ||
|
|
179
|
+
value === null ||
|
|
180
|
+
type === "string" ||
|
|
181
|
+
type === "boolean" ||
|
|
182
|
+
type === "number" ||
|
|
183
|
+
Array.isArray(value) ||
|
|
184
|
+
isPlainObject(value));
|
|
185
|
+
}
|
|
186
|
+
function isPlainObject(value) {
|
|
187
|
+
if (typeof value !== "object" || value === null)
|
|
188
|
+
return false;
|
|
189
|
+
let proto = Object.getPrototypeOf(value);
|
|
190
|
+
if (proto === null)
|
|
191
|
+
return true;
|
|
192
|
+
let baseProto = proto;
|
|
193
|
+
while (Object.getPrototypeOf(baseProto) !== null) {
|
|
194
|
+
baseProto = Object.getPrototypeOf(baseProto);
|
|
195
|
+
}
|
|
196
|
+
return proto === baseProto;
|
|
197
|
+
}
|
|
198
|
+
function findNonSerializableValue(value, path = "") {
|
|
199
|
+
if (!isPlain) {
|
|
200
|
+
return {
|
|
201
|
+
path: path || "root",
|
|
202
|
+
value: value,
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
if (typeof value !== "object" || value === null) {
|
|
206
|
+
return false;
|
|
207
|
+
}
|
|
208
|
+
for (const [key, nestedValue] of Object.entries(value)) {
|
|
209
|
+
const nestedPath = path ? path + "." + key : key;
|
|
210
|
+
if (!isPlain(nestedValue)) {
|
|
211
|
+
return {
|
|
212
|
+
path: nestedPath,
|
|
213
|
+
value: nestedValue,
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
if (typeof nestedValue === "object") {
|
|
217
|
+
const nonSerializableNestedValue = findNonSerializableValue(nestedValue, nestedPath);
|
|
218
|
+
if (nonSerializableNestedValue) {
|
|
219
|
+
return nonSerializableNestedValue;
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
return false;
|
|
224
|
+
}
|
|
225
|
+
exports.findNonSerializableValue = findNonSerializableValue;
|