@fluidframework/map 1.0.1 → 1.1.0
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/dist/directory.js +4 -4
- package/dist/directory.js.map +1 -1
- package/dist/interfaces.d.ts +13 -14
- package/dist/interfaces.d.ts.map +1 -1
- package/dist/interfaces.js.map +1 -1
- package/dist/map.d.ts +5 -0
- package/dist/map.d.ts.map +1 -1
- package/dist/map.js +7 -0
- package/dist/map.js.map +1 -1
- package/dist/mapKernel.d.ts +20 -11
- package/dist/mapKernel.d.ts.map +1 -1
- package/dist/mapKernel.js +160 -82
- package/dist/mapKernel.js.map +1 -1
- package/dist/packageVersion.d.ts +1 -1
- package/dist/packageVersion.js +1 -1
- package/dist/packageVersion.js.map +1 -1
- package/lib/directory.js +4 -4
- package/lib/directory.js.map +1 -1
- package/lib/interfaces.d.ts +13 -14
- package/lib/interfaces.d.ts.map +1 -1
- package/lib/interfaces.js.map +1 -1
- package/lib/map.d.ts +5 -0
- package/lib/map.d.ts.map +1 -1
- package/lib/map.js +7 -0
- package/lib/map.js.map +1 -1
- package/lib/mapKernel.d.ts +20 -11
- package/lib/mapKernel.d.ts.map +1 -1
- package/lib/mapKernel.js +160 -82
- package/lib/mapKernel.js.map +1 -1
- package/lib/packageVersion.d.ts +1 -1
- package/lib/packageVersion.js +1 -1
- package/lib/packageVersion.js.map +1 -1
- package/package.json +15 -15
- package/src/directory.ts +4 -4
- package/src/interfaces.ts +14 -16
- package/src/map.ts +8 -0
- package/src/mapKernel.ts +195 -90
- package/src/packageVersion.ts +1 -1
package/lib/mapKernel.js
CHANGED
|
@@ -5,6 +5,17 @@
|
|
|
5
5
|
import { ValueType } from "@fluidframework/shared-object-base";
|
|
6
6
|
import { assert } from "@fluidframework/common-utils";
|
|
7
7
|
import { LocalValueMaker, makeSerializable, } from "./localValues";
|
|
8
|
+
function isMapKeyLocalOpMetadata(metadata) {
|
|
9
|
+
return metadata !== undefined && typeof metadata.pendingMessageId === "number" &&
|
|
10
|
+
(metadata.type === "add" || metadata.type === "edit");
|
|
11
|
+
}
|
|
12
|
+
function isClearLocalOpMetadata(metadata) {
|
|
13
|
+
return metadata !== undefined && metadata.type === "clear" && typeof metadata.pendingMessageId === "number";
|
|
14
|
+
}
|
|
15
|
+
function isMapLocalOpMetadata(metadata) {
|
|
16
|
+
return metadata !== undefined && typeof metadata.pendingMessageId === "number" &&
|
|
17
|
+
(metadata.type === "add" || metadata.type === "edit" || metadata.type === "clear");
|
|
18
|
+
}
|
|
8
19
|
/**
|
|
9
20
|
* A SharedMap is a map-like distributed data structure.
|
|
10
21
|
*/
|
|
@@ -41,10 +52,9 @@ export class MapKernel {
|
|
|
41
52
|
*/
|
|
42
53
|
this.pendingMessageId = -1;
|
|
43
54
|
/**
|
|
44
|
-
*
|
|
45
|
-
* of that clear operation. Otherwise, is -1.
|
|
55
|
+
* The pending ids of any clears that have been performed locally but not yet ack'd from the server
|
|
46
56
|
*/
|
|
47
|
-
this.
|
|
57
|
+
this.pendingClearMessageIds = [];
|
|
48
58
|
this.localValueMaker = new LocalValueMaker(serializer);
|
|
49
59
|
this.messageHandlers = this.getMessageHandlers();
|
|
50
60
|
}
|
|
@@ -127,12 +137,8 @@ export class MapKernel {
|
|
|
127
137
|
* {@inheritDoc ISharedMap.get}
|
|
128
138
|
*/
|
|
129
139
|
get(key) {
|
|
130
|
-
if (!this.data.has(key)) {
|
|
131
|
-
return undefined;
|
|
132
|
-
}
|
|
133
|
-
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
134
140
|
const localValue = this.data.get(key);
|
|
135
|
-
return localValue.value;
|
|
141
|
+
return localValue === undefined ? undefined : localValue.value;
|
|
136
142
|
}
|
|
137
143
|
/**
|
|
138
144
|
* Check if a key exists in the map.
|
|
@@ -154,7 +160,7 @@ export class MapKernel {
|
|
|
154
160
|
const localValue = this.localValueMaker.fromInMemory(value);
|
|
155
161
|
const serializableValue = makeSerializable(localValue, this.serializer, this.handle);
|
|
156
162
|
// Set the value locally.
|
|
157
|
-
this.setCore(key, localValue, true);
|
|
163
|
+
const previousValue = this.setCore(key, localValue, true);
|
|
158
164
|
// If we are not attached, don't submit the op.
|
|
159
165
|
if (!this.isAttached()) {
|
|
160
166
|
return;
|
|
@@ -164,7 +170,7 @@ export class MapKernel {
|
|
|
164
170
|
type: "set",
|
|
165
171
|
value: serializableValue,
|
|
166
172
|
};
|
|
167
|
-
this.submitMapKeyMessage(op);
|
|
173
|
+
this.submitMapKeyMessage(op, previousValue);
|
|
168
174
|
}
|
|
169
175
|
/**
|
|
170
176
|
* Delete a key from the map.
|
|
@@ -173,22 +179,23 @@ export class MapKernel {
|
|
|
173
179
|
*/
|
|
174
180
|
delete(key) {
|
|
175
181
|
// Delete the key locally first.
|
|
176
|
-
const
|
|
182
|
+
const previousValue = this.deleteCore(key, true);
|
|
177
183
|
// If we are not attached, don't submit the op.
|
|
178
184
|
if (!this.isAttached()) {
|
|
179
|
-
return
|
|
185
|
+
return previousValue !== undefined;
|
|
180
186
|
}
|
|
181
187
|
const op = {
|
|
182
188
|
key,
|
|
183
189
|
type: "delete",
|
|
184
190
|
};
|
|
185
|
-
this.submitMapKeyMessage(op);
|
|
186
|
-
return
|
|
191
|
+
this.submitMapKeyMessage(op, previousValue);
|
|
192
|
+
return previousValue !== undefined;
|
|
187
193
|
}
|
|
188
194
|
/**
|
|
189
195
|
* Clear all data from the map.
|
|
190
196
|
*/
|
|
191
197
|
clear() {
|
|
198
|
+
const copy = this.isAttached() ? new Map(this.data) : undefined;
|
|
192
199
|
// Clear the data locally first.
|
|
193
200
|
this.clearCore(true);
|
|
194
201
|
// If we are not attached, don't submit the op.
|
|
@@ -198,7 +205,7 @@ export class MapKernel {
|
|
|
198
205
|
const op = {
|
|
199
206
|
type: "clear",
|
|
200
207
|
};
|
|
201
|
-
this.submitMapClearMessage(op);
|
|
208
|
+
this.submitMapClearMessage(op, copy);
|
|
202
209
|
}
|
|
203
210
|
/**
|
|
204
211
|
* Serializes the data stored in the shared map to a JSON string
|
|
@@ -247,57 +254,97 @@ export class MapKernel {
|
|
|
247
254
|
* @returns True if the operation was submitted, false otherwise.
|
|
248
255
|
*/
|
|
249
256
|
trySubmitMessage(op, localOpMetadata) {
|
|
250
|
-
const
|
|
251
|
-
if (
|
|
252
|
-
|
|
253
|
-
this.messageHandlers.get(type).submit(op, localOpMetadata);
|
|
254
|
-
return true;
|
|
257
|
+
const handler = this.messageHandlers.get(op.type);
|
|
258
|
+
if (handler === undefined) {
|
|
259
|
+
return false;
|
|
255
260
|
}
|
|
256
|
-
|
|
261
|
+
handler.submit(op, localOpMetadata);
|
|
262
|
+
return true;
|
|
257
263
|
}
|
|
258
264
|
tryGetStashedOpLocalMetadata(op) {
|
|
259
|
-
const
|
|
260
|
-
if (
|
|
261
|
-
|
|
262
|
-
return this.messageHandlers.get(type).getStashedOpLocalMetadata(op);
|
|
265
|
+
const handler = this.messageHandlers.get(op.type);
|
|
266
|
+
if (handler === undefined) {
|
|
267
|
+
throw new Error("no apply stashed op handler");
|
|
263
268
|
}
|
|
264
|
-
|
|
269
|
+
return handler.getStashedOpLocalMetadata(op);
|
|
265
270
|
}
|
|
266
271
|
/**
|
|
267
272
|
* Process the given op if a handler is registered.
|
|
268
|
-
* @param
|
|
273
|
+
* @param op - The message to process
|
|
269
274
|
* @param local - Whether the message originated from the local client
|
|
270
275
|
* @param localOpMetadata - For local client messages, this is the metadata that was submitted with the message.
|
|
271
276
|
* For messages from a remote client, this will be undefined.
|
|
272
277
|
* @returns True if the operation was processed, false otherwise.
|
|
273
278
|
*/
|
|
274
279
|
tryProcessMessage(op, local, localOpMetadata) {
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
280
|
+
const handler = this.messageHandlers.get(op.type);
|
|
281
|
+
if (handler === undefined) {
|
|
282
|
+
return false;
|
|
283
|
+
}
|
|
284
|
+
handler.process(op, local, localOpMetadata);
|
|
285
|
+
return true;
|
|
286
|
+
}
|
|
287
|
+
/**
|
|
288
|
+
* Rollback a local op
|
|
289
|
+
* @param op - The operation to rollback
|
|
290
|
+
* @param localOpMetadata - The local metadata associated with the op.
|
|
291
|
+
*/
|
|
292
|
+
rollback(op, localOpMetadata) {
|
|
293
|
+
if (!isMapLocalOpMetadata(localOpMetadata)) {
|
|
294
|
+
throw new Error("Invalid localOpMetadata");
|
|
295
|
+
}
|
|
296
|
+
if (op.type === "clear" && localOpMetadata.type === "clear") {
|
|
297
|
+
if (localOpMetadata.previousMap === undefined) {
|
|
298
|
+
throw new Error("Cannot rollback without previous map");
|
|
299
|
+
}
|
|
300
|
+
localOpMetadata.previousMap.forEach((localValue, key) => {
|
|
301
|
+
this.setCore(key, localValue, true);
|
|
302
|
+
});
|
|
303
|
+
const lastPendingClearId = this.pendingClearMessageIds.pop();
|
|
304
|
+
if (lastPendingClearId === undefined || lastPendingClearId !== localOpMetadata.pendingMessageId) {
|
|
305
|
+
throw new Error("Rollback op does match last clear");
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
else if (op.type === "delete" || op.type === "set") {
|
|
309
|
+
if (localOpMetadata.type === "add") {
|
|
310
|
+
this.deleteCore(op.key, true);
|
|
311
|
+
}
|
|
312
|
+
else if (localOpMetadata.type === "edit" && localOpMetadata.previousValue !== undefined) {
|
|
313
|
+
this.setCore(op.key, localOpMetadata.previousValue, true);
|
|
314
|
+
}
|
|
315
|
+
else {
|
|
316
|
+
throw new Error("Cannot rollback without previous value");
|
|
317
|
+
}
|
|
318
|
+
const pendingMessageIds = this.pendingKeys.get(op.key);
|
|
319
|
+
const lastPendingMessageId = pendingMessageIds === null || pendingMessageIds === void 0 ? void 0 : pendingMessageIds.pop();
|
|
320
|
+
if (!pendingMessageIds || lastPendingMessageId !== localOpMetadata.pendingMessageId) {
|
|
321
|
+
throw new Error("Rollback op does not match last pending");
|
|
322
|
+
}
|
|
323
|
+
if (pendingMessageIds.length === 0) {
|
|
324
|
+
this.pendingKeys.delete(op.key);
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
else {
|
|
328
|
+
throw new Error("Unsupported op for rollback");
|
|
281
329
|
}
|
|
282
|
-
return false;
|
|
283
330
|
}
|
|
284
331
|
/**
|
|
285
332
|
* Set implementation used for both locally sourced sets as well as incoming remote sets.
|
|
286
333
|
* @param key - The key being set
|
|
287
334
|
* @param value - The value being set
|
|
288
335
|
* @param local - Whether the message originated from the local client
|
|
289
|
-
* @
|
|
336
|
+
* @returns Previous local value of the key, if any
|
|
290
337
|
*/
|
|
291
338
|
setCore(key, value, local) {
|
|
292
|
-
const
|
|
339
|
+
const previousLocalValue = this.data.get(key);
|
|
340
|
+
const previousValue = previousLocalValue === null || previousLocalValue === void 0 ? void 0 : previousLocalValue.value;
|
|
293
341
|
this.data.set(key, value);
|
|
294
|
-
|
|
295
|
-
|
|
342
|
+
this.eventEmitter.emit("valueChanged", { key, previousValue }, local, this.eventEmitter);
|
|
343
|
+
return previousLocalValue;
|
|
296
344
|
}
|
|
297
345
|
/**
|
|
298
346
|
* Clear implementation used for both locally sourced clears as well as incoming remote clears.
|
|
299
347
|
* @param local - Whether the message originated from the local client
|
|
300
|
-
* @param op - The message if from a remote clear, or null if from a local clear
|
|
301
348
|
*/
|
|
302
349
|
clearCore(local) {
|
|
303
350
|
this.data.clear();
|
|
@@ -307,17 +354,16 @@ export class MapKernel {
|
|
|
307
354
|
* Delete implementation used for both locally sourced deletes as well as incoming remote deletes.
|
|
308
355
|
* @param key - The key being deleted
|
|
309
356
|
* @param local - Whether the message originated from the local client
|
|
310
|
-
* @
|
|
311
|
-
* @returns True if the key existed and was deleted, false if it did not exist
|
|
357
|
+
* @returns Previous local value of the key if it existed, undefined if it did not exist
|
|
312
358
|
*/
|
|
313
359
|
deleteCore(key, local) {
|
|
314
|
-
const
|
|
360
|
+
const previousLocalValue = this.data.get(key);
|
|
361
|
+
const previousValue = previousLocalValue === null || previousLocalValue === void 0 ? void 0 : previousLocalValue.value;
|
|
315
362
|
const successfullyRemoved = this.data.delete(key);
|
|
316
363
|
if (successfullyRemoved) {
|
|
317
|
-
|
|
318
|
-
this.eventEmitter.emit("valueChanged", event, local, this.eventEmitter);
|
|
364
|
+
this.eventEmitter.emit("valueChanged", { key, previousValue }, local, this.eventEmitter);
|
|
319
365
|
}
|
|
320
|
-
return
|
|
366
|
+
return previousLocalValue;
|
|
321
367
|
}
|
|
322
368
|
/**
|
|
323
369
|
* Clear all keys in memory in response to a remote clear, but retain keys we have modified but not yet been ack'd.
|
|
@@ -330,9 +376,9 @@ export class MapKernel {
|
|
|
330
376
|
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
331
377
|
temp.set(key, this.data.get(key));
|
|
332
378
|
});
|
|
333
|
-
this.
|
|
379
|
+
this.clearCore(false);
|
|
334
380
|
temp.forEach((value, key) => {
|
|
335
|
-
this.
|
|
381
|
+
this.setCore(key, value, true);
|
|
336
382
|
});
|
|
337
383
|
}
|
|
338
384
|
/**
|
|
@@ -358,27 +404,29 @@ export class MapKernel {
|
|
|
358
404
|
* not process the incoming operation.
|
|
359
405
|
* @param op - Operation to check
|
|
360
406
|
* @param local - Whether the message originated from the local client
|
|
361
|
-
* @param message - The message
|
|
362
407
|
* @param localOpMetadata - For local client messages, this is the metadata that was submitted with the message.
|
|
363
408
|
* For messages from a remote client, this will be undefined.
|
|
364
409
|
* @returns True if the operation should be processed, false otherwise
|
|
365
410
|
*/
|
|
366
411
|
needProcessKeyOperation(op, local, localOpMetadata) {
|
|
367
|
-
if (this.
|
|
412
|
+
if (this.pendingClearMessageIds.length > 0) {
|
|
368
413
|
if (local) {
|
|
369
|
-
assert(localOpMetadata !== undefined && localOpMetadata
|
|
414
|
+
assert(localOpMetadata !== undefined && isMapKeyLocalOpMetadata(localOpMetadata) &&
|
|
415
|
+
localOpMetadata.pendingMessageId < this.pendingClearMessageIds[0], 0x013 /* "Received out of order op when there is an unackd clear message" */);
|
|
370
416
|
}
|
|
371
417
|
// If we have an unack'd clear, we can ignore all ops.
|
|
372
418
|
return false;
|
|
373
419
|
}
|
|
374
|
-
|
|
420
|
+
const pendingKeyMessageId = this.pendingKeys.get(op.key);
|
|
421
|
+
if (pendingKeyMessageId !== undefined) {
|
|
375
422
|
// Found an unack'd op. Clear it from the map if the pendingMessageId in the map matches this message's
|
|
376
423
|
// and don't process the op.
|
|
377
424
|
if (local) {
|
|
378
|
-
assert(localOpMetadata !== undefined, 0x014 /* pendingMessageId is missing from the local client's operation */);
|
|
379
|
-
const
|
|
380
|
-
|
|
381
|
-
|
|
425
|
+
assert(localOpMetadata !== undefined && isMapKeyLocalOpMetadata(localOpMetadata), 0x014 /* pendingMessageId is missing from the local client's operation */);
|
|
426
|
+
const pendingMessageIds = this.pendingKeys.get(op.key);
|
|
427
|
+
assert(pendingMessageIds !== undefined && pendingMessageIds[0] === localOpMetadata.pendingMessageId, 0x2fa /* Unexpected pending message received */);
|
|
428
|
+
pendingMessageIds.shift();
|
|
429
|
+
if (pendingMessageIds.length === 0) {
|
|
382
430
|
this.pendingKeys.delete(op.key);
|
|
383
431
|
}
|
|
384
432
|
}
|
|
@@ -396,11 +444,9 @@ export class MapKernel {
|
|
|
396
444
|
messageHandlers.set("clear", {
|
|
397
445
|
process: (op, local, localOpMetadata) => {
|
|
398
446
|
if (local) {
|
|
399
|
-
assert(localOpMetadata
|
|
400
|
-
const
|
|
401
|
-
|
|
402
|
-
this.pendingClearMessageId = -1;
|
|
403
|
-
}
|
|
447
|
+
assert(isClearLocalOpMetadata(localOpMetadata), 0x015 /* "pendingMessageId is missing from the local client's clear operation" */);
|
|
448
|
+
const pendingClearMessageId = this.pendingClearMessageIds.shift();
|
|
449
|
+
assert(pendingClearMessageId === localOpMetadata.pendingMessageId, 0x2fb /* pendingMessageId does not match */);
|
|
404
450
|
return;
|
|
405
451
|
}
|
|
406
452
|
if (this.pendingKeys.size !== 0) {
|
|
@@ -410,12 +456,15 @@ export class MapKernel {
|
|
|
410
456
|
this.clearCore(local);
|
|
411
457
|
},
|
|
412
458
|
submit: (op, localOpMetadata) => {
|
|
413
|
-
|
|
414
|
-
|
|
459
|
+
assert(isClearLocalOpMetadata(localOpMetadata), 0x2fc /* Invalid localOpMetadata for clear */);
|
|
460
|
+
// We don't reuse the metadata pendingMessageId but send a new one on each submit.
|
|
461
|
+
const pendingClearMessageId = this.pendingClearMessageIds.shift();
|
|
462
|
+
assert(pendingClearMessageId === localOpMetadata.pendingMessageId, 0x2fd /* pendingMessageId does not match */);
|
|
463
|
+
this.submitMapClearMessage(op, localOpMetadata.previousMap);
|
|
415
464
|
},
|
|
416
465
|
getStashedOpLocalMetadata: (op) => {
|
|
417
|
-
// We don't reuse the metadata but send a new one on each submit.
|
|
418
|
-
return this.
|
|
466
|
+
// We don't reuse the metadata pendingMessageId but send a new one on each submit.
|
|
467
|
+
return { type: "clear", pendingMessageId: this.getMapClearMessageId() };
|
|
419
468
|
},
|
|
420
469
|
});
|
|
421
470
|
messageHandlers.set("delete", {
|
|
@@ -426,12 +475,11 @@ export class MapKernel {
|
|
|
426
475
|
this.deleteCore(op.key, local);
|
|
427
476
|
},
|
|
428
477
|
submit: (op, localOpMetadata) => {
|
|
429
|
-
|
|
430
|
-
this.submitMapKeyMessage(op);
|
|
478
|
+
this.resubmitMapKeyMessage(op, localOpMetadata);
|
|
431
479
|
},
|
|
432
480
|
getStashedOpLocalMetadata: (op) => {
|
|
433
|
-
// We don't reuse the metadata but send a new one on each submit.
|
|
434
|
-
return this.
|
|
481
|
+
// We don't reuse the metadata pendingMessageId but send a new one on each submit.
|
|
482
|
+
return { type: "edit", pendingMessageId: this.getMapKeyMessageId(op) };
|
|
435
483
|
},
|
|
436
484
|
});
|
|
437
485
|
messageHandlers.set("set", {
|
|
@@ -444,41 +492,71 @@ export class MapKernel {
|
|
|
444
492
|
this.setCore(op.key, context, local);
|
|
445
493
|
},
|
|
446
494
|
submit: (op, localOpMetadata) => {
|
|
447
|
-
|
|
448
|
-
this.submitMapKeyMessage(op);
|
|
495
|
+
this.resubmitMapKeyMessage(op, localOpMetadata);
|
|
449
496
|
},
|
|
450
497
|
getStashedOpLocalMetadata: (op) => {
|
|
451
|
-
// We don't reuse the metadata but send a new one on each submit.
|
|
452
|
-
return this.
|
|
498
|
+
// We don't reuse the metadata pendingMessageId but send a new one on each submit.
|
|
499
|
+
return { type: "edit", pendingMessageId: this.getMapKeyMessageId(op) };
|
|
453
500
|
},
|
|
454
501
|
});
|
|
455
502
|
return messageHandlers;
|
|
456
503
|
}
|
|
457
|
-
|
|
504
|
+
getMapClearMessageId() {
|
|
458
505
|
const pendingMessageId = ++this.pendingMessageId;
|
|
459
|
-
this.
|
|
506
|
+
this.pendingClearMessageIds.push(pendingMessageId);
|
|
460
507
|
return pendingMessageId;
|
|
461
508
|
}
|
|
462
509
|
/**
|
|
463
510
|
* Submit a clear message to remote clients.
|
|
464
511
|
* @param op - The clear message
|
|
465
512
|
*/
|
|
466
|
-
submitMapClearMessage(op) {
|
|
467
|
-
const
|
|
468
|
-
this.submitMessage(op,
|
|
513
|
+
submitMapClearMessage(op, previousMap) {
|
|
514
|
+
const metadata = { type: "clear", pendingMessageId: this.getMapClearMessageId(), previousMap };
|
|
515
|
+
this.submitMessage(op, metadata);
|
|
469
516
|
}
|
|
470
|
-
|
|
517
|
+
getMapKeyMessageId(op) {
|
|
471
518
|
const pendingMessageId = ++this.pendingMessageId;
|
|
472
|
-
this.pendingKeys.
|
|
519
|
+
const pendingMessageIds = this.pendingKeys.get(op.key);
|
|
520
|
+
if (pendingMessageIds !== undefined) {
|
|
521
|
+
pendingMessageIds.push(pendingMessageId);
|
|
522
|
+
}
|
|
523
|
+
else {
|
|
524
|
+
this.pendingKeys.set(op.key, [pendingMessageId]);
|
|
525
|
+
}
|
|
473
526
|
return pendingMessageId;
|
|
474
527
|
}
|
|
475
528
|
/**
|
|
476
529
|
* Submit a map key message to remote clients.
|
|
477
530
|
* @param op - The map key message
|
|
531
|
+
* @param previousValue - The value of the key before this op
|
|
532
|
+
*/
|
|
533
|
+
submitMapKeyMessage(op, previousValue) {
|
|
534
|
+
const pendingMessageId = this.getMapKeyMessageId(op);
|
|
535
|
+
const localMetadata = previousValue ?
|
|
536
|
+
{ type: "edit", pendingMessageId, previousValue } :
|
|
537
|
+
{ type: "add", pendingMessageId };
|
|
538
|
+
this.submitMessage(op, localMetadata);
|
|
539
|
+
}
|
|
540
|
+
/**
|
|
541
|
+
* Submit a map key message to remote clients based on a previous submit.
|
|
542
|
+
* @param op - The map key message
|
|
543
|
+
* @param localOpMetadata - Metadata from the previous submit
|
|
478
544
|
*/
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
545
|
+
resubmitMapKeyMessage(op, localOpMetadata) {
|
|
546
|
+
assert(isMapKeyLocalOpMetadata(localOpMetadata), 0x2fe /* Invalid localOpMetadata in submit */);
|
|
547
|
+
// clear the old pending message id
|
|
548
|
+
const pendingMessageIds = this.pendingKeys.get(op.key);
|
|
549
|
+
assert(pendingMessageIds !== undefined && pendingMessageIds[0] === localOpMetadata.pendingMessageId, 0x2ff /* Unexpected pending message received */);
|
|
550
|
+
pendingMessageIds.shift();
|
|
551
|
+
if (pendingMessageIds.length === 0) {
|
|
552
|
+
this.pendingKeys.delete(op.key);
|
|
553
|
+
}
|
|
554
|
+
// We don't reuse the metadata pendingMessageId but send a new one on each submit.
|
|
555
|
+
const pendingMessageId = this.getMapKeyMessageId(op);
|
|
556
|
+
const localMetadata = localOpMetadata.type === "edit" ?
|
|
557
|
+
{ type: "edit", pendingMessageId, previousValue: localOpMetadata.previousValue } :
|
|
558
|
+
{ type: "add", pendingMessageId };
|
|
559
|
+
this.submitMessage(op, localMetadata);
|
|
482
560
|
}
|
|
483
561
|
}
|
|
484
562
|
//# sourceMappingURL=mapKernel.js.map
|
package/lib/mapKernel.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mapKernel.js","sourceRoot":"","sources":["../src/mapKernel.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,EAAoB,SAAS,EAAE,MAAM,oCAAoC,CAAC;AACjF,OAAO,EAAE,MAAM,EAAqB,MAAM,8BAA8B,CAAC;AAOzE,OAAO,EAEH,eAAe,EACf,gBAAgB,GACnB,MAAM,eAAe,CAAC;AAgGvB;;GAEG;AACH,MAAM,OAAO,SAAS;IAuClB;;;;;;;;OAQG;IACH,YACqB,UAA4B,EAC5B,MAAoB,EACpB,aAA0D,EAC1D,UAAyB,EACzB,YAAiD;QAJjD,eAAU,GAAV,UAAU,CAAkB;QAC5B,WAAM,GAAN,MAAM,CAAc;QACpB,kBAAa,GAAb,aAAa,CAA6C;QAC1D,eAAU,GAAV,UAAU,CAAe;QACzB,iBAAY,GAAZ,YAAY,CAAqC;QA7CtE;;WAEG;QACc,oBAAe,GAA4C,IAAI,GAAG,EAAE,CAAC;QAEtF;;WAEG;QACc,SAAI,GAAG,IAAI,GAAG,EAAuB,CAAC;QAEvD;;WAEG;QACc,gBAAW,GAAwB,IAAI,GAAG,EAAE,CAAC;QAE9D;;WAEG;QACK,qBAAgB,GAAW,CAAC,CAAC,CAAC;QAEtC;;;WAGG;QACK,0BAAqB,GAAW,CAAC,CAAC,CAAC;QAuBvC,IAAI,CAAC,eAAe,GAAG,IAAI,eAAe,CAAC,UAAU,CAAC,CAAC;QACvD,IAAI,CAAC,eAAe,GAAG,IAAI,CAAC,kBAAkB,EAAE,CAAC;IACrD,CAAC;IAxDD;;OAEG;IACH,IAAW,IAAI;QACX,OAAO,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC;IAC1B,CAAC;IAqDD;;;OAGG;IACI,IAAI;QACP,OAAO,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;IAC5B,CAAC;IAED;;;OAGG;IACI,OAAO;QACV,MAAM,oBAAoB,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC;QACjD,MAAM,QAAQ,GAAG;YACb,IAAI;gBACA,MAAM,OAAO,GAAG,oBAAoB,CAAC,IAAI,EAAE,CAAC;gBAC5C,IAAI,OAAO,CAAC,IAAI,EAAE;oBACd,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;iBAC3C;qBAAM;oBACH,0BAA0B;oBAC1B,OAAO,EAAE,KAAK,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC;iBAC7E;YACL,CAAC;YACD,CAAC,MAAM,CAAC,QAAQ,CAAC;gBACb,OAAO,IAAI,CAAC;YAChB,CAAC;SACJ,CAAC;QACF,OAAO,QAAQ,CAAC;IACpB,CAAC;IAED;;;OAGG;IACI,MAAM;QACT,MAAM,mBAAmB,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC;QAC/C,MAAM,QAAQ,GAAG;YACb,IAAI;gBACA,MAAM,OAAO,GAAG,mBAAmB,CAAC,IAAI,EAAE,CAAC;gBAC3C,IAAI,OAAO,CAAC,IAAI,EAAE;oBACd,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;iBAC3C;qBAAM;oBACH,0BAA0B;oBAC1B,OAAO,EAAE,KAAK,EAAE,OAAO,CAAC,KAAK,CAAC,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC;iBACtD;YACL,CAAC;YACD,CAAC,MAAM,CAAC,QAAQ,CAAC;gBACb,OAAO,IAAI,CAAC;YAChB,CAAC;SACJ,CAAC;QACF,OAAO,QAAQ,CAAC;IACpB,CAAC;IAED;;;OAGG;IACI,CAAC,MAAM,CAAC,QAAQ,CAAC;QACpB,OAAO,IAAI,CAAC,OAAO,EAAE,CAAC;IAC1B,CAAC;IAED;;;OAGG;IACI,OAAO,CAAC,UAAoE;QAC/E,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,UAAU,EAAE,GAAG,EAAE,CAAC,EAAE,EAAE;YACrC,UAAU,CAAC,UAAU,CAAC,KAAK,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC;QACzC,CAAC,CAAC,CAAC;IACP,CAAC;IAED;;OAEG;IACI,GAAG,CAAU,GAAW;QAC3B,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE;YACrB,OAAO,SAAS,CAAC;SACpB;QAED,oEAAoE;QACpE,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAE,CAAC;QAEvC,OAAO,UAAU,CAAC,KAAU,CAAC;IACjC,CAAC;IAED;;;;OAIG;IACI,GAAG,CAAC,GAAW;QAClB,OAAO,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IAC9B,CAAC;IAED;;OAEG;IACI,GAAG,CAAC,GAAW,EAAE,KAAU;QAC9B,uFAAuF;QACvF,IAAI,GAAG,KAAK,SAAS,IAAI,GAAG,KAAK,IAAI,EAAE;YACnC,MAAM,IAAI,KAAK,CAAC,2CAA2C,CAAC,CAAC;SAChE;QAED,yCAAyC;QACzC,MAAM,UAAU,GAAG,IAAI,CAAC,eAAe,CAAC,YAAY,CAAC,KAAK,CAAC,CAAC;QAC5D,MAAM,iBAAiB,GAAG,gBAAgB,CACtC,UAAU,EACV,IAAI,CAAC,UAAU,EACf,IAAI,CAAC,MAAM,CAAC,CAAC;QAEjB,yBAAyB;QACzB,IAAI,CAAC,OAAO,CACR,GAAG,EACH,UAAU,EACV,IAAI,CACP,CAAC;QAEF,+CAA+C;QAC/C,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,EAAE;YACpB,OAAO;SACV;QAED,MAAM,EAAE,GAAqB;YACzB,GAAG;YACH,IAAI,EAAE,KAAK;YACX,KAAK,EAAE,iBAAiB;SAC3B,CAAC;QACF,IAAI,CAAC,mBAAmB,CAAC,EAAE,CAAC,CAAC;IACjC,CAAC;IAED;;;;OAIG;IACI,MAAM,CAAC,GAAW;QACrB,gCAAgC;QAChC,MAAM,mBAAmB,GAAG,IAAI,CAAC,UAAU,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;QAEvD,+CAA+C;QAC/C,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,EAAE;YACpB,OAAO,mBAAmB,CAAC;SAC9B;QAED,MAAM,EAAE,GAAwB;YAC5B,GAAG;YACH,IAAI,EAAE,QAAQ;SACjB,CAAC;QACF,IAAI,CAAC,mBAAmB,CAAC,EAAE,CAAC,CAAC;QAE7B,OAAO,mBAAmB,CAAC;IAC/B,CAAC;IAED;;OAEG;IACI,KAAK;QACR,gCAAgC;QAChC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;QAErB,+CAA+C;QAC/C,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,EAAE;YACpB,OAAO;SACV;QAED,MAAM,EAAE,GAAuB;YAC3B,IAAI,EAAE,OAAO;SAChB,CAAC;QACF,IAAI,CAAC,qBAAqB,CAAC,EAAE,CAAC,CAAC;IACnC,CAAC;IAED;;;;OAIG;IACI,oBAAoB,CAAC,UAA4B;QACpD,MAAM,mBAAmB,GAA6B,EAAE,CAAC;QACzD,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,UAAU,EAAE,GAAG,EAAE,EAAE;YAClC,mBAAmB,CAAC,GAAG,CAAC,GAAG,UAAU,CAAC,cAAc,CAAC,UAAU,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC;QAClF,CAAC,CAAC,CAAC;QACH,OAAO,mBAAmB,CAAC;IAC/B,CAAC;IAEM,sBAAsB,CAAC,UAA4B;QACtD,MAAM,mBAAmB,GAA+B,EAAE,CAAC;QAC3D,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,UAAU,EAAE,GAAG,EAAE,EAAE;YAClC,mBAAmB,CAAC,GAAG,CAAC,GAAG,gBAAgB,CAAC,UAAU,EAAE,UAAU,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC;QACrF,CAAC,CAAC,CAAC;QACH,OAAO,mBAAmB,CAAC;IAC/B,CAAC;IAEM,SAAS,CAAC,UAA4B;QACzC,OAAO,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,sBAAsB,CAAC,UAAU,CAAC,CAAC,CAAC;IACnE,CAAC;IAED;;;OAGG;IACI,wBAAwB,CAAC,IAAgC;QAC5D,KAAK,MAAM,CAAC,GAAG,EAAE,YAAY,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE;YACpD,MAAM,UAAU,GAAG;gBACf,GAAG;gBACH,KAAK,EAAE,IAAI,CAAC,SAAS,CAAC,GAAG,EAAE,YAAY,CAAC;aAC3C,CAAC;YAEF,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,UAAU,CAAC,GAAG,EAAE,UAAU,CAAC,KAAK,CAAC,CAAC;SACnD;IACL,CAAC;IAEM,QAAQ,CAAC,IAAY;QACxB,IAAI,CAAC,wBAAwB,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAA+B,CAAC,CAAC;IAClF,CAAC;IAED;;;;;;;OAOG;IACI,gBAAgB,CAAC,EAAO,EAAE,eAAwB;QACrD,MAAM,IAAI,GAAW,EAAE,CAAC,IAAI,CAAC;QAC7B,IAAI,IAAI,CAAC,eAAe,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE;YAChC,oEAAoE;YACpE,IAAI,CAAC,eAAe,CAAC,GAAG,CAAC,IAAI,CAAE,CAAC,MAAM,CAAC,EAAmB,EAAE,eAAe,CAAC,CAAC;YAC7E,OAAO,IAAI,CAAC;SACf;QACD,OAAO,KAAK,CAAC;IACjB,CAAC;IAEM,4BAA4B,CAAC,EAAO;QACvC,MAAM,IAAI,GAAW,EAAE,CAAC,IAAI,CAAC;QAC7B,IAAI,IAAI,CAAC,eAAe,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE;YAChC,oEAAoE;YACpE,OAAO,IAAI,CAAC,eAAe,CAAC,GAAG,CAAC,IAAI,CAAE,CAAC,yBAAyB,CAAC,EAAmB,CAAC,CAAC;SACzF;QACD,MAAM,IAAI,KAAK,CAAC,6BAA6B,CAAC,CAAC;IACnD,CAAC;IAED;;;;;;;OAOG;IACI,iBAAiB,CACpB,EAAiB,EACjB,KAAc,EACd,eAAwB;QAExB,IAAI,IAAI,CAAC,eAAe,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE;YACnC,oEAAoE;YACpE,IAAI,CAAC,eAAe;iBACf,GAAG,CAAC,EAAE,CAAC,IAAI,CAAE;iBACb,OAAO,CAAC,EAAE,EAAE,KAAK,EAAE,eAAe,CAAC,CAAC;YACzC,OAAO,IAAI,CAAC;SACf;QACD,OAAO,KAAK,CAAC;IACjB,CAAC;IAED;;;;;;OAMG;IACK,OAAO,CAAC,GAAW,EAAE,KAAkB,EAAE,KAAc;QAC3D,MAAM,aAAa,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QACpC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;QAC1B,MAAM,KAAK,GAAkB,EAAE,GAAG,EAAE,aAAa,EAAE,CAAC;QACpD,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,cAAc,EAAE,KAAK,EAAE,KAAK,EAAE,IAAI,CAAC,YAAY,CAAC,CAAC;IAC5E,CAAC;IAED;;;;OAIG;IACK,SAAS,CAAC,KAAc;QAC5B,IAAI,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC;QAClB,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,OAAO,EAAE,KAAK,EAAE,IAAI,CAAC,YAAY,CAAC,CAAC;IAC9D,CAAC;IAED;;;;;;OAMG;IACK,UAAU,CAAC,GAAW,EAAE,KAAc;QAC1C,MAAM,aAAa,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QACpC,MAAM,mBAAmB,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QAClD,IAAI,mBAAmB,EAAE;YACrB,MAAM,KAAK,GAAkB,EAAE,GAAG,EAAE,aAAa,EAAE,CAAC;YACpD,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,cAAc,EAAE,KAAK,EAAE,KAAK,EAAE,IAAI,CAAC,YAAY,CAAC,CAAC;SAC3E;QACD,OAAO,mBAAmB,CAAC;IAC/B,CAAC;IAED;;OAEG;IACK,sBAAsB;QAC1B,yDAAyD;QACzD,8DAA8D;QAC9D,MAAM,IAAI,GAAG,IAAI,GAAG,EAAuB,CAAC;QAC5C,IAAI,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,GAAG,EAAE,EAAE;YACpC,oEAAoE;YACpE,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAE,CAAC,CAAC;QACvC,CAAC,CAAC,CAAC;QACH,IAAI,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC;QAClB,IAAI,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,GAAG,EAAE,EAAE;YACxB,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;QAC9B,CAAC,CAAC,CAAC;IACP,CAAC;IAED;;;;;;;;;OASG;IACK,SAAS,CAAC,GAAW,EAAE,YAAgC;QAC3D,IAAI,YAAY,CAAC,IAAI,KAAK,SAAS,CAAC,SAAS,CAAC,KAAK,CAAC,IAAI,YAAY,CAAC,IAAI,KAAK,SAAS,CAAC,SAAS,CAAC,MAAM,CAAC,EAAE;YACvG,OAAO,IAAI,CAAC,eAAe,CAAC,gBAAgB,CAAC,YAAY,CAAC,CAAC;SAC9D;aAAM;YACH,MAAM,IAAI,KAAK,CAAC,0BAA0B,CAAC,CAAC;SAC/C;IACL,CAAC;IAED;;;;;;;;;OASG;IACK,uBAAuB,CAC3B,EAAoB,EACpB,KAAc,EACd,eAAwB;QAExB,IAAI,IAAI,CAAC,qBAAqB,KAAK,CAAC,CAAC,EAAE;YACnC,IAAI,KAAK,EAAE;gBACP,MAAM,CAAC,eAAe,KAAK,SAAS,IAAI,eAAyB,GAAG,IAAI,CAAC,qBAAqB,EAC1F,KAAK,CAAC,sEAAsE,CAAC,CAAC;aACrF;YACD,sDAAsD;YACtD,OAAO,KAAK,CAAC;SAChB;QAED,IAAI,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE;YAC9B,uGAAuG;YACvG,4BAA4B;YAC5B,IAAI,KAAK,EAAE;gBACP,MAAM,CAAC,eAAe,KAAK,SAAS,EAChC,KAAK,CAAC,mEAAmE,CAAC,CAAC;gBAC/E,MAAM,gBAAgB,GAAG,eAAyB,CAAC;gBACnD,MAAM,mBAAmB,GAAG,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC;gBACzD,IAAI,mBAAmB,KAAK,gBAAgB,EAAE;oBAC1C,IAAI,CAAC,WAAW,CAAC,MAAM,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC;iBACnC;aACJ;YACD,OAAO,KAAK,CAAC;SAChB;QAED,4EAA4E;QAC5E,OAAO,CAAC,KAAK,CAAC;IAClB,CAAC;IAED;;;OAGG;IACK,kBAAkB;QACtB,MAAM,eAAe,GAAG,IAAI,GAAG,EAA8B,CAAC;QAC9D,eAAe,CAAC,GAAG,CACf,OAAO,EACP;YACI,OAAO,EAAE,CAAC,EAAsB,EAAE,KAAK,EAAE,eAAe,EAAE,EAAE;gBACxD,IAAI,KAAK,EAAE;oBACP,MAAM,CAAC,eAAe,KAAK,SAAS,EAChC,KAAK,CAAC,2EAA2E,CAAC,CAAC;oBACvF,MAAM,gBAAgB,GAAG,eAAyB,CAAC;oBACnD,IAAI,IAAI,CAAC,qBAAqB,KAAK,gBAAgB,EAAE;wBACjD,IAAI,CAAC,qBAAqB,GAAG,CAAC,CAAC,CAAC;qBACnC;oBACD,OAAO;iBACV;gBACD,IAAI,IAAI,CAAC,WAAW,CAAC,IAAI,KAAK,CAAC,EAAE;oBAC7B,IAAI,CAAC,sBAAsB,EAAE,CAAC;oBAC9B,OAAO;iBACV;gBACD,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;YAC1B,CAAC;YACD,MAAM,EAAE,CAAC,EAAsB,EAAE,eAAwB,EAAE,EAAE;gBACzD,iEAAiE;gBACjE,IAAI,CAAC,qBAAqB,CAAC,EAAE,CAAC,CAAC;YACnC,CAAC;YACD,yBAAyB,EAAE,CAAC,EAAsB,EAAE,EAAE;gBAClD,iEAAiE;gBACjE,OAAO,IAAI,CAAC,+BAA+B,CAAC,EAAE,CAAC,CAAC;YACpD,CAAC;SACJ,CAAC,CAAC;QACP,eAAe,CAAC,GAAG,CACf,QAAQ,EACR;YACI,OAAO,EAAE,CAAC,EAAuB,EAAE,KAAK,EAAE,eAAe,EAAE,EAAE;gBACzD,IAAI,CAAC,IAAI,CAAC,uBAAuB,CAAC,EAAE,EAAE,KAAK,EAAE,eAAe,CAAC,EAAE;oBAC3D,OAAO;iBACV;gBACD,IAAI,CAAC,UAAU,CAAC,EAAE,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;YACnC,CAAC;YACD,MAAM,EAAE,CAAC,EAAuB,EAAE,eAAwB,EAAE,EAAE;gBAC1D,iEAAiE;gBACjE,IAAI,CAAC,mBAAmB,CAAC,EAAE,CAAC,CAAC;YACjC,CAAC;YACD,yBAAyB,EAAE,CAAC,EAAuB,EAAE,EAAE;gBACnD,iEAAiE;gBACjE,OAAO,IAAI,CAAC,6BAA6B,CAAC,EAAE,CAAC,CAAC;YAClD,CAAC;SACJ,CAAC,CAAC;QACP,eAAe,CAAC,GAAG,CACf,KAAK,EACL;YACI,OAAO,EAAE,CAAC,EAAoB,EAAE,KAAK,EAAE,eAAe,EAAE,EAAE;gBACtD,IAAI,CAAC,IAAI,CAAC,uBAAuB,CAAC,EAAE,EAAE,KAAK,EAAE,eAAe,CAAC,EAAE;oBAC3D,OAAO;iBACV;gBAED,sEAAsE;gBACtE,MAAM,OAAO,GAAG,IAAI,CAAC,SAAS,CAAC,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,KAAK,CAAC,CAAC;gBACjD,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC,GAAG,EAAE,OAAO,EAAE,KAAK,CAAC,CAAC;YACzC,CAAC;YACD,MAAM,EAAE,CAAC,EAAoB,EAAE,eAAwB,EAAE,EAAE;gBACvD,iEAAiE;gBACjE,IAAI,CAAC,mBAAmB,CAAC,EAAE,CAAC,CAAC;YACjC,CAAC;YACD,yBAAyB,EAAE,CAAC,EAAoB,EAAE,EAAE;gBAChD,iEAAiE;gBACjE,OAAO,IAAI,CAAC,6BAA6B,CAAC,EAAE,CAAC,CAAC;YAClD,CAAC;SACJ,CAAC,CAAC;QAEP,OAAO,eAAe,CAAC;IAC3B,CAAC;IAEO,+BAA+B,CAAC,EAAsB;QAC1D,MAAM,gBAAgB,GAAG,EAAE,IAAI,CAAC,gBAAgB,CAAC;QACjD,IAAI,CAAC,qBAAqB,GAAG,gBAAgB,CAAC;QAC9C,OAAO,gBAAgB,CAAC;IAC5B,CAAC;IAED;;;OAGG;IACK,qBAAqB,CAAC,EAAsB;QAChD,MAAM,gBAAgB,GAAG,IAAI,CAAC,+BAA+B,CAAC,EAAE,CAAC,CAAC;QAClE,IAAI,CAAC,aAAa,CAAC,EAAE,EAAE,gBAAgB,CAAC,CAAC;IAC7C,CAAC;IAEO,6BAA6B,CAAC,EAAoB;QACtD,MAAM,gBAAgB,GAAG,EAAE,IAAI,CAAC,gBAAgB,CAAC;QACjD,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,EAAE,gBAAgB,CAAC,CAAC;QAC/C,OAAO,gBAAgB,CAAC;IAC5B,CAAC;IAED;;;OAGG;IACK,mBAAmB,CAAC,EAAoB;QAC5C,MAAM,gBAAgB,GAAG,IAAI,CAAC,6BAA6B,CAAC,EAAE,CAAC,CAAC;QAChE,IAAI,CAAC,aAAa,CAAC,EAAE,EAAE,gBAAgB,CAAC,CAAC;IAC7C,CAAC;CACJ","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, TypedEventEmitter } from \"@fluidframework/common-utils\";\nimport {\n ISerializableValue,\n ISerializedValue,\n IValueChanged,\n ISharedMapEvents,\n} from \"./interfaces\";\nimport {\n ILocalValue,\n LocalValueMaker,\n makeSerializable,\n} from \"./localValues\";\n\n/**\n * Defines the means to process and submit a given op on a map.\n */\ninterface IMapMessageHandler {\n /**\n * Apply the given operation.\n * @param op - The map operation to apply\n * @param local - Whether the message originated from the local client\n * @param localOpMetadata - For local client messages, this is the metadata that was submitted with the message.\n * For messages from a remote client, this will be undefined.\n */\n process(\n op: IMapOperation,\n local: boolean,\n localOpMetadata: unknown,\n ): void;\n\n /**\n * Communicate the operation to remote clients.\n * @param op - The map operation to submit\n * @param localOpMetadata - The metadata to be submitted with the message.\n */\n submit(op: IMapOperation, localOpMetadata: unknown): void;\n\n getStashedOpLocalMetadata(op: IMapOperation): unknown;\n}\n\n/**\n * Operation indicating a value should be set for a key.\n */\nexport interface IMapSetOperation {\n /**\n * String identifier of the operation type.\n */\n type: \"set\";\n\n /**\n * Map key being modified.\n */\n key: string;\n\n /**\n * Value to be set on the key.\n */\n value: ISerializableValue;\n}\n\n/**\n * Operation indicating a key should be deleted from the map.\n */\nexport interface IMapDeleteOperation {\n /**\n * String identifier of the operation type.\n */\n type: \"delete\";\n\n /**\n * Map key being modified.\n */\n key: string;\n}\n\n/**\n * Map key operations are one of several types.\n */\nexport type IMapKeyOperation = IMapSetOperation | IMapDeleteOperation;\n\n/**\n * Operation indicating the map should be cleared.\n */\nexport interface IMapClearOperation {\n /**\n * String identifier of the operation type.\n */\n type: \"clear\";\n}\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 * Directly used in JSON.stringify, direct result from JSON.parse\n */\nexport interface IMapDataObjectSerializable {\n [key: string]: ISerializableValue;\n}\n\nexport interface IMapDataObjectSerialized {\n [key: string]: ISerializedValue;\n}\n\n/**\n * A SharedMap is a map-like distributed data structure.\n */\nexport class MapKernel {\n /**\n * The number of key/value pairs stored in the map.\n */\n public get size(): number {\n return this.data.size;\n }\n\n /**\n * Mapping of op types to message handlers.\n */\n private readonly messageHandlers: ReadonlyMap<string, IMapMessageHandler> = new Map();\n\n /**\n * The in-memory data the map is storing.\n */\n private readonly data = new Map<string, ILocalValue>();\n\n /**\n * Keys that have been modified locally but not yet ack'd from the server.\n */\n private readonly pendingKeys: Map<string, number> = new Map();\n\n /**\n * This is used to assign a unique id to every outgoing operation and helps in tracking unack'd ops.\n */\n private pendingMessageId: number = -1;\n\n /**\n * If a clear has been performed locally but not yet ack'd from the server, then this stores the pending id\n * of that clear operation. Otherwise, is -1.\n */\n private pendingClearMessageId: number = -1;\n\n /**\n * Object to create encapsulations of the values stored in the map.\n */\n private readonly localValueMaker: LocalValueMaker;\n\n /**\n * Create a new shared map kernel.\n * @param serializer - The serializer to serialize / parse handles\n * @param handle - The handle of the shared object using the kernel\n * @param submitMessage - A callback to submit a message through the shared object\n * @param isAttached - To query whether the shared object should generate ops\n * @param valueTypes - The value types to register\n * @param eventEmitter - The object that will emit map events\n */\n constructor(\n private readonly serializer: IFluidSerializer,\n private readonly handle: IFluidHandle,\n private readonly submitMessage: (op: any, localOpMetadata: unknown) => void,\n private readonly isAttached: () => boolean,\n private readonly eventEmitter: TypedEventEmitter<ISharedMapEvents>,\n ) {\n this.localValueMaker = new LocalValueMaker(serializer);\n this.messageHandlers = this.getMessageHandlers();\n }\n\n /**\n * Get an iterator over the keys in this map.\n * @returns The iterator\n */\n public keys(): IterableIterator<string> {\n return this.data.keys();\n }\n\n /**\n * Get an iterator over the entries in this map.\n * @returns The iterator\n */\n public entries(): IterableIterator<[string, any]> {\n const localEntriesIterator = this.data.entries();\n const iterator = {\n next(): IteratorResult<[string, any]> {\n const nextVal = localEntriesIterator.next();\n if (nextVal.done) {\n return { value: undefined, done: true };\n } else {\n // Unpack the stored value\n return { value: [nextVal.value[0], nextVal.value[1].value], done: false };\n }\n },\n [Symbol.iterator]() {\n return this;\n },\n };\n return iterator;\n }\n\n /**\n * Get an iterator over the values in this map.\n * @returns The iterator\n */\n public values(): IterableIterator<any> {\n const localValuesIterator = this.data.values();\n const iterator = {\n next(): IteratorResult<any> {\n const nextVal = localValuesIterator.next();\n if (nextVal.done) {\n return { value: undefined, done: true };\n } else {\n // Unpack the stored value\n return { value: nextVal.value.value, done: false };\n }\n },\n [Symbol.iterator]() {\n return this;\n },\n };\n return iterator;\n }\n\n /**\n * Get an iterator over the entries in this map.\n * @returns The iterator\n */\n public [Symbol.iterator](): IterableIterator<[string, any]> {\n return this.entries();\n }\n\n /**\n * Executes the given callback on each entry in the map.\n * @param callbackFn - Callback function\n */\n public forEach(callbackFn: (value: any, key: string, map: Map<string, any>) => void): void {\n this.data.forEach((localValue, key, m) => {\n callbackFn(localValue.value, key, m);\n });\n }\n\n /**\n * {@inheritDoc ISharedMap.get}\n */\n public get<T = any>(key: string): T | undefined {\n if (!this.data.has(key)) {\n return undefined;\n }\n\n // eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n const localValue = this.data.get(key)!;\n\n return localValue.value as T;\n }\n\n /**\n * Check if a key exists in the map.\n * @param key - The key to check\n * @returns True if the key exists, false otherwise\n */\n public has(key: string): boolean {\n return this.data.has(key);\n }\n\n /**\n * {@inheritDoc ISharedMap.set}\n */\n public set(key: string, value: any) {\n // Undefined/null keys can't be serialized to JSON in the manner we currently snapshot.\n if (key === undefined || key === null) {\n throw new Error(\"Undefined and null keys are not supported\");\n }\n\n // Create a local value and serialize it.\n const localValue = this.localValueMaker.fromInMemory(value);\n const serializableValue = makeSerializable(\n localValue,\n this.serializer,\n this.handle);\n\n // Set the value locally.\n this.setCore(\n key,\n localValue,\n true,\n );\n\n // If we are not attached, don't submit the op.\n if (!this.isAttached()) {\n return;\n }\n\n const op: IMapSetOperation = {\n key,\n type: \"set\",\n value: serializableValue,\n };\n this.submitMapKeyMessage(op);\n }\n\n /**\n * Delete a key from the map.\n * @param key - Key to delete\n * @returns True if the key existed and was deleted, false if it did not exist\n */\n public delete(key: string): boolean {\n // Delete the key locally first.\n const successfullyRemoved = this.deleteCore(key, true);\n\n // If we are not attached, don't submit the op.\n if (!this.isAttached()) {\n return successfullyRemoved;\n }\n\n const op: IMapDeleteOperation = {\n key,\n type: \"delete\",\n };\n this.submitMapKeyMessage(op);\n\n return successfullyRemoved;\n }\n\n /**\n * Clear all data from the map.\n */\n public clear(): void {\n // Clear the data locally first.\n this.clearCore(true);\n\n // If we are not attached, don't submit the op.\n if (!this.isAttached()) {\n return;\n }\n\n const op: IMapClearOperation = {\n type: \"clear\",\n };\n this.submitMapClearMessage(op);\n }\n\n /**\n * Serializes the data stored in the shared map to a JSON string\n * @param serializer - The serializer to use to serialize handles in its values.\n * @returns A JSON string containing serialized map data\n */\n public getSerializedStorage(serializer: IFluidSerializer): IMapDataObjectSerialized {\n const serializableMapData: IMapDataObjectSerialized = {};\n this.data.forEach((localValue, key) => {\n serializableMapData[key] = localValue.makeSerialized(serializer, this.handle);\n });\n return serializableMapData;\n }\n\n public getSerializableStorage(serializer: IFluidSerializer): IMapDataObjectSerializable {\n const serializableMapData: IMapDataObjectSerializable = {};\n this.data.forEach((localValue, key) => {\n serializableMapData[key] = makeSerializable(localValue, serializer, this.handle);\n });\n return serializableMapData;\n }\n\n public serialize(serializer: IFluidSerializer): string {\n return JSON.stringify(this.getSerializableStorage(serializer));\n }\n\n /**\n * Populate the kernel with the given map data.\n * @param data - A JSON string containing serialized map data\n */\n public populateFromSerializable(json: IMapDataObjectSerializable): void {\n for (const [key, serializable] of Object.entries(json)) {\n const localValue = {\n key,\n value: this.makeLocal(key, serializable),\n };\n\n this.data.set(localValue.key, localValue.value);\n }\n }\n\n public populate(json: string): void {\n this.populateFromSerializable(JSON.parse(json) as IMapDataObjectSerializable);\n }\n\n /**\n * Submit the given op if a handler is registered.\n * @param op - The operation to attempt to submit\n * @param localOpMetadata - The local metadata associated with the op. This is kept locally by the runtime\n * and not sent to the server. This will be sent back when this message is received back from the server. This is\n * also sent if we are asked to resubmit the message.\n * @returns True if the operation was submitted, false otherwise.\n */\n public trySubmitMessage(op: any, localOpMetadata: unknown): boolean {\n const type: string = op.type;\n if (this.messageHandlers.has(type)) {\n // eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n this.messageHandlers.get(type)!.submit(op as IMapOperation, localOpMetadata);\n return true;\n }\n return false;\n }\n\n public tryGetStashedOpLocalMetadata(op: any): unknown {\n const type: string = op.type;\n if (this.messageHandlers.has(type)) {\n // eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n return this.messageHandlers.get(type)!.getStashedOpLocalMetadata(op as IMapOperation);\n }\n throw new Error(\"no apply stashed op handler\");\n }\n\n /**\n * Process the given op if a handler is registered.\n * @param message - The message to process\n * @param local - Whether the message originated from the local client\n * @param localOpMetadata - For local client messages, this is the metadata that was submitted with the message.\n * For messages from a remote client, this will be undefined.\n * @returns True if the operation was processed, false otherwise.\n */\n public tryProcessMessage(\n op: IMapOperation,\n local: boolean,\n localOpMetadata: unknown,\n ): boolean {\n if (this.messageHandlers.has(op.type)) {\n // eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n this.messageHandlers\n .get(op.type)!\n .process(op, local, localOpMetadata);\n return true;\n }\n return false;\n }\n\n /**\n * Set implementation used for both locally sourced sets as well as incoming remote sets.\n * @param key - The key being set\n * @param value - The value being set\n * @param local - Whether the message originated from the local client\n * @param op - The message if from a remote set, or null if from a local set\n */\n private setCore(key: string, value: ILocalValue, local: boolean): void {\n const previousValue = this.get(key);\n this.data.set(key, value);\n const event: IValueChanged = { key, previousValue };\n this.eventEmitter.emit(\"valueChanged\", event, local, this.eventEmitter);\n }\n\n /**\n * Clear implementation used for both locally sourced clears as well as incoming remote clears.\n * @param local - Whether the message originated from the local client\n * @param op - The message if from a remote clear, or null if from a local clear\n */\n private clearCore(local: boolean): void {\n this.data.clear();\n this.eventEmitter.emit(\"clear\", local, this.eventEmitter);\n }\n\n /**\n * Delete implementation used for both locally sourced deletes as well as incoming remote deletes.\n * @param key - The key being deleted\n * @param local - Whether the message originated from the local client\n * @param op - The message if from a remote delete, or null if from a local delete\n * @returns True if the key existed and was deleted, false if it did not exist\n */\n private deleteCore(key: string, local: boolean): boolean {\n const previousValue = this.get(key);\n const successfullyRemoved = this.data.delete(key);\n if (successfullyRemoved) {\n const event: IValueChanged = { key, previousValue };\n this.eventEmitter.emit(\"valueChanged\", event, local, this.eventEmitter);\n }\n return successfullyRemoved;\n }\n\n /**\n * Clear all keys in memory in response to a remote clear, but retain keys we have modified but not yet been ack'd.\n */\n private clearExceptPendingKeys(): void {\n // Assuming the pendingKeys is small and the map is large\n // we will get the value for the pendingKeys and clear the map\n const temp = new Map<string, ILocalValue>();\n this.pendingKeys.forEach((value, key) => {\n // eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n temp.set(key, this.data.get(key)!);\n });\n this.data.clear();\n temp.forEach((value, key) => {\n this.data.set(key, value);\n });\n }\n\n /**\n * The remote ISerializableValue we're receiving (either as a result of a load or an incoming set op) will\n * have the information we need to create a real object, but will not be the real object yet. For example,\n * 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 * job is to convert that information into a real object for local usage.\n * @param key - The key that the caller intends to store the local value into (used for ops later). But\n * doesn't actually store the local value into that key. So better not lie!\n * @param serializable - The remote information that we can convert into a real object\n * @returns The local value that was produced\n */\n private makeLocal(key: string, serializable: ISerializableValue): ILocalValue {\n if (serializable.type === ValueType[ValueType.Plain] || serializable.type === ValueType[ValueType.Shared]) {\n return this.localValueMaker.fromSerializable(serializable);\n } else {\n throw new Error(\"Unknown local value type\");\n }\n }\n\n /**\n * If our local operations that have not yet been ack'd will eventually overwrite an incoming operation, we should\n * not process the incoming operation.\n * @param op - Operation to check\n * @param local - Whether the message originated from the local client\n * @param message - The message\n * @param localOpMetadata - For local client messages, this is the metadata that was submitted with the message.\n * For messages from a remote client, this will be undefined.\n * @returns True if the operation should be processed, false otherwise\n */\n private needProcessKeyOperation(\n op: IMapKeyOperation,\n local: boolean,\n localOpMetadata: unknown,\n ): boolean {\n if (this.pendingClearMessageId !== -1) {\n if (local) {\n assert(localOpMetadata !== undefined && localOpMetadata as number < this.pendingClearMessageId,\n 0x013 /* \"Received out of order op when there is an unackd clear message\" */);\n }\n // If we have an unack'd clear, we can ignore all ops.\n return false;\n }\n\n if (this.pendingKeys.has(op.key)) {\n // Found an unack'd op. Clear it from the map if the pendingMessageId in the map matches this message's\n // and don't process the op.\n if (local) {\n assert(localOpMetadata !== undefined,\n 0x014 /* pendingMessageId is missing from the local client's operation */);\n const pendingMessageId = localOpMetadata as number;\n const pendingKeyMessageId = this.pendingKeys.get(op.key);\n if (pendingKeyMessageId === pendingMessageId) {\n this.pendingKeys.delete(op.key);\n }\n }\n return false;\n }\n\n // If we don't have a NACK op on the key, we need to process the remote ops.\n return !local;\n }\n\n /**\n * Get the message handlers for the map.\n * @returns A map of string op names to IMapMessageHandlers for those ops\n */\n private getMessageHandlers() {\n const messageHandlers = new Map<string, IMapMessageHandler>();\n messageHandlers.set(\n \"clear\",\n {\n process: (op: IMapClearOperation, local, localOpMetadata) => {\n if (local) {\n assert(localOpMetadata !== undefined,\n 0x015 /* \"pendingMessageId is missing from the local client's clear operation\" */);\n const pendingMessageId = localOpMetadata as number;\n if (this.pendingClearMessageId === pendingMessageId) {\n this.pendingClearMessageId = -1;\n }\n return;\n }\n if (this.pendingKeys.size !== 0) {\n this.clearExceptPendingKeys();\n return;\n }\n this.clearCore(local);\n },\n submit: (op: IMapClearOperation, localOpMetadata: unknown) => {\n // We don't reuse the metadata but send a new one on each submit.\n this.submitMapClearMessage(op);\n },\n getStashedOpLocalMetadata: (op: IMapClearOperation) => {\n // We don't reuse the metadata but send a new one on each submit.\n return this.getMapClearMessageLocalMetadata(op);\n },\n });\n messageHandlers.set(\n \"delete\",\n {\n process: (op: IMapDeleteOperation, local, localOpMetadata) => {\n if (!this.needProcessKeyOperation(op, local, localOpMetadata)) {\n return;\n }\n this.deleteCore(op.key, local);\n },\n submit: (op: IMapDeleteOperation, localOpMetadata: unknown) => {\n // We don't reuse the metadata but send a new one on each submit.\n this.submitMapKeyMessage(op);\n },\n getStashedOpLocalMetadata: (op: IMapDeleteOperation) => {\n // We don't reuse the metadata but send a new one on each submit.\n return this.getMapKeyMessageLocalMetadata(op);\n },\n });\n messageHandlers.set(\n \"set\",\n {\n process: (op: IMapSetOperation, local, localOpMetadata) => {\n if (!this.needProcessKeyOperation(op, local, localOpMetadata)) {\n return;\n }\n\n // needProcessKeyOperation should have returned false if local is true\n const context = this.makeLocal(op.key, op.value);\n this.setCore(op.key, context, local);\n },\n submit: (op: IMapSetOperation, localOpMetadata: unknown) => {\n // We don't reuse the metadata but send a new one on each submit.\n this.submitMapKeyMessage(op);\n },\n getStashedOpLocalMetadata: (op: IMapSetOperation) => {\n // We don't reuse the metadata but send a new one on each submit.\n return this.getMapKeyMessageLocalMetadata(op);\n },\n });\n\n return messageHandlers;\n }\n\n private getMapClearMessageLocalMetadata(op: IMapClearOperation): number {\n const pendingMessageId = ++this.pendingMessageId;\n this.pendingClearMessageId = pendingMessageId;\n return pendingMessageId;\n }\n\n /**\n * Submit a clear message to remote clients.\n * @param op - The clear message\n */\n private submitMapClearMessage(op: IMapClearOperation): void {\n const pendingMessageId = this.getMapClearMessageLocalMetadata(op);\n this.submitMessage(op, pendingMessageId);\n }\n\n private getMapKeyMessageLocalMetadata(op: IMapKeyOperation): number {\n const pendingMessageId = ++this.pendingMessageId;\n this.pendingKeys.set(op.key, pendingMessageId);\n return pendingMessageId;\n }\n\n /**\n * Submit a map key message to remote clients.\n * @param op - The map key message\n */\n private submitMapKeyMessage(op: IMapKeyOperation): void {\n const pendingMessageId = this.getMapKeyMessageLocalMetadata(op);\n this.submitMessage(op, pendingMessageId);\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"mapKernel.js","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, TypedEventEmitter } from \"@fluidframework/common-utils\";\nimport {\n ISerializableValue,\n ISerializedValue,\n ISharedMapEvents,\n} from \"./interfaces\";\nimport {\n ILocalValue,\n LocalValueMaker,\n makeSerializable,\n} from \"./localValues\";\n\n/**\n * Defines the means to process and submit a given op on a map.\n */\ninterface IMapMessageHandler {\n /**\n * Apply the given operation.\n * @param op - The map operation to apply\n * @param local - Whether the message originated from the local client\n * @param localOpMetadata - For local client messages, this is the metadata that was submitted with the message.\n * For messages from a remote client, this will be undefined.\n */\n process(\n op: IMapOperation,\n local: boolean,\n localOpMetadata: unknown,\n ): void;\n\n /**\n * Communicate the operation to remote clients.\n * @param op - The map operation to submit\n * @param localOpMetadata - The metadata to be submitted with the message.\n */\n submit(op: IMapOperation, localOpMetadata: unknown): void;\n\n getStashedOpLocalMetadata(op: IMapOperation): unknown;\n}\n\n/**\n * Operation indicating a value should be set for a key.\n */\nexport interface IMapSetOperation {\n /**\n * String identifier of the operation type.\n */\n type: \"set\";\n\n /**\n * Map key being modified.\n */\n key: string;\n\n /**\n * Value to be set on the key.\n */\n value: ISerializableValue;\n}\n\n/**\n * Operation indicating a key should be deleted from the map.\n */\nexport interface IMapDeleteOperation {\n /**\n * String identifier of the operation type.\n */\n type: \"delete\";\n\n /**\n * Map key being modified.\n */\n key: string;\n}\n\n/**\n * Map key operations are one of several types.\n */\nexport type IMapKeyOperation = IMapSetOperation | IMapDeleteOperation;\n\n/**\n * Operation indicating the map should be cleared.\n */\nexport interface IMapClearOperation {\n /**\n * String identifier of the operation type.\n */\n type: \"clear\";\n}\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 * Directly used in JSON.stringify, direct result from JSON.parse\n */\nexport interface IMapDataObjectSerializable {\n [key: string]: ISerializableValue;\n}\n\nexport interface IMapDataObjectSerialized {\n [key: string]: ISerializedValue;\n}\n\ninterface IMapKeyEditLocalOpMetadata {\n type: \"edit\";\n pendingMessageId: number;\n previousValue?: ILocalValue;\n}\n\ninterface IMapKeyAddLocalOpMetadata {\n type: \"add\";\n pendingMessageId: number;\n}\n\ninterface IMapClearLocalOpMetadata {\n type: \"clear\";\n pendingMessageId: number;\n previousMap?: Map<string, ILocalValue>;\n}\n\ntype MapKeyLocalOpMetadata = IMapKeyEditLocalOpMetadata | IMapKeyAddLocalOpMetadata;\ntype MapLocalOpMetadata = IMapClearLocalOpMetadata | MapKeyLocalOpMetadata;\n\nfunction isMapKeyLocalOpMetadata(metadata: any): metadata is MapKeyLocalOpMetadata {\n return metadata !== undefined && typeof metadata.pendingMessageId === \"number\" &&\n (metadata.type === \"add\" || metadata.type === \"edit\");\n}\n\nfunction isClearLocalOpMetadata(metadata: any): metadata is IMapClearLocalOpMetadata {\n return metadata !== undefined && metadata.type === \"clear\" && typeof metadata.pendingMessageId === \"number\";\n}\n\nfunction isMapLocalOpMetadata(metadata: any): metadata is MapLocalOpMetadata {\n return metadata !== undefined && typeof metadata.pendingMessageId === \"number\" &&\n (metadata.type === \"add\" || metadata.type === \"edit\" || metadata.type === \"clear\");\n}\n\n/**\n * A SharedMap is a map-like distributed data structure.\n */\nexport class MapKernel {\n /**\n * The number of key/value pairs stored in the map.\n */\n public get size(): number {\n return this.data.size;\n }\n\n /**\n * Mapping of op types to message handlers.\n */\n private readonly messageHandlers: ReadonlyMap<string, IMapMessageHandler> = new Map();\n\n /**\n * The in-memory data the map is storing.\n */\n private readonly data = new Map<string, ILocalValue>();\n\n /**\n * Keys that have been modified locally but not yet ack'd from the server.\n */\n private readonly pendingKeys: Map<string, number[]> = new Map();\n\n /**\n * This is used to assign a unique id to every outgoing operation and helps in tracking unack'd ops.\n */\n private pendingMessageId: number = -1;\n\n /**\n * The pending ids of any clears that have been performed locally but not yet ack'd from the server\n */\n private readonly pendingClearMessageIds: number[] = [];\n\n /**\n * Object to create encapsulations of the values stored in the map.\n */\n private readonly localValueMaker: LocalValueMaker;\n\n /**\n * Create a new shared map kernel.\n * @param serializer - The serializer to serialize / parse handles\n * @param handle - The handle of the shared object using the kernel\n * @param submitMessage - A callback to submit a message through the shared object\n * @param isAttached - To query whether the shared object should generate ops\n * @param valueTypes - The value types to register\n * @param eventEmitter - The object that will emit map events\n */\n constructor(\n private readonly serializer: IFluidSerializer,\n private readonly handle: IFluidHandle,\n private readonly submitMessage: (op: any, localOpMetadata: unknown) => void,\n private readonly isAttached: () => boolean,\n private readonly eventEmitter: TypedEventEmitter<ISharedMapEvents>,\n ) {\n this.localValueMaker = new LocalValueMaker(serializer);\n this.messageHandlers = this.getMessageHandlers();\n }\n\n /**\n * Get an iterator over the keys in this map.\n * @returns The iterator\n */\n public keys(): IterableIterator<string> {\n return this.data.keys();\n }\n\n /**\n * Get an iterator over the entries in this map.\n * @returns The iterator\n */\n public entries(): IterableIterator<[string, any]> {\n const localEntriesIterator = this.data.entries();\n const iterator = {\n next(): IteratorResult<[string, any]> {\n const nextVal = localEntriesIterator.next();\n if (nextVal.done) {\n return { value: undefined, done: true };\n } else {\n // Unpack the stored value\n return { value: [nextVal.value[0], nextVal.value[1].value], done: false };\n }\n },\n [Symbol.iterator]() {\n return this;\n },\n };\n return iterator;\n }\n\n /**\n * Get an iterator over the values in this map.\n * @returns The iterator\n */\n public values(): IterableIterator<any> {\n const localValuesIterator = this.data.values();\n const iterator = {\n next(): IteratorResult<any> {\n const nextVal = localValuesIterator.next();\n if (nextVal.done) {\n return { value: undefined, done: true };\n } else {\n // Unpack the stored value\n return { value: nextVal.value.value, done: false };\n }\n },\n [Symbol.iterator]() {\n return this;\n },\n };\n return iterator;\n }\n\n /**\n * Get an iterator over the entries in this map.\n * @returns The iterator\n */\n public [Symbol.iterator](): IterableIterator<[string, any]> {\n return this.entries();\n }\n\n /**\n * Executes the given callback on each entry in the map.\n * @param callbackFn - Callback function\n */\n public forEach(callbackFn: (value: any, key: string, map: Map<string, any>) => void): void {\n this.data.forEach((localValue, key, m) => {\n callbackFn(localValue.value, key, m);\n });\n }\n\n /**\n * {@inheritDoc ISharedMap.get}\n */\n public get<T = any>(key: string): T | undefined {\n const localValue = this.data.get(key);\n return localValue === undefined ? undefined : localValue.value as T;\n }\n\n /**\n * Check if a key exists in the map.\n * @param key - The key to check\n * @returns True if the key exists, false otherwise\n */\n public has(key: string): boolean {\n return this.data.has(key);\n }\n\n /**\n * {@inheritDoc ISharedMap.set}\n */\n public set(key: string, value: any) {\n // Undefined/null keys can't be serialized to JSON in the manner we currently snapshot.\n if (key === undefined || key === null) {\n throw new Error(\"Undefined and null keys are not supported\");\n }\n\n // Create a local value and serialize it.\n const localValue = this.localValueMaker.fromInMemory(value);\n const serializableValue = makeSerializable(\n localValue,\n this.serializer,\n this.handle);\n\n // Set the value locally.\n const previousValue = this.setCore(\n key,\n localValue,\n true,\n );\n\n // If we are not attached, don't submit the op.\n if (!this.isAttached()) {\n return;\n }\n\n const op: IMapSetOperation = {\n key,\n type: \"set\",\n value: serializableValue,\n };\n this.submitMapKeyMessage(op, previousValue);\n }\n\n /**\n * Delete a key from the map.\n * @param key - Key to delete\n * @returns True if the key existed and was deleted, false if it did not exist\n */\n public delete(key: string): boolean {\n // Delete the key locally first.\n const previousValue = this.deleteCore(key, true);\n\n // If we are not attached, don't submit the op.\n if (!this.isAttached()) {\n return previousValue !== undefined;\n }\n\n const op: IMapDeleteOperation = {\n key,\n type: \"delete\",\n };\n this.submitMapKeyMessage(op, previousValue);\n\n return previousValue !== undefined;\n }\n\n /**\n * Clear all data from the map.\n */\n public clear(): void {\n const copy = this.isAttached() ? new Map<string, ILocalValue>(this.data) : undefined;\n\n // Clear the data locally first.\n this.clearCore(true);\n\n // If we are not attached, don't submit the op.\n if (!this.isAttached()) {\n return;\n }\n\n const op: IMapClearOperation = {\n type: \"clear\",\n };\n this.submitMapClearMessage(op, copy);\n }\n\n /**\n * Serializes the data stored in the shared map to a JSON string\n * @param serializer - The serializer to use to serialize handles in its values.\n * @returns A JSON string containing serialized map data\n */\n public getSerializedStorage(serializer: IFluidSerializer): IMapDataObjectSerialized {\n const serializableMapData: IMapDataObjectSerialized = {};\n this.data.forEach((localValue, key) => {\n serializableMapData[key] = localValue.makeSerialized(serializer, this.handle);\n });\n return serializableMapData;\n }\n\n public getSerializableStorage(serializer: IFluidSerializer): IMapDataObjectSerializable {\n const serializableMapData: IMapDataObjectSerializable = {};\n this.data.forEach((localValue, key) => {\n serializableMapData[key] = makeSerializable(localValue, serializer, this.handle);\n });\n return serializableMapData;\n }\n\n public serialize(serializer: IFluidSerializer): string {\n return JSON.stringify(this.getSerializableStorage(serializer));\n }\n\n /**\n * Populate the kernel with the given map data.\n * @param data - A JSON string containing serialized map data\n */\n public populateFromSerializable(json: IMapDataObjectSerializable): void {\n for (const [key, serializable] of Object.entries(json)) {\n const localValue = {\n key,\n value: this.makeLocal(key, serializable),\n };\n\n this.data.set(localValue.key, localValue.value);\n }\n }\n\n public populate(json: string): void {\n this.populateFromSerializable(JSON.parse(json) as IMapDataObjectSerializable);\n }\n\n /**\n * Submit the given op if a handler is registered.\n * @param op - The operation to attempt to submit\n * @param localOpMetadata - The local metadata associated with the op. This is kept locally by the runtime\n * and not sent to the server. This will be sent back when this message is received back from the server. This is\n * also sent if we are asked to resubmit the message.\n * @returns True if the operation was submitted, false otherwise.\n */\n public trySubmitMessage(op: any, localOpMetadata: unknown): boolean {\n const handler = this.messageHandlers.get(op.type);\n if (handler === undefined) {\n return false;\n }\n handler.submit(op as IMapOperation, localOpMetadata);\n return true;\n }\n\n public tryGetStashedOpLocalMetadata(op: any): unknown {\n const handler = this.messageHandlers.get(op.type);\n if (handler === undefined) {\n throw new Error(\"no apply stashed op handler\");\n }\n return handler.getStashedOpLocalMetadata(op as IMapOperation);\n }\n\n /**\n * Process the given op if a handler is registered.\n * @param op - The message to process\n * @param local - Whether the message originated from the local client\n * @param localOpMetadata - For local client messages, this is the metadata that was submitted with the message.\n * For messages from a remote client, this will be undefined.\n * @returns True if the operation was processed, false otherwise.\n */\n public tryProcessMessage(\n op: IMapOperation,\n local: boolean,\n localOpMetadata: unknown,\n ): boolean {\n const handler = this.messageHandlers.get(op.type);\n if (handler === undefined) {\n return false;\n }\n handler.process(op, local, localOpMetadata);\n return true;\n }\n\n /**\n * Rollback a local op\n * @param op - The operation to rollback\n * @param localOpMetadata - The local metadata associated with the op.\n */\n public rollback(op: any, localOpMetadata: unknown) {\n if (!isMapLocalOpMetadata(localOpMetadata)) {\n throw new Error(\"Invalid localOpMetadata\");\n }\n\n if (op.type === \"clear\" && localOpMetadata.type === \"clear\") {\n if (localOpMetadata.previousMap === undefined) {\n throw new Error(\"Cannot rollback without previous map\");\n }\n localOpMetadata.previousMap.forEach((localValue, key) => {\n this.setCore(key, localValue, true);\n });\n\n const lastPendingClearId = this.pendingClearMessageIds.pop();\n if (lastPendingClearId === undefined || lastPendingClearId !== localOpMetadata.pendingMessageId) {\n throw new Error(\"Rollback op does match last clear\");\n }\n } else if (op.type === \"delete\" || op.type === \"set\") {\n if (localOpMetadata.type === \"add\") {\n this.deleteCore(op.key, true);\n } else if (localOpMetadata.type === \"edit\" && localOpMetadata.previousValue !== undefined) {\n this.setCore(op.key, localOpMetadata.previousValue, true);\n } else {\n throw new Error(\"Cannot rollback without previous value\");\n }\n\n const pendingMessageIds = this.pendingKeys.get(op.key);\n const lastPendingMessageId = pendingMessageIds?.pop();\n if (!pendingMessageIds || lastPendingMessageId !== localOpMetadata.pendingMessageId) {\n throw new Error(\"Rollback op does not match last pending\");\n }\n if (pendingMessageIds.length === 0) {\n this.pendingKeys.delete(op.key);\n }\n } else {\n throw new Error(\"Unsupported op for rollback\");\n }\n }\n\n /**\n * Set implementation used for both locally sourced sets as well as incoming remote sets.\n * @param key - The key being set\n * @param value - The value being set\n * @param local - Whether the message originated from the local client\n * @returns Previous local value of the key, if any\n */\n private setCore(key: string, value: ILocalValue, local: boolean): ILocalValue | undefined {\n const previousLocalValue = this.data.get(key);\n const previousValue = previousLocalValue?.value;\n this.data.set(key, value);\n this.eventEmitter.emit(\"valueChanged\", { key, previousValue }, local, this.eventEmitter);\n return previousLocalValue;\n }\n\n /**\n * Clear implementation used for both locally sourced clears as well as incoming remote clears.\n * @param local - Whether the message originated from the local client\n */\n private clearCore(local: boolean): void {\n this.data.clear();\n this.eventEmitter.emit(\"clear\", local, this.eventEmitter);\n }\n\n /**\n * Delete implementation used for both locally sourced deletes as well as incoming remote deletes.\n * @param key - The key being deleted\n * @param local - Whether the message originated from the local client\n * @returns Previous local value of the key if it existed, undefined if it did not exist\n */\n private deleteCore(key: string, local: boolean): ILocalValue | undefined {\n const previousLocalValue = this.data.get(key);\n const previousValue = previousLocalValue?.value;\n const successfullyRemoved = this.data.delete(key);\n if (successfullyRemoved) {\n this.eventEmitter.emit(\"valueChanged\", { key, previousValue }, local, this.eventEmitter);\n }\n return previousLocalValue;\n }\n\n /**\n * Clear all keys in memory in response to a remote clear, but retain keys we have modified but not yet been ack'd.\n */\n private clearExceptPendingKeys(): void {\n // Assuming the pendingKeys is small and the map is large\n // we will get the value for the pendingKeys and clear the map\n const temp = new Map<string, ILocalValue>();\n this.pendingKeys.forEach((value, key) => {\n // eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n temp.set(key, this.data.get(key)!);\n });\n this.clearCore(false);\n temp.forEach((value, key) => {\n this.setCore(key, value, true);\n });\n }\n\n /**\n * The remote ISerializableValue we're receiving (either as a result of a load or an incoming set op) will\n * have the information we need to create a real object, but will not be the real object yet. For example,\n * 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 * job is to convert that information into a real object for local usage.\n * @param key - The key that the caller intends to store the local value into (used for ops later). But\n * doesn't actually store the local value into that key. So better not lie!\n * @param serializable - The remote information that we can convert into a real object\n * @returns The local value that was produced\n */\n private makeLocal(key: string, serializable: ISerializableValue): ILocalValue {\n if (serializable.type === ValueType[ValueType.Plain] || serializable.type === ValueType[ValueType.Shared]) {\n return this.localValueMaker.fromSerializable(serializable);\n } else {\n throw new Error(\"Unknown local value type\");\n }\n }\n\n /**\n * If our local operations that have not yet been ack'd will eventually overwrite an incoming operation, we should\n * not process the incoming operation.\n * @param op - Operation to check\n * @param local - Whether the message originated from the local client\n * @param localOpMetadata - For local client messages, this is the metadata that was submitted with the message.\n * For messages from a remote client, this will be undefined.\n * @returns True if the operation should be processed, false otherwise\n */\n private needProcessKeyOperation(\n op: IMapKeyOperation,\n local: boolean,\n localOpMetadata: unknown,\n ): boolean {\n if (this.pendingClearMessageIds.length > 0) {\n if (local) {\n assert(localOpMetadata !== undefined && isMapKeyLocalOpMetadata(localOpMetadata) &&\n localOpMetadata.pendingMessageId < this.pendingClearMessageIds[0],\n 0x013 /* \"Received out of order op when there is an unackd clear message\" */);\n }\n // If we have an unack'd clear, we can ignore all ops.\n return false;\n }\n\n const pendingKeyMessageId = this.pendingKeys.get(op.key);\n if (pendingKeyMessageId !== undefined) {\n // Found an unack'd op. Clear it from the map if the pendingMessageId in the map matches this message's\n // and don't process the op.\n if (local) {\n assert(localOpMetadata !== undefined && isMapKeyLocalOpMetadata(localOpMetadata),\n 0x014 /* pendingMessageId is missing from the local client's operation */);\n const pendingMessageIds = this.pendingKeys.get(op.key);\n assert(pendingMessageIds !== undefined && pendingMessageIds[0] === localOpMetadata.pendingMessageId,\n 0x2fa /* Unexpected pending message received */);\n pendingMessageIds.shift();\n if (pendingMessageIds.length === 0) {\n this.pendingKeys.delete(op.key);\n }\n }\n return false;\n }\n\n // If we don't have a NACK op on the key, we need to process the remote ops.\n return !local;\n }\n\n /**\n * Get the message handlers for the map.\n * @returns A map of string op names to IMapMessageHandlers for those ops\n */\n private getMessageHandlers() {\n const messageHandlers = new Map<string, IMapMessageHandler>();\n messageHandlers.set(\n \"clear\",\n {\n process: (op: IMapClearOperation, local, localOpMetadata) => {\n if (local) {\n assert(isClearLocalOpMetadata(localOpMetadata),\n 0x015 /* \"pendingMessageId is missing from the local client's clear operation\" */);\n const pendingClearMessageId = this.pendingClearMessageIds.shift();\n assert(pendingClearMessageId === localOpMetadata.pendingMessageId,\n 0x2fb /* pendingMessageId does not match */);\n return;\n }\n if (this.pendingKeys.size !== 0) {\n this.clearExceptPendingKeys();\n return;\n }\n this.clearCore(local);\n },\n submit: (op: IMapClearOperation, localOpMetadata: unknown) => {\n assert(isClearLocalOpMetadata(localOpMetadata), 0x2fc /* Invalid localOpMetadata for clear */);\n // We don't reuse the metadata pendingMessageId but send a new one on each submit.\n const pendingClearMessageId = this.pendingClearMessageIds.shift();\n assert(pendingClearMessageId === localOpMetadata.pendingMessageId,\n 0x2fd /* pendingMessageId does not match */);\n this.submitMapClearMessage(op, localOpMetadata.previousMap);\n },\n getStashedOpLocalMetadata: (op: IMapClearOperation) => {\n // We don't reuse the metadata pendingMessageId but send a new one on each submit.\n return { type: \"clear\", pendingMessageId: this.getMapClearMessageId() };\n },\n });\n messageHandlers.set(\n \"delete\",\n {\n process: (op: IMapDeleteOperation, local, localOpMetadata) => {\n if (!this.needProcessKeyOperation(op, local, localOpMetadata)) {\n return;\n }\n this.deleteCore(op.key, local);\n },\n submit: (op: IMapDeleteOperation, localOpMetadata: unknown) => {\n this.resubmitMapKeyMessage(op, localOpMetadata);\n },\n getStashedOpLocalMetadata: (op: IMapDeleteOperation) => {\n // We don't reuse the metadata pendingMessageId but send a new one on each submit.\n return { type: \"edit\", pendingMessageId: this.getMapKeyMessageId(op) };\n },\n });\n messageHandlers.set(\n \"set\",\n {\n process: (op: IMapSetOperation, local, localOpMetadata) => {\n if (!this.needProcessKeyOperation(op, local, localOpMetadata)) {\n return;\n }\n\n // needProcessKeyOperation should have returned false if local is true\n const context = this.makeLocal(op.key, op.value);\n this.setCore(op.key, context, local);\n },\n submit: (op: IMapSetOperation, localOpMetadata: unknown) => {\n this.resubmitMapKeyMessage(op, localOpMetadata);\n },\n getStashedOpLocalMetadata: (op: IMapSetOperation) => {\n // We don't reuse the metadata pendingMessageId but send a new one on each submit.\n return { type: \"edit\", pendingMessageId: this.getMapKeyMessageId(op) };\n },\n });\n\n return messageHandlers;\n }\n\n private getMapClearMessageId(): number {\n const pendingMessageId = ++this.pendingMessageId;\n this.pendingClearMessageIds.push(pendingMessageId);\n return pendingMessageId;\n }\n\n /**\n * Submit a clear message to remote clients.\n * @param op - The clear message\n */\n private submitMapClearMessage(op: IMapClearOperation, previousMap?: Map<string, ILocalValue>): void {\n const metadata = { type: \"clear\", pendingMessageId: this.getMapClearMessageId(), previousMap };\n this.submitMessage(op, metadata);\n }\n\n private getMapKeyMessageId(op: IMapKeyOperation): number {\n const pendingMessageId = ++this.pendingMessageId;\n const pendingMessageIds = this.pendingKeys.get(op.key);\n if (pendingMessageIds !== undefined) {\n pendingMessageIds.push(pendingMessageId);\n } else {\n this.pendingKeys.set(op.key, [pendingMessageId]);\n }\n return pendingMessageId;\n }\n\n /**\n * Submit a map key message to remote clients.\n * @param op - The map key message\n * @param previousValue - The value of the key before this op\n */\n private submitMapKeyMessage(op: IMapKeyOperation, previousValue?: ILocalValue): void {\n const pendingMessageId = this.getMapKeyMessageId(op);\n const localMetadata = previousValue ?\n { type: \"edit\", pendingMessageId, previousValue } :\n { type: \"add\", pendingMessageId };\n this.submitMessage(op, localMetadata);\n }\n\n /**\n * Submit a map key message to remote clients based on a previous submit.\n * @param op - The map key message\n * @param localOpMetadata - Metadata from the previous submit\n */\n private resubmitMapKeyMessage(op: IMapKeyOperation, localOpMetadata: unknown): void {\n assert(isMapKeyLocalOpMetadata(localOpMetadata), 0x2fe /* Invalid localOpMetadata in submit */);\n\n // clear the old pending message id\n const pendingMessageIds = this.pendingKeys.get(op.key);\n assert(pendingMessageIds !== undefined && pendingMessageIds[0] === localOpMetadata.pendingMessageId,\n 0x2ff /* Unexpected pending message received */);\n pendingMessageIds.shift();\n if (pendingMessageIds.length === 0) {\n this.pendingKeys.delete(op.key);\n }\n\n // We don't reuse the metadata pendingMessageId but send a new one on each submit.\n const pendingMessageId = this.getMapKeyMessageId(op);\n const localMetadata = localOpMetadata.type === \"edit\" ?\n { type: \"edit\", pendingMessageId, previousValue: localOpMetadata.previousValue } :\n { type: \"add\", pendingMessageId };\n this.submitMessage(op, localMetadata);\n }\n}\n"]}
|
package/lib/packageVersion.d.ts
CHANGED
package/lib/packageVersion.js
CHANGED