@fluidframework/sequence 2.0.0-dev.5.3.2.178189 → 2.0.0-dev.6.4.0.191457

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 (225) hide show
  1. package/CHANGELOG.md +114 -0
  2. package/README.md +4 -3
  3. package/dist/defaultMap.d.ts +1 -1
  4. package/dist/defaultMap.d.ts.map +1 -1
  5. package/dist/defaultMap.js +9 -10
  6. package/dist/defaultMap.js.map +1 -1
  7. package/dist/defaultMapInterfaces.d.ts +1 -1
  8. package/dist/defaultMapInterfaces.d.ts.map +1 -1
  9. package/dist/defaultMapInterfaces.js.map +1 -1
  10. package/dist/index.d.ts +3 -3
  11. package/dist/index.d.ts.map +1 -1
  12. package/dist/index.js +12 -9
  13. package/dist/index.js.map +1 -1
  14. package/dist/intervalCollection.d.ts +11 -424
  15. package/dist/intervalCollection.d.ts.map +1 -1
  16. package/dist/intervalCollection.js +74 -815
  17. package/dist/intervalCollection.js.map +1 -1
  18. package/dist/intervalIndex/endpointInRangeIndex.d.ts +20 -0
  19. package/dist/intervalIndex/endpointInRangeIndex.d.ts.map +1 -0
  20. package/dist/intervalIndex/endpointInRangeIndex.js +60 -0
  21. package/dist/intervalIndex/endpointInRangeIndex.js.map +1 -0
  22. package/dist/intervalIndex/endpointIndex.d.ts +21 -0
  23. package/dist/intervalIndex/endpointIndex.d.ts.map +1 -0
  24. package/dist/intervalIndex/endpointIndex.js +42 -0
  25. package/dist/intervalIndex/endpointIndex.js.map +1 -0
  26. package/dist/intervalIndex/idIntervalIndex.d.ts +12 -0
  27. package/dist/intervalIndex/idIntervalIndex.d.ts.map +1 -0
  28. package/dist/intervalIndex/idIntervalIndex.js +41 -0
  29. package/dist/intervalIndex/idIntervalIndex.js.map +1 -0
  30. package/dist/intervalIndex/index.d.ts +5 -0
  31. package/dist/intervalIndex/index.d.ts.map +1 -1
  32. package/dist/intervalIndex/index.js +9 -1
  33. package/dist/intervalIndex/index.js.map +1 -1
  34. package/dist/intervalIndex/intervalIndex.d.ts +29 -0
  35. package/dist/intervalIndex/intervalIndex.d.ts.map +1 -0
  36. package/dist/intervalIndex/intervalIndex.js +7 -0
  37. package/dist/intervalIndex/intervalIndex.js.map +1 -0
  38. package/dist/intervalIndex/intervalIndexUtils.d.ts +17 -0
  39. package/dist/intervalIndex/intervalIndexUtils.d.ts.map +1 -0
  40. package/dist/intervalIndex/intervalIndexUtils.js +22 -0
  41. package/dist/intervalIndex/intervalIndexUtils.js.map +1 -0
  42. package/dist/intervalIndex/overlappingIntervalsIndex.d.ts +2 -1
  43. package/dist/intervalIndex/overlappingIntervalsIndex.d.ts.map +1 -1
  44. package/dist/intervalIndex/overlappingIntervalsIndex.js +3 -3
  45. package/dist/intervalIndex/overlappingIntervalsIndex.js.map +1 -1
  46. package/dist/intervalIndex/overlappingSequenceIntervalsIndex.js +5 -5
  47. package/dist/intervalIndex/overlappingSequenceIntervalsIndex.js.map +1 -1
  48. package/dist/intervalIndex/sequenceIntervalIndexes.d.ts +1 -1
  49. package/dist/intervalIndex/sequenceIntervalIndexes.d.ts.map +1 -1
  50. package/dist/intervalIndex/sequenceIntervalIndexes.js.map +1 -1
  51. package/dist/intervalIndex/startpointInRangeIndex.d.ts +20 -0
  52. package/dist/intervalIndex/startpointInRangeIndex.d.ts.map +1 -0
  53. package/dist/intervalIndex/startpointInRangeIndex.js +62 -0
  54. package/dist/intervalIndex/startpointInRangeIndex.js.map +1 -0
  55. package/dist/intervalTree.d.ts +2 -56
  56. package/dist/intervalTree.d.ts.map +1 -1
  57. package/dist/intervalTree.js +2 -11
  58. package/dist/intervalTree.js.map +1 -1
  59. package/dist/intervals/index.d.ts +8 -0
  60. package/dist/intervals/index.d.ts.map +1 -0
  61. package/dist/intervals/index.js +23 -0
  62. package/dist/intervals/index.js.map +1 -0
  63. package/dist/intervals/interval.d.ts +88 -0
  64. package/dist/intervals/interval.d.ts.map +1 -0
  65. package/dist/intervals/interval.js +180 -0
  66. package/dist/intervals/interval.js.map +1 -0
  67. package/dist/intervals/intervalUtils.d.ts +200 -0
  68. package/dist/intervals/intervalUtils.d.ts.map +1 -0
  69. package/dist/intervals/intervalUtils.js +79 -0
  70. package/dist/intervals/intervalUtils.js.map +1 -0
  71. package/dist/intervals/sequenceInterval.d.ts +132 -0
  72. package/dist/intervals/sequenceInterval.d.ts.map +1 -0
  73. package/dist/intervals/sequenceInterval.js +313 -0
  74. package/dist/intervals/sequenceInterval.js.map +1 -0
  75. package/dist/packageVersion.d.ts +1 -1
  76. package/dist/packageVersion.js +1 -1
  77. package/dist/packageVersion.js.map +1 -1
  78. package/dist/revertibles.d.ts +1 -1
  79. package/dist/revertibles.d.ts.map +1 -1
  80. package/dist/revertibles.js +45 -52
  81. package/dist/revertibles.js.map +1 -1
  82. package/dist/sequence.d.ts +33 -4
  83. package/dist/sequence.d.ts.map +1 -1
  84. package/dist/sequence.js +91 -47
  85. package/dist/sequence.js.map +1 -1
  86. package/dist/sequenceDeltaEvent.d.ts +8 -3
  87. package/dist/sequenceDeltaEvent.d.ts.map +1 -1
  88. package/dist/sequenceDeltaEvent.js +3 -4
  89. package/dist/sequenceDeltaEvent.js.map +1 -1
  90. package/dist/sharedIntervalCollection.d.ts +2 -1
  91. package/dist/sharedIntervalCollection.d.ts.map +1 -1
  92. package/dist/sharedIntervalCollection.js +2 -2
  93. package/dist/sharedIntervalCollection.js.map +1 -1
  94. package/dist/sharedSequence.d.ts +9 -0
  95. package/dist/sharedSequence.d.ts.map +1 -1
  96. package/dist/sharedSequence.js +9 -6
  97. package/dist/sharedSequence.js.map +1 -1
  98. package/dist/sharedString.d.ts.map +1 -1
  99. package/dist/sharedString.js +9 -29
  100. package/dist/sharedString.js.map +1 -1
  101. package/lib/defaultMap.d.ts +1 -1
  102. package/lib/defaultMap.d.ts.map +1 -1
  103. package/lib/defaultMap.js +5 -6
  104. package/lib/defaultMap.js.map +1 -1
  105. package/lib/defaultMapInterfaces.d.ts +1 -1
  106. package/lib/defaultMapInterfaces.d.ts.map +1 -1
  107. package/lib/defaultMapInterfaces.js.map +1 -1
  108. package/lib/index.d.ts +3 -3
  109. package/lib/index.d.ts.map +1 -1
  110. package/lib/index.js +3 -2
  111. package/lib/index.js.map +1 -1
  112. package/lib/intervalCollection.d.ts +11 -424
  113. package/lib/intervalCollection.d.ts.map +1 -1
  114. package/lib/intervalCollection.js +43 -776
  115. package/lib/intervalCollection.js.map +1 -1
  116. package/lib/intervalIndex/endpointInRangeIndex.d.ts +20 -0
  117. package/lib/intervalIndex/endpointInRangeIndex.d.ts.map +1 -0
  118. package/lib/intervalIndex/endpointInRangeIndex.js +56 -0
  119. package/lib/intervalIndex/endpointInRangeIndex.js.map +1 -0
  120. package/lib/intervalIndex/endpointIndex.d.ts +21 -0
  121. package/lib/intervalIndex/endpointIndex.d.ts.map +1 -0
  122. package/lib/intervalIndex/endpointIndex.js +38 -0
  123. package/lib/intervalIndex/endpointIndex.js.map +1 -0
  124. package/lib/intervalIndex/idIntervalIndex.d.ts +12 -0
  125. package/lib/intervalIndex/idIntervalIndex.d.ts.map +1 -0
  126. package/lib/intervalIndex/idIntervalIndex.js +37 -0
  127. package/lib/intervalIndex/idIntervalIndex.js.map +1 -0
  128. package/lib/intervalIndex/index.d.ts +5 -0
  129. package/lib/intervalIndex/index.d.ts.map +1 -1
  130. package/lib/intervalIndex/index.js +4 -0
  131. package/lib/intervalIndex/index.js.map +1 -1
  132. package/lib/intervalIndex/intervalIndex.d.ts +29 -0
  133. package/lib/intervalIndex/intervalIndex.d.ts.map +1 -0
  134. package/lib/intervalIndex/intervalIndex.js +6 -0
  135. package/lib/intervalIndex/intervalIndex.js.map +1 -0
  136. package/lib/intervalIndex/intervalIndexUtils.d.ts +17 -0
  137. package/lib/intervalIndex/intervalIndexUtils.d.ts.map +1 -0
  138. package/lib/intervalIndex/intervalIndexUtils.js +18 -0
  139. package/lib/intervalIndex/intervalIndexUtils.js.map +1 -0
  140. package/lib/intervalIndex/overlappingIntervalsIndex.d.ts +2 -1
  141. package/lib/intervalIndex/overlappingIntervalsIndex.d.ts.map +1 -1
  142. package/lib/intervalIndex/overlappingIntervalsIndex.js +1 -1
  143. package/lib/intervalIndex/overlappingIntervalsIndex.js.map +1 -1
  144. package/lib/intervalIndex/overlappingSequenceIntervalsIndex.js +1 -1
  145. package/lib/intervalIndex/overlappingSequenceIntervalsIndex.js.map +1 -1
  146. package/lib/intervalIndex/sequenceIntervalIndexes.d.ts +1 -1
  147. package/lib/intervalIndex/sequenceIntervalIndexes.d.ts.map +1 -1
  148. package/lib/intervalIndex/sequenceIntervalIndexes.js.map +1 -1
  149. package/lib/intervalIndex/startpointInRangeIndex.d.ts +20 -0
  150. package/lib/intervalIndex/startpointInRangeIndex.d.ts.map +1 -0
  151. package/lib/intervalIndex/startpointInRangeIndex.js +58 -0
  152. package/lib/intervalIndex/startpointInRangeIndex.js.map +1 -0
  153. package/lib/intervalTree.d.ts +2 -56
  154. package/lib/intervalTree.d.ts.map +1 -1
  155. package/lib/intervalTree.js +2 -11
  156. package/lib/intervalTree.js.map +1 -1
  157. package/lib/intervals/index.d.ts +8 -0
  158. package/lib/intervals/index.d.ts.map +1 -0
  159. package/lib/intervals/index.js +8 -0
  160. package/lib/intervals/index.js.map +1 -0
  161. package/lib/intervals/interval.d.ts +88 -0
  162. package/lib/intervals/interval.d.ts.map +1 -0
  163. package/lib/intervals/interval.js +175 -0
  164. package/lib/intervals/interval.js.map +1 -0
  165. package/lib/intervals/intervalUtils.d.ts +200 -0
  166. package/lib/intervals/intervalUtils.d.ts.map +1 -0
  167. package/lib/intervals/intervalUtils.js +74 -0
  168. package/lib/intervals/intervalUtils.js.map +1 -0
  169. package/lib/intervals/sequenceInterval.d.ts +132 -0
  170. package/lib/intervals/sequenceInterval.d.ts.map +1 -0
  171. package/lib/intervals/sequenceInterval.js +305 -0
  172. package/lib/intervals/sequenceInterval.js.map +1 -0
  173. package/lib/packageVersion.d.ts +1 -1
  174. package/lib/packageVersion.js +1 -1
  175. package/lib/packageVersion.js.map +1 -1
  176. package/lib/revertibles.d.ts +1 -1
  177. package/lib/revertibles.d.ts.map +1 -1
  178. package/lib/revertibles.js +28 -35
  179. package/lib/revertibles.js.map +1 -1
  180. package/lib/sequence.d.ts +33 -4
  181. package/lib/sequence.d.ts.map +1 -1
  182. package/lib/sequence.js +86 -41
  183. package/lib/sequence.js.map +1 -1
  184. package/lib/sequenceDeltaEvent.d.ts +8 -3
  185. package/lib/sequenceDeltaEvent.d.ts.map +1 -1
  186. package/lib/sequenceDeltaEvent.js +2 -3
  187. package/lib/sequenceDeltaEvent.js.map +1 -1
  188. package/lib/sharedIntervalCollection.d.ts +2 -1
  189. package/lib/sharedIntervalCollection.d.ts.map +1 -1
  190. package/lib/sharedIntervalCollection.js +1 -1
  191. package/lib/sharedIntervalCollection.js.map +1 -1
  192. package/lib/sharedSequence.d.ts +9 -0
  193. package/lib/sharedSequence.d.ts.map +1 -1
  194. package/lib/sharedSequence.js +8 -5
  195. package/lib/sharedSequence.js.map +1 -1
  196. package/lib/sharedString.d.ts.map +1 -1
  197. package/lib/sharedString.js +9 -29
  198. package/lib/sharedString.js.map +1 -1
  199. package/package.json +29 -32
  200. package/src/defaultMap.ts +2 -1
  201. package/src/defaultMapInterfaces.ts +1 -1
  202. package/src/index.ts +18 -12
  203. package/src/intervalCollection.ts +42 -1225
  204. package/src/intervalIndex/endpointInRangeIndex.ts +104 -0
  205. package/src/intervalIndex/endpointIndex.ts +78 -0
  206. package/src/intervalIndex/idIntervalIndex.ts +58 -0
  207. package/src/intervalIndex/index.ts +5 -0
  208. package/src/intervalIndex/intervalIndex.ts +31 -0
  209. package/src/intervalIndex/intervalIndexUtils.ts +27 -0
  210. package/src/intervalIndex/overlappingIntervalsIndex.ts +2 -6
  211. package/src/intervalIndex/overlappingSequenceIntervalsIndex.ts +1 -1
  212. package/src/intervalIndex/sequenceIntervalIndexes.ts +1 -1
  213. package/src/intervalIndex/startpointInRangeIndex.ts +109 -0
  214. package/src/intervalTree.ts +3 -75
  215. package/src/intervals/index.ts +25 -0
  216. package/src/intervals/interval.ts +230 -0
  217. package/src/intervals/intervalUtils.ts +256 -0
  218. package/src/intervals/sequenceInterval.ts +494 -0
  219. package/src/packageVersion.ts +1 -1
  220. package/src/revertibles.ts +24 -13
  221. package/src/sequence.ts +100 -35
  222. package/src/sequenceDeltaEvent.ts +12 -4
  223. package/src/sharedIntervalCollection.ts +2 -3
  224. package/src/sharedSequence.ts +11 -5
  225. package/src/sharedString.ts +8 -25
