@fluidframework/merge-tree 0.59.4001 → 1.1.0-75972
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.eslintrc.js +1 -1
- package/README.md +1 -1
- package/REFERENCEPOSITIONS.md +199 -0
- package/dist/client.d.ts +30 -4
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +89 -47
- package/dist/client.js.map +1 -1
- package/dist/collections.d.ts +5 -4
- package/dist/collections.d.ts.map +1 -1
- package/dist/collections.js +17 -18
- package/dist/collections.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -1
- package/dist/index.js.map +1 -1
- package/dist/localReference.d.ts +11 -3
- package/dist/localReference.d.ts.map +1 -1
- package/dist/localReference.js +25 -8
- package/dist/localReference.js.map +1 -1
- package/dist/mergeTree.d.ts +23 -3
- package/dist/mergeTree.d.ts.map +1 -1
- package/dist/mergeTree.js +136 -48
- package/dist/mergeTree.js.map +1 -1
- package/dist/mergeTreeDeltaCallback.d.ts +8 -10
- package/dist/mergeTreeDeltaCallback.d.ts.map +1 -1
- package/dist/mergeTreeDeltaCallback.js +6 -10
- package/dist/mergeTreeDeltaCallback.js.map +1 -1
- package/dist/opBuilder.js +6 -5
- package/dist/opBuilder.js.map +1 -1
- package/dist/ops.d.ts +12 -10
- package/dist/ops.d.ts.map +1 -1
- package/dist/ops.js +7 -7
- package/dist/ops.js.map +1 -1
- package/dist/referencePositions.d.ts +1 -1
- package/dist/referencePositions.d.ts.map +1 -1
- package/dist/referencePositions.js +3 -2
- package/dist/referencePositions.js.map +1 -1
- package/lib/client.d.ts +30 -4
- package/lib/client.d.ts.map +1 -1
- package/lib/client.js +89 -47
- package/lib/client.js.map +1 -1
- package/lib/collections.d.ts +5 -4
- package/lib/collections.d.ts.map +1 -1
- package/lib/collections.js +17 -18
- package/lib/collections.js.map +1 -1
- package/lib/index.d.ts +1 -1
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +1 -1
- package/lib/index.js.map +1 -1
- package/lib/localReference.d.ts +11 -3
- package/lib/localReference.d.ts.map +1 -1
- package/lib/localReference.js +23 -7
- package/lib/localReference.js.map +1 -1
- package/lib/mergeTree.d.ts +23 -3
- package/lib/mergeTree.d.ts.map +1 -1
- package/lib/mergeTree.js +137 -49
- package/lib/mergeTree.js.map +1 -1
- package/lib/mergeTreeDeltaCallback.d.ts +8 -10
- package/lib/mergeTreeDeltaCallback.d.ts.map +1 -1
- package/lib/mergeTreeDeltaCallback.js +6 -10
- package/lib/mergeTreeDeltaCallback.js.map +1 -1
- package/lib/opBuilder.js +6 -5
- package/lib/opBuilder.js.map +1 -1
- package/lib/ops.d.ts +12 -10
- package/lib/ops.d.ts.map +1 -1
- package/lib/ops.js +7 -7
- package/lib/ops.js.map +1 -1
- package/lib/referencePositions.d.ts +1 -1
- package/lib/referencePositions.d.ts.map +1 -1
- package/lib/referencePositions.js +3 -2
- package/lib/referencePositions.js.map +1 -1
- package/package.json +17 -94
- package/src/client.ts +87 -27
- package/src/collections.ts +5 -4
- package/src/index.ts +1 -1
- package/src/localReference.ts +33 -8
- package/src/mergeTree.ts +136 -43
- package/src/mergeTreeDeltaCallback.ts +8 -10
- package/src/ops.ts +13 -10
- package/src/referencePositions.ts +3 -2
package/lib/client.js
CHANGED
|
@@ -10,11 +10,11 @@ import { UnassignedSequenceNumber, UniversalSequenceNumber } from "./constants";
|
|
|
10
10
|
import { LocalReference } from "./localReference";
|
|
11
11
|
import { compareStrings, MergeTree, } from "./mergeTree";
|
|
12
12
|
import { createAnnotateMarkerOp, createAnnotateRangeOp, createGroupOp, createInsertSegmentOp, createRemoveRangeOp, } from "./opBuilder";
|
|
13
|
+
import { MergeTreeDeltaType, } from "./ops";
|
|
13
14
|
import { SnapshotLegacy } from "./snapshotlegacy";
|
|
14
15
|
import { SnapshotLoader } from "./snapshotLoader";
|
|
15
16
|
import { MergeTreeTextHelper } from "./textSegment";
|
|
16
17
|
import { SnapshotV1 } from "./snapshotV1";
|
|
17
|
-
import { DetachedReferencePosition } from "./referencePositions";
|
|
18
18
|
function elapsedMicroseconds(trace) {
|
|
19
19
|
return trace.trace().duration * 1000;
|
|
20
20
|
}
|
|
@@ -221,11 +221,7 @@ export class Client {
|
|
|
221
221
|
return this.mergeTree.removeLocalReferencePosition(lref);
|
|
222
222
|
}
|
|
223
223
|
localReferencePositionToPosition(lref) {
|
|
224
|
-
|
|
225
|
-
if (segment === undefined) {
|
|
226
|
-
return DetachedReferencePosition;
|
|
227
|
-
}
|
|
228
|
-
return this.getPosition(segment) + lref.getOffset();
|
|
224
|
+
return this.mergeTree.referencePositionToLocalPosition(lref);
|
|
229
225
|
}
|
|
230
226
|
/**
|
|
231
227
|
* Given a position specified relative to a marker id, lookup the marker
|
|
@@ -244,7 +240,7 @@ export class Client {
|
|
|
244
240
|
* @returns True if the remove was applied. False if it could not be.
|
|
245
241
|
*/
|
|
246
242
|
applyRemoveRangeOp(opArgs) {
|
|
247
|
-
assert(opArgs.op.type ===
|
|
243
|
+
assert(opArgs.op.type === MergeTreeDeltaType.REMOVE, 0x02d /* "Unexpected op type on range remove!" */);
|
|
248
244
|
const op = opArgs.op;
|
|
249
245
|
const clientArgs = this.getClientSequenceArgs(opArgs);
|
|
250
246
|
const range = this.getValidOpRange(op, clientArgs);
|
|
@@ -265,7 +261,7 @@ export class Client {
|
|
|
265
261
|
* @returns True if the annotate was applied. False if it could not be.
|
|
266
262
|
*/
|
|
267
263
|
applyAnnotateRangeOp(opArgs) {
|
|
268
|
-
assert(opArgs.op.type ===
|
|
264
|
+
assert(opArgs.op.type === MergeTreeDeltaType.ANNOTATE, 0x02e /* "Unexpected op type on range annotate!" */);
|
|
269
265
|
const op = opArgs.op;
|
|
270
266
|
const clientArgs = this.getClientSequenceArgs(opArgs);
|
|
271
267
|
const range = this.getValidOpRange(op, clientArgs);
|
|
@@ -286,7 +282,7 @@ export class Client {
|
|
|
286
282
|
* @returns True if the insert was applied. False if it could not be.
|
|
287
283
|
*/
|
|
288
284
|
applyInsertOp(opArgs) {
|
|
289
|
-
assert(opArgs.op.type ===
|
|
285
|
+
assert(opArgs.op.type === MergeTreeDeltaType.INSERT, 0x02f /* "Unexpected op type on range insert!" */);
|
|
290
286
|
const op = opArgs.op;
|
|
291
287
|
const clientArgs = this.getClientSequenceArgs(opArgs);
|
|
292
288
|
const range = this.getValidOpRange(op, clientArgs);
|
|
@@ -355,12 +351,12 @@ export class Client {
|
|
|
355
351
|
if (start === undefined
|
|
356
352
|
|| start < 0
|
|
357
353
|
|| start > length
|
|
358
|
-
|| start === length && op.type !==
|
|
354
|
+
|| start === length && op.type !== MergeTreeDeltaType.INSERT) {
|
|
359
355
|
invalidPositions.push("start");
|
|
360
356
|
}
|
|
361
357
|
// Validate end if not insert, or insert has end
|
|
362
358
|
//
|
|
363
|
-
if (op.type !==
|
|
359
|
+
if (op.type !== MergeTreeDeltaType.INSERT || end !== undefined) {
|
|
364
360
|
if (end === undefined || end <= start) {
|
|
365
361
|
invalidPositions.push("end");
|
|
366
362
|
}
|
|
@@ -386,13 +382,13 @@ export class Client {
|
|
|
386
382
|
}
|
|
387
383
|
/**
|
|
388
384
|
* Gets the client args from the op if remote, otherwise uses the local clients info
|
|
389
|
-
* @param
|
|
385
|
+
* @param sequencedMessage - The sequencedMessage to get the client sequence args for
|
|
390
386
|
*/
|
|
391
|
-
|
|
387
|
+
getClientSequenceArgsForMessage(sequencedMessage) {
|
|
392
388
|
// If there this no sequenced message, then the op is local
|
|
393
389
|
// and unacked, so use this clients sequenced args
|
|
394
390
|
//
|
|
395
|
-
if (!
|
|
391
|
+
if (!sequencedMessage) {
|
|
396
392
|
const segWindow = this.getCollabWindow();
|
|
397
393
|
return {
|
|
398
394
|
clientId: segWindow.clientId,
|
|
@@ -402,12 +398,19 @@ export class Client {
|
|
|
402
398
|
}
|
|
403
399
|
else {
|
|
404
400
|
return {
|
|
405
|
-
clientId: this.
|
|
406
|
-
referenceSequenceNumber:
|
|
407
|
-
sequenceNumber:
|
|
401
|
+
clientId: this.getOrAddShortClientId(sequencedMessage.clientId),
|
|
402
|
+
referenceSequenceNumber: sequencedMessage.referenceSequenceNumber,
|
|
403
|
+
sequenceNumber: sequencedMessage.sequenceNumber,
|
|
408
404
|
};
|
|
409
405
|
}
|
|
410
406
|
}
|
|
407
|
+
/**
|
|
408
|
+
* Gets the client args from the op if remote, otherwise uses the local clients info
|
|
409
|
+
* @param opArgs - The op arg to get the client sequence args for
|
|
410
|
+
*/
|
|
411
|
+
getClientSequenceArgs(opArgs) {
|
|
412
|
+
return this.getClientSequenceArgsForMessage(opArgs.sequencedMessage);
|
|
413
|
+
}
|
|
411
414
|
ackPendingSegment(opArgs) {
|
|
412
415
|
const ackOp = (deltaOpArgs) => {
|
|
413
416
|
let trace;
|
|
@@ -415,7 +418,7 @@ export class Client {
|
|
|
415
418
|
trace = Trace.start();
|
|
416
419
|
}
|
|
417
420
|
this.mergeTree.ackPendingSegment(deltaOpArgs);
|
|
418
|
-
if (deltaOpArgs.op.type ===
|
|
421
|
+
if (deltaOpArgs.op.type === MergeTreeDeltaType.ANNOTATE) {
|
|
419
422
|
if (deltaOpArgs.op.combiningOp && (deltaOpArgs.op.combiningOp.name === "consensus")) {
|
|
420
423
|
this.updateConsensusProperty(deltaOpArgs.op, deltaOpArgs.sequencedMessage);
|
|
421
424
|
}
|
|
@@ -426,7 +429,7 @@ export class Client {
|
|
|
426
429
|
this.accumWindow += (this.getCurrentSeq() - this.getCollabWindow().minSeq);
|
|
427
430
|
}
|
|
428
431
|
};
|
|
429
|
-
if (opArgs.op.type ===
|
|
432
|
+
if (opArgs.op.type === MergeTreeDeltaType.GROUP) {
|
|
430
433
|
for (const memberOp of opArgs.op.ops) {
|
|
431
434
|
ackOp({
|
|
432
435
|
groupOp: opArgs.op,
|
|
@@ -507,6 +510,47 @@ export class Client {
|
|
|
507
510
|
});
|
|
508
511
|
return segmentPosition;
|
|
509
512
|
}
|
|
513
|
+
/**
|
|
514
|
+
* Rebases a (local) position from the perspective `{ seq: seqNumberFrom, localSeq }` to the perspective
|
|
515
|
+
* of the current sequence number. This is desirable when rebasing operations for reconnection.
|
|
516
|
+
*
|
|
517
|
+
* If the position refers to a segment/offset that was removed by some operation between `seqNumberFrom` and
|
|
518
|
+
* the current sequence number, the returned position will align with the position of a reference given
|
|
519
|
+
* `SlideOnRemove` semantics.
|
|
520
|
+
*/
|
|
521
|
+
rebasePosition(pos, seqNumberFrom, localSeq) {
|
|
522
|
+
assert(localSeq <= this.mergeTree.collabWindow.localSeq, 0x300 /* localSeq greater than collab window */);
|
|
523
|
+
let segment;
|
|
524
|
+
let posAccumulated = 0;
|
|
525
|
+
let offset = pos;
|
|
526
|
+
const isInsertedInView = (seg) => (seg.seq !== undefined && seg.seq !== UnassignedSequenceNumber && seg.seq <= seqNumberFrom)
|
|
527
|
+
|| (seg.localSeq !== undefined && seg.localSeq <= localSeq);
|
|
528
|
+
const isRemovedFromView = ({ removedSeq, localRemovedSeq }) => (removedSeq !== undefined && removedSeq !== UnassignedSequenceNumber && removedSeq <= seqNumberFrom)
|
|
529
|
+
|| (localRemovedSeq !== undefined && localRemovedSeq <= localSeq);
|
|
530
|
+
this.mergeTree.walkAllSegments(this.mergeTree.root, (seg) => {
|
|
531
|
+
assert(seg.seq !== undefined || seg.localSeq !== undefined, 0x301 /* Either seq or localSeq should be defined */);
|
|
532
|
+
segment = seg;
|
|
533
|
+
if (isInsertedInView(seg) && !isRemovedFromView(seg)) {
|
|
534
|
+
posAccumulated += seg.cachedLength;
|
|
535
|
+
if (offset >= seg.cachedLength) {
|
|
536
|
+
offset -= seg.cachedLength;
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
// Keep going while we've yet to reach the segment at the desired position
|
|
540
|
+
return posAccumulated <= pos;
|
|
541
|
+
});
|
|
542
|
+
assert(segment !== undefined, 0x302 /* No segment found */);
|
|
543
|
+
const seqNumberTo = this.getCollabWindow().currentSeq;
|
|
544
|
+
if ((segment.removedSeq !== undefined &&
|
|
545
|
+
segment.removedSeq !== UnassignedSequenceNumber &&
|
|
546
|
+
segment.removedSeq <= seqNumberTo)
|
|
547
|
+
|| (segment.localRemovedSeq !== undefined && segment.localRemovedSeq <= localSeq)) {
|
|
548
|
+
// Segment that the position was in has been removed: null out offset.
|
|
549
|
+
offset = 0;
|
|
550
|
+
}
|
|
551
|
+
assert(0 <= offset && offset < segment.cachedLength, 0x303 /* Invalid offset */);
|
|
552
|
+
return this.findReconnectionPosition(segment, localSeq) + offset;
|
|
553
|
+
}
|
|
510
554
|
resetPendingDeltaToOps(resetOp, segmentGroup) {
|
|
511
555
|
var _a, _b;
|
|
512
556
|
assert(!!segmentGroup, 0x033 /* "Segment group undefined" */);
|
|
@@ -524,7 +568,7 @@ export class Client {
|
|
|
524
568
|
const segmentPosition = this.findReconnectionPosition(segment, segmentGroup.localSeq);
|
|
525
569
|
let newOp;
|
|
526
570
|
switch (resetOp.type) {
|
|
527
|
-
case
|
|
571
|
+
case MergeTreeDeltaType.ANNOTATE:
|
|
528
572
|
assert(((_b = segment.propertyManager) === null || _b === void 0 ? void 0 : _b.hasPendingProperties()) === true, 0x036 /* "Segment has no pending properties" */);
|
|
529
573
|
// if the segment has been removed, there's no need to send the annotate op
|
|
530
574
|
// unless the remove was local, in which case the annotate must have come
|
|
@@ -533,11 +577,11 @@ export class Client {
|
|
|
533
577
|
newOp = createAnnotateRangeOp(segmentPosition, segmentPosition + segment.cachedLength, resetOp.props, resetOp.combiningOp);
|
|
534
578
|
}
|
|
535
579
|
break;
|
|
536
|
-
case
|
|
580
|
+
case MergeTreeDeltaType.INSERT:
|
|
537
581
|
assert(segment.seq === UnassignedSequenceNumber, 0x037 /* "Segment already has assigned sequence number" */);
|
|
538
582
|
newOp = createInsertSegmentOp(segmentPosition, segment);
|
|
539
583
|
break;
|
|
540
|
-
case
|
|
584
|
+
case MergeTreeDeltaType.REMOVE:
|
|
541
585
|
if (segment.localRemovedSeq !== undefined) {
|
|
542
586
|
newOp = createRemoveRangeOp(segmentPosition, segmentPosition + segment.cachedLength);
|
|
543
587
|
}
|
|
@@ -559,16 +603,16 @@ export class Client {
|
|
|
559
603
|
const msg = opArgs.sequencedMessage;
|
|
560
604
|
this.getOrAddShortClientId(msg.clientId);
|
|
561
605
|
switch (op.type) {
|
|
562
|
-
case
|
|
606
|
+
case MergeTreeDeltaType.INSERT:
|
|
563
607
|
this.applyInsertOp(opArgs);
|
|
564
608
|
break;
|
|
565
|
-
case
|
|
609
|
+
case MergeTreeDeltaType.REMOVE:
|
|
566
610
|
this.applyRemoveRangeOp(opArgs);
|
|
567
611
|
break;
|
|
568
|
-
case
|
|
612
|
+
case MergeTreeDeltaType.ANNOTATE:
|
|
569
613
|
this.applyAnnotateRangeOp(opArgs);
|
|
570
614
|
break;
|
|
571
|
-
case
|
|
615
|
+
case MergeTreeDeltaType.GROUP: {
|
|
572
616
|
for (const memberOp of op.ops) {
|
|
573
617
|
this.applyRemoteOp({
|
|
574
618
|
op: memberOp,
|
|
@@ -585,19 +629,19 @@ export class Client {
|
|
|
585
629
|
applyStashedOp(op) {
|
|
586
630
|
let metadata;
|
|
587
631
|
switch (op.type) {
|
|
588
|
-
case
|
|
632
|
+
case MergeTreeDeltaType.INSERT:
|
|
589
633
|
this.applyInsertOp({ op });
|
|
590
634
|
metadata = this.peekPendingSegmentGroups();
|
|
591
635
|
break;
|
|
592
|
-
case
|
|
636
|
+
case MergeTreeDeltaType.REMOVE:
|
|
593
637
|
this.applyRemoveRangeOp({ op });
|
|
594
638
|
metadata = this.peekPendingSegmentGroups();
|
|
595
639
|
break;
|
|
596
|
-
case
|
|
640
|
+
case MergeTreeDeltaType.ANNOTATE:
|
|
597
641
|
this.applyAnnotateRangeOp({ op });
|
|
598
642
|
metadata = this.peekPendingSegmentGroups();
|
|
599
643
|
break;
|
|
600
|
-
case
|
|
644
|
+
case MergeTreeDeltaType.GROUP:
|
|
601
645
|
return op.ops.map((o) => this.applyStashedOp(o));
|
|
602
646
|
default:
|
|
603
647
|
unreachableCase(op, "unrecognized op type");
|
|
@@ -652,7 +696,7 @@ export class Client {
|
|
|
652
696
|
*/
|
|
653
697
|
regeneratePendingOp(resetOp, segmentGroup) {
|
|
654
698
|
const opList = [];
|
|
655
|
-
if (resetOp.type ===
|
|
699
|
+
if (resetOp.type === MergeTreeDeltaType.GROUP) {
|
|
656
700
|
if (Array.isArray(segmentGroup)) {
|
|
657
701
|
assert(resetOp.ops.length === segmentGroup.length, 0x03a /* "Number of ops in 'resetOp' must match the number of segment groups provided." */);
|
|
658
702
|
for (let i = 0; i < resetOp.ops.length; i++) {
|
|
@@ -667,7 +711,7 @@ export class Client {
|
|
|
667
711
|
}
|
|
668
712
|
}
|
|
669
713
|
else {
|
|
670
|
-
assert(resetOp.type !==
|
|
714
|
+
assert(resetOp.type !== MergeTreeDeltaType.GROUP, 0x03c /* "Reset op has 'group' delta type!" */);
|
|
671
715
|
assert(!Array.isArray(segmentGroup), 0x03d /* "segmentGroup is array rather than singleton!" */);
|
|
672
716
|
opList.push(...this.resetPendingDeltaToOps(resetOp, segmentGroup));
|
|
673
717
|
}
|
|
@@ -724,13 +768,13 @@ export class Client {
|
|
|
724
768
|
groupOp,
|
|
725
769
|
};
|
|
726
770
|
switch (op.type) {
|
|
727
|
-
case
|
|
771
|
+
case MergeTreeDeltaType.INSERT:
|
|
728
772
|
this.applyInsertOp(opArgs);
|
|
729
773
|
break;
|
|
730
|
-
case
|
|
774
|
+
case MergeTreeDeltaType.ANNOTATE:
|
|
731
775
|
this.applyAnnotateRangeOp(opArgs);
|
|
732
776
|
break;
|
|
733
|
-
case
|
|
777
|
+
case MergeTreeDeltaType.REMOVE:
|
|
734
778
|
this.applyRemoveRangeOp(opArgs);
|
|
735
779
|
break;
|
|
736
780
|
default:
|
|
@@ -761,18 +805,16 @@ export class Client {
|
|
|
761
805
|
}
|
|
762
806
|
}
|
|
763
807
|
getContainingSegment(pos, op) {
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
}
|
|
775
|
-
return this.mergeTree.getContainingSegment(pos, seq, clientId);
|
|
808
|
+
const args = this.getClientSequenceArgsForMessage(op);
|
|
809
|
+
return this.mergeTree.getContainingSegment(pos, args.referenceSequenceNumber, args.clientId);
|
|
810
|
+
}
|
|
811
|
+
/**
|
|
812
|
+
* Returns the position to slide a reference to if a slide is required.
|
|
813
|
+
* @param segoff - The segment and offset to slide from
|
|
814
|
+
* @returns - segment and offset to slide the reference to
|
|
815
|
+
*/
|
|
816
|
+
getSlideToSegment(segoff) {
|
|
817
|
+
return this.mergeTree._getSlideToSegment(segoff);
|
|
776
818
|
}
|
|
777
819
|
getPropertiesAtPosition(pos) {
|
|
778
820
|
let propertiesAtPosition;
|