@fluidframework/merge-tree 2.21.0 → 2.23.0-323641

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 (181) hide show
  1. package/.eslintrc.cjs +0 -1
  2. package/CHANGELOG.md +4 -0
  3. package/README.md +1 -0
  4. package/dist/attributionCollection.js +5 -7
  5. package/dist/attributionCollection.js.map +1 -1
  6. package/dist/localReference.js +6 -8
  7. package/dist/localReference.js.map +1 -1
  8. package/dist/mergeTree.d.ts.map +1 -1
  9. package/dist/mergeTree.js +69 -34
  10. package/dist/mergeTree.js.map +1 -1
  11. package/dist/mergeTreeNodes.d.ts +15 -4
  12. package/dist/mergeTreeNodes.d.ts.map +1 -1
  13. package/dist/mergeTreeNodes.js +1 -1
  14. package/dist/mergeTreeNodes.js.map +1 -1
  15. package/dist/partialLengths.d.ts +114 -144
  16. package/dist/partialLengths.d.ts.map +1 -1
  17. package/dist/partialLengths.js +431 -525
  18. package/dist/partialLengths.js.map +1 -1
  19. package/dist/perspective.d.ts +10 -1
  20. package/dist/perspective.d.ts.map +1 -1
  21. package/dist/perspective.js +10 -1
  22. package/dist/perspective.js.map +1 -1
  23. package/dist/properties.d.ts.map +1 -1
  24. package/dist/properties.js +2 -3
  25. package/dist/properties.js.map +1 -1
  26. package/dist/revertibles.js +3 -3
  27. package/dist/revertibles.js.map +1 -1
  28. package/dist/segmentInfos.d.ts +3 -0
  29. package/dist/segmentInfos.d.ts.map +1 -1
  30. package/dist/segmentInfos.js.map +1 -1
  31. package/dist/segmentPropertiesManager.js +3 -3
  32. package/dist/segmentPropertiesManager.js.map +1 -1
  33. package/dist/snapshotLoader.js +2 -2
  34. package/dist/snapshotLoader.js.map +1 -1
  35. package/dist/sortedSegmentSet.d.ts +5 -3
  36. package/dist/sortedSegmentSet.d.ts.map +1 -1
  37. package/dist/sortedSegmentSet.js +33 -41
  38. package/dist/sortedSegmentSet.js.map +1 -1
  39. package/dist/sortedSet.d.ts +20 -3
  40. package/dist/sortedSet.d.ts.map +1 -1
  41. package/dist/sortedSet.js +23 -14
  42. package/dist/sortedSet.js.map +1 -1
  43. package/dist/test/Snapshot.perf.spec.js +1 -1
  44. package/dist/test/Snapshot.perf.spec.js.map +1 -1
  45. package/dist/test/client.applyMsg.spec.js +20 -0
  46. package/dist/test/client.applyMsg.spec.js.map +1 -1
  47. package/dist/test/client.applyStashedOpFarm.spec.js +1 -1
  48. package/dist/test/client.applyStashedOpFarm.spec.js.map +1 -1
  49. package/dist/test/client.attributionFarm.spec.js +1 -1
  50. package/dist/test/client.attributionFarm.spec.js.map +1 -1
  51. package/dist/test/client.localReference.spec.js +48 -0
  52. package/dist/test/client.localReference.spec.js.map +1 -1
  53. package/dist/test/client.obliterateFarm.spec.d.ts.map +1 -1
  54. package/dist/test/client.obliterateFarm.spec.js +24 -3
  55. package/dist/test/client.obliterateFarm.spec.js.map +1 -1
  56. package/dist/test/client.reconnectFarm.spec.js +1 -1
  57. package/dist/test/client.reconnectFarm.spec.js.map +1 -1
  58. package/dist/test/client.searchForMarker.spec.js +2 -2
  59. package/dist/test/client.searchForMarker.spec.js.map +1 -1
  60. package/dist/test/mergeTreeOperationRunner.d.ts +1 -1
  61. package/dist/test/mergeTreeOperationRunner.d.ts.map +1 -1
  62. package/dist/test/mergeTreeOperationRunner.js +23 -11
  63. package/dist/test/mergeTreeOperationRunner.js.map +1 -1
  64. package/dist/test/obliterate.concurrent.spec.js +45 -1
  65. package/dist/test/obliterate.concurrent.spec.js.map +1 -1
  66. package/dist/test/obliterate.rangeExpansion.spec.js +81 -5
  67. package/dist/test/obliterate.rangeExpansion.spec.js.map +1 -1
  68. package/dist/test/obliterate.spec.js +3 -3
  69. package/dist/test/obliterate.spec.js.map +1 -1
  70. package/dist/test/obliterateOperations.d.ts +1 -0
  71. package/dist/test/obliterateOperations.d.ts.map +1 -1
  72. package/dist/test/obliterateOperations.js +57 -24
  73. package/dist/test/obliterateOperations.js.map +1 -1
  74. package/dist/test/partialSyncHelper.d.ts +42 -0
  75. package/dist/test/partialSyncHelper.d.ts.map +1 -0
  76. package/dist/test/partialSyncHelper.js +96 -0
  77. package/dist/test/partialSyncHelper.js.map +1 -0
  78. package/dist/test/revertibles.spec.js +3 -3
  79. package/dist/test/revertibles.spec.js.map +1 -1
  80. package/dist/test/sortedSegmentSet.spec.js +21 -0
  81. package/dist/test/sortedSegmentSet.spec.js.map +1 -1
  82. package/dist/test/testClient.d.ts +1 -1
  83. package/dist/test/testClient.d.ts.map +1 -1
  84. package/dist/test/testClient.js +1 -0
  85. package/dist/test/testClient.js.map +1 -1
  86. package/dist/test/testUtils.js +2 -2
  87. package/dist/test/testUtils.js.map +1 -1
  88. package/lib/attributionCollection.js +5 -7
  89. package/lib/attributionCollection.js.map +1 -1
  90. package/lib/localReference.js +6 -8
  91. package/lib/localReference.js.map +1 -1
  92. package/lib/mergeTree.d.ts.map +1 -1
  93. package/lib/mergeTree.js +69 -34
  94. package/lib/mergeTree.js.map +1 -1
  95. package/lib/mergeTreeNodes.d.ts +15 -4
  96. package/lib/mergeTreeNodes.d.ts.map +1 -1
  97. package/lib/mergeTreeNodes.js +1 -1
  98. package/lib/mergeTreeNodes.js.map +1 -1
  99. package/lib/partialLengths.d.ts +114 -144
  100. package/lib/partialLengths.d.ts.map +1 -1
  101. package/lib/partialLengths.js +432 -525
  102. package/lib/partialLengths.js.map +1 -1
  103. package/lib/perspective.d.ts +10 -1
  104. package/lib/perspective.d.ts.map +1 -1
  105. package/lib/perspective.js +10 -1
  106. package/lib/perspective.js.map +1 -1
  107. package/lib/properties.d.ts.map +1 -1
  108. package/lib/properties.js +2 -3
  109. package/lib/properties.js.map +1 -1
  110. package/lib/revertibles.js +3 -3
  111. package/lib/revertibles.js.map +1 -1
  112. package/lib/segmentInfos.d.ts +3 -0
  113. package/lib/segmentInfos.d.ts.map +1 -1
  114. package/lib/segmentInfos.js.map +1 -1
  115. package/lib/segmentPropertiesManager.js +3 -3
  116. package/lib/segmentPropertiesManager.js.map +1 -1
  117. package/lib/snapshotLoader.js +2 -2
  118. package/lib/snapshotLoader.js.map +1 -1
  119. package/lib/sortedSegmentSet.d.ts +5 -3
  120. package/lib/sortedSegmentSet.d.ts.map +1 -1
  121. package/lib/sortedSegmentSet.js +33 -41
  122. package/lib/sortedSegmentSet.js.map +1 -1
  123. package/lib/sortedSet.d.ts +20 -3
  124. package/lib/sortedSet.d.ts.map +1 -1
  125. package/lib/sortedSet.js +23 -14
  126. package/lib/sortedSet.js.map +1 -1
  127. package/lib/test/Snapshot.perf.spec.js +1 -1
  128. package/lib/test/Snapshot.perf.spec.js.map +1 -1
  129. package/lib/test/client.applyMsg.spec.js +20 -0
  130. package/lib/test/client.applyMsg.spec.js.map +1 -1
  131. package/lib/test/client.applyStashedOpFarm.spec.js +1 -1
  132. package/lib/test/client.applyStashedOpFarm.spec.js.map +1 -1
  133. package/lib/test/client.attributionFarm.spec.js +1 -1
  134. package/lib/test/client.attributionFarm.spec.js.map +1 -1
  135. package/lib/test/client.localReference.spec.js +48 -0
  136. package/lib/test/client.localReference.spec.js.map +1 -1
  137. package/lib/test/client.obliterateFarm.spec.d.ts.map +1 -1
  138. package/lib/test/client.obliterateFarm.spec.js +28 -5
  139. package/lib/test/client.obliterateFarm.spec.js.map +1 -1
  140. package/lib/test/client.reconnectFarm.spec.js +1 -1
  141. package/lib/test/client.reconnectFarm.spec.js.map +1 -1
  142. package/lib/test/client.searchForMarker.spec.js +2 -2
  143. package/lib/test/client.searchForMarker.spec.js.map +1 -1
  144. package/lib/test/mergeTreeOperationRunner.d.ts +1 -1
  145. package/lib/test/mergeTreeOperationRunner.d.ts.map +1 -1
  146. package/lib/test/mergeTreeOperationRunner.js +23 -11
  147. package/lib/test/mergeTreeOperationRunner.js.map +1 -1
  148. package/lib/test/obliterate.concurrent.spec.js +45 -1
  149. package/lib/test/obliterate.concurrent.spec.js.map +1 -1
  150. package/lib/test/obliterate.rangeExpansion.spec.js +81 -5
  151. package/lib/test/obliterate.rangeExpansion.spec.js.map +1 -1
  152. package/lib/test/obliterate.spec.js +3 -3
  153. package/lib/test/obliterate.spec.js.map +1 -1
  154. package/lib/test/obliterateOperations.d.ts +1 -0
  155. package/lib/test/obliterateOperations.d.ts.map +1 -1
  156. package/lib/test/obliterateOperations.js +55 -23
  157. package/lib/test/obliterateOperations.js.map +1 -1
  158. package/lib/test/partialSyncHelper.d.ts +42 -0
  159. package/lib/test/partialSyncHelper.d.ts.map +1 -0
  160. package/lib/test/partialSyncHelper.js +92 -0
  161. package/lib/test/partialSyncHelper.js.map +1 -0
  162. package/lib/test/revertibles.spec.js +3 -3
  163. package/lib/test/revertibles.spec.js.map +1 -1
  164. package/lib/test/sortedSegmentSet.spec.js +21 -0
  165. package/lib/test/sortedSegmentSet.spec.js.map +1 -1
  166. package/lib/test/testClient.d.ts +1 -1
  167. package/lib/test/testClient.d.ts.map +1 -1
  168. package/lib/test/testClient.js +1 -0
  169. package/lib/test/testClient.js.map +1 -1
  170. package/lib/test/testUtils.js +2 -2
  171. package/lib/test/testUtils.js.map +1 -1
  172. package/package.json +20 -20
  173. package/src/mergeTree.ts +80 -28
  174. package/src/mergeTreeNodes.ts +15 -4
  175. package/src/partialLengths.ts +559 -776
  176. package/src/perspective.ts +10 -1
  177. package/src/properties.ts +2 -3
  178. package/src/segmentInfos.ts +3 -0
  179. package/src/snapshotLoader.ts +1 -1
  180. package/src/sortedSegmentSet.ts +41 -50
  181. package/src/sortedSet.ts +32 -16
