@rljson/rljson 0.0.65 → 0.0.67

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/dist/rljson.js CHANGED
@@ -1,11 +1,11 @@
1
1
  import { hip, hsh } from "@rljson/hash";
2
2
  import { exampleJsonObject, jsonValueTypes, jsonValueMatchesType, jsonValueType } from "@rljson/json";
3
3
  import { nanoid } from "nanoid";
4
- // @license
5
4
  class Route {
6
5
  constructor(_segments) {
7
6
  this._segments = _segments;
8
7
  }
8
+ _propertyKey;
9
9
  // .............................................................................
10
10
  /**
11
11
  * Creates a Route from a flat string representation.
@@ -17,7 +17,7 @@ class Route {
17
17
  const segments = [];
18
18
  for (const segmentFlat of segmentsFlat) {
19
19
  const [tableKey, refFlat] = segmentFlat.split("@");
20
- const ref = !!refFlat ? refFlat.split(":").length == 2 ? { [tableKey + "HistoryRef"]: refFlat } : { [tableKey + "Ref"]: refFlat } : {};
20
+ const ref = !!refFlat ? refFlat.split(":").length == 2 ? { [tableKey + "InsertHistoryRef"]: refFlat } : { [tableKey + "Ref"]: refFlat } : {};
21
21
  const segment = {
22
22
  tableKey,
23
23
  ...ref
@@ -59,6 +59,78 @@ class Route {
59
59
  }
60
60
  }
61
61
  // .............................................................................
62
+ /**
63
+ * Returns a new Route that is one level higher than the current route.
64
+ * If steps is provided, it returns a new Route that is 'steps' levels higher.
65
+ * @param steps - The number of levels to go higher (optional)
66
+ * @returns A new Route that is one level higher or 'steps' levels higher
67
+ */
68
+ upper(steps) {
69
+ if (steps === void 0) {
70
+ return new Route(this._segments.slice(0, -1));
71
+ } else {
72
+ if (steps < 1) {
73
+ throw new Error("Steps must be greater than 0");
74
+ }
75
+ if (steps >= this._segments.length) {
76
+ throw new Error("Cannot go upper than the top level");
77
+ }
78
+ return new Route(this._segments.slice(0, -steps));
79
+ }
80
+ }
81
+ // .............................................................................
82
+ /**
83
+ * Returns a new Route with the property key set to the last segment's table key.
84
+ * If the property key is already set, it returns the current route.
85
+ * @returns A new Route with the property key set
86
+ */
87
+ toRouteWithProperty() {
88
+ if (this.hasPropertyKey) return this;
89
+ const route = this.upper();
90
+ route.propertyKey = this.segment().tableKey;
91
+ return route;
92
+ }
93
+ // .............................................................................
94
+ /**
95
+ * Returns a new Route without the property key.
96
+ * If the property key is not set, it returns the current route.
97
+ * @returns A new Route without the property key
98
+ */
99
+ toRouteWithoutProperty() {
100
+ if (!this.hasPropertyKey) return this;
101
+ const route = new Route(this._segments);
102
+ route.propertyKey = void 0;
103
+ return route;
104
+ }
105
+ // .............................................................................
106
+ /**
107
+ * Checks if the current route includes another route.
108
+ * A route includes another route if all segments of the other route
109
+ * are present in the current route in the same order.
110
+ * @param other - The other route to check
111
+ * @returns True if the current route includes the other route, false otherwise
112
+ */
113
+ includes(other) {
114
+ if (other._segments.length < this._segments.length) {
115
+ return false;
116
+ }
117
+ for (let i = 0; i < this._segments.length; i++) {
118
+ const thisSegment = this._segments[i];
119
+ const otherSegment = other._segments[i];
120
+ if (thisSegment.tableKey !== otherSegment.tableKey) {
121
+ return false;
122
+ }
123
+ if (Route.segmentHasRef(thisSegment) && Route.segmentHasRef(otherSegment)) {
124
+ const thisRef = Route.segmentRef(thisSegment);
125
+ const otherRef = Route.segmentRef(otherSegment);
126
+ if (thisRef !== otherRef) {
127
+ return false;
128
+ }
129
+ }
130
+ }
131
+ return true;
132
+ }
133
+ // .............................................................................
62
134
  /**
63
135
  * Checks if the current route is the root route.
64
136
  * @returns True if the current route is the root route, false otherwise
@@ -72,6 +144,18 @@ class Route {
72
144
  * @returns The flat string representation of the route (e.g. "/a/b/c")
73
145
  */
74
146
  get flat() {
147
+ let flat = this.flatWithoutPropertyKey;
148
+ if (this.hasPropertyKey) {
149
+ flat += `/${this.propertyKey}`;
150
+ }
151
+ return flat;
152
+ }
153
+ // .............................................................................
154
+ /**
155
+ * Returns the flat string representation of the route without the property key.
156
+ * @returns The flat string representation of the route without the property key
157
+ */
158
+ get flatWithoutPropertyKey() {
75
159
  let flat = "";
76
160
  for (const segment of this._segments) {
77
161
  const tableKey = segment.tableKey;
@@ -81,6 +165,22 @@ class Route {
81
165
  return flat;
82
166
  }
83
167
  // .............................................................................
168
+ /**
169
+ * Returns the flat string representation of the route without any references.
170
+ * @returns The flat string representation of the route without any references
171
+ */
172
+ get flatWithoutRefs() {
173
+ let flat = "";
174
+ for (const segment of this._segments) {
175
+ const tableKey = segment.tableKey;
176
+ flat += `/${tableKey}`;
177
+ }
178
+ if (this.hasPropertyKey) {
179
+ flat += `/${this.propertyKey}`;
180
+ }
181
+ return flat;
182
+ }
183
+ // .............................................................................
84
184
  /**
85
185
  * Returns the segments of the route as an array of strings.
86
186
  * @returns The segments of the route as an array of strings
@@ -121,6 +221,57 @@ class Route {
121
221
  return this._segments.length > 0 && this._segments.every((s) => s.tableKey.length > 0);
122
222
  }
123
223
  // .............................................................................
224
+ /**
225
+ * Checks if the route has a property key.
226
+ * @returns True if the route has a property key, false otherwise
227
+ */
228
+ get hasPropertyKey() {
229
+ return this._propertyKey !== void 0;
230
+ }
231
+ // .............................................................................
232
+ /**
233
+ * Returns the property key of the route if it exists.
234
+ * @returns The property key of the route or undefined if it doesn't exist
235
+ */
236
+ get propertyKey() {
237
+ return this._propertyKey;
238
+ }
239
+ // .............................................................................
240
+ /**
241
+ * Sets the property key of the route.
242
+ * @param key - The property key to set
243
+ */
244
+ set propertyKey(key) {
245
+ this._propertyKey = key;
246
+ }
247
+ // .............................................................................
248
+ /**
249
+ * Checks if two routes are equal.
250
+ * @param other - The other route to compare with
251
+ * @returns True if the routes are equal, false otherwise
252
+ */
253
+ equals(other) {
254
+ return this.flat === other.flat;
255
+ }
256
+ // .............................................................................
257
+ /**
258
+ * Checks if two routes are equal without considering the property key.
259
+ * @param other - The other route to compare with
260
+ * @returns True if the routes are equal without considering the property key, false otherwise
261
+ */
262
+ equalsWithoutPropertyKey(other) {
263
+ return this.flatWithoutPropertyKey === other.flatWithoutPropertyKey;
264
+ }
265
+ // .............................................................................
266
+ /**
267
+ * Checks if two routes are equal without considering the references.
268
+ * @param other - The other route to compare with
269
+ * @returns True if the routes are equal without considering the references, false otherwise
270
+ */
271
+ equalsWithoutRefs(other) {
272
+ return this.flatWithoutRefs === other.flatWithoutRefs;
273
+ }
274
+ // .............................................................................
124
275
  /**
125
276
  * Returns the reference of a segment if it exists.
126
277
  * @param segment - The segment to get the reference from
@@ -131,7 +282,6 @@ class Route {
131
282
  const refKey = Object.keys(segment).find(
132
283
  (k) => k.endsWith("Ref") && k !== "tableKey"
133
284
  );
134
- /* v8 ignore next -- @preserve */
135
285
  if (refKey) {
136
286
  return segment[refKey];
137
287
  }
@@ -140,7 +290,7 @@ class Route {
140
290
  }
141
291
  // .............................................................................
142
292
  /**
143
- * Checks if a segment has any reference (either default or history).
293
+ * Checks if a segment has any reference (either default or insertHistory).
144
294
  * @param segment - The segment to check
145
295
  * @returns True if the segment has any reference, false otherwise
146
296
  */
@@ -149,24 +299,23 @@ class Route {
149
299
  }
150
300
  // .............................................................................
151
301
  /**
152
- * Checks if a segment has a default reference (i.e. not a history reference).
302
+ * Checks if a segment has a default reference (i.e. not a insertHistory reference).
153
303
  * @param segment - The segment to check
154
304
  * @returns True if the segment has a default reference, false otherwise
155
305
  */
156
306
  static segmentHasDefaultRef(segment) {
157
- return this.segmentHasRef(segment) && !this.segmentHasHistoryRef(segment);
307
+ return this.segmentHasRef(segment) && !this.segmentHasInsertHistoryRef(segment);
158
308
  }
159
309
  // .............................................................................
160
310
  /**
161
- * Checks if a segment has a history reference (i.e. an HistoryRef).
311
+ * Checks if a segment has a insertHistory reference (i.e. an InsertHistoryRef).
162
312
  * @param segment - The segment to check
163
- * @returns True if the segment has a history reference, false otherwise
313
+ * @returns True if the segment has a insertHistory reference, false otherwise
164
314
  */
165
- static segmentHasHistoryRef(segment) {
166
- return this.segmentHasRef(segment) && Object.keys(segment).some((k) => k.endsWith("HistoryRef"));
315
+ static segmentHasInsertHistoryRef(segment) {
316
+ return this.segmentHasRef(segment) && Object.keys(segment).some((k) => k.endsWith("InsertHistoryRef"));
167
317
  }
168
318
  }
169
- // @license
170
319
  const bakeryExample = () => {
171
320
  const nutritionalValues = hip({
172
321
  _type: "components",
@@ -294,8 +443,8 @@ const bakeryExample = () => {
294
443
  }
295
444
  ]
296
445
  });
297
- const ingredientsHistory = hip({
298
- _type: "history",
446
+ const ingredientsInsertHistory = hip({
447
+ _type: "insertHistory",
299
448
  _data: [
300
449
  {
301
450
  timeId: "de72:1759123957292",
@@ -324,13 +473,11 @@ const bakeryExample = () => {
324
473
  recipeIngredients,
325
474
  ingredients,
326
475
  nutritionalValues,
327
- ingredientsHistory
476
+ ingredientsInsertHistory
328
477
  };
329
478
  return result;
330
479
  };
331
- // @license
332
480
  const exampleBuffetsTable = () => bakeryExample().buffets;
333
- // @license
334
481
  const createCakeTableCfg = (cakeKey) => ({
335
482
  key: cakeKey,
336
483
  type: "cakes",
@@ -356,9 +503,7 @@ const createCakeTableCfg = (cakeKey) => ({
356
503
  isShared: true
357
504
  });
358
505
  const exampleCakesTable = () => bakeryExample().cakes;
359
- // @license
360
506
  const exampleComponentsTable = () => bakeryExample().nutritionalValues;
361
- // @license
362
507
  const createLayerTableCfg = (layerKey) => ({
363
508
  key: layerKey,
364
509
  type: "layers",
@@ -391,7 +536,6 @@ const createLayerTableCfg = (layerKey) => ({
391
536
  isShared: true
392
537
  });
393
538
  const exampleLayersTable = () => bakeryExample().recipeLayers;
394
- // @license
395
539
  const exampleRevision = () => ({
396
540
  table: "nutritionalValues",
397
541
  id: "flour",
@@ -399,9 +543,7 @@ const exampleRevision = () => ({
399
543
  successor: "IqeoWJjZQNlr-NVk2QT15B",
400
544
  timestamp: 1743558427
401
545
  });
402
- // @license
403
546
  const exampleSliceIdsTable = () => bakeryExample().slices;
404
- // @license
405
547
  class Example {
406
548
  static ok = {
407
549
  bakery: () => bakeryExample(),
@@ -535,26 +677,172 @@ class Example {
535
677
  };
536
678
  },
537
679
  singleRef: () => {
680
+ const tableCfgs = hip({
681
+ _type: "tableCfgs",
682
+ _data: [
683
+ {
684
+ key: "tableA",
685
+ type: "components",
686
+ isHead: false,
687
+ isRoot: false,
688
+ isShared: true,
689
+ columns: [
690
+ {
691
+ key: "_hash",
692
+ type: "string",
693
+ titleLong: "Hash",
694
+ titleShort: "Hash"
695
+ },
696
+ {
697
+ key: "propertyA",
698
+ type: "string",
699
+ titleLong: "Key",
700
+ titleShort: "Key"
701
+ }
702
+ ],
703
+ _hash: ""
704
+ },
705
+ {
706
+ key: "tableB",
707
+ type: "components",
708
+ isHead: false,
709
+ isRoot: false,
710
+ isShared: true,
711
+ columns: [
712
+ {
713
+ key: "_hash",
714
+ type: "string",
715
+ titleLong: "Hash",
716
+ titleShort: "Hash"
717
+ },
718
+ {
719
+ key: "propertyAFromTableA",
720
+ type: "string",
721
+ titleLong: "Table A Reference",
722
+ titleShort: "TableARef",
723
+ ref: {
724
+ tableKey: "tableA",
725
+ columnKey: "propertyA"
726
+ }
727
+ }
728
+ ],
729
+ _hash: ""
730
+ }
731
+ ]
732
+ });
733
+ const tableA = hip({
734
+ _type: "components",
735
+ _tableCfg: tableCfgs._data[0]._hash,
736
+ _data: [
737
+ {
738
+ propertyA: "a0"
739
+ },
740
+ {
741
+ propertyA: "a1"
742
+ }
743
+ ],
744
+ _hash: ""
745
+ });
746
+ const tableB = hip({
747
+ _type: "components",
748
+ _tableCfg: tableCfgs._data[1]._hash,
749
+ _data: [
750
+ {
751
+ propertyAFromTableA: tableA._data[0]._hash
752
+ }
753
+ ],
754
+ _hash: ""
755
+ });
538
756
  return {
539
- tableA: {
540
- _type: "components",
541
- _data: [
542
- {
543
- keyA0: "a0"
544
- },
545
- {
546
- keyA1: "a1"
547
- }
548
- ]
549
- },
550
- tableB: {
551
- _type: "components",
552
- _data: [
553
- {
554
- tableARef: "KFQrf4mEz0UPmUaFHwH4T6"
555
- }
556
- ]
557
- }
757
+ tableCfgs,
758
+ tableA,
759
+ tableB
760
+ };
761
+ },
762
+ multiRef: () => {
763
+ const tableCfgs = hip({
764
+ _type: "tableCfgs",
765
+ _data: [
766
+ {
767
+ key: "tableA",
768
+ type: "components",
769
+ isHead: false,
770
+ isRoot: false,
771
+ isShared: true,
772
+ columns: [
773
+ {
774
+ key: "_hash",
775
+ type: "string",
776
+ titleLong: "Hash",
777
+ titleShort: "Hash"
778
+ },
779
+ {
780
+ key: "propertyA",
781
+ type: "string",
782
+ titleLong: "Key",
783
+ titleShort: "Key"
784
+ }
785
+ ],
786
+ _hash: ""
787
+ },
788
+ {
789
+ key: "tableB",
790
+ type: "components",
791
+ isHead: false,
792
+ isRoot: false,
793
+ isShared: true,
794
+ columns: [
795
+ {
796
+ key: "_hash",
797
+ type: "string",
798
+ titleLong: "Hash",
799
+ titleShort: "Hash"
800
+ },
801
+ {
802
+ key: "propertyAFromTableA",
803
+ type: "jsonValue",
804
+ titleLong: "Table A Reference",
805
+ titleShort: "TableARef",
806
+ ref: {
807
+ tableKey: "tableA",
808
+ columnKey: "propertyA"
809
+ }
810
+ }
811
+ ],
812
+ _hash: ""
813
+ }
814
+ ]
815
+ });
816
+ const tableA = hip({
817
+ _type: "components",
818
+ _tableCfg: tableCfgs._data[0]._hash,
819
+ _data: [
820
+ {
821
+ propertyA: "a0"
822
+ },
823
+ {
824
+ propertyA: "a1"
825
+ }
826
+ ],
827
+ _hash: ""
828
+ });
829
+ const tableB = hip({
830
+ _type: "components",
831
+ _tableCfg: tableCfgs._data[1]._hash,
832
+ _data: [
833
+ {
834
+ propertyAFromTableA: [
835
+ tableA._data[0]._hash,
836
+ tableA._data[1]._hash
837
+ ]
838
+ }
839
+ ],
840
+ _hash: ""
841
+ });
842
+ return {
843
+ tableCfgs,
844
+ tableA,
845
+ tableB
558
846
  };
559
847
  },
560
848
  singleSliceIdRef: () => {
@@ -597,89 +885,6 @@ class Example {
597
885
  }
598
886
  };
599
887
  },
600
- singleNamedRef: () => {
601
- return {
602
- tableA: {
603
- _type: "components",
604
- _data: [
605
- {
606
- keyA0: "a0"
607
- },
608
- {
609
- keyA1: "a1"
610
- }
611
- ]
612
- },
613
- tableB: {
614
- _type: "components",
615
- _data: [
616
- {
617
- namedRef: { component: "tableA", ref: "KFQrf4mEz0UPmUaFHwH4T6" }
618
- }
619
- ]
620
- }
621
- };
622
- },
623
- multiRef: () => {
624
- return {
625
- tableA: {
626
- _type: "components",
627
- _data: [
628
- {
629
- keyA0: "a0"
630
- },
631
- {
632
- keyA1: "a1"
633
- }
634
- ]
635
- },
636
- tableB: {
637
- _type: "components",
638
- _data: [
639
- {
640
- tableARef: ["KFQrf4mEz0UPmUaFHwH4T6", "YPw-pxhqaUOWRFGramr4B1"]
641
- }
642
- ]
643
- }
644
- };
645
- },
646
- multiMixedRef: () => {
647
- return {
648
- tableA: {
649
- _type: "components",
650
- _data: [
651
- {
652
- keyA0: "a0"
653
- },
654
- {
655
- keyA1: "a1"
656
- }
657
- ]
658
- },
659
- tableB: {
660
- _type: "components",
661
- _data: [
662
- {
663
- keyB0: "b0"
664
- },
665
- {
666
- keyB1: "b1"
667
- }
668
- ]
669
- },
670
- tableC: {
671
- _type: "components",
672
- _data: [
673
- {
674
- tableRef: [
675
- { component: "tableA", ref: "KFQrf4mEz0UPmUaFHwH4T6" },
676
- { component: "tableB", ref: "dXhIygNwNMVPEqFbsFJkn6" }
677
- ]
678
- }
679
- ]
680
- }
681
- };
682
- },
683
888
  complete: () => {
684
889
  const sliceIds = hip({
685
890
  _type: "sliceIds",
@@ -777,87 +982,220 @@ class Example {
777
982
  };
778
983
  },
779
984
  missingRef: () => {
985
+ const tableCfgs = hip({
986
+ _type: "tableCfgs",
987
+ _data: [
988
+ {
989
+ key: "tableA",
990
+ type: "components",
991
+ isHead: false,
992
+ isRoot: false,
993
+ isShared: true,
994
+ columns: [
995
+ {
996
+ key: "_hash",
997
+ type: "string",
998
+ titleLong: "Hash",
999
+ titleShort: "Hash"
1000
+ },
1001
+ {
1002
+ key: "propertyA",
1003
+ type: "string",
1004
+ titleLong: "Key",
1005
+ titleShort: "Key"
1006
+ }
1007
+ ],
1008
+ _hash: ""
1009
+ },
1010
+ {
1011
+ key: "tableB",
1012
+ type: "components",
1013
+ isHead: false,
1014
+ isRoot: false,
1015
+ isShared: true,
1016
+ columns: [
1017
+ {
1018
+ key: "_hash",
1019
+ type: "string",
1020
+ titleLong: "Hash",
1021
+ titleShort: "Hash"
1022
+ },
1023
+ {
1024
+ key: "propertyAFromTableA",
1025
+ type: "jsonValue",
1026
+ titleLong: "Table A Reference",
1027
+ titleShort: "TableARef",
1028
+ ref: {
1029
+ tableKey: "tableA",
1030
+ columnKey: "propertyA"
1031
+ }
1032
+ }
1033
+ ],
1034
+ _hash: ""
1035
+ }
1036
+ ]
1037
+ });
1038
+ const tableA = hip({
1039
+ _type: "components",
1040
+ _tableCfg: tableCfgs._data[0]._hash,
1041
+ _data: [
1042
+ {
1043
+ propertyA: "a0"
1044
+ },
1045
+ {
1046
+ propertyA: "a1"
1047
+ }
1048
+ ],
1049
+ _hash: ""
1050
+ });
1051
+ const tableB = hip({
1052
+ _type: "components",
1053
+ _tableCfg: tableCfgs._data[1]._hash,
1054
+ _data: [
1055
+ {
1056
+ propertyAFromTableA: "MISSINGREF"
1057
+ // Missing reference
1058
+ }
1059
+ ],
1060
+ _hash: ""
1061
+ });
780
1062
  return {
781
- tableA: {
782
- _type: "components",
783
- _data: [
784
- {
785
- keyA0: "a0"
786
- },
787
- {
788
- keyA1: "a1"
789
- }
790
- ]
791
- },
792
- tableB: {
793
- _type: "components",
794
- _data: [
795
- {
796
- tableARef: "MISSINGREF"
797
- // MISSINGREF does not exist in tableA
798
- }
799
- ]
800
- }
801
- };
802
- },
803
- missingNamedRef: () => {
804
- return {
805
- tableA: {
806
- _type: "components",
807
- _data: [
808
- {
809
- keyA0: "a0"
810
- },
811
- {
812
- keyA1: "a1"
813
- }
814
- ]
815
- },
816
- tableB: {
817
- _type: "components",
818
- _data: [
819
- {
820
- namedRef: { component: "tableA", ref: "MISSINGREF" }
821
- // MISSINGREF does not exist in tableA
822
- }
823
- ]
824
- }
1063
+ tableCfgs,
1064
+ tableA,
1065
+ tableB
825
1066
  };
826
1067
  },
827
1068
  missingMultiRef: () => {
1069
+ const tableCfgs = hip({
1070
+ _type: "tableCfgs",
1071
+ _data: [
1072
+ {
1073
+ key: "tableA",
1074
+ type: "components",
1075
+ isHead: false,
1076
+ isRoot: false,
1077
+ isShared: true,
1078
+ columns: [
1079
+ {
1080
+ key: "_hash",
1081
+ type: "string",
1082
+ titleLong: "Hash",
1083
+ titleShort: "Hash"
1084
+ },
1085
+ {
1086
+ key: "propertyA",
1087
+ type: "string",
1088
+ titleLong: "Key",
1089
+ titleShort: "Key"
1090
+ }
1091
+ ],
1092
+ _hash: ""
1093
+ },
1094
+ {
1095
+ key: "tableB",
1096
+ type: "components",
1097
+ isHead: false,
1098
+ isRoot: false,
1099
+ isShared: true,
1100
+ columns: [
1101
+ {
1102
+ key: "_hash",
1103
+ type: "string",
1104
+ titleLong: "Hash",
1105
+ titleShort: "Hash"
1106
+ },
1107
+ {
1108
+ key: "propertyAFromTableA",
1109
+ type: "jsonValue",
1110
+ titleLong: "Table A Reference",
1111
+ titleShort: "TableARef",
1112
+ ref: {
1113
+ tableKey: "tableA",
1114
+ columnKey: "propertyA"
1115
+ }
1116
+ }
1117
+ ],
1118
+ _hash: ""
1119
+ }
1120
+ ]
1121
+ });
1122
+ const tableA = hip({
1123
+ _type: "components",
1124
+ _tableCfg: tableCfgs._data[0]._hash,
1125
+ _data: [
1126
+ {
1127
+ propertyA: "a0"
1128
+ },
1129
+ {
1130
+ propertyA: "a1"
1131
+ }
1132
+ ],
1133
+ _hash: ""
1134
+ });
1135
+ const tableB = hip({
1136
+ _type: "components",
1137
+ _tableCfg: tableCfgs._data[1]._hash,
1138
+ _data: [
1139
+ {
1140
+ propertyAFromTableA: [tableA._data[0]._hash, "MISSINGREF"]
1141
+ // Missing reference
1142
+ }
1143
+ ],
1144
+ _hash: ""
1145
+ });
828
1146
  return {
829
- tableA: {
830
- _type: "components",
831
- _data: [
832
- {
833
- keyA0: "a0"
834
- },
835
- {
836
- keyA1: "a1"
837
- }
838
- ]
839
- },
840
- tableB: {
841
- _type: "components",
842
- _data: [
843
- {
844
- tableARef: ["KFQrf4mEz0UPmUaFHwH4T6", "MISSING"]
845
- }
846
- ]
847
- }
1147
+ tableCfgs,
1148
+ tableA,
1149
+ tableB
848
1150
  };
849
1151
  },
850
1152
  missingReferencedTable: () => {
1153
+ const tableCfgs = hip({
1154
+ _type: "tableCfgs",
1155
+ _data: [
1156
+ {
1157
+ key: "tableB",
1158
+ type: "components",
1159
+ isHead: false,
1160
+ isRoot: false,
1161
+ isShared: true,
1162
+ columns: [
1163
+ {
1164
+ key: "_hash",
1165
+ type: "string",
1166
+ titleLong: "Hash",
1167
+ titleShort: "Hash"
1168
+ },
1169
+ {
1170
+ key: "propertyAFromTableA",
1171
+ type: "jsonValue",
1172
+ titleLong: "Table A Reference",
1173
+ titleShort: "TableARef",
1174
+ ref: {
1175
+ tableKey: "tableA",
1176
+ // Referenced table missing
1177
+ columnKey: "propertyA"
1178
+ }
1179
+ }
1180
+ ],
1181
+ _hash: ""
1182
+ }
1183
+ ]
1184
+ });
1185
+ const tableB = hip({
1186
+ _type: "components",
1187
+ _tableCfg: tableCfgs._data[0]._hash,
1188
+ _data: [
1189
+ {
1190
+ propertyAFromTableA: "MISSINGREF"
1191
+ // Missing reference
1192
+ }
1193
+ ],
1194
+ _hash: ""
1195
+ });
851
1196
  return {
852
- tableB: {
853
- _type: "components",
854
- _data: [
855
- {
856
- tableARef: "MISSINGREF"
857
- // tableA is missing
858
- }
859
- ]
860
- }
1197
+ tableCfgs,
1198
+ tableB
861
1199
  };
862
1200
  },
863
1201
  missingSliceId: () => {
@@ -992,7 +1330,6 @@ class Example {
992
1330
  }
993
1331
  };
994
1332
  }
995
- // @license
996
1333
  const throwOnInvalidTableCfg = (tableCfg) => {
997
1334
  if (tableCfg.columns.length < 2) {
998
1335
  throw new Error(
@@ -1099,46 +1436,7 @@ const exampleTableCfg = (tableCfg = void 0) => {
1099
1436
  isShared: false
1100
1437
  };
1101
1438
  };
1102
- // @license
1103
- const createHistoryTableCfg = (tableCfg) => ({
1104
- key: `${tableCfg.key}History`,
1105
- type: "history",
1106
- columns: [
1107
- { key: "_hash", type: "string", titleLong: "Hash", titleShort: "Hash" },
1108
- {
1109
- key: "timeId",
1110
- type: "string",
1111
- titleLong: "Time ID",
1112
- titleShort: "Time ID"
1113
- },
1114
- {
1115
- key: `${tableCfg.key}Ref`,
1116
- type: "string",
1117
- titleLong: "Reference",
1118
- titleShort: "Ref"
1119
- },
1120
- { key: "route", type: "string", titleLong: "Route", titleShort: "Route" },
1121
- {
1122
- key: "origin",
1123
- type: "string",
1124
- titleLong: "Origin",
1125
- titleShort: "Origin"
1126
- },
1127
- {
1128
- key: "previous",
1129
- type: "jsonArray",
1130
- titleLong: "Previous",
1131
- titleShort: "Previous"
1132
- }
1133
- ],
1134
- isHead: false,
1135
- isRoot: false,
1136
- isShared: false
1137
- });
1138
- const exampleHistoryTable = () => bakeryExample().ingredientsHistory;
1139
- // @license
1140
1439
  const objectDepth = (o) => Object(o) === o ? 1 + Math.max(-1, ...Object.values(o).map(objectDepth)) : 0;
1141
- // @license
1142
1440
  class InsertValidator {
1143
1441
  constructor(_insert) {
1144
1442
  this._insert = _insert;
@@ -1192,9 +1490,9 @@ class InsertValidator {
1192
1490
  if (route.isValid) {
1193
1491
  const routeDepth = route.segments.length;
1194
1492
  const valueDepth = objectDepth(this._insert.value);
1195
- if (routeDepth !== valueDepth) {
1493
+ if (routeDepth > valueDepth) {
1196
1494
  this.errors.dataRouteMismatch = {
1197
- error: "Insert route depth does not match value depth. Route depth must match the depth of the value object.",
1495
+ error: "Insert route depth does not match value depth. Route depth must be lower than the depth of the value object.",
1198
1496
  route: this._insert.route,
1199
1497
  routeDepth,
1200
1498
  valueDepth
@@ -1244,13 +1542,47 @@ class InsertValidator {
1244
1542
  const validateInsert = (insert) => {
1245
1543
  return InsertValidator.create(insert).validate();
1246
1544
  };
1247
- // @license
1248
1545
  const exampleInsert = () => ({
1249
1546
  route: "a/b/c",
1250
1547
  command: "add",
1251
1548
  value: { x: { y: { z: true } } }
1252
1549
  });
1253
- // @license
1550
+ const createInsertHistoryTableCfg = (tableCfg) => ({
1551
+ key: `${tableCfg.key}InsertHistory`,
1552
+ type: "insertHistory",
1553
+ columns: [
1554
+ { key: "_hash", type: "string", titleLong: "Hash", titleShort: "Hash" },
1555
+ {
1556
+ key: "timeId",
1557
+ type: "string",
1558
+ titleLong: "Time ID",
1559
+ titleShort: "Time ID"
1560
+ },
1561
+ {
1562
+ key: `${tableCfg.key}Ref`,
1563
+ type: "string",
1564
+ titleLong: "Reference",
1565
+ titleShort: "Ref"
1566
+ },
1567
+ { key: "route", type: "string", titleLong: "Route", titleShort: "Route" },
1568
+ {
1569
+ key: "origin",
1570
+ type: "string",
1571
+ titleLong: "Origin",
1572
+ titleShort: "Origin"
1573
+ },
1574
+ {
1575
+ key: "previous",
1576
+ type: "jsonArray",
1577
+ titleLong: "Previous",
1578
+ titleShort: "Previous"
1579
+ }
1580
+ ],
1581
+ isHead: false,
1582
+ isRoot: false,
1583
+ isShared: false
1584
+ });
1585
+ const exampleInsertHistoryTable = () => bakeryExample().ingredientsInsertHistory;
1254
1586
  const reservedFieldNames = ["_data"];
1255
1587
  const reservedTableKeys = [
1256
1588
  "_hash",
@@ -1290,7 +1622,6 @@ const iterateTables = async (rljson, callback) => {
1290
1622
  throw errors;
1291
1623
  }
1292
1624
  };
1293
- // @license
1294
1625
  const removeDuplicates = (rljson) => {
1295
1626
  const result = {};
1296
1627
  for (const key in rljson) {
@@ -1304,7 +1635,6 @@ const removeDuplicates = (rljson) => {
1304
1635
  }
1305
1636
  return hip(result, { throwOnWrongHashes: false, updateExistingHashes: true });
1306
1637
  };
1307
- // @license
1308
1638
  const timeId = () => {
1309
1639
  return nanoid(4) + ":" + Date.now();
1310
1640
  };
@@ -1314,7 +1644,6 @@ const isTimeId = (id) => {
1314
1644
  if (isNaN(Number(parts[1]))) return false;
1315
1645
  return parts[0].length === 4;
1316
1646
  };
1317
- // @license
1318
1647
  const contentTypes = [
1319
1648
  "buffets",
1320
1649
  "cakes",
@@ -1323,7 +1652,7 @@ const contentTypes = [
1323
1652
  "components",
1324
1653
  "revisions",
1325
1654
  "tableCfgs",
1326
- "history"
1655
+ "insertHistory"
1327
1656
  ];
1328
1657
  const exampleTypedefs = () => {
1329
1658
  return {
@@ -1333,7 +1662,6 @@ const exampleTypedefs = () => {
1333
1662
  contentType: "layers"
1334
1663
  };
1335
1664
  };
1336
- // @license
1337
1665
  const rljsonIndexed = (rljson) => {
1338
1666
  const result = {};
1339
1667
  for (const key in rljson) {
@@ -1356,7 +1684,6 @@ const rljsonIndexed = (rljson) => {
1356
1684
  }
1357
1685
  return result;
1358
1686
  };
1359
- // @license
1360
1687
  class BaseValidator {
1361
1688
  name = "base";
1362
1689
  async validate(rljson) {
@@ -1400,7 +1727,6 @@ class _BaseValidator {
1400
1727
  () => this._rootOrHeadTableHasNoIdColumn(),
1401
1728
  // Check references
1402
1729
  () => this._refsNotFound(),
1403
- () => this._sliceIdRefsNotFound(),
1404
1730
  // Check layers
1405
1731
  () => this._layerBasesNotFound(),
1406
1732
  () => this._layerSliceIdsTableNotFound(),
@@ -1769,36 +2095,44 @@ class _BaseValidator {
1769
2095
  _refsNotFound() {
1770
2096
  const missingRefs = [];
1771
2097
  iterateTablesSync(this.rljson, (tableKey, table) => {
1772
- const tableData = table._data;
1773
- for (const item of tableData) {
1774
- for (const key of Object.keys(item)) {
1775
- if (key.endsWith("Ref")) {
1776
- const targetItemRefs = Array.isArray(item[key]) ? item[key] : [item[key]];
1777
- for (const targetItemRef of targetItemRefs) {
1778
- const targetTableKey = typeof targetItemRef !== "string" ? targetItemRef.component : key.substring(0, key.length - 3);
1779
- const targetItemHash = typeof targetItemRef !== "string" ? targetItemRef.ref : targetItemRef;
1780
- const itemHash = item._hash;
2098
+ if (tableKey === "tableCfgs") return;
2099
+ const tableCfgRef = table._tableCfg;
2100
+ if (!tableCfgRef) return;
2101
+ const tableCfg = this.rljsonIndexed.tableCfgs._data[tableCfgRef];
2102
+ if (!tableCfg) return;
2103
+ const tableRows = table._data;
2104
+ for (const row of tableRows) {
2105
+ for (const columnKey of Object.keys(row)) {
2106
+ if (columnKey.startsWith("_")) continue;
2107
+ const columnCfg = tableCfg.columns.find(
2108
+ (col) => col.key === columnKey
2109
+ );
2110
+ if (columnCfg === void 0) continue;
2111
+ if (columnCfg.ref && columnCfg.ref.tableKey) {
2112
+ const targetTableKey = columnCfg.ref.tableKey;
2113
+ const targetRefs = Array.isArray(row[columnKey]) ? row[columnKey] : [row[columnKey]];
2114
+ for (const targetRef of targetRefs) {
1781
2115
  if (this.tableKeys.indexOf(targetTableKey) === -1) {
1782
2116
  missingRefs.push({
1783
2117
  error: `Target table "${targetTableKey}" not found.`,
1784
2118
  sourceTable: tableKey,
1785
- sourceKey: key,
1786
- sourceItemHash: itemHash,
1787
- targetItemHash,
2119
+ sourceKey: columnKey,
2120
+ sourceItemHash: row._hash,
2121
+ targetItemHash: targetRef,
1788
2122
  targetTable: targetTableKey
1789
2123
  });
1790
2124
  continue;
1791
2125
  }
1792
2126
  const targetTableIndexed = this.rljsonIndexed[targetTableKey];
1793
- const referencedItem = targetTableIndexed._data[targetItemHash];
2127
+ const referencedItem = targetTableIndexed._data[targetRef];
1794
2128
  if (referencedItem === void 0) {
1795
2129
  missingRefs.push({
1796
2130
  sourceTable: tableKey,
1797
- sourceItemHash: itemHash,
1798
- sourceKey: key,
1799
- targetItemHash,
2131
+ sourceItemHash: row._hash,
2132
+ sourceKey: columnKey,
2133
+ targetItemHash: targetRef,
1800
2134
  targetTable: targetTableKey,
1801
- error: `Table "${targetTableKey}" has no item with hash "${targetItemHash}"`
2135
+ error: `Table "${targetTableKey}" has no item with hash "${targetRef}"`
1802
2136
  });
1803
2137
  }
1804
2138
  }
@@ -1814,49 +2148,6 @@ class _BaseValidator {
1814
2148
  }
1815
2149
  }
1816
2150
  // ...........................................................................
1817
- _sliceIdRefsNotFound() {
1818
- const missingSliceIdRefs = [];
1819
- iterateTablesSync(this.rljson, (tableKey, table) => {
1820
- const tableData = table._data;
1821
- for (const item of tableData) {
1822
- for (const key of Object.keys(item)) {
1823
- if (key.endsWith("SliceId")) {
1824
- const targetSliceIds = Array.isArray(item[key]) ? item[key] : [item[key]];
1825
- for (const targetSliceId of targetSliceIds) {
1826
- if (this.tableKeys.indexOf(key) === -1) {
1827
- missingSliceIdRefs.push({
1828
- sourceTable: tableKey,
1829
- targetSliceId,
1830
- targetTable: key,
1831
- error: `Target table "${targetSliceId}" not found.`
1832
- });
1833
- continue;
1834
- }
1835
- const targetSliceIdsTable = this.rljson[key];
1836
- const targetSliceIds2 = targetSliceIdsTable._data.flatMap(
1837
- (d) => [...d.add, ...d.remove ?? []]
1838
- );
1839
- if (targetSliceIds2.indexOf(targetSliceId) === -1) {
1840
- missingSliceIdRefs.push({
1841
- sourceTable: tableKey,
1842
- targetSliceId,
1843
- targetTable: key,
1844
- error: `Table "${key}" has no sliceId "${targetSliceId}"`
1845
- });
1846
- }
1847
- }
1848
- }
1849
- }
1850
- }
1851
- });
1852
- if (missingSliceIdRefs.length > 0) {
1853
- this.errors.refsNotFound = {
1854
- error: "Broken references",
1855
- missingRefs: missingSliceIdRefs
1856
- };
1857
- }
1858
- }
1859
- // ...........................................................................
1860
2151
  _layerBasesNotFound() {
1861
2152
  const brokenLayers = [];
1862
2153
  iterateTablesSync(this.rljson, (tableKey, table) => {
@@ -2169,7 +2460,6 @@ class _BaseValidator {
2169
2460
  }
2170
2461
  }
2171
2462
  const isValidFieldName = (fieldName) => BaseValidator.isValidFieldName(fieldName);
2172
- // @license
2173
2463
  class Validate {
2174
2464
  addValidator(validator) {
2175
2465
  this._validators.push(validator);
@@ -2214,13 +2504,13 @@ export {
2214
2504
  bakeryExample,
2215
2505
  contentTypes,
2216
2506
  createCakeTableCfg,
2217
- createHistoryTableCfg,
2507
+ createInsertHistoryTableCfg,
2218
2508
  createLayerTableCfg,
2219
2509
  exampleBuffetsTable,
2220
2510
  exampleCakesTable,
2221
2511
  exampleComponentsTable,
2222
- exampleHistoryTable,
2223
2512
  exampleInsert,
2513
+ exampleInsertHistoryTable,
2224
2514
  exampleLayersTable,
2225
2515
  exampleRevision,
2226
2516
  exampleRljson,