@fluidframework/sequence 2.31.1 → 2.33.0-333010

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 (130) 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 +11 -13
  7. package/dist/intervalCollection.d.ts.map +1 -1
  8. package/dist/intervalCollection.js +96 -149
  9. package/dist/intervalCollection.js.map +1 -1
  10. package/dist/intervalCollectionMap.d.ts +4 -4
  11. package/dist/intervalCollectionMap.d.ts.map +1 -1
  12. package/dist/intervalCollectionMap.js +16 -49
  13. package/dist/intervalCollectionMap.js.map +1 -1
  14. package/dist/intervalCollectionMapInterfaces.d.ts +21 -15
  15. package/dist/intervalCollectionMapInterfaces.d.ts.map +1 -1
  16. package/dist/intervalCollectionMapInterfaces.js.map +1 -1
  17. package/dist/intervalIndex/endpointInRangeIndex.d.ts.map +1 -1
  18. package/dist/intervalIndex/endpointInRangeIndex.js +2 -2
  19. package/dist/intervalIndex/endpointInRangeIndex.js.map +1 -1
  20. package/dist/intervalIndex/endpointIndex.d.ts.map +1 -1
  21. package/dist/intervalIndex/endpointIndex.js +2 -3
  22. package/dist/intervalIndex/endpointIndex.js.map +1 -1
  23. package/dist/intervalIndex/idIntervalIndex.d.ts.map +1 -1
  24. package/dist/intervalIndex/idIntervalIndex.js +0 -7
  25. package/dist/intervalIndex/idIntervalIndex.js.map +1 -1
  26. package/dist/intervalIndex/index.d.ts +0 -1
  27. package/dist/intervalIndex/index.d.ts.map +1 -1
  28. package/dist/intervalIndex/index.js +1 -3
  29. package/dist/intervalIndex/index.js.map +1 -1
  30. package/dist/intervalIndex/overlappingIntervalsIndex.d.ts.map +1 -1
  31. package/dist/intervalIndex/overlappingIntervalsIndex.js +2 -2
  32. package/dist/intervalIndex/overlappingIntervalsIndex.js.map +1 -1
  33. package/dist/intervalIndex/startpointInRangeIndex.d.ts.map +1 -1
  34. package/dist/intervalIndex/startpointInRangeIndex.js +2 -2
  35. package/dist/intervalIndex/startpointInRangeIndex.js.map +1 -1
  36. package/dist/intervals/index.d.ts +2 -2
  37. package/dist/intervals/index.d.ts.map +1 -1
  38. package/dist/intervals/index.js +3 -1
  39. package/dist/intervals/index.js.map +1 -1
  40. package/dist/intervals/intervalUtils.d.ts +1 -5
  41. package/dist/intervals/intervalUtils.d.ts.map +1 -1
  42. package/dist/intervals/intervalUtils.js.map +1 -1
  43. package/dist/intervals/sequenceInterval.d.ts +20 -9
  44. package/dist/intervals/sequenceInterval.d.ts.map +1 -1
  45. package/dist/intervals/sequenceInterval.js +82 -27
  46. package/dist/intervals/sequenceInterval.js.map +1 -1
  47. package/dist/packageVersion.d.ts +1 -1
  48. package/dist/packageVersion.d.ts.map +1 -1
  49. package/dist/packageVersion.js +1 -1
  50. package/dist/packageVersion.js.map +1 -1
  51. package/dist/revertibles.d.ts.map +1 -1
  52. package/dist/revertibles.js +8 -7
  53. package/dist/revertibles.js.map +1 -1
  54. package/lib/index.d.ts +1 -1
  55. package/lib/index.d.ts.map +1 -1
  56. package/lib/index.js +1 -1
  57. package/lib/index.js.map +1 -1
  58. package/lib/intervalCollection.d.ts +11 -13
  59. package/lib/intervalCollection.d.ts.map +1 -1
  60. package/lib/intervalCollection.js +98 -151
  61. package/lib/intervalCollection.js.map +1 -1
  62. package/lib/intervalCollectionMap.d.ts +4 -4
  63. package/lib/intervalCollectionMap.d.ts.map +1 -1
  64. package/lib/intervalCollectionMap.js +17 -50
  65. package/lib/intervalCollectionMap.js.map +1 -1
  66. package/lib/intervalCollectionMapInterfaces.d.ts +21 -15
  67. package/lib/intervalCollectionMapInterfaces.d.ts.map +1 -1
  68. package/lib/intervalCollectionMapInterfaces.js.map +1 -1
  69. package/lib/intervalIndex/endpointInRangeIndex.d.ts.map +1 -1
  70. package/lib/intervalIndex/endpointInRangeIndex.js +3 -3
  71. package/lib/intervalIndex/endpointInRangeIndex.js.map +1 -1
  72. package/lib/intervalIndex/endpointIndex.d.ts.map +1 -1
  73. package/lib/intervalIndex/endpointIndex.js +3 -4
  74. package/lib/intervalIndex/endpointIndex.js.map +1 -1
  75. package/lib/intervalIndex/idIntervalIndex.d.ts.map +1 -1
  76. package/lib/intervalIndex/idIntervalIndex.js +0 -7
  77. package/lib/intervalIndex/idIntervalIndex.js.map +1 -1
  78. package/lib/intervalIndex/index.d.ts +0 -1
  79. package/lib/intervalIndex/index.d.ts.map +1 -1
  80. package/lib/intervalIndex/index.js +0 -1
  81. package/lib/intervalIndex/index.js.map +1 -1
  82. package/lib/intervalIndex/overlappingIntervalsIndex.d.ts.map +1 -1
  83. package/lib/intervalIndex/overlappingIntervalsIndex.js +3 -3
  84. package/lib/intervalIndex/overlappingIntervalsIndex.js.map +1 -1
  85. package/lib/intervalIndex/startpointInRangeIndex.d.ts.map +1 -1
  86. package/lib/intervalIndex/startpointInRangeIndex.js +3 -3
  87. package/lib/intervalIndex/startpointInRangeIndex.js.map +1 -1
  88. package/lib/intervals/index.d.ts +2 -2
  89. package/lib/intervals/index.d.ts.map +1 -1
  90. package/lib/intervals/index.js +1 -1
  91. package/lib/intervals/index.js.map +1 -1
  92. package/lib/intervals/intervalUtils.d.ts +1 -5
  93. package/lib/intervals/intervalUtils.d.ts.map +1 -1
  94. package/lib/intervals/intervalUtils.js.map +1 -1
  95. package/lib/intervals/sequenceInterval.d.ts +20 -9
  96. package/lib/intervals/sequenceInterval.d.ts.map +1 -1
  97. package/lib/intervals/sequenceInterval.js +81 -28
  98. package/lib/intervals/sequenceInterval.js.map +1 -1
  99. package/lib/packageVersion.d.ts +1 -1
  100. package/lib/packageVersion.d.ts.map +1 -1
  101. package/lib/packageVersion.js +1 -1
  102. package/lib/packageVersion.js.map +1 -1
  103. package/lib/revertibles.d.ts.map +1 -1
  104. package/lib/revertibles.js +8 -7
  105. package/lib/revertibles.js.map +1 -1
  106. package/package.json +19 -18
  107. package/src/index.ts +0 -1
  108. package/src/intervalCollection.ts +135 -198
  109. package/src/intervalCollectionMap.ts +19 -61
  110. package/src/intervalCollectionMapInterfaces.ts +33 -29
  111. package/src/intervalIndex/endpointInRangeIndex.ts +3 -15
  112. package/src/intervalIndex/endpointIndex.ts +3 -17
  113. package/src/intervalIndex/idIntervalIndex.ts +0 -7
  114. package/src/intervalIndex/index.ts +0 -1
  115. package/src/intervalIndex/overlappingIntervalsIndex.ts +3 -12
  116. package/src/intervalIndex/startpointInRangeIndex.ts +3 -15
  117. package/src/intervals/index.ts +2 -1
  118. package/src/intervals/intervalUtils.ts +0 -7
  119. package/src/intervals/sequenceInterval.ts +124 -33
  120. package/src/packageVersion.ts +1 -1
  121. package/src/revertibles.ts +8 -7
  122. package/dist/intervalIndex/overlappingSequenceIntervalsIndex.d.ts +0 -11
  123. package/dist/intervalIndex/overlappingSequenceIntervalsIndex.d.ts.map +0 -1
  124. package/dist/intervalIndex/overlappingSequenceIntervalsIndex.js +0 -38
  125. package/dist/intervalIndex/overlappingSequenceIntervalsIndex.js.map +0 -1
  126. package/lib/intervalIndex/overlappingSequenceIntervalsIndex.d.ts +0 -11
  127. package/lib/intervalIndex/overlappingSequenceIntervalsIndex.d.ts.map +0 -1
  128. package/lib/intervalIndex/overlappingSequenceIntervalsIndex.js +0 -34
  129. package/lib/intervalIndex/overlappingSequenceIntervalsIndex.js.map +0 -1
  130. package/src/intervalIndex/overlappingSequenceIntervalsIndex.ts +0 -80
