@stxt-lang/core 0.5.2 → 0.5.3
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/README.md +1 -0
- package/out/all.js +3 -3
- package/out/core/Constants.d.ts +8 -0
- package/out/core/Constants.js +8 -0
- package/out/core/Line.d.ts +20 -0
- package/out/core/Line.js +15 -0
- package/out/core/LineParser.d.ts +17 -0
- package/out/core/LineParser.js +27 -10
- package/out/core/NameNamespace.d.ts +9 -0
- package/out/core/NameNamespace.js +9 -0
- package/out/core/NameNamespaceParser.d.ts +11 -0
- package/out/core/NameNamespaceParser.js +16 -5
- package/out/core/NamespaceValidator.d.ts +18 -0
- package/out/core/NamespaceValidator.js +18 -0
- package/out/core/Node.d.ts +55 -0
- package/out/core/Node.js +56 -1
- package/out/core/NodeCreator.d.ts +11 -0
- package/out/core/NodeCreator.js +14 -3
- package/out/core/ParseResult.d.ts +26 -0
- package/out/core/ParseResult.js +26 -0
- package/out/core/Parser.d.ts +31 -0
- package/out/core/Parser.js +50 -19
- package/out/core/StringUtils.d.ts +31 -0
- package/out/core/StringUtils.js +40 -9
- package/out/exceptions/ParseException.d.ts +14 -0
- package/out/exceptions/ParseException.js +12 -0
- package/out/exceptions/RuntimeException.d.ts +14 -0
- package/out/exceptions/RuntimeException.js +13 -0
- package/out/exceptions/ValidationException.d.ts +8 -0
- package/out/exceptions/ValidationException.js +8 -0
- package/out/processors/Observer.d.ts +29 -0
- package/out/processors/Validator.d.ts +9 -0
- package/out/runtime/ConditionalValidator.d.ts +15 -0
- package/out/runtime/ConditionalValidator.js +16 -2
- package/out/runtime/NodeWriter.d.ts +18 -0
- package/out/runtime/NodeWriter.js +18 -0
- package/out/runtime/UnifiedSchemaProvider.d.ts +23 -5
- package/out/runtime/UnifiedSchemaProvider.js +28 -10
- package/out/schema/ChildDefinition.d.ts +18 -0
- package/out/schema/ChildDefinition.js +18 -0
- package/out/schema/NodeDefinition.d.ts +44 -0
- package/out/schema/NodeDefinition.js +47 -3
- package/out/schema/Schema.d.ts +26 -0
- package/out/schema/Schema.js +26 -1
- package/out/schema/SchemaParser.d.ts +7 -0
- package/out/schema/SchemaParser.js +26 -16
- package/out/schema/SchemaProvider.d.ts +7 -0
- package/out/schema/SchemaProviderMemory.d.ts +27 -0
- package/out/schema/SchemaProviderMemory.js +27 -0
- package/out/schema/SchemaProviderMeta.d.ts +16 -0
- package/out/schema/SchemaProviderMeta.js +16 -0
- package/out/schema/SchemaValidator.d.ts +20 -0
- package/out/schema/SchemaValidator.js +27 -7
- package/out/schema/Type.d.ts +9 -0
- package/out/schema/TypeRegistry.d.ts +13 -0
- package/out/schema/TypeRegistry.js +18 -5
- package/out/schema/type/BASE64.d.ts +1 -0
- package/out/schema/type/BASE64.js +5 -4
- package/out/schema/type/BINARY.d.ts +1 -0
- package/out/schema/type/BINARY.js +2 -1
- package/out/schema/type/BLOCK.d.ts +1 -0
- package/out/schema/type/BLOCK.js +2 -1
- package/out/schema/type/BOOLEAN.d.ts +1 -0
- package/out/schema/type/BOOLEAN.js +1 -0
- package/out/schema/type/DATE.d.ts +1 -0
- package/out/schema/type/DATE.js +1 -0
- package/out/schema/type/EMAIL.d.ts +1 -0
- package/out/schema/type/EMAIL.js +1 -0
- package/out/schema/type/ENUM.d.ts +1 -0
- package/out/schema/type/ENUM.js +2 -1
- package/out/schema/type/GROUP.d.ts +1 -0
- package/out/schema/type/GROUP.js +2 -1
- package/out/schema/type/HEXADECIMAL.d.ts +1 -0
- package/out/schema/type/HEXADECIMAL.js +2 -1
- package/out/schema/type/INLINE.d.ts +1 -0
- package/out/schema/type/INLINE.js +2 -1
- package/out/schema/type/INTEGER.d.ts +1 -0
- package/out/schema/type/INTEGER.js +1 -0
- package/out/schema/type/MARKDOWN.d.ts +4 -0
- package/out/schema/type/MARKDOWN.js +4 -2
- package/out/schema/type/NATURAL.d.ts +1 -0
- package/out/schema/type/NATURAL.js +1 -0
- package/out/schema/type/NUMBER.d.ts +1 -0
- package/out/schema/type/NUMBER.js +1 -0
- package/out/schema/type/TEXT.d.ts +1 -0
- package/out/schema/type/TEXT.js +1 -0
- package/out/schema/type/TIME.d.ts +1 -0
- package/out/schema/type/TIME.js +1 -0
- package/out/schema/type/TIMESTAMP.d.ts +1 -0
- package/out/schema/type/TIMESTAMP.js +1 -0
- package/out/schema/type/URL.d.ts +1 -0
- package/out/schema/type/URL.js +2 -1
- package/out/schema/type/UUID.d.ts +1 -0
- package/out/schema/type/UUID.js +1 -0
- package/out/schema/type/binaryValue.d.ts +9 -0
- package/out/schema/type/binaryValue.js +9 -3
- package/out/schema/type/regexType.d.ts +8 -0
- package/out/schema/type/regexType.js +9 -1
- package/out/template/ChildLine.d.ts +14 -0
- package/out/template/ChildLine.js +14 -0
- package/out/template/ChildLineParser.d.ts +10 -0
- package/out/template/ChildLineParser.js +17 -8
- package/out/template/MetaTemplateSchemaProvider.d.ts +16 -0
- package/out/template/MetaTemplateSchemaProvider.js +17 -1
- package/out/template/TemplateParser.d.ts +8 -0
- package/out/template/TemplateParser.js +49 -39
- package/out/template/TemplateSchemaProviderMemory.d.ts +18 -0
- package/out/template/TemplateSchemaProviderMemory.js +21 -3
- package/package.json +1 -1
|
@@ -8,32 +8,39 @@ const ValidationException_1 = require("../exceptions/ValidationException");
|
|
|
8
8
|
const RuntimeException_1 = require("../exceptions/RuntimeException");
|
|
9
9
|
const NameNamespaceParser_1 = require("../core/NameNamespaceParser");
|
|
10
10
|
const TypeRegistry_1 = require("./TypeRegistry");
|
|
11
|
+
/**
|
|
12
|
+
* Turns the tree of an already parsed `@stxt.schema` document into a {@link Schema}.
|
|
13
|
+
*
|
|
14
|
+
* @param node root node of the schema document, `Schema (@stxt.schema): ...`.
|
|
15
|
+
* @returns the schema the document defines.
|
|
16
|
+
* @throws ValidationException if the document is not a valid `@stxt.schema` one.
|
|
17
|
+
*/
|
|
11
18
|
function transformNodeToSchema(node) {
|
|
12
19
|
// Node name
|
|
13
20
|
const nodeName = node.getNormalizedName();
|
|
14
21
|
const namespaceSchema = node.getNamespace();
|
|
15
|
-
//
|
|
22
|
+
// Get the name and the namespace
|
|
16
23
|
if (nodeName !== "schema" || namespaceSchema !== Schema_1.Schema.SCHEMA_NAMESPACE) {
|
|
17
|
-
throw new ValidationException_1.ValidationException(node.getLine(), "NOT_STXT_SCHEMA", `
|
|
24
|
+
throw new ValidationException_1.ValidationException(node.getLine(), "NOT_STXT_SCHEMA", `Expected schema(${Schema_1.Schema.SCHEMA_NAMESPACE}) but got ${nodeName}(${namespaceSchema})`);
|
|
18
25
|
}
|
|
19
|
-
//
|
|
26
|
+
// Get the description
|
|
20
27
|
const descrip = node.getChild("description")?.getText();
|
|
21
28
|
const schema = new Schema_1.Schema(node.getValue(), node.getLine(), descrip);
|
|
22
|
-
//
|
|
23
|
-
const allNames = new Set();
|
|
24
|
-
//
|
|
29
|
+
// Used to check that every child is defined
|
|
30
|
+
const allNames = new Set();
|
|
31
|
+
// Get the nodes
|
|
25
32
|
for (const n of node.getChildrenByName("node")) {
|
|
26
33
|
const schNode = createFrom(n, schema.getNamespace());
|
|
27
34
|
schema.addNodeDefinition(schNode);
|
|
28
35
|
allNames.add(schNode.getNormalizedName());
|
|
29
36
|
}
|
|
30
|
-
//
|
|
37
|
+
// Check that every name is defined
|
|
31
38
|
for (const schNode of schema.getNodes().values()) {
|
|
32
39
|
for (const schChild of schNode.getChildren().values()) {
|
|
33
|
-
//
|
|
40
|
+
// Only names of the same namespace are checked
|
|
34
41
|
if (schChild.getNamespace() === schema.getNamespace()) {
|
|
35
|
-
//
|
|
36
|
-
//
|
|
42
|
+
// Defensive leftover from the Java port: ChildDefinition does expose
|
|
43
|
+
// getNormalizedName(), so this check can never fail with the current class.
|
|
37
44
|
const childNorm = schChild.getNormalizedName?.();
|
|
38
45
|
if (!childNorm) {
|
|
39
46
|
throw new RuntimeException_1.RuntimeException("CHILD_DEFINITION_API_MISMATCH", "ChildDefinition.getNormalizedName() is missing in TypeScript version. Add it to ChildDefinition.");
|
|
@@ -46,6 +53,7 @@ function transformNodeToSchema(node) {
|
|
|
46
53
|
}
|
|
47
54
|
return schema;
|
|
48
55
|
}
|
|
56
|
+
/** Builds the definition of a node from a `Node:` entry of the schema document. */
|
|
49
57
|
function createFrom(n, namespace) {
|
|
50
58
|
const name = n.getValue();
|
|
51
59
|
let type = "INLINE";
|
|
@@ -57,7 +65,7 @@ function createFrom(n, namespace) {
|
|
|
57
65
|
const result = new NodeDefinition_1.NodeDefinition(name, type, n.getLine(), description);
|
|
58
66
|
const children = n.getChild("children");
|
|
59
67
|
if (children) {
|
|
60
|
-
//
|
|
68
|
+
// Schema error 13.5: Children in a Node whose type does not admit children
|
|
61
69
|
if (!TypeRegistry_1.TypeRegistry.admitsChildren(type)) {
|
|
62
70
|
throw new ValidationException_1.ValidationException(children.getLine(), "CHILDREN_NOT_ALLOWED_FOR_TYPE", `Type ${type} does not allow children (node ${name})`);
|
|
63
71
|
}
|
|
@@ -65,7 +73,7 @@ function createFrom(n, namespace) {
|
|
|
65
73
|
putChildToSchemaNode(result, child, namespace);
|
|
66
74
|
}
|
|
67
75
|
}
|
|
68
|
-
//
|
|
76
|
+
// Look at the values
|
|
69
77
|
let valuesNodes = n.getChildrenByName("values");
|
|
70
78
|
if (valuesNodes && valuesNodes.length > 0) {
|
|
71
79
|
if (type !== "ENUM") {
|
|
@@ -79,29 +87,31 @@ function createFrom(n, namespace) {
|
|
|
79
87
|
for (const v of values) {
|
|
80
88
|
result.addValue(v.getValue(), v.getLine());
|
|
81
89
|
}
|
|
82
|
-
//
|
|
90
|
+
// For the final ENUM check
|
|
83
91
|
valuesNodes = values;
|
|
84
92
|
}
|
|
85
|
-
//
|
|
93
|
+
// Look at the enum
|
|
86
94
|
if (type === "ENUM" && (!valuesNodes || valuesNodes.length === 0)) {
|
|
87
95
|
throw new ValidationException_1.ValidationException(n.getLine(), "VALUES_EMPTY_FOR_ENUM", "ENUM Type must include values");
|
|
88
96
|
}
|
|
89
97
|
return result;
|
|
90
98
|
}
|
|
99
|
+
/** Adds to a node definition the expected child a `Child:` entry declares. */
|
|
91
100
|
function putChildToSchemaNode(schemaNode, child, defNamespace) {
|
|
92
|
-
//
|
|
101
|
+
// Get the name and the namespace
|
|
93
102
|
const ns = NameNamespaceParser_1.NameNamespaceParser.parse(child.getValue(), defNamespace, child.getLine(), child.getValue());
|
|
94
103
|
const name = ns.getName();
|
|
95
104
|
const namespace = ns.getNamespace();
|
|
96
105
|
const min = getInteger(child, "min");
|
|
97
106
|
const max = getInteger(child, "max");
|
|
98
|
-
//
|
|
107
|
+
// Invalid cardinality when Min > Max (STXT-SCHEMA-SPEC 10 and 13.7)
|
|
99
108
|
if (min !== null && max !== null && min > max) {
|
|
100
109
|
throw new ValidationException_1.ValidationException(child.getLine(), "MIN_GREATER_THAN_MAX", `Min ${min} greater than Max ${max}`);
|
|
101
110
|
}
|
|
102
111
|
const schemaChild = new ChildDefinition_1.ChildDefinition(name, namespace, min, max, child.getLine());
|
|
103
112
|
schemaNode.addChildDefinition(schemaChild);
|
|
104
113
|
}
|
|
114
|
+
/** Reads an integer child (`Min`, `Max`) of a node, or null when it is not there. */
|
|
105
115
|
function getInteger(node, name) {
|
|
106
116
|
const n = node.getChild(name);
|
|
107
117
|
if (!n) {
|
|
@@ -1,4 +1,11 @@
|
|
|
1
1
|
import { Schema } from "./Schema";
|
|
2
|
+
/** Resolves the {@link Schema} that applies to a namespace. Composable implementations (memory, meta-schema...). */
|
|
2
3
|
export interface SchemaProvider {
|
|
4
|
+
/**
|
|
5
|
+
* Resolves the schema that applies to a namespace.
|
|
6
|
+
*
|
|
7
|
+
* @param namespace namespace whose schema is wanted.
|
|
8
|
+
* @returns the schema of the namespace, or null/undefined if there is none for that namespace.
|
|
9
|
+
*/
|
|
3
10
|
getSchema(namespace: string): Schema | null | undefined;
|
|
4
11
|
}
|
|
@@ -1,11 +1,38 @@
|
|
|
1
1
|
import { Schema } from "./Schema";
|
|
2
2
|
import { SchemaProvider } from "./SchemaProvider";
|
|
3
|
+
/**
|
|
4
|
+
* In-memory {@link SchemaProvider}: it keeps the schemas added with {@link SchemaProviderMemory.addSchema}
|
|
5
|
+
* indexed by namespace, and falls back to a parent provider (the meta-schema by default) for the
|
|
6
|
+
* namespaces it does not know.
|
|
7
|
+
*/
|
|
3
8
|
export declare class SchemaProviderMemory implements SchemaProvider {
|
|
4
9
|
private readonly parentSchema;
|
|
10
|
+
/**
|
|
11
|
+
* Creates an empty provider.
|
|
12
|
+
*
|
|
13
|
+
* @param parent provider to fall back to when a namespace is not registered here; the
|
|
14
|
+
* meta-schema provider when omitted.
|
|
15
|
+
*/
|
|
5
16
|
constructor(parent?: SchemaProvider | null | undefined);
|
|
6
17
|
protected readonly schemas: Map<string, Schema>;
|
|
18
|
+
/**
|
|
19
|
+
* Resolves the schema that applies to a namespace, delegating to the parent provider when it
|
|
20
|
+
* is not registered here.
|
|
21
|
+
*
|
|
22
|
+
* @param namespace namespace whose schema is wanted.
|
|
23
|
+
* @returns the schema of the namespace, or null/undefined if neither this provider nor its parent has one.
|
|
24
|
+
*/
|
|
7
25
|
getSchema(namespace: string): Schema | undefined | null;
|
|
26
|
+
/**
|
|
27
|
+
* Parses a schema document, validates it against the meta-schema and registers it under its
|
|
28
|
+
* own namespace.
|
|
29
|
+
*
|
|
30
|
+
* @param txt text of the `@stxt.schema` document.
|
|
31
|
+
* @throws ParseException or ValidationException if the document is not a valid schema.
|
|
32
|
+
*/
|
|
8
33
|
addSchema(txt: string): void;
|
|
34
|
+
/** Removes every schema registered in this provider (the parent one is left untouched). */
|
|
9
35
|
clear(): void;
|
|
36
|
+
/** @returns every schema registered in this provider, in registration order. */
|
|
10
37
|
getAllSchemas(): ReadonlyArray<Schema>;
|
|
11
38
|
}
|
|
@@ -6,7 +6,18 @@ const StringUtils_1 = require("../core/StringUtils");
|
|
|
6
6
|
const SchemaParser_1 = require("./SchemaParser");
|
|
7
7
|
const SchemaProviderMeta_1 = require("./SchemaProviderMeta");
|
|
8
8
|
const SchemaValidator_1 = require("./SchemaValidator");
|
|
9
|
+
/**
|
|
10
|
+
* In-memory {@link SchemaProvider}: it keeps the schemas added with {@link SchemaProviderMemory.addSchema}
|
|
11
|
+
* indexed by namespace, and falls back to a parent provider (the meta-schema by default) for the
|
|
12
|
+
* namespaces it does not know.
|
|
13
|
+
*/
|
|
9
14
|
class SchemaProviderMemory {
|
|
15
|
+
/**
|
|
16
|
+
* Creates an empty provider.
|
|
17
|
+
*
|
|
18
|
+
* @param parent provider to fall back to when a namespace is not registered here; the
|
|
19
|
+
* meta-schema provider when omitted.
|
|
20
|
+
*/
|
|
10
21
|
constructor(parent) {
|
|
11
22
|
this.schemas = new Map();
|
|
12
23
|
if (!parent) {
|
|
@@ -16,6 +27,13 @@ class SchemaProviderMemory {
|
|
|
16
27
|
this.parentSchema = parent;
|
|
17
28
|
}
|
|
18
29
|
}
|
|
30
|
+
/**
|
|
31
|
+
* Resolves the schema that applies to a namespace, delegating to the parent provider when it
|
|
32
|
+
* is not registered here.
|
|
33
|
+
*
|
|
34
|
+
* @param namespace namespace whose schema is wanted.
|
|
35
|
+
* @returns the schema of the namespace, or null/undefined if neither this provider nor its parent has one.
|
|
36
|
+
*/
|
|
19
37
|
getSchema(namespace) {
|
|
20
38
|
const key = StringUtils_1.StringUtils.lowerCase(namespace);
|
|
21
39
|
let result = this.schemas.get(key);
|
|
@@ -24,6 +42,13 @@ class SchemaProviderMemory {
|
|
|
24
42
|
}
|
|
25
43
|
return result;
|
|
26
44
|
}
|
|
45
|
+
/**
|
|
46
|
+
* Parses a schema document, validates it against the meta-schema and registers it under its
|
|
47
|
+
* own namespace.
|
|
48
|
+
*
|
|
49
|
+
* @param txt text of the `@stxt.schema` document.
|
|
50
|
+
* @throws ParseException or ValidationException if the document is not a valid schema.
|
|
51
|
+
*/
|
|
27
52
|
addSchema(txt) {
|
|
28
53
|
const parser = new Parser_1.Parser();
|
|
29
54
|
const node = parser.parse(txt)[0];
|
|
@@ -33,9 +58,11 @@ class SchemaProviderMemory {
|
|
|
33
58
|
const key = schema.getNamespace();
|
|
34
59
|
this.schemas.set(key, schema);
|
|
35
60
|
}
|
|
61
|
+
/** Removes every schema registered in this provider (the parent one is left untouched). */
|
|
36
62
|
clear() {
|
|
37
63
|
this.schemas.clear();
|
|
38
64
|
}
|
|
65
|
+
/** @returns every schema registered in this provider, in registration order. */
|
|
39
66
|
getAllSchemas() {
|
|
40
67
|
return Array.from(this.schemas.values());
|
|
41
68
|
}
|
|
@@ -1,8 +1,24 @@
|
|
|
1
1
|
import { Schema } from "./Schema";
|
|
2
2
|
import { SchemaProvider } from "./SchemaProvider";
|
|
3
|
+
/**
|
|
4
|
+
* {@link SchemaProvider} that defines in code the meta-schema of the schema language itself
|
|
5
|
+
* (`@stxt.schema`), so that a loaded schema can validate itself.
|
|
6
|
+
*/
|
|
3
7
|
export declare class SchemaProviderMeta implements SchemaProvider {
|
|
4
8
|
private static readonly META_TEXT;
|
|
5
9
|
private readonly meta;
|
|
10
|
+
/**
|
|
11
|
+
* Parses the meta-schema and keeps it ready to be served.
|
|
12
|
+
*
|
|
13
|
+
* @throws ValidationException with code `META_SCHEMA_INVALID` if the meta-schema does not produce exactly one document.
|
|
14
|
+
*/
|
|
6
15
|
constructor();
|
|
16
|
+
/**
|
|
17
|
+
* Serves the meta-schema of the schema language.
|
|
18
|
+
*
|
|
19
|
+
* @param namespace namespace whose schema is wanted; only `@stxt.schema` is served.
|
|
20
|
+
* @returns the meta-schema of the schema language.
|
|
21
|
+
* @throws RuntimeException with code `RESOURCE_NOT_FOUND` if any other namespace is asked for.
|
|
22
|
+
*/
|
|
7
23
|
getSchema(namespace: string): Schema;
|
|
8
24
|
}
|
|
@@ -6,7 +6,16 @@ const Parser_1 = require("../core/Parser");
|
|
|
6
6
|
const ValidationException_1 = require("../exceptions/ValidationException");
|
|
7
7
|
const RuntimeException_1 = require("../exceptions/RuntimeException");
|
|
8
8
|
const SchemaParser_1 = require("./SchemaParser");
|
|
9
|
+
/**
|
|
10
|
+
* {@link SchemaProvider} that defines in code the meta-schema of the schema language itself
|
|
11
|
+
* (`@stxt.schema`), so that a loaded schema can validate itself.
|
|
12
|
+
*/
|
|
9
13
|
class SchemaProviderMeta {
|
|
14
|
+
/**
|
|
15
|
+
* Parses the meta-schema and keeps it ready to be served.
|
|
16
|
+
*
|
|
17
|
+
* @throws ValidationException with code `META_SCHEMA_INVALID` if the meta-schema does not produce exactly one document.
|
|
18
|
+
*/
|
|
10
19
|
constructor() {
|
|
11
20
|
const parser = new Parser_1.Parser();
|
|
12
21
|
const nodes = parser.parse(SchemaProviderMeta.META_TEXT);
|
|
@@ -15,6 +24,13 @@ class SchemaProviderMeta {
|
|
|
15
24
|
}
|
|
16
25
|
this.meta = (0, SchemaParser_1.transformNodeToSchema)(nodes[0]);
|
|
17
26
|
}
|
|
27
|
+
/**
|
|
28
|
+
* Serves the meta-schema of the schema language.
|
|
29
|
+
*
|
|
30
|
+
* @param namespace namespace whose schema is wanted; only `@stxt.schema` is served.
|
|
31
|
+
* @returns the meta-schema of the schema language.
|
|
32
|
+
* @throws RuntimeException with code `RESOURCE_NOT_FOUND` if any other namespace is asked for.
|
|
33
|
+
*/
|
|
18
34
|
getSchema(namespace) {
|
|
19
35
|
if (namespace !== Schema_1.Schema.SCHEMA_NAMESPACE) {
|
|
20
36
|
throw new RuntimeException_1.RuntimeException("RESOURCE_NOT_FOUND", `Not found '${namespace}' in namespace: ${Schema_1.Schema.SCHEMA_NAMESPACE}`);
|
|
@@ -3,11 +3,31 @@ import { ValidationException } from "../exceptions/ValidationException";
|
|
|
3
3
|
import { Validator } from "../processors/Validator";
|
|
4
4
|
import { SchemaProvider } from "./SchemaProvider";
|
|
5
5
|
import { Schema } from "./Schema";
|
|
6
|
+
/** {@link Validator} that, for each node, resolves its {@link Schema} through a {@link SchemaProvider} and validates type and cardinality. */
|
|
6
7
|
export declare class SchemaValidator implements Validator {
|
|
7
8
|
private readonly schemaProvider;
|
|
8
9
|
private readonly recursiveValidation;
|
|
10
|
+
/**
|
|
11
|
+
* Creates a validator that resolves schemas through the given provider.
|
|
12
|
+
*
|
|
13
|
+
* @param schemaProvider where to resolve the schema of each namespace from.
|
|
14
|
+
* @param recursive whether the children of each node are validated recursively too.
|
|
15
|
+
*/
|
|
9
16
|
constructor(schemaProvider: SchemaProvider, recursive?: boolean);
|
|
17
|
+
/**
|
|
18
|
+
* Validates a node against the schema of its namespace.
|
|
19
|
+
*
|
|
20
|
+
* @param node already closed node to validate.
|
|
21
|
+
* @returns the validation errors found, or an empty array if the node is valid.
|
|
22
|
+
*/
|
|
10
23
|
validate(node: Node): ValidationException[];
|
|
24
|
+
/**
|
|
25
|
+
* Validates a node against an already resolved schema: existence, value type and cardinalities of its children.
|
|
26
|
+
*
|
|
27
|
+
* @param node node to validate.
|
|
28
|
+
* @param schema schema to validate against.
|
|
29
|
+
* @returns the validation errors found, empty if the node is valid.
|
|
30
|
+
*/
|
|
11
31
|
validateAgainstSchema(node: Node, schema: Schema): ValidationException[];
|
|
12
32
|
private static validateChildrenDeclared;
|
|
13
33
|
private static validateValue;
|
|
@@ -3,23 +3,36 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.SchemaValidator = void 0;
|
|
4
4
|
const ValidationException_1 = require("../exceptions/ValidationException");
|
|
5
5
|
const TypeRegistry_1 = require("./TypeRegistry");
|
|
6
|
+
/** {@link Validator} that, for each node, resolves its {@link Schema} through a {@link SchemaProvider} and validates type and cardinality. */
|
|
6
7
|
class SchemaValidator {
|
|
8
|
+
/**
|
|
9
|
+
* Creates a validator that resolves schemas through the given provider.
|
|
10
|
+
*
|
|
11
|
+
* @param schemaProvider where to resolve the schema of each namespace from.
|
|
12
|
+
* @param recursive whether the children of each node are validated recursively too.
|
|
13
|
+
*/
|
|
7
14
|
constructor(schemaProvider, recursive = false) {
|
|
8
15
|
this.schemaProvider = schemaProvider;
|
|
9
16
|
this.recursiveValidation = recursive;
|
|
10
17
|
}
|
|
18
|
+
/**
|
|
19
|
+
* Validates a node against the schema of its namespace.
|
|
20
|
+
*
|
|
21
|
+
* @param node already closed node to validate.
|
|
22
|
+
* @returns the validation errors found, or an empty array if the node is valid.
|
|
23
|
+
*/
|
|
11
24
|
validate(node) {
|
|
12
25
|
const errors = [];
|
|
13
|
-
//
|
|
26
|
+
// Get the namespace
|
|
14
27
|
const namespace = node.getNamespace();
|
|
15
28
|
const schema = this.schemaProvider.getSchema(namespace);
|
|
16
29
|
if (!schema) {
|
|
17
30
|
errors.push(new ValidationException_1.ValidationException(node.getLine(), "SCHEMA_NOT_FOUND", `Not found schema: ${namespace}`));
|
|
18
31
|
return errors;
|
|
19
32
|
}
|
|
20
|
-
//
|
|
33
|
+
// Validate the node
|
|
21
34
|
errors.push(...this.validateAgainstSchema(node, schema));
|
|
22
|
-
//
|
|
35
|
+
// Validate the children
|
|
23
36
|
if (this.recursiveValidation) {
|
|
24
37
|
for (const childNode of node.getChildren()) {
|
|
25
38
|
errors.push(...this.validate(childNode));
|
|
@@ -27,6 +40,13 @@ class SchemaValidator {
|
|
|
27
40
|
}
|
|
28
41
|
return errors;
|
|
29
42
|
}
|
|
43
|
+
/**
|
|
44
|
+
* Validates a node against an already resolved schema: existence, value type and cardinalities of its children.
|
|
45
|
+
*
|
|
46
|
+
* @param node node to validate.
|
|
47
|
+
* @param schema schema to validate against.
|
|
48
|
+
* @returns the validation errors found, empty if the node is valid.
|
|
49
|
+
*/
|
|
30
50
|
validateAgainstSchema(node, schema) {
|
|
31
51
|
const errors = [];
|
|
32
52
|
const schemaNode = schema.getNodeDefinition(node.getNormalizedName());
|
|
@@ -40,8 +60,8 @@ class SchemaValidator {
|
|
|
40
60
|
errors.push(...SchemaValidator.validateCount(schemaNode, node));
|
|
41
61
|
return errors;
|
|
42
62
|
}
|
|
43
|
-
//
|
|
44
|
-
//
|
|
63
|
+
// Closed content model (STXT-SCHEMA-SPEC, section 6): only the direct children declared
|
|
64
|
+
// in the definition of the parent are allowed; with no Children, nothing is
|
|
45
65
|
static validateChildrenDeclared(nodeDef, node) {
|
|
46
66
|
const errors = [];
|
|
47
67
|
for (const child of node.getChildren()) {
|
|
@@ -101,9 +121,9 @@ class SchemaValidator {
|
|
|
101
121
|
errors.push(new ValidationException_1.ValidationException(node.getLine(), "INVALID_NUMBER", `${childCount} nodes of '${childDef.getQualifiedName()}' and min is ${min}`));
|
|
102
122
|
}
|
|
103
123
|
if (max !== null && childCount > max) {
|
|
104
|
-
// Error
|
|
124
|
+
// Error on the parent
|
|
105
125
|
errors.push(new ValidationException_1.ValidationException(node.getLine(), "INVALID_NUMBER", `${childCount} nodes of '${childDef.getQualifiedName()}' and max is ${max}`));
|
|
106
|
-
// Error
|
|
126
|
+
// Error on each child node beyond the allowed maximum
|
|
107
127
|
for (const child of children) {
|
|
108
128
|
errors.push(new ValidationException_1.ValidationException(child.getLine(), "INVALID_NUMBER", `Too many '${childDef.getQualifiedName()}' nodes: found ${childCount}, max is ${max}`));
|
|
109
129
|
}
|
package/out/schema/Type.d.ts
CHANGED
|
@@ -1,6 +1,15 @@
|
|
|
1
1
|
import { Node } from "../core/Node";
|
|
2
2
|
import { NodeDefinition } from "./NodeDefinition";
|
|
3
|
+
/** Value type of a node (TEXT, INTEGER, URL...). Each type lives in `src/schema/type` as a singleton. */
|
|
3
4
|
export interface Type {
|
|
5
|
+
/**
|
|
6
|
+
* Validates the value of a node against this type.
|
|
7
|
+
*
|
|
8
|
+
* @param nodeDef definition of the node in the schema.
|
|
9
|
+
* @param node node to validate.
|
|
10
|
+
* @throws ValidationException if the value of the node does not match the type.
|
|
11
|
+
*/
|
|
4
12
|
validate(nodeDef: NodeDefinition, node: Node): void;
|
|
13
|
+
/** @returns the name of the type, as used in the schemas (e.g. `"TEXT"`). */
|
|
5
14
|
getName(): string;
|
|
6
15
|
}
|
|
@@ -1,8 +1,21 @@
|
|
|
1
1
|
import { Type } from "./Type";
|
|
2
|
+
/** Static registry of the STXT value types, indexed by name. Adding a new type: a new {@link Type} + `register(...)` here. */
|
|
2
3
|
export declare class TypeRegistry {
|
|
3
4
|
private static readonly REGISTRY;
|
|
4
5
|
private static readonly _init;
|
|
6
|
+
/**
|
|
7
|
+
* Tells whether nodes of a type may have children.
|
|
8
|
+
*
|
|
9
|
+
* @param nodeType name of the type.
|
|
10
|
+
* @returns true if nodes of this type may have children (only INLINE and GROUP).
|
|
11
|
+
*/
|
|
5
12
|
static admitsChildren(nodeType: string): boolean;
|
|
13
|
+
/**
|
|
14
|
+
* Looks up a registered type by name.
|
|
15
|
+
*
|
|
16
|
+
* @param nodeType name of the type to look for.
|
|
17
|
+
* @returns the {@link Type} registered under that name, or undefined if it does not exist.
|
|
18
|
+
*/
|
|
6
19
|
static get(nodeType: string): Type | undefined;
|
|
7
20
|
private static register;
|
|
8
21
|
}
|
|
@@ -21,13 +21,26 @@ const BASE64_1 = require("./type/BASE64");
|
|
|
21
21
|
const GROUP_1 = require("./type/GROUP");
|
|
22
22
|
const ENUM_1 = require("./type/ENUM");
|
|
23
23
|
const MARKDOWN_1 = require("./type/MARKDOWN");
|
|
24
|
+
/** Static registry of the STXT value types, indexed by name. Adding a new type: a new {@link Type} + `register(...)` here. */
|
|
24
25
|
class TypeRegistry {
|
|
25
|
-
// STXT-SCHEMA-SPEC 9 / STXT-TEMPLATE-SPEC 15:
|
|
26
|
+
// STXT-SCHEMA-SPEC 9 / STXT-TEMPLATE-SPEC 15: only INLINE and GROUP admit children
|
|
27
|
+
/**
|
|
28
|
+
* Tells whether nodes of a type may have children.
|
|
29
|
+
*
|
|
30
|
+
* @param nodeType name of the type.
|
|
31
|
+
* @returns true if nodes of this type may have children (only INLINE and GROUP).
|
|
32
|
+
*/
|
|
26
33
|
static admitsChildren(nodeType) {
|
|
27
34
|
return nodeType === "INLINE" || nodeType === "GROUP";
|
|
28
35
|
}
|
|
36
|
+
/**
|
|
37
|
+
* Looks up a registered type by name.
|
|
38
|
+
*
|
|
39
|
+
* @param nodeType name of the type to look for.
|
|
40
|
+
* @returns the {@link Type} registered under that name, or undefined if it does not exist.
|
|
41
|
+
*/
|
|
29
42
|
static get(nodeType) {
|
|
30
|
-
//
|
|
43
|
+
// force _init to run when the class is loaded (in case a bundler does something odd)
|
|
31
44
|
void this._init;
|
|
32
45
|
return this.REGISTRY.get(nodeType);
|
|
33
46
|
}
|
|
@@ -41,12 +54,12 @@ class TypeRegistry {
|
|
|
41
54
|
}
|
|
42
55
|
exports.TypeRegistry = TypeRegistry;
|
|
43
56
|
TypeRegistry.REGISTRY = new Map();
|
|
44
|
-
//
|
|
57
|
+
// Static initialization (no INSTANCE)
|
|
45
58
|
TypeRegistry._init = (() => {
|
|
46
|
-
//
|
|
59
|
+
// Main types
|
|
47
60
|
TypeRegistry.register(INLINE_1.INLINE);
|
|
48
61
|
TypeRegistry.register(BLOCK_1.BLOCK);
|
|
49
|
-
//
|
|
62
|
+
// Subtypes
|
|
50
63
|
TypeRegistry.register(TEXT_1.TEXT);
|
|
51
64
|
TypeRegistry.register(BOOLEAN_1.BOOLEAN);
|
|
52
65
|
TypeRegistry.register(URL_1.URL);
|
|
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.BASE64 = void 0;
|
|
4
4
|
const ValidationException_1 = require("../../exceptions/ValidationException");
|
|
5
5
|
const binaryValue_1 = require("./binaryValue");
|
|
6
|
+
/** `BASE64` type: checks that the content is valid Base64. */
|
|
6
7
|
exports.BASE64 = {
|
|
7
8
|
getName() {
|
|
8
9
|
return "BASE64";
|
|
@@ -10,12 +11,12 @@ exports.BASE64 = {
|
|
|
10
11
|
validate(ndef, n) {
|
|
11
12
|
const raw = (0, binaryValue_1.binaryValue)(n);
|
|
12
13
|
try {
|
|
13
|
-
//
|
|
14
|
+
// Try to decode it
|
|
14
15
|
const buf = Buffer.from(raw, "base64");
|
|
15
|
-
// Re-encode
|
|
16
|
-
// (
|
|
16
|
+
// Re-encode to check consistency
|
|
17
|
+
// (this keeps partially valid strings out)
|
|
17
18
|
const reencoded = buf.toString("base64");
|
|
18
|
-
//
|
|
19
|
+
// Normalize the padding before comparing
|
|
19
20
|
const normalizedInput = raw.replace(/=+$/, "");
|
|
20
21
|
const normalizedReencoded = reencoded.replace(/=+$/, "");
|
|
21
22
|
if (normalizedInput !== normalizedReencoded) {
|
|
@@ -3,11 +3,12 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.BINARY = void 0;
|
|
4
4
|
const ValidationException_1 = require("../../exceptions/ValidationException");
|
|
5
5
|
const binaryValue_1 = require("./binaryValue");
|
|
6
|
+
/** `BINARY` type: checks that the content is a string of zeros and ones (`[01]+`). */
|
|
6
7
|
exports.BINARY = {
|
|
7
8
|
getName() {
|
|
8
9
|
return "BINARY";
|
|
9
10
|
},
|
|
10
|
-
// STXT-SCHEMA-SPEC 9.5:
|
|
11
|
+
// STXT-SCHEMA-SPEC 9.5: [01]+ string
|
|
11
12
|
validate(ndef, n) {
|
|
12
13
|
const value = (0, binaryValue_1.binaryValue)(n);
|
|
13
14
|
if (!/^[01]+$/.test(value)) {
|
package/out/schema/type/BLOCK.js
CHANGED
|
@@ -2,12 +2,13 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.BLOCK = void 0;
|
|
4
4
|
const ValidationException_1 = require("../../exceptions/ValidationException");
|
|
5
|
+
/** `BLOCK` type: text block node (`>>`), with no further restriction on the content. */
|
|
5
6
|
exports.BLOCK = {
|
|
6
7
|
getName() {
|
|
7
8
|
return "BLOCK";
|
|
8
9
|
},
|
|
9
10
|
validate(nodeDef, node) {
|
|
10
|
-
//
|
|
11
|
+
// BLOCK value form (STXT-SCHEMA-SPEC 9.2): block '>>' only, no inline form
|
|
11
12
|
if (!node.isTextNode()) {
|
|
12
13
|
throw new ValidationException_1.ValidationException(node.getLine(), "BLOCK_FORM_REQUIRED", `Node ${node.getQualifiedName()} requires block form '>>'`);
|
|
13
14
|
}
|
|
@@ -2,5 +2,6 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.BOOLEAN = void 0;
|
|
4
4
|
const regexType_1 = require("./regexType");
|
|
5
|
+
/** `BOOLEAN` type: checks that the value is `true` or `false`. */
|
|
5
6
|
exports.BOOLEAN = (0, regexType_1.regexType)("BOOLEAN", /^(true|false)$/, "Invalid boolean");
|
|
6
7
|
//# sourceMappingURL=BOOLEAN.js.map
|
package/out/schema/type/DATE.js
CHANGED
|
@@ -2,5 +2,6 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.DATE = void 0;
|
|
4
4
|
const regexType_1 = require("./regexType");
|
|
5
|
+
/** `DATE` type: checks the `YYYY-MM-DD` format. */
|
|
5
6
|
exports.DATE = (0, regexType_1.regexType)("DATE", /^\d{4}-\d{2}-\d{2}$/, "Invalid date");
|
|
6
7
|
//# sourceMappingURL=DATE.js.map
|
package/out/schema/type/EMAIL.js
CHANGED
|
@@ -2,5 +2,6 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.EMAIL = void 0;
|
|
4
4
|
const regexType_1 = require("./regexType");
|
|
5
|
+
/** `EMAIL` type: checks that the value looks like an e-mail address. */
|
|
5
6
|
exports.EMAIL = (0, regexType_1.regexType)("EMAIL", /^(?=.{1,256}$)(?=.{1,64}@.{1,255}$)(?=.{1,64}@.{1,63}\..{1,63}$)[A-Za-z0-9!#$%&'*+/=?^_`{|}~.-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}$/, "Invalid email");
|
|
6
7
|
//# sourceMappingURL=EMAIL.js.map
|
package/out/schema/type/ENUM.js
CHANGED
|
@@ -2,12 +2,13 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.ENUM = void 0;
|
|
4
4
|
const ValidationException_1 = require("../../exceptions/ValidationException");
|
|
5
|
+
/** `ENUM` type: checks that the value is one of those declared in {@link NodeDefinition.getValues}. */
|
|
5
6
|
exports.ENUM = {
|
|
6
7
|
getName() {
|
|
7
8
|
return "ENUM";
|
|
8
9
|
},
|
|
9
10
|
validate(nodeDef, node) {
|
|
10
|
-
//
|
|
11
|
+
// INLINE value form (STXT-SCHEMA-SPEC 9.3): the block '>>' form is not allowed
|
|
11
12
|
if (node.isTextNode()) {
|
|
12
13
|
throw new ValidationException_1.ValidationException(node.getLine(), "NOT_ALLOWED_TEXT", `Not allowed text in node ${node.getQualifiedName()}`);
|
|
13
14
|
}
|
package/out/schema/type/GROUP.js
CHANGED
|
@@ -2,12 +2,13 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.GROUP = void 0;
|
|
4
4
|
const ValidationException_1 = require("../../exceptions/ValidationException");
|
|
5
|
+
/** `GROUP` type: container node with no value of its own, accepting children just like INLINE. */
|
|
5
6
|
exports.GROUP = {
|
|
6
7
|
getName() {
|
|
7
8
|
return "GROUP";
|
|
8
9
|
},
|
|
9
10
|
validate(nodeDef, node) {
|
|
10
|
-
//
|
|
11
|
+
// NONE value form (STXT-SCHEMA-SPEC 9.2): neither an inline value nor a '>>' block
|
|
11
12
|
if (node.getValue().length > 0 || node.isTextNode()) {
|
|
12
13
|
throw new ValidationException_1.ValidationException(node.getLine(), "INVALID_VALUE", `Node '${node.getName()}' has to be empty`);
|
|
13
14
|
}
|