@fluidframework/merge-tree 2.4.0-294316 → 2.4.0-297027

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 (98) hide show
  1. package/api-report/merge-tree.legacy.alpha.api.md +26 -5
  2. package/dist/attributionPolicy.d.ts.map +1 -1
  3. package/dist/attributionPolicy.js +10 -3
  4. package/dist/attributionPolicy.js.map +1 -1
  5. package/dist/client.d.ts +14 -4
  6. package/dist/client.d.ts.map +1 -1
  7. package/dist/client.js +97 -9
  8. package/dist/client.js.map +1 -1
  9. package/dist/index.d.ts +1 -1
  10. package/dist/index.d.ts.map +1 -1
  11. package/dist/index.js.map +1 -1
  12. package/dist/legacy.d.ts +1 -0
  13. package/dist/mergeTree.d.ts +15 -1
  14. package/dist/mergeTree.d.ts.map +1 -1
  15. package/dist/mergeTree.js +65 -19
  16. package/dist/mergeTree.js.map +1 -1
  17. package/dist/mergeTreeNodes.d.ts +6 -0
  18. package/dist/mergeTreeNodes.d.ts.map +1 -1
  19. package/dist/mergeTreeNodes.js +2 -1
  20. package/dist/mergeTreeNodes.js.map +1 -1
  21. package/dist/opBuilder.d.ts +15 -1
  22. package/dist/opBuilder.d.ts.map +1 -1
  23. package/dist/opBuilder.js +28 -1
  24. package/dist/opBuilder.js.map +1 -1
  25. package/dist/ops.d.ts +27 -1
  26. package/dist/ops.d.ts.map +1 -1
  27. package/dist/ops.js +1 -0
  28. package/dist/ops.js.map +1 -1
  29. package/dist/sequencePlace.d.ts +4 -0
  30. package/dist/sequencePlace.d.ts.map +1 -1
  31. package/dist/sequencePlace.js +17 -1
  32. package/dist/sequencePlace.js.map +1 -1
  33. package/dist/test/obliterate.concurrent.spec.js +18 -0
  34. package/dist/test/obliterate.concurrent.spec.js.map +1 -1
  35. package/dist/test/obliterate.rangeExpansion.spec.js +109 -53
  36. package/dist/test/obliterate.rangeExpansion.spec.js.map +1 -1
  37. package/dist/test/obliterate.spec.js +2 -2
  38. package/dist/test/obliterate.spec.js.map +1 -1
  39. package/dist/test/reconnectHelper.d.ts +8 -6
  40. package/dist/test/reconnectHelper.d.ts.map +1 -1
  41. package/dist/test/reconnectHelper.js +14 -13
  42. package/dist/test/reconnectHelper.js.map +1 -1
  43. package/dist/test/testClientLogger.d.ts.map +1 -1
  44. package/dist/test/testClientLogger.js +19 -8
  45. package/dist/test/testClientLogger.js.map +1 -1
  46. package/lib/attributionPolicy.d.ts.map +1 -1
  47. package/lib/attributionPolicy.js +10 -3
  48. package/lib/attributionPolicy.js.map +1 -1
  49. package/lib/client.d.ts +14 -4
  50. package/lib/client.d.ts.map +1 -1
  51. package/lib/client.js +98 -10
  52. package/lib/client.js.map +1 -1
  53. package/lib/index.d.ts +1 -1
  54. package/lib/index.d.ts.map +1 -1
  55. package/lib/index.js.map +1 -1
  56. package/lib/legacy.d.ts +1 -0
  57. package/lib/mergeTree.d.ts +15 -1
  58. package/lib/mergeTree.d.ts.map +1 -1
  59. package/lib/mergeTree.js +65 -19
  60. package/lib/mergeTree.js.map +1 -1
  61. package/lib/mergeTreeNodes.d.ts +6 -0
  62. package/lib/mergeTreeNodes.d.ts.map +1 -1
  63. package/lib/mergeTreeNodes.js +2 -1
  64. package/lib/mergeTreeNodes.js.map +1 -1
  65. package/lib/opBuilder.d.ts +15 -1
  66. package/lib/opBuilder.d.ts.map +1 -1
  67. package/lib/opBuilder.js +26 -0
  68. package/lib/opBuilder.js.map +1 -1
  69. package/lib/ops.d.ts +27 -1
  70. package/lib/ops.d.ts.map +1 -1
  71. package/lib/ops.js +1 -0
  72. package/lib/ops.js.map +1 -1
  73. package/lib/sequencePlace.d.ts +4 -0
  74. package/lib/sequencePlace.d.ts.map +1 -1
  75. package/lib/sequencePlace.js +15 -0
  76. package/lib/sequencePlace.js.map +1 -1
  77. package/lib/test/obliterate.concurrent.spec.js +18 -0
  78. package/lib/test/obliterate.concurrent.spec.js.map +1 -1
  79. package/lib/test/obliterate.rangeExpansion.spec.js +109 -53
  80. package/lib/test/obliterate.rangeExpansion.spec.js.map +1 -1
  81. package/lib/test/obliterate.spec.js +2 -2
  82. package/lib/test/obliterate.spec.js.map +1 -1
  83. package/lib/test/reconnectHelper.d.ts +8 -6
  84. package/lib/test/reconnectHelper.d.ts.map +1 -1
  85. package/lib/test/reconnectHelper.js +15 -14
  86. package/lib/test/reconnectHelper.js.map +1 -1
  87. package/lib/test/testClientLogger.d.ts.map +1 -1
  88. package/lib/test/testClientLogger.js +19 -8
  89. package/lib/test/testClientLogger.js.map +1 -1
  90. package/package.json +29 -16
  91. package/src/attributionPolicy.ts +5 -0
  92. package/src/client.ts +136 -21
  93. package/src/index.ts +1 -0
  94. package/src/mergeTree.ts +108 -22
  95. package/src/mergeTreeNodes.ts +9 -1
  96. package/src/opBuilder.ts +32 -0
  97. package/src/ops.ts +23 -1
  98. package/src/sequencePlace.ts +16 -0
