@astrale-os/kernel-dsl 1.0.0-beta.15 → 1.0.0-beta.17

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 (87) hide show
  1. package/dist/v1/builder/class/authoring.js +3 -2
  2. package/dist/v1/builder/class/input.d.ts +45 -14
  3. package/dist/v1/builder/index.d.ts +2 -1
  4. package/dist/v1/builder/lowering/class.d.ts +8 -0
  5. package/dist/v1/builder/lowering/class.js +40 -16
  6. package/dist/v1/builder/lowering/schema/compile.js +3 -3
  7. package/dist/v1/builder/policy/authoring.js +97 -88
  8. package/dist/v1/bundle/model.js +1 -1
  9. package/dist/v1/compiled/admission/accept.js +1 -7
  10. package/dist/v1/compiled/admission/semantics/class.js +2 -3
  11. package/dist/v1/compiled/admission/semantics/policy/parity.js +25 -13
  12. package/dist/v1/compiled/admission/semantics/policy/typing.js +2 -2
  13. package/dist/v1/compiled/admission/wire/class.js +23 -18
  14. package/dist/v1/compiled/admission/wire/policy.d.ts +4 -3
  15. package/dist/v1/compiled/admission/wire/policy.js +8 -7
  16. package/dist/v1/compiled/loader/load.js +1 -1
  17. package/dist/v1/domain/create.js +1 -1
  18. package/dist/v1/domain/model/input.d.ts +2 -1
  19. package/dist/v1/domain/projection/classes.js +1 -1
  20. package/dist/v1/domain/validation/create.d.ts +2 -1
  21. package/dist/v1/domain/validation/create.js +1 -1
  22. package/dist/v1/index.d.ts +1 -1
  23. package/dist/v1/language/.spec/schemas/draft-2020-12.schema.json +462 -0
  24. package/dist/v1/language/callable/resolved.d.ts +11 -2
  25. package/dist/v1/language/class/model/class.d.ts +17 -5
  26. package/dist/v1/language/freeze.d.ts +5 -0
  27. package/dist/v1/{schema/canonical → language}/freeze.js +4 -0
  28. package/dist/v1/language/policy/analysis/typing.d.ts +5 -2
  29. package/dist/v1/language/policy/analysis/typing.js +158 -42
  30. package/dist/v1/language/policy/model/policy.d.ts +44 -4
  31. package/dist/v1/language/policy/model/policy.js +14 -0
  32. package/dist/v1/language/policy/normalization/normalize.d.ts +6 -6
  33. package/dist/v1/language/policy/normalization/normalize.js +5 -5
  34. package/dist/v1/language/validation/rule.d.ts +22 -6
  35. package/dist/v1/language/validation/rule.js +4 -15
  36. package/dist/v1/language/value/evaluate/program.d.ts +2 -1
  37. package/dist/v1/language/value/evaluate/program.js +2 -2
  38. package/dist/v1/language/value/evaluate/validate.d.ts +2 -2
  39. package/dist/v1/language/value/evaluate/validate.js +2 -2
  40. package/dist/v1/language/value/ordering.d.ts +5 -0
  41. package/dist/v1/language/value/ordering.js +14 -0
  42. package/dist/v1/language/value/profile/json.validate.d.ts +3 -1
  43. package/dist/v1/language/value/profile/json.validate.js +3 -2
  44. package/dist/v1/language/value/reference/closure.js +3 -4
  45. package/dist/v1/language/value/reference/registry.index.d.ts +2 -0
  46. package/dist/v1/language/value/reference/registry.index.js +31 -15
  47. package/dist/v1/schema/.spec/schemas/domain-schema.schema.json +210 -27
  48. package/dist/v1/schema/admission/callable.js +1 -1
  49. package/dist/v1/schema/canonical/revision.js +4 -4
  50. package/dist/v1/schema/compatibility/comparators/callables.js +1 -1
  51. package/dist/v1/schema/compatibility/comparators/classes.js +4 -4
  52. package/dist/v1/schema/compatibility/comparators/members.js +1 -1
  53. package/dist/v1/schema/compatibility/comparators/values.js +2 -2
  54. package/dist/v1/schema/compatibility/report.js +1 -1
  55. package/dist/v1/schema/compatibility/versioning/index.js +1 -1
  56. package/dist/v1/schema/model/schema.js +1 -1
  57. package/dist/v1/schema/normalization/domain.js +9 -8
  58. package/dist/v1/schema/resolution/domain.js +1 -1
  59. package/dist/v1/schema/resolution/origins.js +3 -2
  60. package/dist/v1/schema/rule.d.ts +8 -14
  61. package/dist/v1/schema/rule.js +14 -2
  62. package/dist/v1/schema/validation/class.d.ts +6 -2
  63. package/dist/v1/schema/validation/class.js +20 -25
  64. package/dist/v1/schema/validation/domain.js +1 -1
  65. package/dist/v1/schema/validation/domain.json-schema.d.ts +2 -2
  66. package/dist/v1/schema/validation/domain.json-schema.js +20 -6
  67. package/dist/v1/schema/validation/domain.structure.d.ts +4765 -1005
  68. package/dist/v1/schema/validation/domain.structure.js +21 -9
  69. package/dist/v1/schema/validation/json.json-schema.d.ts +18 -2
  70. package/dist/v1/schema/validation/json.json-schema.js +14 -4
  71. package/dist/v1/schema/validation/policy/budgets.d.ts +1 -1
  72. package/dist/v1/schema/validation/policy/budgets.js +4 -4
  73. package/dist/v1/schema/validation/policy/context.d.ts +1 -2
  74. package/dist/v1/schema/validation/policy/context.js +2 -7
  75. package/dist/v1/schema/validation/policy/uses.d.ts +10 -2
  76. package/dist/v1/schema/validation/policy/uses.js +26 -13
  77. package/dist/v1/schema/validation/policy/validate.js +2 -3
  78. package/dist/v1/schema/validation/value-api.d.ts +2 -0
  79. package/dist/v1/schema/validation/value-api.js +2 -0
  80. package/dist/v1/schema/validation/value.json-schema.d.ts +7 -9
  81. package/dist/v1/schema/validation/value.json-schema.js +38 -27
  82. package/package.json +2 -1
  83. package/dist/v1/schema/canonical/domain.d.ts +0 -3
  84. package/dist/v1/schema/canonical/domain.digest.d.ts +0 -3
  85. package/dist/v1/schema/canonical/domain.digest.js +0 -5
  86. package/dist/v1/schema/canonical/domain.js +0 -5
  87. package/dist/v1/schema/canonical/freeze.d.ts +0 -1
