@fluidframework/merge-tree 2.4.0-297027 → 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 (78) hide show
  1. package/api-report/merge-tree.legacy.alpha.api.md +0 -1
  2. package/dist/index.d.ts +1 -1
  3. package/dist/index.d.ts.map +1 -1
  4. package/dist/index.js.map +1 -1
  5. package/dist/localReference.d.ts +1 -1
  6. package/dist/localReference.d.ts.map +1 -1
  7. package/dist/localReference.js.map +1 -1
  8. package/dist/mergeTree.d.ts +7 -7
  9. package/dist/mergeTree.d.ts.map +1 -1
  10. package/dist/mergeTree.js +33 -5
  11. package/dist/mergeTree.js.map +1 -1
  12. package/dist/mergeTreeNodes.d.ts +27 -8
  13. package/dist/mergeTreeNodes.d.ts.map +1 -1
  14. package/dist/mergeTreeNodes.js +0 -17
  15. package/dist/mergeTreeNodes.js.map +1 -1
  16. package/dist/revertibles.d.ts.map +1 -1
  17. package/dist/revertibles.js +4 -2
  18. package/dist/revertibles.js.map +1 -1
  19. package/dist/snapshotV1.d.ts.map +1 -1
  20. package/dist/snapshotV1.js +0 -2
  21. package/dist/snapshotV1.js.map +1 -1
  22. package/dist/snapshotlegacy.d.ts.map +1 -1
  23. package/dist/snapshotlegacy.js +0 -1
  24. package/dist/snapshotlegacy.js.map +1 -1
  25. package/dist/test/client.annotateMarker.spec.js.map +1 -1
  26. package/dist/test/client.applyMsg.spec.js +3 -3
  27. package/dist/test/client.applyMsg.spec.js.map +1 -1
  28. package/dist/test/client.localReference.spec.js.map +1 -1
  29. package/dist/test/client.rollback.spec.js.map +1 -1
  30. package/dist/test/mergeTree.annotate.spec.js +29 -19
  31. package/dist/test/mergeTree.annotate.spec.js.map +1 -1
  32. package/dist/test/obliterate.spec.js.map +1 -1
  33. package/dist/test/revertibleFarm.spec.js.map +1 -1
  34. package/dist/zamboni.js +2 -1
  35. package/dist/zamboni.js.map +1 -1
  36. package/lib/index.d.ts +1 -1
  37. package/lib/index.d.ts.map +1 -1
  38. package/lib/index.js.map +1 -1
  39. package/lib/localReference.d.ts +1 -1
  40. package/lib/localReference.d.ts.map +1 -1
  41. package/lib/localReference.js.map +1 -1
  42. package/lib/mergeTree.d.ts +7 -7
  43. package/lib/mergeTree.d.ts.map +1 -1
  44. package/lib/mergeTree.js +35 -7
  45. package/lib/mergeTree.js.map +1 -1
  46. package/lib/mergeTreeNodes.d.ts +27 -8
  47. package/lib/mergeTreeNodes.d.ts.map +1 -1
  48. package/lib/mergeTreeNodes.js +0 -17
  49. package/lib/mergeTreeNodes.js.map +1 -1
  50. package/lib/revertibles.d.ts.map +1 -1
  51. package/lib/revertibles.js +5 -3
  52. package/lib/revertibles.js.map +1 -1
  53. package/lib/snapshotV1.d.ts.map +1 -1
  54. package/lib/snapshotV1.js +0 -2
  55. package/lib/snapshotV1.js.map +1 -1
  56. package/lib/snapshotlegacy.d.ts.map +1 -1
  57. package/lib/snapshotlegacy.js +0 -1
  58. package/lib/snapshotlegacy.js.map +1 -1
  59. package/lib/test/client.annotateMarker.spec.js.map +1 -1
  60. package/lib/test/client.applyMsg.spec.js +3 -3
  61. package/lib/test/client.applyMsg.spec.js.map +1 -1
  62. package/lib/test/client.localReference.spec.js.map +1 -1
  63. package/lib/test/client.rollback.spec.js.map +1 -1
  64. package/lib/test/mergeTree.annotate.spec.js +29 -19
  65. package/lib/test/mergeTree.annotate.spec.js.map +1 -1
  66. package/lib/test/obliterate.spec.js.map +1 -1
  67. package/lib/test/revertibleFarm.spec.js.map +1 -1
  68. package/lib/zamboni.js +2 -1
  69. package/lib/zamboni.js.map +1 -1
  70. package/package.json +17 -17
  71. package/src/index.ts +1 -0
  72. package/src/localReference.ts +5 -5
  73. package/src/mergeTree.ts +92 -53
  74. package/src/mergeTreeNodes.ts +35 -29
  75. package/src/revertibles.ts +12 -5
  76. package/src/snapshotV1.ts +0 -2
  77. package/src/snapshotlegacy.ts +0 -1
  78. package/src/zamboni.ts +3 -2
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fluidframework/merge-tree",
3
- "version": "2.4.0-297027",
3
+ "version": "2.4.0-297385",
4
4
  "description": "Merge tree",
