@astrale-os/kernel-dsl 1.0.0-beta.13 → 1.0.0-beta.14

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 (66) hide show
  1. package/dist/v1/addressing/key.js +2 -1
  2. package/dist/v1/addressing/patterns.d.ts +1 -0
  3. package/dist/v1/addressing/patterns.js +2 -1
  4. package/dist/v1/builder/class/property.d.ts +3 -9
  5. package/dist/v1/builder/class/property.js +1 -0
  6. package/dist/v1/builder/lowering/class.js +5 -1
  7. package/dist/v1/builder/lowering/policy.js +1 -0
  8. package/dist/v1/builder/policy/authoring.js +3 -2
  9. package/dist/v1/bundle/codec.js +2 -2
  10. package/dist/v1/bundle/diagnostic.d.ts +2 -0
  11. package/dist/v1/bundle/diagnostic.js +6 -0
  12. package/dist/v1/bundle/structure.js +2 -2
  13. package/dist/v1/domain/validation/create.d.ts +4 -1
  14. package/dist/v1/domain/validation/create.js +21 -34
  15. package/dist/v1/language/.spec/schemas/value-schema.schema.json +22 -0
  16. package/dist/v1/language/class/model/class.d.ts +4 -4
  17. package/dist/v1/language/class/model/edge.d.ts +2 -2
  18. package/dist/v1/language/class/model/property.d.ts +16 -7
  19. package/dist/v1/language/definition/metadata.d.ts +5 -0
  20. package/dist/v1/language/definition/metadata.js +7 -0
  21. package/dist/v1/language/policy/model/policy.d.ts +5 -5
  22. package/dist/v1/language/policy/normalization/normalize.js +17 -16
  23. package/dist/v1/language/validation/diagnostic.d.ts +1 -1
  24. package/dist/v1/language/validation/rule.d.ts +26 -0
  25. package/dist/v1/language/validation/rule.js +116 -0
  26. package/dist/v1/language/value/profile/meta-schema.compiled.js +4 -1
  27. package/dist/v1/language/value/profile/rules.js +13 -37
  28. package/dist/v1/language/value/profile/vocabulary.d.ts +35 -2
  29. package/dist/v1/language/value/profile/vocabulary.js +52 -5
  30. package/dist/v1/language/value/reference/registry.index.js +2 -1
  31. package/dist/v1/language/value/syntax/keywords.d.ts +3 -3
  32. package/dist/v1/language/value/syntax/keywords.js +4 -20
  33. package/dist/v1/language/value/syntax/node-path/validate.js +2 -3
  34. package/dist/v1/schema/.spec/schemas/domain-schema.schema.json +2336 -0
  35. package/dist/v1/schema/analysis/class.js +33 -15
  36. package/dist/v1/schema/compatibility/api.d.ts +1 -1
  37. package/dist/v1/schema/compatibility/coordinate.js +3 -3
  38. package/dist/v1/schema/compatibility/model.d.ts +5 -0
  39. package/dist/v1/schema/diagnostic.d.ts +17 -3
  40. package/dist/v1/schema/diagnostic.js +11 -0
  41. package/dist/v1/schema/index.d.ts +3 -1
  42. package/dist/v1/schema/index.js +1 -0
  43. package/dist/v1/schema/model/schema.d.ts +19 -1
  44. package/dist/v1/schema/normalization/domain.js +1 -0
  45. package/dist/v1/schema/resolution/closure.js +2 -1
  46. package/dist/v1/schema/resolution/context.js +3 -1
  47. package/dist/v1/schema/rule.d.ts +29 -0
  48. package/dist/v1/schema/rule.js +415 -0
  49. package/dist/v1/schema/validation/callable.d.ts +4 -1
  50. package/dist/v1/schema/validation/callable.js +10 -1
  51. package/dist/v1/schema/validation/class.js +36 -44
  52. package/dist/v1/schema/validation/core.js +3 -3
  53. package/dist/v1/schema/validation/domain.json-schema.d.ts +15 -0
  54. package/dist/v1/schema/validation/domain.json-schema.js +183 -0
  55. package/dist/v1/schema/validation/domain.structure.d.ts +4018 -0
  56. package/dist/v1/schema/validation/domain.structure.js +341 -151
  57. package/dist/v1/schema/validation/json.json-schema.d.ts +24 -0
  58. package/dist/v1/schema/validation/json.json-schema.js +36 -0
  59. package/dist/v1/schema/validation/policy/references.js +5 -21
  60. package/dist/v1/schema/validation/policy/uses.js +0 -3
  61. package/dist/v1/schema/validation/value-api.d.ts +12 -1
  62. package/dist/v1/schema/validation/value-api.js +10 -1
  63. package/dist/v1/schema/validation/value.js +3 -3
  64. package/dist/v1/schema/validation/value.json-schema.d.ts +51 -0
  65. package/dist/v1/schema/validation/value.json-schema.js +126 -0
  66. package/package.json +3 -1
