@stxt-lang/core 0.9.0 → 0.10.0
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/out/all.d.ts +6 -0
- package/out/all.js +8 -1
- package/out/core/Constants.d.ts +7 -0
- package/out/core/Constants.js +7 -0
- package/out/core/LineParser.d.ts +1 -1
- package/out/core/LineParser.js +4 -4
- package/out/core/NamespaceValidator.d.ts +7 -0
- package/out/core/NamespaceValidator.js +9 -0
- package/out/core/NodeCreator.js +1 -1
- package/out/core/Parser.d.ts +5 -0
- package/out/core/Parser.js +12 -8
- package/out/exceptions/ParseException.d.ts +7 -1
- package/out/exceptions/ParseException.js +8 -2
- package/out/exceptions/RuntimeException.d.ts +6 -1
- package/out/exceptions/RuntimeException.js +7 -3
- package/out/schema/NodeDefinition.d.ts +1 -1
- package/out/schema/NodeDefinition.js +2 -2
- package/out/schema/Schema.d.ts +1 -1
- package/out/schema/Schema.js +2 -2
- package/out/schema/SchemaParser.d.ts +2 -1
- package/out/schema/SchemaParser.js +24 -9
- package/out/schema/SchemaProviderMemory.d.ts +2 -1
- package/out/schema/SchemaProviderMemory.js +8 -2
- package/out/schema/SchemaValidator.js +7 -7
- package/out/schema/TypeRegistry.js +1 -1
- package/out/schema/type/BASE64.d.ts +9 -0
- package/out/schema/type/BASE64.js +32 -15
- package/out/schema/type/DATE.d.ts +1 -1
- package/out/schema/type/DATE.js +4 -3
- package/out/schema/type/ENUM.js +1 -1
- package/out/schema/type/GROUP.js +1 -1
- package/out/schema/type/INLINE.js +1 -1
- package/out/schema/type/MARKDOWN.js +1 -1
- package/out/schema/type/TEXT.js +1 -1
- package/out/schema/type/TIME.d.ts +1 -1
- package/out/schema/type/TIME.js +4 -3
- package/out/schema/type/TIMESTAMP.d.ts +4 -1
- package/out/schema/type/TIMESTAMP.js +9 -3
- package/out/schema/type/URL.d.ts +10 -3
- package/out/schema/type/URL.js +11 -24
- package/out/schema/type/binaryValue.d.ts +6 -4
- package/out/schema/type/binaryValue.js +8 -8
- package/out/schema/type/dateTime.d.ts +9 -0
- package/out/schema/type/dateTime.js +23 -0
- package/out/schema/type/rangeType.d.ts +11 -0
- package/out/schema/type/rangeType.js +30 -0
- package/out/schema/type/regexType.js +1 -1
- package/out/template/ChildLineParser.d.ts +1 -1
- package/out/template/ChildLineParser.js +10 -4
- package/out/template/TemplateParser.d.ts +3 -2
- package/out/template/TemplateParser.js +30 -14
- package/out/template/TemplateSchemaProviderMemory.d.ts +4 -3
- package/out/template/TemplateSchemaProviderMemory.js +5 -8
- package/package.json +1 -1
package/out/schema/type/ENUM.js
CHANGED
|
@@ -10,7 +10,7 @@ exports.ENUM = {
|
|
|
10
10
|
validate(nodeDef, node) {
|
|
11
11
|
// INLINE value form (STXT-SCHEMA-SPEC 9.3): the block '>>' form is not allowed
|
|
12
12
|
if (node.isTextNode()) {
|
|
13
|
-
throw new ValidationException_1.ValidationException(node.getLine(), "
|
|
13
|
+
throw new ValidationException_1.ValidationException(node.getLine(), "BLOCK_FORM_NOT_ALLOWED", `Not allowed text in node ${node.getQualifiedName()}`);
|
|
14
14
|
}
|
|
15
15
|
const value = node.getText();
|
|
16
16
|
const allowed = nodeDef.getValues(); // ReadonlySet<string>
|
package/out/schema/type/GROUP.js
CHANGED
|
@@ -10,7 +10,7 @@ exports.GROUP = {
|
|
|
10
10
|
validate(nodeDef, node) {
|
|
11
11
|
// NONE value form (STXT-SCHEMA-SPEC 9.2): neither an inline value nor a '>>' block
|
|
12
12
|
if (node.isTextNode() || node.getText().length > 0) {
|
|
13
|
-
throw new ValidationException_1.ValidationException(node.getLine(), "
|
|
13
|
+
throw new ValidationException_1.ValidationException(node.getLine(), "VALUE_NOT_ALLOWED", `Node '${node.getName()}' has to be empty`);
|
|
14
14
|
}
|
|
15
15
|
},
|
|
16
16
|
};
|
|
@@ -10,7 +10,7 @@ exports.INLINE = {
|
|
|
10
10
|
validate(nodeDef, node) {
|
|
11
11
|
// INLINE value form (STXT-SCHEMA-SPEC 9.2): the block '>>' form is not allowed
|
|
12
12
|
if (node.isTextNode()) {
|
|
13
|
-
throw new ValidationException_1.ValidationException(node.getLine(), "
|
|
13
|
+
throw new ValidationException_1.ValidationException(node.getLine(), "BLOCK_FORM_NOT_ALLOWED", `Not allowed text in node ${node.getQualifiedName()}`);
|
|
14
14
|
}
|
|
15
15
|
},
|
|
16
16
|
};
|
|
@@ -13,7 +13,7 @@ exports.MARKDOWN = {
|
|
|
13
13
|
},
|
|
14
14
|
validate(nodeDef, node) {
|
|
15
15
|
if (node instanceof InlineNode_1.InlineNode && node.getChildren().length > 0) {
|
|
16
|
-
throw new ValidationException_1.ValidationException(node.getLine(), "
|
|
16
|
+
throw new ValidationException_1.ValidationException(node.getLine(), "CHILDREN_NOT_ALLOWED", `Not allowed children nodes in node ${node.getQualifiedName()}`);
|
|
17
17
|
}
|
|
18
18
|
},
|
|
19
19
|
};
|
package/out/schema/type/TEXT.js
CHANGED
|
@@ -10,7 +10,7 @@ exports.TEXT = {
|
|
|
10
10
|
},
|
|
11
11
|
validate(nodeDef, node) {
|
|
12
12
|
if (node instanceof InlineNode_1.InlineNode && node.getChildren().length > 0) {
|
|
13
|
-
throw new ValidationException_1.ValidationException(node.getLine(), "
|
|
13
|
+
throw new ValidationException_1.ValidationException(node.getLine(), "CHILDREN_NOT_ALLOWED", `Not allowed children nodes in node ${node.getQualifiedName()}`);
|
|
14
14
|
}
|
|
15
15
|
},
|
|
16
16
|
};
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
/** `TIME` type:
|
|
1
|
+
/** `TIME` type: `hh:mm:ss` in range (00-23, 00-59, 00-59); no fraction, no zone (STXT-SCHEMA-SPEC 9.4). */
|
|
2
2
|
export declare const TIME: import("../Type").Type;
|
package/out/schema/type/TIME.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.TIME = void 0;
|
|
4
|
-
const
|
|
5
|
-
|
|
6
|
-
|
|
4
|
+
const rangeType_1 = require("./rangeType");
|
|
5
|
+
const dateTime_1 = require("./dateTime");
|
|
6
|
+
/** `TIME` type: `hh:mm:ss` in range (00-23, 00-59, 00-59); no fraction, no zone (STXT-SCHEMA-SPEC 9.4). */
|
|
7
|
+
exports.TIME = (0, rangeType_1.rangeType)("TIME", /^(\d{2}):(\d{2}):(\d{2})$/, m => (0, dateTime_1.isValidTime)(Number(m[1]), Number(m[2]), Number(m[3])), "Invalid time");
|
|
7
8
|
//# sourceMappingURL=TIME.js.map
|
|
@@ -1,2 +1,5 @@
|
|
|
1
|
-
/**
|
|
1
|
+
/**
|
|
2
|
+
* `TIMESTAMP` type: `DATE "T" hh:mm [":" ss ["." digits]] ["Z" | sign hh:mm]` (STXT-SCHEMA-SPEC
|
|
3
|
+
* 9.4). Date, time and offset in range; seconds, fraction (one or more digits) and zone optional.
|
|
4
|
+
*/
|
|
2
5
|
export declare const TIMESTAMP: import("../Type").Type;
|
|
@@ -1,7 +1,13 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.TIMESTAMP = void 0;
|
|
4
|
-
const
|
|
5
|
-
|
|
6
|
-
|
|
4
|
+
const rangeType_1 = require("./rangeType");
|
|
5
|
+
const dateTime_1 = require("./dateTime");
|
|
6
|
+
/**
|
|
7
|
+
* `TIMESTAMP` type: `DATE "T" hh:mm [":" ss ["." digits]] ["Z" | sign hh:mm]` (STXT-SCHEMA-SPEC
|
|
8
|
+
* 9.4). Date, time and offset in range; seconds, fraction (one or more digits) and zone optional.
|
|
9
|
+
*/
|
|
10
|
+
exports.TIMESTAMP = (0, rangeType_1.rangeType)("TIMESTAMP", /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2})(?::(\d{2})(?:\.\d+)?)?(?:Z|[+-](\d{2}):(\d{2}))?$/, m => (0, dateTime_1.isValidDate)(Number(m[1]), Number(m[2]), Number(m[3]))
|
|
11
|
+
&& (0, dateTime_1.isValidTime)(Number(m[4]), Number(m[5]), m[6] === undefined ? 0 : Number(m[6]))
|
|
12
|
+
&& (m[7] === undefined || (0, dateTime_1.isValidTime)(Number(m[7]), Number(m[8]), 0)), "Invalid timestamp");
|
|
7
13
|
//# sourceMappingURL=TIMESTAMP.js.map
|
package/out/schema/type/URL.d.ts
CHANGED
|
@@ -1,3 +1,10 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
/**
|
|
2
|
+
* `URL` type: an absolute URL with a mandatory scheme and host, following the grammar of
|
|
3
|
+
* STXT-SCHEMA-SPEC 9.4 — `scheme "://" [userinfo "@"] host [":" port] ["/" path] ["?" query]
|
|
4
|
+
* ["#" fragment]` — and not the platform URL parser, so every port accepts exactly the same
|
|
5
|
+
* values. Any scheme of the form letter + letters/digits/`+`/`-`/`.` is accepted; the host is
|
|
6
|
+
* non-empty (no TLD required, IPv6 in brackets, non-ASCII kept as it is); a value without a
|
|
7
|
+
* scheme, a scheme without `//` and host (`mailto:`, `urn:`, `file:///`), inner blanks or a
|
|
8
|
+
* non-numeric port are rejected. Nothing is resolved or normalised.
|
|
9
|
+
*/
|
|
10
|
+
export declare const URL: import("../Type").Type;
|
package/out/schema/type/URL.js
CHANGED
|
@@ -1,28 +1,15 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.URL = void 0;
|
|
4
|
-
const
|
|
5
|
-
/**
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
const url = n.getText();
|
|
16
|
-
try {
|
|
17
|
-
const parsed = new globalThis.URL(url);
|
|
18
|
-
const ok = !!parsed.protocol && !!parsed.hostname;
|
|
19
|
-
if (!ok) {
|
|
20
|
-
throw new ValidationException_1.ValidationException(n.getLine(), "INVALID_URL_STRUCTURE", `Invalid URL: ${url}`);
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
|
-
catch {
|
|
24
|
-
throw new ValidationException_1.ValidationException(n.getLine(), "INVALID_VALUE", `Invalid URL: ${url}`);
|
|
25
|
-
}
|
|
26
|
-
},
|
|
27
|
-
};
|
|
4
|
+
const regexType_1 = require("./regexType");
|
|
5
|
+
/**
|
|
6
|
+
* `URL` type: an absolute URL with a mandatory scheme and host, following the grammar of
|
|
7
|
+
* STXT-SCHEMA-SPEC 9.4 — `scheme "://" [userinfo "@"] host [":" port] ["/" path] ["?" query]
|
|
8
|
+
* ["#" fragment]` — and not the platform URL parser, so every port accepts exactly the same
|
|
9
|
+
* values. Any scheme of the form letter + letters/digits/`+`/`-`/`.` is accepted; the host is
|
|
10
|
+
* non-empty (no TLD required, IPv6 in brackets, non-ASCII kept as it is); a value without a
|
|
11
|
+
* scheme, a scheme without `//` and host (`mailto:`, `urn:`, `file:///`), inner blanks or a
|
|
12
|
+
* non-numeric port are rejected. Nothing is resolved or normalised.
|
|
13
|
+
*/
|
|
14
|
+
exports.URL = (0, regexType_1.regexType)("URL", /^[A-Za-z][A-Za-z0-9+.-]*:\/\/(?:[^ \t/?#@]+@)?(?:\[[0-9A-Fa-f:.]+\]|[^ \t/?#@:[\]]+)(?::[0-9]+)?(?:\/[^ \t?#]*)?(?:\?[^ \t#]*)?(?:#[^ \t]*)?$/, "Invalid URL");
|
|
28
15
|
//# sourceMappingURL=URL.js.map
|
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
import { Node } from "../../core/Node";
|
|
2
2
|
/**
|
|
3
3
|
* STXT-SCHEMA-SPEC 9.5: effective value for the INLINE/BLOCK binary types
|
|
4
|
-
* (HEXADECIMAL, BINARY, BASE64).
|
|
5
|
-
*
|
|
6
|
-
*
|
|
4
|
+
* (HEXADECIMAL, BINARY, BASE64). Every blank (U+0020 space, U+0009 tab) is
|
|
5
|
+
* removed wherever it is, in both forms; in BLOCK form the lines are
|
|
6
|
+
* concatenated first, which also drops line breaks and empty lines. So
|
|
7
|
+
* `DE AD BE EF`, `1010 1010` and Base64 wrapped at 76 columns validate. No
|
|
8
|
+
* other character is removed: `DE:AD` or `DE-AD` stay invalid.
|
|
7
9
|
*
|
|
8
10
|
* @param node node whose value is wanted.
|
|
9
|
-
* @returns the
|
|
11
|
+
* @returns the value with no blanks, ready for the grammar of the type.
|
|
10
12
|
*/
|
|
11
13
|
export declare function binaryValue(node: Node): string;
|
|
@@ -4,17 +4,17 @@ exports.binaryValue = binaryValue;
|
|
|
4
4
|
const TextNode_1 = require("../../core/TextNode");
|
|
5
5
|
/**
|
|
6
6
|
* STXT-SCHEMA-SPEC 9.5: effective value for the INLINE/BLOCK binary types
|
|
7
|
-
* (HEXADECIMAL, BINARY, BASE64).
|
|
8
|
-
*
|
|
9
|
-
*
|
|
7
|
+
* (HEXADECIMAL, BINARY, BASE64). Every blank (U+0020 space, U+0009 tab) is
|
|
8
|
+
* removed wherever it is, in both forms; in BLOCK form the lines are
|
|
9
|
+
* concatenated first, which also drops line breaks and empty lines. So
|
|
10
|
+
* `DE AD BE EF`, `1010 1010` and Base64 wrapped at 76 columns validate. No
|
|
11
|
+
* other character is removed: `DE:AD` or `DE-AD` stay invalid.
|
|
10
12
|
*
|
|
11
13
|
* @param node node whose value is wanted.
|
|
12
|
-
* @returns the
|
|
14
|
+
* @returns the value with no blanks, ready for the grammar of the type.
|
|
13
15
|
*/
|
|
14
16
|
function binaryValue(node) {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
}
|
|
18
|
-
return node.getTextLines().map((line) => line.trim()).join("");
|
|
17
|
+
const raw = node instanceof TextNode_1.TextNode ? node.getTextLines().join("") : node.getText();
|
|
18
|
+
return raw.replace(/[ \t]/g, "");
|
|
19
19
|
}
|
|
20
20
|
//# sourceMappingURL=binaryValue.js.map
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Calendar and clock ranges shared by `DATE`, `TIME` and `TIMESTAMP` (STXT-SCHEMA-SPEC 9.4):
|
|
3
|
+
* the shape of a value is checked by each type's regular expression, the ranges by these
|
|
4
|
+
* helpers. Never `Date.parse`, which rolls `2026-02-30` into March.
|
|
5
|
+
*/
|
|
6
|
+
/** True if the year-month-day exists in the proleptic Gregorian calendar (year 0000-9999). */
|
|
7
|
+
export declare function isValidDate(year: number, month: number, day: number): boolean;
|
|
8
|
+
/** True if hour 00-23, minute 00-59, second 00-59 (no leap second). */
|
|
9
|
+
export declare function isValidTime(hour: number, minute: number, second: number): boolean;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Calendar and clock ranges shared by `DATE`, `TIME` and `TIMESTAMP` (STXT-SCHEMA-SPEC 9.4):
|
|
4
|
+
* the shape of a value is checked by each type's regular expression, the ranges by these
|
|
5
|
+
* helpers. Never `Date.parse`, which rolls `2026-02-30` into March.
|
|
6
|
+
*/
|
|
7
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
8
|
+
exports.isValidDate = isValidDate;
|
|
9
|
+
exports.isValidTime = isValidTime;
|
|
10
|
+
/** True if the year-month-day exists in the proleptic Gregorian calendar (year 0000-9999). */
|
|
11
|
+
function isValidDate(year, month, day) {
|
|
12
|
+
if (month < 1 || month > 12 || day < 1) {
|
|
13
|
+
return false;
|
|
14
|
+
}
|
|
15
|
+
const leap = (year % 4 === 0 && year % 100 !== 0) || year % 400 === 0;
|
|
16
|
+
const daysInMonth = [31, leap ? 29 : 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
|
|
17
|
+
return day <= daysInMonth[month - 1];
|
|
18
|
+
}
|
|
19
|
+
/** True if hour 00-23, minute 00-59, second 00-59 (no leap second). */
|
|
20
|
+
function isValidTime(hour, minute, second) {
|
|
21
|
+
return hour <= 23 && minute <= 59 && second <= 59;
|
|
22
|
+
}
|
|
23
|
+
//# sourceMappingURL=dateTime.js.map
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { Type } from "../Type";
|
|
2
|
+
/**
|
|
3
|
+
* A type whose value must match a regular expression and whose captured groups must then pass
|
|
4
|
+
* a range check (the calendar and clock types of STXT-SCHEMA-SPEC 9.4). INLINE value form only.
|
|
5
|
+
*
|
|
6
|
+
* @param name name of the type.
|
|
7
|
+
* @param pattern shape of the value, with the groups `inRange` reads.
|
|
8
|
+
* @param inRange true if the captured groups are in range.
|
|
9
|
+
* @param error text of the `INVALID_VALUE` message.
|
|
10
|
+
*/
|
|
11
|
+
export declare function rangeType(name: string, pattern: RegExp, inRange: (m: RegExpExecArray) => boolean, error: string): Type;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.rangeType = rangeType;
|
|
4
|
+
const ValidationException_1 = require("../../exceptions/ValidationException");
|
|
5
|
+
/**
|
|
6
|
+
* A type whose value must match a regular expression and whose captured groups must then pass
|
|
7
|
+
* a range check (the calendar and clock types of STXT-SCHEMA-SPEC 9.4). INLINE value form only.
|
|
8
|
+
*
|
|
9
|
+
* @param name name of the type.
|
|
10
|
+
* @param pattern shape of the value, with the groups `inRange` reads.
|
|
11
|
+
* @param inRange true if the captured groups are in range.
|
|
12
|
+
* @param error text of the `INVALID_VALUE` message.
|
|
13
|
+
*/
|
|
14
|
+
function rangeType(name, pattern, inRange, error) {
|
|
15
|
+
return {
|
|
16
|
+
getName: () => name,
|
|
17
|
+
validate(nodeDef, node) {
|
|
18
|
+
// INLINE value form (STXT-SCHEMA-SPEC 9.4): the block '>>' form is not allowed
|
|
19
|
+
if (node.isTextNode()) {
|
|
20
|
+
throw new ValidationException_1.ValidationException(node.getLine(), "BLOCK_FORM_NOT_ALLOWED", `Not allowed text in node ${node.getQualifiedName()}`);
|
|
21
|
+
}
|
|
22
|
+
const value = node.getText();
|
|
23
|
+
const m = pattern.exec(value);
|
|
24
|
+
if (m === null || !inRange(m)) {
|
|
25
|
+
throw new ValidationException_1.ValidationException(node.getLine(), "INVALID_VALUE", `${node.getName()}: ${error} (${value})`);
|
|
26
|
+
}
|
|
27
|
+
},
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
//# sourceMappingURL=rangeType.js.map
|
|
@@ -16,7 +16,7 @@ function regexType(name, pattern, error) {
|
|
|
16
16
|
validate(nodeDef, node) {
|
|
17
17
|
// INLINE value form (STXT-SCHEMA-SPEC 9.3/9.4): the block '>>' form is not allowed
|
|
18
18
|
if (node.isTextNode()) {
|
|
19
|
-
throw new ValidationException_1.ValidationException(node.getLine(), "
|
|
19
|
+
throw new ValidationException_1.ValidationException(node.getLine(), "BLOCK_FORM_NOT_ALLOWED", `Not allowed text in node ${node.getQualifiedName()}`);
|
|
20
20
|
}
|
|
21
21
|
const value = node.getText();
|
|
22
22
|
if (!pattern.test(value)) {
|
|
@@ -9,7 +9,7 @@ export declare class ChildLineParser {
|
|
|
9
9
|
* @param rawLine inline value of the node, `(min,max) TYPE [values]`.
|
|
10
10
|
* @param lineNumber line number, for the error messages.
|
|
11
11
|
* @returns the line already split into type, cardinality and values.
|
|
12
|
-
* @throws ValidationException with code `
|
|
12
|
+
* @throws ValidationException with code `STRUCTURE_LINE_NOT_VALID`, `CARDINALITY_NOT_VALID`,
|
|
13
13
|
* `MIN_GREATER_THAN_MAX` or `VALUE_DUPLICATED` if the line is not valid.
|
|
14
14
|
*/
|
|
15
15
|
static parse(rawLine: string, lineNumber: number): ChildLine;
|
|
@@ -12,7 +12,7 @@ class ChildLineParser {
|
|
|
12
12
|
* @param rawLine inline value of the node, `(min,max) TYPE [values]`.
|
|
13
13
|
* @param lineNumber line number, for the error messages.
|
|
14
14
|
* @returns the line already split into type, cardinality and values.
|
|
15
|
-
* @throws ValidationException with code `
|
|
15
|
+
* @throws ValidationException with code `STRUCTURE_LINE_NOT_VALID`, `CARDINALITY_NOT_VALID`,
|
|
16
16
|
* `MIN_GREATER_THAN_MAX` or `VALUE_DUPLICATED` if the line is not valid.
|
|
17
17
|
*/
|
|
18
18
|
static parse(rawLine, lineNumber) {
|
|
@@ -21,7 +21,7 @@ class ChildLineParser {
|
|
|
21
21
|
}
|
|
22
22
|
const m = ChildLineParser.CHILD_LINE_PATTERN.exec(rawLine);
|
|
23
23
|
if (!m) {
|
|
24
|
-
throw new ValidationException_1.ValidationException(lineNumber, "
|
|
24
|
+
throw new ValidationException_1.ValidationException(lineNumber, "STRUCTURE_LINE_NOT_VALID", `Line not valid: ${rawLine}`);
|
|
25
25
|
}
|
|
26
26
|
// m[1]=count, m[2]=type, m[3]=values
|
|
27
27
|
let type = m[2]?.trim() ?? "";
|
|
@@ -54,7 +54,7 @@ class ChildLineParser {
|
|
|
54
54
|
else if (count.includes(",")) {
|
|
55
55
|
const parts = count.split(",");
|
|
56
56
|
if (parts.length !== 2) {
|
|
57
|
-
throw new ValidationException_1.ValidationException(lineNumber, "
|
|
57
|
+
throw new ValidationException_1.ValidationException(lineNumber, "CARDINALITY_NOT_VALID", `Invalid count ${count} in line: ${rawLine}`);
|
|
58
58
|
}
|
|
59
59
|
const aNum = ChildLineParser.parseCount(parts[0].trim(), count, rawLine, lineNumber);
|
|
60
60
|
const bNum = ChildLineParser.parseCount(parts[1].trim(), count, rawLine, lineNumber);
|
|
@@ -77,6 +77,12 @@ class ChildLineParser {
|
|
|
77
77
|
const list = [];
|
|
78
78
|
for (let part of parts) {
|
|
79
79
|
part = part.trim();
|
|
80
|
+
// An empty item ("[a, , b]", "[a, b,]") is an error, as an empty Value: is in a
|
|
81
|
+
// schema (STXT-TEMPLATE-SPEC 14.14). Only the whole list may be empty ("[]"),
|
|
82
|
+
// which the template parser reports as VALUES_REQUIRED.
|
|
83
|
+
if (part.length === 0 && parts.length > 1) {
|
|
84
|
+
throw new ValidationException_1.ValidationException(lineNumber, "VALUE_EMPTY", `Empty ENUM value in ${valuesStr}`);
|
|
85
|
+
}
|
|
80
86
|
if (part.length === 0) {
|
|
81
87
|
continue;
|
|
82
88
|
}
|
|
@@ -97,7 +103,7 @@ class ChildLineParser {
|
|
|
97
103
|
// num, min and max must be non-negative integers, with no trailing text (STXT-TEMPLATE-SPEC 7.1)
|
|
98
104
|
static parseCount(num, count, rawLine, lineNumber) {
|
|
99
105
|
if (!/^\d+$/.test(num)) {
|
|
100
|
-
throw new ValidationException_1.ValidationException(lineNumber, "
|
|
106
|
+
throw new ValidationException_1.ValidationException(lineNumber, "CARDINALITY_NOT_VALID", `Invalid count ${count} in line: ${rawLine}`);
|
|
101
107
|
}
|
|
102
108
|
return parseInt(num, 10);
|
|
103
109
|
}
|
|
@@ -5,7 +5,8 @@ import { Schema } from "../schema/Schema";
|
|
|
5
5
|
*
|
|
6
6
|
* @param node root of the already parsed `@stxt.template` document.
|
|
7
7
|
* @returns the resulting {@link Schema}.
|
|
8
|
-
* @throws ValidationException
|
|
9
|
-
*
|
|
8
|
+
* @throws ValidationException with `TEMPLATE_ROOT_NOT_VALID` or `TEMPLATE_NAMESPACE_EMPTY` if the root is
|
|
9
|
+
* not `Template (@stxt.template): ns`, or with the code of the rule the template breaks, with the
|
|
10
|
+
* line already shifted to the one of the original document.
|
|
10
11
|
*/
|
|
11
12
|
export declare function transformTemplateNodeToSchema(node: Node): Schema;
|
|
@@ -11,17 +11,33 @@ const StringUtils_1 = require("../core/StringUtils");
|
|
|
11
11
|
const ChildLineParser_1 = require("./ChildLineParser");
|
|
12
12
|
const ParseException_1 = require("../exceptions/ParseException");
|
|
13
13
|
const TypeRegistry_1 = require("../schema/TypeRegistry");
|
|
14
|
+
const NamespaceValidator_1 = require("../core/NamespaceValidator");
|
|
15
|
+
/** Namespace of the template language itself, `@stxt.template`. */
|
|
16
|
+
const TEMPLATE_NAMESPACE = "@stxt.template";
|
|
14
17
|
/**
|
|
15
18
|
* Turns the tree of an already parsed `@stxt.template` document into an equivalent {@link Schema}.
|
|
16
19
|
*
|
|
17
20
|
* @param node root of the already parsed `@stxt.template` document.
|
|
18
21
|
* @returns the resulting {@link Schema}.
|
|
19
|
-
* @throws ValidationException
|
|
20
|
-
*
|
|
22
|
+
* @throws ValidationException with `TEMPLATE_ROOT_NOT_VALID` or `TEMPLATE_NAMESPACE_EMPTY` if the root is
|
|
23
|
+
* not `Template (@stxt.template): ns`, or with the code of the rule the template breaks, with the
|
|
24
|
+
* line already shifted to the one of the original document.
|
|
21
25
|
*/
|
|
22
26
|
function transformTemplateNodeToSchema(node) {
|
|
27
|
+
// The root must be `Template (@stxt.template): ns`
|
|
28
|
+
if (node.getCanonicalName() !== "template" || node.getNamespace() !== TEMPLATE_NAMESPACE) {
|
|
29
|
+
throw new ValidationException_1.ValidationException(node.getLine(), "TEMPLATE_ROOT_NOT_VALID", `Expected template(${TEMPLATE_NAMESPACE}) but got ${node.getCanonicalName()}(${node.getNamespace()})`);
|
|
30
|
+
}
|
|
31
|
+
// The target namespace: required, and with a valid format
|
|
32
|
+
const targetNamespace = StringUtils_1.StringUtils.lowerCase(node.getText());
|
|
33
|
+
if (!targetNamespace || targetNamespace.trim().length === 0) {
|
|
34
|
+
throw new ValidationException_1.ValidationException(node.getLine(), "TEMPLATE_NAMESPACE_EMPTY", "Template namespace is empty");
|
|
35
|
+
}
|
|
36
|
+
if (!NamespaceValidator_1.NamespaceValidator.isValid(targetNamespace)) {
|
|
37
|
+
throw new ValidationException_1.ValidationException(node.getLine(), "TEMPLATE_ROOT_NOT_VALID", `Template namespace not valid: ${targetNamespace}`);
|
|
38
|
+
}
|
|
23
39
|
// Set the namespace
|
|
24
|
-
const result = new Schema_1.Schema(
|
|
40
|
+
const result = new Schema_1.Schema(targetNamespace, node.getLine(), undefined);
|
|
25
41
|
// Look for the structure node (a template is an inline root; a text root has none)
|
|
26
42
|
const structure = node instanceof InlineNode_1.InlineNode ? node.getChild("structure") : null;
|
|
27
43
|
if (!structure) {
|
|
@@ -76,7 +92,7 @@ function addToSchema(schema, node) {
|
|
|
76
92
|
// A Structure line must use the template grammar's ':' form. The core parser
|
|
77
93
|
// also accepts BLOCK nodes here, so reject them explicitly (STXT-TEMPLATE-SPEC 6.3).
|
|
78
94
|
if (!(node instanceof InlineNode_1.InlineNode)) {
|
|
79
|
-
throw new ValidationException_1.ValidationException(node.getLine(), "
|
|
95
|
+
throw new ValidationException_1.ValidationException(node.getLine(), "STRUCTURE_LINE_NOT_VALID", "Template Structure lines must use ':'");
|
|
80
96
|
}
|
|
81
97
|
// Get the qualified name
|
|
82
98
|
let namespace = node.getNamespace();
|
|
@@ -91,7 +107,7 @@ function addToSchema(schema, node) {
|
|
|
91
107
|
// and no children (STXT-TEMPLATE-SPEC 6.4, 10 and 14.15)
|
|
92
108
|
const type = cl.getType();
|
|
93
109
|
if (type && type.trim().length > 0) {
|
|
94
|
-
throw new ValidationException_1.ValidationException(node.getLine(), "
|
|
110
|
+
throw new ValidationException_1.ValidationException(node.getLine(), "TYPE_NOT_ALLOWED_IN_EXTERNAL_NAMESPACE", "Not allowed type definition in external namespaces");
|
|
95
111
|
}
|
|
96
112
|
const values = cl.getValues();
|
|
97
113
|
if (values) {
|
|
@@ -125,7 +141,7 @@ function addToSchema(schema, node) {
|
|
|
125
141
|
// Same code as SchemaParser: a template is sugar equivalent to a schema
|
|
126
142
|
// (STXT-TEMPLATE-SPEC 13), so the same condition must not change its code
|
|
127
143
|
// depending on the entry point
|
|
128
|
-
throw new ValidationException_1.ValidationException(node.getLine(), "
|
|
144
|
+
throw new ValidationException_1.ValidationException(node.getLine(), "VALUES_NOT_ALLOWED_FOR_TYPE", `Values only supported for type ENUM, not for type ${type}`);
|
|
129
145
|
}
|
|
130
146
|
for (const v of values) {
|
|
131
147
|
schemaNode.addValue(v, node.getLine());
|
|
@@ -133,13 +149,13 @@ function addToSchema(schema, node) {
|
|
|
133
149
|
}
|
|
134
150
|
// An ENUM with no list of values is an invalid template (STXT-TEMPLATE-SPEC 9 and 13.7)
|
|
135
151
|
if (type === "ENUM" && (!values || values.length === 0)) {
|
|
136
|
-
throw new ValidationException_1.ValidationException(node.getLine(), "
|
|
152
|
+
throw new ValidationException_1.ValidationException(node.getLine(), "VALUES_REQUIRED", "ENUM Type must include values");
|
|
137
153
|
}
|
|
138
154
|
}
|
|
139
155
|
else {
|
|
140
156
|
const type = cl.getType();
|
|
141
157
|
if (!type || !type.startsWith("@")) {
|
|
142
|
-
throw new ValidationException_1.ValidationException(node.getLine(), "
|
|
158
|
+
throw new ValidationException_1.ValidationException(node.getLine(), "REFERENCE_REQUIRED", `Multiple node reference must start with @: ${node.getName()}`);
|
|
143
159
|
}
|
|
144
160
|
const reference = type.substring(1).trim();
|
|
145
161
|
// Reference and explicit type on the same line (STXT-TEMPLATE-SPEC 14.13)
|
|
@@ -148,7 +164,7 @@ function addToSchema(schema, node) {
|
|
|
148
164
|
throw new ValidationException_1.ValidationException(node.getLine(), "REFERENCE_WITH_TYPE_NOT_ALLOWED", `Reference '@${node.getName()}' can not declare a type: ${explicitType}`);
|
|
149
165
|
}
|
|
150
166
|
if (StringUtils_1.StringUtils.normalize(reference) !== node.getCanonicalName()) {
|
|
151
|
-
throw new ValidationException_1.ValidationException(node.getLine(), "
|
|
167
|
+
throw new ValidationException_1.ValidationException(node.getLine(), "REFERENCE_NAME_NOT_VALID", `Reference must be '@${node.getName()}', not '${reference}'`);
|
|
152
168
|
}
|
|
153
169
|
// A reference may override the cardinality, but it may redefine neither the ENUM
|
|
154
170
|
// values nor the children (STXT-TEMPLATE-SPEC 6.4)
|
|
@@ -171,7 +187,7 @@ function addToSchema(schema, node) {
|
|
|
171
187
|
for (const child of childrenNode) {
|
|
172
188
|
// STXT-TEMPLATE-SPEC 6.3: every Structure line uses ':', so a child is inline too
|
|
173
189
|
if (!(child instanceof InlineNode_1.InlineNode)) {
|
|
174
|
-
throw new ValidationException_1.ValidationException(child.getLine(), "
|
|
190
|
+
throw new ValidationException_1.ValidationException(child.getLine(), "STRUCTURE_LINE_NOT_VALID", "Template Structure lines must use ':'");
|
|
175
191
|
}
|
|
176
192
|
cl = ChildLineParser_1.ChildLineParser.parse(child.getValue(), child.getLine());
|
|
177
193
|
const childName = child.getName();
|
|
@@ -213,20 +229,20 @@ function addDescriptions(schema, nodes) {
|
|
|
213
229
|
}
|
|
214
230
|
// No external descriptions
|
|
215
231
|
if (namespace !== schema.getNamespace()) {
|
|
216
|
-
throw new ValidationException_1.ValidationException(node.getLine(), "
|
|
232
|
+
throw new ValidationException_1.ValidationException(node.getLine(), "DESCRIPTION_NOT_ALLOWED_IN_EXTERNAL_NAMESPACE", "Not allowed description in external namespaces");
|
|
217
233
|
}
|
|
218
234
|
// No children either
|
|
219
235
|
if (node instanceof InlineNode_1.InlineNode && node.getChildren().length > 0) {
|
|
220
|
-
throw new ValidationException_1.ValidationException(node.getLine(), "
|
|
236
|
+
throw new ValidationException_1.ValidationException(node.getLine(), "DESCRIPTION_CHILDREN_NOT_ALLOWED", "Not allowed children in description");
|
|
221
237
|
}
|
|
222
238
|
// Look for the node in the schema
|
|
223
239
|
const nodeDef = schema.getNodeDefinition(node.getName());
|
|
224
240
|
if (!nodeDef) {
|
|
225
|
-
throw new ValidationException_1.ValidationException(node.getLine(), "
|
|
241
|
+
throw new ValidationException_1.ValidationException(node.getLine(), "DESCRIPTION_NODE_NOT_FOUND", `Not found node with name: ${node.getName()}`);
|
|
226
242
|
}
|
|
227
243
|
// No more than one entry per node is allowed (STXT-TEMPLATE-SPEC 12)
|
|
228
244
|
if (nodeDef.getDescription() !== undefined) {
|
|
229
|
-
throw new ValidationException_1.ValidationException(node.getLine(), "
|
|
245
|
+
throw new ValidationException_1.ValidationException(node.getLine(), "DESCRIPTION_DUPLICATED", `Exists a previous description for node: ${node.getName()}`);
|
|
230
246
|
}
|
|
231
247
|
nodeDef.setDescription(node.getText());
|
|
232
248
|
});
|
|
@@ -17,9 +17,10 @@ export declare class TemplateSchemaProviderMemory extends SchemaProviderMemory {
|
|
|
17
17
|
* schema it produces.
|
|
18
18
|
*
|
|
19
19
|
* @param template text of the `@stxt.template` document.
|
|
20
|
-
* @throws ValidationException with code `
|
|
21
|
-
* one
|
|
22
|
-
*
|
|
20
|
+
* @throws ValidationException with code `TEMPLATE_MULTIPLE_ROOTS` if the document does not hold
|
|
21
|
+
* exactly one root node, `TEMPLATE_ROOT_NOT_VALID` or `TEMPLATE_NAMESPACE_EMPTY` if that
|
|
22
|
+
* root is not `Template (@stxt.template): ns`, or the first validation error if the
|
|
23
|
+
* template does not validate against the template meta-schema.
|
|
23
24
|
*/
|
|
24
25
|
addTemplate(template: string): void;
|
|
25
26
|
}
|
|
@@ -30,15 +30,16 @@ class TemplateSchemaProviderMemory extends SchemaProviderMemory_1.SchemaProvider
|
|
|
30
30
|
* schema it produces.
|
|
31
31
|
*
|
|
32
32
|
* @param template text of the `@stxt.template` document.
|
|
33
|
-
* @throws ValidationException with code `
|
|
34
|
-
* one
|
|
35
|
-
*
|
|
33
|
+
* @throws ValidationException with code `TEMPLATE_MULTIPLE_ROOTS` if the document does not hold
|
|
34
|
+
* exactly one root node, `TEMPLATE_ROOT_NOT_VALID` or `TEMPLATE_NAMESPACE_EMPTY` if that
|
|
35
|
+
* root is not `Template (@stxt.template): ns`, or the first validation error if the
|
|
36
|
+
* template does not validate against the template meta-schema.
|
|
36
37
|
*/
|
|
37
38
|
addTemplate(template) {
|
|
38
39
|
const parser = new Parser_1.Parser();
|
|
39
40
|
const nodes = parser.parse(template);
|
|
40
41
|
if (nodes.length !== 1) {
|
|
41
|
-
throw new ValidationException_1.ValidationException(0, "
|
|
42
|
+
throw new ValidationException_1.ValidationException(0, "TEMPLATE_MULTIPLE_ROOTS", `A template document must hold exactly 1 root node, got ${nodes.length}`);
|
|
42
43
|
}
|
|
43
44
|
// A template that does not validate against the template meta-schema must not
|
|
44
45
|
// be registered (same policy as UnifiedSchemaProvider/DiscoveryResolver)
|
|
@@ -49,10 +50,6 @@ class TemplateSchemaProviderMemory extends SchemaProviderMemory_1.SchemaProvider
|
|
|
49
50
|
}
|
|
50
51
|
// Build the schema out of the template
|
|
51
52
|
const sch = (0, TemplateParser_1.transformTemplateNodeToSchema)(nodes[0]);
|
|
52
|
-
// Minimum safety check (Java checked the expected namespace here too)
|
|
53
|
-
if (!sch.getNamespace() || sch.getNamespace().trim().length === 0) {
|
|
54
|
-
throw new ValidationException_1.ValidationException(0, "INVALID_SCHEMA", "Schema namespace is empty");
|
|
55
|
-
}
|
|
56
53
|
this.schemas.set(sch.getNamespace(), sch);
|
|
57
54
|
}
|
|
58
55
|
}
|