@fluidframework/sequence 2.32.0 → 2.33.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 (78) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/api-report/sequence.legacy.alpha.api.md +39 -39
  3. package/dist/intervalCollection.d.ts +14 -9
  4. package/dist/intervalCollection.d.ts.map +1 -1
  5. package/dist/intervalCollection.js +137 -77
  6. package/dist/intervalCollection.js.map +1 -1
  7. package/dist/intervalCollectionMap.d.ts +5 -4
  8. package/dist/intervalCollectionMap.d.ts.map +1 -1
  9. package/dist/intervalCollectionMap.js +25 -50
  10. package/dist/intervalCollectionMap.js.map +1 -1
  11. package/dist/intervalCollectionMapInterfaces.d.ts +22 -15
  12. package/dist/intervalCollectionMapInterfaces.d.ts.map +1 -1
  13. package/dist/intervalCollectionMapInterfaces.js.map +1 -1
  14. package/dist/intervalIndex/intervalIndex.d.ts +1 -1
  15. package/dist/intervalIndex/intervalIndex.js.map +1 -1
  16. package/dist/intervalIndex/overlappingIntervalsIndex.d.ts +1 -1
  17. package/dist/intervalIndex/overlappingIntervalsIndex.js.map +1 -1
  18. package/dist/intervals/sequenceInterval.d.ts +3 -3
  19. package/dist/intervals/sequenceInterval.d.ts.map +1 -1
  20. package/dist/intervals/sequenceInterval.js +10 -9
  21. package/dist/intervals/sequenceInterval.js.map +1 -1
  22. package/dist/packageVersion.d.ts +1 -1
  23. package/dist/packageVersion.js +1 -1
  24. package/dist/packageVersion.js.map +1 -1
  25. package/dist/sequence.d.ts +4 -0
  26. package/dist/sequence.d.ts.map +1 -1
  27. package/dist/sequence.js +10 -0
  28. package/dist/sequence.js.map +1 -1
  29. package/dist/sharedIntervalCollection.d.ts +1 -1
  30. package/dist/sharedIntervalCollection.js.map +1 -1
  31. package/dist/sharedString.d.ts +0 -4
  32. package/dist/sharedString.d.ts.map +1 -1
  33. package/dist/sharedString.js +0 -11
  34. package/dist/sharedString.js.map +1 -1
  35. package/lib/intervalCollection.d.ts +14 -9
  36. package/lib/intervalCollection.d.ts.map +1 -1
  37. package/lib/intervalCollection.js +138 -78
  38. package/lib/intervalCollection.js.map +1 -1
  39. package/lib/intervalCollectionMap.d.ts +5 -4
  40. package/lib/intervalCollectionMap.d.ts.map +1 -1
  41. package/lib/intervalCollectionMap.js +26 -51
  42. package/lib/intervalCollectionMap.js.map +1 -1
  43. package/lib/intervalCollectionMapInterfaces.d.ts +22 -15
  44. package/lib/intervalCollectionMapInterfaces.d.ts.map +1 -1
  45. package/lib/intervalCollectionMapInterfaces.js.map +1 -1
  46. package/lib/intervalIndex/intervalIndex.d.ts +1 -1
  47. package/lib/intervalIndex/intervalIndex.js.map +1 -1
  48. package/lib/intervalIndex/overlappingIntervalsIndex.d.ts +1 -1
  49. package/lib/intervalIndex/overlappingIntervalsIndex.js.map +1 -1
  50. package/lib/intervals/sequenceInterval.d.ts +3 -3
  51. package/lib/intervals/sequenceInterval.d.ts.map +1 -1
  52. package/lib/intervals/sequenceInterval.js +10 -9
  53. package/lib/intervals/sequenceInterval.js.map +1 -1
  54. package/lib/packageVersion.d.ts +1 -1
  55. package/lib/packageVersion.js +1 -1
  56. package/lib/packageVersion.js.map +1 -1
  57. package/lib/sequence.d.ts +4 -0
  58. package/lib/sequence.d.ts.map +1 -1
  59. package/lib/sequence.js +10 -0
  60. package/lib/sequence.js.map +1 -1
  61. package/lib/sharedIntervalCollection.d.ts +1 -1
  62. package/lib/sharedIntervalCollection.js.map +1 -1
  63. package/lib/sharedString.d.ts +0 -4
  64. package/lib/sharedString.d.ts.map +1 -1
  65. package/lib/sharedString.js +0 -11
  66. package/lib/sharedString.js.map +1 -1
  67. package/lib/tsdoc-metadata.json +1 -1
  68. package/package.json +18 -20
  69. package/src/intervalCollection.ts +188 -96
  70. package/src/intervalCollectionMap.ts +31 -60
  71. package/src/intervalCollectionMapInterfaces.ts +34 -29
  72. package/src/intervalIndex/intervalIndex.ts +1 -1
  73. package/src/intervalIndex/overlappingIntervalsIndex.ts +1 -1
  74. package/src/intervals/sequenceInterval.ts +14 -2
  75. package/src/packageVersion.ts +1 -1
  76. package/src/sequence.ts +15 -0
  77. package/src/sharedIntervalCollection.ts +1 -1
  78. package/src/sharedString.ts +0 -11
