@fluidframework/map 1.4.0-121020 → 2.0.0-dev-rc.1.0.0.224419

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 (118) hide show
  1. package/.eslintrc.js +12 -11
  2. package/.mocharc.js +12 -0
  3. package/CHANGELOG.md +162 -0
  4. package/README.md +24 -8
  5. package/api-extractor-lint.json +4 -0
  6. package/api-extractor.json +2 -2
  7. package/api-report/map.api.md +297 -0
  8. package/dist/{directory.js → directory.cjs} +749 -228
  9. package/dist/directory.cjs.map +1 -0
  10. package/dist/directory.d.ts +567 -34
  11. package/dist/directory.d.ts.map +1 -1
  12. package/dist/index.cjs +27 -0
  13. package/dist/index.cjs.map +1 -0
  14. package/dist/index.d.ts +5 -5
  15. package/dist/index.d.ts.map +1 -1
  16. package/dist/{interfaces.js → interfaces.cjs} +1 -1
  17. package/dist/interfaces.cjs.map +1 -0
  18. package/dist/interfaces.d.ts +167 -184
  19. package/dist/interfaces.d.ts.map +1 -1
  20. package/dist/internalInterfaces.cjs +7 -0
  21. package/dist/internalInterfaces.cjs.map +1 -0
  22. package/dist/internalInterfaces.d.ts +101 -0
  23. package/dist/internalInterfaces.d.ts.map +1 -0
  24. package/dist/{localValues.js → localValues.cjs} +15 -3
  25. package/dist/localValues.cjs.map +1 -0
  26. package/dist/localValues.d.ts +17 -6
  27. package/dist/localValues.d.ts.map +1 -1
  28. package/dist/map-alpha.d.ts +982 -0
  29. package/dist/map-beta.d.ts +275 -0
  30. package/dist/map-public.d.ts +275 -0
  31. package/dist/map-untrimmed.d.ts +996 -0
  32. package/dist/{map.js → map.cjs} +39 -34
  33. package/dist/map.cjs.map +1 -0
  34. package/dist/map.d.ts +10 -17
  35. package/dist/map.d.ts.map +1 -1
  36. package/dist/{mapKernel.js → mapKernel.cjs} +122 -79
  37. package/dist/mapKernel.cjs.map +1 -0
  38. package/dist/mapKernel.d.ts +17 -48
  39. package/dist/mapKernel.d.ts.map +1 -1
  40. package/dist/{packageVersion.js → packageVersion.cjs} +2 -2
  41. package/dist/packageVersion.cjs.map +1 -0
  42. package/dist/packageVersion.d.ts +1 -1
  43. package/dist/packageVersion.d.ts.map +1 -1
  44. package/dist/tsdoc-metadata.json +11 -0
  45. package/lib/directory.d.mts +902 -0
  46. package/lib/directory.d.mts.map +1 -0
  47. package/lib/{directory.js → directory.mjs} +736 -199
  48. package/lib/directory.mjs.map +1 -0
  49. package/lib/index.d.mts +9 -0
  50. package/lib/index.d.mts.map +1 -0
  51. package/lib/index.mjs +8 -0
  52. package/lib/index.mjs.map +1 -0
  53. package/lib/{interfaces.d.ts → interfaces.d.mts} +167 -184
  54. package/lib/interfaces.d.mts.map +1 -0
  55. package/lib/{interfaces.js → interfaces.mjs} +1 -1
  56. package/lib/interfaces.mjs.map +1 -0
  57. package/lib/internalInterfaces.d.mts +101 -0
  58. package/lib/internalInterfaces.d.mts.map +1 -0
  59. package/lib/internalInterfaces.mjs +6 -0
  60. package/lib/internalInterfaces.mjs.map +1 -0
  61. package/lib/{localValues.d.ts → localValues.d.mts} +18 -7
  62. package/lib/localValues.d.mts.map +1 -0
  63. package/lib/{localValues.js → localValues.mjs} +15 -3
  64. package/lib/localValues.mjs.map +1 -0
  65. package/lib/map-alpha.d.mts +982 -0
  66. package/lib/map-beta.d.mts +275 -0
  67. package/lib/map-public.d.mts +275 -0
  68. package/lib/map-untrimmed.d.mts +996 -0
  69. package/lib/{map.d.ts → map.d.mts} +11 -18
  70. package/lib/map.d.mts.map +1 -0
  71. package/lib/{map.js → map.mjs} +40 -35
  72. package/lib/map.mjs.map +1 -0
  73. package/lib/{mapKernel.d.ts → mapKernel.d.mts} +18 -49
  74. package/lib/mapKernel.d.mts.map +1 -0
  75. package/lib/{mapKernel.js → mapKernel.mjs} +116 -73
  76. package/lib/mapKernel.mjs.map +1 -0
  77. package/lib/{packageVersion.d.ts → packageVersion.d.mts} +1 -1
  78. package/lib/{packageVersion.d.ts.map → packageVersion.d.mts.map} +1 -1
  79. package/lib/{packageVersion.js → packageVersion.mjs} +2 -2
  80. package/lib/packageVersion.mjs.map +1 -0
  81. package/map.test-files.tar +0 -0
  82. package/package.json +105 -65
  83. package/prettier.config.cjs +8 -0
  84. package/src/directory.ts +2544 -1727
  85. package/src/index.ts +31 -5
  86. package/src/interfaces.ts +346 -345
  87. package/src/internalInterfaces.ts +119 -0
  88. package/src/localValues.ts +103 -96
  89. package/src/map.ts +362 -351
  90. package/src/mapKernel.ts +755 -722
  91. package/src/packageVersion.ts +1 -1
  92. package/tsc-multi.test.json +4 -0
  93. package/tsconfig.json +10 -15
  94. package/dist/directory.js.map +0 -1
  95. package/dist/index.js +0 -34
  96. package/dist/index.js.map +0 -1
  97. package/dist/interfaces.js.map +0 -1
  98. package/dist/localValues.js.map +0 -1
  99. package/dist/map.js.map +0 -1
  100. package/dist/mapKernel.js.map +0 -1
  101. package/dist/packageVersion.js.map +0 -1
  102. package/lib/directory.d.ts +0 -369
  103. package/lib/directory.d.ts.map +0 -1
  104. package/lib/directory.js.map +0 -1
  105. package/lib/index.d.ts +0 -20
  106. package/lib/index.d.ts.map +0 -1
  107. package/lib/index.js +0 -20
  108. package/lib/index.js.map +0 -1
  109. package/lib/interfaces.d.ts.map +0 -1
  110. package/lib/interfaces.js.map +0 -1
  111. package/lib/localValues.d.ts.map +0 -1
  112. package/lib/localValues.js.map +0 -1
  113. package/lib/map.d.ts.map +0 -1
  114. package/lib/map.js.map +0 -1
  115. package/lib/mapKernel.d.ts.map +0 -1
  116. package/lib/mapKernel.js.map +0 -1
  117. package/lib/packageVersion.js.map +0 -1
  118. package/tsconfig.esnext.json +0 -7
@@ -6,23 +6,49 @@
6
6
  Object.defineProperty(exports, "__esModule", { value: true });
7
7
  exports.MapKernel = void 0;
8
8
  const shared_object_base_1 = require("@fluidframework/shared-object-base");
9
- const common_utils_1 = require("@fluidframework/common-utils");
10
- const localValues_1 = require("./localValues");
9
+ const core_utils_1 = require("@fluidframework/core-utils");
10
+ const localValues_1 = require("./localValues.cjs");
11
+ /* eslint-disable @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-member-access */
11
12
  function isMapKeyLocalOpMetadata(metadata) {
12
- return metadata !== undefined && typeof metadata.pendingMessageId === "number" &&
13
- (metadata.type === "add" || metadata.type === "edit");
13
+ return (metadata !== undefined &&
14
+ typeof metadata.pendingMessageId === "number" &&
15
+ (metadata.type === "add" || metadata.type === "edit"));
14
16
  }
15
17
  function isClearLocalOpMetadata(metadata) {
16
- return metadata !== undefined && metadata.type === "clear" && typeof metadata.pendingMessageId === "number";
18
+ return (metadata !== undefined &&
19
+ metadata.type === "clear" &&
20
+ typeof metadata.pendingMessageId === "number");
17
21
  }
