@rljson/rljson 0.0.32 → 0.0.34
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/content/buffet.d.ts +4 -0
- package/dist/content/cake.d.ts +6 -2
- package/dist/content/ingredients.d.ts +2 -3
- package/dist/content/layer.d.ts +1 -1
- package/dist/content/revision.d.ts +37 -0
- package/dist/content/table-cfg.d.ts +24 -4
- package/dist/example.d.ts +1 -1
- package/dist/rljson.d.ts +2 -1
- package/dist/rljson.js +152 -47
- package/dist/src/example.ts +38 -22
- package/dist/typedefs.d.ts +7 -1
- package/dist/validate/base-validator.d.ts +2 -0
- package/package.json +10 -10
package/dist/content/buffet.d.ts
CHANGED
package/dist/content/cake.d.ts
CHANGED
|
@@ -19,11 +19,11 @@ export interface Cake extends Json {
|
|
|
19
19
|
* must match these slice ids of the layers.
|
|
20
20
|
* The slice ids can be found in the idSet table.
|
|
21
21
|
*/
|
|
22
|
-
|
|
22
|
+
sliceIds: SliceIdsRef;
|
|
23
23
|
/**
|
|
24
24
|
* The table containing the slice ids of the layer
|
|
25
25
|
*/
|
|
26
|
-
sliceIdsTable
|
|
26
|
+
sliceIdsTable: TableKey;
|
|
27
27
|
/**
|
|
28
28
|
* The table containing the slice layers defining the layers
|
|
29
29
|
*/
|
|
@@ -34,6 +34,10 @@ export interface Cake extends Json {
|
|
|
34
34
|
layers: {
|
|
35
35
|
[layerId: CakeLayerId]: LayerRef;
|
|
36
36
|
};
|
|
37
|
+
/**
|
|
38
|
+
* An optional ID of the cake.
|
|
39
|
+
*/
|
|
40
|
+
id?: string;
|
|
37
41
|
}
|
|
38
42
|
/**
|
|
39
43
|
* A table containing cakes
|
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
import { Json } from '@rljson/json';
|
|
2
1
|
import { NutritionalValues } from '../example/bakery-example.ts';
|
|
3
2
|
import { RljsonTable } from '../rljson.ts';
|
|
4
|
-
import { Ref } from '../typedefs.ts';
|
|
3
|
+
import { JsonWithId, Ref } from '../typedefs.ts';
|
|
5
4
|
/**
|
|
6
5
|
* A reference to a ingredients row in a ingredients table
|
|
7
6
|
*/
|
|
@@ -9,7 +8,7 @@ export type IngredientsRef = Ref;
|
|
|
9
8
|
/**
|
|
10
9
|
* A table containing ingredients
|
|
11
10
|
*/
|
|
12
|
-
export type IngredientsTable<T extends
|
|
11
|
+
export type IngredientsTable<T extends JsonWithId> = RljsonTable<T, 'ingredients'>;
|
|
13
12
|
/**
|
|
14
13
|
* Provides an example ingredients table for test purposes
|
|
15
14
|
*/
|
package/dist/content/layer.d.ts
CHANGED
|
@@ -19,7 +19,7 @@ export interface Layer extends Json {
|
|
|
19
19
|
* The item ids of the layer. If present, the item ids in `assign`
|
|
20
20
|
* must match these ids. The item id sets can be found in the sliceIds table.
|
|
21
21
|
*/
|
|
22
|
-
|
|
22
|
+
sliceIds?: SliceIdsRef;
|
|
23
23
|
/**
|
|
24
24
|
* The table containing the item ids of the layer
|
|
25
25
|
*/
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { Json } from '@rljson/json';
|
|
2
|
+
import { RljsonTable } from '../rljson.ts';
|
|
3
|
+
import { Ref, TableKey } from '../typedefs.ts';
|
|
4
|
+
/**
|
|
5
|
+
* Describes a revision of a row in a table
|
|
6
|
+
*/
|
|
7
|
+
export interface Revision extends Json {
|
|
8
|
+
/**
|
|
9
|
+
* The name of the table the revisione row belongs to
|
|
10
|
+
*/
|
|
11
|
+
table: TableKey;
|
|
12
|
+
/**
|
|
13
|
+
* The predecessor of the revision
|
|
14
|
+
*/
|
|
15
|
+
predecessor: Ref;
|
|
16
|
+
/**
|
|
17
|
+
* The successor of the revision
|
|
18
|
+
*/
|
|
19
|
+
successor: Ref;
|
|
20
|
+
/**
|
|
21
|
+
* The UTC timestamp of the revision
|
|
22
|
+
*/
|
|
23
|
+
timestamp: number;
|
|
24
|
+
/**
|
|
25
|
+
* The optional ID of the revisioned element.
|
|
26
|
+
* Can be used get all revisions of a specific ingredient.
|
|
27
|
+
*/
|
|
28
|
+
id?: string;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* A table containing revisions
|
|
32
|
+
*/
|
|
33
|
+
export type RevisionsTable = RljsonTable<Revision, 'ingredients'>;
|
|
34
|
+
/**
|
|
35
|
+
* Example revision object for test purposes
|
|
36
|
+
*/
|
|
37
|
+
export declare const exampleRevision: () => Revision;
|
|
@@ -9,10 +9,6 @@ export type TableCfgRef = Ref;
|
|
|
9
9
|
* A column configuration
|
|
10
10
|
*/
|
|
11
11
|
export interface ColumnCfg extends Json {
|
|
12
|
-
/**
|
|
13
|
-
* The key of the column used in data
|
|
14
|
-
*/
|
|
15
|
-
key: ColumnKey;
|
|
16
12
|
/**
|
|
17
13
|
* The type of the column
|
|
18
14
|
*/
|
|
@@ -43,6 +39,30 @@ export interface TableCfg extends Json {
|
|
|
43
39
|
* Needs to be increased when new columns are added.
|
|
44
40
|
*/
|
|
45
41
|
version: number;
|
|
42
|
+
/**
|
|
43
|
+
* Head tables serve as versioning entry points.
|
|
44
|
+
*
|
|
45
|
+
* - Head tables must contain an id column
|
|
46
|
+
* - Rows in an head table must contain a non-null id
|
|
47
|
+
* - Same row ids must refer to the same physical object
|
|
48
|
+
* - Head tables have no or only one parent table
|
|
49
|
+
*/
|
|
50
|
+
isHead: boolean;
|
|
51
|
+
/**
|
|
52
|
+
* Root tables are tables that have no parent table.
|
|
53
|
+
*
|
|
54
|
+
* - Root tables are also head tables
|
|
55
|
+
*/
|
|
56
|
+
isRoot: boolean;
|
|
57
|
+
/**
|
|
58
|
+
* Shared tables are tables that are used by multiple parents.
|
|
59
|
+
*
|
|
60
|
+
* - The don't need an id column
|
|
61
|
+
* - Same id can refer to different physical objects
|
|
62
|
+
* - Shared tables can have multiple parents
|
|
63
|
+
* - Shared tables can not be head or root tables
|
|
64
|
+
*/
|
|
65
|
+
isShared: boolean;
|
|
46
66
|
}
|
|
47
67
|
/**
|
|
48
68
|
* A table containing columns
|
package/dist/example.d.ts
CHANGED
package/dist/rljson.d.ts
CHANGED
|
@@ -3,6 +3,7 @@ import { BuffetsTable } from './content/buffet.ts';
|
|
|
3
3
|
import { CakesTable } from './content/cake.ts';
|
|
4
4
|
import { IngredientsTable } from './content/ingredients.ts';
|
|
5
5
|
import { LayersTable } from './content/layer.ts';
|
|
6
|
+
import { RevisionsTable } from './content/revision.ts';
|
|
6
7
|
import { SliceIdsTable } from './content/slice-ids.ts';
|
|
7
8
|
import { TableCfgRef, TablesCfgTable } from './content/table-cfg.ts';
|
|
8
9
|
import { ContentType, Ref, TableKey } from './typedefs.ts';
|
|
@@ -11,7 +12,7 @@ export declare const reservedTableKeys: string[];
|
|
|
11
12
|
/**
|
|
12
13
|
* One of the supported Rljson table types
|
|
13
14
|
*/
|
|
14
|
-
export type TableType = BuffetsTable | IngredientsTable<any> | LayersTable | SliceIdsTable | CakesTable;
|
|
15
|
+
export type TableType = BuffetsTable | IngredientsTable<any> | LayersTable | SliceIdsTable | CakesTable | RevisionsTable;
|
|
15
16
|
/** The rljson data format */
|
|
16
17
|
export interface Rljson extends Json {
|
|
17
18
|
[tableId: TableKey]: TableType;
|
package/dist/rljson.js
CHANGED
|
@@ -9,11 +9,20 @@ const bakeryExample = () => {
|
|
|
9
9
|
_type: "ingredients",
|
|
10
10
|
_data: [
|
|
11
11
|
{
|
|
12
|
+
id: "flour",
|
|
12
13
|
energy: 364,
|
|
13
14
|
fat: 0.98,
|
|
14
15
|
protein: 10.33,
|
|
15
16
|
carbohydrates: 76.31,
|
|
16
17
|
_hash: ""
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
id: "flour",
|
|
21
|
+
energy: 364.1,
|
|
22
|
+
fat: 0.981,
|
|
23
|
+
protein: 10.331,
|
|
24
|
+
carbohydrates: 76.311,
|
|
25
|
+
_hash: ""
|
|
17
26
|
}
|
|
18
27
|
],
|
|
19
28
|
_hash: ""
|
|
@@ -22,7 +31,7 @@ const bakeryExample = () => {
|
|
|
22
31
|
_type: "ingredients",
|
|
23
32
|
_data: [
|
|
24
33
|
{
|
|
25
|
-
|
|
34
|
+
id: "flour",
|
|
26
35
|
amountUnit: "g",
|
|
27
36
|
nutritionalValuesRef: nutritionalValues._data[0]._hash,
|
|
28
37
|
_hash: ""
|
|
@@ -34,6 +43,7 @@ const bakeryExample = () => {
|
|
|
34
43
|
_type: "ingredients",
|
|
35
44
|
_data: [
|
|
36
45
|
{
|
|
46
|
+
id: "flour",
|
|
37
47
|
ingredientsRef: ingredients._data[0]._hash,
|
|
38
48
|
quantity: 500,
|
|
39
49
|
_hash: ""
|
|
@@ -45,6 +55,7 @@ const bakeryExample = () => {
|
|
|
45
55
|
_type: "layers",
|
|
46
56
|
_data: [
|
|
47
57
|
{
|
|
58
|
+
id: "tastyCake",
|
|
48
59
|
ingredientsTable: "recipeIngredients",
|
|
49
60
|
assign: {
|
|
50
61
|
flour: recipeIngredients._data[0]._hash
|
|
@@ -70,23 +81,21 @@ const bakeryExample = () => {
|
|
|
70
81
|
_data: [
|
|
71
82
|
{
|
|
72
83
|
add: ["slice0", "slice1"],
|
|
73
|
-
remove: []
|
|
74
|
-
_hash: ""
|
|
84
|
+
remove: []
|
|
75
85
|
}
|
|
76
|
-
]
|
|
77
|
-
_hash: ""
|
|
86
|
+
]
|
|
78
87
|
});
|
|
79
88
|
const cakes = hip({
|
|
80
89
|
_type: "cakes",
|
|
81
90
|
_data: [
|
|
82
91
|
{
|
|
92
|
+
id: "cake1",
|
|
83
93
|
sliceIdsTable: "slices",
|
|
84
|
-
|
|
94
|
+
sliceIds: slices._data[0]._hash,
|
|
85
95
|
layersTable: "layers",
|
|
86
96
|
layers: {
|
|
87
97
|
flour: layers._data[0]._hash
|
|
88
|
-
}
|
|
89
|
-
_hash: ""
|
|
98
|
+
}
|
|
90
99
|
}
|
|
91
100
|
]
|
|
92
101
|
});
|
|
@@ -94,6 +103,7 @@ const bakeryExample = () => {
|
|
|
94
103
|
_type: "buffets",
|
|
95
104
|
_data: [
|
|
96
105
|
{
|
|
106
|
+
id: "salesCounter",
|
|
97
107
|
items: [
|
|
98
108
|
{
|
|
99
109
|
table: "cakes",
|
|
@@ -156,37 +166,32 @@ __publicField(_Example, "ok", {
|
|
|
156
166
|
_hash: "",
|
|
157
167
|
key: "table",
|
|
158
168
|
type: "ingredients",
|
|
169
|
+
isHead: false,
|
|
170
|
+
isRoot: false,
|
|
171
|
+
isShared: true,
|
|
159
172
|
columns: {
|
|
160
173
|
int: {
|
|
161
|
-
key: "int",
|
|
162
174
|
type: "number"
|
|
163
175
|
},
|
|
164
176
|
double: {
|
|
165
|
-
key: "double",
|
|
166
177
|
type: "number"
|
|
167
178
|
},
|
|
168
179
|
string: {
|
|
169
|
-
key: "string",
|
|
170
180
|
type: "string"
|
|
171
181
|
},
|
|
172
182
|
boolean: {
|
|
173
|
-
key: "boolean",
|
|
174
183
|
type: "boolean"
|
|
175
184
|
},
|
|
176
185
|
null: {
|
|
177
|
-
key: "null",
|
|
178
186
|
type: "null"
|
|
179
187
|
},
|
|
180
188
|
jsonArray: {
|
|
181
|
-
key: "jsonArray",
|
|
182
189
|
type: "jsonArray"
|
|
183
190
|
},
|
|
184
191
|
json: {
|
|
185
|
-
key: "json",
|
|
186
192
|
type: "json"
|
|
187
193
|
},
|
|
188
194
|
jsonValue: {
|
|
189
|
-
key: "jsonValue",
|
|
190
195
|
type: "jsonValue"
|
|
191
196
|
}
|
|
192
197
|
}
|
|
@@ -274,14 +279,14 @@ __publicField(_Example, "ok", {
|
|
|
274
279
|
const ingredient1 = ingredients._data[1];
|
|
275
280
|
const layer0 = hip({
|
|
276
281
|
sliceIdsTable: "sliceIds",
|
|
277
|
-
|
|
282
|
+
sliceIds: "MgHRBYSrhpyl4rvsOmAWcQ",
|
|
278
283
|
ingredientsTable: "ingredients",
|
|
279
284
|
assign: {}
|
|
280
285
|
});
|
|
281
286
|
const layer1 = hip({
|
|
282
287
|
base: layer0._hash,
|
|
283
288
|
sliceIdsTable: "sliceIds",
|
|
284
|
-
|
|
289
|
+
sliceIds: "MgHRBYSrhpyl4rvsOmAWcQ",
|
|
285
290
|
ingredientsTable: "ingredients",
|
|
286
291
|
assign: {
|
|
287
292
|
id0: ingredient0._hash,
|
|
@@ -294,7 +299,7 @@ __publicField(_Example, "ok", {
|
|
|
294
299
|
});
|
|
295
300
|
const cake = hip({
|
|
296
301
|
sliceIdsTable: "sliceIds",
|
|
297
|
-
|
|
302
|
+
sliceIds: sliceIds._data[0]._hash,
|
|
298
303
|
layersTable: "layers",
|
|
299
304
|
layers: {
|
|
300
305
|
layer0: layer0._hash,
|
|
@@ -399,7 +404,10 @@ __publicField(_Example, "broken", {
|
|
|
399
404
|
const result = _Example.ok.singleRow();
|
|
400
405
|
const tableCfg = result.tableCfgs._data[0];
|
|
401
406
|
tableCfg.columns["int"].type = "numberBroken";
|
|
402
|
-
return hip(result,
|
|
407
|
+
return hip(result, {
|
|
408
|
+
updateExistingHashes: true,
|
|
409
|
+
throwOnWrongHashes: false
|
|
410
|
+
});
|
|
403
411
|
}
|
|
404
412
|
},
|
|
405
413
|
layers: {
|
|
@@ -407,13 +415,19 @@ __publicField(_Example, "broken", {
|
|
|
407
415
|
const result = _Example.ok.complete();
|
|
408
416
|
const layer1 = result.layers._data[1];
|
|
409
417
|
layer1.base = "MISSING";
|
|
410
|
-
return hip(result,
|
|
418
|
+
return hip(result, {
|
|
419
|
+
updateExistingHashes: true,
|
|
420
|
+
throwOnWrongHashes: false
|
|
421
|
+
});
|
|
411
422
|
},
|
|
412
423
|
missingSliceIdSet: () => {
|
|
413
424
|
const result = _Example.ok.complete();
|
|
414
425
|
const layer1 = result.layers._data[1];
|
|
415
|
-
layer1.
|
|
416
|
-
return hip(result,
|
|
426
|
+
layer1.sliceIds = "MISSING1";
|
|
427
|
+
return hip(result, {
|
|
428
|
+
updateExistingHashes: true,
|
|
429
|
+
throwOnWrongHashes: false
|
|
430
|
+
});
|
|
417
431
|
},
|
|
418
432
|
missingAssignedIngredientTable: () => {
|
|
419
433
|
const result = _Example.ok.complete();
|
|
@@ -423,27 +437,39 @@ __publicField(_Example, "broken", {
|
|
|
423
437
|
missingAssignedIngredient: () => {
|
|
424
438
|
const result = _Example.ok.complete();
|
|
425
439
|
result.ingredients._data.splice(1, 2);
|
|
426
|
-
return hip(result,
|
|
440
|
+
return hip(result, {
|
|
441
|
+
updateExistingHashes: true,
|
|
442
|
+
throwOnWrongHashes: false
|
|
443
|
+
});
|
|
427
444
|
}
|
|
428
445
|
},
|
|
429
446
|
cakes: {
|
|
430
447
|
missingSliceIdSet: () => {
|
|
431
448
|
const result = _Example.ok.complete();
|
|
432
|
-
result.cakes._data[0].
|
|
433
|
-
hip(result.cakes,
|
|
449
|
+
result.cakes._data[0].sliceIds = "MISSING";
|
|
450
|
+
hip(result.cakes, {
|
|
451
|
+
updateExistingHashes: true,
|
|
452
|
+
throwOnWrongHashes: false
|
|
453
|
+
});
|
|
434
454
|
return result;
|
|
435
455
|
},
|
|
436
456
|
missingLayersTable: () => {
|
|
437
457
|
const result = _Example.ok.complete();
|
|
438
458
|
result.cakes._data[0].layersTable = "MISSING";
|
|
439
|
-
hip(result.cakes,
|
|
459
|
+
hip(result.cakes, {
|
|
460
|
+
updateExistingHashes: true,
|
|
461
|
+
throwOnWrongHashes: false
|
|
462
|
+
});
|
|
440
463
|
return result;
|
|
441
464
|
},
|
|
442
465
|
missingCakeLayer: () => {
|
|
443
466
|
const result = _Example.ok.complete();
|
|
444
467
|
result.cakes._data[0].layers["layer0"] = "MISSING0";
|
|
445
468
|
result.cakes._data[0].layers["layer1"] = "MISSING1";
|
|
446
|
-
hip(result.cakes,
|
|
469
|
+
hip(result.cakes, {
|
|
470
|
+
updateExistingHashes: true,
|
|
471
|
+
throwOnWrongHashes: false
|
|
472
|
+
});
|
|
447
473
|
return result;
|
|
448
474
|
}
|
|
449
475
|
},
|
|
@@ -453,7 +479,7 @@ __publicField(_Example, "broken", {
|
|
|
453
479
|
const buffet = result.buffets._data[0];
|
|
454
480
|
buffet.items[0].table = "MISSING0";
|
|
455
481
|
buffet.items[1].table = "MISSING1";
|
|
456
|
-
hip(result, true, false);
|
|
482
|
+
hip(result, { updateExistingHashes: true, throwOnWrongHashes: false });
|
|
457
483
|
return result;
|
|
458
484
|
},
|
|
459
485
|
missingItems: () => {
|
|
@@ -461,7 +487,7 @@ __publicField(_Example, "broken", {
|
|
|
461
487
|
const buffet = result.buffets._data[0];
|
|
462
488
|
buffet.items[0].ref = "MISSING0";
|
|
463
489
|
buffet.items[1].ref = "MISSING1";
|
|
464
|
-
hip(result, true, false);
|
|
490
|
+
hip(result, { updateExistingHashes: true, throwOnWrongHashes: false });
|
|
465
491
|
return result;
|
|
466
492
|
}
|
|
467
493
|
}
|
|
@@ -475,15 +501,16 @@ const exampleTableCfg = (tableCfg = void 0) => {
|
|
|
475
501
|
version: 1,
|
|
476
502
|
columns: (tableCfg == null ? void 0 : tableCfg.columns) ?? {
|
|
477
503
|
a: {
|
|
478
|
-
key: "a",
|
|
479
504
|
type: "string"
|
|
480
505
|
},
|
|
481
506
|
b: {
|
|
482
|
-
key: "b",
|
|
483
507
|
type: "number"
|
|
484
508
|
}
|
|
485
509
|
},
|
|
486
|
-
type: (tableCfg == null ? void 0 : tableCfg.type) ?? "ingredients"
|
|
510
|
+
type: (tableCfg == null ? void 0 : tableCfg.type) ?? "ingredients",
|
|
511
|
+
isHead: true,
|
|
512
|
+
isRoot: true,
|
|
513
|
+
isShared: false
|
|
487
514
|
};
|
|
488
515
|
};
|
|
489
516
|
// @license
|
|
@@ -511,7 +538,13 @@ const rljsonIndexed = (rljson) => {
|
|
|
511
538
|
};
|
|
512
539
|
// @license
|
|
513
540
|
const reservedFieldNames = ["_type", "_data"];
|
|
514
|
-
const reservedTableKeys = [
|
|
541
|
+
const reservedTableKeys = [
|
|
542
|
+
"_hash",
|
|
543
|
+
"sliceIds",
|
|
544
|
+
"tableCfgs",
|
|
545
|
+
"reverseRefs",
|
|
546
|
+
"revisions"
|
|
547
|
+
];
|
|
515
548
|
const exampleRljson = () => Example.ok.singleRow();
|
|
516
549
|
const iterateTables = (rljson, callback) => {
|
|
517
550
|
for (const tableKey in rljson) {
|
|
@@ -587,6 +620,8 @@ class _BaseValidator {
|
|
|
587
620
|
() => this._missingColumnConfigs(),
|
|
588
621
|
() => this._dataDoesNotMatchColumnConfig(),
|
|
589
622
|
() => this._tableTypesDoNotMatch(),
|
|
623
|
+
() => this._tableCfgHasRootHeadSharedError(),
|
|
624
|
+
() => this._rootOrHeadTableHasNoIdColumn(),
|
|
590
625
|
// Check references
|
|
591
626
|
() => this._refsNotFound(),
|
|
592
627
|
// Check layers
|
|
@@ -893,6 +928,76 @@ class _BaseValidator {
|
|
|
893
928
|
}
|
|
894
929
|
}
|
|
895
930
|
// ...........................................................................
|
|
931
|
+
_tableCfgHasRootHeadSharedError() {
|
|
932
|
+
const rljson = this.rljson;
|
|
933
|
+
const inconsistentTableCfgs = [];
|
|
934
|
+
for (const tableKey of this.tableKeys) {
|
|
935
|
+
const table = rljson[tableKey];
|
|
936
|
+
const cfgRef = table._tableCfg;
|
|
937
|
+
if (!cfgRef) {
|
|
938
|
+
continue;
|
|
939
|
+
}
|
|
940
|
+
const cfg = this.rljsonIndexed.tableCfgs._data[cfgRef];
|
|
941
|
+
const { isRoot, isHead, isShared } = cfg;
|
|
942
|
+
if (isShared && (isRoot || isHead)) {
|
|
943
|
+
inconsistentTableCfgs.push({
|
|
944
|
+
error: "Tables with isShared = true must have isRoot = false and isHead = false",
|
|
945
|
+
table: tableKey,
|
|
946
|
+
tableCfg: cfgRef
|
|
947
|
+
});
|
|
948
|
+
} else if (isRoot && !isHead) {
|
|
949
|
+
inconsistentTableCfgs.push({
|
|
950
|
+
error: "Tables with isRoot = true must also have isHead = true",
|
|
951
|
+
table: tableKey,
|
|
952
|
+
tableCfg: cfgRef
|
|
953
|
+
});
|
|
954
|
+
} else if (!isRoot && !isHead && !isShared) {
|
|
955
|
+
inconsistentTableCfgs.push({
|
|
956
|
+
error: "Tables must be either root, root+head or shared",
|
|
957
|
+
table: tableKey,
|
|
958
|
+
tableCfg: cfgRef
|
|
959
|
+
});
|
|
960
|
+
}
|
|
961
|
+
}
|
|
962
|
+
if (inconsistentTableCfgs.length > 0) {
|
|
963
|
+
this.errors.tableCfgHasRootHeadSharedError = {
|
|
964
|
+
error: "Table configs have inconsistent root/head/shared settings",
|
|
965
|
+
tables: inconsistentTableCfgs
|
|
966
|
+
};
|
|
967
|
+
}
|
|
968
|
+
}
|
|
969
|
+
// ...........................................................................
|
|
970
|
+
_rootOrHeadTableHasNoIdColumn() {
|
|
971
|
+
const rljson = this.rljson;
|
|
972
|
+
const rootOrHeadTablesWithoutIdColumns = [];
|
|
973
|
+
for (const tableKey of this.tableKeys) {
|
|
974
|
+
const table = rljson[tableKey];
|
|
975
|
+
const cfgRef = table._tableCfg;
|
|
976
|
+
if (!cfgRef) {
|
|
977
|
+
continue;
|
|
978
|
+
}
|
|
979
|
+
const cfg = this.rljsonIndexed.tableCfgs._data[cfgRef];
|
|
980
|
+
const isRootOrHeadTable = cfg.isRoot || cfg.isHead;
|
|
981
|
+
if (!isRootOrHeadTable) {
|
|
982
|
+
continue;
|
|
983
|
+
}
|
|
984
|
+
const columns = cfg.columns;
|
|
985
|
+
const idField = columns["id"];
|
|
986
|
+
if (!idField) {
|
|
987
|
+
rootOrHeadTablesWithoutIdColumns.push({
|
|
988
|
+
table: tableKey,
|
|
989
|
+
tableCfg: cfgRef
|
|
990
|
+
});
|
|
991
|
+
}
|
|
992
|
+
}
|
|
993
|
+
if (rootOrHeadTablesWithoutIdColumns.length > 0) {
|
|
994
|
+
this.errors.rootOrHeadTableHasNoIdColumn = {
|
|
995
|
+
error: "Root or head tables must have an id column",
|
|
996
|
+
tables: rootOrHeadTablesWithoutIdColumns
|
|
997
|
+
};
|
|
998
|
+
}
|
|
999
|
+
}
|
|
1000
|
+
// ...........................................................................
|
|
896
1001
|
_dataHasWrongType() {
|
|
897
1002
|
const rljson = this.rljson;
|
|
898
1003
|
const tablesWithWrongType = [];
|
|
@@ -1043,7 +1148,7 @@ class _BaseValidator {
|
|
|
1043
1148
|
}
|
|
1044
1149
|
const layersTable = table;
|
|
1045
1150
|
for (const layer of layersTable._data) {
|
|
1046
|
-
const idSetRef = layer.
|
|
1151
|
+
const idSetRef = layer.sliceIds;
|
|
1047
1152
|
if (!idSetRef) {
|
|
1048
1153
|
continue;
|
|
1049
1154
|
}
|
|
@@ -1124,16 +1229,16 @@ class _BaseValidator {
|
|
|
1124
1229
|
}
|
|
1125
1230
|
const cakesTable = table;
|
|
1126
1231
|
for (const cake of cakesTable._data) {
|
|
1127
|
-
const
|
|
1128
|
-
if (!
|
|
1232
|
+
const sliceIdsTableName = cake.sliceIdsTable;
|
|
1233
|
+
if (!sliceIdsTableName) {
|
|
1129
1234
|
continue;
|
|
1130
1235
|
}
|
|
1131
|
-
const
|
|
1132
|
-
if (!
|
|
1236
|
+
const sliceIdsTable = this.rljsonIndexed[sliceIdsTableName];
|
|
1237
|
+
if (!sliceIdsTable) {
|
|
1133
1238
|
brokenCakes.push({
|
|
1134
1239
|
cakeTable: tableKey,
|
|
1135
1240
|
brokenCake: cake._hash,
|
|
1136
|
-
|
|
1241
|
+
missingSliceIdsTable: sliceIdsTableName
|
|
1137
1242
|
});
|
|
1138
1243
|
}
|
|
1139
1244
|
}
|
|
@@ -1153,18 +1258,18 @@ class _BaseValidator {
|
|
|
1153
1258
|
}
|
|
1154
1259
|
const cakesTable = table;
|
|
1155
1260
|
for (const cake of cakesTable._data) {
|
|
1156
|
-
const
|
|
1157
|
-
if (!
|
|
1261
|
+
const sliceIdsRef = cake.sliceIds;
|
|
1262
|
+
if (!sliceIdsRef) {
|
|
1158
1263
|
continue;
|
|
1159
1264
|
}
|
|
1160
|
-
const
|
|
1161
|
-
const
|
|
1162
|
-
const
|
|
1163
|
-
if (!
|
|
1265
|
+
const sliceIdsTableName = cake.sliceIdsTable;
|
|
1266
|
+
const sliceIdsTable = this.rljsonIndexed[sliceIdsTableName];
|
|
1267
|
+
const sliceIds = sliceIdsTable._data[sliceIdsRef];
|
|
1268
|
+
if (!sliceIds) {
|
|
1164
1269
|
brokenCakes.push({
|
|
1165
1270
|
cakeTable: tableKey,
|
|
1166
1271
|
brokenCake: cake._hash,
|
|
1167
|
-
missingSliceIds:
|
|
1272
|
+
missingSliceIds: sliceIdsRef
|
|
1168
1273
|
});
|
|
1169
1274
|
}
|
|
1170
1275
|
}
|
package/dist/src/example.ts
CHANGED
|
@@ -49,37 +49,32 @@ export class Example {
|
|
|
49
49
|
_hash: '',
|
|
50
50
|
key: 'table',
|
|
51
51
|
type: 'ingredients',
|
|
52
|
+
isHead: false,
|
|
53
|
+
isRoot: false,
|
|
54
|
+
isShared: true,
|
|
52
55
|
columns: {
|
|
53
56
|
int: {
|
|
54
|
-
key: 'int',
|
|
55
57
|
type: 'number',
|
|
56
58
|
},
|
|
57
59
|
double: {
|
|
58
|
-
key: 'double',
|
|
59
60
|
type: 'number',
|
|
60
61
|
},
|
|
61
62
|
string: {
|
|
62
|
-
key: 'string',
|
|
63
63
|
type: 'string',
|
|
64
64
|
},
|
|
65
65
|
boolean: {
|
|
66
|
-
key: 'boolean',
|
|
67
66
|
type: 'boolean',
|
|
68
67
|
},
|
|
69
68
|
null: {
|
|
70
|
-
key: 'null',
|
|
71
69
|
type: 'null',
|
|
72
70
|
},
|
|
73
71
|
jsonArray: {
|
|
74
|
-
key: 'jsonArray',
|
|
75
72
|
type: 'jsonArray',
|
|
76
73
|
},
|
|
77
74
|
json: {
|
|
78
|
-
key: 'json',
|
|
79
75
|
type: 'json',
|
|
80
76
|
},
|
|
81
77
|
jsonValue: {
|
|
82
|
-
key: 'jsonValue',
|
|
83
78
|
type: 'jsonValue',
|
|
84
79
|
},
|
|
85
80
|
},
|
|
@@ -174,7 +169,7 @@ export class Example {
|
|
|
174
169
|
|
|
175
170
|
const layer0: Layer = hip({
|
|
176
171
|
sliceIdsTable: 'sliceIds',
|
|
177
|
-
|
|
172
|
+
sliceIds: 'MgHRBYSrhpyl4rvsOmAWcQ',
|
|
178
173
|
ingredientsTable: 'ingredients',
|
|
179
174
|
assign: {},
|
|
180
175
|
});
|
|
@@ -182,7 +177,7 @@ export class Example {
|
|
|
182
177
|
const layer1: Layer = hip({
|
|
183
178
|
base: layer0._hash as string,
|
|
184
179
|
sliceIdsTable: 'sliceIds',
|
|
185
|
-
|
|
180
|
+
sliceIds: 'MgHRBYSrhpyl4rvsOmAWcQ',
|
|
186
181
|
ingredientsTable: 'ingredients',
|
|
187
182
|
assign: {
|
|
188
183
|
id0: ingredient0._hash,
|
|
@@ -197,7 +192,7 @@ export class Example {
|
|
|
197
192
|
|
|
198
193
|
const cake: Cake = hip({
|
|
199
194
|
sliceIdsTable: 'sliceIds',
|
|
200
|
-
|
|
195
|
+
sliceIds: sliceIds._data[0]._hash as string,
|
|
201
196
|
layersTable: 'layers',
|
|
202
197
|
layers: {
|
|
203
198
|
layer0: layer0._hash as string,
|
|
@@ -309,7 +304,10 @@ export class Example {
|
|
|
309
304
|
const result = Example.ok.singleRow();
|
|
310
305
|
const tableCfg = result.tableCfgs._data[0];
|
|
311
306
|
tableCfg.columns['int'].type = 'numberBroken'; // Break one of the types
|
|
312
|
-
return hip(result,
|
|
307
|
+
return hip(result, {
|
|
308
|
+
updateExistingHashes: true,
|
|
309
|
+
throwOnWrongHashes: false,
|
|
310
|
+
});
|
|
313
311
|
},
|
|
314
312
|
},
|
|
315
313
|
|
|
@@ -320,17 +318,23 @@ export class Example {
|
|
|
320
318
|
layer1.base = 'MISSING'; // Missing base
|
|
321
319
|
|
|
322
320
|
// Recalculate hashes
|
|
323
|
-
return hip(result,
|
|
321
|
+
return hip(result, {
|
|
322
|
+
updateExistingHashes: true,
|
|
323
|
+
throwOnWrongHashes: false,
|
|
324
|
+
});
|
|
324
325
|
},
|
|
325
326
|
|
|
326
327
|
missingSliceIdSet: (): Rljson => {
|
|
327
328
|
const result = Example.ok.complete();
|
|
328
329
|
const layer1 = result.layers._data[1];
|
|
329
330
|
|
|
330
|
-
layer1.
|
|
331
|
+
layer1.sliceIds = 'MISSING1';
|
|
331
332
|
|
|
332
333
|
// Recalculate hashes
|
|
333
|
-
return hip(result,
|
|
334
|
+
return hip(result, {
|
|
335
|
+
updateExistingHashes: true,
|
|
336
|
+
throwOnWrongHashes: false,
|
|
337
|
+
});
|
|
334
338
|
},
|
|
335
339
|
|
|
336
340
|
missingAssignedIngredientTable: (): Rljson => {
|
|
@@ -342,22 +346,31 @@ export class Example {
|
|
|
342
346
|
missingAssignedIngredient: (): Rljson => {
|
|
343
347
|
const result = Example.ok.complete();
|
|
344
348
|
result.ingredients._data.splice(1, 2); // Remove an ingredient that is assigned
|
|
345
|
-
return hip(result,
|
|
349
|
+
return hip(result, {
|
|
350
|
+
updateExistingHashes: true,
|
|
351
|
+
throwOnWrongHashes: false,
|
|
352
|
+
});
|
|
346
353
|
},
|
|
347
354
|
},
|
|
348
355
|
|
|
349
356
|
cakes: {
|
|
350
357
|
missingSliceIdSet: (): Rljson => {
|
|
351
358
|
const result = Example.ok.complete();
|
|
352
|
-
result.cakes._data[0].
|
|
353
|
-
hip(result.cakes,
|
|
359
|
+
result.cakes._data[0].sliceIds = 'MISSING'; // Missing ID set
|
|
360
|
+
hip(result.cakes, {
|
|
361
|
+
updateExistingHashes: true,
|
|
362
|
+
throwOnWrongHashes: false,
|
|
363
|
+
});
|
|
354
364
|
return result;
|
|
355
365
|
},
|
|
356
366
|
|
|
357
367
|
missingLayersTable: (): Rljson => {
|
|
358
368
|
const result = Example.ok.complete();
|
|
359
369
|
result.cakes._data[0].layersTable = 'MISSING'; // Missing layers table
|
|
360
|
-
hip(result.cakes,
|
|
370
|
+
hip(result.cakes, {
|
|
371
|
+
updateExistingHashes: true,
|
|
372
|
+
throwOnWrongHashes: false,
|
|
373
|
+
});
|
|
361
374
|
return result;
|
|
362
375
|
},
|
|
363
376
|
|
|
@@ -365,7 +378,10 @@ export class Example {
|
|
|
365
378
|
const result = Example.ok.complete();
|
|
366
379
|
result.cakes._data[0].layers['layer0'] = 'MISSING0';
|
|
367
380
|
result.cakes._data[0].layers['layer1'] = 'MISSING1';
|
|
368
|
-
hip(result.cakes,
|
|
381
|
+
hip(result.cakes, {
|
|
382
|
+
updateExistingHashes: true,
|
|
383
|
+
throwOnWrongHashes: false,
|
|
384
|
+
});
|
|
369
385
|
return result;
|
|
370
386
|
},
|
|
371
387
|
},
|
|
@@ -376,7 +392,7 @@ export class Example {
|
|
|
376
392
|
const buffet = result.buffets._data[0];
|
|
377
393
|
buffet.items[0].table = 'MISSING0';
|
|
378
394
|
buffet.items[1].table = 'MISSING1';
|
|
379
|
-
hip(result, true, false);
|
|
395
|
+
hip(result, { updateExistingHashes: true, throwOnWrongHashes: false });
|
|
380
396
|
return result;
|
|
381
397
|
},
|
|
382
398
|
|
|
@@ -385,7 +401,7 @@ export class Example {
|
|
|
385
401
|
const buffet = result.buffets._data[0];
|
|
386
402
|
buffet.items[0].ref = 'MISSING0';
|
|
387
403
|
buffet.items[1].ref = 'MISSING1';
|
|
388
|
-
hip(result, true, false);
|
|
404
|
+
hip(result, { updateExistingHashes: true, throwOnWrongHashes: false });
|
|
389
405
|
return result;
|
|
390
406
|
},
|
|
391
407
|
},
|
package/dist/typedefs.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { JsonKey } from '@rljson/json';
|
|
1
|
+
import { Json, JsonKey } from '@rljson/json';
|
|
2
2
|
/**
|
|
3
3
|
* A ref is a hash that references to another element
|
|
4
4
|
*/
|
|
@@ -37,3 +37,9 @@ export declare const exampleTypedefs: () => {
|
|
|
37
37
|
tableKey: TableKey;
|
|
38
38
|
contentType: ContentType;
|
|
39
39
|
};
|
|
40
|
+
/**
|
|
41
|
+
* A json object with an optional id
|
|
42
|
+
*/
|
|
43
|
+
export type JsonWithId = Json & {
|
|
44
|
+
id?: string;
|
|
45
|
+
};
|
|
@@ -16,6 +16,8 @@ export interface BaseErrors extends Errors {
|
|
|
16
16
|
columnConfigNotFound?: Json;
|
|
17
17
|
dataDoesNotMatchColumnConfig?: Json;
|
|
18
18
|
tableTypesDoNotMatch?: Json;
|
|
19
|
+
rootOrHeadTableHasNoIdColumn?: Json;
|
|
20
|
+
tableCfgHasRootHeadSharedError?: Json;
|
|
19
21
|
refsNotFound?: Json;
|
|
20
22
|
layerBasesNotFound?: Json;
|
|
21
23
|
layerSliceIdsTableNotFound?: Json;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rljson/rljson",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.34",
|
|
4
4
|
"packageManager": "pnpm@10.6.3",
|
|
5
5
|
"description": "The RLJSON data format specification",
|
|
6
6
|
"homepage": "https://github.com/rljson/rljson",
|
|
@@ -29,10 +29,10 @@
|
|
|
29
29
|
"updateGoldens": "cross-env UPDATE_GOLDENS=true npm test"
|
|
30
30
|
},
|
|
31
31
|
"devDependencies": {
|
|
32
|
-
"@types/node": "^22.13.
|
|
33
|
-
"@typescript-eslint/eslint-plugin": "^8.
|
|
34
|
-
"@typescript-eslint/parser": "^8.
|
|
35
|
-
"@vitest/coverage-v8": "^3.
|
|
32
|
+
"@types/node": "^22.13.17",
|
|
33
|
+
"@typescript-eslint/eslint-plugin": "^8.29.0",
|
|
34
|
+
"@typescript-eslint/parser": "^8.29.0",
|
|
35
|
+
"@vitest/coverage-v8": "^3.1.1",
|
|
36
36
|
"cross-env": "^7.0.3",
|
|
37
37
|
"eslint": "^9.23.0",
|
|
38
38
|
"eslint-plugin-jsdoc": "^50.6.9",
|
|
@@ -41,16 +41,16 @@
|
|
|
41
41
|
"jsdoc": "^4.0.4",
|
|
42
42
|
"read-pkg": "^9.0.1",
|
|
43
43
|
"typescript": "~5.8.2",
|
|
44
|
-
"typescript-eslint": "^8.
|
|
45
|
-
"vite": "^6.2.
|
|
46
|
-
"vite-node": "^3.
|
|
44
|
+
"typescript-eslint": "^8.29.0",
|
|
45
|
+
"vite": "^6.2.4",
|
|
46
|
+
"vite-node": "^3.1.1",
|
|
47
47
|
"vite-plugin-dts": "^4.5.3",
|
|
48
48
|
"vite-tsconfig-paths": "^5.1.4",
|
|
49
|
-
"vitest": "^3.
|
|
49
|
+
"vitest": "^3.1.1",
|
|
50
50
|
"vitest-dom": "^0.1.1"
|
|
51
51
|
},
|
|
52
52
|
"dependencies": {
|
|
53
|
-
"@rljson/hash": "^0.0.
|
|
53
|
+
"@rljson/hash": "^0.0.13",
|
|
54
54
|
"@rljson/json": "^0.0.18"
|
|
55
55
|
},
|
|
56
56
|
"pnpm": {
|