@stxt-lang/core 0.6.1 → 0.6.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/out/core/Node.d.ts +0 -1
- package/out/core/Node.js +1 -6
- package/out/core/StringUtils.d.ts +11 -0
- package/out/core/StringUtils.js +16 -0
- package/out/discovery/DiscoveryResolver.js +7 -8
- package/out/discovery/DiscoveryResult.d.ts +2 -0
- package/out/discovery/DiscoveryResult.js +10 -0
- package/out/schema/ChildDefinition.js +1 -1
- package/out/schema/NodeDefinition.js +1 -1
- package/out/schema/SchemaProviderMeta.d.ts +7 -3
- package/out/schema/SchemaProviderMeta.js +7 -4
- package/out/template/MetaTemplateSchemaProvider.d.ts +6 -3
- package/out/template/MetaTemplateSchemaProvider.js +6 -4
- package/out/template/TemplateParser.js +5 -0
- package/package.json +1 -1
package/out/core/Node.d.ts
CHANGED
package/out/core/Node.js
CHANGED
|
@@ -38,10 +38,7 @@ class Node {
|
|
|
38
38
|
if (this.value.length > 0 && this.isTextNode()) {
|
|
39
39
|
throw new RuntimeException_1.RuntimeException("INLINE_VALUE_NOT_VALID", "Not empty value with textNode");
|
|
40
40
|
}
|
|
41
|
-
if (!
|
|
42
|
-
throw new ParseException_1.ParseException(line, "INVALID_NODE_NAME", `Node name contains invalid characters: ${name}`);
|
|
43
|
-
}
|
|
44
|
-
if (this.normalizedName.length === 0) {
|
|
41
|
+
if (!StringUtils_1.StringUtils.isValidNodeName(this.name)) {
|
|
45
42
|
throw new ParseException_1.ParseException(line, "INVALID_NODE_NAME", `Node name not valid: ${name}`);
|
|
46
43
|
}
|
|
47
44
|
}
|
|
@@ -166,6 +163,4 @@ class Node {
|
|
|
166
163
|
}
|
|
167
164
|
}
|
|
168
165
|
exports.Node = Node;
|
|
169
|
-
// STXT-SPEC 4.2: Unicode letters and digits (categories L and Nd) plus '-', '_' and space
|
|
170
|
-
Node.VALID_NAME = /^[\p{L}\p{Nd}\-_ ]+$/u;
|
|
171
166
|
//# sourceMappingURL=Node.js.map
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
/** String normalization helpers used for names, namespaces and values. */
|
|
2
2
|
export declare class StringUtils {
|
|
3
|
+
private static readonly NODE_NAME;
|
|
3
4
|
private constructor();
|
|
4
5
|
/**
|
|
5
6
|
* Removes the trailing whitespace of a string.
|
|
@@ -29,6 +30,16 @@ export declare class StringUtils {
|
|
|
29
30
|
* @returns the string with the outer spaces trimmed and the inner ones collapsed into a single one; null/undefined is treated as the empty string.
|
|
30
31
|
*/
|
|
31
32
|
static compactSpaces(s: string | null | undefined): string;
|
|
33
|
+
/**
|
|
34
|
+
* Tells whether a value is a valid STXT node name.
|
|
35
|
+
*
|
|
36
|
+
* The test happens after NFC normalization: the source may use either the
|
|
37
|
+
* precomposed or decomposed Unicode spelling of a letter with a diacritic.
|
|
38
|
+
*
|
|
39
|
+
* @param input name to validate.
|
|
40
|
+
* @returns true if the name contains only permitted characters and has a non-empty canonical name.
|
|
41
|
+
*/
|
|
42
|
+
static isValidNodeName(input: string | null | undefined): boolean;
|
|
32
43
|
/**
|
|
33
44
|
* Builds the canonical name of a node, as defined by STXT-SPEC 4.3.
|
|
34
45
|
*
|
package/out/core/StringUtils.js
CHANGED
|
@@ -51,6 +51,19 @@ class StringUtils {
|
|
|
51
51
|
static compactSpaces(s) {
|
|
52
52
|
return (s ?? "").trim().replace(/\s+/g, " ");
|
|
53
53
|
}
|
|
54
|
+
/**
|
|
55
|
+
* Tells whether a value is a valid STXT node name.
|
|
56
|
+
*
|
|
57
|
+
* The test happens after NFC normalization: the source may use either the
|
|
58
|
+
* precomposed or decomposed Unicode spelling of a letter with a diacritic.
|
|
59
|
+
*
|
|
60
|
+
* @param input name to validate.
|
|
61
|
+
* @returns true if the name contains only permitted characters and has a non-empty canonical name.
|
|
62
|
+
*/
|
|
63
|
+
static isValidNodeName(input) {
|
|
64
|
+
const nfc = this.compactSpaces(input).normalize("NFC");
|
|
65
|
+
return this.NODE_NAME.test(nfc) && this.normalize(nfc).length > 0;
|
|
66
|
+
}
|
|
54
67
|
// Used for the normalized name of the nodes (STXT-SPEC 4.3): NFC + lower case,
|
|
55
68
|
// keeping diacritics and non-Latin alphabets (IDN model)
|
|
56
69
|
/**
|
|
@@ -74,4 +87,7 @@ class StringUtils {
|
|
|
74
87
|
}
|
|
75
88
|
}
|
|
76
89
|
exports.StringUtils = StringUtils;
|
|
90
|
+
// STXT-SPEC 4.2 / 4.3: validate the logical name after NFC so that a
|
|
91
|
+
// decomposed spelling such as "e" + combining acute is accepted as "é".
|
|
92
|
+
StringUtils.NODE_NAME = /^[\p{L}\p{Nd}\-_ ]+$/u;
|
|
77
93
|
//# sourceMappingURL=StringUtils.js.map
|
|
@@ -127,10 +127,9 @@ class DiscoveryResolver {
|
|
|
127
127
|
if (cached) {
|
|
128
128
|
return cached;
|
|
129
129
|
}
|
|
130
|
-
const level = { dir, definitions: new Map(), errors: [] };
|
|
131
|
-
const conflicted = new Set();
|
|
130
|
+
const level = { dir, definitions: new Map(), conflictedNamespaces: new Set(), errors: [] };
|
|
132
131
|
for (const file of await this.collectFiles(dir)) {
|
|
133
|
-
await this.loadFile(file, level
|
|
132
|
+
await this.loadFile(file, level);
|
|
134
133
|
}
|
|
135
134
|
this.levelCache.set(dir, level);
|
|
136
135
|
return level;
|
|
@@ -152,7 +151,7 @@ class DiscoveryResolver {
|
|
|
152
151
|
}
|
|
153
152
|
// Loads one file of a level: parses it and registers every root as a definition,
|
|
154
153
|
// reporting the errors of spec section 8.
|
|
155
|
-
async loadFile(file, level
|
|
154
|
+
async loadFile(file, level) {
|
|
156
155
|
// Spec section 3: every file under a resolution directory must be a definition.
|
|
157
156
|
if (!file.endsWith(STXT_EXTENSION)) {
|
|
158
157
|
level.errors.push(new DiscoveryError_1.DiscoveryError(DiscoveryError_1.DiscoveryError.NOT_A_DEFINITION, file, `Not an STXT definition file: ${file}`));
|
|
@@ -171,12 +170,12 @@ class DiscoveryResolver {
|
|
|
171
170
|
return;
|
|
172
171
|
}
|
|
173
172
|
for (const node of nodes) {
|
|
174
|
-
this.loadRootNode(node, file, level
|
|
173
|
+
this.loadRootNode(node, file, level);
|
|
175
174
|
}
|
|
176
175
|
}
|
|
177
176
|
// Validates one root node against its meta-schema, compiles it to a schema and
|
|
178
177
|
// registers it in the level, detecting same-level duplicates.
|
|
179
|
-
loadRootNode(node, file, level
|
|
178
|
+
loadRootNode(node, file, level) {
|
|
180
179
|
const namespace = node.getNamespace();
|
|
181
180
|
let schema;
|
|
182
181
|
try {
|
|
@@ -200,10 +199,10 @@ class DiscoveryResolver {
|
|
|
200
199
|
const existing = level.definitions.get(key);
|
|
201
200
|
// Spec section 8: on a same-level duplicate, never silently pick one of the
|
|
202
201
|
// definitions — the namespace has no active definition while the conflict exists.
|
|
203
|
-
if (
|
|
202
|
+
if (level.conflictedNamespaces.has(key) || existing) {
|
|
204
203
|
if (existing) {
|
|
205
204
|
level.definitions.delete(key);
|
|
206
|
-
|
|
205
|
+
level.conflictedNamespaces.add(key);
|
|
207
206
|
}
|
|
208
207
|
const firstFile = existing ? existing.file : "another file of this level";
|
|
209
208
|
level.errors.push(new DiscoveryError_1.DiscoveryError(DiscoveryError_1.DiscoveryError.DUPLICATE_NAMESPACE, file, `Duplicate definition for namespace '${schema.getNamespace()}' at level ${level.dir}: ` +
|
|
@@ -24,6 +24,8 @@ export interface DiscoveryLevel {
|
|
|
24
24
|
dir: string;
|
|
25
25
|
/** Definitions of the level by lowercased target namespace, conflicts excluded. */
|
|
26
26
|
definitions: Map<string, DiscoveryDefinition>;
|
|
27
|
+
/** Namespaces with a same-level conflict; they block fallback to farther levels. */
|
|
28
|
+
conflictedNamespaces: Set<string>;
|
|
27
29
|
/** Resolution errors found while loading this level. */
|
|
28
30
|
errors: DiscoveryError[];
|
|
29
31
|
}
|
|
@@ -52,6 +52,11 @@ class DiscoveryResult {
|
|
|
52
52
|
getDefinition(namespace) {
|
|
53
53
|
const key = StringUtils_1.StringUtils.lowerCase(namespace);
|
|
54
54
|
for (const level of this.levels) {
|
|
55
|
+
// STXT-DISCOVERY-SPEC section 8: a closer conflict leaves the namespace
|
|
56
|
+
// without an active definition instead of falling back to a farther level.
|
|
57
|
+
if (level.conflictedNamespaces.has(key)) {
|
|
58
|
+
return undefined;
|
|
59
|
+
}
|
|
55
60
|
const definition = level.definitions.get(key);
|
|
56
61
|
if (definition) {
|
|
57
62
|
return definition;
|
|
@@ -69,6 +74,11 @@ class DiscoveryResult {
|
|
|
69
74
|
const seen = new Set();
|
|
70
75
|
const result = [];
|
|
71
76
|
for (const level of this.levels) {
|
|
77
|
+
// Mark conflicts as seen so getActiveDefinitions() has the same semantics
|
|
78
|
+
// as getDefinition(): they block definitions in farther levels.
|
|
79
|
+
for (const key of level.conflictedNamespaces) {
|
|
80
|
+
seen.add(key);
|
|
81
|
+
}
|
|
72
82
|
for (const [key, definition] of level.definitions) {
|
|
73
83
|
if (!seen.has(key)) {
|
|
74
84
|
seen.add(key);
|
|
@@ -23,7 +23,7 @@ class ChildDefinition {
|
|
|
23
23
|
this.min = min;
|
|
24
24
|
this.max = max;
|
|
25
25
|
NamespaceValidator_1.NamespaceValidator.validateNamespaceFormat(this.namespace, numLine);
|
|
26
|
-
if (this.
|
|
26
|
+
if (!StringUtils_1.StringUtils.isValidNodeName(this.name)) {
|
|
27
27
|
throw new ValidationException_1.ValidationException(numLine, "INVALID_NODE_NAME", `Node name not valid: ${name}`);
|
|
28
28
|
}
|
|
29
29
|
}
|
|
@@ -24,7 +24,7 @@ class NodeDefinition {
|
|
|
24
24
|
this.normalizedName = StringUtils_1.StringUtils.normalize(name);
|
|
25
25
|
this.type = type;
|
|
26
26
|
this.description = description;
|
|
27
|
-
if (this.
|
|
27
|
+
if (!StringUtils_1.StringUtils.isValidNodeName(this.name)) {
|
|
28
28
|
throw new ValidationException_1.ValidationException(line, "INVALID_NODE_NAME", `Node name not valid: ${name}`);
|
|
29
29
|
}
|
|
30
30
|
}
|
|
@@ -16,9 +16,13 @@ export declare class SchemaProviderMeta implements SchemaProvider {
|
|
|
16
16
|
/**
|
|
17
17
|
* Serves the meta-schema of the schema language.
|
|
18
18
|
*
|
|
19
|
+
* Follows the {@link SchemaProvider} contract: providers never throw "not found". Any
|
|
20
|
+
* namespace other than `@stxt.schema` yields `null`, so that this provider can sit at
|
|
21
|
+
* the end of a fallback chain (it is the default parent of {@link SchemaProviderMemory})
|
|
22
|
+
* and the {@link SchemaValidator} is the only one reporting `SCHEMA_NOT_FOUND`.
|
|
23
|
+
*
|
|
19
24
|
* @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.
|
|
25
|
+
* @returns the meta-schema of the schema language, or `null` for any other namespace.
|
|
22
26
|
*/
|
|
23
|
-
getSchema(namespace: string): Schema;
|
|
27
|
+
getSchema(namespace: string): Schema | null;
|
|
24
28
|
}
|
|
@@ -4,7 +4,6 @@ exports.SchemaProviderMeta = void 0;
|
|
|
4
4
|
const Schema_1 = require("./Schema");
|
|
5
5
|
const Parser_1 = require("../core/Parser");
|
|
6
6
|
const ValidationException_1 = require("../exceptions/ValidationException");
|
|
7
|
-
const RuntimeException_1 = require("../exceptions/RuntimeException");
|
|
8
7
|
const SchemaParser_1 = require("./SchemaParser");
|
|
9
8
|
/**
|
|
10
9
|
* {@link SchemaProvider} that defines in code the meta-schema of the schema language itself
|
|
@@ -27,13 +26,17 @@ class SchemaProviderMeta {
|
|
|
27
26
|
/**
|
|
28
27
|
* Serves the meta-schema of the schema language.
|
|
29
28
|
*
|
|
29
|
+
* Follows the {@link SchemaProvider} contract: providers never throw "not found". Any
|
|
30
|
+
* namespace other than `@stxt.schema` yields `null`, so that this provider can sit at
|
|
31
|
+
* the end of a fallback chain (it is the default parent of {@link SchemaProviderMemory})
|
|
32
|
+
* and the {@link SchemaValidator} is the only one reporting `SCHEMA_NOT_FOUND`.
|
|
33
|
+
*
|
|
30
34
|
* @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.
|
|
35
|
+
* @returns the meta-schema of the schema language, or `null` for any other namespace.
|
|
33
36
|
*/
|
|
34
37
|
getSchema(namespace) {
|
|
35
38
|
if (namespace !== Schema_1.Schema.SCHEMA_NAMESPACE) {
|
|
36
|
-
|
|
39
|
+
return null;
|
|
37
40
|
}
|
|
38
41
|
if (!this.meta) {
|
|
39
42
|
throw new ValidationException_1.ValidationException(0, "META_SCHEMA_NOT_AVAILABLE", "Meta schema not available");
|
|
@@ -16,9 +16,12 @@ export declare class MetaTemplateSchemaProvider implements SchemaProvider {
|
|
|
16
16
|
/**
|
|
17
17
|
* Serves the meta-schema of the template language.
|
|
18
18
|
*
|
|
19
|
+
* Follows the {@link SchemaProvider} contract: providers never throw "not found". Any
|
|
20
|
+
* namespace other than `@stxt.template` yields `null`; only the `SchemaValidator`
|
|
21
|
+
* reports `SCHEMA_NOT_FOUND`.
|
|
22
|
+
*
|
|
19
23
|
* @param namespace namespace whose schema is wanted; only `@stxt.template` is served.
|
|
20
|
-
* @returns the meta-schema of the template language.
|
|
21
|
-
* @throws RuntimeException with code `RESOURCE_NOT_FOUND` if any other namespace is asked for.
|
|
24
|
+
* @returns the meta-schema of the template language, or `null` for any other namespace.
|
|
22
25
|
*/
|
|
23
|
-
getSchema(namespace: string): Schema;
|
|
26
|
+
getSchema(namespace: string): Schema | null;
|
|
24
27
|
}
|
|
@@ -3,7 +3,6 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.MetaTemplateSchemaProvider = void 0;
|
|
4
4
|
const Parser_1 = require("../core/Parser");
|
|
5
5
|
const ValidationException_1 = require("../exceptions/ValidationException");
|
|
6
|
-
const RuntimeException_1 = require("../exceptions/RuntimeException");
|
|
7
6
|
const TemplateParser_1 = require("./TemplateParser");
|
|
8
7
|
/**
|
|
9
8
|
* {@link SchemaProvider} that defines in code the meta-schema of the template language itself
|
|
@@ -26,13 +25,16 @@ class MetaTemplateSchemaProvider {
|
|
|
26
25
|
/**
|
|
27
26
|
* Serves the meta-schema of the template language.
|
|
28
27
|
*
|
|
28
|
+
* Follows the {@link SchemaProvider} contract: providers never throw "not found". Any
|
|
29
|
+
* namespace other than `@stxt.template` yields `null`; only the `SchemaValidator`
|
|
30
|
+
* reports `SCHEMA_NOT_FOUND`.
|
|
31
|
+
*
|
|
29
32
|
* @param namespace namespace whose schema is wanted; only `@stxt.template` is served.
|
|
30
|
-
* @returns the meta-schema of the template language.
|
|
31
|
-
* @throws RuntimeException with code `RESOURCE_NOT_FOUND` if any other namespace is asked for.
|
|
33
|
+
* @returns the meta-schema of the template language, or `null` for any other namespace.
|
|
32
34
|
*/
|
|
33
35
|
getSchema(namespace) {
|
|
34
36
|
if (namespace !== "@stxt.template") {
|
|
35
|
-
|
|
37
|
+
return null;
|
|
36
38
|
}
|
|
37
39
|
// meta always exists once the constructor finished, but this mirrors the Java version
|
|
38
40
|
if (!this.meta) {
|
|
@@ -72,6 +72,11 @@ function transformTemplateNodeToSchema(node) {
|
|
|
72
72
|
}
|
|
73
73
|
/** Adds to the schema the definition a node of the structure declares, along with its children. */
|
|
74
74
|
function addToSchema(schema, node) {
|
|
75
|
+
// A Structure line must use the template grammar's ':' form. The core parser
|
|
76
|
+
// also accepts BLOCK nodes here, so reject them explicitly (STXT-TEMPLATE-SPEC 6.3).
|
|
77
|
+
if (node.isTextNode()) {
|
|
78
|
+
throw new ValidationException_1.ValidationException(node.getLine(), "INVALID_CHILD_LINE", "Template Structure lines must use ':'");
|
|
79
|
+
}
|
|
75
80
|
// Get the qualified name
|
|
76
81
|
let namespace = node.getNamespace();
|
|
77
82
|
const name = node.getName();
|