@fluidframework/sequence 2.0.0-dev.4.4.0.162574 → 2.0.0-dev.5.3.2.178189

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 (107) hide show
  1. package/CHANGELOG.md +48 -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 +4 -2
  10. package/dist/index.d.ts.map +1 -1
  11. package/dist/index.js +17 -3
  12. package/dist/index.js.map +1 -1
  13. package/dist/intervalCollection.d.ts +240 -78
  14. package/dist/intervalCollection.d.ts.map +1 -1
  15. package/dist/intervalCollection.js +313 -190
  16. package/dist/intervalCollection.js.map +1 -1
  17. package/dist/intervalIndex/index.d.ts +8 -0
  18. package/dist/intervalIndex/index.d.ts.map +1 -0
  19. package/dist/intervalIndex/index.js +12 -0
  20. package/dist/intervalIndex/index.js.map +1 -0
  21. package/dist/intervalIndex/overlappingIntervalsIndex.d.ts +32 -0
  22. package/dist/intervalIndex/overlappingIntervalsIndex.d.ts.map +1 -0
  23. package/dist/intervalIndex/overlappingIntervalsIndex.js +103 -0
  24. package/dist/intervalIndex/overlappingIntervalsIndex.js.map +1 -0
  25. package/dist/intervalIndex/overlappingSequenceIntervalsIndex.d.ts +8 -0
  26. package/dist/intervalIndex/overlappingSequenceIntervalsIndex.d.ts.map +1 -0
  27. package/dist/intervalIndex/overlappingSequenceIntervalsIndex.js +33 -0
  28. package/dist/intervalIndex/overlappingSequenceIntervalsIndex.js.map +1 -0
  29. package/dist/intervalIndex/sequenceIntervalIndexes.d.ts +33 -0
  30. package/dist/intervalIndex/sequenceIntervalIndexes.d.ts.map +1 -0
  31. package/dist/intervalIndex/sequenceIntervalIndexes.js +7 -0
  32. package/dist/intervalIndex/sequenceIntervalIndexes.js.map +1 -0
  33. package/dist/packageVersion.d.ts +1 -1
  34. package/dist/packageVersion.js +1 -1
  35. package/dist/packageVersion.js.map +1 -1
  36. package/dist/revertibles.d.ts +104 -0
  37. package/dist/revertibles.d.ts.map +1 -0
  38. package/dist/revertibles.js +414 -0
  39. package/dist/revertibles.js.map +1 -0
  40. package/dist/sequence.d.ts +4 -4
  41. package/dist/sequence.d.ts.map +1 -1
  42. package/dist/sequence.js +3 -3
  43. package/dist/sequence.js.map +1 -1
  44. package/dist/sharedIntervalCollection.d.ts +3 -3
  45. package/dist/sharedIntervalCollection.d.ts.map +1 -1
  46. package/dist/sharedIntervalCollection.js +1 -1
  47. package/dist/sharedIntervalCollection.js.map +1 -1
  48. package/dist/tsdoc-metadata.json +11 -0
  49. package/lib/defaultMap.d.ts +3 -2
  50. package/lib/defaultMap.d.ts.map +1 -1
  51. package/lib/defaultMap.js +4 -3
  52. package/lib/defaultMap.js.map +1 -1
  53. package/lib/defaultMapInterfaces.d.ts +12 -1
  54. package/lib/defaultMapInterfaces.d.ts.map +1 -1
  55. package/lib/defaultMapInterfaces.js.map +1 -1
  56. package/lib/index.d.ts +4 -2
  57. package/lib/index.d.ts.map +1 -1
  58. package/lib/index.js +3 -1
  59. package/lib/index.js.map +1 -1
  60. package/lib/intervalCollection.d.ts +240 -78
  61. package/lib/intervalCollection.d.ts.map +1 -1
  62. package/lib/intervalCollection.js +310 -190
  63. package/lib/intervalCollection.js.map +1 -1
  64. package/lib/intervalIndex/index.d.ts +8 -0
  65. package/lib/intervalIndex/index.d.ts.map +1 -0
  66. package/lib/intervalIndex/index.js +7 -0
  67. package/lib/intervalIndex/index.js.map +1 -0
  68. package/lib/intervalIndex/overlappingIntervalsIndex.d.ts +32 -0
  69. package/lib/intervalIndex/overlappingIntervalsIndex.d.ts.map +1 -0
  70. package/lib/intervalIndex/overlappingIntervalsIndex.js +98 -0
  71. package/lib/intervalIndex/overlappingIntervalsIndex.js.map +1 -0
  72. package/lib/intervalIndex/overlappingSequenceIntervalsIndex.d.ts +8 -0
  73. package/lib/intervalIndex/overlappingSequenceIntervalsIndex.d.ts.map +1 -0
  74. package/lib/intervalIndex/overlappingSequenceIntervalsIndex.js +29 -0
  75. package/lib/intervalIndex/overlappingSequenceIntervalsIndex.js.map +1 -0
  76. package/lib/intervalIndex/sequenceIntervalIndexes.d.ts +33 -0
  77. package/lib/intervalIndex/sequenceIntervalIndexes.d.ts.map +1 -0
  78. package/lib/intervalIndex/sequenceIntervalIndexes.js +6 -0
  79. package/lib/intervalIndex/sequenceIntervalIndexes.js.map +1 -0
  80. package/lib/packageVersion.d.ts +1 -1
  81. package/lib/packageVersion.js +1 -1
  82. package/lib/packageVersion.js.map +1 -1
  83. package/lib/revertibles.d.ts +104 -0
  84. package/lib/revertibles.d.ts.map +1 -0
  85. package/lib/revertibles.js +404 -0
  86. package/lib/revertibles.js.map +1 -0
  87. package/lib/sequence.d.ts +4 -4
  88. package/lib/sequence.d.ts.map +1 -1
  89. package/lib/sequence.js +3 -3
  90. package/lib/sequence.js.map +1 -1
  91. package/lib/sharedIntervalCollection.d.ts +3 -3
  92. package/lib/sharedIntervalCollection.d.ts.map +1 -1
  93. package/lib/sharedIntervalCollection.js +1 -1
  94. package/lib/sharedIntervalCollection.js.map +1 -1
  95. package/package.json +22 -24
  96. package/src/defaultMap.ts +4 -1
  97. package/src/defaultMapInterfaces.ts +13 -1
  98. package/src/index.ts +27 -5
  99. package/src/intervalCollection.ts +660 -216
  100. package/src/intervalIndex/index.ts +11 -0
  101. package/src/intervalIndex/overlappingIntervalsIndex.ts +166 -0
  102. package/src/intervalIndex/overlappingSequenceIntervalsIndex.ts +71 -0
  103. package/src/intervalIndex/sequenceIntervalIndexes.ts +32 -0
  104. package/src/packageVersion.ts +1 -1
  105. package/src/revertibles.ts +626 -0
  106. package/src/sequence.ts +12 -2
  107. package/src/sharedIntervalCollection.ts +4 -2