5
5
  "homepage": "https://fluidframework.com",
6
6
  "repository": {
@@ -79,30 +79,30 @@
79
79
  "temp-directory": "nyc/.nyc_output"
80
80
  },
81
81
  "dependencies": {
82
- "@fluid-internal/client-utils": "2.4.0-297027",
83
- "@fluidframework/container-definitions": "2.4.0-297027",
84
- "@fluidframework/core-interfaces": "2.4.0-297027",
85
- "@fluidframework/core-utils": "2.4.0-297027",
86
- "@fluidframework/datastore-definitions": "2.4.0-297027",
87
- "@fluidframework/driver-definitions": "2.4.0-297027",
88
- "@fluidframework/runtime-definitions": "2.4.0-297027",
89
- "@fluidframework/runtime-utils": "2.4.0-297027",
90
- "@fluidframework/shared-object-base": "2.4.0-297027",
91
- "@fluidframework/telemetry-utils": "2.4.0-297027"
82
+ "@fluid-internal/client-utils": "2.4.0-297385",
83
+ "@fluidframework/container-definitions": "2.4.0-297385",
84
+ "@fluidframework/core-interfaces": "2.4.0-297385",
85
+ "@fluidframework/core-utils": "2.4.0-297385",
86
+ "@fluidframework/datastore-definitions": "2.4.0-297385",
87
+ "@fluidframework/driver-definitions": "2.4.0-297385",
88
+ "@fluidframework/runtime-definitions": "2.4.0-297385",
89
+ "@fluidframework/runtime-utils": "2.4.0-297385",
90
+ "@fluidframework/shared-object-base": "2.4.0-297385",
91
+ "@fluidframework/telemetry-utils": "2.4.0-297385"
92
92
  },
93
93
  "devDependencies": {
94
94
  "@arethetypeswrong/cli": "^0.15.2",
95
95
  "@biomejs/biome": "~1.8.3",
96
- "@fluid-internal/mocha-test-setup": "2.4.0-297027",
97
- "@fluid-private/stochastic-test-utils": "2.4.0-297027",
98
- "@fluid-private/test-pairwise-generator": "2.4.0-297027",
96
+ "@fluid-internal/mocha-test-setup": "2.4.0-297385",
97
+ "@fluid-private/stochastic-test-utils": "2.4.0-297385",
98
+ "@fluid-private/test-pairwise-generator": "2.4.0-297385",
99
99
  "@fluid-tools/benchmark": "^0.50.0",
100
100
  "@fluid-tools/build-cli": "^0.46.0",
101
101
  "@fluidframework/build-common": "^2.0.3",
102
102
  "@fluidframework/build-tools": "^0.46.0",
103
103
  "@fluidframework/eslint-config-fluid": "^5.4.0",
104
- "@fluidframework/merge-tree-previous": "npm:@fluidframework/merge-tree@2.3.0",
105
- "@fluidframework/test-runtime-utils": "2.4.0-297027",
104
+ "@fluidframework/merge-tree-previous": "npm:@fluidframework/merge-tree@~2.3.0",
105
+ "@fluidframework/test-runtime-utils": "2.4.0-297385",
106
106
  "@microsoft/api-extractor": "7.47.8",
107
107
  "@types/diff": "^3.5.1",
108
108
  "@types/mocha": "^9.1.1",
@@ -137,7 +137,7 @@
137
137
  "backCompat": false
138
138
  }
