@fluidframework/sequence 2.31.0 → 2.32.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (119) hide show
  1. package/CHANGELOG.md +4 -0
  2. package/dist/index.d.ts +1 -1
  3. package/dist/index.d.ts.map +1 -1
  4. package/dist/index.js +1 -2
  5. package/dist/index.js.map +1 -1
  6. package/dist/intervalCollection.d.ts +5 -9
  7. package/dist/intervalCollection.d.ts.map +1 -1
  8. package/dist/intervalCollection.js +38 -104
  9. package/dist/intervalCollection.js.map +1 -1
  10. package/dist/intervalCollectionMap.d.ts.map +1 -1
  11. package/dist/intervalCollectionMap.js +5 -4
  12. package/dist/intervalCollectionMap.js.map +1 -1
  13. package/dist/intervalIndex/endpointInRangeIndex.d.ts.map +1 -1
  14. package/dist/intervalIndex/endpointInRangeIndex.js +2 -2
  15. package/dist/intervalIndex/endpointInRangeIndex.js.map +1 -1
  16. package/dist/intervalIndex/endpointIndex.d.ts.map +1 -1
  17. package/dist/intervalIndex/endpointIndex.js +2 -3
  18. package/dist/intervalIndex/endpointIndex.js.map +1 -1
  19. package/dist/intervalIndex/idIntervalIndex.d.ts.map +1 -1
  20. package/dist/intervalIndex/idIntervalIndex.js +0 -7
  21. package/dist/intervalIndex/idIntervalIndex.js.map +1 -1
  22. package/dist/intervalIndex/index.d.ts +0 -1
  23. package/dist/intervalIndex/index.d.ts.map +1 -1
  24. package/dist/intervalIndex/index.js +1 -3
  25. package/dist/intervalIndex/index.js.map +1 -1
  26. package/dist/intervalIndex/overlappingIntervalsIndex.d.ts.map +1 -1
  27. package/dist/intervalIndex/overlappingIntervalsIndex.js +2 -2
  28. package/dist/intervalIndex/overlappingIntervalsIndex.js.map +1 -1
  29. package/dist/intervalIndex/startpointInRangeIndex.d.ts.map +1 -1
  30. package/dist/intervalIndex/startpointInRangeIndex.js +2 -2
  31. package/dist/intervalIndex/startpointInRangeIndex.js.map +1 -1
  32. package/dist/intervals/index.d.ts +2 -2
  33. package/dist/intervals/index.d.ts.map +1 -1
  34. package/dist/intervals/index.js +3 -1
  35. package/dist/intervals/index.js.map +1 -1
  36. package/dist/intervals/intervalUtils.d.ts +1 -5
  37. package/dist/intervals/intervalUtils.d.ts.map +1 -1
  38. package/dist/intervals/intervalUtils.js.map +1 -1
  39. package/dist/intervals/sequenceInterval.d.ts +20 -9
  40. package/dist/intervals/sequenceInterval.d.ts.map +1 -1
  41. package/dist/intervals/sequenceInterval.js +82 -27
  42. package/dist/intervals/sequenceInterval.js.map +1 -1
  43. package/dist/packageVersion.d.ts +1 -1
  44. package/dist/packageVersion.js +1 -1
  45. package/dist/packageVersion.js.map +1 -1
  46. package/dist/revertibles.d.ts.map +1 -1
  47. package/dist/revertibles.js +8 -7
  48. package/dist/revertibles.js.map +1 -1
  49. package/lib/index.d.ts +1 -1
  50. package/lib/index.d.ts.map +1 -1
  51. package/lib/index.js +1 -1
  52. package/lib/index.js.map +1 -1
  53. package/lib/intervalCollection.d.ts +5 -9
  54. package/lib/intervalCollection.d.ts.map +1 -1
  55. package/lib/intervalCollection.js +39 -105
  56. package/lib/intervalCollection.js.map +1 -1
  57. package/lib/intervalCollectionMap.d.ts.map +1 -1
  58. package/lib/intervalCollectionMap.js +6 -5
  59. package/lib/intervalCollectionMap.js.map +1 -1
  60. package/lib/intervalIndex/endpointInRangeIndex.d.ts.map +1 -1
  61. package/lib/intervalIndex/endpointInRangeIndex.js +3 -3
  62. package/lib/intervalIndex/endpointInRangeIndex.js.map +1 -1
  63. package/lib/intervalIndex/endpointIndex.d.ts.map +1 -1
  64. package/lib/intervalIndex/endpointIndex.js +3 -4
  65. package/lib/intervalIndex/endpointIndex.js.map +1 -1
  66. package/lib/intervalIndex/idIntervalIndex.d.ts.map +1 -1
  67. package/lib/intervalIndex/idIntervalIndex.js +0 -7
  68. package/lib/intervalIndex/idIntervalIndex.js.map +1 -1
  69. package/lib/intervalIndex/index.d.ts +0 -1
  70. package/lib/intervalIndex/index.d.ts.map +1 -1
  71. package/lib/intervalIndex/index.js +0 -1
  72. package/lib/intervalIndex/index.js.map +1 -1
  73. package/lib/intervalIndex/overlappingIntervalsIndex.d.ts.map +1 -1
  74. package/lib/intervalIndex/overlappingIntervalsIndex.js +3 -3
  75. package/lib/intervalIndex/overlappingIntervalsIndex.js.map +1 -1
  76. package/lib/intervalIndex/startpointInRangeIndex.d.ts.map +1 -1
  77. package/lib/intervalIndex/startpointInRangeIndex.js +3 -3
  78. package/lib/intervalIndex/startpointInRangeIndex.js.map +1 -1
  79. package/lib/intervals/index.d.ts +2 -2
  80. package/lib/intervals/index.d.ts.map +1 -1
  81. package/lib/intervals/index.js +1 -1
  82. package/lib/intervals/index.js.map +1 -1
  83. package/lib/intervals/intervalUtils.d.ts +1 -5
  84. package/lib/intervals/intervalUtils.d.ts.map +1 -1
  85. package/lib/intervals/intervalUtils.js.map +1 -1
  86. package/lib/intervals/sequenceInterval.d.ts +20 -9
  87. package/lib/intervals/sequenceInterval.d.ts.map +1 -1
  88. package/lib/intervals/sequenceInterval.js +81 -28
  89. package/lib/intervals/sequenceInterval.js.map +1 -1
  90. package/lib/packageVersion.d.ts +1 -1
  91. package/lib/packageVersion.js +1 -1
  92. package/lib/packageVersion.js.map +1 -1
  93. package/lib/revertibles.d.ts.map +1 -1
  94. package/lib/revertibles.js +8 -7
  95. package/lib/revertibles.js.map +1 -1
  96. package/package.json +20 -19
  97. package/src/index.ts +0 -1
  98. package/src/intervalCollection.ts +59 -144
  99. package/src/intervalCollectionMap.ts +5 -5
  100. package/src/intervalIndex/endpointInRangeIndex.ts +3 -15
  101. package/src/intervalIndex/endpointIndex.ts +3 -17
  102. package/src/intervalIndex/idIntervalIndex.ts +0 -7
  103. package/src/intervalIndex/index.ts +0 -1
  104. package/src/intervalIndex/overlappingIntervalsIndex.ts +3 -12
  105. package/src/intervalIndex/startpointInRangeIndex.ts +3 -15
  106. package/src/intervals/index.ts +2 -1
  107. package/src/intervals/intervalUtils.ts +0 -7
  108. package/src/intervals/sequenceInterval.ts +124 -33
  109. package/src/packageVersion.ts +1 -1
  110. package/src/revertibles.ts +8 -7
  111. package/dist/intervalIndex/overlappingSequenceIntervalsIndex.d.ts +0 -11
  112. package/dist/intervalIndex/overlappingSequenceIntervalsIndex.d.ts.map +0 -1
  113. package/dist/intervalIndex/overlappingSequenceIntervalsIndex.js +0 -38
  114. package/dist/intervalIndex/overlappingSequenceIntervalsIndex.js.map +0 -1
  115. package/lib/intervalIndex/overlappingSequenceIntervalsIndex.d.ts +0 -11
  116. package/lib/intervalIndex/overlappingSequenceIntervalsIndex.d.ts.map +0 -1
  117. package/lib/intervalIndex/overlappingSequenceIntervalsIndex.js +0 -34
  118. package/lib/intervalIndex/overlappingSequenceIntervalsIndex.js.map +0 -1
  119. package/src/intervalIndex/overlappingSequenceIntervalsIndex.ts +0 -80
