@fluidframework/sequence 2.0.0-internal.4.3.0 → 2.0.0-internal.5.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.
- package/CHANGELOG.md +20 -0
- package/dist/defaultMap.d.ts +3 -2
- package/dist/defaultMap.d.ts.map +1 -1
- package/dist/defaultMap.js +4 -3
- package/dist/defaultMap.js.map +1 -1
- package/dist/defaultMapInterfaces.d.ts +12 -1
- package/dist/defaultMapInterfaces.d.ts.map +1 -1
- package/dist/defaultMapInterfaces.js.map +1 -1
- package/dist/index.d.ts +3 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +11 -3
- package/dist/index.js.map +1 -1
- package/dist/intervalCollection.d.ts +197 -56
- package/dist/intervalCollection.d.ts.map +1 -1
- package/dist/intervalCollection.js +161 -80
- package/dist/intervalCollection.js.map +1 -1
- package/dist/packageVersion.d.ts +1 -1
- package/dist/packageVersion.js +1 -1
- package/dist/packageVersion.js.map +1 -1
- package/dist/revertibles.d.ts +104 -0
- package/dist/revertibles.d.ts.map +1 -0
- package/dist/revertibles.js +374 -0
- package/dist/revertibles.js.map +1 -0
- package/dist/sequence.d.ts +4 -4
- package/dist/sequence.d.ts.map +1 -1
- package/dist/sequence.js +3 -3
- package/dist/sequence.js.map +1 -1
- package/dist/sharedIntervalCollection.d.ts +3 -3
- package/dist/sharedIntervalCollection.d.ts.map +1 -1
- package/dist/sharedIntervalCollection.js +1 -1
- package/dist/sharedIntervalCollection.js.map +1 -1
- package/lib/defaultMap.d.ts +3 -2
- package/lib/defaultMap.d.ts.map +1 -1
- package/lib/defaultMap.js +4 -3
- package/lib/defaultMap.js.map +1 -1
- package/lib/defaultMapInterfaces.d.ts +12 -1
- package/lib/defaultMapInterfaces.d.ts.map +1 -1
- package/lib/defaultMapInterfaces.js.map +1 -1
- package/lib/index.d.ts +3 -2
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +2 -1
- package/lib/index.js.map +1 -1
- package/lib/intervalCollection.d.ts +197 -56
- package/lib/intervalCollection.d.ts.map +1 -1
- package/lib/intervalCollection.js +162 -80
- package/lib/intervalCollection.js.map +1 -1
- package/lib/packageVersion.d.ts +1 -1
- package/lib/packageVersion.js +1 -1
- package/lib/packageVersion.js.map +1 -1
- package/lib/revertibles.d.ts +104 -0
- package/lib/revertibles.d.ts.map +1 -0
- package/lib/revertibles.js +364 -0
- package/lib/revertibles.js.map +1 -0
- package/lib/sequence.d.ts +4 -4
- package/lib/sequence.d.ts.map +1 -1
- package/lib/sequence.js +3 -3
- package/lib/sequence.js.map +1 -1
- package/lib/sharedIntervalCollection.d.ts +3 -3
- package/lib/sharedIntervalCollection.d.ts.map +1 -1
- package/lib/sharedIntervalCollection.js +1 -1
- package/lib/sharedIntervalCollection.js.map +1 -1
- package/package.json +46 -14
- package/src/defaultMap.ts +4 -1
- package/src/defaultMapInterfaces.ts +13 -1
- package/src/index.ts +16 -5
- package/src/intervalCollection.ts +373 -66
- package/src/packageVersion.ts +1 -1
- package/src/revertibles.ts +572 -0
- package/src/sequence.ts +12 -2
- package/src/sharedIntervalCollection.ts +4 -2
|
@@ -16,11 +16,22 @@ var __rest = (this && this.__rest) || function (s, e) {
|
|
|
16
16
|
/* eslint-disable no-bitwise */
|
|
17
17
|
import { assert, TypedEventEmitter } from "@fluidframework/common-utils";
|
|
18
18
|
import { UsageError } from "@fluidframework/container-utils";
|
|
19
|
-
import { addProperties, compareReferencePositions, createMap, MergeTreeDeltaType, minReferencePosition, PropertiesManager, RedBlackTree, ReferenceType, refTypeIncludesFlag, reservedRangeLabelsKey, UnassignedSequenceNumber, maxReferencePosition, createDetachedLocalReferencePosition, DetachedReferencePosition, } from "@fluidframework/merge-tree";
|
|
19
|
+
import { addProperties, compareReferencePositions, createMap, MergeTreeDeltaType, minReferencePosition, PropertiesManager, RedBlackTree, ReferenceType, refTypeIncludesFlag, reservedRangeLabelsKey, UnassignedSequenceNumber, maxReferencePosition, createDetachedLocalReferencePosition, DetachedReferencePosition, SlidingPreference, } from "@fluidframework/merge-tree";
|
|
20
20
|
import { LoggingError } from "@fluidframework/telemetry-utils";
|
|
21
21
|
import { v4 as uuid } from "uuid";
|
|
22
22
|
import { IntervalTree } from "./intervalTree";
|
|
23
23
|
const reservedIntervalIdKey = "intervalId";
|
|
24
|
+
/**
|
|
25
|
+
* Values are used in persisted formats (ops) and revertibles.
|
|
26
|
+
* @alpha
|
|
27
|
+
*/
|
|
28
|
+
export const IntervalOpType = {
|
|
29
|
+
ADD: "add",
|
|
30
|
+
DELETE: "delete",
|
|
31
|
+
CHANGE: "change",
|
|
32
|
+
PROPERTY_CHANGED: "propertyChanged",
|
|
33
|
+
POSITION_REMOVE: "positionRemove",
|
|
34
|
+
};
|
|
24
35
|
export var IntervalType;
|
|
25
36
|
(function (IntervalType) {
|
|
26
37
|
IntervalType[IntervalType["Simple"] = 0] = "Simple";
|
|
@@ -49,6 +60,7 @@ function decompressInterval(interval, label) {
|
|
|
49
60
|
sequenceNumber: interval[2],
|
|
50
61
|
intervalType: interval[3],
|
|
51
62
|
properties: Object.assign(Object.assign({}, interval[4]), { [reservedRangeLabelsKey]: [label] }),
|
|
63
|
+
stickiness: interval[5],
|
|
52
64
|
};
|
|
53
65
|
}
|
|
54
66
|
/**
|
|
@@ -57,14 +69,57 @@ function decompressInterval(interval, label) {
|
|
|
57
69
|
*/
|
|
58
70
|
function compressInterval(interval) {
|
|
59
71
|
const { start, end, sequenceNumber, intervalType, properties } = interval;
|
|
60
|
-
|
|
72
|
+
const base = [
|
|
61
73
|
start,
|
|
62
74
|
end,
|
|
63
75
|
sequenceNumber,
|
|
64
76
|
intervalType,
|
|
65
77
|
Object.assign(Object.assign({}, properties), { [reservedRangeLabelsKey]: undefined }),
|
|
66
78
|
];
|
|
79
|
+
if (interval.stickiness !== undefined && interval.stickiness !== IntervalStickiness.END) {
|
|
80
|
+
base.push(interval.stickiness);
|
|
81
|
+
}
|
|
82
|
+
return base;
|
|
83
|
+
}
|
|
84
|
+
function startReferenceSlidingPreference(stickiness) {
|
|
85
|
+
// if any start stickiness, prefer sliding backwards
|
|
86
|
+
return (stickiness & IntervalStickiness.START) !== 0
|
|
87
|
+
? SlidingPreference.BACKWARD
|
|
88
|
+
: SlidingPreference.FORWARD;
|
|
89
|
+
}
|
|
90
|
+
function endReferenceSlidingPreference(stickiness) {
|
|
91
|
+
// if any end stickiness, prefer sliding forwards
|
|
92
|
+
return (stickiness & IntervalStickiness.END) !== 0
|
|
93
|
+
? SlidingPreference.FORWARD
|
|
94
|
+
: SlidingPreference.BACKWARD;
|
|
67
95
|
}
|
|
96
|
+
/**
|
|
97
|
+
* Determines how an interval should expand when segments are inserted adjacent
|
|
98
|
+
* to the range it spans
|
|
99
|
+
*
|
|
100
|
+
* Note that interval stickiness is currently an experimental feature and must
|
|
101
|
+
* be explicitly enabled with the `intervalStickinessEnabled` flag
|
|
102
|
+
*/
|
|
103
|
+
export const IntervalStickiness = {
|
|
104
|
+
/**
|
|
105
|
+
* Interval does not expand to include adjacent segments
|
|
106
|
+
*/
|
|
107
|
+
NONE: 0b00,
|
|
108
|
+
/**
|
|
109
|
+
* Interval expands to include segments inserted adjacent to the start
|
|
110
|
+
*/
|
|
111
|
+
START: 0b01,
|
|
112
|
+
/**
|
|
113
|
+
* Interval expands to include segments inserted adjacent to the end
|
|
114
|
+
*
|
|
115
|
+
* This is the default stickiness
|
|
116
|
+
*/
|
|
117
|
+
END: 0b10,
|
|
118
|
+
/**
|
|
119
|
+
* Interval expands to include all segments inserted adjacent to it
|
|
120
|
+
*/
|
|
121
|
+
FULL: 0b11,
|
|
122
|
+
};
|
|
68
123
|
/**
|
|
69
124
|
* Serializable interval whose endpoints are plain-old numbers.
|
|
70
125
|
*/
|
|
@@ -98,7 +153,7 @@ export class Interval {
|
|
|
98
153
|
* Adds an auxiliary set of properties to this interval.
|
|
99
154
|
* These properties can be recovered using `getAdditionalPropertySets`
|
|
100
155
|
* @param props - set of properties to add
|
|
101
|
-
* @remarks - This gets called as part of the default conflict resolver for `
|
|
156
|
+
* @remarks - This gets called as part of the default conflict resolver for `IIntervalCollection<Interval>`
|
|
102
157
|
* (i.e. non-sequence-based interval collections). However, the additional properties don't get serialized.
|
|
103
158
|
* This functionality seems half-baked.
|
|
104
159
|
*/
|
|
@@ -254,11 +309,12 @@ export class SequenceInterval {
|
|
|
254
309
|
* End endpoint of this interval.
|
|
255
310
|
* @remarks - This endpoint can be resolved into a character position using the SharedString it's a part of.
|
|
256
311
|
*/
|
|
257
|
-
end, intervalType, props) {
|
|
312
|
+
end, intervalType, props, stickiness = IntervalStickiness.END) {
|
|
258
313
|
this.client = client;
|
|
259
314
|
this.start = start;
|
|
260
315
|
this.end = end;
|
|
261
316
|
this.intervalType = intervalType;
|
|
317
|
+
this.stickiness = stickiness;
|
|
262
318
|
this.propertyManager = new PropertiesManager();
|
|
263
319
|
this.properties = {};
|
|
264
320
|
if (props) {
|
|
@@ -310,13 +366,16 @@ export class SequenceInterval {
|
|
|
310
366
|
if (this.properties) {
|
|
311
367
|
serializedInterval.properties = this.properties;
|
|
312
368
|
}
|
|
369
|
+
if (this.stickiness !== IntervalStickiness.END) {
|
|
370
|
+
serializedInterval.stickiness = this.stickiness;
|
|
371
|
+
}
|
|
313
372
|
return serializedInterval;
|
|
314
373
|
}
|
|
315
374
|
/**
|
|
316
375
|
* {@inheritDoc IInterval.clone}
|
|
317
376
|
*/
|
|
318
377
|
clone() {
|
|
319
|
-
return new SequenceInterval(this.client, this.start, this.end, this.intervalType, this.properties);
|
|
378
|
+
return new SequenceInterval(this.client, this.start, this.end, this.intervalType, this.properties, this.stickiness);
|
|
320
379
|
}
|
|
321
380
|
/**
|
|
322
381
|
* {@inheritDoc IInterval.compare}
|
|
@@ -400,7 +459,7 @@ export class SequenceInterval {
|
|
|
400
459
|
* {@inheritDoc IInterval.modify}
|
|
401
460
|
* @deprecated - This API was never intended to be public and will be marked internal in a future release.
|
|
402
461
|
*/
|
|
403
|
-
modify(label, start, end, op, localSeq) {
|
|
462
|
+
modify(label, start, end, op, localSeq, stickiness = IntervalStickiness.END) {
|
|
404
463
|
const getRefType = (baseType) => {
|
|
405
464
|
let refType = baseType;
|
|
406
465
|
if (op === undefined) {
|
|
@@ -411,14 +470,14 @@ export class SequenceInterval {
|
|
|
411
470
|
};
|
|
412
471
|
let startRef = this.start;
|
|
413
472
|
if (start !== undefined) {
|
|
414
|
-
startRef = createPositionReference(this.client, start, getRefType(this.start.refType), op, undefined, localSeq);
|
|
473
|
+
startRef = createPositionReference(this.client, start, getRefType(this.start.refType), op, undefined, localSeq, startReferenceSlidingPreference(stickiness));
|
|
415
474
|
if (this.start.properties) {
|
|
416
475
|
startRef.addProperties(this.start.properties);
|
|
417
476
|
}
|
|
418
477
|
}
|
|
419
478
|
let endRef = this.end;
|
|
420
479
|
if (end !== undefined) {
|
|
421
|
-
endRef = createPositionReference(this.client, end, getRefType(this.end.refType), op, undefined, localSeq);
|
|
480
|
+
endRef = createPositionReference(this.client, end, getRefType(this.end.refType), op, undefined, localSeq, endReferenceSlidingPreference(stickiness));
|
|
422
481
|
if (this.end.properties) {
|
|
423
482
|
endRef.addProperties(this.end.properties);
|
|
424
483
|
}
|
|
@@ -439,9 +498,9 @@ export class SequenceInterval {
|
|
|
439
498
|
}
|
|
440
499
|
}
|
|
441
500
|
}
|
|
442
|
-
function createPositionReferenceFromSegoff(client, segoff, refType, op, localSeq, fromSnapshot) {
|
|
501
|
+
function createPositionReferenceFromSegoff(client, segoff, refType, op, localSeq, fromSnapshot, slidingPreference) {
|
|
443
502
|
if (segoff.segment) {
|
|
444
|
-
const ref = client.createLocalReferencePosition(segoff.segment, segoff.offset, refType, undefined);
|
|
503
|
+
const ref = client.createLocalReferencePosition(segoff.segment, segoff.offset, refType, undefined, slidingPreference);
|
|
445
504
|
return ref;
|
|
446
505
|
}
|
|
447
506
|
// Creating references on detached segments is allowed for:
|
|
@@ -457,7 +516,7 @@ function createPositionReferenceFromSegoff(client, segoff, refType, op, localSeq
|
|
|
457
516
|
}
|
|
458
517
|
return createDetachedLocalReferencePosition(refType);
|
|
459
518
|
}
|
|
460
|
-
function createPositionReference(client, pos, refType, op, fromSnapshot, localSeq) {
|
|
519
|
+
function createPositionReference(client, pos, refType, op, fromSnapshot, localSeq, slidingPreference) {
|
|
461
520
|
let segoff;
|
|
462
521
|
if (op) {
|
|
463
522
|
assert((refType & ReferenceType.SlideOnRemove) !== 0, 0x2f5 /* op create references must be SlideOnRemove */);
|
|
@@ -471,9 +530,9 @@ function createPositionReference(client, pos, refType, op, fromSnapshot, localSe
|
|
|
471
530
|
assert((refType & ReferenceType.SlideOnRemove) === 0 || !!fromSnapshot, 0x2f6 /* SlideOnRemove references must be op created */);
|
|
472
531
|
segoff = client.getContainingSegment(pos, undefined, localSeq);
|
|
473
532
|
}
|
|
474
|
-
return createPositionReferenceFromSegoff(client, segoff, refType, op, localSeq, fromSnapshot);
|
|
533
|
+
return createPositionReferenceFromSegoff(client, segoff, refType, op, localSeq, fromSnapshot, slidingPreference);
|
|
475
534
|
}
|
|
476
|
-
export function createSequenceInterval(label, start, end, client, intervalType, op, fromSnapshot) {
|
|
535
|
+
export function createSequenceInterval(label, start, end, client, intervalType, op, fromSnapshot, stickiness = IntervalStickiness.END) {
|
|
477
536
|
let beginRefType = ReferenceType.RangeBegin;
|
|
478
537
|
let endRefType = ReferenceType.RangeEnd;
|
|
479
538
|
if (intervalType === IntervalType.Transient) {
|
|
@@ -497,14 +556,14 @@ export function createSequenceInterval(label, start, end, client, intervalType,
|
|
|
497
556
|
endRefType |= ReferenceType.StayOnRemove;
|
|
498
557
|
}
|
|
499
558
|
}
|
|
500
|
-
const startLref = createPositionReference(client, start, beginRefType, op, fromSnapshot);
|
|
501
|
-
const endLref = createPositionReference(client, end, endRefType, op, fromSnapshot);
|
|
559
|
+
const startLref = createPositionReference(client, start, beginRefType, op, fromSnapshot, undefined, startReferenceSlidingPreference(stickiness));
|
|
560
|
+
const endLref = createPositionReference(client, end, endRefType, op, fromSnapshot, undefined, endReferenceSlidingPreference(stickiness));
|
|
502
561
|
const rangeProp = {
|
|
503
562
|
[reservedRangeLabelsKey]: [label],
|
|
504
563
|
};
|
|
505
564
|
startLref.addProperties(rangeProp);
|
|
506
565
|
endLref.addProperties(rangeProp);
|
|
507
|
-
const ival = new SequenceInterval(client, startLref, endLref, intervalType, rangeProp);
|
|
566
|
+
const ival = new SequenceInterval(client, startLref, endLref, intervalType, rangeProp, stickiness);
|
|
508
567
|
return ival;
|
|
509
568
|
}
|
|
510
569
|
export function createIntervalIndex() {
|
|
@@ -673,11 +732,11 @@ export class LocalIntervalCollection {
|
|
|
673
732
|
this.overlappingIntervalsIndex = new OverlappingIntervalsIndex(client, helpers);
|
|
674
733
|
this.idIntervalIndex = new IdIntervalIndex();
|
|
675
734
|
this.endIntervalIndex = new EndpointIndex(client, helpers);
|
|
676
|
-
this.indexes = [
|
|
735
|
+
this.indexes = new Set([
|
|
677
736
|
this.overlappingIntervalsIndex,
|
|
678
737
|
this.idIntervalIndex,
|
|
679
738
|
this.endIntervalIndex,
|
|
680
|
-
];
|
|
739
|
+
]);
|
|
681
740
|
}
|
|
682
741
|
createLegacyId(start, end) {
|
|
683
742
|
// Create a non-unique ID based on start and end to be used on intervals that come from legacy clients
|
|
@@ -717,17 +776,23 @@ export class LocalIntervalCollection {
|
|
|
717
776
|
index.remove(interval);
|
|
718
777
|
}
|
|
719
778
|
}
|
|
779
|
+
appendIndex(index) {
|
|
780
|
+
this.indexes.add(index);
|
|
781
|
+
}
|
|
782
|
+
removeIndex(index) {
|
|
783
|
+
return this.indexes.delete(index);
|
|
784
|
+
}
|
|
720
785
|
removeExistingInterval(interval) {
|
|
721
786
|
this.removeIntervalFromIndexes(interval);
|
|
722
787
|
this.removeIntervalListeners(interval);
|
|
723
788
|
}
|
|
724
|
-
createInterval(start, end, intervalType, op) {
|
|
725
|
-
return this.helpers.create(this.label, start, end, this.client, intervalType, op);
|
|
789
|
+
createInterval(start, end, intervalType, op, stickiness = IntervalStickiness.END) {
|
|
790
|
+
return this.helpers.create(this.label, start, end, this.client, intervalType, op, undefined, stickiness);
|
|
726
791
|
}
|
|
727
|
-
addInterval(start, end, intervalType, props, op) {
|
|
792
|
+
addInterval(start, end, intervalType, props, op, stickiness = IntervalStickiness.END) {
|
|
728
793
|
var _a;
|
|
729
794
|
var _b;
|
|
730
|
-
const interval = this.createInterval(start, end, intervalType, op);
|
|
795
|
+
const interval = this.createInterval(start, end, intervalType, op, stickiness);
|
|
731
796
|
if (interval) {
|
|
732
797
|
if (!interval.properties) {
|
|
733
798
|
interval.properties = createMap();
|
|
@@ -780,7 +845,7 @@ export class LocalIntervalCollection {
|
|
|
780
845
|
// either, so this must be special-cased.
|
|
781
846
|
return ref;
|
|
782
847
|
}
|
|
783
|
-
return this.client.createLocalReferencePosition(segment, ref.getOffset(), ReferenceType.Transient, ref.properties);
|
|
848
|
+
return this.client.createLocalReferencePosition(segment, ref.getOffset(), ReferenceType.Transient, ref.properties, ref.slidingPreference);
|
|
784
849
|
};
|
|
785
850
|
if (interval instanceof SequenceInterval) {
|
|
786
851
|
let previousInterval;
|
|
@@ -815,12 +880,12 @@ export class LocalIntervalCollection {
|
|
|
815
880
|
LocalIntervalCollection.legacyIdPrefix = "legacy";
|
|
816
881
|
export const compareSequenceIntervalEnds = (a, b) => compareReferencePositions(a.end, b.end);
|
|
817
882
|
class SequenceIntervalCollectionFactory {
|
|
818
|
-
load(emitter, raw = []) {
|
|
883
|
+
load(emitter, raw = [], options) {
|
|
819
884
|
const helpers = {
|
|
820
885
|
compareEnds: compareSequenceIntervalEnds,
|
|
821
886
|
create: createSequenceInterval,
|
|
822
887
|
};
|
|
823
|
-
return new IntervalCollection(helpers, true, emitter, raw);
|
|
888
|
+
return new IntervalCollection(helpers, true, emitter, raw, options);
|
|
824
889
|
}
|
|
825
890
|
store(value) {
|
|
826
891
|
return value.serializeInternal();
|
|
@@ -841,7 +906,7 @@ SequenceIntervalCollectionValueType.Name = "sharedStringIntervalCollection";
|
|
|
841
906
|
SequenceIntervalCollectionValueType._factory = new SequenceIntervalCollectionFactory();
|
|
842
907
|
SequenceIntervalCollectionValueType._ops = makeOpsMap();
|
|
843
908
|
const compareIntervalEnds = (a, b) => a.end - b.end;
|
|
844
|
-
function createInterval(label, start, end, client) {
|
|
909
|
+
function createInterval(label, start, end, client, intervalType, op, fromSnapshot) {
|
|
845
910
|
const rangeProp = {};
|
|
846
911
|
if (label && label.length > 0) {
|
|
847
912
|
rangeProp[reservedRangeLabelsKey] = [label];
|
|
@@ -849,12 +914,12 @@ function createInterval(label, start, end, client) {
|
|
|
849
914
|
return new Interval(start, end, rangeProp);
|
|
850
915
|
}
|
|
851
916
|
class IntervalCollectionFactory {
|
|
852
|
-
load(emitter, raw = []) {
|
|
917
|
+
load(emitter, raw = [], options) {
|
|
853
918
|
const helpers = {
|
|
854
919
|
compareEnds: compareIntervalEnds,
|
|
855
920
|
create: createInterval,
|
|
856
921
|
};
|
|
857
|
-
const collection = new IntervalCollection(helpers, false, emitter, raw);
|
|
922
|
+
const collection = new IntervalCollection(helpers, false, emitter, raw, options);
|
|
858
923
|
collection.attachGraph(undefined, "");
|
|
859
924
|
return collection;
|
|
860
925
|
}
|
|
@@ -885,7 +950,7 @@ export function makeOpsMap() {
|
|
|
885
950
|
};
|
|
886
951
|
return new Map([
|
|
887
952
|
[
|
|
888
|
-
|
|
953
|
+
IntervalOpType.ADD,
|
|
889
954
|
{
|
|
890
955
|
process: (collection, params, local, op, localOpMetadata) => {
|
|
891
956
|
// if params is undefined, the interval was deleted during
|
|
@@ -900,7 +965,7 @@ export function makeOpsMap() {
|
|
|
900
965
|
},
|
|
901
966
|
],
|
|
902
967
|
[
|
|
903
|
-
|
|
968
|
+
IntervalOpType.DELETE,
|
|
904
969
|
{
|
|
905
970
|
process: (collection, params, local, op) => {
|
|
906
971
|
assert(op !== undefined, 0x3fc /* op should exist here */);
|
|
@@ -913,7 +978,7 @@ export function makeOpsMap() {
|
|
|
913
978
|
},
|
|
914
979
|
],
|
|
915
980
|
[
|
|
916
|
-
|
|
981
|
+
IntervalOpType.CHANGE,
|
|
917
982
|
{
|
|
918
983
|
process: (collection, params, local, op, localOpMetadata) => {
|
|
919
984
|
// if params is undefined, the interval was deleted during
|
|
@@ -929,7 +994,7 @@ export function makeOpsMap() {
|
|
|
929
994
|
],
|
|
930
995
|
]);
|
|
931
996
|
}
|
|
932
|
-
|
|
997
|
+
class IntervalCollectionIterator {
|
|
933
998
|
constructor(collection, iteratesForward = true, start, end) {
|
|
934
999
|
this.results = [];
|
|
935
1000
|
this.index = 0;
|
|
@@ -949,19 +1014,16 @@ export class IntervalCollectionIterator {
|
|
|
949
1014
|
}
|
|
950
1015
|
}
|
|
951
1016
|
/**
|
|
952
|
-
*
|
|
953
|
-
* This class is not a DDS in its own right, but emits events on mutating operations such that it's possible to
|
|
954
|
-
* integrate into a DDS.
|
|
955
|
-
* This aligns with its usage in `SharedSegmentSequence`, which allows associating intervals to positions in the
|
|
956
|
-
* sequence DDS which are broadcast to all other clients in an eventually consistent fashion.
|
|
1017
|
+
* {@inheritdoc IIntervalCollection}
|
|
957
1018
|
*/
|
|
958
1019
|
export class IntervalCollection extends TypedEventEmitter {
|
|
959
1020
|
/** @internal */
|
|
960
|
-
constructor(helpers, requiresClient, emitter, serializedIntervals) {
|
|
1021
|
+
constructor(helpers, requiresClient, emitter, serializedIntervals, options = {}) {
|
|
961
1022
|
super();
|
|
962
1023
|
this.helpers = helpers;
|
|
963
1024
|
this.requiresClient = requiresClient;
|
|
964
1025
|
this.emitter = emitter;
|
|
1026
|
+
this.options = options;
|
|
965
1027
|
this.localSeqToSerializedInterval = new Map();
|
|
966
1028
|
this.localSeqToRebasedInterval = new Map();
|
|
967
1029
|
this.pendingChangesStart = new Map();
|
|
@@ -973,6 +1035,36 @@ export class IntervalCollection extends TypedEventEmitter {
|
|
|
973
1035
|
get attached() {
|
|
974
1036
|
return !!this.localCollection;
|
|
975
1037
|
}
|
|
1038
|
+
/**
|
|
1039
|
+
* {@inheritdoc IIntervalCollection.attachIndex}
|
|
1040
|
+
*/
|
|
1041
|
+
attachIndex(index) {
|
|
1042
|
+
var _a;
|
|
1043
|
+
if (!this.attached) {
|
|
1044
|
+
throw new LoggingError("The local interval collection must exist");
|
|
1045
|
+
}
|
|
1046
|
+
for (const interval of this) {
|
|
1047
|
+
index.add(interval);
|
|
1048
|
+
}
|
|
1049
|
+
(_a = this.localCollection) === null || _a === void 0 ? void 0 : _a.appendIndex(index);
|
|
1050
|
+
}
|
|
1051
|
+
/**
|
|
1052
|
+
* {@inheritdoc IIntervalCollection.detachIndex}
|
|
1053
|
+
*/
|
|
1054
|
+
detachIndex(index) {
|
|
1055
|
+
var _a;
|
|
1056
|
+
if (!this.attached) {
|
|
1057
|
+
throw new LoggingError("The local interval collection must exist");
|
|
1058
|
+
}
|
|
1059
|
+
// Avoid removing intervals if the index does not exist
|
|
1060
|
+
if (!((_a = this.localCollection) === null || _a === void 0 ? void 0 : _a.removeIndex(index))) {
|
|
1061
|
+
return false;
|
|
1062
|
+
}
|
|
1063
|
+
for (const interval of this) {
|
|
1064
|
+
index.remove(interval);
|
|
1065
|
+
}
|
|
1066
|
+
return true;
|
|
1067
|
+
}
|
|
976
1068
|
rebasePositionWithSegmentSlide(pos, seqNumberFrom, localSeq) {
|
|
977
1069
|
var _a;
|
|
978
1070
|
if (!this.client) {
|
|
@@ -1028,8 +1120,8 @@ export class IntervalCollection extends TypedEventEmitter {
|
|
|
1028
1120
|
if (this.savedSerializedIntervals) {
|
|
1029
1121
|
for (const serializedInterval of this.savedSerializedIntervals) {
|
|
1030
1122
|
this.localCollection.ensureSerializedId(serializedInterval);
|
|
1031
|
-
const { start, end, intervalType, properties } = serializedInterval;
|
|
1032
|
-
const interval = this.helpers.create(label, start, end, client, intervalType, undefined, true);
|
|
1123
|
+
const { start, end, intervalType, properties, stickiness } = serializedInterval;
|
|
1124
|
+
const interval = this.helpers.create(label, start, end, client, intervalType, undefined, true, stickiness);
|
|
1033
1125
|
if (properties) {
|
|
1034
1126
|
interval.addProperties(properties);
|
|
1035
1127
|
}
|
|
@@ -1067,8 +1159,7 @@ export class IntervalCollection extends TypedEventEmitter {
|
|
|
1067
1159
|
}
|
|
1068
1160
|
}
|
|
1069
1161
|
/**
|
|
1070
|
-
* @
|
|
1071
|
-
* If no interval in the collection has this `id`, returns `undefined`.
|
|
1162
|
+
* {@inheritdoc IIntervalCollection.getIntervalById}
|
|
1072
1163
|
*/
|
|
1073
1164
|
getIntervalById(id) {
|
|
1074
1165
|
if (!this.localCollection) {
|
|
@@ -1077,16 +1168,9 @@ export class IntervalCollection extends TypedEventEmitter {
|
|
|
1077
1168
|
return this.localCollection.idIntervalIndex.getIntervalById(id);
|
|
1078
1169
|
}
|
|
1079
1170
|
/**
|
|
1080
|
-
*
|
|
1081
|
-
* @param start - interval start position (inclusive)
|
|
1082
|
-
* @param end - interval end position (exclusive)
|
|
1083
|
-
* @param intervalType - type of the interval. All intervals are SlideOnRemove. Intervals may not be Transient.
|
|
1084
|
-
* @param props - properties of the interval
|
|
1085
|
-
* @returns - the created interval
|
|
1086
|
-
* @remarks - See documentation on {@link SequenceInterval} for comments on interval endpoint semantics: there are subtleties
|
|
1087
|
-
* with how the current half-open behavior is represented.
|
|
1171
|
+
* {@inheritdoc IIntervalCollection.add}
|
|
1088
1172
|
*/
|
|
1089
|
-
add(start, end, intervalType, props) {
|
|
1173
|
+
add(start, end, intervalType, props, stickiness = IntervalStickiness.END) {
|
|
1090
1174
|
var _a, _b;
|
|
1091
1175
|
if (!this.localCollection) {
|
|
1092
1176
|
throw new LoggingError("attach must be called prior to adding intervals");
|
|
@@ -1094,7 +1178,10 @@ export class IntervalCollection extends TypedEventEmitter {
|
|
|
1094
1178
|
if (intervalType & IntervalType.Transient) {
|
|
1095
1179
|
throw new LoggingError("Can not add transient intervals");
|
|
1096
1180
|
}
|
|
1097
|
-
|
|
1181
|
+
if (stickiness !== IntervalStickiness.END && !this.options.intervalStickinessEnabled) {
|
|
1182
|
+
throw new UsageError("attempted to set interval stickiness without enabling `intervalStickinessEnabled` feature flag");
|
|
1183
|
+
}
|
|
1184
|
+
const interval = this.localCollection.addInterval(start, end, intervalType, props, undefined, stickiness);
|
|
1098
1185
|
if (interval) {
|
|
1099
1186
|
const serializedInterval = {
|
|
1100
1187
|
end,
|
|
@@ -1102,6 +1189,7 @@ export class IntervalCollection extends TypedEventEmitter {
|
|
|
1102
1189
|
properties: interval.properties,
|
|
1103
1190
|
sequenceNumber: (_b = (_a = this.client) === null || _a === void 0 ? void 0 : _a.getCurrentSeq()) !== null && _b !== void 0 ? _b : 0,
|
|
1104
1191
|
start,
|
|
1192
|
+
stickiness,
|
|
1105
1193
|
};
|
|
1106
1194
|
const localSeq = this.getNextLocalSeq();
|
|
1107
1195
|
this.localSeqToSerializedInterval.set(localSeq, serializedInterval);
|
|
@@ -1133,9 +1221,7 @@ export class IntervalCollection extends TypedEventEmitter {
|
|
|
1133
1221
|
this.emit("deleteInterval", interval, local, op);
|
|
1134
1222
|
}
|
|
1135
1223
|
/**
|
|
1136
|
-
*
|
|
1137
|
-
* @param id - Id of the interval to remove
|
|
1138
|
-
* @returns the removed interval
|
|
1224
|
+
* {@inheritdoc IIntervalCollection.removeIntervalById}
|
|
1139
1225
|
*/
|
|
1140
1226
|
removeIntervalById(id) {
|
|
1141
1227
|
if (!this.localCollection) {
|
|
@@ -1148,10 +1234,7 @@ export class IntervalCollection extends TypedEventEmitter {
|
|
|
1148
1234
|
return interval;
|
|
1149
1235
|
}
|
|
1150
1236
|
/**
|
|
1151
|
-
*
|
|
1152
|
-
* @param id - Id of the interval whose properties should be changed
|
|
1153
|
-
* @param props - Property set to apply to the interval. Shallow merging is used between any existing properties
|
|
1154
|
-
* and `prop`, i.e. the interval will end up with a property object equivalent to `{ ...oldProps, ...props }`.
|
|
1237
|
+
* {@inheritdoc IIntervalCollection.changeProperties}
|
|
1155
1238
|
*/
|
|
1156
1239
|
changeProperties(id, props) {
|
|
1157
1240
|
if (!this.attached) {
|
|
@@ -1181,11 +1264,7 @@ export class IntervalCollection extends TypedEventEmitter {
|
|
|
1181
1264
|
}
|
|
1182
1265
|
}
|
|
1183
1266
|
/**
|
|
1184
|
-
*
|
|
1185
|
-
* @param id - Id of the interval to change
|
|
1186
|
-
* @param start - New start value, if defined. `undefined` signifies this endpoint should be left unchanged.
|
|
1187
|
-
* @param end - New end value, if defined. `undefined` signifies this endpoint should be left unchanged.
|
|
1188
|
-
* @returns the interval that was changed, if it existed in the collection.
|
|
1267
|
+
* {@inheritdoc IIntervalCollection.change}
|
|
1189
1268
|
*/
|
|
1190
1269
|
change(id, start, end) {
|
|
1191
1270
|
if (!this.localCollection) {
|
|
@@ -1340,6 +1419,9 @@ export class IntervalCollection extends TypedEventEmitter {
|
|
|
1340
1419
|
throw new LoggingError("attachSequence must be called");
|
|
1341
1420
|
}
|
|
1342
1421
|
}
|
|
1422
|
+
/**
|
|
1423
|
+
* {@inheritdoc IIntervalCollection.attachDeserializer}
|
|
1424
|
+
*/
|
|
1343
1425
|
attachDeserializer(onDeserialize) {
|
|
1344
1426
|
// If no deserializer is specified can skip all processing work
|
|
1345
1427
|
if (!onDeserialize) {
|
|
@@ -1460,7 +1542,7 @@ export class IntervalCollection extends TypedEventEmitter {
|
|
|
1460
1542
|
}
|
|
1461
1543
|
if (needsStartUpdate) {
|
|
1462
1544
|
const props = interval.start.properties;
|
|
1463
|
-
interval.start = createPositionReferenceFromSegoff(this.client, newStart, interval.start.refType, op);
|
|
1545
|
+
interval.start = createPositionReferenceFromSegoff(this.client, newStart, interval.start.refType, op, startReferenceSlidingPreference(interval.stickiness));
|
|
1464
1546
|
if (props) {
|
|
1465
1547
|
interval.start.addProperties(props);
|
|
1466
1548
|
}
|
|
@@ -1472,7 +1554,7 @@ export class IntervalCollection extends TypedEventEmitter {
|
|
|
1472
1554
|
}
|
|
1473
1555
|
if (needsEndUpdate) {
|
|
1474
1556
|
const props = interval.end.properties;
|
|
1475
|
-
interval.end = createPositionReferenceFromSegoff(this.client, newEnd, interval.end.refType, op);
|
|
1557
|
+
interval.end = createPositionReferenceFromSegoff(this.client, newEnd, interval.end.refType, op, endReferenceSlidingPreference(interval.stickiness));
|
|
1476
1558
|
if (props) {
|
|
1477
1559
|
interval.end.addProperties(props);
|
|
1478
1560
|
}
|
|
@@ -1503,7 +1585,7 @@ export class IntervalCollection extends TypedEventEmitter {
|
|
|
1503
1585
|
throw new LoggingError("attachSequence must be called");
|
|
1504
1586
|
}
|
|
1505
1587
|
this.localCollection.ensureSerializedId(serializedInterval);
|
|
1506
|
-
const interval = this.localCollection.addInterval(serializedInterval.start, serializedInterval.end, serializedInterval.intervalType, serializedInterval.properties, op);
|
|
1588
|
+
const interval = this.localCollection.addInterval(serializedInterval.start, serializedInterval.end, serializedInterval.intervalType, serializedInterval.properties, op, serializedInterval.stickiness);
|
|
1507
1589
|
if (interval) {
|
|
1508
1590
|
if (this.onDeserialize) {
|
|
1509
1591
|
this.onDeserialize(interval);
|
|
@@ -1546,40 +1628,35 @@ export class IntervalCollection extends TypedEventEmitter {
|
|
|
1546
1628
|
return iterator;
|
|
1547
1629
|
}
|
|
1548
1630
|
/**
|
|
1549
|
-
* @
|
|
1631
|
+
* {@inheritdoc IIntervalCollection.CreateForwardIteratorWithStartPosition}
|
|
1550
1632
|
*/
|
|
1551
1633
|
CreateForwardIteratorWithStartPosition(startPosition) {
|
|
1552
1634
|
const iterator = new IntervalCollectionIterator(this, true, startPosition);
|
|
1553
1635
|
return iterator;
|
|
1554
1636
|
}
|
|
1555
1637
|
/**
|
|
1556
|
-
* @
|
|
1638
|
+
* {@inheritdoc IIntervalCollection.CreateBackwardIteratorWithStartPosition}
|
|
1557
1639
|
*/
|
|
1558
1640
|
CreateBackwardIteratorWithStartPosition(startPosition) {
|
|
1559
1641
|
const iterator = new IntervalCollectionIterator(this, false, startPosition);
|
|
1560
1642
|
return iterator;
|
|
1561
1643
|
}
|
|
1562
1644
|
/**
|
|
1563
|
-
* @
|
|
1645
|
+
* {@inheritdoc IIntervalCollection.CreateForwardIteratorWithEndPosition}
|
|
1564
1646
|
*/
|
|
1565
1647
|
CreateForwardIteratorWithEndPosition(endPosition) {
|
|
1566
1648
|
const iterator = new IntervalCollectionIterator(this, true, undefined, endPosition);
|
|
1567
1649
|
return iterator;
|
|
1568
1650
|
}
|
|
1569
1651
|
/**
|
|
1570
|
-
* @
|
|
1652
|
+
* {@inheritdoc IIntervalCollection.CreateBackwardIteratorWithEndPosition}
|
|
1571
1653
|
*/
|
|
1572
1654
|
CreateBackwardIteratorWithEndPosition(endPosition) {
|
|
1573
1655
|
const iterator = new IntervalCollectionIterator(this, false, undefined, endPosition);
|
|
1574
1656
|
return iterator;
|
|
1575
1657
|
}
|
|
1576
1658
|
/**
|
|
1577
|
-
*
|
|
1578
|
-
* @param results - Array to gather the results into. In lieu of a return value, this array will be populated with
|
|
1579
|
-
* intervals matching the query upon edit.
|
|
1580
|
-
* @param iteratesForward - whether or not iteration should be in the forward direction
|
|
1581
|
-
* @param start - If provided, only match intervals whose start point is equal to `start`.
|
|
1582
|
-
* @param end - If provided, only match intervals whose end point is equal to `end`.
|
|
1659
|
+
* {@inheritdoc IIntervalCollection.gatherIterationResults}
|
|
1583
1660
|
*/
|
|
1584
1661
|
gatherIterationResults(results, iteratesForward, start, end) {
|
|
1585
1662
|
if (!this.localCollection) {
|
|
@@ -1588,8 +1665,7 @@ export class IntervalCollection extends TypedEventEmitter {
|
|
|
1588
1665
|
this.localCollection.overlappingIntervalsIndex.gatherIterationResults(results, iteratesForward, start, end);
|
|
1589
1666
|
}
|
|
1590
1667
|
/**
|
|
1591
|
-
* @
|
|
1592
|
-
* `[startPosition, endPosition]`.
|
|
1668
|
+
* {@inheritdoc IIntervalCollection.findOverlappingIntervals}
|
|
1593
1669
|
*/
|
|
1594
1670
|
findOverlappingIntervals(startPosition, endPosition) {
|
|
1595
1671
|
if (!this.localCollection) {
|
|
@@ -1598,7 +1674,7 @@ export class IntervalCollection extends TypedEventEmitter {
|
|
|
1598
1674
|
return this.localCollection.overlappingIntervalsIndex.findOverlappingIntervals(startPosition, endPosition);
|
|
1599
1675
|
}
|
|
1600
1676
|
/**
|
|
1601
|
-
*
|
|
1677
|
+
* {@inheritdoc IIntervalCollection.map}
|
|
1602
1678
|
*/
|
|
1603
1679
|
map(fn) {
|
|
1604
1680
|
if (!this.localCollection) {
|
|
@@ -1608,12 +1684,18 @@ export class IntervalCollection extends TypedEventEmitter {
|
|
|
1608
1684
|
fn(interval);
|
|
1609
1685
|
}
|
|
1610
1686
|
}
|
|
1687
|
+
/**
|
|
1688
|
+
* {@inheritdoc IIntervalCollection.previousInterval}
|
|
1689
|
+
*/
|
|
1611
1690
|
previousInterval(pos) {
|
|
1612
1691
|
if (!this.localCollection) {
|
|
1613
1692
|
throw new LoggingError("attachSequence must be called");
|
|
1614
1693
|
}
|
|
1615
1694
|
return this.localCollection.endIntervalIndex.previousInterval(pos);
|
|
1616
1695
|
}
|
|
1696
|
+
/**
|
|
1697
|
+
* {@inheritdoc IIntervalCollection.nextInterval}
|
|
1698
|
+
*/
|
|
1617
1699
|
nextInterval(pos) {
|
|
1618
1700
|
if (!this.localCollection) {
|
|
1619
1701
|
throw new LoggingError("attachSequence must be called");
|