package/dist/mergeTree.js CHANGED
@@ -24,6 +24,7 @@ const properties_js_1 = require("./properties.js");
24
24
  const referencePositions_js_1 = require("./referencePositions.js");
25
25
  // eslint-disable-next-line import/no-deprecated
26
26
  const segmentPropertiesManager_js_1 = require("./segmentPropertiesManager.js");
27
+ const sequencePlace_js_1 = require("./sequencePlace.js");
27
28
  const sortedSegmentSet_js_1 = require("./sortedSegmentSet.js");
28
29
  const zamboni_js_1 = require("./zamboni.js");
29
30
  function markSegmentMoved(seg, moveInfo) {
@@ -764,13 +765,15 @@ class MergeTree {
764
765
  segment: pendingSegment,
765
766
  });
766
767
  });
767
- if (opArgs.op.type === ops_js_1.MergeTreeDeltaType.OBLITERATE) {
768
+ if (opArgs.op.type === ops_js_1.MergeTreeDeltaType.OBLITERATE ||
769
+ opArgs.op.type === ops_js_1.MergeTreeDeltaType.OBLITERATE_SIDED) {
768
770
  this.obliterates.addOrUpdate(pendingSegmentGroup.obliterateInfo);
769
771
  }
770
772
  // Perform slides after all segments have been acked, so that
771
773
  // positions after slide are final
772
774
  if (opArgs.op.type === ops_js_1.MergeTreeDeltaType.REMOVE ||
773
- opArgs.op.type === ops_js_1.MergeTreeDeltaType.OBLITERATE) {
775
+ opArgs.op.type === ops_js_1.MergeTreeDeltaType.OBLITERATE ||
776
+ opArgs.op.type === ops_js_1.MergeTreeDeltaType.OBLITERATE_SIDED) {
774
777
  this.slideAckedRemovedSegmentReferences(pendingSegmentGroup.segments);
775
778
  }
776
779
  this.mergeTreeMaintenanceCallback?.({
@@ -970,9 +973,9 @@ class MergeTree {
970
973
  });
971
974
  }
972
975
  this.updateRoot(splitNode);
973
- saveIfLocal(newSegment);
974
976
  insertPos += newSegment.cachedLength;
975
977
  if (!this.options?.mergeTreeEnableObliterate || this.obliterates.empty()) {
978
+ saveIfLocal(newSegment);
976
979
  continue;
977
980
  }
978
981
  // eslint-disable-next-line import/no-deprecated
@@ -999,13 +1002,13 @@ class MergeTree {
999
1002
  movedSeqs.unshift(ob.seq);
1000
1003
  }
1001
1004
  else {
1002
- if (newest === undefined || normalizedNewestSeq < normalizedObSeq) {
1003
- normalizedNewestSeq = normalizedObSeq;
1004
- newest = ob;
1005
- }
1006
1005
  movedClientIds.push(ob.clientId);
1007
1006
  movedSeqs.push(ob.seq);
1008
1007
  }
1008
+ if (newest === undefined || normalizedNewestSeq < normalizedObSeq) {
1009
+ normalizedNewestSeq = normalizedObSeq;
1010
+ newest = ob;
1011
+ }
1009
1012
  }
