@rljson/rljson 0.0.62 → 0.0.65
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/table-cfg.d.ts +17 -0
- package/dist/example/bakery-example.d.ts +2 -2
- package/dist/history/history.d.ts +24 -0
- package/dist/index.d.ts +3 -2
- package/dist/insert/insert-validator.d.ts +47 -0
- package/dist/insert/insert.d.ts +20 -0
- package/dist/rljson.d.ts +2 -2
- package/dist/rljson.js +183 -90
- package/dist/route/route.d.ts +5 -5
- package/dist/src/example.ts +19 -0
- package/dist/typedefs.d.ts +1 -1
- package/package.json +11 -11
- package/dist/edit/edit-validator.d.ts +0 -24
- package/dist/edit/edit.d.ts +0 -41
|
@@ -17,6 +17,23 @@ export interface ColumnCfg extends Json {
|
|
|
17
17
|
* The type of the column
|
|
18
18
|
*/
|
|
19
19
|
type: JsonValueType;
|
|
20
|
+
/**
|
|
21
|
+
* An optional long title of the column
|
|
22
|
+
*/
|
|
23
|
+
titleLong: string;
|
|
24
|
+
/**
|
|
25
|
+
* An optional short title of the column
|
|
26
|
+
*/
|
|
27
|
+
titleShort: string;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* A column configuration with route
|
|
31
|
+
*/
|
|
32
|
+
export interface ColumnCfgWithRoute extends ColumnCfg {
|
|
33
|
+
/**
|
|
34
|
+
* An optional route for reference columns
|
|
35
|
+
*/
|
|
36
|
+
route: string;
|
|
20
37
|
}
|
|
21
38
|
/**
|
|
22
39
|
* Describes the configuration of a table, i.e. table metadata and columns
|
|
@@ -4,7 +4,7 @@ import { CakesTable } from '../content/cake.ts';
|
|
|
4
4
|
import { ComponentsTable } from '../content/components.ts';
|
|
5
5
|
import { LayersTable } from '../content/layer.ts';
|
|
6
6
|
import { SliceIdsTable } from '../content/slice-ids.ts';
|
|
7
|
-
import {
|
|
7
|
+
import { History } from '../history/history.ts';
|
|
8
8
|
import { Rljson } from '../rljson.ts';
|
|
9
9
|
import { Ref } from '../typedefs.ts';
|
|
10
10
|
export interface Ingredient extends Json {
|
|
@@ -32,6 +32,6 @@ export interface Bakery extends Rljson {
|
|
|
32
32
|
recipeIngredients: ComponentsTable<RecipIngredient>;
|
|
33
33
|
ingredients: ComponentsTable<Ingredient>;
|
|
34
34
|
nutritionalValues: ComponentsTable<NutritionalValues>;
|
|
35
|
-
|
|
35
|
+
ingredientsHistory: History<'Ingredients'>;
|
|
36
36
|
}
|
|
37
37
|
export declare const bakeryExample: () => Bakery;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { TableCfg } from '../content/table-cfg.ts';
|
|
2
|
+
import { RljsonTable } from '../rljson.ts';
|
|
3
|
+
import { RouteRef } from '../route/route.ts';
|
|
4
|
+
import { Ref } from '../typedefs.ts';
|
|
5
|
+
export type HistoryTimeId = string;
|
|
6
|
+
export type HistoryRow<Str extends string> = {
|
|
7
|
+
[key in Str as `${Uncapitalize<string & key>}Ref`]: string;
|
|
8
|
+
} & {
|
|
9
|
+
timeId: HistoryTimeId;
|
|
10
|
+
route: RouteRef;
|
|
11
|
+
origin?: Ref;
|
|
12
|
+
previous?: HistoryTimeId[];
|
|
13
|
+
};
|
|
14
|
+
export type History<Str extends string> = RljsonTable<HistoryRow<Str>, 'history'>;
|
|
15
|
+
/**
|
|
16
|
+
* Creates a TableCfg for a History table for the given table configuration
|
|
17
|
+
* @param tableCfg - The table configuration to create the History table for
|
|
18
|
+
* @returns The TableCfg for the History table
|
|
19
|
+
*/
|
|
20
|
+
export declare const createHistoryTableCfg: (tableCfg: TableCfg) => TableCfg;
|
|
21
|
+
/**
|
|
22
|
+
* Provides an example history table for test purposes
|
|
23
|
+
*/
|
|
24
|
+
export declare const exampleHistoryTable: () => History<any>;
|
package/dist/index.d.ts
CHANGED
|
@@ -5,10 +5,11 @@ export * from './content/layer.ts';
|
|
|
5
5
|
export * from './content/revision.ts';
|
|
6
6
|
export * from './content/slice-ids.ts';
|
|
7
7
|
export * from './content/table-cfg.ts';
|
|
8
|
-
export * from './edit/edit-validator.ts';
|
|
9
|
-
export * from './edit/edit.ts';
|
|
10
8
|
export * from './example.ts';
|
|
11
9
|
export * from './example/bakery-example.ts';
|
|
10
|
+
export * from './history/history.ts';
|
|
11
|
+
export * from './insert/insert-validator.ts';
|
|
12
|
+
export * from './insert/insert.ts';
|
|
12
13
|
export * from './rljson.ts';
|
|
13
14
|
export * from './route/route.ts';
|
|
14
15
|
export * from './tools/remove-duplicates.ts';
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { Json } from '@rljson/json';
|
|
2
|
+
import { Errors } from '../validate/validate.ts';
|
|
3
|
+
import { Insert } from './insert.ts';
|
|
4
|
+
export interface InsertErrors extends Errors {
|
|
5
|
+
invalidObject?: Json;
|
|
6
|
+
parameterInvalid?: Json;
|
|
7
|
+
parameterRouteInvalid?: Json;
|
|
8
|
+
parameterCommandInvalid?: Json;
|
|
9
|
+
parameterValueInvalid?: Json;
|
|
10
|
+
parameterOriginInvalid?: Json;
|
|
11
|
+
parameterPreviousInvalid?: Json;
|
|
12
|
+
routeInvalid?: Json;
|
|
13
|
+
dataRouteMismatch?: Json;
|
|
14
|
+
}
|
|
15
|
+
export declare class InsertValidator<T extends Json> {
|
|
16
|
+
private _insert;
|
|
17
|
+
errors: InsertErrors;
|
|
18
|
+
/**
|
|
19
|
+
* Indicates whether there are any errors
|
|
20
|
+
* @returns boolean - True if there are errors, false otherwise
|
|
21
|
+
*/
|
|
22
|
+
get hasErrors(): boolean;
|
|
23
|
+
constructor(_insert: Insert<T>);
|
|
24
|
+
/**
|
|
25
|
+
* Validates the Insert object
|
|
26
|
+
* @returns InsertErrors - The errors found during validation
|
|
27
|
+
*/
|
|
28
|
+
validate(): InsertErrors;
|
|
29
|
+
/**
|
|
30
|
+
* Recursively checks that all reference keys in the value match the route segments
|
|
31
|
+
* @param route - The current route segment
|
|
32
|
+
* @param value - The current value object
|
|
33
|
+
*/
|
|
34
|
+
private _checkMatchingRefs;
|
|
35
|
+
/**
|
|
36
|
+
* Creates an InsertValidator for the given Insert object
|
|
37
|
+
* @param insert - The Insert object to validate
|
|
38
|
+
* @returns InsertValidator - The InsertValidator instance
|
|
39
|
+
*/
|
|
40
|
+
static create(insert: Insert<any>): InsertValidator<any>;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Validates the given Insert object
|
|
44
|
+
* @param insert - The Insert object to validate
|
|
45
|
+
* @returns InsertErrors - The errors found during validation
|
|
46
|
+
*/
|
|
47
|
+
export declare const validateInsert: (insert: Insert<any>) => InsertErrors;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { Json } from '@rljson/json';
|
|
2
|
+
import { RouteRef } from '../route/route.ts';
|
|
3
|
+
import { Ref } from '../typedefs.ts';
|
|
4
|
+
/**
|
|
5
|
+
* An Insert Object describing inserts on data basically
|
|
6
|
+
*/
|
|
7
|
+
export type InsertRef = Ref;
|
|
8
|
+
export type InsertCommand = 'add' | 'remove';
|
|
9
|
+
/**
|
|
10
|
+
* An Insert describes a change to be applied to an Rljson object.
|
|
11
|
+
* @param T - The type of the value being edited, extending Json
|
|
12
|
+
*/
|
|
13
|
+
export type Insert<T extends Json> = {
|
|
14
|
+
command: InsertCommand;
|
|
15
|
+
value: T;
|
|
16
|
+
route: RouteRef;
|
|
17
|
+
origin?: Ref;
|
|
18
|
+
acknowledged?: boolean;
|
|
19
|
+
};
|
|
20
|
+
export declare const exampleInsert: () => Insert<Json>;
|
package/dist/rljson.d.ts
CHANGED
|
@@ -6,14 +6,14 @@ import { LayersTable } from './content/layer.ts';
|
|
|
6
6
|
import { RevisionsTable } from './content/revision.ts';
|
|
7
7
|
import { SliceIdsTable } from './content/slice-ids.ts';
|
|
8
8
|
import { TableCfgRef, TablesCfgTable } from './content/table-cfg.ts';
|
|
9
|
-
import {
|
|
9
|
+
import { History } from './history/history.ts';
|
|
10
10
|
import { ContentType, Ref, TableKey } from './typedefs.ts';
|
|
11
11
|
export declare const reservedFieldNames: string[];
|
|
12
12
|
export declare const reservedTableKeys: string[];
|
|
13
13
|
/**
|
|
14
14
|
* One of the supported Rljson table types
|
|
15
15
|
*/
|
|
16
|
-
export type TableType = BuffetsTable | ComponentsTable<any> | LayersTable | SliceIdsTable | CakesTable | RevisionsTable | TablesCfgTable |
|
|
16
|
+
export type TableType = BuffetsTable | ComponentsTable<any> | LayersTable | SliceIdsTable | CakesTable | RevisionsTable | TablesCfgTable | History<any>;
|
|
17
17
|
/** The rljson data format */
|
|
18
18
|
export interface Rljson extends Json {
|
|
19
19
|
[tableId: TableKey]: TableType;
|
package/dist/rljson.js
CHANGED
|
@@ -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 + "
|
|
20
|
+
const ref = !!refFlat ? refFlat.split(":").length == 2 ? { [tableKey + "HistoryRef"]: refFlat } : { [tableKey + "Ref"]: refFlat } : {};
|
|
21
21
|
const segment = {
|
|
22
22
|
tableKey,
|
|
23
23
|
...ref
|
|
@@ -131,6 +131,7 @@ class Route {
|
|
|
131
131
|
const refKey = Object.keys(segment).find(
|
|
132
132
|
(k) => k.endsWith("Ref") && k !== "tableKey"
|
|
133
133
|
);
|
|
134
|
+
/* v8 ignore next -- @preserve */
|
|
134
135
|
if (refKey) {
|
|
135
136
|
return segment[refKey];
|
|
136
137
|
}
|
|
@@ -139,7 +140,7 @@ class Route {
|
|
|
139
140
|
}
|
|
140
141
|
// .............................................................................
|
|
141
142
|
/**
|
|
142
|
-
* Checks if a segment has any reference (either default or
|
|
143
|
+
* Checks if a segment has any reference (either default or history).
|
|
143
144
|
* @param segment - The segment to check
|
|
144
145
|
* @returns True if the segment has any reference, false otherwise
|
|
145
146
|
*/
|
|
@@ -148,21 +149,21 @@ class Route {
|
|
|
148
149
|
}
|
|
149
150
|
// .............................................................................
|
|
150
151
|
/**
|
|
151
|
-
* Checks if a segment has a default reference (i.e. not a
|
|
152
|
+
* Checks if a segment has a default reference (i.e. not a history reference).
|
|
152
153
|
* @param segment - The segment to check
|
|
153
154
|
* @returns True if the segment has a default reference, false otherwise
|
|
154
155
|
*/
|
|
155
156
|
static segmentHasDefaultRef(segment) {
|
|
156
|
-
return this.segmentHasRef(segment) && !this.
|
|
157
|
+
return this.segmentHasRef(segment) && !this.segmentHasHistoryRef(segment);
|
|
157
158
|
}
|
|
158
159
|
// .............................................................................
|
|
159
160
|
/**
|
|
160
|
-
* Checks if a segment has a
|
|
161
|
+
* Checks if a segment has a history reference (i.e. an HistoryRef).
|
|
161
162
|
* @param segment - The segment to check
|
|
162
|
-
* @returns True if the segment has a
|
|
163
|
+
* @returns True if the segment has a history reference, false otherwise
|
|
163
164
|
*/
|
|
164
|
-
static
|
|
165
|
-
return this.segmentHasRef(segment) && Object.keys(segment).some((k) => k.endsWith("
|
|
165
|
+
static segmentHasHistoryRef(segment) {
|
|
166
|
+
return this.segmentHasRef(segment) && Object.keys(segment).some((k) => k.endsWith("HistoryRef"));
|
|
166
167
|
}
|
|
167
168
|
}
|
|
168
169
|
// @license
|
|
@@ -293,8 +294,8 @@ const bakeryExample = () => {
|
|
|
293
294
|
}
|
|
294
295
|
]
|
|
295
296
|
});
|
|
296
|
-
const
|
|
297
|
-
_type: "
|
|
297
|
+
const ingredientsHistory = hip({
|
|
298
|
+
_type: "history",
|
|
298
299
|
_data: [
|
|
299
300
|
{
|
|
300
301
|
timeId: "de72:1759123957292",
|
|
@@ -323,7 +324,7 @@ const bakeryExample = () => {
|
|
|
323
324
|
recipeIngredients,
|
|
324
325
|
ingredients,
|
|
325
326
|
nutritionalValues,
|
|
326
|
-
|
|
327
|
+
ingredientsHistory
|
|
327
328
|
};
|
|
328
329
|
return result;
|
|
329
330
|
};
|
|
@@ -334,11 +335,21 @@ const createCakeTableCfg = (cakeKey) => ({
|
|
|
334
335
|
key: cakeKey,
|
|
335
336
|
type: "cakes",
|
|
336
337
|
columns: [
|
|
337
|
-
{ key: "_hash", type: "string" },
|
|
338
|
-
{
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
338
|
+
{ key: "_hash", type: "string", titleLong: "Hash", titleShort: "Hash" },
|
|
339
|
+
{
|
|
340
|
+
key: "sliceIdsTable",
|
|
341
|
+
type: "string",
|
|
342
|
+
titleLong: "Slice Ids Table",
|
|
343
|
+
titleShort: "Slice Ids Table"
|
|
344
|
+
},
|
|
345
|
+
{
|
|
346
|
+
key: "sliceIdsRow",
|
|
347
|
+
type: "string",
|
|
348
|
+
titleLong: "Slice Ids Row",
|
|
349
|
+
titleShort: "Slice Ids Row"
|
|
350
|
+
},
|
|
351
|
+
{ key: "layers", type: "json", titleLong: "Layers", titleShort: "Layers" },
|
|
352
|
+
{ key: "id", type: "string", titleLong: "ID", titleShort: "ID" }
|
|
342
353
|
],
|
|
343
354
|
isHead: false,
|
|
344
355
|
isRoot: false,
|
|
@@ -352,13 +363,28 @@ const createLayerTableCfg = (layerKey) => ({
|
|
|
352
363
|
key: layerKey,
|
|
353
364
|
type: "layers",
|
|
354
365
|
columns: [
|
|
355
|
-
{ key: "_hash", type: "string" },
|
|
356
|
-
{ key: "base", type: "string" },
|
|
357
|
-
{
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
366
|
+
{ key: "_hash", type: "string", titleLong: "Hash", titleShort: "Hash" },
|
|
367
|
+
{ key: "base", type: "string", titleLong: "Base", titleShort: "Base" },
|
|
368
|
+
{
|
|
369
|
+
key: "sliceIdsTable",
|
|
370
|
+
type: "string",
|
|
371
|
+
titleLong: "Slice Ids Table",
|
|
372
|
+
titleShort: "Slice Ids Table"
|
|
373
|
+
},
|
|
374
|
+
{
|
|
375
|
+
key: "sliceIdsTableRow",
|
|
376
|
+
type: "string",
|
|
377
|
+
titleLong: "Slice Ids Table Row",
|
|
378
|
+
titleShort: "Slice Ids Table Row"
|
|
379
|
+
},
|
|
380
|
+
{
|
|
381
|
+
key: "componentsTable",
|
|
382
|
+
type: "string",
|
|
383
|
+
titleLong: "Components Table",
|
|
384
|
+
titleShort: "Components Table"
|
|
385
|
+
},
|
|
386
|
+
{ key: "add", type: "json", titleLong: "Add", titleShort: "Add" },
|
|
387
|
+
{ key: "remove", type: "json", titleLong: "Remove", titleShort: "Remove" }
|
|
362
388
|
],
|
|
363
389
|
isHead: false,
|
|
364
390
|
isRoot: false,
|
|
@@ -411,39 +437,57 @@ class Example {
|
|
|
411
437
|
columns: [
|
|
412
438
|
{
|
|
413
439
|
key: "_hash",
|
|
414
|
-
type: "string"
|
|
440
|
+
type: "string",
|
|
441
|
+
titleLong: "Hash",
|
|
442
|
+
titleShort: "Hash"
|
|
415
443
|
},
|
|
416
444
|
{
|
|
417
445
|
key: "int",
|
|
418
|
-
type: "number"
|
|
446
|
+
type: "number",
|
|
447
|
+
titleLong: "Integer",
|
|
448
|
+
titleShort: "Int"
|
|
419
449
|
},
|
|
420
450
|
{
|
|
421
451
|
key: "double",
|
|
422
|
-
type: "number"
|
|
452
|
+
type: "number",
|
|
453
|
+
titleLong: "Double",
|
|
454
|
+
titleShort: "Double"
|
|
423
455
|
},
|
|
424
456
|
{
|
|
425
457
|
key: "string",
|
|
426
|
-
type: "string"
|
|
458
|
+
type: "string",
|
|
459
|
+
titleLong: "String",
|
|
460
|
+
titleShort: "String"
|
|
427
461
|
},
|
|
428
462
|
{
|
|
429
463
|
key: "boolean",
|
|
430
|
-
type: "boolean"
|
|
464
|
+
type: "boolean",
|
|
465
|
+
titleLong: "Boolean",
|
|
466
|
+
titleShort: "Boolean"
|
|
431
467
|
},
|
|
432
468
|
{
|
|
433
469
|
key: "null",
|
|
434
|
-
type: "string"
|
|
470
|
+
type: "string",
|
|
471
|
+
titleLong: "Null",
|
|
472
|
+
titleShort: "Null"
|
|
435
473
|
},
|
|
436
474
|
{
|
|
437
475
|
key: "jsonArray",
|
|
438
|
-
type: "jsonArray"
|
|
476
|
+
type: "jsonArray",
|
|
477
|
+
titleLong: "JSON Array",
|
|
478
|
+
titleShort: "JSONArray"
|
|
439
479
|
},
|
|
440
480
|
{
|
|
441
481
|
key: "json",
|
|
442
|
-
type: "json"
|
|
482
|
+
type: "json",
|
|
483
|
+
titleLong: "JSON Object",
|
|
484
|
+
titleShort: "JSONObject"
|
|
443
485
|
},
|
|
444
486
|
{
|
|
445
487
|
key: "jsonValue",
|
|
446
|
-
type: "jsonValue"
|
|
488
|
+
type: "jsonValue",
|
|
489
|
+
titleLong: "JSON Value",
|
|
490
|
+
titleShort: "JSONValue"
|
|
447
491
|
}
|
|
448
492
|
]
|
|
449
493
|
}
|
|
@@ -1032,14 +1076,20 @@ const exampleTableCfg = (tableCfg = void 0) => {
|
|
|
1032
1076
|
columns: tableCfg?.columns ?? [
|
|
1033
1077
|
{
|
|
1034
1078
|
key: "_hash",
|
|
1079
|
+
titleLong: "Hash",
|
|
1080
|
+
titleShort: "Hash",
|
|
1035
1081
|
type: "string"
|
|
1036
1082
|
},
|
|
1037
1083
|
{
|
|
1038
1084
|
key: "a",
|
|
1085
|
+
titleLong: "Column A",
|
|
1086
|
+
titleShort: "A",
|
|
1039
1087
|
type: "string"
|
|
1040
1088
|
},
|
|
1041
1089
|
{
|
|
1042
1090
|
key: "b",
|
|
1091
|
+
titleLong: "Column B",
|
|
1092
|
+
titleShort: "B",
|
|
1043
1093
|
type: "number"
|
|
1044
1094
|
}
|
|
1045
1095
|
],
|
|
@@ -1050,66 +1100,119 @@ const exampleTableCfg = (tableCfg = void 0) => {
|
|
|
1050
1100
|
};
|
|
1051
1101
|
};
|
|
1052
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
|
|
1053
1140
|
const objectDepth = (o) => Object(o) === o ? 1 + Math.max(-1, ...Object.values(o).map(objectDepth)) : 0;
|
|
1054
1141
|
// @license
|
|
1055
|
-
class
|
|
1056
|
-
constructor(
|
|
1057
|
-
this.
|
|
1142
|
+
class InsertValidator {
|
|
1143
|
+
constructor(_insert) {
|
|
1144
|
+
this._insert = _insert;
|
|
1058
1145
|
}
|
|
1059
1146
|
errors = { hasErrors: false };
|
|
1147
|
+
// ............................................................................
|
|
1148
|
+
/**
|
|
1149
|
+
* Indicates whether there are any errors
|
|
1150
|
+
* @returns boolean - True if there are errors, false otherwise
|
|
1151
|
+
*/
|
|
1060
1152
|
get hasErrors() {
|
|
1061
1153
|
return Object.keys(this.errors).length > 1;
|
|
1062
1154
|
}
|
|
1155
|
+
// ............................................................................
|
|
1156
|
+
/**
|
|
1157
|
+
* Validates the Insert object
|
|
1158
|
+
* @returns InsertErrors - The errors found during validation
|
|
1159
|
+
*/
|
|
1063
1160
|
validate() {
|
|
1064
|
-
if (typeof this.
|
|
1161
|
+
if (typeof this._insert.route !== "string" || this._insert.route.length === 0) {
|
|
1065
1162
|
this.errors.parameterRouteInvalid = {
|
|
1066
|
-
error: "
|
|
1067
|
-
parameter: this.
|
|
1163
|
+
error: "Insert route must be a non-empty string.",
|
|
1164
|
+
parameter: this._insert.route
|
|
1068
1165
|
};
|
|
1069
1166
|
}
|
|
1070
|
-
if (!this.
|
|
1167
|
+
if (!this._insert.command.startsWith("add") && !this._insert.command.startsWith("remove")) {
|
|
1071
1168
|
this.errors.parameterCommandInvalid = {
|
|
1072
|
-
error: "
|
|
1073
|
-
parameter: this.
|
|
1169
|
+
error: "Insert command must be starting with either 'add' or 'remove'.",
|
|
1170
|
+
parameter: this._insert.command
|
|
1074
1171
|
};
|
|
1075
1172
|
}
|
|
1076
|
-
if (typeof this.
|
|
1173
|
+
if (typeof this._insert.value === "undefined" || typeof this._insert.value !== "object" || this._insert.value === null) {
|
|
1077
1174
|
this.errors.parameterValueInvalid = {
|
|
1078
|
-
error: "
|
|
1079
|
-
parameter: this.
|
|
1175
|
+
error: "Insert value must be a non-null object.",
|
|
1176
|
+
parameter: this._insert.value
|
|
1080
1177
|
};
|
|
1081
1178
|
}
|
|
1082
|
-
if (typeof this.
|
|
1179
|
+
if (typeof this._insert.origin !== "undefined" && typeof this._insert.origin !== "string") {
|
|
1083
1180
|
this.errors.parameterOriginInvalid = {
|
|
1084
|
-
error: "
|
|
1085
|
-
parameter: this.
|
|
1181
|
+
error: "Insert origin must be a string if defined.",
|
|
1182
|
+
parameter: this._insert.origin
|
|
1086
1183
|
};
|
|
1087
1184
|
}
|
|
1088
|
-
const route = Route.fromFlat(this.
|
|
1185
|
+
const route = Route.fromFlat(this._insert.route);
|
|
1089
1186
|
if (!route.isValid) {
|
|
1090
1187
|
this.errors.routeInvalid = {
|
|
1091
|
-
error: "
|
|
1092
|
-
route: this.
|
|
1188
|
+
error: "Insert route is not valid.",
|
|
1189
|
+
route: this._insert.route
|
|
1093
1190
|
};
|
|
1094
1191
|
}
|
|
1095
1192
|
if (route.isValid) {
|
|
1096
1193
|
const routeDepth = route.segments.length;
|
|
1097
|
-
const valueDepth = objectDepth(this.
|
|
1194
|
+
const valueDepth = objectDepth(this._insert.value);
|
|
1098
1195
|
if (routeDepth !== valueDepth) {
|
|
1099
1196
|
this.errors.dataRouteMismatch = {
|
|
1100
|
-
error: "
|
|
1101
|
-
route: this.
|
|
1197
|
+
error: "Insert route depth does not match value depth. Route depth must match the depth of the value object.",
|
|
1198
|
+
route: this._insert.route,
|
|
1102
1199
|
routeDepth,
|
|
1103
1200
|
valueDepth
|
|
1104
1201
|
};
|
|
1105
1202
|
}
|
|
1106
1203
|
}
|
|
1107
|
-
if (typeof this.
|
|
1108
|
-
this._checkMatchingRefs(route, this.
|
|
1204
|
+
if (typeof this._insert.value === "object" && this._insert.value !== null) {
|
|
1205
|
+
this._checkMatchingRefs(route, this._insert.value);
|
|
1109
1206
|
}
|
|
1110
1207
|
this.errors.hasErrors = this.hasErrors;
|
|
1111
1208
|
return this.errors;
|
|
1112
1209
|
}
|
|
1210
|
+
// ............................................................................
|
|
1211
|
+
/**
|
|
1212
|
+
* Recursively checks that all reference keys in the value match the route segments
|
|
1213
|
+
* @param route - The current route segment
|
|
1214
|
+
* @param value - The current value object
|
|
1215
|
+
*/
|
|
1113
1216
|
_checkMatchingRefs(route, value) {
|
|
1114
1217
|
const next = route.next;
|
|
1115
1218
|
const keys = Object.keys(value);
|
|
@@ -1118,8 +1221,8 @@ class EditValidator {
|
|
|
1118
1221
|
const refObj = value[key];
|
|
1119
1222
|
if (!next || next.tableKey !== key.slice(0, -3)) {
|
|
1120
1223
|
this.errors.parameterInvalid = {
|
|
1121
|
-
error: `
|
|
1122
|
-
parameter: this.
|
|
1224
|
+
error: `Insert value has a reference key "${key}" that does not match the next route segment.`,
|
|
1225
|
+
parameter: this._insert.value
|
|
1123
1226
|
};
|
|
1124
1227
|
break;
|
|
1125
1228
|
}
|
|
@@ -1128,30 +1231,25 @@ class EditValidator {
|
|
|
1128
1231
|
}
|
|
1129
1232
|
}
|
|
1130
1233
|
}
|
|
1131
|
-
|
|
1132
|
-
|
|
1234
|
+
// ............................................................................
|
|
1235
|
+
/**
|
|
1236
|
+
* Creates an InsertValidator for the given Insert object
|
|
1237
|
+
* @param insert - The Insert object to validate
|
|
1238
|
+
* @returns InsertValidator - The InsertValidator instance
|
|
1239
|
+
*/
|
|
1240
|
+
static create(insert) {
|
|
1241
|
+
return new InsertValidator(insert);
|
|
1133
1242
|
}
|
|
1134
1243
|
}
|
|
1135
|
-
const
|
|
1136
|
-
return
|
|
1244
|
+
const validateInsert = (insert) => {
|
|
1245
|
+
return InsertValidator.create(insert).validate();
|
|
1137
1246
|
};
|
|
1138
1247
|
// @license
|
|
1139
|
-
const
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
{ key: "_hash", type: "string" },
|
|
1144
|
-
{ key: "timeId", type: "string" },
|
|
1145
|
-
{ key: `${tableCfg.key}Ref`, type: "string" },
|
|
1146
|
-
{ key: "route", type: "string" },
|
|
1147
|
-
{ key: "origin", type: "string" },
|
|
1148
|
-
{ key: "previous", type: "jsonArray" }
|
|
1149
|
-
],
|
|
1150
|
-
isHead: false,
|
|
1151
|
-
isRoot: false,
|
|
1152
|
-
isShared: false
|
|
1248
|
+
const exampleInsert = () => ({
|
|
1249
|
+
route: "a/b/c",
|
|
1250
|
+
command: "add",
|
|
1251
|
+
value: { x: { y: { z: true } } }
|
|
1153
1252
|
});
|
|
1154
|
-
const exampleEditsTable = () => bakeryExample().ingredientsEdits;
|
|
1155
1253
|
// @license
|
|
1156
1254
|
const reservedFieldNames = ["_data"];
|
|
1157
1255
|
const reservedTableKeys = [
|
|
@@ -1225,7 +1323,7 @@ const contentTypes = [
|
|
|
1225
1323
|
"components",
|
|
1226
1324
|
"revisions",
|
|
1227
1325
|
"tableCfgs",
|
|
1228
|
-
"
|
|
1326
|
+
"history"
|
|
1229
1327
|
];
|
|
1230
1328
|
const exampleTypedefs = () => {
|
|
1231
1329
|
return {
|
|
@@ -1397,15 +1495,9 @@ class _BaseValidator {
|
|
|
1397
1495
|
throwOnWrongHashes: true
|
|
1398
1496
|
});
|
|
1399
1497
|
} catch (error) {
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
};
|
|
1404
|
-
} else {
|
|
1405
|
-
this.errors.hashesNotValid = {
|
|
1406
|
-
error: "Unknown error"
|
|
1407
|
-
};
|
|
1408
|
-
}
|
|
1498
|
+
this.errors.hashesNotValid = {
|
|
1499
|
+
error: error.message
|
|
1500
|
+
};
|
|
1409
1501
|
}
|
|
1410
1502
|
}
|
|
1411
1503
|
// ...........................................................................
|
|
@@ -2114,20 +2206,21 @@ class Validate {
|
|
|
2114
2206
|
}
|
|
2115
2207
|
export {
|
|
2116
2208
|
BaseValidator,
|
|
2117
|
-
EditValidator,
|
|
2118
2209
|
Example,
|
|
2210
|
+
InsertValidator,
|
|
2119
2211
|
Route,
|
|
2120
2212
|
Validate,
|
|
2121
2213
|
addColumnsToTableCfg,
|
|
2122
2214
|
bakeryExample,
|
|
2123
2215
|
contentTypes,
|
|
2124
2216
|
createCakeTableCfg,
|
|
2125
|
-
|
|
2217
|
+
createHistoryTableCfg,
|
|
2126
2218
|
createLayerTableCfg,
|
|
2127
2219
|
exampleBuffetsTable,
|
|
2128
2220
|
exampleCakesTable,
|
|
2129
2221
|
exampleComponentsTable,
|
|
2130
|
-
|
|
2222
|
+
exampleHistoryTable,
|
|
2223
|
+
exampleInsert,
|
|
2131
2224
|
exampleLayersTable,
|
|
2132
2225
|
exampleRevision,
|
|
2133
2226
|
exampleRljson,
|
|
@@ -2144,6 +2237,6 @@ export {
|
|
|
2144
2237
|
reservedTableKeys,
|
|
2145
2238
|
throwOnInvalidTableCfg,
|
|
2146
2239
|
timeId,
|
|
2147
|
-
|
|
2240
|
+
validateInsert,
|
|
2148
2241
|
validateRljsonAgainstTableCfg
|
|
2149
2242
|
};
|
package/dist/route/route.d.ts
CHANGED
|
@@ -73,21 +73,21 @@ export declare class Route {
|
|
|
73
73
|
*/
|
|
74
74
|
static segmentRef(segment: RouteSegment<any>): string | undefined;
|
|
75
75
|
/**
|
|
76
|
-
* Checks if a segment has any reference (either default or
|
|
76
|
+
* Checks if a segment has any reference (either default or history).
|
|
77
77
|
* @param segment - The segment to check
|
|
78
78
|
* @returns True if the segment has any reference, false otherwise
|
|
79
79
|
*/
|
|
80
80
|
static segmentHasRef(segment: RouteSegment<any>): boolean;
|
|
81
81
|
/**
|
|
82
|
-
* Checks if a segment has a default reference (i.e. not a
|
|
82
|
+
* Checks if a segment has a default reference (i.e. not a history reference).
|
|
83
83
|
* @param segment - The segment to check
|
|
84
84
|
* @returns True if the segment has a default reference, false otherwise
|
|
85
85
|
*/
|
|
86
86
|
static segmentHasDefaultRef(segment: RouteSegment<any>): boolean;
|
|
87
87
|
/**
|
|
88
|
-
* Checks if a segment has a
|
|
88
|
+
* Checks if a segment has a history reference (i.e. an HistoryRef).
|
|
89
89
|
* @param segment - The segment to check
|
|
90
|
-
* @returns True if the segment has a
|
|
90
|
+
* @returns True if the segment has a history reference, false otherwise
|
|
91
91
|
*/
|
|
92
|
-
static
|
|
92
|
+
static segmentHasHistoryRef(segment: RouteSegment<any>): boolean;
|
|
93
93
|
}
|
package/dist/src/example.ts
CHANGED
|
@@ -16,6 +16,7 @@ import { ColumnCfg, TablesCfgTable } from './content/table-cfg.ts';
|
|
|
16
16
|
import { bakeryExample } from './example/bakery-example.ts';
|
|
17
17
|
import { Rljson } from './rljson.ts';
|
|
18
18
|
|
|
19
|
+
|
|
19
20
|
export class Example {
|
|
20
21
|
static readonly ok = {
|
|
21
22
|
bakery: (): Rljson => bakeryExample(),
|
|
@@ -55,38 +56,56 @@ export class Example {
|
|
|
55
56
|
{
|
|
56
57
|
key: '_hash',
|
|
57
58
|
type: 'string',
|
|
59
|
+
titleLong: 'Hash',
|
|
60
|
+
titleShort: 'Hash',
|
|
58
61
|
},
|
|
59
62
|
{
|
|
60
63
|
key: 'int',
|
|
61
64
|
type: 'number',
|
|
65
|
+
titleLong: 'Integer',
|
|
66
|
+
titleShort: 'Int',
|
|
62
67
|
},
|
|
63
68
|
{
|
|
64
69
|
key: 'double',
|
|
65
70
|
type: 'number',
|
|
71
|
+
titleLong: 'Double',
|
|
72
|
+
titleShort: 'Double',
|
|
66
73
|
},
|
|
67
74
|
{
|
|
68
75
|
key: 'string',
|
|
69
76
|
type: 'string',
|
|
77
|
+
titleLong: 'String',
|
|
78
|
+
titleShort: 'String',
|
|
70
79
|
},
|
|
71
80
|
{
|
|
72
81
|
key: 'boolean',
|
|
73
82
|
type: 'boolean',
|
|
83
|
+
titleLong: 'Boolean',
|
|
84
|
+
titleShort: 'Boolean',
|
|
74
85
|
},
|
|
75
86
|
{
|
|
76
87
|
key: 'null',
|
|
77
88
|
type: 'string',
|
|
89
|
+
titleLong: 'Null',
|
|
90
|
+
titleShort: 'Null',
|
|
78
91
|
},
|
|
79
92
|
{
|
|
80
93
|
key: 'jsonArray',
|
|
81
94
|
type: 'jsonArray',
|
|
95
|
+
titleLong: 'JSON Array',
|
|
96
|
+
titleShort: 'JSONArray',
|
|
82
97
|
},
|
|
83
98
|
{
|
|
84
99
|
key: 'json',
|
|
85
100
|
type: 'json',
|
|
101
|
+
titleLong: 'JSON Object',
|
|
102
|
+
titleShort: 'JSONObject',
|
|
86
103
|
},
|
|
87
104
|
{
|
|
88
105
|
key: 'jsonValue',
|
|
89
106
|
type: 'jsonValue',
|
|
107
|
+
titleLong: 'JSON Value',
|
|
108
|
+
titleShort: 'JSONValue',
|
|
90
109
|
},
|
|
91
110
|
],
|
|
92
111
|
},
|
package/dist/typedefs.d.ts
CHANGED
|
@@ -26,7 +26,7 @@ export type ColumnKey = JsonKey;
|
|
|
26
26
|
* - `ids` Tables containing slice ids
|
|
27
27
|
* - `components` Tables containing slice components
|
|
28
28
|
*/
|
|
29
|
-
export declare const contentTypes: readonly ["buffets", "cakes", "layers", "sliceIds", "components", "revisions", "tableCfgs", "
|
|
29
|
+
export declare const contentTypes: readonly ["buffets", "cakes", "layers", "sliceIds", "components", "revisions", "tableCfgs", "history"];
|
|
30
30
|
export type ContentType = (typeof contentTypes)[number];
|
|
31
31
|
/**
|
|
32
32
|
* An example object using the typedefs
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rljson/rljson",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.65",
|
|
4
4
|
"description": "The RLJSON data format specification",
|
|
5
5
|
"homepage": "https://github.com/rljson/rljson",
|
|
6
6
|
"bugs": "https://github.com/rljson/rljson/issues",
|
|
@@ -20,28 +20,28 @@
|
|
|
20
20
|
],
|
|
21
21
|
"type": "module",
|
|
22
22
|
"devDependencies": {
|
|
23
|
-
"@types/node": "^24.
|
|
24
|
-
"@typescript-eslint/eslint-plugin": "^8.46.
|
|
25
|
-
"@typescript-eslint/parser": "^8.46.
|
|
26
|
-
"@vitest/coverage-v8": "^
|
|
23
|
+
"@types/node": "^24.9.1",
|
|
24
|
+
"@typescript-eslint/eslint-plugin": "^8.46.2",
|
|
25
|
+
"@typescript-eslint/parser": "^8.46.2",
|
|
26
|
+
"@vitest/coverage-v8": "^4.0.1",
|
|
27
27
|
"cross-env": "^10.1.0",
|
|
28
|
-
"eslint": "^9.
|
|
29
|
-
"eslint-plugin-jsdoc": "^61.1.
|
|
28
|
+
"eslint": "^9.38.0",
|
|
29
|
+
"eslint-plugin-jsdoc": "^61.1.5",
|
|
30
30
|
"eslint-plugin-tsdoc": "^0.4.0",
|
|
31
31
|
"globals": "^16.4.0",
|
|
32
32
|
"jsdoc": "^4.0.5",
|
|
33
33
|
"read-pkg": "^9.0.1",
|
|
34
34
|
"typescript": "~5.9.3",
|
|
35
|
-
"typescript-eslint": "^8.46.
|
|
36
|
-
"vite": "^7.1.
|
|
35
|
+
"typescript-eslint": "^8.46.2",
|
|
36
|
+
"vite": "^7.1.12",
|
|
37
37
|
"vite-node": "^3.2.4",
|
|
38
38
|
"vite-plugin-dts": "^4.5.4",
|
|
39
39
|
"vite-tsconfig-paths": "^5.1.4",
|
|
40
|
-
"vitest": "^
|
|
40
|
+
"vitest": "^4.0.1",
|
|
41
41
|
"vitest-dom": "^0.1.1"
|
|
42
42
|
},
|
|
43
43
|
"dependencies": {
|
|
44
|
-
"@rljson/hash": "^0.0.
|
|
44
|
+
"@rljson/hash": "^0.0.17",
|
|
45
45
|
"@rljson/json": "^0.0.22",
|
|
46
46
|
"nanoid": "^5.1.6"
|
|
47
47
|
},
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
import { Json } from '@rljson/json';
|
|
2
|
-
import { Errors } from '../validate/validate.ts';
|
|
3
|
-
import { Edit } from './edit.ts';
|
|
4
|
-
export interface EditErrors extends Errors {
|
|
5
|
-
invalidObject?: Json;
|
|
6
|
-
parameterInvalid?: Json;
|
|
7
|
-
parameterRouteInvalid?: Json;
|
|
8
|
-
parameterCommandInvalid?: Json;
|
|
9
|
-
parameterValueInvalid?: Json;
|
|
10
|
-
parameterOriginInvalid?: Json;
|
|
11
|
-
parameterPreviousInvalid?: Json;
|
|
12
|
-
routeInvalid?: Json;
|
|
13
|
-
dataRouteMismatch?: Json;
|
|
14
|
-
}
|
|
15
|
-
export declare class EditValidator<T extends Json> {
|
|
16
|
-
private _edit;
|
|
17
|
-
errors: EditErrors;
|
|
18
|
-
get hasErrors(): boolean;
|
|
19
|
-
constructor(_edit: Edit<T>);
|
|
20
|
-
validate(): EditErrors;
|
|
21
|
-
private _checkMatchingRefs;
|
|
22
|
-
static create(edit: Edit<any>): EditValidator<any>;
|
|
23
|
-
}
|
|
24
|
-
export declare const validateEdit: (edit: Edit<any>) => EditErrors;
|
package/dist/edit/edit.d.ts
DELETED
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
import { Json } from '@rljson/json';
|
|
2
|
-
import { Ref } from '../typedefs.ts';
|
|
3
|
-
import { TableCfg } from '../content/table-cfg.ts';
|
|
4
|
-
import { RljsonTable } from '../rljson.ts';
|
|
5
|
-
import { RouteRef } from '../route/route.ts';
|
|
6
|
-
/**
|
|
7
|
-
* An Edit Object describing edits on data basically
|
|
8
|
-
*/
|
|
9
|
-
export type EditRef = Ref;
|
|
10
|
-
export type EditCommand = 'add' | 'remove';
|
|
11
|
-
/**
|
|
12
|
-
* An Edit describes a change to be applied to an Rljson object.
|
|
13
|
-
* @param T - The type of the value being edited, extending Json
|
|
14
|
-
*/
|
|
15
|
-
export type Edit<T extends Json> = {
|
|
16
|
-
command: EditCommand;
|
|
17
|
-
value: T;
|
|
18
|
-
route: RouteRef;
|
|
19
|
-
origin?: Ref;
|
|
20
|
-
acknowledged?: boolean;
|
|
21
|
-
};
|
|
22
|
-
export type EditProtocolTimeId = string;
|
|
23
|
-
export type EditProtocolRow<Str extends string> = {
|
|
24
|
-
[key in Str as `${Uncapitalize<string & key>}Ref`]: string;
|
|
25
|
-
} & {
|
|
26
|
-
timeId: EditProtocolTimeId;
|
|
27
|
-
route: RouteRef;
|
|
28
|
-
origin?: Ref;
|
|
29
|
-
previous?: EditProtocolTimeId[];
|
|
30
|
-
};
|
|
31
|
-
export type EditProtocol<Str extends string> = RljsonTable<EditProtocolRow<Str>, 'edits'>;
|
|
32
|
-
/**
|
|
33
|
-
* Creates a TableCfg for an Edit Protocol table for the given table configuration
|
|
34
|
-
* @param tableCfg - The table configuration to create the Edit Protocol table for
|
|
35
|
-
* @returns The TableCfg for the Edit Protocol table
|
|
36
|
-
*/
|
|
37
|
-
export declare const createEditProtocolTableCfg: (tableCfg: TableCfg) => TableCfg;
|
|
38
|
-
/**
|
|
39
|
-
* Provides an example Edits table for test purposes
|
|
40
|
-
*/
|
|
41
|
-
export declare const exampleEditsTable: () => EditProtocol<any>;
|