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

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 (147) hide show
  1. package/api-report/merge-tree.legacy.alpha.api.md +25 -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 +2 -2
  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/localReference.d.ts +1 -1
  14. package/dist/localReference.d.ts.map +1 -1
  15. package/dist/localReference.js.map +1 -1
  16. package/dist/mergeTree.d.ts +22 -8
  17. package/dist/mergeTree.d.ts.map +1 -1
  18. package/dist/mergeTree.js +98 -24
  19. package/dist/mergeTree.js.map +1 -1
  20. package/dist/mergeTreeNodes.d.ts +27 -2
  21. package/dist/mergeTreeNodes.d.ts.map +1 -1
  22. package/dist/mergeTreeNodes.js +2 -18
  23. package/dist/mergeTreeNodes.js.map +1 -1
  24. package/dist/opBuilder.d.ts +15 -1
  25. package/dist/opBuilder.d.ts.map +1 -1
  26. package/dist/opBuilder.js +28 -1
  27. package/dist/opBuilder.js.map +1 -1
  28. package/dist/ops.d.ts +27 -1
  29. package/dist/ops.d.ts.map +1 -1
  30. package/dist/ops.js +1 -0
  31. package/dist/ops.js.map +1 -1
  32. package/dist/revertibles.d.ts.map +1 -1
  33. package/dist/revertibles.js +4 -2
  34. package/dist/revertibles.js.map +1 -1
  35. package/dist/sequencePlace.d.ts +4 -0
  36. package/dist/sequencePlace.d.ts.map +1 -1
  37. package/dist/sequencePlace.js +17 -1
  38. package/dist/sequencePlace.js.map +1 -1
  39. package/dist/snapshotV1.d.ts.map +1 -1
  40. package/dist/snapshotV1.js +0 -2
  41. package/dist/snapshotV1.js.map +1 -1
  42. package/dist/snapshotlegacy.d.ts.map +1 -1
  43. package/dist/snapshotlegacy.js +0 -1
  44. package/dist/snapshotlegacy.js.map +1 -1
  45. package/dist/test/client.annotateMarker.spec.js.map +1 -1
  46. package/dist/test/client.applyMsg.spec.js +3 -3
  47. package/dist/test/client.applyMsg.spec.js.map +1 -1
  48. package/dist/test/client.localReference.spec.js.map +1 -1
  49. package/dist/test/client.rollback.spec.js.map +1 -1
  50. package/dist/test/mergeTree.annotate.spec.js +29 -19
  51. package/dist/test/mergeTree.annotate.spec.js.map +1 -1
  52. package/dist/test/obliterate.concurrent.spec.js +18 -0
  53. package/dist/test/obliterate.concurrent.spec.js.map +1 -1
  54. package/dist/test/obliterate.rangeExpansion.spec.js +109 -53
  55. package/dist/test/obliterate.rangeExpansion.spec.js.map +1 -1
  56. package/dist/test/obliterate.spec.js +2 -2
  57. package/dist/test/obliterate.spec.js.map +1 -1
  58. package/dist/test/reconnectHelper.d.ts +8 -6
  59. package/dist/test/reconnectHelper.d.ts.map +1 -1
  60. package/dist/test/reconnectHelper.js +14 -13
  61. package/dist/test/reconnectHelper.js.map +1 -1
  62. package/dist/test/revertibleFarm.spec.js.map +1 -1
  63. package/dist/test/testClientLogger.d.ts.map +1 -1
  64. package/dist/test/testClientLogger.js +19 -8
  65. package/dist/test/testClientLogger.js.map +1 -1
  66. package/dist/zamboni.js +2 -1
  67. package/dist/zamboni.js.map +1 -1
  68. package/lib/attributionPolicy.d.ts.map +1 -1
  69. package/lib/attributionPolicy.js +10 -3
  70. package/lib/attributionPolicy.js.map +1 -1
  71. package/lib/client.d.ts +14 -4
  72. package/lib/client.d.ts.map +1 -1
  73. package/lib/client.js +98 -10
  74. package/lib/client.js.map +1 -1
  75. package/lib/index.d.ts +2 -2
  76. package/lib/index.d.ts.map +1 -1
  77. package/lib/index.js.map +1 -1
  78. package/lib/legacy.d.ts +1 -0
  79. package/lib/localReference.d.ts +1 -1
  80. package/lib/localReference.d.ts.map +1 -1
  81. package/lib/localReference.js.map +1 -1
  82. package/lib/mergeTree.d.ts +22 -8
  83. package/lib/mergeTree.d.ts.map +1 -1
  84. package/lib/mergeTree.js +100 -26
  85. package/lib/mergeTree.js.map +1 -1
  86. package/lib/mergeTreeNodes.d.ts +27 -2
  87. package/lib/mergeTreeNodes.d.ts.map +1 -1
  88. package/lib/mergeTreeNodes.js +2 -18
  89. package/lib/mergeTreeNodes.js.map +1 -1
  90. package/lib/opBuilder.d.ts +15 -1
  91. package/lib/opBuilder.d.ts.map +1 -1
  92. package/lib/opBuilder.js +26 -0
  93. package/lib/opBuilder.js.map +1 -1
  94. package/lib/ops.d.ts +27 -1
  95. package/lib/ops.d.ts.map +1 -1
  96. package/lib/ops.js +1 -0
  97. package/lib/ops.js.map +1 -1
  98. package/lib/revertibles.d.ts.map +1 -1
  99. package/lib/revertibles.js +5 -3
  100. package/lib/revertibles.js.map +1 -1
  101. package/lib/sequencePlace.d.ts +4 -0
  102. package/lib/sequencePlace.d.ts.map +1 -1
  103. package/lib/sequencePlace.js +15 -0
  104. package/lib/sequencePlace.js.map +1 -1
  105. package/lib/snapshotV1.d.ts.map +1 -1
  106. package/lib/snapshotV1.js +0 -2
  107. package/lib/snapshotV1.js.map +1 -1
  108. package/lib/snapshotlegacy.d.ts.map +1 -1
  109. package/lib/snapshotlegacy.js +0 -1
  110. package/lib/snapshotlegacy.js.map +1 -1
  111. package/lib/test/client.annotateMarker.spec.js.map +1 -1
  112. package/lib/test/client.applyMsg.spec.js +3 -3
  113. package/lib/test/client.applyMsg.spec.js.map +1 -1
  114. package/lib/test/client.localReference.spec.js.map +1 -1
  115. package/lib/test/client.rollback.spec.js.map +1 -1
  116. package/lib/test/mergeTree.annotate.spec.js +29 -19
  117. package/lib/test/mergeTree.annotate.spec.js.map +1 -1
  118. package/lib/test/obliterate.concurrent.spec.js +18 -0
  119. package/lib/test/obliterate.concurrent.spec.js.map +1 -1
  120. package/lib/test/obliterate.rangeExpansion.spec.js +109 -53
  121. package/lib/test/obliterate.rangeExpansion.spec.js.map +1 -1
  122. package/lib/test/obliterate.spec.js +2 -2
  123. package/lib/test/obliterate.spec.js.map +1 -1
  124. package/lib/test/reconnectHelper.d.ts +8 -6
  125. package/lib/test/reconnectHelper.d.ts.map +1 -1
  126. package/lib/test/reconnectHelper.js +15 -14
  127. package/lib/test/reconnectHelper.js.map +1 -1
  128. package/lib/test/revertibleFarm.spec.js.map +1 -1
  129. package/lib/test/testClientLogger.d.ts.map +1 -1
  130. package/lib/test/testClientLogger.js +19 -8
  131. package/lib/test/testClientLogger.js.map +1 -1
  132. package/lib/zamboni.js +2 -1
  133. package/lib/zamboni.js.map +1 -1
  134. package/package.json +31 -18
  135. package/src/attributionPolicy.ts +5 -0
  136. package/src/client.ts +136 -21
  137. package/src/index.ts +2 -0
  138. package/src/localReference.ts +5 -5
  139. package/src/mergeTree.ts +200 -75
  140. package/src/mergeTreeNodes.ts +37 -23
  141. package/src/opBuilder.ts +32 -0
  142. package/src/ops.ts +23 -1
  143. package/src/revertibles.ts +12 -5
  144. package/src/sequencePlace.ts +16 -0
  145. package/src/snapshotV1.ts +0 -2
  146. package/src/snapshotlegacy.ts +0 -1
  147. package/src/zamboni.ts +3 -2