139
139
  },
140
- "entrypoint": "internal"
140
+ "entrypoint": "legacy"
141
141
  },
142
142
  "scripts": {
143
143
  "api": "fluid-build . --task api",
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,
@@ -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
 
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,7 +1483,7 @@ 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.
@@ -1603,14 +1606,42 @@ export class MergeTree {
1603
1606
  }
1604
1607
 
1605
1608
  private readonly splitLeafSegment = (
1606
- segment: ISegment | undefined,
1609
+ segment: ISegmentLeaf | undefined,
1607
1610
  pos: number,
1608
1611
  ): ISegmentChanges => {
1609
1612
  if (!(pos > 0 && segment)) {
1610
1613
  return {};
1611
1614
  }
1612
1615
 
1613
- const next = segment.splitAt(pos)!;
1616
+ const next: ISegmentLeaf = segment.splitAt(pos)!;
1617
+
1618
+ if (segment?.segmentGroups) {
1619
+ // eslint-disable-next-line import/no-deprecated
1620
+ next.segmentGroups ??= new SegmentGroupCollection(next);
1621
+ segment.segmentGroups.copyTo(next);
1622
+ }
1623
+
1624
+ if (segment.prevObliterateByInserter) {
1625
+ next.prevObliterateByInserter = segment.prevObliterateByInserter;
1626
+ }
1627
+
1628
+ if (segment.properties) {
1629
+ if (segment.propertyManager === undefined) {
1630
+ next.properties = clone(segment.properties);
1631
+ } else {
1632
+ // eslint-disable-next-line import/no-deprecated
1633
+ next.propertyManager ??= new PropertiesManager();
1634
+ next.properties = segment.propertyManager.copyTo(
1635
+ segment.properties,
1636
+ next.properties,
1637
+ next.propertyManager,
1638
+ );
1639
+ }
1640
+ }
1641
+ if (segment.localRefs) {
1642
+ segment.localRefs.split(pos, next);
1643
+ }
1644
+
1614
1645
  this.mergeTreeMaintenanceCallback?.(
1615
1646
  {
1616
1647
  operation: MergeTreeMaintenanceType.SPLIT,
@@ -1827,6 +1858,7 @@ export class MergeTree {
1827
1858
  clientId: number,
1828
1859
  seq: number,
1829
1860
  opArgs: IMergeTreeDeltaOpArgs,
1861
+
1830
1862
  // eslint-disable-next-line import/no-deprecated
1831
1863
  rollback: PropertiesRollback = PropertiesRollback.None,
1832
1864
  ): void {
@@ -1837,19 +1869,25 @@ export class MergeTree {
1837
1869
  seq === UnassignedSequenceNumber ? ++this.collabWindow.localSeq : undefined;
1838
1870
  // eslint-disable-next-line import/no-deprecated
1839
1871
  let segmentGroup: SegmentGroup | undefined;
1840
- const annotateSegment = (segment: ISegment): boolean => {
1872
+ const annotateSegment = (segment: ISegmentLeaf): boolean => {
1841
1873
  assert(
1842
1874
  !Marker.is(segment) ||
1843
1875
  !(reservedMarkerIdKey in props) ||
1844
1876
  props.markerId === segment.properties?.markerId,
1845
1877
  0x5ad /* Cannot change the markerId of an existing marker */,
1846
1878
  );
1847
- const propertyDeltas = segment.addProperties(
1879
+
1880
+ // eslint-disable-next-line import/no-deprecated
1881
+ const propertyManager = (segment.propertyManager ??= new PropertiesManager());
1882
+ const properties = (segment.properties ??= createMap());
1883
+ const propertyDeltas = propertyManager.addProperties(
1884
+ properties,
1848
1885
  props,
1849
1886
  seq,
1850
1887
  this.collabWindow.collaborating,
1851
1888
  rollback,
1852
1889
  );
1890
+
1853
1891
  if (!isRemovedOrMoved(segment)) {
1854
1892
  deltaSegments.push({ segment, propertyDeltas });
1855
1893
  }
@@ -1904,7 +1942,7 @@ export class MergeTree {
1904
1942
  this.ensureIntervalBoundary(endPos, refSeq, clientId);
1905
1943
 
1906
1944
  let _overwrite = overwrite;
1907
- const localOverlapWithRefs: ISegment[] = [];
1945
+ const localOverlapWithRefs: ISegmentLeaf[] = [];
1908
1946
  const movedSegments: IMergeTreeSegmentDelta[] = [];
1909
1947
  const localSeq =
1910
1948
  seq === UnassignedSequenceNumber ? ++this.collabWindow.localSeq : undefined;
@@ -1960,7 +1998,7 @@ export class MergeTree {
1960
1998
  this.obliterates.addOrUpdate(obliterate);
1961
1999
 
1962
2000
  const markMoved = (
1963
- segment: ISegment,
2001
+ segment: ISegmentLeaf,
1964
2002
  pos: number,
1965
2003
  _start: number,
1966
2004
  _end: number,
@@ -2142,11 +2180,11 @@ export class MergeTree {
2142
2180
  // eslint-disable-next-line import/no-deprecated
2143
2181
  let segmentGroup: SegmentGroup;
2144
2182
  const removedSegments: IMergeTreeSegmentDelta[] = [];
2145
- const localOverlapWithRefs: ISegment[] = [];
2183
+ const localOverlapWithRefs: ISegmentLeaf[] = [];
2146
2184
  const localSeq =
2147
2185
  seq === UnassignedSequenceNumber ? ++this.collabWindow.localSeq : undefined;
2148
2186
  const markRemoved = (
2149
- segment: ISegment,
2187
+ segment: ISegmentLeaf,
2150
2188
  pos: number,
2151
2189
  _start: number,
2152
2190
  _end: number,
@@ -2245,10 +2283,10 @@ export class MergeTree {
2245
2283
  if (pendingSegmentGroup === undefined || pendingSegmentGroup !== localOpMetadata) {
2246
2284
  throw new Error("Rollback op doesn't match last edit");
2247
2285
  }
2248
- // Disabling because a for of loop causes the type of segment to be ISegment, which does not have parent information stored
2286
+ // Disabling because a for of loop causes the type of segment to be ISegmentLeaf, which does not have parent information stored
2249
2287
  // eslint-disable-next-line unicorn/no-array-for-each
2250
2288
  pendingSegmentGroup.segments.forEach((segment: ISegmentLeaf) => {
2251
- const segmentSegmentGroup = segment.segmentGroups?.pop?.();
2289
+ const segmentSegmentGroup = segment?.segmentGroups?.pop?.();
2252
2290
  assert(
2253
2291
  segmentSegmentGroup === pendingSegmentGroup,
2254
2292
  0x3ee /* Unexpected segmentGroup in segment */,
@@ -2298,8 +2336,8 @@ export class MergeTree {
2298
2336
  throw new Error("Rollback op doesn't match last edit");
2299
2337
  }
2300
2338
  let i = 0;
2301
- for (const segment of pendingSegmentGroup.segments) {
2302
- const segmentSegmentGroup = segment.segmentGroups.pop?.();
2339
+ for (const segment of pendingSegmentGroup.segments as ISegmentLeaf[]) {
2340
+ const segmentSegmentGroup = segment?.segmentGroups?.pop?.();
2303
2341
  assert(
2304
2342
  segmentSegmentGroup === pendingSegmentGroup,
2305
2343
  0x3ef /* Unexpected segmentGroup in segment */,
@@ -2330,6 +2368,7 @@ export class MergeTree {
2330
2368
  this.collabWindow.clientId,
2331
2369
  UniversalSequenceNumber,
2332
2370
  { op: annotateOp },
2371
+
2333
2372
  // eslint-disable-next-line import/no-deprecated
2334
2373
  PropertiesRollback.Rollback,
2335
2374
  );
@@ -2344,7 +2383,7 @@ export class MergeTree {
2344
2383
  /**
2345
2384
  * Walk the segments up to the current segment and calculate its position
2346
2385
  */
2347
- private findRollbackPosition(segment: ISegment): number {
2386
+ private findRollbackPosition(segment: ISegmentLeaf): number {
2348
2387
  let segmentPosition = 0;
2349
2388
  walkAllChildSegments(this.root, (seg) => {
2350
2389
  // If we've found the desired segment, terminate the walk and return 'segmentPosition'.
@@ -2447,7 +2486,7 @@ export class MergeTree {
2447
2486
  }
2448
2487
 
2449
2488
  for (
2450
- let segmentToSlide: ListNode<ISegment> | undefined = lastLocalSegment,
2489
+ let segmentToSlide: ListNode<ISegmentLeaf> | undefined = lastLocalSegment,
2451
2490
  nearerSegment = lastLocalSegment?.prev;
2452
2491
  segmentToSlide !== undefined;
2453
2492
  segmentToSlide = nearerSegment, nearerSegment = nearerSegment?.prev
@@ -2485,7 +2524,7 @@ export class MergeTree {
2485
2524
  const newOrder = Array.from(affectedSegments, ({ data }) => data);
2486
2525
  for (const seg of newOrder)
2487
2526
  seg.localRefs?.walkReferences((lref) => lref.callbacks?.beforeSlide?.(lref));
2488
- const perSegmentTrackingGroups = new Map<ISegment, TrackingGroup[]>();
2527
+ const perSegmentTrackingGroups = new Map<ISegmentLeaf, TrackingGroup[]>();
2489
2528
  for (const segment of newOrder) {
2490
2529
  const { trackingCollection } = segment;
2491
2530
  const trackingGroups = [...trackingCollection.trackingGroups];
@@ -2550,7 +2589,7 @@ export class MergeTree {
2550
2589
  * it can fix up its local state to align with what would be expected of the op it resubmits.
2551
2590
  */
2552
2591
  public normalizeSegmentsOnRebase(): void {
2553
- let currentRangeToNormalize = new DoublyLinkedList<ISegment>();
2592
+ let currentRangeToNormalize = new DoublyLinkedList<ISegmentLeaf>();
2554
2593
  let rangeContainsLocalSegs = false;
2555
2594
  let rangeContainsRemoteRemovedSegs = false;
2556
2595
  const normalize = (): void => {
@@ -2573,7 +2612,7 @@ export class MergeTree {
2573
2612
  currentRangeToNormalize.push(seg);
2574
2613
  } else {
2575
2614
  normalize();
2576
- currentRangeToNormalize = new DoublyLinkedList<ISegment>();
2615
+ currentRangeToNormalize = new DoublyLinkedList<ISegmentLeaf>();
2577
2616
  rangeContainsLocalSegs = false;
2578
2617
  rangeContainsRemoteRemovedSegs = false;
2579
2618
  }
@@ -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 {