@fluidframework/sequence 2.0.0-internal.1.4.2 → 2.0.0-internal.2.0.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 (117) hide show
  1. package/dist/defaultMap.d.ts.map +1 -1
  2. package/dist/defaultMap.js +1 -0
  3. package/dist/defaultMap.js.map +1 -1
  4. package/dist/index.d.ts +4 -5
  5. package/dist/index.d.ts.map +1 -1
  6. package/dist/index.js +2 -4
  7. package/dist/index.js.map +1 -1
  8. package/dist/intervalCollection.d.ts +271 -34
  9. package/dist/intervalCollection.d.ts.map +1 -1
  10. package/dist/intervalCollection.js +343 -97
  11. package/dist/intervalCollection.js.map +1 -1
  12. package/dist/intervalTree.d.ts +72 -0
  13. package/dist/intervalTree.d.ts.map +1 -0
  14. package/dist/intervalTree.js +91 -0
  15. package/dist/intervalTree.js.map +1 -0
  16. package/dist/packageVersion.d.ts +1 -1
  17. package/dist/packageVersion.js +1 -1
  18. package/dist/packageVersion.js.map +1 -1
  19. package/dist/sequence.d.ts +66 -15
  20. package/dist/sequence.d.ts.map +1 -1
  21. package/dist/sequence.js +73 -19
  22. package/dist/sequence.js.map +1 -1
  23. package/dist/sequenceDeltaEvent.d.ts +15 -1
  24. package/dist/sequenceDeltaEvent.d.ts.map +1 -1
  25. package/dist/sequenceDeltaEvent.js +2 -1
  26. package/dist/sequenceDeltaEvent.js.map +1 -1
  27. package/dist/sequenceFactory.d.ts +0 -89
  28. package/dist/sequenceFactory.d.ts.map +1 -1
  29. package/dist/sequenceFactory.js +2 -142
  30. package/dist/sequenceFactory.js.map +1 -1
  31. package/dist/sharedIntervalCollection.d.ts +0 -6
  32. package/dist/sharedIntervalCollection.d.ts.map +1 -1
  33. package/dist/sharedIntervalCollection.js +0 -7
  34. package/dist/sharedIntervalCollection.js.map +1 -1
  35. package/dist/sharedSequence.d.ts +2 -2
  36. package/dist/sharedString.d.ts +16 -15
  37. package/dist/sharedString.d.ts.map +1 -1
  38. package/dist/sharedString.js +96 -15
  39. package/dist/sharedString.js.map +1 -1
  40. package/lib/defaultMap.d.ts.map +1 -1
  41. package/lib/defaultMap.js +1 -0
  42. package/lib/defaultMap.js.map +1 -1
  43. package/lib/index.d.ts +4 -5
  44. package/lib/index.d.ts.map +1 -1
  45. package/lib/index.js +2 -4
  46. package/lib/index.js.map +1 -1
  47. package/lib/intervalCollection.d.ts +271 -34
  48. package/lib/intervalCollection.d.ts.map +1 -1
  49. package/lib/intervalCollection.js +341 -98
  50. package/lib/intervalCollection.js.map +1 -1
  51. package/lib/intervalTree.d.ts +72 -0
  52. package/lib/intervalTree.d.ts.map +1 -0
  53. package/lib/intervalTree.js +86 -0
  54. package/lib/intervalTree.js.map +1 -0
  55. package/lib/packageVersion.d.ts +1 -1
  56. package/lib/packageVersion.js +1 -1
  57. package/lib/packageVersion.js.map +1 -1
  58. package/lib/sequence.d.ts +66 -15
  59. package/lib/sequence.d.ts.map +1 -1
  60. package/lib/sequence.js +73 -19
  61. package/lib/sequence.js.map +1 -1
  62. package/lib/sequenceDeltaEvent.d.ts +15 -1
  63. package/lib/sequenceDeltaEvent.d.ts.map +1 -1
  64. package/lib/sequenceDeltaEvent.js +2 -1
  65. package/lib/sequenceDeltaEvent.js.map +1 -1
  66. package/lib/sequenceFactory.d.ts +0 -89
  67. package/lib/sequenceFactory.d.ts.map +1 -1
  68. package/lib/sequenceFactory.js +1 -139
  69. package/lib/sequenceFactory.js.map +1 -1
  70. package/lib/sharedIntervalCollection.d.ts +0 -6
  71. package/lib/sharedIntervalCollection.d.ts.map +1 -1
  72. package/lib/sharedIntervalCollection.js +0 -7
  73. package/lib/sharedIntervalCollection.js.map +1 -1
  74. package/lib/sharedSequence.d.ts +2 -2
  75. package/lib/sharedString.d.ts +16 -15
  76. package/lib/sharedString.d.ts.map +1 -1
  77. package/lib/sharedString.js +97 -16
  78. package/lib/sharedString.js.map +1 -1
  79. package/package.json +99 -27
  80. package/src/defaultMap.ts +3 -0
  81. package/src/index.ts +4 -4
  82. package/src/intervalCollection.ts +486 -143
  83. package/src/intervalTree.ts +166 -0
  84. package/src/packageVersion.ts +1 -1
  85. package/src/sequence.ts +86 -30
  86. package/src/sequenceDeltaEvent.ts +18 -4
  87. package/src/sequenceFactory.ts +2 -163
  88. package/src/sharedIntervalCollection.ts +0 -11
  89. package/src/sharedString.ts +120 -23
  90. package/tsconfig.json +0 -1
  91. package/dist/sharedNumberSequence.d.ts +0 -50
  92. package/dist/sharedNumberSequence.d.ts.map +0 -1
  93. package/dist/sharedNumberSequence.js +0 -61
  94. package/dist/sharedNumberSequence.js.map +0 -1
  95. package/dist/sharedObjectSequence.d.ts +0 -50
  96. package/dist/sharedObjectSequence.d.ts.map +0 -1
  97. package/dist/sharedObjectSequence.js +0 -61
  98. package/dist/sharedObjectSequence.js.map +0 -1
  99. package/dist/sparsematrix.d.ts +0 -152
  100. package/dist/sparsematrix.d.ts.map +0 -1
  101. package/dist/sparsematrix.js +0 -343
  102. package/dist/sparsematrix.js.map +0 -1
  103. package/lib/sharedNumberSequence.d.ts +0 -50
  104. package/lib/sharedNumberSequence.d.ts.map +0 -1
  105. package/lib/sharedNumberSequence.js +0 -57
  106. package/lib/sharedNumberSequence.js.map +0 -1
  107. package/lib/sharedObjectSequence.d.ts +0 -50
  108. package/lib/sharedObjectSequence.d.ts.map +0 -1
  109. package/lib/sharedObjectSequence.js +0 -57
  110. package/lib/sharedObjectSequence.js.map +0 -1
  111. package/lib/sparsematrix.d.ts +0 -152
  112. package/lib/sparsematrix.d.ts.map +0 -1
  113. package/lib/sparsematrix.js +0 -334
  114. package/lib/sparsematrix.js.map +0 -1
  115. package/src/sharedNumberSequence.ts +0 -62
  116. package/src/sharedObjectSequence.ts +0 -62
  117. package/src/sparsematrix.ts +0 -434