1010
1013
  }
1011
1014
  if (oldest && newest?.clientId !== clientId) {
@@ -1025,6 +1028,10 @@ class MergeTree {
1025
1028
  this.blockUpdatePathLengths(newSegment.parent, seq, clientId);
1026
1029
  }
1027
1030
  }
1031
+ else if (oldest && newest?.clientId === clientId) {
1032
+ newSegment.prevObliterateByInserter = newest;
1033
+ }
1034
+ saveIfLocal(newSegment);
1028
1035
  }
1029
1036
  }
1030
1037
  }
@@ -1231,10 +1238,11 @@ class MergeTree {
1231
1238
  (0, zamboni_js_1.zamboniSegments)(this);
1232
1239
  }
1233
1240
  }
1234
- obliterateRange(start, end, refSeq, clientId, seq, overwrite = false, opArgs) {
1235
- errorIfOptionNotTrue(this.options, "mergeTreeEnableObliterate");
1236
- this.ensureIntervalBoundary(start, refSeq, clientId);
1237
- this.ensureIntervalBoundary(end, refSeq, clientId);
1241
+ obliterateRangeSided(start, end, refSeq, clientId, seq, overwrite = false, opArgs) {
1242
+ const startPos = start.side === sequencePlace_js_1.Side.Before ? start.pos : start.pos + 1;
1243
+ const endPos = end.side === sequencePlace_js_1.Side.Before ? end.pos : end.pos + 1;
1244
+ this.ensureIntervalBoundary(startPos, refSeq, clientId);
1245
+ this.ensureIntervalBoundary(endPos, refSeq, clientId);
1238
1246
  let _overwrite = overwrite;
1239
1247
  const localOverlapWithRefs = [];
1240
1248
  const movedSegments = [];
@@ -1249,18 +1257,46 @@ class MergeTree {
1249
1257
  localSeq,
1250
1258
  segmentGroup: undefined,
1251
1259
  };
1252
- const { segment: startSeg } = this.getContainingSegment(start, refSeq, clientId);
1253
- const { segment: endSeg } = this.getContainingSegment(end - 1, refSeq, clientId);
1260
+ const { segment: startSeg } = this.getContainingSegment(start.pos, refSeq, clientId);
1261
+ const { segment: endSeg } = this.getContainingSegment(end.pos, refSeq, clientId);
1254
1262
  (0, internal_1.assert)(startSeg !== undefined && endSeg !== undefined, 0xa3f /* segments cannot be undefined */);
1255
- obliterate.start = this.createLocalReferencePosition(startSeg, 0, ops_js_1.ReferenceType.StayOnRemove, {
1263
+ obliterate.start = this.createLocalReferencePosition(startSeg, start.side === sequencePlace_js_1.Side.Before ? 0 : Math.max(startSeg.cachedLength - 1, 0), ops_js_1.ReferenceType.StayOnRemove, {
1256
1264
  obliterate,
1257
1265
  });
1258
- obliterate.end = this.createLocalReferencePosition(endSeg, endSeg.cachedLength - 1, ops_js_1.ReferenceType.StayOnRemove, {
1266
+ obliterate.end = this.createLocalReferencePosition(endSeg, end.side === sequencePlace_js_1.Side.Before ? 0 : Math.max(endSeg.cachedLength - 1, 0), ops_js_1.ReferenceType.StayOnRemove, {
1259
1267
  obliterate,
1260
1268
  });
1269
+ // Always create a segment group for obliterate,
1270
+ // even if there are no segments currently in the obliteration range.
1271
+ // Segments may be concurrently inserted into the obliteration range,
1272
+ // at which point they are added to the segment group.
1273
+ obliterate.segmentGroup = {
1274
+ segments: [],
1275
+ localSeq,
1276
+ refSeq: this.collabWindow.currentSeq,
1277
+ obliterateInfo: obliterate,
1278
+ };
1279
+ if (this.collabWindow.collaborating && clientId === this.collabWindow.clientId) {
1280
+ this.pendingSegments.push(obliterate.segmentGroup);
1281
+ }
1282
+ this.obliterates.addOrUpdate(obliterate);
1261
1283
  const markMoved = (segment, pos, _start, _end) => {
1262
- var _a;
1284
+ if ((start.side === sequencePlace_js_1.Side.After && startPos === pos + segment.cachedLength) || // exclusive start segment
1285
+ (end.side === sequencePlace_js_1.Side.Before &&
1286
+ endPos === pos &&
1287
+ (0, perspective_js_1.isSegmentPresent)(segment, { refSeq, localSeq })) // exclusive end segment
1288
+ ) {
1289
+ // We walk these segments because we want to also walk any concurrently inserted segments between here and the obliterated segments.
1290
+ // These segments are outside of the obliteration range though, so return true to keep walking.
1291
+ return true;
1292
+ }
1263
1293
  const existingMoveInfo = (0, mergeTreeNodes_js_1.toMoveInfo)(segment);
1294
+ if (segment.prevObliterateByInserter?.seq === constants_js_1.UnassignedSequenceNumber) {
1295
+ // We chose to not obliterate this segment because we are aware of an unacked local obliteration.
1296
+ // The local obliterate has not been sequenced yet, so it is still the newest obliterate we are aware of.
1297
+ // Other clients will also choose not to obliterate this segment because the most recent obliteration has the same clientId
1298
+ return true;
1299
+ }
1264
1300
  if (clientId !== segment.clientId &&
1265
1301
  segment.seq !== undefined &&
1266
1302
  seq !== constants_js_1.UnassignedSequenceNumber &&
@@ -1301,7 +1337,6 @@ class MergeTree {
1301
1337
  if (segment.movedSeq === constants_js_1.UnassignedSequenceNumber &&
1302
1338
  clientId === this.collabWindow.clientId) {
1303
1339
  obliterate.segmentGroup = this.addToPendingList(segment, obliterate.segmentGroup, localSeq);
1304
- (_a = obliterate.segmentGroup).obliterateInfo ?? (_a.obliterateInfo = obliterate);
1305
1340
  }
1306
1341
  else {
1307
1342
  if (MergeTree.options.zamboniSegments) {
@@ -1320,8 +1355,8 @@ class MergeTree {
1320
1355
  }
1321
1356
  return true;
1322
1357
  };
1323
- this.nodeMap(refSeq, clientId, markMoved, undefined, afterMarkMoved, start, end, undefined, seq === constants_js_1.UnassignedSequenceNumber ? undefined : seq);
1324
- this.obliterates.addOrUpdate(obliterate);
1358
+ this.nodeMap(refSeq, clientId, markMoved, undefined, afterMarkMoved, start.pos, end.pos + 1, // include the segment containing the end reference
1359
+ undefined, seq === constants_js_1.UnassignedSequenceNumber ? undefined : seq);
1325
1360
  this.slideAckedRemovedSegmentReferences(localOverlapWithRefs);
1326
1361
  // opArgs == undefined => test code
1327
1362
  if (movedSegments.length > 0) {
@@ -1342,6 +1377,17 @@ class MergeTree {
1342
1377
  (0, zamboni_js_1.zamboniSegments)(this);
1343
1378
  }
1344
1379
  }
1380
+ obliterateRange(start, end, refSeq, clientId, seq, overwrite = false, opArgs) {
1381
+ errorIfOptionNotTrue(this.options, "mergeTreeEnableObliterate");
1382
+ if (this.options?.mergeTreeEnableSidedObliterate) {
1383
+ (0, internal_1.assert)(typeof start === "object" && typeof end === "object", "Start and end must be of type InteriorSequencePlace if mergeTreeEnableSidedObliterate is enabled.");
1384
+ this.obliterateRangeSided(start, end, refSeq, clientId, seq, overwrite, opArgs);
1385
+ }
1386
+ else {
1387
+ (0, internal_1.assert)(typeof start === "number" && typeof end === "number", "Start and end must be numbers if mergeTreeEnableSidedObliterate is not enabled.");
1388
+ this.obliterateRangeSided({ pos: start, side: sequencePlace_js_1.Side.Before }, { pos: end - 1, side: sequencePlace_js_1.Side.After }, refSeq, clientId, seq, overwrite, opArgs);
1389
+ }
1390
+ }
1345
1391
  markRangeRemoved(start, end, refSeq, clientId, seq, overwrite = false, opArgs) {
1346
1392
  let _overwrite = overwrite;
1347
1393
  this.ensureIntervalBoundary(start, refSeq, clientId);