@@ -0,0 +1,116 @@
1
+ /**
2
+ * Every value diagnostic code with the rule it reports. A code added to the language does not
3
+ * compile until it is described here.
4
+ */
5
+ export const VALUE_RULES = frozen({
6
+ CALLABLE_INPUT_FIELD_UNKNOWN: {
7
+ stage: 'value',
8
+ positions: [''],
9
+ statement: 'An input field validated on its own is a field of the Callable input, which has fields only when it is a closed object.',
10
+ },
11
+ DOMAIN_CLASS_ABSTRACT: {
12
+ stage: 'value',
13
+ positions: ['/class'],
14
+ statement: 'Properties are validated for a concrete Class only.',
15
+ },
16
+ DOMAIN_PROPERTY_INPUT_INVALID: {
17
+ stage: 'value',
18
+ positions: ['/properties'],
19
+ statement: 'The Properties of a Class are a canonical map of Property keys to portable values.',
20
+ },
21
+ DOMAIN_PROPERTY_REQUIRED: {
22
+ stage: 'value',
23
+ positions: ['/properties/{key}'],
24
+ statement: 'Every required Property of the Class has a value.',
25
+ },
26
+ DOMAIN_PROPERTY_UNKNOWN: {
27
+ stage: 'value',
28
+ positions: ['/properties/{key}'],
29
+ statement: 'Every Property given a value is a Property of the Class, its own or inherited.',
30
+ },
31
+ VALUE_SCHEMA_INSTANCE_INVALID: {
32
+ stage: 'value',
33
+ positions: ['{/path*}'],
34
+ statement: 'A value satisfies its complete value schema.',
35
+ },
36
+ VS_DIALECT: {
37
+ stage: 'schema',
38
+ positions: ['{/path*}/$schema'],
39
+ statement: 'A value schema names $schema at its root only, and there names the dialect https://schemas.astrale.ai/dsl/v1/value-schema.',
40
+ },
41
+ VS_DYNAMIC_FORBIDDEN: {
42
+ stage: 'schema',
43
+ positions: [
44
+ '{/path*}/$anchor',
45
+ '{/path*}/$dynamicAnchor',
46
+ '{/path*}/$dynamicRef',
47
+ '{/path*}/$vocabulary',
48
+ ],
49
+ statement: 'A value schema names none of $anchor, $dynamicAnchor, $dynamicRef and $vocabulary.',
50
+ },
51
+ VS_KEYWORD_CLOSED: {
52
+ stage: 'schema',
53
+ positions: ['{/path*}/{keyword}'],
54
+ statement: 'Every keyword of a value schema is one of the closed V1 vocabulary.',
55
+ },
56
+ VS_META_SCHEMA: {
57
+ stage: 'schema',
58
+ positions: ['{/path*}'],
59
+ statement: 'A value schema is JSON valid under the meta-schema of the dialect, with fullMatch a string and no two equal values in an enum, at every location.',
60
+ },
61
+ VS_NUMBER_DOMAIN: {
62
+ stage: 'value',
63
+ positions: ['{/path*}'],
64
+ statement: 'A value, like a value schema, is portable JSON: its numbers are finite and not negative zero, its strings well formed, and its size within the value limits.',
65
+ },
66
+ VS_PATH_EXTENSION: {
67
+ stage: 'schema',
68
+ positions: ['{/path*}/x-astrale-path{/path*}', '{/path*}'],
69
+ statement: 'x-astrale-path annotates only a direct Property of a callable input object, beside exactly the $ref of the graph NodePath schema, and reads target node, cardinality one and a non-empty set of Node Class refs it accepts.',
70
+ },
71
+ VS_RECURSION_PRODUCTIVE: {
72
+ stage: 'schema',
73
+ positions: ['{/path*}'],
74
+ statement: 'Every reference cycle of a value schema crosses a keyword that steps into the value: to a member, a member name or an item.',
75
+ },
76
+ VS_REFERENCE_FORM: {
77
+ stage: 'schema',
78
+ positions: ['{/path*}/$ref'],
79
+ statement: 'A $ref is a JSON Pointer fragment into its own value schema, or the id of the graph NodePath schema beside x-astrale-path.',
80
+ },
81
+ VS_REFERENCE_RESOLUTION: {
82
+ stage: 'context',
83
+ positions: [
84
+ '{/path*}/$ref',
85
+ '{/path*}/x-astrale-path/accepts/{index}',
86
+ '',
87
+ '/registry{/path*}',
88
+ ],
89
+ statement: 'A $ref resolves to a location of its own value schema, x-astrale-path accepts Node Classes of the Domain or of a direct dependency, and the schema is checked with the exact dependency closure of its Domain.',
90
+ },
91
+ VS_REGEX_PORTABLE: {
92
+ stage: 'schema',
93
+ positions: ['{/path*}/pattern', '{/path*}/fullMatch', '{/path*}/patternProperties/{pattern}'],
94
+ statement: 'Every pattern, fullMatch and patternProperties name is an RFC 9485 I-Regexp within the regular-expression limits.',
95
+ },
96
+ VS_RESOURCE_ID: {
97
+ stage: 'schema',
98
+ positions: ['{/path*}/$id'],
99
+ statement: 'A value schema declares no $id: it is embedded, not a resource of its own.',
100
+ },
101
+ VS_VALUE_KIND: {
102
+ stage: 'schema',
103
+ positions: ['{/path*}/x-astrale-value-kind'],
104
+ statement: 'x-astrale-value-kind declares instant beside type string at any location, or blob as the whole schema of a Property, exactly as values.blob writes it.',
105
+ },
106
+ VS_VALUE_KIND_JSON: {
107
+ stage: 'warning',
108
+ positions: ['/classes/{class}/properties/{property}'],
109
+ statement: 'A Property of value kind json is filtered by equality only, so its author is warned.',
110
+ },
111
+ });
112
+ function frozen(rules) {
113
+ for (const rule of Object.values(rules))
114
+ Object.freeze(Object.freeze(rule).positions);
115
+ return Object.freeze(rules);
116
+ }
@@ -4,7 +4,7 @@
4
4
  "use strict";
