@fluidframework/sequence 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.
Files changed (65) hide show
  1. package/README.md +18 -6
  2. package/dist/defaultMap.d.ts +2 -6
  3. package/dist/defaultMap.d.ts.map +1 -1
  4. package/dist/defaultMap.js +27 -37
  5. package/dist/defaultMap.js.map +1 -1
  6. package/dist/defaultMapInterfaces.d.ts +24 -3
  7. package/dist/defaultMapInterfaces.d.ts.map +1 -1
  8. package/dist/defaultMapInterfaces.js.map +1 -1
  9. package/dist/index.d.ts +2 -2
  10. package/dist/index.d.ts.map +1 -1
  11. package/dist/index.js.map +1 -1
  12. package/dist/intervalCollection.d.ts +72 -8
  13. package/dist/intervalCollection.d.ts.map +1 -1
  14. package/dist/intervalCollection.js +325 -155
  15. package/dist/intervalCollection.js.map +1 -1
  16. package/dist/packageVersion.d.ts +1 -1
  17. package/dist/packageVersion.js +1 -1
  18. package/dist/packageVersion.js.map +1 -1
  19. package/dist/sequence.d.ts +4 -5
  20. package/dist/sequence.d.ts.map +1 -1
  21. package/dist/sequence.js +11 -15
  22. package/dist/sequence.js.map +1 -1
  23. package/dist/sharedIntervalCollection.d.ts.map +1 -1
  24. package/dist/sharedIntervalCollection.js +1 -1
  25. package/dist/sharedIntervalCollection.js.map +1 -1
  26. package/dist/sharedSequence.js.map +1 -1
  27. package/dist/sparsematrix.js +2 -2
  28. package/dist/sparsematrix.js.map +1 -1
  29. package/lib/defaultMap.d.ts +2 -6
  30. package/lib/defaultMap.d.ts.map +1 -1
  31. package/lib/defaultMap.js +27 -37
  32. package/lib/defaultMap.js.map +1 -1
  33. package/lib/defaultMapInterfaces.d.ts +24 -3
  34. package/lib/defaultMapInterfaces.d.ts.map +1 -1
  35. package/lib/defaultMapInterfaces.js.map +1 -1
  36. package/lib/index.d.ts +2 -2
  37. package/lib/index.d.ts.map +1 -1
  38. package/lib/index.js.map +1 -1
  39. package/lib/intervalCollection.d.ts +72 -8
  40. package/lib/intervalCollection.d.ts.map +1 -1
  41. package/lib/intervalCollection.js +325 -155
  42. package/lib/intervalCollection.js.map +1 -1
  43. package/lib/packageVersion.d.ts +1 -1
  44. package/lib/packageVersion.js +1 -1
  45. package/lib/packageVersion.js.map +1 -1
  46. package/lib/sequence.d.ts +4 -5
  47. package/lib/sequence.d.ts.map +1 -1
  48. package/lib/sequence.js +11 -15
  49. package/lib/sequence.js.map +1 -1
  50. package/lib/sharedIntervalCollection.d.ts.map +1 -1
  51. package/lib/sharedIntervalCollection.js +1 -1
  52. package/lib/sharedIntervalCollection.js.map +1 -1
  53. package/lib/sharedSequence.js.map +1 -1
  54. package/lib/sparsematrix.js +2 -2
  55. package/lib/sparsematrix.js.map +1 -1
  56. package/package.json +20 -44
  57. package/src/defaultMap.ts +39 -41
  58. package/src/defaultMapInterfaces.ts +28 -3
  59. package/src/index.ts +3 -0
  60. package/src/intervalCollection.ts +447 -181
  61. package/src/packageVersion.ts +1 -1
  62. package/src/sequence.ts +17 -21
  63. package/src/sharedIntervalCollection.ts +3 -2
  64. package/src/sharedSequence.ts +1 -1
  65. package/src/sparsematrix.ts +2 -2
