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

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 (95) 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 +186 -176
  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 +7 -3
  15. package/dist/intervalCollection.d.ts.map +1 -1
  16. package/dist/intervalCollection.js +144 -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 +24 -43
  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 +7 -3
  51. package/lib/intervalCollection.d.ts.map +1 -1
  52. package/lib/intervalCollection.js +144 -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 +26 -45
  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 +25 -23
  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 +2198 -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 +752 -711
  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
@@ -177,8 +177,7 @@ 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
  /**
@@ -265,8 +264,8 @@ class SequenceInterval {
265
264
  beforePositionChange,
266
265
  afterPositionChange,
267
266
  };
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 = {});
267
+ const startCbs = ((_a = (_c = this.start).callbacks) !== null && _a !== void 0 ? _a : (_c.callbacks = {}));
268
+ const endCbs = ((_b = (_d = this.end).callbacks) !== null && _b !== void 0 ? _b : (_d.callbacks = {}));
270
269
  startCbs.beforeSlide = endCbs.beforeSlide = beforePositionChange;
271
270
  startCbs.afterSlide = endCbs.afterSlide = afterPositionChange;
272
271
  }
@@ -348,8 +347,8 @@ class SequenceInterval {
348
347
  * {@inheritDoc IInterval.overlaps}
349
348
  */
350
349
  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);
350
+ const result = (0, merge_tree_1.compareReferencePositions)(this.start, b.end) <= 0 &&
351
+ (0, merge_tree_1.compareReferencePositions)(this.end, b.start) >= 0;
353
352
  return result;
354
353
  }
355
354
  /**
@@ -383,7 +382,7 @@ class SequenceInterval {
383
382
  overlapsPos(bstart, bend) {
384
383
  const startPos = this.client.localReferencePositionToPosition(this.start);
385
384
  const endPos = this.client.localReferencePositionToPosition(this.end);
386
- return (endPos > bstart) && (startPos < bend);
385
+ return endPos > bstart && startPos < bend;
387
386
  }
388
387
  /**
389
388
  * {@inheritDoc IInterval.modify}
@@ -428,13 +427,17 @@ class SequenceInterval {
428
427
  }
429
428
  }
430
429
  exports.SequenceInterval = SequenceInterval;
431
- function createPositionReferenceFromSegoff(client, segoff, refType, op) {
430
+ function createPositionReferenceFromSegoff(client, segoff, refType, op, localSeq) {
432
431
  if (segoff.segment) {
433
432
  const ref = client.createLocalReferencePosition(segoff.segment, segoff.offset, refType, undefined);
434
433
  return ref;
435
434
  }
436
- if (!op && !(0, merge_tree_1.refTypeIncludesFlag)(refType, merge_tree_1.ReferenceType.Transient)) {
437
- // reference to segment that dne locally
435
+ // Creating references on detached segments is allowed for:
436
+ // - Transient segments
437
+ // - References coming from a remote client (location may have been concurrently removed)
438
+ // - References being rebased to a new sequence number
439
+ // (segment they originally referred to may have been removed with no suitable replacement)
440
+ if (!op && !localSeq && !(0, merge_tree_1.refTypeIncludesFlag)(refType, merge_tree_1.ReferenceType.Transient)) {
438
441
  throw new container_utils_1.UsageError("Non-transient references need segment");
439
442
  }
440
443
  return (0, merge_tree_1.createDetachedLocalReferencePosition)(refType);
@@ -443,14 +446,17 @@ function createPositionReference(client, pos, refType, op, fromSnapshot, localSe
443
446
  let segoff;
444
447
  if (op) {
445
448
  (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);
449
+ segoff = client.getContainingSegment(pos, {
450
+ referenceSequenceNumber: op.referenceSequenceNumber,
451
+ clientId: op.clientId,
452
+ });
447
453
  segoff = client.getSlideToSegment(segoff);
448
454
  }
449
455
  else {
450
456
  (0, common_utils_1.assert)((refType & merge_tree_1.ReferenceType.SlideOnRemove) === 0 || !!fromSnapshot, 0x2f6 /* SlideOnRemove references must be op created */);
451
457
  segoff = client.getContainingSegment(pos, undefined, localSeq);
