@fluidframework/sequence 2.0.0-dev.2.3.0.115467 → 2.0.0-dev.4.1.0.148229

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 (96) hide show
  1. package/.eslintrc.js +9 -12
  2. package/.mocharc.js +2 -2
  3. package/.vscode/launch.json +15 -14
  4. package/README.md +204 -177
  5. package/api-extractor.json +2 -2
  6. package/dist/defaultMap.d.ts.map +1 -1
  7. package/dist/defaultMap.js +5 -4
  8. package/dist/defaultMap.js.map +1 -1
  9. package/dist/defaultMapInterfaces.d.ts.map +1 -1
  10. package/dist/defaultMapInterfaces.js.map +1 -1
  11. package/dist/index.d.ts +2 -2
  12. package/dist/index.d.ts.map +1 -1
  13. package/dist/index.js.map +1 -1
  14. package/dist/intervalCollection.d.ts +15 -3
  15. package/dist/intervalCollection.d.ts.map +1 -1
  16. package/dist/intervalCollection.js +150 -79
  17. package/dist/intervalCollection.js.map +1 -1
  18. package/dist/intervalTree.d.ts.map +1 -1
  19. package/dist/intervalTree.js.map +1 -1
  20. package/dist/localValues.d.ts.map +1 -1
  21. package/dist/localValues.js.map +1 -1
  22. package/dist/packageVersion.d.ts +1 -1
  23. package/dist/packageVersion.js +1 -1
  24. package/dist/packageVersion.js.map +1 -1
  25. package/dist/sequence.d.ts +1 -1
  26. package/dist/sequence.d.ts.map +1 -1
  27. package/dist/sequence.js +16 -42
  28. package/dist/sequence.js.map +1 -1
  29. package/dist/sequenceDeltaEvent.d.ts.map +1 -1
  30. package/dist/sequenceDeltaEvent.js.map +1 -1
  31. package/dist/sequenceFactory.d.ts.map +1 -1
  32. package/dist/sequenceFactory.js.map +1 -1
  33. package/dist/sharedIntervalCollection.d.ts.map +1 -1
  34. package/dist/sharedIntervalCollection.js.map +1 -1
  35. package/dist/sharedSequence.d.ts.map +1 -1
  36. package/dist/sharedSequence.js +3 -3
  37. package/dist/sharedSequence.js.map +1 -1
  38. package/dist/sharedString.d.ts.map +1 -1
  39. package/dist/sharedString.js +5 -4
  40. package/dist/sharedString.js.map +1 -1
  41. package/lib/defaultMap.d.ts.map +1 -1
  42. package/lib/defaultMap.js +6 -5
  43. package/lib/defaultMap.js.map +1 -1
  44. package/lib/defaultMapInterfaces.d.ts.map +1 -1
  45. package/lib/defaultMapInterfaces.js.map +1 -1
  46. package/lib/index.d.ts +2 -2
  47. package/lib/index.d.ts.map +1 -1
  48. package/lib/index.js +2 -2
  49. package/lib/index.js.map +1 -1
  50. package/lib/intervalCollection.d.ts +15 -3
  51. package/lib/intervalCollection.d.ts.map +1 -1
  52. package/lib/intervalCollection.js +150 -79
  53. package/lib/intervalCollection.js.map +1 -1
  54. package/lib/intervalTree.d.ts.map +1 -1
  55. package/lib/intervalTree.js.map +1 -1
  56. package/lib/localValues.d.ts.map +1 -1
  57. package/lib/localValues.js +1 -1
  58. package/lib/localValues.js.map +1 -1
  59. package/lib/packageVersion.d.ts +1 -1
  60. package/lib/packageVersion.js +1 -1
  61. package/lib/packageVersion.js.map +1 -1
  62. package/lib/sequence.d.ts +1 -1
  63. package/lib/sequence.d.ts.map +1 -1
  64. package/lib/sequence.js +17 -43
  65. package/lib/sequence.js.map +1 -1
  66. package/lib/sequenceDeltaEvent.d.ts.map +1 -1
  67. package/lib/sequenceDeltaEvent.js.map +1 -1
  68. package/lib/sequenceFactory.d.ts.map +1 -1
  69. package/lib/sequenceFactory.js +1 -1
  70. package/lib/sequenceFactory.js.map +1 -1
  71. package/lib/sharedIntervalCollection.d.ts.map +1 -1
  72. package/lib/sharedIntervalCollection.js.map +1 -1
  73. package/lib/sharedSequence.d.ts.map +1 -1
  74. package/lib/sharedSequence.js +4 -4
  75. package/lib/sharedSequence.js.map +1 -1
  76. package/lib/sharedString.d.ts.map +1 -1
  77. package/lib/sharedString.js +5 -4
  78. package/lib/sharedString.js.map +1 -1
  79. package/package.json +79 -67
  80. package/prettier.config.cjs +1 -1
  81. package/src/defaultMap.ts +406 -405
  82. package/src/defaultMapInterfaces.ts +120 -115
  83. package/src/index.ts +27 -17
  84. package/src/intervalCollection.ts +2206 -1932
  85. package/src/intervalTree.ts +139 -139
  86. package/src/localValues.ts +64 -73
  87. package/src/packageVersion.ts +1 -1
  88. package/src/sequence.ts +738 -710
  89. package/src/sequenceDeltaEvent.ts +143 -137
  90. package/src/sequenceFactory.ts +48 -46
  91. package/src/sharedIntervalCollection.ts +150 -136
  92. package/src/sharedSequence.ts +165 -160
  93. package/src/sharedString.ts +385 -343
  94. package/tsconfig.esnext.json +6 -6
  95. package/tsconfig.json +8 -12
  96. package/.editorconfig +0 -7
