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

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 (130) hide show
  1. package/api-report/merge-tree.legacy.alpha.api.md +0 -1
  2. package/dist/client.d.ts.map +1 -1
  3. package/dist/client.js +3 -3
  4. package/dist/client.js.map +1 -1
  5. package/dist/collections/list.d.ts +1 -0
  6. package/dist/collections/list.d.ts.map +1 -1
  7. package/dist/collections/list.js +4 -1
  8. package/dist/collections/list.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/localReference.d.ts +1 -1
  13. package/dist/localReference.d.ts.map +1 -1
  14. package/dist/localReference.js.map +1 -1
  15. package/dist/mergeTree.d.ts +9 -9
  16. package/dist/mergeTree.d.ts.map +1 -1
  17. package/dist/mergeTree.js +41 -13
  18. package/dist/mergeTree.js.map +1 -1
  19. package/dist/mergeTreeNodes.d.ts +27 -8
  20. package/dist/mergeTreeNodes.d.ts.map +1 -1
  21. package/dist/mergeTreeNodes.js +0 -17
  22. package/dist/mergeTreeNodes.js.map +1 -1
  23. package/dist/revertibles.d.ts.map +1 -1
  24. package/dist/revertibles.js +4 -2
  25. package/dist/revertibles.js.map +1 -1
  26. package/dist/snapshotV1.d.ts.map +1 -1
  27. package/dist/snapshotV1.js +0 -2
  28. package/dist/snapshotV1.js.map +1 -1
  29. package/dist/snapshotlegacy.d.ts.map +1 -1
  30. package/dist/snapshotlegacy.js +0 -1
  31. package/dist/snapshotlegacy.js.map +1 -1
  32. package/dist/test/beastTest.spec.d.ts.map +1 -1
  33. package/dist/test/beastTest.spec.js +3 -3
  34. package/dist/test/beastTest.spec.js.map +1 -1
  35. package/dist/test/client.annotateMarker.spec.js.map +1 -1
  36. package/dist/test/client.applyMsg.spec.js +79 -3
  37. package/dist/test/client.applyMsg.spec.js.map +1 -1
  38. package/dist/test/client.conflictFarm.spec.js +17 -4
  39. package/dist/test/client.conflictFarm.spec.js.map +1 -1
  40. package/dist/test/client.localReference.spec.js.map +1 -1
  41. package/dist/test/client.rollback.spec.js.map +1 -1
  42. package/dist/test/mergeTree.annotate.spec.js +29 -19
  43. package/dist/test/mergeTree.annotate.spec.js.map +1 -1
  44. package/dist/test/mergeTree.insertingWalk.spec.js +2 -2
  45. package/dist/test/mergeTree.insertingWalk.spec.js.map +1 -1
  46. package/dist/test/mergeTreeOperationRunner.d.ts +1 -0
  47. package/dist/test/mergeTreeOperationRunner.d.ts.map +1 -1
  48. package/dist/test/mergeTreeOperationRunner.js +22 -1
  49. package/dist/test/mergeTreeOperationRunner.js.map +1 -1
  50. package/dist/test/obliterate.partialLength.spec.js +0 -4
  51. package/dist/test/obliterate.partialLength.spec.js.map +1 -1
  52. package/dist/test/obliterate.spec.js +0 -6
  53. package/dist/test/obliterate.spec.js.map +1 -1
  54. package/dist/test/revertibleFarm.spec.js.map +1 -1
  55. package/dist/test/testUtils.d.ts +2 -3
  56. package/dist/test/testUtils.d.ts.map +1 -1
  57. package/dist/test/testUtils.js +4 -4
  58. package/dist/test/testUtils.js.map +1 -1
  59. package/dist/zamboni.js +2 -1
  60. package/dist/zamboni.js.map +1 -1
  61. package/lib/client.d.ts.map +1 -1
  62. package/lib/client.js +3 -3
  63. package/lib/client.js.map +1 -1
  64. package/lib/collections/list.d.ts +1 -0
  65. package/lib/collections/list.d.ts.map +1 -1
  66. package/lib/collections/list.js +4 -1
  67. package/lib/collections/list.js.map +1 -1
  68. package/lib/index.d.ts +1 -1
  69. package/lib/index.d.ts.map +1 -1
  70. package/lib/index.js.map +1 -1
  71. package/lib/localReference.d.ts +1 -1
  72. package/lib/localReference.d.ts.map +1 -1
  73. package/lib/localReference.js.map +1 -1
  74. package/lib/mergeTree.d.ts +9 -9
  75. package/lib/mergeTree.d.ts.map +1 -1
  76. package/lib/mergeTree.js +43 -15
  77. package/lib/mergeTree.js.map +1 -1
  78. package/lib/mergeTreeNodes.d.ts +27 -8
  79. package/lib/mergeTreeNodes.d.ts.map +1 -1
  80. package/lib/mergeTreeNodes.js +0 -17
  81. package/lib/mergeTreeNodes.js.map +1 -1
  82. package/lib/revertibles.d.ts.map +1 -1
  83. package/lib/revertibles.js +5 -3
  84. package/lib/revertibles.js.map +1 -1
  85. package/lib/snapshotV1.d.ts.map +1 -1
  86. package/lib/snapshotV1.js +0 -2
  87. package/lib/snapshotV1.js.map +1 -1
  88. package/lib/snapshotlegacy.d.ts.map +1 -1
  89. package/lib/snapshotlegacy.js +0 -1
  90. package/lib/snapshotlegacy.js.map +1 -1
  91. package/lib/test/beastTest.spec.d.ts.map +1 -1
  92. package/lib/test/beastTest.spec.js +3 -3
  93. package/lib/test/beastTest.spec.js.map +1 -1
  94. package/lib/test/client.annotateMarker.spec.js.map +1 -1
  95. package/lib/test/client.applyMsg.spec.js +79 -3
  96. package/lib/test/client.applyMsg.spec.js.map +1 -1
  97. package/lib/test/client.conflictFarm.spec.js +18 -5
  98. package/lib/test/client.conflictFarm.spec.js.map +1 -1
  99. package/lib/test/client.localReference.spec.js.map +1 -1
  100. package/lib/test/client.rollback.spec.js.map +1 -1
  101. package/lib/test/mergeTree.annotate.spec.js +29 -19
  102. package/lib/test/mergeTree.annotate.spec.js.map +1 -1
  103. package/lib/test/mergeTree.insertingWalk.spec.js +2 -2
  104. package/lib/test/mergeTree.insertingWalk.spec.js.map +1 -1
  105. package/lib/test/mergeTreeOperationRunner.d.ts +1 -0
  106. package/lib/test/mergeTreeOperationRunner.d.ts.map +1 -1
  107. package/lib/test/mergeTreeOperationRunner.js +20 -0
  108. package/lib/test/mergeTreeOperationRunner.js.map +1 -1
  109. package/lib/test/obliterate.partialLength.spec.js +0 -4
  110. package/lib/test/obliterate.partialLength.spec.js.map +1 -1
  111. package/lib/test/obliterate.spec.js +0 -6
  112. package/lib/test/obliterate.spec.js.map +1 -1
  113. package/lib/test/revertibleFarm.spec.js.map +1 -1
  114. package/lib/test/testUtils.d.ts +2 -3
  115. package/lib/test/testUtils.d.ts.map +1 -1
  116. package/lib/test/testUtils.js +4 -4
  117. package/lib/test/testUtils.js.map +1 -1
  118. package/lib/zamboni.js +2 -1
  119. package/lib/zamboni.js.map +1 -1
  120. package/package.json +20 -21
  121. package/src/client.ts +0 -3
  122. package/src/collections/list.ts +6 -0
  123. package/src/index.ts +1 -0
  124. package/src/localReference.ts +5 -5
  125. package/src/mergeTree.ts +95 -62
  126. package/src/mergeTreeNodes.ts +35 -29
  127. package/src/revertibles.ts +12 -5
  128. package/src/snapshotV1.ts +0 -2
  129. package/src/snapshotlegacy.ts +0 -1
  130. package/src/zamboni.ts +3 -2
