@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.
- package/dist/v1/builder/class/authoring.js +3 -2
- package/dist/v1/builder/class/input.d.ts +45 -14
- package/dist/v1/builder/index.d.ts +2 -1
- package/dist/v1/builder/lowering/class.d.ts +8 -0
- package/dist/v1/builder/lowering/class.js +40 -16
- package/dist/v1/builder/lowering/schema/compile.js +3 -3
- package/dist/v1/builder/policy/authoring.js +97 -88
- package/dist/v1/bundle/model.js +1 -1
- package/dist/v1/compiled/admission/accept.js +1 -7
- package/dist/v1/compiled/admission/semantics/class.js +2 -3
- package/dist/v1/compiled/admission/semantics/policy/parity.js +25 -13
- package/dist/v1/compiled/admission/semantics/policy/typing.js +2 -2
- package/dist/v1/compiled/admission/wire/class.js +23 -18
- package/dist/v1/compiled/admission/wire/policy.d.ts +4 -3
- package/dist/v1/compiled/admission/wire/policy.js +8 -7
- package/dist/v1/compiled/loader/load.js +1 -1
- package/dist/v1/domain/create.js +1 -1
- package/dist/v1/domain/model/input.d.ts +2 -1
- package/dist/v1/domain/projection/classes.js +1 -1
- package/dist/v1/domain/validation/create.d.ts +2 -1
- package/dist/v1/domain/validation/create.js +1 -1
- package/dist/v1/index.d.ts +1 -1
- package/dist/v1/language/.spec/schemas/draft-2020-12.schema.json +462 -0
- package/dist/v1/language/callable/resolved.d.ts +11 -2
- package/dist/v1/language/class/model/class.d.ts +17 -5
- package/dist/v1/language/freeze.d.ts +5 -0
- package/dist/v1/{schema/canonical → language}/freeze.js +4 -0
- package/dist/v1/language/policy/analysis/typing.d.ts +5 -2
- package/dist/v1/language/policy/analysis/typing.js +158 -42
- package/dist/v1/language/policy/model/policy.d.ts +44 -4
- package/dist/v1/language/policy/model/policy.js +14 -0
- package/dist/v1/language/policy/normalization/normalize.d.ts +6 -6
- package/dist/v1/language/policy/normalization/normalize.js +5 -5
- package/dist/v1/language/validation/rule.d.ts +22 -6
- package/dist/v1/language/validation/rule.js +4 -15
- package/dist/v1/language/value/evaluate/program.d.ts +2 -1
- package/dist/v1/language/value/evaluate/program.js +2 -2
- package/dist/v1/language/value/evaluate/validate.d.ts +2 -2
- package/dist/v1/language/value/evaluate/validate.js +2 -2
- package/dist/v1/language/value/ordering.d.ts +5 -0
- package/dist/v1/language/value/ordering.js +14 -0
- package/dist/v1/language/value/profile/json.validate.d.ts +3 -1
- package/dist/v1/language/value/profile/json.validate.js +3 -2
- package/dist/v1/language/value/reference/closure.js +3 -4
- package/dist/v1/language/value/reference/registry.index.d.ts +2 -0
- package/dist/v1/language/value/reference/registry.index.js +31 -15
- package/dist/v1/schema/.spec/schemas/domain-schema.schema.json +210 -27
- package/dist/v1/schema/admission/callable.js +1 -1
- package/dist/v1/schema/canonical/revision.js +4 -4
- package/dist/v1/schema/compatibility/comparators/callables.js +1 -1
- package/dist/v1/schema/compatibility/comparators/classes.js +4 -4
- package/dist/v1/schema/compatibility/comparators/members.js +1 -1
- package/dist/v1/schema/compatibility/comparators/values.js +2 -2
- package/dist/v1/schema/compatibility/report.js +1 -1
- package/dist/v1/schema/compatibility/versioning/index.js +1 -1
- package/dist/v1/schema/model/schema.js +1 -1
- package/dist/v1/schema/normalization/domain.js +9 -8
- package/dist/v1/schema/resolution/domain.js +1 -1
- package/dist/v1/schema/resolution/origins.js +3 -2
- package/dist/v1/schema/rule.d.ts +8 -14
- package/dist/v1/schema/rule.js +14 -2
- package/dist/v1/schema/validation/class.d.ts +6 -2
- package/dist/v1/schema/validation/class.js +20 -25
- package/dist/v1/schema/validation/domain.js +1 -1
- package/dist/v1/schema/validation/domain.json-schema.d.ts +2 -2
- package/dist/v1/schema/validation/domain.json-schema.js +20 -6
- package/dist/v1/schema/validation/domain.structure.d.ts +4765 -1005
- package/dist/v1/schema/validation/domain.structure.js +21 -9
- package/dist/v1/schema/validation/json.json-schema.d.ts +18 -2
- package/dist/v1/schema/validation/json.json-schema.js +14 -4
- package/dist/v1/schema/validation/policy/budgets.d.ts +1 -1
- package/dist/v1/schema/validation/policy/budgets.js +4 -4
- package/dist/v1/schema/validation/policy/context.d.ts +1 -2
- package/dist/v1/schema/validation/policy/context.js +2 -7
- package/dist/v1/schema/validation/policy/uses.d.ts +10 -2
- package/dist/v1/schema/validation/policy/uses.js +26 -13
- package/dist/v1/schema/validation/policy/validate.js +2 -3
- package/dist/v1/schema/validation/value-api.d.ts +2 -0
- package/dist/v1/schema/validation/value-api.js +2 -0
- package/dist/v1/schema/validation/value.json-schema.d.ts +7 -9
- package/dist/v1/schema/validation/value.json-schema.js +38 -27
- package/package.json +2 -1
- package/dist/v1/schema/canonical/domain.d.ts +0 -3
- package/dist/v1/schema/canonical/domain.digest.d.ts +0 -3
- package/dist/v1/schema/canonical/domain.digest.js +0 -5
- package/dist/v1/schema/canonical/domain.js +0 -5
- 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:
|
|
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
|
|
110
|
-
return {
|
|
111
|
-
|
|
112
|
-
|
|
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
|
|
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')
|
package/dist/v1/schema/rule.d.ts
CHANGED
|
@@ -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
|
-
/**
|
|
12
|
-
|
|
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
|
package/dist/v1/schema/rule.js
CHANGED
|
@@ -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
|
-
|
|
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
|
-
/**
|
|
7
|
-
|
|
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
|
|
13
|
+
import { createDomainValueSchemaRegistry } from '../resolution/value.registry.js';
|
|
14
14
|
const formatDefinitionRef = Key.of;
|
|
15
|
-
/**
|
|
16
|
-
|
|
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
|
-
(
|
|
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(
|
|
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(
|
|
48
|
+
validateUniqueProperties(registryOf, analysis, ref, declaration, pointer, diagnostics);
|
|
39
49
|
}
|
|
40
50
|
return Object.freeze(diagnostics);
|
|
41
51
|
}
|
|
42
|
-
function validateFacet(
|
|
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(
|
|
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
|
-
|
|
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():
|
|
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 = {
|
|
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(
|
|
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.
|
|
67
|
-
|
|
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
|
+
}
|