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 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
- get path() {
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, name);
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, explicitName) {
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
- const isPrimitive = (v) => typeof v === "string" || typeof v === "number" || typeof v === "boolean";
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
- console.warn(`Some fields were skipped due to unsupported types: ${skipped.join(", ")}`);
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
- return false;
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);