package/src/defaultMap.ts CHANGED
@@ -24,6 +24,7 @@ import {
24
24
  IValueType,
25
25
  IValueTypeOperationValue,
26
26
  ISharedDefaultMapEvents,
27
+ IMapMessageLocalMetadata,
27
28
  } from "./defaultMapInterfaces";
28
29
 
29
30
  /**
@@ -49,13 +50,11 @@ interface IMapMessageHandler {
49
50
  * Communicate the operation to remote clients.
50
51
  * @param op - The map operation to submit
51
52
  */
52
- submit(op: IMapOperation): void;
53
+ submit(op: IMapOperation, localOpMetadata: IMapMessageLocalMetadata): void;
53
54
 
54
- getStashedOpLocalMetadata(op: IMapOperation): unknown;
55
- }
55
+ resubmit(op: IMapOperation, localOpMetadata: IMapMessageLocalMetadata): void;
56
56
 
57
- export interface IMapMessageLocalMetadata {
58
- lastProcessedSeq: number;
57
+ getStashedOpLocalMetadata(op: IMapOperation): unknown;
59
58
  }
60
59
 
61
60
  /**
@@ -121,8 +120,6 @@ export class DefaultMap<T> {
121
120
  */
122
121
  private readonly data = new Map<string, ValueTypeLocalValue<T>>();
123
122
 
124
- private lastProcessedSeq: number = -1;
125
-
126
123
  /**
127
124
  * Create a new default map.
128
125
  * @param serializer - The serializer to serialize / parse handles
@@ -158,12 +155,9 @@ export class DefaultMap<T> {
158
155
  const iterator = {
159
156
  next(): IteratorResult<[string, any]> {
160
157
  const nextVal = localEntriesIterator.next();
161
- if (nextVal.done) {
162
- return { value: undefined, done: true };
163
- } else {
164
- // Unpack the stored value
165
- return { value: [nextVal.value[0], nextVal.value[1].value], done: false };
166
- }
158
+ return nextVal.done
159
+ ? { value: undefined, done: true }
160
+ : { value: [nextVal.value[0], nextVal.value[1].value], done: false }; // Unpack the stored value
167
161
  },
168
162
  [Symbol.iterator]() {
169
163
  return this;
@@ -181,12 +175,9 @@ export class DefaultMap<T> {
181
175
  const iterator = {
182
176
  next(): IteratorResult<any> {
183
177
  const nextVal = localValuesIterator.next();
184
- if (nextVal.done) {
185
- return { value: undefined, done: true };
186
- } else {
187
- // Unpack the stored value
188
- return { value: nextVal.value.value, done: false };
189
- }
178
+ return nextVal.done
179
+ ? { value: undefined, done: true }
180
+ : { value: nextVal.value.value, done: false }; // Unpack the stored value
190
181
  },
191
182
  [Symbol.iterator]() {
192
183
  return this;
@@ -217,10 +208,8 @@ export class DefaultMap<T> {
217
208
  * {@inheritDoc ISharedMap.get}
218
209
  */
219
210
  public get(key: string): T {
220
- let localValue = this.data.get(key);
221
- if (!this.data.has(key)) {
222
- localValue = this.createCore(key, true);
223
- }
211
+ const localValue = this.data.get(key) ?? this.createCore(key, true);
212
+
224
213
  return localValue.value;
225
214
  }
226
215
 
@@ -271,8 +260,14 @@ export class DefaultMap<T> {
271
260
  continue;
272
261
  }
273
262
 
263
+ // Back-compat: Sequence previously arbitrarily prefixed all interval collection keys with
264
+ // "intervalCollections/". This would burden users trying to iterate the collection and
265
+ // access its value, as well as those trying to match a create message to its underlying
266
+ // collection. See https://github.com/microsoft/FluidFramework/issues/10557 for more context.
267
+ const normalizedKey = key.startsWith("intervalCollections/") ? key.substring(20) : key;
268
+
274
269
  const localValue = {
275
- key,
270
+ key: normalizedKey,
276
271
  value: this.makeLocal(key, serializable),
277
272
  };
