@fluidframework/sequence 0.59.4001 → 1.1.0-75972
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.
- package/.eslintrc.js +1 -1
- package/README.md +18 -6
- package/dist/defaultMap.d.ts +2 -6
- package/dist/defaultMap.d.ts.map +1 -1
- package/dist/defaultMap.js +27 -37
- package/dist/defaultMap.js.map +1 -1
- package/dist/defaultMapInterfaces.d.ts +24 -3
- package/dist/defaultMapInterfaces.d.ts.map +1 -1
- package/dist/defaultMapInterfaces.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/intervalCollection.d.ts +93 -8
- package/dist/intervalCollection.d.ts.map +1 -1
- package/dist/intervalCollection.js +434 -178
- package/dist/intervalCollection.js.map +1 -1
- package/dist/packageVersion.d.ts +1 -1
- package/dist/packageVersion.d.ts.map +1 -1
- package/dist/packageVersion.js +1 -1
- package/dist/packageVersion.js.map +1 -1
- package/dist/sequence.d.ts +6 -7
- package/dist/sequence.d.ts.map +1 -1
- package/dist/sequence.js +17 -21
- package/dist/sequence.js.map +1 -1
- package/dist/sharedIntervalCollection.d.ts.map +1 -1
- package/dist/sharedIntervalCollection.js +2 -2
- package/dist/sharedIntervalCollection.js.map +1 -1
- package/dist/sharedSequence.js.map +1 -1
- package/dist/sparsematrix.js +2 -2
- package/dist/sparsematrix.js.map +1 -1
- package/lib/defaultMap.d.ts +2 -6
- package/lib/defaultMap.d.ts.map +1 -1
- package/lib/defaultMap.js +27 -37
- package/lib/defaultMap.js.map +1 -1
- package/lib/defaultMapInterfaces.d.ts +24 -3
- package/lib/defaultMapInterfaces.d.ts.map +1 -1
- package/lib/defaultMapInterfaces.js.map +1 -1
- package/lib/index.d.ts +2 -2
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js.map +1 -1
- package/lib/intervalCollection.d.ts +93 -8
- package/lib/intervalCollection.d.ts.map +1 -1
- package/lib/intervalCollection.js +435 -179
- package/lib/intervalCollection.js.map +1 -1
- package/lib/packageVersion.d.ts +1 -1
- package/lib/packageVersion.d.ts.map +1 -1
- package/lib/packageVersion.js +1 -1
- package/lib/packageVersion.js.map +1 -1
- package/lib/sequence.d.ts +6 -7
- package/lib/sequence.d.ts.map +1 -1
- package/lib/sequence.js +18 -22
- package/lib/sequence.js.map +1 -1
- package/lib/sharedIntervalCollection.d.ts.map +1 -1
- package/lib/sharedIntervalCollection.js +2 -2
- package/lib/sharedIntervalCollection.js.map +1 -1
- package/lib/sharedSequence.js.map +1 -1
- package/lib/sparsematrix.js +2 -2
- package/lib/sparsematrix.js.map +1 -1
- package/package.json +23 -45
- package/src/defaultMap.ts +39 -41
- package/src/defaultMapInterfaces.ts +28 -3
- package/src/index.ts +3 -0
- package/src/intervalCollection.ts +575 -211
- package/src/packageVersion.ts +1 -1
- package/src/sequence.ts +36 -38
- package/src/sharedIntervalCollection.ts +4 -3
- package/src/sharedSequence.ts +1 -1
- package/src/sparsematrix.ts +2 -2
|
@@ -18,20 +18,61 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
18
18
|
exports.IntervalCollection = exports.IntervalCollectionIterator = exports.IntervalCollectionValueType = exports.SequenceIntervalCollectionValueType = exports.LocalIntervalCollection = exports.createIntervalIndex = exports.defaultIntervalConflictResolver = 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
|
+
const container_utils_1 = require("@fluidframework/container-utils");
|
|
21
22
|
const merge_tree_1 = require("@fluidframework/merge-tree");
|
|
23
|
+
const telemetry_utils_1 = require("@fluidframework/telemetry-utils");
|
|
22
24
|
const uuid_1 = require("uuid");
|
|
23
25
|
const reservedIntervalIdKey = "intervalId";
|
|
24
26
|
var IntervalType;
|
|
25
27
|
(function (IntervalType) {
|
|
26
28
|
IntervalType[IntervalType["Simple"] = 0] = "Simple";
|
|
27
29
|
IntervalType[IntervalType["Nest"] = 1] = "Nest";
|
|
30
|
+
/**
|
|
31
|
+
* SlideOnRemove indicates that the ends of the interval will slide if the segment
|
|
32
|
+
* they reference is removed and acked.
|
|
33
|
+
* See `packages\dds\merge-tree\REFERENCEPOSITIONS.md` for details
|
|
34
|
+
* SlideOnRemove is the default interval behavior and does not need to be specified.
|
|
35
|
+
*/
|
|
28
36
|
IntervalType[IntervalType["SlideOnRemove"] = 2] = "SlideOnRemove";
|
|
37
|
+
/**
|
|
38
|
+
* @internal
|
|
39
|
+
* A temporary interval, used internally
|
|
40
|
+
*/
|
|
29
41
|
IntervalType[IntervalType["Transient"] = 4] = "Transient";
|
|
30
42
|
})(IntervalType = exports.IntervalType || (exports.IntervalType = {}));
|
|
43
|
+
/**
|
|
44
|
+
* Decompress an interval after loading a summary from JSON. The exact format
|
|
45
|
+
* of this compression is unspecified and subject to change
|
|
46
|
+
*/
|
|
47
|
+
function decompressInterval(interval, label) {
|
|
48
|
+
return {
|
|
49
|
+
start: interval[0],
|
|
50
|
+
end: interval[1],
|
|
51
|
+
sequenceNumber: interval[2],
|
|
52
|
+
intervalType: interval[3],
|
|
53
|
+
properties: Object.assign(Object.assign({}, interval[4]), { [merge_tree_1.reservedRangeLabelsKey]: label }),
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Compress an interval prior to serialization as JSON. The exact format of this
|
|
58
|
+
* compression is unspecified and subject to change
|
|
59
|
+
*/
|
|
60
|
+
function compressInterval(interval) {
|
|
61
|
+
const { start, end, sequenceNumber, intervalType, properties } = interval;
|
|
62
|
+
return [
|
|
63
|
+
start,
|
|
64
|
+
end,
|
|
65
|
+
sequenceNumber,
|
|
66
|
+
intervalType,
|
|
67
|
+
Object.assign(Object.assign({}, properties), { [merge_tree_1.reservedRangeLabelsKey]: undefined }),
|
|
68
|
+
];
|
|
69
|
+
}
|
|
31
70
|
class Interval {
|
|
32
71
|
constructor(start, end, props) {
|
|
33
72
|
this.start = start;
|
|
34
73
|
this.end = end;
|
|
74
|
+
this.propertyManager = new merge_tree_1.PropertiesManager();
|
|
75
|
+
this.properties = {};
|
|
35
76
|
if (props) {
|
|
36
77
|
this.addProperties(props);
|
|
37
78
|
}
|
|
@@ -114,12 +155,7 @@ class Interval {
|
|
|
114
155
|
}
|
|
115
156
|
addProperties(newProps, collaborating = false, seq, op) {
|
|
116
157
|
if (newProps) {
|
|
117
|
-
|
|
118
|
-
this.propertyManager = new merge_tree_1.PropertiesManager();
|
|
119
|
-
}
|
|
120
|
-
if (!this.properties) {
|
|
121
|
-
this.properties = (0, merge_tree_1.createMap)();
|
|
122
|
-
}
|
|
158
|
+
this.initializeProperties();
|
|
123
159
|
return this.propertyManager.addProperties(this.properties, newProps, op, seq, collaborating);
|
|
124
160
|
}
|
|
125
161
|
}
|
|
@@ -130,7 +166,20 @@ class Interval {
|
|
|
130
166
|
// Return undefined to indicate that no change is necessary.
|
|
131
167
|
return;
|
|
132
168
|
}
|
|
133
|
-
|
|
169
|
+
const newInterval = new Interval(startPos, endPos);
|
|
170
|
+
if (this.properties) {
|
|
171
|
+
newInterval.initializeProperties();
|
|
172
|
+
this.propertyManager.copyTo(this.properties, newInterval.properties, newInterval.propertyManager);
|
|
173
|
+
}
|
|
174
|
+
return newInterval;
|
|
175
|
+
}
|
|
176
|
+
initializeProperties() {
|
|
177
|
+
if (!this.propertyManager) {
|
|
178
|
+
this.propertyManager = new merge_tree_1.PropertiesManager();
|
|
179
|
+
}
|
|
180
|
+
if (!this.properties) {
|
|
181
|
+
this.properties = (0, merge_tree_1.createMap)();
|
|
182
|
+
}
|
|
134
183
|
}
|
|
135
184
|
}
|
|
136
185
|
exports.Interval = Interval;
|
|
@@ -139,10 +188,41 @@ class SequenceInterval {
|
|
|
139
188
|
this.start = start;
|
|
140
189
|
this.end = end;
|
|
141
190
|
this.intervalType = intervalType;
|
|
191
|
+
this.propertyManager = new merge_tree_1.PropertiesManager();
|
|
192
|
+
this.properties = {};
|
|
142
193
|
if (props) {
|
|
143
194
|
this.addProperties(props);
|
|
144
195
|
}
|
|
145
196
|
}
|
|
197
|
+
/**
|
|
198
|
+
* @internal
|
|
199
|
+
* Subscribes to position change events on this interval if there are no current listeners.
|
|
200
|
+
*/
|
|
201
|
+
addPositionChangeListeners(beforePositionChange, afterPositionChange) {
|
|
202
|
+
var _a, _b;
|
|
203
|
+
var _c, _d;
|
|
204
|
+
if (this.callbacks === undefined) {
|
|
205
|
+
this.callbacks = {
|
|
206
|
+
beforePositionChange,
|
|
207
|
+
afterPositionChange,
|
|
208
|
+
};
|
|
209
|
+
const startCbs = (_a = (_c = this.start).callbacks) !== null && _a !== void 0 ? _a : (_c.callbacks = {});
|
|
210
|
+
const endCbs = (_b = (_d = this.end).callbacks) !== null && _b !== void 0 ? _b : (_d.callbacks = {});
|
|
211
|
+
startCbs.beforeSlide = endCbs.beforeSlide = beforePositionChange;
|
|
212
|
+
startCbs.afterSlide = endCbs.afterSlide = afterPositionChange;
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
/**
|
|
216
|
+
* @internal
|
|
217
|
+
* Removes the currently subscribed position change listeners.
|
|
218
|
+
*/
|
|
219
|
+
removePositionChangeListeners() {
|
|
220
|
+
if (this.callbacks) {
|
|
221
|
+
this.callbacks = undefined;
|
|
222
|
+
this.start.callbacks = undefined;
|
|
223
|
+
this.end.callbacks = undefined;
|
|
224
|
+
}
|
|
225
|
+
}
|
|
146
226
|
serialize(client) {
|
|
147
227
|
const startPosition = this.start.toPosition();
|
|
148
228
|
const endPosition = this.end.toPosition();
|
|
@@ -206,12 +286,7 @@ class SequenceInterval {
|
|
|
206
286
|
return new SequenceInterval(this.start.min(b.start), this.end.max(b.end), this.intervalType);
|
|
207
287
|
}
|
|
208
288
|
addProperties(newProps, collab = false, seq, op) {
|
|
209
|
-
|
|
210
|
-
this.propertyManager = new merge_tree_1.PropertiesManager();
|
|
211
|
-
}
|
|
212
|
-
if (!this.properties) {
|
|
213
|
-
this.properties = (0, merge_tree_1.createMap)();
|
|
214
|
-
}
|
|
289
|
+
this.initializeProperties();
|
|
215
290
|
return this.propertyManager.addProperties(this.properties, newProps, op, seq, collab);
|
|
216
291
|
}
|
|
217
292
|
overlapsPos(bstart, bend) {
|
|
@@ -220,60 +295,103 @@ class SequenceInterval {
|
|
|
220
295
|
return (endPos > bstart) && (startPos < bend);
|
|
221
296
|
}
|
|
222
297
|
modify(label, start, end, op) {
|
|
223
|
-
const
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
298
|
+
const getRefType = (baseType) => {
|
|
299
|
+
let refType = baseType;
|
|
300
|
+
if (op === undefined) {
|
|
301
|
+
refType &= ~merge_tree_1.ReferenceType.SlideOnRemove;
|
|
302
|
+
refType |= merge_tree_1.ReferenceType.StayOnRemove;
|
|
303
|
+
}
|
|
304
|
+
return refType;
|
|
305
|
+
};
|
|
306
|
+
let startRef = this.start;
|
|
307
|
+
if (start !== undefined) {
|
|
308
|
+
startRef = createPositionReference(this.start.getClient(), start, getRefType(this.start.refType), op);
|
|
309
|
+
startRef.addProperties(this.start.properties);
|
|
310
|
+
}
|
|
311
|
+
let endRef = this.end;
|
|
312
|
+
if (end !== undefined) {
|
|
313
|
+
endRef = createPositionReference(this.end.getClient(), end, getRefType(this.end.refType), op);
|
|
314
|
+
endRef.addProperties(this.end.properties);
|
|
315
|
+
}
|
|
316
|
+
startRef.pairedRef = endRef;
|
|
317
|
+
endRef.pairedRef = startRef;
|
|
318
|
+
const newInterval = new SequenceInterval(startRef, endRef, this.intervalType);
|
|
230
319
|
if (this.properties) {
|
|
231
|
-
newInterval.
|
|
320
|
+
newInterval.initializeProperties();
|
|
321
|
+
this.propertyManager.copyTo(this.properties, newInterval.properties, newInterval.propertyManager);
|
|
232
322
|
}
|
|
233
323
|
return newInterval;
|
|
234
324
|
}
|
|
325
|
+
initializeProperties() {
|
|
326
|
+
if (!this.propertyManager) {
|
|
327
|
+
this.propertyManager = new merge_tree_1.PropertiesManager();
|
|
328
|
+
}
|
|
329
|
+
if (!this.properties) {
|
|
330
|
+
this.properties = (0, merge_tree_1.createMap)();
|
|
331
|
+
}
|
|
332
|
+
}
|
|
235
333
|
}
|
|
236
334
|
exports.SequenceInterval = SequenceInterval;
|
|
237
|
-
function
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
335
|
+
function createPositionReferenceFromSegoff(client, segoff, refType, op) {
|
|
336
|
+
if (segoff.segment) {
|
|
337
|
+
const ref = client.createLocalReferencePosition(segoff.segment, segoff.offset, refType, undefined);
|
|
338
|
+
return ref;
|
|
339
|
+
}
|
|
340
|
+
else {
|
|
341
|
+
if (!op && !(0, merge_tree_1.refTypeIncludesFlag)(refType, merge_tree_1.ReferenceType.Transient)) {
|
|
342
|
+
throw new container_utils_1.UsageError("Non-transient references need segment");
|
|
243
343
|
}
|
|
244
|
-
return
|
|
344
|
+
return new merge_tree_1.LocalReference(client, undefined, 0, refType);
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
function createPositionReference(client, pos, refType, op) {
|
|
348
|
+
let segoff;
|
|
349
|
+
if (op) {
|
|
350
|
+
(0, common_utils_1.assert)((refType & merge_tree_1.ReferenceType.SlideOnRemove) !== 0, 0x2f5 /* op create references must be SlideOnRemove */);
|
|
351
|
+
segoff = client.getContainingSegment(pos, op);
|
|
352
|
+
segoff = client.getSlideToSegment(segoff);
|
|
353
|
+
}
|
|
354
|
+
else {
|
|
355
|
+
(0, common_utils_1.assert)((refType & merge_tree_1.ReferenceType.SlideOnRemove) === 0, 0x2f6 /* SlideOnRemove references must be op created */);
|
|
356
|
+
segoff = client.getContainingSegment(pos);
|
|
245
357
|
}
|
|
246
|
-
return
|
|
358
|
+
return createPositionReferenceFromSegoff(client, segoff, refType, op);
|
|
247
359
|
}
|
|
248
360
|
function createSequenceInterval(label, start, end, client, intervalType, op) {
|
|
249
361
|
let beginRefType = merge_tree_1.ReferenceType.RangeBegin;
|
|
250
362
|
let endRefType = merge_tree_1.ReferenceType.RangeEnd;
|
|
251
|
-
if (intervalType === IntervalType.
|
|
252
|
-
beginRefType = merge_tree_1.ReferenceType.NestBegin;
|
|
253
|
-
endRefType = merge_tree_1.ReferenceType.NestEnd;
|
|
254
|
-
}
|
|
255
|
-
else if (intervalType === IntervalType.Transient) {
|
|
363
|
+
if (intervalType === IntervalType.Transient) {
|
|
256
364
|
beginRefType = merge_tree_1.ReferenceType.Transient;
|
|
257
365
|
endRefType = merge_tree_1.ReferenceType.Transient;
|
|
258
366
|
}
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
367
|
+
else {
|
|
368
|
+
if (intervalType === IntervalType.Nest) {
|
|
369
|
+
beginRefType = merge_tree_1.ReferenceType.NestBegin;
|
|
370
|
+
endRefType = merge_tree_1.ReferenceType.NestEnd;
|
|
371
|
+
}
|
|
372
|
+
// All non-transient interval references must eventually be SlideOnRemove
|
|
373
|
+
// To ensure eventual consistency, they must start as StayOnRemove when
|
|
374
|
+
// pending (created locally and creation op is not acked)
|
|
375
|
+
if (op) {
|
|
376
|
+
beginRefType |= merge_tree_1.ReferenceType.SlideOnRemove;
|
|
377
|
+
endRefType |= merge_tree_1.ReferenceType.SlideOnRemove;
|
|
378
|
+
}
|
|
379
|
+
else {
|
|
380
|
+
beginRefType |= merge_tree_1.ReferenceType.StayOnRemove;
|
|
381
|
+
endRefType |= merge_tree_1.ReferenceType.StayOnRemove;
|
|
382
|
+
}
|
|
263
383
|
}
|
|
264
384
|
const startLref = createPositionReference(client, start, beginRefType, op);
|
|
265
385
|
const endLref = createPositionReference(client, end, endRefType, op);
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
return ival;
|
|
276
|
-
}
|
|
386
|
+
startLref.pairedRef = endLref;
|
|
387
|
+
endLref.pairedRef = startLref;
|
|
388
|
+
const rangeProp = {
|
|
389
|
+
[merge_tree_1.reservedRangeLabelsKey]: [label],
|
|
390
|
+
};
|
|
391
|
+
startLref.addProperties(rangeProp);
|
|
392
|
+
endLref.addProperties(rangeProp);
|
|
393
|
+
const ival = new SequenceInterval(startLref, endLref, intervalType, rangeProp);
|
|
394
|
+
return ival;
|
|
277
395
|
}
|
|
278
396
|
function defaultIntervalConflictResolver(a, b) {
|
|
279
397
|
a.addPropertySet(b.properties);
|
|
@@ -296,11 +414,15 @@ function createIntervalIndex(conflict) {
|
|
|
296
414
|
}
|
|
297
415
|
exports.createIntervalIndex = createIntervalIndex;
|
|
298
416
|
class LocalIntervalCollection {
|
|
299
|
-
constructor(client, label, helpers
|
|
417
|
+
constructor(client, label, helpers,
|
|
418
|
+
/** Callback invoked each time one of the endpoints of an interval slides. */
|
|
419
|
+
onPositionChange) {
|
|
300
420
|
this.client = client;
|
|
301
421
|
this.label = label;
|
|
302
422
|
this.helpers = helpers;
|
|
423
|
+
this.onPositionChange = onPositionChange;
|
|
303
424
|
this.intervalTree = new merge_tree_1.IntervalTree();
|
|
425
|
+
this.intervalIdMap = new Map();
|
|
304
426
|
// eslint-disable-next-line @typescript-eslint/unbound-method
|
|
305
427
|
this.endIntervalTree = new merge_tree_1.RedBlackTree(helpers.compareEnds);
|
|
306
428
|
}
|
|
@@ -308,7 +430,7 @@ class LocalIntervalCollection {
|
|
|
308
430
|
this.conflictResolver = conflictResolver;
|
|
309
431
|
this.endConflictResolver =
|
|
310
432
|
(key, currentKey) => {
|
|
311
|
-
const ival =
|
|
433
|
+
const ival = conflictResolver(key, currentKey);
|
|
312
434
|
return {
|
|
313
435
|
data: ival,
|
|
314
436
|
key: ival,
|
|
@@ -323,13 +445,22 @@ class LocalIntervalCollection {
|
|
|
323
445
|
// without ID's.
|
|
324
446
|
return `${LocalIntervalCollection.legacyIdPrefix}${start}-${end}`;
|
|
325
447
|
}
|
|
448
|
+
/**
|
|
449
|
+
* Validates that a serialized interval has the ID property. Creates an ID
|
|
450
|
+
* if one does not already exist
|
|
451
|
+
*
|
|
452
|
+
* @param serializedInterval - The interval to be checked
|
|
453
|
+
* @returns The interval's existing or newly created id
|
|
454
|
+
*/
|
|
326
455
|
ensureSerializedId(serializedInterval) {
|
|
327
456
|
var _a;
|
|
328
|
-
|
|
457
|
+
let id = (_a = serializedInterval.properties) === null || _a === void 0 ? void 0 : _a[reservedIntervalIdKey];
|
|
458
|
+
if (id === undefined) {
|
|
329
459
|
// An interval came over the wire without an ID, so create a non-unique one based on start/end.
|
|
330
460
|
// This will allow all clients to refer to this interval consistently.
|
|
461
|
+
id = this.createLegacyId(serializedInterval.start, serializedInterval.end);
|
|
331
462
|
const newProps = {
|
|
332
|
-
[reservedIntervalIdKey]:
|
|
463
|
+
[reservedIntervalIdKey]: id,
|
|
333
464
|
};
|
|
334
465
|
serializedInterval.properties = (0, merge_tree_1.addProperties)(serializedInterval.properties, newProps);
|
|
335
466
|
}
|
|
@@ -339,6 +470,7 @@ class LocalIntervalCollection {
|
|
|
339
470
|
enumerable: true,
|
|
340
471
|
writable: false,
|
|
341
472
|
});
|
|
473
|
+
return id;
|
|
342
474
|
}
|
|
343
475
|
mapUntil(fn) {
|
|
344
476
|
this.intervalTree.mapUntil(fn);
|
|
@@ -405,14 +537,12 @@ class LocalIntervalCollection {
|
|
|
405
537
|
}
|
|
406
538
|
}
|
|
407
539
|
findOverlappingIntervals(startPosition, endPosition) {
|
|
408
|
-
if (
|
|
409
|
-
const transientInterval = this.helpers.create("transient", startPosition, endPosition, this.client, IntervalType.Transient);
|
|
410
|
-
const overlappingIntervalNodes = this.intervalTree.match(transientInterval);
|
|
411
|
-
return overlappingIntervalNodes.map((node) => node.key);
|
|
412
|
-
}
|
|
413
|
-
else {
|
|
540
|
+
if (endPosition < startPosition || this.intervalTree.intervals.isEmpty()) {
|
|
414
541
|
return [];
|
|
415
542
|
}
|
|
543
|
+
const transientInterval = this.helpers.create("transient", startPosition, endPosition, this.client, IntervalType.Transient);
|
|
544
|
+
const overlappingIntervalNodes = this.intervalTree.match(transientInterval);
|
|
545
|
+
return overlappingIntervalNodes.map((node) => node.key);
|
|
416
546
|
}
|
|
417
547
|
previousInterval(pos) {
|
|
418
548
|
const transientInterval = this.helpers.create("transient", pos, pos, this.client, IntervalType.Transient);
|
|
@@ -434,9 +564,16 @@ class LocalIntervalCollection {
|
|
|
434
564
|
this.endIntervalTree.remove(transientInterval);
|
|
435
565
|
return transientInterval;
|
|
436
566
|
}
|
|
437
|
-
|
|
567
|
+
removeIntervalFromIndex(interval) {
|
|
438
568
|
this.intervalTree.removeExisting(interval);
|
|
439
569
|
this.endIntervalTree.remove(interval);
|
|
570
|
+
const id = interval.getIntervalId();
|
|
571
|
+
(0, common_utils_1.assert)(id !== undefined, 0x311 /* expected id to exist on interval */);
|
|
572
|
+
this.intervalIdMap.delete(id);
|
|
573
|
+
}
|
|
574
|
+
removeExistingInterval(interval) {
|
|
575
|
+
this.removeIntervalFromIndex(interval);
|
|
576
|
+
this.removeIntervalListeners(interval);
|
|
440
577
|
}
|
|
441
578
|
createInterval(start, end, intervalType, op) {
|
|
442
579
|
return this.helpers.create(this.label, start, end, this.client, intervalType, op);
|
|
@@ -458,8 +595,9 @@ class LocalIntervalCollection {
|
|
|
458
595
|
}
|
|
459
596
|
return interval;
|
|
460
597
|
}
|
|
461
|
-
|
|
462
|
-
|
|
598
|
+
addIntervalToIndex(interval) {
|
|
599
|
+
const id = interval.getIntervalId();
|
|
600
|
+
(0, common_utils_1.assert)(id !== undefined, 0x2c0 /* "ID must be created before adding interval to collection" */);
|
|
463
601
|
// Make the ID immutable.
|
|
464
602
|
Object.defineProperty(interval.properties, reservedIntervalIdKey, {
|
|
465
603
|
configurable: false,
|
|
@@ -468,17 +606,14 @@ class LocalIntervalCollection {
|
|
|
468
606
|
});
|
|
469
607
|
this.intervalTree.put(interval, this.conflictResolver);
|
|
470
608
|
this.endIntervalTree.put(interval, interval, this.endConflictResolver);
|
|
609
|
+
this.intervalIdMap.set(id, interval);
|
|
610
|
+
}
|
|
611
|
+
add(interval) {
|
|
612
|
+
this.addIntervalToIndex(interval);
|
|
613
|
+
this.addIntervalListeners(interval);
|
|
471
614
|
}
|
|
472
615
|
getIntervalById(id) {
|
|
473
|
-
|
|
474
|
-
this.mapUntil((interval) => {
|
|
475
|
-
if (interval.getIntervalId() === id) {
|
|
476
|
-
result = interval;
|
|
477
|
-
return false;
|
|
478
|
-
}
|
|
479
|
-
return true;
|
|
480
|
-
});
|
|
481
|
-
return result;
|
|
616
|
+
return this.intervalIdMap.get(id);
|
|
482
617
|
}
|
|
483
618
|
changeInterval(interval, start, end, op) {
|
|
484
619
|
const newInterval = interval.modify(this.label, start, end, op);
|
|
@@ -491,7 +626,25 @@ class LocalIntervalCollection {
|
|
|
491
626
|
serialize() {
|
|
492
627
|
const client = this.client;
|
|
493
628
|
const intervals = this.intervalTree.intervals.keys();
|
|
494
|
-
return
|
|
629
|
+
return {
|
|
630
|
+
label: this.label,
|
|
631
|
+
intervals: intervals.map((interval) => compressInterval(interval.serialize(client))),
|
|
632
|
+
version: 2,
|
|
633
|
+
};
|
|
634
|
+
}
|
|
635
|
+
addIntervalListeners(interval) {
|
|
636
|
+
if (interval instanceof SequenceInterval) {
|
|
637
|
+
interval.addPositionChangeListeners(() => this.removeIntervalFromIndex(interval), () => {
|
|
638
|
+
var _a;
|
|
639
|
+
this.addIntervalToIndex(interval);
|
|
640
|
+
(_a = this.onPositionChange) === null || _a === void 0 ? void 0 : _a.call(this, interval);
|
|
641
|
+
});
|
|
642
|
+
}
|
|
643
|
+
}
|
|
644
|
+
removeIntervalListeners(interval) {
|
|
645
|
+
if (interval instanceof SequenceInterval) {
|
|
646
|
+
interval.removePositionChangeListeners();
|
|
647
|
+
}
|
|
495
648
|
}
|
|
496
649
|
}
|
|
497
650
|
exports.LocalIntervalCollection = LocalIntervalCollection;
|
|
@@ -523,37 +676,12 @@ class SequenceIntervalCollectionValueType {
|
|
|
523
676
|
exports.SequenceIntervalCollectionValueType = SequenceIntervalCollectionValueType;
|
|
524
677
|
SequenceIntervalCollectionValueType.Name = "sharedStringIntervalCollection";
|
|
525
678
|
SequenceIntervalCollectionValueType._factory = new SequenceIntervalCollectionFactory();
|
|
526
|
-
SequenceIntervalCollectionValueType._ops =
|
|
527
|
-
"add",
|
|
528
|
-
{
|
|
529
|
-
process: (value, params, local, op) => {
|
|
530
|
-
value.ackAdd(params, local, op);
|
|
531
|
-
},
|
|
532
|
-
},
|
|
533
|
-
],
|
|
534
|
-
[
|
|
535
|
-
"delete",
|
|
536
|
-
{
|
|
537
|
-
process: (value, params, local, op) => {
|
|
538
|
-
value.ackDelete(params, local, op);
|
|
539
|
-
},
|
|
540
|
-
},
|
|
541
|
-
],
|
|
542
|
-
[
|
|
543
|
-
"change",
|
|
544
|
-
{
|
|
545
|
-
process: (value, params, local, op) => {
|
|
546
|
-
value.ackChange(params, local, op);
|
|
547
|
-
},
|
|
548
|
-
},
|
|
549
|
-
]]);
|
|
679
|
+
SequenceIntervalCollectionValueType._ops = makeOpsMap();
|
|
550
680
|
const compareIntervalEnds = (a, b) => a.end - b.end;
|
|
551
681
|
function createInterval(label, start, end, client) {
|
|
552
|
-
|
|
553
|
-
if (label &&
|
|
554
|
-
rangeProp =
|
|
555
|
-
[merge_tree_1.reservedRangeLabelsKey]: [label],
|
|
556
|
-
};
|
|
682
|
+
const rangeProp = {};
|
|
683
|
+
if (label && label.length > 0) {
|
|
684
|
+
rangeProp[merge_tree_1.reservedRangeLabelsKey] = [label];
|
|
557
685
|
}
|
|
558
686
|
return new Interval(start, end, rangeProp);
|
|
559
687
|
}
|
|
@@ -585,30 +713,45 @@ class IntervalCollectionValueType {
|
|
|
585
713
|
exports.IntervalCollectionValueType = IntervalCollectionValueType;
|
|
586
714
|
IntervalCollectionValueType.Name = "sharedIntervalCollection";
|
|
587
715
|
IntervalCollectionValueType._factory = new IntervalCollectionFactory();
|
|
588
|
-
IntervalCollectionValueType._ops =
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
716
|
+
IntervalCollectionValueType._ops = makeOpsMap();
|
|
717
|
+
function makeOpsMap() {
|
|
718
|
+
const rebase = (collection, op, localOpMetadata) => {
|
|
719
|
+
const { localSeq } = localOpMetadata;
|
|
720
|
+
const rebasedValue = collection.rebaseLocalInterval(op.opName, op.value, localSeq);
|
|
721
|
+
const rebasedOp = Object.assign(Object.assign({}, op), { value: rebasedValue });
|
|
722
|
+
return { rebasedOp, rebasedLocalOpMetadata: localOpMetadata };
|
|
723
|
+
};
|
|
724
|
+
return new Map([[
|
|
725
|
+
"add",
|
|
726
|
+
{
|
|
727
|
+
process: (collection, params, local, op) => {
|
|
728
|
+
collection.ackAdd(params, local, op);
|
|
729
|
+
},
|
|
730
|
+
rebase,
|
|
593
731
|
},
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
732
|
+
],
|
|
733
|
+
[
|
|
734
|
+
"delete",
|
|
735
|
+
{
|
|
736
|
+
process: (collection, params, local, op) => {
|
|
737
|
+
collection.ackDelete(params, local, op);
|
|
738
|
+
},
|
|
739
|
+
rebase: (collection, op, localOpMetadata) => {
|
|
740
|
+
// Deletion of intervals is based on id, so requires no rebasing.
|
|
741
|
+
return { rebasedOp: op, rebasedLocalOpMetadata: localOpMetadata };
|
|
742
|
+
},
|
|
601
743
|
},
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
744
|
+
],
|
|
745
|
+
[
|
|
746
|
+
"change",
|
|
747
|
+
{
|
|
748
|
+
process: (collection, params, local, op) => {
|
|
749
|
+
collection.ackChange(params, local, op);
|
|
750
|
+
},
|
|
751
|
+
rebase,
|
|
609
752
|
},
|
|
610
|
-
|
|
611
|
-
|
|
753
|
+
]]);
|
|
754
|
+
}
|
|
612
755
|
class IntervalCollectionIterator {
|
|
613
756
|
constructor(collection, iteratesForward = true, start, end) {
|
|
614
757
|
this.results = [];
|
|
@@ -630,26 +773,35 @@ class IntervalCollectionIterator {
|
|
|
630
773
|
}
|
|
631
774
|
exports.IntervalCollectionIterator = IntervalCollectionIterator;
|
|
632
775
|
class IntervalCollection extends common_utils_1.TypedEventEmitter {
|
|
776
|
+
/** @internal */
|
|
633
777
|
constructor(helpers, requiresClient, emitter, serializedIntervals) {
|
|
634
778
|
super();
|
|
635
779
|
this.helpers = helpers;
|
|
636
780
|
this.requiresClient = requiresClient;
|
|
637
781
|
this.emitter = emitter;
|
|
638
|
-
this.
|
|
782
|
+
this.pendingChangesStart = new Map();
|
|
783
|
+
this.pendingChangesEnd = new Map();
|
|
784
|
+
if (Array.isArray(serializedIntervals)) {
|
|
785
|
+
this.savedSerializedIntervals = serializedIntervals;
|
|
786
|
+
}
|
|
787
|
+
else {
|
|
788
|
+
this.savedSerializedIntervals =
|
|
789
|
+
serializedIntervals.intervals.map((i) => decompressInterval(i, serializedIntervals.label));
|
|
790
|
+
}
|
|
639
791
|
}
|
|
640
792
|
get attached() {
|
|
641
793
|
return !!this.localCollection;
|
|
642
794
|
}
|
|
643
795
|
attachGraph(client, label) {
|
|
644
796
|
if (this.attached) {
|
|
645
|
-
throw new
|
|
797
|
+
throw new telemetry_utils_1.LoggingError("Only supports one Sequence attach");
|
|
646
798
|
}
|
|
647
799
|
if ((client === undefined) && (this.requiresClient)) {
|
|
648
|
-
throw new
|
|
800
|
+
throw new telemetry_utils_1.LoggingError("Client required for this collection");
|
|
649
801
|
}
|
|
650
802
|
// Instantiate the local interval collection based on the saved intervals
|
|
651
803
|
this.client = client;
|
|
652
|
-
this.localCollection = new LocalIntervalCollection(client, label, this.helpers);
|
|
804
|
+
this.localCollection = new LocalIntervalCollection(client, label, this.helpers, (interval) => this.emit("changeInterval", interval, true, undefined));
|
|
653
805
|
if (this.savedSerializedIntervals) {
|
|
654
806
|
for (const serializedInterval of this.savedSerializedIntervals) {
|
|
655
807
|
this.localCollection.ensureSerializedId(serializedInterval);
|
|
@@ -658,16 +810,33 @@ class IntervalCollection extends common_utils_1.TypedEventEmitter {
|
|
|
658
810
|
}
|
|
659
811
|
this.savedSerializedIntervals = undefined;
|
|
660
812
|
}
|
|
813
|
+
/**
|
|
814
|
+
* Gets the next local sequence number, modifying this client's collab window in doing so.
|
|
815
|
+
*/
|
|
816
|
+
getNextLocalSeq() {
|
|
817
|
+
return ++this.client.getCollabWindow().localSeq;
|
|
818
|
+
}
|
|
661
819
|
getIntervalById(id) {
|
|
662
820
|
if (!this.attached) {
|
|
663
|
-
throw new
|
|
821
|
+
throw new telemetry_utils_1.LoggingError("attach must be called before accessing intervals");
|
|
664
822
|
}
|
|
665
823
|
return this.localCollection.getIntervalById(id);
|
|
666
824
|
}
|
|
825
|
+
/**
|
|
826
|
+
* Create a new interval and add it to the collection
|
|
827
|
+
* @param start - interval start position
|
|
828
|
+
* @param end - interval end position
|
|
829
|
+
* @param intervalType - type of the interval. All intervals are SlideOnRemove. Intervals may not be Transient.
|
|
830
|
+
* @param props - properties of the interval
|
|
831
|
+
* @returns - the created interval
|
|
832
|
+
*/
|
|
667
833
|
add(start, end, intervalType, props) {
|
|
668
834
|
var _a, _b;
|
|
669
835
|
if (!this.attached) {
|
|
670
|
-
throw new
|
|
836
|
+
throw new telemetry_utils_1.LoggingError("attach must be called prior to adding intervals");
|
|
837
|
+
}
|
|
838
|
+
if (intervalType & IntervalType.Transient) {
|
|
839
|
+
throw new telemetry_utils_1.LoggingError("Can not add transient intervals");
|
|
671
840
|
}
|
|
672
841
|
const interval = this.localCollection.addInterval(start, end, intervalType, props);
|
|
673
842
|
if (interval) {
|
|
@@ -679,7 +848,7 @@ class IntervalCollection extends common_utils_1.TypedEventEmitter {
|
|
|
679
848
|
start,
|
|
680
849
|
};
|
|
681
850
|
// Local ops get submitted to the server. Remote ops have the deserializer run.
|
|
682
|
-
this.emitter.emit("add", undefined, serializedInterval);
|
|
851
|
+
this.emitter.emit("add", undefined, serializedInterval, { localSeq: this.getNextLocalSeq() });
|
|
683
852
|
}
|
|
684
853
|
this.emit("addInterval", interval, true, undefined);
|
|
685
854
|
return interval;
|
|
@@ -690,7 +859,7 @@ class IntervalCollection extends common_utils_1.TypedEventEmitter {
|
|
|
690
859
|
if (interval) {
|
|
691
860
|
// Local ops get submitted to the server. Remote ops have the deserializer run.
|
|
692
861
|
if (local) {
|
|
693
|
-
this.emitter.emit("delete", undefined, interval.serialize(this.client));
|
|
862
|
+
this.emitter.emit("delete", undefined, interval.serialize(this.client), { localSeq: this.getNextLocalSeq() });
|
|
694
863
|
}
|
|
695
864
|
else {
|
|
696
865
|
if (this.onDeserialize) {
|
|
@@ -709,40 +878,41 @@ class IntervalCollection extends common_utils_1.TypedEventEmitter {
|
|
|
709
878
|
}
|
|
710
879
|
changeProperties(id, props) {
|
|
711
880
|
if (!this.attached) {
|
|
712
|
-
throw new
|
|
881
|
+
throw new telemetry_utils_1.LoggingError("Attach must be called before accessing intervals");
|
|
713
882
|
}
|
|
714
883
|
if (typeof (id) !== "string") {
|
|
715
|
-
throw new
|
|
884
|
+
throw new telemetry_utils_1.LoggingError("Change API requires an ID that is a string");
|
|
716
885
|
}
|
|
717
886
|
if (!props) {
|
|
718
|
-
throw new
|
|
887
|
+
throw new telemetry_utils_1.LoggingError("changeProperties should be called with a property set");
|
|
719
888
|
}
|
|
720
889
|
const interval = this.getIntervalById(id);
|
|
721
890
|
if (interval) {
|
|
722
891
|
// Pass Unassigned as the sequence number to indicate that this is a local op that is waiting for an ack.
|
|
723
892
|
const deltaProps = interval.addProperties(props, true, merge_tree_1.UnassignedSequenceNumber);
|
|
724
893
|
const serializedInterval = interval.serialize(this.client);
|
|
725
|
-
// Emit a change op that will only change properties. Add the ID to
|
|
894
|
+
// Emit a change op that will only change properties. Add the ID to
|
|
895
|
+
// the property bag provided by the caller.
|
|
726
896
|
serializedInterval.start = undefined;
|
|
727
897
|
serializedInterval.end = undefined;
|
|
728
898
|
serializedInterval.properties = props;
|
|
729
899
|
serializedInterval.properties[reservedIntervalIdKey] = interval.getIntervalId();
|
|
730
|
-
this.emitter.emit("change", undefined, serializedInterval);
|
|
900
|
+
this.emitter.emit("change", undefined, serializedInterval, { localSeq: this.getNextLocalSeq() });
|
|
731
901
|
this.emit("propertyChanged", interval, deltaProps);
|
|
732
902
|
}
|
|
733
903
|
this.emit("changeInterval", interval, true, undefined);
|
|
734
904
|
}
|
|
735
905
|
change(id, start, end) {
|
|
736
906
|
if (!this.attached) {
|
|
737
|
-
throw new
|
|
907
|
+
throw new telemetry_utils_1.LoggingError("Attach must be called before accessing intervals");
|
|
738
908
|
}
|
|
909
|
+
// Force id to be a string.
|
|
739
910
|
if (typeof (id) !== "string") {
|
|
740
|
-
throw new
|
|
911
|
+
throw new telemetry_utils_1.LoggingError("Change API requires an ID that is a string");
|
|
741
912
|
}
|
|
742
|
-
// Force id to be a string.
|
|
743
913
|
const interval = this.getIntervalById(id);
|
|
744
914
|
if (interval) {
|
|
745
|
-
this.localCollection.changeInterval(interval, start, end);
|
|
915
|
+
const newInterval = this.localCollection.changeInterval(interval, start, end);
|
|
746
916
|
const serializedInterval = interval.serialize(this.client);
|
|
747
917
|
serializedInterval.start = start;
|
|
748
918
|
serializedInterval.end = end;
|
|
@@ -751,23 +921,19 @@ class IntervalCollection extends common_utils_1.TypedEventEmitter {
|
|
|
751
921
|
{
|
|
752
922
|
[reservedIntervalIdKey]: interval.getIntervalId(),
|
|
753
923
|
};
|
|
754
|
-
this.emitter.emit("change", undefined, serializedInterval);
|
|
924
|
+
this.emitter.emit("change", undefined, serializedInterval, { localSeq: this.getNextLocalSeq() });
|
|
755
925
|
this.addPendingChange(id, serializedInterval);
|
|
926
|
+
this.emit("changeInterval", newInterval, true, undefined);
|
|
927
|
+
return newInterval;
|
|
756
928
|
}
|
|
757
|
-
|
|
758
|
-
return
|
|
929
|
+
// No interval to change
|
|
930
|
+
return undefined;
|
|
759
931
|
}
|
|
760
932
|
addPendingChange(id, serializedInterval) {
|
|
761
933
|
if (serializedInterval.start !== undefined) {
|
|
762
|
-
if (!this.pendingChangesStart) {
|
|
763
|
-
this.pendingChangesStart = new Map();
|
|
764
|
-
}
|
|
765
934
|
this.addPendingChangeHelper(id, this.pendingChangesStart, serializedInterval);
|
|
766
935
|
}
|
|
767
936
|
if (serializedInterval.end !== undefined) {
|
|
768
|
-
if (!this.pendingChangesEnd) {
|
|
769
|
-
this.pendingChangesEnd = new Map();
|
|
770
|
-
}
|
|
771
937
|
this.addPendingChangeHelper(id, this.pendingChangesEnd, serializedInterval);
|
|
772
938
|
}
|
|
773
939
|
}
|
|
@@ -780,8 +946,9 @@ class IntervalCollection extends common_utils_1.TypedEventEmitter {
|
|
|
780
946
|
entries.push(serializedInterval);
|
|
781
947
|
}
|
|
782
948
|
removePendingChange(serializedInterval) {
|
|
949
|
+
var _a;
|
|
783
950
|
// Change ops always have an ID.
|
|
784
|
-
const id = serializedInterval.properties[reservedIntervalIdKey];
|
|
951
|
+
const id = (_a = serializedInterval.properties) === null || _a === void 0 ? void 0 : _a[reservedIntervalIdKey];
|
|
785
952
|
if (serializedInterval.start !== undefined) {
|
|
786
953
|
this.removePendingChangeHelper(id, this.pendingChangesStart, serializedInterval);
|
|
787
954
|
}
|
|
@@ -790,26 +957,24 @@ class IntervalCollection extends common_utils_1.TypedEventEmitter {
|
|
|
790
957
|
}
|
|
791
958
|
}
|
|
792
959
|
removePendingChangeHelper(id, pendingChanges, serializedInterval) {
|
|
793
|
-
const entries = pendingChanges
|
|
960
|
+
const entries = pendingChanges.get(id);
|
|
794
961
|
if (entries) {
|
|
795
962
|
const pendingChange = entries.shift();
|
|
796
963
|
if (entries.length === 0) {
|
|
797
964
|
pendingChanges.delete(id);
|
|
798
965
|
}
|
|
799
|
-
if (pendingChange.start !== serializedInterval.start ||
|
|
800
|
-
pendingChange.end !== serializedInterval.end) {
|
|
801
|
-
throw new
|
|
966
|
+
if ((pendingChange === null || pendingChange === void 0 ? void 0 : pendingChange.start) !== serializedInterval.start ||
|
|
967
|
+
(pendingChange === null || pendingChange === void 0 ? void 0 : pendingChange.end) !== serializedInterval.end) {
|
|
968
|
+
throw new telemetry_utils_1.LoggingError("Mismatch in pending changes");
|
|
802
969
|
}
|
|
803
970
|
}
|
|
804
971
|
}
|
|
805
972
|
hasPendingChangeStart(id) {
|
|
806
|
-
|
|
807
|
-
const entries = (_a = this.pendingChangesStart) === null || _a === void 0 ? void 0 : _a.get(id);
|
|
973
|
+
const entries = this.pendingChangesStart.get(id);
|
|
808
974
|
return entries && entries.length !== 0;
|
|
809
975
|
}
|
|
810
976
|
hasPendingChangeEnd(id) {
|
|
811
|
-
|
|
812
|
-
const entries = (_a = this.pendingChangesEnd) === null || _a === void 0 ? void 0 : _a.get(id);
|
|
977
|
+
const entries = this.pendingChangesEnd.get(id);
|
|
813
978
|
return entries && entries.length !== 0;
|
|
814
979
|
}
|
|
815
980
|
/** @deprecated - use ackChange */
|
|
@@ -818,22 +983,23 @@ class IntervalCollection extends common_utils_1.TypedEventEmitter {
|
|
|
818
983
|
}
|
|
819
984
|
/** @internal */
|
|
820
985
|
ackChange(serializedInterval, local, op) {
|
|
821
|
-
var _a, _b;
|
|
986
|
+
var _a, _b, _c, _d;
|
|
822
987
|
if (!this.attached) {
|
|
823
|
-
throw new
|
|
988
|
+
throw new telemetry_utils_1.LoggingError("Attach must be called before accessing intervals");
|
|
824
989
|
}
|
|
825
990
|
let interval;
|
|
826
991
|
if (local) {
|
|
827
992
|
// This is an ack from the server. Remove the pending change.
|
|
828
993
|
this.removePendingChange(serializedInterval);
|
|
829
|
-
const id = serializedInterval.properties[reservedIntervalIdKey];
|
|
994
|
+
const id = (_a = serializedInterval.properties) === null || _a === void 0 ? void 0 : _a[reservedIntervalIdKey];
|
|
830
995
|
interval = this.getIntervalById(id);
|
|
831
996
|
if (interval) {
|
|
832
997
|
// Let the propertyManager prune its pending change-properties set.
|
|
833
|
-
(
|
|
834
|
-
type:
|
|
835
|
-
props: serializedInterval.properties,
|
|
998
|
+
(_b = interval.propertyManager) === null || _b === void 0 ? void 0 : _b.ackPendingProperties({
|
|
999
|
+
type: merge_tree_1.MergeTreeDeltaType.ANNOTATE,
|
|
1000
|
+
props: (_c = serializedInterval.properties) !== null && _c !== void 0 ? _c : {},
|
|
836
1001
|
});
|
|
1002
|
+
this.ackInterval(interval, op);
|
|
837
1003
|
}
|
|
838
1004
|
}
|
|
839
1005
|
else {
|
|
@@ -842,7 +1008,7 @@ class IntervalCollection extends common_utils_1.TypedEventEmitter {
|
|
|
842
1008
|
// Note that the ID is in the property bag only to allow us to find the interval.
|
|
843
1009
|
// This API cannot change the ID, and writing to the ID property will result in an exception. So we
|
|
844
1010
|
// strip it out of the properties here.
|
|
845
|
-
const
|
|
1011
|
+
const _e = serializedInterval.properties, _f = reservedIntervalIdKey, id = _e[_f], newProps = __rest(_e, [typeof _f === "symbol" ? _f : _f + ""]);
|
|
846
1012
|
interval = this.getIntervalById(id);
|
|
847
1013
|
if (interval) {
|
|
848
1014
|
let start;
|
|
@@ -857,7 +1023,7 @@ class IntervalCollection extends common_utils_1.TypedEventEmitter {
|
|
|
857
1023
|
if (start !== undefined || end !== undefined) {
|
|
858
1024
|
// If changeInterval gives us a new interval, work with that one. Otherwise keep working with
|
|
859
1025
|
// the one we originally found in the tree.
|
|
860
|
-
interval = (
|
|
1026
|
+
interval = (_d = this.localCollection.changeInterval(interval, start, end, op)) !== null && _d !== void 0 ? _d : interval;
|
|
861
1027
|
}
|
|
862
1028
|
const deltaProps = interval.addProperties(newProps, true, op.sequenceNumber);
|
|
863
1029
|
if (this.onDeserialize) {
|
|
@@ -872,7 +1038,7 @@ class IntervalCollection extends common_utils_1.TypedEventEmitter {
|
|
|
872
1038
|
}
|
|
873
1039
|
addConflictResolver(conflictResolver) {
|
|
874
1040
|
if (!this.attached) {
|
|
875
|
-
throw new
|
|
1041
|
+
throw new telemetry_utils_1.LoggingError("attachSequence must be called");
|
|
876
1042
|
}
|
|
877
1043
|
this.localCollection.addConflictResolver(conflictResolver);
|
|
878
1044
|
}
|
|
@@ -885,22 +1051,109 @@ class IntervalCollection extends common_utils_1.TypedEventEmitter {
|
|
|
885
1051
|
this.onDeserialize = onDeserialize;
|
|
886
1052
|
// Trigger the async prepare work across all values in the collection
|
|
887
1053
|
this.localCollection.map((interval) => {
|
|
888
|
-
|
|
1054
|
+
onDeserialize(interval);
|
|
889
1055
|
});
|
|
890
1056
|
}
|
|
1057
|
+
/** @internal */
|
|
1058
|
+
rebaseLocalInterval(opName, serializedInterval, localSeq) {
|
|
1059
|
+
var _a, _b;
|
|
1060
|
+
if (!this.attached) {
|
|
1061
|
+
throw new telemetry_utils_1.LoggingError("attachSequence must be called");
|
|
1062
|
+
}
|
|
1063
|
+
const { start, end, intervalType, properties, sequenceNumber } = serializedInterval;
|
|
1064
|
+
const startRebased = start === undefined ? undefined :
|
|
1065
|
+
this.client.rebasePosition(start, sequenceNumber, localSeq);
|
|
1066
|
+
const endRebased = end === undefined ? undefined :
|
|
1067
|
+
this.client.rebasePosition(end, sequenceNumber, localSeq);
|
|
1068
|
+
const intervalId = properties === null || properties === void 0 ? void 0 : properties[reservedIntervalIdKey];
|
|
1069
|
+
const rebased = {
|
|
1070
|
+
start: startRebased,
|
|
1071
|
+
end: endRebased,
|
|
1072
|
+
intervalType,
|
|
1073
|
+
sequenceNumber: (_b = (_a = this.client) === null || _a === void 0 ? void 0 : _a.getCurrentSeq()) !== null && _b !== void 0 ? _b : 0,
|
|
1074
|
+
properties,
|
|
1075
|
+
};
|
|
1076
|
+
if (opName === "change" && (this.hasPendingChangeStart(intervalId) || this.hasPendingChangeEnd(intervalId))) {
|
|
1077
|
+
this.removePendingChange(serializedInterval);
|
|
1078
|
+
this.addPendingChange(intervalId, rebased);
|
|
1079
|
+
}
|
|
1080
|
+
return rebased;
|
|
1081
|
+
}
|
|
1082
|
+
getSlideToSegment(lref) {
|
|
1083
|
+
const segoff = { segment: lref.segment, offset: lref.offset };
|
|
1084
|
+
const newSegoff = this.client.getSlideToSegment(segoff);
|
|
1085
|
+
const value = (segoff.segment === newSegoff.segment && segoff.offset === newSegoff.offset) ? undefined : newSegoff;
|
|
1086
|
+
return value;
|
|
1087
|
+
}
|
|
1088
|
+
setSlideOnRemove(lref) {
|
|
1089
|
+
let refType = lref.refType;
|
|
1090
|
+
refType = refType & ~merge_tree_1.ReferenceType.StayOnRemove;
|
|
1091
|
+
refType = refType | merge_tree_1.ReferenceType.SlideOnRemove;
|
|
1092
|
+
lref.refType = refType;
|
|
1093
|
+
}
|
|
1094
|
+
ackInterval(interval, op) {
|
|
1095
|
+
// in current usage, interval is always a SequenceInterval
|
|
1096
|
+
if (!(interval instanceof SequenceInterval)) {
|
|
1097
|
+
return;
|
|
1098
|
+
}
|
|
1099
|
+
if (!(0, merge_tree_1.refTypeIncludesFlag)(interval.start, merge_tree_1.ReferenceType.StayOnRemove) &&
|
|
1100
|
+
!(0, merge_tree_1.refTypeIncludesFlag)(interval.end, merge_tree_1.ReferenceType.StayOnRemove)) {
|
|
1101
|
+
return;
|
|
1102
|
+
}
|
|
1103
|
+
const newStart = this.getSlideToSegment(interval.start);
|
|
1104
|
+
const newEnd = this.getSlideToSegment(interval.end);
|
|
1105
|
+
const id = interval.properties[reservedIntervalIdKey];
|
|
1106
|
+
const hasPendingStartChange = this.hasPendingChangeStart(id);
|
|
1107
|
+
const hasPendingEndChange = this.hasPendingChangeEnd(id);
|
|
1108
|
+
if (!hasPendingStartChange) {
|
|
1109
|
+
this.setSlideOnRemove(interval.start);
|
|
1110
|
+
}
|
|
1111
|
+
if (!hasPendingEndChange) {
|
|
1112
|
+
this.setSlideOnRemove(interval.end);
|
|
1113
|
+
}
|
|
1114
|
+
const needsStartUpdate = newStart !== undefined && !hasPendingStartChange;
|
|
1115
|
+
const needsEndUpdate = newEnd !== undefined && !hasPendingEndChange;
|
|
1116
|
+
if (needsStartUpdate || needsEndUpdate) {
|
|
1117
|
+
// In this case, where we change the start or end of an interval,
|
|
1118
|
+
// it is necessary to remove and re-add the interval listeners.
|
|
1119
|
+
// This ensures that the correct listeners are added to the ReferencePosition.
|
|
1120
|
+
this.localCollection.removeExistingInterval(interval);
|
|
1121
|
+
if (needsStartUpdate) {
|
|
1122
|
+
const props = interval.start.properties;
|
|
1123
|
+
this.client.removeLocalReferencePosition(interval.start);
|
|
1124
|
+
interval.start = createPositionReferenceFromSegoff(this.client, newStart, interval.start.refType, op);
|
|
1125
|
+
if (props) {
|
|
1126
|
+
interval.start.addProperties(props);
|
|
1127
|
+
}
|
|
1128
|
+
}
|
|
1129
|
+
if (needsEndUpdate) {
|
|
1130
|
+
const props = interval.end.properties;
|
|
1131
|
+
this.client.removeLocalReferencePosition(interval.end);
|
|
1132
|
+
interval.end = createPositionReferenceFromSegoff(this.client, newEnd, interval.end.refType, op);
|
|
1133
|
+
if (props) {
|
|
1134
|
+
interval.end.addProperties(props);
|
|
1135
|
+
}
|
|
1136
|
+
}
|
|
1137
|
+
this.localCollection.add(interval);
|
|
1138
|
+
}
|
|
1139
|
+
}
|
|
891
1140
|
/** @deprecated - use ackAdd */
|
|
892
1141
|
addInternal(serializedInterval, local, op) {
|
|
893
1142
|
return this.ackAdd(serializedInterval, local, op);
|
|
894
1143
|
}
|
|
895
1144
|
/** @internal */
|
|
896
1145
|
ackAdd(serializedInterval, local, op) {
|
|
1146
|
+
var _a;
|
|
897
1147
|
if (local) {
|
|
898
|
-
|
|
899
|
-
|
|
1148
|
+
const id = (_a = serializedInterval.properties) === null || _a === void 0 ? void 0 : _a[reservedIntervalIdKey];
|
|
1149
|
+
const localInterval = this.getIntervalById(id);
|
|
1150
|
+
if (localInterval) {
|
|
1151
|
+
this.ackInterval(localInterval, op);
|
|
1152
|
+
}
|
|
900
1153
|
return;
|
|
901
1154
|
}
|
|
902
1155
|
if (!this.attached) {
|
|
903
|
-
throw new
|
|
1156
|
+
throw new telemetry_utils_1.LoggingError("attachSequence must be called");
|
|
904
1157
|
}
|
|
905
1158
|
this.localCollection.ensureSerializedId(serializedInterval);
|
|
906
1159
|
const interval = this.localCollection.addInterval(serializedInterval.start, serializedInterval.end, serializedInterval.intervalType, serializedInterval.properties, op);
|
|
@@ -925,17 +1178,20 @@ class IntervalCollection extends common_utils_1.TypedEventEmitter {
|
|
|
925
1178
|
return;
|
|
926
1179
|
}
|
|
927
1180
|
if (!this.attached) {
|
|
928
|
-
throw new
|
|
1181
|
+
throw new telemetry_utils_1.LoggingError("attach must be called prior to deleting intervals");
|
|
929
1182
|
}
|
|
930
|
-
this.localCollection.ensureSerializedId(serializedInterval);
|
|
931
|
-
const interval = this.localCollection.getIntervalById(
|
|
1183
|
+
const id = this.localCollection.ensureSerializedId(serializedInterval);
|
|
1184
|
+
const interval = this.localCollection.getIntervalById(id);
|
|
932
1185
|
if (interval) {
|
|
933
1186
|
this.deleteExistingInterval(interval, local, op);
|
|
934
1187
|
}
|
|
935
1188
|
}
|
|
1189
|
+
/**
|
|
1190
|
+
* @internal
|
|
1191
|
+
*/
|
|
936
1192
|
serializeInternal() {
|
|
937
1193
|
if (!this.attached) {
|
|
938
|
-
throw new
|
|
1194
|
+
throw new telemetry_utils_1.LoggingError("attachSequence must be called");
|
|
939
1195
|
}
|
|
940
1196
|
return this.localCollection.serialize();
|
|
941
1197
|
}
|
|
@@ -967,25 +1223,25 @@ class IntervalCollection extends common_utils_1.TypedEventEmitter {
|
|
|
967
1223
|
}
|
|
968
1224
|
findOverlappingIntervals(startPosition, endPosition) {
|
|
969
1225
|
if (!this.attached) {
|
|
970
|
-
throw new
|
|
1226
|
+
throw new telemetry_utils_1.LoggingError("attachSequence must be called");
|
|
971
1227
|
}
|
|
972
1228
|
return this.localCollection.findOverlappingIntervals(startPosition, endPosition);
|
|
973
1229
|
}
|
|
974
1230
|
map(fn) {
|
|
975
1231
|
if (!this.attached) {
|
|
976
|
-
throw new
|
|
1232
|
+
throw new telemetry_utils_1.LoggingError("attachSequence must be called");
|
|
977
1233
|
}
|
|
978
1234
|
this.localCollection.map(fn);
|
|
979
1235
|
}
|
|
980
1236
|
previousInterval(pos) {
|
|
981
1237
|
if (!this.attached) {
|
|
982
|
-
throw new
|
|
1238
|
+
throw new telemetry_utils_1.LoggingError("attachSequence must be called");
|
|
983
1239
|
}
|
|
984
1240
|
return this.localCollection.previousInterval(pos);
|
|
985
1241
|
}
|
|
986
1242
|
nextInterval(pos) {
|
|
987
1243
|
if (!this.attached) {
|
|
988
|
-
throw new
|
|
1244
|
+
throw new telemetry_utils_1.LoggingError("attachSequence must be called");
|
|
989
1245
|
}
|
|
990
1246
|
return this.localCollection.nextInterval(pos);
|
|
991
1247
|
}
|