@liveblocks/client 0.13.2 → 0.14.0-beta.1

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/room.js CHANGED
@@ -129,18 +129,23 @@ function makeStateMachine(state, context, mockedEffects) {
129
129
  };
130
130
  return genericSubscribe(cb);
131
131
  }
132
- function createRootFromMessage(message) {
133
- state.root = load(message.items);
132
+ function createOrUpdateRootFromMessage(message) {
133
+ if (message.items.length === 0) {
134
+ throw new Error("Internal error: cannot load storage without items");
135
+ }
136
+ if (state.root) {
137
+ updateRoot(message.items);
138
+ }
139
+ else {
140
+ state.root = load(message.items);
141
+ }
134
142
  for (const key in state.defaultStorageRoot) {
135
143
  if (state.root.get(key) == null) {
136
144
  state.root.set(key, state.defaultStorageRoot[key]);
137
145
  }
138
146
  }
139
147
  }
140
- function load(items) {
141
- if (items.length === 0) {
142
- throw new Error("Internal error: cannot load storage without items");
143
- }
148
+ function buildRootAndParentToChildren(items) {
144
149
  const parentToChildren = new Map();
145
150
  let root = null;
146
151
  for (const tuple of items) {
@@ -161,6 +166,23 @@ function makeStateMachine(state, context, mockedEffects) {
161
166
  if (root == null) {
162
167
  throw new Error("Root can't be null");
163
168
  }
169
+ return [root, parentToChildren];
170
+ }
171
+ function updateRoot(items) {
172
+ if (!state.root) {
173
+ return;
174
+ }
175
+ const currentItems = new Map();
176
+ state.items.forEach((liveCrdt, id) => {
177
+ currentItems.set(id, liveCrdt._toSerializedCrdt());
178
+ });
179
+ // Get operations that represent the diff between 2 states.
180
+ const ops = (0, utils_1.getTreesDiffOperations)(currentItems, new Map(items));
181
+ const result = apply(ops, false);
182
+ notify(result.updates);
183
+ }
184
+ function load(items) {
185
+ const [root, parentToChildren] = buildRootAndParentToChildren(items);
164
186
  return LiveObject_1.LiveObject._deserialize(root, parentToChildren, {
165
187
  addItem,
166
188
  deleteItem,
@@ -249,7 +271,10 @@ function makeStateMachine(state, context, mockedEffects) {
249
271
  state.connection.state === "connecting") {
250
272
  return state.connection.id;
251
273
  }
252
- throw new Error("Internal. Tried to get connection id but connection is not open");
274
+ else if (state.lastConnectionId !== null) {
275
+ return state.lastConnectionId;
276
+ }
277
+ throw new Error("Internal. Tried to get connection id but connection was never open");
253
278
  }
254
279
  function generateId() {
255
280
  return `${getConnectionId()}:${state.clock++}`;
@@ -257,7 +282,7 @@ function makeStateMachine(state, context, mockedEffects) {
257
282
  function generateOpId() {
258
283
  return `${getConnectionId()}:${state.opClock++}`;
259
284
  }
260
- function apply(item) {
285
+ function apply(item, isLocal) {
261
286
  const result = {
262
287
  reverse: [],
263
288
  updates: { nodes: new Set(), presence: false },
@@ -284,7 +309,11 @@ function makeStateMachine(state, context, mockedEffects) {
284
309
  result.updates.presence = true;
285
310
  }
286
311
  else {
287
- const applyOpResult = applyOp(op);
312
+ // Ops applied after undo/redo don't have an opId.
313
+ if (isLocal && !op.opId) {
314
+ op.opId = generateOpId();
315
+ }
316
+ const applyOpResult = applyOp(op, isLocal);
288
317
  if (applyOpResult.modified) {
289
318
  result.updates.nodes.add(applyOpResult.modified);
290
319
  result.reverse.unshift(...applyOpResult.reverse);
@@ -293,7 +322,10 @@ function makeStateMachine(state, context, mockedEffects) {
293
322
  }
294
323
  return result;
295
324
  }
296
- function applyOp(op) {
325
+ function applyOp(op, isLocal) {
326
+ if (op.opId) {
327
+ state.offlineOperations.delete(op.opId);
328
+ }
297
329
  switch (op.type) {
298
330
  case live_1.OpType.DeleteObjectKey:
299
331
  case live_1.OpType.UpdateObject:
@@ -302,7 +334,7 @@ function makeStateMachine(state, context, mockedEffects) {
302
334
  if (item == null) {
303
335
  return { modified: false };
304
336
  }
305
- return item._apply(op);
337
+ return item._apply(op, isLocal);
306
338
  }
307
339
  case live_1.OpType.SetParentKey: {
308
340
  const item = state.items.get(op.id);
@@ -330,28 +362,28 @@ function makeStateMachine(state, context, mockedEffects) {
330
362
  if (parent == null || getItem(op.id) != null) {
331
363
  return { modified: false };
332
364
  }
333
- return parent._attachChild(op.id, op.parentKey, new LiveObject_1.LiveObject(op.data));
365
+ return parent._attachChild(op.id, op.parentKey, new LiveObject_1.LiveObject(op.data), isLocal);
334
366
  }
335
367
  case live_1.OpType.CreateList: {
336
368
  const parent = state.items.get(op.parentId);
337
369
  if (parent == null || getItem(op.id) != null) {
338
370
  return { modified: false };
339
371
  }
340
- return parent._attachChild(op.id, op.parentKey, new LiveList_1.LiveList());
372
+ return parent._attachChild(op.id, op.parentKey, new LiveList_1.LiveList(), isLocal);
341
373
  }
342
374
  case live_1.OpType.CreateRegister: {
343
375
  const parent = state.items.get(op.parentId);
344
376
  if (parent == null || getItem(op.id) != null) {
345
377
  return { modified: false };
346
378
  }
347
- return parent._attachChild(op.id, op.parentKey, new LiveRegister_1.LiveRegister(op.data));
379
+ return parent._attachChild(op.id, op.parentKey, new LiveRegister_1.LiveRegister(op.data), isLocal);
348
380
  }
349
381
  case live_1.OpType.CreateMap: {
350
382
  const parent = state.items.get(op.parentId);
351
383
  if (parent == null || getItem(op.id) != null) {
352
384
  return { modified: false };
353
385
  }
354
- return parent._attachChild(op.id, op.parentKey, new LiveMap_1.LiveMap());
386
+ return parent._attachChild(op.id, op.parentKey, new LiveMap_1.LiveMap(), isLocal);
355
387
  }
356
388
  }
357
389
  return { modified: false };
@@ -571,12 +603,13 @@ See v0.13 release notes for more information.
571
603
  break;
572
604
  }
573
605
  case live_1.ServerMessageType.InitialStorageState: {
574
- createRootFromMessage(subMessage);
606
+ createOrUpdateRootFromMessage(subMessage);
607
+ applyAndSendOfflineOps();
575
608
  _getInitialStateResolver === null || _getInitialStateResolver === void 0 ? void 0 : _getInitialStateResolver();
576
609
  break;
577
610
  }
578
611
  case live_1.ServerMessageType.UpdateStorage: {
579
- const applyResult = apply(subMessage.ops);
612
+ const applyResult = apply(subMessage.ops, false);
580
613
  for (const node of applyResult.updates.nodes) {
581
614
  updates.nodes.add(node);
582
615
  }
@@ -640,6 +673,10 @@ See v0.13 release notes for more information.
640
673
  if (state.connection.state === "connecting") {
641
674
  updateConnection(Object.assign(Object.assign({}, state.connection), { state: "open" }));
642
675
  state.numberOfRetry = 0;
676
+ state.lastConnectionId = state.connection.id;
677
+ if (state.root) {
678
+ state.buffer.messages.push({ type: live_1.ClientMessageType.FetchStorage });
679
+ }
643
680
  tryFlushing();
644
681
  }
645
682
  else {
@@ -679,11 +716,29 @@ See v0.13 release notes for more information.
679
716
  clearInterval(state.intervalHandles.heartbeat);
680
717
  connect();
681
718
  }
682
- function tryFlushing() {
683
- if (state.socket == null) {
719
+ function applyAndSendOfflineOps() {
720
+ if (state.offlineOperations.size === 0) {
684
721
  return;
685
722
  }
686
- if (state.socket.readyState !== WebSocket.OPEN) {
723
+ const messages = [];
724
+ const ops = Array.from(state.offlineOperations.values());
725
+ const result = apply(ops, true);
726
+ messages.push({
727
+ type: live_1.ClientMessageType.UpdateStorage,
728
+ ops: ops,
729
+ });
730
+ notify(result.updates);
731
+ effects.send(messages);
732
+ }
733
+ function tryFlushing() {
734
+ const storageOps = state.buffer.storageOperations;
735
+ if (storageOps.length > 0) {
736
+ storageOps.forEach((op) => {
737
+ state.offlineOperations.set(op.opId, op);
738
+ });
739
+ }
740
+ if (state.socket == null || state.socket.readyState !== WebSocket.OPEN) {
741
+ state.buffer.storageOperations = [];
687
742
  return;
688
743
  }
689
744
  const now = Date.now();
@@ -801,7 +856,7 @@ See v0.13 release notes for more information.
801
856
  return;
802
857
  }
803
858
  state.isHistoryPaused = false;
804
- const result = apply(historyItem);
859
+ const result = apply(historyItem, true);
805
860
  notify(result.updates);
806
861
  state.redoStack.push(result.reverse);
807
862
  for (const op of historyItem) {
@@ -820,7 +875,7 @@ See v0.13 release notes for more information.
820
875
  return;
821
876
  }
822
877
  state.isHistoryPaused = false;
823
- const result = apply(historyItem);
878
+ const result = apply(historyItem, true);
824
879
  notify(result.updates);
825
880
  state.undoStack.push(result.reverse);
826
881
  for (const op of historyItem) {
@@ -872,6 +927,16 @@ See v0.13 release notes for more information.
872
927
  }
873
928
  state.pausedHistory = [];
874
929
  }
930
+ function simulateSocketClose() {
931
+ if (state.socket) {
932
+ state.socket.close();
933
+ }
934
+ }
935
+ function simulateSendCloseEvent(event) {
936
+ if (state.socket) {
937
+ onClose(event);
938
+ }
939
+ }
875
940
  return {
876
941
  // Internal
877
942
  onOpen,
@@ -880,6 +945,9 @@ See v0.13 release notes for more information.
880
945
  authenticationSuccess,
881
946
  heartbeat,
882
947
  onNavigatorOnline,
948
+ // Internal dev tools
949
+ simulateSocketClose,
950
+ simulateSendCloseEvent,
883
951
  // onWakeUp,
884
952
  onVisibilityChange,
885
953
  getUndoStack: () => state.undoStack,
@@ -912,6 +980,7 @@ exports.makeStateMachine = makeStateMachine;
912
980
  function defaultState(me, defaultStorageRoot) {
913
981
  return {
914
982
  connection: { state: "closed" },
983
+ lastConnectionId: null,
915
984
  socket: null,
916
985
  listeners: {
917
986
  event: [],
@@ -956,6 +1025,7 @@ function defaultState(me, defaultStorageRoot) {
956
1025
  updates: { nodes: new Set(), presence: false, others: [] },
957
1026
  reverseOps: [],
958
1027
  },
1028
+ offlineOperations: new Map(),
959
1029
  };
960
1030
  }
961
1031
  exports.defaultState = defaultState;
@@ -1002,6 +1072,11 @@ function createRoom(name, options) {
1002
1072
  pause: machine.pauseHistory,
1003
1073
  resume: machine.resumeHistory,
1004
1074
  },
1075
+ // @ts-ignore
1076
+ internalDevTools: {
1077
+ closeWebsocket: machine.simulateSocketClose,
1078
+ sendCloseEvent: machine.simulateSendCloseEvent,
1079
+ },
1005
1080
  };
1006
1081
  return {
1007
1082
  connect: machine.connect,
@@ -1,8 +1,9 @@
1
1
  import { AbstractCrdt, Doc } from "./AbstractCrdt";
2
- import { SerializedCrdtWithId } from "./live";
2
+ import { SerializedCrdtWithId, Op, SerializedCrdt } from "./live";
3
3
  export declare function remove<T>(array: T[], item: T): void;
4
4
  export declare function isSameNodeOrChildOf(node: AbstractCrdt, parent: AbstractCrdt): boolean;
5
5
  export declare function deserialize(entry: SerializedCrdtWithId, parentToChildren: Map<string, SerializedCrdtWithId[]>, doc: Doc): AbstractCrdt;
6
6
  export declare function isCrdt(obj: any): obj is AbstractCrdt;
7
7
  export declare function selfOrRegisterValue(obj: AbstractCrdt): any;
8
8
  export declare function selfOrRegister(obj: any): AbstractCrdt;
9
+ export declare function getTreesDiffOperations(currentItems: Map<string, SerializedCrdt>, newItems: Map<string, SerializedCrdt>): Op[];
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.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,78 @@ 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;
@@ -1,4 +1,4 @@
1
- import { Op } from "./live";
1
+ import { Op, SerializedCrdt } from "./live";
2
2
  export declare type ApplyResult = {
3
3
  reverse: Op[];
4
4
  modified: AbstractCrdt;
@@ -33,7 +33,7 @@ export declare abstract class AbstractCrdt {
33
33
  /**
34
34
  * INTERNAL
35
35
  */
36
- _apply(op: Op): ApplyResult;
36
+ _apply(op: Op, isLocal: boolean): ApplyResult;
37
37
  /**
38
38
  * INTERNAL
39
39
  */
@@ -45,7 +45,7 @@ export declare abstract class AbstractCrdt {
45
45
  /**
46
46
  * INTERNAL
47
47
  */
48
- abstract _attachChild(id: string, key: string, crdt: AbstractCrdt): ApplyResult;
48
+ abstract _attachChild(id: string, key: string, crdt: AbstractCrdt, isLocal: boolean): ApplyResult;
49
49
  /**
50
50
  * INTERNAL
51
51
  */
@@ -57,5 +57,9 @@ export declare abstract class AbstractCrdt {
57
57
  /**
58
58
  * INTERNAL
59
59
  */
60
- abstract _serialize(parentId: string, parentKey: string): Op[];
60
+ abstract _serialize(parentId: string, parentKey: string, doc?: Doc): Op[];
61
+ /**
62
+ * INTERNAL
63
+ */
64
+ abstract _toSerializedCrdt(): SerializedCrdt;
61
65
  }
@@ -45,12 +45,12 @@ 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
52
  const parent = this._parent;
53
- const reverse = this._serialize(this._parent._id, this._parentKey);
53
+ const reverse = this._serialize(this._parent._id, this._parentKey, __classPrivateFieldGet(this, _AbstractCrdt_doc, "f"));
54
54
  this._parent._detachChild(this);
55
55
  return { modified: parent, reverse };
56
56
  }
@@ -1,5 +1,5 @@
1
1
  import { AbstractCrdt, Doc, ApplyResult } from "./AbstractCrdt";
2
- import { SerializedList, SerializedCrdtWithId, Op } from "./live";
2
+ import { SerializedList, SerializedCrdtWithId, Op, SerializedCrdt } from "./live";
3
3
  /**
4
4
  * The LiveList class represents an ordered collection of items that is synchorinized across clients.
5
5
  */
@@ -13,7 +13,7 @@ export declare class LiveList<T> extends AbstractCrdt {
13
13
  /**
14
14
  * INTERNAL
15
15
  */
16
- _serialize(parentId?: string, parentKey?: string): Op[];
16
+ _serialize(parentId?: string, parentKey?: string, doc?: Doc): Op[];
17
17
  /**
18
18
  * INTERNAL
19
19
  */
@@ -25,7 +25,7 @@ export declare class LiveList<T> extends AbstractCrdt {
25
25
  /**
26
26
  * INTERNAL
27
27
  */
28
- _attachChild(id: string, key: string, child: AbstractCrdt): ApplyResult;
28
+ _attachChild(id: string, key: string, child: AbstractCrdt, isLocal: boolean): ApplyResult;
29
29
  /**
30
30
  * INTERNAL
31
31
  */
@@ -37,7 +37,11 @@ export declare class LiveList<T> extends AbstractCrdt {
37
37
  /**
38
38
  * INTERNAL
39
39
  */
40
- _apply(op: Op): ApplyResult;
40
+ _apply(op: Op, isLocal: boolean): ApplyResult;
41
+ /**
42
+ * INTERNAL
43
+ */
44
+ _toSerializedCrdt(): SerializedCrdt;
41
45
  /**
42
46
  * Returns the number of elements.
43
47
  */
@@ -12,7 +12,7 @@ var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (
12
12
  var _LiveList_items, _LiveListIterator_innerIterator;
13
13
  import { AbstractCrdt } from "./AbstractCrdt";
14
14
  import { deserialize, selfOrRegister, selfOrRegisterValue } from "./utils";
15
- import { OpType, } from "./live";
15
+ import { OpType, CrdtType, } from "./live";
16
16
  import { makePosition, compare } from "./position";
17
17
  /**
18
18
  * The LiveList class represents an ordered collection of items that is synchorinized across clients.
@@ -51,7 +51,7 @@ export class LiveList extends AbstractCrdt {
51
51
  /**
52
52
  * INTERNAL
53
53
  */
54
- _serialize(parentId, parentKey) {
54
+ _serialize(parentId, parentKey, doc) {
55
55
  if (this._id == null) {
56
56
  throw new Error("Cannot serialize item is not attached");
57
57
  }
@@ -61,13 +61,14 @@ export class LiveList extends AbstractCrdt {
61
61
  const ops = [];
62
62
  const op = {
63
63
  id: this._id,
64
+ opId: doc === null || doc === void 0 ? void 0 : doc.generateOpId(),
64
65
  type: OpType.CreateList,
65
66
  parentId,
66
67
  parentKey,
67
68
  };
68
69
  ops.push(op);
69
70
  for (const [value, key] of __classPrivateFieldGet(this, _LiveList_items, "f")) {
70
- ops.push(...value._serialize(this._id, key));
71
+ ops.push(...value._serialize(this._id, key, doc));
71
72
  }
72
73
  return ops;
73
74
  }
@@ -92,7 +93,7 @@ export class LiveList extends AbstractCrdt {
92
93
  /**
93
94
  * INTERNAL
94
95
  */
95
- _attachChild(id, key, child) {
96
+ _attachChild(id, key, child, isLocal) {
96
97
  var _a;
97
98
  if (this._doc == null) {
98
99
  throw new Error("Can't attach child if doc is not present");
@@ -100,11 +101,24 @@ export class LiveList extends AbstractCrdt {
100
101
  child._attach(id, this._doc);
101
102
  child._setParentLink(this, key);
102
103
  const index = __classPrivateFieldGet(this, _LiveList_items, "f").findIndex((entry) => entry[1] === key);
103
- // Assign a temporary position until we get the fix from the backend
104
+ let newKey = key;
105
+ // If there is a conflict
104
106
  if (index !== -1) {
105
- __classPrivateFieldGet(this, _LiveList_items, "f")[index][1] = makePosition(key, (_a = __classPrivateFieldGet(this, _LiveList_items, "f")[index + 1]) === null || _a === void 0 ? void 0 : _a[1]);
107
+ if (isLocal) {
108
+ // If change is local => assign a temporary position to newly attached child
109
+ let before = __classPrivateFieldGet(this, _LiveList_items, "f")[index] ? __classPrivateFieldGet(this, _LiveList_items, "f")[index][1] : undefined;
110
+ let after = __classPrivateFieldGet(this, _LiveList_items, "f")[index + 1]
111
+ ? __classPrivateFieldGet(this, _LiveList_items, "f")[index + 1][1]
112
+ : undefined;
113
+ newKey = makePosition(before, after);
114
+ child._setParentLink(this, newKey);
115
+ }
116
+ else {
117
+ // If change is remote => assign a temporary position to existing child until we get the fix from the backend
118
+ __classPrivateFieldGet(this, _LiveList_items, "f")[index][1] = makePosition(key, (_a = __classPrivateFieldGet(this, _LiveList_items, "f")[index + 1]) === null || _a === void 0 ? void 0 : _a[1]);
119
+ }
106
120
  }
107
- __classPrivateFieldGet(this, _LiveList_items, "f").push([child, key]);
121
+ __classPrivateFieldGet(this, _LiveList_items, "f").push([child, newKey]);
108
122
  __classPrivateFieldGet(this, _LiveList_items, "f").sort((itemA, itemB) => compare(itemA[1], itemB[1]));
109
123
  return { reverse: [{ type: OpType.DeleteCrdt, id }], modified: this };
110
124
  }
@@ -138,8 +152,19 @@ export class LiveList extends AbstractCrdt {
138
152
  /**
139
153
  * INTERNAL
140
154
  */
141
- _apply(op) {
142
- return super._apply(op);
155
+ _apply(op, isLocal) {
156
+ return super._apply(op, isLocal);
157
+ }
158
+ /**
159
+ * INTERNAL
160
+ */
161
+ _toSerializedCrdt() {
162
+ var _a;
163
+ return {
164
+ type: CrdtType.List,
165
+ parentId: (_a = this._parent) === null || _a === void 0 ? void 0 : _a._id,
166
+ parentKey: this._parentKey,
167
+ };
143
168
  }
144
169
  /**
145
170
  * Returns the number of elements.
@@ -173,7 +198,7 @@ export class LiveList extends AbstractCrdt {
173
198
  if (this._doc && this._id) {
174
199
  const id = this._doc.generateId();
175
200
  value._attach(id, this._doc);
176
- this._doc.dispatch(value._serialize(this._id, position), [{ type: OpType.DeleteCrdt, id }], [this]);
201
+ this._doc.dispatch(value._serialize(this._id, position, this._doc), [{ type: OpType.DeleteCrdt, id }], [this]);
177
202
  }
178
203
  }
179
204
  /**
@@ -219,6 +244,7 @@ export class LiveList extends AbstractCrdt {
219
244
  {
220
245
  type: OpType.SetParentKey,
221
246
  id: item[0]._id,
247
+ opId: this._doc.generateOpId(),
222
248
  parentKey: position,
223
249
  },
224
250
  ], [
@@ -247,6 +273,7 @@ export class LiveList extends AbstractCrdt {
247
273
  this._doc.dispatch([
248
274
  {
249
275
  id: childRecordId,
276
+ opId: this._doc.generateOpId(),
250
277
  type: OpType.DeleteCrdt,
251
278
  },
252
279
  ], item[0]._serialize(this._id, item[1]), [this]);
@@ -1,5 +1,5 @@
1
1
  import { AbstractCrdt, Doc, ApplyResult } from "./AbstractCrdt";
2
- import { Op, SerializedCrdtWithId } from "./live";
2
+ import { Op, SerializedCrdtWithId, SerializedCrdt } from "./live";
3
3
  /**
4
4
  * The LiveMap class is similar to a JavaScript Map that is synchronized on all clients.
5
5
  * Keys should be a string, and values should be serializable to JSON.
@@ -11,7 +11,7 @@ export declare class LiveMap<TKey extends string, TValue> extends AbstractCrdt {
11
11
  /**
12
12
  * INTERNAL
13
13
  */
14
- _serialize(parentId?: string, parentKey?: string): Op[];
14
+ _serialize(parentId?: string, parentKey?: string, doc?: Doc): Op[];
15
15
  /**
16
16
  * INTERNAL
17
17
  */
@@ -23,7 +23,7 @@ export declare class LiveMap<TKey extends string, TValue> extends AbstractCrdt {
23
23
  /**
24
24
  * INTERNAL
25
25
  */
26
- _attachChild(id: string, key: TKey, child: AbstractCrdt): ApplyResult;
26
+ _attachChild(id: string, key: TKey, child: AbstractCrdt, isLocal: boolean): ApplyResult;
27
27
  /**
28
28
  * INTERNAL
29
29
  */
@@ -32,6 +32,10 @@ export declare class LiveMap<TKey extends string, TValue> extends AbstractCrdt {
32
32
  * INTERNAL
33
33
  */
34
34
  _detachChild(child: AbstractCrdt): void;
35
+ /**
36
+ * INTERNAL
37
+ */
38
+ _toSerializedCrdt(): SerializedCrdt;
35
39
  /**
36
40
  * Returns a specified element from the LiveMap.
37
41
  * @param key The key of the element to return.
@@ -38,7 +38,7 @@ export class LiveMap extends AbstractCrdt {
38
38
  /**
39
39
  * INTERNAL
40
40
  */
41
- _serialize(parentId, parentKey) {
41
+ _serialize(parentId, parentKey, doc) {
42
42
  if (this._id == null) {
43
43
  throw new Error("Cannot serialize item is not attached");
44
44
  }
@@ -48,13 +48,14 @@ export class LiveMap extends AbstractCrdt {
48
48
  const ops = [];
49
49
  const op = {
50
50
  id: this._id,
51
+ opId: doc === null || doc === void 0 ? void 0 : doc.generateOpId(),
51
52
  type: OpType.CreateMap,
52
53
  parentId,
53
54
  parentKey,
54
55
  };
55
56
  ops.push(op);
56
57
  for (const [key, value] of __classPrivateFieldGet(this, _LiveMap_map, "f")) {
57
- ops.push(...value._serialize(this._id, key));
58
+ ops.push(...value._serialize(this._id, key, doc));
58
59
  }
59
60
  return ops;
60
61
  }
@@ -96,7 +97,7 @@ export class LiveMap extends AbstractCrdt {
96
97
  /**
97
98
  * INTERNAL
98
99
  */
99
- _attachChild(id, key, child) {
100
+ _attachChild(id, key, child, isLocal) {
100
101
  if (this._doc == null) {
101
102
  throw new Error("Can't attach child if doc is not present");
102
103
  }
@@ -134,6 +135,17 @@ export class LiveMap extends AbstractCrdt {
134
135
  }
135
136
  child._detach();
136
137
  }
138
+ /**
139
+ * INTERNAL
140
+ */
141
+ _toSerializedCrdt() {
142
+ var _a;
143
+ return {
144
+ type: CrdtType.Map,
145
+ parentId: (_a = this._parent) === null || _a === void 0 ? void 0 : _a._id,
146
+ parentKey: this._parentKey,
147
+ };
148
+ }
137
149
  /**
138
150
  * Returns a specified element from the LiveMap.
139
151
  * @param key The key of the element to return.
@@ -162,7 +174,7 @@ export class LiveMap extends AbstractCrdt {
162
174
  if (this._doc && this._id) {
163
175
  const id = this._doc.generateId();
164
176
  item._attach(id, this._doc);
165
- this._doc.dispatch(item._serialize(this._id, key), oldValue
177
+ this._doc.dispatch(item._serialize(this._id, key, this._doc), oldValue
166
178
  ? oldValue._serialize(this._id, key)
167
179
  : [{ type: OpType.DeleteCrdt, id }], [this]);
168
180
  }
@@ -192,7 +204,13 @@ export class LiveMap extends AbstractCrdt {
192
204
  }
193
205
  item._detach();
194
206
  if (this._doc && item._id) {
195
- this._doc.dispatch([{ type: OpType.DeleteCrdt, id: item._id }], item._serialize(this._id, key), [this]);
207
+ this._doc.dispatch([
208
+ {
209
+ type: OpType.DeleteCrdt,
210
+ id: item._id,
211
+ opId: this._doc.generateOpId(),
212
+ },
213
+ ], item._serialize(this._id, key), [this]);
196
214
  }
197
215
  __classPrivateFieldGet(this, _LiveMap_map, "f").delete(key);
198
216
  return true;