@stxt-lang/core 0.9.1 → 0.11.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/README.md CHANGED
@@ -117,7 +117,6 @@ import {
117
117
  Parser,
118
118
  UnifiedSchemaProvider,
119
119
  SchemaValidator,
120
- ConditionalValidator,
121
120
  ValidationException,
122
121
  } from '@stxt-lang/core';
123
122
 
@@ -138,8 +137,8 @@ const provider = new UnifiedSchemaProvider();
138
137
  provider.addFile(schemaText);
139
138
 
140
139
  const parser = new Parser();
141
- // ConditionalValidator only validates nodes that carry a namespace
142
- parser.registerValidator(new ConditionalValidator(new SchemaValidator(provider)));
140
+ // Only nodes that carry a namespace are validated; free nodes pass through
141
+ parser.registerValidator(new SchemaValidator(provider));
143
142
 
144
143
  const result = parser.parseResult(documentText);
145
144
 
@@ -223,7 +222,7 @@ class NodeEnvironment implements DiscoveryEnvironment {
223
222
  With those in place, resolving a document and validating it is two steps — and note that `DiscoveryResult` implements `SchemaProvider`, so it goes straight into the validator:
224
223
 
225
224
  ```ts
226
- import { Parser, SchemaValidator, ConditionalValidator } from '@stxt-lang/core';
225
+ import { Parser, SchemaValidator } from '@stxt-lang/core';
227
226
 
228
227
  const resolver = new DiscoveryResolver(new NodeFileSystem(), new NodeEnvironment());
229
228
 
@@ -240,7 +239,7 @@ for (const error of result.getErrors()) {
240
239
  }
241
240
 
242
241
  const parser = new Parser();
243
- parser.registerValidator(new ConditionalValidator(new SchemaValidator(result)));
242
+ parser.registerValidator(new SchemaValidator(result));
244
243
 
245
244
  const parsed = parser.parseResult(documentText);
246
245
  ```
@@ -297,10 +296,11 @@ const doc = NodeWriter.toSTXTDocs(result.getNodes(), IndentStyle.SPACES_4);
297
296
  Everything importable from the package:
298
297
 
299
298
  - **Parsing** — `Node`, `InlineNode`, `TextNode`, `Parser`, `ParseResult`, `Line`, `Constants`, `parseLine`, `StringUtils`
300
- - **Exceptions** — `ParseException`, `ValidationException`
301
- - **Extension points** — `Observer`
302
- - **Schemas** — `Schema`, `SchemaValidator`, `SchemaProvider`, `NodeDefinition`, `ChildDefinition`, `transformNodeToSchema`, `transformTemplateNodeToSchema`
303
- - **Runtime** — `UnifiedSchemaProvider`, `ConditionalValidator`, `NodeWriter`, `IndentStyle`
299
+ - **Exceptions** — `ParseException`, `ValidationException`, `RuntimeException`
300
+ - **Extension points** — `Observer`, `Validator`
301
+ - **Schemas** — `Schema`, `SchemaValidator`, `SchemaProvider`, `SchemaProviderMemory`, `SchemaProviderMeta`, `NodeDefinition`, `ChildDefinition`, `TypeRegistry`, `Type`, `transformNodeToSchema`
302
+ - **Templates** — `transformTemplateNodeToSchema`, `TEMPLATE_NAMESPACE`, `TemplateSchemaProviderMemory`, `MetaTemplateSchemaProvider`
303
+ - **Runtime** — `UnifiedSchemaProvider`, `NodeWriter`, `IndentStyle`
304
304
  - **Discovery** — `DiscoveryResolver`, `DiscoveryOptions`, `DiscoveryResult`, `DiscoveryDefinition`, `DiscoveryLevel`, `DiscoveryError`, `DiscoveryFileSystem`, `DiscoveryEntry`, `DiscoveryEnvironment`
305
305
 
306
306
  ## License
package/out/all.d.ts CHANGED
@@ -5,23 +5,36 @@ 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 STXT-SPEC (the base syntax) this library implements, distinct from the version of
10
+ * the package; each specification is versioned independently (STXT-SPEC §1.1). Same value as
11
+ * `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";
11
17
  export { ValidationException } from "./exceptions/ValidationException";
18
+ export { RuntimeException } from "./exceptions/RuntimeException";
12
19
  export { Observer } from "./processors/Observer";
20
+ export { Validator } from "./processors/Validator";
13
21
  export { Schema } from "./schema/Schema";
14
22
  export { SchemaValidator } from "./schema/SchemaValidator";
15
23
  export { SchemaProvider } from "./schema/SchemaProvider";
24
+ export { SchemaProviderMemory } from "./schema/SchemaProviderMemory";
25
+ export { SchemaProviderMeta } from "./schema/SchemaProviderMeta";
26
+ export { TypeRegistry } from "./schema/TypeRegistry";
27
+ export type { Type } from "./schema/Type";
16
28
  export { NodeDefinition } from "./schema/NodeDefinition";
17
29
  export { ChildDefinition } from "./schema/ChildDefinition";
18
30
  export { transformNodeToSchema } from "./schema/SchemaParser";
19
31
  export { UnifiedSchemaProvider } from "./runtime/UnifiedSchemaProvider";
20
- export { ConditionalValidator } from "./runtime/ConditionalValidator";
21
32
  export { NodeWriter, IndentStyle } from "./runtime/NodeWriter";
22
33
  export { toCanonicalTree, toCanonicalJson } from "./runtime/TreeJson";
23
34
  export type { CanonicalDocument, CanonicalNode, CanonicalInlineNode, CanonicalBlockNode } from "./runtime/TreeJson";
24
- export { transformTemplateNodeToSchema } from "./template/TemplateParser";
35
+ export { transformTemplateNodeToSchema, TEMPLATE_NAMESPACE } from "./template/TemplateParser";
36
+ export { TemplateSchemaProviderMemory } from "./template/TemplateSchemaProviderMemory";
37
+ export { MetaTemplateSchemaProvider } from "./template/MetaTemplateSchemaProvider";
25
38
  export { DiscoveryResolver, DiscoveryOptions } from "./discovery/DiscoveryResolver";
26
39
  export { DiscoveryResult, DiscoveryDefinition, DiscoveryLevel } from "./discovery/DiscoveryResult";
27
40
  export { DiscoveryError } from "./discovery/DiscoveryError";
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.MetaTemplateSchemaProvider = exports.TemplateSchemaProviderMemory = exports.TEMPLATE_NAMESPACE = exports.transformTemplateNodeToSchema = exports.toCanonicalJson = exports.toCanonicalTree = exports.IndentStyle = exports.NodeWriter = exports.UnifiedSchemaProvider = exports.transformNodeToSchema = exports.ChildDefinition = exports.NodeDefinition = exports.TypeRegistry = exports.SchemaProviderMeta = exports.SchemaProviderMemory = exports.SchemaValidator = exports.Schema = exports.RuntimeException = 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 STXT-SPEC (the base syntax) this library implements, distinct from the version of
24
+ * the package; each specification is versioned independently (STXT-SPEC §1.1). Same value as
25
+ * `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");
@@ -26,10 +33,18 @@ var ParseException_1 = require("./exceptions/ParseException");
26
33
  Object.defineProperty(exports, "ParseException", { enumerable: true, get: function () { return ParseException_1.ParseException; } });
27
34
  var ValidationException_1 = require("./exceptions/ValidationException");
28
35
  Object.defineProperty(exports, "ValidationException", { enumerable: true, get: function () { return ValidationException_1.ValidationException; } });
36
+ var RuntimeException_1 = require("./exceptions/RuntimeException");
37
+ Object.defineProperty(exports, "RuntimeException", { enumerable: true, get: function () { return RuntimeException_1.RuntimeException; } });
29
38
  var Schema_1 = require("./schema/Schema");
30
39
  Object.defineProperty(exports, "Schema", { enumerable: true, get: function () { return Schema_1.Schema; } });
31
40
  var SchemaValidator_1 = require("./schema/SchemaValidator");
32
41
  Object.defineProperty(exports, "SchemaValidator", { enumerable: true, get: function () { return SchemaValidator_1.SchemaValidator; } });
42
+ var SchemaProviderMemory_1 = require("./schema/SchemaProviderMemory");
43
+ Object.defineProperty(exports, "SchemaProviderMemory", { enumerable: true, get: function () { return SchemaProviderMemory_1.SchemaProviderMemory; } });
44
+ var SchemaProviderMeta_1 = require("./schema/SchemaProviderMeta");
45
+ Object.defineProperty(exports, "SchemaProviderMeta", { enumerable: true, get: function () { return SchemaProviderMeta_1.SchemaProviderMeta; } });
46
+ var TypeRegistry_1 = require("./schema/TypeRegistry");
47
+ Object.defineProperty(exports, "TypeRegistry", { enumerable: true, get: function () { return TypeRegistry_1.TypeRegistry; } });
33
48
  var NodeDefinition_1 = require("./schema/NodeDefinition");
34
49
  Object.defineProperty(exports, "NodeDefinition", { enumerable: true, get: function () { return NodeDefinition_1.NodeDefinition; } });
35
50
  var ChildDefinition_1 = require("./schema/ChildDefinition");
@@ -38,8 +53,6 @@ var SchemaParser_1 = require("./schema/SchemaParser");
38
53
  Object.defineProperty(exports, "transformNodeToSchema", { enumerable: true, get: function () { return SchemaParser_1.transformNodeToSchema; } });
39
54
  var UnifiedSchemaProvider_1 = require("./runtime/UnifiedSchemaProvider");
40
55
  Object.defineProperty(exports, "UnifiedSchemaProvider", { enumerable: true, get: function () { return UnifiedSchemaProvider_1.UnifiedSchemaProvider; } });
41
- var ConditionalValidator_1 = require("./runtime/ConditionalValidator");
42
- Object.defineProperty(exports, "ConditionalValidator", { enumerable: true, get: function () { return ConditionalValidator_1.ConditionalValidator; } });
43
56
  var NodeWriter_1 = require("./runtime/NodeWriter");
44
57
  Object.defineProperty(exports, "NodeWriter", { enumerable: true, get: function () { return NodeWriter_1.NodeWriter; } });
45
58
  Object.defineProperty(exports, "IndentStyle", { enumerable: true, get: function () { return NodeWriter_1.IndentStyle; } });
@@ -48,6 +61,11 @@ Object.defineProperty(exports, "toCanonicalTree", { enumerable: true, get: funct
48
61
  Object.defineProperty(exports, "toCanonicalJson", { enumerable: true, get: function () { return TreeJson_1.toCanonicalJson; } });
49
62
  var TemplateParser_1 = require("./template/TemplateParser");
50
63
  Object.defineProperty(exports, "transformTemplateNodeToSchema", { enumerable: true, get: function () { return TemplateParser_1.transformTemplateNodeToSchema; } });
64
+ Object.defineProperty(exports, "TEMPLATE_NAMESPACE", { enumerable: true, get: function () { return TemplateParser_1.TEMPLATE_NAMESPACE; } });
65
+ var TemplateSchemaProviderMemory_1 = require("./template/TemplateSchemaProviderMemory");
66
+ Object.defineProperty(exports, "TemplateSchemaProviderMemory", { enumerable: true, get: function () { return TemplateSchemaProviderMemory_1.TemplateSchemaProviderMemory; } });
67
+ var MetaTemplateSchemaProvider_1 = require("./template/MetaTemplateSchemaProvider");
68
+ Object.defineProperty(exports, "MetaTemplateSchemaProvider", { enumerable: true, get: function () { return MetaTemplateSchemaProvider_1.MetaTemplateSchemaProvider; } });
51
69
  var DiscoveryResolver_1 = require("./discovery/DiscoveryResolver");
52
70
  Object.defineProperty(exports, "DiscoveryResolver", { enumerable: true, get: function () { return DiscoveryResolver_1.DiscoveryResolver; } });
53
71
  var DiscoveryResult_1 = require("./discovery/DiscoveryResult");
@@ -1,5 +1,13 @@
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 STXT-SPEC (the base syntax) this library implements; "STXT 1.0" on its own means
5
+ * this number (STXT-SPEC §1.1). Each specification is versioned independently, so the schema,
6
+ * template, tree and discovery specs may carry other numbers. It is distinct from the version
7
+ * of the package: the package may be released many times against the same specification
8
+ * version.
9
+ */
10
+ static readonly SPEC_VERSION: string;
3
11
  /** Character that opens a comment line. */
4
12
  static readonly COMMENT_CHAR: string;
5
13
  /** Number of spaces that make up one indentation level. */
@@ -5,6 +5,14 @@ exports.Constants = void 0;
5
5
  class Constants {
6
6
  }
7
7
  exports.Constants = Constants;
8
+ /**
9
+ * Version of STXT-SPEC (the base syntax) this library implements; "STXT 1.0" on its own means
10
+ * this number (STXT-SPEC §1.1). Each specification is versioned independently, so the schema,
11
+ * template, tree and discovery specs may carry other numbers. It is distinct from the version
12
+ * of the package: the package may be released many times against the same specification
13
+ * version.
14
+ */
15
+ Constants.SPEC_VERSION = "1.0";
8
16
  /** Character that opens a comment line. */
9
17
  Constants.COMMENT_CHAR = "#";
10
18
  /** Number of spaces that make up one indentation level. */
@@ -15,8 +15,8 @@ export declare class Line {
15
15
  readonly isComment: boolean;
16
16
  /** True if the line is a text line belonging to an open BLOCK node (`>>`). */
17
17
  readonly isBlock: boolean;
18
- /** Number of characters the indentation took up. */
19
- readonly indentLength: number;
18
+ /** Index of the first character of the content (the number of characters the indentation took up). */
19
+ readonly contentStart: number;
20
20
  /**
21
21
  * Creates a line already split into indentation and content.
22
22
  *
@@ -24,9 +24,9 @@ export declare class Line {
24
24
  * @param content content of the line without its indentation.
25
25
  * @param isComment true if the line is a comment.
26
26
  * @param isBlock true if the line belongs to an open text block.
27
- * @param indentLength number of characters the indentation took up.
27
+ * @param contentStart index of the first character of the content, i.e. the number of characters the indentation took up.
28
28
  */
29
- constructor(level: number, content: string, isComment: boolean, isBlock: boolean, indentLength: number);
29
+ constructor(level: number, content: string, isComment: boolean, isBlock: boolean, contentStart: number);
30
30
  /** @returns true if the line has no content beyond blanks (space/tab only, spec 4). */
31
31
  isEmpty(): boolean;
32
32
  }
package/out/core/Line.js CHANGED
@@ -15,14 +15,14 @@ class Line {
15
15
  * @param content content of the line without its indentation.
16
16
  * @param isComment true if the line is a comment.
17
17
  * @param isBlock true if the line belongs to an open text block.
18
- * @param indentLength number of characters the indentation took up.
18
+ * @param contentStart index of the first character of the content, i.e. the number of characters the indentation took up.
19
19
  */
20
- constructor(level, content, isComment, isBlock, indentLength) {
20
+ constructor(level, content, isComment, isBlock, contentStart) {
21
21
  this.level = level;
22
22
  this.content = content;
23
23
  this.isComment = isComment;
24
24
  this.isBlock = isBlock;
25
- this.indentLength = indentLength;
25
+ this.contentStart = contentStart;
26
26
  }
27
27
  /** @returns true if the line has no content beyond blanks (space/tab only, spec 4). */
28
28
  isEmpty() {
@@ -68,7 +68,9 @@ function parseLine(line, lastNodeBlock, lastLevel, numLine, validate = true) {
68
68
  if (validate && sawSpace && sawTab && text.length > 0) {
69
69
  throw new ParseException_1.ParseException(numLine, "INDENTATION_MIXED", `Mixed tabs and spaces in indentation`);
70
70
  }
71
- return new Line_1.Line(level, text, false, true, pointer);
71
+ // pointer is the index of the indentation character that crossed the block level;
72
+ // the indentation took pointer + 1 characters
73
+ return new Line_1.Line(level, text, false, true, pointer + 1);
72
74
  }
73
75
  // Move the pointer forward
74
76
  pointer++;
@@ -51,12 +51,6 @@ export declare abstract class Node {
51
51
  setName(name: string): void;
52
52
  /** @returns the canonical name of the node (STXT-SPEC §4.3), used to compare/look up by structural identity. */
53
53
  getCanonicalName(): string;
54
- /**
55
- * @returns the canonical name of the node.
56
- * @deprecated since 0.7.0, use {@link Node.getCanonicalName}; "canonical name" is the term of
57
- * the specifications. To be removed in a later version.
58
- */
59
- getNormalizedName(): string;
60
54
  /** @returns the canonical name prefixed by the effective namespace (`namespace:name`), or just the canonical name when there is no namespace. */
61
55
  getQualifiedName(): string;
62
56
  /** @returns the namespace this node declares itself, lower-cased, or the empty string if it declares none (and so inherits the parent's). */
package/out/core/Node.js CHANGED
@@ -68,14 +68,6 @@ class Node {
68
68
  getCanonicalName() {
69
69
  return this.canonicalName;
70
70
  }
71
- /**
72
- * @returns the canonical name of the node.
73
- * @deprecated since 0.7.0, use {@link Node.getCanonicalName}; "canonical name" is the term of
74
- * the specifications. To be removed in a later version.
75
- */
76
- getNormalizedName() {
77
- return this.canonicalName;
78
- }
79
71
  /** @returns the canonical name prefixed by the effective namespace (`namespace:name`), or just the canonical name when there is no namespace. */
80
72
  getQualifiedName() {
81
73
  const namespace = this.getNamespace();
@@ -35,7 +35,7 @@ export interface DiscoveryLevel {
35
35
  * error found along the way.
36
36
  *
37
37
  * It implements {@link SchemaProvider}, so it can be handed directly to a
38
- * `SchemaValidator`/`ConditionalValidator` to validate the document it was resolved for.
38
+ * `SchemaValidator` to validate the document it was resolved for.
39
39
  * Like `UnifiedSchemaProvider`, it serves the meta-schemas of the two reserved namespaces
40
40
  * itself, so schema and template documents also validate against it.
41
41
  */
@@ -8,7 +8,7 @@ const StringUtils_1 = require("../core/StringUtils");
8
8
  * error found along the way.
9
9
  *
10
10
  * It implements {@link SchemaProvider}, so it can be handed directly to a
11
- * `SchemaValidator`/`ConditionalValidator` to validate the document it was resolved for.
11
+ * `SchemaValidator` to validate the document it was resolved for.
12
12
  * Like `UnifiedSchemaProvider`, it serves the meta-schemas of the two reserved namespaces
13
13
  * itself, so schema and template documents also validate against it.
14
14
  */
@@ -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
- /** @returns a readable representation of the error, with its line and its code. */
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
- /** @returns a readable representation of the error, with its line and its code. */
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 `${this.name} [line=${this.line}, code=${this.code}]: ${this.message}`;
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
- /** @returns a readable representation of the error, with its code. */
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
- /** @returns a readable representation of the error, with its code. */
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
- const message = this.message;
29
- return `${this.name}[${this.code}]${message ? `: ${message}` : ""}`;
33
+ return `[${this.code}] ${this.message}`;
30
34
  }
31
35
  }
32
36
  exports.RuntimeException = RuntimeException;
@@ -1,6 +1,6 @@
1
1
  /** Definition of an expected child inside a {@link NodeDefinition}: name, namespace and min/max cardinality. */
2
2
  export declare class ChildDefinition {
3
- private readonly normalizedName;
3
+ private readonly canonicalName;
4
4
  private readonly name;
5
5
  private readonly namespace;
6
6
  private readonly min;
@@ -20,11 +20,6 @@ export declare class ChildDefinition {
20
20
  getName(): string;
21
21
  /** @returns the canonical name of the expected child. */
22
22
  getCanonicalName(): string;
23
- /**
24
- * @returns the canonical name of the expected child.
25
- * @deprecated since 0.7.0, use getCanonicalName().
26
- */
27
- getNormalizedName(): string;
28
23
  /** @returns the namespace of the expected child, or the empty string if it has none. */
29
24
  getNamespace(): string;
30
25
  /** @returns the minimum cardinality, or null if there is no minimum. */
@@ -36,7 +31,7 @@ export declare class ChildDefinition {
36
31
  /** @returns a plain object with the definition, so that JSON.stringify serializes it. */
37
32
  toJSON(): {
38
33
  name: string;
39
- normalizedName: string;
34
+ canonicalName: string;
40
35
  namespace: string;
41
36
  min: number | null;
42
37
  max: number | null;
@@ -18,7 +18,7 @@ class ChildDefinition {
18
18
  */
19
19
  constructor(name, namespace, min, max, numLine) {
20
20
  this.name = StringUtils_1.StringUtils.compactSpaces(name);
21
- this.normalizedName = StringUtils_1.StringUtils.normalize(name);
21
+ this.canonicalName = StringUtils_1.StringUtils.normalize(name);
22
22
  this.namespace = StringUtils_1.StringUtils.lowerCase(namespace);
23
23
  this.min = min;
24
24
  this.max = max;
@@ -33,14 +33,7 @@ class ChildDefinition {
33
33
  }
34
34
  /** @returns the canonical name of the expected child. */
35
35
  getCanonicalName() {
36
- return this.normalizedName;
37
- }
38
- /**
39
- * @returns the canonical name of the expected child.
40
- * @deprecated since 0.7.0, use getCanonicalName().
41
- */
42
- getNormalizedName() {
43
- return this.normalizedName;
36
+ return this.canonicalName;
44
37
  }
45
38
  /** @returns the namespace of the expected child, or the empty string if it has none. */
46
39
  getNamespace() {
@@ -57,14 +50,14 @@ class ChildDefinition {
57
50
  /** @returns the canonical name prefixed by its namespace, used as the key in {@link NodeDefinition.getChildren}. */
58
51
  getQualifiedName() {
59
52
  return this.namespace.length === 0
60
- ? this.normalizedName
61
- : `${this.namespace}:${this.normalizedName}`;
53
+ ? this.canonicalName
54
+ : `${this.namespace}:${this.canonicalName}`;
62
55
  }
63
56
  /** @returns a plain object with the definition, so that JSON.stringify serializes it. */
64
57
  toJSON() {
65
58
  return {
66
59
  name: this.getName(),
67
- normalizedName: this.getNormalizedName(),
60
+ canonicalName: this.getCanonicalName(),
68
61
  namespace: this.getNamespace(),
69
62
  min: this.getMin(),
70
63
  max: this.getMax(),
@@ -5,7 +5,7 @@ import { ChildDefinition } from "./ChildDefinition";
5
5
  */
6
6
  export declare class NodeDefinition {
7
7
  private readonly name;
8
- private readonly normalizedName;
8
+ private readonly canonicalName;
9
9
  private readonly type;
10
10
  private description;
11
11
  private readonly children;
@@ -24,11 +24,6 @@ export declare class NodeDefinition {
24
24
  getName(): string;
25
25
  /** @returns the canonical name of the node. */
26
26
  getCanonicalName(): string;
27
- /**
28
- * @returns the canonical name of the node.
29
- * @deprecated since 0.7.0, use getCanonicalName().
30
- */
31
- getNormalizedName(): string;
32
27
  /** @returns the name of the value type of this node (see {@link TypeRegistry}). */
33
28
  getType(): string;
34
29
  /** @returns the definitions of the expected children, indexed by their qualified canonical name. */
@@ -68,12 +63,12 @@ export declare class NodeDefinition {
68
63
  /** @returns a plain object with the definition, so that JSON.stringify serializes it. */
69
64
  toJSON(): {
70
65
  name: string;
71
- normalizedName: string;
66
+ canonicalName: string;
72
67
  type: string;
73
68
  description: string | undefined;
74
69
  children: {
75
70
  name: string;
76
- normalizedName: string;
71
+ canonicalName: string;
77
72
  namespace: string;
78
73
  min: number | null;
79
74
  max: number | null;
@@ -21,7 +21,7 @@ class NodeDefinition {
21
21
  this.children = new Map();
22
22
  this.values = new Set();
23
23
  this.name = StringUtils_1.StringUtils.compactSpaces(name);
24
- this.normalizedName = StringUtils_1.StringUtils.normalize(name);
24
+ this.canonicalName = StringUtils_1.StringUtils.normalize(name);
25
25
  this.type = type;
26
26
  this.description = description;
27
27
  if (!StringUtils_1.StringUtils.isValidNodeName(this.name)) {
@@ -34,14 +34,7 @@ class NodeDefinition {
34
34
  }
35
35
  /** @returns the canonical name of the node. */
36
36
  getCanonicalName() {
37
- return this.normalizedName;
38
- }
39
- /**
40
- * @returns the canonical name of the node.
41
- * @deprecated since 0.7.0, use getCanonicalName().
42
- */
43
- getNormalizedName() {
44
- return this.normalizedName;
37
+ return this.canonicalName;
45
38
  }
46
39
  /** @returns the name of the value type of this node (see {@link TypeRegistry}). */
47
40
  getType() {
@@ -113,7 +106,7 @@ class NodeDefinition {
113
106
  toJSON() {
114
107
  return {
115
108
  name: this.getName(),
116
- normalizedName: this.getNormalizedName(),
109
+ canonicalName: this.getCanonicalName(),
117
110
  type: this.getType(),
118
111
  description: this.description,
119
112
  children: Array.from(this.getChildren().values()).map(c => c.toJSON()),
@@ -15,6 +15,8 @@ export declare class Schema {
15
15
  * @throws ParseException if the namespace is not well formed.
16
16
  */
17
17
  constructor(namespace: string | null | undefined, line: number, description: string | undefined);
18
+ /** @returns the description of the schema (STXT-SCHEMA-SPEC §6.1), or undefined if it has none. */
19
+ getDescription(): string | undefined;
18
20
  /** @returns the node definitions, indexed by their canonical name. */
19
21
  getNodes(): ReadonlyMap<string, NodeDefinition>;
20
22
  /**
@@ -38,12 +40,12 @@ export declare class Schema {
38
40
  namespace: string;
39
41
  nodes: {
40
42
  name: string;
41
- normalizedName: string;
43
+ canonicalName: string;
42
44
  type: string;
43
45
  description: string | undefined;
44
46
  children: {
45
47
  name: string;
46
- normalizedName: string;
48
+ canonicalName: string;
47
49
  namespace: string;
48
50
  min: number | null;
49
51
  max: number | null;
@@ -20,6 +20,10 @@ class Schema {
20
20
  this.description = description;
21
21
  NamespaceValidator_1.NamespaceValidator.validateNamespaceFormat(this.namespace, line);
22
22
  }
23
+ /** @returns the description of the schema (STXT-SCHEMA-SPEC §6.1), or undefined if it has none. */
24
+ getDescription() {
25
+ return this.description;
26
+ }
23
27
  /** @returns the node definitions, indexed by their canonical name. */
24
28
  getNodes() {
25
29
  return this.nodes;
@@ -100,6 +100,11 @@ function createFrom(node, namespace) {
100
100
  const valuesNode = valuesNodes[0];
101
101
  const values = inline(valuesNode).getChildrenByName("value");
102
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
+ }
103
108
  result.addValue(v.getText(), v.getLine());
104
109
  }
105
110
  // For the final ENUM check
@@ -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
- const raw = (0, binaryValue_1.binaryValue)(n);
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,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). In BLOCK form, validation applies to the
5
- * concatenation of the lines of the block, ignoring line breaks, empty lines
6
- * and the leading and trailing spaces or tabs of each line.
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 inline value, or the lines of the block already concatenated.
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). In BLOCK form, validation applies to the
8
- * concatenation of the lines of the block, ignoring line breaks, empty lines
9
- * and the leading and trailing spaces or tabs of each line.
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 inline value, or the lines of the block already concatenated.
14
+ * @returns the value with no blanks, ready for the grammar of the type.
13
15
  */
14
16
  function binaryValue(node) {
15
- if (!(node instanceof TextNode_1.TextNode)) {
16
- return node.getText();
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
@@ -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
  }
@@ -1,5 +1,8 @@
1
1
  import { Node } from "../core/Node";
2
2
  import { Schema } from "../schema/Schema";
3
+ /** Namespace of the template language itself, `@stxt.template`. */
4
+ /** Namespace of the template language itself, `@stxt.template`. */
5
+ export declare const TEMPLATE_NAMESPACE = "@stxt.template";
3
6
  /**
4
7
  * Turns the tree of an already parsed `@stxt.template` document into an equivalent {@link Schema}.
5
8
  *
@@ -1,5 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.TEMPLATE_NAMESPACE = void 0;
3
4
  exports.transformTemplateNodeToSchema = transformTemplateNodeToSchema;
4
5
  const InlineNode_1 = require("../core/InlineNode");
5
6
  const Parser_1 = require("../core/Parser");
@@ -13,7 +14,8 @@ const ParseException_1 = require("../exceptions/ParseException");
13
14
  const TypeRegistry_1 = require("../schema/TypeRegistry");
14
15
  const NamespaceValidator_1 = require("../core/NamespaceValidator");
15
16
  /** Namespace of the template language itself, `@stxt.template`. */
16
- const TEMPLATE_NAMESPACE = "@stxt.template";
17
+ /** Namespace of the template language itself, `@stxt.template`. */
18
+ exports.TEMPLATE_NAMESPACE = "@stxt.template";
17
19
  /**
18
20
  * Turns the tree of an already parsed `@stxt.template` document into an equivalent {@link Schema}.
19
21
  *
@@ -25,8 +27,8 @@ const TEMPLATE_NAMESPACE = "@stxt.template";
25
27
  */
26
28
  function transformTemplateNodeToSchema(node) {
27
29
  // 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
+ if (node.getCanonicalName() !== "template" || node.getNamespace() !== exports.TEMPLATE_NAMESPACE) {
31
+ throw new ValidationException_1.ValidationException(node.getLine(), "TEMPLATE_ROOT_NOT_VALID", `Expected template(${exports.TEMPLATE_NAMESPACE}) but got ${node.getCanonicalName()}(${node.getNamespace()})`);
30
32
  }
31
33
  // The target namespace: required, and with a valid format
32
34
  const targetNamespace = StringUtils_1.StringUtils.lowerCase(node.getText());
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stxt-lang/core",
3
- "version": "0.9.1",
3
+ "version": "0.11.0",
4
4
  "description": "Parser and schema validator for STXT, an indentation-based structured-text format.",
5
5
  "main": "out/all.js",
6
6
  "types": "out/all.d.ts",