@liveblocks/client 0.13.3 → 0.15.0-alpha.2

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.
Files changed (45) hide show
  1. package/lib/cjs/AbstractCrdt.d.ts +13 -7
  2. package/lib/cjs/AbstractCrdt.js +2 -5
  3. package/lib/cjs/LiveList.d.ts +16 -6
  4. package/lib/cjs/LiveList.js +148 -17
  5. package/lib/cjs/LiveMap.d.ts +12 -4
  6. package/lib/cjs/LiveMap.js +57 -7
  7. package/lib/cjs/LiveObject.d.ts +22 -8
  8. package/lib/cjs/LiveObject.js +109 -24
  9. package/lib/cjs/LiveRegister.d.ts +13 -5
  10. package/lib/cjs/LiveRegister.js +23 -4
  11. package/lib/cjs/immutable.d.ts +1 -0
  12. package/lib/cjs/immutable.js +64 -6
  13. package/lib/cjs/index.d.ts +1 -0
  14. package/lib/cjs/index.js +8 -1
  15. package/lib/cjs/live.d.ts +7 -0
  16. package/lib/cjs/room.d.ts +9 -1
  17. package/lib/cjs/room.js +131 -70
  18. package/lib/cjs/types.d.ts +25 -0
  19. package/lib/cjs/utils.d.ts +4 -1
  20. package/lib/cjs/utils.js +101 -1
  21. package/lib/esm/AbstractCrdt.d.ts +13 -7
  22. package/lib/esm/AbstractCrdt.js +2 -5
  23. package/lib/esm/LiveList.d.ts +16 -6
  24. package/lib/esm/LiveList.js +149 -18
  25. package/lib/esm/LiveMap.d.ts +12 -4
  26. package/lib/esm/LiveMap.js +57 -7
  27. package/lib/esm/LiveObject.d.ts +22 -8
  28. package/lib/esm/LiveObject.js +109 -24
  29. package/lib/esm/LiveRegister.d.ts +13 -5
  30. package/lib/esm/LiveRegister.js +24 -5
  31. package/lib/esm/immutable.d.ts +1 -0
  32. package/lib/esm/immutable.js +63 -6
  33. package/lib/esm/index.d.ts +1 -0
  34. package/lib/esm/index.js +1 -0
  35. package/lib/esm/live.d.ts +7 -0
  36. package/lib/esm/room.d.ts +9 -1
  37. package/lib/esm/room.js +132 -71
  38. package/lib/esm/types.d.ts +25 -0
  39. package/lib/esm/utils.d.ts +4 -1
  40. package/lib/esm/utils.js +99 -1
  41. package/package.json +2 -2
  42. package/lib/cjs/immutable/index.d.ts +0 -7
  43. package/lib/cjs/immutable/index.js +0 -214
  44. package/lib/esm/immutable/index.d.ts +0 -7
  45. package/lib/esm/immutable/index.js +0 -207
package/lib/esm/room.js CHANGED
@@ -7,7 +7,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
7
7
  step((generator = generator.apply(thisArg, _arguments || [])).next());
8
8
  });
9
9
  };
10
- import { isSameNodeOrChildOf, remove } from "./utils";
10
+ import { getTreesDiffOperations, isSameNodeOrChildOf, remove, mergeStorageUpdates, } from "./utils";
11
11
  import auth, { parseToken } from "./authentication";
12
12
  import { ClientMessageType, ServerMessageType, OpType, } from "./live";
13
13
  import { LiveMap } from "./LiveMap";
@@ -110,18 +110,23 @@ export function makeStateMachine(state, context, mockedEffects) {
110
110
  };
111
111
  return genericSubscribe(cb);
112
112
  }
