@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
@@ -48,11 +48,43 @@ export interface IMergeNodeCommon {
48
48
  ordinal: string;
49
49
  isLeaf(): this is ISegment;
50
50
  }
51
+
51
52
  /**
53
+ * This interface exposes internal things to dds that leverage merge tree,
54
+ * like sequence and matrix.
55
+ *
56
+ * We use tiered interface to control visibility of segment properties.
57
+ * This sits between ISegment and ISegmentLeaf. It should only expose
58
+ * things tagged internal.
59
+ *
60
+ * @internal
61
+ */
62
+ export type ISegmentInternal = ISegment & {
63
+ localRefs?: LocalReferenceCollection;
64
+ };
65
+
66
+ /**
67
+ * We use tiered interface to control visibility of segment properties.
68
+ * This is the lowest interface and is not exported, it site below ISegment and ISegmentInternal.
69
+ * It should only expose unexported things.
70
+ *
52
71
  * someday we may split tree leaves from segments, but for now they are the same
53
72
  * this is just a convenience type that makes it clear that we need something that is both a segment and a leaf node
54
73
  */
55
- export type ISegmentLeaf = ISegment & { parent?: MergeBlock };
74
+ export type ISegmentLeaf = ISegmentInternal & {
75
+ parent?: MergeBlock;
76
+ // eslint-disable-next-line import/no-deprecated
77
+ segmentGroups?: SegmentGroupCollection;
78
+ // eslint-disable-next-line import/no-deprecated
79
+ propertyManager?: PropertiesManager;
80
+
81
+ /**
82
+ * If a segment is inserted into an obliterated range,
83
+ * but the newest obliteration of that range was by the inserting client,
84
+ * then the segment is not obliterated because it is aware of the latest obliteration.
85
+ */
86
+ prevObliterateByInserter?: ObliterateInfo;
87
+ };
56
88
  export type IMergeNode = MergeBlock | ISegmentLeaf;
57
89
 
58
90
  /**
@@ -388,6 +420,7 @@ export interface SegmentActions<TClientData> {
388
420
  * @deprecated This functionality was not meant to be exported and will be removed in a future release
389
421
  * @legacy
390
422
  * @alpha
423
+ * @privateRemarks After deprecation period this interface should be made internal
391
424
  */