@@ -16,11 +16,22 @@ var __rest = (this && this.__rest) || function (s, e) {
16
16
  /* eslint-disable no-bitwise */
17
17
  import { assert, TypedEventEmitter } from "@fluidframework/common-utils";
18
18
  import { UsageError } from "@fluidframework/container-utils";
19
- import { addProperties, compareReferencePositions, createMap, MergeTreeDeltaType, minReferencePosition, PropertiesManager, RedBlackTree, ReferenceType, refTypeIncludesFlag, reservedRangeLabelsKey, UnassignedSequenceNumber, maxReferencePosition, createDetachedLocalReferencePosition, DetachedReferencePosition, } from "@fluidframework/merge-tree";
19
+ import { addProperties, compareReferencePositions, createMap, getSlideToSegoff, MergeTreeDeltaType, minReferencePosition, PropertiesManager, RedBlackTree, ReferenceType, refTypeIncludesFlag, reservedRangeLabelsKey, UnassignedSequenceNumber, maxReferencePosition, createDetachedLocalReferencePosition, DetachedReferencePosition, SlidingPreference, } from "@fluidframework/merge-tree";
20
20
  import { LoggingError } from "@fluidframework/telemetry-utils";
21
21
  import { v4 as uuid } from "uuid";
22
- import { IntervalTree } from "./intervalTree";
22
+ import { createOverlappingIntervalsIndex } from "./intervalIndex";
23
23
  const reservedIntervalIdKey = "intervalId";
24
+ /**
25
+ * Values are used in persisted formats (ops) and revertibles.
26
+ * @alpha
27
+ */
28
+ export const IntervalOpType = {
29
+ ADD: "add",
30
+ DELETE: "delete",
31
+ CHANGE: "change",
32
+ PROPERTY_CHANGED: "propertyChanged",
33
+ POSITION_REMOVE: "positionRemove",
34
+ };
24
35
  export var IntervalType;
25
36
  (function (IntervalType) {
26
37
  IntervalType[IntervalType["Simple"] = 0] = "Simple";
@@ -28,7 +39,7 @@ export var IntervalType;
28
39
  /**
29
40
  * SlideOnRemove indicates that the ends of the interval will slide if the segment
30
41
  * they reference is removed and acked.
31
- * See `packages\dds\merge-tree\REFERENCEPOSITIONS.md` for details
42
+ * See `packages\dds\merge-tree\docs\REFERENCEPOSITIONS.md` for details
32
43
  * SlideOnRemove is the default interval behavior and does not need to be specified.
33
44
  */
34
45
  IntervalType[IntervalType["SlideOnRemove"] = 2] = "SlideOnRemove";
@@ -49,6 +60,7 @@ function decompressInterval(interval, label) {
49
60
  sequenceNumber: interval[2],
50
61
  intervalType: interval[3],
51
62
  properties: Object.assign(Object.assign({}, interval[4]), { [reservedRangeLabelsKey]: [label] }),
63
+ stickiness: interval[5],
52
64
  };
53
65
  }
54
66
  /**
@@ -57,14 +69,57 @@ function decompressInterval(interval, label) {
57
69
  */
58
70
  function compressInterval(interval) {
59
71
  const { start, end, sequenceNumber, intervalType, properties } = interval;
60
- return [
72
+ const base = [
61
73
  start,
62
74
  end,
63
75
  sequenceNumber,
64
76
  intervalType,
65
77
  Object.assign(Object.assign({}, properties), { [reservedRangeLabelsKey]: undefined }),
66
78
  ];
79
+ if (interval.stickiness !== undefined && interval.stickiness !== IntervalStickiness.END) {
80
+ base.push(interval.stickiness);
81
+ }
82
+ return base;
83
+ }
84
+ function startReferenceSlidingPreference(stickiness) {
85
+ // if any start stickiness, prefer sliding backwards
86
+ return (stickiness & IntervalStickiness.START) !== 0
87
+ ? SlidingPreference.BACKWARD
88
+ : SlidingPreference.FORWARD;
89
+ }
90
+ function endReferenceSlidingPreference(stickiness) {
91
+ // if any end stickiness, prefer sliding forwards
92
+ return (stickiness & IntervalStickiness.END) !== 0
93
+ ? SlidingPreference.FORWARD
94
+ : SlidingPreference.BACKWARD;
67
95
  }
96
+ /**
97
+ * Determines how an interval should expand when segments are inserted adjacent
98
+ * to the range it spans
99
+ *
100
+ * Note that interval stickiness is currently an experimental feature and must
101
+ * be explicitly enabled with the `intervalStickinessEnabled` flag
102
+ */
103
+ export const IntervalStickiness = {
104
+ /**
105
+ * Interval does not expand to include adjacent segments
106
+ */
107
+ NONE: 0b00,
108
+ /**
109
+ * Interval expands to include segments inserted adjacent to the start
110
+ */
111
+ START: 0b01,
112
+ /**
113
+ * Interval expands to include segments inserted adjacent to the end
114
+ *
115
+ * This is the default stickiness
116
+ */
117
+ END: 0b10,
118
+ /**
119
+ * Interval expands to include all segments inserted adjacent to it
120
+ */
121
+ FULL: 0b11,
122
+ };
68
123
  /**
69
124
  * Serializable interval whose endpoints are plain-old numbers.
70
125
  */
@@ -98,7 +153,7 @@ export class Interval {
98
153
  * Adds an auxiliary set of properties to this interval.
99
154
  * These properties can be recovered using `getAdditionalPropertySets`
100
155
  * @param props - set of properties to add
101
- * @remarks - This gets called as part of the default conflict resolver for `IntervalCollection<Interval>`
156
+ * @remarks - This gets called as part of the default conflict resolver for `IIntervalCollection<Interval>`
102
157
  * (i.e. non-sequence-based interval collections). However, the additional properties don't get serialized.
103
158
  * This functionality seems half-baked.
104
159
  */
@@ -223,7 +278,7 @@ export class Interval {
223
278
  }
224
279
  }
225
280
  /**
226
- * Interval impelmentation whose ends are associated with positions in a mutatable sequence.
281
+ * Interval implementation whose ends are associated with positions in a mutatable sequence.
227
282
  * As such, when content is inserted into the middle of the interval, the interval expands to
228
283
  * include that content.
229
284
  *
@@ -254,11 +309,12 @@ export class SequenceInterval {
254
309
  * End endpoint of this interval.
255
310
  * @remarks - This endpoint can be resolved into a character position using the SharedString it's a part of.
256
311
  */
257
- end, intervalType, props) {
312
+ end, intervalType, props, stickiness = IntervalStickiness.END) {
258
313
  this.client = client;
259
314
  this.start = start;
260
315
  this.end = end;
261
316
  this.intervalType = intervalType;
317
+ this.stickiness = stickiness;
262
318
  this.propertyManager = new PropertiesManager();
263
319
  this.properties = {};
264
320
  if (props) {
@@ -310,13 +366,16 @@ export class SequenceInterval {
310
366
  if (this.properties) {
311
367
  serializedInterval.properties = this.properties;
312
368
  }
369
+ if (this.stickiness !== IntervalStickiness.END) {
370
+ serializedInterval.stickiness = this.stickiness;
371
+ }
313
372
  return serializedInterval;
314
373
  }
315
374
  /**
316
375
  * {@inheritDoc IInterval.clone}
317
376
  */
318
377
  clone() {
319
- return new SequenceInterval(this.client, this.start, this.end, this.intervalType, this.properties);
378
+ return new SequenceInterval(this.client, this.start, this.end, this.intervalType, this.properties, this.stickiness);
320
379
  }
321
380
  /**
322
381
  * {@inheritDoc IInterval.compare}
@@ -400,7 +459,7 @@ export class SequenceInterval {
400
459
  * {@inheritDoc IInterval.modify}
401
460
  * @deprecated - This API was never intended to be public and will be marked internal in a future release.
402
461
  */
403
- modify(label, start, end, op, localSeq) {
462
+ modify(label, start, end, op, localSeq, stickiness = IntervalStickiness.END) {
404
463
  const getRefType = (baseType) => {
405
464
  let refType = baseType;
406
465
  if (op === undefined) {
@@ -411,14 +470,14 @@ export class SequenceInterval {
411
470
  };
412
471
  let startRef = this.start;
413
472
  if (start !== undefined) {
414
- startRef = createPositionReference(this.client, start, getRefType(this.start.refType), op, undefined, localSeq);
473
+ startRef = createPositionReference(this.client, start, getRefType(this.start.refType), op, undefined, localSeq, startReferenceSlidingPreference(stickiness));
415
474
  if (this.start.properties) {
416
475
  startRef.addProperties(this.start.properties);
417
476
  }
418
477
  }
419
478
  let endRef = this.end;
420
479
  if (end !== undefined) {
421
- endRef = createPositionReference(this.client, end, getRefType(this.end.refType), op, undefined, localSeq);
480
+ endRef = createPositionReference(this.client, end, getRefType(this.end.refType), op, undefined, localSeq, endReferenceSlidingPreference(stickiness));
422
481
  if (this.end.properties) {
423
482
  endRef.addProperties(this.end.properties);
424
483
  }
@@ -439,9 +498,9 @@ export class SequenceInterval {
439
498
  }
440
499
  }
441
500
  }
442
- function createPositionReferenceFromSegoff(client, segoff, refType, op, localSeq, fromSnapshot) {
501
+ export function createPositionReferenceFromSegoff(client, segoff, refType, op, localSeq, fromSnapshot, slidingPreference) {
443
502
  if (segoff.segment) {
444
- const ref = client.createLocalReferencePosition(segoff.segment, segoff.offset, refType, undefined);
503
+ const ref = client.createLocalReferencePosition(segoff.segment, segoff.offset, refType, undefined, slidingPreference);
445
504
  return ref;
446
505
  }
447
506
  // Creating references on detached segments is allowed for:
@@ -457,7 +516,7 @@ function createPositionReferenceFromSegoff(client, segoff, refType, op, localSeq
457
516
  }
458
517
  return createDetachedLocalReferencePosition(refType);
459
518
  }
460
- function createPositionReference(client, pos, refType, op, fromSnapshot, localSeq) {
519
+ function createPositionReference(client, pos, refType, op, fromSnapshot, localSeq, slidingPreference) {
461
520
  let segoff;
462
521
  if (op) {
463
522
  assert((refType & ReferenceType.SlideOnRemove) !== 0, 0x2f5 /* op create references must be SlideOnRemove */);
@@ -465,15 +524,15 @@ function createPositionReference(client, pos, refType, op, fromSnapshot, localSe
465
524
  referenceSequenceNumber: op.referenceSequenceNumber,
466
525
  clientId: op.clientId,
467
526
  });
468
- segoff = client.getSlideToSegment(segoff);
527
+ segoff = getSlideToSegoff(segoff);
469
528
  }
470
529
  else {
471
530
  assert((refType & ReferenceType.SlideOnRemove) === 0 || !!fromSnapshot, 0x2f6 /* SlideOnRemove references must be op created */);
472
531
  segoff = client.getContainingSegment(pos, undefined, localSeq);
473
532
  }
474
- return createPositionReferenceFromSegoff(client, segoff, refType, op, localSeq, fromSnapshot);
533
+ return createPositionReferenceFromSegoff(client, segoff, refType, op, localSeq, fromSnapshot, slidingPreference);
475
534
  }
476
- export function createSequenceInterval(label, start, end, client, intervalType, op, fromSnapshot) {
535
+ export function createSequenceInterval(label, start, end, client, intervalType, op, fromSnapshot, stickiness = IntervalStickiness.END) {
477
536
  let beginRefType = ReferenceType.RangeBegin;
478
537
  let endRefType = ReferenceType.RangeEnd;
479
538
  if (intervalType === IntervalType.Transient) {
@@ -497,14 +556,14 @@ export function createSequenceInterval(label, start, end, client, intervalType,
497
556
  endRefType |= ReferenceType.StayOnRemove;
498
557
  }
499
558
  }
500
- const startLref = createPositionReference(client, start, beginRefType, op, fromSnapshot);
501
- const endLref = createPositionReference(client, end, endRefType, op, fromSnapshot);
559
+ const startLref = createPositionReference(client, start, beginRefType, op, fromSnapshot, undefined, startReferenceSlidingPreference(stickiness));
560
+ const endLref = createPositionReference(client, end, endRefType, op, fromSnapshot, undefined, endReferenceSlidingPreference(stickiness));
502
561
  const rangeProp = {
503
562
  [reservedRangeLabelsKey]: [label],
504
563
  };
505
564
  startLref.addProperties(rangeProp);
506
565
  endLref.addProperties(rangeProp);
507
- const ival = new SequenceInterval(client, startLref, endLref, intervalType, rangeProp);
566
+ const ival = new SequenceInterval(client, startLref, endLref, intervalType, rangeProp, stickiness);
508
567
  return ival;
509
568
  }
510
569
  export function createIntervalIndex() {
@@ -515,98 +574,6 @@ export function createIntervalIndex() {
515
574
  const lc = new LocalIntervalCollection(undefined, "", helpers);
516
575
  return lc;
517
576
  }
518
- class OverlappingIntervalsIndex {
519
- constructor(client, helpers) {
520
- this.client = client;
521
- this.helpers = helpers;
522
- this.intervalTree = new IntervalTree();
523
- }
524
- map(fn) {
525
- this.intervalTree.map(fn);
526
- }
527
- mapUntil(fn) {
528
- this.intervalTree.mapUntil(fn);
529
- }
530
- gatherIterationResults(results, iteratesForward, start, end) {
531
- if (this.intervalTree.intervals.isEmpty()) {
532
- return;
533
- }
534
- if (start === undefined && end === undefined) {
535
- // No start/end provided. Gather the whole tree in the specified order.
536
- if (iteratesForward) {
537
- this.intervalTree.map((interval) => {
538
- results.push(interval);
539
- });
540
- }
541
- else {
542
- this.intervalTree.mapBackward((interval) => {
543
- results.push(interval);
544
- });
545
- }
546
- }
547
- else {
548
- const transientInterval = this.helpers.create("transient", start, end, this.client, IntervalType.Transient);
549
- if (start === undefined) {
550
- // Only end position provided. Since the tree is not sorted by end position,
551
- // walk the whole tree in the specified order, gathering intervals that match the end.
552
- if (iteratesForward) {
553
- this.intervalTree.map((interval) => {
554
- if (transientInterval.compareEnd(interval) === 0) {
555
- results.push(interval);
556
- }
557
- });
558
- }
559
- else {
560
- this.intervalTree.mapBackward((interval) => {
561
- if (transientInterval.compareEnd(interval) === 0) {
562
- results.push(interval);
563
- }
564
- });
565
- }
566
- }
567
- else {
568
- // Start and (possibly) end provided. Walk the subtrees that may contain
569
- // this start position.
570
- const compareFn = end === undefined
571
- ? (node) => {
572
- return transientInterval.compareStart(node.key);
573
- }
574
- : (node) => {
575
- return transientInterval.compare(node.key);
576
- };
577
- const continueLeftFn = (cmpResult) => cmpResult <= 0;
578
- const continueRightFn = (cmpResult) => cmpResult >= 0;
579
- const actionFn = (node) => {
580
- results.push(node.key);
581
- };
582
- if (iteratesForward) {
583
- this.intervalTree.intervals.walkExactMatchesForward(compareFn, actionFn, continueLeftFn, continueRightFn);
584
- }
585
- else {
586
- this.intervalTree.intervals.walkExactMatchesBackward(compareFn, actionFn, continueLeftFn, continueRightFn);
587
- }
588
- }
589
- }
590
- }
591
- /**
592
- * @returns an array of all intervals contained in this collection that overlap the range
593
- * `[startPosition, endPosition)`.
594
- */
595
- findOverlappingIntervals(startPosition, endPosition) {
596
- if (endPosition < startPosition || this.intervalTree.intervals.isEmpty()) {
597
- return [];
598
- }
599
- const transientInterval = this.helpers.create("transient", startPosition, endPosition, this.client, IntervalType.Transient);
600
- const overlappingIntervalNodes = this.intervalTree.match(transientInterval);
601
- return overlappingIntervalNodes.map((node) => node.key);
602
- }
603
- remove(interval) {
604
- this.intervalTree.removeExisting(interval);
605
- }
606
- add(interval) {
607
- this.intervalTree.put(interval);
608
- }
609
- }
610
577
  class IdIntervalIndex {
611
578
  constructor() {
612
579
  this.intervalIdMap = new Map();
@@ -662,6 +629,117 @@ class EndpointIndex {
662
629
  this.endIntervalTree.remove(interval);
663
630
  }
664
631
  }
632
+ /**
633
+ * Interface for intervals that have comparison override properties.
634
+ */
635
+ const forceCompare = Symbol();
636
+ /**
637
+ * Compares two objects based on their comparison override properties.
638
+ * @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).
639
+ */
640
+ function compareOverrideables(a, b) {
641
+ var _a, _b;
642
+ const forceCompareA = (_a = a[forceCompare]) !== null && _a !== void 0 ? _a : 0;
643
+ const forceCompareB = (_b = b[forceCompare]) !== null && _b !== void 0 ? _b : 0;
644
+ return forceCompareA - forceCompareB;
645
+ }
646
+ class EndpointInRangeIndex {
647
+ constructor(helpers, client) {
648
+ this.helpers = helpers;
649
+ this.client = client;
650
+ this.intervalTree = new RedBlackTree((a, b) => {
651
+ const compareEndsResult = helpers.compareEnds(a, b);
652
+ if (compareEndsResult !== 0) {
653
+ return compareEndsResult;
654
+ }
655
+ const overrideablesComparison = compareOverrideables(a, b);
656
+ if (overrideablesComparison !== 0) {
657
+ return overrideablesComparison;
658
+ }
659
+ const aId = a.getIntervalId();
660
+ const bId = b.getIntervalId();
661
+ if (aId !== undefined && bId !== undefined) {
662
+ return aId.localeCompare(bId);
663
+ }
664
+ return 0;
665
+ });
666
+ }
667
+ add(interval) {
668
+ this.intervalTree.put(interval, interval);
669
+ }
670
+ remove(interval) {
671
+ this.intervalTree.remove(interval);
672
+ }
673
+ findIntervalsWithEndpointInRange(start, end) {
674
+ if (start <= 0 || start > end || this.intervalTree.isEmpty()) {
675
+ return [];
676
+ }
677
+ const results = [];
678
+ const action = (node) => {
679
+ results.push(node.data);
680
+ return true;
681
+ };
682
+ const transientStartInterval = this.helpers.create("transient", start, start, this.client, IntervalType.Transient);
683
+ const transientEndInterval = this.helpers.create("transient", end, end, this.client, IntervalType.Transient);
684
+ // Add comparison overrides to the transient intervals
685
+ transientStartInterval[forceCompare] = -1;
686
+ transientEndInterval[forceCompare] = 1;
687
+ this.intervalTree.mapRange(action, results, transientStartInterval, transientEndInterval);
688
+ return results;
689
+ }
690
+ }
691
+ class StartpointInRangeIndex {
692
+ constructor(helpers, client) {
693
+ this.helpers = helpers;
694
+ this.client = client;
695
+ this.intervalTree = new RedBlackTree((a, b) => {
696
+ assert(typeof helpers.compareStarts === "function", 0x6d1 /* compareStarts does not exist in the helpers */);
697
+ const compareStartsResult = helpers.compareStarts(a, b);
698
+ if (compareStartsResult !== 0) {
699
+ return compareStartsResult;
700
+ }
701
+ const overrideablesComparison = compareOverrideables(a, b);
702
+ if (overrideablesComparison !== 0) {
703
+ return overrideablesComparison;
704
+ }
705
+ const aId = a.getIntervalId();
706
+ const bId = b.getIntervalId();
707
+ if (aId !== undefined && bId !== undefined) {
708
+ return aId.localeCompare(bId);
709
+ }
710
+ return 0;
711
+ });
712
+ }
713
+ add(interval) {
714
+ this.intervalTree.put(interval, interval);
715
+ }
716
+ remove(interval) {
717
+ this.intervalTree.remove(interval);
718
+ }
719
+ findIntervalsWithStartpointInRange(start, end) {
720
+ if (start <= 0 || start > end || this.intervalTree.isEmpty()) {
721
+ return [];
722
+ }
723
+ const results = [];
724
+ const action = (node) => {
725
+ results.push(node.data);
726
+ return true;
727
+ };
728
+ const transientStartInterval = this.helpers.create("transient", start, start, this.client, IntervalType.Transient);
729
+ const transientEndInterval = this.helpers.create("transient", end, end, this.client, IntervalType.Transient);
730
+ // Add comparison overrides to the transient intervals
731
+ transientStartInterval[forceCompare] = -1;
732
+ transientEndInterval[forceCompare] = 1;
733
+ this.intervalTree.mapRange(action, results, transientStartInterval, transientEndInterval);
734
+ return results;
735
+ }
736
+ }
737
+ export function createEndpointInRangeIndex(helpers, client) {
738
+ return new EndpointInRangeIndex(helpers, client);
739
+ }
740
+ export function createStartpointInRangeIndex(helpers, client) {
741
+ return new StartpointInRangeIndex(helpers, client);
742
+ }
665
743
  export class LocalIntervalCollection {
666
744
  constructor(client, label, helpers,
667
745
  /** Callback invoked each time one of the endpoints of an interval slides. */
@@ -670,14 +748,14 @@ export class LocalIntervalCollection {
670
748
  this.label = label;
671
749
  this.helpers = helpers;
672
750
  this.onPositionChange = onPositionChange;
673
- this.overlappingIntervalsIndex = new OverlappingIntervalsIndex(client, helpers);
751
+ this.overlappingIntervalsIndex = createOverlappingIntervalsIndex(client, helpers);
674
752
  this.idIntervalIndex = new IdIntervalIndex();
675
753
  this.endIntervalIndex = new EndpointIndex(client, helpers);
676
- this.indexes = [
754
+ this.indexes = new Set([
677
755
  this.overlappingIntervalsIndex,
678
756
  this.idIntervalIndex,
679
757
  this.endIntervalIndex,
680
- ];
758
+ ]);
681
759
  }
682
760
  createLegacyId(start, end) {
683
761
  // Create a non-unique ID based on start and end to be used on intervals that come from legacy clients
@@ -717,22 +795,35 @@ export class LocalIntervalCollection {
717
795
  index.remove(interval);
718
796
  }
719
797
  }
798
+ appendIndex(index) {
799
+ this.indexes.add(index);
800
+ }
801
+ removeIndex(index) {
802
+ return this.indexes.delete(index);
803
+ }
720
804
  removeExistingInterval(interval) {
721
805
  this.removeIntervalFromIndexes(interval);
722
806
  this.removeIntervalListeners(interval);
723
807
  }
724
- createInterval(start, end, intervalType, op) {
725
- return this.helpers.create(this.label, start, end, this.client, intervalType, op);
808
+ createInterval(start, end, intervalType, op, stickiness = IntervalStickiness.END) {
809
+ return this.helpers.create(this.label, start, end, this.client, intervalType, op, undefined, stickiness);
726
810
  }
727
- addInterval(start, end, intervalType, props, op) {
811
+ addInterval(start, end, intervalType, props, op, stickiness = IntervalStickiness.END) {
728
812
  var _a;
729
813
  var _b;
730
- const interval = this.createInterval(start, end, intervalType, op);
814
+ const interval = this.createInterval(start, end, intervalType, op, stickiness);
731
815
  if (interval) {
732
816
  if (!interval.properties) {
733
817
  interval.properties = createMap();
734
818
  }
735
819
  if (props) {
820
+ // This check is intended to prevent scenarios where a random interval is created and then
821
+ // inserted into a collection. The aim is to ensure that the collection is created first
822
+ // then the user can create/add intervals based on the collection
823
+ if (props[reservedRangeLabelsKey] !== undefined &&
824
+ props[reservedRangeLabelsKey][0] !== this.label) {
825
+ throw new LoggingError("Adding an interval that belongs to another interval collection is not permitted");
826
+ }
736
827
  interval.addProperties(props);
737
828
  }
738
829
  (_a = (_b = interval.properties)[reservedIntervalIdKey]) !== null && _a !== void 0 ? _a : (_b[reservedIntervalIdKey] = uuid());
@@ -780,7 +871,7 @@ export class LocalIntervalCollection {
780
871
  // either, so this must be special-cased.
781
872
  return ref;
782
873
  }
783
- return this.client.createLocalReferencePosition(segment, ref.getOffset(), ReferenceType.Transient, ref.properties);
874
+ return this.client.createLocalReferencePosition(segment, ref.getOffset(), ReferenceType.Transient, ref.properties, ref.slidingPreference);
784
875
  };
785
876
  if (interval instanceof SequenceInterval) {
786
877
  let previousInterval;
@@ -814,13 +905,20 @@ export class LocalIntervalCollection {
814
905
  }
815
906
  LocalIntervalCollection.legacyIdPrefix = "legacy";
816
907
  export const compareSequenceIntervalEnds = (a, b) => compareReferencePositions(a.end, b.end);
908
+ export const compareSequenceIntervalStarts = (a, b) => compareReferencePositions(a.start, b.start);
909
+ export const sequenceIntervalHelpers = {
910
+ compareEnds: compareSequenceIntervalEnds,
911
+ compareStarts: compareSequenceIntervalStarts,
912
+ create: createSequenceInterval,
913
+ };
914
+ export const intervalHelpers = {
915
+ compareEnds: (a, b) => a.end - b.end,
916
+ compareStarts: (a, b) => a.start - b.start,
917
+ create: createInterval,
918
+ };
817
919
  class SequenceIntervalCollectionFactory {
818
- load(emitter, raw = []) {
819
- const helpers = {
820
- compareEnds: compareSequenceIntervalEnds,
821
- create: createSequenceInterval,
822
- };
823
- return new IntervalCollection(helpers, true, emitter, raw);
920
+ load(emitter, raw = [], options) {
921
+ return new IntervalCollection(sequenceIntervalHelpers, true, emitter, raw, options);
824
922
  }
825
923
  store(value) {
826
924
  return value.serializeInternal();
@@ -841,7 +939,7 @@ SequenceIntervalCollectionValueType.Name = "sharedStringIntervalCollection";
841
939
  SequenceIntervalCollectionValueType._factory = new SequenceIntervalCollectionFactory();
842
940
  SequenceIntervalCollectionValueType._ops = makeOpsMap();
843
941
  const compareIntervalEnds = (a, b) => a.end - b.end;
844
- function createInterval(label, start, end, client) {
942
+ function createInterval(label, start, end, client, intervalType, op, fromSnapshot) {
845
943
  const rangeProp = {};
846
944
  if (label && label.length > 0) {
847
945
  rangeProp[reservedRangeLabelsKey] = [label];
@@ -849,12 +947,12 @@ function createInterval(label, start, end, client) {
849
947
  return new Interval(start, end, rangeProp);
850
948
  }
851
949
  class IntervalCollectionFactory {
852
- load(emitter, raw = []) {
950
+ load(emitter, raw = [], options) {
853
951
  const helpers = {
854
952
  compareEnds: compareIntervalEnds,
855
953
  create: createInterval,
856
954
  };
857
- const collection = new IntervalCollection(helpers, false, emitter, raw);
955
+ const collection = new IntervalCollection(helpers, false, emitter, raw, options);
858
956
  collection.attachGraph(undefined, "");
859
957
  return collection;
860
958
  }
@@ -885,7 +983,7 @@ export function makeOpsMap() {
885
983
  };
886
984
  return new Map([
887
985
  [
888
- "add",
986
+ IntervalOpType.ADD,
889
987
  {
890
988
  process: (collection, params, local, op, localOpMetadata) => {
891
989
  // if params is undefined, the interval was deleted during
@@ -900,7 +998,7 @@ export function makeOpsMap() {
900
998
  },
901
999
  ],
902
1000
  [
903
- "delete",
1001
+ IntervalOpType.DELETE,
904
1002
  {
905
1003
  process: (collection, params, local, op) => {
906
1004
  assert(op !== undefined, 0x3fc /* op should exist here */);
@@ -913,7 +1011,7 @@ export function makeOpsMap() {
913
1011
  },
914
1012
  ],
915
1013
  [
916
- "change",
1014
+ IntervalOpType.CHANGE,
917
1015
  {
918
1016
  process: (collection, params, local, op, localOpMetadata) => {
919
1017
  // if params is undefined, the interval was deleted during
@@ -929,7 +1027,7 @@ export function makeOpsMap() {
929
1027
  ],
930
1028
  ]);
931
1029
  }
932
- export class IntervalCollectionIterator {
1030
+ class IntervalCollectionIterator {
933
1031
  constructor(collection, iteratesForward = true, start, end) {
934
1032
  this.results = [];
935
1033
  this.index = 0;
@@ -949,19 +1047,16 @@ export class IntervalCollectionIterator {
949
1047
  }
950
1048
  }
951
1049
  /**
952
- * Collection of intervals that supports addition, modification, removal, and efficient spatial querying.
953
- * This class is not a DDS in its own right, but emits events on mutating operations such that it's possible to
954
- * integrate into a DDS.
955
- * This aligns with its usage in `SharedSegmentSequence`, which allows associating intervals to positions in the
956
- * sequence DDS which are broadcast to all other clients in an eventually consistent fashion.
1050
+ * {@inheritdoc IIntervalCollection}
957
1051
  */
958
1052
  export class IntervalCollection extends TypedEventEmitter {
959
1053
  /** @internal */
960
- constructor(helpers, requiresClient, emitter, serializedIntervals) {
1054
+ constructor(helpers, requiresClient, emitter, serializedIntervals, options = {}) {
961
1055
  super();
962
1056
  this.helpers = helpers;
963
1057
  this.requiresClient = requiresClient;
964
1058
  this.emitter = emitter;
1059
+ this.options = options;
965
1060
  this.localSeqToSerializedInterval = new Map();
966
1061
  this.localSeqToRebasedInterval = new Map();
967
1062
  this.pendingChangesStart = new Map();
@@ -973,6 +1068,36 @@ export class IntervalCollection extends TypedEventEmitter {
973
1068
  get attached() {
974
1069
  return !!this.localCollection;
975
1070
  }
1071
+ /**
1072
+ * {@inheritdoc IIntervalCollection.attachIndex}
1073
+ */
1074
+ attachIndex(index) {
1075
+ var _a;
1076
+ if (!this.attached) {
1077
+ throw new LoggingError("The local interval collection must exist");
1078
+ }
1079
+ for (const interval of this) {
1080
+ index.add(interval);
1081
+ }
1082
+ (_a = this.localCollection) === null || _a === void 0 ? void 0 : _a.appendIndex(index);
1083
+ }
1084
+ /**
1085
+ * {@inheritdoc IIntervalCollection.detachIndex}
1086
+ */
1087
+ detachIndex(index) {
1088
+ var _a;
1089
+ if (!this.attached) {
1090
+ throw new LoggingError("The local interval collection must exist");
1091
+ }
1092
+ // Avoid removing intervals if the index does not exist
1093
+ if (!((_a = this.localCollection) === null || _a === void 0 ? void 0 : _a.removeIndex(index))) {
1094
+ return false;
1095
+ }
1096
+ for (const interval of this) {
1097
+ index.remove(interval);
1098
+ }
1099
+ return true;
1100
+ }
976
1101
  rebasePositionWithSegmentSlide(pos, seqNumberFrom, localSeq) {
977
1102
  var _a;
978
1103
  if (!this.client) {
@@ -985,7 +1110,7 @@ export class IntervalCollection extends TypedEventEmitter {
985
1110
  }, localSeq);
986
1111
  // if segment is undefined, it slid off the string
987
1112
  assert(segment !== undefined, 0x54e /* No segment found */);
988
- const segoff = (_a = this.client.getSlideToSegment({ segment, offset })) !== null && _a !== void 0 ? _a : segment;
1113
+ const segoff = (_a = getSlideToSegoff({ segment, offset })) !== null && _a !== void 0 ? _a : segment;
989
1114
  // case happens when rebasing op, but concurrently entire string has been deleted
990
1115
  if (segoff.segment === undefined || segoff.offset === undefined) {
991
1116
  return DetachedReferencePosition;
@@ -1024,12 +1149,12 @@ export class IntervalCollection extends TypedEventEmitter {
1024
1149
  }
1025
1150
  });
1026
1151
  }
1027
- this.localCollection = new LocalIntervalCollection(client, label, this.helpers, (interval, previousInterval) => this.emitChange(interval, previousInterval, true));
1152
+ this.localCollection = new LocalIntervalCollection(client, label, this.helpers, (interval, previousInterval) => this.emitChange(interval, previousInterval, true, true));
1028
1153
  if (this.savedSerializedIntervals) {
1029
1154
  for (const serializedInterval of this.savedSerializedIntervals) {
1030
1155
  this.localCollection.ensureSerializedId(serializedInterval);
1031
- const { start, end, intervalType, properties } = serializedInterval;
1032
- const interval = this.helpers.create(label, start, end, client, intervalType, undefined, true);
1156
+ const { start, end, intervalType, properties, stickiness } = serializedInterval;
1157
+ const interval = this.helpers.create(label, start, end, client, intervalType, undefined, true, stickiness);
1033
1158
  if (properties) {
1034
1159
  interval.addProperties(properties);
1035
1160
  }
@@ -1047,7 +1172,7 @@ export class IntervalCollection extends TypedEventEmitter {
1047
1172
  }
1048
1173
  return 0;
1049
1174
  }
1050
- emitChange(interval, previousInterval, local, op) {
1175
+ emitChange(interval, previousInterval, local, slide, op) {
1051
1176
  // Temporarily make references transient so that positional queries work (non-transient refs
1052
1177
  // on resolve to DetachedPosition on any segments that don't contain them). The original refType
1053
1178
  // is restored as single-endpoint changes re-use previous references.
@@ -1058,17 +1183,16 @@ export class IntervalCollection extends TypedEventEmitter {
1058
1183
  endRefType = previousInterval.end.refType;
1059
1184
  previousInterval.start.refType = ReferenceType.Transient;
1060
1185
  previousInterval.end.refType = ReferenceType.Transient;
1061
- this.emit("changeInterval", interval, previousInterval, local, op);
1186
+ this.emit("changeInterval", interval, previousInterval, local, op, slide);
1062
1187
  previousInterval.start.refType = startRefType;
1063
1188
  previousInterval.end.refType = endRefType;
1064
1189
  }
1065
1190
  else {
1066
- this.emit("changeInterval", interval, previousInterval, local, op);
1191
+ this.emit("changeInterval", interval, previousInterval, local, op, slide);
1067
1192
  }
1068
1193
  }
1069
1194
  /**
1070
- * @returns the interval in this collection that has the provided `id`.
1071
- * If no interval in the collection has this `id`, returns `undefined`.
1195
+ * {@inheritdoc IIntervalCollection.getIntervalById}
1072
1196
  */
1073
1197
  getIntervalById(id) {
1074
1198
  if (!this.localCollection) {
@@ -1077,16 +1201,9 @@ export class IntervalCollection extends TypedEventEmitter {
1077
1201
  return this.localCollection.idIntervalIndex.getIntervalById(id);
1078
1202
  }
1079
1203
  /**
1080
- * Creates a new interval and add it to the collection.
1081
- * @param start - interval start position (inclusive)
1082
- * @param end - interval end position (exclusive)
1083
- * @param intervalType - type of the interval. All intervals are SlideOnRemove. Intervals may not be Transient.
1084
- * @param props - properties of the interval
1085
- * @returns - the created interval
1086
- * @remarks - See documentation on {@link SequenceInterval} for comments on interval endpoint semantics: there are subtleties
1087
- * with how the current half-open behavior is represented.
1204
+ * {@inheritdoc IIntervalCollection.add}
1088
1205
  */
1089
- add(start, end, intervalType, props) {
1206
+ add(start, end, intervalType, props, stickiness = IntervalStickiness.END) {
1090
1207
  var _a, _b;
1091
1208
  if (!this.localCollection) {
1092
1209
  throw new LoggingError("attach must be called prior to adding intervals");
@@ -1094,7 +1211,10 @@ export class IntervalCollection extends TypedEventEmitter {
1094
1211
  if (intervalType & IntervalType.Transient) {
1095
1212
  throw new LoggingError("Can not add transient intervals");
1096
1213
  }
1097
- const interval = this.localCollection.addInterval(start, end, intervalType, props);
1214
+ if (stickiness !== IntervalStickiness.END && !this.options.intervalStickinessEnabled) {
1215
+ throw new UsageError("attempted to set interval stickiness without enabling `intervalStickinessEnabled` feature flag");
1216
+ }
1217
+ const interval = this.localCollection.addInterval(start, end, intervalType, props, undefined, stickiness);
1098
1218
  if (interval) {
1099
1219
  const serializedInterval = {
1100
1220
  end,
@@ -1102,6 +1222,7 @@ export class IntervalCollection extends TypedEventEmitter {
1102
1222
  properties: interval.properties,
1103
1223
  sequenceNumber: (_b = (_a = this.client) === null || _a === void 0 ? void 0 : _a.getCurrentSeq()) !== null && _b !== void 0 ? _b : 0,
1104
1224
  start,
1225
+ stickiness,
1105
1226
  };
1106
1227
  const localSeq = this.getNextLocalSeq();
1107
1228
  this.localSeqToSerializedInterval.set(localSeq, serializedInterval);
@@ -1133,9 +1254,7 @@ export class IntervalCollection extends TypedEventEmitter {
1133
1254
  this.emit("deleteInterval", interval, local, op);
1134
1255
  }
1135
1256
  /**
1136
- * Removes an interval from the collection.
1137
- * @param id - Id of the interval to remove
1138
- * @returns the removed interval
1257
+ * {@inheritdoc IIntervalCollection.removeIntervalById}
1139
1258
  */
1140
1259
  removeIntervalById(id) {
1141
1260
  if (!this.localCollection) {
@@ -1148,10 +1267,7 @@ export class IntervalCollection extends TypedEventEmitter {
1148
1267
  return interval;
1149
1268
  }
1150
1269
  /**
1151
- * Changes the properties on an existing interval.
1152
- * @param id - Id of the interval whose properties should be changed
1153
- * @param props - Property set to apply to the interval. Shallow merging is used between any existing properties
1154
- * and `prop`, i.e. the interval will end up with a property object equivalent to `{ ...oldProps, ...props }`.
1270
+ * {@inheritdoc IIntervalCollection.changeProperties}
1155
1271
  */
1156
1272
  changeProperties(id, props) {
1157
1273
  if (!this.attached) {
@@ -1163,6 +1279,11 @@ export class IntervalCollection extends TypedEventEmitter {
1163
1279
  if (!props) {
1164
1280
  throw new LoggingError("changeProperties should be called with a property set");
1165
1281
  }
1282
+ // prevent the overwriting of an interval label, it should remain unchanged
1283
+ // once it has been inserted into the collection.
1284
+ if (props[reservedRangeLabelsKey] !== undefined) {
1285
+ throw new LoggingError("The label property should not be modified once inserted to the collection");
1286
+ }
1166
1287
  const interval = this.getIntervalById(id);
1167
1288
  if (interval) {
1168
1289
  // Pass Unassigned as the sequence number to indicate that this is a local op that is waiting for an ack.
@@ -1181,11 +1302,7 @@ export class IntervalCollection extends TypedEventEmitter {
1181
1302
  }
1182
1303
  }
1183
1304
  /**
1184
- * Changes the endpoints of an existing interval.
1185
- * @param id - Id of the interval to change
1186
- * @param start - New start value, if defined. `undefined` signifies this endpoint should be left unchanged.
1187
- * @param end - New end value, if defined. `undefined` signifies this endpoint should be left unchanged.
1188
- * @returns the interval that was changed, if it existed in the collection.
1305
+ * {@inheritdoc IIntervalCollection.change}
1189
1306
  */
1190
1307
  change(id, start, end) {
1191
1308
  if (!this.localCollection) {
@@ -1212,7 +1329,7 @@ export class IntervalCollection extends TypedEventEmitter {
1212
1329
  this.localSeqToSerializedInterval.set(localSeq, serializedInterval);
1213
1330
  this.emitter.emit("change", undefined, serializedInterval, { localSeq });
1214
1331
  this.addPendingChange(id, serializedInterval);
1215
- this.emitChange(newInterval, interval, true);
1332
+ this.emitChange(newInterval, interval, true, false);
1216
1333
  return newInterval;
1217
1334
  }
1218
1335
  // No interval to change
@@ -1320,7 +1437,7 @@ export class IntervalCollection extends TypedEventEmitter {
1320
1437
  this.onDeserialize(newInterval);
1321
1438
  }
1322
1439
  if (newInterval !== interval) {
1323
- this.emitChange(newInterval, interval, local, op);
1440
+ this.emitChange(newInterval, interval, local, false, op);
1324
1441
  }
1325
1442
  const changedProperties = Object.keys(newProps).length > 0;
1326
1443
  if (changedProperties) {
@@ -1340,6 +1457,9 @@ export class IntervalCollection extends TypedEventEmitter {
1340
1457
  throw new LoggingError("attachSequence must be called");
1341
1458
  }
1342
1459
  }
1460
+ /**
1461
+ * {@inheritdoc IIntervalCollection.attachDeserializer}
1462
+ */
1343
1463
  attachDeserializer(onDeserialize) {
1344
1464
  // If no deserializer is specified can skip all processing work
1345
1465
  if (!onDeserialize) {
@@ -1410,7 +1530,7 @@ export class IntervalCollection extends TypedEventEmitter {
1410
1530
  if (((_b = (_a = segoff.segment) === null || _a === void 0 ? void 0 : _a.localRefs) === null || _b === void 0 ? void 0 : _b.has(lref)) !== true) {
1411
1531
  return undefined;
1412
1532
  }
1413
- const newSegoff = this.client.getSlideToSegment(segoff);
1533
+ const newSegoff = getSlideToSegoff(segoff);
1414
1534
  const value = segoff.segment === newSegoff.segment && segoff.offset === newSegoff.offset
1415
1535
  ? undefined
1416
1536
  : newSegoff;
@@ -1460,7 +1580,7 @@ export class IntervalCollection extends TypedEventEmitter {
1460
1580
  }
1461
1581
  if (needsStartUpdate) {
1462
1582
  const props = interval.start.properties;
1463
- interval.start = createPositionReferenceFromSegoff(this.client, newStart, interval.start.refType, op);
1583
+ interval.start = createPositionReferenceFromSegoff(this.client, newStart, interval.start.refType, op, startReferenceSlidingPreference(interval.stickiness));
1464
1584
  if (props) {
1465
1585
  interval.start.addProperties(props);
1466
1586
  }
@@ -1472,7 +1592,7 @@ export class IntervalCollection extends TypedEventEmitter {
1472
1592
  }
1473
1593
  if (needsEndUpdate) {
1474
1594
  const props = interval.end.properties;
1475
- interval.end = createPositionReferenceFromSegoff(this.client, newEnd, interval.end.refType, op);
1595
+ interval.end = createPositionReferenceFromSegoff(this.client, newEnd, interval.end.refType, op, endReferenceSlidingPreference(interval.stickiness));
1476
1596
  if (props) {
1477
1597
  interval.end.addProperties(props);
1478
1598
  }
@@ -1483,7 +1603,7 @@ export class IntervalCollection extends TypedEventEmitter {
1483
1603
  (_b = oldSeg === null || oldSeg === void 0 ? void 0 : oldSeg.localRefs) === null || _b === void 0 ? void 0 : _b.addLocalRef(oldInterval.end, oldInterval.end.getOffset());
1484
1604
  }
1485
1605
  this.localCollection.add(interval);
1486
- this.emitChange(interval, oldInterval, true, op);
1606
+ this.emitChange(interval, oldInterval, true, true, op);
1487
1607
  }
1488
1608
  }
1489
1609
  /** @internal */
@@ -1503,7 +1623,7 @@ export class IntervalCollection extends TypedEventEmitter {
1503
1623
  throw new LoggingError("attachSequence must be called");
1504
1624
  }
1505
1625
  this.localCollection.ensureSerializedId(serializedInterval);
1506
- const interval = this.localCollection.addInterval(serializedInterval.start, serializedInterval.end, serializedInterval.intervalType, serializedInterval.properties, op);
1626
+ const interval = this.localCollection.addInterval(serializedInterval.start, serializedInterval.end, serializedInterval.intervalType, serializedInterval.properties, op, serializedInterval.stickiness);
1507
1627
  if (interval) {
1508
1628
  if (this.onDeserialize) {
1509
1629
  this.onDeserialize(interval);
@@ -1546,40 +1666,35 @@ export class IntervalCollection extends TypedEventEmitter {
1546
1666
  return iterator;
1547
1667
  }
1548
1668
  /**
1549
- * @returns a forward iterator over all intervals in this collection with start point equal to `startPosition`.
1669
+ * {@inheritdoc IIntervalCollection.CreateForwardIteratorWithStartPosition}
1550
1670
  */
1551
1671
  CreateForwardIteratorWithStartPosition(startPosition) {
1552
1672
  const iterator = new IntervalCollectionIterator(this, true, startPosition);
1553
1673
  return iterator;
1554
1674
  }
1555
1675
  /**
1556
- * @returns a backward iterator over all intervals in this collection with start point equal to `startPosition`.
1676
+ * {@inheritdoc IIntervalCollection.CreateBackwardIteratorWithStartPosition}
1557
1677
  */
1558
1678
  CreateBackwardIteratorWithStartPosition(startPosition) {
1559
1679
  const iterator = new IntervalCollectionIterator(this, false, startPosition);
1560
1680
  return iterator;
1561
1681
  }
1562
1682
  /**
1563
- * @returns a forward iterator over all intervals in this collection with end point equal to `endPosition`.
1683
+ * {@inheritdoc IIntervalCollection.CreateForwardIteratorWithEndPosition}
1564
1684
  */
1565
1685
  CreateForwardIteratorWithEndPosition(endPosition) {
1566
1686
  const iterator = new IntervalCollectionIterator(this, true, undefined, endPosition);
1567
1687
  return iterator;
1568
1688
  }
1569
1689
  /**
1570
- * @returns a backward iterator over all intervals in this collection with end point equal to `endPosition`.
1690
+ * {@inheritdoc IIntervalCollection.CreateBackwardIteratorWithEndPosition}
1571
1691
  */
1572
1692
  CreateBackwardIteratorWithEndPosition(endPosition) {
1573
1693
  const iterator = new IntervalCollectionIterator(this, false, undefined, endPosition);
1574
1694
  return iterator;
1575
1695
  }
1576
1696
  /**
1577
- * Gathers iteration results that optionally match a start/end criteria into the provided array.
1578
- * @param results - Array to gather the results into. In lieu of a return value, this array will be populated with
1579
- * intervals matching the query upon edit.
1580
- * @param iteratesForward - whether or not iteration should be in the forward direction
1581
- * @param start - If provided, only match intervals whose start point is equal to `start`.
1582
- * @param end - If provided, only match intervals whose end point is equal to `end`.
1697
+ * {@inheritdoc IIntervalCollection.gatherIterationResults}
1583
1698
  */
1584
1699
  gatherIterationResults(results, iteratesForward, start, end) {
1585
1700
  if (!this.localCollection) {
@@ -1588,8 +1703,7 @@ export class IntervalCollection extends TypedEventEmitter {
1588
1703
  this.localCollection.overlappingIntervalsIndex.gatherIterationResults(results, iteratesForward, start, end);
1589
1704
  }
1590
1705
  /**
1591
- * @returns an array of all intervals in this collection that overlap with the interval
1592
- * `[startPosition, endPosition]`.
1706
+ * {@inheritdoc IIntervalCollection.findOverlappingIntervals}
1593
1707
  */
1594
1708
  findOverlappingIntervals(startPosition, endPosition) {
1595
1709
  if (!this.localCollection) {
@@ -1598,7 +1712,7 @@ export class IntervalCollection extends TypedEventEmitter {
1598
1712
  return this.localCollection.overlappingIntervalsIndex.findOverlappingIntervals(startPosition, endPosition);
1599
1713
  }
1600
1714
  /**
1601
- * Applies a function to each interval in this collection.
1715
+ * {@inheritdoc IIntervalCollection.map}
1602
1716
  */
1603
1717
  map(fn) {
1604
1718
  if (!this.localCollection) {
@@ -1608,12 +1722,18 @@ export class IntervalCollection extends TypedEventEmitter {
1608
1722
  fn(interval);
1609
1723
  }
1610
1724
  }
1725
+ /**
1726
+ * {@inheritdoc IIntervalCollection.previousInterval}
1727
+ */
1611
1728
  previousInterval(pos) {
1612
1729
  if (!this.localCollection) {
1613
1730
  throw new LoggingError("attachSequence must be called");
1614
1731
  }
1615
1732
  return this.localCollection.endIntervalIndex.previousInterval(pos);
1616
1733
  }
1734
+ /**
1735
+ * {@inheritdoc IIntervalCollection.nextInterval}
1736
+ */
1617
1737
  nextInterval(pos) {
1618
1738
  if (!this.localCollection) {
1619
1739
  throw new LoggingError("attachSequence must be called");