113
- function createRootFromMessage(message) {
114
- state.root = load(message.items);
113
+ function createOrUpdateRootFromMessage(message) {
114
+ if (message.items.length === 0) {
115
+ throw new Error("Internal error: cannot load storage without items");
116
+ }
117
+ if (state.root) {
118
+ updateRoot(message.items);
119
+ }
120
+ else {
121
+ state.root = load(message.items);
122
+ }
115
123
  for (const key in state.defaultStorageRoot) {
116
124
  if (state.root.get(key) == null) {
117
125
  state.root.set(key, state.defaultStorageRoot[key]);
118
126
  }
119
127
  }
120
128
  }
121
- function load(items) {
122
- if (items.length === 0) {
123
- throw new Error("Internal error: cannot load storage without items");
124
- }
129
+ function buildRootAndParentToChildren(items) {
125
130
  const parentToChildren = new Map();
126
131
  let root = null;
127
132
  for (const tuple of items) {
@@ -142,6 +147,23 @@ export function makeStateMachine(state, context, mockedEffects) {
142
147
  if (root == null) {
143
148
  throw new Error("Root can't be null");
144
149
  }
150
+ return [root, parentToChildren];
151
+ }
152
+ function updateRoot(items) {
153
+ if (!state.root) {
154
+ return;
155
+ }
156
+ const currentItems = new Map();
157
+ state.items.forEach((liveCrdt, id) => {
158
+ currentItems.set(id, liveCrdt._toSerializedCrdt());
159
+ });
160
+ // Get operations that represent the diff between 2 states.
161
+ const ops = getTreesDiffOperations(currentItems, new Map(items));
162
+ const result = apply(ops, false);
163
+ notify(result.updates);
164
+ }
165
+ function load(items) {
166
+ const [root, parentToChildren] = buildRootAndParentToChildren(items);
145
167
  return LiveObject._deserialize(root, parentToChildren, {
146
168
  addItem,
147
169
  deleteItem,
@@ -171,22 +193,22 @@ export function makeStateMachine(state, context, mockedEffects) {
171
193
  state.undoStack.push(historyItem);
172
194
  }
173
195
  }
174
- function storageDispatch(ops, reverse, modified) {
196
+ function storageDispatch(ops, reverse, storageUpdates) {
175
197
  if (state.isBatching) {
176
198
  state.batch.ops.push(...ops);
177
- for (const item of modified) {
178
- state.batch.updates.nodes.add(item);
179
- }
199
+ storageUpdates.forEach((value, key) => {
200
+ state.batch.updates.storageUpdates.set(key, mergeStorageUpdates(state.batch.updates.storageUpdates.get(key), value));
201
+ });
180
202
  state.batch.reverseOps.push(...reverse);
181
203
  }
182
204
  else {
183
205
  addToUndoStack(reverse);
184
206
  state.redoStack = [];
185
207
  dispatch(ops);
186
- notify({ nodes: new Set(modified) });
208
+ notify({ storageUpdates: storageUpdates });
187
209
  }
188
210
  }
189
- function notify({ nodes = new Set(), presence = false, others = [], }) {
211
+ function notify({ storageUpdates = new Map(), presence = false, others = [], }) {
190
212
  if (others.length > 0) {
191
213
  state.others = makeOthers(state.users);
192
214
  for (const event of others) {
@@ -200,28 +222,9 @@ export function makeStateMachine(state, context, mockedEffects) {
200
222
  listener(state.me);
201
223
  }
202
224
  }
203
- if (nodes.size > 0) {
225
+ if (storageUpdates.size > 0) {
204
226
  for (const subscriber of state.listeners.storage) {
205
- subscriber(Array.from(nodes).map((m) => {
206
- if (m instanceof LiveObject) {
207
- return {
208
- type: "LiveObject",
209
- node: m,
210
- };
211
- }
212
- else if (m instanceof LiveList) {
213
- return {
214
- type: "LiveList",
215
- node: m,
216
- };
217
- }
218
- else {
219
- return {
220
- type: "LiveMap",
221
- node: m,
222
- };
223
- }
224
- }));
227
+ subscriber(Array.from(storageUpdates.values()));
225
228
  }
226
229
  }
227
230
  }
@@ -230,7 +233,10 @@ export function makeStateMachine(state, context, mockedEffects) {
230
233
  state.connection.state === "connecting") {
231
234
  return state.connection.id;
232
235
  }
233
- throw new Error("Internal. Tried to get connection id but connection is not open");
236
+ else if (state.lastConnectionId !== null) {
237
+ return state.lastConnectionId;
238
+ }
239
+ throw new Error("Internal. Tried to get connection id but connection was never open");
234
240
  }
235
241
  function generateId() {
236
242
  return `${getConnectionId()}:${state.clock++}`;
@@ -238,10 +244,13 @@ export function makeStateMachine(state, context, mockedEffects) {
238
244
  function generateOpId() {
239
245
  return `${getConnectionId()}:${state.opClock++}`;
240
246
  }
241
- function apply(item) {
247
+ function apply(item, isLocal) {
242
248
  const result = {
243
249
  reverse: [],
244
- updates: { nodes: new Set(), presence: false },
250
+ updates: {
251
+ storageUpdates: new Map(),
252
+ presence: false,
253
+ },
245
254
  };
246
255
  for (const op of item) {
247
256
  if (op.type === "presence") {
@@ -265,16 +274,23 @@ export function makeStateMachine(state, context, mockedEffects) {
265
274
  result.updates.presence = true;
266
275
  }
267
276
  else {
268
- const applyOpResult = applyOp(op);
277
+ // Ops applied after undo/redo don't have an opId.
278
+ if (isLocal && !op.opId) {
279
+ op.opId = generateOpId();
280
+ }
281
+ const applyOpResult = applyOp(op, isLocal);
269
282
  if (applyOpResult.modified) {
270
- result.updates.nodes.add(applyOpResult.modified);
283
+ result.updates.storageUpdates.set(applyOpResult.modified.node._id, mergeStorageUpdates(result.updates.storageUpdates.get(applyOpResult.modified.node._id), applyOpResult.modified));
271
284
  result.reverse.unshift(...applyOpResult.reverse);
272
285
  }
273
286
  }
274
287
  }
275
288
  return result;
276
289
  }
277
- function applyOp(op) {
290
+ function applyOp(op, isLocal) {
291
+ if (op.opId) {
292
+ state.offlineOperations.delete(op.opId);
293
+ }
278
294
  switch (op.type) {
279
295
  case OpType.DeleteObjectKey:
280
296
  case OpType.UpdateObject:
@@ -283,7 +299,7 @@ export function makeStateMachine(state, context, mockedEffects) {
283
299
  if (item == null) {
284
300
  return { modified: false };
285
301
  }
286
- return item._apply(op);
302
+ return item._apply(op, isLocal);
287
303
  }
288
304
  case OpType.SetParentKey: {
289
305
  const item = state.items.get(op.id);
@@ -292,17 +308,12 @@ export function makeStateMachine(state, context, mockedEffects) {
292
308
  }
293
309
  if (item._parent instanceof LiveList) {
294
310
  const previousKey = item._parentKey;
295
- item._parent._setChildKey(op.parentKey, item);
296
- return {
297
- reverse: [
298
- {
299
- type: OpType.SetParentKey,
300
- id: item._id,
301
- parentKey: previousKey,
302
- },
303
- ],
304
- modified: item._parent,
305
- };
311
+ if (previousKey === op.parentKey) {
312
+ return { modified: false };
313
+ }
314
+ else {
315
+ return item._parent._setChildKey(op.parentKey, item, previousKey);
316
+ }
306
317
  }
307
318
  return { modified: false };
308
319
  }
@@ -311,28 +322,28 @@ export function makeStateMachine(state, context, mockedEffects) {
311
322
  if (parent == null || getItem(op.id) != null) {
312
323
  return { modified: false };
313
324
  }
314
- return parent._attachChild(op.id, op.parentKey, new LiveObject(op.data));
325
+ return parent._attachChild(op.id, op.parentKey, new LiveObject(op.data), isLocal);
315
326
  }
316
327
  case OpType.CreateList: {
317
328
  const parent = state.items.get(op.parentId);
318
329
  if (parent == null || getItem(op.id) != null) {
319
330
  return { modified: false };
320
331
  }
321
- return parent._attachChild(op.id, op.parentKey, new LiveList());
332
+ return parent._attachChild(op.id, op.parentKey, new LiveList(), isLocal);
322
333
  }
323
334
  case OpType.CreateRegister: {
324
335
  const parent = state.items.get(op.parentId);
325
336
  if (parent == null || getItem(op.id) != null) {
326
337
  return { modified: false };
327
338
  }
328
- return parent._attachChild(op.id, op.parentKey, new LiveRegister(op.data));
339
+ return parent._attachChild(op.id, op.parentKey, new LiveRegister(op.data), isLocal);
329
340
  }
330
341
  case OpType.CreateMap: {
331
342
  const parent = state.items.get(op.parentId);
332
343
  if (parent == null || getItem(op.id) != null) {
333
344
  return { modified: false };
334
345
  }
335
- return parent._attachChild(op.id, op.parentKey, new LiveMap());
346
+ return parent._attachChild(op.id, op.parentKey, new LiveMap(), isLocal);
336
347
  }
337
348
  }
338
349
  return { modified: false };
@@ -525,7 +536,7 @@ See v0.13 release notes for more information.
525
536
  subMessages.push(message);
526
537
  }
527
538
  const updates = {
528
- nodes: new Set(),
539
+ storageUpdates: new Map(),
529
540
  others: [],
530
541
  };
531
542
  for (const subMessage of subMessages) {
@@ -554,15 +565,16 @@ See v0.13 release notes for more information.
554
565
  break;
555
566
  }
556
567
  case ServerMessageType.InitialStorageState: {
557
- createRootFromMessage(subMessage);
568
+ createOrUpdateRootFromMessage(subMessage);
569
+ applyAndSendOfflineOps();
558
570
  _getInitialStateResolver === null || _getInitialStateResolver === void 0 ? void 0 : _getInitialStateResolver();
559
571
  break;
560
572
  }
561
573
  case ServerMessageType.UpdateStorage: {
562
- const applyResult = apply(subMessage.ops);
563
- for (const node of applyResult.updates.nodes) {
564
- updates.nodes.add(node);
565
- }
574
+ const applyResult = apply(subMessage.ops, false);
575
+ applyResult.updates.storageUpdates.forEach((value, key) => {
576
+ updates.storageUpdates.set(key, mergeStorageUpdates(updates.storageUpdates.get(key), value));
577
+ });
566
578
  break;
567
579
  }
568
580
  }
@@ -629,6 +641,10 @@ See v0.13 release notes for more information.
629
641
  if (state.connection.state === "connecting") {
630
642
  updateConnection(Object.assign(Object.assign({}, state.connection), { state: "open" }));
631
643
  state.numberOfRetry = 0;
644
+ state.lastConnectionId = state.connection.id;
645
+ if (state.root) {
646
+ state.buffer.messages.push({ type: ClientMessageType.FetchStorage });
647
+ }
632
648
  tryFlushing();
633
649
  }
634
650
  else {
@@ -668,11 +684,29 @@ See v0.13 release notes for more information.
668
684
  clearInterval(state.intervalHandles.heartbeat);
669
685
  connect();
670
686
  }
671
- function tryFlushing() {
672
- if (state.socket == null) {
687
+ function applyAndSendOfflineOps() {
688
+ if (state.offlineOperations.size === 0) {
673
689
  return;
674
690
  }
675
- if (state.socket.readyState !== WebSocket.OPEN) {
691
+ const messages = [];
692
+ const ops = Array.from(state.offlineOperations.values());
693
+ const result = apply(ops, true);
694
+ messages.push({
695
+ type: ClientMessageType.UpdateStorage,
696
+ ops: ops,
697
+ });
698
+ notify(result.updates);
699
+ effects.send(messages);
700
+ }
701
+ function tryFlushing() {
702
+ const storageOps = state.buffer.storageOperations;
703
+ if (storageOps.length > 0) {
704
+ storageOps.forEach((op) => {
705
+ state.offlineOperations.set(op.opId, op);
706
+ });
707
+ }
708
+ if (state.socket == null || state.socket.readyState !== WebSocket.OPEN) {
709
+ state.buffer.storageOperations = [];
676
710
  return;
677
711
  }
678
712
  const now = Date.now();
@@ -792,7 +826,7 @@ See v0.13 release notes for more information.
792
826
  return;
793
827
  }
794
828
  state.isHistoryPaused = false;
795
- const result = apply(historyItem);
829
+ const result = apply(historyItem, true);
796
830
  notify(result.updates);
797
831
  state.redoStack.push(result.reverse);
798
832
  for (const op of historyItem) {
@@ -811,7 +845,7 @@ See v0.13 release notes for more information.
811
845
  return;
812
846
  }
813
847
  state.isHistoryPaused = false;
814
- const result = apply(historyItem);
848
+ const result = apply(historyItem, true);
815
849
  notify(result.updates);
816
850
  state.undoStack.push(result.reverse);
817
851
  for (const op of historyItem) {
@@ -834,8 +868,11 @@ See v0.13 release notes for more information.
834
868
  if (state.batch.reverseOps.length > 0) {
835
869
  addToUndoStack(state.batch.reverseOps);
836
870
  }
837
- // Clear the redo stack because batch is always called from a local operation
838
- state.redoStack = [];
871
+ if (state.batch.ops.length > 0) {
872
+ // Only clear the redo stack if something has changed during a batch
873
+ // Clear the redo stack because batch is always called from a local operation
874
+ state.redoStack = [];
875
+ }
839
876
  if (state.batch.ops.length > 0) {
840
877
  dispatch(state.batch.ops);
841
878
  }
@@ -845,7 +882,7 @@ See v0.13 release notes for more information.
845
882
  reverseOps: [],
846
883
  updates: {
847
884
  others: [],
848
- nodes: new Set(),
885
+ storageUpdates: new Map(),
849
886
  presence: false,
850
887
  },
851
888
  };
@@ -863,6 +900,16 @@ See v0.13 release notes for more information.
863
900
  }
864
901
  state.pausedHistory = [];
865
902
  }
903
+ function simulateSocketClose() {
904
+ if (state.socket) {
905
+ state.socket.close();
906
+ }
907
+ }
908
+ function simulateSendCloseEvent(event) {
909
+ if (state.socket) {
910
+ onClose(event);
911
+ }
912
+ }
866
913
  return {
867
914
  // Internal
868
915
  onOpen,
@@ -871,6 +918,9 @@ See v0.13 release notes for more information.
871
918
  authenticationSuccess,
872
919
  heartbeat,
873
920
  onNavigatorOnline,
921
+ // Internal dev tools
922
+ simulateSocketClose,
923
+ simulateSendCloseEvent,
874
924
  // onWakeUp,
875
925
  onVisibilityChange,
876
926
  getUndoStack: () => state.undoStack,
@@ -902,6 +952,7 @@ See v0.13 release notes for more information.
902
952
  export function defaultState(me, defaultStorageRoot) {
903
953
  return {
904
954
  connection: { state: "closed" },
955
+ lastConnectionId: null,
905
956
  socket: null,
906
957
  listeners: {
907
958
  event: [],
@@ -943,9 +994,14 @@ export function defaultState(me, defaultStorageRoot) {
943
994
  isBatching: false,
944
995
  batch: {
945
996
  ops: [],
946
- updates: { nodes: new Set(), presence: false, others: [] },
997
+ updates: {
998
+ storageUpdates: new Map(),
999
+ presence: false,
1000
+ others: [],
1001
+ },
947
1002
  reverseOps: [],
948
1003
  },
1004
+ offlineOperations: new Map(),
949
1005
  };
950
1006
  }
951
1007
  export function createRoom(name, options) {
@@ -991,6 +1047,11 @@ export function createRoom(name, options) {
991
1047
  pause: machine.pauseHistory,
992
1048
  resume: machine.resumeHistory,
993
1049
  },
1050
+ // @ts-ignore
1051
+ internalDevTools: {
1052
+ closeWebsocket: machine.simulateSocketClose,
1053
+ sendCloseEvent: machine.simulateSendCloseEvent,
1054
+ },
994
1055
  };
995
1056
  return {
996
1057
  connect: machine.connect,
@@ -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
  /**
@@ -1,8 +1,11 @@
1
1
  import { AbstractCrdt, Doc } from "./AbstractCrdt";
2
- import { SerializedCrdtWithId } from "./live";
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;
6
7
  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;
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;
package/lib/esm/utils.js CHANGED
@@ -1,4 +1,4 @@
1
- import { CrdtType, } from "./live";
1
+ import { CrdtType, OpType, } from "./live";
2
2
  import { LiveList } from "./LiveList";
3
3
  import { LiveMap } from "./LiveMap";
4
4
  import { LiveObject } from "./LiveObject";
@@ -64,3 +64,101 @@ export function selfOrRegister(obj) {
64
64
  return new LiveRegister(obj);
65
65
  }
66
66
  }
67
+ export function getTreesDiffOperations(currentItems, newItems) {
68
+ const ops = [];
69
+ currentItems.forEach((_, id) => {
70
+ if (!newItems.get(id)) {
71
+ // Delete crdt
72
+ ops.push({
73
+ type: OpType.DeleteCrdt,
74
+ id: id,
75
+ });
76
+ }
77
+ });
78
+ newItems.forEach((crdt, id) => {
79
+ const currentCrdt = currentItems.get(id);
80
+ if (currentCrdt) {
81
+ if (crdt.type === CrdtType.Object) {
82
+ if (JSON.stringify(crdt.data) !==
83
+ JSON.stringify(currentCrdt.data)) {
84
+ ops.push({
85
+ type: OpType.UpdateObject,
86
+ id: id,
87
+ data: crdt.data,
88
+ });
89
+ }
90
+ }
91
+ if (crdt.parentKey !== currentCrdt.parentKey) {
92
+ ops.push({
93
+ type: OpType.SetParentKey,
94
+ id: id,
95
+ parentKey: crdt.parentKey,
96
+ });
97
+ }
98
+ }
99
+ else {
100
+ // new Crdt
101
+ switch (crdt.type) {
102
+ case CrdtType.Register:
103
+ ops.push({
104
+ type: OpType.CreateRegister,
105
+ id: id,
106
+ parentId: crdt.parentId,
107
+ parentKey: crdt.parentKey,
108
+ data: crdt.data,
109
+ });
110
+ break;
111
+ case CrdtType.List:
112
+ ops.push({
113
+ type: OpType.CreateList,
114
+ id: id,
115
+ parentId: crdt.parentId,
116
+ parentKey: crdt.parentKey,
117
+ });
118
+ break;
119
+ case CrdtType.Object:
120
+ ops.push({
121
+ type: OpType.CreateObject,
122
+ id: id,
123
+ parentId: crdt.parentId,
124
+ parentKey: crdt.parentKey,
125
+ data: crdt.data,
126
+ });
127
+ break;
128
+ case CrdtType.Map:
129
+ ops.push({
130
+ type: OpType.CreateMap,
131
+ id: id,
132
+ parentId: crdt.parentId,
133
+ parentKey: crdt.parentKey,
134
+ });
135
+ break;
136
+ }
137
+ }
138
+ });
139
+ return ops;
140
+ }
141
+ export function mergeStorageUpdates(first, second) {
142
+ if (!first) {
143
+ return second;
144
+ }
145
+ if (second.type === "LiveObject") {
146
+ const updates = first.updates;
147
+ for (const [key, value] of Object.entries(second.updates)) {
148
+ updates[key] = value;
149
+ }
150
+ return Object.assign(Object.assign({}, second), { updates: updates });
151
+ }
152
+ else if (second.type === "LiveMap") {
153
+ const updates = first.updates;
154
+ for (const [key, value] of Object.entries(second.updates)) {
155
+ updates[key] = value;
156
+ }
157
+ return Object.assign(Object.assign({}, second), { updates: updates });
158
+ }
159
+ else if (second.type === "LiveList") {
160
+ const updates = first.updates;
161
+ return Object.assign(Object.assign({}, second), { updates: updates.concat(second.updates) });
162
+ }
163
+ return second;
164
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@liveblocks/client",
3
- "version": "0.13.3",
3
+ "version": "0.15.0-alpha.2",
4
4
  "description": "",
5
5
  "main": "./lib/cjs/index.js",
6
6
  "module": "./lib/esm/index.js",
@@ -38,4 +38,4 @@
38
38
  "url": "https://github.com/liveblocks/liveblocks.git",
39
39
  "directory": "packages/liveblocks"
40
40
  }
41
- }
41
+ }
@@ -1,7 +0,0 @@
1
- import { LiveList } from "../LiveList";
2
- import { LiveObject } from "../LiveObject";
3
- import { StorageUpdate } from "../types";
4
- export declare function liveObjectToJson(liveObject: LiveObject<any>): any;
5
- export declare function patchLiveList<T>(liveList: LiveList<T>, prev: Array<T>, next: Array<T>): void;
6
- export declare function patchLiveObject<T extends Record<string, any>>(root: LiveObject<T>, prev: T, next: T): void;
7
- export declare function patchImmutableObject<T>(state: T, updates: StorageUpdate[]): T;