392
425
  export interface ObliterateInfo {
393
426
  start: LocalReferencePosition;
@@ -661,7 +694,8 @@ export abstract class BaseSegment implements ISegment {
661
694
  return false;
662
695
  }
663
696
 
664
- case MergeTreeDeltaType.OBLITERATE: {
697
+ case MergeTreeDeltaType.OBLITERATE:
698
+ case MergeTreeDeltaType.OBLITERATE_SIDED: {
665
699
  const moveInfo: IMoveInfo | undefined = toMoveInfo(this);
666
700
  assert(moveInfo !== undefined, 0x86e /* On obliterate ack, missing move info! */);
667
701
  const obliterateInfo = segmentGroup.obliterateInfo;
@@ -697,7 +731,6 @@ export abstract class BaseSegment implements ISegment {
697
731
  return undefined;
698
732
  }
699
733
 
700
- this.copyPropertiesTo(leafSegment);
701
734
  // eslint-disable-next-line @typescript-eslint/no-this-alias, unicorn/no-this-assignment
702
735
  const thisAsMergeSegment: ISegmentLeaf = this;
703
736
  leafSegment.parent = thisAsMergeSegment.parent;
@@ -721,11 +754,8 @@ export abstract class BaseSegment implements ISegment {
721
754
  leafSegment.movedSeqs = this.movedSeqs?.slice();
722
755
  leafSegment.localMovedSeq = this.localMovedSeq;
723
756
  leafSegment.wasMovedOnInsert = this.wasMovedOnInsert;
724
- this.segmentGroups.copyTo(leafSegment);
757
+
725
758
  this.trackingCollection.copyTo(leafSegment);
726
- if (this.localRefs) {
727
- this.localRefs.split(pos, leafSegment);
728
- }
729
759
  if (this.attribution) {
730
760
  leafSegment.attribution = this.attribution.splitAt(pos);
731
761
  }
@@ -733,22 +763,6 @@ export abstract class BaseSegment implements ISegment {
733
763
  return leafSegment;
734
764
  }
735
765
 
736
- private copyPropertiesTo(other: ISegment): void {
737
- if (this.properties !== undefined) {
738
- if (this.propertyManager) {
739
- // eslint-disable-next-line import/no-deprecated
740
- other.propertyManager = new PropertiesManager();
741
- other.properties = this.propertyManager.copyTo(
742
- this.properties,
743
- other.properties,
744
- other.propertyManager,
745
- );
746
- } else {
747
- other.properties = clone(this.properties);
748
- }
749
- }
750
- }
751
-
752
766
  public abstract clone(): ISegment;
753
767
 
754
768
  public append(other: ISegment): void {
package/src/opBuilder.ts CHANGED
@@ -14,8 +14,10 @@ import {
14
14
  IMergeTreeObliterateMsg,
15
15
  IMergeTreeRemoveMsg,
16
16
  MergeTreeDeltaType,
17
+ type IMergeTreeObliterateSidedMsg,
17
18
  } from "./ops.js";
18
19
  import { PropertySet } from "./properties.js";
20
+ import { normalizePlace, Side, type SequencePlace } from "./sequencePlace.js";
19
21
 
20
22
  /**
21
23
  * Creates the op for annotating the markers with the provided properties
@@ -97,6 +99,36 @@ export function createObliterateRangeOp(start: number, end: number): IMergeTreeO
97
99
  };
98
100
  }
99
101
 
102
+ /**
103
+ * Creates the op to obliterate a range
104
+ *
105
+ * @param start - The start of the range to obliterate.
106
+ * If a number is provided, the range will start before that index.
107
+ * @param end - The end of the range to obliterate.
108
+ * If a number is provided, the range will end after that index -1.
109
+ * This preserves the previous behavior of not expanding obliteration ranges at the endpoints
110
+ * for uses which predate the availability of endpoint expansion.
111
+ *
112
+ * @internal
113
+ */
114
+ export function createObliterateRangeOpSided(
115
+ start: SequencePlace,
116
+ end: SequencePlace,
117
+ ): IMergeTreeObliterateSidedMsg {
118
+ const startPlace = normalizePlace(start);
119
+ // If a number is provided, default to after the previous index.
120
+ // This preserves the behavior of obliterate prior to the introduction of endpoint expansion.
121
+ const endPlace =
122
+ typeof end === "number"
123
+ ? { pos: end - 1, side: Side.After } // default to inclusive bounds
124
+ : normalizePlace(end);
125
+ return {
126
+ type: MergeTreeDeltaType.OBLITERATE_SIDED,
127
+ pos1: { pos: startPlace.pos, before: startPlace.side === Side.Before },
128
+ pos2: { pos: endPlace.pos, before: endPlace.side === Side.Before },
129
+ };
130
+ }
131
+
100
132
  /**
101
133
  * Creates an op for inserting a segment at the specified position.
102
134
  *
package/src/ops.ts CHANGED
@@ -70,6 +70,7 @@ export const MergeTreeDeltaType = {
70
70
  */
71
71
  GROUP: 3,
72
72
  OBLITERATE: 4,
73
+ OBLITERATE_SIDED: 5,
73
74
  } as const;
74
75
 
75
76
  /**
@@ -162,6 +163,26 @@ export interface IMergeTreeObliterateMsg extends IMergeTreeDelta {
162
163
  relativePos2?: never;
163
164
  }
164
165
 
166
+ /**
167
+ * @legacy
168
+ * @alpha
169
+ */
170
+ export interface IMergeTreeObliterateSidedMsg extends IMergeTreeDelta {
171
+ type: typeof MergeTreeDeltaType.OBLITERATE_SIDED;
172
+ pos1: { pos: number; before: boolean };
173
+ /**
174
+ * This field is currently unused, but we keep it around to make the union
175
+ * type of all merge-tree messages have the same fields
176
+ */
177
+ relativePos1?: never;
178
+ pos2: { pos: number; before: boolean };
179
+ /**
180
+ * This field is currently unused, but we keep it around to make the union
181
+ * type of all merge-tree messages have the same fields
182
+ */
183
+ relativePos2?: never;
184
+ }
185
+
165
186
  /**
166
187
  * @legacy
167
188
  * @alpha
@@ -206,7 +227,8 @@ export type IMergeTreeDeltaOp =
206
227
  | IMergeTreeInsertMsg
207
228
  | IMergeTreeRemoveMsg
208
229
  | IMergeTreeAnnotateMsg
209
- | IMergeTreeObliterateMsg;
230
+ | IMergeTreeObliterateMsg
231
+ | IMergeTreeObliterateSidedMsg;
210
232
 
211
233
  /**
212
234
  * @legacy
@@ -12,7 +12,12 @@ import { LocalReferenceCollection, LocalReferencePosition } from "./localReferen
12
12
  import { MergeTree, findRootMergeBlock } from "./mergeTree.js";
13
13
  import { IMergeTreeDeltaCallbackArgs } from "./mergeTreeDeltaCallback.js";
14
14
  import { depthFirstNodeWalk } from "./mergeTreeNodeWalk.js";
15
- import { ISegment, ISegmentLeaf, toRemovalInfo } from "./mergeTreeNodes.js";
15
+ import {
16
+ ISegment,
17
+ ISegmentInternal,
18
+ toRemovalInfo,
19
+ type ISegmentLeaf,
20
+ } from "./mergeTreeNodes.js";
16
21
  import { ITrackingGroup, Trackable, UnorderedTrackingGroup } from "./mergeTreeTracking.js";
17
22
  import { IJSONSegment, MergeTreeDeltaType, ReferenceType } from "./ops.js";
18
23
  import { PropertySet, matchProperties } from "./properties.js";
@@ -241,7 +246,8 @@ export function discardMergeTreeDeltaRevertible(
241
246
  t.trackingCollection.unlink(r.trackingGroup);
242
247
  // remove untracked local references
243
248
  if (t.trackingCollection.empty && !t.isLeaf()) {
244
- t.getSegment()?.localRefs?.removeLocalRef(t);
249
+ const segment: ISegmentInternal | undefined = t.getSegment();
250
+ segment?.localRefs?.removeLocalRef(t);
245
251
  }
246
252
  }
247
253
  }
@@ -281,7 +287,7 @@ function revertLocalRemove(
281
287
 
282
288
  assert(!tracked.isLeaf(), 0x3f4 /* removes must track local refs */);
283
289
 
284
- const refSeg = tracked.getSegment();
290
+ const refSeg: ISegmentInternal | undefined = tracked.getSegment();
285
291
  let realPos = mergeTreeWithRevert.referencePositionToLocalPosition(tracked);
286
292
 
287
293
  // References which are on EndOfStringSegment don't return detached for pos,
@@ -334,7 +340,7 @@ function revertLocalRemove(
334
340
  insertSegment.parent!,
335
341
  insertSegment,
336
342
  undefined,
337
- (seg) => {
343
+ (seg: ISegmentInternal) => {
338
344
  if (seg.localRefs?.empty === false) {
339
345
  return seg.localRefs.walkReferences(refHandler, undefined, forward);
340
346
  }
@@ -366,7 +372,8 @@ function revertLocalRemove(
366
372
  tg.link(insertSegment);
367
373
  tg.unlink(tracked);
368
374
  }
369
- tracked.getSegment()?.localRefs?.removeLocalRef(tracked);
375
+ const segment: ISegmentInternal | undefined = tracked.getSegment();
376
+ segment?.localRefs?.removeLocalRef(tracked);
370
377
  }
371
378
  }
372
379
 
@@ -87,3 +87,19 @@ export function endpointPosAndSide(
87
87
  endPos,
88
88
  };
89
89
  }
90
+
91
+ /**
92
+ * Returns the given place in InteriorSequencePlace form.
93
+ */
94
+ export function normalizePlace(place: SequencePlace): InteriorSequencePlace {
95
+ if (typeof place === "number") {
96
+ return { pos: place, side: Side.Before };
97
+ }
98
+ if (place === "start") {
99
+ return { pos: -1, side: Side.After };
100
+ }
101
+ if (place === "end") {
102
+ return { pos: -1, side: Side.Before };
103
+ }
104
+ return place;
105
+ }
package/src/snapshotV1.ts CHANGED
@@ -212,7 +212,6 @@ export class SnapshotV1 {
212
212
  if (segment) {
213
213
  if (segment.properties !== undefined && Object.keys(segment.properties).length === 0) {
214
214
  segment.properties = undefined;
215
- segment.propertyManager = undefined;
216
215
  }
217
216
  pushSegRaw(
218
217
  segment.toJSONObject() as JsonSegmentSpecs,
@@ -282,7 +281,6 @@ export class SnapshotV1 {
282
281
 
283
282
  if (segment.properties !== undefined && Object.keys(segment.properties).length === 0) {
284
283
  segment.properties = undefined;
285
- segment.propertyManager = undefined;
286
284
  }
287
285
  const raw: IJSONSegmentWithMergeInfo & { removedClient?: string } = {
288
286
  json: segment.toJSONObject() as IJSONSegment,
@@ -246,7 +246,6 @@ export class SnapshotLegacy {
246
246
  totalLength += segment.cachedLength;
247
247
  if (segment.properties !== undefined && Object.keys(segment.properties).length === 0) {
248
248
  segment.properties = undefined;
249
- segment.propertyManager = undefined;
250
249
  }
251
250
  this.segments!.push(segment);
252
251
  });
package/src/zamboni.ts CHANGED
@@ -135,8 +135,9 @@ function scourNode(node: MergeBlock, holdNodes: IMergeNode[], mergeTree: MergeTr
135
135
  // when possible.
136
136
  let prevSegment: ISegment | undefined;
137
137
  for (let k = 0; k < node.childCount; k++) {
138
- const childNode = node.children[k];
139
- if (!childNode.isLeaf() || !childNode.segmentGroups.empty) {
138
+ // TODO Non null asserting, why is this not null?
139
+ const childNode = node.children[k]!;
140
+ if (!childNode.isLeaf() || childNode.segmentGroups?.empty === false) {
140
141
  holdNodes.push(childNode);
141
142
  prevSegment = undefined;
142
143
  continue;