@fluidframework/merge-tree 2.12.0 → 2.13.0

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 (285) hide show
  1. package/CHANGELOG.md +4 -0
  2. package/dist/MergeTreeTextHelper.d.ts.map +1 -1
  3. package/dist/MergeTreeTextHelper.js +0 -2
  4. package/dist/MergeTreeTextHelper.js.map +1 -1
  5. package/dist/attributionPolicy.d.ts.map +1 -1
  6. package/dist/attributionPolicy.js +6 -16
  7. package/dist/attributionPolicy.js.map +1 -1
  8. package/dist/client.d.ts +3 -4
  9. package/dist/client.d.ts.map +1 -1
  10. package/dist/client.js +39 -28
  11. package/dist/client.js.map +1 -1
  12. package/dist/endOfTreeSegment.d.ts +2 -1
  13. package/dist/endOfTreeSegment.d.ts.map +1 -1
  14. package/dist/endOfTreeSegment.js.map +1 -1
  15. package/dist/index.d.ts +2 -1
  16. package/dist/index.d.ts.map +1 -1
  17. package/dist/index.js +2 -4
  18. package/dist/index.js.map +1 -1
  19. package/dist/mergeTree.d.ts +8 -7
  20. package/dist/mergeTree.d.ts.map +1 -1
  21. package/dist/mergeTree.js +190 -216
  22. package/dist/mergeTree.js.map +1 -1
  23. package/dist/mergeTreeNodeWalk.d.ts.map +1 -1
  24. package/dist/mergeTreeNodeWalk.js +3 -2
  25. package/dist/mergeTreeNodeWalk.js.map +1 -1
  26. package/dist/mergeTreeNodes.d.ts +76 -162
  27. package/dist/mergeTreeNodes.d.ts.map +1 -1
  28. package/dist/mergeTreeNodes.js +100 -112
  29. package/dist/mergeTreeNodes.js.map +1 -1
  30. package/dist/mergeTreeTracking.d.ts.map +1 -1
  31. package/dist/mergeTreeTracking.js +0 -2
  32. package/dist/mergeTreeTracking.js.map +1 -1
  33. package/dist/partialLengths.d.ts +2 -2
  34. package/dist/partialLengths.d.ts.map +1 -1
  35. package/dist/partialLengths.js +28 -26
  36. package/dist/partialLengths.js.map +1 -1
  37. package/dist/perspective.d.ts +3 -2
  38. package/dist/perspective.d.ts.map +1 -1
  39. package/dist/perspective.js +11 -4
  40. package/dist/perspective.js.map +1 -1
  41. package/dist/referencePositions.d.ts.map +1 -1
  42. package/dist/referencePositions.js +4 -1
  43. package/dist/referencePositions.js.map +1 -1
  44. package/dist/revertibles.d.ts.map +1 -1
  45. package/dist/revertibles.js +10 -11
  46. package/dist/revertibles.js.map +1 -1
  47. package/dist/segmentGroupCollection.d.ts +4 -4
  48. package/dist/segmentGroupCollection.d.ts.map +1 -1
  49. package/dist/segmentGroupCollection.js +0 -6
  50. package/dist/segmentGroupCollection.js.map +1 -1
  51. package/dist/segmentInfos.d.ts +257 -0
  52. package/dist/segmentInfos.d.ts.map +1 -0
  53. package/dist/segmentInfos.js +166 -0
  54. package/dist/segmentInfos.js.map +1 -0
  55. package/dist/snapshotLoader.d.ts.map +1 -1
  56. package/dist/snapshotLoader.js +38 -44
  57. package/dist/snapshotLoader.js.map +1 -1
  58. package/dist/snapshotV1.d.ts.map +1 -1
  59. package/dist/snapshotV1.js +9 -12
  60. package/dist/snapshotV1.js.map +1 -1
  61. package/dist/snapshotlegacy.d.ts +2 -2
  62. package/dist/snapshotlegacy.d.ts.map +1 -1
  63. package/dist/snapshotlegacy.js +5 -3
  64. package/dist/snapshotlegacy.js.map +1 -1
  65. package/dist/sortedSegmentSet.d.ts.map +1 -1
  66. package/dist/sortedSegmentSet.js +5 -8
  67. package/dist/sortedSegmentSet.js.map +1 -1
  68. package/dist/test/beastTest.spec.d.ts +0 -2
  69. package/dist/test/beastTest.spec.d.ts.map +1 -1
  70. package/dist/test/beastTest.spec.js +1 -5
  71. package/dist/test/beastTest.spec.js.map +1 -1
  72. package/dist/test/client.annotateMarker.spec.js.map +1 -1
  73. package/dist/test/client.applyMsg.spec.js +15 -12
  74. package/dist/test/client.applyMsg.spec.js.map +1 -1
  75. package/dist/test/client.attributionFarm.spec.js.map +1 -1
  76. package/dist/test/client.getPosition.spec.js +3 -2
  77. package/dist/test/client.getPosition.spec.js.map +1 -1
  78. package/dist/test/client.localReference.spec.js +6 -6
  79. package/dist/test/client.localReference.spec.js.map +1 -1
  80. package/dist/test/client.localReferenceFarm.spec.js.map +1 -1
  81. package/dist/test/client.rollback.spec.js.map +1 -1
  82. package/dist/test/dirname.cjs +0 -1
  83. package/dist/test/dirname.cjs.map +1 -1
  84. package/dist/test/index.d.ts +1 -1
  85. package/dist/test/index.d.ts.map +1 -1
  86. package/dist/test/index.js +2 -4
  87. package/dist/test/index.js.map +1 -1
  88. package/dist/test/mergeTree.annotate.spec.js +3 -0
  89. package/dist/test/mergeTree.annotate.spec.js.map +1 -1
  90. package/dist/test/mergeTree.insertingWalk.spec.js +1 -1
  91. package/dist/test/mergeTree.insertingWalk.spec.js.map +1 -1
  92. package/dist/test/mergeTree.markRangeRemoved.spec.js +2 -0
  93. package/dist/test/mergeTree.markRangeRemoved.spec.js.map +1 -1
  94. package/dist/test/mergeTree.walk.spec.js.map +1 -1
  95. package/dist/test/mergeTreeOperationRunner.d.ts.map +1 -1
  96. package/dist/test/mergeTreeOperationRunner.js +2 -3
  97. package/dist/test/mergeTreeOperationRunner.js.map +1 -1
  98. package/dist/test/obliterate.spec.js.map +1 -1
  99. package/dist/test/propertyManager.spec.js.map +1 -1
  100. package/dist/test/reconnectHelper.d.ts +2 -1
  101. package/dist/test/reconnectHelper.d.ts.map +1 -1
  102. package/dist/test/reconnectHelper.js.map +1 -1
  103. package/dist/test/resetPendingSegmentsToOp.spec.js.map +1 -1
  104. package/dist/test/revertibleFarm.spec.js.map +1 -1
  105. package/dist/test/segmentGroupCollection.spec.js +15 -3
  106. package/dist/test/segmentGroupCollection.spec.js.map +1 -1
  107. package/dist/test/snapshot.utils.d.ts +2 -2
  108. package/dist/test/snapshot.utils.d.ts.map +1 -1
  109. package/dist/test/snapshot.utils.js.map +1 -1
  110. package/dist/test/sortedSegmentSet.spec.js +4 -3
  111. package/dist/test/sortedSegmentSet.spec.js.map +1 -1
  112. package/dist/test/testClient.d.ts +8 -6
  113. package/dist/test/testClient.d.ts.map +1 -1
  114. package/dist/test/testClient.js +29 -27
  115. package/dist/test/testClient.js.map +1 -1
  116. package/dist/test/testClientLogger.d.ts.map +1 -1
  117. package/dist/test/testClientLogger.js +6 -4
  118. package/dist/test/testClientLogger.js.map +1 -1
  119. package/dist/test/testUtils.d.ts +2 -2
  120. package/dist/test/testUtils.d.ts.map +1 -1
  121. package/dist/test/testUtils.js +32 -8
  122. package/dist/test/testUtils.js.map +1 -1
  123. package/dist/test/text.d.ts +2 -2
  124. package/dist/test/text.d.ts.map +1 -1
  125. package/dist/test/text.js +12 -6
  126. package/dist/test/text.js.map +1 -1
  127. package/dist/test/tracking.spec.js.map +1 -1
  128. package/dist/test/wordUnitTests.spec.js +1 -1
  129. package/dist/test/wordUnitTests.spec.js.map +1 -1
  130. package/dist/zamboni.d.ts.map +1 -1
  131. package/dist/zamboni.js +8 -7
  132. package/dist/zamboni.js.map +1 -1
  133. package/lib/MergeTreeTextHelper.d.ts.map +1 -1
  134. package/lib/MergeTreeTextHelper.js +0 -2
  135. package/lib/MergeTreeTextHelper.js.map +1 -1
  136. package/lib/attributionPolicy.d.ts.map +1 -1
  137. package/lib/attributionPolicy.js +6 -16
  138. package/lib/attributionPolicy.js.map +1 -1
  139. package/lib/client.d.ts +3 -4
  140. package/lib/client.d.ts.map +1 -1
  141. package/lib/client.js +40 -29
  142. package/lib/client.js.map +1 -1
  143. package/lib/endOfTreeSegment.d.ts +2 -1
  144. package/lib/endOfTreeSegment.d.ts.map +1 -1
  145. package/lib/endOfTreeSegment.js.map +1 -1
  146. package/lib/index.d.ts +2 -1
  147. package/lib/index.d.ts.map +1 -1
  148. package/lib/index.js +1 -1
  149. package/lib/index.js.map +1 -1
  150. package/lib/mergeTree.d.ts +8 -7
  151. package/lib/mergeTree.d.ts.map +1 -1
  152. package/lib/mergeTree.js +177 -205
  153. package/lib/mergeTree.js.map +1 -1
  154. package/lib/mergeTreeNodeWalk.d.ts.map +1 -1
  155. package/lib/mergeTreeNodeWalk.js +3 -2
  156. package/lib/mergeTreeNodeWalk.js.map +1 -1
  157. package/lib/mergeTreeNodes.d.ts +76 -162
  158. package/lib/mergeTreeNodes.d.ts.map +1 -1
  159. package/lib/mergeTreeNodes.js +95 -108
  160. package/lib/mergeTreeNodes.js.map +1 -1
  161. package/lib/mergeTreeTracking.d.ts.map +1 -1
  162. package/lib/mergeTreeTracking.js +0 -2
  163. package/lib/mergeTreeTracking.js.map +1 -1
  164. package/lib/partialLengths.d.ts +2 -2
  165. package/lib/partialLengths.d.ts.map +1 -1
  166. package/lib/partialLengths.js +25 -23
  167. package/lib/partialLengths.js.map +1 -1
  168. package/lib/perspective.d.ts +3 -2
  169. package/lib/perspective.d.ts.map +1 -1
  170. package/lib/perspective.js +11 -4
  171. package/lib/perspective.js.map +1 -1
  172. package/lib/referencePositions.d.ts.map +1 -1
  173. package/lib/referencePositions.js +4 -1
  174. package/lib/referencePositions.js.map +1 -1
  175. package/lib/revertibles.d.ts.map +1 -1
  176. package/lib/revertibles.js +8 -9
  177. package/lib/revertibles.js.map +1 -1
  178. package/lib/segmentGroupCollection.d.ts +4 -4
  179. package/lib/segmentGroupCollection.d.ts.map +1 -1
  180. package/lib/segmentGroupCollection.js +0 -6
  181. package/lib/segmentGroupCollection.js.map +1 -1
  182. package/lib/segmentInfos.d.ts +257 -0
  183. package/lib/segmentInfos.d.ts.map +1 -0
  184. package/lib/segmentInfos.js +145 -0
  185. package/lib/segmentInfos.js.map +1 -0
  186. package/lib/snapshotLoader.d.ts.map +1 -1
  187. package/lib/snapshotLoader.js +38 -44
  188. package/lib/snapshotLoader.js.map +1 -1
  189. package/lib/snapshotV1.d.ts.map +1 -1
  190. package/lib/snapshotV1.js +9 -12
  191. package/lib/snapshotV1.js.map +1 -1
  192. package/lib/snapshotlegacy.d.ts +2 -2
  193. package/lib/snapshotlegacy.d.ts.map +1 -1
  194. package/lib/snapshotlegacy.js +5 -3
  195. package/lib/snapshotlegacy.js.map +1 -1
  196. package/lib/sortedSegmentSet.d.ts.map +1 -1
  197. package/lib/sortedSegmentSet.js +5 -8
  198. package/lib/sortedSegmentSet.js.map +1 -1
  199. package/lib/test/beastTest.spec.d.ts +0 -2
  200. package/lib/test/beastTest.spec.d.ts.map +1 -1
  201. package/lib/test/beastTest.spec.js +0 -3
  202. package/lib/test/beastTest.spec.js.map +1 -1
  203. package/lib/test/client.annotateMarker.spec.js.map +1 -1
  204. package/lib/test/client.applyMsg.spec.js +15 -12
  205. package/lib/test/client.applyMsg.spec.js.map +1 -1
  206. package/lib/test/client.attributionFarm.spec.js.map +1 -1
  207. package/lib/test/client.getPosition.spec.js +3 -2
  208. package/lib/test/client.getPosition.spec.js.map +1 -1
  209. package/lib/test/client.localReference.spec.js +1 -1
  210. package/lib/test/client.localReference.spec.js.map +1 -1
  211. package/lib/test/client.localReferenceFarm.spec.js.map +1 -1
  212. package/lib/test/client.rollback.spec.js +1 -1
  213. package/lib/test/client.rollback.spec.js.map +1 -1
  214. package/lib/test/dirname.cjs +0 -1
  215. package/lib/test/dirname.cjs.map +1 -1
  216. package/lib/test/index.d.ts +1 -1
  217. package/lib/test/index.d.ts.map +1 -1
  218. package/lib/test/index.js +1 -1
  219. package/lib/test/index.js.map +1 -1
  220. package/lib/test/mergeTree.annotate.spec.js +3 -0
  221. package/lib/test/mergeTree.annotate.spec.js.map +1 -1
  222. package/lib/test/mergeTree.insertingWalk.spec.js +2 -2
  223. package/lib/test/mergeTree.insertingWalk.spec.js.map +1 -1
  224. package/lib/test/mergeTree.markRangeRemoved.spec.js +2 -0
  225. package/lib/test/mergeTree.markRangeRemoved.spec.js.map +1 -1
  226. package/lib/test/mergeTree.walk.spec.js.map +1 -1
  227. package/lib/test/mergeTreeOperationRunner.d.ts.map +1 -1
  228. package/lib/test/mergeTreeOperationRunner.js +1 -2
  229. package/lib/test/mergeTreeOperationRunner.js.map +1 -1
  230. package/lib/test/obliterate.spec.js.map +1 -1
  231. package/lib/test/propertyManager.spec.js.map +1 -1
  232. package/lib/test/reconnectHelper.d.ts +2 -1
  233. package/lib/test/reconnectHelper.d.ts.map +1 -1
  234. package/lib/test/reconnectHelper.js.map +1 -1
  235. package/lib/test/resetPendingSegmentsToOp.spec.js.map +1 -1
  236. package/lib/test/revertibleFarm.spec.js.map +1 -1
  237. package/lib/test/segmentGroupCollection.spec.js +15 -3
  238. package/lib/test/segmentGroupCollection.spec.js.map +1 -1
  239. package/lib/test/snapshot.utils.d.ts +2 -2
  240. package/lib/test/snapshot.utils.d.ts.map +1 -1
  241. package/lib/test/snapshot.utils.js.map +1 -1
  242. package/lib/test/sortedSegmentSet.spec.js +4 -3
  243. package/lib/test/sortedSegmentSet.spec.js.map +1 -1
  244. package/lib/test/testClient.d.ts +8 -6
  245. package/lib/test/testClient.d.ts.map +1 -1
  246. package/lib/test/testClient.js +30 -28
  247. package/lib/test/testClient.js.map +1 -1
  248. package/lib/test/testClientLogger.d.ts.map +1 -1
  249. package/lib/test/testClientLogger.js +5 -3
  250. package/lib/test/testClientLogger.js.map +1 -1
  251. package/lib/test/testUtils.d.ts +2 -2
  252. package/lib/test/testUtils.d.ts.map +1 -1
  253. package/lib/test/testUtils.js +9 -8
  254. package/lib/test/testUtils.js.map +1 -1
  255. package/lib/test/text.d.ts +2 -2
  256. package/lib/test/text.d.ts.map +1 -1
  257. package/lib/test/text.js +12 -6
  258. package/lib/test/text.js.map +1 -1
  259. package/lib/test/tracking.spec.js.map +1 -1
  260. package/lib/test/wordUnitTests.spec.js +1 -1
  261. package/lib/test/wordUnitTests.spec.js.map +1 -1
  262. package/lib/zamboni.d.ts.map +1 -1
  263. package/lib/zamboni.js +7 -6
  264. package/lib/zamboni.js.map +1 -1
  265. package/package.json +17 -17
  266. package/src/MergeTreeTextHelper.ts +2 -4
  267. package/src/attributionPolicy.ts +5 -13
  268. package/src/client.ts +55 -44
  269. package/src/endOfTreeSegment.ts +3 -3
  270. package/src/index.ts +4 -6
  271. package/src/mergeTree.ts +245 -282
  272. package/src/mergeTreeNodeWalk.ts +3 -2
  273. package/src/mergeTreeNodes.ts +190 -322
  274. package/src/mergeTreeTracking.ts +0 -3
  275. package/src/partialLengths.ts +42 -27
  276. package/src/perspective.ts +27 -4
  277. package/src/referencePositions.ts +4 -1
  278. package/src/revertibles.ts +19 -13
  279. package/src/segmentGroupCollection.ts +7 -18
  280. package/src/segmentInfos.ts +377 -0
  281. package/src/snapshotLoader.ts +60 -57
  282. package/src/snapshotV1.ts +14 -16
  283. package/src/snapshotlegacy.ts +12 -17
  284. package/src/sortedSegmentSet.ts +6 -8
  285. package/src/zamboni.ts +10 -12
