reptree 0.1.1 → 0.1.3
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 +42 -14
- package/dist/index.cjs +369 -94
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +103 -18
- package/dist/index.d.ts +103 -18
- package/dist/index.js +368 -94
- package/dist/index.js.map +1 -1
- package/package.json +12 -5
package/dist/index.js
CHANGED
|
@@ -459,6 +459,191 @@ var Vertex = class {
|
|
|
459
459
|
}
|
|
460
460
|
};
|
|
461
461
|
|
|
462
|
+
// src/StateVector.ts
|
|
463
|
+
function subtractRanges(rangesA, rangesB) {
|
|
464
|
+
if (rangesB.length === 0) return rangesA.map((r) => [...r]);
|
|
465
|
+
if (rangesA.length === 0) return [];
|
|
466
|
+
const result = [];
|
|
467
|
+
let indexB = 0;
|
|
468
|
+
for (const rangeA of rangesA) {
|
|
469
|
+
let currentStart = rangeA[0];
|
|
470
|
+
const endA = rangeA[1];
|
|
471
|
+
while (indexB < rangesB.length && rangesB[indexB][1] < currentStart) {
|
|
472
|
+
indexB++;
|
|
473
|
+
}
|
|
474
|
+
while (indexB < rangesB.length && rangesB[indexB][0] <= endA) {
|
|
475
|
+
const startB = rangesB[indexB][0];
|
|
476
|
+
const endB = rangesB[indexB][1];
|
|
477
|
+
if (currentStart < startB) {
|
|
478
|
+
result.push([currentStart, Math.min(endA, startB - 1)]);
|
|
479
|
+
}
|
|
480
|
+
currentStart = Math.max(currentStart, endB + 1);
|
|
481
|
+
if (currentStart > endA) break;
|
|
482
|
+
if (endB >= endA) break;
|
|
483
|
+
if (endB < currentStart) {
|
|
484
|
+
indexB++;
|
|
485
|
+
} else if (startB >= currentStart) {
|
|
486
|
+
indexB++;
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
if (currentStart <= endA) {
|
|
490
|
+
result.push([currentStart, endA]);
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
return result;
|
|
494
|
+
}
|
|
495
|
+
var StateVector = class _StateVector {
|
|
496
|
+
/**
|
|
497
|
+
* Creates a new StateVector.
|
|
498
|
+
* @param initialState Optional initial state to copy from
|
|
499
|
+
*/
|
|
500
|
+
constructor(initialState = {}) {
|
|
501
|
+
this.ranges = {};
|
|
502
|
+
for (const [peerId, peerRanges] of Object.entries(initialState)) {
|
|
503
|
+
this.ranges[peerId] = peerRanges.map((range) => [...range]);
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
/**
|
|
507
|
+
* Updates the state vector with a newly applied operation.
|
|
508
|
+
* Assumes ranges are sorted and non-overlapping.
|
|
509
|
+
*
|
|
510
|
+
* @param peerId The peer ID of the operation
|
|
511
|
+
* @param counter The counter value of the operation
|
|
512
|
+
*/
|
|
513
|
+
update(peerId, counter) {
|
|
514
|
+
if (!this.ranges[peerId]) {
|
|
515
|
+
this.ranges[peerId] = [];
|
|
516
|
+
}
|
|
517
|
+
const ranges = this.ranges[peerId];
|
|
518
|
+
if (ranges.length === 0) {
|
|
519
|
+
ranges.push([counter, counter]);
|
|
520
|
+
return;
|
|
521
|
+
}
|
|
522
|
+
let rangeExtendedOrMerged = false;
|
|
523
|
+
let insertIndex = -1;
|
|
524
|
+
for (let i = 0; i < ranges.length; i++) {
|
|
525
|
+
const range = ranges[i];
|
|
526
|
+
if (counter >= range[0] && counter <= range[1]) {
|
|
527
|
+
rangeExtendedOrMerged = true;
|
|
528
|
+
break;
|
|
529
|
+
}
|
|
530
|
+
if (counter === range[0] - 1) {
|
|
531
|
+
range[0] = counter;
|
|
532
|
+
rangeExtendedOrMerged = true;
|
|
533
|
+
if (i > 0 && range[0] === ranges[i - 1][1] + 1) {
|
|
534
|
+
ranges[i - 1][1] = range[1];
|
|
535
|
+
ranges.splice(i, 1);
|
|
536
|
+
}
|
|
537
|
+
break;
|
|
538
|
+
}
|
|
539
|
+
if (counter === range[1] + 1) {
|
|
540
|
+
range[1] = counter;
|
|
541
|
+
rangeExtendedOrMerged = true;
|
|
542
|
+
if (i < ranges.length - 1 && range[1] + 1 === ranges[i + 1][0]) {
|
|
543
|
+
range[1] = ranges[i + 1][1];
|
|
544
|
+
ranges.splice(i + 1, 1);
|
|
545
|
+
}
|
|
546
|
+
break;
|
|
547
|
+
}
|
|
548
|
+
if (counter < range[0] && insertIndex === -1) {
|
|
549
|
+
insertIndex = i;
|
|
550
|
+
}
|
|
551
|
+
}
|
|
552
|
+
if (!rangeExtendedOrMerged) {
|
|
553
|
+
if (insertIndex === -1) {
|
|
554
|
+
insertIndex = ranges.length;
|
|
555
|
+
}
|
|
556
|
+
ranges.splice(insertIndex, 0, [counter, counter]);
|
|
557
|
+
if (insertIndex > 0 && ranges[insertIndex][0] === ranges[insertIndex - 1][1] + 1) {
|
|
558
|
+
ranges[insertIndex - 1][1] = ranges[insertIndex][1];
|
|
559
|
+
ranges.splice(insertIndex, 1);
|
|
560
|
+
insertIndex--;
|
|
561
|
+
}
|
|
562
|
+
if (insertIndex < ranges.length - 1 && ranges[insertIndex][1] + 1 === ranges[insertIndex + 1][0]) {
|
|
563
|
+
ranges[insertIndex][1] = ranges[insertIndex + 1][1];
|
|
564
|
+
ranges.splice(insertIndex + 1, 1);
|
|
565
|
+
}
|
|
566
|
+
}
|
|
567
|
+
}
|
|
568
|
+
/**
|
|
569
|
+
* Updates the state vector with a newly applied operation.
|
|
570
|
+
*
|
|
571
|
+
* @param op The operation that was just applied
|
|
572
|
+
*/
|
|
573
|
+
updateFromOp(op) {
|
|
574
|
+
this.update(op.id.peerId, op.id.counter);
|
|
575
|
+
}
|
|
576
|
+
/**
|
|
577
|
+
* Returns the current state vector.
|
|
578
|
+
* Returns a readonly reference to the internal state.
|
|
579
|
+
*/
|
|
580
|
+
getState() {
|
|
581
|
+
return this.ranges;
|
|
582
|
+
}
|
|
583
|
+
/**
|
|
584
|
+
* Calculates which operation ranges we have that the other state vector is missing
|
|
585
|
+
* by comparing state vectors.
|
|
586
|
+
*
|
|
587
|
+
* @param other The other state vector to compare against
|
|
588
|
+
* @returns Array of operation ID ranges that we have but they don't
|
|
589
|
+
*/
|
|
590
|
+
diff(other) {
|
|
591
|
+
const missingRanges = [];
|
|
592
|
+
const theirState = other.getState();
|
|
593
|
+
for (const [peerId, ourRanges] of Object.entries(this.ranges)) {
|
|
594
|
+
const theirRanges = theirState[peerId] || [];
|
|
595
|
+
const missing = subtractRanges(ourRanges, theirRanges);
|
|
596
|
+
for (const [start, end] of missing) {
|
|
597
|
+
if (start <= end) {
|
|
598
|
+
missingRanges.push({
|
|
599
|
+
peerId,
|
|
600
|
+
start,
|
|
601
|
+
end
|
|
602
|
+
});
|
|
603
|
+
}
|
|
604
|
+
}
|
|
605
|
+
}
|
|
606
|
+
return missingRanges;
|
|
607
|
+
}
|
|
608
|
+
/**
|
|
609
|
+
* Checks if the state vector contains the given operation ID
|
|
610
|
+
*
|
|
611
|
+
* @param opId The operation ID to check
|
|
612
|
+
* @returns true if the operation is in the state vector, false otherwise
|
|
613
|
+
*/
|
|
614
|
+
contains(opId) {
|
|
615
|
+
const peerId = opId.peerId;
|
|
616
|
+
const counter = opId.counter;
|
|
617
|
+
if (!this.ranges[peerId]) {
|
|
618
|
+
return false;
|
|
619
|
+
}
|
|
620
|
+
for (const [start, end] of this.ranges[peerId]) {
|
|
621
|
+
if (counter >= start && counter <= end) {
|
|
622
|
+
return true;
|
|
623
|
+
}
|
|
624
|
+
}
|
|
625
|
+
return false;
|
|
626
|
+
}
|
|
627
|
+
/**
|
|
628
|
+
* Creates a copy of this state vector
|
|
629
|
+
*/
|
|
630
|
+
clone() {
|
|
631
|
+
return new _StateVector(this.ranges);
|
|
632
|
+
}
|
|
633
|
+
/**
|
|
634
|
+
* Builds a state vector from an array of operations
|
|
635
|
+
* @param operations The operations to build the state vector from
|
|
636
|
+
* @returns A new StateVector instance
|
|
637
|
+
*/
|
|
638
|
+
static fromOperations(operations) {
|
|
639
|
+
const stateVector = new _StateVector();
|
|
640
|
+
for (const op of operations) {
|
|
641
|
+
stateVector.updateFromOp(op);
|
|
642
|
+
}
|
|
643
|
+
return stateVector;
|
|
644
|
+
}
|
|
645
|
+
};
|
|
646
|
+
|
|
462
647
|
// src/RepTree.ts
|
|
463
648
|
var _RepTree = class _RepTree {
|
|
464
649
|
/**
|
|
@@ -474,31 +659,40 @@ var _RepTree = class _RepTree {
|
|
|
474
659
|
this.localOps = [];
|
|
475
660
|
this.pendingMovesWithMissingParent = /* @__PURE__ */ new Map();
|
|
476
661
|
this.pendingPropertiesWithMissingVertex = /* @__PURE__ */ new Map();
|
|
477
|
-
this.
|
|
662
|
+
this.knownOps = /* @__PURE__ */ new Set();
|
|
478
663
|
this.parentIdBeforeMove = /* @__PURE__ */ new Map();
|
|
479
664
|
this.opAppliedCallbacks = [];
|
|
480
665
|
this.maxDepth = _RepTree.DEFAULT_MAX_DEPTH;
|
|
666
|
+
this._stateVectorEnabled = true;
|
|
481
667
|
this.peerId = peerId;
|
|
482
668
|
this.state = new TreeState();
|
|
669
|
+
this.stateVector = new StateVector();
|
|
483
670
|
if (ops != null && ops.length > 0) {
|
|
484
|
-
let rootMoveOp;
|
|
485
|
-
for (let i = 0; i < ops.length; i++) {
|
|
486
|
-
if (isMoveVertexOp(ops[i]) && ops[i].parentId === null) {
|
|
487
|
-
rootMoveOp = ops[i];
|
|
488
|
-
break;
|
|
489
|
-
}
|
|
490
|
-
}
|
|
491
|
-
if (rootMoveOp) {
|
|
492
|
-
this.rootVertexId = rootMoveOp.targetId;
|
|
493
|
-
} else {
|
|
494
|
-
throw new Error("The operations has to contain a move operation with a parentId as null to set the root vertex");
|
|
495
|
-
}
|
|
496
671
|
this.applyOps(ops);
|
|
497
|
-
this.
|
|
672
|
+
const root = this.root;
|
|
673
|
+
if (!root) {
|
|
674
|
+
throw new Error("There has to be a root vertex in the operations");
|
|
675
|
+
}
|
|
498
676
|
} else {
|
|
499
|
-
this.
|
|
500
|
-
|
|
677
|
+
this.ensureNullVertex();
|
|
678
|
+
}
|
|
679
|
+
}
|
|
680
|
+
get root() {
|
|
681
|
+
if (!this.rootVertexId) {
|
|
682
|
+
const vertices = this.state.getAllVertices();
|
|
683
|
+
for (const vertex of vertices) {
|
|
684
|
+
if (vertex.parentId === null && vertex.id !== _RepTree.NULL_VERTEX_ID) {
|
|
685
|
+
this.rootVertexId = vertex.id;
|
|
686
|
+
return new Vertex(this, vertex);
|
|
687
|
+
}
|
|
688
|
+
}
|
|
689
|
+
return void 0;
|
|
690
|
+
}
|
|
691
|
+
const rootVertex = this.state.getVertex(this.rootVertexId);
|
|
692
|
+
if (!rootVertex) {
|
|
693
|
+
throw new Error("Root vertex not found");
|
|
501
694
|
}
|
|
695
|
+
return new Vertex(this, rootVertex);
|
|
502
696
|
}
|
|
503
697
|
getMoveOps() {
|
|
504
698
|
return this.moveOps;
|
|
@@ -510,13 +704,6 @@ var _RepTree = class _RepTree {
|
|
|
510
704
|
const vertex = this.state.getVertex(vertexId);
|
|
511
705
|
return vertex ? new Vertex(this, vertex) : void 0;
|
|
512
706
|
}
|
|
513
|
-
get rootVertex() {
|
|
514
|
-
const rootVertex = this.state.getVertex(this.rootVertexId);
|
|
515
|
-
if (!rootVertex) {
|
|
516
|
-
throw new Error("Root vertex not found");
|
|
517
|
-
}
|
|
518
|
-
return new Vertex(this, rootVertex);
|
|
519
|
-
}
|
|
520
707
|
getAllVertices() {
|
|
521
708
|
return this.state.getAllVertices().map((v) => new Vertex(this, v));
|
|
522
709
|
}
|
|
@@ -567,6 +754,17 @@ var _RepTree = class _RepTree {
|
|
|
567
754
|
setMaxDepth(maxDepth) {
|
|
568
755
|
this.maxDepth = maxDepth;
|
|
569
756
|
}
|
|
757
|
+
createRoot() {
|
|
758
|
+
if (this.rootVertexId) {
|
|
759
|
+
throw new Error("Root vertex already exists");
|
|
760
|
+
}
|
|
761
|
+
this.rootVertexId = this.newVertexInternalWithUUID(null);
|
|
762
|
+
const rootVertex = this.state.getVertex(this.rootVertexId);
|
|
763
|
+
if (!rootVertex) {
|
|
764
|
+
throw new Error("Root vertex not found");
|
|
765
|
+
}
|
|
766
|
+
return new Vertex(this, rootVertex);
|
|
767
|
+
}
|
|
570
768
|
newVertex(parentId, props = null) {
|
|
571
769
|
const typedProps = props;
|
|
572
770
|
const vertexId = this.newVertexInternalWithUUID(parentId);
|
|
@@ -599,7 +797,7 @@ var _RepTree = class _RepTree {
|
|
|
599
797
|
this.applyMove(op);
|
|
600
798
|
}
|
|
601
799
|
deleteVertex(vertexId) {
|
|
602
|
-
this.moveVertex(vertexId, _RepTree.
|
|
800
|
+
this.moveVertex(vertexId, _RepTree.NULL_VERTEX_ID);
|
|
603
801
|
}
|
|
604
802
|
setTransientVertexProperty(vertexId, key, value) {
|
|
605
803
|
this.lamportClock++;
|
|
@@ -623,6 +821,9 @@ var _RepTree = class _RepTree {
|
|
|
623
821
|
path = path.replace(/^\/+/, "");
|
|
624
822
|
path = path.replace(/\/+$/, "");
|
|
625
823
|
const pathParts = path.split("/");
|
|
824
|
+
if (!this.rootVertexId) {
|
|
825
|
+
return void 0;
|
|
826
|
+
}
|
|
626
827
|
const root = this.state.getVertex(this.rootVertexId);
|
|
627
828
|
if (!root) {
|
|
628
829
|
throw new Error("The root vertex is not found");
|
|
@@ -644,12 +845,38 @@ var _RepTree = class _RepTree {
|
|
|
644
845
|
return void 0;
|
|
645
846
|
}
|
|
646
847
|
printTree() {
|
|
848
|
+
if (!this.rootVertexId) {
|
|
849
|
+
return "";
|
|
850
|
+
}
|
|
647
851
|
return this.state.printTree(this.rootVertexId);
|
|
648
852
|
}
|
|
649
853
|
merge(ops) {
|
|
650
854
|
this.applyOps(ops);
|
|
651
855
|
}
|
|
856
|
+
/** Applies operations in an optimized way, sorting move ops by OpId to avoid undo-do-redo cycles */
|
|
857
|
+
applyOpsOptimizedForLotsOfMoves(ops) {
|
|
858
|
+
const newMoveOps = ops.filter((op) => isMoveVertexOp(op) && !this.knownOps.has(op.id.toString()));
|
|
859
|
+
if (newMoveOps.length > 0) {
|
|
860
|
+
const allMoveOps = [...this.moveOps, ...newMoveOps];
|
|
861
|
+
allMoveOps.sort((a, b) => OpId.compare(a.id, b.id));
|
|
862
|
+
for (let i = 0, len = allMoveOps.length; i < len; i++) {
|
|
863
|
+
const op = allMoveOps[i];
|
|
864
|
+
this.applyMove(op);
|
|
865
|
+
}
|
|
866
|
+
}
|
|
867
|
+
const propertyOps = ops.filter((op) => isSetPropertyOp(op) && !this.knownOps.has(op.id.toString()));
|
|
868
|
+
for (let i = 0, len = propertyOps.length; i < len; i++) {
|
|
869
|
+
const op = propertyOps[i];
|
|
870
|
+
this.applyProperty(op);
|
|
871
|
+
}
|
|
872
|
+
}
|
|
652
873
|
compareStructure(other) {
|
|
874
|
+
if (this.root?.id !== other.root?.id) {
|
|
875
|
+
return false;
|
|
876
|
+
}
|
|
877
|
+
if (!this.rootVertexId) {
|
|
878
|
+
return true;
|
|
879
|
+
}
|
|
653
880
|
return _RepTree.compareVertices(this.rootVertexId, this, other);
|
|
654
881
|
}
|
|
655
882
|
compareMoveOps(other) {
|
|
@@ -759,8 +986,8 @@ var _RepTree = class _RepTree {
|
|
|
759
986
|
const vertexId = uuid();
|
|
760
987
|
return this.newVertexInternal(vertexId, parentId);
|
|
761
988
|
}
|
|
762
|
-
|
|
763
|
-
const vertexId = _RepTree.
|
|
989
|
+
ensureNullVertex() {
|
|
990
|
+
const vertexId = _RepTree.NULL_VERTEX_ID;
|
|
764
991
|
if (this.state.getVertex(vertexId)) {
|
|
765
992
|
return;
|
|
766
993
|
}
|
|
@@ -772,6 +999,19 @@ var _RepTree = class _RepTree {
|
|
|
772
999
|
this.lamportClock = operation.id.counter;
|
|
773
1000
|
}
|
|
774
1001
|
}
|
|
1002
|
+
applyPendingMovesForParent(parentId) {
|
|
1003
|
+
if (!this.state.getVertex(parentId)) {
|
|
1004
|
+
return;
|
|
1005
|
+
}
|
|
1006
|
+
const pendingMoves = this.pendingMovesWithMissingParent.get(parentId);
|
|
1007
|
+
if (!pendingMoves) {
|
|
1008
|
+
return;
|
|
1009
|
+
}
|
|
1010
|
+
this.pendingMovesWithMissingParent.delete(parentId);
|
|
1011
|
+
for (const pendingOp of pendingMoves) {
|
|
1012
|
+
this.applyMove(pendingOp);
|
|
1013
|
+
}
|
|
1014
|
+
}
|
|
775
1015
|
applyMove(op) {
|
|
776
1016
|
if (op.parentId !== null && !this.state.getVertex(op.parentId)) {
|
|
777
1017
|
if (!this.pendingMovesWithMissingParent.has(op.parentId)) {
|
|
@@ -806,15 +1046,52 @@ var _RepTree = class _RepTree {
|
|
|
806
1046
|
}
|
|
807
1047
|
this.applyPendingMovesForParent(op.targetId);
|
|
808
1048
|
}
|
|
809
|
-
|
|
810
|
-
this.
|
|
811
|
-
|
|
812
|
-
|
|
1049
|
+
setPropertyAndItsOpId(op) {
|
|
1050
|
+
this.propertiesAndTheirOpIds.set(`${op.key}@${op.targetId}`, op.id);
|
|
1051
|
+
this.state.setProperty(op.targetId, op.key, op.value);
|
|
1052
|
+
this.reportOpAsApplied(op);
|
|
1053
|
+
}
|
|
1054
|
+
setTransientPropertyAndItsOpId(op) {
|
|
1055
|
+
this.transientPropertiesAndTheirOpIds.set(`${op.key}@${op.targetId}`, op.id);
|
|
1056
|
+
this.state.setTransientProperty(op.targetId, op.key, op.value);
|
|
1057
|
+
this.reportOpAsApplied(op);
|
|
1058
|
+
}
|
|
1059
|
+
applyProperty(op) {
|
|
1060
|
+
const targetVertex = this.state.getVertex(op.targetId);
|
|
1061
|
+
if (!targetVertex) {
|
|
1062
|
+
if (op.transient) {
|
|
1063
|
+
return;
|
|
1064
|
+
}
|
|
1065
|
+
if (!this.pendingPropertiesWithMissingVertex.has(op.targetId)) {
|
|
1066
|
+
this.pendingPropertiesWithMissingVertex.set(op.targetId, []);
|
|
1067
|
+
}
|
|
1068
|
+
this.pendingPropertiesWithMissingVertex.get(op.targetId).push(op);
|
|
1069
|
+
return;
|
|
1070
|
+
}
|
|
1071
|
+
this.updateLamportClock(op);
|
|
1072
|
+
const prevTransientOpId = this.transientPropertiesAndTheirOpIds.get(`${op.key}@${op.targetId}`);
|
|
1073
|
+
const prevProp = targetVertex.getProperty(op.key);
|
|
1074
|
+
const prevOpId = this.propertiesAndTheirOpIds.get(`${op.key}@${op.targetId}`);
|
|
1075
|
+
if (!op.transient) {
|
|
1076
|
+
this.setPropertyOps.push(op);
|
|
1077
|
+
if (!prevProp || !prevOpId || op.id.isGreaterThan(prevOpId)) {
|
|
1078
|
+
this.setPropertyAndItsOpId(op);
|
|
1079
|
+
} else {
|
|
1080
|
+
this.knownOps.add(op.id.toString());
|
|
1081
|
+
}
|
|
1082
|
+
if (prevTransientOpId && op.id.isGreaterThan(prevTransientOpId)) {
|
|
1083
|
+
this.transientPropertiesAndTheirOpIds.delete(`${op.key}@${op.targetId}`);
|
|
1084
|
+
targetVertex.removeTransientProperty(op.key);
|
|
1085
|
+
}
|
|
1086
|
+
} else {
|
|
1087
|
+
if (!prevTransientOpId || op.id.isGreaterThan(prevTransientOpId)) {
|
|
1088
|
+
this.setTransientPropertyAndItsOpId(op);
|
|
1089
|
+
}
|
|
813
1090
|
}
|
|
814
1091
|
}
|
|
815
1092
|
applyOps(ops) {
|
|
816
1093
|
for (const op of ops) {
|
|
817
|
-
if (this.
|
|
1094
|
+
if (this.knownOps.has(op.id.toString())) {
|
|
818
1095
|
continue;
|
|
819
1096
|
}
|
|
820
1097
|
if (isMoveVertexOp(op)) {
|
|
@@ -824,34 +1101,13 @@ var _RepTree = class _RepTree {
|
|
|
824
1101
|
}
|
|
825
1102
|
}
|
|
826
1103
|
}
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
const allMoveOps = [...this.moveOps, ...newMoveOps];
|
|
832
|
-
allMoveOps.sort((a, b) => OpId.compare(a.id, b.id));
|
|
833
|
-
for (let i = 0, len = allMoveOps.length; i < len; i++) {
|
|
834
|
-
const op = allMoveOps[i];
|
|
835
|
-
this.applyMove(op);
|
|
836
|
-
}
|
|
837
|
-
}
|
|
838
|
-
const propertyOps = ops.filter((op) => isSetPropertyOp(op) && !this.appliedOps.has(op.id.toString()));
|
|
839
|
-
for (let i = 0, len = propertyOps.length; i < len; i++) {
|
|
840
|
-
const op = propertyOps[i];
|
|
841
|
-
this.applyProperty(op);
|
|
842
|
-
}
|
|
843
|
-
}
|
|
844
|
-
applyPendingMovesForParent(parentId) {
|
|
845
|
-
if (!this.state.getVertex(parentId)) {
|
|
846
|
-
return;
|
|
847
|
-
}
|
|
848
|
-
const pendingMoves = this.pendingMovesWithMissingParent.get(parentId);
|
|
849
|
-
if (!pendingMoves) {
|
|
850
|
-
return;
|
|
1104
|
+
reportOpAsApplied(op) {
|
|
1105
|
+
this.knownOps.add(op.id.toString());
|
|
1106
|
+
if (this._stateVectorEnabled) {
|
|
1107
|
+
this.stateVector.updateFromOp(op);
|
|
851
1108
|
}
|
|
852
|
-
this.
|
|
853
|
-
|
|
854
|
-
this.applyMove(pendingOp);
|
|
1109
|
+
for (const callback of this.opAppliedCallbacks) {
|
|
1110
|
+
callback(op);
|
|
855
1111
|
}
|
|
856
1112
|
}
|
|
857
1113
|
tryToMove(op) {
|
|
@@ -864,6 +1120,7 @@ var _RepTree = class _RepTree {
|
|
|
864
1120
|
this.state.moveVertex(op.targetId, op.parentId);
|
|
865
1121
|
if (!targetVertex) {
|
|
866
1122
|
const pendingProperties = this.pendingPropertiesWithMissingVertex.get(op.targetId) || [];
|
|
1123
|
+
this.pendingPropertiesWithMissingVertex.delete(op.targetId);
|
|
867
1124
|
for (const prop of pendingProperties) {
|
|
868
1125
|
this.setPropertyAndItsOpId(prop);
|
|
869
1126
|
}
|
|
@@ -881,54 +1138,71 @@ var _RepTree = class _RepTree {
|
|
|
881
1138
|
}
|
|
882
1139
|
this.state.moveVertex(op.targetId, prevParentId);
|
|
883
1140
|
}
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
this.
|
|
891
|
-
|
|
892
|
-
|
|
1141
|
+
// --- Range-Based State Vector Methods ---
|
|
1142
|
+
/**
|
|
1143
|
+
* Returns the current state vector.
|
|
1144
|
+
* Returns a readonly reference to the internal state vector.
|
|
1145
|
+
*/
|
|
1146
|
+
getStateVector() {
|
|
1147
|
+
if (!this._stateVectorEnabled) {
|
|
1148
|
+
return null;
|
|
1149
|
+
}
|
|
1150
|
+
return this.stateVector.getState();
|
|
893
1151
|
}
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
1152
|
+
/**
|
|
1153
|
+
* Determines which operations are needed to synchronize
|
|
1154
|
+
* with the provided state vector.
|
|
1155
|
+
*
|
|
1156
|
+
* @param theirStateVector The state vector from another peer
|
|
1157
|
+
* @returns Operations that should be sent to the other peer, sorted by OpId.
|
|
1158
|
+
*/
|
|
1159
|
+
getMissingOps(theirStateVector) {
|
|
1160
|
+
if (!this._stateVectorEnabled) {
|
|
1161
|
+
return [...this.moveOps, ...this.setPropertyOps];
|
|
1162
|
+
}
|
|
1163
|
+
const otherStateVector = new StateVector(theirStateVector);
|
|
1164
|
+
const missingRanges = this.stateVector.diff(otherStateVector);
|
|
1165
|
+
const missingOps = [];
|
|
1166
|
+
const allOps = [...this.moveOps, ...this.setPropertyOps];
|
|
1167
|
+
for (const op of allOps) {
|
|
1168
|
+
for (const range of missingRanges) {
|
|
1169
|
+
if (op.id.peerId === range.peerId && op.id.counter >= range.start && op.id.counter <= range.end) {
|
|
1170
|
+
missingOps.push(op);
|
|
1171
|
+
break;
|
|
1172
|
+
}
|
|
902
1173
|
}
|
|
903
|
-
this.pendingPropertiesWithMissingVertex.get(op.targetId).push(op);
|
|
904
|
-
return;
|
|
905
1174
|
}
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
1175
|
+
missingOps.sort((a, b) => OpId.compare(a.id, b.id));
|
|
1176
|
+
return missingOps;
|
|
1177
|
+
}
|
|
1178
|
+
/**
|
|
1179
|
+
* Gets or sets whether state vector tracking is enabled
|
|
1180
|
+
*/
|
|
1181
|
+
get stateVectorEnabled() {
|
|
1182
|
+
return this._stateVectorEnabled;
|
|
1183
|
+
}
|
|
1184
|
+
/**
|
|
1185
|
+
* Sets the state vector enabled status
|
|
1186
|
+
* When enabled, rebuilds the state vector from existing operations if needed
|
|
1187
|
+
*/
|
|
1188
|
+
set stateVectorEnabled(value) {
|
|
1189
|
+
if (value === this._stateVectorEnabled) return;
|
|
1190
|
+
if (value) {
|
|
1191
|
+
this._stateVectorEnabled = true;
|
|
1192
|
+
this.stateVector = StateVector.fromOperations([...this.moveOps, ...this.setPropertyOps]);
|
|
919
1193
|
} else {
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
}
|
|
1194
|
+
this._stateVectorEnabled = false;
|
|
1195
|
+
this.stateVector = new StateVector();
|
|
923
1196
|
}
|
|
924
1197
|
}
|
|
925
1198
|
};
|
|
926
|
-
_RepTree.
|
|
1199
|
+
_RepTree.NULL_VERTEX_ID = "0";
|
|
927
1200
|
_RepTree.DEFAULT_MAX_DEPTH = 1e5;
|
|
928
1201
|
var RepTree = _RepTree;
|
|
929
1202
|
export {
|
|
930
1203
|
OpId,
|
|
931
1204
|
RepTree,
|
|
1205
|
+
StateVector,
|
|
932
1206
|
TreeState,
|
|
933
1207
|
Vertex,
|
|
934
1208
|
VertexState,
|