@stxt-lang/core 0.15.0 → 0.16.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.
Files changed (38) hide show
  1. package/README.md +2 -2
  2. package/out/core/InlineNode.js +1 -0
  3. package/out/core/NameNamespace.d.ts +5 -5
  4. package/out/core/NameNamespace.js +5 -5
  5. package/out/core/NamespaceValidator.d.ts +7 -7
  6. package/out/core/NamespaceValidator.js +7 -7
  7. package/out/core/Parser.d.ts +5 -0
  8. package/out/core/Parser.js +15 -17
  9. package/out/core/StringUtils.d.ts +0 -7
  10. package/out/core/StringUtils.js +0 -10
  11. package/out/discovery/DiscoveryResolver.d.ts +1 -1
  12. package/out/discovery/DiscoveryResolver.js +36 -17
  13. package/out/discovery/DiscoveryResult.js +6 -4
  14. package/out/exceptions/ParseException.d.ts +8 -1
  15. package/out/exceptions/ParseException.js +7 -0
  16. package/out/runtime/UnifiedSchemaProvider.d.ts +1 -3
  17. package/out/runtime/UnifiedSchemaProvider.js +14 -32
  18. package/out/schema/DefinitionCompiler.d.ts +27 -0
  19. package/out/schema/DefinitionCompiler.js +53 -0
  20. package/out/schema/NodeDefinition.js +6 -3
  21. package/out/schema/Schema.d.ts +2 -0
  22. package/out/schema/Schema.js +7 -4
  23. package/out/schema/SchemaParser.js +13 -12
  24. package/out/schema/SchemaProviderMemory.js +3 -19
  25. package/out/schema/SchemaProviderMeta.d.ts +7 -1
  26. package/out/schema/SchemaProviderMeta.js +10 -9
  27. package/out/schema/SchemaValidator.js +5 -3
  28. package/out/schema/type/BASE64.d.ts +4 -2
  29. package/out/schema/type/BASE64.js +34 -10
  30. package/out/schema/type/MARKDOWN.d.ts +2 -1
  31. package/out/schema/type/MARKDOWN.js +4 -8
  32. package/out/template/ChildLineParser.js +12 -9
  33. package/out/template/MetaTemplateSchemaProvider.d.ts +3 -1
  34. package/out/template/MetaTemplateSchemaProvider.js +12 -11
  35. package/out/template/TemplateParser.d.ts +1 -2
  36. package/out/template/TemplateParser.js +116 -89
  37. package/out/template/TemplateSchemaProviderMemory.js +3 -19
  38. package/package.json +2 -2
@@ -13,9 +13,8 @@ const ChildLineParser_1 = require("./ChildLineParser");
13
13
  const ParseException_1 = require("../exceptions/ParseException");
14
14
  const TypeRegistry_1 = require("../schema/TypeRegistry");
15
15
  const NamespaceValidator_1 = require("../core/NamespaceValidator");
