@fluidframework/sequence 2.43.0-343119 → 2.43.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 +21 -0
- package/api-report/sequence.legacy.alpha.api.md +2 -2
- package/dist/intervalCollection.d.ts +4 -9
- package/dist/intervalCollection.d.ts.map +1 -1
- package/dist/intervalCollection.js +96 -82
- package/dist/intervalCollection.js.map +1 -1
- package/dist/intervalCollectionMap.d.ts +1 -1
- package/dist/intervalCollectionMap.d.ts.map +1 -1
- package/dist/intervalCollectionMap.js +2 -2
- package/dist/intervalCollectionMap.js.map +1 -1
- package/dist/intervalCollectionMapInterfaces.d.ts +13 -6
- package/dist/intervalCollectionMapInterfaces.d.ts.map +1 -1
- package/dist/intervalCollectionMapInterfaces.js.map +1 -1
- package/dist/intervals/intervalUtils.d.ts +4 -3
- package/dist/intervals/intervalUtils.d.ts.map +1 -1
- package/dist/intervals/intervalUtils.js +16 -3
- package/dist/intervals/intervalUtils.js.map +1 -1
- package/dist/intervals/sequenceInterval.d.ts +21 -7
- package/dist/intervals/sequenceInterval.d.ts.map +1 -1
- package/dist/intervals/sequenceInterval.js +88 -16
- package/dist/intervals/sequenceInterval.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.js +1 -1
- package/dist/sequence.js.map +1 -1
- package/lib/intervalCollection.d.ts +4 -9
- package/lib/intervalCollection.d.ts.map +1 -1
- package/lib/intervalCollection.js +98 -82
- package/lib/intervalCollection.js.map +1 -1
- package/lib/intervalCollectionMap.d.ts +1 -1
- package/lib/intervalCollectionMap.d.ts.map +1 -1
- package/lib/intervalCollectionMap.js +2 -2
- package/lib/intervalCollectionMap.js.map +1 -1
- package/lib/intervalCollectionMapInterfaces.d.ts +13 -6
- package/lib/intervalCollectionMapInterfaces.d.ts.map +1 -1
- package/lib/intervalCollectionMapInterfaces.js.map +1 -1
- package/lib/intervals/intervalUtils.d.ts +4 -3
- package/lib/intervals/intervalUtils.d.ts.map +1 -1
- package/lib/intervals/intervalUtils.js +15 -3
- package/lib/intervals/intervalUtils.js.map +1 -1
- package/lib/intervals/sequenceInterval.d.ts +21 -7
- package/lib/intervals/sequenceInterval.d.ts.map +1 -1
- package/lib/intervals/sequenceInterval.js +88 -16
- package/lib/intervals/sequenceInterval.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.js +1 -1
- package/lib/sequence.js.map +1 -1
- package/package.json +18 -18
- package/src/intervalCollection.ts +122 -142
- package/src/intervalCollectionMap.ts +6 -2
- package/src/intervalCollectionMapInterfaces.ts +15 -5
- package/src/intervals/intervalUtils.ts +31 -3
- package/src/intervals/sequenceInterval.ts +135 -72
- package/src/packageVersion.ts +1 -1
- package/src/sequence.ts +1 -1
|
@@ -11,12 +11,10 @@ import { assert, unreachableCase } from "@fluidframework/core-utils/internal";
|
|
|
11
11
|
import { ISequencedDocumentMessage } from "@fluidframework/driver-definitions/internal";
|
|
12
12
|
import {
|
|
13
13
|
Client,
|
|
14
|
-
DetachedReferencePosition,
|
|
15
14
|
ISegment,
|
|
16
15
|
LocalReferencePosition,
|
|
17
16
|
PropertySet,
|
|
18
17
|
ReferenceType,
|
|
19
|
-
SlidingPreference,
|
|
20
18
|
getSlideToSegoff,
|
|
21
19
|
refTypeIncludesFlag,
|
|
22
20
|
reservedRangeLabelsKey,
|
|
@@ -27,6 +25,7 @@ import {
|
|
|
27
25
|
createLocalReconnectingPerspective,
|
|
28
26
|
DoublyLinkedList,
|
|
29
27
|
type ListNode,
|
|
28
|
+
SlidingPreference,
|
|
30
29
|
} from "@fluidframework/merge-tree/internal";
|
|
31
30
|
import { LoggingError, UsageError } from "@fluidframework/telemetry-utils/internal";
|
|
32
31
|
import { v4 as uuid } from "uuid";
|
|
@@ -57,9 +56,7 @@ import {
|
|
|
57
56
|
SerializedIntervalDelta,
|
|
58
57
|
createPositionReferenceFromSegoff,
|
|
59
58
|
createSequenceInterval,
|
|
60
|
-
endReferenceSlidingPreference,
|
|
61
59
|
getSerializedProperties,
|
|
62
|
-
startReferenceSlidingPreference,
|
|
63
60
|
} from "./intervals/index.js";
|
|
64
61
|
|
|
65
62
|
export type ISerializedIntervalCollectionV1 = ISerializedInterval[];
|
|
@@ -70,7 +67,7 @@ export interface ISerializedIntervalCollectionV2 {
|
|
|
70
67
|
intervals: CompressedSerializedInterval[];
|
|
71
68
|
}
|
|
72
69
|
|
|
73
|
-
|
|
70
|
+
function sidesFromStickiness(stickiness: IntervalStickiness) {
|
|
74
71
|
const startSide = (stickiness & IntervalStickiness.START) !== 0 ? Side.After : Side.Before;
|
|
75
72
|
const endSide = (stickiness & IntervalStickiness.END) !== 0 ? Side.Before : Side.After;
|
|
76
73
|
|
|
@@ -138,25 +135,6 @@ export function toOptionalSequencePlace(
|
|
|
138
135
|
return typeof pos === "number" && side !== undefined ? { pos, side } : pos;
|
|
139
136
|
}
|
|
140
137
|
|
|
141
|
-
export function computeStickinessFromSide(
|
|
142
|
-
startPos: number | "start" | "end" | undefined = -1,
|
|
143
|
-
startSide: Side = Side.Before,
|
|
144
|
-
endPos: number | "start" | "end" | undefined = -1,
|
|
145
|
-
endSide: Side = Side.Before,
|
|
146
|
-
): IntervalStickiness {
|
|
147
|
-
let stickiness: IntervalStickiness = IntervalStickiness.NONE;
|
|
148
|
-
|
|
149
|
-
if (startSide === Side.After || startPos === "start") {
|
|
150
|
-
stickiness |= IntervalStickiness.START;
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
if (endSide === Side.Before || endPos === "end") {
|
|
154
|
-
stickiness |= IntervalStickiness.END;
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
return stickiness as IntervalStickiness;
|
|
158
|
-
}
|
|
159
|
-
|
|
160
138
|
export class LocalIntervalCollection {
|
|
161
139
|
public readonly overlappingIntervalsIndex: ISequenceOverlappingIntervalsIndex;
|
|
162
140
|
public readonly idIntervalIndex: IIdIntervalIndex;
|
|
@@ -721,25 +699,27 @@ function removeMetadataFromPendingChanges(
|
|
|
721
699
|
): IntervalMessageLocalMetadata {
|
|
722
700
|
const acked = (localOpMetadataNode as ListNode<IntervalMessageLocalMetadata>)?.remove()
|
|
723
701
|
?.data;
|
|
724
|
-
assert(acked !== undefined,
|
|
702
|
+
assert(acked !== undefined, 0xbbe /* local change must exist */);
|
|
725
703
|
acked.endpointChangesNode?.remove();
|
|
726
704
|
return acked;
|
|
727
705
|
}
|
|
728
706
|
|
|
729
707
|
function clearEmptyPendingEntry(pendingChanges: PendingChanges, id: string) {
|
|
730
708
|
const pending = pendingChanges[id];
|
|
731
|
-
assert(pending !== undefined,
|
|
709
|
+
assert(pending !== undefined, 0xbbf /* pending must exist for local process */);
|
|
732
710
|
if (pending.local.empty) {
|
|
733
711
|
assert(
|
|
734
712
|
pending.endpointChanges?.empty !== false,
|
|
735
|
-
|
|
713
|
+
0xbc0 /* endpointChanges must be empty if not pending changes */,
|
|
736
714
|
);
|
|
737
715
|
// eslint-disable-next-line @typescript-eslint/no-dynamic-delete
|
|
738
716
|
delete pendingChanges[id];
|
|
739
717
|
}
|
|
740
718
|
}
|
|
741
719
|
|
|
742
|
-
function hasEndpointChanges(
|
|
720
|
+
function hasEndpointChanges(
|
|
721
|
+
serialized: SerializedIntervalDelta,
|
|
722
|
+
): serialized is ISerializedInterval {
|
|
743
723
|
return serialized.start !== undefined && serialized.end !== undefined;
|
|
744
724
|
}
|
|
745
725
|
|
|
@@ -781,6 +761,7 @@ export class IntervalCollection
|
|
|
781
761
|
if (md.type === "add" || (md.type === "change" && hasEndpointChanges(op.value))) {
|
|
782
762
|
const endpointChanges = (pending.endpointChanges ??= new DoublyLinkedList());
|
|
783
763
|
md.endpointChangesNode = endpointChanges.push(md).last;
|
|
764
|
+
md.rebased = undefined;
|
|
784
765
|
}
|
|
785
766
|
submitDelta(op, pending.local.push(md).last);
|
|
786
767
|
};
|
|
@@ -887,7 +868,7 @@ export class IntervalCollection
|
|
|
887
868
|
const { opName, value } = op;
|
|
888
869
|
assert(
|
|
889
870
|
(local === false && localOpMetadata === undefined) || opName === localOpMetadata?.type,
|
|
890
|
-
|
|
871
|
+
0xbc1 /* must be same type */,
|
|
891
872
|
);
|
|
892
873
|
switch (opName) {
|
|
893
874
|
case "add": {
|
|
@@ -924,6 +905,7 @@ export class IntervalCollection
|
|
|
924
905
|
public resubmitMessage(
|
|
925
906
|
op: IIntervalCollectionTypeOperationValue,
|
|
926
907
|
maybeMetadata: unknown,
|
|
908
|
+
squash: boolean,
|
|
927
909
|
): void {
|
|
928
910
|
const { opName, value } = op;
|
|
929
911
|
|
|
@@ -932,7 +914,7 @@ export class IntervalCollection
|
|
|
932
914
|
const rebasedValue =
|
|
933
915
|
localOpMetadata.endpointChangesNode === undefined
|
|
934
916
|
? value
|
|
935
|
-
: this.rebaseLocalInterval(localOpMetadata);
|
|
917
|
+
: this.rebaseLocalInterval(value, localOpMetadata, squash);
|
|
936
918
|
|
|
937
919
|
if (rebasedValue === undefined) {
|
|
938
920
|
const { id } = getSerializedProperties(value);
|
|
@@ -974,69 +956,69 @@ export class IntervalCollection
|
|
|
974
956
|
}
|
|
975
957
|
}
|
|
976
958
|
|
|
977
|
-
private
|
|
978
|
-
|
|
979
|
-
seqNumberFrom: number,
|
|
959
|
+
private rebaseReferenceWithSegmentSlide(
|
|
960
|
+
ref: LocalReferencePosition,
|
|
980
961
|
localSeq: number,
|
|
981
|
-
|
|
962
|
+
squash: boolean,
|
|
963
|
+
): { segment: ISegment; offset: number } | undefined {
|
|
982
964
|
if (!this.client) {
|
|
983
965
|
throw new LoggingError("mergeTree client must exist");
|
|
984
966
|
}
|
|
985
967
|
|
|
986
|
-
if (pos === "start" || pos === "end") {
|
|
987
|
-
return pos;
|
|
988
|
-
}
|
|
989
|
-
|
|
990
968
|
const { clientId } = this.client.getCollabWindow();
|
|
991
|
-
const
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
clientId: this.client.getLongClientId(clientId),
|
|
997
|
-
},
|
|
998
|
-
localSeq,
|
|
999
|
-
) ?? {};
|
|
1000
|
-
|
|
1001
|
-
// if segment is undefined, it slid off the string
|
|
1002
|
-
assert(segment !== undefined && offset !== undefined, 0x54e /* No segment found */);
|
|
969
|
+
const segment: ISegmentInternal | undefined = ref.getSegment();
|
|
970
|
+
if (segment?.endpointType) {
|
|
971
|
+
return { segment, offset: 0 };
|
|
972
|
+
}
|
|
973
|
+
const offset = ref.getOffset();
|
|
1003
974
|
|
|
1004
975
|
const segoff = getSlideToSegoff(
|
|
1005
|
-
{ segment, offset },
|
|
1006
|
-
|
|
1007
|
-
createLocalReconnectingPerspective(
|
|
1008
|
-
|
|
976
|
+
segment === undefined ? undefined : { segment, offset },
|
|
977
|
+
ref.slidingPreference,
|
|
978
|
+
createLocalReconnectingPerspective(
|
|
979
|
+
this.client.getCurrentSeq(),
|
|
980
|
+
clientId,
|
|
981
|
+
localSeq,
|
|
982
|
+
squash,
|
|
983
|
+
),
|
|
984
|
+
ref.canSlideToEndpoint,
|
|
1009
985
|
);
|
|
1010
986
|
|
|
1011
987
|
// case happens when rebasing op, but concurrently entire string has been deleted
|
|
1012
|
-
if (segoff
|
|
1013
|
-
|
|
988
|
+
if (segoff === undefined) {
|
|
989
|
+
if (ref.canSlideToEndpoint !== true) {
|
|
990
|
+
return undefined;
|
|
991
|
+
}
|
|
992
|
+
return {
|
|
993
|
+
segment:
|
|
994
|
+
ref.slidingPreference === SlidingPreference.FORWARD
|
|
995
|
+
? this.client.endOfTree
|
|
996
|
+
: this.client.startOfTree,
|
|
997
|
+
offset: 0,
|
|
998
|
+
};
|
|
1014
999
|
}
|
|
1015
|
-
|
|
1016
|
-
assert(
|
|
1017
|
-
offset !== undefined && 0 <= offset && offset < segment.cachedLength,
|
|
1018
|
-
0x54f /* Invalid offset */,
|
|
1019
|
-
);
|
|
1020
|
-
return this.client.findReconnectionPosition(segoff.segment, localSeq) + segoff.offset;
|
|
1000
|
+
return segoff;
|
|
1021
1001
|
}
|
|
1022
1002
|
|
|
1023
1003
|
private computeRebasedPositions(
|
|
1024
1004
|
localOpMetadata: IntervalAddLocalMetadata | IntervalChangeLocalMetadata,
|
|
1025
|
-
|
|
1005
|
+
squash: boolean,
|
|
1006
|
+
): Record<"start" | "end", { segment: ISegmentInternal; offset: number }> | "detached" {
|
|
1026
1007
|
assert(
|
|
1027
1008
|
this.client !== undefined,
|
|
1028
1009
|
0x550 /* Client should be defined when computing rebased position */,
|
|
1029
1010
|
);
|
|
1030
|
-
|
|
1031
|
-
const
|
|
1032
|
-
const
|
|
1033
|
-
if (start
|
|
1034
|
-
|
|
1011
|
+
|
|
1012
|
+
const { localSeq, interval } = localOpMetadata;
|
|
1013
|
+
const start = this.rebaseReferenceWithSegmentSlide(interval.start, localSeq, squash);
|
|
1014
|
+
if (start === undefined) {
|
|
1015
|
+
return "detached";
|
|
1035
1016
|
}
|
|
1036
|
-
|
|
1037
|
-
|
|
1017
|
+
const end = this.rebaseReferenceWithSegmentSlide(interval.end, localSeq, squash);
|
|
1018
|
+
if (end === undefined) {
|
|
1019
|
+
return "detached";
|
|
1038
1020
|
}
|
|
1039
|
-
return
|
|
1021
|
+
return { start, end };
|
|
1040
1022
|
}
|
|
1041
1023
|
|
|
1042
1024
|
public attachGraph(client: Client, label: string) {
|
|
@@ -1051,11 +1033,11 @@ export class IntervalCollection
|
|
|
1051
1033
|
// Instantiate the local interval collection based on the saved intervals
|
|
1052
1034
|
this.client = client;
|
|
1053
1035
|
if (client) {
|
|
1054
|
-
client.on("normalize", () => {
|
|
1036
|
+
client.on("normalize", (squash) => {
|
|
1055
1037
|
for (const pending of Object.values(this.pending)) {
|
|
1056
1038
|
if (pending?.endpointChanges !== undefined) {
|
|
1057
1039
|
for (const local of pending.endpointChanges) {
|
|
1058
|
-
local.data.rebased = this.computeRebasedPositions(local.data);
|
|
1040
|
+
local.data.rebased = this.computeRebasedPositions(local.data, squash);
|
|
1059
1041
|
}
|
|
1060
1042
|
}
|
|
1061
1043
|
}
|
|
@@ -1216,7 +1198,7 @@ export class IntervalCollection
|
|
|
1216
1198
|
{
|
|
1217
1199
|
type: "add",
|
|
1218
1200
|
localSeq,
|
|
1219
|
-
|
|
1201
|
+
interval,
|
|
1220
1202
|
},
|
|
1221
1203
|
);
|
|
1222
1204
|
}
|
|
@@ -1345,7 +1327,7 @@ export class IntervalCollection
|
|
|
1345
1327
|
type: "change",
|
|
1346
1328
|
localSeq,
|
|
1347
1329
|
previous: interval.serialize(),
|
|
1348
|
-
|
|
1330
|
+
interval: newInterval ?? interval,
|
|
1349
1331
|
};
|
|
1350
1332
|
|
|
1351
1333
|
this.submitDelta(
|
|
@@ -1369,8 +1351,10 @@ export class IntervalCollection
|
|
|
1369
1351
|
}
|
|
1370
1352
|
if (newInterval) {
|
|
1371
1353
|
this.emitChange(newInterval, interval, true, false);
|
|
1372
|
-
|
|
1373
|
-
|
|
1354
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
1355
|
+
interval.start.properties!.interval = undefined;
|
|
1356
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
1357
|
+
interval.end.properties!.interval = undefined;
|
|
1374
1358
|
}
|
|
1375
1359
|
return newInterval;
|
|
1376
1360
|
}
|
|
@@ -1477,65 +1461,71 @@ export class IntervalCollection
|
|
|
1477
1461
|
*
|
|
1478
1462
|
*/
|
|
1479
1463
|
public rebaseLocalInterval(
|
|
1464
|
+
original: SerializedIntervalDelta,
|
|
1480
1465
|
localOpMetadata: IntervalAddLocalMetadata | IntervalChangeLocalMetadata,
|
|
1466
|
+
squash: boolean,
|
|
1481
1467
|
): SerializedIntervalDelta | undefined {
|
|
1482
|
-
|
|
1483
|
-
if (!this.client) {
|
|
1468
|
+
if (!this.client || !hasEndpointChanges(original)) {
|
|
1484
1469
|
// If there's no associated mergeTree client, the originally submitted op is still correct.
|
|
1485
1470
|
return original;
|
|
1486
1471
|
}
|
|
1487
|
-
if (!this.attached) {
|
|
1472
|
+
if (!this.attached || this.localCollection === undefined) {
|
|
1488
1473
|
throw new LoggingError("attachSequence must be called");
|
|
1489
1474
|
}
|
|
1490
1475
|
|
|
1491
|
-
const { localSeq } = localOpMetadata;
|
|
1492
|
-
const { intervalType, properties, stickiness, startSide, endSide } = original;
|
|
1476
|
+
const { localSeq, interval } = localOpMetadata;
|
|
1493
1477
|
const { id } = getSerializedProperties(original);
|
|
1494
|
-
const
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
const rebased: SerializedIntervalDelta = {
|
|
1500
|
-
start: startRebased,
|
|
1501
|
-
end: endRebased,
|
|
1502
|
-
intervalType,
|
|
1503
|
-
sequenceNumber: this.client?.getCurrentSeq() ?? 0,
|
|
1504
|
-
properties,
|
|
1505
|
-
stickiness,
|
|
1506
|
-
startSide,
|
|
1507
|
-
endSide,
|
|
1508
|
-
};
|
|
1478
|
+
const rebasedEndpoint = (localOpMetadata.rebased ??= this.computeRebasedPositions(
|
|
1479
|
+
localOpMetadata,
|
|
1480
|
+
squash,
|
|
1481
|
+
));
|
|
1482
|
+
const localInterval = this.localCollection.idIntervalIndex.getIntervalById(id);
|
|
1509
1483
|
|
|
1510
1484
|
// if the interval slid off the string, rebase the op to be a noop and delete the interval.
|
|
1511
|
-
if (
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1485
|
+
if (rebasedEndpoint === "detached") {
|
|
1486
|
+
if (
|
|
1487
|
+
localInterval !== undefined &&
|
|
1488
|
+
(localInterval === interval || localOpMetadata.type === "add")
|
|
1489
|
+
) {
|
|
1516
1490
|
this.localCollection?.removeExistingInterval(localInterval);
|
|
1517
1491
|
}
|
|
1518
1492
|
return undefined;
|
|
1519
1493
|
}
|
|
1520
1494
|
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1495
|
+
const { start, end } = rebasedEndpoint;
|
|
1496
|
+
if (
|
|
1497
|
+
interval.start.getSegment() !== start.segment ||
|
|
1498
|
+
interval.start.getOffset() !== start.offset ||
|
|
1499
|
+
interval.end.getSegment() !== end.segment ||
|
|
1500
|
+
interval.end.getOffset() !== end.offset
|
|
1501
|
+
) {
|
|
1502
|
+
if (localInterval === interval) {
|
|
1503
|
+
this.localCollection.removeExistingInterval(localInterval);
|
|
1504
|
+
}
|
|
1505
|
+
const old = interval.clone();
|
|
1506
|
+
interval.moveEndpointReferences(rebasedEndpoint);
|
|
1507
|
+
if (localInterval === interval) {
|
|
1508
|
+
this.localCollection.add(interval);
|
|
1509
|
+
this.emitChange(interval, old, true, true);
|
|
1510
|
+
}
|
|
1511
|
+
this.client.removeLocalReferencePosition(old.start);
|
|
1512
|
+
this.client.removeLocalReferencePosition(old.end);
|
|
1531
1513
|
}
|
|
1532
1514
|
|
|
1533
|
-
return
|
|
1515
|
+
return {
|
|
1516
|
+
...original,
|
|
1517
|
+
start:
|
|
1518
|
+
start.segment.endpointType ??
|
|
1519
|
+
this.client.findReconnectionPosition(start.segment, localSeq) + start.offset,
|
|
1520
|
+
end:
|
|
1521
|
+
end.segment.endpointType ??
|
|
1522
|
+
this.client.findReconnectionPosition(end.segment, localSeq) + end.offset,
|
|
1523
|
+
sequenceNumber: this.client?.getCurrentSeq() ?? 0,
|
|
1524
|
+
};
|
|
1534
1525
|
}
|
|
1535
1526
|
|
|
1536
1527
|
private getSlideToSegment(
|
|
1537
1528
|
lref: LocalReferencePosition,
|
|
1538
|
-
slidingPreference: SlidingPreference,
|
|
1539
1529
|
): { segment: ISegment; offset: number } | undefined {
|
|
1540
1530
|
if (!this.client) {
|
|
1541
1531
|
throw new LoggingError("client does not exist");
|
|
@@ -1553,9 +1543,9 @@ export class IntervalCollection
|
|
|
1553
1543
|
}
|
|
1554
1544
|
return getSlideToSegoff(
|
|
1555
1545
|
segoff,
|
|
1556
|
-
slidingPreference,
|
|
1546
|
+
lref.slidingPreference,
|
|
1557
1547
|
undefined,
|
|
1558
|
-
|
|
1548
|
+
lref.canSlideToEndpoint,
|
|
1559
1549
|
);
|
|
1560
1550
|
}
|
|
1561
1551
|
|
|
@@ -1567,14 +1557,8 @@ export class IntervalCollection
|
|
|
1567
1557
|
return;
|
|
1568
1558
|
}
|
|
1569
1559
|
|
|
1570
|
-
const newStart = this.getSlideToSegment(
|
|
1571
|
-
|
|
1572
|
-
startReferenceSlidingPreference(interval.stickiness),
|
|
1573
|
-
);
|
|
1574
|
-
const newEnd = this.getSlideToSegment(
|
|
1575
|
-
interval.end,
|
|
1576
|
-
endReferenceSlidingPreference(interval.stickiness),
|
|
1577
|
-
);
|
|
1560
|
+
const newStart = this.getSlideToSegment(interval.start);
|
|
1561
|
+
const newEnd = this.getSlideToSegment(interval.end);
|
|
1578
1562
|
|
|
1579
1563
|
const id = interval.getIntervalId();
|
|
1580
1564
|
const hasPendingChange = this.hasPendingEndpointChanges(id);
|
|
@@ -1606,16 +1590,14 @@ export class IntervalCollection
|
|
|
1606
1590
|
|
|
1607
1591
|
if (needsStartUpdate) {
|
|
1608
1592
|
const props = interval.start.properties;
|
|
1609
|
-
interval.start = createPositionReferenceFromSegoff(
|
|
1610
|
-
this.client,
|
|
1611
|
-
newStart,
|
|
1612
|
-
interval.start.refType,
|
|
1593
|
+
interval.start = createPositionReferenceFromSegoff({
|
|
1594
|
+
client: this.client,
|
|
1595
|
+
segoff: newStart,
|
|
1596
|
+
refType: interval.start.refType,
|
|
1613
1597
|
op,
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
startReferenceSlidingPreference(interval.stickiness) === SlidingPreference.BACKWARD,
|
|
1618
|
-
);
|
|
1598
|
+
slidingPreference: interval.start.slidingPreference,
|
|
1599
|
+
canSlideToEndpoint: interval.start.canSlideToEndpoint,
|
|
1600
|
+
});
|
|
1619
1601
|
if (props) {
|
|
1620
1602
|
interval.start.addProperties(props);
|
|
1621
1603
|
}
|
|
@@ -1627,16 +1609,14 @@ export class IntervalCollection
|
|
|
1627
1609
|
}
|
|
1628
1610
|
if (needsEndUpdate) {
|
|
1629
1611
|
const props = interval.end.properties;
|
|
1630
|
-
interval.end = createPositionReferenceFromSegoff(
|
|
1631
|
-
this.client,
|
|
1632
|
-
newEnd,
|
|
1633
|
-
interval.end.refType,
|
|
1612
|
+
interval.end = createPositionReferenceFromSegoff({
|
|
1613
|
+
client: this.client,
|
|
1614
|
+
segoff: newEnd,
|
|
1615
|
+
refType: interval.end.refType,
|
|
1634
1616
|
op,
|
|
1635
|
-
|
|
1636
|
-
|
|
1637
|
-
|
|
1638
|
-
endReferenceSlidingPreference(interval.stickiness) === SlidingPreference.FORWARD,
|
|
1639
|
-
);
|
|
1617
|
+
slidingPreference: interval.end.slidingPreference,
|
|
1618
|
+
canSlideToEndpoint: interval.end.canSlideToEndpoint,
|
|
1619
|
+
});
|
|
1640
1620
|
if (props) {
|
|
1641
1621
|
interval.end.addProperties(props);
|
|
1642
1622
|
}
|
|
@@ -188,12 +188,16 @@ export class IntervalCollectionMap {
|
|
|
188
188
|
* also sent if we are asked to resubmit the message.
|
|
189
189
|
* @returns True if the operation was submitted, false otherwise.
|
|
190
190
|
*/
|
|
191
|
-
public tryResubmitMessage(
|
|
191
|
+
public tryResubmitMessage(
|
|
192
|
+
content: unknown,
|
|
193
|
+
localOpMetadata: unknown,
|
|
194
|
+
squash: boolean,
|
|
195
|
+
): boolean {
|
|
192
196
|
if (isMapOperation(content)) {
|
|
193
197
|
const { value, key } = content;
|
|
194
198
|
const localValue = this.data.get(key);
|
|
195
199
|
assert(localValue !== undefined, 0x3f8 /* Local value expected on resubmission */);
|
|
196
|
-
localValue.resubmitMessage(value, localOpMetadata);
|
|
200
|
+
localValue.resubmitMessage(value, localOpMetadata, squash);
|
|
197
201
|
return true;
|
|
198
202
|
}
|
|
199
203
|
return false;
|
|
@@ -4,7 +4,11 @@
|
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
import { ISequencedDocumentMessage } from "@fluidframework/driver-definitions/internal";
|
|
7
|
-
import {
|
|
7
|
+
import {
|
|
8
|
+
IMergeTreeOptions,
|
|
9
|
+
ListNode,
|
|
10
|
+
type ISegmentInternal,
|
|
11
|
+
} from "@fluidframework/merge-tree/internal";
|
|
8
12
|
|
|
9
13
|
import type {
|
|
10
14
|
IntervalCollection,
|
|
@@ -15,28 +19,34 @@ import {
|
|
|
15
19
|
ISerializedInterval,
|
|
16
20
|
IntervalDeltaOpType,
|
|
17
21
|
SerializedIntervalDelta,
|
|
22
|
+
type SequenceIntervalClass,
|
|
18
23
|
} from "./intervals/index.js";
|
|
19
24
|
|
|
20
25
|
export interface IntervalAddLocalMetadata {
|
|
21
26
|
type: typeof IntervalDeltaOpType.ADD;
|
|
22
27
|
localSeq: number;
|
|
23
28
|
endpointChangesNode?: ListNode<IntervalAddLocalMetadata | IntervalChangeLocalMetadata>;
|
|
24
|
-
rebased?:
|
|
25
|
-
|
|
29
|
+
rebased?:
|
|
30
|
+
| Record<"start" | "end", { segment: ISegmentInternal; offset: number }>
|
|
31
|
+
| "detached";
|
|
32
|
+
interval: SequenceIntervalClass;
|
|
26
33
|
}
|
|
27
34
|
export interface IntervalChangeLocalMetadata {
|
|
28
35
|
type: typeof IntervalDeltaOpType.CHANGE;
|
|
29
36
|
localSeq: number;
|
|
30
37
|
previous: ISerializedInterval;
|
|
31
38
|
endpointChangesNode?: ListNode<IntervalChangeLocalMetadata | IntervalChangeLocalMetadata>;
|
|
32
|
-
rebased?:
|
|
33
|
-
|
|
39
|
+
rebased?:
|
|
40
|
+
| Record<"start" | "end", { segment: ISegmentInternal; offset: number }>
|
|
41
|
+
| "detached";
|
|
42
|
+
interval: SequenceIntervalClass;
|
|
34
43
|
}
|
|
35
44
|
export interface IntervalDeleteLocalMetadata {
|
|
36
45
|
type: typeof IntervalDeltaOpType.DELETE;
|
|
37
46
|
localSeq: number;
|
|
38
47
|
previous: ISerializedInterval;
|
|
39
48
|
endpointChangesNode?: undefined;
|
|
49
|
+
interval?: undefined;
|
|
40
50
|
}
|
|
41
51
|
export type IntervalMessageLocalMetadata =
|
|
42
52
|
| IntervalAddLocalMetadata
|
|
@@ -57,7 +57,7 @@ export interface IInterval {
|
|
|
57
57
|
end: SequencePlace | undefined,
|
|
58
58
|
op?: ISequencedDocumentMessage,
|
|
59
59
|
localSeq?: number,
|
|
60
|
-
|
|
60
|
+
canSlideToEndpoint?: boolean,
|
|
61
61
|
): IInterval | undefined;
|
|
62
62
|
/**
|
|
63
63
|
* @returns whether this interval overlaps with `b`.
|
|
@@ -264,8 +264,12 @@ export const IntervalStickiness = {
|
|
|
264
264
|
export type IntervalStickiness = (typeof IntervalStickiness)[keyof typeof IntervalStickiness];
|
|
265
265
|
|
|
266
266
|
export function startReferenceSlidingPreference(
|
|
267
|
-
|
|
267
|
+
startPos: number | "start" | "end" | undefined,
|
|
268
|
+
startSide: Side,
|
|
269
|
+
endPos: number | "start" | "end" | undefined,
|
|
270
|
+
endSide: Side,
|
|
268
271
|
): SlidingPreference {
|
|
272
|
+
const stickiness = computeStickinessFromSide(startPos, startSide, endPos, endSide);
|
|
269
273
|
// if any start stickiness, prefer sliding backwards
|
|
270
274
|
return (stickiness & IntervalStickiness.START) === 0
|
|
271
275
|
? SlidingPreference.FORWARD
|
|
@@ -273,10 +277,34 @@ export function startReferenceSlidingPreference(
|
|
|
273
277
|
}
|
|
274
278
|
|
|
275
279
|
export function endReferenceSlidingPreference(
|
|
276
|
-
|
|
280
|
+
startPos: number | "start" | "end" | undefined,
|
|
281
|
+
startSide: Side,
|
|
282
|
+
endPos: number | "start" | "end" | undefined,
|
|
283
|
+
endSide: Side,
|
|
277
284
|
): SlidingPreference {
|
|
285
|
+
const stickiness = computeStickinessFromSide(startPos, startSide, endPos, endSide);
|
|
286
|
+
|
|
278
287
|
// if any end stickiness, prefer sliding forwards
|
|
279
288
|
return (stickiness & IntervalStickiness.END) === 0
|
|
280
289
|
? SlidingPreference.BACKWARD
|
|
281
290
|
: SlidingPreference.FORWARD;
|
|
282
291
|
}
|
|
292
|
+
|
|
293
|
+
export function computeStickinessFromSide(
|
|
294
|
+
startPos: number | "start" | "end" | undefined,
|
|
295
|
+
startSide: Side,
|
|
296
|
+
endPos: number | "start" | "end" | undefined,
|
|
297
|
+
endSide: Side,
|
|
298
|
+
): IntervalStickiness {
|
|
299
|
+
let stickiness: IntervalStickiness = IntervalStickiness.NONE;
|
|
300
|
+
|
|
301
|
+
if (startSide === Side.After || startPos === "start") {
|
|
302
|
+
stickiness |= IntervalStickiness.START;
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
if (endSide === Side.Before || endPos === "end") {
|
|
306
|
+
stickiness |= IntervalStickiness.END;
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
return stickiness as IntervalStickiness;
|
|
310
|
+
}
|