@@ -5,35 +5,26 @@
5
5
 
6
6
  /* eslint-disable no-bitwise */
7
7
 
8
- import { assert, TypedEventEmitter } from "@fluidframework/common-utils";
9
- import { IEvent } from "@fluidframework/common-definitions";
10
- import { UsageError } from "@fluidframework/container-utils";
8
+ import { TypedEventEmitter } from "@fluid-internal/client-utils";
9
+ import { assert } from "@fluidframework/core-utils";
10
+ import { IEvent } from "@fluidframework/core-interfaces";
11
11
  import {
12
12
  addProperties,
13
13
  Client,
14
- compareReferencePositions,
15
14
  createMap,
16
15
  getSlideToSegoff,
17
- ICombiningOp,
18
16
  ISegment,
19
17
  MergeTreeDeltaType,
20
- minReferencePosition,
21
- PropertiesManager,
22
18
  PropertySet,
23
- RedBlackTree,
24
19
  LocalReferencePosition,
25
20
  ReferenceType,
26
21
  refTypeIncludesFlag,
27
22
  reservedRangeLabelsKey,
28
23
  UnassignedSequenceNumber,
29
- maxReferencePosition,
30
- createDetachedLocalReferencePosition,
31
24
  DetachedReferencePosition,
32
- SlidingPreference,
33
- PropertyAction,
34
25
  } from "@fluidframework/merge-tree";
