@fluidframework/sequence 2.0.0-internal.4.2.1 → 2.0.0-internal.4.4.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 +12 -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 -1
- package/dist/index.js.map +1 -1
- package/dist/intervalCollection.d.ts +199 -46
- package/dist/intervalCollection.d.ts.map +1 -1
- package/dist/intervalCollection.js +164 -78
- 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 +2 -2
- 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.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 +199 -46
- package/lib/intervalCollection.d.ts.map +1 -1
- package/lib/intervalCollection.js +164 -78
- 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 +2 -2
- 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.map +1 -1
- package/lib/sharedIntervalCollection.js +1 -1
- package/lib/sharedIntervalCollection.js.map +1 -1
- package/package.json +38 -14
- package/src/defaultMap.ts +4 -1
- package/src/defaultMapInterfaces.ts +13 -1
- package/src/index.ts +16 -1
- package/src/intervalCollection.ts +370 -57
- package/src/packageVersion.ts +1 -1
- package/src/revertibles.ts +572 -0
- package/src/sequence.ts +12 -3
- package/src/sharedIntervalCollection.ts +3 -2
- package/.vscode/launch.json +0 -16
|
@@ -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
|
*/
|
|
@@ -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,6 +994,10 @@ export function makeOpsMap() {
|
|
|
929
994
|
],
|
|
930
995
|
]);
|
|
931
996
|
}
|
|
997
|
+
/**
|
|
998
|
+
* @deprecated - Public export will be removed. Use an appropriate iterator creation method on
|
|
999
|
+
* {@link IIntervalCollection} to iterate intervals instead.
|
|
1000
|
+
*/
|
|
932
1001
|
export class IntervalCollectionIterator {
|
|
933
1002
|
constructor(collection, iteratesForward = true, start, end) {
|
|
934
1003
|
this.results = [];
|
|
@@ -949,19 +1018,16 @@ export class IntervalCollectionIterator {
|
|
|
949
1018
|
}
|
|
950
1019
|
}
|
|
951
1020
|
/**
|
|
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.
|
|
1021
|
+
* @deprecated - Use {@link IIntervalCollection} instead.
|
|
957
1022
|
*/
|
|
958
1023
|
export class IntervalCollection extends TypedEventEmitter {
|
|
959
1024
|
/** @internal */
|
|
960
|
-
constructor(helpers, requiresClient, emitter, serializedIntervals) {
|
|
1025
|
+
constructor(helpers, requiresClient, emitter, serializedIntervals, options = {}) {
|
|
961
1026
|
super();
|
|
962
1027
|
this.helpers = helpers;
|
|
963
1028
|
this.requiresClient = requiresClient;
|
|
964
1029
|
this.emitter = emitter;
|
|
1030
|
+
this.options = options;
|
|
965
1031
|
this.localSeqToSerializedInterval = new Map();
|
|
966
1032
|
this.localSeqToRebasedInterval = new Map();
|
|
967
1033
|
this.pendingChangesStart = new Map();
|
|
@@ -973,6 +1039,36 @@ export class IntervalCollection extends TypedEventEmitter {
|
|
|
973
1039
|
get attached() {
|
|
974
1040
|
return !!this.localCollection;
|
|
975
1041
|
}
|
|
1042
|
+
/**
|
|
1043
|
+
* {@inheritdoc IIntervalCollection.attachIndex}
|
|
1044
|
+
*/
|
|
1045
|
+
attachIndex(index) {
|
|
1046
|
+
var _a;
|
|
1047
|
+
if (!this.attached) {
|
|
1048
|
+
throw new LoggingError("The local interval collection must exist");
|
|
1049
|
+
}
|
|
1050
|
+
for (const interval of this) {
|
|
1051
|
+
index.add(interval);
|
|
1052
|
+
}
|
|
1053
|
+
(_a = this.localCollection) === null || _a === void 0 ? void 0 : _a.appendIndex(index);
|
|
1054
|
+
}
|
|
1055
|
+
/**
|
|
1056
|
+
* {@inheritdoc IIntervalCollection.detachIndex}
|
|
1057
|
+
*/
|
|
1058
|
+
detachIndex(index) {
|
|
1059
|
+
var _a;
|
|
1060
|
+
if (!this.attached) {
|
|
1061
|
+
throw new LoggingError("The local interval collection must exist");
|
|
1062
|
+
}
|
|
1063
|
+
// Avoid removing intervals if the index does not exist
|
|
1064
|
+
if (!((_a = this.localCollection) === null || _a === void 0 ? void 0 : _a.removeIndex(index))) {
|
|
1065
|
+
return false;
|
|
1066
|
+
}
|
|
1067
|
+
for (const interval of this) {
|
|
1068
|
+
index.remove(interval);
|
|
1069
|
+
}
|
|
1070
|
+
return true;
|
|
1071
|
+
}
|
|
976
1072
|
rebasePositionWithSegmentSlide(pos, seqNumberFrom, localSeq) {
|
|
977
1073
|
var _a;
|
|
978
1074
|
if (!this.client) {
|
|
@@ -1028,8 +1124,8 @@ export class IntervalCollection extends TypedEventEmitter {
|
|
|
1028
1124
|
if (this.savedSerializedIntervals) {
|
|
1029
1125
|
for (const serializedInterval of this.savedSerializedIntervals) {
|
|
1030
1126
|
this.localCollection.ensureSerializedId(serializedInterval);
|
|
1031
|
-
const { start, end, intervalType, properties } = serializedInterval;
|
|
1032
|
-
const interval = this.helpers.create(label, start, end, client, intervalType, undefined, true);
|
|
1127
|
+
const { start, end, intervalType, properties, stickiness } = serializedInterval;
|
|
1128
|
+
const interval = this.helpers.create(label, start, end, client, intervalType, undefined, true, stickiness);
|
|
1033
1129
|
if (properties) {
|
|
1034
1130
|
interval.addProperties(properties);
|
|
1035
1131
|
}
|
|
@@ -1067,8 +1163,7 @@ export class IntervalCollection extends TypedEventEmitter {
|
|
|
1067
1163
|
}
|
|
1068
1164
|
}
|
|
1069
1165
|
/**
|
|
1070
|
-
* @
|
|
1071
|
-
* If no interval in the collection has this `id`, returns `undefined`.
|
|
1166
|
+
* {@inheritdoc IIntervalCollection.getIntervalById}
|
|
1072
1167
|
*/
|
|
1073
1168
|
getIntervalById(id) {
|
|
1074
1169
|
if (!this.localCollection) {
|
|
@@ -1077,16 +1172,9 @@ export class IntervalCollection extends TypedEventEmitter {
|
|
|
1077
1172
|
return this.localCollection.idIntervalIndex.getIntervalById(id);
|
|
1078
1173
|
}
|
|
1079
1174
|
/**
|
|
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.
|
|
1175
|
+
* {@inheritdoc IIntervalCollection.add}
|
|
1088
1176
|
*/
|
|
1089
|
-
add(start, end, intervalType, props) {
|
|
1177
|
+
add(start, end, intervalType, props, stickiness = IntervalStickiness.END) {
|
|
1090
1178
|
var _a, _b;
|
|
1091
1179
|
if (!this.localCollection) {
|
|
1092
1180
|
throw new LoggingError("attach must be called prior to adding intervals");
|
|
@@ -1094,7 +1182,10 @@ export class IntervalCollection extends TypedEventEmitter {
|
|
|
1094
1182
|
if (intervalType & IntervalType.Transient) {
|
|
1095
1183
|
throw new LoggingError("Can not add transient intervals");
|
|
1096
1184
|
}
|
|
1097
|
-
|
|
1185
|
+
if (stickiness !== IntervalStickiness.END && !this.options.intervalStickinessEnabled) {
|
|
1186
|
+
throw new UsageError("attempted to set interval stickiness without enabling `intervalStickinessEnabled` feature flag");
|
|
1187
|
+
}
|
|
1188
|
+
const interval = this.localCollection.addInterval(start, end, intervalType, props, undefined, stickiness);
|
|
1098
1189
|
if (interval) {
|
|
1099
1190
|
const serializedInterval = {
|
|
1100
1191
|
end,
|
|
@@ -1102,6 +1193,7 @@ export class IntervalCollection extends TypedEventEmitter {
|
|
|
1102
1193
|
properties: interval.properties,
|
|
1103
1194
|
sequenceNumber: (_b = (_a = this.client) === null || _a === void 0 ? void 0 : _a.getCurrentSeq()) !== null && _b !== void 0 ? _b : 0,
|
|
1104
1195
|
start,
|
|
1196
|
+
stickiness,
|
|
1105
1197
|
};
|
|
1106
1198
|
const localSeq = this.getNextLocalSeq();
|
|
1107
1199
|
this.localSeqToSerializedInterval.set(localSeq, serializedInterval);
|
|
@@ -1133,9 +1225,7 @@ export class IntervalCollection extends TypedEventEmitter {
|
|
|
1133
1225
|
this.emit("deleteInterval", interval, local, op);
|
|
1134
1226
|
}
|
|
1135
1227
|
/**
|
|
1136
|
-
*
|
|
1137
|
-
* @param id - Id of the interval to remove
|
|
1138
|
-
* @returns the removed interval
|
|
1228
|
+
* {@inheritdoc IIntervalCollection.removeIntervalById}
|
|
1139
1229
|
*/
|
|
1140
1230
|
removeIntervalById(id) {
|
|
1141
1231
|
if (!this.localCollection) {
|
|
@@ -1148,10 +1238,7 @@ export class IntervalCollection extends TypedEventEmitter {
|
|
|
1148
1238
|
return interval;
|
|
1149
1239
|
}
|
|
1150
1240
|
/**
|
|
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 }`.
|
|
1241
|
+
* {@inheritdoc IIntervalCollection.changeProperties}
|
|
1155
1242
|
*/
|
|
1156
1243
|
changeProperties(id, props) {
|
|
1157
1244
|
if (!this.attached) {
|
|
@@ -1181,11 +1268,7 @@ export class IntervalCollection extends TypedEventEmitter {
|
|
|
1181
1268
|
}
|
|
1182
1269
|
}
|
|
1183
1270
|
/**
|
|
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.
|
|
1271
|
+
* {@inheritdoc IIntervalCollection.change}
|
|
1189
1272
|
*/
|
|
1190
1273
|
change(id, start, end) {
|
|
1191
1274
|
if (!this.localCollection) {
|
|
@@ -1340,6 +1423,9 @@ export class IntervalCollection extends TypedEventEmitter {
|
|
|
1340
1423
|
throw new LoggingError("attachSequence must be called");
|
|
1341
1424
|
}
|
|
1342
1425
|
}
|
|
1426
|
+
/**
|
|
1427
|
+
* {@inheritdoc IIntervalCollection.attachDeserializer}
|
|
1428
|
+
*/
|
|
1343
1429
|
attachDeserializer(onDeserialize) {
|
|
1344
1430
|
// If no deserializer is specified can skip all processing work
|
|
1345
1431
|
if (!onDeserialize) {
|
|
@@ -1460,7 +1546,7 @@ export class IntervalCollection extends TypedEventEmitter {
|
|
|
1460
1546
|
}
|
|
1461
1547
|
if (needsStartUpdate) {
|
|
1462
1548
|
const props = interval.start.properties;
|
|
1463
|
-
interval.start = createPositionReferenceFromSegoff(this.client, newStart, interval.start.refType, op);
|
|
1549
|
+
interval.start = createPositionReferenceFromSegoff(this.client, newStart, interval.start.refType, op, startReferenceSlidingPreference(interval.stickiness));
|
|
1464
1550
|
if (props) {
|
|
1465
1551
|
interval.start.addProperties(props);
|
|
1466
1552
|
}
|
|
@@ -1472,7 +1558,7 @@ export class IntervalCollection extends TypedEventEmitter {
|
|
|
1472
1558
|
}
|
|
1473
1559
|
if (needsEndUpdate) {
|
|
1474
1560
|
const props = interval.end.properties;
|
|
1475
|
-
interval.end = createPositionReferenceFromSegoff(this.client, newEnd, interval.end.refType, op);
|
|
1561
|
+
interval.end = createPositionReferenceFromSegoff(this.client, newEnd, interval.end.refType, op, endReferenceSlidingPreference(interval.stickiness));
|
|
1476
1562
|
if (props) {
|
|
1477
1563
|
interval.end.addProperties(props);
|
|
1478
1564
|
}
|
|
@@ -1503,7 +1589,7 @@ export class IntervalCollection extends TypedEventEmitter {
|
|
|
1503
1589
|
throw new LoggingError("attachSequence must be called");
|
|
1504
1590
|
}
|
|
1505
1591
|
this.localCollection.ensureSerializedId(serializedInterval);
|
|
1506
|
-
const interval = this.localCollection.addInterval(serializedInterval.start, serializedInterval.end, serializedInterval.intervalType, serializedInterval.properties, op);
|
|
1592
|
+
const interval = this.localCollection.addInterval(serializedInterval.start, serializedInterval.end, serializedInterval.intervalType, serializedInterval.properties, op, serializedInterval.stickiness);
|
|
1507
1593
|
if (interval) {
|
|
1508
1594
|
if (this.onDeserialize) {
|
|
1509
1595
|
this.onDeserialize(interval);
|
|
@@ -1546,40 +1632,35 @@ export class IntervalCollection extends TypedEventEmitter {
|
|
|
1546
1632
|
return iterator;
|
|
1547
1633
|
}
|
|
1548
1634
|
/**
|
|
1549
|
-
* @
|
|
1635
|
+
* {@inheritdoc IIntervalCollection.CreateForwardIteratorWithStartPosition}
|
|
1550
1636
|
*/
|
|
1551
1637
|
CreateForwardIteratorWithStartPosition(startPosition) {
|
|
1552
1638
|
const iterator = new IntervalCollectionIterator(this, true, startPosition);
|
|
1553
1639
|
return iterator;
|
|
1554
1640
|
}
|
|
1555
1641
|
/**
|
|
1556
|
-
* @
|
|
1642
|
+
* {@inheritdoc IIntervalCollection.CreateBackwardIteratorWithStartPosition}
|
|
1557
1643
|
*/
|
|
1558
1644
|
CreateBackwardIteratorWithStartPosition(startPosition) {
|
|
1559
1645
|
const iterator = new IntervalCollectionIterator(this, false, startPosition);
|
|
1560
1646
|
return iterator;
|
|
1561
1647
|
}
|
|
1562
1648
|
/**
|
|
1563
|
-
* @
|
|
1649
|
+
* {@inheritdoc IIntervalCollection.CreateForwardIteratorWithEndPosition}
|
|
1564
1650
|
*/
|
|
1565
1651
|
CreateForwardIteratorWithEndPosition(endPosition) {
|
|
1566
1652
|
const iterator = new IntervalCollectionIterator(this, true, undefined, endPosition);
|
|
1567
1653
|
return iterator;
|
|
1568
1654
|
}
|
|
1569
1655
|
/**
|
|
1570
|
-
* @
|
|
1656
|
+
* {@inheritdoc IIntervalCollection.CreateBackwardIteratorWithEndPosition}
|
|
1571
1657
|
*/
|
|
1572
1658
|
CreateBackwardIteratorWithEndPosition(endPosition) {
|
|
1573
1659
|
const iterator = new IntervalCollectionIterator(this, false, undefined, endPosition);
|
|
1574
1660
|
return iterator;
|
|
1575
1661
|
}
|
|
1576
1662
|
/**
|
|
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`.
|
|
1663
|
+
* {@inheritdoc IIntervalCollection.gatherIterationResults}
|
|
1583
1664
|
*/
|
|
1584
1665
|
gatherIterationResults(results, iteratesForward, start, end) {
|
|
1585
1666
|
if (!this.localCollection) {
|
|
@@ -1588,8 +1669,7 @@ export class IntervalCollection extends TypedEventEmitter {
|
|
|
1588
1669
|
this.localCollection.overlappingIntervalsIndex.gatherIterationResults(results, iteratesForward, start, end);
|
|
1589
1670
|
}
|
|
1590
1671
|
/**
|
|
1591
|
-
* @
|
|
1592
|
-
* `[startPosition, endPosition]`.
|
|
1672
|
+
* {@inheritdoc IIntervalCollection.findOverlappingIntervals}
|
|
1593
1673
|
*/
|
|
1594
1674
|
findOverlappingIntervals(startPosition, endPosition) {
|
|
1595
1675
|
if (!this.localCollection) {
|
|
@@ -1598,7 +1678,7 @@ export class IntervalCollection extends TypedEventEmitter {
|
|
|
1598
1678
|
return this.localCollection.overlappingIntervalsIndex.findOverlappingIntervals(startPosition, endPosition);
|
|
1599
1679
|
}
|
|
1600
1680
|
/**
|
|
1601
|
-
*
|
|
1681
|
+
* {@inheritdoc IIntervalCollection.map}
|
|
1602
1682
|
*/
|
|
1603
1683
|
map(fn) {
|
|
1604
1684
|
if (!this.localCollection) {
|
|
@@ -1608,12 +1688,18 @@ export class IntervalCollection extends TypedEventEmitter {
|
|
|
1608
1688
|
fn(interval);
|
|
1609
1689
|
}
|
|
1610
1690
|
}
|
|
1691
|
+
/**
|
|
1692
|
+
* {@inheritdoc IIntervalCollection.previousInterval}
|
|
1693
|
+
*/
|
|
1611
1694
|
previousInterval(pos) {
|
|
1612
1695
|
if (!this.localCollection) {
|
|
1613
1696
|
throw new LoggingError("attachSequence must be called");
|
|
1614
1697
|
}
|
|
1615
1698
|
return this.localCollection.endIntervalIndex.previousInterval(pos);
|
|
1616
1699
|
}
|
|
1700
|
+
/**
|
|
1701
|
+
* {@inheritdoc IIntervalCollection.nextInterval}
|
|
1702
|
+
*/
|
|
1617
1703
|
nextInterval(pos) {
|
|
1618
1704
|
if (!this.localCollection) {
|
|
1619
1705
|
throw new LoggingError("attachSequence must be called");
|