@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.
- package/.eslintrc.js +12 -11
- package/.mocharc.js +12 -0
- package/CHANGELOG.md +162 -0
- package/README.md +24 -8
- package/api-extractor-lint.json +4 -0
- package/api-extractor.json +2 -2
- package/api-report/map.api.md +297 -0
- package/dist/{directory.js → directory.cjs} +749 -228
- package/dist/directory.cjs.map +1 -0
- package/dist/directory.d.ts +567 -34
- package/dist/directory.d.ts.map +1 -1
- package/dist/index.cjs +27 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.ts +5 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/{interfaces.js → interfaces.cjs} +1 -1
- package/dist/interfaces.cjs.map +1 -0
- package/dist/interfaces.d.ts +167 -184
- package/dist/interfaces.d.ts.map +1 -1
- package/dist/internalInterfaces.cjs +7 -0
- package/dist/internalInterfaces.cjs.map +1 -0
- package/dist/internalInterfaces.d.ts +101 -0
- package/dist/internalInterfaces.d.ts.map +1 -0
- package/dist/{localValues.js → localValues.cjs} +15 -3
- package/dist/localValues.cjs.map +1 -0
- package/dist/localValues.d.ts +17 -6
- package/dist/localValues.d.ts.map +1 -1
- package/dist/map-alpha.d.ts +982 -0
- package/dist/map-beta.d.ts +275 -0
- package/dist/map-public.d.ts +275 -0
- package/dist/map-untrimmed.d.ts +996 -0
- package/dist/{map.js → map.cjs} +39 -34
- package/dist/map.cjs.map +1 -0
- package/dist/map.d.ts +10 -17
- package/dist/map.d.ts.map +1 -1
- package/dist/{mapKernel.js → mapKernel.cjs} +122 -79
- package/dist/mapKernel.cjs.map +1 -0
- package/dist/mapKernel.d.ts +17 -48
- package/dist/mapKernel.d.ts.map +1 -1
- package/dist/{packageVersion.js → packageVersion.cjs} +2 -2
- package/dist/packageVersion.cjs.map +1 -0
- package/dist/packageVersion.d.ts +1 -1
- package/dist/packageVersion.d.ts.map +1 -1
- package/dist/tsdoc-metadata.json +11 -0
- package/lib/directory.d.mts +902 -0
- package/lib/directory.d.mts.map +1 -0
- package/lib/{directory.js → directory.mjs} +736 -199
- package/lib/directory.mjs.map +1 -0
- package/lib/index.d.mts +9 -0
- package/lib/index.d.mts.map +1 -0
- package/lib/index.mjs +8 -0
- package/lib/index.mjs.map +1 -0
- package/lib/{interfaces.d.ts → interfaces.d.mts} +167 -184
- package/lib/interfaces.d.mts.map +1 -0
- package/lib/{interfaces.js → interfaces.mjs} +1 -1
- package/lib/interfaces.mjs.map +1 -0
- package/lib/internalInterfaces.d.mts +101 -0
- package/lib/internalInterfaces.d.mts.map +1 -0
- package/lib/internalInterfaces.mjs +6 -0
- package/lib/internalInterfaces.mjs.map +1 -0
- package/lib/{localValues.d.ts → localValues.d.mts} +18 -7
- package/lib/localValues.d.mts.map +1 -0
- package/lib/{localValues.js → localValues.mjs} +15 -3
- package/lib/localValues.mjs.map +1 -0
- package/lib/map-alpha.d.mts +982 -0
- package/lib/map-beta.d.mts +275 -0
- package/lib/map-public.d.mts +275 -0
- package/lib/map-untrimmed.d.mts +996 -0
- package/lib/{map.d.ts → map.d.mts} +11 -18
- package/lib/map.d.mts.map +1 -0
- package/lib/{map.js → map.mjs} +40 -35
- package/lib/map.mjs.map +1 -0
- package/lib/{mapKernel.d.ts → mapKernel.d.mts} +18 -49
- package/lib/mapKernel.d.mts.map +1 -0
- package/lib/{mapKernel.js → mapKernel.mjs} +116 -73
- package/lib/mapKernel.mjs.map +1 -0
- package/lib/{packageVersion.d.ts → packageVersion.d.mts} +1 -1
- package/lib/{packageVersion.d.ts.map → packageVersion.d.mts.map} +1 -1
- package/lib/{packageVersion.js → packageVersion.mjs} +2 -2
- package/lib/packageVersion.mjs.map +1 -0
- package/map.test-files.tar +0 -0
- package/package.json +105 -65
- package/prettier.config.cjs +8 -0
- package/src/directory.ts +2544 -1727
- package/src/index.ts +31 -5
- package/src/interfaces.ts +346 -345
- package/src/internalInterfaces.ts +119 -0
- package/src/localValues.ts +103 -96
- package/src/map.ts +362 -351
- package/src/mapKernel.ts +755 -722
- package/src/packageVersion.ts +1 -1
- package/tsc-multi.test.json +4 -0
- package/tsconfig.json +10 -15
- package/dist/directory.js.map +0 -1
- package/dist/index.js +0 -34
- package/dist/index.js.map +0 -1
- package/dist/interfaces.js.map +0 -1
- package/dist/localValues.js.map +0 -1
- package/dist/map.js.map +0 -1
- package/dist/mapKernel.js.map +0 -1
- package/dist/packageVersion.js.map +0 -1
- package/lib/directory.d.ts +0 -369
- package/lib/directory.d.ts.map +0 -1
- package/lib/directory.js.map +0 -1
- package/lib/index.d.ts +0 -20
- package/lib/index.d.ts.map +0 -1
- package/lib/index.js +0 -20
- package/lib/index.js.map +0 -1
- package/lib/interfaces.d.ts.map +0 -1
- package/lib/interfaces.js.map +0 -1
- package/lib/localValues.d.ts.map +0 -1
- package/lib/localValues.js.map +0 -1
- package/lib/map.d.ts.map +0 -1
- package/lib/map.js.map +0 -1
- package/lib/mapKernel.d.ts.map +0 -1
- package/lib/mapKernel.js.map +0 -1
- package/lib/packageVersion.js.map +0 -1
- 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
|
|
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 &&
|
|
13
|
-
|
|
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 &&
|
|
18
|
+
return (metadata !== undefined &&
|
|
19
|
+
metadata.type === "clear" &&
|
|
20
|
+
typeof metadata.pendingMessageId === "number");
|
|
17
21
|
}
|
|
18
22
|
function isMapLocalOpMetadata(metadata) {
|
|
19
|
-
return metadata !== undefined &&
|
|
20
|
-
|
|
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
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
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
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
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.
|
|
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.
|
|
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
|
-
|
|
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.
|
|
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.
|
|
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 ||
|
|
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" &&
|
|
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
|
|
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
|
|
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
|
|
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.
|
|
379
|
-
//
|
|
380
|
-
|
|
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.
|
|
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] ||
|
|
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,
|
|
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
|
|
424
|
-
if (
|
|
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,
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
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,
|
|
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,
|
|
487
|
+
(0, core_utils_1.assert)(pendingClearMessageId === localOpMetadata.pendingMessageId, 0x2fb /* pendingMessageId does not match */);
|
|
453
488
|
return;
|
|
454
489
|
}
|
|
455
|
-
if (this.pendingKeys.size
|
|
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,
|
|
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,
|
|
500
|
+
(0, core_utils_1.assert)(pendingClearMessageId === localOpMetadata.pendingMessageId, 0x2fd /* pendingMessageId does not match */);
|
|
466
501
|
this.submitMapClearMessage(op, localOpMetadata.previousMap);
|
|
467
502
|
},
|
|
468
|
-
|
|
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
|
|
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
|
-
|
|
520
|
+
applyStashedOp: (op) => {
|
|
484
521
|
// We don't reuse the metadata pendingMessageId but send a new one on each submit.
|
|
485
|
-
|
|
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
|
-
|
|
538
|
+
applyStashedOp: (op) => {
|
|
501
539
|
// We don't reuse the metadata pendingMessageId but send a new one on each submit.
|
|
502
|
-
|
|
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 =
|
|
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
|
|
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,
|
|
550
|
-
//
|
|
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
|
-
|
|
553
|
-
|
|
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.
|
|
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"]}
|