@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/all.d.ts
CHANGED
|
@@ -5,6 +5,12 @@ export { Parser } from "./core/Parser";
|
|
|
5
5
|
export { ParseResult } from "./core/ParseResult";
|
|
6
6
|
export { Line } from "./core/Line";
|
|
7
7
|
export { Constants } from "./core/Constants";
|
|
8
|
+
/**
|
|
9
|
+
* Version of the STXT specifications (STXT-SPEC, STXT-TREE-SPEC, STXT-SCHEMA-SPEC,
|
|
10
|
+
* STXT-TEMPLATE-SPEC, STXT-DISCOVERY-SPEC) this library implements, distinct from the version of
|
|
11
|
+
* the package. Same value as `Constants.SPEC_VERSION`.
|
|
12
|
+
*/
|
|
13
|
+
export declare const SPEC_VERSION: string;
|
|
8
14
|
export { parseLine } from "./core/LineParser";
|
|
9
15
|
export { StringUtils } from "./core/StringUtils";
|
|
10
16
|
export { ParseException } from "./exceptions/ParseException";
|
package/out/all.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
// Anything that should be consumable by third parties (e.g. the VSCode extension)
|
|
4
4
|
// has to be re-exported from here.
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.DiscoveryError = exports.DiscoveryResult = exports.DiscoveryResolver = exports.transformTemplateNodeToSchema = exports.toCanonicalJson = exports.toCanonicalTree = exports.IndentStyle = exports.NodeWriter = exports.ConditionalValidator = exports.UnifiedSchemaProvider = exports.transformNodeToSchema = exports.ChildDefinition = exports.NodeDefinition = exports.SchemaValidator = exports.Schema = exports.ValidationException = exports.ParseException = exports.StringUtils = exports.parseLine = exports.Constants = exports.Line = exports.ParseResult = exports.Parser = exports.TextNode = exports.InlineNode = exports.Node = void 0;
|
|
6
|
+
exports.DiscoveryError = exports.DiscoveryResult = exports.DiscoveryResolver = exports.transformTemplateNodeToSchema = exports.toCanonicalJson = exports.toCanonicalTree = exports.IndentStyle = exports.NodeWriter = exports.ConditionalValidator = exports.UnifiedSchemaProvider = exports.transformNodeToSchema = exports.ChildDefinition = exports.NodeDefinition = exports.SchemaValidator = exports.Schema = exports.ValidationException = exports.ParseException = exports.StringUtils = exports.parseLine = exports.SPEC_VERSION = exports.Constants = exports.Line = exports.ParseResult = exports.Parser = exports.TextNode = exports.InlineNode = exports.Node = void 0;
|
|
7
7
|
var Node_1 = require("./core/Node");
|
|
8
8
|
Object.defineProperty(exports, "Node", { enumerable: true, get: function () { return Node_1.Node; } });
|
|
9
9
|
var InlineNode_1 = require("./core/InlineNode");
|
|
@@ -18,6 +18,13 @@ var Line_1 = require("./core/Line");
|
|
|
18
18
|
Object.defineProperty(exports, "Line", { enumerable: true, get: function () { return Line_1.Line; } });
|
|
19
19
|
var Constants_1 = require("./core/Constants");
|
|
20
20
|
Object.defineProperty(exports, "Constants", { enumerable: true, get: function () { return Constants_1.Constants; } });
|
|
21
|
+
const Constants_2 = require("./core/Constants");
|
|
22
|
+
/**
|
|
23
|
+
* Version of the STXT specifications (STXT-SPEC, STXT-TREE-SPEC, STXT-SCHEMA-SPEC,
|
|
24
|
+
* STXT-TEMPLATE-SPEC, STXT-DISCOVERY-SPEC) this library implements, distinct from the version of
|
|
25
|
+
* the package. Same value as `Constants.SPEC_VERSION`.
|
|
26
|
+
*/
|
|
27
|
+
exports.SPEC_VERSION = Constants_2.Constants.SPEC_VERSION;
|
|
21
28
|
var LineParser_1 = require("./core/LineParser");
|
|
22
29
|
Object.defineProperty(exports, "parseLine", { enumerable: true, get: function () { return LineParser_1.parseLine; } });
|
|
23
30
|
var StringUtils_1 = require("./core/StringUtils");
|
package/out/core/Constants.d.ts
CHANGED
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
/** Characters and sizes fixed by STXT-SPEC that the parser and the writer share. */
|
|
2
2
|
export declare class Constants {
|
|
3
|
+
/**
|
|
4
|
+
* Version of the STXT specifications (STXT-SPEC, STXT-TREE-SPEC, STXT-SCHEMA-SPEC,
|
|
5
|
+
* STXT-TEMPLATE-SPEC, STXT-DISCOVERY-SPEC) this library implements. It is distinct from the
|
|
6
|
+
* version of the package: the package may be released many times against the same
|
|
7
|
+
* specification version.
|
|
8
|
+
*/
|
|
9
|
+
static readonly SPEC_VERSION: string;
|
|
3
10
|
/** Character that opens a comment line. */
|
|
4
11
|
static readonly COMMENT_CHAR: string;
|
|
5
12
|
/** Number of spaces that make up one indentation level. */
|
package/out/core/Constants.js
CHANGED
|
@@ -5,6 +5,13 @@ exports.Constants = void 0;
|
|
|
5
5
|
class Constants {
|
|
6
6
|
}
|
|
7
7
|
exports.Constants = Constants;
|
|
8
|
+
/**
|
|
9
|
+
* Version of the STXT specifications (STXT-SPEC, STXT-TREE-SPEC, STXT-SCHEMA-SPEC,
|
|
10
|
+
* STXT-TEMPLATE-SPEC, STXT-DISCOVERY-SPEC) this library implements. It is distinct from the
|
|
11
|
+
* version of the package: the package may be released many times against the same
|
|
12
|
+
* specification version.
|
|
13
|
+
*/
|
|
14
|
+
Constants.SPEC_VERSION = "1.0";
|
|
8
15
|
/** Character that opens a comment line. */
|
|
9
16
|
Constants.COMMENT_CHAR = "#";
|
|
10
17
|
/** Number of spaces that make up one indentation level. */
|
package/out/core/LineParser.d.ts
CHANGED
|
@@ -16,7 +16,7 @@ import { Line } from "./Line";
|
|
|
16
16
|
* @param numLine line number, for the error messages.
|
|
17
17
|
* @param validate false to split the line without enforcing the indentation rules.
|
|
18
18
|
* @returns the line already split into indentation and content.
|
|
19
|
-
* @throws ParseException with code `
|
|
19
|
+
* @throws ParseException with code `INDENTATION_MIXED`, `INDENTATION_SPACES_NOT_VALID` or
|
|
20
20
|
* `INDENTATION_LEVEL_NOT_VALID` if the indentation is not valid.
|
|
21
21
|
*/
|
|
22
22
|
export declare function parseLine(line: string, lastNodeBlock: boolean, lastLevel: number, numLine: number, validate?: boolean): Line;
|
package/out/core/LineParser.js
CHANGED
|
@@ -22,7 +22,7 @@ const Line_1 = require("./Line");
|
|
|
22
22
|
* @param numLine line number, for the error messages.
|
|
23
23
|
* @param validate false to split the line without enforcing the indentation rules.
|
|
24
24
|
* @returns the line already split into indentation and content.
|
|
25
|
-
* @throws ParseException with code `
|
|
25
|
+
* @throws ParseException with code `INDENTATION_MIXED`, `INDENTATION_SPACES_NOT_VALID` or
|
|
26
26
|
* `INDENTATION_LEVEL_NOT_VALID` if the indentation is not valid.
|
|
27
27
|
*/
|
|
28
28
|
function parseLine(line, lastNodeBlock, lastLevel, numLine, validate = true) {
|
|
@@ -66,7 +66,7 @@ function parseLine(line, lastNodeBlock, lastLevel, numLine, validate = true) {
|
|
|
66
66
|
// The prefix covering the block level must be homogeneous (spec 10.2, rule 2);
|
|
67
67
|
// empty lines are always preserved and are exempt from it (spec 10.3)
|
|
68
68
|
if (validate && sawSpace && sawTab && text.length > 0) {
|
|
69
|
-
throw new ParseException_1.ParseException(numLine, "
|
|
69
|
+
throw new ParseException_1.ParseException(numLine, "INDENTATION_MIXED", `Mixed tabs and spaces in indentation`);
|
|
70
70
|
}
|
|
71
71
|
return new Line_1.Line(level, text, false, true, pointer);
|
|
72
72
|
}
|
|
@@ -83,11 +83,11 @@ function parseLine(line, lastNodeBlock, lastLevel, numLine, validate = true) {
|
|
|
83
83
|
}
|
|
84
84
|
// Tabs and spaces mixed in the indentation (spec 8.1 and 8.3)
|
|
85
85
|
if (validate && sawSpace && sawTab) {
|
|
86
|
-
throw new ParseException_1.ParseException(numLine, "
|
|
86
|
+
throw new ParseException_1.ParseException(numLine, "INDENTATION_MIXED", `Mixed tabs and spaces in indentation`);
|
|
87
87
|
}
|
|
88
88
|
// Indentation with spaces that is not a multiple of 4
|
|
89
89
|
if (validate && spaces > 0) {
|
|
90
|
-
throw new ParseException_1.ParseException(numLine, "
|
|
90
|
+
throw new ParseException_1.ParseException(numLine, "INDENTATION_SPACES_NOT_VALID", `There are ${spaces} spaces before node`);
|
|
91
91
|
}
|
|
92
92
|
// Validate the level (spec 11.3). Comments included (spec 9): lastLevel is the level of the
|
|
93
93
|
// last NODE, a comment never becomes the reference.
|
|
@@ -18,5 +18,12 @@ export declare class NamespaceValidator {
|
|
|
18
18
|
* @param lineNumber line number, for the error message.
|
|
19
19
|
* @throws ParseException with code `INVALID_NAMESPACE` if it does not match the format.
|
|
20
20
|
*/
|
|
21
|
+
/**
|
|
22
|
+
* Tells whether a namespace matches the format, without throwing.
|
|
23
|
+
*
|
|
24
|
+
* @param namespace already normalized namespace to check.
|
|
25
|
+
* @returns true if it matches the format; false when it is null, empty or malformed.
|
|
26
|
+
*/
|
|
27
|
+
static isValid(namespace: string | null | undefined): boolean;
|
|
21
28
|
static validateNamespaceFormat(namespace: string | null | undefined, lineNumber: number): void;
|
|
22
29
|
}
|
|
@@ -11,6 +11,15 @@ class NamespaceValidator {
|
|
|
11
11
|
* @param lineNumber line number, for the error message.
|
|
12
12
|
* @throws ParseException with code `INVALID_NAMESPACE` if it does not match the format.
|
|
13
13
|
*/
|
|
14
|
+
/**
|
|
15
|
+
* Tells whether a namespace matches the format, without throwing.
|
|
16
|
+
*
|
|
17
|
+
* @param namespace already normalized namespace to check.
|
|
18
|
+
* @returns true if it matches the format; false when it is null, empty or malformed.
|
|
19
|
+
*/
|
|
20
|
+
static isValid(namespace) {
|
|
21
|
+
return !!namespace && NamespaceValidator.NAMESPACE_FORMAT.test(namespace);
|
|
22
|
+
}
|
|
14
23
|
static validateNamespaceFormat(namespace, lineNumber) {
|
|
15
24
|
if (!namespace) {
|
|
16
25
|
return;
|
package/out/core/NodeCreator.js
CHANGED
|
@@ -49,7 +49,7 @@ function createNode(lineIndent, lineNumber) {
|
|
|
49
49
|
value = line.substring(nodeIndex + Constants_1.Constants.SEP_NODE.length);
|
|
50
50
|
}
|
|
51
51
|
if (textNode && StringUtils_1.StringUtils.trim(value).length > 0) {
|
|
52
|
-
throw new ParseException_1.ParseException(lineNumber, "
|
|
52
|
+
throw new ParseException_1.ParseException(lineNumber, "BLOCK_VALUE_NOT_ALLOWED", `Line not valid: ${line}`);
|
|
53
53
|
}
|
|
54
54
|
// The namespace the line declares, if any (empty when it inherits)
|
|
55
55
|
const nameNamespace = NameNamespaceParser_1.NameNamespaceParser.parse(name, null, lineNumber, line);
|
package/out/core/Parser.d.ts
CHANGED
|
@@ -41,6 +41,11 @@ export declare class Parser {
|
|
|
41
41
|
*/
|
|
42
42
|
parseResult(content: string): ParseResult;
|
|
43
43
|
private processLine;
|
|
44
|
+
/**
|
|
45
|
+
* Records an error raised while parsing or validating a line. Typed exceptions travel as they
|
|
46
|
+
* are; anything else is wrapped under the code `UNEXPECTED_ERROR` (as a ValidationException
|
|
47
|
+
* when it was raised by a validator, so that the subtype still tells the phase apart).
|
|
48
|
+
*/
|
|
44
49
|
private handleError;
|
|
45
50
|
private closeToLevel;
|
|
46
51
|
private removeUTF8BOM;
|
package/out/core/Parser.js
CHANGED
|
@@ -6,6 +6,7 @@ const LineParser_1 = require("./LineParser");
|
|
|
6
6
|
const NodeCreator_1 = require("./NodeCreator");
|
|
7
7
|
const ParseResult_1 = require("./ParseResult");
|
|
8
8
|
const ParseException_1 = require("../exceptions/ParseException");
|
|
9
|
+
const ValidationException_1 = require("../exceptions/ValidationException");
|
|
9
10
|
/**
|
|
10
11
|
* Line-by-line STXT parsing engine. It knows nothing about schemas: semantic validation is
|
|
11
12
|
* plugged in through {@link Parser.registerValidator} and {@link Parser.registerObserver}.
|
|
@@ -145,17 +146,20 @@ class Parser {
|
|
|
145
146
|
this.handleError(e, lineNumber, result);
|
|
146
147
|
}
|
|
147
148
|
}
|
|
148
|
-
|
|
149
|
+
/**
|
|
150
|
+
* Records an error raised while parsing or validating a line. Typed exceptions travel as they
|
|
151
|
+
* are; anything else is wrapped under the code `UNEXPECTED_ERROR` (as a ValidationException
|
|
152
|
+
* when it was raised by a validator, so that the subtype still tells the phase apart).
|
|
153
|
+
*/
|
|
154
|
+
handleError(e, line, result, validating = false) {
|
|
149
155
|
if (e instanceof ParseException_1.ParseException) {
|
|
150
156
|
result.addError(e);
|
|
151
157
|
}
|
|
152
|
-
else if (e instanceof Error) {
|
|
153
|
-
// Turn generic errors into a ParseException
|
|
154
|
-
result.addError(new ParseException_1.ParseException(line, errorCode, e.message));
|
|
155
|
-
}
|
|
156
158
|
else {
|
|
157
|
-
|
|
158
|
-
result.addError(
|
|
159
|
+
const message = e instanceof Error ? e.message : String(e);
|
|
160
|
+
result.addError(validating
|
|
161
|
+
? new ValidationException_1.ValidationException(line, "UNEXPECTED_ERROR", message)
|
|
162
|
+
: new ParseException_1.ParseException(line, "UNEXPECTED_ERROR", message));
|
|
159
163
|
}
|
|
160
164
|
}
|
|
161
165
|
closeToLevel(stack, targetLevel, result) {
|
|
@@ -170,7 +174,7 @@ class Parser {
|
|
|
170
174
|
});
|
|
171
175
|
}
|
|
172
176
|
catch (e) {
|
|
173
|
-
this.handleError(e, completed.getLine(), result,
|
|
177
|
+
this.handleError(e, completed.getLine(), result, true);
|
|
174
178
|
}
|
|
175
179
|
});
|
|
176
180
|
// Hand it over to the observers
|
|
@@ -15,6 +15,12 @@ export declare class ParseException extends Error {
|
|
|
15
15
|
* @param message descriptive message.
|
|
16
16
|
*/
|
|
17
17
|
constructor(line: number, code: string, message: string);
|
|
18
|
-
/**
|
|
18
|
+
/**
|
|
19
|
+
* The string form carries the frame that `message` deliberately leaves out: `message` is only
|
|
20
|
+
* the description, and the code and the line are separate fields that whoever formats output
|
|
21
|
+
* composes. Same framing in every port since 0.10.0.
|
|
22
|
+
*
|
|
23
|
+
* @returns `[CODE] line N: message`.
|
|
24
|
+
*/
|
|
19
25
|
toString(): string;
|
|
20
26
|
}
|
|
@@ -20,9 +20,15 @@ class ParseException extends Error {
|
|
|
20
20
|
this.code = code;
|
|
21
21
|
Object.setPrototypeOf(this, ParseException.prototype);
|
|
22
22
|
}
|
|
23
|
-
/**
|
|
23
|
+
/**
|
|
24
|
+
* The string form carries the frame that `message` deliberately leaves out: `message` is only
|
|
25
|
+
* the description, and the code and the line are separate fields that whoever formats output
|
|
26
|
+
* composes. Same framing in every port since 0.10.0.
|
|
27
|
+
*
|
|
28
|
+
* @returns `[CODE] line N: message`.
|
|
29
|
+
*/
|
|
24
30
|
toString() {
|
|
25
|
-
return
|
|
31
|
+
return `[${this.code}] line ${this.line}: ${this.message}`;
|
|
26
32
|
}
|
|
27
33
|
}
|
|
28
34
|
exports.ParseException = ParseException;
|
|
@@ -15,6 +15,11 @@ export declare class RuntimeException extends Error {
|
|
|
15
15
|
constructor(code: string, message: string);
|
|
16
16
|
/** @returns the error code in UPPERCASE. */
|
|
17
17
|
getCode(): string;
|
|
18
|
-
/**
|
|
18
|
+
/**
|
|
19
|
+
* The string form carries the frame that `message` deliberately leaves out; same framing in
|
|
20
|
+
* every port since 0.10.0.
|
|
21
|
+
*
|
|
22
|
+
* @returns `[CODE] message`.
|
|
23
|
+
*/
|
|
19
24
|
toString(): string;
|
|
20
25
|
}
|
|
@@ -23,10 +23,14 @@ class RuntimeException extends Error {
|
|
|
23
23
|
getCode() {
|
|
24
24
|
return this.code;
|
|
25
25
|
}
|
|
26
|
-
/**
|
|
26
|
+
/**
|
|
27
|
+
* The string form carries the frame that `message` deliberately leaves out; same framing in
|
|
28
|
+
* every port since 0.10.0.
|
|
29
|
+
*
|
|
30
|
+
* @returns `[CODE] message`.
|
|
31
|
+
*/
|
|
27
32
|
toString() {
|
|
28
|
-
|
|
29
|
-
return `${this.name}[${this.code}]${message ? `: ${message}` : ""}`;
|
|
33
|
+
return `[${this.code}] ${this.message}`;
|
|
30
34
|
}
|
|
31
35
|
}
|
|
32
36
|
exports.RuntimeException = RuntimeException;
|
|
@@ -45,7 +45,7 @@ export declare class NodeDefinition {
|
|
|
45
45
|
* Adds the definition of an expected child.
|
|
46
46
|
*
|
|
47
47
|
* @param childDefinition definition of the child to add.
|
|
48
|
-
* @throws ValidationException with code `
|
|
48
|
+
* @throws ValidationException with code `CHILD_DUPLICATED` if a definition for that child already existed.
|
|
49
49
|
*/
|
|
50
50
|
addChildDefinition(childDefinition: ChildDefinition): void;
|
|
51
51
|
/**
|
|
@@ -67,12 +67,12 @@ class NodeDefinition {
|
|
|
67
67
|
* Adds the definition of an expected child.
|
|
68
68
|
*
|
|
69
69
|
* @param childDefinition definition of the child to add.
|
|
70
|
-
* @throws ValidationException with code `
|
|
70
|
+
* @throws ValidationException with code `CHILD_DUPLICATED` if a definition for that child already existed.
|
|
71
71
|
*/
|
|
72
72
|
addChildDefinition(childDefinition) {
|
|
73
73
|
const qname = childDefinition.getQualifiedName();
|
|
74
74
|
if (this.children.has(qname)) {
|
|
75
|
-
throw new ValidationException_1.ValidationException(0, "
|
|
75
|
+
throw new ValidationException_1.ValidationException(0, "CHILD_DUPLICATED", `Exists a previous node definition with: ${qname}`);
|
|
76
76
|
}
|
|
77
77
|
this.children.set(qname, childDefinition);
|
|
78
78
|
}
|
package/out/schema/Schema.d.ts
CHANGED
|
@@ -28,7 +28,7 @@ export declare class Schema {
|
|
|
28
28
|
* Adds the definition of a node to this schema.
|
|
29
29
|
*
|
|
30
30
|
* @param nodeDefinition node definition to add.
|
|
31
|
-
* @throws ValidationException with code `
|
|
31
|
+
* @throws ValidationException with code `NODE_DUPLICATED` if there already was a node definition with the same name.
|
|
32
32
|
*/
|
|
33
33
|
addNodeDefinition(nodeDefinition: NodeDefinition): void;
|
|
34
34
|
/** @returns the namespace this schema applies to. */
|
package/out/schema/Schema.js
CHANGED
|
@@ -37,12 +37,12 @@ class Schema {
|
|
|
37
37
|
* Adds the definition of a node to this schema.
|
|
38
38
|
*
|
|
39
39
|
* @param nodeDefinition node definition to add.
|
|
40
|
-
* @throws ValidationException with code `
|
|
40
|
+
* @throws ValidationException with code `NODE_DUPLICATED` if there already was a node definition with the same name.
|
|
41
41
|
*/
|
|
42
42
|
addNodeDefinition(nodeDefinition) {
|
|
43
43
|
const qname = nodeDefinition.getCanonicalName();
|
|
44
44
|
if (this.nodes.has(qname)) {
|
|
45
|
-
throw new ValidationException_1.ValidationException(0, "
|
|
45
|
+
throw new ValidationException_1.ValidationException(0, "NODE_DUPLICATED", `Exists a previous node definition with: ${qname}`);
|
|
46
46
|
}
|
|
47
47
|
this.nodes.set(qname, nodeDefinition);
|
|
48
48
|
}
|
|
@@ -5,6 +5,7 @@ import { Node } from "../core/Node";
|
|
|
5
5
|
*
|
|
6
6
|
* @param node root node of the schema document, `Schema (@stxt.schema): ...`.
|
|
7
7
|
* @returns the schema the document defines.
|
|
8
|
-
* @throws ValidationException
|
|
8
|
+
* @throws ValidationException with `SCHEMA_ROOT_NOT_VALID`, `SCHEMA_NAMESPACE_EMPTY`, `SCHEMA_NODE_NOT_INLINE`
|
|
9
|
+
* or the code of the first rule of the schema language the document breaks, if the document is not a valid `@stxt.schema` one.
|
|
9
10
|
*/
|
|
10
11
|
export declare function transformNodeToSchema(node: Node): Schema;
|
|
@@ -6,7 +6,8 @@ const NodeDefinition_1 = require("./NodeDefinition");
|
|
|
6
6
|
const ChildDefinition_1 = require("./ChildDefinition");
|
|
7
7
|
const InlineNode_1 = require("../core/InlineNode");
|
|
8
8
|
const ValidationException_1 = require("../exceptions/ValidationException");
|
|
9
|
-
const
|
|
9
|
+
const NamespaceValidator_1 = require("../core/NamespaceValidator");
|
|
10
|
+
const StringUtils_1 = require("../core/StringUtils");
|
|
10
11
|
const NameNamespaceParser_1 = require("../core/NameNamespaceParser");
|
|
11
12
|
const TypeRegistry_1 = require("./TypeRegistry");
|
|
12
13
|
/**
|
|
@@ -14,7 +15,8 @@ const TypeRegistry_1 = require("./TypeRegistry");
|
|
|
14
15
|
*
|
|
15
16
|
* @param node root node of the schema document, `Schema (@stxt.schema): ...`.
|
|
16
17
|
* @returns the schema the document defines.
|
|
17
|
-
* @throws ValidationException
|
|
18
|
+
* @throws ValidationException with `SCHEMA_ROOT_NOT_VALID`, `SCHEMA_NAMESPACE_EMPTY`, `SCHEMA_NODE_NOT_INLINE`
|
|
19
|
+
* or the code of the first rule of the schema language the document breaks, if the document is not a valid `@stxt.schema` one.
|
|
18
20
|
*/
|
|
19
21
|
function transformNodeToSchema(node) {
|
|
20
22
|
// Node name
|
|
@@ -22,12 +24,20 @@ function transformNodeToSchema(node) {
|
|
|
22
24
|
const namespaceSchema = node.getNamespace();
|
|
23
25
|
// Get the name and the namespace
|
|
24
26
|
if (nodeName !== "schema" || namespaceSchema !== Schema_1.Schema.SCHEMA_NAMESPACE) {
|
|
25
|
-
throw new ValidationException_1.ValidationException(node.getLine(), "
|
|
27
|
+
throw new ValidationException_1.ValidationException(node.getLine(), "SCHEMA_ROOT_NOT_VALID", `Expected schema(${Schema_1.Schema.SCHEMA_NAMESPACE}) but got ${nodeName}(${namespaceSchema})`);
|
|
26
28
|
}
|
|
27
29
|
const root = inline(node);
|
|
30
|
+
// The target namespace: required, and with a valid format
|
|
31
|
+
const targetNamespace = StringUtils_1.StringUtils.lowerCase(root.getValue());
|
|
32
|
+
if (!targetNamespace || targetNamespace.trim().length === 0) {
|
|
33
|
+
throw new ValidationException_1.ValidationException(root.getLine(), "SCHEMA_NAMESPACE_EMPTY", "Schema namespace is empty");
|
|
34
|
+
}
|
|
35
|
+
if (!NamespaceValidator_1.NamespaceValidator.isValid(targetNamespace)) {
|
|
36
|
+
throw new ValidationException_1.ValidationException(root.getLine(), "SCHEMA_ROOT_NOT_VALID", `Schema namespace not valid: ${targetNamespace}`);
|
|
37
|
+
}
|
|
28
38
|
// Get the description
|
|
29
39
|
const descrip = root.getChild("description")?.getText();
|
|
30
|
-
const schema = new Schema_1.Schema(
|
|
40
|
+
const schema = new Schema_1.Schema(targetNamespace, root.getLine(), descrip);
|
|
31
41
|
// Used to check that every child is defined
|
|
32
42
|
const allNames = new Set();
|
|
33
43
|
// Get the nodes
|
|
@@ -55,7 +65,7 @@ function inline(node) {
|
|
|
55
65
|
if (node instanceof InlineNode_1.InlineNode) {
|
|
56
66
|
return node;
|
|
57
67
|
}
|
|
58
|
-
throw new ValidationException_1.ValidationException(node.getLine(), "
|
|
68
|
+
throw new ValidationException_1.ValidationException(node.getLine(), "SCHEMA_NODE_NOT_INLINE", `Node '${node.getName()}' must be inline in a schema`);
|
|
59
69
|
}
|
|
60
70
|
/** Builds the definition of a node from a `Node:` entry of the schema document. */
|
|
61
71
|
function createFrom(node, namespace) {
|
|
@@ -82,14 +92,19 @@ function createFrom(node, namespace) {
|
|
|
82
92
|
let valuesNodes = n.getChildrenByName("values");
|
|
83
93
|
if (valuesNodes && valuesNodes.length > 0) {
|
|
84
94
|
if (type !== "ENUM") {
|
|
85
|
-
throw new ValidationException_1.ValidationException(n.getLine(), "
|
|
95
|
+
throw new ValidationException_1.ValidationException(n.getLine(), "VALUES_NOT_ALLOWED_FOR_TYPE", `Values only supported for type ENUM, not for type ${type}`);
|
|
86
96
|
}
|
|
87
97
|
if (valuesNodes.length > 1) {
|
|
88
|
-
throw new
|
|
98
|
+
throw new ValidationException_1.ValidationException(valuesNodes[1].getLine(), "VALUES_DUPLICATED", `Node '${n.getValue()}' defines 'Values' ${valuesNodes.length} times`);
|
|
89
99
|
}
|
|
90
100
|
const valuesNode = valuesNodes[0];
|
|
91
101
|
const values = inline(valuesNode).getChildrenByName("value");
|
|
92
102
|
for (const v of values) {
|
|
103
|
+
// An empty Value: is a schema error (STXT-SCHEMA-SPEC 7.2, condition 14 of section 13):
|
|
104
|
+
// an enumeration whose only valid value is the empty string makes no sense
|
|
105
|
+
if (v.getText().length === 0) {
|
|
106
|
+
throw new ValidationException_1.ValidationException(v.getLine(), "VALUE_EMPTY", "Value of ENUM cannot be empty");
|
|
107
|
+
}
|
|
93
108
|
result.addValue(v.getText(), v.getLine());
|
|
94
109
|
}
|
|
95
110
|
// For the final ENUM check
|
|
@@ -97,7 +112,7 @@ function createFrom(node, namespace) {
|
|
|
97
112
|
}
|
|
98
113
|
// Look at the enum
|
|
99
114
|
if (type === "ENUM" && (!valuesNodes || valuesNodes.length === 0)) {
|
|
100
|
-
throw new ValidationException_1.ValidationException(n.getLine(), "
|
|
115
|
+
throw new ValidationException_1.ValidationException(n.getLine(), "VALUES_REQUIRED", "ENUM Type must include values");
|
|
101
116
|
}
|
|
102
117
|
return result;
|
|
103
118
|
}
|
|
@@ -126,7 +141,7 @@ function getInteger(node, name) {
|
|
|
126
141
|
const raw = n.getText();
|
|
127
142
|
const parsed = Number.parseInt(raw, 10);
|
|
128
143
|
if (Number.isNaN(parsed)) {
|
|
129
|
-
throw new ValidationException_1.ValidationException(node.getLine(), "
|
|
144
|
+
throw new ValidationException_1.ValidationException(node.getLine(), "CARDINALITY_NOT_VALID", `Integer not valid: ${raw}`);
|
|
130
145
|
}
|
|
131
146
|
return parsed;
|
|
132
147
|
}
|
|
@@ -28,7 +28,8 @@ export declare class SchemaProviderMemory implements SchemaProvider {
|
|
|
28
28
|
* own namespace.
|
|
29
29
|
*
|
|
30
30
|
* @param txt text of the `@stxt.schema` document.
|
|
31
|
-
* @throws ParseException or ValidationException if the document is not a valid schema
|
|
31
|
+
* @throws ParseException or ValidationException if the document is not a valid schema; in
|
|
32
|
+
* particular `SCHEMA_MULTIPLE_ROOTS` if it does not hold exactly one root node.
|
|
32
33
|
*/
|
|
33
34
|
addSchema(txt: string): void;
|
|
34
35
|
/** Removes every schema registered in this provider (the parent one is left untouched). */
|
|
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.SchemaProviderMemory = void 0;
|
|
4
4
|
const Parser_1 = require("../core/Parser");
|
|
5
5
|
const StringUtils_1 = require("../core/StringUtils");
|
|
6
|
+
const ValidationException_1 = require("../exceptions/ValidationException");
|
|
6
7
|
const SchemaParser_1 = require("./SchemaParser");
|
|
7
8
|
const SchemaProviderMeta_1 = require("./SchemaProviderMeta");
|
|
8
9
|
const SchemaValidator_1 = require("./SchemaValidator");
|
|
@@ -47,11 +48,16 @@ class SchemaProviderMemory {
|
|
|
47
48
|
* own namespace.
|
|
48
49
|
*
|
|
49
50
|
* @param txt text of the `@stxt.schema` document.
|
|
50
|
-
* @throws ParseException or ValidationException if the document is not a valid schema
|
|
51
|
+
* @throws ParseException or ValidationException if the document is not a valid schema; in
|
|
52
|
+
* particular `SCHEMA_MULTIPLE_ROOTS` if it does not hold exactly one root node.
|
|
51
53
|
*/
|
|
52
54
|
addSchema(txt) {
|
|
53
55
|
const parser = new Parser_1.Parser();
|
|
54
|
-
const
|
|
56
|
+
const nodes = parser.parse(txt);
|
|
57
|
+
if (nodes.length !== 1) {
|
|
58
|
+
throw new ValidationException_1.ValidationException(0, "SCHEMA_MULTIPLE_ROOTS", `A schema document must hold exactly 1 root node, got ${nodes.length}`);
|
|
59
|
+
}
|
|
60
|
+
const node = nodes[0];
|
|
55
61
|
// A schema that does not validate against its meta-schema must not be
|
|
56
62
|
// registered (same policy as UnifiedSchemaProvider/DiscoveryResolver)
|
|
57
63
|
const schemaValidator = new SchemaValidator_1.SchemaValidator(new SchemaProviderMeta_1.SchemaProviderMeta(), true);
|
|
@@ -65,7 +65,7 @@ class SchemaValidator {
|
|
|
65
65
|
const schemaNode = schema.getNodeDefinition(node.getCanonicalName());
|
|
66
66
|
if (!schemaNode) {
|
|
67
67
|
const error = `NOT EXIST NODE ${node.getCanonicalName()} for namespace ${schema.getNamespace()}`;
|
|
68
|
-
errors.push(new ValidationException_1.ValidationException(node.getLine(), "
|
|
68
|
+
errors.push(new ValidationException_1.ValidationException(node.getLine(), "NODE_NOT_DEFINED_IN_SCHEMA", error));
|
|
69
69
|
return errors;
|
|
70
70
|
}
|
|
71
71
|
errors.push(...SchemaValidator.validateValue(schemaNode, node));
|
|
@@ -93,7 +93,7 @@ class SchemaValidator {
|
|
|
93
93
|
const nodeType = nodeDef.getType();
|
|
94
94
|
const validator = TypeRegistry_1.TypeRegistry.get(nodeType);
|
|
95
95
|
if (!validator) {
|
|
96
|
-
errors.push(new ValidationException_1.ValidationException(node.getLine(), "
|
|
96
|
+
errors.push(new ValidationException_1.ValidationException(node.getLine(), "TYPE_NOT_VALID", `Node type not supported: ${nodeType}`));
|
|
97
97
|
return errors;
|
|
98
98
|
}
|
|
99
99
|
try {
|
|
@@ -104,10 +104,10 @@ class SchemaValidator {
|
|
|
104
104
|
errors.push(e);
|
|
105
105
|
}
|
|
106
106
|
else if (e instanceof Error) {
|
|
107
|
-
errors.push(new ValidationException_1.ValidationException(node.getLine(), "
|
|
107
|
+
errors.push(new ValidationException_1.ValidationException(node.getLine(), "UNEXPECTED_ERROR", e.message));
|
|
108
108
|
}
|
|
109
109
|
else {
|
|
110
|
-
errors.push(new ValidationException_1.ValidationException(node.getLine(), "
|
|
110
|
+
errors.push(new ValidationException_1.ValidationException(node.getLine(), "UNEXPECTED_ERROR", String(e)));
|
|
111
111
|
}
|
|
112
112
|
}
|
|
113
113
|
return errors;
|
|
@@ -135,14 +135,14 @@ class SchemaValidator {
|
|
|
135
135
|
const min = childDef.getMin(); // number | null
|
|
136
136
|
const max = childDef.getMax(); // number | null
|
|
137
137
|
if (min !== null && childCount < min) {
|
|
138
|
-
errors.push(new ValidationException_1.ValidationException(node.getLine(), "
|
|
138
|
+
errors.push(new ValidationException_1.ValidationException(node.getLine(), "TOO_FEW_CHILDREN", `${childCount} nodes of '${childDef.getQualifiedName()}' and min is ${min}`));
|
|
139
139
|
}
|
|
140
140
|
if (max !== null && childCount > max) {
|
|
141
141
|
// Error on the parent
|
|
142
|
-
errors.push(new ValidationException_1.ValidationException(node.getLine(), "
|
|
142
|
+
errors.push(new ValidationException_1.ValidationException(node.getLine(), "TOO_MANY_CHILDREN", `${childCount} nodes of '${childDef.getQualifiedName()}' and max is ${max}`));
|
|
143
143
|
// Error on each child node beyond the allowed maximum
|
|
144
144
|
for (const child of children) {
|
|
145
|
-
errors.push(new ValidationException_1.ValidationException(child.getLine(), "
|
|
145
|
+
errors.push(new ValidationException_1.ValidationException(child.getLine(), "TOO_MANY_CHILDREN", `Too many '${childDef.getQualifiedName()}' nodes: found ${childCount}, max is ${max}`));
|
|
146
146
|
}
|
|
147
147
|
}
|
|
148
148
|
return errors;
|
|
@@ -47,7 +47,7 @@ class TypeRegistry {
|
|
|
47
47
|
static register(instance) {
|
|
48
48
|
const name = instance.getName();
|
|
49
49
|
if (this.REGISTRY.has(name)) {
|
|
50
|
-
throw new RuntimeException_1.RuntimeException("
|
|
50
|
+
throw new RuntimeException_1.RuntimeException("TYPE_DUPLICATED", `Type already defined: ${name}`);
|
|
51
51
|
}
|
|
52
52
|
this.REGISTRY.set(name, instance);
|
|
53
53
|
}
|
|
@@ -1,3 +1,12 @@
|
|
|
1
1
|
import { Type } from "../Type";
|
|
2
|
+
/**
|
|
3
|
+
* STXT-SCHEMA-SPEC 9.5: standard Base64 (not URL-safe), padding optional, no leftover bits,
|
|
4
|
+
* never empty. The check is a regular expression plus the leftover-bits rule, never the
|
|
5
|
+
* platform decoder, which silently ignores characters outside the alphabet.
|
|
6
|
+
*
|
|
7
|
+
* @param value value already stripped of blanks.
|
|
8
|
+
* @returns whether it is valid Base64.
|
|
9
|
+
*/
|
|
10
|
+
export declare function isValidBase64(value: string): boolean;
|
|
2
11
|
/** `BASE64` type: checks that the content is valid Base64. */
|
|
3
12
|
export declare const BASE64: Type;
|
|
@@ -1,29 +1,46 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.BASE64 = void 0;
|
|
4
|
+
exports.isValidBase64 = isValidBase64;
|
|
4
5
|
const ValidationException_1 = require("../../exceptions/ValidationException");
|
|
5
6
|
const binaryValue_1 = require("./binaryValue");
|
|
7
|
+
/** Standard alphabet of RFC 4648 section 4, used to check the leftover bits of the last character. */
|
|
8
|
+
const ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
|
9
|
+
/**
|
|
10
|
+
* Shape of a Base64 value (STXT-SCHEMA-SPEC 9.5): groups of four characters of the standard
|
|
11
|
+
* alphabet, with an optional final group of two or three characters whose `=` padding may be
|
|
12
|
+
* omitted. The empty string matches here and is rejected separately.
|
|
13
|
+
*/
|
|
14
|
+
const SHAPE = /^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}(?:==)?|[A-Za-z0-9+/]{3}=?)?$/;
|
|
15
|
+
/**
|
|
16
|
+
* STXT-SCHEMA-SPEC 9.5: standard Base64 (not URL-safe), padding optional, no leftover bits,
|
|
17
|
+
* never empty. The check is a regular expression plus the leftover-bits rule, never the
|
|
18
|
+
* platform decoder, which silently ignores characters outside the alphabet.
|
|
19
|
+
*
|
|
20
|
+
* @param value value already stripped of blanks.
|
|
21
|
+
* @returns whether it is valid Base64.
|
|
22
|
+
*/
|
|
23
|
+
function isValidBase64(value) {
|
|
24
|
+
if (value.length === 0 || !SHAPE.test(value)) {
|
|
25
|
+
return false;
|
|
26
|
+
}
|
|
27
|
+
const data = value.replace(/=+$/, "");
|
|
28
|
+
const rest = data.length % 4;
|
|
29
|
+
if (rest === 0) {
|
|
30
|
+
return true;
|
|
31
|
+
}
|
|
32
|
+
// The last character encodes 6 bits; with 2 characters 4 of them are leftover, with 3, 2 of them.
|
|
33
|
+
const last = ALPHABET.indexOf(data.charAt(data.length - 1));
|
|
34
|
+
const mask = rest === 2 ? 0x0f : 0x03;
|
|
35
|
+
return (last & mask) === 0;
|
|
36
|
+
}
|
|
6
37
|
/** `BASE64` type: checks that the content is valid Base64. */
|
|
7
38
|
exports.BASE64 = {
|
|
8
39
|
getName() {
|
|
9
40
|
return "BASE64";
|
|
10
41
|
},
|
|
11
42
|
validate(ndef, n) {
|
|
12
|
-
|
|
13
|
-
try {
|
|
14
|
-
// Try to decode it
|
|
15
|
-
const buf = Buffer.from(raw, "base64");
|
|
16
|
-
// Re-encode to check consistency
|
|
17
|
-
// (this keeps partially valid strings out)
|
|
18
|
-
const reencoded = buf.toString("base64");
|
|
19
|
-
// Normalize the padding before comparing
|
|
20
|
-
const normalizedInput = raw.replace(/=+$/, "");
|
|
21
|
-
const normalizedReencoded = reencoded.replace(/=+$/, "");
|
|
22
|
-
if (normalizedInput !== normalizedReencoded) {
|
|
23
|
-
throw new ValidationException_1.ValidationException(n.getLine(), "INVALID_VALUE", `Node '${n.getName()}' Invalid Base64`);
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
catch {
|
|
43
|
+
if (!isValidBase64((0, binaryValue_1.binaryValue)(n))) {
|
|
27
44
|
throw new ValidationException_1.ValidationException(n.getLine(), "INVALID_VALUE", `Node '${n.getName()}' Invalid Base64`);
|
|
28
45
|
}
|
|
29
46
|
},
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
/** `DATE` type:
|
|
1
|
+
/** `DATE` type: `YYYY-MM-DD`, an existing date of the proleptic Gregorian calendar (STXT-SCHEMA-SPEC 9.4). */
|
|
2
2
|
export declare const DATE: import("../Type").Type;
|
package/out/schema/type/DATE.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.DATE = void 0;
|
|
4
|
-
const
|
|
5
|
-
|
|
6
|
-
|
|
4
|
+
const rangeType_1 = require("./rangeType");
|
|
5
|
+
const dateTime_1 = require("./dateTime");
|
|
6
|
+
/** `DATE` type: `YYYY-MM-DD`, an existing date of the proleptic Gregorian calendar (STXT-SCHEMA-SPEC 9.4). */
|
|
7
|
+
exports.DATE = (0, rangeType_1.rangeType)("DATE", /^(\d{4})-(\d{2})-(\d{2})$/, m => (0, dateTime_1.isValidDate)(Number(m[1]), Number(m[2]), Number(m[3])), "Invalid date");
|
|
7
8
|
//# sourceMappingURL=DATE.js.map
|