@@ -5,12 +5,11 @@
5
5
  /* eslint-disable no-bitwise */
6
6
  import { TypedEventEmitter } from "@fluid-internal/client-utils";
7
7
  import { assert } from "@fluidframework/core-utils/internal";
8
- import { DetachedReferencePosition, ReferenceType, SlidingPreference, UnassignedSequenceNumber, UniversalSequenceNumber, addProperties, getSlideToSegoff, refTypeIncludesFlag, reservedRangeLabelsKey, Side, endpointPosAndSide, PropertiesManager, createMap, } from "@fluidframework/merge-tree/internal";
8
+ import { DetachedReferencePosition, ReferenceType, SlidingPreference, getSlideToSegoff, refTypeIncludesFlag, reservedRangeLabelsKey, Side, endpointPosAndSide, createLocalReconnectingPerspective, } from "@fluidframework/merge-tree/internal";
9
9
  import { LoggingError, UsageError } from "@fluidframework/telemetry-utils/internal";
10
10
  import { v4 as uuid } from "uuid";
11
11
  import { createIdIntervalIndex, EndpointIndex, OverlappingIntervalsIndex, } from "./intervalIndex/index.js";
12
- import { IntervalDeltaOpType, IntervalStickiness, IntervalType, createPositionReferenceFromSegoff, createSequenceInterval, endReferenceSlidingPreference, startReferenceSlidingPreference, } from "./intervals/index.js";
13
- export const reservedIntervalIdKey = "intervalId";
12
+ import { IntervalDeltaOpType, IntervalStickiness, IntervalType, createPositionReferenceFromSegoff, createSequenceInterval, endReferenceSlidingPreference, getSerializedProperties, startReferenceSlidingPreference, } from "./intervals/index.js";
14
13
  export function sidesFromStickiness(stickiness) {
15
14
  const startSide = (stickiness & IntervalStickiness.START) !== 0 ? Side.After : Side.Before;
16
15
  const endSide = (stickiness & IntervalStickiness.END) !== 0 ? Side.Before : Side.After;
@@ -88,11 +87,6 @@ export class LocalIntervalCollection {
88
87
  this.endIntervalIndex,
89
88
  ]);
