@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
@@ -17,18 +17,14 @@ import {
17
17
  PropertySet,
18
18
  ReferenceType,
19
19
  SlidingPreference,
20
- UnassignedSequenceNumber,
21
- UniversalSequenceNumber,
22
- addProperties,
23
20
  getSlideToSegoff,
24
21
  refTypeIncludesFlag,
25
22
  reservedRangeLabelsKey,
26
23
  Side,
27
24
  SequencePlace,
28
25
  endpointPosAndSide,
29
- PropertiesManager,
30
26
  type ISegmentInternal,
31
- createMap,
27
+ createLocalReconnectingPerspective,
32
28
  } from "@fluidframework/merge-tree/internal";
33
29
  import { LoggingError, UsageError } from "@fluidframework/telemetry-utils/internal";
34
30
  import { v4 as uuid } from "uuid";
@@ -62,13 +58,11 @@ import {
62
58
  createPositionReferenceFromSegoff,
63
59
  createSequenceInterval,
64
60
  endReferenceSlidingPreference,
61
+ getSerializedProperties,
65
62
  startReferenceSlidingPreference,
66
63
  type ISerializableInterval,
67
- type ISerializableIntervalPrivate,
68
64
  } from "./intervals/index.js";
69
65
 
70
- export const reservedIntervalIdKey = "intervalId";
71
-
72
66
  export type ISerializedIntervalCollectionV1 = ISerializedInterval[];
73
67
 
74
68
  export interface ISerializedIntervalCollectionV2 {
@@ -165,7 +159,6 @@ export function computeStickinessFromSide(
165
159
  }
166
160
 
167
161
  export class LocalIntervalCollection {
168
- private static readonly legacyIdPrefix = "legacy";
169
162
  public readonly overlappingIntervalsIndex: ISequenceOverlappingIntervalsIndex;
170
163
  public readonly idIntervalIndex: IIdIntervalIndex;
171
164
  public readonly endIntervalIndex: IEndpointIndex;
@@ -191,15 +184,6 @@ export class LocalIntervalCollection {
191
184
  ]);
192
185
  }
193
186
 
194
- public createLegacyId(
195
- start: number | "start" | "end",
196
- end: number | "start" | "end",
197
- ): string {
198
- // Create a non-unique ID based on start and end to be used on intervals that come from legacy clients
199
- // without ID's.
200
- return `${LocalIntervalCollection.legacyIdPrefix}${start}-${end}`;
201
- }
202
-
203
187
  /**
204
188
  * Validates that a serialized interval has the ID property. Creates an ID
205
189
  * if one does not already exist
@@ -207,27 +191,6 @@ export class LocalIntervalCollection {
207
191
  * @param serializedInterval - The interval to be checked
208
192
  * @returns The interval's existing or newly created id
209
193
  */
210
- public ensureSerializedId(serializedInterval: ISerializedInterval): string {
211
- let id: string | undefined = serializedInterval.properties?.[reservedIntervalIdKey];
212
- if (id === undefined) {
213
- // Back-compat: 0.39 and earlier did not have IDs on intervals. If an interval from such a client
214
- // comes over the wire, create a non-unique one based on start/end.
215
- // This will allow all clients to refer to this interval consistently.
216
- id = this.createLegacyId(serializedInterval.start, serializedInterval.end);
217
- const newProps = {
218
- [reservedIntervalIdKey]: id,
219
- };
220
- serializedInterval.properties = addProperties(serializedInterval.properties, newProps);
221
- }
222
- // Make the ID immutable for safety's sake.
223
- Object.defineProperty(serializedInterval.properties, reservedIntervalIdKey, {
224
- configurable: false,
225
- enumerable: true,
226
- writable: false,
227
- });
228
-
229
- return id;
230
- }
231
194
 