@@ -2,6 +2,7 @@ import { json } from '@astrale-os/kernel-dsl/value';
2
2
  import { compareDefinitionRefs, compareUnicode } from '../../addressing/ordering.js';
3
3
  import { isBinaryInput } from '../../language/callable/model/callable.js';
4
4
  import { compareEdgeUniqueFields, compareEdgeUniqueTuples, compareNodeUniqueTuples, } from '../../language/class/unique.js';
5
+ import { CLASS_OPERATIONS } from '../../language/policy/model/policy.js';
5
6
  import { normalizeClassRule, normalizePolicyChecks, normalizePolicyExpression, } from '../../language/policy/normalization/normalize.js';
6
7
  import { admitStateTopology } from '../../language/state/topology.js';
7
8
  import { canonicalizeValueSchema } from '../../language/value/profile/canonicalize.js';
@@ -72,7 +73,7 @@ function canonicalizeClass(value) {
72
73
  methods: mapRecord(value.methods, canonicalizeMethod),
73
74
  ...(value.data === undefined ? {} : { data: { mediaType: value.data.mediaType } }),
74
75
  ...(value.icon === undefined ? {} : { icon: value.icon }),
75
- policies: canonicalizeNodePolicies(value.policies),
76
+ policies: canonicalizeClassPolicies(value.policies),
76
77
  ...(value.unique === undefined ? {} : { unique: canonicalizeNodeUnique(value.unique) }),
77
78
  };