package/src/client.ts CHANGED
@@ -56,6 +56,7 @@ import {
56
56
  createGroupOp,
57
57
  createInsertSegmentOp,
58
58
  createObliterateRangeOp,
59
+ createObliterateRangeOpSided,
59
60
  createRemoveRangeOp,
60
61
  } from "./opBuilder.js";
61
62
  import {
@@ -72,9 +73,11 @@ import {
72
73
  IRelativePosition,
73
74
  MergeTreeDeltaType,
74
75
  ReferenceType,
76
+ type IMergeTreeObliterateSidedMsg,
75
77
  } from "./ops.js";
76
78
  import { PropertySet } from "./properties.js";
77
79
  import { DetachedReferencePosition, ReferencePosition } from "./referencePositions.js";
80
+ import { Side, type InteriorSequencePlace } from "./sequencePlace.js";
78
81
  import { SnapshotLoader } from "./snapshotLoader.js";
79
82
  import { SnapshotV1 } from "./snapshotV1.js";
80
83
  import { SnapshotLegacy } from "./snapshotlegacy.js";
@@ -99,6 +102,7 @@ export interface IIntegerRange {
99
102
  * they need for rebasing their ops on reconnection.
100
103
  * @legacy
101
104
  * @alpha
105
+ * @deprecated This functionality was not meant to be exported and will be removed in a future release
102
106
  */
103
107
  export interface IClientEvents {
104
108
  (event: "normalize", listener: (target: IEventThisPlaceHolder) => void): void;
@@ -252,15 +256,26 @@ export class Client extends TypedEventEmitter<IClientEvents> {
252
256
  * Obliterates the range. This is similar to removing the range, but also
253
257
  * includes any concurrently inserted content.
254
258
  *
255
- * @param start - The inclusive start of the range to obliterate
256
- * @param end - The exclusive end of the range to obliterate
259
+ * @param start - The start of the range to obliterate. Inclusive is side is Before (default).
260
+ * @param end - The end of the range to obliterate. Exclusive is side is After
261
+ * (default is to be after the last included character, but number index is exclusive).
257
262
  */
258
263
  public obliterateRangeLocal(
259
- start: number,
260
- end: number,
264
+ start: number | InteriorSequencePlace,
265
+ end: number | InteriorSequencePlace,
266
+ // eslint-disable-next-line import/no-deprecated
267
+ ): IMergeTreeObliterateMsg | IMergeTreeObliterateSidedMsg {
261
268
  // eslint-disable-next-line import/no-deprecated
262
- ): IMergeTreeObliterateMsg {
263
- const obliterateOp = createObliterateRangeOp(start, end);
269
+ let obliterateOp: IMergeTreeObliterateMsg | IMergeTreeObliterateSidedMsg;
270
+ if (this._mergeTree.options?.mergeTreeEnableSidedObliterate) {
271
+ obliterateOp = createObliterateRangeOpSided(start, end);
272
+ } else {
273
+ assert(
274
+ typeof start === "number" && typeof end === "number",
275
+ "Start and end must be numbers if mergeTreeEnableSidedObliterate is not enabled.",
276
+ );
277
+ obliterateOp = createObliterateRangeOp(start, end);
278
+ }
264
279
  this.applyObliterateRangeOp({ op: obliterateOp });
265
280
  return obliterateOp;
266
281
  }
@@ -472,22 +487,39 @@ export class Client extends TypedEventEmitter<IClientEvents> {
472
487
 
473
488
  private applyObliterateRangeOp(opArgs: IMergeTreeDeltaOpArgs): void {
474
489
  assert(
475
- opArgs.op.type === MergeTreeDeltaType.OBLITERATE,
490
+ opArgs.op.type === MergeTreeDeltaType.OBLITERATE ||
491
+ opArgs.op.type === MergeTreeDeltaType.OBLITERATE_SIDED,
476
492
  0x866 /* Unexpected op type on range obliterate! */,
477
493
  );
478
494
  const op = opArgs.op;
479
495
  const clientArgs = this.getClientSequenceArgs(opArgs);
480
- const range = this.getValidOpRange(op, clientArgs);
481
-
482
- this._mergeTree.obliterateRange(
483
- range.start,
484
- range.end,
485
- clientArgs.referenceSequenceNumber,
486
- clientArgs.clientId,
487
- clientArgs.sequenceNumber,
488
- false,
489
- opArgs,
490
- );
496
+ if (this._mergeTree.options?.mergeTreeEnableSidedObliterate) {
497
+ const { start, end } = this.getValidSidedRange(op, clientArgs);
498
+ this._mergeTree.obliterateRange(
499
+ start,
500
+ end,
501
+ clientArgs.referenceSequenceNumber,
502
+ clientArgs.clientId,
503
+ clientArgs.sequenceNumber,
504
+ false,
505
+ opArgs,
506
+ );
507
+ } else {
508
+ assert(
509
+ op.type === MergeTreeDeltaType.OBLITERATE,
510
+ "Unexpected sided obliterate while mergeTreeEnableSidedObliterate is disabled",
511
+ );
512
+ const range = this.getValidOpRange(op, clientArgs);
513
+ this._mergeTree.obliterateRange(
514
+ range.start,
515
+ range.end,
516
+ clientArgs.referenceSequenceNumber,
517
+ clientArgs.clientId,
518
+ clientArgs.sequenceNumber,
519
+ false,
520
+ opArgs,
521
+ );
522
+ }
491
523
  }
492
524
 
493
525
  /**
@@ -565,6 +597,82 @@ export class Client extends TypedEventEmitter<IClientEvents> {
565
597
  );
566
598
  }
567
599
 
600
+ /**
601
+ * Returns a valid range for the op, or throws if the range is invalid
602
+ * @param op - The op to generate the range for
603
+ * @param clientArgs - The client args for the op
604
+ * @throws LoggingError if the range is invalid
605
+ */
606
+ private getValidSidedRange(
607
+ // eslint-disable-next-line import/no-deprecated
608
+ op: IMergeTreeObliterateSidedMsg | IMergeTreeObliterateMsg,
609
+ clientArgs: IMergeTreeClientSequenceArgs,
610
+ ): {
611
+ start: InteriorSequencePlace;
612
+ end: InteriorSequencePlace;
613
+ } {
614
+ const invalidPositions: string[] = [];
615
+ let start: InteriorSequencePlace | undefined;
616
+ let end: InteriorSequencePlace | undefined;
617
+ if (op.pos1 === undefined) {
618
+ invalidPositions.push("start");
619
+ } else {
620
+ start =
621
+ typeof op.pos1 === "object"
622
+ ? { pos: op.pos1.pos, side: op.pos1.before ? Side.Before : Side.After }
623
+ : { pos: op.pos1, side: Side.Before };
624
+ }
625
+ if (op.pos2 === undefined) {
626
+ invalidPositions.push("end");
627
+ } else {
628
+ end =
629
+ typeof op.pos2 === "object"
630
+ ? { pos: op.pos2.pos, side: op.pos2.before ? Side.Before : Side.After }
631
+ : { pos: op.pos2 - 1, side: Side.After };
632
+ }
633
+
634
+ // Validate if local op
635
+ if (clientArgs.clientId === this.getClientId()) {
636
+ const length = this._mergeTree.getLength(
637
+ this.getCollabWindow().currentSeq,
638
+ this.getClientId(),
639
+ );
640
+ if (start !== undefined && (start.pos >= length || start.pos < 0)) {
641
+ // start out of bounds
642
+ invalidPositions.push("start");
643
+ }
644
+ if (end !== undefined && (end.pos >= length || end.pos < 0)) {
645
+ invalidPositions.push("end");
646
+ }
647
+ if (
648
+ start !== undefined &&
649
+ end !== undefined &&
650
+ (start.pos > end.pos ||
651
+ (start.pos === end.pos && start.side !== end.side && start.side === Side.After))
652
+ ) {
653
+ // end is before start
654
+ invalidPositions.push("inverted");
655
+ }
656
+ if (invalidPositions.length > 0) {
657
+ throw new LoggingError("InvalidRange", {
658
+ usageError: true,
659
+ invalidPositions: invalidPositions.toString(),
660
+ length,
661
+ opType: op.type,
662
+ opPos1Relative: op.relativePos1 !== undefined,
663
+ opPos2Relative: op.relativePos2 !== undefined,
664
+ opPos1: JSON.stringify(op.pos1),
665
+ opPos2: JSON.stringify(op.pos2),
666
+ start: JSON.stringify(start),
667
+ end: JSON.stringify(end),
668
+ });
669
+ }
670
+ }
671
+
672
+ assert(start !== undefined && end !== undefined, "Missing start or end of range");
673
+ return { start, end };
674
+ }
675
+
568
676
  /**
569
677
  * Returns a valid range for the op, or undefined
570
678
  * @param op - The op to generate the range for
@@ -614,7 +722,6 @@ export class Client extends TypedEventEmitter<IClientEvents> {
614
722
  invalidPositions.push("start");
615
723
  }
616
724
  // Validate end if not insert, or insert has end
617
- //
618
725
  if (
619
726
  (op.type !== MergeTreeDeltaType.INSERT || end !== undefined) &&
620
727
  (end === undefined || end <= start!)
@@ -892,7 +999,12 @@ export class Client extends TypedEventEmitter<IClientEvents> {
892
999
 
893
1000
  const first = opList[0];
894
1001
 
895
- if (!!first && first.pos2 !== undefined) {
1002
+ if (
1003
+ !!first &&
1004
+ first.pos2 !== undefined &&
1005
+ first.type !== MergeTreeDeltaType.OBLITERATE_SIDED &&
1006
+ newOp.type !== MergeTreeDeltaType.OBLITERATE_SIDED
1007
+ ) {
896
1008
  first.pos2 += newOp.pos2! - newOp.pos1!;
897
1009
  } else {
898
1010
  opList.push(newOp);
@@ -939,7 +1051,8 @@ export class Client extends TypedEventEmitter<IClientEvents> {
939
1051
  this.applyAnnotateRangeOp(opArgs);
940
1052
  break;
941
1053
  }
942
- case MergeTreeDeltaType.OBLITERATE: {
1054
+ case MergeTreeDeltaType.OBLITERATE:
1055
+ case MergeTreeDeltaType.OBLITERATE_SIDED: {
943
1056
  this.applyObliterateRangeOp(opArgs);
944
1057
  break;
945
1058
  }
@@ -973,6 +1086,7 @@ export class Client extends TypedEventEmitter<IClientEvents> {
973
1086
  this.applyAnnotateRangeOp({ op });
974
1087
  break;
975
1088
  }
1089
+ case MergeTreeDeltaType.OBLITERATE_SIDED:
976
1090
  case MergeTreeDeltaType.OBLITERATE: {
977
1091
  this.applyObliterateRangeOp({ op });
978
1092
  break;
@@ -1205,6 +1319,7 @@ export class Client extends TypedEventEmitter<IClientEvents> {
1205
1319
  this.applyRemoveRangeOp(opArgs);
1206
1320
  break;
1207
1321
  }
1322
+ case MergeTreeDeltaType.OBLITERATE_SIDED:
1208
1323
  case MergeTreeDeltaType.OBLITERATE: {
1209
1324
  this.applyObliterateRangeOp(opArgs);
1210
1325
  break;
package/src/index.ts CHANGED
@@ -71,6 +71,7 @@ export {
71
71
  SegmentGroup,
72
72
  toRemovalInfo,
73
73
  ObliterateInfo,
74
+ ISegmentInternal,
74
75
  } from "./mergeTreeNodes.js";
75
76
  export {
76
77
  Trackable,
@@ -100,6 +101,7 @@ export {
100
101
  MergeTreeDeltaType,
101
102
  ReferenceType,
102
103
  IMergeTreeObliterateMsg,
104
+ IMergeTreeObliterateSidedMsg,
103
105
  } from "./ops.js";
104
106
  export {
105
107
  addProperties,
@@ -7,7 +7,7 @@ import { assert } from "@fluidframework/core-utils/internal";
7
7
  import { UsageError } from "@fluidframework/telemetry-utils/internal";
8
8
 
9
9
  import { DoublyLinkedList, ListNode, walkList } from "./collections/index.js";
10
- import { ISegment } from "./mergeTreeNodes.js";
10
+ import { ISegmentInternal, type ISegment } from "./mergeTreeNodes.js";
11
11
  import { TrackingGroup, TrackingGroupCollection } from "./mergeTreeTracking.js";
12
12
  import { ReferenceType } from "./ops.js";
13
13
  import { PropertySet, addProperties } from "./properties.js";
@@ -86,7 +86,7 @@ export interface LocalReferencePosition extends ReferencePosition {
86
86
  class LocalReference implements LocalReferencePosition {
87
87
  public properties: PropertySet | undefined;
88
88
 
89
- private segment: ISegment | undefined;
89
+ private segment: ISegmentInternal | undefined;
90
90
  private offset: number = 0;
91
91
  private listNode: ListNode<LocalReference> | undefined;
92
92
 
@@ -109,7 +109,7 @@ class LocalReference implements LocalReferencePosition {
109
109
  }
110
110
 
111
111
  public link(
112
- segment: ISegment | undefined,
112
+ segment: ISegmentInternal | undefined,
113
113
  offset: number,
114
114
  listNode: ListNode<LocalReference> | undefined,
115
115
  ): void {
@@ -132,7 +132,7 @@ class LocalReference implements LocalReferencePosition {
132
132
  this.offset = offset;
133
133
  }
134
134
 
135
- public isLeaf(): this is ISegment {
135
+ public isLeaf(): this is ISegmentInternal {
136
136
  return false;
137
137
  }
138
138
 
@@ -140,7 +140,7 @@ class LocalReference implements LocalReferencePosition {
140
140
  this.properties = addProperties(this.properties, newProps);
141
141
  }
142
142
 
143
- public getSegment(): ISegment | undefined {
143
+ public getSegment(): ISegmentInternal | undefined {
144
144
  return this.segment;
145
145
  }
146
146