452
458
  }
453
- return createPositionReferenceFromSegoff(client, segoff, refType, op);
459
+ return createPositionReferenceFromSegoff(client, segoff, refType, op, localSeq);
454
460
  }
455
461
  function createSequenceInterval(label, start, end, client, intervalType, op, fromSnapshot) {
456
462
  let beginRefType = merge_tree_1.ReferenceType.RangeBegin;
@@ -522,14 +528,13 @@ class LocalIntervalCollection {
522
528
  }
523
529
  addConflictResolver(conflictResolver) {
524
530
  this.conflictResolver = conflictResolver;
525
- this.endConflictResolver =
526
- (key, currentKey) => {
527
- const ival = conflictResolver(key, currentKey);
528
- return {
529
- data: ival,
530
- key: ival,
531
- };
531
+ this.endConflictResolver = (key, currentKey) => {
532
+ const ival = conflictResolver(key, currentKey);
533
+ return {
534
+ data: ival,
535
+ key: ival,
532
536
  };
537
+ };
533
538
  }
534
539
  map(fn) {
535
540
  this.intervalTree.map(fn);
@@ -609,11 +614,11 @@ class LocalIntervalCollection {
609
614
  else {
610
615
  // Start and (possibly) end provided. Walk the subtrees that may contain
611
616
  // this start position.
612
- const compareFn = end === undefined ?
613
- (node) => {
617
+ const compareFn = end === undefined
618
+ ? (node) => {
614
619
  return transientInterval.compareStart(node.key);
615
- } :
616
- (node) => {
620
+ }
621
+ : (node) => {
617
622
  return transientInterval.compare(node.key);
618
623
  };
619
624
  const continueLeftFn = (cmpResult) => cmpResult <= 0;
@@ -748,18 +753,25 @@ class LocalIntervalCollection {
748
753
  };
749
754
  if (interval instanceof SequenceInterval) {
750
755
  let previousInterval;
756
+ let pendingChanges = 0;
751
757
  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);
758
+ pendingChanges++;
759
+ // Note: both start and end can change and invoke beforeSlide on each endpoint before afterSlide.
760
+ if (!previousInterval) {
761
+ previousInterval = interval.clone();
762
+ previousInterval.start = cloneRef(previousInterval.start);
763
+ previousInterval.end = cloneRef(previousInterval.end);
764
+ this.removeIntervalFromIndex(interval);
765
+ }
757
766
  }, () => {
758
767
  var _a;
759
768
  (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;
769
+ pendingChanges--;
770
+ if (pendingChanges === 0) {
771
+ this.addIntervalToIndex(interval);
772
+ (_a = this.onPositionChange) === null || _a === void 0 ? void 0 : _a.call(this, interval, previousInterval);
773
+ previousInterval = undefined;
774
+ }
763
775
  });
764
776
  }
765
777
  }
@@ -844,17 +856,18 @@ function makeOpsMap() {
844
856
  const rebasedOp = Object.assign(Object.assign({}, op), { value: rebasedValue });
845
857
  return { rebasedOp, rebasedLocalOpMetadata: localOpMetadata };
846
858
  };
847
- return new Map([[
859
+ return new Map([
860
+ [
848
861
  "add",
849
862
  {
850
- process: (collection, params, local, op) => {
863
+ process: (collection, params, local, op, localOpMetadata) => {
851
864
  // if params is undefined, the interval was deleted during
852
865
  // rebasing
853
866
  if (!params) {
854
867
  return;
855
868
  }
856
869
  (0, common_utils_1.assert)(op !== undefined, 0x3fb /* op should exist here */);
857
- collection.ackAdd(params, local, op);
870
+ collection.ackAdd(params, local, op, localOpMetadata);
858
871
  },
859
872
  rebase,
860
873
  },
@@ -875,18 +888,19 @@ function makeOpsMap() {
875
888
  [
876
889
  "change",
877
890
  {
878
- process: (collection, params, local, op) => {
891
+ process: (collection, params, local, op, localOpMetadata) => {
879
892
  // if params is undefined, the interval was deleted during
880
893
  // rebasing
881
894
  if (!params) {
882
895
  return;
883
896
  }
884
897
  (0, common_utils_1.assert)(op !== undefined, 0x3fd /* op should exist here */);
885
- collection.ackChange(params, local, op);
898
+ collection.ackChange(params, local, op, localOpMetadata);
886
899
  },
887
900
  rebase,
888
901
  },
889
- ]]);
902
+ ],
903
+ ]);
890
904
  }
891
905
  exports.makeOpsMap = makeOpsMap;
892
906
  class IntervalCollectionIterator {
@@ -923,6 +937,8 @@ class IntervalCollection extends common_utils_1.TypedEventEmitter {
923
937
  this.helpers = helpers;
924
938
  this.requiresClient = requiresClient;
925
939
  this.emitter = emitter;
940
+ this.localSeqToSerializedInterval = new Map();
941
+ this.localSeqToRebasedInterval = new Map();
926
942
  this.pendingChangesStart = new Map();
927
943
  this.pendingChangesEnd = new Map();
928
944
  this.savedSerializedIntervals = Array.isArray(serializedIntervals)
@@ -932,16 +948,57 @@ class IntervalCollection extends common_utils_1.TypedEventEmitter {
932
948
  get attached() {
933
949
  return !!this.localCollection;
934
950
  }
951
+ rebasePositionWithSegmentSlide(pos, seqNumberFrom, localSeq) {
952
+ var _a;
953
+ if (!this.client) {
954
+ throw new telemetry_utils_1.LoggingError("mergeTree client must exist");
955
+ }
956
+ const { clientId } = this.client.getCollabWindow();
957
+ const { segment, offset } = this.client.getContainingSegment(pos, {
958
+ referenceSequenceNumber: seqNumberFrom,
959
+ clientId: this.client.getLongClientId(clientId),
960
+ }, localSeq);
961
+ // if segment is undefined, it slid off the string
962
+ (0, common_utils_1.assert)(segment !== undefined, 0x54e /* No segment found */);
963
+ const segoff = (_a = this.client.getSlideToSegment({ segment, offset })) !== null && _a !== void 0 ? _a : segment;
964
+ // case happens when rebasing op, but concurrently entire string has been deleted
965
+ if (segoff.segment === undefined || segoff.offset === undefined) {
966
+ return merge_tree_1.DetachedReferencePosition;
967
+ }
968
+ (0, common_utils_1.assert)(offset !== undefined && 0 <= offset && offset < segment.cachedLength, 0x54f /* Invalid offset */);
969
+ return this.client.findReconnectionPosition(segoff.segment, localSeq) + segoff.offset;
970
+ }
971
+ computeRebasedPositions(localSeq) {
972
+ (0, common_utils_1.assert)(this.client !== undefined, 0x550 /* Client should be defined when computing rebased position */);
973
+ const original = this.localSeqToSerializedInterval.get(localSeq);
974
+ (0, common_utils_1.assert)(original !== undefined, 0x551 /* Failed to store pending serialized interval info for this localSeq. */);
975
+ const rebased = Object.assign({}, original);
976
+ const { start, end, sequenceNumber } = original;
977
+ if (start !== undefined) {
978
+ rebased.start = this.rebasePositionWithSegmentSlide(start, sequenceNumber, localSeq);
979
+ }
980
+ if (end !== undefined) {
981
+ rebased.end = this.rebasePositionWithSegmentSlide(end, sequenceNumber, localSeq);
982
+ }
983
+ return rebased;
984
+ }
935
985
  /** @internal */
936
986
  attachGraph(client, label) {
937
987
  if (this.attached) {
938
988
  throw new telemetry_utils_1.LoggingError("Only supports one Sequence attach");
939
989
  }
940
- if ((client === undefined) && (this.requiresClient)) {
990
+ if (client === undefined && this.requiresClient) {
941
991
  throw new telemetry_utils_1.LoggingError("Client required for this collection");
942
992
  }
943
993
  // Instantiate the local interval collection based on the saved intervals
944
994
  this.client = client;
995
+ if (client) {
996
+ client.on("normalize", () => {
997
+ for (const localSeq of this.localSeqToSerializedInterval.keys()) {
998
+ this.localSeqToRebasedInterval.set(localSeq, this.computeRebasedPositions(localSeq));
999
+ }
1000
+ });
1001
+ }
945
1002
  this.localCollection = new LocalIntervalCollection(client, label, this.helpers, (interval, previousInterval) => this.emitChange(interval, previousInterval, true));
946
1003
  if (this.savedSerializedIntervals) {
947
1004
  for (const serializedInterval of this.savedSerializedIntervals) {
@@ -1019,8 +1076,10 @@ class IntervalCollection extends common_utils_1.TypedEventEmitter {
1019
1076
  sequenceNumber: (_b = (_a = this.client) === null || _a === void 0 ? void 0 : _a.getCurrentSeq()) !== null && _b !== void 0 ? _b : 0,
1020
1077
  start,
1021
1078
  };
1079
+ const localSeq = this.getNextLocalSeq();
1080
+ this.localSeqToSerializedInterval.set(localSeq, serializedInterval);
1022
1081
  // Local ops get submitted to the server. Remote ops have the deserializer run.
1023
- this.emitter.emit("add", undefined, serializedInterval, { localSeq: this.getNextLocalSeq() });
1082
+ this.emitter.emit("add", undefined, serializedInterval, { localSeq });
1024
1083
  }
1025
1084
  this.emit("addInterval", interval, true, undefined);
1026
1085
  return interval;
@@ -1034,7 +1093,9 @@ class IntervalCollection extends common_utils_1.TypedEventEmitter {
1034
1093
  if (interval) {
1035
1094
  // Local ops get submitted to the server. Remote ops have the deserializer run.
1036
1095
  if (local) {
1037
- this.emitter.emit("delete", undefined, interval.serialize(), { localSeq: this.getNextLocalSeq() });
1096
+ this.emitter.emit("delete", undefined, interval.serialize(), {
1097
+ localSeq: this.getNextLocalSeq(),
1098
+ });
1038
1099
  }
1039
1100
  else {
1040
1101
  if (this.onDeserialize) {
@@ -1069,7 +1130,7 @@ class IntervalCollection extends common_utils_1.TypedEventEmitter {
1069
1130
  if (!this.attached) {
1070
1131
  throw new telemetry_utils_1.LoggingError("Attach must be called before accessing intervals");
1071
1132
  }
1072
- if (typeof (id) !== "string") {
1133
+ if (typeof id !== "string") {
1073
1134
  throw new telemetry_utils_1.LoggingError("Change API requires an ID that is a string");
1074
1135
  }
1075
1136
  if (!props) {
@@ -1086,7 +1147,9 @@ class IntervalCollection extends common_utils_1.TypedEventEmitter {
1086
1147
  serializedInterval.end = undefined;
1087
1148
  serializedInterval.properties = props;
1088
1149
  serializedInterval.properties[reservedIntervalIdKey] = interval.getIntervalId();
1089
- this.emitter.emit("change", undefined, serializedInterval, { localSeq: this.getNextLocalSeq() });
1150
+ const localSeq = this.getNextLocalSeq();
1151
+ this.localSeqToSerializedInterval.set(localSeq, serializedInterval);
1152
+ this.emitter.emit("change", undefined, serializedInterval, { localSeq });
1090
1153
  this.emit("propertyChanged", interval, deltaProps, true, undefined);
1091
1154
  }
1092
1155
  }
@@ -1102,7 +1165,7 @@ class IntervalCollection extends common_utils_1.TypedEventEmitter {
1102
1165
  throw new telemetry_utils_1.LoggingError("Attach must be called before accessing intervals");
1103
1166
  }
1104
1167
  // Force id to be a string.
1105
- if (typeof (id) !== "string") {
1168
+ if (typeof id !== "string") {
1106
1169
  throw new telemetry_utils_1.LoggingError("Change API requires an ID that is a string");
1107
1170
  }
1108
1171
  const interval = this.getIntervalById(id);
@@ -1115,11 +1178,12 @@ class IntervalCollection extends common_utils_1.TypedEventEmitter {
1115
1178
  serializedInterval.start = start;
1116
1179
  serializedInterval.end = end;
1117
1180
  // 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() });
1181
+ serializedInterval.properties = {
1182
+ [reservedIntervalIdKey]: interval.getIntervalId(),
1183
+ };
1184
+ const localSeq = this.getNextLocalSeq();
1185
+ this.localSeqToSerializedInterval.set(localSeq, serializedInterval);
1186
+ this.emitter.emit("change", undefined, serializedInterval, { localSeq });
1123
1187
  this.addPendingChange(id, serializedInterval);
1124
1188
  this.emitChange(newInterval, interval, true);
1125
1189
  return newInterval;
@@ -1176,12 +1240,14 @@ class IntervalCollection extends common_utils_1.TypedEventEmitter {
1176
1240
  return entries && entries.length !== 0;
1177
1241
  }
1178
1242
  /** @internal */
1179
- ackChange(serializedInterval, local, op) {
1243
+ ackChange(serializedInterval, local, op, localOpMetadata) {
1180
1244
  var _a, _b, _c, _d;
1181
1245
  if (!this.localCollection) {
1182
1246
  throw new telemetry_utils_1.LoggingError("Attach must be called before accessing intervals");
1183
1247
  }
1184
1248
  if (local) {
1249
+ (0, common_utils_1.assert)(localOpMetadata !== undefined, 0x552 /* op metadata should be defined for local op */);
1250
+ this.localSeqToSerializedInterval.delete(localOpMetadata === null || localOpMetadata === void 0 ? void 0 : localOpMetadata.localSeq);
1185
1251
  // This is an ack from the server. Remove the pending change.
1186
1252
  this.removePendingChange(serializedInterval);
1187
1253
  }
@@ -1219,7 +1285,8 @@ class IntervalCollection extends common_utils_1.TypedEventEmitter {
1219
1285
  if (start !== undefined || end !== undefined) {
1220
1286
  // If changeInterval gives us a new interval, work with that one. Otherwise keep working with
1221
1287
  // the one we originally found in the tree.
1222
- newInterval = (_d = this.localCollection.changeInterval(interval, start, end, op)) !== null && _d !== void 0 ? _d : interval;
1288
+ newInterval =
1289
+ (_d = this.localCollection.changeInterval(interval, start, end, op)) !== null && _d !== void 0 ? _d : interval;
1223
1290
  }
1224
1291
  const deltaProps = newInterval.addProperties(newProps, true, op.sequenceNumber);
1225
1292
  if (this.onDeserialize) {
@@ -1261,7 +1328,7 @@ class IntervalCollection extends common_utils_1.TypedEventEmitter {
1261
1328
  * @internal
1262
1329
  */
1263
1330
  rebaseLocalInterval(opName, serializedInterval, localSeq) {
1264
- var _a, _b, _c, _d, _e, _f, _g;
1331
+ var _a, _b, _c, _d, _e, _f;
1265
1332
  if (!this.client) {
1266
1333
  // If there's no associated mergeTree client, the originally submitted op is still correct.
1267
1334
  return serializedInterval;
@@ -1269,44 +1336,36 @@ class IntervalCollection extends common_utils_1.TypedEventEmitter {
1269
1336
  if (!this.attached) {
1270
1337
  throw new telemetry_utils_1.LoggingError("attachSequence must be called");
1271
1338
  }
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);
1339
+ const { intervalType, properties } = serializedInterval;
1340
+ const { start: startRebased, end: endRebased } = (_a = this.localSeqToRebasedInterval.get(localSeq)) !== null && _a !== void 0 ? _a : this.computeRebasedPositions(localSeq);
1277
1341
  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);
1342
+ const localInterval = (_b = this.localCollection) === null || _b === void 0 ? void 0 : _b.getIntervalById(intervalId);
1279
1343
  const rebased = {
1280
1344
  start: startRebased,
1281
1345
  end: endRebased,
1282
1346
  intervalType,
1283
- sequenceNumber: (_c = (_b = this.client) === null || _b === void 0 ? void 0 : _b.getCurrentSeq()) !== null && _c !== void 0 ? _c : 0,
1347
+ sequenceNumber: (_d = (_c = this.client) === null || _c === void 0 ? void 0 : _c.getCurrentSeq()) !== null && _d !== void 0 ? _d : 0,
1284
1348
  properties,
1285
1349
  };
1286
- if (opName === "change" && (this.hasPendingChangeStart(intervalId) || this.hasPendingChangeEnd(intervalId))) {
1350
+ if (opName === "change" &&
1351
+ (this.hasPendingChangeStart(intervalId) || this.hasPendingChangeEnd(intervalId))) {
1287
1352
  this.removePendingChange(serializedInterval);
1288
1353
  this.addPendingChange(intervalId, rebased);
1289
1354
  }
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) {
1355
+ // if the interval slid off the string, rebase the op to be a noop and delete the interval.
1356
+ if (startRebased === merge_tree_1.DetachedReferencePosition ||
1357
+ endRebased === merge_tree_1.DetachedReferencePosition) {
1293
1358
  if (localInterval) {
1294
- (_d = this.localCollection) === null || _d === void 0 ? void 0 : _d.removeExistingInterval(localInterval);
1359
+ (_e = this.localCollection) === null || _e === void 0 ? void 0 : _e.removeExistingInterval(localInterval);
1295
1360
  }
1296
1361
  return undefined;
1297
1362
  }
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);
1363
+ if (localInterval !== undefined) {
1364
+ // we know we must be using `SequenceInterval` because `this.client` exists
1365
+ (0, common_utils_1.assert)(localInterval instanceof SequenceInterval, 0x3a0 /* localInterval must be `SequenceInterval` when used with client */);
1366
+ // The rebased op may place this interval's endpoints on different segments. Calling `changeInterval` here
1367
+ // updates the local client's state to be consistent with the emitted op.
1368
+ (_f = this.localCollection) === null || _f === void 0 ? void 0 : _f.changeInterval(localInterval, startRebased, endRebased, undefined, localSeq);
1310
1369
  }
1311
1370
  return rebased;
1312
1371
  }
@@ -1320,7 +1379,9 @@ class IntervalCollection extends common_utils_1.TypedEventEmitter {
1320
1379
  return undefined;
1321
1380
  }
1322
1381
  const newSegoff = this.client.getSlideToSegment(segoff);
1323
- const value = (segoff.segment === newSegoff.segment && segoff.offset === newSegoff.offset) ? undefined : newSegoff;
1382
+ const value = segoff.segment === newSegoff.segment && segoff.offset === newSegoff.offset
1383
+ ? undefined
1384
+ : newSegoff;
1324
1385
  return value;
1325
1386
  }
1326
1387
  setSlideOnRemove(lref) {
@@ -1394,9 +1455,11 @@ class IntervalCollection extends common_utils_1.TypedEventEmitter {
1394
1455
  }
1395
1456
  }
1396
1457
  /** @internal */
1397
- ackAdd(serializedInterval, local, op) {
1458
+ ackAdd(serializedInterval, local, op, localOpMetadata) {
1398
1459
  var _a;
1399
1460
  if (local) {
1461
+ (0, common_utils_1.assert)(localOpMetadata !== undefined, 0x553 /* op metadata should be defined for local op */);
1462
+ this.localSeqToSerializedInterval.delete(localOpMetadata.localSeq);
1400
1463
  const id = (_a = serializedInterval.properties) === null || _a === void 0 ? void 0 : _a[reservedIntervalIdKey];
1401
1464
  const localInterval = this.getIntervalById(id);
1402
1465
  if (localInterval) {
@@ -1533,8 +1596,10 @@ exports.IntervalCollection = IntervalCollection;
1533
1596
  */
1534
1597
  function intervalLocatorFromEndpoint(potentialEndpoint) {
1535
1598
  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;
1599
+ const { interval, [merge_tree_1.reservedRangeLabelsKey]: collectionNameArray } = (_a = potentialEndpoint.properties) !== null && _a !== void 0 ? _a : {};
1600
+ return interval && (collectionNameArray === null || collectionNameArray === void 0 ? void 0 : collectionNameArray.length) === 1
1601
+ ? { label: collectionNameArray[0], interval }
1602
+ : undefined;
1538
1603
  }
1539
1604
  exports.intervalLocatorFromEndpoint = intervalLocatorFromEndpoint;
1540
1605
  //# sourceMappingURL=intervalCollection.js.map