@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
|
@@ -5,12 +5,12 @@
|
|
|
5
5
|
/* eslint-disable no-bitwise */
|
|
6
6
|
import { TypedEventEmitter } from "@fluid-internal/client-utils";
|
|
7
7
|
import { assert, unreachableCase } from "@fluidframework/core-utils/internal";
|
|
8
|
-
import {
|
|
8
|
+
import { ReferenceType, getSlideToSegoff, refTypeIncludesFlag, reservedRangeLabelsKey, Side, endpointPosAndSide, createLocalReconnectingPerspective, DoublyLinkedList, SlidingPreference, } from "@fluidframework/merge-tree/internal";
|
|
9
9
|
import { LoggingError, UsageError } from "@fluidframework/telemetry-utils/internal";
|
|
10
10
|
import { v4 as uuid } from "uuid";
|
|
11
11
|
import { createIdIntervalIndex, EndpointIndex, OverlappingIntervalsIndex, } from "./intervalIndex/index.js";
|
|
12
|
-
import { IntervalStickiness, IntervalType, createPositionReferenceFromSegoff, createSequenceInterval,
|
|
13
|
-
|
|
12
|
+
import { IntervalStickiness, IntervalType, createPositionReferenceFromSegoff, createSequenceInterval, getSerializedProperties, } from "./intervals/index.js";
|
|
13
|
+
function sidesFromStickiness(stickiness) {
|
|
14
14
|
const startSide = (stickiness & IntervalStickiness.START) !== 0 ? Side.After : Side.Before;
|
|
15
15
|
const endSide = (stickiness & IntervalStickiness.END) !== 0 ? Side.Before : Side.After;
|
|
16
16
|
return { startSide, endSide };
|
|
@@ -60,16 +60,6 @@ export function toSequencePlace(pos, side) {
|
|
|
60
60
|
export function toOptionalSequencePlace(pos, side) {
|
|
61
61
|
return typeof pos === "number" && side !== undefined ? { pos, side } : pos;
|
|
62
62
|
}
|
|
63
|
-
export function computeStickinessFromSide(startPos = -1, startSide = Side.Before, endPos = -1, endSide = Side.Before) {
|
|
64
|
-
let stickiness = IntervalStickiness.NONE;
|
|
65
|
-
if (startSide === Side.After || startPos === "start") {
|
|
66
|
-
stickiness |= IntervalStickiness.START;
|
|
67
|
-
}
|
|
68
|
-
if (endSide === Side.Before || endPos === "end") {
|
|
69
|
-
stickiness |= IntervalStickiness.END;
|
|
70
|
-
}
|
|
71
|
-
return stickiness;
|
|
72
|
-
}
|
|
73
63
|
export class LocalIntervalCollection {
|
|
74
64
|
constructor(client, label, options,
|
|
75
65
|
/** Callback invoked each time one of the endpoints of an interval slides. */
|
|
@@ -211,15 +201,15 @@ class IntervalCollectionIterator {
|
|
|
211
201
|
function removeMetadataFromPendingChanges(localOpMetadataNode) {
|
|
212
202
|
const acked = localOpMetadataNode?.remove()
|
|
213
203
|
?.data;
|
|
214
|
-
assert(acked !== undefined,
|
|
204
|
+
assert(acked !== undefined, 0xbbe /* local change must exist */);
|
|
215
205
|
acked.endpointChangesNode?.remove();
|
|
216
206
|
return acked;
|
|
217
207
|
}
|
|
218
208
|
function clearEmptyPendingEntry(pendingChanges, id) {
|
|
219
209
|
const pending = pendingChanges[id];
|
|
220
|
-
assert(pending !== undefined,
|
|
210
|
+
assert(pending !== undefined, 0xbbf /* pending must exist for local process */);
|
|
221
211
|
if (pending.local.empty) {
|
|
222
|
-
assert(pending.endpointChanges?.empty !== false,
|
|
212
|
+
assert(pending.endpointChanges?.empty !== false, 0xbc0 /* endpointChanges must be empty if not pending changes */);
|
|
223
213
|
// eslint-disable-next-line @typescript-eslint/no-dynamic-delete
|
|
224
214
|
delete pendingChanges[id];
|
|
225
215
|
}
|
|
@@ -246,6 +236,7 @@ export class IntervalCollection extends TypedEventEmitter {
|
|
|
246
236
|
if (md.type === "add" || (md.type === "change" && hasEndpointChanges(op.value))) {
|
|
247
237
|
const endpointChanges = (pending.endpointChanges ??= new DoublyLinkedList());
|
|
248
238
|
md.endpointChangesNode = endpointChanges.push(md).last;
|
|
239
|
+
md.rebased = undefined;
|
|
249
240
|
}
|
|
250
241
|
submitDelta(op, pending.local.push(md).last);
|
|
251
242
|
};
|
|
@@ -332,7 +323,7 @@ export class IntervalCollection extends TypedEventEmitter {
|
|
|
332
323
|
? removeMetadataFromPendingChanges(maybeMetadata)
|
|
333
324
|
: undefined;
|
|
334
325
|
const { opName, value } = op;
|
|
335
|
-
assert((local === false && localOpMetadata === undefined) || opName === localOpMetadata?.type,
|
|
326
|
+
assert((local === false && localOpMetadata === undefined) || opName === localOpMetadata?.type, 0xbc1 /* must be same type */);
|
|
336
327
|
switch (opName) {
|
|
337
328
|
case "add": {
|
|
338
329
|
this.ackAdd(value, local, message,
|
|
@@ -357,12 +348,12 @@ export class IntervalCollection extends TypedEventEmitter {
|
|
|
357
348
|
clearEmptyPendingEntry(this.pending, id);
|
|
358
349
|
}
|
|
359
350
|
}
|
|
360
|
-
resubmitMessage(op, maybeMetadata) {
|
|
351
|
+
resubmitMessage(op, maybeMetadata, squash) {
|
|
361
352
|
const { opName, value } = op;
|
|
362
353
|
const localOpMetadata = removeMetadataFromPendingChanges(maybeMetadata);
|
|
363
354
|
const rebasedValue = localOpMetadata.endpointChangesNode === undefined
|
|
364
355
|
? value
|
|
365
|
-
: this.rebaseLocalInterval(localOpMetadata);
|
|
356
|
+
: this.rebaseLocalInterval(value, localOpMetadata, squash);
|
|
366
357
|
if (rebasedValue === undefined) {
|
|
367
358
|
const { id } = getSerializedProperties(value);
|
|
368
359
|
clearEmptyPendingEntry(this.pending, id);
|
|
@@ -400,40 +391,43 @@ export class IntervalCollection extends TypedEventEmitter {
|
|
|
400
391
|
throw new Error("unknown ops should not be stashed");
|
|
401
392
|
}
|
|
402
393
|
}
|
|
403
|
-
|
|
394
|
+
rebaseReferenceWithSegmentSlide(ref, localSeq, squash) {
|
|
404
395
|
if (!this.client) {
|
|
405
396
|
throw new LoggingError("mergeTree client must exist");
|
|
406
397
|
}
|
|
407
|
-
if (pos === "start" || pos === "end") {
|
|
408
|
-
return pos;
|
|
409
|
-
}
|
|
410
398
|
const { clientId } = this.client.getCollabWindow();
|
|
411
|
-
const
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
}
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
const segoff = getSlideToSegoff({ segment, offset }, undefined, createLocalReconnectingPerspective(this.client.getCurrentSeq(), clientId, localSeq), this.options.mergeTreeReferencesCanSlideToEndpoint);
|
|
399
|
+
const segment = ref.getSegment();
|
|
400
|
+
if (segment?.endpointType) {
|
|
401
|
+
return { segment, offset: 0 };
|
|
402
|
+
}
|
|
403
|
+
const offset = ref.getOffset();
|
|
404
|
+
const segoff = getSlideToSegoff(segment === undefined ? undefined : { segment, offset }, ref.slidingPreference, createLocalReconnectingPerspective(this.client.getCurrentSeq(), clientId, localSeq, squash), ref.canSlideToEndpoint);
|
|
418
405
|
// case happens when rebasing op, but concurrently entire string has been deleted
|
|
419
|
-
if (segoff
|
|
420
|
-
|
|
406
|
+
if (segoff === undefined) {
|
|
407
|
+
if (ref.canSlideToEndpoint !== true) {
|
|
408
|
+
return undefined;
|
|
409
|
+
}
|
|
410
|
+
return {
|
|
411
|
+
segment: ref.slidingPreference === SlidingPreference.FORWARD
|
|
412
|
+
? this.client.endOfTree
|
|
413
|
+
: this.client.startOfTree,
|
|
414
|
+
offset: 0,
|
|
415
|
+
};
|
|
421
416
|
}
|
|
422
|
-
|
|
423
|
-
return this.client.findReconnectionPosition(segoff.segment, localSeq) + segoff.offset;
|
|
417
|
+
return segoff;
|
|
424
418
|
}
|
|
425
|
-
computeRebasedPositions(localOpMetadata) {
|
|
419
|
+
computeRebasedPositions(localOpMetadata, squash) {
|
|
426
420
|
assert(this.client !== undefined, 0x550 /* Client should be defined when computing rebased position */);
|
|
427
|
-
const { localSeq,
|
|
428
|
-
const
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
rebased.start = this.rebasePositionWithSegmentSlide(start, sequenceNumber, localSeq);
|
|
421
|
+
const { localSeq, interval } = localOpMetadata;
|
|
422
|
+
const start = this.rebaseReferenceWithSegmentSlide(interval.start, localSeq, squash);
|
|
423
|
+
if (start === undefined) {
|
|
424
|
+
return "detached";
|
|
432
425
|
}
|
|
433
|
-
|
|
434
|
-
|
|
426
|
+
const end = this.rebaseReferenceWithSegmentSlide(interval.end, localSeq, squash);
|
|
427
|
+
if (end === undefined) {
|
|
428
|
+
return "detached";
|
|
435
429
|
}
|
|
436
|
-
return
|
|
430
|
+
return { start, end };
|
|
437
431
|
}
|
|
438
432
|
attachGraph(client, label) {
|
|
439
433
|
if (this.attached) {
|
|
@@ -445,11 +439,11 @@ export class IntervalCollection extends TypedEventEmitter {
|
|
|
445
439
|
// Instantiate the local interval collection based on the saved intervals
|
|
446
440
|
this.client = client;
|
|
447
441
|
if (client) {
|
|
448
|
-
client.on("normalize", () => {
|
|
442
|
+
client.on("normalize", (squash) => {
|
|
449
443
|
for (const pending of Object.values(this.pending)) {
|
|
450
444
|
if (pending?.endpointChanges !== undefined) {
|
|
451
445
|
for (const local of pending.endpointChanges) {
|
|
452
|
-
local.data.rebased = this.computeRebasedPositions(local.data);
|
|
446
|
+
local.data.rebased = this.computeRebasedPositions(local.data, squash);
|
|
453
447
|
}
|
|
454
448
|
}
|
|
455
449
|
}
|
|
@@ -538,7 +532,7 @@ export class IntervalCollection extends TypedEventEmitter {
|
|
|
538
532
|
}, {
|
|
539
533
|
type: "add",
|
|
540
534
|
localSeq,
|
|
541
|
-
|
|
535
|
+
interval,
|
|
542
536
|
});
|
|
543
537
|
}
|
|
544
538
|
}
|
|
@@ -628,7 +622,7 @@ export class IntervalCollection extends TypedEventEmitter {
|
|
|
628
622
|
type: "change",
|
|
629
623
|
localSeq,
|
|
630
624
|
previous: interval.serialize(),
|
|
631
|
-
|
|
625
|
+
interval: newInterval ?? interval,
|
|
632
626
|
};
|
|
633
627
|
this.submitDelta({
|
|
634
628
|
opName: "change",
|
|
@@ -641,8 +635,10 @@ export class IntervalCollection extends TypedEventEmitter {
|
|
|
641
635
|
}
|
|
642
636
|
if (newInterval) {
|
|
643
637
|
this.emitChange(newInterval, interval, true, false);
|
|
644
|
-
|
|
645
|
-
|
|
638
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
639
|
+
interval.start.properties.interval = undefined;
|
|
640
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
641
|
+
interval.end.properties.interval = undefined;
|
|
646
642
|
}
|
|
647
643
|
return newInterval;
|
|
648
644
|
}
|
|
@@ -725,47 +721,53 @@ export class IntervalCollection extends TypedEventEmitter {
|
|
|
725
721
|
* to a range that no longer exists, and the interval was unable to slide.
|
|
726
722
|
*
|
|
727
723
|
*/
|
|
728
|
-
rebaseLocalInterval(localOpMetadata) {
|
|
729
|
-
|
|
730
|
-
if (!this.client) {
|
|
724
|
+
rebaseLocalInterval(original, localOpMetadata, squash) {
|
|
725
|
+
if (!this.client || !hasEndpointChanges(original)) {
|
|
731
726
|
// If there's no associated mergeTree client, the originally submitted op is still correct.
|
|
732
727
|
return original;
|
|
733
728
|
}
|
|
734
|
-
if (!this.attached) {
|
|
729
|
+
if (!this.attached || this.localCollection === undefined) {
|
|
735
730
|
throw new LoggingError("attachSequence must be called");
|
|
736
731
|
}
|
|
737
|
-
const { localSeq } = localOpMetadata;
|
|
738
|
-
const { intervalType, properties, stickiness, startSide, endSide } = original;
|
|
732
|
+
const { localSeq, interval } = localOpMetadata;
|
|
739
733
|
const { id } = getSerializedProperties(original);
|
|
740
|
-
const
|
|
741
|
-
|
|
742
|
-
const localInterval = this.localCollection?.idIntervalIndex.getIntervalById(id);
|
|
743
|
-
const rebased = {
|
|
744
|
-
start: startRebased,
|
|
745
|
-
end: endRebased,
|
|
746
|
-
intervalType,
|
|
747
|
-
sequenceNumber: this.client?.getCurrentSeq() ?? 0,
|
|
748
|
-
properties,
|
|
749
|
-
stickiness,
|
|
750
|
-
startSide,
|
|
751
|
-
endSide,
|
|
752
|
-
};
|
|
734
|
+
const rebasedEndpoint = (localOpMetadata.rebased ??= this.computeRebasedPositions(localOpMetadata, squash));
|
|
735
|
+
const localInterval = this.localCollection.idIntervalIndex.getIntervalById(id);
|
|
753
736
|
// if the interval slid off the string, rebase the op to be a noop and delete the interval.
|
|
754
|
-
if (
|
|
755
|
-
(
|
|
756
|
-
|
|
737
|
+
if (rebasedEndpoint === "detached") {
|
|
738
|
+
if (localInterval !== undefined &&
|
|
739
|
+
(localInterval === interval || localOpMetadata.type === "add")) {
|
|
757
740
|
this.localCollection?.removeExistingInterval(localInterval);
|
|
758
741
|
}
|
|
759
742
|
return undefined;
|
|
760
743
|
}
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
744
|
+
const { start, end } = rebasedEndpoint;
|
|
745
|
+
if (interval.start.getSegment() !== start.segment ||
|
|
746
|
+
interval.start.getOffset() !== start.offset ||
|
|
747
|
+
interval.end.getSegment() !== end.segment ||
|
|
748
|
+
interval.end.getOffset() !== end.offset) {
|
|
749
|
+
if (localInterval === interval) {
|
|
750
|
+
this.localCollection.removeExistingInterval(localInterval);
|
|
751
|
+
}
|
|
752
|
+
const old = interval.clone();
|
|
753
|
+
interval.moveEndpointReferences(rebasedEndpoint);
|
|
754
|
+
if (localInterval === interval) {
|
|
755
|
+
this.localCollection.add(interval);
|
|
756
|
+
this.emitChange(interval, old, true, true);
|
|
757
|
+
}
|
|
758
|
+
this.client.removeLocalReferencePosition(old.start);
|
|
759
|
+
this.client.removeLocalReferencePosition(old.end);
|
|
765
760
|
}
|
|
766
|
-
return
|
|
761
|
+
return {
|
|
762
|
+
...original,
|
|
763
|
+
start: start.segment.endpointType ??
|
|
764
|
+
this.client.findReconnectionPosition(start.segment, localSeq) + start.offset,
|
|
765
|
+
end: end.segment.endpointType ??
|
|
766
|
+
this.client.findReconnectionPosition(end.segment, localSeq) + end.offset,
|
|
767
|
+
sequenceNumber: this.client?.getCurrentSeq() ?? 0,
|
|
768
|
+
};
|
|
767
769
|
}
|
|
768
|
-
getSlideToSegment(lref
|
|
770
|
+
getSlideToSegment(lref) {
|
|
769
771
|
if (!this.client) {
|
|
770
772
|
throw new LoggingError("client does not exist");
|
|
771
773
|
}
|
|
@@ -780,15 +782,15 @@ export class IntervalCollection extends TypedEventEmitter {
|
|
|
780
782
|
if (segoff.segment.localRefs?.has(lref) !== true) {
|
|
781
783
|
return undefined;
|
|
782
784
|
}
|
|
783
|
-
return getSlideToSegoff(segoff, slidingPreference, undefined,
|
|
785
|
+
return getSlideToSegoff(segoff, lref.slidingPreference, undefined, lref.canSlideToEndpoint);
|
|
784
786
|
}
|
|
785
787
|
ackInterval(interval, op) {
|
|
786
788
|
if (!refTypeIncludesFlag(interval.start, ReferenceType.StayOnRemove) &&
|
|
787
789
|
!refTypeIncludesFlag(interval.end, ReferenceType.StayOnRemove)) {
|
|
788
790
|
return;
|
|
789
791
|
}
|
|
790
|
-
const newStart = this.getSlideToSegment(interval.start
|
|
791
|
-
const newEnd = this.getSlideToSegment(interval.end
|
|
792
|
+
const newStart = this.getSlideToSegment(interval.start);
|
|
793
|
+
const newEnd = this.getSlideToSegment(interval.end);
|
|
792
794
|
const id = interval.getIntervalId();
|
|
793
795
|
const hasPendingChange = this.hasPendingEndpointChanges(id);
|
|
794
796
|
if (!hasPendingChange) {
|
|
@@ -812,7 +814,14 @@ export class IntervalCollection extends TypedEventEmitter {
|
|
|
812
814
|
}
|
|
813
815
|
if (needsStartUpdate) {
|
|
814
816
|
const props = interval.start.properties;
|
|
815
|
-
interval.start = createPositionReferenceFromSegoff(
|
|
817
|
+
interval.start = createPositionReferenceFromSegoff({
|
|
818
|
+
client: this.client,
|
|
819
|
+
segoff: newStart,
|
|
820
|
+
refType: interval.start.refType,
|
|
821
|
+
op,
|
|
822
|
+
slidingPreference: interval.start.slidingPreference,
|
|
823
|
+
canSlideToEndpoint: interval.start.canSlideToEndpoint,
|
|
824
|
+
});
|
|
816
825
|
if (props) {
|
|
817
826
|
interval.start.addProperties(props);
|
|
818
827
|
}
|
|
@@ -824,7 +833,14 @@ export class IntervalCollection extends TypedEventEmitter {
|
|
|
824
833
|
}
|
|
825
834
|
if (needsEndUpdate) {
|
|
826
835
|
const props = interval.end.properties;
|
|
827
|
-
interval.end = createPositionReferenceFromSegoff(
|
|
836
|
+
interval.end = createPositionReferenceFromSegoff({
|
|
837
|
+
client: this.client,
|
|
838
|
+
segoff: newEnd,
|
|
839
|
+
refType: interval.end.refType,
|
|
840
|
+
op,
|
|
841
|
+
slidingPreference: interval.end.slidingPreference,
|
|
842
|
+
canSlideToEndpoint: interval.end.canSlideToEndpoint,
|
|
843
|
+
});
|
|
828
844
|
if (props) {
|
|
829
845
|
interval.end.addProperties(props);
|
|
830
846
|
}
|