278
273
 
@@ -292,17 +287,11 @@ export class DefaultMap<T> {
292
287
  * also sent if we are asked to resubmit the message.
293
288
  * @returns True if the operation was submitted, false otherwise.
294
289
  */
295
- public trySubmitMessage(op: any, localOpMetadata: IMapMessageLocalMetadata): boolean {
290
+ public tryResubmitMessage(op: any, localOpMetadata: IMapMessageLocalMetadata): boolean {
296
291
  const type: string = op.type;
297
292
  const handler = this.messageHandlers.get(type);
298
293
  if (handler !== undefined) {
299
- const mapLocalMetadata: Partial<IMapMessageLocalMetadata> = localOpMetadata;
300
- // we don't know how to rebase these operations, so if any other op has come in
301
- // we will fail.
302
- if (this.lastProcessedSeq !== mapLocalMetadata?.lastProcessedSeq) {
303
- throw new Error("SharedInterval does not support reconnect in presence of external changes");
304
- }
305
- handler.submit(op as IMapOperation);
294
+ handler.resubmit(op as IMapOperation, localOpMetadata);
306
295
  return true;
307
296
  }
308
297
  return false;
@@ -331,9 +320,6 @@ export class DefaultMap<T> {
331
320
  message: ISequencedDocumentMessage | undefined,
332
321
  localOpMetadata: unknown,
333
322
  ): boolean {
334
- // track the seq of every incoming message, so we can detect if any
335
- // changes happened during a resubmit
336
- this.lastProcessedSeq = message.sequenceNumber;
337
323
  const handler = this.messageHandlers.get(op.type);
338
324
  if (handler !== undefined) {
339
325
  handler.process(op, local, message, localOpMetadata as IMapMessageLocalMetadata);
@@ -402,14 +388,26 @@ export class DefaultMap<T> {
402
388
  const translatedValue = parseHandles(
403
389
  op.value.value,
404
390
  this.serializer);
405
- handler.process(previousValue, translatedValue, local, message);
391
+ handler.process(previousValue, translatedValue, local, message, localOpMetadata);
406
392
  const event: IValueChanged = { key: op.key, previousValue };
407
393
  this.eventEmitter.emit("valueChanged", event, local, message, this.eventEmitter);
408
394
  },
409
- submit: (op: IMapValueTypeOperation) => {
395
+ submit: (op: IMapValueTypeOperation, localOpMetadata: IMapMessageLocalMetadata) => {
410
396
  this.submitMessage(
411
397
  op,
412
- { lastProcessedSeq: this.lastProcessedSeq },
398
+ localOpMetadata,
399
+ );
400
+ },
401
+ resubmit: (op: IMapValueTypeOperation, localOpMetadata: IMapMessageLocalMetadata) => {
402
+ const localValue = this.data.get(op.key);
403
+ const handler = localValue.getOpHandler(op.value.opName);
404
+ const {
405
+ rebasedOp,
406
+ rebasedLocalOpMetadata,
407
+ } = handler.rebase(localValue.value, op.value, localOpMetadata);
408
+ this.submitMessage(
409
+ { ...op, value: rebasedOp },
410
+ rebasedLocalOpMetadata,
413
411
  );
414
412
  },
415
413
  getStashedOpLocalMetadata: (op: IMapValueTypeOperation) => {
@@ -427,7 +425,7 @@ export class DefaultMap<T> {
427
425
  * @returns A value op emitter for the given key
428
426
  */
429
427
  private makeMapValueOpEmitter(key: string): IValueOpEmitter {
430
- const emit = (opName: string, previousValue: any, params: any) => {
428
+ const emit = (opName: string, previousValue: any, params: any, localOpMetadata: IMapMessageLocalMetadata) => {
431
429
  const translatedParams = makeHandlesSerializable(
432
430
  params,
433
431
  this.serializer,
@@ -441,8 +439,8 @@ export class DefaultMap<T> {
441
439
  value: translatedParams,
442
440
  },
443
441
  };
444
- // Send the localOpMetadata as undefined because we don't care about the ack.
445
- this.submitMessage(op, { lastProcessedSeq: this.lastProcessedSeq });
442
+
443
+ this.submitMessage(op, localOpMetadata);
446
444
 
447
445
  const event: IValueChanged = { key, previousValue };
448
446
  this.eventEmitter.emit("valueChanged", event, true, null, this.eventEmitter);
@@ -24,6 +24,7 @@ export interface IValueChanged {
24
24
 
25
25
  /**
26
26
  * Value types are given an IValueOpEmitter to emit their ops through the container type that holds them.
27
+ * @internal
27
28
  */
28
29
  export interface IValueOpEmitter {
29
30
  /**
@@ -31,9 +32,17 @@ export interface IValueOpEmitter {
31
32
  * @param opName - Name of the emitted operation
32
33
  * @param previousValue - JSONable previous value as defined by the value type
33
34
  * @param params - JSONable params for the operation as defined by the value type
34
- * @alpha
35
+ * @param localOpMetadata - JSONable local metadata which should be submitted with the op
36
+ * @internal
35
37
  */
36
- emit(opName: string, previousValue: any, params: any): void;
38
+ emit(opName: string, previousValue: any, params: any, localOpMetadata: IMapMessageLocalMetadata): void;
39
+ }
40
+
41
+ /**
42
+ * @internal
43
+ */
44
+ export interface IMapMessageLocalMetadata {
45
+ localSeq: number;
37
46
  }
38
47
 
39
48
  /**
@@ -72,14 +81,30 @@ export interface IValueOperation<T> {
72
81
  * @param params - The params on the incoming operation
73
82
  * @param local - Whether the operation originated from this client
74
83
  * @param message - The operation itself
84
+ * @param localOpMetadata - any local metadata submitted by `IValueOpEmitter.emit`.
75
85
  * @alpha
76
86
  */
77
87
  process(
78
88
  value: T,
79
89
  params: any,
80
90
  local: boolean,
81
- message: ISequencedDocumentMessage | undefined
91
+ message: ISequencedDocumentMessage | undefined,
92
+ localOpMetadata: IMapMessageLocalMetadata | undefined
82
93
  );
94
+
95
+ /**
96
+ * Rebases an `op` on `value` from its original perspective (ref/local seq) to the current
97
+ * perspective. Should be invoked on reconnection.
98
+ * @param value - The current value stored at the given key, which should be the value type.
99
+ * @param op - The op to be rebased.
100
+ * @param localOpMetadata - Any local metadata that was originally submitted with the op.
101
+ * @returns A rebased version of the op and any local metadata that should be submitted with it.
102
+ */
103
+ rebase(
104
+ value: T,
105
+ op: IValueTypeOperationValue,
106
+ localOpMetadata: IMapMessageLocalMetadata
107
+ ): { rebasedOp: IValueTypeOperationValue; rebasedLocalOpMetadata: IMapMessageLocalMetadata; };
83
108
  }
84
109
 
85
110
  /**
package/src/index.ts CHANGED
@@ -14,8 +14,11 @@ export {
14
14
  ISerializableInterval,
15
15
  ISerializedInterval,
16
16
  SequenceInterval,
17
+ ISerializedIntervalCollectionV2,
18
+ CompressedSerializedInterval,
17
19
  } from "./intervalCollection";
18
20
  export {
21
+ IMapMessageLocalMetadata,
19
22
  IValueOpEmitter,
20
23
  } from "./defaultMapInterfaces";
21
24
  export * from "./sharedString";