78
79
  }
@@ -81,7 +82,7 @@ function canonicalizeClass(value) {
81
82
  ...base,
82
83
  orientation: 'directed',
83
84
  endpoints: canonicalizeDirectedEndpoints(value.endpoints),
84
- policies: value.policies,
85
+ policies: canonicalizeClassPolicies(value.policies),
85
86
  constraints: value.constraints,
86
87
  ...(value.unique === undefined ? {} : { unique: canonicalizeUnique(value.unique) }),
87
88
  }
@@ -89,7 +90,7 @@ function canonicalizeClass(value) {
89
90
  ...base,
90
91
  orientation: 'undirected',
91
92
  endpoints: canonicalizeIncidentEndpoints(value.endpoints),
92
- policies: value.policies,
93
+ policies: canonicalizeClassPolicies(value.policies),
93
94
  constraints: value.constraints,
94
95
  };
95
96
  }
@@ -106,11 +107,11 @@ function canonicalizeUnique(tuples) {
106
107
  .sort(compareEdgeUniqueTuples);
107
108
  }
108
109
  /** A rule keeps one spelling: normalized checks, or the Policy a lone `self` check names. */
109
- function canonicalizeNodePolicies(value) {
110
- return {
111
- ...(value.read === undefined ? {} : { read: normalizeClassRule(value.read) }),
112
- ...(value.traverse === undefined ? {} : { traverse: normalizeClassRule(value.traverse) }),
113
- };
110
+ function canonicalizeClassPolicies(value) {
111
+ return Object.fromEntries(CLASS_OPERATIONS.flatMap((operation) => {
112
+ const policy = value[operation];
113
+ return policy === undefined ? [] : [[operation, normalizeClassRule(policy)]];
114
+ }));
114
115
  }
115
116
  function canonicalizeFunction(value) {
116
117
  return {
@@ -97,7 +97,7 @@ export function callableValidation(input, output, registry, patterns) {
97
97
  const fields = inputFieldNodes(inputProgram);
98
98
  return Object.freeze({
99
99
  ...(fields === undefined ? {} : { fields: Object.freeze(Object.keys(fields)) }),
100
- validateInput: (value) => validatePreparedInstance(prepared.value, value),
100
+ validateInput: (value, options) => validatePreparedInstance(prepared.value, value, options),
101
101
  validateInputField(field, value) {
102
102
  const nodes = fields?.[field];
103
103
  return nodes === undefined
@@ -1,6 +1,6 @@
1
1
  import { compareDefinitionRefs, definitionRefIndex } from '../../addressing/ordering.js';
2
2
  import { isBinaryInput } from '../../language/callable/model/callable.js';
3
- import { isClassRuleChecks, policyVariableClass } from '../../language/policy/model/policy.js';
3
+ import { CLASS_OPERATIONS, isClassRuleChecks, policyVariableClass, } from '../../language/policy/model/policy.js';
4
4
  import { isValueObject as isJsonObject } from '../../language/value/model/object.js';
5
5
  import { walkSchema } from '../../language/value/syntax/walk.js';
6
6
  /** Collect only semantic refs, never strings or objects embedded as JSON data. */
@@ -58,7 +58,8 @@ export function collectClassRefs(declaration, add) {
58
58
  Object.values(declaration.methods).forEach((method) => collectCallableRefs(method, add));
59
59
  }
60
60
  export function collectClassPolicyRefs(declaration, add) {
61
- for (const policy of [declaration.policies.read, declaration.policies.traverse]) {
61
+ for (const operation of CLASS_OPERATIONS) {
62
+ const policy = declaration.policies[operation];
62
63
  if (isClassRuleChecks(policy))
63
64
  collectPolicyCheckRefs(policy, add);
64
65
  else if (typeof policy === 'object')
@@ -1,5 +1,5 @@
1
+ import type { DiagnosticRule } from '../language/validation/rule.js';
1
2
  import type { SchemaDiagnosticCode } from './diagnostic.js';
2
- import { type LimitKey } from '../language/limits.js';
3
3
  /**
4
4
  * What a rule reads to decide. `structure`: the shape of the document alone. `rule`: the whole
5
5
  * document. `context`: the document and its environment, so one document can be accepted in one
@@ -8,22 +8,16 @@ import { type LimitKey } from '../language/limits.js';
8
8
  * closure its acceptance retained.
9
9
  */
10
10
  export type SchemaRuleStage = 'structure' | 'rule' | 'context';
11
- /** The rule one Schema diagnostic code reports. */
12
- export interface SchemaRule {
11
+ /**
12
+ * The rule one Schema diagnostic code reports. A final `{/path*}` of its positions stands for any
13
+ * further tokens; `{endpoint}` is `source`, `target`, `0` or `1`; `{count}` is `outgoing`,
14
+ * `incoming` or `incident`; `{operation}` is `read` or `traverse`; `{key}` is a Property key. A
15
+ * condition is a fragment such as `#/$defs/edge-class/if`.
16
+ */
17
+ export interface SchemaRule extends DiagnosticRule {
13
18
  readonly stage: SchemaRuleStage;
14
- /**
15
- * The positions the rule constrains, which its diagnostics point at: JSON Pointer templates in
16
- * which a `{name}` token stands for one token of the kind it names and a final `{/path*}` for any
17
- * further tokens. `{endpoint}` is `source`, `target`, `0` or `1`; `{count}` is `outgoing`,
18
- * `incoming` or `incident`; `{operation}` is `read` or `traverse`; `{key}` is a Property key.
19
- */
20
- readonly positions: readonly [string, ...string[]];
21
- /** The rule, in one plain sentence. */
22
- readonly statement: string;
23
19
  /** What the feature the rule guards enforces on data or on calls, where it enforces something. */
24
20
  readonly enforces?: string;
25
- /** The limits of `limits` the rule enforces, by key, where it enforces some. */
26
- readonly limits?: readonly [LimitKey, ...LimitKey[]];
27
21
  }
28
22
  /**
29
23
  * Every Schema diagnostic code with the rule it reports. A code added to the DSL does not compile
@@ -1,6 +1,6 @@
1
1
  import { defaultLimits } from '@astrale-os/kernel-dsl/value';
2
+ import { deepFreeze } from '../language/freeze.js';
2
3
  import { limits } from '../language/limits.js';
3
- import { deepFreeze } from './canonical/freeze.js';
4
4
  const ANY = ['{/path*}'];
5
5
  const CHECKS = [
6
6
  '/classes/{class}/policies/{operation}{/path*}',
@@ -38,7 +38,8 @@ export const RULES = deepFreeze({
38
38
  },
39
39
  DV_CALLABLE_IO: {
40
40
  stage: 'rule',
41
- positions: [...at('/input/mode'), ...at('/output/mode')],
41
+ // A Method takes a value input only: its input has no mode.
42
+ positions: ['/functions/{function}/input/mode', ...at('/output/mode')],
42
43
  statement: 'A durable Function or Method takes a value input and returns a value output.',
43
44
  },
44
45
  DV_CLASS_ABSTRACT: {
@@ -185,6 +186,7 @@ export const RULES = deepFreeze({
185
186
  stage: 'structure',
186
187
  positions: ANY,
187
188
  statement: `The document is portable JSON: as text, UTF-8 that parses, within ${count(defaultLimits.maxBytes)} bytes, with no byte-order mark and no repeated member name; as a value, null, booleans, finite numbers other than negative zero, strings, arrays and plain objects, with no string or member name that holds U+0000 or a lone surrogate or exceeds ${count(defaultLimits.maxStringBytes)} bytes of UTF-8, at most ${count(defaultLimits.maxDepth)} nested arrays and objects, and at most ${count(defaultLimits.maxMembers)} members and items in all.`,
189
+ conditions: ['#/$defs/portable-string/not'],
188
190
  },
189
191
  DV_METHOD_ABSTRACT: {
190
192
  stage: 'rule',
@@ -247,6 +249,7 @@ export const RULES = deepFreeze({
247
249
  ...at('/output/item{/path*}'),
248
250
  ],
249
251
  statement: 'Every embedded value schema is in the value language: its diagnostic names the refusal in `valueCode`, and the schema of an automatic or State Property is not a blob.',
252
+ conditions: ['#/$defs/node-property-definition/allOf/0/if'],
250
253
  },
251
254
  PL_BUDGET: {
252
255
  stage: 'rule',
@@ -307,16 +310,19 @@ export const RULES = deepFreeze({
307
310
  stage: 'structure',
308
311
  positions: ['/functions/{function}/input'],
309
312
  statement: 'A Function input in binary mode is a binary input: it declares its metadata and maximumBytes.',
313
+ conditions: ['#/$defs/function-definition/properties/input/anyOf/1/not'],
310
314
  },
311
315
  STRUCTURE_CALLABLE_POLICY_AUTH: {
312
316
  stage: 'structure',
313
317
  positions: at('/policy'),
314
318
  statement: 'Only an authorized Function or Method declares a Policy.',
319
+ conditions: ['#/$defs/function-definition/if', '#/$defs/method-definition/if'],
315
320
  },
316
321
  STRUCTURE_DEPENDENCY_ALIAS: {
317
322
  stage: 'structure',
318
323
  positions: ['/dependencies/{alias}'],
319
324
  statement: 'A dependency alias is neither prototype nor constructor.',
325
+ conditions: ['#/$defs/dependency-alias/not'],
320
326
  },
321
327
  STRUCTURE_DOMAIN_NAME: {
322
328
  stage: 'structure',
@@ -338,6 +344,7 @@ export const RULES = deepFreeze({
338
344
  stage: 'structure',
339
345
  positions: ['/classes/{class}/properties/{property}/schema/x-astrale-value-kind'],
340
346
  statement: 'The schema of an Edge Property is not a blob: only a Node holds Blob contents.',
347
+ conditions: ['#/$defs/edge-property-definition/properties/schema/not'],
341
348
  },
342
349
  STRUCTURE_EDGE_ORIENTATION: {
343
350
  stage: 'structure',
@@ -348,6 +355,7 @@ export const RULES = deepFreeze({
348
355
  ],
349
356
  statement: 'A directed Edge Class has source and target endpoints and an undirected one two incident endpoints, and only a directed one declares unique tuples or is acyclic.',
350
357
  enforces: 'No Edges of an acyclic Class form a directed cycle.',
358
+ conditions: ['#/$defs/edge-class/if'],
351
359
  },
352
360
  STRUCTURE_FORMAT: {
353
361
  stage: 'structure',
@@ -384,6 +392,10 @@ export const RULES = deepFreeze({
384
392
  stage: 'structure',
385
393
  positions: ANY,
386
394
  statement: 'No object of the document has a member named __proto__, except the transitions of a State.',
395
+ conditions: [
396
+ '#/$defs/json-value/anyOf/5/propertyNames/not',
397
+ '#/$defs/value-schema/anyOf/1/propertyNames/not',
398
+ ],
387
399
  },
388
400
  STRUCTURE_REQUIRED: {
389
401
  stage: 'structure',
@@ -1,10 +1,14 @@
1
1
  import type { ClassRef } from '../../addressing/index.js';
2
2
  import type { ClassDefinition } from '../../language/class/model/class.js';
3
+ import type { ValueSchemaRegistry } from '../../language/value/reference/registry.js';
3
4
  import type { DefinitionAnalysisResult } from '../analysis/class.js';
4
5
  import type { SchemaDiagnostic } from '../diagnostic.js';
5
6
  import type { DomainResolutionContext } from '../resolution/context.js';
6
- /** Validate the semantic contracts owned by Class declarations. */
7
- export declare function validateDefinitions(environment: DomainResolutionContext, analysis: DefinitionAnalysisResult): readonly SchemaDiagnostic[];
7
+ /**
8
+ * Validate the semantic contracts owned by Class declarations. `registry` is the candidate's value
9
+ * registry; each dependency's is built at most once, so the Classes share them.
10
+ */
11
+ export declare function validateDefinitions(environment: DomainResolutionContext, analysis: DefinitionAnalysisResult, registry: ValueSchemaRegistry): readonly SchemaDiagnostic[];
8
12
  /** Whether `child` safely refines its parent Edge contract `parent`. */
9
13
  export declare function edgeContractRefines(environment: DomainResolutionContext, child: Extract<ClassDefinition, {
10
14
  readonly kind: 'edge';
@@ -10,24 +10,34 @@ import { isBlobValueSchema } from '../../language/value/model/blob.js';
10
10
  import { classifyOrdering } from '../../language/value/ordering.js';
11
11
  import { escapePointerToken } from '../../language/value/syntax/pointer.js';
12
12
  import { diagnostic } from '../diagnostic.js';
13
- import { createDomainValueSchemaRegistry, createValueSchemaRegistry, } from '../resolution/value.registry.js';
13
+ import { createDomainValueSchemaRegistry } from '../resolution/value.registry.js';
14
14
  const formatDefinitionRef = Key.of;
15
- /** Validate the semantic contracts owned by Class declarations. */
16
- export function validateDefinitions(environment, analysis) {
15
+ /**
16
+ * Validate the semantic contracts owned by Class declarations. `registry` is the candidate's value
17
+ * registry; each dependency's is built at most once, so the Classes share them.
18
+ */
19
+ export function validateDefinitions(environment, analysis, registry) {
17
20
  const diagnostics = [...analysis.diagnostics];
18
21
  const schema = environment.candidate;
19
22
  let scope;
23
+ const registries = new Map([[schema.origin, registry]]);
24
+ const registryOf = (origin) => {
25
+ if (!registries.has(origin)) {
26
+ const dependency = environment.dependencies.get(origin);
27
+ registries.set(origin, dependency === undefined ? undefined : createDomainValueSchemaRegistry(dependency));
28
+ }
29
+ return registries.get(origin);
30
+ };
20
31
  for (const [name, declaration] of Object.entries(schema.classes)) {
21
32
  const ref = { origin: schema.origin, kind: 'class', name };
22
33
  const pointer = `/classes/${escapePointerToken(name)}`;
23
34
  if (!equalDefinitionRefs(declaration.ref, ref)) {
24
35
  diagnostics.push(diagnostic('DV_REF_RESOLVES', `${pointer}/ref`, `Class ref must be exactly ${formatDefinitionRef(ref)}.`, formatDefinitionRef(declaration.ref)));
25
36
  }
26
- if (declaration.abstract &&
27
- (declaration.policies.read !== undefined || declaration.policies.traverse !== undefined)) {
28
- diagnostics.push(diagnostic('DV_CLASS_ABSTRACT', `${pointer}/policies`, 'Abstract Classes cannot declare observation Policies.'));
37
+ if (declaration.abstract && Object.keys(declaration.policies).length > 0) {
38
+ diagnostics.push(diagnostic('DV_CLASS_ABSTRACT', `${pointer}/policies`, 'Abstract Classes cannot declare Class Policies.'));
29
39
  }
30
- validateFacet(environment, declaration, pointer, diagnostics);
40
+ validateFacet(registry, declaration, pointer, diagnostics);
31
41
  validateParents(environment, ref, declaration.kind, declaration.extends, diagnostics);
32
42
  if (declaration.kind === 'edge') {
33
43
  validateEdgeContract(environment, ref, declaration, pointer, diagnostics);
@@ -35,12 +45,11 @@ export function validateDefinitions(environment, analysis) {
35
45
  validateEdgeLifetimes(scope, ref, declaration, pointer, diagnostics);
36
46
  validateEdgeClassComposition(environment, ref, declaration, pointer, diagnostics);
37
47
  }
38
- validateUniqueProperties(environment, analysis, ref, declaration, pointer, diagnostics);
48
+ validateUniqueProperties(registryOf, analysis, ref, declaration, pointer, diagnostics);
39
49
  }
40
50
  return Object.freeze(diagnostics);
41
51
  }
42
- function validateFacet(environment, declaration, pointer, diagnostics) {
43
- const valueRegistry = createValueSchemaRegistry(environment);
52
+ function validateFacet(valueRegistry, declaration, pointer, diagnostics) {
44
53
  for (const [name, property] of Object.entries(declaration.properties)) {
45
54
  const propertyPointer = `${pointer}/properties/${escapePointerToken(name)}`;
46
55
  // The structure refuses a blob on an Edge Property and a State Property on an Edge Class.
@@ -147,7 +156,7 @@ function validateEdgeContract(environment, owner, declaration, pointer, diagnost
147
156
  * inherit without redefinition, so the key reads one meaning on every descendant it covers. A Node
148
157
  * tuple names Properties only; an Edge tuple names them as `{ property }` beside its other fields.
149
158
  */
150
- function validateUniqueProperties(environment, analysis, owner, declaration, pointer, diagnostics) {
159
+ function validateUniqueProperties(registryOf, analysis, owner, declaration, pointer, diagnostics) {
151
160
  if (declaration.kind === 'edge' && declaration.orientation !== 'directed')
152
161
  return;
153
162
  if (declaration.unique === undefined)
@@ -156,20 +165,6 @@ function validateUniqueProperties(environment, analysis, owner, declaration, poi
156
165
  if (properties === undefined)
157
166
  return;
158
167
  const code = declaration.kind === 'node' ? 'DV_NODE_UNIQUE' : 'DV_EDGE_UNIQUE';
159
- const registries = new Map();
160
- const registryOf = (origin) => {
161
- if (!registries.has(origin)) {
162
- const schema = environment.dependencies.get(origin);
163
- const registry = origin === environment.candidate.origin
164
- ? createValueSchemaRegistry(environment)
165
- : schema === undefined
166
- ? undefined
167
- : createDomainValueSchemaRegistry(schema);
168
- if (registry !== undefined)
169
- registries.set(origin, registry);
170
- }
171
- return registries.get(origin);
172
- };
173
168
  const check = (key, at) => {
174
169
  const property = properties.get(key);
175
170
  if (property === undefined) {
@@ -15,7 +15,7 @@ export function validateDomainSchemaSemantics(schema, lookup) {
15
15
  const diagnostics = [
16
16
  ...context.diagnostics,
17
17
  ...validateEmbeddedValueSchemas(schema, registry),
18
- ...validateDefinitions(context, analysis),
18
+ ...validateDefinitions(context, analysis, registry),
19
19
  ...validateContracts(schema, registry),
20
20
  ...validatePolicies(context, analysis),
21
21
  ...validateViews(context),
@@ -1,5 +1,5 @@
1
1
  import { type core as zodCore } from 'zod';
2
- export type JsonSchemaDocument = zodCore.JSONSchema.JSONSchema;
2
+ import { type JsonSchema } from './json.json-schema.js';
3
3
  /**
4
4
  * The JSON Schema of an authored Domain schema document, rendered from the structure the Kernel
5
5
  * enforces. Zod derives what it reads; `documentJsonSchema` states each refinement it cannot read,
@@ -7,7 +7,7 @@ export type JsonSchemaDocument = zodCore.JSONSchema.JSONSchema;
7
7
  * rendering, so that no check of the structure is dropped silently. Every value schema is in the
8
8
  * published dialect and states the profile rules JSON Schema can state; the others are named.
9
9
  */
10
- export declare function domainSchemaJsonSchema(): JsonSchemaDocument;
10
+ export declare function domainSchemaJsonSchema(): JsonSchema;
11
11
  /**
12
12
  * Every refinement and custom part of the structure carries its statement in `documentJsonSchema`:
13
13
  * the keywords that state it, or its named rule. Only parts zod renders take part.
@@ -3,10 +3,14 @@ import { VALUE_RULES } from '../../language/validation/rule.js';
3
3
  import { escapePointerToken } from '../../language/value/syntax/pointer.js';
4
4
  import { RULES } from '../rule.js';
5
5
  import { domainSchemaStructure, documentJsonSchema, structureRules, } from './domain.structure.js';
6
- import { jsonValueJsonSchema, jsonValueRules } from './json.json-schema.js';
6
+ import { jsonValueJsonSchema, jsonValueRules, } from './json.json-schema.js';
7
7
  import { valueSchemaJsonSchema, valueSchemaRules, } from './value.json-schema.js';
8
8
  /** The checks the published document names, each with its stage and the positions it constrains. */
9
- const namedRules = { ...structureRules, ...jsonValueRules, ...valueSchemaRules };
9
+ const namedRules = {
10
+ ...structureRules,
11
+ ...jsonValueRules,
12
+ ...valueSchemaRules,
13
+ };
10
14
  /**
11
15
  * The JSON Schema of an authored Domain schema document, rendered from the structure the Kernel
12
16
  * enforces. Zod derives what it reads; `documentJsonSchema` states each refinement it cannot read,
@@ -55,7 +59,7 @@ export function domainSchemaJsonSchema() {
55
59
  'node-property-definition': { ...nodeProperty, allOf: [values.nodeProperty] },
56
60
  ...json.definitions,
57
61
  ...values.definitions,
58
- }).sort(([left], [right]) => (left < right ? -1 : left > right ? 1 : 0)));
62
+ }).sort(byName));
59
63
  const document = { ...root, allOf: [json.root] };
60
64
  const positions = rulePositions({ ...document, $defs: definitions });
61
65
  return {
@@ -63,8 +67,9 @@ export function domainSchemaJsonSchema() {
63
67
  $id: 'https://schemas.astrale.ai/dsl/v1/domain-schema',
64
68
  title: 'V1 DomainSchema',
65
69
  $comment: 'Generated from the structure the Kernel enforces; value schemas are in the dialect https://schemas.astrale.ai/dsl/v1/value-schema. Each check JSON Schema cannot state is named under x-astrale-rules, with its stage and the positions it constrains.',
66
- 'x-astrale-rules': Object.fromEntries(Object.keys(namedRules).sort().map((name) => {
67
- const { statement, ...rule } = namedRules[name];
70
+ 'x-astrale-rules': Object.fromEntries(Object.entries(namedRules)
71
+ .sort(byName)
72
+ .map(([name, { statement, ...rule }]) => {
68
73
  const named = { ...rule, stage: stageOf(rule), statement };
69
74
  return [name, { ...named, positions: positions.get(name) ?? [] }];
70
75
  })),
@@ -168,8 +173,11 @@ function rulePositions(document) {
168
173
  const schema = value;
169
174
  const rules = schema['x-astrale-rule'];
170
175
  if (rules !== undefined) {
171
- for (const rule of [rules].flat())
176
+ for (const rule of [rules].flat()) {
177
+ if (!isNamedRule(rule))
178
+ throw new Error(`The marker ${String(rule)} names no rule.`);
172
179
  note(rule, pointer);
180
+ }
173
181
  delete schema['x-astrale-rule'];
174
182
  }
175
183
  if (typeof schema.maxLength === 'number')
@@ -181,3 +189,9 @@ function rulePositions(document) {
181
189
  visit(document, '');
182
190
  return positions;
183
191
  }
192
+ function isNamedRule(rule) {
193
+ return typeof rule === 'string' && Object.hasOwn(namedRules, rule);
194
+ }
195
+ function byName([left], [right]) {
196
+ return left < right ? -1 : left > right ? 1 : 0;
197
+ }