232
195
  private removeIntervalFromIndexes(interval: SequenceIntervalClass) {
233
196
  for (const index of this.indexes) {
@@ -248,54 +211,38 @@ export class LocalIntervalCollection {
248
211
  this.removeIntervalListeners(interval);
249
212
  }
250
213
 
251
- public createInterval(
214
+ public addInterval(
215
+ id: string,
252
216
  start: SequencePlace,
253
217
  end: SequencePlace,
254
- intervalType: IntervalType,
218
+ props?: PropertySet,
255
219
  op?: ISequencedDocumentMessage,
256
- ): SequenceIntervalClass {
257
- return createSequenceInterval(
220
+ ) {
221
+ // This check is intended to prevent scenarios where a random interval is created and then
222
+ // inserted into a collection. The aim is to ensure that the collection is created first
223
+ // then the user can create/add intervals based on the collection
224
+ if (
225
+ props?.[reservedRangeLabelsKey] !== undefined &&
226
+ props[reservedRangeLabelsKey][0] !== this.label
227
+ ) {
228
+ throw new LoggingError(
229
+ "Adding an interval that belongs to another interval collection is not permitted",
230
+ );
231
+ }
232
+ const interval: SequenceIntervalClass = createSequenceInterval(
258
233
  this.label,
234
+ id,
259
235
  start,
260
236
  end,
261
237
  this.client,
262
- intervalType,
238
+ IntervalType.SlideOnRemove,
263
239
  op,
264
240
  undefined,
265
241
  this.options.mergeTreeReferencesCanSlideToEndpoint,
242
+ props,
266
243
  );
267
- }
268
-
269
- public addInterval(
270
- start: SequencePlace,
271
- end: SequencePlace,
272
- intervalType: IntervalType,
273
- props?: PropertySet,
274
- op?: ISequencedDocumentMessage,
275
- ) {
276
- const interval: SequenceIntervalClass = this.createInterval(start, end, intervalType, op);
277
- if (interval) {
278
- if (!interval.properties) {
279
- interval.properties = createMap<any>();
280
- }
281
244
 
282
- if (props) {
283
- // This check is intended to prevent scenarios where a random interval is created and then
284
- // inserted into a collection. The aim is to ensure that the collection is created first
285
- // then the user can create/add intervals based on the collection
286
- if (
287
- props[reservedRangeLabelsKey] !== undefined &&
288
- props[reservedRangeLabelsKey][0] !== this.label
289
- ) {
290
- throw new LoggingError(
291
- "Adding an interval that belongs to another interval collection is not permitted",
292
- );
293
- }
294
- interval.properties = addProperties(interval.properties, props);
295
- }
296
- interval.properties[reservedIntervalIdKey] ??= uuid();
297
- this.add(interval);
298
- }
245
+ this.add(interval);
299
246
  return interval;
300
247
  }
301
248
 
@@ -1209,6 +1156,7 @@ export class IntervalCollection
1209
1156
  getSlideToSegoff(
1210
1157
  { segment, offset },
1211
1158
  undefined,
1159
+ createLocalReconnectingPerspective(this.client.getCurrentSeq(), clientId, localSeq),
1212
1160
  this.options.mergeTreeReferencesCanSlideToEndpoint,
1213
1161
  ) ?? segment;
1214
1162
 
@@ -1274,12 +1222,11 @@ export class IntervalCollection
1274
1222
  );
1275
1223
  if (this.savedSerializedIntervals) {
1276
1224
  for (const serializedInterval of this.savedSerializedIntervals) {
1277
- this.localCollection.ensureSerializedId(serializedInterval);
1225
+ const { id, properties } = getSerializedProperties(serializedInterval);
1278
1226
  const {
1279
1227
  start: startPos,
1280
1228
  end: endPos,
1281
1229
  intervalType,
1282
- properties,
1283
1230
  startSide,
1284
1231
  endSide,
1285
1232
  } = serializedInterval;
@@ -1293,6 +1240,7 @@ export class IntervalCollection
1293
1240
  : endPos;
1294
1241
  const interval = createSequenceInterval(
1295
1242
  label,
1243
+ id,
1296
1244
  start,
1297
1245
  end,
1298
1246
  client,
@@ -1300,10 +1248,8 @@ export class IntervalCollection
1300
1248
  undefined,
1301
1249
  true,
1302
1250
  this.options.mergeTreeReferencesCanSlideToEndpoint,
1251
+ properties,
1303
1252
  );
1304
- if (properties) {
1305
- interval.properties = addProperties(interval.properties, properties);
1306
- }
1307
1253
  this.localCollection.add(interval);
1308
1254
  }
1309
1255
  }
@@ -1345,7 +1291,7 @@ export class IntervalCollection
1345
1291
  /**
1346
1292
  * {@inheritdoc IIntervalCollection.getIntervalById}
1347
1293
  */
1348
- public getIntervalById(id: string): ISerializableIntervalPrivate | undefined {
1294
+ public getIntervalById(id: string): SequenceIntervalClass | undefined {
1349
1295
  if (!this.localCollection) {
1350
1296
  throw new LoggingError("attach must be called before accessing intervals");
1351
1297
  }
@@ -1367,10 +1313,12 @@ export class IntervalCollection
1367
1313
  * {@inheritdoc IIntervalCollection.add}
1368
1314
  */
1369
1315
  public add({
1316
+ id,
1370
1317
  start,
1371
1318
  end,
1372
1319
  props,
1373
1320
  }: {
1321
+ id?: string;
1374
1322
  start: SequencePlace;
1375
1323
  end: SequencePlace;
1376
1324
  props?: PropertySet;
@@ -1389,14 +1337,12 @@ export class IntervalCollection
1389
1337
  0x793 /* start and end cannot be undefined because they were not passed in as undefined */,
1390
1338
  );
1391
1339
 
1392
- const stickiness = computeStickinessFromSide(startPos, startSide, endPos, endSide);
1393
-
1394
1340
  this.assertStickinessEnabled(start, end);
1395
1341
 
1396
1342
  const interval: SequenceIntervalClass = this.localCollection.addInterval(
1343
+ id ?? uuid(),
1397
1344
  toSequencePlace(startPos, startSide),
1398
1345
  toSequencePlace(endPos, endSide),
1399
- IntervalType.SlideOnRemove,
1400
1346
  props,
1401
1347
  );
1402
1348
 
@@ -1405,16 +1351,7 @@ export class IntervalCollection
1405
1351
  setSlideOnRemove(interval.start);
1406
1352
  setSlideOnRemove(interval.end);
1407
1353
  }
1408
- const serializedInterval: ISerializedInterval = {
1409
- start: startPos,
1410
- end: endPos,
1411
- intervalType: IntervalType.SlideOnRemove,
1412
- properties: { ...interval.properties },
1413
- sequenceNumber: this.client?.getCurrentSeq() ?? 0,
1414
- stickiness,
1415
- startSide,
1416
- endSide,
1417
- };
1354
+ const serializedInterval: ISerializedInterval = interval.serialize();
1418
1355
  const localSeq = this.getNextLocalSeq();
1419
1356
  if (this.isCollaborating) {
1420
1357
  this.localSeqToSerializedInterval.set(localSeq, serializedInterval);
@@ -1518,35 +1455,24 @@ export class IntervalCollection
1518
1455
  let deltaProps: PropertySet | undefined;
1519
1456
  let newInterval: SequenceIntervalClass | undefined;
1520
1457
  if (props !== undefined) {
1521
- interval.propertyManager ??= new PropertiesManager();
1522
- deltaProps = interval.propertyManager.handleProperties(
1523
- { props },
1524
- interval,
1525
- this.isCollaborating ? UnassignedSequenceNumber : UniversalSequenceNumber,
1526
- UniversalSequenceNumber,
1527
- true,
1528
- );
1458
+ deltaProps = interval.changeProperties(props);
1529
1459
  }
1530
- if (start !== undefined && end !== undefined) {
1460
+ const changeEndpoints = start !== undefined && end !== undefined;
1461
+ if (changeEndpoints) {
1531
1462
  newInterval = this.localCollection.changeInterval(interval, start, end);
1532
1463
  if (!this.isCollaborating && newInterval !== undefined) {
1533
1464
  setSlideOnRemove(newInterval.start);
1534
1465
  setSlideOnRemove(newInterval.end);
1535
1466
  }
1536
1467
  }
1537
- const serializedInterval: SerializedIntervalDelta = interval.serialize();
1538
- const { startPos, startSide, endPos, endSide } = endpointPosAndSide(start, end);
1539
- const stickiness = computeStickinessFromSide(startPos, startSide, endPos, endSide);
1540
- serializedInterval.start = startPos;
1541
- serializedInterval.end = endPos;
1542
- serializedInterval.startSide = startSide;
1543
- serializedInterval.endSide = endSide;
1544
- serializedInterval.stickiness = stickiness;
1545
1468
  // Emit a property bag containing the ID and the other (if any) properties changed
1546
- serializedInterval.properties = {
1547
- [reservedIntervalIdKey]: interval.getIntervalId(),
1548
- ...props,
1549
- };
1469
+ const serializedInterval: SerializedIntervalDelta = (
1470
+ newInterval ?? interval
1471
+ ).serializeDelta({
1472
+ props,
1473
+ includeEndpoints: changeEndpoints,
1474
+ });
1475
+
1550
1476
  const localSeq = this.getNextLocalSeq();
1551
1477
  if (this.isCollaborating) {
1552
1478
  this.localSeqToSerializedInterval.set(localSeq, serializedInterval);
@@ -1615,7 +1541,7 @@ export class IntervalCollection
1615
1541
 
1616
1542
  private removePendingChange(serializedInterval: SerializedIntervalDelta) {
1617
1543
  // Change ops always have an ID.
1618
- const id: string = serializedInterval.properties?.[reservedIntervalIdKey];
1544
+ const { id } = getSerializedProperties(serializedInterval);
1619
1545
  if (serializedInterval.start !== undefined) {
1620
1546
  this.removePendingChangeHelper(id, this.pendingChangesStart, serializedInterval);
1621
1547
  }
@@ -1677,20 +1603,16 @@ export class IntervalCollection
1677
1603
  // Note that the ID is in the property bag only to allow us to find the interval.
1678
1604
  // This API cannot change the ID, and writing to the ID property will result in an exception. So we
1679
1605
  // strip it out of the properties here.
1680
- const { [reservedIntervalIdKey]: id, ...newProps } = serializedInterval.properties ?? {};
1606
+ const { id, properties } = getSerializedProperties(serializedInterval);
1681
1607
  assert(id !== undefined, 0x3fe /* id must exist on the interval */);
1682
- const interval: ISerializableIntervalPrivate | undefined = this.getIntervalById(id);
1608
+ const interval: SequenceIntervalClass | undefined = this.getIntervalById(id);
1683
1609
  if (!interval) {
1684
1610
  // The interval has been removed locally; no-op.
1685
1611
  return;
1686
1612
  }
1687
1613
 
1688
1614
  if (local) {
1689
- interval.propertyManager ??= new PropertiesManager();
1690
- // Let the propertyManager prune its pending change-properties set.
1691
- interval.propertyManager.ack(op.sequenceNumber, op.minimumSequenceNumber, {
1692
- props: newProps,
1693
- });
1615
+ interval.ackPropertiesChange(properties, op);
1694
1616
 
1695
1617
  this.ackInterval(interval, op);
1696
1618
  } else {
@@ -1718,14 +1640,8 @@ export class IntervalCollection
1718
1640
  op,
1719
1641
  ) ?? interval;
1720
1642
  }
1721
- newInterval.propertyManager ??= new PropertiesManager();
1722
- const deltaProps = newInterval.propertyManager.handleProperties(
1723
- { props: newProps },
1724
- newInterval,
1725
- op.sequenceNumber,
1726
- op.minimumSequenceNumber,
1727
- true,
1728
- );
1643
+ const deltaProps = newInterval.changeProperties(properties, op);
1644
+
1729
1645
  if (this.onDeserialize) {
1730
1646
  this.onDeserialize(newInterval);
1731
1647
  }
@@ -1734,7 +1650,7 @@ export class IntervalCollection
1734
1650
  this.emitChange(newInterval, interval, local, false, op);
1735
1651
  }
1736
1652
 
1737
- const changedProperties = Object.keys(newProps).length > 0;
1653
+ const changedProperties = Object.keys(properties).length > 0;
1738
1654
  if (changedProperties) {
1739
1655
  this.emit("propertyChanged", interval, deltaProps, local, op);
1740
1656
  this.emit("changed", interval, deltaProps, undefined, local, false);
@@ -1780,12 +1696,11 @@ export class IntervalCollection
1780
1696
  }
1781
1697
 
1782
1698
  const { intervalType, properties, stickiness, startSide, endSide } = serializedInterval;
1783
-
1699
+ const { id } = getSerializedProperties(serializedInterval);
1784
1700
  const { start: startRebased, end: endRebased } =
1785
1701
  this.localSeqToRebasedInterval.get(localSeq) ?? this.computeRebasedPositions(localSeq);
1786
1702
 
1787
- const intervalId = properties?.[reservedIntervalIdKey];
1788
- const localInterval = this.localCollection?.idIntervalIndex.getIntervalById(intervalId);
1703
+ const localInterval = this.localCollection?.idIntervalIndex.getIntervalById(id);
1789
1704
 
1790
1705
  const rebased: SerializedIntervalDelta = {
1791
1706
  start: startRebased,
@@ -1801,10 +1716,10 @@ export class IntervalCollection
1801
1716
  if (
1802
1717
  opName === "change" &&
1803
1718
  // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing -- ?? is not logically equivalent when .hasPendingChangeStart returns false.
1804
- (this.hasPendingChangeStart(intervalId) || this.hasPendingChangeEnd(intervalId))
1719
+ (this.hasPendingChangeStart(id) || this.hasPendingChangeEnd(id))
1805
1720
  ) {
1806
1721
  this.removePendingChange(serializedInterval);
1807
- this.addPendingChange(intervalId, rebased);
1722
+ this.addPendingChange(id, rebased);
1808
1723
  }
1809
1724
 
1810
1725
  // if the interval slid off the string, rebase the op to be a noop and delete the interval.
@@ -1850,6 +1765,7 @@ export class IntervalCollection
1850
1765
  const newSegoff = getSlideToSegoff(
1851
1766
  segoff,
1852
1767
  slidingPreference,
1768
+ undefined,
1853
1769
  this.options.mergeTreeReferencesCanSlideToEndpoint,
1854
1770
  );
1855
1771
  const value: { segment: ISegment | undefined; offset: number | undefined } | undefined =
@@ -1876,7 +1792,7 @@ export class IntervalCollection
1876
1792
  endReferenceSlidingPreference(interval.stickiness),
1877
1793
  );
1878
1794
 
1879
- const id = interval.properties[reservedIntervalIdKey];
1795
+ const id = interval.getIntervalId();
1880
1796
  const hasPendingStartChange = this.hasPendingChangeStart(id);
1881
1797
  const hasPendingEndChange = this.hasPendingChangeEnd(id);
1882
1798
 
@@ -1960,13 +1876,14 @@ export class IntervalCollection
1960
1876
  op: ISequencedDocumentMessage,
1961
1877
  localOpMetadata: IMapMessageLocalMetadata | undefined,
1962
1878
  ) {
1879
+ const { id, properties } = getSerializedProperties(serializedInterval);
1880
+
1963
1881
  if (local) {
1964
1882
  assert(
1965
1883
  localOpMetadata !== undefined,
1966
1884
  0x553 /* op metadata should be defined for local op */,
1967
1885
  );
1968
1886
  this.localSeqToSerializedInterval.delete(localOpMetadata.localSeq);
1969
- const id: string = serializedInterval.properties?.[reservedIntervalIdKey];
1970
1887
  const localInterval = this.getIntervalById(id);
1971
1888
  if (localInterval) {
1972
1889
  this.ackInterval(localInterval, op);
@@ -1978,13 +1895,11 @@ export class IntervalCollection
1978
1895
  throw new LoggingError("attachSequence must be called");
1979
1896
  }
1980
1897
 
1981
- this.localCollection.ensureSerializedId(serializedInterval);
1982
-
1983
1898
  const interval: SequenceIntervalClass = this.localCollection.addInterval(
1899
+ id,
1984
1900
  toSequencePlace(serializedInterval.start, serializedInterval.startSide ?? Side.Before),
1985
1901
  toSequencePlace(serializedInterval.end, serializedInterval.endSide ?? Side.Before),
1986
- serializedInterval.intervalType,
1987
- serializedInterval.properties,
1902
+ properties,
1988
1903
  op,
1989
1904
  );
1990
1905
 
@@ -2015,7 +1930,7 @@ export class IntervalCollection
2015
1930
  throw new LoggingError("attach must be called prior to deleting intervals");
2016
1931
  }
2017
1932
 
2018
- const id = this.localCollection.ensureSerializedId(serializedInterval);
1933
+ const { id } = getSerializedProperties(serializedInterval);
2019
1934
  const interval = this.localCollection.idIntervalIndex.getIntervalById(id);
2020
1935
  if (interval) {
2021
1936
  this.deleteExistingInterval(interval, local, op);
@@ -14,7 +14,6 @@ import { makeSerializable } from "./IntervalCollectionValues.js";
14
14
  import {
15
15
  IntervalCollection,
16
16
  opsMap,
17
- reservedIntervalIdKey,
18
17
  toOptionalSequencePlace,
19
18
  toSequencePlace,
20
19
  type ISerializedIntervalCollectionV1,
@@ -26,6 +25,7 @@ import {
26
25
  ISerializableIntervalCollection,
27
26
  SequenceOptions,
28
27
  } from "./intervalCollectionMapInterfaces.js";
28
+ import { getSerializedProperties } from "./intervals/index.js";
29
29
 
30
30
  function isMapOperation(op: unknown): op is IMapOperation {
31
31
  return typeof op === "object" && op !== null && "type" in op && op.type === "act";
@@ -217,30 +217,30 @@ export class IntervalCollectionMap {
217
217
  if (isMapOperation(op)) {
218
218
  const { value, key } = op;
219
219
  const map = this.get(key);
220
+ const { id, properties } = getSerializedProperties(value.value);
220
221
 
221
222
  switch (value.opName) {
222
223
  case "add": {
223
224
  map.add({
225
+ id,
224
226
  // Todo: we should improve typing so we know add ops always have start and end
225
227
  // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
226
228
  start: toSequencePlace(value.value.start!, value.value.startSide),
227
229
  // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
228
230
  end: toSequencePlace(value.value.end!, value.value.endSide),
229
- props: value.value.properties,
231
+ props: properties,
230
232
  });
231
233
  return true;
232
234
  }
233
235
  case "change": {
234
- const { [reservedIntervalIdKey]: id, ...props } = value.value.properties ?? {};
235
236
  map.change(id, {
236
237
  start: toOptionalSequencePlace(value.value.start, value.value.startSide),
237
238
  end: toOptionalSequencePlace(value.value.end, value.value.endSide),
238
- props,
239
+ props: properties,
239
240
  });
240
241
  return true;
241
242
  }
242
243
  case "delete": {
243
- const { [reservedIntervalIdKey]: id } = value.value.properties ?? {};
244
244
  map.removeIntervalById(id);
245
245
  return true;
246
246
  }
@@ -7,7 +7,7 @@
7
7
 
8
8
  import { Client, PropertyAction, RedBlackTree } from "@fluidframework/merge-tree/internal";
9
9
 
10
- import { IntervalType, SequenceInterval, createSequenceInterval } from "../intervals/index.js";
10
+ import { SequenceInterval, createTransientInterval } from "../intervals/index.js";
11
11
  import { ISharedString } from "../sharedString.js";
12
12
 
13
13
  import { type SequenceIntervalIndex } from "./intervalIndex.js";
@@ -77,21 +77,9 @@ export class EndpointInRangeIndex implements IEndpointInRangeIndex {
77
77
  return true;
78
78
  };
79
79
 
80
- const transientStartInterval = createSequenceInterval(
81
- "transient",
82
- start,
83
- start,
84
- this.client,
85
- IntervalType.Transient,
86
- );
80
+ const transientStartInterval = createTransientInterval(start, start, this.client);
87
81
 
88
- const transientEndInterval = createSequenceInterval(
89
- "transient",
90
- end,
91
- end,
92
- this.client,
93
- IntervalType.Transient,
94
- );
82
+ const transientEndInterval = createTransientInterval(end, end, this.client);
95
83
 
96
84
  // Add comparison overrides to the transient intervals
97
85
  (transientStartInterval as Partial<HasComparisonOverride>)[forceCompare] = -1;
@@ -3,11 +3,9 @@
3
3
  * Licensed under the MIT License.
4
4
  */
5
5
 
6
- /* eslint-disable import/no-deprecated */
7
-
8
6
  import { Client, RedBlackTree } from "@fluidframework/merge-tree/internal";
9
7
 
10
- import { createSequenceInterval, IntervalType, SequenceInterval } from "../intervals/index.js";
8
+ import { createTransientInterval, SequenceInterval } from "../intervals/index.js";
11
9
  import { ISharedString } from "../sharedString.js";
12
10
 
13
11
  import { type SequenceIntervalIndex } from "./intervalIndex.js";
@@ -39,13 +37,7 @@ export class EndpointIndex implements IEndpointIndex {
39
37
  }
40
38
 
41
39
  public previousInterval(pos: number): SequenceInterval | undefined {
42
- const transientInterval = createSequenceInterval(
43
- "transient",
44
- pos,
45
- pos,
46
- this.client,
47
- IntervalType.Transient,
48
- );
40
+ const transientInterval = createTransientInterval(pos, pos, this.client);
49
41
  const rbNode = this.endIntervalTree.floor(transientInterval);
50
42
  if (rbNode) {
51
43
  return rbNode.data;
@@ -53,13 +45,7 @@ export class EndpointIndex implements IEndpointIndex {
53
45
  }
54
46
 
55
47
  public nextInterval(pos: number): SequenceInterval | undefined {
56
- const transientInterval = createSequenceInterval(
57
- "transient",
58
- pos,
59
- pos,
60
- this.client,
61
- IntervalType.Transient,
62
- );
48
+ const transientInterval = createTransientInterval(pos, pos, this.client);
63
49
  const rbNode = this.endIntervalTree.ceil(transientInterval);
64
50
  if (rbNode) {
65
51
  return rbNode.data;
@@ -5,7 +5,6 @@
5
5
 
6
6
  import { assert } from "@fluidframework/core-utils/internal";
7
7
 
8
- import { reservedIntervalIdKey } from "../intervalCollection.js";
9
8
  import { type SequenceIntervalClass } from "../intervals/index.js";
10
9
 
11
10
  import { type SequenceIntervalIndex } from "./intervalIndex.js";
@@ -26,12 +25,6 @@ class IdIntervalIndex implements IIdIntervalIndex, Iterable<SequenceIntervalClas
26
25
  id !== undefined,
27
26
  0x2c0 /* "ID must be created before adding interval to collection" */,
28
27
  );
29
- // Make the ID immutable.
30
- Object.defineProperty(interval.properties, reservedIntervalIdKey, {
31
- configurable: false,
32
- enumerable: true,
33
- writable: false,
34
- });
35
28
  this.intervalIdMap.set(id, interval);
36
29
  }
37
30
 
@@ -23,4 +23,3 @@ export {
23
23
  OverlappingIntervalsIndex,
24
24
  ISequenceOverlappingIntervalsIndex,
25
25
  } from "./overlappingIntervalsIndex.js";
26
- export { createOverlappingSequenceIntervalsIndex } from "./overlappingSequenceIntervalsIndex.js";
@@ -14,9 +14,8 @@ import {
14
14
  import { IntervalNode, IntervalTree } from "../intervalTree.js";
15
15
  import {
16
16
  ISerializableInterval,
17
- IntervalType,
18
17
  SequenceInterval,
19
- createSequenceInterval,
18
+ createTransientInterval,
20
19
  } from "../intervals/index.js";
21
20
  import { ISharedString } from "../sharedString.js";
22
21
 
@@ -108,12 +107,10 @@ export class OverlappingIntervalsIndex implements ISequenceOverlappingIntervalsI
108
107
  });
109
108
  }
110
109
  } else {
111
- const transientInterval: SequenceInterval = createSequenceInterval(
112
- "transient",
110
+ const transientInterval: SequenceInterval = createTransientInterval(
113
111
  start ?? "start",
114
112
  end ?? "end",
115
113
  this.client,
116
- IntervalType.Transient,
117
114
  );
118
115
 
119
116
  if (start === undefined) {
@@ -184,13 +181,7 @@ export class OverlappingIntervalsIndex implements ISequenceOverlappingIntervalsI
184
181
  ) {
185
182
  return [];
186
183
  }
187
- const transientInterval = createSequenceInterval(
188
- "transient",
189
- start,
190
- end,
191
- this.client,
192
- IntervalType.Transient,
193
- );
184
+ const transientInterval = createTransientInterval(start, end, this.client);
194
185
 
195
186
  const overlappingIntervalNodes = this.intervalTree.match(transientInterval);
196
187
  return overlappingIntervalNodes.map((node) => node.key);
@@ -7,7 +7,7 @@
7
7
 
8
8
  import { Client, PropertyAction, RedBlackTree } from "@fluidframework/merge-tree/internal";
9
9
 
10
- import { IntervalType, SequenceInterval, createSequenceInterval } from "../intervals/index.js";
10
+ import { SequenceInterval, createTransientInterval } from "../intervals/index.js";
11
11
  import { ISharedString } from "../sharedString.js";
12
12
 
13
13
  import { type SequenceIntervalIndex } from "./intervalIndex.js";
@@ -76,21 +76,9 @@ export class StartpointInRangeIndex implements IStartpointInRangeIndex {
76
76
  return true;
77
77
  };
78
78
 
79
- const transientStartInterval = createSequenceInterval(
80
- "transient",
81
- start,
82
- start,
83
- this.client,
84
- IntervalType.Transient,
85
- );
79
+ const transientStartInterval = createTransientInterval(start, start, this.client);
86
80
 
87
- const transientEndInterval = createSequenceInterval(
88
- "transient",
89
- end,
90
- end,
91
- this.client,
92
- IntervalType.Transient,
93
- );
81
+ const transientEndInterval = createTransientInterval(end, end, this.client);
94
82
 
95
83
  // Add comparison overrides to the transient intervals
96
84
  (transientStartInterval as Partial<HasComparisonOverride>)[forceCompare] = -1;
@@ -11,7 +11,6 @@ export {
11
11
  IntervalType,
12
12
  IntervalDeltaOpType,
13
13
  IntervalStickiness,
14
- ISerializableIntervalPrivate,
15
14
  SerializedIntervalDelta,
16
15
  CompressedSerializedInterval,
17
16
  endReferenceSlidingPreference,
@@ -22,4 +21,6 @@ export {
22
21
  SequenceIntervalClass,
23
22
  createSequenceInterval,
24
23
  createPositionReferenceFromSegoff,
24
+ createTransientInterval,
25
+ getSerializedProperties,
25
26
  } from "./sequenceInterval.js";