@fluidframework/sequence 2.0.0-dev.4.4.0.162253 → 2.0.0-dev.5.2.0.169897

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 (71) hide show
  1. package/CHANGELOG.md +20 -0
  2. package/dist/defaultMap.d.ts +3 -2
  3. package/dist/defaultMap.d.ts.map +1 -1
  4. package/dist/defaultMap.js +4 -3
  5. package/dist/defaultMap.js.map +1 -1
  6. package/dist/defaultMapInterfaces.d.ts +12 -1
  7. package/dist/defaultMapInterfaces.d.ts.map +1 -1
  8. package/dist/defaultMapInterfaces.js.map +1 -1
  9. package/dist/index.d.ts +3 -2
  10. package/dist/index.d.ts.map +1 -1
  11. package/dist/index.js +14 -3
  12. package/dist/index.js.map +1 -1
  13. package/dist/intervalCollection.d.ts +231 -59
  14. package/dist/intervalCollection.d.ts.map +1 -1
  15. package/dist/intervalCollection.js +288 -86
  16. package/dist/intervalCollection.js.map +1 -1
  17. package/dist/packageVersion.d.ts +1 -1
  18. package/dist/packageVersion.js +1 -1
  19. package/dist/packageVersion.js.map +1 -1
  20. package/dist/revertibles.d.ts +104 -0
  21. package/dist/revertibles.d.ts.map +1 -0
  22. package/dist/revertibles.js +374 -0
  23. package/dist/revertibles.js.map +1 -0
  24. package/dist/sequence.d.ts +4 -4
  25. package/dist/sequence.d.ts.map +1 -1
  26. package/dist/sequence.js +3 -3
  27. package/dist/sequence.js.map +1 -1
  28. package/dist/sharedIntervalCollection.d.ts +3 -3
  29. package/dist/sharedIntervalCollection.d.ts.map +1 -1
  30. package/dist/sharedIntervalCollection.js +1 -1
  31. package/dist/sharedIntervalCollection.js.map +1 -1
  32. package/dist/tsdoc-metadata.json +11 -0
  33. package/lib/defaultMap.d.ts +3 -2
  34. package/lib/defaultMap.d.ts.map +1 -1
  35. package/lib/defaultMap.js +4 -3
  36. package/lib/defaultMap.js.map +1 -1
  37. package/lib/defaultMapInterfaces.d.ts +12 -1
  38. package/lib/defaultMapInterfaces.d.ts.map +1 -1
  39. package/lib/defaultMapInterfaces.js.map +1 -1
  40. package/lib/index.d.ts +3 -2
  41. package/lib/index.d.ts.map +1 -1
  42. package/lib/index.js +2 -1
  43. package/lib/index.js.map +1 -1
  44. package/lib/intervalCollection.d.ts +231 -59
  45. package/lib/intervalCollection.d.ts.map +1 -1
  46. package/lib/intervalCollection.js +286 -86
  47. package/lib/intervalCollection.js.map +1 -1
  48. package/lib/packageVersion.d.ts +1 -1
  49. package/lib/packageVersion.js +1 -1
  50. package/lib/packageVersion.js.map +1 -1
  51. package/lib/revertibles.d.ts +104 -0
  52. package/lib/revertibles.d.ts.map +1 -0
  53. package/lib/revertibles.js +364 -0
  54. package/lib/revertibles.js.map +1 -0
  55. package/lib/sequence.d.ts +4 -4
  56. package/lib/sequence.d.ts.map +1 -1
  57. package/lib/sequence.js +3 -3
  58. package/lib/sequence.js.map +1 -1
  59. package/lib/sharedIntervalCollection.d.ts +3 -3
  60. package/lib/sharedIntervalCollection.d.ts.map +1 -1
  61. package/lib/sharedIntervalCollection.js +1 -1
  62. package/lib/sharedIntervalCollection.js.map +1 -1
  63. package/package.json +20 -22
  64. package/src/defaultMap.ts +4 -1
  65. package/src/defaultMapInterfaces.ts +13 -1
  66. package/src/index.ts +21 -5
  67. package/src/intervalCollection.ts +619 -73
  68. package/src/packageVersion.ts +1 -1
  69. package/src/revertibles.ts +572 -0
  70. package/src/sequence.ts +12 -2
  71. package/src/sharedIntervalCollection.ts +4 -2
@@ -28,6 +28,8 @@ import {
28
28
  maxReferencePosition,
29
29
  createDetachedLocalReferencePosition,
30
30
  DetachedReferencePosition,
31
+ SlidingPreference,
32
+ PropertyAction,
31
33
  } from "@fluidframework/merge-tree";
32
34
  import { ISequencedDocumentMessage } from "@fluidframework/protocol-definitions";
33
35
  import { LoggingError } from "@fluidframework/telemetry-utils";
@@ -39,11 +41,24 @@ import {
39
41
  IValueOperation,
40
42
  IValueType,
41
43
  IValueTypeOperationValue,
44
+ SequenceOptions,
42
45
  } from "./defaultMapInterfaces";
43
46
  import { IInterval, IntervalConflictResolver, IntervalTree, IntervalNode } from "./intervalTree";
44
47
 
45
48
  const reservedIntervalIdKey = "intervalId";
46
49
 