@@ -15,13 +15,14 @@ var __rest = (this && this.__rest) || function (s, e) {
15
15
  return t;
16
16
  };
17
17
  Object.defineProperty(exports, "__esModule", { value: true });
18
- exports.intervalLocatorFromEndpoint = exports.IntervalCollection = exports.IntervalCollectionIterator = exports.IntervalCollectionValueType = exports.SequenceIntervalCollectionValueType = exports.LocalIntervalCollection = exports.createIntervalIndex = exports.defaultIntervalConflictResolver = exports.SequenceInterval = exports.Interval = exports.IntervalType = void 0;
18
+ exports.intervalLocatorFromEndpoint = exports.IntervalCollection = exports.IntervalCollectionIterator = exports.makeOpsMap = exports.IntervalCollectionValueType = exports.SequenceIntervalCollectionValueType = exports.compareSequenceIntervalEnds = exports.LocalIntervalCollection = exports.createIntervalIndex = exports.defaultIntervalConflictResolver = exports.createSequenceInterval = exports.SequenceInterval = exports.Interval = exports.IntervalType = void 0;
19
19
  /* eslint-disable no-bitwise */
20
20
  const common_utils_1 = require("@fluidframework/common-utils");
21
21
  const container_utils_1 = require("@fluidframework/container-utils");
22
22
  const merge_tree_1 = require("@fluidframework/merge-tree");
23
23
  const telemetry_utils_1 = require("@fluidframework/telemetry-utils");
24
24
  const uuid_1 = require("uuid");
25
+ const intervalTree_1 = require("./intervalTree");
25
26
  const reservedIntervalIdKey = "intervalId";
26
27
  var IntervalType;