5
5
  export const validate = validate20;
6
6
  export default validate20;
7
- const schema31 = { "$schema": "https://json-schema.org/draft/2020-12/schema", "$id": "https://schemas.astrale.ai/dsl/v1/value-schema", "$vocabulary": {}, "allOf": [{ "$ref": "https://json-schema.org/draft/2020-12/schema" }], "properties": { "fullMatch": { "type": "string" }, "x-astrale-path": { "type": "object" } } };
7
+ const schema31 = { "$schema": "https://json-schema.org/draft/2020-12/schema", "$id": "https://schemas.astrale.ai/dsl/v1/value-schema", "$dynamicAnchor": "meta", "$vocabulary": { "https://json-schema.org/draft/2020-12/vocab/core": true, "https://json-schema.org/draft/2020-12/vocab/applicator": true, "https://json-schema.org/draft/2020-12/vocab/unevaluated": true, "https://json-schema.org/draft/2020-12/vocab/validation": true, "https://json-schema.org/draft/2020-12/vocab/meta-data": true, "https://json-schema.org/draft/2020-12/vocab/format-annotation": true, "https://json-schema.org/draft/2020-12/vocab/content": true, "https://schemas.astrale.ai/dsl/v1/value-schema/vocabulary": true }, "title": "Astrale V1 value schema", "$comment": "The meta-schema of the V1 value-schema dialect: Draft 2020-12 and the required Astrale vocabulary (fullMatch, x-astrale-path, x-astrale-value-kind). Its $dynamicAnchor extends Draft 2020-12 at every subschema, not only the root. The rules of the profile beyond it are published as schema.values.RULES.", "allOf": [{ "$ref": "https://json-schema.org/draft/2020-12/schema" }], "properties": { "fullMatch": { "type": "string" }, "x-astrale-path": { "type": "object" } } };
8
8
  const schema0 = { "$schema": "https://json-schema.org/draft/2020-12/schema", "$id": "https://json-schema.org/draft/2020-12/schema", "$vocabulary": { "https://json-schema.org/draft/2020-12/vocab/core": true, "https://json-schema.org/draft/2020-12/vocab/applicator": true, "https://json-schema.org/draft/2020-12/vocab/unevaluated": true, "https://json-schema.org/draft/2020-12/vocab/validation": true, "https://json-schema.org/draft/2020-12/vocab/meta-data": true, "https://json-schema.org/draft/2020-12/vocab/format-annotation": true, "https://json-schema.org/draft/2020-12/vocab/content": true }, "$dynamicAnchor": "meta", "title": "Core and Validation specifications meta-schema", "allOf": [{ "$ref": "meta/core" }, { "$ref": "meta/applicator" }, { "$ref": "meta/unevaluated" }, { "$ref": "meta/validation" }, { "$ref": "meta/meta-data" }, { "$ref": "meta/format-annotation" }, { "$ref": "meta/content" }], "type": ["object", "boolean"], "$comment": "This meta-schema also defines keywords that have appeared in previous drafts in order to prevent incompatible extensions as they remain in common use.", "properties": { "definitions": { "$comment": "\"definitions\" has been replaced by \"$defs\".", "type": "object", "additionalProperties": { "$dynamicRef": "#meta" }, "deprecated": true, "default": {} }, "dependencies": { "$comment": "\"dependencies\" has been split and replaced by \"dependentSchemas\" and \"dependentRequired\" in order to serve their differing semantics.", "type": "object", "additionalProperties": { "anyOf": [{ "$dynamicRef": "#meta" }, { "$ref": "meta/validation#/$defs/stringArray" }] }, "deprecated": true, "default": {} }, "$recursiveAnchor": { "$comment": "\"$recursiveAnchor\" has been replaced by \"$dynamicAnchor\".", "$ref": "meta/core#/$defs/anchorString", "deprecated": true }, "$recursiveRef": { "$comment": "\"$recursiveRef\" has been replaced by \"$dynamicRef\".", "$ref": "meta/core#/$defs/uriReferenceString", "deprecated": true } } };