@@ -4,7 +4,7 @@
4
4
  * Licensed under the MIT License.
5
5
  */
6
6
  Object.defineProperty(exports, "__esModule", { value: true });
7
- exports.intervalLocatorFromEndpoint = exports.IntervalCollection = exports.opsMap = exports.LocalIntervalCollection = exports.computeStickinessFromSide = exports.toOptionalSequencePlace = exports.toSequencePlace = exports.sidesFromStickiness = exports.reservedIntervalIdKey = void 0;
7
+ exports.intervalLocatorFromEndpoint = exports.IntervalCollection = exports.LocalIntervalCollection = exports.computeStickinessFromSide = exports.toOptionalSequencePlace = exports.toSequencePlace = exports.sidesFromStickiness = void 0;
8
8
  /* eslint-disable no-bitwise */
9
9
  const client_utils_1 = require("@fluid-internal/client-utils");
10
10
  const internal_1 = require("@fluidframework/core-utils/internal");
@@ -13,7 +13,6 @@ const internal_3 = require("@fluidframework/telemetry-utils/internal");
13
13
  const uuid_1 = require("uuid");
14
14
  const index_js_1 = require("./intervalIndex/index.js");
15
15
  const index_js_2 = require("./intervals/index.js");
16
- exports.reservedIntervalIdKey = "intervalId";
17
16
  function sidesFromStickiness(stickiness) {
18
17
  const startSide = (stickiness & index_js_2.IntervalStickiness.START) !== 0 ? internal_2.Side.After : internal_2.Side.Before;
19
18
  const endSide = (stickiness & index_js_2.IntervalStickiness.END) !== 0 ? internal_2.Side.Before : internal_2.Side.After;
@@ -95,11 +94,6 @@ class LocalIntervalCollection {
95
94
  this.endIntervalIndex,
96
95
  ]);
