@rljson/rljson 0.0.62 → 0.0.64
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/edit/history.d.ts +24 -0
- package/dist/edit/insert-validator.d.ts +47 -0
- package/dist/edit/insert.d.ts +19 -0
- package/dist/example/bakery-example.d.ts +2 -2
- package/dist/index.d.ts +3 -2
- package/dist/rljson.d.ts +2 -2
- package/dist/rljson.js +174 -83
- 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 +8 -8
- 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
|
|
@@ -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>;
|
|
@@ -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,19 @@
|
|
|
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
|
+
};
|
|
@@ -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 '../edit/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;
|
package/dist/index.d.ts
CHANGED
|
@@ -5,8 +5,9 @@ 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/
|
|
9
|
-
export * from './edit/
|
|
8
|
+
export * from './edit/history.ts';
|
|
9
|
+
export * from './edit/insert-validator.ts';
|
|
10
|
+
export * from './edit/insert.ts';
|
|
10
11
|
export * from './example.ts';
|
|
11
12
|
export * from './example/bakery-example.ts';
|
|
12
13
|
export * from './rljson.ts';
|
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 './edit/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
|
|
@@ -139,7 +139,7 @@ class Route {
|
|
|
139
139
|
}
|
|
140
140
|
// .............................................................................
|
|
141
141
|
/**
|
|
142
|
-
* Checks if a segment has any reference (either default or
|
|
142
|
+
* Checks if a segment has any reference (either default or history).
|
|
143
143
|
* @param segment - The segment to check
|
|
144
144
|
* @returns True if the segment has any reference, false otherwise
|
|
145
145
|
*/
|
|
@@ -148,21 +148,21 @@ class Route {
|
|
|
148
148
|
}
|
|
149
149
|
// .............................................................................
|
|
150
150
|
/**
|
|
151
|
-
* Checks if a segment has a default reference (i.e. not a
|
|
151
|
+
* Checks if a segment has a default reference (i.e. not a history reference).
|
|
152
152
|
* @param segment - The segment to check
|
|
153
153
|
* @returns True if the segment has a default reference, false otherwise
|
|
154
154
|
*/
|
|
155
155
|
static segmentHasDefaultRef(segment) {
|
|
156
|
-
return this.segmentHasRef(segment) && !this.
|
|
156
|
+
return this.segmentHasRef(segment) && !this.segmentHasHistoryRef(segment);
|
|
157
157
|
}
|
|
158
158
|
// .............................................................................
|
|
159
159
|
/**
|
|
160
|
-
* Checks if a segment has a
|
|
160
|
+
* Checks if a segment has a history reference (i.e. an HistoryRef).
|
|
161
161
|
* @param segment - The segment to check
|
|
162
|
-
* @returns True if the segment has a
|
|
162
|
+
* @returns True if the segment has a history reference, false otherwise
|
|
163
163
|
*/
|
|
164
|
-
static
|
|
165
|
-
return this.segmentHasRef(segment) && Object.keys(segment).some((k) => k.endsWith("
|
|
164
|
+
static segmentHasHistoryRef(segment) {
|
|
165
|
+
return this.segmentHasRef(segment) && Object.keys(segment).some((k) => k.endsWith("HistoryRef"));
|
|
166
166
|
}
|
|
167
167
|
}
|
|
168
168
|
// @license
|
|
@@ -293,8 +293,8 @@ const bakeryExample = () => {
|
|
|
293
293
|
}
|
|
294
294
|
]
|
|
295
295
|
});
|
|
296
|
-
const
|
|
297
|
-
_type: "
|
|
296
|
+
const ingredientsHistory = hip({
|
|
297
|
+
_type: "history",
|
|
298
298
|
_data: [
|
|
299
299
|
{
|
|
300
300
|
timeId: "de72:1759123957292",
|
|
@@ -323,7 +323,7 @@ const bakeryExample = () => {
|
|
|
323
323
|
recipeIngredients,
|
|
324
324
|
ingredients,
|
|
325
325
|
nutritionalValues,
|
|
326
|
-
|
|
326
|
+
ingredientsHistory
|
|
327
327
|
};
|
|
328
328
|
return result;
|
|
329
329
|
};
|
|
@@ -334,11 +334,21 @@ const createCakeTableCfg = (cakeKey) => ({
|
|
|
334
334
|
key: cakeKey,
|
|
335
335
|
type: "cakes",
|
|
336
336
|
columns: [
|
|
337
|
-
{ key: "_hash", type: "string" },
|
|
338
|
-
{
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
337
|
+
{ key: "_hash", type: "string", titleLong: "Hash", titleShort: "Hash" },
|
|
338
|
+
{
|
|
339
|
+
key: "sliceIdsTable",
|
|
340
|
+
type: "string",
|
|
341
|
+
titleLong: "Slice Ids Table",
|
|
342
|
+
titleShort: "Slice Ids Table"
|
|
343
|
+
},
|
|
344
|
+
{
|
|
345
|
+
key: "sliceIdsRow",
|
|
346
|
+
type: "string",
|
|
347
|
+
titleLong: "Slice Ids Row",
|
|
348
|
+
titleShort: "Slice Ids Row"
|
|
349
|
+
},
|
|
350
|
+
{ key: "layers", type: "json", titleLong: "Layers", titleShort: "Layers" },
|
|
351
|
+
{ key: "id", type: "string", titleLong: "ID", titleShort: "ID" }
|
|
342
352
|
],
|
|
343
353
|
isHead: false,
|
|
344
354
|
isRoot: false,
|
|
@@ -352,13 +362,28 @@ const createLayerTableCfg = (layerKey) => ({
|
|
|
352
362
|
key: layerKey,
|
|
353
363
|
type: "layers",
|
|
354
364
|
columns: [
|
|
355
|
-
{ key: "_hash", type: "string" },
|
|
356
|
-
{ key: "base", type: "string" },
|
|
357
|
-
{
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
365
|
+
{ key: "_hash", type: "string", titleLong: "Hash", titleShort: "Hash" },
|
|
366
|
+
{ key: "base", type: "string", titleLong: "Base", titleShort: "Base" },
|
|
367
|
+
{
|
|
368
|
+
key: "sliceIdsTable",
|
|
369
|
+
type: "string",
|
|
370
|
+
titleLong: "Slice Ids Table",
|
|
371
|
+
titleShort: "Slice Ids Table"
|
|
372
|
+
},
|
|
373
|
+
{
|
|
374
|
+
key: "sliceIdsTableRow",
|
|
375
|
+
type: "string",
|
|
376
|
+
titleLong: "Slice Ids Table Row",
|
|
377
|
+
titleShort: "Slice Ids Table Row"
|
|
378
|
+
},
|
|
379
|
+
{
|
|
380
|
+
key: "componentsTable",
|
|
381
|
+
type: "string",
|
|
382
|
+
titleLong: "Components Table",
|
|
383
|
+
titleShort: "Components Table"
|
|
384
|
+
},
|
|
385
|
+
{ key: "add", type: "json", titleLong: "Add", titleShort: "Add" },
|
|
386
|
+
{ key: "remove", type: "json", titleLong: "Remove", titleShort: "Remove" }
|
|
362
387
|
],
|
|
363
388
|
isHead: false,
|
|
364
389
|
isRoot: false,
|
|
@@ -411,39 +436,57 @@ class Example {
|
|
|
411
436
|
columns: [
|
|
412
437
|
{
|
|
413
438
|
key: "_hash",
|
|
414
|
-
type: "string"
|
|
439
|
+
type: "string",
|
|
440
|
+
titleLong: "Hash",
|
|
441
|
+
titleShort: "Hash"
|
|
415
442
|
},
|
|
416
443
|
{
|
|
417
444
|
key: "int",
|
|
418
|
-
type: "number"
|
|
445
|
+
type: "number",
|
|
446
|
+
titleLong: "Integer",
|
|
447
|
+
titleShort: "Int"
|
|
419
448
|
},
|
|
420
449
|
{
|
|
421
450
|
key: "double",
|
|
422
|
-
type: "number"
|
|
451
|
+
type: "number",
|
|
452
|
+
titleLong: "Double",
|
|
453
|
+
titleShort: "Double"
|
|
423
454
|
},
|
|
424
455
|
{
|
|
425
456
|
key: "string",
|
|
426
|
-
type: "string"
|
|
457
|
+
type: "string",
|
|
458
|
+
titleLong: "String",
|
|
459
|
+
titleShort: "String"
|
|
427
460
|
},
|
|
428
461
|
{
|
|
429
462
|
key: "boolean",
|
|
430
|
-
type: "boolean"
|
|
463
|
+
type: "boolean",
|
|
464
|
+
titleLong: "Boolean",
|
|
465
|
+
titleShort: "Boolean"
|
|
431
466
|
},
|
|
432
467
|
{
|
|
433
468
|
key: "null",
|
|
434
|
-
type: "string"
|
|
469
|
+
type: "string",
|
|
470
|
+
titleLong: "Null",
|
|
471
|
+
titleShort: "Null"
|
|
435
472
|
},
|
|
436
473
|
{
|
|
437
474
|
key: "jsonArray",
|
|
438
|
-
type: "jsonArray"
|
|
475
|
+
type: "jsonArray",
|
|
476
|
+
titleLong: "JSON Array",
|
|
477
|
+
titleShort: "JSONArray"
|
|
439
478
|
},
|
|
440
479
|
{
|
|
441
480
|
key: "json",
|
|
442
|
-
type: "json"
|
|
481
|
+
type: "json",
|
|
482
|
+
titleLong: "JSON Object",
|
|
483
|
+
titleShort: "JSONObject"
|
|
443
484
|
},
|
|
444
485
|
{
|
|
445
486
|
key: "jsonValue",
|
|
446
|
-
type: "jsonValue"
|
|
487
|
+
type: "jsonValue",
|
|
488
|
+
titleLong: "JSON Value",
|
|
489
|
+
titleShort: "JSONValue"
|
|
447
490
|
}
|
|
448
491
|
]
|
|
449
492
|
}
|
|
@@ -1032,14 +1075,20 @@ const exampleTableCfg = (tableCfg = void 0) => {
|
|
|
1032
1075
|
columns: tableCfg?.columns ?? [
|
|
1033
1076
|
{
|
|
1034
1077
|
key: "_hash",
|
|
1078
|
+
titleLong: "Hash",
|
|
1079
|
+
titleShort: "Hash",
|
|
1035
1080
|
type: "string"
|
|
1036
1081
|
},
|
|
1037
1082
|
{
|
|
1038
1083
|
key: "a",
|
|
1084
|
+
titleLong: "Column A",
|
|
1085
|
+
titleShort: "A",
|
|
1039
1086
|
type: "string"
|
|
1040
1087
|
},
|
|
1041
1088
|
{
|
|
1042
1089
|
key: "b",
|
|
1090
|
+
titleLong: "Column B",
|
|
1091
|
+
titleShort: "B",
|
|
1043
1092
|
type: "number"
|
|
1044
1093
|
}
|
|
1045
1094
|
],
|
|
@@ -1050,66 +1099,119 @@ const exampleTableCfg = (tableCfg = void 0) => {
|
|
|
1050
1099
|
};
|
|
1051
1100
|
};
|
|
1052
1101
|
// @license
|
|
1102
|
+
const createHistoryTableCfg = (tableCfg) => ({
|
|
1103
|
+
key: `${tableCfg.key}History`,
|
|
1104
|
+
type: "history",
|
|
1105
|
+
columns: [
|
|
1106
|
+
{ key: "_hash", type: "string", titleLong: "Hash", titleShort: "Hash" },
|
|
1107
|
+
{
|
|
1108
|
+
key: "timeId",
|
|
1109
|
+
type: "string",
|
|
1110
|
+
titleLong: "Time ID",
|
|
1111
|
+
titleShort: "Time ID"
|
|
1112
|
+
},
|
|
1113
|
+
{
|
|
1114
|
+
key: `${tableCfg.key}Ref`,
|
|
1115
|
+
type: "string",
|
|
1116
|
+
titleLong: "Reference",
|
|
1117
|
+
titleShort: "Ref"
|
|
1118
|
+
},
|
|
1119
|
+
{ key: "route", type: "string", titleLong: "Route", titleShort: "Route" },
|
|
1120
|
+
{
|
|
1121
|
+
key: "origin",
|
|
1122
|
+
type: "string",
|
|
1123
|
+
titleLong: "Origin",
|
|
1124
|
+
titleShort: "Origin"
|
|
1125
|
+
},
|
|
1126
|
+
{
|
|
1127
|
+
key: "previous",
|
|
1128
|
+
type: "jsonArray",
|
|
1129
|
+
titleLong: "Previous",
|
|
1130
|
+
titleShort: "Previous"
|
|
1131
|
+
}
|
|
1132
|
+
],
|
|
1133
|
+
isHead: false,
|
|
1134
|
+
isRoot: false,
|
|
1135
|
+
isShared: false
|
|
1136
|
+
});
|
|
1137
|
+
const exampleHistoryTable = () => bakeryExample().ingredientsHistory;
|
|
1138
|
+
// @license
|
|
1053
1139
|
const objectDepth = (o) => Object(o) === o ? 1 + Math.max(-1, ...Object.values(o).map(objectDepth)) : 0;
|
|
1054
1140
|
// @license
|
|
1055
|
-
class
|
|
1056
|
-
constructor(
|
|
1057
|
-
this.
|
|
1141
|
+
class InsertValidator {
|
|
1142
|
+
constructor(_insert) {
|
|
1143
|
+
this._insert = _insert;
|
|
1058
1144
|
}
|
|
1059
1145
|
errors = { hasErrors: false };
|
|
1146
|
+
// ............................................................................
|
|
1147
|
+
/**
|
|
1148
|
+
* Indicates whether there are any errors
|
|
1149
|
+
* @returns boolean - True if there are errors, false otherwise
|
|
1150
|
+
*/
|
|
1060
1151
|
get hasErrors() {
|
|
1061
1152
|
return Object.keys(this.errors).length > 1;
|
|
1062
1153
|
}
|
|
1154
|
+
// ............................................................................
|
|
1155
|
+
/**
|
|
1156
|
+
* Validates the Insert object
|
|
1157
|
+
* @returns InsertErrors - The errors found during validation
|
|
1158
|
+
*/
|
|
1063
1159
|
validate() {
|
|
1064
|
-
if (typeof this.
|
|
1160
|
+
if (typeof this._insert.route !== "string" || this._insert.route.length === 0) {
|
|
1065
1161
|
this.errors.parameterRouteInvalid = {
|
|
1066
|
-
error: "
|
|
1067
|
-
parameter: this.
|
|
1162
|
+
error: "Insert route must be a non-empty string.",
|
|
1163
|
+
parameter: this._insert.route
|
|
1068
1164
|
};
|
|
1069
1165
|
}
|
|
1070
|
-
if (!this.
|
|
1166
|
+
if (!this._insert.command.startsWith("add") && !this._insert.command.startsWith("remove")) {
|
|
1071
1167
|
this.errors.parameterCommandInvalid = {
|
|
1072
|
-
error: "
|
|
1073
|
-
parameter: this.
|
|
1168
|
+
error: "Insert command must be starting with either 'add' or 'remove'.",
|
|
1169
|
+
parameter: this._insert.command
|
|
1074
1170
|
};
|
|
1075
1171
|
}
|
|
1076
|
-
if (typeof this.
|
|
1172
|
+
if (typeof this._insert.value === "undefined" || typeof this._insert.value !== "object" || this._insert.value === null) {
|
|
1077
1173
|
this.errors.parameterValueInvalid = {
|
|
1078
|
-
error: "
|
|
1079
|
-
parameter: this.
|
|
1174
|
+
error: "Insert value must be a non-null object.",
|
|
1175
|
+
parameter: this._insert.value
|
|
1080
1176
|
};
|
|
1081
1177
|
}
|
|
1082
|
-
if (typeof this.
|
|
1178
|
+
if (typeof this._insert.origin !== "undefined" && typeof this._insert.origin !== "string") {
|
|
1083
1179
|
this.errors.parameterOriginInvalid = {
|
|
1084
|
-
error: "
|
|
1085
|
-
parameter: this.
|
|
1180
|
+
error: "Insert origin must be a string if defined.",
|
|
1181
|
+
parameter: this._insert.origin
|
|
1086
1182
|
};
|
|
1087
1183
|
}
|
|
1088
|
-
const route = Route.fromFlat(this.
|
|
1184
|
+
const route = Route.fromFlat(this._insert.route);
|
|
1089
1185
|
if (!route.isValid) {
|
|
1090
1186
|
this.errors.routeInvalid = {
|
|
1091
|
-
error: "
|
|
1092
|
-
route: this.
|
|
1187
|
+
error: "Insert route is not valid.",
|
|
1188
|
+
route: this._insert.route
|
|
1093
1189
|
};
|
|
1094
1190
|
}
|
|
1095
1191
|
if (route.isValid) {
|
|
1096
1192
|
const routeDepth = route.segments.length;
|
|
1097
|
-
const valueDepth = objectDepth(this.
|
|
1193
|
+
const valueDepth = objectDepth(this._insert.value);
|
|
1098
1194
|
if (routeDepth !== valueDepth) {
|
|
1099
1195
|
this.errors.dataRouteMismatch = {
|
|
1100
|
-
error: "
|
|
1101
|
-
route: this.
|
|
1196
|
+
error: "Insert route depth does not match value depth. Route depth must match the depth of the value object.",
|
|
1197
|
+
route: this._insert.route,
|
|
1102
1198
|
routeDepth,
|
|
1103
1199
|
valueDepth
|
|
1104
1200
|
};
|
|
1105
1201
|
}
|
|
1106
1202
|
}
|
|
1107
|
-
if (typeof this.
|
|
1108
|
-
this._checkMatchingRefs(route, this.
|
|
1203
|
+
if (typeof this._insert.value === "object" && this._insert.value !== null) {
|
|
1204
|
+
this._checkMatchingRefs(route, this._insert.value);
|
|
1109
1205
|
}
|
|
1110
1206
|
this.errors.hasErrors = this.hasErrors;
|
|
1111
1207
|
return this.errors;
|
|
1112
1208
|
}
|
|
1209
|
+
// ............................................................................
|
|
1210
|
+
/**
|
|
1211
|
+
* Recursively checks that all reference keys in the value match the route segments
|
|
1212
|
+
* @param route - The current route segment
|
|
1213
|
+
* @param value - The current value object
|
|
1214
|
+
*/
|
|
1113
1215
|
_checkMatchingRefs(route, value) {
|
|
1114
1216
|
const next = route.next;
|
|
1115
1217
|
const keys = Object.keys(value);
|
|
@@ -1118,8 +1220,8 @@ class EditValidator {
|
|
|
1118
1220
|
const refObj = value[key];
|
|
1119
1221
|
if (!next || next.tableKey !== key.slice(0, -3)) {
|
|
1120
1222
|
this.errors.parameterInvalid = {
|
|
1121
|
-
error: `
|
|
1122
|
-
parameter: this.
|
|
1223
|
+
error: `Insert value has a reference key "${key}" that does not match the next route segment.`,
|
|
1224
|
+
parameter: this._insert.value
|
|
1123
1225
|
};
|
|
1124
1226
|
break;
|
|
1125
1227
|
}
|
|
@@ -1128,31 +1230,20 @@ class EditValidator {
|
|
|
1128
1230
|
}
|
|
1129
1231
|
}
|
|
1130
1232
|
}
|
|
1131
|
-
|
|
1132
|
-
|
|
1233
|
+
// ............................................................................
|
|
1234
|
+
/**
|
|
1235
|
+
* Creates an InsertValidator for the given Insert object
|
|
1236
|
+
* @param insert - The Insert object to validate
|
|
1237
|
+
* @returns InsertValidator - The InsertValidator instance
|
|
1238
|
+
*/
|
|
1239
|
+
static create(insert) {
|
|
1240
|
+
return new InsertValidator(insert);
|
|
1133
1241
|
}
|
|
1134
1242
|
}
|
|
1135
|
-
const
|
|
1136
|
-
return
|
|
1243
|
+
const validateInsert = (insert) => {
|
|
1244
|
+
return InsertValidator.create(insert).validate();
|
|
1137
1245
|
};
|
|
1138
1246
|
// @license
|
|
1139
|
-
const createEditProtocolTableCfg = (tableCfg) => ({
|
|
1140
|
-
key: `${tableCfg.key}Edits`,
|
|
1141
|
-
type: "edits",
|
|
1142
|
-
columns: [
|
|
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
|
|
1153
|
-
});
|
|
1154
|
-
const exampleEditsTable = () => bakeryExample().ingredientsEdits;
|
|
1155
|
-
// @license
|
|
1156
1247
|
const reservedFieldNames = ["_data"];
|
|
1157
1248
|
const reservedTableKeys = [
|
|
1158
1249
|
"_hash",
|
|
@@ -1225,7 +1316,7 @@ const contentTypes = [
|
|
|
1225
1316
|
"components",
|
|
1226
1317
|
"revisions",
|
|
1227
1318
|
"tableCfgs",
|
|
1228
|
-
"
|
|
1319
|
+
"history"
|
|
1229
1320
|
];
|
|
1230
1321
|
const exampleTypedefs = () => {
|
|
1231
1322
|
return {
|
|
@@ -2114,20 +2205,20 @@ class Validate {
|
|
|
2114
2205
|
}
|
|
2115
2206
|
export {
|
|
2116
2207
|
BaseValidator,
|
|
2117
|
-
EditValidator,
|
|
2118
2208
|
Example,
|
|
2209
|
+
InsertValidator,
|
|
2119
2210
|
Route,
|
|
2120
2211
|
Validate,
|
|
2121
2212
|
addColumnsToTableCfg,
|
|
2122
2213
|
bakeryExample,
|
|
2123
2214
|
contentTypes,
|
|
2124
2215
|
createCakeTableCfg,
|
|
2125
|
-
|
|
2216
|
+
createHistoryTableCfg,
|
|
2126
2217
|
createLayerTableCfg,
|
|
2127
2218
|
exampleBuffetsTable,
|
|
2128
2219
|
exampleCakesTable,
|
|
2129
2220
|
exampleComponentsTable,
|
|
2130
|
-
|
|
2221
|
+
exampleHistoryTable,
|
|
2131
2222
|
exampleLayersTable,
|
|
2132
2223
|
exampleRevision,
|
|
2133
2224
|
exampleRljson,
|
|
@@ -2144,6 +2235,6 @@ export {
|
|
|
2144
2235
|
reservedTableKeys,
|
|
2145
2236
|
throwOnInvalidTableCfg,
|
|
2146
2237
|
timeId,
|
|
2147
|
-
|
|
2238
|
+
validateInsert,
|
|
2148
2239
|
validateRljsonAgainstTableCfg
|
|
2149
2240
|
};
|
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.64",
|
|
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,20 +20,20 @@
|
|
|
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.
|
|
23
|
+
"@types/node": "^24.9.1",
|
|
24
|
+
"@typescript-eslint/eslint-plugin": "^8.46.2",
|
|
25
|
+
"@typescript-eslint/parser": "^8.46.2",
|
|
26
26
|
"@vitest/coverage-v8": "^3.2.4",
|
|
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.11",
|
|
37
37
|
"vite-node": "^3.2.4",
|
|
38
38
|
"vite-plugin-dts": "^4.5.4",
|
|
39
39
|
"vite-tsconfig-paths": "^5.1.4",
|
|
@@ -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>;
|