@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.
@@ -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
+ }
@@ -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
@@ -60,7 +60,7 @@ export declare type State = {
60
60
  updates: {
61
61
  others: [];
62
62
  presence: boolean;
63
- nodes: Set<AbstractCrdt>;
63
+ storageUpdates: Map<string, StorageUpdate>;
64
64
  };
65
65
  };
66
66
  offlineOperations: Map<string, Op>;
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, modified) {
195
+ function storageDispatch(ops, reverse, storageUpdates) {
196
196
  if (state.isBatching) {
197
197
  state.batch.ops.push(...ops);
198
- for (const item of modified) {
199
- state.batch.updates.nodes.add(item);
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({ nodes: new Set(modified) });
207
+ notify({ storageUpdates: storageUpdates });
208
208
  }
209
209
  }
210
- function notify({ nodes = new Set(), presence = false, others = [], }) {
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 (nodes.size > 0) {
224
+ if (storageUpdates.size > 0) {
225
225
  for (const subscriber of state.listeners.storage) {
226
- subscriber(Array.from(nodes).map((m) => {
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: { nodes: new Set(), presence: false },
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.nodes.add(applyOpResult.modified);
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
- item._parent._setChildKey(op.parentKey, item);
327
- return {
328
- reverse: [
329
- {
330
- type: live_1.OpType.SetParentKey,
331
- id: item._id,
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
- nodes: new Set(),
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
- for (const node of applyResult.updates.nodes) {
599
- updates.nodes.add(node);
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
- // Clear the redo stack because batch is always called from a local operation
900
- state.redoStack = [];
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
- nodes: new Set(),
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: { nodes: new Set(), presence: false, others: [] },
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
  /////////////
@@ -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
  /**
@@ -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;