package/src/mergeTree.ts CHANGED
@@ -49,7 +49,6 @@ import {
49
49
  IMergeNode,
50
50
  IMoveInfo,
51
51
  IRemovalInfo,
52
- ISegment,
53
52
  ISegmentAction,
54
53
  ISegmentChanges,
55
54
  ISegmentLeaf,
@@ -63,6 +62,7 @@ import {
63
62
  seqLTE,
64
63
  toMoveInfo,
65
64
  toRemovalInfo,
65
+ type ISegmentInternal,
66
66
  // eslint-disable-next-line import/no-deprecated
67
67
  type ObliterateInfo,
68
68
  } from "./mergeTreeNodes.js";
@@ -80,7 +80,7 @@ import {
80
80
  } from "./ops.js";
81
81
  import { PartialSequenceLengths } from "./partialLengths.js";
82
82
  import { PerspectiveImpl, isSegmentPresent } from "./perspective.js";
83
- import { PropertySet, createMap, extend, extendIfUndefined } from "./properties.js";
83
+ import { PropertySet, clone, createMap, extend, extendIfUndefined } from "./properties.js";
84
84
  import {
85
85
  DetachedReferencePosition,
86
86
  ReferencePosition,
@@ -89,12 +89,14 @@ import {
89
89
  refTypeIncludesFlag,
90
90
  } from "./referencePositions.js";
91
91
  // eslint-disable-next-line import/no-deprecated
92
- import { PropertiesRollback } from "./segmentPropertiesManager.js";
92
+ import { SegmentGroupCollection } from "./segmentGroupCollection.js";
93
+ // eslint-disable-next-line import/no-deprecated
94
+ import { PropertiesManager, PropertiesRollback } from "./segmentPropertiesManager.js";
93
95
  import { Side, type InteriorSequencePlace } from "./sequencePlace.js";
94
96
  import { SortedSegmentSet } from "./sortedSegmentSet.js";
95
97
  import { zamboniSegments } from "./zamboni.js";
96
98
 
97
- function markSegmentMoved(seg: ISegment, moveInfo: IMoveInfo): void {
99
+ function markSegmentMoved(seg: ISegmentLeaf, moveInfo: IMoveInfo): void {
98
100
  seg.moveDst = moveInfo.moveDst;
99
101
  seg.movedClientIds = [...moveInfo.movedClientIds];
100
102
  seg.movedSeqs = [moveInfo.movedSeq];
@@ -103,29 +105,29 @@ function markSegmentMoved(seg: ISegment, moveInfo: IMoveInfo): void {
103
105
  seg.wasMovedOnInsert = moveInfo.wasMovedOnInsert;
104
106
  }
105
107
 
106
- function isMoved(segment: ISegment): segment is ISegment & IMoveInfo {
108
+ function isMoved(segment: ISegmentLeaf): segment is ISegmentLeaf & IMoveInfo {
107
109
  return toMoveInfo(segment) !== undefined;
108
110
  }
109
111
 
110
- function isRemoved(segment: ISegment): segment is ISegment & IRemovalInfo {
112
+ function isRemoved(segment: ISegmentLeaf): segment is ISegmentLeaf & IRemovalInfo {
111
113
  return toRemovalInfo(segment) !== undefined;
112
114
  }
113
115
 
114
- function isRemovedAndAcked(segment: ISegment): segment is ISegment & IRemovalInfo {
116
+ function isRemovedAndAcked(segment: ISegmentLeaf): segment is ISegmentLeaf & IRemovalInfo {
115
117
  const removalInfo = toRemovalInfo(segment);
116
118
  return removalInfo !== undefined && removalInfo.removedSeq !== UnassignedSequenceNumber;
117
119
  }
118
120
 
119
- function isMovedAndAcked(segment: ISegment): segment is ISegment & IMoveInfo {
121
+ function isMovedAndAcked(segment: ISegmentLeaf): segment is ISegmentLeaf & IMoveInfo {
120
122
  const moveInfo = toMoveInfo(segment);
121
123
  return moveInfo !== undefined && moveInfo.movedSeq !== UnassignedSequenceNumber;
122
124
  }
123
125
 
124
- function isRemovedAndAckedOrMovedAndAcked(segment: ISegment): boolean {
126
+ function isRemovedAndAckedOrMovedAndAcked(segment: ISegmentLeaf): boolean {
125
127
  return isRemovedAndAcked(segment) || isMovedAndAcked(segment);
126
128
  }
127
129
 
128
- function isRemovedOrMoved(segment: ISegment): boolean {
130
+ function isRemovedOrMoved(segment: ISegmentLeaf): boolean {
129
131
  return isRemoved(segment) || isMoved(segment);
130
132
  }
131
133
 
@@ -315,11 +317,11 @@ export function findRootMergeBlock(
315
317
  * @internal
316
318
  */
317
319
  function getSlideToSegment(
318
- segment: ISegment | undefined,
320
+ segment: ISegmentLeaf | undefined,
319
321
  slidingPreference: SlidingPreference = SlidingPreference.FORWARD,
320
- cache?: Map<ISegment, { seg?: ISegment }>,
322
+ cache?: Map<ISegmentLeaf, { seg?: ISegmentLeaf }>,
321
323
  useNewSlidingBehavior: boolean = false,
322
- ): [ISegment | undefined, "start" | "end" | undefined] {
324
+ ): [ISegmentLeaf | undefined, "start" | "end" | undefined] {
323
325
  if (
324
326
  !segment ||
325
327
  !isRemovedAndAckedOrMovedAndAcked(segment) ||
@@ -332,9 +334,9 @@ function getSlideToSegment(
332
334
  if (cachedSegment !== undefined) {
333
335
  return [cachedSegment.seg, undefined];
334
336
  }
335
- const result: { seg?: ISegment } = {};
337
+ const result: { seg?: ISegmentLeaf } = {};
336
338
  cache?.set(segment, result);
337
- const goFurtherToFindSlideToSegment = (seg: ISegment): boolean => {
339
+ const goFurtherToFindSlideToSegment = (seg: ISegmentLeaf): boolean => {
338
340
  if (seg.seq !== UnassignedSequenceNumber && !isRemovedAndAckedOrMovedAndAcked(seg)) {
339
341
  result.seg = seg;
340
342
  return false;
@@ -392,11 +394,11 @@ function getSlideToSegment(
392
394
  * @internal
393
395
  */
394
396
  export function getSlideToSegoff(
395
- segoff: { segment: ISegment | undefined; offset: number | undefined },
397
+ segoff: { segment: ISegmentInternal | undefined; offset: number | undefined },
396
398
  slidingPreference: SlidingPreference = SlidingPreference.FORWARD,
397
399
  useNewSlidingBehavior: boolean = false,
398
400
  ): {
399
- segment: ISegment | undefined;
401
+ segment: ISegmentInternal | undefined;
400
402
  offset: number | undefined;
401
403
  } {
402
404
  if (segoff.segment === undefined) {
@@ -470,7 +472,7 @@ class Obliterates {
470
472
  }
471
473
 
472
474
  // eslint-disable-next-line import/no-deprecated
473
- public findOverlapping(seg: ISegment): Iterable<ObliterateInfo> {
475
+ public findOverlapping(seg: ISegmentLeaf): Iterable<ObliterateInfo> {
474
476
  // eslint-disable-next-line import/no-deprecated
475
477
  const overlapping: ObliterateInfo[] = [];
476
478
  for (const start of this.startOrdered.items) {
@@ -555,7 +557,7 @@ export class MergeTree {
555
557
  * numbers corresponding to un-acked operations give valid results.
556
558
  */
557
559
  public localNetLength(
558
- segment: ISegment,
560
+ segment: ISegmentLeaf,
559
561
  refSeq?: number,
560
562
  localSeq?: number,
561
563
  ): number | undefined {
@@ -631,7 +633,7 @@ export class MergeTree {
631
633
  return index;
632
634
  }
633
635
 
634
- public reloadFromSegments(segments: ISegment[]): void {
636
+ public reloadFromSegments(segments: ISegmentLeaf[]): void {
635
637
  // This code assumes that a later call to `startCollaboration()` will initialize partial lengths.
636
638
  assert(
637
639
  !this.collabWindow.collaborating,
@@ -742,7 +744,7 @@ export class MergeTree {
742
744
  return totalOffset;
743
745
  }
744
746
 
745
- public getContainingSegment<T extends ISegment>(
747
+ public getContainingSegment<T extends ISegmentLeaf>(
746
748
  pos: number,
747
749
  refSeq: number,
748
750
  clientId: number,
@@ -759,7 +761,7 @@ export class MergeTree {
759
761
  let offset: number | undefined;
760
762
 
761
763
  const leaf = (
762
- leafSeg: ISegment,
764
+ leafSeg: ISegmentLeaf,
763
765
  segpos: number,
764
766
  _refSeq: number,
765
767
  _clientId: number,
@@ -793,17 +795,17 @@ export class MergeTree {
793
795
  *
794
796
  * @param segments - An array of (not necessarily contiguous) segments with increasing ordinals.
795
797
  */
796
- private slideAckedRemovedSegmentReferences(segments: ISegment[]): void {
798
+ private slideAckedRemovedSegmentReferences(segments: ISegmentLeaf[]): void {
797
799
  // References are slid in groups to preserve their order.
798
800
  let currentForwardSlideGroup: LocalReferenceCollection[] = [];
799
801
  let currentBackwardSlideGroup: LocalReferenceCollection[] = [];
800
802
 
801
803
  let currentForwardMaybeEndpoint: "start" | "end" | undefined;
802
- let currentForwardSlideDestination: ISegment | undefined;
804
+ let currentForwardSlideDestination: ISegmentLeaf | undefined;
803
805
  let currentForwardSlideIsForward: boolean | undefined;
804
806
 
805
807
  let currentBackwardMaybeEndpoint: "start" | "end" | undefined;
806
- let currentBackwardSlideDestination: ISegment | undefined;
808
+ let currentBackwardSlideDestination: ISegmentLeaf | undefined;
807
809
  let currentBackwardSlideIsForward: boolean | undefined;
808
810
 
809
811
  const slideGroup = (
@@ -862,8 +864,8 @@ export class MergeTree {
862
864
  };
863
865
 
864
866
  const trySlideSegment = (
865
- segment: ISegment,
866
- currentSlideDestination: ISegment | undefined,
867
+ segment: ISegmentLeaf,
868
+ currentSlideDestination: ISegmentLeaf | undefined,
867
869
  currentSlideIsForward: boolean | undefined,
868
870
  currentSlideGroup: LocalReferenceCollection[],
869
871
  pred: (ref: LocalReferencePosition) => boolean,
@@ -871,7 +873,7 @@ export class MergeTree {
871
873
  currentMaybeEndpoint: "start" | "end" | undefined,
872
874
  reassign: (
873
875
  localRefs: LocalReferenceCollection,
874
- slideToSegment: ISegment | undefined,
876
+ slideToSegment: ISegmentLeaf | undefined,
875
877
  slideIsForward: boolean,
876
878
  maybeEndpoint: "start" | "end" | undefined,
877
879
  ) => void,
@@ -916,8 +918,8 @@ export class MergeTree {
916
918
  }
917
919
  };
918
920
 
919
- const forwardSegmentCache = new Map<ISegment, { seg?: ISegment }>();
920
- const backwardSegmentCache = new Map<ISegment, { seg?: ISegment }>();
921
+ const forwardSegmentCache = new Map<ISegmentLeaf, { seg?: ISegmentLeaf }>();
922
+ const backwardSegmentCache = new Map<ISegmentLeaf, { seg?: ISegmentLeaf }>();
921
923
  for (const segment of segments) {
922
924
  assert(
923
925
  isRemovedAndAckedOrMovedAndAcked(segment),
@@ -1283,7 +1285,7 @@ export class MergeTree {
1283
1285
  }
1284
1286
 
1285
1287
  private addToPendingList(
1286
- segment: ISegment,
1288
+ segment: ISegmentLeaf,
1287
1289
  // eslint-disable-next-line import/no-deprecated
1288
1290
  segmentGroup?: SegmentGroup,
1289
1291
  localSeq?: number,
@@ -1312,8 +1314,9 @@ export class MergeTree {
1312
1314
  if (previousProps) {
1313
1315
  _segmentGroup.previousProps!.push(previousProps);
1314
1316
  }
1315
-
1316
- segment.segmentGroups.enqueue(_segmentGroup);
1317
+ // eslint-disable-next-line import/no-deprecated
1318
+ const segmentGroups = (segment.segmentGroups ??= new SegmentGroupCollection(segment));
1319
+ segmentGroups.enqueue(_segmentGroup);
1317
1320
  return _segmentGroup;
1318
1321
  }
1319
1322
 
@@ -1362,7 +1365,7 @@ export class MergeTree {
1362
1365
 
1363
1366
  public insertSegments(
1364
1367
  pos: number,
1365
- segments: ISegment[],
1368
+ segments: ISegmentLeaf[],
1366
1369
  refSeq: number,
1367
1370
  clientId: number,
1368
1371
  seq: number,
@@ -1459,7 +1462,7 @@ export class MergeTree {
1459
1462
  };
1460
1463
  // eslint-disable-next-line import/no-deprecated
1461
1464
  let segmentGroup: SegmentGroup;
1462
- const saveIfLocal = (locSegment: ISegment): void => {
1465
+ const saveIfLocal = (locSegment: ISegmentLeaf): void => {
1463
1466
  // Save segment so we can assign sequence number when acked by server
1464
1467
  if (this.collabWindow.collaborating) {
1465
1468
  if (
@@ -1480,11 +1483,10 @@ export class MergeTree {
1480
1483
  }
1481
1484
  };
1482
1485
  const onLeaf = (
1483
- segment: ISegment | undefined,
1486
+ segment: ISegmentLeaf | undefined,
1484
1487
  _pos: number,
1485
1488
  context: InsertContext,
1486
1489
  // Keeping this function within the scope of blockInsert for readability.
1487
- // eslint-disable-next-line unicorn/consistent-function-scoping
1488
1490
  ): ISegmentChanges => {
1489
1491
  const segmentChanges: ISegmentChanges = {};
1490
1492
  if (segment) {
@@ -1603,14 +1605,42 @@ export class MergeTree {
1603
1605
  }
1604
1606
 
1605
1607
  private readonly splitLeafSegment = (
1606
- segment: ISegment | undefined,
1608
+ segment: ISegmentLeaf | undefined,
1607
1609
  pos: number,
1608
1610
  ): ISegmentChanges => {
1609
1611
  if (!(pos > 0 && segment)) {
1610
1612
  return {};
1611
1613
  }
1612
1614
 
1613
- const next = segment.splitAt(pos)!;
1615
+ const next: ISegmentLeaf = segment.splitAt(pos)!;
1616
+
1617
+ if (segment?.segmentGroups) {
1618
+ // eslint-disable-next-line import/no-deprecated
1619
+ next.segmentGroups ??= new SegmentGroupCollection(next);
1620
+ segment.segmentGroups.copyTo(next);
1621
+ }
1622
+
1623
+ if (segment.prevObliterateByInserter) {
1624
+ next.prevObliterateByInserter = segment.prevObliterateByInserter;
1625
+ }
1626
+
1627
+ if (segment.properties) {
1628
+ if (segment.propertyManager === undefined) {
1629
+ next.properties = clone(segment.properties);
1630
+ } else {
1631
+ // eslint-disable-next-line import/no-deprecated
1632
+ next.propertyManager ??= new PropertiesManager();
1633
+ next.properties = segment.propertyManager.copyTo(
1634
+ segment.properties,
1635
+ next.properties,
1636
+ next.propertyManager,
1637
+ );
1638
+ }
1639
+ }
1640
+ if (segment.localRefs) {
1641
+ segment.localRefs.split(pos, next);
1642
+ }
1643
+
1614
1644
  this.mergeTreeMaintenanceCallback?.(
1615
1645
  {
1616
1646
  operation: MergeTreeMaintenanceType.SPLIT,
@@ -1827,6 +1857,7 @@ export class MergeTree {
1827
1857
  clientId: number,
1828
1858
  seq: number,
1829
1859
  opArgs: IMergeTreeDeltaOpArgs,
1860
+
1830
1861
  // eslint-disable-next-line import/no-deprecated
1831
1862
  rollback: PropertiesRollback = PropertiesRollback.None,
1832
1863
  ): void {
@@ -1837,19 +1868,25 @@ export class MergeTree {
1837
1868
  seq === UnassignedSequenceNumber ? ++this.collabWindow.localSeq : undefined;
1838
1869
  // eslint-disable-next-line import/no-deprecated
1839
1870
  let segmentGroup: SegmentGroup | undefined;
1840
- const annotateSegment = (segment: ISegment): boolean => {
1871
+ const annotateSegment = (segment: ISegmentLeaf): boolean => {
1841
1872
  assert(
1842
1873
  !Marker.is(segment) ||
1843
1874
  !(reservedMarkerIdKey in props) ||
1844
1875
  props.markerId === segment.properties?.markerId,
1845
1876
  0x5ad /* Cannot change the markerId of an existing marker */,
1846
1877
  );
1847
- const propertyDeltas = segment.addProperties(
1878
+
1879
+ // eslint-disable-next-line import/no-deprecated
1880
+ const propertyManager = (segment.propertyManager ??= new PropertiesManager());
1881
+ const properties = (segment.properties ??= createMap());
1882
+ const propertyDeltas = propertyManager.addProperties(
1883
+ properties,
1848
1884
  props,
1849
1885
  seq,
1850
1886
  this.collabWindow.collaborating,
1851
1887
  rollback,
1852
1888
  );
1889
+
1853
1890
  if (!isRemovedOrMoved(segment)) {
1854
1891
  deltaSegments.push({ segment, propertyDeltas });
1855
1892
  }
@@ -1894,7 +1931,6 @@ export class MergeTree {
1894
1931
  refSeq: number,
1895
1932
  clientId: number,
1896
1933
  seq: number,
1897
- overwrite: boolean = false,
1898
1934
  opArgs: IMergeTreeDeltaOpArgs,
1899
1935
  ): void {
1900
1936
  const startPos = start.side === Side.Before ? start.pos : start.pos + 1;
@@ -1903,8 +1939,8 @@ export class MergeTree {
1903
1939
  this.ensureIntervalBoundary(startPos, refSeq, clientId);
1904
1940
  this.ensureIntervalBoundary(endPos, refSeq, clientId);
1905
1941
 
1906
- let _overwrite = overwrite;
1907
- const localOverlapWithRefs: ISegment[] = [];
1942
+ let _overwrite = false;
1943
+ const localOverlapWithRefs: ISegmentLeaf[] = [];
1908
1944
  const movedSegments: IMergeTreeSegmentDelta[] = [];
1909
1945
  const localSeq =
1910
1946
  seq === UnassignedSequenceNumber ? ++this.collabWindow.localSeq : undefined;
@@ -1960,7 +1996,7 @@ export class MergeTree {
1960
1996
  this.obliterates.addOrUpdate(obliterate);
1961
1997
 
1962
1998
  const markMoved = (
1963
- segment: ISegment,
1999
+ segment: ISegmentLeaf,
1964
2000
  pos: number,
1965
2001
  _start: number,
1966
2002
  _end: number,
@@ -2100,7 +2136,6 @@ export class MergeTree {
2100
2136
  refSeq: number,
2101
2137
  clientId: number,
2102
2138
  seq: number,
2103
- overwrite: boolean = false,
2104
2139
  opArgs: IMergeTreeDeltaOpArgs,
2105
2140
  ): void {
2106
2141
  errorIfOptionNotTrue(this.options, "mergeTreeEnableObliterate");
@@ -2109,7 +2144,7 @@ export class MergeTree {
2109
2144
  typeof start === "object" && typeof end === "object",
2110
2145
  "Start and end must be of type InteriorSequencePlace if mergeTreeEnableSidedObliterate is enabled.",
2111
2146
  );
2112
- this.obliterateRangeSided(start, end, refSeq, clientId, seq, overwrite, opArgs);
2147
+ this.obliterateRangeSided(start, end, refSeq, clientId, seq, opArgs);
2113
2148
  } else {
2114
2149
  assert(
2115
2150
  typeof start === "number" && typeof end === "number",
@@ -2121,7 +2156,6 @@ export class MergeTree {
2121
2156
  refSeq,
2122
2157
  clientId,
2123
2158
  seq,
2124
- overwrite,
2125
2159
  opArgs,
2126
2160
  );
2127
2161
  }
@@ -2133,20 +2167,19 @@ export class MergeTree {
2133
2167
  refSeq: number,
2134
2168
  clientId: number,
2135
2169
  seq: number,
2136
- overwrite = false,
2137
2170
  opArgs: IMergeTreeDeltaOpArgs,
2138
2171
  ): void {
2139
- let _overwrite = overwrite;
2172
+ let _overwrite = false;
2140
2173
  this.ensureIntervalBoundary(start, refSeq, clientId);
2141
2174
  this.ensureIntervalBoundary(end, refSeq, clientId);
2142
2175
  // eslint-disable-next-line import/no-deprecated
2143
2176
  let segmentGroup: SegmentGroup;
2144
2177
  const removedSegments: IMergeTreeSegmentDelta[] = [];
2145
- const localOverlapWithRefs: ISegment[] = [];
2178
+ const localOverlapWithRefs: ISegmentLeaf[] = [];
2146
2179
  const localSeq =
2147
2180
  seq === UnassignedSequenceNumber ? ++this.collabWindow.localSeq : undefined;
2148
2181
  const markRemoved = (
2149
- segment: ISegment,
2182
+ segment: ISegmentLeaf,
2150
2183
  pos: number,
2151
2184
  _start: number,
2152
2185
  _end: number,
@@ -2245,10 +2278,10 @@ export class MergeTree {
2245
2278
  if (pendingSegmentGroup === undefined || pendingSegmentGroup !== localOpMetadata) {
2246
2279
  throw new Error("Rollback op doesn't match last edit");
2247
2280
  }
2248
- // Disabling because a for of loop causes the type of segment to be ISegment, which does not have parent information stored
2281
+ // Disabling because a for of loop causes the type of segment to be ISegmentLeaf, which does not have parent information stored
2249
2282
  // eslint-disable-next-line unicorn/no-array-for-each
2250
2283
  pendingSegmentGroup.segments.forEach((segment: ISegmentLeaf) => {
2251
- const segmentSegmentGroup = segment.segmentGroups?.pop?.();
2284
+ const segmentSegmentGroup = segment?.segmentGroups?.pop?.();
2252
2285
  assert(
2253
2286
  segmentSegmentGroup === pendingSegmentGroup,
2254
2287
  0x3ee /* Unexpected segmentGroup in segment */,
@@ -2298,8 +2331,8 @@ export class MergeTree {
2298
2331
  throw new Error("Rollback op doesn't match last edit");
2299
2332
  }
2300
2333
  let i = 0;
2301
- for (const segment of pendingSegmentGroup.segments) {
2302
- const segmentSegmentGroup = segment.segmentGroups.pop?.();
2334
+ for (const segment of pendingSegmentGroup.segments as ISegmentLeaf[]) {
2335
+ const segmentSegmentGroup = segment?.segmentGroups?.pop?.();
2303
2336
  assert(
2304
2337
  segmentSegmentGroup === pendingSegmentGroup,
2305
2338
  0x3ef /* Unexpected segmentGroup in segment */,
@@ -2316,7 +2349,6 @@ export class MergeTree {
2316
2349
  UniversalSequenceNumber,
2317
2350
  this.collabWindow.clientId,
2318
2351
  UniversalSequenceNumber,
2319
- false,
2320
2352
  { op: removeOp },
2321
2353
  );
2322
2354
  } /* op.type === MergeTreeDeltaType.ANNOTATE */ else {
@@ -2330,6 +2362,7 @@ export class MergeTree {
2330
2362
  this.collabWindow.clientId,
2331
2363
  UniversalSequenceNumber,
2332
2364
  { op: annotateOp },
2365
+
2333
2366
  // eslint-disable-next-line import/no-deprecated
2334
2367
  PropertiesRollback.Rollback,
2335
2368
  );
@@ -2344,7 +2377,7 @@ export class MergeTree {
2344
2377
  /**
2345
2378
  * Walk the segments up to the current segment and calculate its position
2346
2379
  */
2347
- private findRollbackPosition(segment: ISegment): number {
2380
+ private findRollbackPosition(segment: ISegmentLeaf): number {
2348
2381
  let segmentPosition = 0;
2349
2382
  walkAllChildSegments(this.root, (seg) => {
2350
2383
  // If we've found the desired segment, terminate the walk and return 'segmentPosition'.
@@ -2447,7 +2480,7 @@ export class MergeTree {
2447
2480
  }
2448
2481
 
2449
2482
  for (
2450
- let segmentToSlide: ListNode<ISegment> | undefined = lastLocalSegment,
2483
+ let segmentToSlide: ListNode<ISegmentLeaf> | undefined = lastLocalSegment,
2451
2484
  nearerSegment = lastLocalSegment?.prev;
2452
2485
  segmentToSlide !== undefined;
2453
2486
  segmentToSlide = nearerSegment, nearerSegment = nearerSegment?.prev
@@ -2485,7 +2518,7 @@ export class MergeTree {
2485
2518
  const newOrder = Array.from(affectedSegments, ({ data }) => data);
2486
2519
  for (const seg of newOrder)
2487
2520
  seg.localRefs?.walkReferences((lref) => lref.callbacks?.beforeSlide?.(lref));
2488
- const perSegmentTrackingGroups = new Map<ISegment, TrackingGroup[]>();
2521
+ const perSegmentTrackingGroups = new Map<ISegmentLeaf, TrackingGroup[]>();
2489
2522
  for (const segment of newOrder) {
2490
2523
  const { trackingCollection } = segment;
2491
2524
  const trackingGroups = [...trackingCollection.trackingGroups];
@@ -2550,7 +2583,7 @@ export class MergeTree {
2550
2583
  * it can fix up its local state to align with what would be expected of the op it resubmits.
2551
2584
  */
2552
2585
  public normalizeSegmentsOnRebase(): void {
2553
- let currentRangeToNormalize = new DoublyLinkedList<ISegment>();
2586
+ let currentRangeToNormalize = new DoublyLinkedList<ISegmentLeaf>();
2554
2587
  let rangeContainsLocalSegs = false;
2555
2588
  let rangeContainsRemoteRemovedSegs = false;
2556
2589
  const normalize = (): void => {
@@ -2573,7 +2606,7 @@ export class MergeTree {
2573
2606
  currentRangeToNormalize.push(seg);
2574
2607
  } else {
2575
2608
  normalize();
2576
- currentRangeToNormalize = new DoublyLinkedList<ISegment>();
2609
+ currentRangeToNormalize = new DoublyLinkedList<ISegmentLeaf>();
2577
2610
  rangeContainsLocalSegs = false;
2578
2611
  rangeContainsRemoteRemovedSegs = false;
2579
2612
  }
@@ -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
  /**
@@ -160,13 +192,6 @@ export interface IMoveInfo {
160
192
  * calculations
161
193
  */
162
194
  wasMovedOnInsert: boolean;
163
-
164
- /**
165
- * If a segment is inserted into an obliterated range,
166
- * but the newest obliteration of that range was by the inserting client,
167
- * then the segment is not obliterated because it is aware of the latest obliteration.
168
- */
169
- prevObliterateByInserter?: ObliterateInfo;
170
195
  }
171
196
 
172
197
  export function toMoveInfo(maybe: Partial<IMoveInfo> | undefined): IMoveInfo | undefined {
@@ -395,6 +420,7 @@ export interface SegmentActions<TClientData> {
395
420
  * @deprecated This functionality was not meant to be exported and will be removed in a future release
396
421
  * @legacy
397
422
  * @alpha
423
+ * @privateRemarks After deprecation period this interface should be made internal
398
424
  */
399
425
  export interface ObliterateInfo {
400
426
  start: LocalReferencePosition;
@@ -705,7 +731,6 @@ export abstract class BaseSegment implements ISegment {
705
731
  return undefined;
706
732
  }
707
733
 
708
- this.copyPropertiesTo(leafSegment);
709
734
  // eslint-disable-next-line @typescript-eslint/no-this-alias, unicorn/no-this-assignment
710
735
  const thisAsMergeSegment: ISegmentLeaf = this;
711
736
  leafSegment.parent = thisAsMergeSegment.parent;
@@ -729,11 +754,8 @@ export abstract class BaseSegment implements ISegment {
729
754
  leafSegment.movedSeqs = this.movedSeqs?.slice();
730
755
  leafSegment.localMovedSeq = this.localMovedSeq;
731
756
  leafSegment.wasMovedOnInsert = this.wasMovedOnInsert;
732
- this.segmentGroups.copyTo(leafSegment);
757
+
733
758
  this.trackingCollection.copyTo(leafSegment);
734
- if (this.localRefs) {
735
- this.localRefs.split(pos, leafSegment);
736
- }
737
759
  if (this.attribution) {
738
760
  leafSegment.attribution = this.attribution.splitAt(pos);
739
761
  }
@@ -741,22 +763,6 @@ export abstract class BaseSegment implements ISegment {
741
763
  return leafSegment;
742
764
  }
743
765
 
744
- private copyPropertiesTo(other: ISegment): void {
745
- if (this.properties !== undefined) {
746
- if (this.propertyManager) {
747
- // eslint-disable-next-line import/no-deprecated
748
- other.propertyManager = new PropertiesManager();
749
- other.properties = this.propertyManager.copyTo(
750
- this.properties,
751
- other.properties,
752
- other.propertyManager,
753
- );
754
- } else {
755
- other.properties = clone(this.properties);
756
- }
757
- }
758
- }
759
-
760
766
  public abstract clone(): ISegment;
761
767
 
762
768
  public append(other: ISegment): void {
@@ -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
 
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
  });