package/dist/mergeTree.js CHANGED
@@ -286,6 +286,8 @@ class MergeTree {
286
286
  // for now assume only markers have ids and so point directly at the Segment
287
287
  // if we need to have pointers to non-markers, we can change to point at local refs
288
288
  this.idToMarker = new Map();
289
+ // TODO:AB#29553: This property doesn't seem to be adequately round-tripped through summarization.
290
+ // Specifically, it seems like we drop information about obliterates within the collab window for at least V1 summaries.
289
291
  this.obliterates = new Obliterates(this);
290
292
  this.splitLeafSegment = (segment, pos) => {
291
293
  if (!(pos > 0 && segment)) {
@@ -294,11 +296,11 @@ class MergeTree {
294
296
  const next = segment.splitAt(pos);
295
297
  (0, mergeTreeNodes_js_1.assertSegmentLeaf)(next);
296
298
  if (segment?.segmentGroups) {
297
- next.segmentGroups ?? (next.segmentGroups = new segmentGroupCollection_js_1.SegmentGroupCollection(next));
299
+ next.segmentGroups ??= new segmentGroupCollection_js_1.SegmentGroupCollection(next);
298
300
  segment.segmentGroups.copyTo(next.segmentGroups);
299
301
  }
300
- if (segment.prevObliterateByInserter) {
301
- next.prevObliterateByInserter = segment.prevObliterateByInserter;
302
+ if (segment.obliteratePrecedingInsertion) {
303
+ next.obliteratePrecedingInsertion = segment.obliteratePrecedingInsertion;
302
304
  }
303
305
  (0, segmentPropertiesManager_js_1.copyPropertiesAndManager)(segment, next);
304
306
  if (segment.localRefs) {
@@ -795,7 +797,7 @@ class MergeTree {
795
797
  const overlappingRemoves = [];
796
798
  pendingSegmentGroup.segments.map((pendingSegment) => {
797
799
  const overlappingRemove = !ackSegment(pendingSegment, pendingSegmentGroup, opArgs);
798
- overwrite = overlappingRemove || overwrite;
800
+ overwrite ||= overlappingRemove || (0, segmentInfos_js_1.toMoveInfo)(pendingSegment) !== undefined;
799
801
  overlappingRemoves.push(overlappingRemove);
800
802
  if (MergeTree.options.zamboniSegments) {
801
803
  this.addToLRUSet(pendingSegment, seq);
@@ -851,7 +853,7 @@ class MergeTree {
851
853
  if (previousProps) {
852
854
  _segmentGroup.previousProps.push(previousProps);
853
855
  }
854
- const segmentGroups = (segment.segmentGroups ?? (segment.segmentGroups = new segmentGroupCollection_js_1.SegmentGroupCollection(segment)));
856
+ const segmentGroups = (segment.segmentGroups ??= new segmentGroupCollection_js_1.SegmentGroupCollection(segment));
855
857
  segmentGroups.enqueue(_segmentGroup);
856
858
  return _segmentGroup;
857
859
  }
@@ -1036,15 +1038,21 @@ class MergeTree {
1036
1038
  ? Number.MAX_SAFE_INTEGER - this.collabWindow.localSeq + ob.localSeq
1037
1039
  : ob.seq;
1038
1040
  if (normalizedObSeq > refSeq) {
1039
- if (oldest === undefined || normalizedOldestSeq > normalizedObSeq) {
1040
- normalizedOldestSeq = normalizedObSeq;
1041
- oldest = ob;
1042
- movedClientIds.unshift(ob.clientId);
1043
- movedSeqs.unshift(ob.seq);
1044
- }
1045
- else {
1046
- movedClientIds.push(ob.clientId);
1047
- movedSeqs.push(ob.seq);
1041
+ // Any obliterate from the same client that's inserting this segment cannot cause the segment to be marked as
1042
+ // obliterated (since that client must have performed the obliterate before this insertion).
1043
+ // We still need to consider such obliterates when determining the winning obliterate for the insertion point,
1044
+ // see `obliteratePrecedingInsertion` docs.
1045
+ if (clientId !== ob.clientId) {
1046
+ if (oldest === undefined || normalizedOldestSeq > normalizedObSeq) {
1047
+ normalizedOldestSeq = normalizedObSeq;
1048
+ oldest = ob;
1049
+ movedClientIds.unshift(ob.clientId);
1050
+ movedSeqs.unshift(ob.seq);
1051
+ }
1052
+ else {
1053
+ movedClientIds.push(ob.clientId);
1054
+ movedSeqs.push(ob.seq);
1055
+ }
1048
1056
  }
1049
1057
  if (newest === undefined || normalizedNewestSeq < normalizedObSeq) {
1050
1058
  normalizedNewestSeq = normalizedObSeq;
@@ -1052,6 +1060,10 @@ class MergeTree {
1052
1060
  }
1053
1061
  }
1054
1062
  }
1063
+ newSegment.obliteratePrecedingInsertion = newest;
1064
+ // See doc comment on obliteratePrecedingInsertion for more details: if the newest obliterate was performed
1065
+ // by the same client that's inserting this segment, we let them insert into this range and therefore don't
1066
+ // mark it obliterated.
1055
1067
  if (oldest && newest?.clientId !== clientId) {
1056
1068
  const moveInfo = {
1057
1069
  movedClientIds,
@@ -1066,12 +1078,14 @@ class MergeTree {
1066
1078
  this.addToPendingList(newSegment, oldest.segmentGroup);
1067
1079
  }
1068
1080
  if (newSegment.parent) {
1069
- this.blockUpdatePathLengths(newSegment.parent, seq, clientId);
1081
+ // The incremental update codepath in theory can handle most cases where segments are obliterated upon insertion,
1082
+ // but it's not idempotent with respect to segment insertion in the first place. Since we already update partial
1083
+ // lengths inside the inserting walk, we'd be at risk of double-counting the insertion in any case if we allow
1084
+ // incremental updates here.
1085
+ const newStructure = true;
1086
+ this.blockUpdatePathLengths(newSegment.parent, moveInfo.movedSeq, clientId, newStructure);
1070
1087
  }
1071
1088
  }
1072
- else if (oldest && newest?.clientId === clientId) {
1073
- newSegment.prevObliterateByInserter = newest;
1074
- }
1075
1089
  saveIfLocal(newSegment);
1076
1090
  }
1077
1091
  }
@@ -1247,7 +1261,7 @@ class MergeTree {
1247
1261
  (0, internal_1.assert)(!mergeTreeNodes_js_1.Marker.is(segment) ||
1248
1262
  !(mergeTreeNodes_js_1.reservedMarkerIdKey in opObj) ||
1249
1263
  opObj.markerId === segment.properties?.markerId, 0x5ad /* Cannot change the markerId of an existing marker */);
1250
- const propertyManager = (segment.propertyManager ?? (segment.propertyManager = new segmentPropertiesManager_js_1.PropertiesManager()));
1264
+ const propertyManager = (segment.propertyManager ??= new segmentPropertiesManager_js_1.PropertiesManager());
1251
1265
  const propertyDeltas = propertyManager.handleProperties(propsOrAdjust, segment, seq, this.collabWindow.minSeq, this.collabWindow.collaborating, rollback);
1252
1266
  if (!isRemovedOrMoved(segment)) {
1253
1267
  deltaSegments.push({ segment, propertyDeltas });
@@ -1323,40 +1337,53 @@ class MergeTree {
1323
1337
  if ((start.side === sequencePlace_js_1.Side.After && startPos === pos + segment.cachedLength) || // exclusive start segment
1324
1338
  (end.side === sequencePlace_js_1.Side.Before &&
1325
1339
  endPos === pos &&
1326
- (0, perspective_js_1.isSegmentPresent)(segment, { refSeq, localSeq })) // exclusive end segment
1340
+ // TODO:AB#29765: The clientId check here should be handled by isSegmentPresent and/or PerspectiveImpl
1341
+ (segment.clientId === clientId || (0, perspective_js_1.isSegmentPresent)(segment, { refSeq, localSeq }))) // exclusive end segment
1327
1342
  ) {
1328
1343
  // We walk these segments because we want to also walk any concurrently inserted segments between here and the obliterated segments.
1329
1344
  // These segments are outside of the obliteration range though, so return true to keep walking.
1330
1345
  return true;
1331
1346
  }
1332
1347
  const existingMoveInfo = (0, segmentInfos_js_1.toMoveInfo)(segment);
1333
- if (segment.prevObliterateByInserter?.seq === constants_js_1.UnassignedSequenceNumber) {
1348
+ // The "last-to-obliterate-gets-to-insert" policy described by the doc comment on `obliteratePrecedingInsertion`
1349
+ // is mostly handled by logic at insertion time, but we need a small bit of handling here.
1350
+ // Specifically, we want to avoid marking a local-only segment as obliterated when we know one of our own local obliterates
1351
+ // will win against the obliterate we're processing, hence the early exit.
1352
+ if (segment.seq === constants_js_1.UnassignedSequenceNumber &&
1353
+ segment.obliteratePrecedingInsertion?.seq === constants_js_1.UnassignedSequenceNumber &&
1354
+ seq !== constants_js_1.UnassignedSequenceNumber) {
1334
1355
  // We chose to not obliterate this segment because we are aware of an unacked local obliteration.
1335
1356
  // The local obliterate has not been sequenced yet, so it is still the newest obliterate we are aware of.
1336
1357
  // Other clients will also choose not to obliterate this segment because the most recent obliteration has the same clientId
1337
1358
  return true;
1338
1359
  }
1339
- const wasMovedOnInsert = clientId !== segment.clientId &&
1340
- segment.seq !== undefined &&
1341
- seq !== constants_js_1.UnassignedSequenceNumber &&
1342
- (refSeq < segment.seq || segment.seq === constants_js_1.UnassignedSequenceNumber);
1360
+ // Partial lengths incrementality is not supported for overlapping obliterate/removes.
1361
+ _overwrite ||= existingMoveInfo !== undefined || (0, segmentInfos_js_1.toRemovalInfo)(segment) !== undefined;
1343
1362
  if (existingMoveInfo === undefined) {
1344
1363
  const movedSeg = (0, segmentInfos_js_1.overwriteInfo)(segment, {
1345
1364
  movedClientIds: [clientId],
1346
1365
  movedSeq: seq,
1347
1366
  localMovedSeq: localSeq,
1348
1367
  movedSeqs: [seq],
1349
- wasMovedOnInsert,
1368
+ wasMovedOnInsert: segment.seq === constants_js_1.UnassignedSequenceNumber && seq !== constants_js_1.UnassignedSequenceNumber,
1350
1369
  });
1351
- if (!(0, segmentInfos_js_1.toRemovalInfo)(movedSeg)) {
1370
+ const existingRemoval = (0, segmentInfos_js_1.toRemovalInfo)(movedSeg);
1371
+ if (existingRemoval === undefined) {
1352
1372
  movedSegments.push(movedSeg);
1353
1373
  }
1374
+ else if (existingRemoval.removedSeq === constants_js_1.UnassignedSequenceNumber &&
1375
+ segment.localRefs?.empty === false) {
1376
+ // We removed this locally already so we don't need to event it again, but it might have references
1377
+ // that need sliding now that a move may have been acked.
1378
+ localOverlapWithRefs.push(segment);
1379
+ }
1354
1380
  }
1355
1381
  else {
1356
- _overwrite = true;
1357
- // never move wasMovedOnInsert from true to false
1358
- existingMoveInfo.wasMovedOnInsert || (existingMoveInfo.wasMovedOnInsert = wasMovedOnInsert);
1359
1382
  if (existingMoveInfo.movedSeq === constants_js_1.UnassignedSequenceNumber) {
1383
+ // Should not need explicit set here, but this should be implied:
1384
+ (0, internal_1.assert)(!existingMoveInfo.wasMovedOnInsert, 0xab4 /* Local obliterate cannot have removed a segment as soon as it was inserted */);
1385
+ (0, internal_1.assert)(seq !== constants_js_1.UnassignedSequenceNumber, 0xab5 /* Cannot obliterate the same segment locally twice */);
1386
+ existingMoveInfo.wasMovedOnInsert = segment.seq === constants_js_1.UnassignedSequenceNumber;
1360
1387
  // we moved this locally, but someone else moved it first
1361
1388
  // so put them at the head of the list
1362
1389
  // The list isn't ordered, but we keep the first move at the head
@@ -1441,18 +1468,26 @@ class MergeTree {
1441
1468
  const localSeq = seq === constants_js_1.UnassignedSequenceNumber ? ++this.collabWindow.localSeq : undefined;
1442
1469
  const markRemoved = (segment, pos, _start, _end) => {
1443
1470
  const existingRemovalInfo = (0, segmentInfos_js_1.toRemovalInfo)(segment);
1471
+ // Partial lengths incrementality is not supported for overlapping obliterate/removes.
1472
+ _overwrite ||= existingRemovalInfo !== undefined || (0, segmentInfos_js_1.toMoveInfo)(segment) !== undefined;
1444
1473
  if (existingRemovalInfo === undefined) {
1445
1474
  const removed = (0, segmentInfos_js_1.overwriteInfo)(segment, {
1446
1475
  removedClientIds: [clientId],
1447
1476
  removedSeq: seq,
1448
1477
  localRemovedSeq: localSeq,
1449
1478
  });
1450
- if (!(0, segmentInfos_js_1.toMoveInfo)(removed)) {
1479
+ const existingMoveInfo = (0, segmentInfos_js_1.toMoveInfo)(removed);
1480
+ if (existingMoveInfo === undefined) {
1451
1481
  removedSegments.push(removed);
1452
1482
  }
1483
+ else if (existingMoveInfo.movedSeq === constants_js_1.UnassignedSequenceNumber &&
1484
+ segment.localRefs?.empty === false) {
1485
+ // We moved this locally already so we don't need to event it again, but it might have references
1486
+ // that need sliding now that a remove may have been acked.
1487
+ localOverlapWithRefs.push(segment);
1488
+ }
1453
1489
  }
1454
1490
  else {
1455
- _overwrite = true;
1456
1491
  if (existingRemovalInfo.removedSeq === constants_js_1.UnassignedSequenceNumber) {
1457
1492
  // we removed this locally, but someone else removed it first
1458
1493
  // so put them at the head of the list
@@ -1771,7 +1806,7 @@ class MergeTree {
1771
1806
  const node = block.children[i];
1772
1807
  const nodeLength = nodeTotalLength(this, node);
1773
1808
  if (nodeLength !== undefined) {
1774
- len ?? (len = 0);
1809
+ len ??= 0;
1775
1810
  len += nodeLength;
1776
1811
  }
1777
1812
  if (node.isLeaf()) {
@@ -1791,7 +1826,7 @@ class MergeTree {
1791
1826
  // The later, and right most children overwrite
1792
1827
  // whereas early, and left most do not overwrite
1793
1828
  rightmostTiles[tileLabel] = segment;
1794
- leftmostTiles[tileLabel] ?? (leftmostTiles[tileLabel] = segment);
1829
+ leftmostTiles[tileLabel] ??= segment;
1795
1830
  }
1796
1831
  }
1797
1832
  }