reptree 0.1.2 → 0.1.4
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/README.md +4 -6
- package/dist/index.cjs +252 -120
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +75 -22
- package/dist/index.d.ts +75 -22
- package/dist/index.js +251 -120
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -27,19 +27,17 @@ import { RepTree } from 'reptree';
|
|
|
27
27
|
|
|
28
28
|
// Create a new tree
|
|
29
29
|
const tree = new RepTree('peer1');
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
const rootVertex = tree.rootVertex;
|
|
33
|
-
rootVertex.name = 'Project';
|
|
30
|
+
const root = tree.createRoot();
|
|
31
|
+
root.name = 'Project';
|
|
34
32
|
|
|
35
33
|
// Create a folder structure with properties
|
|
36
|
-
const docsFolder =
|
|
34
|
+
const docsFolder = root.newNamedChild('Docs');
|
|
37
35
|
docsFolder.setProperties({
|
|
38
36
|
type: 'folder',
|
|
39
37
|
icon: 'folder-icon'
|
|
40
38
|
});
|
|
41
39
|
|
|
42
|
-
const imagesFolder =
|
|
40
|
+
const imagesFolder = root.newNamedChild('Images');
|
|
43
41
|
imagesFolder.setProperties({
|
|
44
42
|
type: 'folder',
|
|
45
43
|
icon: 'image-icon'
|
package/dist/index.cjs
CHANGED
|
@@ -22,6 +22,7 @@ var index_exports = {};
|
|
|
22
22
|
__export(index_exports, {
|
|
23
23
|
OpId: () => OpId,
|
|
24
24
|
RepTree: () => RepTree,
|
|
25
|
+
StateVector: () => StateVector,
|
|
25
26
|
TreeState: () => TreeState,
|
|
26
27
|
Vertex: () => Vertex,
|
|
27
28
|
VertexState: () => VertexState,
|
|
@@ -360,9 +361,21 @@ var TreeState = class {
|
|
|
360
361
|
}
|
|
361
362
|
}
|
|
362
363
|
const children = this.getChildrenIds(vertexId);
|
|
363
|
-
|
|
364
|
-
const
|
|
365
|
-
const
|
|
364
|
+
const sortedChildren = [...children].sort((a, b) => {
|
|
365
|
+
const vertexA = this.getVertex(a);
|
|
366
|
+
const vertexB = this.getVertex(b);
|
|
367
|
+
const nameA = vertexA?.getProperty("_n");
|
|
368
|
+
const nameB = vertexB?.getProperty("_n");
|
|
369
|
+
if (nameA && nameB) {
|
|
370
|
+
return nameA.localeCompare(nameB);
|
|
371
|
+
}
|
|
372
|
+
if (nameA) return -1;
|
|
373
|
+
if (nameB) return 1;
|
|
374
|
+
return a.localeCompare(b);
|
|
375
|
+
});
|
|
376
|
+
for (let i = 0; i < sortedChildren.length; i++) {
|
|
377
|
+
const childId = sortedChildren[i];
|
|
378
|
+
const isLastChild = i === sortedChildren.length - 1;
|
|
366
379
|
result += this.printTree(childId, indent + (isLast ? " " : "\u2502 "), isLastChild);
|
|
367
380
|
}
|
|
368
381
|
return result;
|
|
@@ -495,7 +508,7 @@ var Vertex = class {
|
|
|
495
508
|
}
|
|
496
509
|
};
|
|
497
510
|
|
|
498
|
-
// src/
|
|
511
|
+
// src/StateVector.ts
|
|
499
512
|
function subtractRanges(rangesA, rangesB) {
|
|
500
513
|
if (rangesB.length === 0) return rangesA.map((r) => [...r]);
|
|
501
514
|
if (rangesA.length === 0) return [];
|
|
@@ -528,12 +541,165 @@ function subtractRanges(rangesA, rangesB) {
|
|
|
528
541
|
}
|
|
529
542
|
return result;
|
|
530
543
|
}
|
|
544
|
+
var StateVector = class _StateVector {
|
|
545
|
+
/**
|
|
546
|
+
* Creates a new StateVector.
|
|
547
|
+
* @param initialState Optional initial state to copy from
|
|
548
|
+
*/
|
|
549
|
+
constructor(initialState = {}) {
|
|
550
|
+
this.ranges = {};
|
|
551
|
+
for (const [peerId, peerRanges] of Object.entries(initialState)) {
|
|
552
|
+
this.ranges[peerId] = peerRanges.map((range) => [...range]);
|
|
553
|
+
}
|
|
554
|
+
}
|
|
555
|
+
/**
|
|
556
|
+
* Updates the state vector with a newly applied operation.
|
|
557
|
+
* Assumes ranges are sorted and non-overlapping.
|
|
558
|
+
*
|
|
559
|
+
* @param peerId The peer ID of the operation
|
|
560
|
+
* @param counter The counter value of the operation
|
|
561
|
+
*/
|
|
562
|
+
update(peerId, counter) {
|
|
563
|
+
if (!this.ranges[peerId]) {
|
|
564
|
+
this.ranges[peerId] = [];
|
|
565
|
+
}
|
|
566
|
+
const ranges = this.ranges[peerId];
|
|
567
|
+
if (ranges.length === 0) {
|
|
568
|
+
ranges.push([counter, counter]);
|
|
569
|
+
return;
|
|
570
|
+
}
|
|
571
|
+
let rangeExtendedOrMerged = false;
|
|
572
|
+
let insertIndex = -1;
|
|
573
|
+
for (let i = 0; i < ranges.length; i++) {
|
|
574
|
+
const range = ranges[i];
|
|
575
|
+
if (counter >= range[0] && counter <= range[1]) {
|
|
576
|
+
rangeExtendedOrMerged = true;
|
|
577
|
+
break;
|
|
578
|
+
}
|
|
579
|
+
if (counter === range[0] - 1) {
|
|
580
|
+
range[0] = counter;
|
|
581
|
+
rangeExtendedOrMerged = true;
|
|
582
|
+
if (i > 0 && range[0] === ranges[i - 1][1] + 1) {
|
|
583
|
+
ranges[i - 1][1] = range[1];
|
|
584
|
+
ranges.splice(i, 1);
|
|
585
|
+
}
|
|
586
|
+
break;
|
|
587
|
+
}
|
|
588
|
+
if (counter === range[1] + 1) {
|
|
589
|
+
range[1] = counter;
|
|
590
|
+
rangeExtendedOrMerged = true;
|
|
591
|
+
if (i < ranges.length - 1 && range[1] + 1 === ranges[i + 1][0]) {
|
|
592
|
+
range[1] = ranges[i + 1][1];
|
|
593
|
+
ranges.splice(i + 1, 1);
|
|
594
|
+
}
|
|
595
|
+
break;
|
|
596
|
+
}
|
|
597
|
+
if (counter < range[0] && insertIndex === -1) {
|
|
598
|
+
insertIndex = i;
|
|
599
|
+
}
|
|
600
|
+
}
|
|
601
|
+
if (!rangeExtendedOrMerged) {
|
|
602
|
+
if (insertIndex === -1) {
|
|
603
|
+
insertIndex = ranges.length;
|
|
604
|
+
}
|
|
605
|
+
ranges.splice(insertIndex, 0, [counter, counter]);
|
|
606
|
+
if (insertIndex > 0 && ranges[insertIndex][0] === ranges[insertIndex - 1][1] + 1) {
|
|
607
|
+
ranges[insertIndex - 1][1] = ranges[insertIndex][1];
|
|
608
|
+
ranges.splice(insertIndex, 1);
|
|
609
|
+
insertIndex--;
|
|
610
|
+
}
|
|
611
|
+
if (insertIndex < ranges.length - 1 && ranges[insertIndex][1] + 1 === ranges[insertIndex + 1][0]) {
|
|
612
|
+
ranges[insertIndex][1] = ranges[insertIndex + 1][1];
|
|
613
|
+
ranges.splice(insertIndex + 1, 1);
|
|
614
|
+
}
|
|
615
|
+
}
|
|
616
|
+
}
|
|
617
|
+
/**
|
|
618
|
+
* Updates the state vector with a newly applied operation.
|
|
619
|
+
*
|
|
620
|
+
* @param op The operation that was just applied
|
|
621
|
+
*/
|
|
622
|
+
updateFromOp(op) {
|
|
623
|
+
this.update(op.id.peerId, op.id.counter);
|
|
624
|
+
}
|
|
625
|
+
/**
|
|
626
|
+
* Returns the current state vector.
|
|
627
|
+
* Returns a readonly reference to the internal state.
|
|
628
|
+
*/
|
|
629
|
+
getState() {
|
|
630
|
+
return this.ranges;
|
|
631
|
+
}
|
|
632
|
+
/**
|
|
633
|
+
* Calculates which operation ranges we have that the other state vector is missing
|
|
634
|
+
* by comparing state vectors.
|
|
635
|
+
*
|
|
636
|
+
* @param other The other state vector to compare against
|
|
637
|
+
* @returns Array of operation ID ranges that we have but they don't
|
|
638
|
+
*/
|
|
639
|
+
diff(other) {
|
|
640
|
+
const missingRanges = [];
|
|
641
|
+
const theirState = other.getState();
|
|
642
|
+
for (const [peerId, ourRanges] of Object.entries(this.ranges)) {
|
|
643
|
+
const theirRanges = theirState[peerId] || [];
|
|
644
|
+
const missing = subtractRanges(ourRanges, theirRanges);
|
|
645
|
+
for (const [start, end] of missing) {
|
|
646
|
+
if (start <= end) {
|
|
647
|
+
missingRanges.push({
|
|
648
|
+
peerId,
|
|
649
|
+
start,
|
|
650
|
+
end
|
|
651
|
+
});
|
|
652
|
+
}
|
|
653
|
+
}
|
|
654
|
+
}
|
|
655
|
+
return missingRanges;
|
|
656
|
+
}
|
|
657
|
+
/**
|
|
658
|
+
* Checks if the state vector contains the given operation ID
|
|
659
|
+
*
|
|
660
|
+
* @param opId The operation ID to check
|
|
661
|
+
* @returns true if the operation is in the state vector, false otherwise
|
|
662
|
+
*/
|
|
663
|
+
contains(opId) {
|
|
664
|
+
const peerId = opId.peerId;
|
|
665
|
+
const counter = opId.counter;
|
|
666
|
+
if (!this.ranges[peerId]) {
|
|
667
|
+
return false;
|
|
668
|
+
}
|
|
669
|
+
for (const [start, end] of this.ranges[peerId]) {
|
|
670
|
+
if (counter >= start && counter <= end) {
|
|
671
|
+
return true;
|
|
672
|
+
}
|
|
673
|
+
}
|
|
674
|
+
return false;
|
|
675
|
+
}
|
|
676
|
+
/**
|
|
677
|
+
* Creates a copy of this state vector
|
|
678
|
+
*/
|
|
679
|
+
clone() {
|
|
680
|
+
return new _StateVector(this.ranges);
|
|
681
|
+
}
|
|
682
|
+
/**
|
|
683
|
+
* Builds a state vector from an array of operations
|
|
684
|
+
* @param operations The operations to build the state vector from
|
|
685
|
+
* @returns A new StateVector instance
|
|
686
|
+
*/
|
|
687
|
+
static fromOperations(operations) {
|
|
688
|
+
const stateVector = new _StateVector();
|
|
689
|
+
for (const op of operations) {
|
|
690
|
+
stateVector.updateFromOp(op);
|
|
691
|
+
}
|
|
692
|
+
return stateVector;
|
|
693
|
+
}
|
|
694
|
+
};
|
|
695
|
+
|
|
696
|
+
// src/RepTree.ts
|
|
531
697
|
var _RepTree = class _RepTree {
|
|
532
698
|
/**
|
|
533
|
-
* @param peerId - The peer ID of the current client
|
|
534
|
-
* @param ops - The operations to replicate an existing tree, if
|
|
699
|
+
* @param peerId - The peer ID of the current client. Should be unique across all peers.
|
|
700
|
+
* @param ops - The operations to replicate an existing tree, if not provided - an empty tree will be created without a root vertex
|
|
535
701
|
*/
|
|
536
|
-
constructor(peerId, ops
|
|
702
|
+
constructor(peerId, ops) {
|
|
537
703
|
this.lamportClock = 0;
|
|
538
704
|
this.moveOps = [];
|
|
539
705
|
this.setPropertyOps = [];
|
|
@@ -546,30 +712,37 @@ var _RepTree = class _RepTree {
|
|
|
546
712
|
this.parentIdBeforeMove = /* @__PURE__ */ new Map();
|
|
547
713
|
this.opAppliedCallbacks = [];
|
|
548
714
|
this.maxDepth = _RepTree.DEFAULT_MAX_DEPTH;
|
|
549
|
-
|
|
550
|
-
this.stateVector = {};
|
|
715
|
+
this._stateVectorEnabled = true;
|
|
551
716
|
this.peerId = peerId;
|
|
552
717
|
this.state = new TreeState();
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
for (let i = 0; i < ops.length; i++) {
|
|
556
|
-
if (isMoveVertexOp(ops[i]) && ops[i].parentId === null) {
|
|
557
|
-
rootMoveOp = ops[i];
|
|
558
|
-
break;
|
|
559
|
-
}
|
|
560
|
-
}
|
|
561
|
-
if (rootMoveOp) {
|
|
562
|
-
this.rootVertexId = rootMoveOp.targetId;
|
|
563
|
-
} else {
|
|
564
|
-
throw new Error("The operations has to contain a move operation with a parentId as null to set the root vertex");
|
|
565
|
-
}
|
|
718
|
+
this.stateVector = new StateVector();
|
|
719
|
+
if (ops && ops.length > 0) {
|
|
566
720
|
this.applyOps(ops);
|
|
567
|
-
this.
|
|
721
|
+
const root = this.root;
|
|
722
|
+
if (!root) {
|
|
723
|
+
throw new Error("There has to be a root vertex in the operations");
|
|
724
|
+
}
|
|
568
725
|
} else {
|
|
569
|
-
this.rootVertexId = this.newVertexInternalWithUUID(null);
|
|
570
726
|
this.ensureNullVertex();
|
|
571
727
|
}
|
|
572
728
|
}
|
|
729
|
+
get root() {
|
|
730
|
+
if (!this.rootVertexId) {
|
|
731
|
+
const vertices = this.state.getAllVertices();
|
|
732
|
+
for (const vertex of vertices) {
|
|
733
|
+
if (vertex.parentId === null && vertex.id !== _RepTree.NULL_VERTEX_ID) {
|
|
734
|
+
this.rootVertexId = vertex.id;
|
|
735
|
+
return new Vertex(this, vertex);
|
|
736
|
+
}
|
|
737
|
+
}
|
|
738
|
+
return void 0;
|
|
739
|
+
}
|
|
740
|
+
const rootVertex = this.state.getVertex(this.rootVertexId);
|
|
741
|
+
if (!rootVertex) {
|
|
742
|
+
throw new Error("Root vertex not found");
|
|
743
|
+
}
|
|
744
|
+
return new Vertex(this, rootVertex);
|
|
745
|
+
}
|
|
573
746
|
getMoveOps() {
|
|
574
747
|
return this.moveOps;
|
|
575
748
|
}
|
|
@@ -580,13 +753,6 @@ var _RepTree = class _RepTree {
|
|
|
580
753
|
const vertex = this.state.getVertex(vertexId);
|
|
581
754
|
return vertex ? new Vertex(this, vertex) : void 0;
|
|
582
755
|
}
|
|
583
|
-
get rootVertex() {
|
|
584
|
-
const rootVertex = this.state.getVertex(this.rootVertexId);
|
|
585
|
-
if (!rootVertex) {
|
|
586
|
-
throw new Error("Root vertex not found");
|
|
587
|
-
}
|
|
588
|
-
return new Vertex(this, rootVertex);
|
|
589
|
-
}
|
|
590
756
|
getAllVertices() {
|
|
591
757
|
return this.state.getAllVertices().map((v) => new Vertex(this, v));
|
|
592
758
|
}
|
|
@@ -637,6 +803,17 @@ var _RepTree = class _RepTree {
|
|
|
637
803
|
setMaxDepth(maxDepth) {
|
|
638
804
|
this.maxDepth = maxDepth;
|
|
639
805
|
}
|
|
806
|
+
createRoot() {
|
|
807
|
+
if (this.rootVertexId) {
|
|
808
|
+
throw new Error("Root vertex already exists");
|
|
809
|
+
}
|
|
810
|
+
this.rootVertexId = this.newVertexInternalWithUUID(null);
|
|
811
|
+
const rootVertex = this.state.getVertex(this.rootVertexId);
|
|
812
|
+
if (!rootVertex) {
|
|
813
|
+
throw new Error("Root vertex not found");
|
|
814
|
+
}
|
|
815
|
+
return new Vertex(this, rootVertex);
|
|
816
|
+
}
|
|
640
817
|
newVertex(parentId, props = null) {
|
|
641
818
|
const typedProps = props;
|
|
642
819
|
const vertexId = this.newVertexInternalWithUUID(parentId);
|
|
@@ -693,6 +870,9 @@ var _RepTree = class _RepTree {
|
|
|
693
870
|
path = path.replace(/^\/+/, "");
|
|
694
871
|
path = path.replace(/\/+$/, "");
|
|
695
872
|
const pathParts = path.split("/");
|
|
873
|
+
if (!this.rootVertexId) {
|
|
874
|
+
return void 0;
|
|
875
|
+
}
|
|
696
876
|
const root = this.state.getVertex(this.rootVertexId);
|
|
697
877
|
if (!root) {
|
|
698
878
|
throw new Error("The root vertex is not found");
|
|
@@ -714,6 +894,9 @@ var _RepTree = class _RepTree {
|
|
|
714
894
|
return void 0;
|
|
715
895
|
}
|
|
716
896
|
printTree() {
|
|
897
|
+
if (!this.rootVertexId) {
|
|
898
|
+
return "";
|
|
899
|
+
}
|
|
717
900
|
return this.state.printTree(this.rootVertexId);
|
|
718
901
|
}
|
|
719
902
|
merge(ops) {
|
|
@@ -737,6 +920,12 @@ var _RepTree = class _RepTree {
|
|
|
737
920
|
}
|
|
738
921
|
}
|
|
739
922
|
compareStructure(other) {
|
|
923
|
+
if (this.root?.id !== other.root?.id) {
|
|
924
|
+
return false;
|
|
925
|
+
}
|
|
926
|
+
if (!this.rootVertexId) {
|
|
927
|
+
return true;
|
|
928
|
+
}
|
|
740
929
|
return _RepTree.compareVertices(this.rootVertexId, this, other);
|
|
741
930
|
}
|
|
742
931
|
compareMoveOps(other) {
|
|
@@ -963,7 +1152,9 @@ var _RepTree = class _RepTree {
|
|
|
963
1152
|
}
|
|
964
1153
|
reportOpAsApplied(op) {
|
|
965
1154
|
this.knownOps.add(op.id.toString());
|
|
966
|
-
this.
|
|
1155
|
+
if (this._stateVectorEnabled) {
|
|
1156
|
+
this.stateVector.updateFromOp(op);
|
|
1157
|
+
}
|
|
967
1158
|
for (const callback of this.opAppliedCallbacks) {
|
|
968
1159
|
callback(op);
|
|
969
1160
|
}
|
|
@@ -997,99 +1188,15 @@ var _RepTree = class _RepTree {
|
|
|
997
1188
|
this.state.moveVertex(op.targetId, prevParentId);
|
|
998
1189
|
}
|
|
999
1190
|
// --- Range-Based State Vector Methods ---
|
|
1000
|
-
/**
|
|
1001
|
-
* Updates the state vector with a newly applied operation.
|
|
1002
|
-
* Assumes ranges are sorted and non-overlapping.
|
|
1003
|
-
*
|
|
1004
|
-
* @param op The operation that was just applied
|
|
1005
|
-
*/
|
|
1006
|
-
updateStateVector(op) {
|
|
1007
|
-
const peerId = op.id.peerId;
|
|
1008
|
-
const counter = op.id.counter;
|
|
1009
|
-
if (!this.stateVector[peerId]) {
|
|
1010
|
-
this.stateVector[peerId] = [];
|
|
1011
|
-
}
|
|
1012
|
-
const ranges = this.stateVector[peerId];
|
|
1013
|
-
if (ranges.length === 0) {
|
|
1014
|
-
ranges.push([counter, counter]);
|
|
1015
|
-
return;
|
|
1016
|
-
}
|
|
1017
|
-
let rangeExtendedOrMerged = false;
|
|
1018
|
-
let insertIndex = -1;
|
|
1019
|
-
for (let i = 0; i < ranges.length; i++) {
|
|
1020
|
-
const range = ranges[i];
|
|
1021
|
-
if (counter >= range[0] && counter <= range[1]) {
|
|
1022
|
-
rangeExtendedOrMerged = true;
|
|
1023
|
-
break;
|
|
1024
|
-
}
|
|
1025
|
-
if (counter === range[0] - 1) {
|
|
1026
|
-
range[0] = counter;
|
|
1027
|
-
rangeExtendedOrMerged = true;
|
|
1028
|
-
if (i > 0 && range[0] === ranges[i - 1][1] + 1) {
|
|
1029
|
-
ranges[i - 1][1] = range[1];
|
|
1030
|
-
ranges.splice(i, 1);
|
|
1031
|
-
}
|
|
1032
|
-
break;
|
|
1033
|
-
}
|
|
1034
|
-
if (counter === range[1] + 1) {
|
|
1035
|
-
range[1] = counter;
|
|
1036
|
-
rangeExtendedOrMerged = true;
|
|
1037
|
-
if (i < ranges.length - 1 && range[1] + 1 === ranges[i + 1][0]) {
|
|
1038
|
-
range[1] = ranges[i + 1][1];
|
|
1039
|
-
ranges.splice(i + 1, 1);
|
|
1040
|
-
}
|
|
1041
|
-
break;
|
|
1042
|
-
}
|
|
1043
|
-
if (counter < range[0] && insertIndex === -1) {
|
|
1044
|
-
insertIndex = i;
|
|
1045
|
-
}
|
|
1046
|
-
}
|
|
1047
|
-
if (!rangeExtendedOrMerged) {
|
|
1048
|
-
if (insertIndex === -1) {
|
|
1049
|
-
insertIndex = ranges.length;
|
|
1050
|
-
}
|
|
1051
|
-
ranges.splice(insertIndex, 0, [counter, counter]);
|
|
1052
|
-
if (insertIndex > 0 && ranges[insertIndex][0] === ranges[insertIndex - 1][1] + 1) {
|
|
1053
|
-
ranges[insertIndex - 1][1] = ranges[insertIndex][1];
|
|
1054
|
-
ranges.splice(insertIndex, 1);
|
|
1055
|
-
insertIndex--;
|
|
1056
|
-
}
|
|
1057
|
-
if (insertIndex < ranges.length - 1 && ranges[insertIndex][1] + 1 === ranges[insertIndex + 1][0]) {
|
|
1058
|
-
ranges[insertIndex][1] = ranges[insertIndex + 1][1];
|
|
1059
|
-
ranges.splice(insertIndex + 1, 1);
|
|
1060
|
-
}
|
|
1061
|
-
}
|
|
1062
|
-
}
|
|
1063
1191
|
/**
|
|
1064
1192
|
* Returns the current state vector.
|
|
1065
1193
|
* Returns a readonly reference to the internal state vector.
|
|
1066
1194
|
*/
|
|
1067
1195
|
getStateVector() {
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
/**
|
|
1071
|
-
* Calculates which operation ranges we have that the other peer is missing
|
|
1072
|
-
* by comparing state vectors.
|
|
1073
|
-
*
|
|
1074
|
-
* @param theirStateVector The state vector from another peer
|
|
1075
|
-
* @returns Array of operation ID ranges that we have but they don't
|
|
1076
|
-
*/
|
|
1077
|
-
diffStateVectors(theirStateVector) {
|
|
1078
|
-
const missingRanges = [];
|
|
1079
|
-
for (const [peerId, ourRanges] of Object.entries(this.stateVector)) {
|
|
1080
|
-
const theirRanges = theirStateVector[peerId] || [];
|
|
1081
|
-
const missing = subtractRanges(ourRanges, theirRanges);
|
|
1082
|
-
for (const [start, end] of missing) {
|
|
1083
|
-
if (start <= end) {
|
|
1084
|
-
missingRanges.push({
|
|
1085
|
-
peerId,
|
|
1086
|
-
start,
|
|
1087
|
-
end
|
|
1088
|
-
});
|
|
1089
|
-
}
|
|
1090
|
-
}
|
|
1196
|
+
if (!this._stateVectorEnabled) {
|
|
1197
|
+
return null;
|
|
1091
1198
|
}
|
|
1092
|
-
return
|
|
1199
|
+
return this.stateVector.getState();
|
|
1093
1200
|
}
|
|
1094
1201
|
/**
|
|
1095
1202
|
* Determines which operations are needed to synchronize
|
|
@@ -1099,7 +1206,11 @@ var _RepTree = class _RepTree {
|
|
|
1099
1206
|
* @returns Operations that should be sent to the other peer, sorted by OpId.
|
|
1100
1207
|
*/
|
|
1101
1208
|
getMissingOps(theirStateVector) {
|
|
1102
|
-
|
|
1209
|
+
if (!this._stateVectorEnabled) {
|
|
1210
|
+
return [...this.moveOps, ...this.setPropertyOps];
|
|
1211
|
+
}
|
|
1212
|
+
const otherStateVector = new StateVector(theirStateVector);
|
|
1213
|
+
const missingRanges = this.stateVector.diff(otherStateVector);
|
|
1103
1214
|
const missingOps = [];
|
|
1104
1215
|
const allOps = [...this.moveOps, ...this.setPropertyOps];
|
|
1105
1216
|
for (const op of allOps) {
|
|
@@ -1113,6 +1224,26 @@ var _RepTree = class _RepTree {
|
|
|
1113
1224
|
missingOps.sort((a, b) => OpId.compare(a.id, b.id));
|
|
1114
1225
|
return missingOps;
|
|
1115
1226
|
}
|
|
1227
|
+
/**
|
|
1228
|
+
* Gets or sets whether state vector tracking is enabled
|
|
1229
|
+
*/
|
|
1230
|
+
get stateVectorEnabled() {
|
|
1231
|
+
return this._stateVectorEnabled;
|
|
1232
|
+
}
|
|
1233
|
+
/**
|
|
1234
|
+
* Sets the state vector enabled status
|
|
1235
|
+
* When enabled, rebuilds the state vector from existing operations if needed
|
|
1236
|
+
*/
|
|
1237
|
+
set stateVectorEnabled(value) {
|
|
1238
|
+
if (value === this._stateVectorEnabled) return;
|
|
1239
|
+
if (value) {
|
|
1240
|
+
this._stateVectorEnabled = true;
|
|
1241
|
+
this.stateVector = StateVector.fromOperations([...this.moveOps, ...this.setPropertyOps]);
|
|
1242
|
+
} else {
|
|
1243
|
+
this._stateVectorEnabled = false;
|
|
1244
|
+
this.stateVector = new StateVector();
|
|
1245
|
+
}
|
|
1246
|
+
}
|
|
1116
1247
|
};
|
|
1117
1248
|
_RepTree.NULL_VERTEX_ID = "0";
|
|
1118
1249
|
_RepTree.DEFAULT_MAX_DEPTH = 1e5;
|
|
@@ -1121,6 +1252,7 @@ var RepTree = _RepTree;
|
|
|
1121
1252
|
0 && (module.exports = {
|
|
1122
1253
|
OpId,
|
|
1123
1254
|
RepTree,
|
|
1255
|
+
StateVector,
|
|
1124
1256
|
TreeState,
|
|
1125
1257
|
Vertex,
|
|
1126
1258
|
VertexState,
|