50
+ /**
51
+ * Values are used in persisted formats (ops) and revertibles.
52
+ * @alpha
53
+ */
54
+ export const IntervalOpType = {
55
+ ADD: "add",
56
+ DELETE: "delete",
57
+ CHANGE: "change",
58
+ PROPERTY_CHANGED: "propertyChanged",
59
+ POSITION_REMOVE: "positionRemove",
60
+ } as const;
61
+
47
62
  export enum IntervalType {
48
63
  Simple = 0x0,
49
64
  Nest = 0x1,
@@ -51,7 +66,7 @@ export enum IntervalType {
51
66
  /**
52
67
  * SlideOnRemove indicates that the ends of the interval will slide if the segment
53
68
  * they reference is removed and acked.
54
- * See `packages\dds\merge-tree\REFERENCEPOSITIONS.md` for details
69
+ * See `packages\dds\merge-tree\docs\REFERENCEPOSITIONS.md` for details
55
70
  * SlideOnRemove is the default interval behavior and does not need to be specified.
56
71
  */
57
72
  SlideOnRemove = 0x2, // SlideOnRemove is default behavior - all intervals are SlideOnRemove
@@ -83,6 +98,7 @@ export interface ISerializedInterval {
83
98
  end: number;
84
99
  /** Interval type to create */
85
100
  intervalType: IntervalType;
101
+ stickiness?: IntervalStickiness;
86
102
  /** Any properties the interval has */
87
103
  properties?: PropertySet;
88
104
  }
@@ -101,13 +117,12 @@ export type SerializedIntervalDelta = Omit<ISerializedInterval, "start" | "end"
101
117
  *
102
118
  * Intervals are of the format:
103
119
  *
104
- * [start, end, sequenceNumber, intervalType, properties]
120
+ * [start, end, sequenceNumber, intervalType, properties, stickiness?]
105
121
  */
106
- export type CompressedSerializedInterval = [number, number, number, IntervalType, PropertySet];
122
+ export type CompressedSerializedInterval =
123
+ | [number, number, number, IntervalType, PropertySet, IntervalStickiness]
124
+ | [number, number, number, IntervalType, PropertySet];
107
125
 
108
- /**
109
- * @internal
110
- */
111
126
  export interface ISerializedIntervalCollectionV2 {
112
127
  label: string;
113
128
  version: 2;
@@ -128,6 +143,7 @@ function decompressInterval(
128
143
  sequenceNumber: interval[2],
129
144
  intervalType: interval[3],
130
145
  properties: { ...interval[4], [reservedRangeLabelsKey]: [label] },
146
+ stickiness: interval[5],
131
147
  };
132
148
  }
133
149
 
@@ -138,7 +154,7 @@ function decompressInterval(
138
154
  function compressInterval(interval: ISerializedInterval): CompressedSerializedInterval {
139
155
  const { start, end, sequenceNumber, intervalType, properties } = interval;
140
156
 
141
- return [
157
+ const base: CompressedSerializedInterval = [
142
158
  start,
143
159
  end,
144
160
  sequenceNumber,
@@ -147,6 +163,26 @@ function compressInterval(interval: ISerializedInterval): CompressedSerializedIn
147
163
  // in the `label` field of the summary
148
164
  { ...properties, [reservedRangeLabelsKey]: undefined },
149
165
  ];
166
+
167
+ if (interval.stickiness !== undefined && interval.stickiness !== IntervalStickiness.END) {
168
+ base.push(interval.stickiness);
169
+ }
170
+
171
+ return base;
172
+ }
173
+
174
+ function startReferenceSlidingPreference(stickiness: IntervalStickiness): SlidingPreference {
175
+ // if any start stickiness, prefer sliding backwards
176
+ return (stickiness & IntervalStickiness.START) !== 0
177
+ ? SlidingPreference.BACKWARD
178
+ : SlidingPreference.FORWARD;
179
+ }
180
+
181
+ function endReferenceSlidingPreference(stickiness: IntervalStickiness): SlidingPreference {
182
+ // if any end stickiness, prefer sliding forwards
183
+ return (stickiness & IntervalStickiness.END) !== 0
184
+ ? SlidingPreference.FORWARD
185
+ : SlidingPreference.BACKWARD;
150
186
  }
151
187
 
152
188
  export interface ISerializableInterval extends IInterval {
@@ -172,14 +208,18 @@ export interface ISerializableInterval extends IInterval {
172
208
  getIntervalId(): string | undefined;
173
209
  }
174
210
 
211
+ /**
212
+ * @sealed
213
+ */
175
214
  export interface IIntervalHelpers<TInterval extends ISerializableInterval> {
176
215
  compareEnds(a: TInterval, b: TInterval): number;
216
+ compareStarts?(a: TInterval, b: TInterval): number;
177
217
  /**
178
218
  *
179
219
  * @param label - label of the interval collection this interval is being added to. This parameter is
180
220
  * irrelevant for transient intervals.
181
221
  * @param start - numerical start position of the interval
182
- * @param end - numberical end position of the interval
222
+ * @param end - numerical end position of the interval
183
223
  * @param client - client creating the interval
184
224
  * @param intervalType - Type of interval to create. Default is SlideOnRemove
185
225
  * @param op - If this create came from a remote client, op that created it. Default is undefined (i.e. local)
@@ -193,9 +233,50 @@ export interface IIntervalHelpers<TInterval extends ISerializableInterval> {
193
233
  intervalType: IntervalType,
194
234
  op?: ISequencedDocumentMessage,
195
235
  fromSnapshot?: boolean,
236
+ stickiness?: IntervalStickiness,
196
237
  ): TInterval;
197
238
  }
198
239
 
240
+ /**
241
+ * Determines how an interval should expand when segments are inserted adjacent
242
+ * to the range it spans
243
+ *
244
+ * Note that interval stickiness is currently an experimental feature and must
245
+ * be explicitly enabled with the `intervalStickinessEnabled` flag
246
+ */
247
+ export const IntervalStickiness = {
248
+ /**
249
+ * Interval does not expand to include adjacent segments
250
+ */
251
+ NONE: 0b00,
252
+
253
+ /**
254
+ * Interval expands to include segments inserted adjacent to the start
255
+ */
256
+ START: 0b01,
257
+
258
+ /**
259
+ * Interval expands to include segments inserted adjacent to the end
260
+ *
261
+ * This is the default stickiness
262
+ */
263
+ END: 0b10,
264
+
265
+ /**
266
+ * Interval expands to include all segments inserted adjacent to it
267
+ */
268
+ FULL: 0b11,
269
+ } as const;
270
+
271
+ /**
272
+ * Determines how an interval should expand when segments are inserted adjacent
273
+ * to the range it spans
274
+ *
275
+ * Note that interval stickiness is currently an experimental feature and must
276
+ * be explicitly enabled with the `intervalStickinessEnabled` flag
277
+ */
278
+ export type IntervalStickiness = typeof IntervalStickiness[keyof typeof IntervalStickiness];
279
+
199
280
  /**
200
281
  * Serializable interval whose endpoints are plain-old numbers.
201
282
  */
@@ -240,7 +321,7 @@ export class Interval implements ISerializableInterval {
240
321
  * Adds an auxiliary set of properties to this interval.
241
322
  * These properties can be recovered using `getAdditionalPropertySets`
242
323
  * @param props - set of properties to add
243
- * @remarks - This gets called as part of the default conflict resolver for `IntervalCollection<Interval>`
324
+ * @remarks - This gets called as part of the default conflict resolver for `IIntervalCollection<Interval>`
244
325
  * (i.e. non-sequence-based interval collections). However, the additional properties don't get serialized.
245
326
  * This functionality seems half-baked.
246
327
  */
@@ -394,7 +475,7 @@ export class Interval implements ISerializableInterval {
394
475
  }
395
476
 
396
477
  /**
397
- * Interval impelmentation whose ends are associated with positions in a mutatable sequence.
478
+ * Interval implementation whose ends are associated with positions in a mutatable sequence.
398
479
  * As such, when content is inserted into the middle of the interval, the interval expands to
399
480
  * include that content.
400
481
  *
@@ -439,6 +520,7 @@ export class SequenceInterval implements ISerializableInterval {
439
520
  public end: LocalReferencePosition,
440
521
  public intervalType: IntervalType,
441
522
  props?: PropertySet,
523
+ public readonly stickiness: IntervalStickiness = IntervalStickiness.END,
442
524
  ) {
443
525
  this.propertyManager = new PropertiesManager();
444
526
  this.properties = {};
@@ -500,6 +582,9 @@ export class SequenceInterval implements ISerializableInterval {
500
582
  if (this.properties) {
501
583
  serializedInterval.properties = this.properties;
502
584
  }
585
+ if (this.stickiness !== IntervalStickiness.END) {
586
+ serializedInterval.stickiness = this.stickiness;
587
+ }
503
588
 
504
589
  return serializedInterval;
505
590
  }
@@ -514,6 +599,7 @@ export class SequenceInterval implements ISerializableInterval {
514
599
  this.end,
515
600
  this.intervalType,
516
601
  this.properties,
602
+ this.stickiness,
517
603
  );
518
604
  }
519
605
 
@@ -621,6 +707,7 @@ export class SequenceInterval implements ISerializableInterval {
621
707
  end: number,
622
708
  op?: ISequencedDocumentMessage,
623
709
  localSeq?: number,
710
+ stickiness: IntervalStickiness = IntervalStickiness.END,
624
711
  ) {
625
712
  const getRefType = (baseType: ReferenceType): ReferenceType => {
626
713
  let refType = baseType;
@@ -640,6 +727,7 @@ export class SequenceInterval implements ISerializableInterval {
640
727
  op,
641
728
  undefined,
642
729
  localSeq,
730
+ startReferenceSlidingPreference(stickiness),
643
731
  );
644
732
  if (this.start.properties) {
645
733
  startRef.addProperties(this.start.properties);
@@ -655,6 +743,7 @@ export class SequenceInterval implements ISerializableInterval {
655
743
  op,
656
744
  undefined,
657
745
  localSeq,
746
+ endReferenceSlidingPreference(stickiness),
658
747
  );
659
748
  if (this.end.properties) {
660
749
  endRef.addProperties(this.end.properties);
@@ -690,6 +779,7 @@ function createPositionReferenceFromSegoff(
690
779
  op?: ISequencedDocumentMessage,
691
780
  localSeq?: number,
692
781
  fromSnapshot?: boolean,
782
+ slidingPreference?: SlidingPreference,
693
783
  ): LocalReferencePosition {
694
784
  if (segoff.segment) {
695
785
  const ref = client.createLocalReferencePosition(
@@ -697,6 +787,7 @@ function createPositionReferenceFromSegoff(
697
787
  segoff.offset,
698
788
  refType,
699
789
  undefined,
790
+ slidingPreference,
700
791
  );
701
792
  return ref;
702
793
  }
@@ -725,6 +816,7 @@ function createPositionReference(
725
816
  op?: ISequencedDocumentMessage,
726
817
  fromSnapshot?: boolean,
727
818
  localSeq?: number,
819
+ slidingPreference?: SlidingPreference,
728
820
  ): LocalReferencePosition {
729
821
  let segoff;
730
822
  if (op) {
@@ -744,7 +836,16 @@ function createPositionReference(
744
836
  );
745
837
  segoff = client.getContainingSegment(pos, undefined, localSeq);
746
838
  }
747
- return createPositionReferenceFromSegoff(client, segoff, refType, op, localSeq, fromSnapshot);
839
+
840
+ return createPositionReferenceFromSegoff(
841
+ client,
842
+ segoff,
843
+ refType,
844
+ op,
845
+ localSeq,
846
+ fromSnapshot,
847
+ slidingPreference,
848
+ );
748
849
  }
749
850
 
750
851
  export function createSequenceInterval(
@@ -755,6 +856,7 @@ export function createSequenceInterval(
755
856
  intervalType: IntervalType,
756
857
  op?: ISequencedDocumentMessage,
757
858
  fromSnapshot?: boolean,
859
+ stickiness: IntervalStickiness = IntervalStickiness.END,
758
860
  ): SequenceInterval {
759
861
  let beginRefType = ReferenceType.RangeBegin;
760
862
  let endRefType = ReferenceType.RangeEnd;
@@ -778,15 +880,38 @@ export function createSequenceInterval(
778
880
  }
779
881
  }
780
882
 
781
- const startLref = createPositionReference(client, start, beginRefType, op, fromSnapshot);
782
- const endLref = createPositionReference(client, end, endRefType, op, fromSnapshot);
883
+ const startLref = createPositionReference(
884
+ client,
885
+ start,
886
+ beginRefType,
887
+ op,
888
+ fromSnapshot,
889
+ undefined,
890
+ startReferenceSlidingPreference(stickiness),
891
+ );
892
+ const endLref = createPositionReference(
893
+ client,
894
+ end,
895
+ endRefType,
896
+ op,
897
+ fromSnapshot,
898
+ undefined,
899
+ endReferenceSlidingPreference(stickiness),
900
+ );
783
901
  const rangeProp = {
784
902
  [reservedRangeLabelsKey]: [label],
785
903
  };
786
904
  startLref.addProperties(rangeProp);
787
905
  endLref.addProperties(rangeProp);
788
906
 
789
- const ival = new SequenceInterval(client, startLref, endLref, intervalType, rangeProp);
907
+ const ival = new SequenceInterval(
908
+ client,
909
+ startLref,
910
+ endLref,
911
+ intervalType,
912
+ rangeProp,
913
+ stickiness,
914
+ );
790
915
  return ival;
791
916
  }
792
917
 
@@ -1036,12 +1161,229 @@ class EndpointIndex<TInterval extends ISerializableInterval> implements Interval
1036
1161
  }
1037
1162
  }
1038
1163
 
1164
+ /**
1165
+ * Collection of intervals.
1166
+ *
1167
+ * Provide additional APIs to support efficiently querying a collection of intervals whose endpoints fall within a specified range.
1168
+ */
1169
+ export interface IEndpointInRangeIndex<TInterval extends ISerializableInterval>
1170
+ extends IntervalIndex<TInterval> {
1171
+ /**
1172
+ * @returns an array of all intervals contained in this collection whose endpoints locate in the range [start, end] (includes both ends)
1173
+ */
1174
+ findIntervalsWithEndpointInRange(start: number, end: number);
1175
+ }
1176
+
1177
+ /**
1178
+ * Collection of intervals.
1179
+ *
1180
+ * Provide additional APIs to support efficiently querying a collection of intervals whose startpoints fall within a specified range.
1181
+ */
1182
+ export interface IStartpointInRangeIndex<TInterval extends ISerializableInterval>
1183
+ extends IntervalIndex<TInterval> {
1184
+ /**
1185
+ * @returns an array of all intervals contained in this collection whose startpoints locate in the range [start, end] (includes both ends)
1186
+ */
1187
+ findIntervalsWithStartpointInRange(start: number, end: number);
1188
+ }
1189
+
1190
+ /**
1191
+ * Interface for intervals that have comparison override properties.
1192
+ */
1193
+ const forceCompare = Symbol();
1194
+
1195
+ interface HasComparisonOverride {
1196
+ [forceCompare]: number;
1197
+ }
1198
+
1199
+ /**
1200
+ * Compares two objects based on their comparison override properties.
1201
+ * @returns A number indicating the order of the intervals (negative for a is lower than b, 0 for tie, positive for a is greater than b).
1202
+ */
1203
+ function compareOverrideables(
1204
+ a: Partial<HasComparisonOverride>,
1205
+ b: Partial<HasComparisonOverride>,
1206
+ ): number {
1207
+ const forceCompareA = a[forceCompare] ?? 0;
1208
+ const forceCompareB = b[forceCompare] ?? 0;
1209
+
1210
+ return forceCompareA - forceCompareB;
1211
+ }
1212
+
1213
+ class EndpointInRangeIndex<TInterval extends ISerializableInterval>
1214
+ implements IEndpointInRangeIndex<TInterval>
1215
+ {
1216
+ private readonly intervalTree;
1217
+
1218
+ constructor(
1219
+ private readonly helpers: IIntervalHelpers<TInterval>,
1220
+ private readonly client: Client,
1221
+ ) {
1222
+ this.intervalTree = new RedBlackTree<TInterval, TInterval>((a: TInterval, b: TInterval) => {
1223
+ const compareEndsResult = helpers.compareEnds(a, b);
1224
+ if (compareEndsResult !== 0) {
1225
+ return compareEndsResult;
1226
+ }
1227
+
1228
+ const overrideablesComparison = compareOverrideables(
1229
+ a as Partial<HasComparisonOverride>,
1230
+ b as Partial<HasComparisonOverride>,
1231
+ );
1232
+ if (overrideablesComparison !== 0) {
1233
+ return overrideablesComparison;
1234
+ }
1235
+
1236
+ const aId = a.getIntervalId();
1237
+ const bId = b.getIntervalId();
1238
+ if (aId !== undefined && bId !== undefined) {
1239
+ return aId.localeCompare(bId);
1240
+ }
1241
+ return 0;
1242
+ });
1243
+ }
1244
+
1245
+ public add(interval: TInterval): void {
1246
+ this.intervalTree.put(interval, interval);
1247
+ }
1248
+
1249
+ public remove(interval: TInterval): void {
1250
+ this.intervalTree.remove(interval);
1251
+ }
1252
+
1253
+ public findIntervalsWithEndpointInRange(start: number, end: number) {
1254
+ if (start <= 0 || start > end || this.intervalTree.isEmpty()) {
1255
+ return [];
1256
+ }
1257
+ const results: TInterval[] = [];
1258
+ const action: PropertyAction<TInterval, TInterval> = (node) => {
1259
+ results.push(node.data);
1260
+ return true;
1261
+ };
1262
+
1263
+ const transientStartInterval = this.helpers.create(
1264
+ "transient",
1265
+ start,
1266
+ start,
1267
+ this.client,
1268
+ IntervalType.Transient,
1269
+ );
1270
+
1271
+ const transientEndInterval = this.helpers.create(
1272
+ "transient",
1273
+ end,
1274
+ end,
1275
+ this.client,
1276
+ IntervalType.Transient,
1277
+ );
1278
+
1279
+ // Add comparison overrides to the transient intervals
1280
+ (transientStartInterval as Partial<HasComparisonOverride>)[forceCompare] = -1;
1281
+ (transientEndInterval as Partial<HasComparisonOverride>)[forceCompare] = 1;
1282
+
1283
+ this.intervalTree.mapRange(action, results, transientStartInterval, transientEndInterval);
1284
+ return results;
1285
+ }
1286
+ }
1287
+
1288
+ class StartpointInRangeIndex<TInterval extends ISerializableInterval>
1289
+ implements IStartpointInRangeIndex<TInterval>
1290
+ {
1291
+ private readonly intervalTree;
1292
+
1293
+ constructor(
1294
+ private readonly helpers: IIntervalHelpers<TInterval>,
1295
+ private readonly client: Client,
1296
+ ) {
1297
+ this.intervalTree = new RedBlackTree<TInterval, TInterval>((a: TInterval, b: TInterval) => {
1298
+ assert(
1299
+ typeof helpers.compareStarts === "function",
1300
+ 0x6d1 /* compareStarts does not exist in the helpers */,
1301
+ );
1302
+
1303
+ const compareStartsResult = helpers.compareStarts(a, b);
1304
+ if (compareStartsResult !== 0) {
1305
+ return compareStartsResult;
1306
+ }
1307
+
1308
+ const overrideablesComparison = compareOverrideables(
1309
+ a as Partial<HasComparisonOverride>,
1310
+ b as Partial<HasComparisonOverride>,
1311
+ );
1312
+ if (overrideablesComparison !== 0) {
1313
+ return overrideablesComparison;
1314
+ }
1315
+ const aId = a.getIntervalId();
1316
+ const bId = b.getIntervalId();
1317
+ if (aId !== undefined && bId !== undefined) {
1318
+ return aId.localeCompare(bId);
1319
+ }
1320
+ return 0;
1321
+ });
1322
+ }
1323
+
1324
+ public add(interval: TInterval): void {
1325
+ this.intervalTree.put(interval, interval);
1326
+ }
1327
+
1328
+ public remove(interval: TInterval): void {
1329
+ this.intervalTree.remove(interval);
1330
+ }
1331
+
1332
+ public findIntervalsWithStartpointInRange(start: number, end: number) {
1333
+ if (start <= 0 || start > end || this.intervalTree.isEmpty()) {
1334
+ return [];
1335
+ }
1336
+ const results: TInterval[] = [];
1337
+ const action: PropertyAction<TInterval, TInterval> = (node) => {
1338
+ results.push(node.data);
1339
+ return true;
1340
+ };
1341
+
1342
+ const transientStartInterval = this.helpers.create(
1343
+ "transient",
1344
+ start,
1345
+ start,
1346
+ this.client,
1347
+ IntervalType.Transient,
1348
+ );
1349
+
1350
+ const transientEndInterval = this.helpers.create(
1351
+ "transient",
1352
+ end,
1353
+ end,
1354
+ this.client,
1355
+ IntervalType.Transient,
1356
+ );
1357
+
1358
+ // Add comparison overrides to the transient intervals
1359
+ (transientStartInterval as Partial<HasComparisonOverride>)[forceCompare] = -1;
1360
+ (transientEndInterval as Partial<HasComparisonOverride>)[forceCompare] = 1;
1361
+
1362
+ this.intervalTree.mapRange(action, results, transientStartInterval, transientEndInterval);
1363
+ return results;
1364
+ }
1365
+ }
1366
+
1367
+ export function createEndpointInRangeIndex<TInterval extends ISerializableInterval>(
1368
+ helpers: IIntervalHelpers<TInterval>,
1369
+ client: Client,
1370
+ ): IEndpointInRangeIndex<TInterval> {
1371
+ return new EndpointInRangeIndex<TInterval>(helpers, client);
1372
+ }
1373
+
1374
+ export function createStartpointInRangeIndex<TInterval extends ISerializableInterval>(
1375
+ helpers: IIntervalHelpers<TInterval>,
1376
+ client: Client,
1377
+ ): IStartpointInRangeIndex<TInterval> {
1378
+ return new StartpointInRangeIndex<TInterval>(helpers, client);
1379
+ }
1380
+
1039
1381
  export class LocalIntervalCollection<TInterval extends ISerializableInterval> {
1040
1382
  private static readonly legacyIdPrefix = "legacy";
1041
1383
  public readonly overlappingIntervalsIndex: OverlappingIntervalsIndex<TInterval>;
1042
1384
  public readonly idIntervalIndex: IdIntervalIndex<TInterval>;
1043
1385
  public readonly endIntervalIndex: EndpointIndex<TInterval>;
1044
- private readonly indexes: IntervalIndex<TInterval>[];
1386
+ private readonly indexes: Set<IntervalIndex<TInterval>>;
1045
1387
 
1046
1388
  constructor(
1047
1389
  private readonly client: Client,
@@ -1056,11 +1398,11 @@ export class LocalIntervalCollection<TInterval extends ISerializableInterval> {
1056
1398
  this.overlappingIntervalsIndex = new OverlappingIntervalsIndex(client, helpers);
1057
1399
  this.idIntervalIndex = new IdIntervalIndex();
1058
1400
  this.endIntervalIndex = new EndpointIndex(client, helpers);
1059
- this.indexes = [
1401
+ this.indexes = new Set([
1060
1402
  this.overlappingIntervalsIndex,
1061
1403
  this.idIntervalIndex,
1062
1404
  this.endIntervalIndex,
1063
- ];
1405
+ ]);
1064
1406
  }
1065
1407
 
1066
1408
  public createLegacyId(start: number, end: number): string {
@@ -1104,6 +1446,14 @@ export class LocalIntervalCollection<TInterval extends ISerializableInterval> {
1104
1446
  }
1105
1447
  }
1106
1448
 
1449
+ public appendIndex(index: IntervalIndex<TInterval>) {
1450
+ this.indexes.add(index);
1451
+ }
1452
+
1453
+ public removeIndex(index: IntervalIndex<TInterval>): boolean {
1454
+ return this.indexes.delete(index);
1455
+ }
1456
+
1107
1457
  public removeExistingInterval(interval: TInterval) {
1108
1458
  this.removeIntervalFromIndexes(interval);
1109
1459
  this.removeIntervalListeners(interval);
@@ -1114,8 +1464,18 @@ export class LocalIntervalCollection<TInterval extends ISerializableInterval> {
1114
1464
  end: number,
1115
1465
  intervalType: IntervalType,
1116
1466
  op?: ISequencedDocumentMessage,
1467
+ stickiness: IntervalStickiness = IntervalStickiness.END,
1117
1468
  ): TInterval {
1118
- return this.helpers.create(this.label, start, end, this.client, intervalType, op);
1469
+ return this.helpers.create(
1470
+ this.label,
1471
+ start,
1472
+ end,
1473
+ this.client,
1474
+ intervalType,
1475
+ op,
1476
+ undefined,
1477
+ stickiness,
1478
+ );
1119
1479
  }
1120
1480
 
1121
1481
  public addInterval(
@@ -1124,8 +1484,9 @@ export class LocalIntervalCollection<TInterval extends ISerializableInterval> {
1124
1484
  intervalType: IntervalType,
1125
1485
  props?: PropertySet,
1126
1486
  op?: ISequencedDocumentMessage,
1487
+ stickiness: IntervalStickiness = IntervalStickiness.END,
1127
1488
  ) {
1128
- const interval: TInterval = this.createInterval(start, end, intervalType, op);
1489
+ const interval: TInterval = this.createInterval(start, end, intervalType, op, stickiness);
1129
1490
  if (interval) {
1130
1491
  if (!interval.properties) {
1131
1492
  interval.properties = createMap<any>();
@@ -1201,6 +1562,7 @@ export class LocalIntervalCollection<TInterval extends ISerializableInterval> {
1201
1562
  ref.getOffset(),
1202
1563
  ReferenceType.Transient,
1203
1564
  ref.properties,
1565
+ ref.slidingPreference,
1204
1566
  );
1205
1567
  };
1206
1568
  if (interval instanceof SequenceInterval) {
@@ -1243,18 +1605,36 @@ export class LocalIntervalCollection<TInterval extends ISerializableInterval> {
1243
1605
  export const compareSequenceIntervalEnds = (a: SequenceInterval, b: SequenceInterval): number =>
1244
1606
  compareReferencePositions(a.end, b.end);
1245
1607
 
1608
+ export const compareSequenceIntervalStarts = (a: SequenceInterval, b: SequenceInterval): number =>
1609
+ compareReferencePositions(a.start, b.start);
1610
+
1611
+ export const sequenceIntervalHelpers: IIntervalHelpers<SequenceInterval> = {
1612
+ compareEnds: compareSequenceIntervalEnds,
1613
+ compareStarts: compareSequenceIntervalStarts,
1614
+ create: createSequenceInterval,
1615
+ };
1616
+
1617
+ export const intervalHelpers: IIntervalHelpers<Interval> = {
1618
+ compareEnds: (a: Interval, b: Interval) => a.end - b.end,
1619
+ compareStarts: (a: Interval, b: Interval) => a.start - b.start,
1620
+ create: createInterval,
1621
+ };
1622
+
1246
1623
  class SequenceIntervalCollectionFactory
1247
1624
  implements IValueFactory<IntervalCollection<SequenceInterval>>
1248
1625
  {
1249
1626
  public load(
1250
1627
  emitter: IValueOpEmitter,
1251
1628
  raw: ISerializedInterval[] | ISerializedIntervalCollectionV2 = [],
1629
+ options?: Partial<SequenceOptions>,
1252
1630
  ): IntervalCollection<SequenceInterval> {
1253
- const helpers: IIntervalHelpers<SequenceInterval> = {
1254
- compareEnds: compareSequenceIntervalEnds,
1255
- create: createSequenceInterval,
1256
- };
1257
- return new IntervalCollection<SequenceInterval>(helpers, true, emitter, raw);
1631
+ return new IntervalCollection<SequenceInterval>(
1632
+ sequenceIntervalHelpers,
1633
+ true,
1634
+ emitter,
1635
+ raw,
1636
+ options,
1637
+ );
1258
1638
  }
1259
1639
 
1260
1640
  public store(
@@ -1289,7 +1669,15 @@ export class SequenceIntervalCollectionValueType
1289
1669
 
1290
1670
  const compareIntervalEnds = (a: Interval, b: Interval) => a.end - b.end;
1291
1671
 
1292
- function createInterval(label: string, start: number, end: number, client: Client): Interval {
1672
+ function createInterval(
1673
+ label: string,
1674
+ start: number,
1675
+ end: number,
1676
+ client: Client,
1677
+ intervalType?: IntervalType,
1678
+ op?: ISequencedDocumentMessage,
1679
+ fromSnapshot?: boolean,
1680
+ ): Interval {
1293
1681
  const rangeProp: PropertySet = {};
1294
1682
 
1295
1683
  if (label && label.length > 0) {
@@ -1303,12 +1691,13 @@ class IntervalCollectionFactory implements IValueFactory<IntervalCollection<Inte
1303
1691
  public load(
1304
1692
  emitter: IValueOpEmitter,
1305
1693
  raw: ISerializedInterval[] | ISerializedIntervalCollectionV2 = [],
1694
+ options?: Partial<SequenceOptions>,
1306
1695
  ): IntervalCollection<Interval> {
1307
1696
  const helpers: IIntervalHelpers<Interval> = {
1308
1697
  compareEnds: compareIntervalEnds,
1309
1698
  create: createInterval,
1310
1699
  };
1311
- const collection = new IntervalCollection<Interval>(helpers, false, emitter, raw);
1700
+ const collection = new IntervalCollection<Interval>(helpers, false, emitter, raw, options);
1312
1701
  collection.attachGraph(undefined as any as Client, "");
1313
1702
  return collection;
1314
1703
  }
@@ -1355,7 +1744,7 @@ export function makeOpsMap<T extends ISerializableInterval>(): Map<
1355
1744
 
1356
1745
  return new Map<string, IValueOperation<IntervalCollection<T>>>([
1357
1746
  [
1358
- "add",
1747
+ IntervalOpType.ADD,
1359
1748
  {
1360
1749
  process: (collection, params, local, op, localOpMetadata) => {
1361
1750
  // if params is undefined, the interval was deleted during
@@ -1370,7 +1759,7 @@ export function makeOpsMap<T extends ISerializableInterval>(): Map<
1370
1759
  },
1371
1760
  ],
1372
1761
  [
1373
- "delete",
1762
+ IntervalOpType.DELETE,
1374
1763
  {
1375
1764
  process: (collection, params, local, op) => {
1376
1765
  assert(op !== undefined, 0x3fc /* op should exist here */);
@@ -1383,7 +1772,7 @@ export function makeOpsMap<T extends ISerializableInterval>(): Map<
1383
1772
  },
1384
1773
  ],
1385
1774
  [
1386
- "change",
1775
+ IntervalOpType.CHANGE,
1387
1776
  {
1388
1777
  process: (collection, params, local, op, localOpMetadata) => {
1389
1778
  // if params is undefined, the interval was deleted during
@@ -1402,7 +1791,7 @@ export function makeOpsMap<T extends ISerializableInterval>(): Map<
1402
1791
 
1403
1792
  export type DeserializeCallback = (properties: PropertySet) => void;
1404
1793
 
1405
- export class IntervalCollectionIterator<TInterval extends ISerializableInterval>
1794
+ class IntervalCollectionIterator<TInterval extends ISerializableInterval>
1406
1795
  implements Iterator<TInterval>
1407
1796
  {
1408
1797
  private readonly results: TInterval[];
@@ -1495,14 +1884,137 @@ export interface IIntervalCollectionEvent<TInterval extends ISerializableInterva
1495
1884
 
1496
1885
  /**
1497
1886
  * Collection of intervals that supports addition, modification, removal, and efficient spatial querying.
1498
- * This class is not a DDS in its own right, but emits events on mutating operations such that it's possible to
1499
- * integrate into a DDS.
1500
- * This aligns with its usage in `SharedSegmentSequence`, which allows associating intervals to positions in the
1501
- * sequence DDS which are broadcast to all other clients in an eventually consistent fashion.
1887
+ * Changes to this collection will be incur updates on collaborating clients (i.e. they are not local-only).
1502
1888
  */
1503
- export class IntervalCollection<TInterval extends ISerializableInterval> extends TypedEventEmitter<
1504
- IIntervalCollectionEvent<TInterval>
1505
- > {
1889
+ export interface IIntervalCollection<TInterval extends ISerializableInterval>
1890
+ extends TypedEventEmitter<IIntervalCollectionEvent<TInterval>> {
1891
+ readonly attached: boolean;
1892
+ /**
1893
+ * Attaches an index to this collection.
1894
+ * All intervals which are part of this collection will be added to the index, and the index will automatically
1895
+ * be updated when this collection updates due to local or remote changes.
1896
+ *
1897
+ * @remarks - After attaching an index to an interval collection, applications should typically store this
1898
+ * index somewhere in their in-memory data model for future reference and querying.
1899
+ */
1900
+ attachIndex(index: IntervalIndex<TInterval>): void;
1901
+ /**
1902
+ * Detaches an index from this collection.
1903
+ * All intervals which are part of this collection will be removed from the index, and updates to this collection
1904
+ * due to local or remote changes will no longer incur updates to the index.
1905
+ *
1906
+ * @returns - Return false if the target index cannot be found in the indexes, otherwise remove all intervals in the index and return true
1907
+ */
1908
+ detachIndex(index: IntervalIndex<TInterval>): boolean;
1909
+ /**
1910
+ * @returns the interval in this collection that has the provided `id`.
1911
+ * If no interval in the collection has this `id`, returns `undefined`.
1912
+ */
1913
+ getIntervalById(id: string): TInterval | undefined;
1914
+ /**
1915
+ * Creates a new interval and add it to the collection.
1916
+ * @param start - interval start position (inclusive)
1917
+ * @param end - interval end position (exclusive)
1918
+ * @param intervalType - type of the interval. All intervals are SlideOnRemove. Intervals may not be Transient.
1919
+ * @param props - properties of the interval
1920
+ * @param stickiness - {@link (IntervalStickiness:type)} to apply to the added interval.
1921
+ * @returns - the created interval
1922
+ * @remarks - See documentation on {@link SequenceInterval} for comments on interval endpoint semantics: there are subtleties
1923
+ * with how the current half-open behavior is represented.
1924
+ */
1925
+ add(
1926
+ start: number,
1927
+ end: number,
1928
+ intervalType: IntervalType,
1929
+ props?: PropertySet,
1930
+ stickiness?: IntervalStickiness,
1931
+ ): TInterval;
1932
+ /**
1933
+ * Removes an interval from the collection.
1934
+ * @param id - Id of the interval to remove
1935
+ * @returns the removed interval
1936
+ */
1937
+ removeIntervalById(id: string): TInterval | undefined;
1938
+ /**
1939
+ * Changes the properties on an existing interval.
1940
+ * @param id - Id of the interval whose properties should be changed
1941
+ * @param props - Property set to apply to the interval. Shallow merging is used between any existing properties
1942
+ * and `prop`, i.e. the interval will end up with a property object equivalent to `{ ...oldProps, ...props }`.
1943
+ */
1944
+ changeProperties(id: string, props: PropertySet);
1945
+ /**
1946
+ * Changes the endpoints of an existing interval.
1947
+ * @param id - Id of the interval to change
1948
+ * @param start - New start value, if defined. `undefined` signifies this endpoint should be left unchanged.
1949
+ * @param end - New end value, if defined. `undefined` signifies this endpoint should be left unchanged.
1950
+ * @returns the interval that was changed, if it existed in the collection.
1951
+ */
1952
+ change(id: string, start?: number, end?: number): TInterval | undefined;
1953
+
1954
+ attachDeserializer(onDeserialize: DeserializeCallback): void;
1955
+ /**
1956
+ * @returns an iterator over all intervals in this collection.
1957
+ */
1958
+ [Symbol.iterator](): Iterator<TInterval>;
1959
+
1960
+ /**
1961
+ * @returns a forward iterator over all intervals in this collection with start point equal to `startPosition`.
1962
+ */
1963
+ CreateForwardIteratorWithStartPosition(startPosition: number): Iterator<TInterval>;
1964
+
1965
+ /**
1966
+ * @returns a backward iterator over all intervals in this collection with start point equal to `startPosition`.
1967
+ */
1968
+ CreateBackwardIteratorWithStartPosition(startPosition: number): Iterator<TInterval>;
1969
+
1970
+ /**
1971
+ * @returns a forward iterator over all intervals in this collection with end point equal to `endPosition`.
1972
+ */
1973
+ CreateForwardIteratorWithEndPosition(endPosition: number): Iterator<TInterval>;
1974
+
1975
+ /**
1976
+ * @returns a backward iterator over all intervals in this collection with end point equal to `endPosition`.
1977
+ */
1978
+ CreateBackwardIteratorWithEndPosition(endPosition: number): Iterator<TInterval>;
1979
+
1980
+ /**
1981
+ * Gathers iteration results that optionally match a start/end criteria into the provided array.
1982
+ * @param results - Array to gather the results into. In lieu of a return value, this array will be populated with
1983
+ * intervals matching the query upon edit.
1984
+ * @param iteratesForward - whether or not iteration should be in the forward direction
1985
+ * @param start - If provided, only match intervals whose start point is equal to `start`.
1986
+ * @param end - If provided, only match intervals whose end point is equal to `end`.
1987
+ */
1988
+ gatherIterationResults(
1989
+ results: TInterval[],
1990
+ iteratesForward: boolean,
1991
+ start?: number,
1992
+ end?: number,
1993
+ ): void;
1994
+
1995
+ /**
1996
+ * @returns an array of all intervals in this collection that overlap with the interval
1997
+ * `[startPosition, endPosition]`.
1998
+ */
1999
+ findOverlappingIntervals(startPosition: number, endPosition: number): TInterval[];
2000
+
2001
+ /**
2002
+ * Applies a function to each interval in this collection.
2003
+ */
2004
+ map(fn: (interval: TInterval) => void): void;
2005
+
2006
+ previousInterval(pos: number): TInterval | undefined;
2007
+
2008
+ nextInterval(pos: number): TInterval | undefined;
2009
+ }
2010
+
2011
+ /**
2012
+ * {@inheritdoc IIntervalCollection}
2013
+ */
2014
+ export class IntervalCollection<TInterval extends ISerializableInterval>
2015
+ extends TypedEventEmitter<IIntervalCollectionEvent<TInterval>>
2016
+ implements IIntervalCollection<TInterval>
2017
+ {
1506
2018
  private savedSerializedIntervals?: ISerializedInterval[];
1507
2019
  private localCollection: LocalIntervalCollection<TInterval> | undefined;
1508
2020
  private onDeserialize: DeserializeCallback | undefined;
@@ -1534,6 +2046,7 @@ export class IntervalCollection<TInterval extends ISerializableInterval> extends
1534
2046
  private readonly requiresClient: boolean,
1535
2047
  private readonly emitter: IValueOpEmitter,
1536
2048
  serializedIntervals: ISerializedInterval[] | ISerializedIntervalCollectionV2,
2049
+ private readonly options: Partial<SequenceOptions> = {},
1537
2050
  ) {
1538
2051
  super();
1539
2052
 
@@ -1544,6 +2057,40 @@ export class IntervalCollection<TInterval extends ISerializableInterval> extends
1544
2057
  );
1545
2058
  }
1546
2059
 
2060
+ /**
2061
+ * {@inheritdoc IIntervalCollection.attachIndex}
2062
+ */
2063
+ public attachIndex(index: IntervalIndex<TInterval>): void {
2064
+ if (!this.attached) {
2065
+ throw new LoggingError("The local interval collection must exist");
2066
+ }
2067
+ for (const interval of this) {
2068
+ index.add(interval);
2069
+ }
2070
+
2071
+ this.localCollection?.appendIndex(index);
2072
+ }
2073
+
2074
+ /**
2075
+ * {@inheritdoc IIntervalCollection.detachIndex}
2076
+ */
2077
+ public detachIndex(index: IntervalIndex<TInterval>): boolean {
2078
+ if (!this.attached) {
2079
+ throw new LoggingError("The local interval collection must exist");
2080
+ }
2081
+
2082
+ // Avoid removing intervals if the index does not exist
2083
+ if (!this.localCollection?.removeIndex(index)) {
2084
+ return false;
2085
+ }
2086
+
2087
+ for (const interval of this) {
2088
+ index.remove(interval);
2089
+ }
2090
+
2091
+ return true;
2092
+ }
2093
+
1547
2094
  private rebasePositionWithSegmentSlide(
1548
2095
  pos: number,
1549
2096
  seqNumberFrom: number,
@@ -1634,7 +2181,7 @@ export class IntervalCollection<TInterval extends ISerializableInterval> extends
1634
2181
  if (this.savedSerializedIntervals) {
1635
2182
  for (const serializedInterval of this.savedSerializedIntervals) {
1636
2183
  this.localCollection.ensureSerializedId(serializedInterval);
1637
- const { start, end, intervalType, properties } = serializedInterval;
2184
+ const { start, end, intervalType, properties, stickiness } = serializedInterval;
1638
2185
  const interval = this.helpers.create(
1639
2186
  label,
1640
2187
  start,
@@ -1643,6 +2190,7 @@ export class IntervalCollection<TInterval extends ISerializableInterval> extends
1643
2190
  intervalType,
1644
2191
  undefined,
1645
2192
  true,
2193
+ stickiness,
1646
2194
  );
1647
2195
  if (properties) {
1648
2196
  interval.addProperties(properties);
@@ -1689,8 +2237,7 @@ export class IntervalCollection<TInterval extends ISerializableInterval> extends
1689
2237
  }
1690
2238
 
1691
2239
  /**
1692
- * @returns the interval in this collection that has the provided `id`.
1693
- * If no interval in the collection has this `id`, returns `undefined`.
2240
+ * {@inheritdoc IIntervalCollection.getIntervalById}
1694
2241
  */
1695
2242
  public getIntervalById(id: string) {
1696
2243
  if (!this.localCollection) {
@@ -1700,20 +2247,14 @@ export class IntervalCollection<TInterval extends ISerializableInterval> extends
1700
2247
  }
1701
2248
 
1702
2249
  /**
1703
- * Creates a new interval and add it to the collection.
1704
- * @param start - interval start position (inclusive)
1705
- * @param end - interval end position (exclusive)
1706
- * @param intervalType - type of the interval. All intervals are SlideOnRemove. Intervals may not be Transient.
1707
- * @param props - properties of the interval
1708
- * @returns - the created interval
1709
- * @remarks - See documentation on {@link SequenceInterval} for comments on interval endpoint semantics: there are subtleties
1710
- * with how the current half-open behavior is represented.
2250
+ * {@inheritdoc IIntervalCollection.add}
1711
2251
  */
1712
2252
  public add(
1713
2253
  start: number,
1714
2254
  end: number,
1715
2255
  intervalType: IntervalType,
1716
2256
  props?: PropertySet,
2257
+ stickiness: IntervalStickiness = IntervalStickiness.END,
1717
2258
  ): TInterval {
1718
2259
  if (!this.localCollection) {
1719
2260
  throw new LoggingError("attach must be called prior to adding intervals");
@@ -1721,12 +2262,19 @@ export class IntervalCollection<TInterval extends ISerializableInterval> extends
1721
2262
  if (intervalType & IntervalType.Transient) {
1722
2263
  throw new LoggingError("Can not add transient intervals");
1723
2264
  }
2265
+ if (stickiness !== IntervalStickiness.END && !this.options.intervalStickinessEnabled) {
2266
+ throw new UsageError(
2267
+ "attempted to set interval stickiness without enabling `intervalStickinessEnabled` feature flag",
2268
+ );
2269
+ }
1724
2270
 
1725
2271
  const interval: TInterval = this.localCollection.addInterval(
1726
2272
  start,
1727
2273
  end,
1728
2274
  intervalType,
1729
2275
  props,
2276
+ undefined,
2277
+ stickiness,
1730
2278
  );
1731
2279
 
1732
2280
  if (interval) {
@@ -1736,6 +2284,7 @@ export class IntervalCollection<TInterval extends ISerializableInterval> extends
1736
2284
  properties: interval.properties,
1737
2285
  sequenceNumber: this.client?.getCurrentSeq() ?? 0,
1738
2286
  start,
2287
+ stickiness,
1739
2288
  };
1740
2289
  const localSeq = this.getNextLocalSeq();
1741
2290
  this.localSeqToSerializedInterval.set(localSeq, serializedInterval);
@@ -1776,9 +2325,7 @@ export class IntervalCollection<TInterval extends ISerializableInterval> extends
1776
2325
  }
1777
2326
 
1778
2327
  /**
1779
- * Removes an interval from the collection.
1780
- * @param id - Id of the interval to remove
1781
- * @returns the removed interval
2328
+ * {@inheritdoc IIntervalCollection.removeIntervalById}
1782
2329
  */
1783
2330
  public removeIntervalById(id: string) {
1784
2331
  if (!this.localCollection) {
@@ -1792,10 +2339,7 @@ export class IntervalCollection<TInterval extends ISerializableInterval> extends
1792
2339
  }
1793
2340
 
1794
2341
  /**
1795
- * Changes the properties on an existing interval.
1796
- * @param id - Id of the interval whose properties should be changed
1797
- * @param props - Property set to apply to the interval. Shallow merging is used between any existing properties
1798
- * and `prop`, i.e. the interval will end up with a property object equivalent to `{ ...oldProps, ...props }`.
2342
+ * {@inheritdoc IIntervalCollection.changeProperties}
1799
2343
  */
1800
2344
  public changeProperties(id: string, props: PropertySet) {
1801
2345
  if (!this.attached) {
@@ -1829,11 +2373,7 @@ export class IntervalCollection<TInterval extends ISerializableInterval> extends
1829
2373
  }
1830
2374
 
1831
2375
  /**
1832
- * Changes the endpoints of an existing interval.
1833
- * @param id - Id of the interval to change
1834
- * @param start - New start value, if defined. `undefined` signifies this endpoint should be left unchanged.
1835
- * @param end - New end value, if defined. `undefined` signifies this endpoint should be left unchanged.
1836
- * @returns the interval that was changed, if it existed in the collection.
2376
+ * {@inheritdoc IIntervalCollection.change}
1837
2377
  */
1838
2378
  public change(id: string, start?: number, end?: number): TInterval | undefined {
1839
2379
  if (!this.localCollection) {
@@ -2021,6 +2561,9 @@ export class IntervalCollection<TInterval extends ISerializableInterval> extends
2021
2561
  }
2022
2562
  }
2023
2563
 
2564
+ /**
2565
+ * {@inheritdoc IIntervalCollection.attachDeserializer}
2566
+ */
2024
2567
  public attachDeserializer(onDeserialize: DeserializeCallback): void {
2025
2568
  // If no deserializer is specified can skip all processing work
2026
2569
  if (!onDeserialize) {
@@ -2188,6 +2731,7 @@ export class IntervalCollection<TInterval extends ISerializableInterval> extends
2188
2731
  newStart,
2189
2732
  interval.start.refType,
2190
2733
  op,
2734
+ startReferenceSlidingPreference(interval.stickiness),
2191
2735
  );
2192
2736
  if (props) {
2193
2737
  interval.start.addProperties(props);
@@ -2205,6 +2749,7 @@ export class IntervalCollection<TInterval extends ISerializableInterval> extends
2205
2749
  newEnd,
2206
2750
  interval.end.refType,
2207
2751
  op,
2752
+ endReferenceSlidingPreference(interval.stickiness),
2208
2753
  );
2209
2754
  if (props) {
2210
2755
  interval.end.addProperties(props);
@@ -2253,6 +2798,7 @@ export class IntervalCollection<TInterval extends ISerializableInterval> extends
2253
2798
  serializedInterval.intervalType,
2254
2799
  serializedInterval.properties,
2255
2800
  op,
2801
+ serializedInterval.stickiness,
2256
2802
  );
2257
2803
 
2258
2804
  if (interval) {
@@ -2310,7 +2856,7 @@ export class IntervalCollection<TInterval extends ISerializableInterval> extends
2310
2856
  }
2311
2857
 
2312
2858
  /**
2313
- * @returns a forward iterator over all intervals in this collection with start point equal to `startPosition`.
2859
+ * {@inheritdoc IIntervalCollection.CreateForwardIteratorWithStartPosition}
2314
2860
  */
2315
2861
  public CreateForwardIteratorWithStartPosition(
2316
2862
  startPosition: number,
@@ -2320,7 +2866,7 @@ export class IntervalCollection<TInterval extends ISerializableInterval> extends
2320
2866
  }
2321
2867
 
2322
2868
  /**
2323
- * @returns a backward iterator over all intervals in this collection with start point equal to `startPosition`.
2869
+ * {@inheritdoc IIntervalCollection.CreateBackwardIteratorWithStartPosition}
2324
2870
  */
2325
2871
  public CreateBackwardIteratorWithStartPosition(
2326
2872
  startPosition: number,
@@ -2330,7 +2876,7 @@ export class IntervalCollection<TInterval extends ISerializableInterval> extends
2330
2876
  }
2331
2877
 
2332
2878
  /**
2333
- * @returns a forward iterator over all intervals in this collection with end point equal to `endPosition`.
2879
+ * {@inheritdoc IIntervalCollection.CreateForwardIteratorWithEndPosition}
2334
2880
  */
2335
2881
  public CreateForwardIteratorWithEndPosition(
2336
2882
  endPosition: number,
@@ -2345,7 +2891,7 @@ export class IntervalCollection<TInterval extends ISerializableInterval> extends
2345
2891
  }
2346
2892
 
2347
2893
  /**
2348
- * @returns a backward iterator over all intervals in this collection with end point equal to `endPosition`.
2894
+ * {@inheritdoc IIntervalCollection.CreateBackwardIteratorWithEndPosition}
2349
2895
  */
2350
2896
  public CreateBackwardIteratorWithEndPosition(
2351
2897
  endPosition: number,
@@ -2360,12 +2906,7 @@ export class IntervalCollection<TInterval extends ISerializableInterval> extends
2360
2906
  }
2361
2907
 
2362
2908
  /**
2363
- * Gathers iteration results that optionally match a start/end criteria into the provided array.
2364
- * @param results - Array to gather the results into. In lieu of a return value, this array will be populated with
2365
- * intervals matching the query upon edit.
2366
- * @param iteratesForward - whether or not iteration should be in the forward direction
2367
- * @param start - If provided, only match intervals whose start point is equal to `start`.
2368
- * @param end - If provided, only match intervals whose end point is equal to `end`.
2909
+ * {@inheritdoc IIntervalCollection.gatherIterationResults}
2369
2910
  */
2370
2911
  public gatherIterationResults(
2371
2912
  results: TInterval[],
@@ -2386,8 +2927,7 @@ export class IntervalCollection<TInterval extends ISerializableInterval> extends
2386
2927
  }
2387
2928
 
2388
2929
  /**
2389
- * @returns an array of all intervals in this collection that overlap with the interval
2390
- * `[startPosition, endPosition]`.
2930
+ * {@inheritdoc IIntervalCollection.findOverlappingIntervals}
2391
2931
  */
2392
2932
  public findOverlappingIntervals(startPosition: number, endPosition: number): TInterval[] {
2393
2933
  if (!this.localCollection) {
@@ -2401,7 +2941,7 @@ export class IntervalCollection<TInterval extends ISerializableInterval> extends
2401
2941
  }
2402
2942
 
2403
2943
  /**
2404
- * Applies a function to each interval in this collection.
2944
+ * {@inheritdoc IIntervalCollection.map}
2405
2945
  */
2406
2946
  public map(fn: (interval: TInterval) => void) {
2407
2947
  if (!this.localCollection) {
@@ -2413,6 +2953,9 @@ export class IntervalCollection<TInterval extends ISerializableInterval> extends
2413
2953
  }
2414
2954
  }
2415
2955
 
2956
+ /**
2957
+ * {@inheritdoc IIntervalCollection.previousInterval}
2958
+ */
2416
2959
  public previousInterval(pos: number): TInterval | undefined {
2417
2960
  if (!this.localCollection) {
2418
2961
  throw new LoggingError("attachSequence must be called");
@@ -2421,6 +2964,9 @@ export class IntervalCollection<TInterval extends ISerializableInterval> extends
2421
2964
  return this.localCollection.endIntervalIndex.previousInterval(pos);
2422
2965
  }
2423
2966
 
2967
+ /**
2968
+ * {@inheritdoc IIntervalCollection.nextInterval}
2969
+ */
2424
2970
  public nextInterval(pos: number): TInterval | undefined {
2425
2971
  if (!this.localCollection) {
2426
2972
  throw new LoggingError("attachSequence must be called");