27
28
  (function (IntervalType) {
@@ -67,6 +68,9 @@ function compressInterval(interval) {
67
68
  Object.assign(Object.assign({}, properties), { [merge_tree_1.reservedRangeLabelsKey]: undefined }),
68
69
  ];
69
70
  }
71
+ /**
72
+ * Serializable interval whose endpoints are plain-old numbers.
73
+ */
70
74
  class Interval {
71
75
  constructor(start, end, props) {
72
76
  this.start = start;
@@ -77,6 +81,9 @@ class Interval {
77
81
  this.addProperties(props);
78
82
  }
79
83
  }
84
+ /**
85
+ * {@inheritDoc ISerializableInterval.getIntervalId}
86
+ */
80
87
  getIntervalId() {
81
88
  var _a;
82
89
  const id = (_a = this.properties) === null || _a === void 0 ? void 0 : _a[reservedIntervalIdKey];
@@ -85,22 +92,36 @@ class Interval {
85
92
  }
86
93
  return `${id}`;
87
94
  }
95
+ /**
96
+ * @returns an array containing any auxiliary property sets added with `addPropertySet`.
97
+ */
88
98
  getAdditionalPropertySets() {
89
- return this.auxProps;
99
+ var _a;
100
+ return (_a = this.auxProps) !== null && _a !== void 0 ? _a : [];
90
101
  }
102
+ /**
103
+ * Adds an auxiliary set of properties to this interval.
104
+ * These properties can be recovered using `getAdditionalPropertySets`
105
+ * @param props - set of properties to add
106
+ * @remarks - This gets called as part of the default conflict resolver for `IntervalCollection<Interval>`
107
+ * (i.e. non-sequence-based interval collections). However, the additional properties don't get serialized.
108
+ * This functionality seems half-baked.
109
+ */
91
110
  addPropertySet(props) {
92
111
  if (this.auxProps === undefined) {
93
112
  this.auxProps = [];
94
113
  }
95
114
  this.auxProps.push(props);
96
115
  }
97
- serialize(client) {
98
- var _a;
99
- const seq = (_a = client === null || client === void 0 ? void 0 : client.getCurrentSeq()) !== null && _a !== void 0 ? _a : 0;
116
+ /**
117
+ * {@inheritDoc ISerializableInterval.serialize}
118
+ * @internal
119
+ */
120
+ serialize() {
100
121
  const serializedInterval = {
101
122
  end: this.end,
102
123
  intervalType: 0,
103
- sequenceNumber: seq,
124
+ sequenceNumber: 0,
104
125
  start: this.start,
105
126
  };
106
127
  if (this.properties) {
@@ -108,9 +129,15 @@ class Interval {
108
129
  }
109
130
  return serializedInterval;
110
131
  }
132
+ /**
133
+ * {@inheritDoc IInterval.clone}
134
+ */
111
135
  clone() {
112
136
  return new Interval(this.start, this.end, this.properties);
113
137
  }
138
+ /**
139
+ * {@inheritDoc IInterval.compare}
140
+ */
114
141
  compare(b) {
115
142
  const startResult = this.compareStart(b);
116
143
  if (startResult === 0) {
@@ -134,29 +161,47 @@ class Interval {
134
161
  return startResult;
135
162
  }
136
163
  }
164
+ /**
165
+ * {@inheritDoc IInterval.compareStart}
166
+ */
137
167
  compareStart(b) {
138
168
  return this.start - b.start;
139
169
  }
170
+ /**
171
+ * {@inheritDoc IInterval.compareEnd}
172
+ */
140
173
  compareEnd(b) {
141
174
  return this.end - b.end;
142
175
  }
176
+ /**
177
+ * {@inheritDoc IInterval.overlaps}
178
+ */
143
179
  overlaps(b) {
144
180
  const result = (this.start <= b.end) &&
145
181
  (this.end >= b.start);
146
182
  return result;
147
183
  }
184
+ /**
185
+ * {@inheritDoc IInterval.union}
186
+ */
148
187
  union(b) {
149
188
  return new Interval(Math.min(this.start, b.start), Math.max(this.end, b.end), this.properties);
150
189
  }
151
190
  getProperties() {
152
191
  return this.properties;
153
192
  }
193
+ /**
194
+ * {@inheritDoc ISerializableInterval.addProperties}
195
+ */
154
196
  addProperties(newProps, collaborating = false, seq, op) {
155
197
  if (newProps) {
156
198
  this.initializeProperties();
157
199
  return this.propertyManager.addProperties(this.properties, newProps, op, seq, collaborating);
158
200
  }
159
201
  }
202
+ /**
203
+ * {@inheritDoc IInterval.modify}
204
+ */
160
205
  modify(label, start, end, op) {
161
206
  const startPos = start !== null && start !== void 0 ? start : this.start;
162
207
  const endPos = end !== null && end !== void 0 ? end : this.end;
@@ -181,8 +226,23 @@ class Interval {
181
226
  }
182
227
  }
183
228
  exports.Interval = Interval;
229
+ /**
230
+ * Interval impelmentation whose ends are associated with positions in a mutatable sequence.
231
+ * As such, when content is inserted into the middle of the interval, the interval expands to
232
+ * include that content.
233
+ */
184
234
  class SequenceInterval {
185
- constructor(client, start, end, intervalType, props) {
235
+ constructor(client,
236
+ /**
237
+ * Start endpoint of this interval.
238
+ * @remarks - This endpoint can be resolved into a character position using the SharedString it's a part of.
239
+ */
240
+ start,
241
+ /**
242
+ * End endpoint of this interval.
243
+ * @remarks - This endpoint can be resolved into a character position using the SharedString it's a part of.
244
+ */
245
+ end, intervalType, props) {
186
246
  this.client = client;
187
247
  this.start = start;
188
248
  this.end = end;
@@ -222,13 +282,17 @@ class SequenceInterval {
222
282
  this.end.callbacks = undefined;
223
283
  }
224
284
  }
225
- serialize(client) {
226
- const startPosition = client.localReferencePositionToPosition(this.start);
227
- const endPosition = client.localReferencePositionToPosition(this.end);
285
+ /**
286
+ * {@inheritDoc ISerializableInterval.serialize}
287
+ * @internal
288
+ */
289
+ serialize() {
290
+ const startPosition = this.client.localReferencePositionToPosition(this.start);
291
+ const endPosition = this.client.localReferencePositionToPosition(this.end);
228
292
  const serializedInterval = {
229
293
  end: endPosition,
230
294
  intervalType: this.intervalType,
231
- sequenceNumber: client.getCurrentSeq(),
295
+ sequenceNumber: this.client.getCurrentSeq(),
232
296
  start: startPosition,
233
297
  };
234
298
  if (this.properties) {
@@ -236,9 +300,15 @@ class SequenceInterval {
236
300
  }
237
301
  return serializedInterval;
238
302
  }
303
+ /**
304
+ * {@inheritDoc IInterval.clone}
305
+ */
239
306
  clone() {
240
307
  return new SequenceInterval(this.client, this.start, this.end, this.intervalType, this.properties);
241
308
  }
309
+ /**
310
+ * {@inheritDoc IInterval.compare}
311
+ */
242
312
  compare(b) {
243
313
  const startResult = this.compareStart(b);
244
314
  if (startResult === 0) {
@@ -262,17 +332,29 @@ class SequenceInterval {
262
332
  return startResult;
263
333
  }
264
334
  }
335
+ /**
336
+ * {@inheritDoc IInterval.compareStart}
337
+ */
265
338
  compareStart(b) {
266
339
  return (0, merge_tree_1.compareReferencePositions)(this.start, b.start);
267
340
  }
341
+ /**
342
+ * {@inheritDoc IInterval.compareEnd}
343
+ */
268
344
  compareEnd(b) {
269
345
  return (0, merge_tree_1.compareReferencePositions)(this.end, b.end);
270
346
  }
347
+ /**
348
+ * {@inheritDoc IInterval.overlaps}
349
+ */
271
350
  overlaps(b) {
272
351
  const result = ((0, merge_tree_1.compareReferencePositions)(this.start, b.end) <= 0) &&
273
352
  ((0, merge_tree_1.compareReferencePositions)(this.end, b.start) >= 0);
274
353
  return result;
275
354
  }
355
+ /**
356
+ * {@inheritDoc ISerializableInterval.getIntervalId}
357
+ */
276
358
  getIntervalId() {
277
359
  var _a;
278
360
  const id = (_a = this.properties) === null || _a === void 0 ? void 0 : _a[reservedIntervalIdKey];
@@ -281,18 +363,31 @@ class SequenceInterval {
281
363
  }
282
364
  return `${id}`;
283
365
  }
366
+ /**
367
+ * {@inheritDoc IInterval.union}
368
+ */
284
369
  union(b) {
285
370
  return new SequenceInterval(this.client, (0, merge_tree_1.minReferencePosition)(this.start, b.start), (0, merge_tree_1.maxReferencePosition)(this.end, b.end), this.intervalType);
286
371
  }
372
+ /**
373
+ * {@inheritDoc ISerializableInterval.addProperties}
374
+ */
287
375
  addProperties(newProps, collab = false, seq, op) {
288
376
  this.initializeProperties();
289
377
  return this.propertyManager.addProperties(this.properties, newProps, op, seq, collab);
290
378
  }
379
+ /**
380
+ * @returns whether this interval overlaps two numerical positions.
381
+ * @remarks - this is currently strict overlap, which doesn't align with the endpoint treatment of`.overlaps()`
382
+ */
291
383
  overlapsPos(bstart, bend) {
292
384
  const startPos = this.client.localReferencePositionToPosition(this.start);
293
385
  const endPos = this.client.localReferencePositionToPosition(this.end);
294
386
  return (endPos > bstart) && (startPos < bend);
295
387
  }
388
+ /**
389
+ * {@inheritDoc IInterval.modify}
390
+ */
296
391
  modify(label, start, end, op, localSeq) {
297
392
  const getRefType = (baseType) => {
298
393
  let refType = baseType;
@@ -305,12 +400,16 @@ class SequenceInterval {
305
400
  let startRef = this.start;
306
401
  if (start !== undefined) {
307
402
  startRef = createPositionReference(this.client, start, getRefType(this.start.refType), op, undefined, localSeq);
308
- startRef.addProperties(this.start.properties);
403
+ if (this.start.properties) {
404
+ startRef.addProperties(this.start.properties);
405
+ }
309
406
  }
310
407
  let endRef = this.end;
311
408
  if (end !== undefined) {
312
409
  endRef = createPositionReference(this.client, end, getRefType(this.end.refType), op, undefined, localSeq);
313
- endRef.addProperties(this.end.properties);
410
+ if (this.end.properties) {
411
+ endRef.addProperties(this.end.properties);
412
+ }
314
413
  }
315
414
  const newInterval = new SequenceInterval(this.client, startRef, endRef, this.intervalType);
316
415
  if (this.properties) {
@@ -348,7 +447,7 @@ function createPositionReference(client, pos, refType, op, fromSnapshot, localSe
348
447
  segoff = client.getSlideToSegment(segoff);
349
448
  }
350
449
  else {
351
- (0, common_utils_1.assert)((refType & merge_tree_1.ReferenceType.SlideOnRemove) === 0 || fromSnapshot, 0x2f6 /* SlideOnRemove references must be op created */);
450
+ (0, common_utils_1.assert)((refType & merge_tree_1.ReferenceType.SlideOnRemove) === 0 || !!fromSnapshot, 0x2f6 /* SlideOnRemove references must be op created */);
352
451
  segoff = client.getContainingSegment(pos, undefined, localSeq);
353
452
  }
354
453
  return createPositionReferenceFromSegoff(client, segoff, refType, op);
@@ -387,6 +486,7 @@ function createSequenceInterval(label, start, end, client, intervalType, op, fro
387
486
  const ival = new SequenceInterval(client, startLref, endLref, intervalType, rangeProp);
388
487
  return ival;
389
488
  }
489
+ exports.createSequenceInterval = createSequenceInterval;
390
490
  function defaultIntervalConflictResolver(a, b) {
391
491
  a.addPropertySet(b.properties);
392
492
  return a;
@@ -415,7 +515,7 @@ class LocalIntervalCollection {
415
515
  this.label = label;
416
516
  this.helpers = helpers;
417
517
  this.onPositionChange = onPositionChange;
418
- this.intervalTree = new merge_tree_1.IntervalTree();
518
+ this.intervalTree = new intervalTree_1.IntervalTree();
419
519
  this.intervalIdMap = new Map();
420
520
  // eslint-disable-next-line @typescript-eslint/unbound-method
421
521
  this.endIntervalTree = new merge_tree_1.RedBlackTree(helpers.compareEnds);
@@ -530,6 +630,10 @@ class LocalIntervalCollection {
530
630
  }
531
631
  }
532
632
  }
633
+ /**
634
+ * @returns an array of all intervals contained in this collection that overlap the range
635
+ * `[startPosition, endPosition]`.
636
+ */
533
637
  findOverlappingIntervals(startPosition, endPosition) {
534
638
  if (endPosition < startPosition || this.intervalTree.intervals.isEmpty()) {
535
639
  return [];
@@ -624,20 +728,38 @@ class LocalIntervalCollection {
624
728
  return newInterval;
625
729
  }
626
730
  serialize() {
627
- const client = this.client;
628
731
  const intervals = this.intervalTree.intervals.keys();
629
732
  return {
630
733
  label: this.label,
631
- intervals: intervals.map((interval) => compressInterval(interval.serialize(client))),
734
+ intervals: intervals.map((interval) => compressInterval(interval.serialize())),
632
735
  version: 2,
633
736
  };
634
737
  }
635
738
  addIntervalListeners(interval) {
739
+ const cloneRef = (ref) => {
740
+ const segment = ref.getSegment();
741
+ if (segment === undefined) {
742
+ // Cloning is unnecessary: refs which have slid off the string entirely
743
+ // never get slid back on. Creation code for refs doesn't accept undefined segment
744
+ // either, so this must be special-cased.
745
+ return ref;
746
+ }
747
+ return this.client.createLocalReferencePosition(segment, ref.getOffset(), merge_tree_1.ReferenceType.Transient, ref.properties);
748
+ };
636
749
  if (interval instanceof SequenceInterval) {
637
- interval.addPositionChangeListeners(() => this.removeIntervalFromIndex(interval), () => {
750
+ let previousInterval;
751
+ interval.addPositionChangeListeners(() => {
752
+ (0, common_utils_1.assert)(!previousInterval, 0x3f9 /* Invalid interleaving of before/after slide */);
753
+ previousInterval = interval.clone();
754
+ previousInterval.start = cloneRef(previousInterval.start);
755
+ previousInterval.end = cloneRef(previousInterval.end);
756
+ this.removeIntervalFromIndex(interval);
757
+ }, () => {
638
758
  var _a;
759
+ (0, common_utils_1.assert)(previousInterval !== undefined, 0x3fa /* Invalid interleaving of before/after slide */);
639
760
  this.addIntervalToIndex(interval);
640
- (_a = this.onPositionChange) === null || _a === void 0 ? void 0 : _a.call(this, interval);
761
+ (_a = this.onPositionChange) === null || _a === void 0 ? void 0 : _a.call(this, interval, previousInterval);
762
+ previousInterval = undefined;
641
763
  });
642
764
  }
643
765
  }
@@ -650,10 +772,11 @@ class LocalIntervalCollection {
650
772
  exports.LocalIntervalCollection = LocalIntervalCollection;
651
773
  LocalIntervalCollection.legacyIdPrefix = "legacy";
652
774
  const compareSequenceIntervalEnds = (a, b) => (0, merge_tree_1.compareReferencePositions)(a.end, b.end);
775
+ exports.compareSequenceIntervalEnds = compareSequenceIntervalEnds;
653
776
  class SequenceIntervalCollectionFactory {
654
777
  load(emitter, raw = []) {
655
778
  const helpers = {
656
- compareEnds: compareSequenceIntervalEnds,
779
+ compareEnds: exports.compareSequenceIntervalEnds,
657
780
  create: createSequenceInterval,
658
781
  };
659
782
  return new IntervalCollection(helpers, true, emitter, raw);
@@ -730,6 +853,7 @@ function makeOpsMap() {
730
853
  if (!params) {
731
854
  return;
732
855
  }
856
+ (0, common_utils_1.assert)(op !== undefined, 0x3fb /* op should exist here */);
733
857
  collection.ackAdd(params, local, op);
734
858
  },
735
859
  rebase,
@@ -739,6 +863,7 @@ function makeOpsMap() {
739
863
  "delete",
740
864
  {
741
865
  process: (collection, params, local, op) => {
866
+ (0, common_utils_1.assert)(op !== undefined, 0x3fc /* op should exist here */);
742
867
  collection.ackDelete(params, local, op);
743
868
  },
744
869
  rebase: (collection, op, localOpMetadata) => {
@@ -756,12 +881,14 @@ function makeOpsMap() {
756
881
  if (!params) {
757
882
  return;
758
883
  }
884
+ (0, common_utils_1.assert)(op !== undefined, 0x3fd /* op should exist here */);
759
885
  collection.ackChange(params, local, op);
760
886
  },
761
887
  rebase,
762
888
  },
763
889
  ]]);
764
890
  }
891
+ exports.makeOpsMap = makeOpsMap;
765
892
  class IntervalCollectionIterator {
766
893
  constructor(collection, iteratesForward = true, start, end) {
767
894
  this.results = [];
@@ -769,19 +896,26 @@ class IntervalCollectionIterator {
769
896
  collection.gatherIterationResults(this.results, iteratesForward, start, end);
770
897
  }
771
898
  next() {
772
- let _value;
773
- let _done = true;
774
899
  if (this.index < this.results.length) {
775
- _value = this.results[this.index++];
776
- _done = false;
900
+ return {
901
+ value: this.results[this.index++],
902
+ done: false,
903
+ };
777
904
  }
778
905
  return {
779
- value: _value,
780
- done: _done,
906
+ value: undefined,
907
+ done: true,
781
908
  };
782
909
  }
783
910
  }
784
911
  exports.IntervalCollectionIterator = IntervalCollectionIterator;
912
+ /**
913
+ * Collection of intervals that supports addition, modification, removal, and efficient spatial querying.
914
+ * This class is not a DDS in its own right, but emits events on mutating operations such that it's possible to
915
+ * integrate into a DDS.
916
+ * This aligns with its usage in `SharedSegmentSequence`, which allows associating intervals to positions in the
917
+ * sequence DDS which are broadcast to all other clients in an eventually consistent fashion.
918
+ */
785
919
  class IntervalCollection extends common_utils_1.TypedEventEmitter {
786
920
  /** @internal */
787
921
  constructor(helpers, requiresClient, emitter, serializedIntervals) {
@@ -798,6 +932,7 @@ class IntervalCollection extends common_utils_1.TypedEventEmitter {
798
932
  get attached() {
799
933
  return !!this.localCollection;
800
934
  }
935
+ /** @internal */
801
936
  attachGraph(client, label) {
802
937
  if (this.attached) {
803
938
  throw new telemetry_utils_1.LoggingError("Only supports one Sequence attach");
@@ -807,13 +942,15 @@ class IntervalCollection extends common_utils_1.TypedEventEmitter {
807
942
  }
808
943
  // Instantiate the local interval collection based on the saved intervals
809
944
  this.client = client;
810
- this.localCollection = new LocalIntervalCollection(client, label, this.helpers, (interval) => this.emit("changeInterval", interval, true, undefined));
945
+ this.localCollection = new LocalIntervalCollection(client, label, this.helpers, (interval, previousInterval) => this.emitChange(interval, previousInterval, true));
811
946
  if (this.savedSerializedIntervals) {
812
947
  for (const serializedInterval of this.savedSerializedIntervals) {
813
948
  this.localCollection.ensureSerializedId(serializedInterval);
814
949
  const { start, end, intervalType, properties } = serializedInterval;
815
950
  const interval = this.helpers.create(label, start, end, client, intervalType, undefined, true);
816
- interval.addProperties(properties);
951
+ if (properties) {
952
+ interval.addProperties(properties);
953
+ }
817
954
  this.localCollection.add(interval);
818
955
  }
819
956
  }
@@ -828,14 +965,37 @@ class IntervalCollection extends common_utils_1.TypedEventEmitter {
828
965
  }
829
966
  return 0;
830
967
  }
968
+ emitChange(interval, previousInterval, local, op) {
969
+ // Temporarily make references transient so that positional queries work (non-transient refs
970
+ // on resolve to DetachedPosition on any segments that don't contain them). The original refType
971
+ // is restored as single-endpoint changes re-use previous references.
972
+ let startRefType;
973
+ let endRefType;
974
+ if (previousInterval instanceof SequenceInterval) {
975
+ startRefType = previousInterval.start.refType;
976
+ endRefType = previousInterval.end.refType;
977
+ previousInterval.start.refType = merge_tree_1.ReferenceType.Transient;
978
+ previousInterval.end.refType = merge_tree_1.ReferenceType.Transient;
979
+ this.emit("changeInterval", interval, previousInterval, local, op);
980
+ previousInterval.start.refType = startRefType;
981
+ previousInterval.end.refType = endRefType;
982
+ }
983
+ else {
984
+ this.emit("changeInterval", interval, previousInterval, local, op);
985
+ }
986
+ }
987
+ /**
988
+ * @returns the interval in this collection that has the provided `id`.
989
+ * If no interval in the collection has this `id`, returns `undefined`.
990
+ */
831
991
  getIntervalById(id) {
832
- if (!this.attached) {
992
+ if (!this.localCollection) {
833
993
  throw new telemetry_utils_1.LoggingError("attach must be called before accessing intervals");
834
994
  }
835
995
  return this.localCollection.getIntervalById(id);
836
996
  }
837
997
  /**
838
- * Create a new interval and add it to the collection
998
+ * Creates a new interval and add it to the collection.
839
999
  * @param start - interval start position
840
1000
  * @param end - interval end position
841
1001
  * @param intervalType - type of the interval. All intervals are SlideOnRemove. Intervals may not be Transient.
@@ -844,7 +1004,7 @@ class IntervalCollection extends common_utils_1.TypedEventEmitter {
844
1004
  */
845
1005
  add(start, end, intervalType, props) {
846
1006
  var _a, _b;
847
- if (!this.attached) {
1007
+ if (!this.localCollection) {
848
1008
  throw new telemetry_utils_1.LoggingError("attach must be called prior to adding intervals");
849
1009
  }
850
1010
  if (intervalType & IntervalType.Transient) {
@@ -866,12 +1026,15 @@ class IntervalCollection extends common_utils_1.TypedEventEmitter {
866
1026
  return interval;
867
1027
  }
868
1028
  deleteExistingInterval(interval, local, op) {
1029
+ if (!this.localCollection) {
1030
+ throw new telemetry_utils_1.LoggingError("Attach must be called before accessing intervals");
1031
+ }
869
1032
  // The given interval is known to exist in the collection.
870
1033
  this.localCollection.removeExistingInterval(interval);
871
1034
  if (interval) {
872
1035
  // Local ops get submitted to the server. Remote ops have the deserializer run.
873
1036
  if (local) {
874
- this.emitter.emit("delete", undefined, interval.serialize(this.client), { localSeq: this.getNextLocalSeq() });
1037
+ this.emitter.emit("delete", undefined, interval.serialize(), { localSeq: this.getNextLocalSeq() });
875
1038
  }
876
1039
  else {
877
1040
  if (this.onDeserialize) {
@@ -881,13 +1044,27 @@ class IntervalCollection extends common_utils_1.TypedEventEmitter {
881
1044
  }
882
1045
  this.emit("deleteInterval", interval, local, op);
883
1046
  }
1047
+ /**
1048
+ * Removes an interval from the collection.
1049
+ * @param id - Id of the interval to remove
1050
+ * @returns the removed interval
1051
+ */
884
1052
  removeIntervalById(id) {
1053
+ if (!this.localCollection) {
1054
+ throw new telemetry_utils_1.LoggingError("Attach must be called before accessing intervals");
1055
+ }
885
1056
  const interval = this.localCollection.getIntervalById(id);
886
1057
  if (interval) {
887
1058
  this.deleteExistingInterval(interval, true, undefined);
888
1059
  }
889
1060
  return interval;
890
1061
  }
1062
+ /**
1063
+ * Changes the properties on an existing interval.
1064
+ * @param id - Id of the interval whose properties should be changed
1065
+ * @param props - Property set to apply to the interval. Shallow merging is used between any existing properties
1066
+ * and `prop`, i.e. the interval will end up with a property object equivalent to `{ ...oldProps, ...props }`.
1067
+ */
891
1068
  changeProperties(id, props) {
892
1069
  if (!this.attached) {
893
1070
  throw new telemetry_utils_1.LoggingError("Attach must be called before accessing intervals");
@@ -902,7 +1079,7 @@ class IntervalCollection extends common_utils_1.TypedEventEmitter {
902
1079
  if (interval) {
903
1080
  // Pass Unassigned as the sequence number to indicate that this is a local op that is waiting for an ack.
904
1081
  const deltaProps = interval.addProperties(props, true, merge_tree_1.UnassignedSequenceNumber);
905
- const serializedInterval = interval.serialize(this.client);
1082
+ const serializedInterval = interval.serialize();
906
1083
  // Emit a change op that will only change properties. Add the ID to
907
1084
  // the property bag provided by the caller.
908
1085
  serializedInterval.start = undefined;
@@ -910,12 +1087,18 @@ class IntervalCollection extends common_utils_1.TypedEventEmitter {
910
1087
  serializedInterval.properties = props;
911
1088
  serializedInterval.properties[reservedIntervalIdKey] = interval.getIntervalId();
912
1089
  this.emitter.emit("change", undefined, serializedInterval, { localSeq: this.getNextLocalSeq() });
913
- this.emit("propertyChanged", interval, deltaProps);
1090
+ this.emit("propertyChanged", interval, deltaProps, true, undefined);
914
1091
  }
915
- this.emit("changeInterval", interval, true, undefined);
916
1092
  }
1093
+ /**
1094
+ * Changes the endpoints of an existing interval.
1095
+ * @param id - Id of the interval to change
1096
+ * @param start - New start value, if defined. `undefined` signifies this endpoint should be left unchanged.
1097
+ * @param end - New end value, if defined. `undefined` signifies this endpoint should be left unchanged.
1098
+ * @returns the interval that was changed, if it existed in the collection.
1099
+ */
917
1100
  change(id, start, end) {
918
- if (!this.attached) {
1101
+ if (!this.localCollection) {
919
1102
  throw new telemetry_utils_1.LoggingError("Attach must be called before accessing intervals");
920
1103
  }
921
1104
  // Force id to be a string.
@@ -925,7 +1108,10 @@ class IntervalCollection extends common_utils_1.TypedEventEmitter {
925
1108
  const interval = this.getIntervalById(id);
926
1109
  if (interval) {
927
1110
  const newInterval = this.localCollection.changeInterval(interval, start, end);
928
- const serializedInterval = interval.serialize(this.client);
1111
+ if (!newInterval) {
1112
+ return undefined;
1113
+ }
1114
+ const serializedInterval = interval.serialize();
929
1115
  serializedInterval.start = start;
930
1116
  serializedInterval.end = end;
931
1117
  // Emit a property bag containing only the ID, as we don't intend for this op to change any properties.
@@ -935,7 +1121,7 @@ class IntervalCollection extends common_utils_1.TypedEventEmitter {
935
1121
  };
936
1122
  this.emitter.emit("change", undefined, serializedInterval, { localSeq: this.getNextLocalSeq() });
937
1123
  this.addPendingChange(id, serializedInterval);
938
- this.emit("changeInterval", newInterval, true, undefined);
1124
+ this.emitChange(newInterval, interval, true);
939
1125
  return newInterval;
940
1126
  }
941
1127
  // No interval to change
@@ -992,65 +1178,70 @@ class IntervalCollection extends common_utils_1.TypedEventEmitter {
992
1178
  /** @internal */
993
1179
  ackChange(serializedInterval, local, op) {
994
1180
  var _a, _b, _c, _d;
995
- if (!this.attached) {
1181
+ if (!this.localCollection) {
996
1182
  throw new telemetry_utils_1.LoggingError("Attach must be called before accessing intervals");
997
1183
  }
998
- let interval;
999
1184
  if (local) {
1000
1185
  // This is an ack from the server. Remove the pending change.
1001
1186
  this.removePendingChange(serializedInterval);
1002
- const id = (_a = serializedInterval.properties) === null || _a === void 0 ? void 0 : _a[reservedIntervalIdKey];
1003
- interval = this.getIntervalById(id);
1004
- if (interval) {
1005
- // Let the propertyManager prune its pending change-properties set.
1006
- (_b = interval.propertyManager) === null || _b === void 0 ? void 0 : _b.ackPendingProperties({
1007
- type: merge_tree_1.MergeTreeDeltaType.ANNOTATE,
1008
- props: (_c = serializedInterval.properties) !== null && _c !== void 0 ? _c : {},
1009
- });
1010
- this.ackInterval(interval, op);
1011
- }
1187
+ }
1188
+ // Note that the ID is in the property bag only to allow us to find the interval.
1189
+ // This API cannot change the ID, and writing to the ID property will result in an exception. So we
1190
+ // strip it out of the properties here.
1191
+ const _e = (_a = serializedInterval.properties) !== null && _a !== void 0 ? _a : {}, _f = reservedIntervalIdKey, id = _e[_f], newProps = __rest(_e, [typeof _f === "symbol" ? _f : _f + ""]);
1192
+ (0, common_utils_1.assert)(id !== undefined, 0x3fe /* id must exist on the interval */);
1193
+ const interval = this.getIntervalById(id);
1194
+ if (!interval) {
1195
+ // The interval has been removed locally; no-op.
1196
+ return;
1197
+ }
1198
+ if (local) {
1199
+ // Let the propertyManager prune its pending change-properties set.
1200
+ (_b = interval.propertyManager) === null || _b === void 0 ? void 0 : _b.ackPendingProperties({
1201
+ type: merge_tree_1.MergeTreeDeltaType.ANNOTATE,
1202
+ props: (_c = serializedInterval.properties) !== null && _c !== void 0 ? _c : {},
1203
+ });
1204
+ this.ackInterval(interval, op);
1012
1205
  }
1013
1206
  else {
1014
1207
  // If there are pending changes with this ID, don't apply the remote start/end change, as the local ack
1015
1208
  // should be the winning change.
1016
- // Note that the ID is in the property bag only to allow us to find the interval.
1017
- // This API cannot change the ID, and writing to the ID property will result in an exception. So we
1018
- // strip it out of the properties here.
1019
- const _e = serializedInterval.properties, _f = reservedIntervalIdKey, id = _e[_f], newProps = __rest(_e, [typeof _f === "symbol" ? _f : _f + ""]);
1020
- interval = this.getIntervalById(id);
1021
- if (interval) {
1022
- let start;
1023
- let end;
1024
- // Track pending start/end independently of one another.
1025
- if (!this.hasPendingChangeStart(id)) {
1026
- start = serializedInterval.start;
1027
- }
1028
- if (!this.hasPendingChangeEnd(id)) {
1029
- end = serializedInterval.end;
1030
- }
1031
- if (start !== undefined || end !== undefined) {
1032
- // If changeInterval gives us a new interval, work with that one. Otherwise keep working with
1033
- // the one we originally found in the tree.
1034
- interval = (_d = this.localCollection.changeInterval(interval, start, end, op)) !== null && _d !== void 0 ? _d : interval;
1035
- }
1036
- const deltaProps = interval.addProperties(newProps, true, op.sequenceNumber);
1037
- if (this.onDeserialize) {
1038
- this.onDeserialize(interval);
1039
- }
1040
- this.emit("propertyChanged", interval, deltaProps);
1209
+ let start;
1210
+ let end;
1211
+ // Track pending start/end independently of one another.
1212
+ if (!this.hasPendingChangeStart(id)) {
1213
+ start = serializedInterval.start;
1214
+ }
1215
+ if (!this.hasPendingChangeEnd(id)) {
1216
+ end = serializedInterval.end;
1217
+ }
1218
+ let newInterval = interval;
1219
+ if (start !== undefined || end !== undefined) {
1220
+ // If changeInterval gives us a new interval, work with that one. Otherwise keep working with
1221
+ // the one we originally found in the tree.
1222
+ newInterval = (_d = this.localCollection.changeInterval(interval, start, end, op)) !== null && _d !== void 0 ? _d : interval;
1223
+ }
1224
+ const deltaProps = newInterval.addProperties(newProps, true, op.sequenceNumber);
1225
+ if (this.onDeserialize) {
1226
+ this.onDeserialize(newInterval);
1227
+ }
1228
+ if (newInterval !== interval) {
1229
+ this.emitChange(newInterval, interval, local, op);
1230
+ }
1231
+ const changedProperties = Object.keys(newProps).length > 0;
1232
+ if (changedProperties) {
1233
+ this.emit("propertyChanged", interval, deltaProps, local, op);
1041
1234
  }
1042
- }
1043
- if (interval) {
1044
- this.emit("changeInterval", interval, local, op);
1045
1235
  }
1046
1236
  }
1047
1237
  addConflictResolver(conflictResolver) {
1048
- if (!this.attached) {
1238
+ if (!this.localCollection) {
1049
1239
  throw new telemetry_utils_1.LoggingError("attachSequence must be called");
1050
1240
  }
1051
1241
  this.localCollection.addConflictResolver(conflictResolver);
1052
1242
  }
1053
1243
  attachDeserializer(onDeserialize) {
1244
+ var _a;
1054
1245
  // If no deserializer is specified can skip all processing work
1055
1246
  if (!onDeserialize) {
1056
1247
  return;
@@ -1058,7 +1249,7 @@ class IntervalCollection extends common_utils_1.TypedEventEmitter {
1058
1249
  // Start by storing the callbacks so that any subsequent modifications make use of them
1059
1250
  this.onDeserialize = onDeserialize;
1060
1251
  // Trigger the async prepare work across all values in the collection
1061
- this.localCollection.map((interval) => {
1252
+ (_a = this.localCollection) === null || _a === void 0 ? void 0 : _a.map((interval) => {
1062
1253
  onDeserialize(interval);
1063
1254
  });
1064
1255
  }
@@ -1070,7 +1261,7 @@ class IntervalCollection extends common_utils_1.TypedEventEmitter {
1070
1261
  * @internal
1071
1262
  */
1072
1263
  rebaseLocalInterval(opName, serializedInterval, localSeq) {
1073
- var _a, _b;
1264
+ var _a, _b, _c, _d, _e, _f, _g;
1074
1265
  if (!this.client) {
1075
1266
  // If there's no associated mergeTree client, the originally submitted op is still correct.
1076
1267
  return serializedInterval;
@@ -1084,12 +1275,12 @@ class IntervalCollection extends common_utils_1.TypedEventEmitter {
1084
1275
  const endRebased = end === undefined ? undefined :
1085
1276
  this.client.rebasePosition(end, sequenceNumber, localSeq);
1086
1277
  const intervalId = properties === null || properties === void 0 ? void 0 : properties[reservedIntervalIdKey];
1087
- const localInterval = this.localCollection.getIntervalById(intervalId);
1278
+ const localInterval = (_a = this.localCollection) === null || _a === void 0 ? void 0 : _a.getIntervalById(intervalId);
1088
1279
  const rebased = {
1089
1280
  start: startRebased,
1090
1281
  end: endRebased,
1091
1282
  intervalType,
1092
- sequenceNumber: (_b = (_a = this.client) === null || _a === void 0 ? void 0 : _a.getCurrentSeq()) !== null && _b !== void 0 ? _b : 0,
1283
+ sequenceNumber: (_c = (_b = this.client) === null || _b === void 0 ? void 0 : _b.getCurrentSeq()) !== null && _c !== void 0 ? _c : 0,
1093
1284
  properties,
1094
1285
  };
1095
1286
  if (opName === "change" && (this.hasPendingChangeStart(intervalId) || this.hasPendingChangeEnd(intervalId))) {
@@ -1100,7 +1291,7 @@ class IntervalCollection extends common_utils_1.TypedEventEmitter {
1100
1291
  // delete the interval
1101
1292
  if (startRebased === merge_tree_1.DetachedReferencePosition || endRebased === merge_tree_1.DetachedReferencePosition) {
1102
1293
  if (localInterval) {
1103
- this.localCollection.removeExistingInterval(localInterval);
1294
+ (_d = this.localCollection) === null || _d === void 0 ? void 0 : _d.removeExistingInterval(localInterval);
1104
1295
  }
1105
1296
  return undefined;
1106
1297
  }
@@ -1113,14 +1304,21 @@ class IntervalCollection extends common_utils_1.TypedEventEmitter {
1113
1304
  const endSegment = this.getSlideToSegment(localInterval.end);
1114
1305
  // we need to slide because the reference has been removed
1115
1306
  if (startSegment || endSegment) {
1116
- const newStart = startSegment && this.client.getPosition(startSegment.segment, localSeq) + startSegment.offset;
1117
- const newEnd = endSegment && this.client.getPosition(endSegment.segment, localSeq) + endSegment.offset;
1118
- this.localCollection.changeInterval(localInterval, newStart, newEnd, undefined, localSeq);
1307
+ const newStart = startSegment && this.client.getPosition(startSegment.segment, localSeq) + ((_e = startSegment.offset) !== null && _e !== void 0 ? _e : 0);
1308
+ const newEnd = endSegment && this.client.getPosition(endSegment.segment, localSeq) + ((_f = endSegment.offset) !== null && _f !== void 0 ? _f : 0);
1309
+ (_g = this.localCollection) === null || _g === void 0 ? void 0 : _g.changeInterval(localInterval, newStart, newEnd, undefined, localSeq);
1119
1310
  }
1120
1311
  return rebased;
1121
1312
  }
1122
1313
  getSlideToSegment(lref) {
1314
+ var _a, _b;
1315
+ if (!this.client) {
1316
+ throw new telemetry_utils_1.LoggingError("client does not exist");
1317
+ }
1123
1318
  const segoff = { segment: lref.getSegment(), offset: lref.getOffset() };
1319
+ if (((_b = (_a = segoff.segment) === null || _a === void 0 ? void 0 : _a.localRefs) === null || _b === void 0 ? void 0 : _b.has(lref)) !== true) {
1320
+ return undefined;
1321
+ }
1124
1322
  const newSegoff = this.client.getSlideToSegment(segoff);
1125
1323
  const value = (segoff.segment === newSegoff.segment && segoff.offset === newSegoff.offset) ? undefined : newSegoff;
1126
1324
  return value;
@@ -1132,6 +1330,7 @@ class IntervalCollection extends common_utils_1.TypedEventEmitter {
1132
1330
  lref.refType = refType;
1133
1331
  }
1134
1332
  ackInterval(interval, op) {
1333
+ var _a, _b;
1135
1334
  // Only SequenceIntervals need potential sliding
1136
1335
  if (!(interval instanceof SequenceInterval)) {
1137
1336
  return;
@@ -1154,27 +1353,44 @@ class IntervalCollection extends common_utils_1.TypedEventEmitter {
1154
1353
  const needsStartUpdate = newStart !== undefined && !hasPendingStartChange;
1155
1354
  const needsEndUpdate = newEnd !== undefined && !hasPendingEndChange;
1156
1355
  if (needsStartUpdate || needsEndUpdate) {
1356
+ if (!this.localCollection) {
1357
+ throw new telemetry_utils_1.LoggingError("Attach must be called before accessing intervals");
1358
+ }
1359
+ // `interval`'s endpoints will get modified in-place, so clone it prior to doing so for event emission.
1360
+ const oldInterval = interval.clone();
1157
1361
  // In this case, where we change the start or end of an interval,
1158
1362
  // it is necessary to remove and re-add the interval listeners.
1159
1363
  // This ensures that the correct listeners are added to the LocalReferencePosition.
1160
1364
  this.localCollection.removeExistingInterval(interval);
1365
+ if (!this.client) {
1366
+ throw new telemetry_utils_1.LoggingError("client does not exist");
1367
+ }
1161
1368
  if (needsStartUpdate) {
1162
1369
  const props = interval.start.properties;
1163
- this.client.removeLocalReferencePosition(interval.start);
1164
1370
  interval.start = createPositionReferenceFromSegoff(this.client, newStart, interval.start.refType, op);
1165
1371
  if (props) {
1166
1372
  interval.start.addProperties(props);
1167
1373
  }
1374
+ const oldSeg = oldInterval.start.getSegment();
1375
+ // remove and rebuild start interval as transient for event
1376
+ this.client.removeLocalReferencePosition(oldInterval.start);
1377
+ oldInterval.start.refType = merge_tree_1.ReferenceType.Transient;
1378
+ (_a = oldSeg === null || oldSeg === void 0 ? void 0 : oldSeg.localRefs) === null || _a === void 0 ? void 0 : _a.addLocalRef(oldInterval.start, oldInterval.start.getOffset());
1168
1379
  }
1169
1380
  if (needsEndUpdate) {
1170
1381
  const props = interval.end.properties;
1171
- this.client.removeLocalReferencePosition(interval.end);
1172
1382
  interval.end = createPositionReferenceFromSegoff(this.client, newEnd, interval.end.refType, op);
1173
1383
  if (props) {
1174
1384
  interval.end.addProperties(props);
1175
1385
  }
1386
+ // remove and rebuild end interval as transient for event
1387
+ const oldSeg = oldInterval.end.getSegment();
1388
+ this.client.removeLocalReferencePosition(oldInterval.end);
1389
+ oldInterval.end.refType = merge_tree_1.ReferenceType.Transient;
1390
+ (_b = oldSeg === null || oldSeg === void 0 ? void 0 : oldSeg.localRefs) === null || _b === void 0 ? void 0 : _b.addLocalRef(oldInterval.end, oldInterval.end.getOffset());
1176
1391
  }
1177
1392
  this.localCollection.add(interval);
1393
+ this.emitChange(interval, oldInterval, true, op);
1178
1394
  }
1179
1395
  }
1180
1396
  /** @internal */
@@ -1188,7 +1404,7 @@ class IntervalCollection extends common_utils_1.TypedEventEmitter {
1188
1404
  }
1189
1405
  return;
1190
1406
  }
1191
- if (!this.attached) {
1407
+ if (!this.localCollection) {
1192
1408
  throw new telemetry_utils_1.LoggingError("attachSequence must be called");
1193
1409
  }
1194
1410
  this.localCollection.ensureSerializedId(serializedInterval);
@@ -1209,7 +1425,7 @@ class IntervalCollection extends common_utils_1.TypedEventEmitter {
1209
1425
  // locally deleted interval whenever a lookup happens.
1210
1426
  return;
1211
1427
  }
1212
- if (!this.attached) {
1428
+ if (!this.localCollection) {
1213
1429
  throw new telemetry_utils_1.LoggingError("attach must be called prior to deleting intervals");
1214
1430
  }
1215
1431
  const id = this.localCollection.ensureSerializedId(serializedInterval);
@@ -1222,57 +1438,87 @@ class IntervalCollection extends common_utils_1.TypedEventEmitter {
1222
1438
  * @internal
1223
1439
  */
1224
1440
  serializeInternal() {
1225
- if (!this.attached) {
1441
+ if (!this.localCollection) {
1226
1442
  throw new telemetry_utils_1.LoggingError("attachSequence must be called");
1227
1443
  }
1228
1444
  return this.localCollection.serialize();
1229
1445
  }
1446
+ /**
1447
+ * @returns an iterator over all intervals in this collection.
1448
+ */
1230
1449
  [Symbol.iterator]() {
1231
1450
  const iterator = new IntervalCollectionIterator(this);
1232
1451
  return iterator;
1233
1452
  }
1453
+ /**
1454
+ * @returns a forward iterator over all intervals in this collection with start point equal to `startPosition`.
1455
+ */
1234
1456
  CreateForwardIteratorWithStartPosition(startPosition) {
1235
1457
  const iterator = new IntervalCollectionIterator(this, true, startPosition);
1236
1458
  return iterator;
1237
1459
  }
1460
+ /**
1461
+ * @returns a backward iterator over all intervals in this collection with start point equal to `startPosition`.
1462
+ */
1238
1463
  CreateBackwardIteratorWithStartPosition(startPosition) {
1239
1464
  const iterator = new IntervalCollectionIterator(this, false, startPosition);
1240
1465
  return iterator;
1241
1466
  }
1467
+ /**
1468
+ * @returns a forward iterator over all intervals in this collection with end point equal to `endPosition`.
1469
+ */
1242
1470
  CreateForwardIteratorWithEndPosition(endPosition) {
1243
1471
  const iterator = new IntervalCollectionIterator(this, true, undefined, endPosition);
1244
1472
  return iterator;
1245
1473
  }
1474
+ /**
1475
+ * @returns a backward iterator over all intervals in this collection with end point equal to `endPosition`.
1476
+ */
1246
1477
  CreateBackwardIteratorWithEndPosition(endPosition) {
1247
1478
  const iterator = new IntervalCollectionIterator(this, false, undefined, endPosition);
1248
1479
  return iterator;
1249
1480
  }
1481
+ /**
1482
+ * Gathers iteration results that optionally match a start/end criteria into the provided array.
1483
+ * @param results - Array to gather the results into. In lieu of a return value, this array will be populated with
1484
+ * intervals matching the query upon edit.
1485
+ * @param iteratesForward - whether or not iteration should be in the forward direction
1486
+ * @param start - If provided, only match intervals whose start point is equal to `start`.
1487
+ * @param end - If provided, only match intervals whose end point is equal to `end`.
1488
+ */
1250
1489
  gatherIterationResults(results, iteratesForward, start, end) {
1251
- if (!this.attached) {
1490
+ if (!this.localCollection) {
1252
1491
  return;
1253
1492
  }
1254
1493
  this.localCollection.gatherIterationResults(results, iteratesForward, start, end);
1255
1494
  }
1495
+ /**
1496
+ * @returns an array of all intervals in this collection that overlap with the interval
1497
+ * `[startPosition, endPosition]`.
1498
+ */
1256
1499
  findOverlappingIntervals(startPosition, endPosition) {
1257
- if (!this.attached) {
1500
+ if (!this.localCollection) {
1258
1501
  throw new telemetry_utils_1.LoggingError("attachSequence must be called");
1259
1502
  }
1260
1503
  return this.localCollection.findOverlappingIntervals(startPosition, endPosition);
1261
1504
  }
1505
+ /**
1506
+ * Applies a function to each interval in this collection.
1507
+ */
1262
1508
  map(fn) {
1263
- if (!this.attached) {
1509
+ if (!this.localCollection) {
1264
1510
  throw new telemetry_utils_1.LoggingError("attachSequence must be called");
1265
1511
  }
1266
1512
  this.localCollection.map(fn);
1267
1513
  }
1268
1514
  previousInterval(pos) {
1269
- if (!this.attached) {
1515
+ if (!this.localCollection) {
1270
1516
  throw new telemetry_utils_1.LoggingError("attachSequence must be called");
1271
1517
  }
1272
1518
  return this.localCollection.previousInterval(pos);
1273
1519
  }
1274
1520
  nextInterval(pos) {
1275
- if (!this.attached) {
1521
+ if (!this.localCollection) {
1276
1522
  throw new telemetry_utils_1.LoggingError("attachSequence must be called");
1277
1523
  }
1278
1524
  return this.localCollection.nextInterval(pos);