18
22
  function isMapLocalOpMetadata(metadata) {
19
- return metadata !== undefined && typeof metadata.pendingMessageId === "number" &&
20
- (metadata.type === "add" || metadata.type === "edit" || metadata.type === "clear");
23
+ return (metadata !== undefined &&
24
+ typeof metadata.pendingMessageId === "number" &&
25
+ (metadata.type === "add" || metadata.type === "edit" || metadata.type === "clear"));
26
+ }
27
+ /* eslint-enable @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-member-access */
28
+ function createClearLocalOpMetadata(op, pendingClearMessageId, previousMap) {
29
+ const localMetadata = {
30
+ type: "clear",
31
+ pendingMessageId: pendingClearMessageId,
32
+ previousMap,
33
+ };
34
+ return localMetadata;
35
+ }
36
+ function createKeyLocalOpMetadata(op, pendingMessageId, previousValue) {
37
+ const localMetadata = previousValue
38
+ ? { type: "edit", pendingMessageId, previousValue }
39
+ : { type: "add", pendingMessageId };
40
+ return localMetadata;
21
41
  }
22
42
  /**
23
43
  * A SharedMap is a map-like distributed data structure.
24
44
  */
25
45
  class MapKernel {
46
+ /**
47
+ * The number of key/value pairs stored in the map.
48
+ */
49
+ get size() {
50
+ return this.data.size;
51
+ }
26
52
  /**
27
53
  * Create a new shared map kernel.
28
54
  * @param serializer - The serializer to serialize / parse handles
@@ -61,12 +87,6 @@ class MapKernel {
61
87
  this.localValueMaker = new localValues_1.LocalValueMaker(serializer);
62
88
  this.messageHandlers = this.getMessageHandlers();
63
89
  }
64
- /**
65
- * The number of key/value pairs stored in the map.
66
- */
67
- get size() {
68
- return this.data.size;
69
- }
70
90
  /**
71
91
  * Get an iterator over the keys in this map.
72
92
  * @returns The iterator
@@ -78,18 +98,17 @@ class MapKernel {
78
98
  * Get an iterator over the entries in this map.
79
99
  * @returns The iterator
80
100
  */
101
+ // TODO: Use `unknown` instead (breaking change).
102
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
81
103
  entries() {
82
104
  const localEntriesIterator = this.data.entries();
83
105
  const iterator = {
84
106
  next() {
85
107
  const nextVal = localEntriesIterator.next();
86
- if (nextVal.done) {
87
- return { value: undefined, done: true };
88
- }
89
- else {
90
- // Unpack the stored value
91
- return { value: [nextVal.value[0], nextVal.value[1].value], done: false };
92
- }
108
+ return nextVal.done
109
+ ? { value: undefined, done: true }
110
+ : // Unpack the stored value
111
+ { value: [nextVal.value[0], nextVal.value[1].value], done: false };
93
112
  },
94
113
  [Symbol.iterator]() {
95
114
  return this;
@@ -101,18 +120,17 @@ class MapKernel {
101
120
  * Get an iterator over the values in this map.
102
121
  * @returns The iterator
103
122
  */
123
+ // TODO: Use `unknown` instead (breaking change).
124
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
104
125
  values() {
105
126
  const localValuesIterator = this.data.values();
106
127
  const iterator = {
107
128
  next() {
108
129
  const nextVal = localValuesIterator.next();
109
- if (nextVal.done) {
110
- return { value: undefined, done: true };
111
- }
112
- else {
113
- // Unpack the stored value
114
- return { value: nextVal.value.value, done: false };
115
- }
130
+ return nextVal.done
131
+ ? { value: undefined, done: true }
132
+ : // Unpack the stored value
133
+ { value: nextVal.value.value, done: false };
116
134
  },
117
135
  [Symbol.iterator]() {
118
136
  return this;
@@ -124,6 +142,8 @@ class MapKernel {
124
142
  * Get an iterator over the entries in this map.
125
143
  * @returns The iterator
126
144
  */
145
+ // TODO: Use `unknown` instead (breaking change).
146
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
127
147
  [Symbol.iterator]() {
128
148
  return this.entries();
129
149
  }
@@ -132,6 +152,7 @@ class MapKernel {
132
152
  * @param callbackFn - Callback function
133
153
  */
134
154
  forEach(callbackFn) {
155
+ // eslint-disable-next-line unicorn/no-array-for-each
135
156
  this.data.forEach((localValue, key, m) => {
136
157
  callbackFn(localValue.value, key, m);
137
158
  });
@@ -139,6 +160,8 @@ class MapKernel {
139
160
  /**
140
161
  * {@inheritDoc ISharedMap.get}
141
162
  */
163
+ // TODO: Use `unknown` instead (breaking change).
164
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
142
165
  get(key) {
143
166
  const localValue = this.data.get(key);
144
167
  return localValue === undefined ? undefined : localValue.value;
@@ -201,6 +224,8 @@ class MapKernel {
201
224
  const copy = this.isAttached() ? new Map(this.data) : undefined;
202
225
  // Clear the data locally first.
203
226
  this.clearCore(true);
227
+ // Clear the pendingKeys immediately, the local unack'd operations are aborted
228
+ this.pendingKeys.clear();
204
229
  // If we are not attached, don't submit the op.
205
230
  if (!this.isAttached()) {
206
231
  return;
@@ -217,16 +242,16 @@ class MapKernel {
217
242
  */
218
243
  getSerializedStorage(serializer) {
219
244
  const serializableMapData = {};
220
- this.data.forEach((localValue, key) => {
245
+ for (const [key, localValue] of this.data.entries()) {
221
246
  serializableMapData[key] = localValue.makeSerialized(serializer, this.handle);
222
- });
247
+ }
223
248
  return serializableMapData;
224
249
  }
225
250
  getSerializableStorage(serializer) {
226
251
  const serializableMapData = {};
227
- this.data.forEach((localValue, key) => {
252
+ for (const [key, localValue] of this.data.entries()) {
228
253
  serializableMapData[key] = (0, localValues_1.makeSerializable)(localValue, serializer, this.handle);
229
- });
254
+ }
230
255
  return serializableMapData;
231
256
  }
232
257
  serialize(serializer) {
@@ -264,12 +289,12 @@ class MapKernel {
264
289
  handler.submit(op, localOpMetadata);
265
290
  return true;
266
291
  }
267
- tryGetStashedOpLocalMetadata(op) {
292
+ tryApplyStashedOp(op) {
268
293
  const handler = this.messageHandlers.get(op.type);
269
294
  if (handler === undefined) {
270
295
  throw new Error("no apply stashed op handler");
271
296
  }
272
- return handler.getStashedOpLocalMetadata(op);
297
+ return handler.applyStashedOp(op);
273
298
  }
274
299
  /**
275
300
  * Process the given op if a handler is registered.
@@ -287,11 +312,13 @@ class MapKernel {
287
312
  handler.process(op, local, localOpMetadata);
288
313
  return true;
289
314
  }
315
+ /* eslint-disable @typescript-eslint/no-unsafe-member-access */
290
316
  /**
291
317
  * Rollback a local op
292
318
  * @param op - The operation to rollback
293
319
  * @param localOpMetadata - The local metadata associated with the op.
294
320
  */
321
+ // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any
295
322
  rollback(op, localOpMetadata) {
296
323
  if (!isMapLocalOpMetadata(localOpMetadata)) {
297
324
  throw new Error("Invalid localOpMetadata");
@@ -300,11 +327,12 @@ class MapKernel {
300
327
  if (localOpMetadata.previousMap === undefined) {
301
328
  throw new Error("Cannot rollback without previous map");
302
329
  }
303
- localOpMetadata.previousMap.forEach((localValue, key) => {
330
+ for (const [key, localValue] of localOpMetadata.previousMap.entries()) {
304
331
  this.setCore(key, localValue, true);
305
- });
332
+ }
306
333
  const lastPendingClearId = this.pendingClearMessageIds.pop();
307
- if (lastPendingClearId === undefined || lastPendingClearId !== localOpMetadata.pendingMessageId) {
334
+ if (lastPendingClearId === undefined ||
335
+ lastPendingClearId !== localOpMetadata.pendingMessageId) {
308
336
  throw new Error("Rollback op does match last clear");
309
337
  }
310
338
  }
@@ -312,14 +340,15 @@ class MapKernel {
312
340
  if (localOpMetadata.type === "add") {
313
341
  this.deleteCore(op.key, true);
314
342
  }
315
- else if (localOpMetadata.type === "edit" && localOpMetadata.previousValue !== undefined) {
343
+ else if (localOpMetadata.type === "edit" &&
344
+ localOpMetadata.previousValue !== undefined) {
316
345
  this.setCore(op.key, localOpMetadata.previousValue, true);
317
346
  }
318
347
  else {
319
348
  throw new Error("Cannot rollback without previous value");
320
349
  }
321
350
  const pendingMessageIds = this.pendingKeys.get(op.key);
322
- const lastPendingMessageId = pendingMessageIds === null || pendingMessageIds === void 0 ? void 0 : pendingMessageIds.pop();
351
+ const lastPendingMessageId = pendingMessageIds?.pop();
323
352
  if (!pendingMessageIds || lastPendingMessageId !== localOpMetadata.pendingMessageId) {
324
353
  throw new Error("Rollback op does not match last pending");
325
354
  }
@@ -331,6 +360,7 @@ class MapKernel {
331
360
  throw new Error("Unsupported op for rollback");
332
361
  }
333
362
  }
363
+ /* eslint-enable @typescript-eslint/no-unsafe-member-access */
334
364
  /**
335
365
  * Set implementation used for both locally sourced sets as well as incoming remote sets.
336
366
  * @param key - The key being set
@@ -340,7 +370,7 @@ class MapKernel {
340
370
  */
341
371
  setCore(key, value, local) {
342
372
  const previousLocalValue = this.data.get(key);
343
- const previousValue = previousLocalValue === null || previousLocalValue === void 0 ? void 0 : previousLocalValue.value;
373
+ const previousValue = previousLocalValue?.value;
344
374
  this.data.set(key, value);
345
375
  this.eventEmitter.emit("valueChanged", { key, previousValue }, local, this.eventEmitter);
346
376
  return previousLocalValue;
@@ -361,7 +391,7 @@ class MapKernel {
361
391
  */
362
392
  deleteCore(key, local) {
363
393
  const previousLocalValue = this.data.get(key);
364
- const previousValue = previousLocalValue === null || previousLocalValue === void 0 ? void 0 : previousLocalValue.value;
394
+ const previousValue = previousLocalValue?.value;
365
395
  const successfullyRemoved = this.data.delete(key);
366
396
  if (successfullyRemoved) {
367
397
  this.eventEmitter.emit("valueChanged", { key, previousValue }, local, this.eventEmitter);
@@ -375,14 +405,17 @@ class MapKernel {
375
405
  // Assuming the pendingKeys is small and the map is large
376
406
  // we will get the value for the pendingKeys and clear the map
377
407
  const temp = new Map();
378
- this.pendingKeys.forEach((value, key) => {
379
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
380
- temp.set(key, this.data.get(key));
381
- });
408
+ for (const key of this.pendingKeys.keys()) {
409
+ // Verify if the most recent pending operation is a delete op, no need to retain it if so.
410
+ // This ensures the map size remains consistent.
411
+ if (this.data.has(key)) {
412
+ temp.set(key, this.data.get(key));
413
+ }
414
+ }
382
415
  this.clearCore(false);
383
- temp.forEach((value, key) => {
416
+ for (const [key, value] of temp.entries()) {
384
417
  this.setCore(key, value, true);
385
- });
418
+ }
386
419
  }
387
420
  /**
388
421
  * The remote ISerializableValue we're receiving (either as a result of a load or an incoming set op) will
@@ -394,8 +427,10 @@ class MapKernel {
394
427
  * @param serializable - The remote information that we can convert into a real object
395
428
  * @returns The local value that was produced
396
429
  */
430
+ // eslint-disable-next-line import/no-deprecated
397
431
  makeLocal(key, serializable) {
398
- if (serializable.type === shared_object_base_1.ValueType[shared_object_base_1.ValueType.Plain] || serializable.type === shared_object_base_1.ValueType[shared_object_base_1.ValueType.Shared]) {
432
+ if (serializable.type === shared_object_base_1.ValueType[shared_object_base_1.ValueType.Plain] ||
433
+ serializable.type === shared_object_base_1.ValueType[shared_object_base_1.ValueType.Shared]) {
399
434
  return this.localValueMaker.fromSerializable(serializable);
400
435
  }
401
436
  else {
@@ -414,22 +449,22 @@ class MapKernel {
414
449
  needProcessKeyOperation(op, local, localOpMetadata) {
415
450
  if (this.pendingClearMessageIds.length > 0) {
416
451
  if (local) {
417
- (0, common_utils_1.assert)(localOpMetadata !== undefined && isMapKeyLocalOpMetadata(localOpMetadata) &&
452
+ (0, core_utils_1.assert)(localOpMetadata !== undefined &&
453
+ isMapKeyLocalOpMetadata(localOpMetadata) &&
418
454
  localOpMetadata.pendingMessageId < this.pendingClearMessageIds[0], 0x013 /* "Received out of order op when there is an unackd clear message" */);
419
455
  }
420
456
  // If we have an unack'd clear, we can ignore all ops.
421
457
  return false;
422
458
  }
423
- const pendingKeyMessageId = this.pendingKeys.get(op.key);
424
- if (pendingKeyMessageId !== undefined) {
459
+ const pendingKeyMessageIds = this.pendingKeys.get(op.key);
460
+ if (pendingKeyMessageIds !== undefined) {
425
461
  // Found an unack'd op. Clear it from the map if the pendingMessageId in the map matches this message's
426
462
  // and don't process the op.
427
463
  if (local) {
428
- (0, common_utils_1.assert)(localOpMetadata !== undefined && isMapKeyLocalOpMetadata(localOpMetadata), 0x014 /* pendingMessageId is missing from the local client's operation */);
429
- const pendingMessageIds = this.pendingKeys.get(op.key);
430
- (0, common_utils_1.assert)(pendingMessageIds !== undefined && pendingMessageIds[0] === localOpMetadata.pendingMessageId, 0x2fa /* Unexpected pending message received */);
431
- pendingMessageIds.shift();
432
- if (pendingMessageIds.length === 0) {
464
+ (0, core_utils_1.assert)(localOpMetadata !== undefined && isMapKeyLocalOpMetadata(localOpMetadata), 0x014 /* pendingMessageId is missing from the local client's operation */);
465
+ (0, core_utils_1.assert)(pendingKeyMessageIds[0] === localOpMetadata.pendingMessageId, 0x2fa /* Unexpected pending message received */);
466
+ pendingKeyMessageIds.shift();
467
+ if (pendingKeyMessageIds.length === 0) {
433
468
  this.pendingKeys.delete(op.key);
434
469
  }
435
470
  }
@@ -447,27 +482,29 @@ class MapKernel {
447
482
  messageHandlers.set("clear", {
448
483
  process: (op, local, localOpMetadata) => {
449
484
  if (local) {
450
- (0, common_utils_1.assert)(isClearLocalOpMetadata(localOpMetadata), 0x015 /* "pendingMessageId is missing from the local client's clear operation" */);
485
+ (0, core_utils_1.assert)(isClearLocalOpMetadata(localOpMetadata), 0x015 /* "pendingMessageId is missing from the local client's clear operation" */);
451
486
  const pendingClearMessageId = this.pendingClearMessageIds.shift();
452
- (0, common_utils_1.assert)(pendingClearMessageId === localOpMetadata.pendingMessageId, 0x2fb /* pendingMessageId does not match */);
487
+ (0, core_utils_1.assert)(pendingClearMessageId === localOpMetadata.pendingMessageId, 0x2fb /* pendingMessageId does not match */);
453
488
  return;
454
489
  }
455
- if (this.pendingKeys.size !== 0) {
490
+ if (this.pendingKeys.size > 0) {
456
491
  this.clearExceptPendingKeys();
457
492
  return;
458
493
  }
459
494
  this.clearCore(local);
460
495
  },
461
496
  submit: (op, localOpMetadata) => {
462
- (0, common_utils_1.assert)(isClearLocalOpMetadata(localOpMetadata), 0x2fc /* Invalid localOpMetadata for clear */);
497
+ (0, core_utils_1.assert)(isClearLocalOpMetadata(localOpMetadata), 0x2fc /* Invalid localOpMetadata for clear */);
463
498
  // We don't reuse the metadata pendingMessageId but send a new one on each submit.
464
499
  const pendingClearMessageId = this.pendingClearMessageIds.shift();
465
- (0, common_utils_1.assert)(pendingClearMessageId === localOpMetadata.pendingMessageId, 0x2fd /* pendingMessageId does not match */);
500
+ (0, core_utils_1.assert)(pendingClearMessageId === localOpMetadata.pendingMessageId, 0x2fd /* pendingMessageId does not match */);
466
501
  this.submitMapClearMessage(op, localOpMetadata.previousMap);
467
502
  },
468
- getStashedOpLocalMetadata: (op) => {
503
+ applyStashedOp: (op) => {
504
+ const copy = new Map(this.data);
505
+ this.clearCore(true);
469
506
  // We don't reuse the metadata pendingMessageId but send a new one on each submit.
470
- return { type: "clear", pendingMessageId: this.getMapClearMessageId() };
507
+ return createClearLocalOpMetadata(op, this.getMapClearMessageId(), copy);
471
508
  },
472
509
  });
473
510
  messageHandlers.set("delete", {
@@ -480,9 +517,10 @@ class MapKernel {
480
517
  submit: (op, localOpMetadata) => {
481
518
  this.resubmitMapKeyMessage(op, localOpMetadata);
482
519
  },
483
- getStashedOpLocalMetadata: (op) => {
520
+ applyStashedOp: (op) => {
484
521
  // We don't reuse the metadata pendingMessageId but send a new one on each submit.
485
- return { type: "edit", pendingMessageId: this.getMapKeyMessageId(op) };
522
+ const previousValue = this.deleteCore(op.key, true);
523
+ return createKeyLocalOpMetadata(op, this.getMapKeyMessageId(op), previousValue);
486
524
  },
487
525
  });
488
526
  messageHandlers.set("set", {
@@ -497,9 +535,11 @@ class MapKernel {
497
535
  submit: (op, localOpMetadata) => {
498
536
  this.resubmitMapKeyMessage(op, localOpMetadata);
499
537
  },
500
- getStashedOpLocalMetadata: (op) => {
538
+ applyStashedOp: (op) => {
501
539
  // We don't reuse the metadata pendingMessageId but send a new one on each submit.
502
- return { type: "edit", pendingMessageId: this.getMapKeyMessageId(op) };
540
+ const context = this.makeLocal(op.key, op.value);
541
+ const previousValue = this.setCore(op.key, context, true);
542
+ return createKeyLocalOpMetadata(op, this.getMapKeyMessageId(op), previousValue);
503
543
  },
504
544
  });
505
545
  return messageHandlers;
@@ -514,7 +554,7 @@ class MapKernel {
514
554
  * @param op - The clear message
515
555
  */
516
556
  submitMapClearMessage(op, previousMap) {
517
- const metadata = { type: "clear", pendingMessageId: this.getMapClearMessageId(), previousMap };
557
+ const metadata = createClearLocalOpMetadata(op, this.getMapClearMessageId(), previousMap);
518
558
  this.submitMessage(op, metadata);
519
559
  }
520
560
  getMapKeyMessageId(op) {
@@ -534,10 +574,7 @@ class MapKernel {
534
574
  * @param previousValue - The value of the key before this op
535
575
  */
536
576
  submitMapKeyMessage(op, previousValue) {
537
- const pendingMessageId = this.getMapKeyMessageId(op);
538
- const localMetadata = previousValue ?
539
- { type: "edit", pendingMessageId, previousValue } :
540
- { type: "add", pendingMessageId };
577
+ const localMetadata = createKeyLocalOpMetadata(op, this.getMapKeyMessageId(op), previousValue);
541
578
  this.submitMessage(op, localMetadata);
542
579
  }
543
580
  /**
@@ -546,21 +583,27 @@ class MapKernel {
546
583
  * @param localOpMetadata - Metadata from the previous submit
547
584
  */
548
585
  resubmitMapKeyMessage(op, localOpMetadata) {
549
- (0, common_utils_1.assert)(isMapKeyLocalOpMetadata(localOpMetadata), 0x2fe /* Invalid localOpMetadata in submit */);
550
- // clear the old pending message id
586
+ (0, core_utils_1.assert)(isMapKeyLocalOpMetadata(localOpMetadata), 0x2fe /* Invalid localOpMetadata in submit */);
587
+ // no need to submit messages for op's that have been aborted
551
588
  const pendingMessageIds = this.pendingKeys.get(op.key);
552
- (0, common_utils_1.assert)(pendingMessageIds !== undefined && pendingMessageIds[0] === localOpMetadata.pendingMessageId, 0x2ff /* Unexpected pending message received */);
553
- pendingMessageIds.shift();
589
+ if (pendingMessageIds === undefined) {
590
+ return;
591
+ }
592
+ const index = pendingMessageIds.findIndex((id) => id === localOpMetadata.pendingMessageId);
593
+ if (index === -1) {
594
+ return;
595
+ }
596
+ pendingMessageIds.splice(index, 1);
554
597
  if (pendingMessageIds.length === 0) {
555
598
  this.pendingKeys.delete(op.key);
556
599
  }
557
600
  // We don't reuse the metadata pendingMessageId but send a new one on each submit.
558
601
  const pendingMessageId = this.getMapKeyMessageId(op);
559
- const localMetadata = localOpMetadata.type === "edit" ?
560
- { type: "edit", pendingMessageId, previousValue: localOpMetadata.previousValue } :
561
- { type: "add", pendingMessageId };
602
+ const localMetadata = localOpMetadata.type === "edit"
603
+ ? { type: "edit", pendingMessageId, previousValue: localOpMetadata.previousValue }
604
+ : { type: "add", pendingMessageId };
562
605
  this.submitMessage(op, localMetadata);
563
606
  }
564
607
  }
565
608
  exports.MapKernel = MapKernel;
566
- //# sourceMappingURL=mapKernel.js.map
609
+ //# sourceMappingURL=mapKernel.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mapKernel.cjs","sourceRoot":"","sources":["../src/mapKernel.ts"],"names":[],"mappings":"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","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport { IFluidHandle } from \"@fluidframework/core-interfaces\";\nimport { IFluidSerializer, ValueType } from \"@fluidframework/shared-object-base\";\nimport { assert } from \"@fluidframework/core-utils\";\nimport { TypedEventEmitter } from \"@fluid-internal/client-utils\";\n// eslint-disable-next-line import/no-deprecated\nimport { ISerializableValue, ISerializedValue, ISharedMapEvents } from \"./interfaces\";\nimport {\n\tIMapSetOperation,\n\tIMapDeleteOperation,\n\tIMapClearOperation,\n\tIMapKeyEditLocalOpMetadata,\n\tIMapKeyAddLocalOpMetadata,\n\tIMapClearLocalOpMetadata,\n} from \"./internalInterfaces\";\nimport { ILocalValue, LocalValueMaker, makeSerializable } from \"./localValues\";\n\n/**\n * Defines the means to process and submit a given op on a map.\n */\ninterface IMapMessageHandler {\n\t/**\n\t * Apply the given operation.\n\t * @param op - The map operation to apply\n\t * @param local - Whether the message originated from the local client\n\t * @param localOpMetadata - For local client messages, this is the metadata that was submitted with the message.\n\t * For messages from a remote client, this will be undefined.\n\t */\n\tprocess(op: IMapOperation, local: boolean, localOpMetadata: MapLocalOpMetadata): void;\n\n\t/**\n\t * Communicate the operation to remote clients.\n\t * @param op - The map operation to submit\n\t * @param localOpMetadata - The metadata to be submitted with the message.\n\t */\n\tsubmit(op: IMapOperation, localOpMetadata: MapLocalOpMetadata): void;\n\n\tapplyStashedOp(op: IMapOperation): MapLocalOpMetadata;\n}\n\n/**\n * Map key operations are one of several types.\n */\nexport type IMapKeyOperation = IMapSetOperation | IMapDeleteOperation;\n\n/**\n * Description of a map delta operation\n */\nexport type IMapOperation = IMapKeyOperation | IMapClearOperation;\n\n/**\n * Defines the in-memory object structure to be used for the conversion to/from serialized.\n *\n * @remarks Directly used in\n * {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify\n * | JSON.stringify}, direct result from\n * {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/parse | JSON.parse}.\n */\nexport interface IMapDataObjectSerializable {\n\t// eslint-disable-next-line import/no-deprecated\n\t[key: string]: ISerializableValue;\n}\n\n/**\n * Serialized key/value data.\n */\nexport interface IMapDataObjectSerialized {\n\t[key: string]: ISerializedValue;\n}\n\ntype MapKeyLocalOpMetadata = IMapKeyEditLocalOpMetadata | IMapKeyAddLocalOpMetadata;\ntype MapLocalOpMetadata = IMapClearLocalOpMetadata | MapKeyLocalOpMetadata;\n\n/* eslint-disable @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-member-access */\n\nfunction isMapKeyLocalOpMetadata(metadata: any): metadata is MapKeyLocalOpMetadata {\n\treturn (\n\t\tmetadata !== undefined &&\n\t\ttypeof metadata.pendingMessageId === \"number\" &&\n\t\t(metadata.type === \"add\" || metadata.type === \"edit\")\n\t);\n}\n\nfunction isClearLocalOpMetadata(metadata: any): metadata is IMapClearLocalOpMetadata {\n\treturn (\n\t\tmetadata !== undefined &&\n\t\tmetadata.type === \"clear\" &&\n\t\ttypeof metadata.pendingMessageId === \"number\"\n\t);\n}\n\nfunction isMapLocalOpMetadata(metadata: any): metadata is MapLocalOpMetadata {\n\treturn (\n\t\tmetadata !== undefined &&\n\t\ttypeof metadata.pendingMessageId === \"number\" &&\n\t\t(metadata.type === \"add\" || metadata.type === \"edit\" || metadata.type === \"clear\")\n\t);\n}\n\n/* eslint-enable @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-member-access */\n\nfunction createClearLocalOpMetadata(\n\top: IMapClearOperation,\n\tpendingClearMessageId: number,\n\tpreviousMap?: Map<string, ILocalValue>,\n): IMapClearLocalOpMetadata {\n\tconst localMetadata: IMapClearLocalOpMetadata = {\n\t\ttype: \"clear\",\n\t\tpendingMessageId: pendingClearMessageId,\n\t\tpreviousMap,\n\t};\n\treturn localMetadata;\n}\n\nfunction createKeyLocalOpMetadata(\n\top: IMapKeyOperation,\n\tpendingMessageId: number,\n\tpreviousValue?: ILocalValue,\n): MapKeyLocalOpMetadata {\n\tconst localMetadata: MapKeyLocalOpMetadata = previousValue\n\t\t? { type: \"edit\", pendingMessageId, previousValue }\n\t\t: { type: \"add\", pendingMessageId };\n\treturn localMetadata;\n}\n\n/**\n * A SharedMap is a map-like distributed data structure.\n */\nexport class MapKernel {\n\t/**\n\t * The number of key/value pairs stored in the map.\n\t */\n\tpublic get size(): number {\n\t\treturn this.data.size;\n\t}\n\n\t/**\n\t * Mapping of op types to message handlers.\n\t */\n\tprivate readonly messageHandlers: ReadonlyMap<string, IMapMessageHandler> = new Map();\n\n\t/**\n\t * The in-memory data the map is storing.\n\t */\n\tprivate readonly data = new Map<string, ILocalValue>();\n\n\t/**\n\t * Keys that have been modified locally but not yet ack'd from the server.\n\t */\n\tprivate readonly pendingKeys: Map<string, number[]> = new Map();\n\n\t/**\n\t * This is used to assign a unique id to every outgoing operation and helps in tracking unack'd ops.\n\t */\n\tprivate pendingMessageId: number = -1;\n\n\t/**\n\t * The pending ids of any clears that have been performed locally but not yet ack'd from the server\n\t */\n\tprivate readonly pendingClearMessageIds: number[] = [];\n\n\t/**\n\t * Object to create encapsulations of the values stored in the map.\n\t */\n\tprivate readonly localValueMaker: LocalValueMaker;\n\n\t/**\n\t * Create a new shared map kernel.\n\t * @param serializer - The serializer to serialize / parse handles\n\t * @param handle - The handle of the shared object using the kernel\n\t * @param submitMessage - A callback to submit a message through the shared object\n\t * @param isAttached - To query whether the shared object should generate ops\n\t * @param valueTypes - The value types to register\n\t * @param eventEmitter - The object that will emit map events\n\t */\n\tpublic constructor(\n\t\tprivate readonly serializer: IFluidSerializer,\n\t\tprivate readonly handle: IFluidHandle,\n\t\tprivate readonly submitMessage: (op: unknown, localOpMetadata: unknown) => void,\n\t\tprivate readonly isAttached: () => boolean,\n\t\tprivate readonly eventEmitter: TypedEventEmitter<ISharedMapEvents>,\n\t) {\n\t\tthis.localValueMaker = new LocalValueMaker(serializer);\n\t\tthis.messageHandlers = this.getMessageHandlers();\n\t}\n\n\t/**\n\t * Get an iterator over the keys in this map.\n\t * @returns The iterator\n\t */\n\tpublic keys(): IterableIterator<string> {\n\t\treturn this.data.keys();\n\t}\n\n\t/**\n\t * Get an iterator over the entries in this map.\n\t * @returns The iterator\n\t */\n\t// TODO: Use `unknown` instead (breaking change).\n\t// eslint-disable-next-line @typescript-eslint/no-explicit-any\n\tpublic entries(): IterableIterator<[string, any]> {\n\t\tconst localEntriesIterator = this.data.entries();\n\t\tconst iterator = {\n\t\t\tnext(): IteratorResult<[string, unknown]> {\n\t\t\t\tconst nextVal = localEntriesIterator.next();\n\t\t\t\treturn nextVal.done\n\t\t\t\t\t? { value: undefined, done: true }\n\t\t\t\t\t: // Unpack the stored value\n\t\t\t\t\t { value: [nextVal.value[0], nextVal.value[1].value], done: false };\n\t\t\t},\n\t\t\t[Symbol.iterator](): IterableIterator<[string, unknown]> {\n\t\t\t\treturn this;\n\t\t\t},\n\t\t};\n\t\treturn iterator;\n\t}\n\n\t/**\n\t * Get an iterator over the values in this map.\n\t * @returns The iterator\n\t */\n\t// TODO: Use `unknown` instead (breaking change).\n\t// eslint-disable-next-line @typescript-eslint/no-explicit-any\n\tpublic values(): IterableIterator<any> {\n\t\tconst localValuesIterator = this.data.values();\n\t\tconst iterator = {\n\t\t\tnext(): IteratorResult<unknown> {\n\t\t\t\tconst nextVal = localValuesIterator.next();\n\t\t\t\treturn nextVal.done\n\t\t\t\t\t? { value: undefined, done: true }\n\t\t\t\t\t: // Unpack the stored value\n\t\t\t\t\t { value: nextVal.value.value as unknown, done: false };\n\t\t\t},\n\t\t\t[Symbol.iterator](): IterableIterator<unknown> {\n\t\t\t\treturn this;\n\t\t\t},\n\t\t};\n\t\treturn iterator;\n\t}\n\n\t/**\n\t * Get an iterator over the entries in this map.\n\t * @returns The iterator\n\t */\n\t// TODO: Use `unknown` instead (breaking change).\n\t// eslint-disable-next-line @typescript-eslint/no-explicit-any\n\tpublic [Symbol.iterator](): IterableIterator<[string, any]> {\n\t\treturn this.entries();\n\t}\n\n\t/**\n\t * Executes the given callback on each entry in the map.\n\t * @param callbackFn - Callback function\n\t */\n\tpublic forEach(\n\t\tcallbackFn: (value: unknown, key: string, map: Map<string, unknown>) => void,\n\t): void {\n\t\t// eslint-disable-next-line unicorn/no-array-for-each\n\t\tthis.data.forEach((localValue, key, m) => {\n\t\t\tcallbackFn(localValue.value, key, m);\n\t\t});\n\t}\n\n\t/**\n\t * {@inheritDoc ISharedMap.get}\n\t */\n\t// TODO: Use `unknown` instead (breaking change).\n\t// eslint-disable-next-line @typescript-eslint/no-explicit-any\n\tpublic get<T = any>(key: string): T | undefined {\n\t\tconst localValue = this.data.get(key);\n\t\treturn localValue === undefined ? undefined : (localValue.value as T);\n\t}\n\n\t/**\n\t * Check if a key exists in the map.\n\t * @param key - The key to check\n\t * @returns True if the key exists, false otherwise\n\t */\n\tpublic has(key: string): boolean {\n\t\treturn this.data.has(key);\n\t}\n\n\t/**\n\t * {@inheritDoc ISharedMap.set}\n\t */\n\tpublic set(key: string, value: unknown): void {\n\t\t// Undefined/null keys can't be serialized to JSON in the manner we currently snapshot.\n\t\tif (key === undefined || key === null) {\n\t\t\tthrow new Error(\"Undefined and null keys are not supported\");\n\t\t}\n\n\t\t// Create a local value and serialize it.\n\t\tconst localValue = this.localValueMaker.fromInMemory(value);\n\t\tconst serializableValue = makeSerializable(localValue, this.serializer, this.handle);\n\n\t\t// Set the value locally.\n\t\tconst previousValue = this.setCore(key, localValue, true);\n\n\t\t// If we are not attached, don't submit the op.\n\t\tif (!this.isAttached()) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst op: IMapSetOperation = {\n\t\t\tkey,\n\t\t\ttype: \"set\",\n\t\t\tvalue: serializableValue,\n\t\t};\n\t\tthis.submitMapKeyMessage(op, previousValue);\n\t}\n\n\t/**\n\t * Delete a key from the map.\n\t * @param key - Key to delete\n\t * @returns True if the key existed and was deleted, false if it did not exist\n\t */\n\tpublic delete(key: string): boolean {\n\t\t// Delete the key locally first.\n\t\tconst previousValue = this.deleteCore(key, true);\n\n\t\t// If we are not attached, don't submit the op.\n\t\tif (!this.isAttached()) {\n\t\t\treturn previousValue !== undefined;\n\t\t}\n\n\t\tconst op: IMapDeleteOperation = {\n\t\t\tkey,\n\t\t\ttype: \"delete\",\n\t\t};\n\t\tthis.submitMapKeyMessage(op, previousValue);\n\n\t\treturn previousValue !== undefined;\n\t}\n\n\t/**\n\t * Clear all data from the map.\n\t */\n\tpublic clear(): void {\n\t\tconst copy = this.isAttached() ? new Map<string, ILocalValue>(this.data) : undefined;\n\n\t\t// Clear the data locally first.\n\t\tthis.clearCore(true);\n\n\t\t// Clear the pendingKeys immediately, the local unack'd operations are aborted\n\t\tthis.pendingKeys.clear();\n\n\t\t// If we are not attached, don't submit the op.\n\t\tif (!this.isAttached()) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst op: IMapClearOperation = {\n\t\t\ttype: \"clear\",\n\t\t};\n\t\tthis.submitMapClearMessage(op, copy);\n\t}\n\n\t/**\n\t * Serializes the data stored in the shared map to a JSON string\n\t * @param serializer - The serializer to use to serialize handles in its values.\n\t * @returns A JSON string containing serialized map data\n\t */\n\tpublic getSerializedStorage(serializer: IFluidSerializer): IMapDataObjectSerialized {\n\t\tconst serializableMapData: IMapDataObjectSerialized = {};\n\t\tfor (const [key, localValue] of this.data.entries()) {\n\t\t\tserializableMapData[key] = localValue.makeSerialized(serializer, this.handle);\n\t\t}\n\t\treturn serializableMapData;\n\t}\n\n\tpublic getSerializableStorage(serializer: IFluidSerializer): IMapDataObjectSerializable {\n\t\tconst serializableMapData: IMapDataObjectSerializable = {};\n\t\tfor (const [key, localValue] of this.data.entries()) {\n\t\t\tserializableMapData[key] = makeSerializable(localValue, serializer, this.handle);\n\t\t}\n\t\treturn serializableMapData;\n\t}\n\n\tpublic serialize(serializer: IFluidSerializer): string {\n\t\treturn JSON.stringify(this.getSerializableStorage(serializer));\n\t}\n\n\t/**\n\t * Populate the kernel with the given map data.\n\t * @param data - A JSON string containing serialized map data\n\t */\n\tpublic populateFromSerializable(json: IMapDataObjectSerializable): void {\n\t\tfor (const [key, serializable] of Object.entries(json)) {\n\t\t\tconst localValue = {\n\t\t\t\tkey,\n\t\t\t\tvalue: this.makeLocal(key, serializable),\n\t\t\t};\n\n\t\t\tthis.data.set(localValue.key, localValue.value);\n\t\t}\n\t}\n\n\tpublic populate(json: string): void {\n\t\tthis.populateFromSerializable(JSON.parse(json) as IMapDataObjectSerializable);\n\t}\n\n\t/**\n\t * Submit the given op if a handler is registered.\n\t * @param op - The operation to attempt to submit\n\t * @param localOpMetadata - The local metadata associated with the op. This is kept locally by the runtime\n\t * and not sent to the server. This will be sent back when this message is received back from the server. This is\n\t * also sent if we are asked to resubmit the message.\n\t * @returns True if the operation was submitted, false otherwise.\n\t */\n\tpublic trySubmitMessage(op: IMapOperation, localOpMetadata: unknown): boolean {\n\t\tconst handler = this.messageHandlers.get(op.type);\n\t\tif (handler === undefined) {\n\t\t\treturn false;\n\t\t}\n\t\thandler.submit(op, localOpMetadata as MapLocalOpMetadata);\n\t\treturn true;\n\t}\n\n\tpublic tryApplyStashedOp(op: IMapOperation): unknown {\n\t\tconst handler = this.messageHandlers.get(op.type);\n\t\tif (handler === undefined) {\n\t\t\tthrow new Error(\"no apply stashed op handler\");\n\t\t}\n\t\treturn handler.applyStashedOp(op);\n\t}\n\n\t/**\n\t * Process the given op if a handler is registered.\n\t * @param op - The message to process\n\t * @param local - Whether the message originated from the local client\n\t * @param localOpMetadata - For local client messages, this is the metadata that was submitted with the message.\n\t * For messages from a remote client, this will be undefined.\n\t * @returns True if the operation was processed, false otherwise.\n\t */\n\tpublic tryProcessMessage(op: IMapOperation, local: boolean, localOpMetadata: unknown): boolean {\n\t\tconst handler = this.messageHandlers.get(op.type);\n\t\tif (handler === undefined) {\n\t\t\treturn false;\n\t\t}\n\t\thandler.process(op, local, localOpMetadata as MapLocalOpMetadata);\n\t\treturn true;\n\t}\n\n\t/* eslint-disable @typescript-eslint/no-unsafe-member-access */\n\n\t/**\n\t * Rollback a local op\n\t * @param op - The operation to rollback\n\t * @param localOpMetadata - The local metadata associated with the op.\n\t */\n\t// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any\n\tpublic rollback(op: any, localOpMetadata: unknown): void {\n\t\tif (!isMapLocalOpMetadata(localOpMetadata)) {\n\t\t\tthrow new Error(\"Invalid localOpMetadata\");\n\t\t}\n\n\t\tif (op.type === \"clear\" && localOpMetadata.type === \"clear\") {\n\t\t\tif (localOpMetadata.previousMap === undefined) {\n\t\t\t\tthrow new Error(\"Cannot rollback without previous map\");\n\t\t\t}\n\t\t\tfor (const [key, localValue] of localOpMetadata.previousMap.entries()) {\n\t\t\t\tthis.setCore(key, localValue, true);\n\t\t\t}\n\n\t\t\tconst lastPendingClearId = this.pendingClearMessageIds.pop();\n\t\t\tif (\n\t\t\t\tlastPendingClearId === undefined ||\n\t\t\t\tlastPendingClearId !== localOpMetadata.pendingMessageId\n\t\t\t) {\n\t\t\t\tthrow new Error(\"Rollback op does match last clear\");\n\t\t\t}\n\t\t} else if (op.type === \"delete\" || op.type === \"set\") {\n\t\t\tif (localOpMetadata.type === \"add\") {\n\t\t\t\tthis.deleteCore(op.key as string, true);\n\t\t\t} else if (\n\t\t\t\tlocalOpMetadata.type === \"edit\" &&\n\t\t\t\tlocalOpMetadata.previousValue !== undefined\n\t\t\t) {\n\t\t\t\tthis.setCore(op.key as string, localOpMetadata.previousValue, true);\n\t\t\t} else {\n\t\t\t\tthrow new Error(\"Cannot rollback without previous value\");\n\t\t\t}\n\n\t\t\tconst pendingMessageIds = this.pendingKeys.get(op.key as string);\n\t\t\tconst lastPendingMessageId = pendingMessageIds?.pop();\n\t\t\tif (!pendingMessageIds || lastPendingMessageId !== localOpMetadata.pendingMessageId) {\n\t\t\t\tthrow new Error(\"Rollback op does not match last pending\");\n\t\t\t}\n\t\t\tif (pendingMessageIds.length === 0) {\n\t\t\t\tthis.pendingKeys.delete(op.key as string);\n\t\t\t}\n\t\t} else {\n\t\t\tthrow new Error(\"Unsupported op for rollback\");\n\t\t}\n\t}\n\n\t/* eslint-enable @typescript-eslint/no-unsafe-member-access */\n\n\t/**\n\t * Set implementation used for both locally sourced sets as well as incoming remote sets.\n\t * @param key - The key being set\n\t * @param value - The value being set\n\t * @param local - Whether the message originated from the local client\n\t * @returns Previous local value of the key, if any\n\t */\n\tprivate setCore(key: string, value: ILocalValue, local: boolean): ILocalValue | undefined {\n\t\tconst previousLocalValue = this.data.get(key);\n\t\tconst previousValue: unknown = previousLocalValue?.value;\n\t\tthis.data.set(key, value);\n\t\tthis.eventEmitter.emit(\"valueChanged\", { key, previousValue }, local, this.eventEmitter);\n\t\treturn previousLocalValue;\n\t}\n\n\t/**\n\t * Clear implementation used for both locally sourced clears as well as incoming remote clears.\n\t * @param local - Whether the message originated from the local client\n\t */\n\tprivate clearCore(local: boolean): void {\n\t\tthis.data.clear();\n\t\tthis.eventEmitter.emit(\"clear\", local, this.eventEmitter);\n\t}\n\n\t/**\n\t * Delete implementation used for both locally sourced deletes as well as incoming remote deletes.\n\t * @param key - The key being deleted\n\t * @param local - Whether the message originated from the local client\n\t * @returns Previous local value of the key if it existed, undefined if it did not exist\n\t */\n\tprivate deleteCore(key: string, local: boolean): ILocalValue | undefined {\n\t\tconst previousLocalValue = this.data.get(key);\n\t\tconst previousValue: unknown = previousLocalValue?.value;\n\t\tconst successfullyRemoved = this.data.delete(key);\n\t\tif (successfullyRemoved) {\n\t\t\tthis.eventEmitter.emit(\n\t\t\t\t\"valueChanged\",\n\t\t\t\t{ key, previousValue },\n\t\t\t\tlocal,\n\t\t\t\tthis.eventEmitter,\n\t\t\t);\n\t\t}\n\t\treturn previousLocalValue;\n\t}\n\n\t/**\n\t * Clear all keys in memory in response to a remote clear, but retain keys we have modified but not yet been ack'd.\n\t */\n\tprivate clearExceptPendingKeys(): void {\n\t\t// Assuming the pendingKeys is small and the map is large\n\t\t// we will get the value for the pendingKeys and clear the map\n\t\tconst temp = new Map<string, ILocalValue>();\n\t\tfor (const key of this.pendingKeys.keys()) {\n\t\t\t// Verify if the most recent pending operation is a delete op, no need to retain it if so.\n\t\t\t// This ensures the map size remains consistent.\n\t\t\tif (this.data.has(key)) {\n\t\t\t\ttemp.set(key, this.data.get(key) as ILocalValue);\n\t\t\t}\n\t\t}\n\t\tthis.clearCore(false);\n\t\tfor (const [key, value] of temp.entries()) {\n\t\t\tthis.setCore(key, value, true);\n\t\t}\n\t}\n\n\t/**\n\t * The remote ISerializableValue we're receiving (either as a result of a load or an incoming set op) will\n\t * have the information we need to create a real object, but will not be the real object yet. For example,\n\t * we might know it's a map and the map's ID but not have the actual map or its data yet. makeLocal's\n\t * job is to convert that information into a real object for local usage.\n\t * @param key - The key that the caller intends to store the local value into (used for ops later). But\n\t * doesn't actually store the local value into that key. So better not lie!\n\t * @param serializable - The remote information that we can convert into a real object\n\t * @returns The local value that was produced\n\t */\n\t// eslint-disable-next-line import/no-deprecated\n\tprivate makeLocal(key: string, serializable: ISerializableValue): ILocalValue {\n\t\tif (\n\t\t\tserializable.type === ValueType[ValueType.Plain] ||\n\t\t\tserializable.type === ValueType[ValueType.Shared]\n\t\t) {\n\t\t\treturn this.localValueMaker.fromSerializable(serializable);\n\t\t} else {\n\t\t\tthrow new Error(\"Unknown local value type\");\n\t\t}\n\t}\n\n\t/**\n\t * If our local operations that have not yet been ack'd will eventually overwrite an incoming operation, we should\n\t * not process the incoming operation.\n\t * @param op - Operation to check\n\t * @param local - Whether the message originated from the local client\n\t * @param localOpMetadata - For local client messages, this is the metadata that was submitted with the message.\n\t * For messages from a remote client, this will be undefined.\n\t * @returns True if the operation should be processed, false otherwise\n\t */\n\tprivate needProcessKeyOperation(\n\t\top: IMapKeyOperation,\n\t\tlocal: boolean,\n\t\tlocalOpMetadata: MapLocalOpMetadata,\n\t): boolean {\n\t\tif (this.pendingClearMessageIds.length > 0) {\n\t\t\tif (local) {\n\t\t\t\tassert(\n\t\t\t\t\tlocalOpMetadata !== undefined &&\n\t\t\t\t\t\tisMapKeyLocalOpMetadata(localOpMetadata) &&\n\t\t\t\t\t\tlocalOpMetadata.pendingMessageId < this.pendingClearMessageIds[0],\n\t\t\t\t\t0x013 /* \"Received out of order op when there is an unackd clear message\" */,\n\t\t\t\t);\n\t\t\t}\n\t\t\t// If we have an unack'd clear, we can ignore all ops.\n\t\t\treturn false;\n\t\t}\n\n\t\tconst pendingKeyMessageIds = this.pendingKeys.get(op.key);\n\t\tif (pendingKeyMessageIds !== undefined) {\n\t\t\t// Found an unack'd op. Clear it from the map if the pendingMessageId in the map matches this message's\n\t\t\t// and don't process the op.\n\t\t\tif (local) {\n\t\t\t\tassert(\n\t\t\t\t\tlocalOpMetadata !== undefined && isMapKeyLocalOpMetadata(localOpMetadata),\n\t\t\t\t\t0x014 /* pendingMessageId is missing from the local client's operation */,\n\t\t\t\t);\n\t\t\t\tassert(\n\t\t\t\t\tpendingKeyMessageIds[0] === localOpMetadata.pendingMessageId,\n\t\t\t\t\t0x2fa /* Unexpected pending message received */,\n\t\t\t\t);\n\t\t\t\tpendingKeyMessageIds.shift();\n\t\t\t\tif (pendingKeyMessageIds.length === 0) {\n\t\t\t\t\tthis.pendingKeys.delete(op.key);\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn false;\n\t\t}\n\n\t\t// If we don't have a NACK op on the key, we need to process the remote ops.\n\t\treturn !local;\n\t}\n\n\t/**\n\t * Get the message handlers for the map.\n\t * @returns A map of string op names to IMapMessageHandlers for those ops\n\t */\n\tprivate getMessageHandlers(): Map<string, IMapMessageHandler> {\n\t\tconst messageHandlers = new Map<string, IMapMessageHandler>();\n\t\tmessageHandlers.set(\"clear\", {\n\t\t\tprocess: (op: IMapClearOperation, local, localOpMetadata) => {\n\t\t\t\tif (local) {\n\t\t\t\t\tassert(\n\t\t\t\t\t\tisClearLocalOpMetadata(localOpMetadata),\n\t\t\t\t\t\t0x015 /* \"pendingMessageId is missing from the local client's clear operation\" */,\n\t\t\t\t\t);\n\t\t\t\t\tconst pendingClearMessageId = this.pendingClearMessageIds.shift();\n\t\t\t\t\tassert(\n\t\t\t\t\t\tpendingClearMessageId === localOpMetadata.pendingMessageId,\n\t\t\t\t\t\t0x2fb /* pendingMessageId does not match */,\n\t\t\t\t\t);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tif (this.pendingKeys.size > 0) {\n\t\t\t\t\tthis.clearExceptPendingKeys();\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tthis.clearCore(local);\n\t\t\t},\n\t\t\tsubmit: (op: IMapClearOperation, localOpMetadata: IMapClearLocalOpMetadata) => {\n\t\t\t\tassert(\n\t\t\t\t\tisClearLocalOpMetadata(localOpMetadata),\n\t\t\t\t\t0x2fc /* Invalid localOpMetadata for clear */,\n\t\t\t\t);\n\t\t\t\t// We don't reuse the metadata pendingMessageId but send a new one on each submit.\n\t\t\t\tconst pendingClearMessageId = this.pendingClearMessageIds.shift();\n\t\t\t\tassert(\n\t\t\t\t\tpendingClearMessageId === localOpMetadata.pendingMessageId,\n\t\t\t\t\t0x2fd /* pendingMessageId does not match */,\n\t\t\t\t);\n\t\t\t\tthis.submitMapClearMessage(op, localOpMetadata.previousMap);\n\t\t\t},\n\t\t\tapplyStashedOp: (op: IMapClearOperation) => {\n\t\t\t\tconst copy = new Map<string, ILocalValue>(this.data);\n\t\t\t\tthis.clearCore(true);\n\t\t\t\t// We don't reuse the metadata pendingMessageId but send a new one on each submit.\n\t\t\t\treturn createClearLocalOpMetadata(op, this.getMapClearMessageId(), copy);\n\t\t\t},\n\t\t});\n\t\tmessageHandlers.set(\"delete\", {\n\t\t\tprocess: (op: IMapDeleteOperation, local, localOpMetadata) => {\n\t\t\t\tif (!this.needProcessKeyOperation(op, local, localOpMetadata)) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tthis.deleteCore(op.key, local);\n\t\t\t},\n\t\t\tsubmit: (op: IMapDeleteOperation, localOpMetadata: MapKeyLocalOpMetadata) => {\n\t\t\t\tthis.resubmitMapKeyMessage(op, localOpMetadata);\n\t\t\t},\n\t\t\tapplyStashedOp: (op: IMapDeleteOperation) => {\n\t\t\t\t// We don't reuse the metadata pendingMessageId but send a new one on each submit.\n\t\t\t\tconst previousValue = this.deleteCore(op.key, true);\n\t\t\t\treturn createKeyLocalOpMetadata(op, this.getMapKeyMessageId(op), previousValue);\n\t\t\t},\n\t\t});\n\t\tmessageHandlers.set(\"set\", {\n\t\t\tprocess: (op: IMapSetOperation, local, localOpMetadata) => {\n\t\t\t\tif (!this.needProcessKeyOperation(op, local, localOpMetadata)) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\t// needProcessKeyOperation should have returned false if local is true\n\t\t\t\tconst context = this.makeLocal(op.key, op.value);\n\t\t\t\tthis.setCore(op.key, context, local);\n\t\t\t},\n\t\t\tsubmit: (op: IMapSetOperation, localOpMetadata: MapKeyLocalOpMetadata) => {\n\t\t\t\tthis.resubmitMapKeyMessage(op, localOpMetadata);\n\t\t\t},\n\t\t\tapplyStashedOp: (op: IMapSetOperation) => {\n\t\t\t\t// We don't reuse the metadata pendingMessageId but send a new one on each submit.\n\t\t\t\tconst context = this.makeLocal(op.key, op.value);\n\t\t\t\tconst previousValue = this.setCore(op.key, context, true);\n\t\t\t\treturn createKeyLocalOpMetadata(op, this.getMapKeyMessageId(op), previousValue);\n\t\t\t},\n\t\t});\n\n\t\treturn messageHandlers;\n\t}\n\n\tprivate getMapClearMessageId(): number {\n\t\tconst pendingMessageId = ++this.pendingMessageId;\n\t\tthis.pendingClearMessageIds.push(pendingMessageId);\n\t\treturn pendingMessageId;\n\t}\n\n\t/**\n\t * Submit a clear message to remote clients.\n\t * @param op - The clear message\n\t */\n\tprivate submitMapClearMessage(\n\t\top: IMapClearOperation,\n\t\tpreviousMap?: Map<string, ILocalValue>,\n\t): void {\n\t\tconst metadata = createClearLocalOpMetadata(op, this.getMapClearMessageId(), previousMap);\n\t\tthis.submitMessage(op, metadata);\n\t}\n\n\tprivate getMapKeyMessageId(op: IMapKeyOperation): number {\n\t\tconst pendingMessageId = ++this.pendingMessageId;\n\t\tconst pendingMessageIds = this.pendingKeys.get(op.key);\n\t\tif (pendingMessageIds !== undefined) {\n\t\t\tpendingMessageIds.push(pendingMessageId);\n\t\t} else {\n\t\t\tthis.pendingKeys.set(op.key, [pendingMessageId]);\n\t\t}\n\t\treturn pendingMessageId;\n\t}\n\n\t/**\n\t * Submit a map key message to remote clients.\n\t * @param op - The map key message\n\t * @param previousValue - The value of the key before this op\n\t */\n\tprivate submitMapKeyMessage(op: IMapKeyOperation, previousValue?: ILocalValue): void {\n\t\tconst localMetadata = createKeyLocalOpMetadata(\n\t\t\top,\n\t\t\tthis.getMapKeyMessageId(op),\n\t\t\tpreviousValue,\n\t\t);\n\t\tthis.submitMessage(op, localMetadata);\n\t}\n\n\t/**\n\t * Submit a map key message to remote clients based on a previous submit.\n\t * @param op - The map key message\n\t * @param localOpMetadata - Metadata from the previous submit\n\t */\n\tprivate resubmitMapKeyMessage(op: IMapKeyOperation, localOpMetadata: MapLocalOpMetadata): void {\n\t\tassert(\n\t\t\tisMapKeyLocalOpMetadata(localOpMetadata),\n\t\t\t0x2fe /* Invalid localOpMetadata in submit */,\n\t\t);\n\n\t\t// no need to submit messages for op's that have been aborted\n\t\tconst pendingMessageIds = this.pendingKeys.get(op.key);\n\t\tif (pendingMessageIds === undefined) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst index = pendingMessageIds.findIndex((id) => id === localOpMetadata.pendingMessageId);\n\t\tif (index === -1) {\n\t\t\treturn;\n\t\t}\n\n\t\tpendingMessageIds.splice(index, 1);\n\t\tif (pendingMessageIds.length === 0) {\n\t\t\tthis.pendingKeys.delete(op.key);\n\t\t}\n\n\t\t// We don't reuse the metadata pendingMessageId but send a new one on each submit.\n\t\tconst pendingMessageId = this.getMapKeyMessageId(op);\n\t\tconst localMetadata =\n\t\t\tlocalOpMetadata.type === \"edit\"\n\t\t\t\t? { type: \"edit\", pendingMessageId, previousValue: localOpMetadata.previousValue }\n\t\t\t\t: { type: \"add\", pendingMessageId };\n\t\tthis.submitMessage(op, localMetadata);\n\t}\n}\n"]}