@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/cjs/room.js CHANGED
@@ -132,18 +132,23 @@ function makeStateMachine(state, context, mockedEffects) {
132
132
  };
133
133
  return genericSubscribe(cb);
134
134
  }
135
- function createRootFromMessage(message) {
136
- state.root = load(message.items);
135
+ function createOrUpdateRootFromMessage(message) {
136
+ if (message.items.length === 0) {
137
+ throw new Error("Internal error: cannot load storage without items");
138
+ }
139
+ if (state.root) {
140
+ updateRoot(message.items);
141
+ }
142
+ else {
143
+ state.root = load(message.items);
144
+ }
137
145
  for (const key in state.defaultStorageRoot) {
138
146
  if (state.root.get(key) == null) {
139
147
  state.root.set(key, state.defaultStorageRoot[key]);
140
148
  }
141
149
  }
142
150
  }
143
- function load(items) {
144
- if (items.length === 0) {
145
- throw new Error("Internal error: cannot load storage without items");
146
- }
151
+ function buildRootAndParentToChildren(items) {
147
152
  const parentToChildren = new Map();
148
153
  let root = null;
149
154
  for (const tuple of items) {
@@ -164,6 +169,23 @@ function makeStateMachine(state, context, mockedEffects) {
164
169
  if (root == null) {
165
170
  throw new Error("Root can't be null");
166
171
  }
172
+ return [root, parentToChildren];
173
+ }
174
+ function updateRoot(items) {
175
+ if (!state.root) {
176
+ return;
177
+ }
178
+ const currentItems = new Map();
179
+ state.items.forEach((liveCrdt, id) => {
180
+ currentItems.set(id, liveCrdt._toSerializedCrdt());
181
+ });
182
+ // Get operations that represent the diff between 2 states.
183
+ const ops = (0, utils_1.getTreesDiffOperations)(currentItems, new Map(items));
184
+ const result = apply(ops, false);
185
+ notify(result.updates);
186
+ }
187
+ function load(items) {
188
+ const [root, parentToChildren] = buildRootAndParentToChildren(items);
167
189
  return LiveObject_1.LiveObject._deserialize(root, parentToChildren, {
168
190
  addItem,
169
191
  deleteItem,
@@ -193,22 +215,22 @@ function makeStateMachine(state, context, mockedEffects) {
193
215
  state.undoStack.push(historyItem);
194
216
  }
195
217
  }
196
- function storageDispatch(ops, reverse, modified) {
218
+ function storageDispatch(ops, reverse, storageUpdates) {
197
219
  if (state.isBatching) {
198
220
  state.batch.ops.push(...ops);
199
- for (const item of modified) {
200
- state.batch.updates.nodes.add(item);
201
- }
221
+ storageUpdates.forEach((value, key) => {
222
+ state.batch.updates.storageUpdates.set(key, (0, utils_1.mergeStorageUpdates)(state.batch.updates.storageUpdates.get(key), value));
223
+ });
202
224
  state.batch.reverseOps.push(...reverse);
203
225
  }
204
226
  else {
205
227
  addToUndoStack(reverse);
206
228
  state.redoStack = [];
207
229
  dispatch(ops);
208
- notify({ nodes: new Set(modified) });
230
+ notify({ storageUpdates: storageUpdates });
209
231
  }
210
232
  }
211
- function notify({ nodes = new Set(), presence = false, others = [], }) {
233
+ function notify({ storageUpdates = new Map(), presence = false, others = [], }) {
212
234
  if (others.length > 0) {
213
235
  state.others = makeOthers(state.users);
214
236
  for (const event of others) {
@@ -222,28 +244,9 @@ function makeStateMachine(state, context, mockedEffects) {
222
244
  listener(state.me);
223
245
  }
224
246
  }
225
- if (nodes.size > 0) {
247
+ if (storageUpdates.size > 0) {
226
248
  for (const subscriber of state.listeners.storage) {
227
- subscriber(Array.from(nodes).map((m) => {
228
- if (m instanceof LiveObject_1.LiveObject) {
229
- return {
230
- type: "LiveObject",
231
- node: m,
232
- };
233
- }
234
- else if (m instanceof LiveList_1.LiveList) {
235
- return {
236
- type: "LiveList",
237
- node: m,
238
- };
239
- }
240
- else {
241
- return {
242
- type: "LiveMap",
243
- node: m,
244
- };
245
- }
246
- }));
249
+ subscriber(Array.from(storageUpdates.values()));
247
250
  }
248
251
  }
249
252
  }
@@ -252,7 +255,10 @@ function makeStateMachine(state, context, mockedEffects) {
252
255
  state.connection.state === "connecting") {
253
256
  return state.connection.id;
254
257
  }
255
- throw new Error("Internal. Tried to get connection id but connection is not open");
258
+ else if (state.lastConnectionId !== null) {
259
+ return state.lastConnectionId;
260
+ }
261
+ throw new Error("Internal. Tried to get connection id but connection was never open");
256
262
  }
257
263
  function generateId() {
258
264
  return `${getConnectionId()}:${state.clock++}`;
@@ -260,10 +266,13 @@ function makeStateMachine(state, context, mockedEffects) {
260
266
  function generateOpId() {
261
267
  return `${getConnectionId()}:${state.opClock++}`;
262
268
  }
263
- function apply(item) {
269
+ function apply(item, isLocal) {
264
270
  const result = {
265
271
  reverse: [],
266
- updates: { nodes: new Set(), presence: false },
272
+ updates: {
273
+ storageUpdates: new Map(),
274
+ presence: false,
275
+ },
267
276
  };
268
277
  for (const op of item) {
269
278
  if (op.type === "presence") {
@@ -287,16 +296,23 @@ function makeStateMachine(state, context, mockedEffects) {
287
296
  result.updates.presence = true;
288
297
  }
289
298
  else {
290
- const applyOpResult = applyOp(op);
299
+ // Ops applied after undo/redo don't have an opId.
300
+ if (isLocal && !op.opId) {
301
+ op.opId = generateOpId();
302
+ }
303
+ const applyOpResult = applyOp(op, isLocal);
291
304
  if (applyOpResult.modified) {
292
- result.updates.nodes.add(applyOpResult.modified);
305
+ result.updates.storageUpdates.set(applyOpResult.modified.node._id, (0, utils_1.mergeStorageUpdates)(result.updates.storageUpdates.get(applyOpResult.modified.node._id), applyOpResult.modified));
293
306
  result.reverse.unshift(...applyOpResult.reverse);
294
307
  }
295
308
  }
296
309
  }
297
310
  return result;
298
311
  }
299
- function applyOp(op) {
312
+ function applyOp(op, isLocal) {
313
+ if (op.opId) {
314
+ state.offlineOperations.delete(op.opId);
315
+ }
300
316
  switch (op.type) {
301
317
  case live_1.OpType.DeleteObjectKey:
302
318
  case live_1.OpType.UpdateObject:
@@ -305,7 +321,7 @@ function makeStateMachine(state, context, mockedEffects) {
305
321
  if (item == null) {
306
322
  return { modified: false };
307
323
  }
308
- return item._apply(op);
324
+ return item._apply(op, isLocal);
309
325
  }
310
326
  case live_1.OpType.SetParentKey: {
311
327
  const item = state.items.get(op.id);
@@ -314,17 +330,12 @@ function makeStateMachine(state, context, mockedEffects) {
314
330
  }
315
331
  if (item._parent instanceof LiveList_1.LiveList) {
316
332
  const previousKey = item._parentKey;
317
- item._parent._setChildKey(op.parentKey, item);
318
- return {
319
- reverse: [
320
- {
321
- type: live_1.OpType.SetParentKey,
322
- id: item._id,
323
- parentKey: previousKey,
324
- },
325
- ],
326
- modified: item._parent,
327
- };
333
+ if (previousKey === op.parentKey) {
334
+ return { modified: false };
335
+ }
336
+ else {
337
+ return item._parent._setChildKey(op.parentKey, item, previousKey);
338
+ }
328
339
  }
329
340
  return { modified: false };
330
341
  }
@@ -333,28 +344,28 @@ function makeStateMachine(state, context, mockedEffects) {
333
344
  if (parent == null || getItem(op.id) != null) {
334
345
  return { modified: false };
335
346
  }
336
- return parent._attachChild(op.id, op.parentKey, new LiveObject_1.LiveObject(op.data));
347
+ return parent._attachChild(op.id, op.parentKey, new LiveObject_1.LiveObject(op.data), isLocal);
337
348
  }
338
349
  case live_1.OpType.CreateList: {
339
350
  const parent = state.items.get(op.parentId);
340
351
  if (parent == null || getItem(op.id) != null) {
341
352
  return { modified: false };
342
353
  }
343
- return parent._attachChild(op.id, op.parentKey, new LiveList_1.LiveList());
354
+ return parent._attachChild(op.id, op.parentKey, new LiveList_1.LiveList(), isLocal);
344
355
  }
345
356
  case live_1.OpType.CreateRegister: {
346
357
  const parent = state.items.get(op.parentId);
347
358
  if (parent == null || getItem(op.id) != null) {
348
359
  return { modified: false };
349
360
  }
350
- return parent._attachChild(op.id, op.parentKey, new LiveRegister_1.LiveRegister(op.data));
361
+ return parent._attachChild(op.id, op.parentKey, new LiveRegister_1.LiveRegister(op.data), isLocal);
351
362
  }
352
363
  case live_1.OpType.CreateMap: {
353
364
  const parent = state.items.get(op.parentId);
354
365
  if (parent == null || getItem(op.id) != null) {
355
366
  return { modified: false };
356
367
  }
357
- return parent._attachChild(op.id, op.parentKey, new LiveMap_1.LiveMap());
368
+ return parent._attachChild(op.id, op.parentKey, new LiveMap_1.LiveMap(), isLocal);
358
369
  }
359
370
  }
360
371
  return { modified: false };
@@ -547,7 +558,7 @@ See v0.13 release notes for more information.
547
558
  subMessages.push(message);
548
559
  }
549
560
  const updates = {
550
- nodes: new Set(),
561
+ storageUpdates: new Map(),
551
562
  others: [],
552
563
  };
553
564
  for (const subMessage of subMessages) {
@@ -576,15 +587,16 @@ See v0.13 release notes for more information.
576
587
  break;
577
588
  }
578
589
  case live_1.ServerMessageType.InitialStorageState: {
579
- createRootFromMessage(subMessage);
590
+ createOrUpdateRootFromMessage(subMessage);
591
+ applyAndSendOfflineOps();
580
592
  _getInitialStateResolver === null || _getInitialStateResolver === void 0 ? void 0 : _getInitialStateResolver();
581
593
  break;
582
594
  }
583
595
  case live_1.ServerMessageType.UpdateStorage: {
584
- const applyResult = apply(subMessage.ops);
585
- for (const node of applyResult.updates.nodes) {
586
- updates.nodes.add(node);
587
- }
596
+ const applyResult = apply(subMessage.ops, false);
597
+ applyResult.updates.storageUpdates.forEach((value, key) => {
598
+ updates.storageUpdates.set(key, (0, utils_1.mergeStorageUpdates)(updates.storageUpdates.get(key), value));
599
+ });
588
600
  break;
589
601
  }
590
602
  }
@@ -651,6 +663,10 @@ See v0.13 release notes for more information.
651
663
  if (state.connection.state === "connecting") {
652
664
  updateConnection(Object.assign(Object.assign({}, state.connection), { state: "open" }));
653
665
  state.numberOfRetry = 0;
666
+ state.lastConnectionId = state.connection.id;
667
+ if (state.root) {
668
+ state.buffer.messages.push({ type: live_1.ClientMessageType.FetchStorage });
669
+ }
654
670
  tryFlushing();
655
671
  }
656
672
  else {
@@ -690,11 +706,29 @@ See v0.13 release notes for more information.
690
706
  clearInterval(state.intervalHandles.heartbeat);
691
707
  connect();
692
708
  }
693
- function tryFlushing() {
694
- if (state.socket == null) {
709
+ function applyAndSendOfflineOps() {
710
+ if (state.offlineOperations.size === 0) {
695
711
  return;
696
712
  }
697
- if (state.socket.readyState !== WebSocket.OPEN) {
713
+ const messages = [];
714
+ const ops = Array.from(state.offlineOperations.values());
715
+ const result = apply(ops, true);
716
+ messages.push({
717
+ type: live_1.ClientMessageType.UpdateStorage,
718
+ ops: ops,
719
+ });
720
+ notify(result.updates);
721
+ effects.send(messages);
722
+ }
723
+ function tryFlushing() {
724
+ const storageOps = state.buffer.storageOperations;
725
+ if (storageOps.length > 0) {
726
+ storageOps.forEach((op) => {
727
+ state.offlineOperations.set(op.opId, op);
728
+ });
729
+ }
730
+ if (state.socket == null || state.socket.readyState !== WebSocket.OPEN) {
731
+ state.buffer.storageOperations = [];
698
732
  return;
699
733
  }
700
734
  const now = Date.now();
@@ -814,7 +848,7 @@ See v0.13 release notes for more information.
814
848
  return;
815
849
  }
816
850
  state.isHistoryPaused = false;
817
- const result = apply(historyItem);
851
+ const result = apply(historyItem, true);
818
852
  notify(result.updates);
819
853
  state.redoStack.push(result.reverse);
820
854
  for (const op of historyItem) {
@@ -833,7 +867,7 @@ See v0.13 release notes for more information.
833
867
  return;
834
868
  }
835
869
  state.isHistoryPaused = false;
836
- const result = apply(historyItem);
870
+ const result = apply(historyItem, true);
837
871
  notify(result.updates);
838
872
  state.undoStack.push(result.reverse);
839
873
  for (const op of historyItem) {
@@ -856,8 +890,11 @@ See v0.13 release notes for more information.
856
890
  if (state.batch.reverseOps.length > 0) {
857
891
  addToUndoStack(state.batch.reverseOps);
858
892
  }
859
- // Clear the redo stack because batch is always called from a local operation
860
- state.redoStack = [];
893
+ if (state.batch.ops.length > 0) {
894
+ // Only clear the redo stack if something has changed during a batch
895
+ // Clear the redo stack because batch is always called from a local operation
896
+ state.redoStack = [];
897
+ }
861
898
  if (state.batch.ops.length > 0) {
862
899
  dispatch(state.batch.ops);
863
900
  }
@@ -867,7 +904,7 @@ See v0.13 release notes for more information.
867
904
  reverseOps: [],
868
905
  updates: {
869
906
  others: [],
870
- nodes: new Set(),
907
+ storageUpdates: new Map(),
871
908
  presence: false,
872
909
  },
873
910
  };
@@ -885,6 +922,16 @@ See v0.13 release notes for more information.
885
922
  }
886
923
  state.pausedHistory = [];
887
924
  }
925
+ function simulateSocketClose() {
926
+ if (state.socket) {
927
+ state.socket.close();
928
+ }
929
+ }
930
+ function simulateSendCloseEvent(event) {
931
+ if (state.socket) {
932
+ onClose(event);
933
+ }
934
+ }
888
935
  return {
889
936
  // Internal
890
937
  onOpen,
@@ -893,6 +940,9 @@ See v0.13 release notes for more information.
893
940
  authenticationSuccess,
894
941
  heartbeat,
895
942
  onNavigatorOnline,
943
+ // Internal dev tools
944
+ simulateSocketClose,
945
+ simulateSendCloseEvent,
896
946
  // onWakeUp,
897
947
  onVisibilityChange,
898
948
  getUndoStack: () => state.undoStack,
@@ -925,6 +975,7 @@ exports.makeStateMachine = makeStateMachine;
925
975
  function defaultState(me, defaultStorageRoot) {
926
976
  return {
927
977
  connection: { state: "closed" },
978
+ lastConnectionId: null,
928
979
  socket: null,
929
980
  listeners: {
930
981
  event: [],
@@ -966,9 +1017,14 @@ function defaultState(me, defaultStorageRoot) {
966
1017
  isBatching: false,
967
1018
  batch: {
968
1019
  ops: [],
969
- updates: { nodes: new Set(), presence: false, others: [] },
1020
+ updates: {
1021
+ storageUpdates: new Map(),
1022
+ presence: false,
1023
+ others: [],
1024
+ },
970
1025
  reverseOps: [],
971
1026
  },
1027
+ offlineOperations: new Map(),
972
1028
  };
973
1029
  }
974
1030
  exports.defaultState = defaultState;
@@ -1015,6 +1071,11 @@ function createRoom(name, options) {
1015
1071
  pause: machine.pauseHistory,
1016
1072
  resume: machine.resumeHistory,
1017
1073
  },
1074
+ // @ts-ignore
1075
+ internalDevTools: {
1076
+ closeWebsocket: machine.simulateSocketClose,
1077
+ sendCloseEvent: machine.simulateSendCloseEvent,
1078
+ },
1018
1079
  };
1019
1080
  return {
1020
1081
  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/cjs/utils.js CHANGED
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.selfOrRegister = exports.selfOrRegisterValue = exports.isCrdt = exports.deserialize = exports.isSameNodeOrChildOf = exports.remove = void 0;
3
+ 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");
@@ -73,3 +73,103 @@ function selfOrRegister(obj) {
73
73
  }
74
74
  }
75
75
  exports.selfOrRegister = selfOrRegister;
76
+ function getTreesDiffOperations(currentItems, newItems) {
77
+ const ops = [];
78
+ currentItems.forEach((_, id) => {
79
+ if (!newItems.get(id)) {
80
+ // Delete crdt
81
+ ops.push({
82
+ type: live_1.OpType.DeleteCrdt,
83
+ id: id,
84
+ });
85
+ }
86
+ });
87
+ newItems.forEach((crdt, id) => {
88
+ const currentCrdt = currentItems.get(id);
89
+ if (currentCrdt) {
90
+ if (crdt.type === live_1.CrdtType.Object) {
91
+ if (JSON.stringify(crdt.data) !==
92
+ JSON.stringify(currentCrdt.data)) {
93
+ ops.push({
94
+ type: live_1.OpType.UpdateObject,
95
+ id: id,
96
+ data: crdt.data,
97
+ });
98
+ }
99
+ }
100
+ if (crdt.parentKey !== currentCrdt.parentKey) {
101
+ ops.push({
102
+ type: live_1.OpType.SetParentKey,
103
+ id: id,
104
+ parentKey: crdt.parentKey,
105
+ });
106
+ }
107
+ }
108
+ else {
109
+ // new Crdt
110
+ switch (crdt.type) {
111
+ case live_1.CrdtType.Register:
112
+ ops.push({
113
+ type: live_1.OpType.CreateRegister,
114
+ id: id,
115
+ parentId: crdt.parentId,
116
+ parentKey: crdt.parentKey,
117
+ data: crdt.data,
118
+ });
119
+ break;
120
+ case live_1.CrdtType.List:
121
+ ops.push({
122
+ type: live_1.OpType.CreateList,
123
+ id: id,
124
+ parentId: crdt.parentId,
125
+ parentKey: crdt.parentKey,
126
+ });
127
+ break;
128
+ case live_1.CrdtType.Object:
129
+ ops.push({
130
+ type: live_1.OpType.CreateObject,
131
+ id: id,
132
+ parentId: crdt.parentId,
133
+ parentKey: crdt.parentKey,
134
+ data: crdt.data,
135
+ });
136
+ break;
137
+ case live_1.CrdtType.Map:
138
+ ops.push({
139
+ type: live_1.OpType.CreateMap,
140
+ id: id,
141
+ parentId: crdt.parentId,
142
+ parentKey: crdt.parentKey,
143
+ });
144
+ break;
145
+ }
146
+ }
147
+ });
148
+ return ops;
149
+ }
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;
@@ -1,7 +1,8 @@
1
- import { Op } from "./live";
1
+ import { Op, SerializedCrdt } from "./live";
2
+ import { StorageUpdate } from "./types";
2
3
  export declare type ApplyResult = {
3
4
  reverse: Op[];
4
- modified: AbstractCrdt;
5
+ modified: StorageUpdate;
5
6
  } | {
6
7
  modified: false;
7
8
  };
@@ -10,7 +11,7 @@ export interface Doc {
10
11
  generateOpId: () => string;
11
12
  addItem: (id: string, item: AbstractCrdt) => void;
12
13
  deleteItem: (id: string) => void;
13
- dispatch: (ops: Op[], reverseOps: Op[], modified: AbstractCrdt[]) => void;
14
+ dispatch: (ops: Op[], reverseOps: Op[], storageUpdates: Map<string, StorageUpdate>) => void;
14
15
  }
15
16
  export declare abstract class AbstractCrdt {
16
17
  #private;
@@ -33,7 +34,7 @@ export declare abstract class AbstractCrdt {
33
34
  /**
34
35
  * INTERNAL
35
36
  */
36
- _apply(op: Op): ApplyResult;
37
+ _apply(op: Op, isLocal: boolean): ApplyResult;
37
38
  /**
38
39
  * INTERNAL
39
40
  */
@@ -45,7 +46,7 @@ export declare abstract class AbstractCrdt {
45
46
  /**
46
47
  * INTERNAL
47
48
  */
48
- abstract _attachChild(id: string, key: string, crdt: AbstractCrdt): ApplyResult;
49
+ abstract _attachChild(id: string, key: string, crdt: AbstractCrdt, isLocal: boolean): ApplyResult;
49
50
  /**
50
51
  * INTERNAL
51
52
  */
@@ -53,9 +54,14 @@ export declare abstract class AbstractCrdt {
53
54
  /**
54
55
  * INTERNAL
55
56
  */
56
- abstract _detachChild(crdt: AbstractCrdt): void;
57
+ abstract _detachChild(crdt: AbstractCrdt): ApplyResult;
57
58
  /**
58
59
  * INTERNAL
59
60
  */
60
- abstract _serialize(parentId: string, parentKey: string): Op[];
61
+ abstract _serialize(parentId: string, parentKey: string, doc?: Doc): Op[];
62
+ /**
63
+ * INTERNAL
64
+ */
65
+ abstract _toSerializedCrdt(): SerializedCrdt;
66
+ abstract _getType(): string;
61
67
  }
@@ -45,14 +45,11 @@ export class AbstractCrdt {
45
45
  /**
46
46
  * INTERNAL
47
47
  */
48
- _apply(op) {
48
+ _apply(op, isLocal) {
49
49
  switch (op.type) {
50
50
  case OpType.DeleteCrdt: {
51
51
  if (this._parent != null && this._parentKey != null) {
52
- const parent = this._parent;
53
- const reverse = this._serialize(this._parent._id, this._parentKey);
54
- this._parent._detachChild(this);
55
- return { modified: parent, reverse };
52
+ return this._parent._detachChild(this);
56
53
  }
57
54
  return { modified: false };
58
55
  }