@rljson/rljson 0.0.14 → 0.0.16
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/example.d.ts +13 -8
- package/dist/index.d.ts +4 -0
- package/dist/rljson.d.ts +18 -4
- package/dist/rljson.js +828 -97
- package/dist/src/example.ts +135 -57
- package/package.json +2 -2
package/dist/example.d.ts
CHANGED
|
@@ -10,16 +10,21 @@ export declare class Example {
|
|
|
10
10
|
complete: () => Rljson;
|
|
11
11
|
};
|
|
12
12
|
static readonly broken: {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
13
|
+
base: {
|
|
14
|
+
brokenTableName: () => {
|
|
15
|
+
brok$en: {
|
|
16
|
+
_type: string;
|
|
17
|
+
_data: never[];
|
|
18
|
+
};
|
|
17
19
|
};
|
|
20
|
+
missingData: () => Rljson;
|
|
21
|
+
dataNotBeingAnArray: () => Rljson;
|
|
22
|
+
missingRef: () => Rljson;
|
|
23
|
+
missingReferencedTable: () => Rljson;
|
|
24
|
+
};
|
|
25
|
+
tableCfg: {
|
|
26
|
+
wrongType: () => import('@rljson/hash').Hashed<Rljson>;
|
|
18
27
|
};
|
|
19
|
-
missingData: () => Rljson;
|
|
20
|
-
dataNotBeingAnArray: () => Rljson;
|
|
21
|
-
missingRef: () => Rljson;
|
|
22
|
-
missingReferencedTable: () => Rljson;
|
|
23
28
|
collections: {
|
|
24
29
|
missingBase: () => Rljson;
|
|
25
30
|
missingIdSet: () => Rljson;
|
package/dist/index.d.ts
CHANGED
|
@@ -3,7 +3,11 @@ export * from './content/cake.ts';
|
|
|
3
3
|
export * from './content/collection.ts';
|
|
4
4
|
export * from './content/id-set.ts';
|
|
5
5
|
export * from './content/properties.ts';
|
|
6
|
+
export * from './content/table-cfg.ts';
|
|
6
7
|
export * from './example.ts';
|
|
8
|
+
export * from './example/bakery-example.ts';
|
|
7
9
|
export * from './rljson-indexed.ts';
|
|
8
10
|
export * from './rljson.ts';
|
|
9
11
|
export * from './typedefs.ts';
|
|
12
|
+
export * from './validate/base-validator.ts';
|
|
13
|
+
export * from './validate/validate.ts';
|
package/dist/rljson.d.ts
CHANGED
|
@@ -4,6 +4,7 @@ import { CakesTable } from './content/cake.ts';
|
|
|
4
4
|
import { CollectionsTable } from './content/collection.ts';
|
|
5
5
|
import { IdSetsTable } from './content/id-set.ts';
|
|
6
6
|
import { PropertiesTable } from './content/properties.ts';
|
|
7
|
+
import { TableCfgRef, TablesCfgTable } from './content/table-cfg.ts';
|
|
7
8
|
import { ContentType, Ref, TableName } from './typedefs.ts';
|
|
8
9
|
export declare const reservedFieldNames: string[];
|
|
9
10
|
export declare const reservedTableNames: string[];
|
|
@@ -19,15 +20,26 @@ export interface Rljson extends Json {
|
|
|
19
20
|
* Rljson set with private fields
|
|
20
21
|
*/
|
|
21
22
|
export type RljsonPrivate = {
|
|
23
|
+
/**
|
|
24
|
+
* The hash of the Rljson object
|
|
25
|
+
*/
|
|
26
|
+
_hash: string;
|
|
22
27
|
/**
|
|
23
28
|
* Contains id sets used accross the Rljson object
|
|
24
29
|
*/
|
|
25
|
-
|
|
30
|
+
_idSet?: IdSetsTable;
|
|
31
|
+
/**
|
|
32
|
+
* References that are not part of the Rljson object
|
|
33
|
+
*/
|
|
34
|
+
_externalRefs?: Ref[];
|
|
35
|
+
/**
|
|
36
|
+
* Referenced tables that are not part of the Rljson object
|
|
37
|
+
*/
|
|
38
|
+
_externalTables?: Ref[];
|
|
26
39
|
/**
|
|
27
|
-
*
|
|
28
|
-
* validation does not throw an error.
|
|
40
|
+
* Column configurations used accross the Rljson object
|
|
29
41
|
*/
|
|
30
|
-
|
|
42
|
+
_tableCfgs?: TablesCfgTable;
|
|
31
43
|
};
|
|
32
44
|
/** An example rljson object */
|
|
33
45
|
export declare const exampleRljson: () => Rljson;
|
|
@@ -37,6 +49,8 @@ export interface RljsonTable<Data extends Json, Type extends ContentType> extend
|
|
|
37
49
|
_data: Data[];
|
|
38
50
|
/** The type of the table. If not set, the type is "properties" */
|
|
39
51
|
_type: Type;
|
|
52
|
+
/** The columns configuration of the table */
|
|
53
|
+
_tableCfg?: TableCfgRef;
|
|
40
54
|
}
|
|
41
55
|
/**
|
|
42
56
|
* Iterates over all tables of an Rljson object.
|
package/dist/rljson.js
CHANGED
|
@@ -2,99 +2,92 @@ var __defProp = Object.defineProperty;
|
|
|
2
2
|
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
3
3
|
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
4
4
|
import { hip, hsh } from "@rljson/hash";
|
|
5
|
-
import { exampleJsonObject } from "@rljson/json";
|
|
5
|
+
import { exampleJsonObject, jsonValueTypes, jsonValueMatchesType } from "@rljson/json";
|
|
6
6
|
// @license
|
|
7
7
|
const bakeryExample = () => {
|
|
8
|
-
|
|
9
|
-
// Every rljson object has a list of id-sets other objects can refer to
|
|
8
|
+
const result = {
|
|
10
9
|
_idSets: {
|
|
11
10
|
_type: "idSets",
|
|
12
11
|
_data: [
|
|
13
12
|
{
|
|
14
13
|
add: ["slice0", "slice1"],
|
|
15
|
-
_hash: "
|
|
14
|
+
_hash: "Ko990SJfgPJvNGxC63CRf7"
|
|
16
15
|
}
|
|
17
|
-
]
|
|
16
|
+
],
|
|
17
|
+
_hash: "NiojsJvZ7iEU7WeMWttzyJ"
|
|
18
18
|
},
|
|
19
|
-
// A bakery offers a variety of buffets containing different pastries
|
|
20
19
|
buffets: {
|
|
21
20
|
_type: "buffets",
|
|
22
21
|
_data: [
|
|
23
22
|
{
|
|
24
|
-
items: [
|
|
23
|
+
items: [
|
|
24
|
+
{
|
|
25
|
+
table: "cakes",
|
|
26
|
+
ref: "KdLv3zTftqKKUeqYrTtO2r"
|
|
27
|
+
}
|
|
28
|
+
]
|
|
25
29
|
}
|
|
26
30
|
]
|
|
27
31
|
},
|
|
28
|
-
// A cake is a collection of layers, i.e. a base, a filling and a topping.
|
|
29
|
-
// Each layer of the cake has the same slice structure.
|
|
30
32
|
cakes: {
|
|
31
33
|
_type: "cakes",
|
|
32
34
|
_data: [
|
|
33
35
|
{
|
|
34
|
-
idSet: "
|
|
36
|
+
idSet: "Ko990SJfgPJvNGxC63CRf7",
|
|
35
37
|
collections: "layers",
|
|
36
38
|
layers: {
|
|
37
|
-
_hash: "
|
|
39
|
+
_hash: "RBNvo1WzZ4oRRq0W9-hknp"
|
|
38
40
|
},
|
|
39
|
-
_hash: "
|
|
41
|
+
_hash: "KdLv3zTftqKKUeqYrTtO2r"
|
|
40
42
|
}
|
|
41
|
-
]
|
|
42
|
-
_hash: "fw8IrV05Z1ZekPXTdUdmt8"
|
|
43
|
+
]
|
|
43
44
|
},
|
|
44
|
-
// Cakes are cut into slices. The cake layers are shared among the slices.
|
|
45
45
|
slices: {
|
|
46
46
|
_type: "idSets",
|
|
47
47
|
_data: [
|
|
48
48
|
{
|
|
49
49
|
add: ["slice0", "slice1"],
|
|
50
|
-
remove: []
|
|
50
|
+
remove: [],
|
|
51
|
+
_hash: "wyYfK5E4ArrMKQ_zvi2-EE"
|
|
51
52
|
}
|
|
52
|
-
]
|
|
53
|
+
],
|
|
54
|
+
_hash: "Qt6FzyzwHdEdYC3fKUXaAm"
|
|
53
55
|
},
|
|
54
|
-
// A layer is a collection of ingredients described by a recipe
|
|
55
56
|
layers: {
|
|
56
57
|
_type: "collections",
|
|
57
58
|
_data: [
|
|
58
59
|
{
|
|
59
60
|
properties: "recipes",
|
|
60
61
|
assign: {
|
|
61
|
-
slice0: "
|
|
62
|
-
slice1: "
|
|
63
|
-
|
|
64
|
-
},
|
|
65
|
-
_hash: "IIg0e-6Qr73s7sNZe0RhKs"
|
|
62
|
+
slice0: "uRTo_Jmt9lOA09e2QnwB9W",
|
|
63
|
+
slice1: "uRTo_Jmt9lOA09e2QnwB9W"
|
|
64
|
+
}
|
|
66
65
|
}
|
|
67
|
-
]
|
|
68
|
-
_hash: "e3utfEf_VS1-ljAMgqesua"
|
|
66
|
+
]
|
|
69
67
|
},
|
|
70
|
-
// Recipes are sets of ingredients
|
|
71
68
|
recipes: {
|
|
72
69
|
_type: "collections",
|
|
73
70
|
_data: [
|
|
74
71
|
{
|
|
75
72
|
properties: "recipeIngredients",
|
|
76
73
|
assign: {
|
|
77
|
-
flour: "
|
|
78
|
-
_hash: "UImWlyWZTSGhNAMC1JUzF1"
|
|
74
|
+
flour: "RCA4yzQe6mYOqquoLijKop"
|
|
79
75
|
},
|
|
80
|
-
_hash: "
|
|
76
|
+
_hash: "uRTo_Jmt9lOA09e2QnwB9W"
|
|
81
77
|
}
|
|
82
|
-
]
|
|
83
|
-
_hash: "r3lw7VsnywyasRmtynZqIt"
|
|
78
|
+
]
|
|
84
79
|
},
|
|
85
|
-
// A recipe ingredient combines an ingredient type with a quantity
|
|
86
80
|
recipeIngredients: {
|
|
87
81
|
_type: "properties",
|
|
88
82
|
_data: [
|
|
89
83
|
{
|
|
90
|
-
|
|
84
|
+
ingredientsRef: "CdSJV-WOfnFT1svec3iJ6x",
|
|
91
85
|
quantity: 500,
|
|
92
|
-
_hash: "
|
|
86
|
+
_hash: "RCA4yzQe6mYOqquoLijKop"
|
|
93
87
|
}
|
|
94
88
|
],
|
|
95
|
-
_hash: "
|
|
89
|
+
_hash: "R6dJq4ZJ3QDa9Bz8QJDhNq"
|
|
96
90
|
},
|
|
97
|
-
// A table describing basic properties of ingredients
|
|
98
91
|
ingredients: {
|
|
99
92
|
_type: "properties",
|
|
100
93
|
_data: [
|
|
@@ -107,7 +100,6 @@ const bakeryExample = () => {
|
|
|
107
100
|
],
|
|
108
101
|
_hash: "FgJeTM0NcZvXwFcU-PD8Jf"
|
|
109
102
|
},
|
|
110
|
-
// A table with nutritive values of ingredients
|
|
111
103
|
nutritiveValues: {
|
|
112
104
|
_type: "properties",
|
|
113
105
|
_data: [
|
|
@@ -122,6 +114,7 @@ const bakeryExample = () => {
|
|
|
122
114
|
_hash: "7k4OqQtk71w3yVGMoA9F6p"
|
|
123
115
|
}
|
|
124
116
|
};
|
|
117
|
+
return result;
|
|
125
118
|
};
|
|
126
119
|
// @license
|
|
127
120
|
const exampleBuffetsTable = () => bakeryExample().buffets;
|
|
@@ -155,12 +148,77 @@ __publicField(_Example, "ok", {
|
|
|
155
148
|
};
|
|
156
149
|
},
|
|
157
150
|
singleRow: () => {
|
|
158
|
-
|
|
151
|
+
const tableCfgs = {
|
|
152
|
+
_hash: "",
|
|
153
|
+
_type: "properties",
|
|
154
|
+
_data: [
|
|
155
|
+
{
|
|
156
|
+
_hash: "R-rCQ4YwYYJAp6uAo6S_6n",
|
|
157
|
+
name: "Single Row Table",
|
|
158
|
+
jsonKey: "table",
|
|
159
|
+
columns: {
|
|
160
|
+
int: {
|
|
161
|
+
jsonKey: "int",
|
|
162
|
+
type: "number",
|
|
163
|
+
name: "Integer",
|
|
164
|
+
nameShort: "Int"
|
|
165
|
+
},
|
|
166
|
+
double: {
|
|
167
|
+
jsonKey: "double",
|
|
168
|
+
type: "number",
|
|
169
|
+
name: "Double",
|
|
170
|
+
nameShort: "Dbl"
|
|
171
|
+
},
|
|
172
|
+
string: {
|
|
173
|
+
jsonKey: "string",
|
|
174
|
+
type: "string",
|
|
175
|
+
name: "String",
|
|
176
|
+
nameShort: "Str"
|
|
177
|
+
},
|
|
178
|
+
boolean: {
|
|
179
|
+
jsonKey: "boolean",
|
|
180
|
+
type: "boolean",
|
|
181
|
+
name: "Boolean",
|
|
182
|
+
nameShort: "Bool"
|
|
183
|
+
},
|
|
184
|
+
null: {
|
|
185
|
+
jsonKey: "null",
|
|
186
|
+
type: "null",
|
|
187
|
+
name: "null",
|
|
188
|
+
nameShort: "null"
|
|
189
|
+
},
|
|
190
|
+
jsonArray: {
|
|
191
|
+
jsonKey: "jsonArray",
|
|
192
|
+
type: "jsonArray",
|
|
193
|
+
name: "Json Array",
|
|
194
|
+
nameShort: "Jarray"
|
|
195
|
+
},
|
|
196
|
+
json: {
|
|
197
|
+
jsonKey: "json",
|
|
198
|
+
type: "json",
|
|
199
|
+
name: "Json",
|
|
200
|
+
nameShort: "Json"
|
|
201
|
+
},
|
|
202
|
+
jsonValue: {
|
|
203
|
+
jsonKey: "jsonValue",
|
|
204
|
+
type: "jsonValue",
|
|
205
|
+
name: "Json Value",
|
|
206
|
+
nameShort: "Jval"
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
]
|
|
211
|
+
};
|
|
212
|
+
const result = {
|
|
213
|
+
_tableCfgs: tableCfgs,
|
|
159
214
|
table: {
|
|
160
215
|
_type: "properties",
|
|
161
|
-
|
|
216
|
+
_tableCfg: "R-rCQ4YwYYJAp6uAo6S_6n",
|
|
217
|
+
_data: [exampleJsonObject()],
|
|
218
|
+
_hash: ""
|
|
162
219
|
}
|
|
163
220
|
};
|
|
221
|
+
return result;
|
|
164
222
|
},
|
|
165
223
|
multipleRows: () => {
|
|
166
224
|
return {
|
|
@@ -289,65 +347,75 @@ __publicField(_Example, "ok", {
|
|
|
289
347
|
}
|
|
290
348
|
});
|
|
291
349
|
__publicField(_Example, "broken", {
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
350
|
+
base: {
|
|
351
|
+
brokenTableName: () => {
|
|
352
|
+
return {
|
|
353
|
+
brok$en: {
|
|
354
|
+
_type: "properties",
|
|
355
|
+
_data: []
|
|
356
|
+
}
|
|
357
|
+
};
|
|
358
|
+
},
|
|
359
|
+
missingData: () => {
|
|
360
|
+
return {
|
|
361
|
+
table: {
|
|
362
|
+
_type: "properties"
|
|
363
|
+
}
|
|
364
|
+
};
|
|
365
|
+
},
|
|
366
|
+
dataNotBeingAnArray: () => {
|
|
367
|
+
return {
|
|
368
|
+
table: {
|
|
369
|
+
_type: "properties",
|
|
370
|
+
_data: {}
|
|
371
|
+
}
|
|
372
|
+
};
|
|
373
|
+
},
|
|
374
|
+
missingRef: () => {
|
|
375
|
+
return {
|
|
376
|
+
tableA: {
|
|
377
|
+
_type: "properties",
|
|
378
|
+
_data: [
|
|
379
|
+
{
|
|
380
|
+
keyA0: "a0"
|
|
381
|
+
},
|
|
382
|
+
{
|
|
383
|
+
keyA1: "a1"
|
|
384
|
+
}
|
|
385
|
+
]
|
|
386
|
+
},
|
|
387
|
+
tableB: {
|
|
388
|
+
_type: "properties",
|
|
389
|
+
_data: [
|
|
390
|
+
{
|
|
391
|
+
tableARef: "MISSINGREF"
|
|
392
|
+
// MISSINGREF does not exist in tableA
|
|
393
|
+
}
|
|
394
|
+
]
|
|
395
|
+
}
|
|
396
|
+
};
|
|
397
|
+
},
|
|
398
|
+
missingReferencedTable: () => {
|
|
399
|
+
return {
|
|
400
|
+
tableB: {
|
|
401
|
+
_type: "properties",
|
|
402
|
+
_data: [
|
|
403
|
+
{
|
|
404
|
+
tableARef: "MISSINGREF"
|
|
405
|
+
// tableA is missing
|
|
406
|
+
}
|
|
407
|
+
]
|
|
408
|
+
}
|
|
409
|
+
};
|
|
410
|
+
}
|
|
338
411
|
},
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
// tableA is missing
|
|
347
|
-
}
|
|
348
|
-
]
|
|
349
|
-
}
|
|
350
|
-
};
|
|
412
|
+
tableCfg: {
|
|
413
|
+
wrongType: () => {
|
|
414
|
+
const result = _Example.ok.singleRow();
|
|
415
|
+
const tableCfg = result._tableCfgs._data[0];
|
|
416
|
+
tableCfg.columns["int"].type = "numberBroken";
|
|
417
|
+
return hip(result, true, false);
|
|
418
|
+
}
|
|
351
419
|
},
|
|
352
420
|
collections: {
|
|
353
421
|
missingBase: () => {
|
|
@@ -415,6 +483,8 @@ __publicField(_Example, "broken", {
|
|
|
415
483
|
});
|
|
416
484
|
let Example = _Example;
|
|
417
485
|
// @license
|
|
486
|
+
const exampleTableCfgTable = () => Example.ok.singleRow()._tableCfgs;
|
|
487
|
+
// @license
|
|
418
488
|
const rljsonIndexed = (rljson) => {
|
|
419
489
|
const result = {};
|
|
420
490
|
for (const key in rljson) {
|
|
@@ -459,15 +529,676 @@ const exampleTypedefs = () => {
|
|
|
459
529
|
contentType: "collections"
|
|
460
530
|
};
|
|
461
531
|
};
|
|
532
|
+
// @license
|
|
533
|
+
class BaseValidator {
|
|
534
|
+
constructor() {
|
|
535
|
+
__publicField(this, "name", "syntax");
|
|
536
|
+
}
|
|
537
|
+
async validate(rljson) {
|
|
538
|
+
return this.validateSync(rljson);
|
|
539
|
+
}
|
|
540
|
+
validateSync(rljson) {
|
|
541
|
+
return new _BaseValidator(rljson).validate();
|
|
542
|
+
}
|
|
543
|
+
static isValidFieldName(fieldName) {
|
|
544
|
+
return /^[a-z][a-zA-Z0-9]*$/.test(fieldName);
|
|
545
|
+
}
|
|
546
|
+
}
|
|
547
|
+
class _BaseValidator {
|
|
548
|
+
constructor(rljson) {
|
|
549
|
+
__publicField(this, "errors", { hasErrors: false });
|
|
550
|
+
// ######################
|
|
551
|
+
// Private
|
|
552
|
+
// ######################
|
|
553
|
+
__publicField(this, "tableNames");
|
|
554
|
+
__publicField(this, "rljsonIndexed");
|
|
555
|
+
this.rljson = rljson;
|
|
556
|
+
this.tableNames = Object.keys(this.rljson).filter(
|
|
557
|
+
(table) => !table.startsWith("_")
|
|
558
|
+
);
|
|
559
|
+
this.rljsonIndexed = rljsonIndexed(rljson);
|
|
560
|
+
}
|
|
561
|
+
get hasErrors() {
|
|
562
|
+
return Object.keys(this.errors).length > 1;
|
|
563
|
+
}
|
|
564
|
+
validate() {
|
|
565
|
+
const steps = [
|
|
566
|
+
// Base checks
|
|
567
|
+
() => this._writeAndValidHashes(),
|
|
568
|
+
() => this._tableNamesNotLowerCamelCase(),
|
|
569
|
+
() => this._tableNamesDoNotEndWithRef(),
|
|
570
|
+
() => this._columnNamesNotLowerCamelCase(),
|
|
571
|
+
() => this._dataNotFound(),
|
|
572
|
+
() => this._dataHasWrongType(),
|
|
573
|
+
// Check table cfg
|
|
574
|
+
() => this._tableCfgsReferencedTableNameNotFound(),
|
|
575
|
+
() => this._tableCfgsHaveWrongType(),
|
|
576
|
+
() => this._tableCfgNotFound(),
|
|
577
|
+
() => this._missingColumnConfigs(),
|
|
578
|
+
() => this._dataDoesNotMatchColumnConfig(),
|
|
579
|
+
// Check references
|
|
580
|
+
() => this._refsNotFound(),
|
|
581
|
+
// Check collections
|
|
582
|
+
() => this._collectionBasesNotFound(),
|
|
583
|
+
() => this._collectionIdSetsExist(),
|
|
584
|
+
() => this._collectionPropertyAssignmentsNotFound(),
|
|
585
|
+
// Check cakes
|
|
586
|
+
() => this._cakeIdSetsNotFound(),
|
|
587
|
+
() => this._cakeCollectionTablesNotFound(),
|
|
588
|
+
// Check buffets
|
|
589
|
+
() => this._buffetReferencedTableNotFound()
|
|
590
|
+
];
|
|
591
|
+
for (const step of steps) {
|
|
592
|
+
step();
|
|
593
|
+
if (this.hasErrors) {
|
|
594
|
+
break;
|
|
595
|
+
}
|
|
596
|
+
}
|
|
597
|
+
this.errors.hasErrors = this.hasErrors;
|
|
598
|
+
return this.errors;
|
|
599
|
+
}
|
|
600
|
+
_tableNamesNotLowerCamelCase() {
|
|
601
|
+
const invalidTableNames = [];
|
|
602
|
+
for (const tableName of this.tableNames) {
|
|
603
|
+
if (!BaseValidator.isValidFieldName(tableName)) {
|
|
604
|
+
invalidTableNames.push(tableName);
|
|
605
|
+
}
|
|
606
|
+
}
|
|
607
|
+
if (invalidTableNames.length > 0) {
|
|
608
|
+
this.errors.tableNamesNotLowerCamelCase = {
|
|
609
|
+
error: "Table names must be lower camel case",
|
|
610
|
+
invalidTableNames
|
|
611
|
+
};
|
|
612
|
+
}
|
|
613
|
+
}
|
|
614
|
+
// ...........................................................................
|
|
615
|
+
_tableNamesDoNotEndWithRef() {
|
|
616
|
+
const invalidTableNames = [];
|
|
617
|
+
for (const tableName of this.tableNames) {
|
|
618
|
+
if (tableName.endsWith("Ref")) {
|
|
619
|
+
invalidTableNames.push(tableName);
|
|
620
|
+
}
|
|
621
|
+
}
|
|
622
|
+
if (invalidTableNames.length > 0) {
|
|
623
|
+
this.errors.tableNamesDoNotEndWithRef = {
|
|
624
|
+
error: 'Table names must not end with "Ref"',
|
|
625
|
+
invalidTableNames
|
|
626
|
+
};
|
|
627
|
+
}
|
|
628
|
+
}
|
|
629
|
+
// ...........................................................................
|
|
630
|
+
_columnNamesNotLowerCamelCase() {
|
|
631
|
+
const invalidColumnNames = {};
|
|
632
|
+
let hadErrors = false;
|
|
633
|
+
for (const tableName of this.tableNames) {
|
|
634
|
+
const table = this.rljson[tableName];
|
|
635
|
+
if (!table._data || !Array.isArray(table._data)) {
|
|
636
|
+
continue;
|
|
637
|
+
}
|
|
638
|
+
for (const row of table._data) {
|
|
639
|
+
for (const columnName in row) {
|
|
640
|
+
if (columnName.startsWith("_")) {
|
|
641
|
+
continue;
|
|
642
|
+
}
|
|
643
|
+
if (!BaseValidator.isValidFieldName(columnName)) {
|
|
644
|
+
invalidColumnNames[tableName] ?? (invalidColumnNames[tableName] = []);
|
|
645
|
+
invalidColumnNames[tableName].push(columnName);
|
|
646
|
+
hadErrors = true;
|
|
647
|
+
}
|
|
648
|
+
}
|
|
649
|
+
}
|
|
650
|
+
}
|
|
651
|
+
if (hadErrors) {
|
|
652
|
+
this.errors.columnNamesNotLowerCamelCase = {
|
|
653
|
+
error: "Column names must be lower camel case",
|
|
654
|
+
invalidColumnNames
|
|
655
|
+
};
|
|
656
|
+
}
|
|
657
|
+
}
|
|
658
|
+
// ...........................................................................
|
|
659
|
+
_writeAndValidHashes() {
|
|
660
|
+
try {
|
|
661
|
+
this.rljson = hsh(this.rljson, {
|
|
662
|
+
inPlace: false,
|
|
663
|
+
updateExistingHashes: true,
|
|
664
|
+
throwOnWrongHashes: true
|
|
665
|
+
});
|
|
666
|
+
} catch (error) {
|
|
667
|
+
if (error instanceof Error) {
|
|
668
|
+
this.errors.hashesNotValid = {
|
|
669
|
+
error: error.message
|
|
670
|
+
};
|
|
671
|
+
} else {
|
|
672
|
+
this.errors.hashesNotValid = {
|
|
673
|
+
error: "Unknown error"
|
|
674
|
+
};
|
|
675
|
+
}
|
|
676
|
+
}
|
|
677
|
+
}
|
|
678
|
+
// ...........................................................................
|
|
679
|
+
/// Checks if data is valid
|
|
680
|
+
_dataNotFound() {
|
|
681
|
+
const rljson = this.rljson;
|
|
682
|
+
const tablesWithMissingData = [];
|
|
683
|
+
for (const table of this.tableNames) {
|
|
684
|
+
const tableData = rljson[table];
|
|
685
|
+
const items = tableData["_data"];
|
|
686
|
+
if (items == null) {
|
|
687
|
+
tablesWithMissingData.push(table);
|
|
688
|
+
}
|
|
689
|
+
}
|
|
690
|
+
if (tablesWithMissingData.length > 0) {
|
|
691
|
+
this.errors.dataNotFound = {
|
|
692
|
+
error: "_data is missing in tables",
|
|
693
|
+
tables: tablesWithMissingData
|
|
694
|
+
};
|
|
695
|
+
}
|
|
696
|
+
}
|
|
697
|
+
// ...........................................................................
|
|
698
|
+
_tableCfgsReferencedTableNameNotFound() {
|
|
699
|
+
const tableCfgs = this.rljson._tableCfgs;
|
|
700
|
+
if (!tableCfgs) {
|
|
701
|
+
return;
|
|
702
|
+
}
|
|
703
|
+
const brokenCfgs = [];
|
|
704
|
+
for (const item of tableCfgs._data) {
|
|
705
|
+
const table = this.rljson[item.jsonKey];
|
|
706
|
+
if (!table) {
|
|
707
|
+
brokenCfgs.push({
|
|
708
|
+
brokenTableCfg: item._hash,
|
|
709
|
+
tableNameNotFound: item.jsonKey
|
|
710
|
+
});
|
|
711
|
+
}
|
|
712
|
+
}
|
|
713
|
+
if (brokenCfgs.length > 0) {
|
|
714
|
+
this.errors.tableCfgsReferencedTableNameNotFound = {
|
|
715
|
+
error: "Tables referenced in _tableCfgs not found",
|
|
716
|
+
brokenCfgs
|
|
717
|
+
};
|
|
718
|
+
}
|
|
719
|
+
}
|
|
720
|
+
// ...........................................................................
|
|
721
|
+
_tableCfgsHaveWrongType() {
|
|
722
|
+
const tableCfgs = this.rljson._tableCfgs;
|
|
723
|
+
if (!tableCfgs) {
|
|
724
|
+
return;
|
|
725
|
+
}
|
|
726
|
+
const brokenCfgs = [];
|
|
727
|
+
for (const item of tableCfgs._data) {
|
|
728
|
+
for (const columnKey in item.columns) {
|
|
729
|
+
if (columnKey.startsWith("_")) {
|
|
730
|
+
continue;
|
|
731
|
+
}
|
|
732
|
+
const column = item.columns[columnKey];
|
|
733
|
+
if (jsonValueTypes.indexOf(column.type) === -1) {
|
|
734
|
+
brokenCfgs.push({
|
|
735
|
+
brokenTableCfg: item._hash,
|
|
736
|
+
brokenColumnKey: columnKey,
|
|
737
|
+
brokenColumnType: column.type
|
|
738
|
+
});
|
|
739
|
+
}
|
|
740
|
+
}
|
|
741
|
+
}
|
|
742
|
+
if (brokenCfgs.length > 0) {
|
|
743
|
+
this.errors.tableCfgsHaveWrongTypes = {
|
|
744
|
+
error: "Some of the columns have invalid types. Valid types are: " + jsonValueTypes.join(", "),
|
|
745
|
+
brokenCfgs
|
|
746
|
+
};
|
|
747
|
+
}
|
|
748
|
+
}
|
|
749
|
+
// ...........................................................................
|
|
750
|
+
_tableCfgNotFound() {
|
|
751
|
+
const tableCfgs = this.rljsonIndexed._tableCfgs;
|
|
752
|
+
const tableCfgNotFound = [];
|
|
753
|
+
iterateTables(this.rljson, (tableName, table) => {
|
|
754
|
+
const tableCfgRef = table._tableCfg;
|
|
755
|
+
if (!tableCfgRef) {
|
|
756
|
+
return;
|
|
757
|
+
}
|
|
758
|
+
const tableCfgData = tableCfgs._data[tableCfgRef];
|
|
759
|
+
if (!tableCfgData) {
|
|
760
|
+
tableCfgNotFound.push({
|
|
761
|
+
tableWithBrokenTableCfgRef: tableName,
|
|
762
|
+
brokenTableCfgRef: tableCfgRef
|
|
763
|
+
});
|
|
764
|
+
return;
|
|
765
|
+
}
|
|
766
|
+
});
|
|
767
|
+
if (tableCfgNotFound.length > 0) {
|
|
768
|
+
this.errors.tableCfgReferencedNotFound = {
|
|
769
|
+
error: "Referenced table config not found",
|
|
770
|
+
tableCfgNotFound
|
|
771
|
+
};
|
|
772
|
+
}
|
|
773
|
+
}
|
|
774
|
+
// ...........................................................................
|
|
775
|
+
_missingColumnConfigs() {
|
|
776
|
+
const tableCfgs = this.rljsonIndexed._tableCfgs;
|
|
777
|
+
const missingColumnConfigs = [];
|
|
778
|
+
iterateTables(this.rljson, (tableName, table) => {
|
|
779
|
+
const tableCfgRef = table._tableCfg;
|
|
780
|
+
if (!tableCfgRef) {
|
|
781
|
+
return;
|
|
782
|
+
}
|
|
783
|
+
const tableCfgData = tableCfgs._data[tableCfgRef];
|
|
784
|
+
const processedColumnKeys = [];
|
|
785
|
+
for (const row of table._data) {
|
|
786
|
+
const columnKeys = Object.keys(row).filter(
|
|
787
|
+
(key) => !key.startsWith("_")
|
|
788
|
+
);
|
|
789
|
+
const newColumnKey = columnKeys.filter(
|
|
790
|
+
(key) => processedColumnKeys.indexOf(key) === -1
|
|
791
|
+
);
|
|
792
|
+
for (const columnKey of newColumnKey) {
|
|
793
|
+
if (!tableCfgData.columns[columnKey]) {
|
|
794
|
+
missingColumnConfigs.push({
|
|
795
|
+
tableCfg: tableCfgRef,
|
|
796
|
+
row: row._hash,
|
|
797
|
+
column: columnKey,
|
|
798
|
+
table: tableName
|
|
799
|
+
});
|
|
800
|
+
}
|
|
801
|
+
processedColumnKeys.push(columnKey);
|
|
802
|
+
}
|
|
803
|
+
}
|
|
804
|
+
});
|
|
805
|
+
if (missingColumnConfigs.length > 0) {
|
|
806
|
+
this.errors.columnConfigNotFound = {
|
|
807
|
+
error: "Column configurations not found",
|
|
808
|
+
missingColumnConfigs
|
|
809
|
+
};
|
|
810
|
+
}
|
|
811
|
+
}
|
|
812
|
+
// ...........................................................................
|
|
813
|
+
_dataDoesNotMatchColumnConfig() {
|
|
814
|
+
const tableCfgs = this.rljsonIndexed._tableCfgs;
|
|
815
|
+
const brokenValues = [];
|
|
816
|
+
iterateTables(this.rljson, (tableName, table) => {
|
|
817
|
+
const tableCfgRef = table._tableCfg;
|
|
818
|
+
if (!tableCfgRef) {
|
|
819
|
+
return;
|
|
820
|
+
}
|
|
821
|
+
const tableCfgData = tableCfgs._data[tableCfgRef];
|
|
822
|
+
for (const row of table._data) {
|
|
823
|
+
const columnKeys = Object.keys(row).filter(
|
|
824
|
+
(key) => !key.startsWith("_")
|
|
825
|
+
);
|
|
826
|
+
for (const columnKey of columnKeys) {
|
|
827
|
+
const columnConfig = tableCfgData.columns[columnKey];
|
|
828
|
+
const value = row[columnKey];
|
|
829
|
+
if (value == null || value == void 0) {
|
|
830
|
+
continue;
|
|
831
|
+
}
|
|
832
|
+
const typeShould = columnConfig.type;
|
|
833
|
+
if (!jsonValueMatchesType(value, typeShould)) {
|
|
834
|
+
brokenValues.push({
|
|
835
|
+
table: tableName,
|
|
836
|
+
row: row._hash,
|
|
837
|
+
column: columnKey,
|
|
838
|
+
tableCfg: tableCfgRef
|
|
839
|
+
});
|
|
840
|
+
}
|
|
841
|
+
}
|
|
842
|
+
}
|
|
843
|
+
});
|
|
844
|
+
if (brokenValues.length > 0) {
|
|
845
|
+
this.errors.dataDoesNotMatchColumnConfig = {
|
|
846
|
+
error: "Table values have wrong types",
|
|
847
|
+
brokenValues
|
|
848
|
+
};
|
|
849
|
+
}
|
|
850
|
+
}
|
|
851
|
+
// ...........................................................................
|
|
852
|
+
_dataHasWrongType() {
|
|
853
|
+
const rljson = this.rljson;
|
|
854
|
+
const tablesWithWrongType = [];
|
|
855
|
+
for (const tableName of this.tableNames) {
|
|
856
|
+
const tableData = rljson[tableName];
|
|
857
|
+
const items = tableData["_data"];
|
|
858
|
+
if (!Array.isArray(items)) {
|
|
859
|
+
tablesWithWrongType.push(tableName);
|
|
860
|
+
}
|
|
861
|
+
}
|
|
862
|
+
if (tablesWithWrongType.length > 0) {
|
|
863
|
+
this.errors.dataHasWrongType = {
|
|
864
|
+
error: "_data must be a list",
|
|
865
|
+
tables: tablesWithWrongType
|
|
866
|
+
};
|
|
867
|
+
}
|
|
868
|
+
}
|
|
869
|
+
_refsNotFound() {
|
|
870
|
+
const missingRefs = [];
|
|
871
|
+
iterateTables(this.rljson, (tableName, table) => {
|
|
872
|
+
const tableData = table._data;
|
|
873
|
+
for (const item of tableData) {
|
|
874
|
+
for (const key of Object.keys(item)) {
|
|
875
|
+
if (key.endsWith("Ref")) {
|
|
876
|
+
const targetItemHash = item[key];
|
|
877
|
+
const targetTableName = key.substring(0, key.length - 3);
|
|
878
|
+
const itemHash = item._hash;
|
|
879
|
+
if (this.tableNames.indexOf(targetTableName) === -1) {
|
|
880
|
+
missingRefs.push({
|
|
881
|
+
error: `Target table "${targetTableName}" not found.`,
|
|
882
|
+
sourceTable: tableName,
|
|
883
|
+
sourceKey: key,
|
|
884
|
+
sourceItemHash: itemHash,
|
|
885
|
+
targetItemHash,
|
|
886
|
+
targetTable: targetTableName
|
|
887
|
+
});
|
|
888
|
+
continue;
|
|
889
|
+
}
|
|
890
|
+
const targetTableIndexed = this.rljsonIndexed[targetTableName];
|
|
891
|
+
const referencedItem = targetTableIndexed._data[targetItemHash];
|
|
892
|
+
if (referencedItem === void 0) {
|
|
893
|
+
missingRefs.push({
|
|
894
|
+
sourceTable: tableName,
|
|
895
|
+
sourceItemHash: itemHash,
|
|
896
|
+
sourceKey: key,
|
|
897
|
+
targetItemHash,
|
|
898
|
+
targetTable: targetTableName,
|
|
899
|
+
error: `Table "${targetTableName}" has no item with hash "${targetItemHash}"`
|
|
900
|
+
});
|
|
901
|
+
}
|
|
902
|
+
}
|
|
903
|
+
}
|
|
904
|
+
}
|
|
905
|
+
});
|
|
906
|
+
if (missingRefs.length > 0) {
|
|
907
|
+
this.errors.refsNotFound = {
|
|
908
|
+
error: "Broken references",
|
|
909
|
+
missingRefs
|
|
910
|
+
};
|
|
911
|
+
}
|
|
912
|
+
}
|
|
913
|
+
_collectionBasesNotFound() {
|
|
914
|
+
const brokenCollections = [];
|
|
915
|
+
iterateTables(this.rljson, (tableName, table) => {
|
|
916
|
+
if (table._type !== "collections") {
|
|
917
|
+
return;
|
|
918
|
+
}
|
|
919
|
+
const collectionsIndexed = this.rljsonIndexed[tableName];
|
|
920
|
+
const collectionsTable = table;
|
|
921
|
+
for (const collection of collectionsTable._data) {
|
|
922
|
+
const baseRef = collection.base;
|
|
923
|
+
if (!baseRef) {
|
|
924
|
+
continue;
|
|
925
|
+
}
|
|
926
|
+
const baseCollection = collectionsIndexed._data[baseRef];
|
|
927
|
+
if (!baseCollection) {
|
|
928
|
+
brokenCollections.push({
|
|
929
|
+
collectionsTable: tableName,
|
|
930
|
+
brokenCollection: collection._hash,
|
|
931
|
+
missingBaseCollection: baseRef
|
|
932
|
+
});
|
|
933
|
+
}
|
|
934
|
+
}
|
|
935
|
+
});
|
|
936
|
+
if (brokenCollections.length > 0) {
|
|
937
|
+
this.errors.collectionBasesNotFound = {
|
|
938
|
+
error: "Base collections are missing",
|
|
939
|
+
brokenCollections
|
|
940
|
+
};
|
|
941
|
+
}
|
|
942
|
+
}
|
|
943
|
+
_collectionIdSetsExist() {
|
|
944
|
+
const brokenCollections = [];
|
|
945
|
+
iterateTables(this.rljson, (tableName, table) => {
|
|
946
|
+
if (table._type !== "collections") {
|
|
947
|
+
return;
|
|
948
|
+
}
|
|
949
|
+
const idSets = this.rljsonIndexed._idSets;
|
|
950
|
+
const collectionsTable = table;
|
|
951
|
+
for (const collection of collectionsTable._data) {
|
|
952
|
+
const idSetRef = collection.idSet;
|
|
953
|
+
if (!idSetRef) {
|
|
954
|
+
continue;
|
|
955
|
+
}
|
|
956
|
+
const idSet = idSets._data[idSetRef];
|
|
957
|
+
if (!idSet) {
|
|
958
|
+
brokenCollections.push({
|
|
959
|
+
collectionsTable: tableName,
|
|
960
|
+
collectionHash: collection._hash,
|
|
961
|
+
missingIdSet: idSetRef
|
|
962
|
+
});
|
|
963
|
+
}
|
|
964
|
+
}
|
|
965
|
+
});
|
|
966
|
+
if (brokenCollections.length > 0) {
|
|
967
|
+
this.errors.collectionIdSetsExist = {
|
|
968
|
+
error: "Id sets of collections are missing",
|
|
969
|
+
brokenCollections
|
|
970
|
+
};
|
|
971
|
+
}
|
|
972
|
+
}
|
|
973
|
+
_collectionPropertyAssignmentsNotFound() {
|
|
974
|
+
const missingPropertyTables = [];
|
|
975
|
+
const brokenAssignments = [];
|
|
976
|
+
iterateTables(this.rljson, (tableName, table) => {
|
|
977
|
+
if (table._type !== "collections") {
|
|
978
|
+
return;
|
|
979
|
+
}
|
|
980
|
+
const collectionsTable = table;
|
|
981
|
+
for (const collection of collectionsTable._data) {
|
|
982
|
+
const propertyTableName = collection.properties;
|
|
983
|
+
const propertiesTable = this.rljsonIndexed[propertyTableName];
|
|
984
|
+
if (!propertiesTable) {
|
|
985
|
+
missingPropertyTables.push({
|
|
986
|
+
brokenCollection: collection._hash,
|
|
987
|
+
collectionsTable: tableName,
|
|
988
|
+
missingPropertyTable: propertyTableName
|
|
989
|
+
});
|
|
990
|
+
continue;
|
|
991
|
+
}
|
|
992
|
+
const assignments = collection.assign;
|
|
993
|
+
for (const itemId in assignments) {
|
|
994
|
+
if (itemId.startsWith("_")) {
|
|
995
|
+
continue;
|
|
996
|
+
}
|
|
997
|
+
const propertyHash = assignments[itemId];
|
|
998
|
+
if (!propertiesTable._data[propertyHash]) {
|
|
999
|
+
brokenAssignments.push({
|
|
1000
|
+
collectionsTable: tableName,
|
|
1001
|
+
brokenCollection: collection._hash,
|
|
1002
|
+
referencedPropertyTable: propertyTableName,
|
|
1003
|
+
brokenAssignment: itemId,
|
|
1004
|
+
missingProperty: propertyHash
|
|
1005
|
+
});
|
|
1006
|
+
}
|
|
1007
|
+
}
|
|
1008
|
+
}
|
|
1009
|
+
});
|
|
1010
|
+
if (missingPropertyTables.length > 0) {
|
|
1011
|
+
this.errors.collectionPropertyTablesNotFound = {
|
|
1012
|
+
error: "Collection property tables do not exist",
|
|
1013
|
+
collections: missingPropertyTables
|
|
1014
|
+
};
|
|
1015
|
+
}
|
|
1016
|
+
if (brokenAssignments.length > 0) {
|
|
1017
|
+
this.errors.collectionPropertyAssignmentsNotFound = {
|
|
1018
|
+
error: "Collection property assignments are broken",
|
|
1019
|
+
brokenAssignments
|
|
1020
|
+
};
|
|
1021
|
+
}
|
|
1022
|
+
}
|
|
1023
|
+
_cakeIdSetsNotFound() {
|
|
1024
|
+
const brokenCakes = [];
|
|
1025
|
+
iterateTables(this.rljson, (tableName, table) => {
|
|
1026
|
+
if (table._type !== "cakes") {
|
|
1027
|
+
return;
|
|
1028
|
+
}
|
|
1029
|
+
const idSets = this.rljsonIndexed._idSets;
|
|
1030
|
+
const cakesTable = table;
|
|
1031
|
+
for (const cake of cakesTable._data) {
|
|
1032
|
+
const idSetRef = cake.idSet;
|
|
1033
|
+
if (!idSetRef) {
|
|
1034
|
+
continue;
|
|
1035
|
+
}
|
|
1036
|
+
const idSet = idSets._data[idSetRef];
|
|
1037
|
+
if (!idSet) {
|
|
1038
|
+
brokenCakes.push({
|
|
1039
|
+
cakeTable: tableName,
|
|
1040
|
+
brokenCake: cake._hash,
|
|
1041
|
+
missingIdSet: idSetRef
|
|
1042
|
+
});
|
|
1043
|
+
}
|
|
1044
|
+
}
|
|
1045
|
+
});
|
|
1046
|
+
if (brokenCakes.length > 0) {
|
|
1047
|
+
this.errors.cakeIdSetsNotFound = {
|
|
1048
|
+
error: "Id sets of cakes are missing",
|
|
1049
|
+
brokenCakes
|
|
1050
|
+
};
|
|
1051
|
+
}
|
|
1052
|
+
}
|
|
1053
|
+
_cakeCollectionTablesNotFound() {
|
|
1054
|
+
const missingCollectionTables = [];
|
|
1055
|
+
const missingLayerCollections = [];
|
|
1056
|
+
iterateTables(this.rljson, (tableName, table) => {
|
|
1057
|
+
if (table._type !== "cakes") {
|
|
1058
|
+
return;
|
|
1059
|
+
}
|
|
1060
|
+
const cakesTable = table;
|
|
1061
|
+
for (const cake of cakesTable._data) {
|
|
1062
|
+
const collectionsTableName = cake.collections;
|
|
1063
|
+
const collectionsTable = this.rljsonIndexed[collectionsTableName];
|
|
1064
|
+
if (!collectionsTable) {
|
|
1065
|
+
missingCollectionTables.push({
|
|
1066
|
+
cakeTable: tableName,
|
|
1067
|
+
brokenCake: cake._hash,
|
|
1068
|
+
missingCollectionsTable: collectionsTableName
|
|
1069
|
+
});
|
|
1070
|
+
continue;
|
|
1071
|
+
}
|
|
1072
|
+
for (const layer in cake.layers) {
|
|
1073
|
+
if (layer.startsWith("_")) {
|
|
1074
|
+
continue;
|
|
1075
|
+
}
|
|
1076
|
+
const collectionRef = cake.layers[layer];
|
|
1077
|
+
const collection = collectionsTable._data[collectionRef];
|
|
1078
|
+
if (!collection) {
|
|
1079
|
+
missingLayerCollections.push({
|
|
1080
|
+
cakeTable: tableName,
|
|
1081
|
+
brokenCake: cake._hash,
|
|
1082
|
+
brokenLayerName: layer,
|
|
1083
|
+
collectionsTable: collectionsTableName,
|
|
1084
|
+
missingCollection: collectionRef
|
|
1085
|
+
});
|
|
1086
|
+
}
|
|
1087
|
+
}
|
|
1088
|
+
}
|
|
1089
|
+
});
|
|
1090
|
+
if (missingCollectionTables.length > 0) {
|
|
1091
|
+
this.errors.cakeCollectionTablesNotFound = {
|
|
1092
|
+
error: "Collection tables of cakes are missing",
|
|
1093
|
+
brokenCakes: missingCollectionTables
|
|
1094
|
+
};
|
|
1095
|
+
}
|
|
1096
|
+
if (missingLayerCollections.length > 0) {
|
|
1097
|
+
this.errors.cakeLayerCollectionsNotFound = {
|
|
1098
|
+
error: "Layer collections of cakes are missing",
|
|
1099
|
+
brokenCakes: missingLayerCollections
|
|
1100
|
+
};
|
|
1101
|
+
}
|
|
1102
|
+
}
|
|
1103
|
+
_buffetReferencedTableNotFound() {
|
|
1104
|
+
const missingTables = [];
|
|
1105
|
+
const missingItems = [];
|
|
1106
|
+
iterateTables(this.rljson, (tableName, table) => {
|
|
1107
|
+
if (table._type !== "buffets") {
|
|
1108
|
+
return;
|
|
1109
|
+
}
|
|
1110
|
+
const buffetsTable = table;
|
|
1111
|
+
for (const buffet of buffetsTable._data) {
|
|
1112
|
+
for (const item of buffet.items) {
|
|
1113
|
+
const itemTableName = item.table;
|
|
1114
|
+
const itemTable = this.rljsonIndexed[itemTableName];
|
|
1115
|
+
if (!itemTable) {
|
|
1116
|
+
missingTables.push({
|
|
1117
|
+
buffetTable: tableName,
|
|
1118
|
+
brokenBuffet: buffet._hash,
|
|
1119
|
+
missingItemTable: itemTableName
|
|
1120
|
+
});
|
|
1121
|
+
continue;
|
|
1122
|
+
}
|
|
1123
|
+
const ref = item.ref;
|
|
1124
|
+
const referencedItem = itemTable._data[ref];
|
|
1125
|
+
if (!referencedItem) {
|
|
1126
|
+
missingItems.push({
|
|
1127
|
+
buffetTable: tableName,
|
|
1128
|
+
brokenBuffet: buffet._hash,
|
|
1129
|
+
itemTable: itemTableName,
|
|
1130
|
+
missingItem: ref
|
|
1131
|
+
});
|
|
1132
|
+
}
|
|
1133
|
+
}
|
|
1134
|
+
}
|
|
1135
|
+
});
|
|
1136
|
+
if (missingTables.length > 0) {
|
|
1137
|
+
this.errors.buffetReferencedTablesNotFound = {
|
|
1138
|
+
error: "Referenced tables of buffets are missing",
|
|
1139
|
+
brokenBuffets: missingTables
|
|
1140
|
+
};
|
|
1141
|
+
}
|
|
1142
|
+
if (missingItems.length > 0) {
|
|
1143
|
+
this.errors.buffetReferencedItemsNotFound = {
|
|
1144
|
+
error: "Referenced items of buffets are missing",
|
|
1145
|
+
brokenItems: missingItems
|
|
1146
|
+
};
|
|
1147
|
+
}
|
|
1148
|
+
}
|
|
1149
|
+
}
|
|
1150
|
+
const isValidFieldName = (fieldName) => BaseValidator.isValidFieldName(fieldName);
|
|
1151
|
+
// @license
|
|
1152
|
+
class Validate {
|
|
1153
|
+
constructor() {
|
|
1154
|
+
// ######################
|
|
1155
|
+
// Private
|
|
1156
|
+
// ######################
|
|
1157
|
+
__publicField(this, "_validators", []);
|
|
1158
|
+
}
|
|
1159
|
+
addValidator(validator) {
|
|
1160
|
+
this._validators.push(validator);
|
|
1161
|
+
}
|
|
1162
|
+
async run(rljson) {
|
|
1163
|
+
const result = await Promise.all(
|
|
1164
|
+
this._validators.map(async (validator) => {
|
|
1165
|
+
const errors = await validator.validate(rljson);
|
|
1166
|
+
const name = validator.name;
|
|
1167
|
+
return {
|
|
1168
|
+
[name]: errors
|
|
1169
|
+
};
|
|
1170
|
+
})
|
|
1171
|
+
);
|
|
1172
|
+
return result.reduce((acc, errors) => {
|
|
1173
|
+
let hasErrors = false;
|
|
1174
|
+
for (const key of Object.keys(errors)) {
|
|
1175
|
+
const e = Object.keys(errors[key]);
|
|
1176
|
+
if (e.length > 0) {
|
|
1177
|
+
hasErrors = true;
|
|
1178
|
+
break;
|
|
1179
|
+
}
|
|
1180
|
+
}
|
|
1181
|
+
if (hasErrors) {
|
|
1182
|
+
return { ...acc, ...errors };
|
|
1183
|
+
}
|
|
1184
|
+
return acc;
|
|
1185
|
+
}, {});
|
|
1186
|
+
}
|
|
1187
|
+
}
|
|
462
1188
|
export {
|
|
1189
|
+
BaseValidator,
|
|
463
1190
|
Example,
|
|
1191
|
+
Validate,
|
|
1192
|
+
bakeryExample,
|
|
464
1193
|
exampleBuffetsTable,
|
|
465
1194
|
exampleCakesTable,
|
|
466
1195
|
exampleCollectionsTable,
|
|
467
1196
|
exampleIdSetsTable,
|
|
468
1197
|
examplePropertiesTable,
|
|
469
1198
|
exampleRljson,
|
|
1199
|
+
exampleTableCfgTable,
|
|
470
1200
|
exampleTypedefs,
|
|
1201
|
+
isValidFieldName,
|
|
471
1202
|
iterateTables,
|
|
472
1203
|
reservedFieldNames,
|
|
473
1204
|
reservedTableNames,
|
package/dist/src/example.ts
CHANGED
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
import { hip } from '@rljson/hash';
|
|
8
8
|
import { exampleJsonObject } from '@rljson/json';
|
|
9
9
|
|
|
10
|
+
import { TablesCfgTable } from './content/table-cfg.ts';
|
|
10
11
|
import { bakeryExample } from './example/bakery-example.ts';
|
|
11
12
|
import { Rljson } from './rljson.ts';
|
|
12
13
|
|
|
@@ -33,12 +34,78 @@ export class Example {
|
|
|
33
34
|
},
|
|
34
35
|
|
|
35
36
|
singleRow: (): Rljson => {
|
|
36
|
-
|
|
37
|
+
const tableCfgs: TablesCfgTable = {
|
|
38
|
+
_hash: '',
|
|
39
|
+
_type: 'properties',
|
|
40
|
+
_data: [
|
|
41
|
+
{
|
|
42
|
+
_hash: 'R-rCQ4YwYYJAp6uAo6S_6n',
|
|
43
|
+
name: 'Single Row Table',
|
|
44
|
+
jsonKey: 'table',
|
|
45
|
+
columns: {
|
|
46
|
+
int: {
|
|
47
|
+
jsonKey: 'int',
|
|
48
|
+
type: 'number',
|
|
49
|
+
name: 'Integer',
|
|
50
|
+
nameShort: 'Int',
|
|
51
|
+
},
|
|
52
|
+
double: {
|
|
53
|
+
jsonKey: 'double',
|
|
54
|
+
type: 'number',
|
|
55
|
+
name: 'Double',
|
|
56
|
+
nameShort: 'Dbl',
|
|
57
|
+
},
|
|
58
|
+
string: {
|
|
59
|
+
jsonKey: 'string',
|
|
60
|
+
type: 'string',
|
|
61
|
+
name: 'String',
|
|
62
|
+
nameShort: 'Str',
|
|
63
|
+
},
|
|
64
|
+
boolean: {
|
|
65
|
+
jsonKey: 'boolean',
|
|
66
|
+
type: 'boolean',
|
|
67
|
+
name: 'Boolean',
|
|
68
|
+
nameShort: 'Bool',
|
|
69
|
+
},
|
|
70
|
+
null: {
|
|
71
|
+
jsonKey: 'null',
|
|
72
|
+
type: 'null',
|
|
73
|
+
name: 'null',
|
|
74
|
+
nameShort: 'null',
|
|
75
|
+
},
|
|
76
|
+
jsonArray: {
|
|
77
|
+
jsonKey: 'jsonArray',
|
|
78
|
+
type: 'jsonArray',
|
|
79
|
+
name: 'Json Array',
|
|
80
|
+
nameShort: 'Jarray',
|
|
81
|
+
},
|
|
82
|
+
json: {
|
|
83
|
+
jsonKey: 'json',
|
|
84
|
+
type: 'json',
|
|
85
|
+
name: 'Json',
|
|
86
|
+
nameShort: 'Json',
|
|
87
|
+
},
|
|
88
|
+
jsonValue: {
|
|
89
|
+
jsonKey: 'jsonValue',
|
|
90
|
+
type: 'jsonValue',
|
|
91
|
+
name: 'Json Value',
|
|
92
|
+
nameShort: 'Jval',
|
|
93
|
+
},
|
|
94
|
+
},
|
|
95
|
+
},
|
|
96
|
+
],
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
const result: Rljson = {
|
|
100
|
+
_tableCfgs: tableCfgs,
|
|
37
101
|
table: {
|
|
38
102
|
_type: 'properties',
|
|
103
|
+
_tableCfg: 'R-rCQ4YwYYJAp6uAo6S_6n',
|
|
39
104
|
_data: [exampleJsonObject()],
|
|
105
|
+
_hash: '',
|
|
40
106
|
},
|
|
41
107
|
};
|
|
108
|
+
return result as Rljson;
|
|
42
109
|
},
|
|
43
110
|
|
|
44
111
|
multipleRows: (): Rljson => {
|
|
@@ -177,67 +244,78 @@ export class Example {
|
|
|
177
244
|
};
|
|
178
245
|
|
|
179
246
|
static readonly broken = {
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
247
|
+
base: {
|
|
248
|
+
brokenTableName: () => {
|
|
249
|
+
return {
|
|
250
|
+
brok$en: {
|
|
251
|
+
_type: 'properties',
|
|
252
|
+
_data: [],
|
|
253
|
+
},
|
|
254
|
+
};
|
|
255
|
+
},
|
|
188
256
|
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
257
|
+
missingData: () => {
|
|
258
|
+
return {
|
|
259
|
+
table: {
|
|
260
|
+
_type: 'properties',
|
|
261
|
+
},
|
|
262
|
+
} as unknown as Rljson;
|
|
263
|
+
},
|
|
196
264
|
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
265
|
+
dataNotBeingAnArray: () => {
|
|
266
|
+
return {
|
|
267
|
+
table: {
|
|
268
|
+
_type: 'properties',
|
|
269
|
+
_data: {},
|
|
270
|
+
},
|
|
271
|
+
} as unknown as Rljson;
|
|
272
|
+
},
|
|
205
273
|
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
274
|
+
missingRef: (): Rljson => {
|
|
275
|
+
return {
|
|
276
|
+
tableA: {
|
|
277
|
+
_type: 'properties',
|
|
278
|
+
_data: [
|
|
279
|
+
{
|
|
280
|
+
keyA0: 'a0',
|
|
281
|
+
},
|
|
282
|
+
{
|
|
283
|
+
keyA1: 'a1',
|
|
284
|
+
},
|
|
285
|
+
],
|
|
286
|
+
},
|
|
287
|
+
tableB: {
|
|
288
|
+
_type: 'properties',
|
|
289
|
+
_data: [
|
|
290
|
+
{
|
|
291
|
+
tableARef: 'MISSINGREF', // MISSINGREF does not exist in tableA
|
|
292
|
+
},
|
|
293
|
+
],
|
|
294
|
+
},
|
|
295
|
+
};
|
|
296
|
+
},
|
|
297
|
+
|
|
298
|
+
missingReferencedTable: (): Rljson => {
|
|
299
|
+
return {
|
|
300
|
+
tableB: {
|
|
301
|
+
_type: 'properties',
|
|
302
|
+
_data: [
|
|
303
|
+
{
|
|
304
|
+
tableARef: 'MISSINGREF', // tableA is missing
|
|
305
|
+
},
|
|
306
|
+
],
|
|
307
|
+
},
|
|
308
|
+
};
|
|
309
|
+
},
|
|
228
310
|
},
|
|
229
311
|
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
},
|
|
238
|
-
],
|
|
239
|
-
},
|
|
240
|
-
};
|
|
312
|
+
tableCfg: {
|
|
313
|
+
wrongType: () => {
|
|
314
|
+
const result = Example.ok.singleRow();
|
|
315
|
+
const tableCfg = result._tableCfgs._data[0];
|
|
316
|
+
tableCfg.columns['int'].type = 'numberBroken'; // Break one of the types
|
|
317
|
+
return hip(result, true, false);
|
|
318
|
+
},
|
|
241
319
|
},
|
|
242
320
|
|
|
243
321
|
collections: {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rljson/rljson",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.16",
|
|
4
4
|
"packageManager": "pnpm@10.6.3",
|
|
5
5
|
"description": "The RLJSON data format specification",
|
|
6
6
|
"homepage": "https://github.com/rljson/rljson",
|
|
@@ -51,7 +51,7 @@
|
|
|
51
51
|
},
|
|
52
52
|
"dependencies": {
|
|
53
53
|
"@rljson/hash": "^0.0.12",
|
|
54
|
-
"@rljson/json": "^0.0.
|
|
54
|
+
"@rljson/json": "^0.0.18"
|
|
55
55
|
},
|
|
56
56
|
"pnpm": {
|
|
57
57
|
"onlyBuiltDependencies": [
|