@@ -177,12 +177,12 @@ class Interval {
177
177
  * {@inheritDoc IInterval.overlaps}
178
178
  */
179
179
  overlaps(b) {
180
- const result = (this.start <= b.end) &&
181
- (this.end >= b.start);
180
+ const result = this.start <= b.end && this.end >= b.start;
182
181
  return result;
183
182
  }
184
183
  /**
185
184
  * {@inheritDoc IInterval.union}
185
+ * @deprecated - This API was never intended to be public and will be marked internal in a future release.
186
186
  */
187
187
  union(b) {
188
188
  return new Interval(Math.min(this.start, b.start), Math.max(this.end, b.end), this.properties);
@@ -192,6 +192,7 @@ class Interval {
192
192
  }
193
193
  /**
194
194
  * {@inheritDoc ISerializableInterval.addProperties}
195
+ * @deprecated - This API was never intended to be public and will be marked internal in a future release.
195
196
  */
196
197
  addProperties(newProps, collaborating = false, seq, op) {
197
198
  if (newProps) {
@@ -201,6 +202,7 @@ class Interval {
201
202
  }
202
203
  /**
203
204
  * {@inheritDoc IInterval.modify}
205
+ * @deprecated - This API was never intended to be public and will be marked internal in a future release.
204
206
  */
205
207
  modify(label, start, end, op) {
206
208
  const startPos = start !== null && start !== void 0 ? start : this.start;
@@ -265,8 +267,8 @@ class SequenceInterval {
265
267
  beforePositionChange,
266
268
  afterPositionChange,
267
269
  };
268
- const startCbs = (_a = (_c = this.start).callbacks) !== null && _a !== void 0 ? _a : (_c.callbacks = {});
269
- const endCbs = (_b = (_d = this.end).callbacks) !== null && _b !== void 0 ? _b : (_d.callbacks = {});
270
+ const startCbs = ((_a = (_c = this.start).callbacks) !== null && _a !== void 0 ? _a : (_c.callbacks = {}));
271
+ const endCbs = ((_b = (_d = this.end).callbacks) !== null && _b !== void 0 ? _b : (_d.callbacks = {}));
270
272
  startCbs.beforeSlide = endCbs.beforeSlide = beforePositionChange;
271
273
  startCbs.afterSlide = endCbs.afterSlide = afterPositionChange;
272
274
  }
@@ -348,8 +350,8 @@ class SequenceInterval {
348
350
  * {@inheritDoc IInterval.overlaps}
349
351
  */
350
352
  overlaps(b) {
351
- const result = ((0, merge_tree_1.compareReferencePositions)(this.start, b.end) <= 0) &&
352
- ((0, merge_tree_1.compareReferencePositions)(this.end, b.start) >= 0);
353
+ const result = (0, merge_tree_1.compareReferencePositions)(this.start, b.end) <= 0 &&
354
+ (0, merge_tree_1.compareReferencePositions)(this.end, b.start) >= 0;
353
355
  return result;
354
356
  }
355
357
  /**
@@ -365,12 +367,14 @@ class SequenceInterval {
365
367
  }
366
368
  /**
367
369
  * {@inheritDoc IInterval.union}
370
+ * @deprecated - This API was never intended to be public and will be marked internal in a future release.
368
371
  */
369
372
  union(b) {
370
373
  return new SequenceInterval(this.client, (0, merge_tree_1.minReferencePosition)(this.start, b.start), (0, merge_tree_1.maxReferencePosition)(this.end, b.end), this.intervalType);
371
374
  }
372
375
  /**
373
376
  * {@inheritDoc ISerializableInterval.addProperties}
377
+ * @deprecated - This API was never intended to be public and will be marked internal in a future release.
374
378
  */
375
379
  addProperties(newProps, collab = false, seq, op) {
376
380
  this.initializeProperties();
@@ -383,10 +387,11 @@ class SequenceInterval {
383
387
  overlapsPos(bstart, bend) {
384
388
  const startPos = this.client.localReferencePositionToPosition(this.start);
385
389
  const endPos = this.client.localReferencePositionToPosition(this.end);
386
- return (endPos > bstart) && (startPos < bend);
390
+ return endPos > bstart && startPos < bend;
387
391
  }
388
392
  /**
389
393
  * {@inheritDoc IInterval.modify}
394
+ * @deprecated - This API was never intended to be public and will be marked internal in a future release.
390
395
  */
391
396
  modify(label, start, end, op, localSeq) {
392
397
  const getRefType = (baseType) => {
@@ -428,13 +433,17 @@ class SequenceInterval {
428
433
  }
429
434
  }
430
435
  exports.SequenceInterval = SequenceInterval;
431
- function createPositionReferenceFromSegoff(client, segoff, refType, op) {
436
+ function createPositionReferenceFromSegoff(client, segoff, refType, op, localSeq) {
432
437
  if (segoff.segment) {
433
438
  const ref = client.createLocalReferencePosition(segoff.segment, segoff.offset, refType, undefined);
434
439
  return ref;
435
440
  }
436
- if (!op && !(0, merge_tree_1.refTypeIncludesFlag)(refType, merge_tree_1.ReferenceType.Transient)) {
437
- // reference to segment that dne locally
441
+ // Creating references on detached segments is allowed for:
442
+ // - Transient segments
443
+ // - References coming from a remote client (location may have been concurrently removed)
444
+ // - References being rebased to a new sequence number
445
+ // (segment they originally referred to may have been removed with no suitable replacement)
446
+ if (!op && !localSeq && !(0, merge_tree_1.refTypeIncludesFlag)(refType, merge_tree_1.ReferenceType.Transient)) {
438
447
  throw new container_utils_1.UsageError("Non-transient references need segment");
439
448
  }
440
449
  return (0, merge_tree_1.createDetachedLocalReferencePosition)(refType);
@@ -443,14 +452,17 @@ function createPositionReference(client, pos, refType, op, fromSnapshot, localSe
443
452
  let segoff;
444
453
  if (op) {
445
454
  (0, common_utils_1.assert)((refType & merge_tree_1.ReferenceType.SlideOnRemove) !== 0, 0x2f5 /* op create references must be SlideOnRemove */);
446
- segoff = client.getContainingSegment(pos, op);
455
+ segoff = client.getContainingSegment(pos, {
456
+ referenceSequenceNumber: op.referenceSequenceNumber,
457
+ clientId: op.clientId,
458
+ });
447
459
  segoff = client.getSlideToSegment(segoff);
448
460
  }
449
461
  else {
450
462
  (0, common_utils_1.assert)((refType & merge_tree_1.ReferenceType.SlideOnRemove) === 0 || !!fromSnapshot, 0x2f6 /* SlideOnRemove references must be op created */);
451
463
  segoff = client.getContainingSegment(pos, undefined, localSeq);
452
464
  }
453
- return createPositionReferenceFromSegoff(client, segoff, refType, op);
465
+ return createPositionReferenceFromSegoff(client, segoff, refType, op, localSeq);
454
466
  }
455
467
  function createSequenceInterval(label, start, end, client, intervalType, op, fromSnapshot) {
456
468
  let beginRefType = merge_tree_1.ReferenceType.RangeBegin;
@@ -522,14 +534,13 @@ class LocalIntervalCollection {
522
534
  }
523
535
  addConflictResolver(conflictResolver) {
524
536
  this.conflictResolver = conflictResolver;
525
- this.endConflictResolver =
526
- (key, currentKey) => {
527
- const ival = conflictResolver(key, currentKey);
528
- return {
529
- data: ival,
530
- key: ival,
531
- };
537
+ this.endConflictResolver = (key, currentKey) => {
538
+ const ival = conflictResolver(key, currentKey);
539
+ return {
540
+ data: ival,
541
+ key: ival,
532
542
  };
543
+ };
533
544
  }
534
545
  map(fn) {
535
546
  this.intervalTree.map(fn);
@@ -609,11 +620,11 @@ class LocalIntervalCollection {
609
620
  else {
610
621
  // Start and (possibly) end provided. Walk the subtrees that may contain
611
622
  // this start position.
612
- const compareFn = end === undefined ?
613
- (node) => {
623
+ const compareFn = end === undefined
624
+ ? (node) => {
614
625
  return transientInterval.compareStart(node.key);
615
- } :
616
- (node) => {
626
+ }
627
+ : (node) => {
617
628
  return transientInterval.compare(node.key);
618
629
  };
619
630
  const continueLeftFn = (cmpResult) => cmpResult <= 0;
@@ -748,18 +759,25 @@ class LocalIntervalCollection {
748
759
  };
749
760
  if (interval instanceof SequenceInterval) {
750
761
  let previousInterval;
762
+ let pendingChanges = 0;
751
763
  interval.addPositionChangeListeners(() => {
752
- (0, common_utils_1.assert)(!previousInterval, 0x3f9 /* Invalid interleaving of before/after slide */);
753
- previousInterval = interval.clone();
754
- previousInterval.start = cloneRef(previousInterval.start);
755
- previousInterval.end = cloneRef(previousInterval.end);
756
- this.removeIntervalFromIndex(interval);
764
+ pendingChanges++;
765
+ // Note: both start and end can change and invoke beforeSlide on each endpoint before afterSlide.
766
+ if (!previousInterval) {
767
+ previousInterval = interval.clone();
768
+ previousInterval.start = cloneRef(previousInterval.start);
769
+ previousInterval.end = cloneRef(previousInterval.end);
770
+ this.removeIntervalFromIndex(interval);
771
+ }
757
772
  }, () => {
758
773
  var _a;
759
774
  (0, common_utils_1.assert)(previousInterval !== undefined, 0x3fa /* Invalid interleaving of before/after slide */);
760
- this.addIntervalToIndex(interval);
761
- (_a = this.onPositionChange) === null || _a === void 0 ? void 0 : _a.call(this, interval, previousInterval);
762
- previousInterval = undefined;
775
+ pendingChanges--;
776
+ if (pendingChanges === 0) {
777
+ this.addIntervalToIndex(interval);
778
+ (_a = this.onPositionChange) === null || _a === void 0 ? void 0 : _a.call(this, interval, previousInterval);
779
+ previousInterval = undefined;
780
+ }
763
781
  });
764
782
  }
765
783
  }
@@ -844,17 +862,18 @@ function makeOpsMap() {
844
862
  const rebasedOp = Object.assign(Object.assign({}, op), { value: rebasedValue });
845
863
  return { rebasedOp, rebasedLocalOpMetadata: localOpMetadata };
846
864
  };
847
- return new Map([[
865
+ return new Map([
866
+ [
848
867
  "add",
849
868
  {
850
- process: (collection, params, local, op) => {
869
+ process: (collection, params, local, op, localOpMetadata) => {
851
870
  // if params is undefined, the interval was deleted during
852
871
  // rebasing
853
872
  if (!params) {
854
873
  return;
855
874
  }
856
875
  (0, common_utils_1.assert)(op !== undefined, 0x3fb /* op should exist here */);
857
- collection.ackAdd(params, local, op);
876
+ collection.ackAdd(params, local, op, localOpMetadata);
858
877
  },
859
878
  rebase,
860
879
  },
@@ -875,18 +894,19 @@ function makeOpsMap() {
875
894
  [
876
895
  "change",
877
896
  {
878
- process: (collection, params, local, op) => {
897
+ process: (collection, params, local, op, localOpMetadata) => {
879
898
  // if params is undefined, the interval was deleted during
880
899
  // rebasing
881
900
  if (!params) {
882
901
  return;
883
902
  }
884
903
  (0, common_utils_1.assert)(op !== undefined, 0x3fd /* op should exist here */);
885
- collection.ackChange(params, local, op);
904
+ collection.ackChange(params, local, op, localOpMetadata);
886
905
  },
887
906
  rebase,
888
907
  },
889
- ]]);
908
+ ],
909
+ ]);
890
910
  }
891
911
  exports.makeOpsMap = makeOpsMap;
892
912
  class IntervalCollectionIterator {
@@ -923,6 +943,8 @@ class IntervalCollection extends common_utils_1.TypedEventEmitter {
923
943
  this.helpers = helpers;
924
944
  this.requiresClient = requiresClient;
925
945
  this.emitter = emitter;
946
+ this.localSeqToSerializedInterval = new Map();
947
+ this.localSeqToRebasedInterval = new Map();
926
948
  this.pendingChangesStart = new Map();
927
949
  this.pendingChangesEnd = new Map();
928
950
  this.savedSerializedIntervals = Array.isArray(serializedIntervals)
@@ -932,16 +954,57 @@ class IntervalCollection extends common_utils_1.TypedEventEmitter {
932
954
  get attached() {
933
955
  return !!this.localCollection;
934
956
  }
957
+ rebasePositionWithSegmentSlide(pos, seqNumberFrom, localSeq) {
958
+ var _a;
959
+ if (!this.client) {
960
+ throw new telemetry_utils_1.LoggingError("mergeTree client must exist");
961
+ }
962
+ const { clientId } = this.client.getCollabWindow();
963
+ const { segment, offset } = this.client.getContainingSegment(pos, {
964
+ referenceSequenceNumber: seqNumberFrom,
965
+ clientId: this.client.getLongClientId(clientId),
966
+ }, localSeq);
967
+ // if segment is undefined, it slid off the string
968
+ (0, common_utils_1.assert)(segment !== undefined, 0x54e /* No segment found */);
969
+ const segoff = (_a = this.client.getSlideToSegment({ segment, offset })) !== null && _a !== void 0 ? _a : segment;
970
+ // case happens when rebasing op, but concurrently entire string has been deleted
971
+ if (segoff.segment === undefined || segoff.offset === undefined) {
972
+ return merge_tree_1.DetachedReferencePosition;
973
+ }
974
+ (0, common_utils_1.assert)(offset !== undefined && 0 <= offset && offset < segment.cachedLength, 0x54f /* Invalid offset */);
975
+ return this.client.findReconnectionPosition(segoff.segment, localSeq) + segoff.offset;
976
+ }
977
+ computeRebasedPositions(localSeq) {
978
+ (0, common_utils_1.assert)(this.client !== undefined, 0x550 /* Client should be defined when computing rebased position */);
979
+ const original = this.localSeqToSerializedInterval.get(localSeq);
980
+ (0, common_utils_1.assert)(original !== undefined, 0x551 /* Failed to store pending serialized interval info for this localSeq. */);
981
+ const rebased = Object.assign({}, original);
982
+ const { start, end, sequenceNumber } = original;
983
+ if (start !== undefined) {
984
+ rebased.start = this.rebasePositionWithSegmentSlide(start, sequenceNumber, localSeq);
985
+ }
986
+ if (end !== undefined) {
987
+ rebased.end = this.rebasePositionWithSegmentSlide(end, sequenceNumber, localSeq);
988
+ }
989
+ return rebased;
990
+ }
935
991
  /** @internal */
936
992
  attachGraph(client, label) {
937
993
  if (this.attached) {
938
994
  throw new telemetry_utils_1.LoggingError("Only supports one Sequence attach");
939
995
  }
940
- if ((client === undefined) && (this.requiresClient)) {
996
+ if (client === undefined && this.requiresClient) {
941
997
  throw new telemetry_utils_1.LoggingError("Client required for this collection");
942
998
  }
943
999
  // Instantiate the local interval collection based on the saved intervals
944
1000
  this.client = client;
1001
+ if (client) {
1002
+ client.on("normalize", () => {
1003
+ for (const localSeq of this.localSeqToSerializedInterval.keys()) {
1004
+ this.localSeqToRebasedInterval.set(localSeq, this.computeRebasedPositions(localSeq));
1005
+ }
1006
+ });
1007
+ }
945
1008
  this.localCollection = new LocalIntervalCollection(client, label, this.helpers, (interval, previousInterval) => this.emitChange(interval, previousInterval, true));
946
1009
  if (this.savedSerializedIntervals) {
947
1010
  for (const serializedInterval of this.savedSerializedIntervals) {
@@ -1019,8 +1082,10 @@ class IntervalCollection extends common_utils_1.TypedEventEmitter {
1019
1082
  sequenceNumber: (_b = (_a = this.client) === null || _a === void 0 ? void 0 : _a.getCurrentSeq()) !== null && _b !== void 0 ? _b : 0,
1020
1083
  start,
1021
1084
  };
1085
+ const localSeq = this.getNextLocalSeq();
1086
+ this.localSeqToSerializedInterval.set(localSeq, serializedInterval);
1022
1087
  // Local ops get submitted to the server. Remote ops have the deserializer run.
1023
- this.emitter.emit("add", undefined, serializedInterval, { localSeq: this.getNextLocalSeq() });
1088
+ this.emitter.emit("add", undefined, serializedInterval, { localSeq });
1024
1089
  }
1025
1090
  this.emit("addInterval", interval, true, undefined);
1026
1091
  return interval;
@@ -1034,7 +1099,9 @@ class IntervalCollection extends common_utils_1.TypedEventEmitter {
1034
1099
  if (interval) {
1035
1100
  // Local ops get submitted to the server. Remote ops have the deserializer run.
1036
1101
  if (local) {
1037
- this.emitter.emit("delete", undefined, interval.serialize(), { localSeq: this.getNextLocalSeq() });
1102
+ this.emitter.emit("delete", undefined, interval.serialize(), {
1103
+ localSeq: this.getNextLocalSeq(),
1104
+ });
1038
1105
  }
1039
1106
  else {
1040
1107
  if (this.onDeserialize) {
@@ -1069,7 +1136,7 @@ class IntervalCollection extends common_utils_1.TypedEventEmitter {
1069
1136
  if (!this.attached) {
1070
1137
  throw new telemetry_utils_1.LoggingError("Attach must be called before accessing intervals");
1071
1138
  }
1072
- if (typeof (id) !== "string") {
1139
+ if (typeof id !== "string") {
1073
1140
  throw new telemetry_utils_1.LoggingError("Change API requires an ID that is a string");
1074
1141
  }
1075
1142
  if (!props) {
@@ -1086,7 +1153,9 @@ class IntervalCollection extends common_utils_1.TypedEventEmitter {
1086
1153
  serializedInterval.end = undefined;
1087
1154
  serializedInterval.properties = props;
1088
1155
  serializedInterval.properties[reservedIntervalIdKey] = interval.getIntervalId();
1089
- this.emitter.emit("change", undefined, serializedInterval, { localSeq: this.getNextLocalSeq() });
1156
+ const localSeq = this.getNextLocalSeq();
1157
+ this.localSeqToSerializedInterval.set(localSeq, serializedInterval);
1158
+ this.emitter.emit("change", undefined, serializedInterval, { localSeq });
1090
1159
  this.emit("propertyChanged", interval, deltaProps, true, undefined);
1091
1160
  }
1092
1161
  }
@@ -1102,7 +1171,7 @@ class IntervalCollection extends common_utils_1.TypedEventEmitter {
1102
1171
  throw new telemetry_utils_1.LoggingError("Attach must be called before accessing intervals");
1103
1172
  }
1104
1173
  // Force id to be a string.
1105
- if (typeof (id) !== "string") {
1174
+ if (typeof id !== "string") {
1106
1175
  throw new telemetry_utils_1.LoggingError("Change API requires an ID that is a string");
1107
1176
  }
1108
1177
  const interval = this.getIntervalById(id);
@@ -1115,11 +1184,12 @@ class IntervalCollection extends common_utils_1.TypedEventEmitter {
1115
1184
  serializedInterval.start = start;
1116
1185
  serializedInterval.end = end;
1117
1186
  // Emit a property bag containing only the ID, as we don't intend for this op to change any properties.
1118
- serializedInterval.properties =
1119
- {
1120
- [reservedIntervalIdKey]: interval.getIntervalId(),
1121
- };
1122
- this.emitter.emit("change", undefined, serializedInterval, { localSeq: this.getNextLocalSeq() });
1187
+ serializedInterval.properties = {
1188
+ [reservedIntervalIdKey]: interval.getIntervalId(),
1189
+ };
1190
+ const localSeq = this.getNextLocalSeq();
1191
+ this.localSeqToSerializedInterval.set(localSeq, serializedInterval);
1192
+ this.emitter.emit("change", undefined, serializedInterval, { localSeq });
1123
1193
  this.addPendingChange(id, serializedInterval);
1124
1194
  this.emitChange(newInterval, interval, true);
1125
1195
  return newInterval;
@@ -1176,12 +1246,14 @@ class IntervalCollection extends common_utils_1.TypedEventEmitter {
1176
1246
  return entries && entries.length !== 0;
1177
1247
  }
1178
1248
  /** @internal */
1179
- ackChange(serializedInterval, local, op) {
1249
+ ackChange(serializedInterval, local, op, localOpMetadata) {
1180
1250
  var _a, _b, _c, _d;
1181
1251
  if (!this.localCollection) {
1182
1252
  throw new telemetry_utils_1.LoggingError("Attach must be called before accessing intervals");
1183
1253
  }
1184
1254
  if (local) {
1255
+ (0, common_utils_1.assert)(localOpMetadata !== undefined, 0x552 /* op metadata should be defined for local op */);
1256
+ this.localSeqToSerializedInterval.delete(localOpMetadata === null || localOpMetadata === void 0 ? void 0 : localOpMetadata.localSeq);
1185
1257
  // This is an ack from the server. Remove the pending change.
1186
1258
  this.removePendingChange(serializedInterval);
1187
1259
  }
@@ -1219,7 +1291,8 @@ class IntervalCollection extends common_utils_1.TypedEventEmitter {
1219
1291
  if (start !== undefined || end !== undefined) {
1220
1292
  // If changeInterval gives us a new interval, work with that one. Otherwise keep working with
1221
1293
  // the one we originally found in the tree.
1222
- newInterval = (_d = this.localCollection.changeInterval(interval, start, end, op)) !== null && _d !== void 0 ? _d : interval;
1294
+ newInterval =
1295
+ (_d = this.localCollection.changeInterval(interval, start, end, op)) !== null && _d !== void 0 ? _d : interval;
1223
1296
  }
1224
1297
  const deltaProps = newInterval.addProperties(newProps, true, op.sequenceNumber);
1225
1298
  if (this.onDeserialize) {
@@ -1261,7 +1334,7 @@ class IntervalCollection extends common_utils_1.TypedEventEmitter {
1261
1334
  * @internal
1262
1335
  */
1263
1336
  rebaseLocalInterval(opName, serializedInterval, localSeq) {
1264
- var _a, _b, _c, _d, _e, _f, _g;
1337
+ var _a, _b, _c, _d, _e, _f;
1265
1338
  if (!this.client) {
1266
1339
  // If there's no associated mergeTree client, the originally submitted op is still correct.
1267
1340
  return serializedInterval;
@@ -1269,44 +1342,36 @@ class IntervalCollection extends common_utils_1.TypedEventEmitter {
1269
1342
  if (!this.attached) {
1270
1343
  throw new telemetry_utils_1.LoggingError("attachSequence must be called");
1271
1344
  }
1272
- const { start, end, intervalType, properties, sequenceNumber } = serializedInterval;
1273
- const startRebased = start === undefined ? undefined :
1274
- this.client.rebasePosition(start, sequenceNumber, localSeq);
1275
- const endRebased = end === undefined ? undefined :
1276
- this.client.rebasePosition(end, sequenceNumber, localSeq);
1345
+ const { intervalType, properties } = serializedInterval;
1346
+ const { start: startRebased, end: endRebased } = (_a = this.localSeqToRebasedInterval.get(localSeq)) !== null && _a !== void 0 ? _a : this.computeRebasedPositions(localSeq);
1277
1347
  const intervalId = properties === null || properties === void 0 ? void 0 : properties[reservedIntervalIdKey];
1278
- const localInterval = (_a = this.localCollection) === null || _a === void 0 ? void 0 : _a.getIntervalById(intervalId);
1348
+ const localInterval = (_b = this.localCollection) === null || _b === void 0 ? void 0 : _b.getIntervalById(intervalId);
1279
1349
  const rebased = {
1280
1350
  start: startRebased,
1281
1351
  end: endRebased,
1282
1352
  intervalType,
1283
- sequenceNumber: (_c = (_b = this.client) === null || _b === void 0 ? void 0 : _b.getCurrentSeq()) !== null && _c !== void 0 ? _c : 0,
1353
+ sequenceNumber: (_d = (_c = this.client) === null || _c === void 0 ? void 0 : _c.getCurrentSeq()) !== null && _d !== void 0 ? _d : 0,
1284
1354
  properties,
1285
1355
  };
1286
- if (opName === "change" && (this.hasPendingChangeStart(intervalId) || this.hasPendingChangeEnd(intervalId))) {
1356
+ if (opName === "change" &&
1357
+ (this.hasPendingChangeStart(intervalId) || this.hasPendingChangeEnd(intervalId))) {
1287
1358
  this.removePendingChange(serializedInterval);
1288
1359
  this.addPendingChange(intervalId, rebased);
1289
1360
  }
1290
- // if the interval slid off the string, rebase the op to be a noop and
1291
- // delete the interval
1292
- if (startRebased === merge_tree_1.DetachedReferencePosition || endRebased === merge_tree_1.DetachedReferencePosition) {
1361
+ // if the interval slid off the string, rebase the op to be a noop and delete the interval.
1362
+ if (startRebased === merge_tree_1.DetachedReferencePosition ||
1363
+ endRebased === merge_tree_1.DetachedReferencePosition) {
1293
1364
  if (localInterval) {
1294
- (_d = this.localCollection) === null || _d === void 0 ? void 0 : _d.removeExistingInterval(localInterval);
1365
+ (_e = this.localCollection) === null || _e === void 0 ? void 0 : _e.removeExistingInterval(localInterval);
1295
1366
  }
1296
1367
  return undefined;
1297
1368
  }
1298
- if (!localInterval) {
1299
- return rebased;
1300
- }
1301
- // we know we must be using `SequenceInterval` because `this.client` exists
1302
- (0, common_utils_1.assert)(localInterval instanceof SequenceInterval, 0x3a0 /* localInterval must be `SequenceInterval` when used with client */);
1303
- const startSegment = this.getSlideToSegment(localInterval.start);
1304
- const endSegment = this.getSlideToSegment(localInterval.end);
1305
- // we need to slide because the reference has been removed
1306
- if (startSegment || endSegment) {
1307
- const newStart = startSegment && this.client.getPosition(startSegment.segment, localSeq) + ((_e = startSegment.offset) !== null && _e !== void 0 ? _e : 0);
1308
- const newEnd = endSegment && this.client.getPosition(endSegment.segment, localSeq) + ((_f = endSegment.offset) !== null && _f !== void 0 ? _f : 0);
1309
- (_g = this.localCollection) === null || _g === void 0 ? void 0 : _g.changeInterval(localInterval, newStart, newEnd, undefined, localSeq);
1369
+ if (localInterval !== undefined) {
1370
+ // we know we must be using `SequenceInterval` because `this.client` exists
1371
+ (0, common_utils_1.assert)(localInterval instanceof SequenceInterval, 0x3a0 /* localInterval must be `SequenceInterval` when used with client */);
1372
+ // The rebased op may place this interval's endpoints on different segments. Calling `changeInterval` here
1373
+ // updates the local client's state to be consistent with the emitted op.
1374
+ (_f = this.localCollection) === null || _f === void 0 ? void 0 : _f.changeInterval(localInterval, startRebased, endRebased, undefined, localSeq);
1310
1375
  }
1311
1376
  return rebased;
1312
1377
  }
@@ -1320,7 +1385,9 @@ class IntervalCollection extends common_utils_1.TypedEventEmitter {
1320
1385
  return undefined;
1321
1386
  }
1322
1387
  const newSegoff = this.client.getSlideToSegment(segoff);
1323
- const value = (segoff.segment === newSegoff.segment && segoff.offset === newSegoff.offset) ? undefined : newSegoff;
1388
+ const value = segoff.segment === newSegoff.segment && segoff.offset === newSegoff.offset
1389
+ ? undefined
1390
+ : newSegoff;
1324
1391
  return value;
1325
1392
  }
1326
1393
  setSlideOnRemove(lref) {
@@ -1394,9 +1461,11 @@ class IntervalCollection extends common_utils_1.TypedEventEmitter {
1394
1461
  }
1395
1462
  }
1396
1463
  /** @internal */
1397
- ackAdd(serializedInterval, local, op) {
1464
+ ackAdd(serializedInterval, local, op, localOpMetadata) {
1398
1465
  var _a;
1399
1466
  if (local) {
1467
+ (0, common_utils_1.assert)(localOpMetadata !== undefined, 0x553 /* op metadata should be defined for local op */);
1468
+ this.localSeqToSerializedInterval.delete(localOpMetadata.localSeq);
1400
1469
  const id = (_a = serializedInterval.properties) === null || _a === void 0 ? void 0 : _a[reservedIntervalIdKey];
1401
1470
  const localInterval = this.getIntervalById(id);
1402
1471
  if (localInterval) {
@@ -1533,8 +1602,10 @@ exports.IntervalCollection = IntervalCollection;
1533
1602
  */
1534
1603
  function intervalLocatorFromEndpoint(potentialEndpoint) {
1535
1604
  var _a;
1536
- const { interval, [merge_tree_1.reservedRangeLabelsKey]: collectionNameArray, } = (_a = potentialEndpoint.properties) !== null && _a !== void 0 ? _a : {};
1537
- return (interval && (collectionNameArray === null || collectionNameArray === void 0 ? void 0 : collectionNameArray.length) === 1) ? { label: collectionNameArray[0], interval } : undefined;
1605
+ const { interval, [merge_tree_1.reservedRangeLabelsKey]: collectionNameArray } = (_a = potentialEndpoint.properties) !== null && _a !== void 0 ? _a : {};
1606
+ return interval && (collectionNameArray === null || collectionNameArray === void 0 ? void 0 : collectionNameArray.length) === 1
1607
+ ? { label: collectionNameArray[0], interval }
1608
+ : undefined;
1538
1609
  }
1539
1610
  exports.intervalLocatorFromEndpoint = intervalLocatorFromEndpoint;
1540
1611
  //# sourceMappingURL=intervalCollection.js.map