reptree 0.6.0 → 0.8.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/README.md +3 -3
- package/dist/index.cjs +149 -30
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +48 -12
- package/dist/index.d.ts +48 -12
- package/dist/index.js +149 -30
- package/dist/index.js.map +1 -1
- package/package.json +2 -4
package/README.md
CHANGED
|
@@ -33,9 +33,10 @@ const qa = company.newNamedChild("qa");
|
|
|
33
33
|
// Create a vertex in another vertex
|
|
34
34
|
const alice = qa.newChild();
|
|
35
35
|
|
|
36
|
-
// Set properties
|
|
36
|
+
// Set properties (supports any JSON-serializable values)
|
|
37
37
|
alice.setProperty("name", "Alice");
|
|
38
38
|
alice.setProperty("age", 32);
|
|
39
|
+
alice.setProperty("meta", { department: "QA", skills: ["cypress", "playwright"], flags: { lead: false } });
|
|
39
40
|
|
|
40
41
|
// Move the vertex inside a different vertex
|
|
41
42
|
alice.moveTo(devs);
|
|
@@ -89,8 +90,7 @@ const logoFile = imagesFolder.newNamedChild("logo.png");
|
|
|
89
90
|
logoFile.setProperties({
|
|
90
91
|
type: "file",
|
|
91
92
|
size: 15360,
|
|
92
|
-
dimensions: "512x512",
|
|
93
|
-
format: "png",
|
|
93
|
+
meta: { dimensions: "512x512", format: "png" },
|
|
94
94
|
s3Path: "s3://my-bucket/images/logo.png",
|
|
95
95
|
});
|
|
96
96
|
|
package/dist/index.cjs
CHANGED
|
@@ -385,7 +385,7 @@ var TreeState = class {
|
|
|
385
385
|
}
|
|
386
386
|
};
|
|
387
387
|
|
|
388
|
-
// src/uuid.ts
|
|
388
|
+
// src/utils/uuid.ts
|
|
389
389
|
var removeDashes = (guid) => guid.replace(/-/g, "");
|
|
390
390
|
function uuid() {
|
|
391
391
|
return removeDashes(crypto.randomUUID());
|
|
@@ -501,18 +501,29 @@ function bindVertex(tree, id, schemaOrOptions) {
|
|
|
501
501
|
// src/Vertex.ts
|
|
502
502
|
var Vertex = class _Vertex {
|
|
503
503
|
constructor(tree, state) {
|
|
504
|
-
this.tree = tree;
|
|
505
504
|
this.state = state;
|
|
505
|
+
this._tree = tree;
|
|
506
506
|
}
|
|
507
|
+
/** Returns the tree this vertex belongs to. */
|
|
508
|
+
get tree() {
|
|
509
|
+
return this._tree;
|
|
510
|
+
}
|
|
511
|
+
set tree(value) {
|
|
512
|
+
this._tree = value;
|
|
513
|
+
}
|
|
514
|
+
/** Returns the ID of this vertex. */
|
|
507
515
|
get id() {
|
|
508
516
|
return this.state.id;
|
|
509
517
|
}
|
|
518
|
+
/** Returns the name of this vertex. The name is stored as a property with the key 'name'. */
|
|
510
519
|
get name() {
|
|
511
520
|
return this.getProperty("name");
|
|
512
521
|
}
|
|
522
|
+
/** Sets the name of this vertex. The name is stored as a property with the key 'name'. */
|
|
513
523
|
set name(name) {
|
|
514
524
|
this.tree.setVertexProperty(this.id, "name", name);
|
|
515
525
|
}
|
|
526
|
+
/** Returns the creation date of this vertex. The creation date is stored as a property with the key '_c'. */
|
|
516
527
|
get createdAt() {
|
|
517
528
|
const createdAt = this.getProperty("_c");
|
|
518
529
|
if (!createdAt) {
|
|
@@ -520,30 +531,51 @@ var Vertex = class _Vertex {
|
|
|
520
531
|
}
|
|
521
532
|
return new Date(createdAt);
|
|
522
533
|
}
|
|
523
|
-
|
|
524
|
-
throw new Error("Not implemented");
|
|
525
|
-
}
|
|
534
|
+
/** Returns the ID of the parent vertex of this vertex. */
|
|
526
535
|
get parentId() {
|
|
527
536
|
return this.state.parentId;
|
|
528
537
|
}
|
|
538
|
+
/** Returns the parent vertex of this vertex. */
|
|
529
539
|
get parent() {
|
|
530
540
|
if (!this.parentId) {
|
|
531
541
|
return void 0;
|
|
532
542
|
}
|
|
533
543
|
return this.tree.getVertex(this.parentId);
|
|
534
544
|
}
|
|
545
|
+
/** Returns the children vertices of this vertex. */
|
|
535
546
|
get children() {
|
|
536
547
|
return this.tree.getChildren(this.id);
|
|
537
548
|
}
|
|
549
|
+
/** Returns the IDs of the children vertices of this vertex. */
|
|
538
550
|
get childrenIds() {
|
|
539
551
|
return this.tree.getChildrenIds(this.id);
|
|
540
552
|
}
|
|
553
|
+
/** Returns the ancestors of this vertex. The first element is the root vertex.
|
|
554
|
+
* E.g root -> grandparent -> parent.
|
|
555
|
+
* Doesn't include this vertex in the array.
|
|
556
|
+
*/
|
|
557
|
+
get ancestors() {
|
|
558
|
+
return this.tree.getAncestors(this.id);
|
|
559
|
+
}
|
|
560
|
+
/** Returns the ID of the root vertex of the tree this vertex belongs to. */
|
|
561
|
+
get treeId() {
|
|
562
|
+
return this.root.id;
|
|
563
|
+
}
|
|
564
|
+
/** Returns the root vertex of the tree this vertex belongs to. */
|
|
565
|
+
get root() {
|
|
566
|
+
const root = this.tree.root;
|
|
567
|
+
if (!root) {
|
|
568
|
+
throw new Error("Root vertex of the tree is not set");
|
|
569
|
+
}
|
|
570
|
+
return root;
|
|
571
|
+
}
|
|
541
572
|
getAsTypedObject() {
|
|
542
573
|
return this.getProperties();
|
|
543
574
|
}
|
|
544
575
|
getChildrenAsTypedArray() {
|
|
545
576
|
return this.children.map((v) => v.getAsTypedObject());
|
|
546
577
|
}
|
|
578
|
+
/** Creates a new child vertex of this vertex. */
|
|
547
579
|
newChild(props) {
|
|
548
580
|
if (props && typeof props === "object" && "children" in props) {
|
|
549
581
|
throw new Error("Passing children inside props is not supported at the moment");
|
|
@@ -551,13 +583,15 @@ var Vertex = class _Vertex {
|
|
|
551
583
|
const normalized = _Vertex.normalizePropsForCreation(props);
|
|
552
584
|
return this.tree.newVertex(this.id, normalized);
|
|
553
585
|
}
|
|
586
|
+
/** Creates a new named child vertex of this vertex. */
|
|
554
587
|
newNamedChild(name, props) {
|
|
555
588
|
if (props && typeof props === "object" && "children" in props) {
|
|
556
589
|
throw new Error("Passing children inside props is not supported at the moment");
|
|
557
590
|
}
|
|
558
|
-
const normalized = _Vertex.normalizePropsForCreation(props
|
|
591
|
+
const normalized = _Vertex.normalizePropsForCreation(props);
|
|
559
592
|
return this.tree.newNamedVertex(this.id, name, normalized);
|
|
560
593
|
}
|
|
594
|
+
/** Sets a property on this vertex. */
|
|
561
595
|
setProperty(key, value) {
|
|
562
596
|
const existingValue = this.getProperty(key, false);
|
|
563
597
|
if (existingValue === value) {
|
|
@@ -565,6 +599,7 @@ var Vertex = class _Vertex {
|
|
|
565
599
|
}
|
|
566
600
|
this.tree.setVertexProperty(this.id, key, value);
|
|
567
601
|
}
|
|
602
|
+
/** Sets a transient property on this vertex. Transient properties are not persisted to the tree and are not included in the state vector. */
|
|
568
603
|
setTransientProperty(key, value) {
|
|
569
604
|
const existingValue = this.getProperty(key);
|
|
570
605
|
if (existingValue === value) {
|
|
@@ -572,17 +607,21 @@ var Vertex = class _Vertex {
|
|
|
572
607
|
}
|
|
573
608
|
this.tree.setTransientVertexProperty(this.id, key, value);
|
|
574
609
|
}
|
|
610
|
+
/** Promotes all transient (temporary) properties to persistent properties. */
|
|
575
611
|
commitTransients() {
|
|
576
612
|
this.tree.commitTransients(this.id);
|
|
577
613
|
}
|
|
614
|
+
/** Sets multiple properties on this vertex. */
|
|
578
615
|
setProperties(props) {
|
|
579
616
|
for (const [key, value] of Object.entries(props)) {
|
|
580
617
|
this.setProperty(key, value);
|
|
581
618
|
}
|
|
582
619
|
}
|
|
620
|
+
/** Returns the value of a property on this vertex. */
|
|
583
621
|
getProperty(key, includingTransient = true) {
|
|
584
622
|
return this.tree.getVertexProperty(this.id, key, includingTransient);
|
|
585
623
|
}
|
|
624
|
+
/** Returns all properties on this vertex. */
|
|
586
625
|
getProperties() {
|
|
587
626
|
const props = {};
|
|
588
627
|
this.tree.getVertexProperties(this.id).forEach((p) => {
|
|
@@ -602,10 +641,12 @@ var Vertex = class _Vertex {
|
|
|
602
641
|
findAllTypedChildrenWithProperty(key, value) {
|
|
603
642
|
return this.findAllChildrenWithProperty(key, value).map((c) => c.getAsTypedObject());
|
|
604
643
|
}
|
|
644
|
+
/** Observes changes to this vertex. */
|
|
605
645
|
observe(listener) {
|
|
606
646
|
const unobserve = this.tree.observe(this.id, listener);
|
|
607
647
|
return () => unobserve();
|
|
608
648
|
}
|
|
649
|
+
/** Observes changes to the children of this vertex. */
|
|
609
650
|
observeChildren(listener) {
|
|
610
651
|
const unobserve = this.tree.observe(this.id, (events) => {
|
|
611
652
|
if (events.some((e) => e.type === "children")) {
|
|
@@ -634,21 +675,32 @@ var Vertex = class _Vertex {
|
|
|
634
675
|
* - Filters unsupported field types with a console warning
|
|
635
676
|
* - When a name param is provided to newNamedChild, ignores conflicting name in props
|
|
636
677
|
*/
|
|
637
|
-
static normalizePropsForCreation(props
|
|
678
|
+
static normalizePropsForCreation(props) {
|
|
638
679
|
if (!props) return null;
|
|
639
680
|
const input = props;
|
|
640
681
|
const out = {};
|
|
641
682
|
const skipped = [];
|
|
683
|
+
const isJsonValue2 = (v) => {
|
|
684
|
+
if (v === null) return true;
|
|
685
|
+
const t = typeof v;
|
|
686
|
+
if (t === "string" || t === "number" || t === "boolean") return true;
|
|
687
|
+
if (Array.isArray(v)) return v.every(isJsonValue2);
|
|
688
|
+
if (t === "object") {
|
|
689
|
+
const proto = Object.getPrototypeOf(v);
|
|
690
|
+
if (proto !== Object.prototype && proto !== null) return false;
|
|
691
|
+
for (const val of Object.values(v)) {
|
|
692
|
+
if (!isJsonValue2(val)) return false;
|
|
693
|
+
}
|
|
694
|
+
return true;
|
|
695
|
+
}
|
|
696
|
+
return false;
|
|
697
|
+
};
|
|
642
698
|
for (const [rawKey, rawValue] of Object.entries(input)) {
|
|
643
699
|
if (rawValue === void 0) {
|
|
644
700
|
continue;
|
|
645
701
|
}
|
|
646
702
|
if (rawKey === "children") continue;
|
|
647
703
|
let key = rawKey;
|
|
648
|
-
if (rawKey === "name" && explicitName !== void 0) {
|
|
649
|
-
console.warn('newNamedChild: "name" in props is ignored because a name argument was provided');
|
|
650
|
-
continue;
|
|
651
|
-
}
|
|
652
704
|
let value = rawValue;
|
|
653
705
|
if (key === "_c") {
|
|
654
706
|
if (value instanceof Date) {
|
|
@@ -659,23 +711,14 @@ var Vertex = class _Vertex {
|
|
|
659
711
|
continue;
|
|
660
712
|
}
|
|
661
713
|
}
|
|
662
|
-
|
|
663
|
-
if (Array.isArray(value)) {
|
|
664
|
-
if (!value.every(isPrimitive)) {
|
|
665
|
-
skipped.push(rawKey);
|
|
666
|
-
continue;
|
|
667
|
-
}
|
|
668
|
-
} else if (typeof value === "object" && value !== null) {
|
|
669
|
-
skipped.push(rawKey);
|
|
670
|
-
continue;
|
|
671
|
-
} else if (!isPrimitive(value)) {
|
|
714
|
+
if (!isJsonValue2(value)) {
|
|
672
715
|
skipped.push(rawKey);
|
|
673
716
|
continue;
|
|
674
717
|
}
|
|
675
718
|
out[key] = value;
|
|
676
719
|
}
|
|
677
720
|
if (skipped.length > 0) {
|
|
678
|
-
|
|
721
|
+
throw new Error(`Unsupported property types for keys: ${skipped.join(", ")}`);
|
|
679
722
|
}
|
|
680
723
|
return Object.keys(out).length > 0 ? out : null;
|
|
681
724
|
}
|
|
@@ -866,6 +909,61 @@ var StateVector = class _StateVector {
|
|
|
866
909
|
}
|
|
867
910
|
};
|
|
868
911
|
|
|
912
|
+
// src/utils/deepEqual.ts
|
|
913
|
+
function deepEqual(a, b) {
|
|
914
|
+
if (a === b) return true;
|
|
915
|
+
if (a === null || b === null) return a === b;
|
|
916
|
+
const ta = typeof a;
|
|
917
|
+
const tb = typeof b;
|
|
918
|
+
if (ta !== tb) return false;
|
|
919
|
+
if (ta !== "object") return false;
|
|
920
|
+
const aIsArr = Array.isArray(a);
|
|
921
|
+
const bIsArr = Array.isArray(b);
|
|
922
|
+
if (aIsArr || bIsArr) {
|
|
923
|
+
if (!(aIsArr && bIsArr)) return false;
|
|
924
|
+
if (a.length !== b.length) return false;
|
|
925
|
+
for (let i = 0; i < a.length; i++) {
|
|
926
|
+
if (!deepEqual(a[i], b[i])) return false;
|
|
927
|
+
}
|
|
928
|
+
return true;
|
|
929
|
+
}
|
|
930
|
+
const aProto = Object.getPrototypeOf(a);
|
|
931
|
+
const bProto = Object.getPrototypeOf(b);
|
|
932
|
+
if (aProto !== Object.prototype && aProto !== null || bProto !== Object.prototype && bProto !== null) {
|
|
933
|
+
return false;
|
|
934
|
+
}
|
|
935
|
+
const aKeys = Object.keys(a);
|
|
936
|
+
const bKeys = Object.keys(b);
|
|
937
|
+
if (aKeys.length !== bKeys.length) return false;
|
|
938
|
+
for (const key of aKeys) {
|
|
939
|
+
if (!Object.prototype.hasOwnProperty.call(b, key)) return false;
|
|
940
|
+
if (!deepEqual(a[key], b[key])) return false;
|
|
941
|
+
}
|
|
942
|
+
return true;
|
|
943
|
+
}
|
|
944
|
+
|
|
945
|
+
// src/utils/isJsonValue.ts
|
|
946
|
+
function isJsonValue(v) {
|
|
947
|
+
if (v === void 0) return true;
|
|
948
|
+
if (v === null) return true;
|
|
949
|
+
const t = typeof v;
|
|
950
|
+
if (t === "string" || t === "number" || t === "boolean") return true;
|
|
951
|
+
if (t === "bigint" || t === "function" || t === "symbol") return false;
|
|
952
|
+
if (Array.isArray(v)) return v.every(isJsonValue);
|
|
953
|
+
if (t === "object") {
|
|
954
|
+
if (v instanceof Date) return false;
|
|
955
|
+
if (v instanceof Map || v instanceof Set || v instanceof RegExp) return false;
|
|
956
|
+
if (ArrayBuffer.isView(v)) return false;
|
|
957
|
+
const proto = Object.getPrototypeOf(v);
|
|
958
|
+
if (proto !== Object.prototype && proto !== null) return false;
|
|
959
|
+
for (const val of Object.values(v)) {
|
|
960
|
+
if (!isJsonValue(val)) return false;
|
|
961
|
+
}
|
|
962
|
+
return true;
|
|
963
|
+
}
|
|
964
|
+
return false;
|
|
965
|
+
}
|
|
966
|
+
|
|
869
967
|
// src/RepTree.ts
|
|
870
968
|
var _RepTree = class _RepTree {
|
|
871
969
|
/**
|
|
@@ -878,7 +976,6 @@ var _RepTree = class _RepTree {
|
|
|
878
976
|
this.setPropertyOps = [];
|
|
879
977
|
this.propertiesAndTheirOpIds = /* @__PURE__ */ new Map();
|
|
880
978
|
this.transientPropertiesAndTheirOpIds = /* @__PURE__ */ new Map();
|
|
881
|
-
// Observers for non-structural properties are not used
|
|
882
979
|
this.localOps = [];
|
|
883
980
|
this.pendingMovesWithMissingParent = /* @__PURE__ */ new Map();
|
|
884
981
|
this.pendingPropertiesWithMissingVertex = /* @__PURE__ */ new Map();
|
|
@@ -943,6 +1040,7 @@ var _RepTree = class _RepTree {
|
|
|
943
1040
|
getChildrenIds(vertexId) {
|
|
944
1041
|
return this.state.getChildrenIds(vertexId);
|
|
945
1042
|
}
|
|
1043
|
+
/** Returns the ancestors of the given vertex. The first element is the root vertex. */
|
|
946
1044
|
getAncestors(vertexId) {
|
|
947
1045
|
const ancestors = [];
|
|
948
1046
|
let currentVertex = this.state.getVertex(vertexId);
|
|
@@ -1032,6 +1130,9 @@ var _RepTree = class _RepTree {
|
|
|
1032
1130
|
this.moveVertex(vertexId, _RepTree.NULL_VERTEX_ID);
|
|
1033
1131
|
}
|
|
1034
1132
|
setTransientVertexProperty(vertexId, key, value) {
|
|
1133
|
+
if (!isJsonValue(value)) {
|
|
1134
|
+
throw new Error(`Unsupported transient property value for key "${key}"`);
|
|
1135
|
+
}
|
|
1035
1136
|
this.lamportClock++;
|
|
1036
1137
|
const op = newSetTransientVertexPropertyOp(this.lamportClock, this.peerId, vertexId, key, value);
|
|
1037
1138
|
this.localOps.push(op);
|
|
@@ -1057,6 +1158,29 @@ var _RepTree = class _RepTree {
|
|
|
1057
1158
|
vertex.clearAllTransientProperties();
|
|
1058
1159
|
}
|
|
1059
1160
|
setVertexProperty(vertexId, key, value) {
|
|
1161
|
+
const isJsonValue2 = (v) => {
|
|
1162
|
+
if (v === void 0) return true;
|
|
1163
|
+
if (v === null) return true;
|
|
1164
|
+
const t = typeof v;
|
|
1165
|
+
if (t === "string" || t === "number" || t === "boolean") return true;
|
|
1166
|
+
if (t === "bigint" || t === "function" || t === "symbol") return false;
|
|
1167
|
+
if (Array.isArray(v)) return v.every(isJsonValue2);
|
|
1168
|
+
if (t === "object") {
|
|
1169
|
+
if (v instanceof Date) return false;
|
|
1170
|
+
if (v instanceof Map || v instanceof Set || v instanceof RegExp) return false;
|
|
1171
|
+
if (ArrayBuffer.isView(v)) return false;
|
|
1172
|
+
const proto = Object.getPrototypeOf(v);
|
|
1173
|
+
if (proto !== Object.prototype && proto !== null) return false;
|
|
1174
|
+
for (const val of Object.values(v)) {
|
|
1175
|
+
if (!isJsonValue2(val)) return false;
|
|
1176
|
+
}
|
|
1177
|
+
return true;
|
|
1178
|
+
}
|
|
1179
|
+
return false;
|
|
1180
|
+
};
|
|
1181
|
+
if (!isJsonValue2(value)) {
|
|
1182
|
+
throw new Error(`Unsupported property value for key "${key}"`);
|
|
1183
|
+
}
|
|
1060
1184
|
this.lamportClock++;
|
|
1061
1185
|
const op = newSetVertexPropertyOp(this.lamportClock, this.peerId, vertexId, key, value);
|
|
1062
1186
|
this.localOps.push(op);
|
|
@@ -1218,12 +1342,8 @@ var _RepTree = class _RepTree {
|
|
|
1218
1342
|
}
|
|
1219
1343
|
for (const propA of propertiesA) {
|
|
1220
1344
|
const propB = propertiesB.find((p) => p.key === propA.key);
|
|
1221
|
-
if (!propB)
|
|
1222
|
-
|
|
1223
|
-
}
|
|
1224
|
-
if (propA.value !== propB.value) {
|
|
1225
|
-
return false;
|
|
1226
|
-
}
|
|
1345
|
+
if (!propB) return false;
|
|
1346
|
+
if (!deepEqual(propA.value, propB.value)) return false;
|
|
1227
1347
|
}
|
|
1228
1348
|
}
|
|
1229
1349
|
for (const childId of childrenA) {
|
|
@@ -1353,7 +1473,6 @@ var _RepTree = class _RepTree {
|
|
|
1353
1473
|
}
|
|
1354
1474
|
}
|
|
1355
1475
|
}
|
|
1356
|
-
// Non-LWW modify-property flow removed
|
|
1357
1476
|
applyOperation(op) {
|
|
1358
1477
|
if (isMoveVertexOp(op)) {
|
|
1359
1478
|
this.applyMove(op);
|