90
89
  }
91
- createLegacyId(start, end) {
92
- // Create a non-unique ID based on start and end to be used on intervals that come from legacy clients
93
- // without ID's.
94
- return `${LocalIntervalCollection.legacyIdPrefix}${start}-${end}`;
95
- }
96
90
  /**
97
91
  * Validates that a serialized interval has the ID property. Creates an ID
98
92
  * if one does not already exist
@@ -100,26 +94,6 @@ export class LocalIntervalCollection {
100
94
  * @param serializedInterval - The interval to be checked
101
95
  * @returns The interval's existing or newly created id
102
96
  */
103
- ensureSerializedId(serializedInterval) {
104
- let id = serializedInterval.properties?.[reservedIntervalIdKey];
105
- if (id === undefined) {
106
- // Back-compat: 0.39 and earlier did not have IDs on intervals. If an interval from such a client
107
- // comes over the wire, create a non-unique one based on start/end.
108
- // This will allow all clients to refer to this interval consistently.
109
- id = this.createLegacyId(serializedInterval.start, serializedInterval.end);
110
- const newProps = {
111
- [reservedIntervalIdKey]: id,
112
- };
113
- serializedInterval.properties = addProperties(serializedInterval.properties, newProps);
114
- }
115
- // Make the ID immutable for safety's sake.
116
- Object.defineProperty(serializedInterval.properties, reservedIntervalIdKey, {
117
- configurable: false,
118
- enumerable: true,
119
- writable: false,
120
- });
121
- return id;
122
- }
123
97
  removeIntervalFromIndexes(interval) {
124
98
  for (const index of this.indexes) {
125
99
  index.remove(interval);
@@ -135,28 +109,16 @@ export class LocalIntervalCollection {
135
109
  this.removeIntervalFromIndexes(interval);
136
110
  this.removeIntervalListeners(interval);
137
111
  }
138
- createInterval(start, end, intervalType, op) {
139
- return createSequenceInterval(this.label, start, end, this.client, intervalType, op, undefined, this.options.mergeTreeReferencesCanSlideToEndpoint);
140
- }
141
- addInterval(start, end, intervalType, props, op) {
142
- const interval = this.createInterval(start, end, intervalType, op);
143
- if (interval) {
144
- if (!interval.properties) {
145
- interval.properties = createMap();
146
- }
147
- if (props) {
148
- // This check is intended to prevent scenarios where a random interval is created and then
149
- // inserted into a collection. The aim is to ensure that the collection is created first
150
- // then the user can create/add intervals based on the collection
151
- if (props[reservedRangeLabelsKey] !== undefined &&
152
- props[reservedRangeLabelsKey][0] !== this.label) {
153
- throw new LoggingError("Adding an interval that belongs to another interval collection is not permitted");
154
- }
155
- interval.properties = addProperties(interval.properties, props);
156
- }
157
- interval.properties[reservedIntervalIdKey] ??= uuid();
158
- this.add(interval);
112
+ addInterval(id, start, end, props, op) {
113
+ // This check is intended to prevent scenarios where a random interval is created and then
114
+ // inserted into a collection. The aim is to ensure that the collection is created first
115
+ // then the user can create/add intervals based on the collection
116
+ if (props?.[reservedRangeLabelsKey] !== undefined &&
117
+ props[reservedRangeLabelsKey][0] !== this.label) {
118
+ throw new LoggingError("Adding an interval that belongs to another interval collection is not permitted");
159
119
  }
120
+ const interval = createSequenceInterval(this.label, id, start, end, this.client, IntervalType.SlideOnRemove, op, undefined, this.options.mergeTreeReferencesCanSlideToEndpoint, props);
121
+ this.add(interval);
160
122
  return interval;
161
123
  }
162
124
  linkEndpointsToInterval(interval) {
@@ -227,7 +189,6 @@ export class LocalIntervalCollection {
227
189
  interval.removePositionChangeListeners();
228
190
  }
229
191
  }
230
- LocalIntervalCollection.legacyIdPrefix = "legacy";
231
192
  const rebase = (collection, op, localOpMetadata) => {
232
193
  const { localSeq } = localOpMetadata;
233
194
  const rebasedValue = collection.rebaseLocalInterval(op.opName, op.value, localSeq);
@@ -353,7 +314,7 @@ export class IntervalCollection extends TypedEventEmitter {
353
314
  }, localSeq);
354
315
  // if segment is undefined, it slid off the string
355
316
  assert(segment !== undefined, 0x54e /* No segment found */);
356
- const segoff = getSlideToSegoff({ segment, offset }, undefined, this.options.mergeTreeReferencesCanSlideToEndpoint) ?? segment;
317
+ const segoff = getSlideToSegoff({ segment, offset }, undefined, createLocalReconnectingPerspective(this.client.getCurrentSeq(), clientId, localSeq), this.options.mergeTreeReferencesCanSlideToEndpoint) ?? segment;
357
318
  // case happens when rebasing op, but concurrently entire string has been deleted
358
319
  if (segoff.segment === undefined || segoff.offset === undefined) {
359
320
  return DetachedReferencePosition;
@@ -394,18 +355,15 @@ export class IntervalCollection extends TypedEventEmitter {
394
355
  this.localCollection = new LocalIntervalCollection(client, label, this.options, (interval, previousInterval) => this.emitChange(interval, previousInterval, true, true));
395
356
  if (this.savedSerializedIntervals) {
396
357
  for (const serializedInterval of this.savedSerializedIntervals) {
397
- this.localCollection.ensureSerializedId(serializedInterval);
398
- const { start: startPos, end: endPos, intervalType, properties, startSide, endSide, } = serializedInterval;
358
+ const { id, properties } = getSerializedProperties(serializedInterval);
359
+ const { start: startPos, end: endPos, intervalType, startSide, endSide, } = serializedInterval;
399
360
  const start = typeof startPos === "number" && startSide !== undefined
400
361
  ? { pos: startPos, side: startSide }
401
362
  : startPos;
402
363
  const end = typeof endPos === "number" && endSide !== undefined
403
364
  ? { pos: endPos, side: endSide }
404
365
  : endPos;
405
- const interval = createSequenceInterval(label, start, end, client, intervalType, undefined, true, this.options.mergeTreeReferencesCanSlideToEndpoint);
406
- if (properties) {
407
- interval.properties = addProperties(interval.properties, properties);
408
- }
366
+ const interval = createSequenceInterval(label, id, start, end, client, intervalType, undefined, true, this.options.mergeTreeReferencesCanSlideToEndpoint, properties);
409
367
  this.localCollection.add(interval);
410
368
  }
411
369
  }
@@ -451,7 +409,7 @@ export class IntervalCollection extends TypedEventEmitter {
451
409
  /**
452
410
  * {@inheritdoc IIntervalCollection.add}
453
411
  */
454
- add({ start, end, props, }) {
412
+ add({ id, start, end, props, }) {
455
413
  if (!this.localCollection) {
456
414
  throw new LoggingError("attach must be called prior to adding intervals");
457
415
  }
@@ -460,24 +418,14 @@ export class IntervalCollection extends TypedEventEmitter {
460
418
  endPos !== undefined &&
461
419
  startSide !== undefined &&
462
420
  endSide !== undefined, 0x793 /* start and end cannot be undefined because they were not passed in as undefined */);
463
- const stickiness = computeStickinessFromSide(startPos, startSide, endPos, endSide);
464
421
  this.assertStickinessEnabled(start, end);
465
- const interval = this.localCollection.addInterval(toSequencePlace(startPos, startSide), toSequencePlace(endPos, endSide), IntervalType.SlideOnRemove, props);
422
+ const interval = this.localCollection.addInterval(id ?? uuid(), toSequencePlace(startPos, startSide), toSequencePlace(endPos, endSide), props);
466
423
  if (interval) {
467
424
  if (!this.isCollaborating) {
468
425
  setSlideOnRemove(interval.start);
469
426
  setSlideOnRemove(interval.end);
470
427
  }
471
- const serializedInterval = {
472
- start: startPos,
473
- end: endPos,
474
- intervalType: IntervalType.SlideOnRemove,
475
- properties: { ...interval.properties },
476
- sequenceNumber: this.client?.getCurrentSeq() ?? 0,
477
- stickiness,
478
- startSide,
479
- endSide,
480
- };
428
+ const serializedInterval = interval.serialize();
481
429
  const localSeq = this.getNextLocalSeq();
482
430
  if (this.isCollaborating) {
483
431
  this.localSeqToSerializedInterval.set(localSeq, serializedInterval);
@@ -554,29 +502,21 @@ export class IntervalCollection extends TypedEventEmitter {
554
502
  let deltaProps;
555
503
  let newInterval;
556
504
  if (props !== undefined) {
557
- interval.propertyManager ??= new PropertiesManager();
558
- deltaProps = interval.propertyManager.handleProperties({ props }, interval, this.isCollaborating ? UnassignedSequenceNumber : UniversalSequenceNumber, UniversalSequenceNumber, true);
505
+ deltaProps = interval.changeProperties(props);
559
506
  }
560
- if (start !== undefined && end !== undefined) {
507
+ const changeEndpoints = start !== undefined && end !== undefined;
508
+ if (changeEndpoints) {
561
509
  newInterval = this.localCollection.changeInterval(interval, start, end);
562
510
  if (!this.isCollaborating && newInterval !== undefined) {
563
511
  setSlideOnRemove(newInterval.start);
564
512
  setSlideOnRemove(newInterval.end);
565
513
  }
566
514
  }
567
- const serializedInterval = interval.serialize();
568
- const { startPos, startSide, endPos, endSide } = endpointPosAndSide(start, end);
569
- const stickiness = computeStickinessFromSide(startPos, startSide, endPos, endSide);
570
- serializedInterval.start = startPos;
571
- serializedInterval.end = endPos;
572
- serializedInterval.startSide = startSide;
573
- serializedInterval.endSide = endSide;
574
- serializedInterval.stickiness = stickiness;
575
515
  // Emit a property bag containing the ID and the other (if any) properties changed
576
- serializedInterval.properties = {
577
- [reservedIntervalIdKey]: interval.getIntervalId(),
578
- ...props,
579
- };
516
+ const serializedInterval = (newInterval ?? interval).serializeDelta({
517
+ props,
518
+ includeEndpoints: changeEndpoints,
519
+ });
580
520
  const localSeq = this.getNextLocalSeq();
581
521
  if (this.isCollaborating) {
582
522
  this.localSeqToSerializedInterval.set(localSeq, serializedInterval);
@@ -626,7 +566,7 @@ export class IntervalCollection extends TypedEventEmitter {
626
566
  }
627
567
  removePendingChange(serializedInterval) {
628
568
  // Change ops always have an ID.
629
- const id = serializedInterval.properties?.[reservedIntervalIdKey];
569
+ const { id } = getSerializedProperties(serializedInterval);
630
570
  if (serializedInterval.start !== undefined) {
631
571
  this.removePendingChangeHelper(id, this.pendingChangesStart, serializedInterval);
632
572
  }
@@ -668,7 +608,7 @@ export class IntervalCollection extends TypedEventEmitter {
668
608
  // Note that the ID is in the property bag only to allow us to find the interval.
669
609
  // This API cannot change the ID, and writing to the ID property will result in an exception. So we
670
610
  // strip it out of the properties here.
671
- const { [reservedIntervalIdKey]: id, ...newProps } = serializedInterval.properties ?? {};
611
+ const { id, properties } = getSerializedProperties(serializedInterval);
672
612
  assert(id !== undefined, 0x3fe /* id must exist on the interval */);
673
613
  const interval = this.getIntervalById(id);
674
614
  if (!interval) {
@@ -676,11 +616,7 @@ export class IntervalCollection extends TypedEventEmitter {
676
616
  return;
677
617
  }
678
618
  if (local) {
679
- interval.propertyManager ??= new PropertiesManager();
680
- // Let the propertyManager prune its pending change-properties set.
681
- interval.propertyManager.ack(op.sequenceNumber, op.minimumSequenceNumber, {
682
- props: newProps,
683
- });
619
+ interval.ackPropertiesChange(properties, op);
684
620
  this.ackInterval(interval, op);
685
621
  }
686
622
  else {
@@ -702,15 +638,14 @@ export class IntervalCollection extends TypedEventEmitter {
702
638
  newInterval =
703
639
  this.localCollection.changeInterval(interval, toOptionalSequencePlace(start, serializedInterval.startSide ?? Side.Before), toOptionalSequencePlace(end, serializedInterval.endSide ?? Side.Before), op) ?? interval;
704
640
  }
705
- newInterval.propertyManager ??= new PropertiesManager();
706
- const deltaProps = newInterval.propertyManager.handleProperties({ props: newProps }, newInterval, op.sequenceNumber, op.minimumSequenceNumber, true);
641
+ const deltaProps = newInterval.changeProperties(properties, op);
707
642
  if (this.onDeserialize) {
708
643
  this.onDeserialize(newInterval);
709
644
  }
710
645
  if (newInterval !== interval) {
711
646
  this.emitChange(newInterval, interval, local, false, op);
712
647
  }
713
- const changedProperties = Object.keys(newProps).length > 0;
648
+ const changedProperties = Object.keys(properties).length > 0;
714
649
  if (changedProperties) {
715
650
  this.emit("propertyChanged", interval, deltaProps, local, op);
716
651
  this.emit("changed", interval, deltaProps, undefined, local, false);
@@ -747,9 +682,9 @@ export class IntervalCollection extends TypedEventEmitter {
747
682
  throw new LoggingError("attachSequence must be called");
748
683
  }
749
684
  const { intervalType, properties, stickiness, startSide, endSide } = serializedInterval;
685
+ const { id } = getSerializedProperties(serializedInterval);
750
686
  const { start: startRebased, end: endRebased } = this.localSeqToRebasedInterval.get(localSeq) ?? this.computeRebasedPositions(localSeq);
751
- const intervalId = properties?.[reservedIntervalIdKey];
752
- const localInterval = this.localCollection?.idIntervalIndex.getIntervalById(intervalId);
687
+ const localInterval = this.localCollection?.idIntervalIndex.getIntervalById(id);
753
688
  const rebased = {
754
689
  start: startRebased,
755
690
  end: endRebased,
@@ -762,9 +697,9 @@ export class IntervalCollection extends TypedEventEmitter {
762
697
  };
763
698
  if (opName === "change" &&
764
699
  // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing -- ?? is not logically equivalent when .hasPendingChangeStart returns false.
765
- (this.hasPendingChangeStart(intervalId) || this.hasPendingChangeEnd(intervalId))) {
700
+ (this.hasPendingChangeStart(id) || this.hasPendingChangeEnd(id))) {
766
701
  this.removePendingChange(serializedInterval);
767
- this.addPendingChange(intervalId, rebased);
702
+ this.addPendingChange(id, rebased);
768
703
  }
769
704
  // if the interval slid off the string, rebase the op to be a noop and delete the interval.
770
705
  if (!this.options.mergeTreeReferencesCanSlideToEndpoint &&
@@ -792,7 +727,7 @@ export class IntervalCollection extends TypedEventEmitter {
792
727
  if (segoff.segment?.localRefs?.has(lref) !== true) {
793
728
  return undefined;
794
729
  }
795
- const newSegoff = getSlideToSegoff(segoff, slidingPreference, this.options.mergeTreeReferencesCanSlideToEndpoint);
730
+ const newSegoff = getSlideToSegoff(segoff, slidingPreference, undefined, this.options.mergeTreeReferencesCanSlideToEndpoint);
796
731
  const value = segoff.segment === newSegoff.segment && segoff.offset === newSegoff.offset
797
732
  ? undefined
798
733
  : newSegoff;
@@ -805,7 +740,7 @@ export class IntervalCollection extends TypedEventEmitter {
805
740
  }
806
741
  const newStart = this.getSlideToSegment(interval.start, startReferenceSlidingPreference(interval.stickiness));
807
742
  const newEnd = this.getSlideToSegment(interval.end, endReferenceSlidingPreference(interval.stickiness));
808
- const id = interval.properties[reservedIntervalIdKey];
743
+ const id = interval.getIntervalId();
809
744
  const hasPendingStartChange = this.hasPendingChangeStart(id);
810
745
  const hasPendingEndChange = this.hasPendingChangeEnd(id);
811
746
  if (!hasPendingStartChange) {
@@ -858,10 +793,10 @@ export class IntervalCollection extends TypedEventEmitter {
858
793
  }
859
794
  }
860
795
  ackAdd(serializedInterval, local, op, localOpMetadata) {
796
+ const { id, properties } = getSerializedProperties(serializedInterval);
861
797
  if (local) {
862
798
  assert(localOpMetadata !== undefined, 0x553 /* op metadata should be defined for local op */);
863
799
  this.localSeqToSerializedInterval.delete(localOpMetadata.localSeq);
864
- const id = serializedInterval.properties?.[reservedIntervalIdKey];
865
800
  const localInterval = this.getIntervalById(id);
866
801
  if (localInterval) {
867
802
  this.ackInterval(localInterval, op);
@@ -871,8 +806,7 @@ export class IntervalCollection extends TypedEventEmitter {
871
806
  if (!this.localCollection) {
872
807
  throw new LoggingError("attachSequence must be called");
873
808
  }
874
- this.localCollection.ensureSerializedId(serializedInterval);
875
- const interval = this.localCollection.addInterval(toSequencePlace(serializedInterval.start, serializedInterval.startSide ?? Side.Before), toSequencePlace(serializedInterval.end, serializedInterval.endSide ?? Side.Before), serializedInterval.intervalType, serializedInterval.properties, op);
809
+ const interval = this.localCollection.addInterval(id, toSequencePlace(serializedInterval.start, serializedInterval.startSide ?? Side.Before), toSequencePlace(serializedInterval.end, serializedInterval.endSide ?? Side.Before), properties, op);
876
810
  if (interval) {
877
811
  if (this.onDeserialize) {
878
812
  this.onDeserialize(interval);
@@ -891,7 +825,7 @@ export class IntervalCollection extends TypedEventEmitter {
891
825
  if (!this.localCollection) {
892
826
  throw new LoggingError("attach must be called prior to deleting intervals");
893
827
  }
894
- const id = this.localCollection.ensureSerializedId(serializedInterval);
828
+ const { id } = getSerializedProperties(serializedInterval);
895
829
  const interval = this.localCollection.idIntervalIndex.getIntervalById(id);
896
830
  if (interval) {
897
831
  this.deleteExistingInterval(interval, local, op);