35
26
  import { ISequencedDocumentMessage } from "@fluidframework/protocol-definitions";
36
- import { LoggingError } from "@fluidframework/telemetry-utils";
27
+ import { LoggingError, UsageError } from "@fluidframework/telemetry-utils";
37
28
  import { v4 as uuid } from "uuid";
38
29
  import {
39
30
  IMapMessageLocalMetadata,
@@ -44,87 +35,35 @@ import {
44
35
  IValueTypeOperationValue,
45
36
  SequenceOptions,
46
37
  } from "./defaultMapInterfaces";
47
- import { IInterval, IntervalConflictResolver } from "./intervalTree";
48
- import { IOverlappingIntervalsIndex, createOverlappingIntervalsIndex } from "./intervalIndex";
38
+ import {
39
+ CompressedSerializedInterval,
40
+ IIntervalHelpers,
41
+ Interval,
42
+ IntervalOpType,
43
+ IntervalStickiness,
44
+ IntervalType,
45
+ ISerializableInterval,
46
+ ISerializedInterval,
47
+ SequenceInterval,
48
+ SerializedIntervalDelta,
49
+ createPositionReferenceFromSegoff,
50
+ endReferenceSlidingPreference,
51
+ startReferenceSlidingPreference,
52
+ sequenceIntervalHelpers,
53
+ createInterval,
54
+ } from "./intervals";
55
+ import {
56
+ IEndpointIndex,
57
+ IIdIntervalIndex,
58
+ IOverlappingIntervalsIndex,
59
+ IntervalIndex,
60
+ createEndpointIndex,
61
+ createIdIntervalIndex,
62
+ createOverlappingIntervalsIndex,
63
+ } from "./intervalIndex";
49
64
 
50
65
  const reservedIntervalIdKey = "intervalId";
51
66
 
52
- /**
53
- * Values are used in persisted formats (ops) and revertibles.
54
- * @alpha
55
- */
56
- export const IntervalOpType = {
57
- ADD: "add",
58
- DELETE: "delete",
59
- CHANGE: "change",
60
- PROPERTY_CHANGED: "propertyChanged",
61
- POSITION_REMOVE: "positionRemove",
62
- } as const;
63
-
64
- export enum IntervalType {
65
- Simple = 0x0,
66
- Nest = 0x1,
67
-
68
- /**
69
- * SlideOnRemove indicates that the ends of the interval will slide if the segment
70
- * they reference is removed and acked.
71
- * See `packages\dds\merge-tree\docs\REFERENCEPOSITIONS.md` for details
72
- * SlideOnRemove is the default interval behavior and does not need to be specified.
73
- */
74
- SlideOnRemove = 0x2, // SlideOnRemove is default behavior - all intervals are SlideOnRemove
75
-
76
- /**
77
- * A temporary interval, used internally
78
- * @internal
79
- */
80
- Transient = 0x4,
81
- }
82
-
83
- /**
84
- * Serialized object representation of an interval.
85
- * This representation is used for ops that create or change intervals.
86
- * @internal
87
- */
88
- export interface ISerializedInterval {
89
- /**
90
- * Sequence number at which `start` and `end` should be interpreted
91
- *
92
- * @remarks - It's unclear that this is necessary to store here.
93
- * This should just be the refSeq on the op that modified the interval, which should be available via other means.
94
- * At the time of writing, it's not plumbed through to the reconnect/rebase code, however, which does need it.
95
- */
96
- sequenceNumber: number;
97
- /** Start position of the interval */
98
- start: number;
99
- /** End position of the interval */
100
- end: number;
101
- /** Interval type to create */
102
- intervalType: IntervalType;
103
- stickiness?: IntervalStickiness;
104
- /** Any properties the interval has */
105
- properties?: PropertySet;
106
- }
107
-
108
- /**
109
- * Represents a change that should be applied to an existing interval.
110
- * Changes can modify any of start/end/properties, with `undefined` signifying no change should be made.
111
- * @internal
112
- */
113
- export type SerializedIntervalDelta = Omit<ISerializedInterval, "start" | "end" | "properties"> &
114
- Partial<Pick<ISerializedInterval, "start" | "end" | "properties">>;
115
-
116
- /**
117
- * A size optimization to avoid redundantly storing keys when serializing intervals
118
- * as JSON for summaries.
119
- *
120
- * Intervals are of the format:
121
- *
122
- * [start, end, sequenceNumber, intervalType, properties, stickiness?]
123
- */
124
- export type CompressedSerializedInterval =
125
- | [number, number, number, IntervalType, PropertySet, IntervalStickiness]
126
- | [number, number, number, IntervalType, PropertySet];
127
-
128
67
  export interface ISerializedIntervalCollectionV2 {
129
68
  label: string;
130
69
  version: 2;
@@ -173,1091 +112,20 @@ function compressInterval(interval: ISerializedInterval): CompressedSerializedIn
173
112
  return base;
174
113
  }
175
114
 
176
- function startReferenceSlidingPreference(stickiness: IntervalStickiness): SlidingPreference {
177
- // if any start stickiness, prefer sliding backwards
178
- return (stickiness & IntervalStickiness.START) !== 0
179
- ? SlidingPreference.BACKWARD
180
- : SlidingPreference.FORWARD;
181
- }
182
-
183
- function endReferenceSlidingPreference(stickiness: IntervalStickiness): SlidingPreference {
184
- // if any end stickiness, prefer sliding forwards
185
- return (stickiness & IntervalStickiness.END) !== 0
186
- ? SlidingPreference.FORWARD
187
- : SlidingPreference.BACKWARD;
188
- }
189
-
190
- export interface ISerializableInterval extends IInterval {
191
- /** Serializable bag of properties associated with the interval. */
192
- properties: PropertySet;
193
- /** @internal */
194
- propertyManager: PropertiesManager;
195
- /** @internal */
196
- serialize(): ISerializedInterval;
197
- /** @internal */
198
- addProperties(
199
- props: PropertySet,
200
- collaborating?: boolean,
201
- seq?: number,
202
- ): PropertySet | undefined;
203
- /**
204
- * Gets the id associated with this interval.
205
- * When the interval is used as part of an interval collection, this id can be used to modify or remove the
206
- * interval.
207
- * @remarks - This signature includes `undefined` strictly for backwards-compatibility reasons, as older versions
208
- * of Fluid didn't always write interval ids.
209
- */
210
- getIntervalId(): string | undefined;
211
- }
212
-
213
- /**
214
- * @sealed
215
- */
216
- export interface IIntervalHelpers<TInterval extends ISerializableInterval> {
217
- compareEnds(a: TInterval, b: TInterval): number;
218
- compareStarts?(a: TInterval, b: TInterval): number;
219
- /**
220
- *
221
- * @param label - label of the interval collection this interval is being added to. This parameter is
222
- * irrelevant for transient intervals.
223
- * @param start - numerical start position of the interval
224
- * @param end - numerical end position of the interval
225
- * @param client - client creating the interval
226
- * @param intervalType - Type of interval to create. Default is SlideOnRemove
227
- * @param op - If this create came from a remote client, op that created it. Default is undefined (i.e. local)
228
- * @param fromSnapshot - If this create came from loading a snapshot. Default is false.
229
- */
230
- create(
231
- label: string,
232
- start: number | undefined,
233
- end: number | undefined,
234
- client: Client | undefined,
235
- intervalType: IntervalType,
236
- op?: ISequencedDocumentMessage,
237
- fromSnapshot?: boolean,
238
- stickiness?: IntervalStickiness,
239
- ): TInterval;
240
- }
241
-
242
- /**
243
- * Determines how an interval should expand when segments are inserted adjacent
244
- * to the range it spans
245
- *
246
- * Note that interval stickiness is currently an experimental feature and must
247
- * be explicitly enabled with the `intervalStickinessEnabled` flag
248
- */
249
- export const IntervalStickiness = {
250
- /**
251
- * Interval does not expand to include adjacent segments
252
- */
253
- NONE: 0b00,
254
-
255
- /**
256
- * Interval expands to include segments inserted adjacent to the start
257
- */
258
- START: 0b01,
259
-
260
- /**
261
- * Interval expands to include segments inserted adjacent to the end
262
- *
263
- * This is the default stickiness
264
- */
265
- END: 0b10,
266
-
267
- /**
268
- * Interval expands to include all segments inserted adjacent to it
269
- */
270
- FULL: 0b11,
271
- } as const;
272
-
273
- /**
274
- * Determines how an interval should expand when segments are inserted adjacent
275
- * to the range it spans
276
- *
277
- * Note that interval stickiness is currently an experimental feature and must
278
- * be explicitly enabled with the `intervalStickinessEnabled` flag
279
- */
280
- export type IntervalStickiness = typeof IntervalStickiness[keyof typeof IntervalStickiness];
281
-
282
- /**
283
- * Serializable interval whose endpoints are plain-old numbers.
284
- */
285
- export class Interval implements ISerializableInterval {
286
- /**
287
- * {@inheritDoc ISerializableInterval.properties}
288
- */
289
- public properties: PropertySet;
290
- /** @internal */
291
- public auxProps: PropertySet[] | undefined;
292
- /**
293
- * {@inheritDoc ISerializableInterval.propertyManager}
294
- * @deprecated - This API was never intended to be public and will be marked internal in a future release.
295
- */
296
- public propertyManager: PropertiesManager;
297
- constructor(public start: number, public end: number, props?: PropertySet) {
298
- this.propertyManager = new PropertiesManager();
299
- this.properties = {};
300
-
301
- if (props) {
302
- this.addProperties(props);
303
- }
304
- }
305
-
306
- /**
307
- * {@inheritDoc ISerializableInterval.getIntervalId}
308
- */
309
- public getIntervalId(): string {
310
- const id = this.properties?.[reservedIntervalIdKey];
311
- assert(id !== undefined, 0x5e1 /* interval ID should not be undefined */);
312
- return `${id}`;
313
- }
314
-
315
- /**
316
- * @returns an array containing any auxiliary property sets added with `addPropertySet`.
317
- */
318
- public getAdditionalPropertySets(): PropertySet[] {
319
- return this.auxProps ?? [];
320
- }
321
-
322
- /**
323
- * Adds an auxiliary set of properties to this interval.
324
- * These properties can be recovered using `getAdditionalPropertySets`
325
- * @param props - set of properties to add
326
- * @remarks - This gets called as part of the default conflict resolver for `IIntervalCollection<Interval>`
327
- * (i.e. non-sequence-based interval collections). However, the additional properties don't get serialized.
328
- * This functionality seems half-baked.
329
- */
330
- public addPropertySet(props: PropertySet) {
331
- if (this.auxProps === undefined) {
332
- this.auxProps = [];
333
- }
334
- this.auxProps.push(props);
335
- }
336
-
337
- /**
338
- * {@inheritDoc ISerializableInterval.serialize}
339
- * @internal
340
- */
341
- public serialize(): ISerializedInterval {
342
- const serializedInterval: ISerializedInterval = {
343
- end: this.end,
344
- intervalType: 0,
345
- sequenceNumber: 0,
346
- start: this.start,
347
- };
348
- if (this.properties) {
349
- serializedInterval.properties = this.properties;
350
- }
351
- return serializedInterval;
352
- }
353
-
354
- /**
355
- * {@inheritDoc IInterval.clone}
356
- */
357
- public clone() {
358
- return new Interval(this.start, this.end, this.properties);
359
- }
360
-
361
- /**
362
- * {@inheritDoc IInterval.compare}
363
- */
364
- public compare(b: Interval) {
365
- const startResult = this.compareStart(b);
366
- if (startResult === 0) {
367
- const endResult = this.compareEnd(b);
368
- if (endResult === 0) {
369
- const thisId = this.getIntervalId();
370
- if (thisId) {
371
- const bId = b.getIntervalId();
372
- if (bId) {
373
- return thisId > bId ? 1 : thisId < bId ? -1 : 0;
374
- }
375
- return 0;
376
- }
377
- return 0;
378
- } else {
379
- return endResult;
380
- }
381
- } else {
382
- return startResult;
383
- }
384
- }
385
-
386
- /**
387
- * {@inheritDoc IInterval.compareStart}
388
- */
389
- public compareStart(b: Interval) {
390
- return this.start - b.start;
391
- }
392
-
393
- /**
394
- * {@inheritDoc IInterval.compareEnd}
395
- */
396
- public compareEnd(b: Interval) {
397
- return this.end - b.end;
398
- }
399
-
400
- /**
401
- * {@inheritDoc IInterval.overlaps}
402
- */
403
- public overlaps(b: Interval) {
404
- const result = this.start <= b.end && this.end >= b.start;
405
- return result;
406
- }
407
-
408
- /**
409
- * {@inheritDoc IInterval.union}
410
- * @deprecated - This API was never intended to be public and will be marked internal in a future release.
411
- */
412
- public union(b: Interval) {
413
- return new Interval(
414
- Math.min(this.start, b.start),
415
- Math.max(this.end, b.end),
416
- this.properties,
417
- );
418
- }
419
-
420
- public getProperties() {
421
- return this.properties;
422
- }
423
-
424
- /**
425
- * {@inheritDoc ISerializableInterval.addProperties}
426
- * @deprecated - This API was never intended to be public and will be marked internal in a future release.
427
- */
428
- public addProperties(
429
- newProps: PropertySet,
430
- collaborating: boolean = false,
431
- seq?: number,
432
- op?: ICombiningOp,
433
- ): PropertySet | undefined {
434
- if (newProps) {
435
- this.initializeProperties();
436
- return this.propertyManager.addProperties(
437
- this.properties,
438
- newProps,
439
- op,
440
- seq,
441
- collaborating,
442
- );
443
- }
444
- }
445
-
446
- /**
447
- * {@inheritDoc IInterval.modify}
448
- * @deprecated - This API was never intended to be public and will be marked internal in a future release.
449
- */
450
- public modify(label: string, start: number, end: number, op?: ISequencedDocumentMessage) {
451
- const startPos = start ?? this.start;
452
- const endPos = end ?? this.end;
453
- if (this.start === startPos && this.end === endPos) {
454
- // Return undefined to indicate that no change is necessary.
455
- return;
456
- }
457
- const newInterval = new Interval(startPos, endPos);
458
- if (this.properties) {
459
- newInterval.initializeProperties();
460
- this.propertyManager.copyTo(
461
- this.properties,
462
- newInterval.properties,
463
- newInterval.propertyManager,
464
- );
465
- }
466
- return newInterval;
467
- }
468
-
469
- private initializeProperties(): void {
470
- if (!this.propertyManager) {
471
- this.propertyManager = new PropertiesManager();
472
- }
473
- if (!this.properties) {
474
- this.properties = createMap<any>();
475
- }
476
- }
477
- }
478
-
479
- /**
480
- * Interval implementation whose ends are associated with positions in a mutatable sequence.
481
- * As such, when content is inserted into the middle of the interval, the interval expands to
482
- * include that content.
483
- *
484
- * @remarks - The endpoint's position should be treated exclusively to get reasonable behavior--i.e.
485
- * an interval referring to "hello" in "hello world" should have a start position of 0 and an end
486
- * position of 5.
487
- *
488
- * To see why, consider what happens if "llo wor" is removed from the string to make "held".
489
- * The interval's startpoint remains on the "h" (it isn't altered), but the interval's endpoint
490
- * slides forward to the next unremoved position, which is the "l" in "held".
491
- * Users would generally expect the interval to now refer to "he" (as it is the subset of content
492
- * remaining after the removal), hence the "l" should be excluded.
493
- * If the interval endpoint was treated inclusively, the interval would now refer to "hel", which
494
- * is undesirable.
495
- *
496
- * Since the end of an interval is treated exclusively but cannot be greater than or equal to the
497
- * length of the associated sequence, application models which leverage interval collections should
498
- * consider inserting a marker at the end of the sequence to represent the end of the content.
499
- */
500
- export class SequenceInterval implements ISerializableInterval {
501
- /**
502
- * {@inheritDoc ISerializableInterval.properties}
503
- */
504
- public properties: PropertySet;
505
- /**
506
- * {@inheritDoc ISerializableInterval.propertyManager}
507
- * @deprecated - This API was never intended to be public and will be marked internal in a future release.
508
- */
509
- public propertyManager: PropertiesManager;
510
-
511
- constructor(
512
- private readonly client: Client,
513
- /**
514
- * Start endpoint of this interval.
515
- * @remarks - This endpoint can be resolved into a character position using the SharedString it's a part of.
516
- */
517
- public start: LocalReferencePosition,
518
- /**
519
- * End endpoint of this interval.
520
- * @remarks - This endpoint can be resolved into a character position using the SharedString it's a part of.
521
- */
522
- public end: LocalReferencePosition,
523
- public intervalType: IntervalType,
524
- props?: PropertySet,
525
- public readonly stickiness: IntervalStickiness = IntervalStickiness.END,
526
- ) {
527
- this.propertyManager = new PropertiesManager();
528
- this.properties = {};
529
-
530
- if (props) {
531
- this.addProperties(props);
532
- }
533
- }
534
-
535
- private callbacks?: Record<"beforePositionChange" | "afterPositionChange", () => void>;
536
-
537
- /**
538
- * Subscribes to position change events on this interval if there are no current listeners.
539
- * @internal
540
- */
541
- public addPositionChangeListeners(
542
- beforePositionChange: () => void,
543
- afterPositionChange: () => void,
544
- ): void {
545
- if (this.callbacks === undefined) {
546
- this.callbacks = {
547
- beforePositionChange,
548
- afterPositionChange,
549
- };
550
-
551
- const startCbs = (this.start.callbacks ??= {});
552
- const endCbs = (this.end.callbacks ??= {});
553
- startCbs.beforeSlide = endCbs.beforeSlide = beforePositionChange;
554
- startCbs.afterSlide = endCbs.afterSlide = afterPositionChange;
555
- }
556
- }
557
-
558
- /**
559
- * Removes the currently subscribed position change listeners.
560
- * @internal
561
- */
562
- public removePositionChangeListeners(): void {
563
- if (this.callbacks) {
564
- this.callbacks = undefined;
565
- this.start.callbacks = undefined;
566
- this.end.callbacks = undefined;
567
- }
568
- }
569
-
570
- /**
571
- * {@inheritDoc ISerializableInterval.serialize}
572
- * @internal
573
- */
574
- public serialize(): ISerializedInterval {
575
- const startPosition = this.client.localReferencePositionToPosition(this.start);
576
- const endPosition = this.client.localReferencePositionToPosition(this.end);
577
- const serializedInterval: ISerializedInterval = {
578
- end: endPosition,
579
- intervalType: this.intervalType,
580
- sequenceNumber: this.client.getCurrentSeq(),
581
- start: startPosition,
582
- };
583
-
584
- if (this.properties) {
585
- serializedInterval.properties = this.properties;
586
- }
587
- if (this.stickiness !== IntervalStickiness.END) {
588
- serializedInterval.stickiness = this.stickiness;
589
- }
590
-
591
- return serializedInterval;
592
- }
593
-
594
- /**
595
- * {@inheritDoc IInterval.clone}
596
- */
597
- public clone() {
598
- return new SequenceInterval(
599
- this.client,
600
- this.start,
601
- this.end,
602
- this.intervalType,
603
- this.properties,
604
- this.stickiness,
605
- );
606
- }
607
-
608
- /**
609
- * {@inheritDoc IInterval.compare}
610
- */
611
- public compare(b: SequenceInterval) {
612
- const startResult = this.compareStart(b);
613
- if (startResult === 0) {
614
- const endResult = this.compareEnd(b);
615
- if (endResult === 0) {
616
- const thisId = this.getIntervalId();
617
- if (thisId) {
618
- const bId = b.getIntervalId();
619
- if (bId) {
620
- return thisId > bId ? 1 : thisId < bId ? -1 : 0;
621
- }
622
- return 0;
623
- }
624
- return 0;
625
- } else {
626
- return endResult;
627
- }
628
- } else {
629
- return startResult;
630
- }
631
- }
632
-
633
- /**
634
- * {@inheritDoc IInterval.compareStart}
635
- */
636
- public compareStart(b: SequenceInterval) {
637
- return compareReferencePositions(this.start, b.start);
638
- }
639
-
640
- /**
641
- * {@inheritDoc IInterval.compareEnd}
642
- */
643
- public compareEnd(b: SequenceInterval) {
644
- return compareReferencePositions(this.end, b.end);
645
- }
646
-
647
- /**
648
- * {@inheritDoc IInterval.overlaps}
649
- */
650
- public overlaps(b: SequenceInterval) {
651
- const result =
652
- compareReferencePositions(this.start, b.end) <= 0 &&
653
- compareReferencePositions(this.end, b.start) >= 0;
654
- return result;
655
- }
656
-
657
- /**
658
- * {@inheritDoc ISerializableInterval.getIntervalId}
659
- */
660
- public getIntervalId(): string {
661
- const id = this.properties?.[reservedIntervalIdKey];
662
- assert(id !== undefined, 0x5e2 /* interval ID should not be undefined */);
663
- return `${id}`;
664
- }
665
-
666
- /**
667
- * {@inheritDoc IInterval.union}
668
- * @deprecated - This API was never intended to be public and will be marked internal in a future release.
669
- */
670
- public union(b: SequenceInterval) {
671
- return new SequenceInterval(
672
- this.client,
673
- minReferencePosition(this.start, b.start),
674
- maxReferencePosition(this.end, b.end),
675
- this.intervalType,
676
- );
677
- }
678
-
679
- /**
680
- * {@inheritDoc ISerializableInterval.addProperties}
681
- * @deprecated - This API was never intended to be public and will be marked internal in a future release.
682
- */
683
- public addProperties(
684
- newProps: PropertySet,
685
- collab: boolean = false,
686
- seq?: number,
687
- op?: ICombiningOp,
688
- ): PropertySet | undefined {
689
- this.initializeProperties();
690
- return this.propertyManager.addProperties(this.properties, newProps, op, seq, collab);
691
- }
692
-
693
- /**
694
- * @returns whether this interval overlaps two numerical positions.
695
- */
696
- public overlapsPos(bstart: number, bend: number) {
697
- const startPos = this.client.localReferencePositionToPosition(this.start);
698
- const endPos = this.client.localReferencePositionToPosition(this.end);
699
- return endPos > bstart && startPos < bend;
700
- }
701
-
702
- /**
703
- * {@inheritDoc IInterval.modify}
704
- * @deprecated - This API was never intended to be public and will be marked internal in a future release.
705
- */
706
- public modify(
707
- label: string,
708
- start: number,
709
- end: number,
710
- op?: ISequencedDocumentMessage,
711
- localSeq?: number,
712
- stickiness: IntervalStickiness = IntervalStickiness.END,
713
- ) {
714
- const getRefType = (baseType: ReferenceType): ReferenceType => {
715
- let refType = baseType;
716
- if (op === undefined) {
717
- refType &= ~ReferenceType.SlideOnRemove;
718
- refType |= ReferenceType.StayOnRemove;
719
- }
720
- return refType;
721
- };
722
-
723
- let startRef = this.start;
724
- if (start !== undefined) {
725
- startRef = createPositionReference(
726
- this.client,
727
- start,
728
- getRefType(this.start.refType),
729
- op,
730
- undefined,
731
- localSeq,
732
- startReferenceSlidingPreference(stickiness),
733
- );
734
- if (this.start.properties) {
735
- startRef.addProperties(this.start.properties);
736
- }
737
- }
738
-
739
- let endRef = this.end;
740
- if (end !== undefined) {
741
- endRef = createPositionReference(
742
- this.client,
743
- end,
744
- getRefType(this.end.refType),
745
- op,
746
- undefined,
747
- localSeq,
748
- endReferenceSlidingPreference(stickiness),
749
- );
750
- if (this.end.properties) {
751
- endRef.addProperties(this.end.properties);
752
- }
753
- }
754
-
755
- const newInterval = new SequenceInterval(this.client, startRef, endRef, this.intervalType);
756
- if (this.properties) {
757
- newInterval.initializeProperties();
758
- this.propertyManager.copyTo(
759
- this.properties,
760
- newInterval.properties,
761
- newInterval.propertyManager,
762
- );
763
- }
764
- return newInterval;
765
- }
766
-
767
- private initializeProperties(): void {
768
- if (!this.propertyManager) {
769
- this.propertyManager = new PropertiesManager();
770
- }
771
- if (!this.properties) {
772
- this.properties = createMap<any>();
773
- }
774
- }
775
- }
776
-
777
- export function createPositionReferenceFromSegoff(
778
- client: Client,
779
- segoff: { segment: ISegment | undefined; offset: number | undefined },
780
- refType: ReferenceType,
781
- op?: ISequencedDocumentMessage,
782
- localSeq?: number,
783
- fromSnapshot?: boolean,
784
- slidingPreference?: SlidingPreference,
785
- ): LocalReferencePosition {
786
- if (segoff.segment) {
787
- const ref = client.createLocalReferencePosition(
788
- segoff.segment,
789
- segoff.offset,
790
- refType,
791
- undefined,
792
- slidingPreference,
793
- );
794
- return ref;
795
- }
796
-
797
- // Creating references on detached segments is allowed for:
798
- // - Transient segments
799
- // - References coming from a remote client (location may have been concurrently removed)
800
- // - References being rebased to a new sequence number
801
- // (segment they originally referred to may have been removed with no suitable replacement)
802
- if (
803
- !op &&
804
- !localSeq &&
805
- !fromSnapshot &&
806
- !refTypeIncludesFlag(refType, ReferenceType.Transient)
807
- ) {
808
- throw new UsageError("Non-transient references need segment");
809
- }
810
-
811
- return createDetachedLocalReferencePosition(refType);
812
- }
813
-
814
- function createPositionReference(
815
- client: Client,
816
- pos: number,
817
- refType: ReferenceType,
818
- op?: ISequencedDocumentMessage,
819
- fromSnapshot?: boolean,
820
- localSeq?: number,
821
- slidingPreference?: SlidingPreference,
822
- ): LocalReferencePosition {
823
- let segoff;
824
- if (op) {
825
- assert(
826
- (refType & ReferenceType.SlideOnRemove) !== 0,
827
- 0x2f5 /* op create references must be SlideOnRemove */,
828
- );
829
- segoff = client.getContainingSegment(pos, {
830
- referenceSequenceNumber: op.referenceSequenceNumber,
831
- clientId: op.clientId,
832
- });
833
- segoff = getSlideToSegoff(segoff);
834
- } else {
835
- assert(
836
- (refType & ReferenceType.SlideOnRemove) === 0 || !!fromSnapshot,
837
- 0x2f6 /* SlideOnRemove references must be op created */,
838
- );
839
- segoff = client.getContainingSegment(pos, undefined, localSeq);
840
- }
841
-
842
- return createPositionReferenceFromSegoff(
843
- client,
844
- segoff,
845
- refType,
846
- op,
847
- localSeq,
848
- fromSnapshot,
849
- slidingPreference,
850
- );
851
- }
852
-
853
- export function createSequenceInterval(
854
- label: string,
855
- start: number,
856
- end: number,
857
- client: Client,
858
- intervalType: IntervalType,
859
- op?: ISequencedDocumentMessage,
860
- fromSnapshot?: boolean,
861
- stickiness: IntervalStickiness = IntervalStickiness.END,
862
- ): SequenceInterval {
863
- let beginRefType = ReferenceType.RangeBegin;
864
- let endRefType = ReferenceType.RangeEnd;
865
- if (intervalType === IntervalType.Transient) {
866
- beginRefType = ReferenceType.Transient;
867
- endRefType = ReferenceType.Transient;
868
- } else {
869
- if (intervalType === IntervalType.Nest) {
870
- beginRefType = ReferenceType.NestBegin;
871
- endRefType = ReferenceType.NestEnd;
872
- }
873
- // All non-transient interval references must eventually be SlideOnRemove
874
- // To ensure eventual consistency, they must start as StayOnRemove when
875
- // pending (created locally and creation op is not acked)
876
- if (op || fromSnapshot) {
877
- beginRefType |= ReferenceType.SlideOnRemove;
878
- endRefType |= ReferenceType.SlideOnRemove;
879
- } else {
880
- beginRefType |= ReferenceType.StayOnRemove;
881
- endRefType |= ReferenceType.StayOnRemove;
882
- }
883
- }
884
-
885
- const startLref = createPositionReference(
886
- client,
887
- start,
888
- beginRefType,
889
- op,
890
- fromSnapshot,
891
- undefined,
892
- startReferenceSlidingPreference(stickiness),
893
- );
894
-
895
- const endLref = createPositionReference(
896
- client,
897
- end,
898
- endRefType,
899
- op,
900
- fromSnapshot,
901
- undefined,
902
- endReferenceSlidingPreference(stickiness),
903
- );
904
-
905
- const rangeProp = {
906
- [reservedRangeLabelsKey]: [label],
907
- };
908
- startLref.addProperties(rangeProp);
909
- endLref.addProperties(rangeProp);
910
-
911
- const ival = new SequenceInterval(
912
- client,
913
- startLref,
914
- endLref,
915
- intervalType,
916
- rangeProp,
917
- stickiness,
918
- );
919
- return ival;
920
- }
921
-
922
115
  export function createIntervalIndex() {
923
116
  const helpers: IIntervalHelpers<Interval> = {
924
- compareEnds: compareIntervalEnds,
117
+ compareEnds: (a: Interval, b: Interval) => a.end - b.end,
925
118
  create: createInterval,
926
119
  };
927
120
  const lc = new LocalIntervalCollection<Interval>(undefined as any as Client, "", helpers);
928
121
  return lc;
929
122
  }
930
123
 
931
- /**
932
- * Collection of intervals.
933
- *
934
- * Implementers of this interface will typically implement additional APIs to support efficiently querying a collection
935
- * of intervals in some manner, for example:
936
- * - "find all intervals with start endpoint between these two points"
937
- * - "find all intervals which overlap this range"
938
- * etc.
939
- */
940
- export interface IntervalIndex<TInterval extends ISerializableInterval> {
941
- /**
942
- * Adds an interval to the index.
943
- * @remarks - Application code should never need to invoke this method on their index for production scenarios:
944
- * Fluid handles adding and removing intervals from an index in response to sequence or interval changes.
945
- */
946
- add(interval: TInterval): void;
947
-
948
- /**
949
- * Removes an interval from the index.
950
- * @remarks - Application code should never need to invoke this method on their index for production scenarios:
951
- * Fluid handles adding and removing intervals from an index in response to sequence or interval changes.
952
- */
953
- remove(interval: TInterval): void;
954
- }
955
-
956
- class IdIntervalIndex<TInterval extends ISerializableInterval>
957
- implements IntervalIndex<TInterval>, Iterable<TInterval>
958
- {
959
- private readonly intervalIdMap: Map<string, TInterval> = new Map();
960
-
961
- public add(interval: TInterval) {
962
- const id = interval.getIntervalId();
963
- assert(
964
- id !== undefined,
965
- 0x2c0 /* "ID must be created before adding interval to collection" */,
966
- );
967
- // Make the ID immutable.
968
- Object.defineProperty(interval.properties, reservedIntervalIdKey, {
969
- configurable: false,
970
- enumerable: true,
971
- writable: false,
972
- });
973
- this.intervalIdMap.set(id, interval);
974
- }
975
-
976
- public remove(interval: TInterval) {
977
- const id = interval.getIntervalId();
978
- assert(id !== undefined, 0x311 /* expected id to exist on interval */);
979
- this.intervalIdMap.delete(id);
980
- }
981
-
982
- public getIntervalById(id: string) {
983
- return this.intervalIdMap.get(id);
984
- }
985
-
986
- public [Symbol.iterator]() {
987
- return this.intervalIdMap.values();
988
- }
989
- }
990
-
991
- class EndpointIndex<TInterval extends ISerializableInterval> implements IntervalIndex<TInterval> {
992
- private readonly endIntervalTree: RedBlackTree<TInterval, TInterval>;
993
-
994
- constructor(
995
- private readonly client: Client,
996
- private readonly helpers: IIntervalHelpers<TInterval>,
997
- ) {
998
- // eslint-disable-next-line @typescript-eslint/unbound-method
999
- this.endIntervalTree = new RedBlackTree<TInterval, TInterval>(helpers.compareEnds);
1000
- }
1001
-
1002
- public previousInterval(pos: number) {
1003
- const transientInterval = this.helpers.create(
1004
- "transient",
1005
- pos,
1006
- pos,
1007
- this.client,
1008
- IntervalType.Transient,
1009
- );
1010
- const rbNode = this.endIntervalTree.floor(transientInterval);
1011
- if (rbNode) {
1012
- return rbNode.data;
1013
- }
1014
- }
1015
-
1016
- public nextInterval(pos: number) {
1017
- const transientInterval = this.helpers.create(
1018
- "transient",
1019
- pos,
1020
- pos,
1021
- this.client,
1022
- IntervalType.Transient,
1023
- );
1024
- const rbNode = this.endIntervalTree.ceil(transientInterval);
1025
- if (rbNode) {
1026
- return rbNode.data;
1027
- }
1028
- }
1029
-
1030
- public add(interval: TInterval): void {
1031
- this.endIntervalTree.put(interval, interval);
1032
- }
1033
-
1034
- public remove(interval: TInterval): void {
1035
- this.endIntervalTree.remove(interval);
1036
- }
1037
- }
1038
-
1039
- /**
1040
- * Collection of intervals.
1041
- *
1042
- * Provide additional APIs to support efficiently querying a collection of intervals whose endpoints fall within a specified range.
1043
- */
1044
- export interface IEndpointInRangeIndex<TInterval extends ISerializableInterval>
1045
- extends IntervalIndex<TInterval> {
1046
- /**
1047
- * @returns an array of all intervals contained in this collection whose endpoints locate in the range [start, end] (includes both ends)
1048
- */
1049
- findIntervalsWithEndpointInRange(start: number, end: number);
1050
- }
1051
-
1052
- /**
1053
- * Collection of intervals.
1054
- *
1055
- * Provide additional APIs to support efficiently querying a collection of intervals whose startpoints fall within a specified range.
1056
- */
1057
- export interface IStartpointInRangeIndex<TInterval extends ISerializableInterval>
1058
- extends IntervalIndex<TInterval> {
1059
- /**
1060
- * @returns an array of all intervals contained in this collection whose startpoints locate in the range [start, end] (includes both ends)
1061
- */
1062
- findIntervalsWithStartpointInRange(start: number, end: number);
1063
- }
1064
-
1065
- /**
1066
- * Interface for intervals that have comparison override properties.
1067
- */
1068
- const forceCompare = Symbol();
1069
-
1070
- interface HasComparisonOverride {
1071
- [forceCompare]: number;
1072
- }
1073
-
1074
- /**
1075
- * Compares two objects based on their comparison override properties.
1076
- * @returns A number indicating the order of the intervals (negative for a is lower than b, 0 for tie, positive for a is greater than b).
1077
- */
1078
- function compareOverrideables(
1079
- a: Partial<HasComparisonOverride>,
1080
- b: Partial<HasComparisonOverride>,
1081
- ): number {
1082
- const forceCompareA = a[forceCompare] ?? 0;
1083
- const forceCompareB = b[forceCompare] ?? 0;
1084
-
1085
- return forceCompareA - forceCompareB;
1086
- }
1087
-
1088
- class EndpointInRangeIndex<TInterval extends ISerializableInterval>
1089
- implements IEndpointInRangeIndex<TInterval>
1090
- {
1091
- private readonly intervalTree;
1092
-
1093
- constructor(
1094
- private readonly helpers: IIntervalHelpers<TInterval>,
1095
- private readonly client: Client,
1096
- ) {
1097
- this.intervalTree = new RedBlackTree<TInterval, TInterval>((a: TInterval, b: TInterval) => {
1098
- const compareEndsResult = helpers.compareEnds(a, b);
1099
- if (compareEndsResult !== 0) {
1100
- return compareEndsResult;
1101
- }
1102
-
1103
- const overrideablesComparison = compareOverrideables(
1104
- a as Partial<HasComparisonOverride>,
1105
- b as Partial<HasComparisonOverride>,
1106
- );
1107
- if (overrideablesComparison !== 0) {
1108
- return overrideablesComparison;
1109
- }
1110
-
1111
- const aId = a.getIntervalId();
1112
- const bId = b.getIntervalId();
1113
- if (aId !== undefined && bId !== undefined) {
1114
- return aId.localeCompare(bId);
1115
- }
1116
- return 0;
1117
- });
1118
- }
1119
-
1120
- public add(interval: TInterval): void {
1121
- this.intervalTree.put(interval, interval);
1122
- }
1123
-
1124
- public remove(interval: TInterval): void {
1125
- this.intervalTree.remove(interval);
1126
- }
1127
-
1128
- public findIntervalsWithEndpointInRange(start: number, end: number) {
1129
- if (start <= 0 || start > end || this.intervalTree.isEmpty()) {
1130
- return [];
1131
- }
1132
- const results: TInterval[] = [];
1133
- const action: PropertyAction<TInterval, TInterval> = (node) => {
1134
- results.push(node.data);
1135
- return true;
1136
- };
1137
-
1138
- const transientStartInterval = this.helpers.create(
1139
- "transient",
1140
- start,
1141
- start,
1142
- this.client,
1143
- IntervalType.Transient,
1144
- );
1145
-
1146
- const transientEndInterval = this.helpers.create(
1147
- "transient",
1148
- end,
1149
- end,
1150
- this.client,
1151
- IntervalType.Transient,
1152
- );
1153
-
1154
- // Add comparison overrides to the transient intervals
1155
- (transientStartInterval as Partial<HasComparisonOverride>)[forceCompare] = -1;
1156
- (transientEndInterval as Partial<HasComparisonOverride>)[forceCompare] = 1;
1157
-
1158
- this.intervalTree.mapRange(action, results, transientStartInterval, transientEndInterval);
1159
- return results;
1160
- }
1161
- }
1162
-
1163
- class StartpointInRangeIndex<TInterval extends ISerializableInterval>
1164
- implements IStartpointInRangeIndex<TInterval>
1165
- {
1166
- private readonly intervalTree;
1167
-
1168
- constructor(
1169
- private readonly helpers: IIntervalHelpers<TInterval>,
1170
- private readonly client: Client,
1171
- ) {
1172
- this.intervalTree = new RedBlackTree<TInterval, TInterval>((a: TInterval, b: TInterval) => {
1173
- assert(
1174
- typeof helpers.compareStarts === "function",
1175
- 0x6d1 /* compareStarts does not exist in the helpers */,
1176
- );
1177
-
1178
- const compareStartsResult = helpers.compareStarts(a, b);
1179
- if (compareStartsResult !== 0) {
1180
- return compareStartsResult;
1181
- }
1182
-
1183
- const overrideablesComparison = compareOverrideables(
1184
- a as Partial<HasComparisonOverride>,
1185
- b as Partial<HasComparisonOverride>,
1186
- );
1187
- if (overrideablesComparison !== 0) {
1188
- return overrideablesComparison;
1189
- }
1190
- const aId = a.getIntervalId();
1191
- const bId = b.getIntervalId();
1192
- if (aId !== undefined && bId !== undefined) {
1193
- return aId.localeCompare(bId);
1194
- }
1195
- return 0;
1196
- });
1197
- }
1198
-
1199
- public add(interval: TInterval): void {
1200
- this.intervalTree.put(interval, interval);
1201
- }
1202
-
1203
- public remove(interval: TInterval): void {
1204
- this.intervalTree.remove(interval);
1205
- }
1206
-
1207
- public findIntervalsWithStartpointInRange(start: number, end: number) {
1208
- if (start <= 0 || start > end || this.intervalTree.isEmpty()) {
1209
- return [];
1210
- }
1211
- const results: TInterval[] = [];
1212
- const action: PropertyAction<TInterval, TInterval> = (node) => {
1213
- results.push(node.data);
1214
- return true;
1215
- };
1216
-
1217
- const transientStartInterval = this.helpers.create(
1218
- "transient",
1219
- start,
1220
- start,
1221
- this.client,
1222
- IntervalType.Transient,
1223
- );
1224
-
1225
- const transientEndInterval = this.helpers.create(
1226
- "transient",
1227
- end,
1228
- end,
1229
- this.client,
1230
- IntervalType.Transient,
1231
- );
1232
-
1233
- // Add comparison overrides to the transient intervals
1234
- (transientStartInterval as Partial<HasComparisonOverride>)[forceCompare] = -1;
1235
- (transientEndInterval as Partial<HasComparisonOverride>)[forceCompare] = 1;
1236
-
1237
- this.intervalTree.mapRange(action, results, transientStartInterval, transientEndInterval);
1238
- return results;
1239
- }
1240
- }
1241
-
1242
- export function createEndpointInRangeIndex<TInterval extends ISerializableInterval>(
1243
- helpers: IIntervalHelpers<TInterval>,
1244
- client: Client,
1245
- ): IEndpointInRangeIndex<TInterval> {
1246
- return new EndpointInRangeIndex<TInterval>(helpers, client);
1247
- }
1248
-
1249
- export function createStartpointInRangeIndex<TInterval extends ISerializableInterval>(
1250
- helpers: IIntervalHelpers<TInterval>,
1251
- client: Client,
1252
- ): IStartpointInRangeIndex<TInterval> {
1253
- return new StartpointInRangeIndex<TInterval>(helpers, client);
1254
- }
1255
-
1256
124
  export class LocalIntervalCollection<TInterval extends ISerializableInterval> {
1257
125
  private static readonly legacyIdPrefix = "legacy";
1258
126
  public readonly overlappingIntervalsIndex: IOverlappingIntervalsIndex<TInterval>;
1259
- public readonly idIntervalIndex: IdIntervalIndex<TInterval>;
1260
- public readonly endIntervalIndex: EndpointIndex<TInterval>;
127
+ public readonly idIntervalIndex: IIdIntervalIndex<TInterval>;
128
+ public readonly endIntervalIndex: IEndpointIndex<TInterval>;
1261
129
  private readonly indexes: Set<IntervalIndex<TInterval>>;
1262
130
 
1263
131
  constructor(
@@ -1271,8 +139,8 @@ export class LocalIntervalCollection<TInterval extends ISerializableInterval> {
1271
139
  ) => void,
1272
140
  ) {
1273
141
  this.overlappingIntervalsIndex = createOverlappingIntervalsIndex(client, helpers);
1274
- this.idIntervalIndex = new IdIntervalIndex();
1275
- this.endIntervalIndex = new EndpointIndex(client, helpers);
142
+ this.idIntervalIndex = createIdIntervalIndex<TInterval>();
143
+ this.endIntervalIndex = createEndpointIndex(client, helpers);
1276
144
  this.indexes = new Set([
1277
145
  this.overlappingIntervalsIndex,
1278
146
  this.idIntervalIndex,
@@ -1488,24 +356,6 @@ export class LocalIntervalCollection<TInterval extends ISerializableInterval> {
1488
356
  }
1489
357
  }
1490
358
 
1491
- export const compareSequenceIntervalEnds = (a: SequenceInterval, b: SequenceInterval): number =>
1492
- compareReferencePositions(a.end, b.end);
1493
-
1494
- export const compareSequenceIntervalStarts = (a: SequenceInterval, b: SequenceInterval): number =>
1495
- compareReferencePositions(a.start, b.start);
1496
-
1497
- export const sequenceIntervalHelpers: IIntervalHelpers<SequenceInterval> = {
1498
- compareEnds: compareSequenceIntervalEnds,
1499
- compareStarts: compareSequenceIntervalStarts,
1500
- create: createSequenceInterval,
1501
- };
1502
-
1503
- export const intervalHelpers: IIntervalHelpers<Interval> = {
1504
- compareEnds: (a: Interval, b: Interval) => a.end - b.end,
1505
- compareStarts: (a: Interval, b: Interval) => a.start - b.start,
1506
- create: createInterval,
1507
- };
1508
-
1509
359
  class SequenceIntervalCollectionFactory
1510
360
  implements IValueFactory<IntervalCollection<SequenceInterval>>
1511
361
  {
@@ -1553,26 +403,6 @@ export class SequenceIntervalCollectionValueType
1553
403
  private static readonly _ops = makeOpsMap<SequenceInterval>();
1554
404
  }
1555
405
 
1556
- const compareIntervalEnds = (a: Interval, b: Interval) => a.end - b.end;
1557
-
1558
- function createInterval(
1559
- label: string,
1560
- start: number,
1561
- end: number,
1562
- client: Client,
1563
- intervalType?: IntervalType,
1564
- op?: ISequencedDocumentMessage,
1565
- fromSnapshot?: boolean,
1566
- ): Interval {
1567
- const rangeProp: PropertySet = {};
1568
-
1569
- if (label && label.length > 0) {
1570
- rangeProp[reservedRangeLabelsKey] = [label];
1571
- }
1572
-
1573
- return new Interval(start, end, rangeProp);
1574
- }
1575
-
1576
406
  class IntervalCollectionFactory implements IValueFactory<IntervalCollection<Interval>> {
1577
407
  public load(
1578
408
  emitter: IValueOpEmitter,
@@ -1580,7 +410,7 @@ class IntervalCollectionFactory implements IValueFactory<IntervalCollection<Inte
1580
410
  options?: Partial<SequenceOptions>,
1581
411
  ): IntervalCollection<Interval> {
1582
412
  const helpers: IIntervalHelpers<Interval> = {
1583
- compareEnds: compareIntervalEnds,
413
+ compareEnds: (a: Interval, b: Interval) => a.end - b.end,
1584
414
  create: createInterval,
1585
415
  };
1586
416
  const collection = new IntervalCollection<Interval>(helpers, false, emitter, raw, options);
@@ -1782,7 +612,7 @@ export interface IIntervalCollection<TInterval extends ISerializableInterval>
1782
612
  * All intervals which are part of this collection will be added to the index, and the index will automatically
1783
613
  * be updated when this collection updates due to local or remote changes.
1784
614
  *
1785
- * @remarks - After attaching an index to an interval collection, applications should typically store this
615
+ * @remarks After attaching an index to an interval collection, applications should typically store this
1786
616
  * index somewhere in their in-memory data model for future reference and querying.
1787
617
  */
1788
618
  attachIndex(index: IntervalIndex<TInterval>): void;
@@ -1791,7 +621,7 @@ export interface IIntervalCollection<TInterval extends ISerializableInterval>
1791
621
  * All intervals which are part of this collection will be removed from the index, and updates to this collection
1792
622
  * due to local or remote changes will no longer incur updates to the index.
1793
623
  *
1794
- * @returns - Return false if the target index cannot be found in the indexes, otherwise remove all intervals in the index and return true
624
+ * @returns `false` if the target index cannot be found in the indexes, otherwise remove all intervals in the index and return `true`.
1795
625
  */
1796
626
  detachIndex(index: IntervalIndex<TInterval>): boolean;
1797
627
  /**
@@ -1806,8 +636,8 @@ export interface IIntervalCollection<TInterval extends ISerializableInterval>
1806
636
  * @param intervalType - type of the interval. All intervals are SlideOnRemove. Intervals may not be Transient.
1807
637
  * @param props - properties of the interval
1808
638
  * @param stickiness - {@link (IntervalStickiness:type)} to apply to the added interval.
1809
- * @returns - the created interval
1810
- * @remarks - See documentation on {@link SequenceInterval} for comments on interval endpoint semantics: there are subtleties
639
+ * @returns The created interval
640
+ * @remarks See documentation on {@link SequenceInterval} for comments on interval endpoint semantics: there are subtleties
1811
641
  * with how the current half-open behavior is represented.
1812
642
  */
1813
643
  add(
@@ -2128,7 +958,7 @@ export class IntervalCollection<TInterval extends ISerializableInterval>
2128
958
  /**
2129
959
  * {@inheritdoc IIntervalCollection.getIntervalById}
2130
960
  */
2131
- public getIntervalById(id: string) {
961
+ public getIntervalById(id: string): TInterval | undefined {
2132
962
  if (!this.localCollection) {
2133
963
  throw new LoggingError("attach must be called before accessing intervals");
2134
964
  }
@@ -2216,7 +1046,7 @@ export class IntervalCollection<TInterval extends ISerializableInterval>
2216
1046
  /**
2217
1047
  * {@inheritdoc IIntervalCollection.removeIntervalById}
2218
1048
  */
2219
- public removeIntervalById(id: string) {
1049
+ public removeIntervalById(id: string): TInterval | undefined {
2220
1050
  if (!this.localCollection) {
2221
1051
  throw new LoggingError("Attach must be called before accessing intervals");
2222
1052
  }
@@ -2444,19 +1274,6 @@ export class IntervalCollection<TInterval extends ISerializableInterval>
2444
1274
  }
2445
1275
  }
2446
1276
 
2447
- /**
2448
- * @deprecated - This functionality was useful when adding two intervals at the same start/end positions resulted
2449
- * in a conflict. This is no longer the case (as of PR#6407), as interval collections support multiple intervals
2450
- * at the same location and gives each interval a unique id.
2451
- *
2452
- * As such, the conflict resolver is never invoked and unnecessary. This API will be removed in an upcoming release.
2453
- */
2454
- public addConflictResolver(_: IntervalConflictResolver<TInterval>): void {
2455
- if (!this.localCollection) {
2456
- throw new LoggingError("attachSequence must be called");
2457
- }
2458
- }
2459
-
2460
1277
  /**
2461
1278
  * {@inheritdoc IIntervalCollection.attachDeserializer}
2462
1279
  */