@@ -4,6 +4,7 @@
4
4
  */
5
5
 
6
6
  import { type ISegmentLeaf, type MergeBlock, IMergeNode } from "./mergeTreeNodes.js";
7
+ import { isMergeNodeInfo } from "./segmentInfos.js";
7
8
 
8
9
  export const LeafAction = {
9
10
  Exit: false,
@@ -124,7 +125,7 @@ export function forwardExcursion(
124
125
  startNode: IMergeNode,
125
126
  leafAction: (seg: ISegmentLeaf) => boolean | undefined,
126
127
  ): boolean {
127
- if (startNode.parent === undefined) {
128
+ if (!isMergeNodeInfo(startNode)) {
128
129
  return true;
129
130
  }
130
131
 
@@ -147,7 +148,7 @@ export function backwardExcursion(
147
148
  startNode: IMergeNode,
148
149
  leafAction: (seg: ISegmentLeaf) => boolean | undefined,
149
150
  ): boolean {
150
- if (startNode.parent === undefined) {
151
+ if (!isMergeNodeInfo(startNode)) {
151
152
  return true;
152
153
  }
153
154
  return depthFirstNodeWalk(
@@ -3,8 +3,6 @@
3
3
  * Licensed under the MIT License.
4
4
  */
5
5
 
6
- /* eslint-disable @typescript-eslint/no-non-null-assertion */
7
-
8
6
  import { assert } from "@fluidframework/core-utils/internal";
9
7
  import { AttributionKey } from "@fluidframework/runtime-definitions/internal";
10
8
 
@@ -21,14 +19,22 @@ import { IJSONSegment, IMarkerDef, ReferenceType } from "./ops.js";
21
19
  import { computeHierarchicalOrdinal } from "./ordinal.js";
22
20
  import type { PartialSequenceLengths } from "./partialLengths.js";
23
21
  import { PropertySet, clone, createMap, type MapLike } from "./properties.js";
24
- import {
25
- ReferencePosition,
26
- refGetTileLabels,
27
- refTypeIncludesFlag,
28
- } from "./referencePositions.js";
29
- // eslint-disable-next-line import/no-deprecated
22
+ import { ReferencePosition } from "./referencePositions.js";
30
23
  import { SegmentGroupCollection } from "./segmentGroupCollection.js";
31
- // eslint-disable-next-line import/no-deprecated
24
+ import {
25
+ isInserted,
26
+ isMergeNodeInfo as isMergeNode,
27
+ isMoved,
28
+ isRemoved,
29
+ overwriteInfo,
30
+ type IInsertionInfo,
31
+ type IMergeNodeInfo,
32
+ // eslint-disable-next-line import/no-deprecated
33
+ type IMoveInfo,
34
+ // eslint-disable-next-line import/no-deprecated
35
+ type IRemovalInfo,
36
+ type SegmentWithInfo,
37
+ } from "./segmentInfos.js";
32
38
  import { PropertiesManager } from "./segmentPropertiesManager.js";
33
39
 
34
40
  /**
@@ -55,163 +61,103 @@ export interface IMergeNodeCommon {
55
61
  * like sequence and matrix.
56
62
  *
57
63
  * We use tiered interface to control visibility of segment properties.
58
- * This sits between ISegment and ISegmentLeaf. It should only expose
64
+ * This sits between ISegment and ISegmentPrivate. It should only expose
59
65
  * things tagged internal.
60
66
  *
67
+ * Everything added here beyond ISegment should be optional to keep the ability
68
+ * to implicitly convert between the tiered interfaces.
69
+ *
61
70
  * @internal
62
71
  */
63
- export type ISegmentInternal = Omit<ISegment, keyof IRemovalInfo | keyof IMoveInfo> &
64
- Partial<IMergeNodeCommon> &
65
- Partial<IRemovalInfo> &
66
- Partial<IMoveInfo> & {
67
- // eslint-disable-next-line import/no-deprecated
68
- localRefs?: LocalReferenceCollection;
69
- };
72
+ export type ISegmentInternal = Omit<
73
+ ISegment,
74
+ // eslint-disable-next-line import/no-deprecated
75
+ keyof IRemovalInfo | keyof IMoveInfo
76
+ > & {
77
+ // eslint-disable-next-line import/no-deprecated
78
+ localRefs?: LocalReferenceCollection;
79
+ };
70
80
 
71
81
  /**
72
82
  * We use tiered interface to control visibility of segment properties.
73
83
  * This is the lowest interface and is not exported, it site below ISegment and ISegmentInternal.
74
84
  * It should only expose unexported things.
75
85
  *
86
+ * Everything added here beyond ISegmentInternal should be optional to keep the ability
87
+ * to implicitly convert between the tiered interfaces.
88
+ *
76
89
  * someday we may split tree leaves from segments, but for now they are the same
77
90
  * this is just a convenience type that makes it clear that we need something that is both a segment and a leaf node
78
91
  */
79
- export type ISegmentLeaf = ISegmentInternal & {
80
- parent?: MergeBlock;
81
- // eslint-disable-next-line import/no-deprecated
82
- segmentGroups?: SegmentGroupCollection;
83
- // eslint-disable-next-line import/no-deprecated
84
- propertyManager?: PropertiesManager;
85
- /**
86
- * If a segment is inserted into an obliterated range,
87
- * but the newest obliteration of that range was by the inserting client,
88
- * then the segment is not obliterated because it is aware of the latest obliteration.
89
- */
90
- prevObliterateByInserter?: ObliterateInfo;
91
- };
92
- export type IMergeNode = MergeBlock | ISegmentLeaf;
92
+ export type ISegmentPrivate = ISegmentInternal & // eslint-disable-next-line import/no-deprecated
93
+ Partial<IInsertionInfo & IMergeNodeInfo> & {
94
+ segmentGroups?: SegmentGroupCollection;
95
+ propertyManager?: PropertiesManager;
96
+ /**
97
+ * If a segment is inserted into an obliterated range,
98
+ * but the newest obliteration of that range was by the inserting client,
99
+ * then the segment is not obliterated because it is aware of the latest obliteration.
100
+ */
101
+ prevObliterateByInserter?: ObliterateInfo;
102
+ /**
103
+ * Whether or not this segment is a special segment denoting the start or
104
+ * end of the tree
105
+ *
106
+ * Endpoint segments are imaginary segments positioned immediately before or
107
+ * after the tree. These segments cannot be referenced by regular operations
108
+ * and exist primarily as a bucket for local references to slide onto during
109
+ * deletion of regular segments.
110
+ */
111
+ readonly endpointType?: "start" | "end";
112
+ };
93
113
 
94
114
  /**
95
- * Contains removal information associated to an {@link ISegment}.
96
- * @legacy
97
- * @alpha
98
- * @deprecated - This interface will be removed in 2.20 with no replacement.
115
+ * Segment leafs are segments that have both IMergeNodeInfo and IInsertionInfo. This means they
116
+ * are inserted at a position, and bound via their parent MergeBlock to the merge tree. MergeBlocks'
117
+ * children are either a segment leaf, or another merge block for interior nodes of the tree. When working
118
+ * within the tree it is generally unnecessary to use type coercions methods common to the infos, and segment
119
+ * leafs, as the children of MergeBlocks are already well typed. However, when segments come from outside the
120
+ * merge tree, like via client's public methods, it becomes necessary to use the type coercions methods
121
+ * to ensure the passed in segment objects are correctly bound to the merge tree.
99
122
  */
100
- export interface IRemovalInfo {
101
- /**
102
- * Local seq at which this segment was removed, if the removal is yet-to-be acked.
103
- */
104
- localRemovedSeq?: number;
105
- /**
106
- * Seq at which this segment was removed.
107
- */
108
- removedSeq: number;
109
- /**
110
- * List of client IDs that have removed this segment.
111
- * The client that actually removed the segment (i.e. whose removal op was sequenced first) is stored as the first
112
- * client in this list. Other clients in the list have all issued concurrent ops to remove the segment.
113
- * @remarks When this list has length \> 1, this is referred to as the "overlapping remove" case.
114
- */
115
- removedClientIds: number[];
116
- }
123
+ export type ISegmentLeaf = SegmentWithInfo<IMergeNodeInfo & IInsertionInfo>;
124
+ /**
125
+ * A type-guard which determines if the segment has segment leaf, and
126
+ * returns true if it does, along with applying strong typing.
127
+ * @param nodeLike - The segment-like object to check.
128
+ * @returns True if the segment is a segment leaf, otherwise false.
129
+ */
130
+ export const isSegmentLeaf = (segmentLike: unknown): segmentLike is ISegmentLeaf =>
131
+ isInserted(segmentLike) && isMergeNode(segmentLike);
117
132
 
118
133
  /**
119
- * Returns the removal information for a segment.
134
+ * Converts a segment-like object to a segment leaf object if possible.
120
135
  *
121
- * @internal
136
+ * @param segmentLike - The segment-like object to convert.
137
+ * @returns The segment leaf if the conversion is possible, otherwise undefined.
122
138
  */
123
- export function toRemovalInfo(
124
- maybe: Partial<IRemovalInfo> | undefined,
125
- ): IRemovalInfo | undefined {
126
- if (maybe?.removedClientIds !== undefined && maybe?.removedSeq !== undefined) {
127
- return maybe as IRemovalInfo;
128
- }
129
- assert(
130
- maybe?.removedClientIds === undefined && maybe?.removedSeq === undefined,
131
- 0x2bf /* "both removedClientIds and removedSeq should be set or not set" */,
132
- );
133
- }
134
-
139
+ export const toSegmentLeaf = (segmentLike: unknown): ISegmentLeaf | undefined =>
140
+ isSegmentLeaf(segmentLike) ? segmentLike : undefined;
135
141
  /**
136
- * Tracks information about when and where this segment was moved to.
142
+ * Asserts that the segment is a segment leaf. Usage of this function should not produce a user facing error.
137
143
  *
138
- * Note that merge-tree does not currently support moving and only supports
139
- * obliterate. The fields below include "move" in their names to avoid renaming
140
- * in the future, when moves _are_ supported.
141
- * @legacy
142
- * @alpha
143
- * @deprecated - This interface will be removed in 2.20 with no replacement.
144
+ * @param segmentLike - The segment-like object to check.
145
+ * @throws Will throw an error if the segment is not a segment leaf.
144
146
  */
145
- export interface IMoveInfo {
146
- /**
147
- * Local seq at which this segment was moved if the move is yet-to-be
148
- * acked.
149
- */
150
- localMovedSeq?: number;
151
-
152
- /**
153
- * The first seq at which this segment was moved.
154
- */
155
- movedSeq: number;
156
-
157
- /**
158
- * All seqs at which this segment was moved. In the case of overlapping,
159
- * concurrent moves this array will contain multiple seqs.
160
- *
161
- * The seq at `movedSeqs[i]` corresponds to the client id at `movedClientIds[i]`.
162
- *
163
- * The first element corresponds to the seq of the first move
164
- */
165
- movedSeqs: number[];
166
-
167
- /**
168
- * A reference to the inserted destination segment corresponding to this
169
- * segment's move.
170
- *
171
- * If undefined, the move was an obliterate.
172
- *
173
- * Currently this field is unused, as we only support obliterate operations
174
- */
175
- moveDst?: ReferencePosition;
176
-
177
- /**
178
- * List of client IDs that have moved this segment.
179
- *
180
- * The client that actually moved the segment (i.e. whose move op was sequenced
181
- * first) is stored as the first client in this list. Other clients in the
182
- * list have all issued concurrent ops to move the segment.
183
- */
184
- movedClientIds: number[];
185
-
186
- /**
187
- * If this segment was inserted into a concurrently moved range and
188
- * the move op was sequenced before the insertion op. In this case,
189
- * the segment is visible only to the inserting client
190
- *
191
- * `wasMovedOnInsert` only applies for acked obliterates. That is, if
192
- * a segment inserted by a remote client is moved on insertion by a local
193
- * and unacked obliterate, we do not consider it as having been moved
194
- * on insert
195
- *
196
- * If a segment is moved on insertion, its length is only ever visible to
197
- * the client that inserted the segment. This is relevant in partial length
198
- * calculations
199
- */
200
- wasMovedOnInsert: boolean;
201
- }
147
+ export const assertSegmentLeaf: (segmentLike: unknown) => asserts segmentLike is ISegmentLeaf =
148
+ (segmentLike) => assert(isSegmentLeaf(segmentLike), 0xaab /* must be segment leaf */);
149
+ /**
150
+ * This type is used for building MergeBlocks from segments and other MergeBlocks. We need this
151
+ * type as segments may not yet be bound to the tree, so lack merge node info which is required for
152
+ * segment leafs.
153
+ */
154
+ export type IMergeNodeBuilder = MergeBlock | SegmentWithInfo<IInsertionInfo>;
202
155
 
203
- export function toMoveInfo(maybe: Partial<IMoveInfo> | undefined): IMoveInfo | undefined {
204
- if (maybe?.movedClientIds !== undefined && maybe?.movedSeq !== undefined) {
205
- return maybe as IMoveInfo;
206
- }
207
- assert(
208
- maybe?.movedClientIds === undefined &&
209
- maybe?.movedSeq === undefined &&
210
- maybe?.movedSeqs === undefined &&
211
- maybe?.wasMovedOnInsert === undefined,
212
- 0x86d /* movedClientIds, movedSeq, wasMovedOnInsert, and movedSeqs should all be either set or not set */,
213
- );
214
- }
156
+ /**
157
+ * This type is used by MergeBlocks to define their children, which are either segments or other
158
+ * MergeBlocks.
159
+ */
160
+ export type IMergeNode = MergeBlock | ISegmentLeaf;
215
161
 
216
162
  /**
217
163
  * A segment representing a portion of the merge tree.
@@ -266,18 +212,6 @@ export interface ISegment {
266
212
  * @deprecated - This property will be removed in 2.20 with no replacement.
267
213
  */
268
214
  localSeq?: number;
269
- /**
270
- * Local seq at which this segment was removed. If this is defined, `removedSeq` will initially be set to
271
- * UnassignedSequenceNumber. However, if another client concurrently removes the same segment, `removedSeq`
272
- * will be updated to the seq at which that client removed this segment.
273
- *
274
- * Like {@link ISegment.localSeq}, this field is cleared once the local removal of the segment is acked.
275
- *
276
- * @privateRemarks
277
- * See {@link CollaborationWindow.localSeq} for more information on the semantics of localSeq.
278
- * @deprecated - This property will be removed in 2.20 with no replacement.
279
- */
280
- localRemovedSeq?: number;
281
215
  /**
282
216
  * Seq at which this segment was inserted.
283
217
  * If undefined, it is assumed the segment was inserted prior to the collab window's minimum sequence number.
@@ -320,6 +254,18 @@ export interface ISegment {
320
254
  */
321
255
  ordinal: string;
322
256
 
257
+ /**
258
+ * Local seq at which this segment was removed. If this is defined, `removedSeq` will initially be set to
259
+ * UnassignedSequenceNumber. However, if another client concurrently removes the same segment, `removedSeq`
260
+ * will be updated to the seq at which that client removed this segment.
261
+ *
262
+ * Like {@link ISegment.localSeq}, this field is cleared once the local removal of the segment is acked.
263
+ *
264
+ * @privateRemarks
265
+ * See {@link CollaborationWindow.localSeq} for more information on the semantics of localSeq.
266
+ * @deprecated - This property will be removed in 2.20 with no replacement.
267
+ */
268
+ localRemovedSeq?: number;
323
269
  /**
324
270
  * {@inheritDoc @fluidframework/merge-tree#IRemovalInfo.removedSeq}
325
271
  * @deprecated - This property will be removed in 2.20 with no replacement.
@@ -369,16 +315,7 @@ export interface ISegment {
369
315
  * @alpha
370
316
  */
371
317
  export function segmentIsRemoved(segment: ISegment): boolean {
372
- const leaf: ISegmentLeaf = segment;
373
- return leaf.removedSeq !== undefined;
374
- }
375
-
376
- /**
377
- * @internal
378
- */
379
- export interface IMarkerModifiedAction {
380
- // eslint-disable-next-line @typescript-eslint/prefer-function-type
381
- (marker: Marker): void;
318
+ return isRemoved(segment);
382
319
  }
383
320
 
384
321
  /**
@@ -397,72 +334,17 @@ export interface ISegmentAction<TClientData> {
397
334
  accum: TClientData,
398
335
  ): boolean;
399
336
  }
400
- /**
401
- * @internal
402
- */
403
337
  export interface ISegmentChanges {
404
- next?: ISegmentInternal;
405
- replaceCurrent?: ISegmentInternal;
406
- }
407
- /**
408
- * @internal
409
- */
410
- export interface BlockAction<TClientData> {
411
- // eslint-disable-next-line @typescript-eslint/prefer-function-type
412
- (
413
- block: MergeBlock,
414
- pos: number,
415
- refSeq: number,
416
- clientId: number,
417
- start: number | undefined,
418
- end: number | undefined,
419
- accum: TClientData,
420
- ): boolean;
421
- }
422
-
423
- /**
424
- * @internal
425
- */
426
- export interface NodeAction<TClientData> {
427
- // eslint-disable-next-line @typescript-eslint/prefer-function-type
428
- (
429
- node: IMergeNode,
430
- pos: number,
431
- refSeq: number,
432
- clientId: number,
433
- start: number | undefined,
434
- end: number | undefined,
435
- clientData: TClientData,
436
- ): boolean;
338
+ next?: SegmentWithInfo<IInsertionInfo>;
339
+ replaceCurrent?: SegmentWithInfo<IInsertionInfo>;
437
340
  }
438
341
 
439
- /**
440
- * @internal
441
- */
442
342
  export interface InsertContext {
443
- candidateSegment?: ISegmentInternal;
444
- leaf: (
445
- segment: ISegmentInternal | undefined,
446
- pos: number,
447
- ic: InsertContext,
448
- ) => ISegmentChanges;
343
+ candidateSegment?: SegmentWithInfo<IInsertionInfo>;
344
+ leaf: (segment: ISegmentLeaf | undefined, pos: number, ic: InsertContext) => ISegmentChanges;
449
345
  continuePredicate?: (continueFromBlock: MergeBlock) => boolean;
450
346
  }
451
347
 
452
- /**
453
- * @internal
454
- */
455
- export interface SegmentActions<TClientData> {
456
- leaf?: ISegmentAction<TClientData>;
457
- shift?: NodeAction<TClientData>;
458
- contains?: NodeAction<TClientData>;
459
- pre?: BlockAction<TClientData>;
460
- post?: BlockAction<TClientData>;
461
- }
462
-
463
- /**
464
- * @internal
465
- */
466
348
  export interface ObliterateInfo {
467
349
  start: LocalReferencePosition;
468
350
  end: LocalReferencePosition;
@@ -473,11 +355,8 @@ export interface ObliterateInfo {
473
355
  segmentGroup: SegmentGroup | undefined;
474
356
  }
475
357
 
476
- /**
477
- * @internal
478
- */
479
- export interface SegmentGroup<S extends ISegmentInternal = ISegmentInternal> {
480
- segments: S[];
358
+ export interface SegmentGroup {
359
+ segments: ISegmentLeaf[];
481
360
  previousProps?: PropertySet[];
482
361
  localSeq?: number;
483
362
  refSeq: number;
@@ -489,13 +368,9 @@ export interface SegmentGroup<S extends ISegmentInternal = ISegmentInternal> {
489
368
  * the MergeTree always inserts first, then checks for overflow and splits if the child count equals
490
369
  * `MaxNodesInBlock`. (i.e., `MaxNodesInBlock` contains 1 extra slot for temporary storage to
491
370
  * facilitate splits.)
492
- * @internal
493
371
  */
494
372
  export const MaxNodesInBlock = 8;
495
- /**
496
- * @internal
497
- */
498
- export class MergeBlock implements IMergeNodeCommon {
373
+ export class MergeBlock implements Partial<IMergeNodeInfo> {
499
374
  public children: IMergeNode[];
500
375
  public needsScour?: boolean;
501
376
  public parent?: MergeBlock;
@@ -553,15 +428,22 @@ export class MergeBlock implements IMergeNodeCommon {
553
428
  index === 0 ? undefined : this.children[index - 1]?.ordinal,
554
429
  );
555
430
  }
556
-
557
- public assignChild(child: IMergeNode, index: number, updateOrdinal = true): void {
558
- child.parent = this;
559
- child.index = index;
560
- if (updateOrdinal) {
561
- this.setOrdinal(child, index);
562
- }
563
- this.children[index] = child;
564
- }
431
+ }
432
+ export function assignChild<C extends IMergeNodeBuilder>(
433
+ parent: MergeBlock,
434
+ child: C,
435
+ index: number,
436
+ updateOrdinal = true,
437
+ ): asserts child is C & IMergeNodeInfo {
438
+ const node = Object.assign<C, IMergeNodeInfo>(child, {
439
+ parent,
440
+ index,
441
+ ordinal: child.ordinal ?? "",
442
+ });
443
+ if (updateOrdinal) {
444
+ parent.setOrdinal(node, index);
445
+ }
446
+ parent.children[index] = node;
565
447
  }
566
448
 
567
449
  export function seqLTE(seq: number, minOrRefSeq: number): boolean {
@@ -656,18 +538,32 @@ export abstract class BaseSegment implements ISegment {
656
538
  }
657
539
 
658
540
  protected cloneInto(b: ISegment): void {
659
- b.clientId = this.clientId;
541
+ const seg: ISegmentPrivate = b;
542
+ if (isInserted(this)) {
543
+ overwriteInfo<IInsertionInfo>(seg, {
544
+ clientId: this.clientId,
545
+ seq: this.seq,
546
+ });
547
+ }
660
548
  // TODO: deep clone properties
661
- b.properties = clone(this.properties);
662
- b.removedClientIds = this.removedClientIds?.slice();
663
- // TODO: copy removed client overlap and branch removal info
664
- b.removedSeq = this.removedSeq;
665
- b.movedClientIds = this.movedClientIds?.slice();
666
- b.movedSeq = this.movedSeq;
667
- b.movedSeqs = this.movedSeqs;
668
- b.wasMovedOnInsert = this.wasMovedOnInsert;
669
- b.seq = this.seq;
670
- b.attribution = this.attribution?.clone();
549
+ seg.properties = clone(this.properties);
550
+ if (isRemoved(this)) {
551
+ // eslint-disable-next-line import/no-deprecated
552
+ overwriteInfo<IRemovalInfo>(seg, {
553
+ removedSeq: this.removedSeq,
554
+ removedClientIds: [...this.removedClientIds],
555
+ });
556
+ }
557
+ if (isMoved(this)) {
558
+ // eslint-disable-next-line import/no-deprecated
559
+ overwriteInfo<IMoveInfo>(seg, {
560
+ movedSeq: this.movedSeq,
561
+ movedSeqs: [...this.movedSeqs],
562
+ wasMovedOnInsert: this.wasMovedOnInsert,
563
+ movedClientIds: [...this.movedClientIds],
564
+ });
565
+ }
566
+ seg.attribution = this.attribution?.clone();
671
567
  }
672
568
 
673
569
  public canAppend(segment: ISegment): boolean {
@@ -689,35 +585,51 @@ export abstract class BaseSegment implements ISegment {
689
585
  return undefined;
690
586
  }
691
587
 
692
- const leafSegment: ISegmentLeaf | undefined = this.createSplitSegmentAt(pos);
588
+ const leafSegment: ISegmentPrivate | undefined = this.createSplitSegmentAt(pos);
693
589
 
694
590
  if (!leafSegment) {
695
591
  return undefined;
696
592
  }
697
593
 
698
- // eslint-disable-next-line @typescript-eslint/no-this-alias, unicorn/no-this-assignment
699
- const thisAsMergeSegment: ISegmentLeaf = this;
700
- leafSegment.parent = thisAsMergeSegment.parent;
701
-
702
- // Give the leaf a temporary yet valid ordinal.
703
- // when this segment is put in the tree, it will get its real ordinal,
704
- // but this ordinal meets all the necessary invariants for now.
705
- // Ordinals exist purely for lexicographical sort order and use a small set of valid bytes for each string character.
706
- // The extra handling fromCodePoint has for things like surrogate pairs is therefore unnecessary.
707
- // eslint-disable-next-line unicorn/prefer-code-point
708
- leafSegment.ordinal = this.ordinal + String.fromCharCode(0);
709
-
710
- leafSegment.removedClientIds = this.removedClientIds?.slice();
711
- leafSegment.removedSeq = this.removedSeq;
712
- leafSegment.localRemovedSeq = this.localRemovedSeq;
713
- leafSegment.seq = this.seq;
714
- leafSegment.localSeq = this.localSeq;
715
- leafSegment.clientId = this.clientId;
716
- leafSegment.movedClientIds = this.movedClientIds?.slice();
717
- leafSegment.movedSeq = this.movedSeq;
718
- leafSegment.movedSeqs = this.movedSeqs?.slice();
719
- leafSegment.localMovedSeq = this.localMovedSeq;
720
- leafSegment.wasMovedOnInsert = this.wasMovedOnInsert;
594
+ if (isMergeNode(this)) {
595
+ overwriteInfo<IMergeNodeInfo>(leafSegment, {
596
+ index: this.index + 1,
597
+ // Give the leaf a temporary yet valid ordinal.
598
+ // when this segment is put in the tree, it will get its real ordinal,
599
+ // but this ordinal meets all the necessary invariants for now.
600
+ // Ordinals exist purely for lexicographical sort order and use a small set of valid bytes for each string character.
601
+ // The extra handling fromCodePoint has for things like surrogate pairs is therefore unnecessary.
602
+ // eslint-disable-next-line unicorn/prefer-code-point
603
+ ordinal: this.ordinal + String.fromCharCode(0),
604
+ parent: this.parent,
605
+ });
606
+ }
607
+
608
+ if (isInserted(this)) {
609
+ overwriteInfo<IInsertionInfo>(leafSegment, {
610
+ seq: this.seq,
611
+ localSeq: this.localSeq,
612
+ clientId: this.clientId,
613
+ });
614
+ }
615
+ if (isRemoved(this)) {
616
+ // eslint-disable-next-line import/no-deprecated
617
+ overwriteInfo<IRemovalInfo>(leafSegment, {
618
+ removedClientIds: [...this.removedClientIds],
619
+ removedSeq: this.removedSeq,
620
+ localRemovedSeq: this.localRemovedSeq,
621
+ });
622
+ }
623
+ if (isMoved(this)) {
624
+ // eslint-disable-next-line import/no-deprecated
625
+ overwriteInfo<IMoveInfo>(leafSegment, {
626
+ movedClientIds: [...this.movedClientIds],
627
+ movedSeq: this.movedSeq,
628
+ movedSeqs: [...this.movedSeqs],
629
+ localMovedSeq: this.localMovedSeq,
630
+ wasMovedOnInsert: this.wasMovedOnInsert,
631
+ });
632
+ }
721
633
 
722
634
  this.trackingCollection.copyTo(leafSegment);
723
635
  if (this.attribution) {
@@ -901,7 +813,7 @@ export class CollaborationWindow {
901
813
  * { localSeq: 1, seq: UnassignedSequenceNumber, text: "C" },
902
814
  * ]
903
815
  * ```
904
- * (note that {@link ISegment.localSeq} tracks the localSeq at which a segment was inserted)
816
+ * (note that localSeq tracks the localSeq at which a segment was inserted)
905
817
  *
906
818
  * Suppose the client then disconnects and reconnects before any of its insertions are acked. The reconnect flow will necessitate
907
819
  * that the client regenerates and resubmits ops based on its current segment state as well as the original op that was sent.
@@ -961,47 +873,3 @@ export const compareNumbers = (a: number, b: number): number => a - b;
961
873
  * Compares two strings.
962
874
  */
963
875
  export const compareStrings = (a: string, b: string): number => a.localeCompare(b);
964
-
965
- /**
966
- * Get a human-readable string for a given {@link Marker}.
967
- *
968
- * @remarks This function is intended for debugging only. The exact format of
969
- * this string should not be relied upon between versions.
970
- * @internal
971
- */
972
- export function debugMarkerToString(marker: Marker): string {
973
- let bbuf = "";
974
- if (refTypeIncludesFlag(marker, ReferenceType.Tile)) {
975
- bbuf += "Tile";
976
- }
977
- let lbuf = "";
978
- const id = marker.getId();
979
- if (id) {
980
- bbuf += ` (${id}) `;
981
- }
982
- const tileLabels = refGetTileLabels(marker);
983
- if (tileLabels) {
984
- lbuf += "tile -- ";
985
- for (let i = 0, len = tileLabels.length; i < len; i++) {
986
- const tileLabel = tileLabels[i];
987
- if (i > 0) {
988
- lbuf += "; ";
989
- }
990
- lbuf += tileLabel;
991
- }
992
- }
993
-
994
- let pbuf = "";
995
- if (marker.properties) {
996
- pbuf += JSON.stringify(marker.properties, (key, value) => {
997
- // Avoid circular reference when stringifying makers containing handles.
998
- // (Substitute a debug string instead.)
999
- // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-member-access
1000
- const handle = !!value && value.IFluidHandle;
1001
-
1002
- // eslint-disable-next-line @typescript-eslint/no-unsafe-return, @typescript-eslint/no-unsafe-member-access
1003
- return handle ? `#Handle(${handle.routeContext.path}/${handle.path})` : value;
1004
- });
1005
- }
1006
- return `M ${bbuf}: ${lbuf} ${pbuf}`;
1007
- }