16
- /** Namespace of the template language itself, `@stxt.template`. */
17
- /** Namespace of the template language itself, `@stxt.template`. */
18
- exports.TEMPLATE_NAMESPACE = "@stxt.template";
16
+ /** Namespace of the template language itself; the canonical constant is {@link Schema.TEMPLATE_NAMESPACE}. */
17
+ exports.TEMPLATE_NAMESPACE = Schema_1.Schema.TEMPLATE_NAMESPACE;
19
18
  /**
20
19
  * Turns the tree of an already parsed `@stxt.template` document into an equivalent {@link Schema}.
21
20
  *
@@ -32,7 +31,7 @@ function transformTemplateNodeToSchema(node) {
32
31
  }
33
32
  // The target namespace: required, and with a valid format
34
33
  const targetNamespace = StringUtils_1.StringUtils.lowerCase(node.getText());
35
- if (!targetNamespace || targetNamespace.trim().length === 0) {
34
+ if (!targetNamespace || StringUtils_1.StringUtils.trim(targetNamespace).length === 0) {
36
35
  throw new ValidationException_1.ValidationException(node.getLine(), "TEMPLATE_NAMESPACE_EMPTY", "Template namespace is empty");
37
36
  }
38
37
  if (!NamespaceValidator_1.NamespaceValidator.isValid(targetNamespace)) {
@@ -89,116 +88,144 @@ function transformTemplateNodeToSchema(node) {
89
88
  // Return the result
90
89
  return result;
91
90
  }
92
- /** Adds to the schema the definition a node of the structure declares, along with its children. */
91
+ /**
92
+ * Adds to the schema the definition a node of the structure declares, along with its children.
93
+ *
94
+ * Only the orchestration lives here; each of the three shapes a Structure line can take has its
95
+ * own helper below: a node of an external namespace ({@link validateExternalNode}, nothing is
96
+ * created), a name seen for the first time ({@link createDefinition}) or a reappearance
97
+ * ({@link validateReference}, nothing is created). The children of a definition are declared
98
+ * and recursed by {@link addChildren}.
99
+ */
93
100
  function addToSchema(schema, node) {
94
101
  // A Structure line must use the template grammar's ':' form. The core parser
95
102
  // also accepts BLOCK nodes here, so reject them explicitly (STXT-TEMPLATE-SPEC 6.3).
96
103
  if (!(node instanceof InlineNode_1.InlineNode)) {
97
104
  throw new ValidationException_1.ValidationException(node.getLine(), "STRUCTURE_LINE_NOT_VALID", "Template Structure lines must use ':'");
98
105
  }
99
- // Get the qualified name
106
+ // Parse the RuleSpec (cardinality / type / values) from the inline value
107
+ const cl = ChildLineParser_1.ChildLineParser.parse(node.getValue(), node.getLine());
108
+ // No explicit namespace => the target namespace of the template
100
109
  let namespace = node.getNamespace();
101
- const name = node.getName();
102
- // Look at the data
103
- let cl = ChildLineParser_1.ChildLineParser.parse(node.getValue(), node.getLine());
104
110
  if (!namespace || namespace === "") {
105
111
  namespace = schema.getNamespace();
106
112
  }
107
113
  if (namespace !== schema.getNamespace()) {
108
- // An external node may only declare cardinality: no type, no ENUM values
109
- // and no children (STXT-TEMPLATE-SPEC 6.4, 10 and 14.15)
110
- const type = cl.getType();
111
- if (type && type.trim().length > 0) {
112
- throw new ValidationException_1.ValidationException(node.getLine(), "TYPE_NOT_ALLOWED_IN_EXTERNAL_NAMESPACE", "Not allowed type definition in external namespaces");
113
- }
114
- const values = cl.getValues();
115
- if (values) {
116
- throw new ValidationException_1.ValidationException(node.getLine(), "VALUES_NOT_ALLOWED_IN_EXTERNAL_NAMESPACE", `Not allowed values in external namespaces (node ${node.getName()})`);
117
- }
118
- if (node.getChildren().length > 0) {
119
- throw new ValidationException_1.ValidationException(node.getLine(), "CHILDREN_NOT_ALLOWED_IN_EXTERNAL_NAMESPACE", `Not allowed children in external namespaces (node ${node.getName()})`);
120
- }
121
- // Nodes that do not belong to @stxt.template create nothing here!!
122
- return;
123
- }
124
- // Check whether it is new and add it to the list
125
- let schemaNode = schema.getNodeDefinition(name);
126
- if (!schemaNode) {
127
- // New one
128
- const type = cl.getType() ?? "INLINE";
129
- // At this point the schema already holds both the previous definitions, already closed,
130
- // and the open ancestors, so a reference that does not resolve here resolves to
131
- // nothing (STXT-TEMPLATE-SPEC 6.4 and 14.11)
132
- if (type.startsWith("@")) {
133
- throw new ValidationException_1.ValidationException(node.getLine(), "REFERENCE_NOT_FOUND", `Reference '${type}' does not point to a previous definition or an open ancestor`);
134
- }
135
- schemaNode = new NodeDefinition_1.NodeDefinition(node.getName(), type, node.getLine(), undefined);
136
- schema.addNodeDefinition(schemaNode);
137
- if (!TypeRegistry_1.TypeRegistry.get(type)) {
138
- throw new ValidationException_1.ValidationException(node.getLine(), "TYPE_NOT_VALID", `Type not valid: ${type}`);
139
- }
140
- const values = cl.getValues();
141
- if (values) {
142
- if (type !== "ENUM") {
143
- // Same code as SchemaParser: a template is sugar equivalent to a schema
144
- // (STXT-TEMPLATE-SPEC 13), so the same condition must not change its code
145
- // depending on the entry point
146
- throw new ValidationException_1.ValidationException(node.getLine(), "VALUES_NOT_ALLOWED_FOR_TYPE", `Values only supported for type ENUM, not for type ${type}`);
147
- }
148
- for (const v of values) {
149
- schemaNode.addValue(v, node.getLine());
150
- }
151
- }
152
- // An ENUM with no list of values is an invalid template (STXT-TEMPLATE-SPEC 9 and 13.7)
153
- if (type === "ENUM" && (!values || values.length === 0)) {
154
- throw new ValidationException_1.ValidationException(node.getLine(), "VALUES_REQUIRED", "ENUM Type must include values");
155
- }
114
+ validateExternalNode(node, cl);
115
+ return; // no definitions are created for nodes of other namespaces
156
116
  }
157
- else {
158
- const type = cl.getType();
159
- if (!type || !type.startsWith("@")) {
160
- throw new ValidationException_1.ValidationException(node.getLine(), "REFERENCE_REQUIRED", `Multiple node reference must start with @: ${node.getName()}`);
161
- }
162
- const reference = type.substring(1).trim();
163
- // Reference and explicit type on the same line (STXT-TEMPLATE-SPEC 14.13)
164
- const explicitType = referenceType(reference, node.getCanonicalName());
165
- if (explicitType) {
166
- throw new ValidationException_1.ValidationException(node.getLine(), "REFERENCE_WITH_TYPE_NOT_ALLOWED", `Reference '@${node.getName()}' can not declare a type: ${explicitType}`);
167
- }
168
- if (StringUtils_1.StringUtils.normalize(reference) !== node.getCanonicalName()) {
169
- throw new ValidationException_1.ValidationException(node.getLine(), "REFERENCE_NAME_NOT_VALID", `Reference must be '@${node.getName()}', not '${reference}'`);
170
- }
171
- // A reference may override the cardinality, but it may redefine neither the ENUM
172
- // values nor the children (STXT-TEMPLATE-SPEC 6.4)
173
- const values = cl.getValues();
174
- if (values) {
175
- throw new ValidationException_1.ValidationException(node.getLine(), "VALUES_NOT_ALLOWED_IN_REFERENCE", `Reference '@${node.getName()}' can not redefine ENUM values`);
117
+ // New definition or a reappearance (reference)?
118
+ let schemaNode = schema.getNodeDefinition(node.getName());
119
+ if (schemaNode) {
120
+ validateReference(node, cl);
121
+ return; // valid reference: nothing is redefined, no children are processed
122
+ }
123
+ schemaNode = createDefinition(schema, node, cl);
124
+ addChildren(schema, schemaNode, node);
125
+ }
126
+ /**
127
+ * Cross-namespace node (STXT-TEMPLATE-SPEC 6.4, 10 and 14.15): not defined locally; it may only
128
+ * declare cardinality — no type, no ENUM values and no children.
129
+ */
130
+ function validateExternalNode(node, cl) {
131
+ const type = cl.getType();
132
+ if (type && StringUtils_1.StringUtils.trim(type).length > 0) {
133
+ throw new ValidationException_1.ValidationException(node.getLine(), "TYPE_NOT_ALLOWED_IN_EXTERNAL_NAMESPACE", "Not allowed type definition in external namespaces");
134
+ }
135
+ if (cl.getValues()) {
136
+ throw new ValidationException_1.ValidationException(node.getLine(), "VALUES_NOT_ALLOWED_IN_EXTERNAL_NAMESPACE", `Not allowed values in external namespaces (node ${node.getName()})`);
137
+ }
138
+ if (node.getChildren().length > 0) {
139
+ throw new ValidationException_1.ValidationException(node.getLine(), "CHILDREN_NOT_ALLOWED_IN_EXTERNAL_NAMESPACE", `Not allowed children in external namespaces (node ${node.getName()})`);
140
+ }
141
+ }
142
+ /**
143
+ * First appearance of a name: creates its {@link NodeDefinition} (with its type and its ENUM
144
+ * values, when it has them) and registers it in the schema.
145
+ */
146
+ function createDefinition(schema, node, cl) {
147
+ const type = cl.getType() ?? "INLINE";
148
+ // At this point the schema already holds both the previous definitions, already closed,
149
+ // and the open ancestors, so a reference that does not resolve here resolves to
150
+ // nothing (STXT-TEMPLATE-SPEC 6.4 and 14.11)
151
+ if (type.startsWith("@")) {
152
+ throw new ValidationException_1.ValidationException(node.getLine(), "REFERENCE_NOT_FOUND", `Reference '${type}' does not point to a previous definition or an open ancestor`);
153
+ }
154
+ const schemaNode = new NodeDefinition_1.NodeDefinition(node.getName(), type, node.getLine(), undefined);
155
+ schema.addNodeDefinition(schemaNode);
156
+ if (!TypeRegistry_1.TypeRegistry.get(type)) {
157
+ throw new ValidationException_1.ValidationException(node.getLine(), "TYPE_NOT_VALID", `Type not valid: ${type}`);
158
+ }
159
+ const values = cl.getValues();
160
+ if (values) {
161
+ if (type !== "ENUM") {
162
+ // Same code as SchemaParser: a template is sugar equivalent to a schema
163
+ // (STXT-TEMPLATE-SPEC 13), so the same condition must not change its code
164
+ // depending on the entry point
165
+ throw new ValidationException_1.ValidationException(node.getLine(), "VALUES_NOT_ALLOWED_FOR_TYPE", `Values only supported for type ENUM, not for type ${type}`);
176
166
  }
177
- if (node.getChildren().length > 0) {
178
- throw new ValidationException_1.ValidationException(node.getLine(), "CHILDREN_NOT_ALLOWED_IN_REFERENCE", `Reference '@${node.getName()}' can not redefine children`);
167
+ for (const v of values) {
168
+ schemaNode.addValue(v, node.getLine());
179
169
  }
180
- return; // OK Definition
181
170
  }
182
- // Once it exists, try to create its children if it has any.
183
- const childrenNode = node.getChildren();
184
- // Template error 14.9: node with children and an effective type that does not admit children
185
- if (childrenNode.length > 0 && !TypeRegistry_1.TypeRegistry.admitsChildren(schemaNode.getType())) {
171
+ // An ENUM with no list of values is an invalid template (STXT-TEMPLATE-SPEC 9 and 13.7)
172
+ if (type === "ENUM" && (!values || values.length === 0)) {
173
+ throw new ValidationException_1.ValidationException(node.getLine(), "VALUES_REQUIRED", "ENUM Type must include values");
174
+ }
175
+ return schemaNode;
176
+ }
177
+ /**
178
+ * Reappearance of an already defined name: it must be a `@Node Name` reference, and a reference
179
+ * may override the cardinality but may redefine neither the ENUM values nor the children
180
+ * (STXT-TEMPLATE-SPEC 6.4, 14.12 and 14.13).
181
+ */
182
+ function validateReference(node, cl) {
183
+ const type = cl.getType();
184
+ // A reappearance without "@" would redefine an existing node: error.
185
+ if (!type || !type.startsWith("@")) {
186
+ throw new ValidationException_1.ValidationException(node.getLine(), "REFERENCE_REQUIRED", `Multiple node reference must start with @: ${node.getName()}`);
187
+ }
188
+ const reference = StringUtils_1.StringUtils.trim(type.substring(1));
189
+ // Reference and explicit type on the same line (STXT-TEMPLATE-SPEC 14.13)
190
+ const explicitType = referenceType(reference, node.getCanonicalName());
191
+ if (explicitType) {
192
+ throw new ValidationException_1.ValidationException(node.getLine(), "REFERENCE_WITH_TYPE_NOT_ALLOWED", `Reference '@${node.getName()}' can not declare a type: ${explicitType}`);
193
+ }
194
+ // The name of the reference must match (canonically) the one of the line (14.12)
195
+ if (StringUtils_1.StringUtils.normalize(reference) !== node.getCanonicalName()) {
196
+ throw new ValidationException_1.ValidationException(node.getLine(), "REFERENCE_NAME_NOT_VALID", `Reference must be '@${node.getName()}', not '${reference}'`);
197
+ }
198
+ if (cl.getValues()) {
199
+ throw new ValidationException_1.ValidationException(node.getLine(), "VALUES_NOT_ALLOWED_IN_REFERENCE", `Reference '@${node.getName()}' can not redefine ENUM values`);
200
+ }
201
+ if (node.getChildren().length > 0) {
202
+ throw new ValidationException_1.ValidationException(node.getLine(), "CHILDREN_NOT_ALLOWED_IN_REFERENCE", `Reference '@${node.getName()}' can not redefine children`);
203
+ }
204
+ }
205
+ /**
206
+ * Declares every direct child of a definition as a {@link ChildDefinition} (with its cardinality)
207
+ * and recurses into each one as a definition/reference of its own.
208
+ */
209
+ function addChildren(schema, schemaNode, node) {
210
+ const children = node.getChildren();
211
+ // Template error 14.9: children under an effective type that does not admit them
212
+ if (children.length > 0 && !TypeRegistry_1.TypeRegistry.admitsChildren(schemaNode.getType())) {
186
213
  throw new ValidationException_1.ValidationException(node.getLine(), "CHILDREN_NOT_ALLOWED_FOR_TYPE", `Type ${schemaNode.getType()} does not allow children (node ${node.getName()})`);
187
214
  }
188
- // Add the children
189
- for (const child of childrenNode) {
215
+ for (const child of children) {
190
216
  // STXT-TEMPLATE-SPEC 6.3: every Structure line uses ':', so a child is inline too
191
217
  if (!(child instanceof InlineNode_1.InlineNode)) {
192
218
  throw new ValidationException_1.ValidationException(child.getLine(), "STRUCTURE_LINE_NOT_VALID", "Template Structure lines must use ':'");
193
219
  }
194
- cl = ChildLineParser_1.ChildLineParser.parse(child.getValue(), child.getLine());
195
- const childName = child.getName();
220
+ const childCl = ChildLineParser_1.ChildLineParser.parse(child.getValue(), child.getLine());
196
221
  let childNamespace = child.getNamespace();
197
222
  if (!childNamespace || childNamespace === "") {
198
223
  childNamespace = schema.getNamespace();
199
224
  }
200
- const schChild = new ChildDefinition_1.ChildDefinition(childName, childNamespace, cl.getMin(), cl.getMax(), child.getLine());
225
+ // The child is declared as a Child (with its cardinality) in the current definition
226
+ const schChild = new ChildDefinition_1.ChildDefinition(child.getName(), childNamespace, childCl.getMin(), childCl.getMax(), child.getLine());
201
227
  schemaNode.addChildDefinition(schChild);
228
+ // And processed recursively as a definition/reference
202
229
  addToSchema(schema, child);
203
230
  }
204
231
  }
@@ -214,7 +241,7 @@ function referenceType(reference, normalizedName) {
214
241
  if (cut < 0) {
215
242
  return null;
216
243
  }
217
- const candidate = reference.substring(cut + 1).trim();
244
+ const candidate = StringUtils_1.StringUtils.trim(reference.substring(cut + 1));
218
245
  const rest = reference.substring(0, cut);
219
246
  if (TypeRegistry_1.TypeRegistry.get(candidate) && StringUtils_1.StringUtils.normalize(rest) === normalizedName) {
220
247
  return candidate;
@@ -1,10 +1,7 @@
1
1
  "use strict";
2
- // TemplateSchemaProvider.ts
3
2
  Object.defineProperty(exports, "__esModule", { value: true });
4
3
  exports.TemplateSchemaProviderMemory = void 0;
5
- const Parser_1 = require("../core/Parser");
6
- const SchemaValidator_1 = require("../schema/SchemaValidator");
7
- const ValidationException_1 = require("../exceptions/ValidationException");
4
+ const DefinitionCompiler_1 = require("../schema/DefinitionCompiler");
8
5
  const MetaTemplateSchemaProvider_1 = require("./MetaTemplateSchemaProvider");
9
6
  const SchemaProviderMemory_1 = require("../schema/SchemaProviderMemory");
10
7
  const TemplateParser_1 = require("./TemplateParser");
@@ -36,21 +33,8 @@ class TemplateSchemaProviderMemory extends SchemaProviderMemory_1.SchemaProvider
36
33
  * template does not validate against the template meta-schema.
37
34
  */
38
35
  addTemplate(template) {
39
- const parser = new Parser_1.Parser();
40
- const nodes = parser.parse(template);
41
- if (nodes.length !== 1) {
42
- throw new ValidationException_1.ValidationException(0, "TEMPLATE_MULTIPLE_ROOTS", `A template document must hold exactly 1 root node, got ${nodes.length}`);
43
- }
44
- // A template that does not validate against the template meta-schema must not
45
- // be registered (same policy as UnifiedSchemaProvider/DiscoveryResolver)
46
- const schemaValidator = new SchemaValidator_1.SchemaValidator(new MetaTemplateSchemaProvider_1.MetaTemplateSchemaProvider(), true);
47
- const errors = schemaValidator.validate(nodes[0]);
48
- if (errors.length > 0) {
49
- throw errors[0];
50
- }
51
- // Build the schema out of the template
52
- const sch = (0, TemplateParser_1.transformTemplateNodeToSchema)(nodes[0]);
53
- this.schemas.set(sch.getNamespace(), sch);
36
+ const schema = (0, DefinitionCompiler_1.compileDefinitionDocument)(template, new MetaTemplateSchemaProvider_1.MetaTemplateSchemaProvider(), TemplateParser_1.transformTemplateNodeToSchema, "TEMPLATE_MULTIPLE_ROOTS", "template");
37
+ this.schemas.set(schema.getNamespace(), schema);
54
38
  }
55
39
  }
56
40
  exports.TemplateSchemaProviderMemory = TemplateSchemaProviderMemory;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@stxt-lang/core",
3
- "version": "0.15.0",
4
- "description": "Parser and schema validator for STXT, an indentation-based structured-text format.",
3
+ "version": "0.16.0",
4
+ "description": "Parser and schema validator for STXT, an indentation-based structured-text language.",
5
5
  "main": "out/all.js",
6
6
  "types": "out/all.d.ts",
7
7
  "publishConfig": {