@@ -13,9 +13,6 @@ import { ValueType, IFluidSerializer } from "@fluidframework/shared-object-base/
13
13
  import { makeSerializable } from "./IntervalCollectionValues.js";
14
14
  import {
15
15
  IntervalCollection,
16
- opsMap,
17
- toOptionalSequencePlace,
18
- toSequencePlace,
19
16
  type ISerializedIntervalCollectionV1,
20
17
  type ISerializedIntervalCollectionV2,
21
18
  } from "./intervalCollection.js";
@@ -25,7 +22,6 @@ import {
25
22
  ISerializableIntervalCollection,
26
23
  SequenceOptions,
27
24
  } from "./intervalCollectionMapInterfaces.js";
28
- import { getSerializedProperties } from "./intervals/index.js";
29
25
 
30
26
  function isMapOperation(op: unknown): op is IMapOperation {
31
27
  return typeof op === "object" && op !== null && "type" in op && op.type === "act";
@@ -190,63 +186,46 @@ export class IntervalCollectionMap {
190
186
 
191
187
  /**
192
188
  * Submit the given op if a handler is registered.
193
- * @param op - The operation to attempt to submit
189
+ * @param content - The operation to attempt to submit
194
190
  * @param localOpMetadata - The local metadata associated with the op. This is kept locally by the runtime
195
191
  * and not sent to the server. This will be sent back when this message is received back from the server. This is
196
192
  * also sent if we are asked to resubmit the message.
197
193
  * @returns True if the operation was submitted, false otherwise.
198
194
  */
199
- public tryResubmitMessage(op: unknown, localOpMetadata: IMapMessageLocalMetadata): boolean {
200
- if (isMapOperation(op)) {
201
- const localValue = this.data.get(op.key);
202
-
195
+ public tryResubmitMessage(
196
+ content: unknown,
197
+ localOpMetadata: IMapMessageLocalMetadata,
198
+ ): boolean {
199
+ if (isMapOperation(content)) {
200
+ const { value, key } = content;
201
+ const localValue = this.data.get(key);
203
202
  assert(localValue !== undefined, 0x3f8 /* Local value expected on resubmission */);
203
+ localValue.resubmitMessage(value, localOpMetadata);
204
+ return true;
205
+ }
206
+ return false;
207
+ }
208
+
209
+ public tryRollback(content: any, localOpMetadata: unknown) {
210
+ if (isMapOperation(content)) {
211
+ const localValue = this.data.get(content.key);
212
+
213
+ assert(localValue !== undefined, 0xb7e /* Local value expected on rollback */);
214
+
215
+ localValue.rollback(content.value, localOpMetadata as IMapMessageLocalMetadata);
204
216
 
205
- const handler = opsMap[op.value.opName];
206
- const rebased = handler.rebase(localValue, op.value, localOpMetadata);
207
- if (rebased !== undefined) {
208
- const { rebasedOp, rebasedLocalOpMetadata } = rebased;
209
- this.submitMessage({ ...op, value: rebasedOp }, rebasedLocalOpMetadata);
210
- }
211
217
  return true;
212
218
  }
213
219
  return false;
214
220
  }
215
221
 
216
- public tryApplyStashedOp(op: unknown): boolean {
217
- if (isMapOperation(op)) {
218
- const { value, key } = op;
222
+ public tryApplyStashedOp(content: unknown): boolean {
223
+ if (isMapOperation(content)) {
224
+ const { value, key } = content;
219
225
  const map = this.get(key);
220
- const { id, properties } = getSerializedProperties(value.value);
221
226
 
222
- switch (value.opName) {
223
- case "add": {
224
- map.add({
225
- id,
226
- // Todo: we should improve typing so we know add ops always have start and end
227
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
228
- start: toSequencePlace(value.value.start!, value.value.startSide),
229
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
230
- end: toSequencePlace(value.value.end!, value.value.endSide),
231
- props: properties,
232
- });
233
- return true;
234
- }
235
- case "change": {
236
- map.change(id, {
237
- start: toOptionalSequencePlace(value.value.start, value.value.startSide),
238
- end: toOptionalSequencePlace(value.value.end, value.value.endSide),
239
- props: properties,
240
- });
241
- return true;
242
- }
243
- case "delete": {
244
- map.removeIntervalById(id);
245
- return true;
246
- }
247
- default:
248
- throw new Error("unknown ops should not be stashed");
249
- }
227
+ map.applyStashedOp(value);
228
+ return true;
250
229
  }
251
230
  return false;
252
231
  }
@@ -267,23 +246,15 @@ export class IntervalCollectionMap {
267
246
  * (since its data no longer matches what other clients think the data should be) and will avoid overriding document content or misleading the users into thinking their current state is accurate.
268
247
  */
269
248
  public tryProcessMessage(
270
- op: unknown,
249
+ content: unknown,
271
250
  local: boolean,
272
251
  message: ISequencedDocumentMessage,
273
252
  localOpMetadata: unknown,
274
253
  ): boolean {
275
- if (isMapOperation(op)) {
276
- const localValue = this.data.get(op.key) ?? this.createCore(op.key, local);
277
- const handler = opsMap[op.value.opName];
278
- const previousValue = localValue;
279
- const translatedValue = op.value.value as any;
280
- handler.process(
281
- previousValue,
282
- translatedValue,
283
- local,
284
- message,
285
- localOpMetadata as IMapMessageLocalMetadata,
286
- );
254
+ if (isMapOperation(content)) {
255
+ const { value, key } = content;
256
+ const localValue = this.data.get(key) ?? this.createCore(key, local);
257
+ localValue.process(value, local, message, localOpMetadata as IMapMessageLocalMetadata);
287
258
  return true;
288
259
  }
289
260
  return false;
@@ -19,6 +19,7 @@ import {
19
19
 
20
20
  export interface IMapMessageLocalMetadata {
21
21
  localSeq: number;
22
+ previous?: ISerializedInterval;
22
23
  }
23
24
 
24
25
  /**
@@ -74,25 +75,6 @@ export interface IIntervalCollectionOperation {
74
75
  message: ISequencedDocumentMessage | undefined,
75
76
  localOpMetadata: IMapMessageLocalMetadata | undefined,
76
77
  ): void;
77
-
78
- /**
79
- * Rebases an `op` on `value` from its original perspective (ref/local seq) to the current
80
- * perspective. Should be invoked on reconnection.
81
- * @param value - The current value stored at the given key, which should be the value type.
82
- * @param op - The op to be rebased.
83
- * @param localOpMetadata - Any local metadata that was originally submitted with the op.
84
- * @returns A rebased version of the op and any local metadata that should be submitted with it.
85
- */
86
- rebase(
87
- value: IntervalCollection,
88
- op: IIntervalCollectionTypeOperationValue,
89
- localOpMetadata: IMapMessageLocalMetadata,
90
- ):
91
- | {
92
- rebasedOp: IIntervalCollectionTypeOperationValue;
93
- rebasedLocalOpMetadata: IMapMessageLocalMetadata;
94
- }
95
- | undefined;
96
78
  }
97
79
 
98
80
  /**
@@ -138,14 +120,37 @@ export interface ISerializedIntervalCollection {
138
120
  * serializable via JSON.stringify/parse but differs in that it has no equivalency with an in-memory value - rather
139
121
  * it just describes an operation to be applied to an already-in-memory value.
140
122
  */
141
- export interface IIntervalCollectionTypeOperationValue {
142
- /**
143
- * The name of the operation.
144
- */
145
- opName: IntervalDeltaOpType;
123
+ export type IIntervalCollectionTypeOperationValue =
124
+ | {
125
+ /**
126
+ * The name of the operation.
127
+ */
128
+ opName: typeof IntervalDeltaOpType.ADD;
146
129
 
147
- /**
148
- * The payload that is submitted along with the operation.
149
- */
150
- value: SerializedIntervalDelta;
151
- }
130
+ /**
131
+ * The payload that is submitted along with the operation.
132
+ */
133
+ value: ISerializedInterval;
134
+ }
135
+ | {
136
+ /**
137
+ * The name of the operation.
138
+ */
139
+ opName: typeof IntervalDeltaOpType.CHANGE;
140
+
141
+ /**
142
+ * The payload that is submitted along with the operation.
143
+ */
144
+ value: SerializedIntervalDelta;
145
+ }
146
+ | {
147
+ /**
148
+ * The name of the operation.
149
+ */
150
+ opName: typeof IntervalDeltaOpType.DELETE;
151
+
152
+ /**
153
+ * The payload that is submitted along with the operation.
154
+ */
155
+ value: SerializedIntervalDelta;
156
+ };
@@ -16,7 +16,7 @@ import { ISerializableInterval, type SequenceInterval } from "../intervals/index
16
16
  * @legacy
17
17
  * @alpha
18
18
  *
19
- * @remarks The generic version of this interface is no longer used and will be removed. Use {@link SequenceIntervalIndex} instead.
19
+ * @deprecated The generic version of this interface is no longer used and will be removed. Use {@link SequenceIntervalIndex} instead.
20
20
  */
21
21
  export interface IntervalIndex<TInterval extends ISerializableInterval> {
22
22
  /**
@@ -26,7 +26,7 @@ import { IntervalIndex, type SequenceIntervalIndex } from "./intervalIndex.js";
26
26
  * Use {@link ISequenceOverlappingIntervalsIndex} instead.
27
27
  * @legacy
28
28
  * @alpha
29
- * @remarks The generic version of this interface is no longer used and will be removed. Use {@link ISequenceOverlappingIntervalsIndex} instead.
29
+ * @deprecated The generic version of this interface is no longer used and will be removed. Use {@link ISequenceOverlappingIntervalsIndex} instead.
30
30
  */
31
31
  export interface IOverlappingIntervalsIndex<TInterval extends ISerializableInterval>
32
32
  extends IntervalIndex<TInterval> {
@@ -219,7 +219,11 @@ export class SequenceIntervalClass implements SequenceInterval {
219
219
  return this.#props.properties;
220
220
  }
221
221
 
222
- public changeProperties(props: PropertySet | undefined, op?: ISequencedDocumentMessage) {
222
+ public changeProperties(
223
+ props: PropertySet | undefined,
224
+ op?: ISequencedDocumentMessage,
225
+ rollback?: boolean,
226
+ ) {
223
227
  if (props !== undefined) {
224
228
  this.#props.propertyManager ??= new PropertiesManager();
225
229
  return this.#props.propertyManager.handleProperties(
@@ -230,6 +234,7 @@ export class SequenceIntervalClass implements SequenceInterval {
230
234
  : UniversalSequenceNumber,
231
235
  op?.minimumSequenceNumber ?? UniversalSequenceNumber,
232
236
  this.client.getCollabWindow().collaborating,
237
+ rollback,
233
238
  );
234
239
  }
235
240
  }
@@ -573,6 +578,7 @@ export function createPositionReferenceFromSegoff(
573
578
  fromSnapshot?: boolean,
574
579
  slidingPreference?: SlidingPreference,
575
580
  canSlideToEndpoint?: boolean,
581
+ rollback?: boolean,
576
582
  ): LocalReferencePosition {
577
583
  if (segoff === "start" || segoff === "end") {
578
584
  return client.createLocalReferencePosition(
@@ -606,7 +612,8 @@ export function createPositionReferenceFromSegoff(
606
612
  !op &&
607
613
  !localSeq &&
608
614
  !fromSnapshot &&
609
- !refTypeIncludesFlag(refType, ReferenceType.Transient)
615
+ !refTypeIncludesFlag(refType, ReferenceType.Transient) &&
616
+ !rollback
610
617
  ) {
611
618
  throw new UsageError("Non-transient references need segment");
612
619
  }
@@ -624,6 +631,7 @@ function createPositionReference(
624
631
  slidingPreference?: SlidingPreference,
625
632
  exclusive: boolean = false,
626
633
  useNewSlidingBehavior: boolean = false,
634
+ rollback?: boolean,
627
635
  ): LocalReferencePosition {
628
636
  let segoff;
629
637
 
@@ -661,6 +669,7 @@ function createPositionReference(
661
669
  fromSnapshot,
662
670
  slidingPreference,
663
671
  exclusive,
672
+ rollback,
664
673
  );
665
674
  }
666
675
 
@@ -690,6 +699,7 @@ export function createSequenceInterval(
690
699
  fromSnapshot?: boolean,
691
700
  useNewSlidingBehavior: boolean = false,
692
701
  props?: PropertySet,
702
+ rollback?: boolean,
693
703
  ): SequenceIntervalClass {
694
704
  const { startPos, startSide, endPos, endSide } = endpointPosAndSide(
695
705
  start ?? "start",
@@ -731,6 +741,7 @@ export function createSequenceInterval(
731
741
  startReferenceSlidingPreference(stickiness),
732
742
  startReferenceSlidingPreference(stickiness) === SlidingPreference.BACKWARD,
733
743
  useNewSlidingBehavior,
744
+ rollback,
734
745
  );
735
746
 
736
747
  const endLref = createPositionReference(
@@ -743,6 +754,7 @@ export function createSequenceInterval(
743
754
  endReferenceSlidingPreference(stickiness),
744
755
  endReferenceSlidingPreference(stickiness) === SlidingPreference.FORWARD,
745
756
  useNewSlidingBehavior,
757
+ rollback,
746
758
  );
747
759
 
748
760
  const rangeProp = {
@@ -6,4 +6,4 @@
6
6
  */
7
7
 
8
8
  export const pkgName = "@fluidframework/sequence";
9
- export const pkgVersion = "2.32.0";
9
+ export const pkgVersion = "2.33.0";
package/src/sequence.ts CHANGED
@@ -796,6 +796,21 @@ export abstract class SharedSegmentSequence<T extends ISegment>
796
796
  });
797
797
  }
798
798
 
799
+ /**
800
+ * Revert an op
801
+ */
802
+ protected rollback(content: any, localOpMetadata: unknown): void {
803
+ const originalRefSeq = this.inFlightRefSeqs.pop();
804
+ assert(
805
+ originalRefSeq !== undefined,
806
+ 0xb7f /* Expected a recorded refSeq when rolling back an op */,
807
+ );
808
+
809
+ if (!this.intervalCollections.tryRollback(content, localOpMetadata)) {
810
+ this.client.rollback(content, localOpMetadata);
811
+ }
812
+ }
813
+
799
814
  /**
800
815
  * {@inheritDoc @fluidframework/shared-object-base#SharedObject.loadCore}
801
816
  */
@@ -10,7 +10,7 @@ import { ISerializableInterval } from "./intervals/index.js";
10
10
  /**
11
11
  * @legacy
12
12
  * @alpha
13
- * @remarks This interface is no longer used and will be removed.
13
+ * @deprecated This interface is no longer used and will be removed.
14
14
  */
15
15
  export interface ISharedIntervalCollection<TInterval extends ISerializableInterval> {
16
16
  // eslint-disable-next-line import/no-deprecated
@@ -271,17 +271,6 @@ export class SharedStringClass
271
271
  public getMarkerFromId(id: string): ISegment | undefined {
272
272
  return this.client.getMarkerFromId(id);
273
273
  }
274
-
275
- /**
276
- * Revert an op
277
- */
278
- protected rollback(content: any, localOpMetadata: unknown): void {
279
- if (this.client.rollback !== undefined) {
280
- this.client.rollback(content, localOpMetadata);
281
- } else {
282
- super.rollback(content, localOpMetadata);
283
- }
284
- }
285
274
  }
286
275
 
287
276
  interface ITextAndMarkerAccumulator {