97
96
  }
98
- createLegacyId(start, end) {
99
- // Create a non-unique ID based on start and end to be used on intervals that come from legacy clients
100
- // without ID's.
101
- return `${LocalIntervalCollection.legacyIdPrefix}${start}-${end}`;
102
- }
103
97
  /**
104
98
  * Validates that a serialized interval has the ID property. Creates an ID
105
99
  * if one does not already exist
@@ -107,26 +101,6 @@ class LocalIntervalCollection {
107
101
  * @param serializedInterval - The interval to be checked
108
102
  * @returns The interval's existing or newly created id
109
103
  */
110
- ensureSerializedId(serializedInterval) {
111
- let id = serializedInterval.properties?.[exports.reservedIntervalIdKey];
112
- if (id === undefined) {
113
- // Back-compat: 0.39 and earlier did not have IDs on intervals. If an interval from such a client
114
- // comes over the wire, create a non-unique one based on start/end.
115
- // This will allow all clients to refer to this interval consistently.
116
- id = this.createLegacyId(serializedInterval.start, serializedInterval.end);
117
- const newProps = {
118
- [exports.reservedIntervalIdKey]: id,
119
- };
120
- serializedInterval.properties = (0, internal_2.addProperties)(serializedInterval.properties, newProps);
121
- }
122
- // Make the ID immutable for safety's sake.
123
- Object.defineProperty(serializedInterval.properties, exports.reservedIntervalIdKey, {
124
- configurable: false,
125
- enumerable: true,
126
- writable: false,
127
- });
128
- return id;
129
- }
130
104
  removeIntervalFromIndexes(interval) {
131
105
  for (const index of this.indexes) {
132
106
  index.remove(interval);
@@ -142,28 +116,16 @@ class LocalIntervalCollection {
142
116
  this.removeIntervalFromIndexes(interval);
143
117
  this.removeIntervalListeners(interval);
144
118
  }
145
- createInterval(start, end, intervalType, op) {
146
- return (0, index_js_2.createSequenceInterval)(this.label, start, end, this.client, intervalType, op, undefined, this.options.mergeTreeReferencesCanSlideToEndpoint);
147
- }
148
- addInterval(start, end, intervalType, props, op) {
149
- const interval = this.createInterval(start, end, intervalType, op);
150
- if (interval) {
151
- if (!interval.properties) {
152
- interval.properties = (0, internal_2.createMap)();
153
- }
154
- if (props) {
155
- // This check is intended to prevent scenarios where a random interval is created and then
156
- // inserted into a collection. The aim is to ensure that the collection is created first
157
- // then the user can create/add intervals based on the collection
158
- if (props[internal_2.reservedRangeLabelsKey] !== undefined &&
159
- props[internal_2.reservedRangeLabelsKey][0] !== this.label) {
160
- throw new internal_3.LoggingError("Adding an interval that belongs to another interval collection is not permitted");
161
- }
162
- interval.properties = (0, internal_2.addProperties)(interval.properties, props);
163
- }
164
- interval.properties[exports.reservedIntervalIdKey] ??= (0, uuid_1.v4)();
165
- this.add(interval);
119
+ addInterval(id, start, end, props, op) {
120
+ // This check is intended to prevent scenarios where a random interval is created and then
121
+ // inserted into a collection. The aim is to ensure that the collection is created first
122
+ // then the user can create/add intervals based on the collection
123
+ if (props?.[internal_2.reservedRangeLabelsKey] !== undefined &&
124
+ props[internal_2.reservedRangeLabelsKey][0] !== this.label) {
125
+ throw new internal_3.LoggingError("Adding an interval that belongs to another interval collection is not permitted");
166
126
  }
127
+ const interval = (0, index_js_2.createSequenceInterval)(this.label, id, start, end, this.client, index_js_2.IntervalType.SlideOnRemove, op, undefined, this.options.mergeTreeReferencesCanSlideToEndpoint, props);
128
+ this.add(interval);
167
129
  return interval;
168
130
  }
169
131
  linkEndpointsToInterval(interval) {
@@ -235,52 +197,6 @@ class LocalIntervalCollection {
235
197
  }
236
198
  }
237
199
  exports.LocalIntervalCollection = LocalIntervalCollection;
238
- LocalIntervalCollection.legacyIdPrefix = "legacy";
239
- const rebase = (collection, op, localOpMetadata) => {
240
- const { localSeq } = localOpMetadata;
241
- const rebasedValue = collection.rebaseLocalInterval(op.opName, op.value, localSeq);
242
- if (rebasedValue === undefined) {
243
- return undefined;
244
- }
245
- const rebasedOp = { ...op, value: rebasedValue };
246
- return { rebasedOp, rebasedLocalOpMetadata: localOpMetadata };
247
- };
248
- exports.opsMap = {
249
- [index_js_2.IntervalDeltaOpType.ADD]: {
250
- process: (collection, params, local, op, localOpMetadata) => {
251
- // if params is undefined, the interval was deleted during
252
- // rebasing
253
- if (!params) {
254
- return;
255
- }
256
- (0, internal_1.assert)(op !== undefined, 0x3fb /* op should exist here */);
257
- collection.ackAdd(params, local, op, localOpMetadata);
258
- },
259
- rebase,
260
- },
261
- [index_js_2.IntervalDeltaOpType.DELETE]: {
262
- process: (collection, params, local, op) => {
263
- (0, internal_1.assert)(op !== undefined, 0x3fc /* op should exist here */);
264
- collection.ackDelete(params, local, op);
265
- },
266
- rebase: (collection, op, localOpMetadata) => {
267
- // Deletion of intervals is based on id, so requires no rebasing.
268
- return { rebasedOp: op, rebasedLocalOpMetadata: localOpMetadata };
269
- },
270
- },
271
- [index_js_2.IntervalDeltaOpType.CHANGE]: {
272
- process: (collection, params, local, op, localOpMetadata) => {
273
- // if params is undefined, the interval was deleted during
274
- // rebasing
275
- if (!params) {
276
- return;
277
- }
278
- (0, internal_1.assert)(op !== undefined, 0x3fd /* op should exist here */);
279
- collection.ackChange(params, local, op, localOpMetadata);
280
- },
281
- rebase,
282
- },
283
- };
284
200
  class IntervalCollectionIterator {
285
201
  constructor(collection, iteratesForward = true, start, end) {
286
202
  this.results = [];
@@ -347,6 +263,64 @@ class IntervalCollection extends client_utils_1.TypedEventEmitter {
347
263
  }
348
264
  return true;
349
265
  }
266
+ process(op, local, message, localOpMetadata) {
267
+ const { opName, value } = op;
268
+ switch (opName) {
269
+ case "add": {
270
+ this.ackAdd(value, local, message, localOpMetadata);
271
+ break;
272
+ }
273
+ case "delete": {
274
+ this.ackDelete(value, local, message);
275
+ break;
276
+ }
277
+ case "change": {
278
+ this.ackChange(value, local, message, localOpMetadata);
279
+ break;
280
+ }
281
+ default:
282
+ (0, internal_1.unreachableCase)(opName);
283
+ }
284
+ }
285
+ resubmitMessage(op, localOpMetadata) {
286
+ const { opName, value } = op;
287
+ const { localSeq } = localOpMetadata;
288
+ const rebasedValue = opName === "delete" ? value : this.rebaseLocalInterval(opName, value, localSeq);
289
+ if (rebasedValue === undefined) {
290
+ return undefined;
291
+ }
292
+ this.submitDelta({ opName, value: rebasedValue }, localOpMetadata);
293
+ }
294
+ applyStashedOp(op) {
295
+ const { opName, value } = op;
296
+ const { id, properties } = (0, index_js_2.getSerializedProperties)(value);
297
+ switch (opName) {
298
+ case "add": {
299
+ this.add({
300
+ id,
301
+ // Todo: we should improve typing so we know add ops always have start and end
302
+ start: toSequencePlace(value.start, value.startSide),
303
+ end: toSequencePlace(value.end, value.endSide),
304
+ props: properties,
305
+ });
306
+ break;
307
+ }
308
+ case "change": {
309
+ this.change(id, {
310
+ start: toOptionalSequencePlace(value.start, value.startSide),
311
+ end: toOptionalSequencePlace(value.end, value.endSide),
312
+ props: properties,
313
+ });
314
+ break;
315
+ }
316
+ case "delete": {
317
+ this.removeIntervalById(id);
318
+ break;
319
+ }
320
+ default:
321
+ throw new Error("unknown ops should not be stashed");
322
+ }
323
+ }
350
324
  rebasePositionWithSegmentSlide(pos, seqNumberFrom, localSeq) {
351
325
  if (!this.client) {
352
326
  throw new internal_3.LoggingError("mergeTree client must exist");
@@ -361,7 +335,7 @@ class IntervalCollection extends client_utils_1.TypedEventEmitter {
361
335
  }, localSeq);
362
336
  // if segment is undefined, it slid off the string
363
337
  (0, internal_1.assert)(segment !== undefined, 0x54e /* No segment found */);
364
- const segoff = (0, internal_2.getSlideToSegoff)({ segment, offset }, undefined, this.options.mergeTreeReferencesCanSlideToEndpoint) ?? segment;
338
+ const segoff = (0, internal_2.getSlideToSegoff)({ segment, offset }, undefined, (0, internal_2.createLocalReconnectingPerspective)(this.client.getCurrentSeq(), clientId, localSeq), this.options.mergeTreeReferencesCanSlideToEndpoint) ?? segment;
365
339
  // case happens when rebasing op, but concurrently entire string has been deleted
366
340
  if (segoff.segment === undefined || segoff.offset === undefined) {
367
341
  return internal_2.DetachedReferencePosition;
@@ -402,18 +376,15 @@ class IntervalCollection extends client_utils_1.TypedEventEmitter {
402
376
  this.localCollection = new LocalIntervalCollection(client, label, this.options, (interval, previousInterval) => this.emitChange(interval, previousInterval, true, true));
403
377
  if (this.savedSerializedIntervals) {
404
378
  for (const serializedInterval of this.savedSerializedIntervals) {
405
- this.localCollection.ensureSerializedId(serializedInterval);
406
- const { start: startPos, end: endPos, intervalType, properties, startSide, endSide, } = serializedInterval;
379
+ const { id, properties } = (0, index_js_2.getSerializedProperties)(serializedInterval);
380
+ const { start: startPos, end: endPos, intervalType, startSide, endSide, } = serializedInterval;
407
381
  const start = typeof startPos === "number" && startSide !== undefined
408
382
  ? { pos: startPos, side: startSide }
409
383
  : startPos;
410
384
  const end = typeof endPos === "number" && endSide !== undefined
411
385
  ? { pos: endPos, side: endSide }
412
386
  : endPos;
413
- const interval = (0, index_js_2.createSequenceInterval)(label, start, end, client, intervalType, undefined, true, this.options.mergeTreeReferencesCanSlideToEndpoint);
414
- if (properties) {
415
- interval.properties = (0, internal_2.addProperties)(interval.properties, properties);
416
- }
387
+ const interval = (0, index_js_2.createSequenceInterval)(label, id, start, end, client, intervalType, undefined, true, this.options.mergeTreeReferencesCanSlideToEndpoint, properties);
417
388
  this.localCollection.add(interval);
418
389
  }
419
390
  }
@@ -459,7 +430,7 @@ class IntervalCollection extends client_utils_1.TypedEventEmitter {
459
430
  /**
460
431
  * {@inheritdoc IIntervalCollection.add}
461
432
  */
462
- add({ start, end, props, }) {
433
+ add({ id, start, end, props, }) {
463
434
  if (!this.localCollection) {
464
435
  throw new internal_3.LoggingError("attach must be called prior to adding intervals");
465
436
  }
@@ -468,24 +439,14 @@ class IntervalCollection extends client_utils_1.TypedEventEmitter {
468
439
  endPos !== undefined &&
469
440
  startSide !== undefined &&
470
441
  endSide !== undefined, 0x793 /* start and end cannot be undefined because they were not passed in as undefined */);
471
- const stickiness = computeStickinessFromSide(startPos, startSide, endPos, endSide);
472
442
  this.assertStickinessEnabled(start, end);
473
- const interval = this.localCollection.addInterval(toSequencePlace(startPos, startSide), toSequencePlace(endPos, endSide), index_js_2.IntervalType.SlideOnRemove, props);
443
+ const interval = this.localCollection.addInterval(id ?? (0, uuid_1.v4)(), toSequencePlace(startPos, startSide), toSequencePlace(endPos, endSide), props);
474
444
  if (interval) {
475
445
  if (!this.isCollaborating) {
476
446
  setSlideOnRemove(interval.start);
477
447
  setSlideOnRemove(interval.end);
478
448
  }
479
- const serializedInterval = {
480
- start: startPos,
481
- end: endPos,
482
- intervalType: index_js_2.IntervalType.SlideOnRemove,
483
- properties: { ...interval.properties },
484
- sequenceNumber: this.client?.getCurrentSeq() ?? 0,
485
- stickiness,
486
- startSide,
487
- endSide,
488
- };
449
+ const serializedInterval = interval.serialize();
489
450
  const localSeq = this.getNextLocalSeq();
490
451
  if (this.isCollaborating) {
491
452
  this.localSeqToSerializedInterval.set(localSeq, serializedInterval);
@@ -562,29 +523,21 @@ class IntervalCollection extends client_utils_1.TypedEventEmitter {
562
523
  let deltaProps;
563
524
  let newInterval;
564
525
  if (props !== undefined) {
565
- interval.propertyManager ??= new internal_2.PropertiesManager();
566
- deltaProps = interval.propertyManager.handleProperties({ props }, interval, this.isCollaborating ? internal_2.UnassignedSequenceNumber : internal_2.UniversalSequenceNumber, internal_2.UniversalSequenceNumber, true);
526
+ deltaProps = interval.changeProperties(props);
567
527
  }
568
- if (start !== undefined && end !== undefined) {
528
+ const changeEndpoints = start !== undefined && end !== undefined;
529
+ if (changeEndpoints) {
569
530
  newInterval = this.localCollection.changeInterval(interval, start, end);
570
531
  if (!this.isCollaborating && newInterval !== undefined) {
571
532
  setSlideOnRemove(newInterval.start);
572
533
  setSlideOnRemove(newInterval.end);
573
534
  }
574
535
  }
575
- const serializedInterval = interval.serialize();
576
- const { startPos, startSide, endPos, endSide } = (0, internal_2.endpointPosAndSide)(start, end);
577
- const stickiness = computeStickinessFromSide(startPos, startSide, endPos, endSide);
578
- serializedInterval.start = startPos;
579
- serializedInterval.end = endPos;
580
- serializedInterval.startSide = startSide;
581
- serializedInterval.endSide = endSide;
582
- serializedInterval.stickiness = stickiness;
583
536
  // Emit a property bag containing the ID and the other (if any) properties changed
584
- serializedInterval.properties = {
585
- [exports.reservedIntervalIdKey]: interval.getIntervalId(),
586
- ...props,
587
- };
537
+ const serializedInterval = (newInterval ?? interval).serializeDelta({
538
+ props,
539
+ includeEndpoints: changeEndpoints,
540
+ });
588
541
  const localSeq = this.getNextLocalSeq();
589
542
  if (this.isCollaborating) {
590
543
  this.localSeqToSerializedInterval.set(localSeq, serializedInterval);
@@ -634,7 +587,7 @@ class IntervalCollection extends client_utils_1.TypedEventEmitter {
634
587
  }
635
588
  removePendingChange(serializedInterval) {
636
589
  // Change ops always have an ID.
637
- const id = serializedInterval.properties?.[exports.reservedIntervalIdKey];
590
+ const { id } = (0, index_js_2.getSerializedProperties)(serializedInterval);
638
591
  if (serializedInterval.start !== undefined) {
639
592
  this.removePendingChangeHelper(id, this.pendingChangesStart, serializedInterval);
640
593
  }
@@ -676,7 +629,7 @@ class IntervalCollection extends client_utils_1.TypedEventEmitter {
676
629
  // Note that the ID is in the property bag only to allow us to find the interval.
677
630
  // This API cannot change the ID, and writing to the ID property will result in an exception. So we
678
631
  // strip it out of the properties here.
679
- const { [exports.reservedIntervalIdKey]: id, ...newProps } = serializedInterval.properties ?? {};
632
+ const { id, properties } = (0, index_js_2.getSerializedProperties)(serializedInterval);
680
633
  (0, internal_1.assert)(id !== undefined, 0x3fe /* id must exist on the interval */);
681
634
  const interval = this.getIntervalById(id);
682
635
  if (!interval) {
@@ -684,11 +637,7 @@ class IntervalCollection extends client_utils_1.TypedEventEmitter {
684
637
  return;
685
638
  }
686
639
  if (local) {
687
- interval.propertyManager ??= new internal_2.PropertiesManager();
688
- // Let the propertyManager prune its pending change-properties set.
689
- interval.propertyManager.ack(op.sequenceNumber, op.minimumSequenceNumber, {
690
- props: newProps,
691
- });
640
+ interval.ackPropertiesChange(properties, op);
692
641
  this.ackInterval(interval, op);
693
642
  }
694
643
  else {
@@ -710,15 +659,14 @@ class IntervalCollection extends client_utils_1.TypedEventEmitter {
710
659
  newInterval =
711
660
  this.localCollection.changeInterval(interval, toOptionalSequencePlace(start, serializedInterval.startSide ?? internal_2.Side.Before), toOptionalSequencePlace(end, serializedInterval.endSide ?? internal_2.Side.Before), op) ?? interval;
712
661
  }
713
- newInterval.propertyManager ??= new internal_2.PropertiesManager();
714
- const deltaProps = newInterval.propertyManager.handleProperties({ props: newProps }, newInterval, op.sequenceNumber, op.minimumSequenceNumber, true);
662
+ const deltaProps = newInterval.changeProperties(properties, op);
715
663
  if (this.onDeserialize) {
716
664
  this.onDeserialize(newInterval);
717
665
  }
718
666
  if (newInterval !== interval) {
719
667
  this.emitChange(newInterval, interval, local, false, op);
720
668
  }
721
- const changedProperties = Object.keys(newProps).length > 0;
669
+ const changedProperties = Object.keys(properties).length > 0;
722
670
  if (changedProperties) {
723
671
  this.emit("propertyChanged", interval, deltaProps, local, op);
724
672
  this.emit("changed", interval, deltaProps, undefined, local, false);
@@ -755,9 +703,9 @@ class IntervalCollection extends client_utils_1.TypedEventEmitter {
755
703
  throw new internal_3.LoggingError("attachSequence must be called");
756
704
  }
757
705
  const { intervalType, properties, stickiness, startSide, endSide } = serializedInterval;
706
+ const { id } = (0, index_js_2.getSerializedProperties)(serializedInterval);
758
707
  const { start: startRebased, end: endRebased } = this.localSeqToRebasedInterval.get(localSeq) ?? this.computeRebasedPositions(localSeq);
759
- const intervalId = properties?.[exports.reservedIntervalIdKey];
760
- const localInterval = this.localCollection?.idIntervalIndex.getIntervalById(intervalId);
708
+ const localInterval = this.localCollection?.idIntervalIndex.getIntervalById(id);
761
709
  const rebased = {
762
710
  start: startRebased,
763
711
  end: endRebased,
@@ -770,9 +718,9 @@ class IntervalCollection extends client_utils_1.TypedEventEmitter {
770
718
  };
771
719
  if (opName === "change" &&
772
720
  // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing -- ?? is not logically equivalent when .hasPendingChangeStart returns false.
773
- (this.hasPendingChangeStart(intervalId) || this.hasPendingChangeEnd(intervalId))) {
721
+ (this.hasPendingChangeStart(id) || this.hasPendingChangeEnd(id))) {
774
722
  this.removePendingChange(serializedInterval);
775
- this.addPendingChange(intervalId, rebased);
723
+ this.addPendingChange(id, rebased);
776
724
  }
777
725
  // if the interval slid off the string, rebase the op to be a noop and delete the interval.
778
726
  if (!this.options.mergeTreeReferencesCanSlideToEndpoint &&
@@ -800,7 +748,7 @@ class IntervalCollection extends client_utils_1.TypedEventEmitter {
800
748
  if (segoff.segment?.localRefs?.has(lref) !== true) {
801
749
  return undefined;
802
750
  }
803
- const newSegoff = (0, internal_2.getSlideToSegoff)(segoff, slidingPreference, this.options.mergeTreeReferencesCanSlideToEndpoint);
751
+ const newSegoff = (0, internal_2.getSlideToSegoff)(segoff, slidingPreference, undefined, this.options.mergeTreeReferencesCanSlideToEndpoint);
804
752
  const value = segoff.segment === newSegoff.segment && segoff.offset === newSegoff.offset
805
753
  ? undefined
806
754
  : newSegoff;
@@ -813,7 +761,7 @@ class IntervalCollection extends client_utils_1.TypedEventEmitter {
813
761
  }
814
762
  const newStart = this.getSlideToSegment(interval.start, (0, index_js_2.startReferenceSlidingPreference)(interval.stickiness));
815
763
  const newEnd = this.getSlideToSegment(interval.end, (0, index_js_2.endReferenceSlidingPreference)(interval.stickiness));
816
- const id = interval.properties[exports.reservedIntervalIdKey];
764
+ const id = interval.getIntervalId();
817
765
  const hasPendingStartChange = this.hasPendingChangeStart(id);
818
766
  const hasPendingEndChange = this.hasPendingChangeEnd(id);
819
767
  if (!hasPendingStartChange) {
@@ -866,10 +814,10 @@ class IntervalCollection extends client_utils_1.TypedEventEmitter {
866
814
  }
867
815
  }
868
816
  ackAdd(serializedInterval, local, op, localOpMetadata) {
817
+ const { id, properties } = (0, index_js_2.getSerializedProperties)(serializedInterval);
869
818
  if (local) {
870
819
  (0, internal_1.assert)(localOpMetadata !== undefined, 0x553 /* op metadata should be defined for local op */);
871
820
  this.localSeqToSerializedInterval.delete(localOpMetadata.localSeq);
872
- const id = serializedInterval.properties?.[exports.reservedIntervalIdKey];
873
821
  const localInterval = this.getIntervalById(id);
874
822
  if (localInterval) {
875
823
  this.ackInterval(localInterval, op);
@@ -879,8 +827,7 @@ class IntervalCollection extends client_utils_1.TypedEventEmitter {
879
827
  if (!this.localCollection) {
880
828
  throw new internal_3.LoggingError("attachSequence must be called");
881
829
  }
882
- this.localCollection.ensureSerializedId(serializedInterval);
883
- const interval = this.localCollection.addInterval(toSequencePlace(serializedInterval.start, serializedInterval.startSide ?? internal_2.Side.Before), toSequencePlace(serializedInterval.end, serializedInterval.endSide ?? internal_2.Side.Before), serializedInterval.intervalType, serializedInterval.properties, op);
830
+ const interval = this.localCollection.addInterval(id, toSequencePlace(serializedInterval.start, serializedInterval.startSide ?? internal_2.Side.Before), toSequencePlace(serializedInterval.end, serializedInterval.endSide ?? internal_2.Side.Before), properties, op);
884
831
  if (interval) {
885
832
  if (this.onDeserialize) {
886
833
  this.onDeserialize(interval);
@@ -899,7 +846,7 @@ class IntervalCollection extends client_utils_1.TypedEventEmitter {
899
846
  if (!this.localCollection) {
900
847
  throw new internal_3.LoggingError("attach must be called prior to deleting intervals");
901
848
  }
902
- const id = this.localCollection.ensureSerializedId(serializedInterval);
849
+ const { id } = (0, index_js_2.getSerializedProperties)(serializedInterval);
903
850
  const interval = this.localCollection.idIntervalIndex.getIntervalById(id);
904
851
  if (interval) {
905
852
  this.deleteExistingInterval(interval, local, op);