9
9
  const schema23 = { "type": "array", "items": { "type": "string" }, "uniqueItems": true, "default": [] };
10
10
  const schema5 = { "type": "string", "pattern": "^[A-Za-z_][-A-Za-z0-9._]*$" };
@@ -2042,6 +2042,9 @@ function validate20(data, { instancePath = "", parentData, parentDataProperty, r
2042
2042
  if (evaluated0.dynamicItems) {
2043
2043
  evaluated0.items = undefined;
2044
2044
  }
2045
+ if (!dynamicAnchors.meta) {
2046
+ dynamicAnchors.meta = validate20;
2047
+ }
2045
2048
  if (!(validate0(data, { instancePath, parentData, parentDataProperty, rootData, dynamicAnchors }))) {
2046
2049
  vErrors = vErrors === null ? validate0.errors : vErrors.concat(validate0.errors);
2047
2050
  errors = vErrors.length;
@@ -6,7 +6,9 @@ import { VALUE_SCHEMA_DIALECT } from '../model/schema.js';
6
6
  import { refuseIRegexp, stateBudgetMessage } from '../regexp/syntax.js';
7
7
  import { escapePointerToken } from '../syntax/pointer.js';
8
8
  import { walkSchema } from '../syntax/walk.js';
9
- import { FORBIDDEN_DYNAMIC_KEYWORDS, STANDARD_SCHEMA_KEYWORDS } from './vocabulary.js';
9
+ import { FORBIDDEN_KEYWORDS, VALUE_KEYWORDS } from './vocabulary.js';
10
+ const KEYWORDS = new Set(VALUE_KEYWORDS);
11
+ const FORBIDDEN = new Set(FORBIDDEN_KEYWORDS);
10
12
  export function validateSchemaProfile(schema, context, pointer) {
11
13
  const diagnostics = [];
12
14
  walkSchema(schema, (location) => {
@@ -14,10 +16,10 @@ export function validateSchemaProfile(schema, context, pointer) {
14
16
  return;
15
17
  const currentPointer = `${pointer}${location.pointer}`;
16
18
  for (const keyword of Object.keys(location.schema)) {
17
- if (!STANDARD_SCHEMA_KEYWORDS.has(keyword)) {
19
+ if (!KEYWORDS.has(keyword)) {
18
20
  diagnostics.push(diagnostic('VS_KEYWORD_CLOSED', `${currentPointer}/${escapePointerToken(keyword)}`, `Keyword ${keyword} is outside the closed V1 Draft 2020-12 vocabulary.`));
19
21
  }
20
- if (FORBIDDEN_DYNAMIC_KEYWORDS.has(keyword)) {
22
+ if (FORBIDDEN.has(keyword)) {
21
23
  diagnostics.push(diagnostic('VS_DYNAMIC_FORBIDDEN', `${currentPointer}/${escapePointerToken(keyword)}`, `Keyword ${keyword} is forbidden by V1.`));
22
24
  }
23
25
  }
@@ -29,7 +31,11 @@ export function validateSchemaProfile(schema, context, pointer) {
29
31
  if (Object.hasOwn(location.schema, '$id')) {
30
32
  diagnostics.push(diagnostic('VS_RESOURCE_ID', `${currentPointer}/$id`, '$id is forbidden in an embedded V1 value schema.'));
31
33
  }
32
- validateSemanticSets(location.schema, currentPointer, diagnostics);
34
+ // The dialect refuses a repeated type, required or dependentRequired entry; it lets an enum
35
+ // repeat a value.
36
+ if (Array.isArray(location.schema.enum)) {
37
+ rejectCanonicalDuplicates(location.schema.enum, `${currentPointer}/enum`, diagnostics);
38
+ }
33
39
  validateValueKind(location.schema, context, location.depth, currentPointer, diagnostics);
34
40
  validateRegexes(location.schema, currentPointer, diagnostics);
35
41
  });
@@ -74,28 +80,8 @@ function isCanonicalBlobSchema(input) {
74
80
  function isString(input) {
75
81
  return typeof input === 'string';
76
82
  }
77
- function validateSemanticSets(schema, pointer, diagnostics) {
78
- if (Array.isArray(schema.type)) {
79
- rejectPrimitiveDuplicates(schema.type, `${pointer}/type`, 'type', diagnostics);
80
- }
81
- if (Array.isArray(schema.required)) {
82
- rejectPrimitiveDuplicates(schema.required, `${pointer}/required`, 'required', diagnostics);
83
- }
84
- if (Array.isArray(schema.enum)) {
85
- rejectCanonicalDuplicates(schema.enum, `${pointer}/enum`, 'enum', diagnostics);
86
- }
87
- if (isJsonObject(schema.dependentRequired)) {
88
- for (const [name, dependencies] of Object.entries(schema.dependentRequired)) {
89
- if (Array.isArray(dependencies)) {
90
- rejectPrimitiveDuplicates(dependencies, `${pointer}/dependentRequired/${escapePointerToken(name)}`, 'dependentRequired', diagnostics);
91
- }
92
- }
93
- }
94
- }
83
+ /** The dialect refuses a fullMatch that is not a string, at every location. */
95
84
  function validateRegexes(schema, pointer, diagnostics) {
96
- if (Object.hasOwn(schema, 'fullMatch') && typeof schema.fullMatch !== 'string') {
97
- diagnostics.push(diagnostic('VS_META_SCHEMA', `${pointer}/fullMatch`, 'fullMatch must be an I-Regexp source string.'));
98
- }
99
85
  if (typeof schema.fullMatch === 'string') {
100
86
  portableRegex(schema.fullMatch, `${pointer}/fullMatch`, 'fullMatch', 'fullMatch must satisfy the complete RFC 9485 I-Regexp syntax.', diagnostics);
101
87
  }
@@ -114,17 +100,7 @@ function portableRegex(source, pointer, subject, syntax, diagnostics) {
114
100
  return;
115
101
  diagnostics.push(diagnostic('VS_REGEX_PORTABLE', pointer, refusal === 'syntax' ? syntax : stateBudgetMessage(subject)));
116
102
  }
117
- function rejectPrimitiveDuplicates(values, pointer, name, diagnostics) {
118
- const seen = new Set();
119
- for (const value of values) {
120
- if (seen.has(value)) {
121
- diagnostics.push(diagnostic('VS_META_SCHEMA', pointer, `${name} is a duplicate-free semantic set.`));
122
- return;
123
- }
124
- seen.add(value);
125
- }
126
- }
127
- function rejectCanonicalDuplicates(values, pointer, name, diagnostics) {
103
+ function rejectCanonicalDuplicates(values, pointer, diagnostics) {
128
104
  const seen = new Set();
129
105
  for (const value of values) {
130
106
  let key;
@@ -135,7 +111,7 @@ function rejectCanonicalDuplicates(values, pointer, name, diagnostics) {
135
111
  continue;
136
112
  }
137
113
  if (seen.has(key)) {
138
- diagnostics.push(diagnostic('VS_META_SCHEMA', pointer, `${name} rejects canonically equal duplicates.`));
114
+ diagnostics.push(diagnostic('VS_META_SCHEMA', pointer, 'enum rejects canonically equal duplicates.'));
139
115
  return;
140
116
  }
141
117
  seen.add(key);
@@ -1,2 +1,35 @@
1
- export declare const STANDARD_SCHEMA_KEYWORDS: Set<string>;
2
- export declare const FORBIDDEN_DYNAMIC_KEYWORDS: Set<string>;
1
+ declare const keywords: readonly ['$anchor', '$comment', '$defs', '$dynamicAnchor', '$dynamicRef', '$id', '$ref', '$schema', '$vocabulary', 'additionalProperties', 'allOf', 'anyOf', 'const', 'contains', 'contentEncoding', 'contentMediaType', 'contentSchema', 'default', 'dependentRequired', 'dependentSchemas', 'deprecated', 'description', 'else', 'enum', 'examples', 'exclusiveMaximum', 'exclusiveMinimum', 'format', 'fullMatch', 'if', 'items', 'maxContains', 'maxItems', 'maxLength', 'maxProperties', 'maximum', 'minContains', 'minItems', 'minLength', 'minProperties', 'minimum', 'multipleOf', 'not', 'oneOf', 'pattern', 'patternProperties', 'prefixItems', 'properties', 'propertyNames', 'readOnly', 'required', 'then', 'title', 'type', 'unevaluatedItems', 'unevaluatedProperties', 'uniqueItems', 'writeOnly', 'x-astrale-path', 'x-astrale-value-kind'];
2
+ export type ValueKeyword = (typeof keywords)[number];
3
+ /**
4
+ * The closed V1 vocabulary: every keyword a value schema may name, the Astrale profile's included
5
+ * (`fullMatch`, `x-astrale-path`, `x-astrale-value-kind`). Any other keyword is refused
6
+ * (`VS_KEYWORD_CLOSED`). Other rules restrict some of these: the forbidden keywords never appear,
7
+ * `$id` never, `$schema` only at the root. Frozen and Unicode-ordered.
8
+ */
9
+ export declare const VALUE_KEYWORDS: readonly ValueKeyword[];
10
+ /** The keywords of the vocabulary refused wherever they appear (`VS_DYNAMIC_FORBIDDEN`). */
11
+ export declare const FORBIDDEN_KEYWORDS: readonly ValueKeyword[];
12
+ /**
13
+ * The keywords whose value holds subschemas, by its shape: one schema, an array of schemas, or an
14
+ * object of named schemas. A value schema's locations are its root and the subschemas these hold.
15
+ */
16
+ export declare const SUBSCHEMA_KEYWORDS: {
17
+ readonly single: readonly ValueKeyword[];
18
+ readonly array: readonly ValueKeyword[];
19
+ readonly map: readonly ValueKeyword[];
20
+ };
21
+ /** A value kind a schema declares with `x-astrale-value-kind`, because no JSON type gives it. */
22
+ export interface DeclaredValueKind {
23
+ /** The JSON type of its values, which the schema states beside the declaration. */
24
+ readonly type: 'string' | 'object';
25
+ /**
26
+ * Where a schema may declare it: at any location of any value schema, or only as the whole
27
+ * schema of a Node Property that is neither automatic nor a State Property.
28
+ */
29
+ readonly placement: 'any' | 'node-property';
30
+ /** The kind, in one plain sentence. */
31
+ readonly statement: string;
32
+ }
33
+ /** The value kinds `x-astrale-value-kind` declares; it admits no other value. */
34
+ export declare const DECLARED_VALUE_KINDS: Readonly<Record<'instant' | 'blob', DeclaredValueKind>>;
35
+ export {};
@@ -1,4 +1,4 @@
1
- export const STANDARD_SCHEMA_KEYWORDS = new Set([
1
+ const keywords = [
2
2
  '$anchor',
3
3
  '$comment',
4
4
  '$defs',
@@ -31,15 +31,15 @@ export const STANDARD_SCHEMA_KEYWORDS = new Set([
31
31
  'if',
32
32
  'items',
33
33
  'maxContains',
34
- 'maximum',
35
34
  'maxItems',
36
35
  'maxLength',
37
36
  'maxProperties',
37
+ 'maximum',
38
38
  'minContains',
39
- 'minimum',
40
39
  'minItems',
41
40
  'minLength',
42
41
  'minProperties',
42
+ 'minimum',
43
43
  'multipleOf',
44
44
  'not',
45
45
  'oneOf',
@@ -59,10 +59,57 @@ export const STANDARD_SCHEMA_KEYWORDS = new Set([
59
59
  'writeOnly',
60
60
  'x-astrale-path',
61
61
  'x-astrale-value-kind',
62
- ]);
63
- export const FORBIDDEN_DYNAMIC_KEYWORDS = new Set([
62
+ ];
63
+ /**
64
+ * The closed V1 vocabulary: every keyword a value schema may name, the Astrale profile's included
65
+ * (`fullMatch`, `x-astrale-path`, `x-astrale-value-kind`). Any other keyword is refused
66
+ * (`VS_KEYWORD_CLOSED`). Other rules restrict some of these: the forbidden keywords never appear,
67
+ * `$id` never, `$schema` only at the root. Frozen and Unicode-ordered.
68
+ */
69
+ export const VALUE_KEYWORDS = Object.freeze(keywords);
70
+ /** The keywords of the vocabulary refused wherever they appear (`VS_DYNAMIC_FORBIDDEN`). */
71
+ export const FORBIDDEN_KEYWORDS = Object.freeze([
64
72
  '$anchor',
65
73
  '$dynamicAnchor',
66
74
  '$dynamicRef',
67
75
  '$vocabulary',
68
76
  ]);
77
+ /**
78
+ * The keywords whose value holds subschemas, by its shape: one schema, an array of schemas, or an
79
+ * object of named schemas. A value schema's locations are its root and the subschemas these hold.
80
+ */
81
+ export const SUBSCHEMA_KEYWORDS = Object.freeze({
82
+ single: Object.freeze([
83
+ 'additionalProperties',
84
+ 'contains',
85
+ 'contentSchema',
86
+ 'else',
87
+ 'if',
88
+ 'items',
89
+ 'not',
90
+ 'propertyNames',
91
+ 'then',
92
+ 'unevaluatedItems',
93
+ 'unevaluatedProperties',
94
+ ]),
95
+ array: Object.freeze(['allOf', 'anyOf', 'oneOf', 'prefixItems']),
96
+ map: Object.freeze([
97
+ '$defs',
98
+ 'dependentSchemas',
99
+ 'patternProperties',
100
+ 'properties',
101
+ ]),
102
+ });
103
+ /** The value kinds `x-astrale-value-kind` declares; it admits no other value. */
104
+ export const DECLARED_VALUE_KINDS = Object.freeze({
105
+ instant: Object.freeze({
106
+ type: 'string',
107
+ placement: 'any',
108
+ statement: 'A UTC instant written YYYY-MM-DDTHH:mm:ss.sssZ, in the years 0000 to 9999, whose text order is its chronological order.',
109
+ }),
110
+ blob: Object.freeze({
111
+ type: 'object',
112
+ placement: 'node-property',
113
+ statement: 'The Ref of stored contents, its digest, media type and size: the schema is exactly a values.blob output, and the Property is written with an upload receipt.',
114
+ }),
115
+ });
@@ -31,8 +31,9 @@ export function indexRegistry(registry, pointer) {
31
31
  dependenciesByOwner.set(domain.origin, pins);
32
32
  for (const dependency of domain.dependencies) {
33
33
  const target = domains.get(dependency.origin);
34
+ // The schema at `pointer` cannot be judged without the closure it may reference.
34
35
  if (target === undefined || target.revision !== dependency.revision) {
35
- diagnostics.push(diagnostic('VS_REFERENCE_RESOLUTION', `${pointer}/registry/${domain.origin}/dependencies`, `Registry closure is missing exact ${dependency.origin}@${dependency.revision} required by ${domain.origin}.`));
36
+ diagnostics.push(diagnostic('VS_REFERENCE_RESOLUTION', pointer, `Registry closure is missing exact ${dependency.origin}@${dependency.revision} required by ${domain.origin}.`));
36
37
  }
37
38
  }
38
39
  }
@@ -1,3 +1,3 @@
1
- export declare const SCHEMA_ARRAY_KEYWORDS: Set<string>;
2
- export declare const SCHEMA_MAP_KEYWORDS: Set<string>;
3
- export declare const SCHEMA_SINGLE_KEYWORDS: Set<string>;
1
+ export declare const SCHEMA_ARRAY_KEYWORDS: ReadonlySet<string>;
2
+ export declare const SCHEMA_MAP_KEYWORDS: ReadonlySet<string>;
3
+ export declare const SCHEMA_SINGLE_KEYWORDS: ReadonlySet<string>;
@@ -1,20 +1,4 @@
1
- export const SCHEMA_ARRAY_KEYWORDS = new Set(['allOf', 'anyOf', 'oneOf', 'prefixItems']);
2
- export const SCHEMA_MAP_KEYWORDS = new Set([
3
- '$defs',
4
- 'dependentSchemas',
5
- 'patternProperties',
6
- 'properties',
7
- ]);
8
- export const SCHEMA_SINGLE_KEYWORDS = new Set([
9
- 'additionalProperties',
10
- 'contains',
11
- 'contentSchema',
12
- 'else',
13
- 'if',
14
- 'items',
15
- 'not',
16
- 'propertyNames',
17
- 'then',
18
- 'unevaluatedItems',
19
- 'unevaluatedProperties',
20
- ]);
1
+ import { SUBSCHEMA_KEYWORDS } from '../profile/vocabulary.js';
2
+ export const SCHEMA_ARRAY_KEYWORDS = new Set(SUBSCHEMA_KEYWORDS.array);
3
+ export const SCHEMA_MAP_KEYWORDS = new Set(SUBSCHEMA_KEYWORDS.map);
4
+ export const SCHEMA_SINGLE_KEYWORDS = new Set(SUBSCHEMA_KEYWORDS.single);
@@ -29,11 +29,10 @@ export function validateNodePathExtension(schema, context, pointer, definitionEx
29
29
  location.schema.$ref !== GRAPH_NODE_PATH_SCHEMA_ID) {
30
30
  diagnostics.push(diagnostic('VS_PATH_EXTENSION', `${pointer}${location.pointer}`, `A Path field schema contains exactly $ref ${GRAPH_NODE_PATH_SCHEMA_ID} and x-astrale-path.`, GRAPH_NODE_PATH_SCHEMA_ID));
31
31
  }
32
+ // The dialect refuses an annotation that is not an object, at every location.
32
33
  const annotation = location.schema['x-astrale-path'];
33
- if (!isJsonObject(annotation)) {
34
- diagnostics.push(diagnostic('VS_PATH_EXTENSION', extensionPointer, 'x-astrale-path must be a closed annotation object.'));
34
+ if (!isJsonObject(annotation))
35
35
  return;
36
- }
37
36
  if (!hasExactKeys(annotation, ['target', 'cardinality', 'accepts'])) {
38
37
  diagnostics.push(diagnostic('VS_PATH_EXTENSION', extensionPointer, 'x-astrale-path contains exactly target, cardinality, and accepts.'));
39
38
  }