@astrale-os/kernel-dsl 1.0.0-beta.12 → 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.
- package/dist/v1/addressing/definition-ref.js +21 -37
- package/dist/v1/addressing/key.js +2 -1
- package/dist/v1/addressing/ordering.d.ts +2 -0
- package/dist/v1/addressing/ordering.js +4 -0
- package/dist/v1/addressing/patterns.d.ts +1 -1
- package/dist/v1/addressing/patterns.js +2 -6
- package/dist/v1/builder/class/authoring.js +5 -28
- package/dist/v1/builder/class/property.d.ts +3 -9
- package/dist/v1/builder/class/property.js +1 -0
- package/dist/v1/builder/lowering/class.js +5 -1
- package/dist/v1/builder/lowering/policy.js +1 -0
- package/dist/v1/builder/lowering/schema/context.js +2 -4
- package/dist/v1/builder/lowering/value/path.js +2 -4
- package/dist/v1/builder/lowering/value/zod.js +1 -3
- package/dist/v1/builder/policy/authoring.js +3 -2
- package/dist/v1/builder/schema/define.d.ts +0 -1
- package/dist/v1/builder/schema/diagnostic.d.ts +2 -1
- package/dist/v1/builder/schema/diagnostic.js +5 -10
- package/dist/v1/builder/schema/projection.d.ts +1 -1
- package/dist/v1/builder/value/full-match.d.ts +0 -1
- package/dist/v1/builder/value/full-match.js +0 -1
- package/dist/v1/bundle/codec.js +4 -6
- package/dist/v1/bundle/diagnostic.d.ts +2 -0
- package/dist/v1/bundle/diagnostic.js +9 -7
- package/dist/v1/bundle/structure.js +4 -6
- package/dist/v1/compiled/admission/diagnostic.d.ts +1 -1
- package/dist/v1/compiled/admission/diagnostic.js +1 -3
- package/dist/v1/compiled/admission/revision.d.ts +9 -1
- package/dist/v1/compiled/admission/revision.js +6 -3
- package/dist/v1/compiled/admission/semantics/class.d.ts +4 -1
- package/dist/v1/compiled/admission/semantics/class.js +4 -8
- package/dist/v1/compiled/admission/semantics/core.d.ts +1 -24
- package/dist/v1/compiled/admission/semantics/core.js +54 -34
- package/dist/v1/compiled/admission/semantics/edge.d.ts +2 -4
- package/dist/v1/compiled/admission/semantics/edge.js +6 -65
- package/dist/v1/compiled/admission/semantics/policy/callable.js +0 -2
- package/dist/v1/compiled/admission/semantics/policy/contracts.d.ts +5 -20
- package/dist/v1/compiled/admission/semantics/policy/contracts.js +8 -21
- package/dist/v1/compiled/admission/semantics/policy/parity.js +13 -22
- package/dist/v1/compiled/admission/semantics/policy/pattern.d.ts +4 -1
- package/dist/v1/compiled/admission/semantics/policy/pattern.js +4 -16
- package/dist/v1/compiled/admission/semantics/policy/typing.d.ts +2 -24
- package/dist/v1/compiled/admission/semantics/policy/typing.js +19 -201
- package/dist/v1/compiled/compiler/compile.js +6 -8
- package/dist/v1/compiled/diagnostic.js +2 -3
- package/dist/v1/compiled/loader/load.d.ts +3 -2
- package/dist/v1/compiled/loader/load.js +10 -44
- package/dist/v1/domain/create.js +3 -5
- package/dist/v1/domain/namespace/member-collection.js +4 -6
- package/dist/v1/domain/validation/create.d.ts +4 -2
- package/dist/v1/domain/validation/create.js +21 -69
- package/dist/v1/language/.spec/schemas/value-schema.schema.json +22 -0
- package/dist/v1/language/class/inheritance.d.ts +18 -0
- package/dist/v1/language/class/inheritance.js +73 -0
- package/dist/v1/language/class/model/class.d.ts +4 -4
- package/dist/v1/language/class/model/edge.d.ts +2 -2
- package/dist/v1/language/class/model/property.d.ts +16 -7
- package/dist/v1/language/class/property-values.js +2 -4
- package/dist/v1/language/core/facts.d.ts +72 -0
- package/dist/v1/language/core/facts.js +95 -0
- package/dist/v1/language/definition/metadata.d.ts +5 -0
- package/dist/v1/language/definition/metadata.js +7 -0
- package/dist/v1/language/policy/analysis/branches.d.ts +8 -0
- package/dist/v1/language/policy/analysis/branches.js +2 -1
- package/dist/v1/language/policy/analysis/budgets.d.ts +13 -1
- package/dist/v1/language/policy/analysis/budgets.js +104 -0
- package/dist/v1/language/policy/analysis/typing.d.ts +48 -0
- package/dist/v1/language/policy/analysis/typing.js +338 -0
- package/dist/v1/language/policy/model/policy.d.ts +5 -5
- package/dist/v1/language/policy/normalization/normalize.js +17 -16
- package/dist/v1/language/validation/diagnostic.d.ts +15 -1
- package/dist/v1/language/validation/diagnostic.js +21 -0
- package/dist/v1/language/validation/rule.d.ts +26 -0
- package/dist/v1/language/validation/rule.js +116 -0
- package/dist/v1/language/value/compile/fields.js +4 -8
- package/dist/v1/language/value/profile/meta-schema.compiled.js +4 -1
- package/dist/v1/language/value/profile/rules.js +17 -43
- package/dist/v1/language/value/profile/validate.js +1 -10
- package/dist/v1/language/value/profile/vocabulary.d.ts +35 -2
- package/dist/v1/language/value/profile/vocabulary.js +52 -5
- package/dist/v1/language/value/reference/recursion.js +2 -22
- package/dist/v1/language/value/reference/registry.index.js +2 -1
- package/dist/v1/language/value/syntax/keywords.d.ts +3 -3
- package/dist/v1/language/value/syntax/keywords.js +4 -20
- package/dist/v1/language/value/syntax/node-path/validate.js +2 -3
- package/dist/v1/language/value/syntax/pointer.d.ts +2 -0
- package/dist/v1/language/value/syntax/pointer.js +5 -2
- package/dist/v1/language/value/syntax/walk.d.ts +2 -0
- package/dist/v1/language/value/syntax/walk.js +19 -20
- package/dist/v1/schema/.spec/schemas/domain-schema.schema.json +2336 -0
- package/dist/v1/schema/analysis/class.js +33 -15
- package/dist/v1/schema/compatibility/api.d.ts +1 -1
- package/dist/v1/schema/compatibility/coordinate.js +3 -3
- package/dist/v1/schema/compatibility/diff.js +2 -4
- package/dist/v1/schema/compatibility/meaning/footprint.js +16 -50
- package/dist/v1/schema/compatibility/model.d.ts +5 -0
- package/dist/v1/schema/compatibility/structure/requirements.js +26 -58
- package/dist/v1/schema/compatibility/walk.d.ts +17 -0
- package/dist/v1/schema/compatibility/walk.js +41 -0
- package/dist/v1/schema/diagnostic.d.ts +17 -3
- package/dist/v1/schema/diagnostic.js +14 -11
- package/dist/v1/schema/index.d.ts +3 -1
- package/dist/v1/schema/index.js +1 -0
- package/dist/v1/schema/model/schema.d.ts +19 -1
- package/dist/v1/schema/normalization/domain.js +1 -0
- package/dist/v1/schema/resolution/closure.js +2 -1
- package/dist/v1/schema/resolution/context.js +6 -6
- package/dist/v1/schema/rule.d.ts +29 -0
- package/dist/v1/schema/rule.js +415 -0
- package/dist/v1/schema/validation/callable.d.ts +4 -1
- package/dist/v1/schema/validation/callable.js +13 -6
- package/dist/v1/schema/validation/class.js +46 -96
- package/dist/v1/schema/validation/core.js +68 -202
- package/dist/v1/schema/validation/domain.js +1 -10
- package/dist/v1/schema/validation/domain.json-schema.d.ts +15 -0
- package/dist/v1/schema/validation/domain.json-schema.js +183 -0
- package/dist/v1/schema/validation/domain.structure.d.ts +4018 -0
- package/dist/v1/schema/validation/domain.structure.js +343 -155
- package/dist/v1/schema/validation/json.json-schema.d.ts +24 -0
- package/dist/v1/schema/validation/json.json-schema.js +36 -0
- package/dist/v1/schema/validation/policy/budgets.js +5 -112
- package/dist/v1/schema/validation/policy/context.d.ts +5 -3
- package/dist/v1/schema/validation/policy/context.js +5 -3
- package/dist/v1/schema/validation/policy/objects.d.ts +1 -1
- package/dist/v1/schema/validation/policy/objects.js +6 -14
- package/dist/v1/schema/validation/policy/references.js +10 -27
- package/dist/v1/schema/validation/policy/typing.d.ts +3 -18
- package/dist/v1/schema/validation/policy/typing.js +22 -356
- package/dist/v1/schema/validation/policy/uses.d.ts +1 -1
- package/dist/v1/schema/validation/policy/uses.js +9 -25
- package/dist/v1/schema/validation/policy/validate.js +3 -2
- package/dist/v1/schema/validation/value-api.d.ts +12 -1
- package/dist/v1/schema/validation/value-api.js +12 -3
- package/dist/v1/schema/validation/value.js +8 -10
- package/dist/v1/schema/validation/value.json-schema.d.ts +51 -0
- package/dist/v1/schema/validation/value.json-schema.js +126 -0
- package/dist/v1/schema/validation/view.js +4 -8
- package/package.json +3 -1
- package/dist/v1/compiled/admission/semantics/cardinality.d.ts +0 -5
- package/dist/v1/compiled/admission/semantics/cardinality.js +0 -128
- package/dist/v1/compiled/admission/semantics/policy/equality.d.ts +0 -8
- package/dist/v1/compiled/admission/semantics/policy/equality.js +0 -27
- package/dist/v1/compiled/admission/semantics/policy/measure.d.ts +0 -9
- package/dist/v1/compiled/admission/semantics/policy/measure.js +0 -81
- package/dist/v1/compiled/admission/semantics/policy/variants.d.ts +0 -10
- package/dist/v1/compiled/admission/semantics/policy/variants.js +0 -65
- package/dist/v1/language/class/index.d.ts +0 -8
- package/dist/v1/language/class/index.js +0 -1
- package/dist/v1/language/core/index.d.ts +0 -2
- package/dist/v1/language/core/index.js +0 -1
- package/dist/v1/language/value/index.d.ts +0 -11
- package/dist/v1/language/value/index.js +0 -8
|
@@ -1,12 +1,14 @@
|
|
|
1
|
+
import { equalDefinitionRefs } from '../../addressing/definition-ref.js';
|
|
1
2
|
import { Key } from '../../addressing/key.js';
|
|
2
3
|
import { definitionRefIndex } from '../../addressing/ordering.js';
|
|
3
4
|
import { isMethodExecutable } from '../../language/callable/executable.js';
|
|
4
|
-
import { classSatisfies as satisfiesClass,
|
|
5
|
+
import { classSatisfies as satisfiesClass, edgeContractRefines as refinesEdgeContract, } from '../../language/class/inheritance.js';
|
|
5
6
|
import { lifetimeBreaches, lifetimeScope } from '../../language/class/lifetime.js';
|
|
6
7
|
import { keyableProperty } from '../../language/class/unique.js';
|
|
7
8
|
import { admitStateTopology } from '../../language/state/topology.js';
|
|
8
9
|
import { isBlobValueSchema } from '../../language/value/model/blob.js';
|
|
9
10
|
import { classifyOrdering } from '../../language/value/ordering.js';
|
|
11
|
+
import { escapePointerToken } from '../../language/value/syntax/pointer.js';
|
|
10
12
|
import { diagnostic } from '../diagnostic.js';
|
|
11
13
|
import { createDomainValueSchemaRegistry, createValueSchemaRegistry, } from '../resolution/value.registry.js';
|
|
12
14
|
const formatDefinitionRef = Key.of;
|
|
@@ -16,9 +18,9 @@ export function validateDefinitions(environment, analysis) {
|
|
|
16
18
|
const schema = environment.candidate;
|
|
17
19
|
let scope;
|
|
18
20
|
for (const [name, declaration] of Object.entries(schema.classes)) {
|
|
19
|
-
const ref =
|
|
20
|
-
const pointer = `/classes/${
|
|
21
|
-
if (!
|
|
21
|
+
const ref = { origin: schema.origin, kind: 'class', name };
|
|
22
|
+
const pointer = `/classes/${escapePointerToken(name)}`;
|
|
23
|
+
if (!equalDefinitionRefs(declaration.ref, ref)) {
|
|
22
24
|
diagnostics.push(diagnostic('DV_REF_RESOLVES', `${pointer}/ref`, `Class ref must be exactly ${formatDefinitionRef(ref)}.`, formatDefinitionRef(declaration.ref)));
|
|
23
25
|
}
|
|
24
26
|
if (declaration.abstract &&
|
|
@@ -28,27 +30,25 @@ export function validateDefinitions(environment, analysis) {
|
|
|
28
30
|
validateFacet(environment, declaration, pointer, diagnostics);
|
|
29
31
|
validateParents(environment, ref, declaration.kind, declaration.extends, diagnostics);
|
|
30
32
|
if (declaration.kind === 'edge') {
|
|
31
|
-
validateEdgeContract(environment, ref, declaration, diagnostics);
|
|
33
|
+
validateEdgeContract(environment, ref, declaration, pointer, diagnostics);
|
|
32
34
|
scope ??= lifetimeScope([...environment.definitions.values()].map(({ declaration }) => declaration));
|
|
33
35
|
validateEdgeLifetimes(scope, ref, declaration, pointer, diagnostics);
|
|
36
|
+
validateEdgeClassComposition(environment, ref, declaration, pointer, diagnostics);
|
|
34
37
|
}
|
|
35
38
|
validateUniqueProperties(environment, analysis, ref, declaration, pointer, diagnostics);
|
|
36
39
|
}
|
|
37
|
-
validateEdgeClassComposition(environment, diagnostics);
|
|
38
40
|
return Object.freeze(diagnostics);
|
|
39
41
|
}
|
|
40
42
|
function validateFacet(environment, declaration, pointer, diagnostics) {
|
|
41
43
|
const valueRegistry = createValueSchemaRegistry(environment);
|
|
42
44
|
for (const [name, property] of Object.entries(declaration.properties)) {
|
|
43
|
-
const propertyPointer = `${pointer}/properties/${
|
|
45
|
+
const propertyPointer = `${pointer}/properties/${escapePointerToken(name)}`;
|
|
46
|
+
// The structure refuses a blob on an Edge Property and a State Property on an Edge Class.
|
|
44
47
|
if (isBlobValueSchema(property.schema) &&
|
|
45
|
-
(
|
|
48
|
+
(property.auto !== undefined || property.state !== undefined)) {
|
|
46
49
|
diagnostics.push(diagnostic('DV_VALUE_SCHEMA', `${propertyPointer}/schema/x-astrale-value-kind`, 'Blob values are allowed only on non-automatic, non-state Node properties.'));
|
|
47
50
|
}
|
|
48
51
|
if (property.state !== undefined) {
|
|
49
|
-
if (declaration.kind !== 'node') {
|
|
50
|
-
diagnostics.push(diagnostic('DV_PROPERTY_STATE', `${propertyPointer}/state`, 'A State Property may be declared only on a Node Class.'));
|
|
51
|
-
}
|
|
52
52
|
try {
|
|
53
53
|
const topology = admitStateTopology(property.state);
|
|
54
54
|
const expected = Object.keys(topology.transitions).sort();
|
|
@@ -92,7 +92,7 @@ function validateFacet(environment, declaration, pointer, diagnostics) {
|
|
|
92
92
|
if (declaration.kind !== 'node')
|
|
93
93
|
return;
|
|
94
94
|
for (const [name, method] of Object.entries(declaration.methods)) {
|
|
95
|
-
const methodPointer = `${pointer}/methods/${
|
|
95
|
+
const methodPointer = `${pointer}/methods/${escapePointerToken(name)}`;
|
|
96
96
|
if (isMethodExecutable(declaration, method))
|
|
97
97
|
continue;
|
|
98
98
|
if (method.policy !== undefined) {
|
|
@@ -107,7 +107,7 @@ function validateFacet(environment, declaration, pointer, diagnostics) {
|
|
|
107
107
|
function validateParents(environment, owner, ownerKind, parents, diagnostics) {
|
|
108
108
|
for (const [index, parent] of parents.entries()) {
|
|
109
109
|
const target = environment.resolve(parent);
|
|
110
|
-
const pointer = `/classes/${
|
|
110
|
+
const pointer = `/classes/${escapePointerToken(owner.name)}/extends/${index}`;
|
|
111
111
|
if (target === undefined) {
|
|
112
112
|
diagnostics.push(diagnostic('DV_REF_RESOLVES', pointer, `Parent ${formatDefinitionRef(parent)} does not resolve exactly.`, formatDefinitionRef(parent)));
|
|
113
113
|
continue;
|
|
@@ -120,23 +120,26 @@ function validateParents(environment, owner, ownerKind, parents, diagnostics) {
|
|
|
120
120
|
}
|
|
121
121
|
}
|
|
122
122
|
}
|
|
123
|
-
function validateEdgeContract(environment, owner, declaration, diagnostics) {
|
|
124
|
-
const
|
|
123
|
+
function validateEdgeContract(environment, owner, declaration, pointer, diagnostics) {
|
|
124
|
+
const directed = declaration.orientation === 'directed';
|
|
125
|
+
const endpoints = directed
|
|
125
126
|
? [declaration.endpoints.source, declaration.endpoints.target]
|
|
126
127
|
: declaration.endpoints;
|
|
128
|
+
const keys = directed ? ['source', 'target'] : ['0', '1'];
|
|
127
129
|
if (endpoints[0].role === endpoints[1].role) {
|
|
128
|
-
diagnostics.push(diagnostic('DV_EDGE_ROLE',
|
|
129
|
-
}
|
|
130
|
-
const counts = declaration.orientation === 'directed'
|
|
131
|
-
? [declaration.endpoints.source.outgoing, declaration.endpoints.target.incoming]
|
|
132
|
-
: [declaration.endpoints[0].incident, declaration.endpoints[1].incident];
|
|
133
|
-
validateEndpoint(environment, owner, endpoints[0], counts[0], declaration.orientation === 'directed' ? 'source' : 'first endpoint', diagnostics);
|
|
134
|
-
validateEndpoint(environment, owner, endpoints[1], counts[1], declaration.orientation === 'directed' ? 'target' : 'second endpoint', diagnostics);
|
|
135
|
-
if (declaration.orientation === 'undirected') {
|
|
136
|
-
if (declaration.constraints.acyclic === true) {
|
|
137
|
-
diagnostics.push(diagnostic('DV_EDGE_CONSTRAINT', '', 'acyclic is valid only for directed Edge contracts.'));
|
|
138
|
-
}
|
|
130
|
+
diagnostics.push(diagnostic('DV_EDGE_ROLE', `${pointer}/endpoints/${keys[1]}/role`, `${formatDefinitionRef(owner)} has equal endpoint roles.`));
|
|
139
131
|
}
|
|
132
|
+
const counts = directed
|
|
133
|
+
? [
|
|
134
|
+
['outgoing', declaration.endpoints.source.outgoing],
|
|
135
|
+
['incoming', declaration.endpoints.target.incoming],
|
|
136
|
+
]
|
|
137
|
+
: [
|
|
138
|
+
['incident', declaration.endpoints[0].incident],
|
|
139
|
+
['incident', declaration.endpoints[1].incident],
|
|
140
|
+
];
|
|
141
|
+
validateEndpoint(environment, owner, endpoints[0], counts[0], directed ? 'source' : 'first endpoint', `${pointer}/endpoints/${keys[0]}`, diagnostics);
|
|
142
|
+
validateEndpoint(environment, owner, endpoints[1], counts[1], directed ? 'target' : 'second endpoint', `${pointer}/endpoints/${keys[1]}`, diagnostics);
|
|
140
143
|
}
|
|
141
144
|
/**
|
|
142
145
|
* Each Property a key tuple names is one of the Class's own or inherited Properties, and keyable:
|
|
@@ -188,25 +191,20 @@ function validateUniqueProperties(environment, analysis, owner, declaration, poi
|
|
|
188
191
|
check(field.property, `${pointer}/unique/${t}/${f}/property`);
|
|
189
192
|
}));
|
|
190
193
|
}
|
|
191
|
-
|
|
194
|
+
// The structure refuses a repeated accepted Class before semantics: `accepts` is a set.
|
|
195
|
+
function validateEndpoint(environment, owner, endpoint, [countField, count], side, pointer, diagnostics) {
|
|
192
196
|
// An Edge Class binds only the Nodes of its Domain and its dependents: it cannot oblige the
|
|
193
197
|
// Nodes of a Class another Domain owns to carry its Edges.
|
|
194
198
|
const foreign = endpoint.accepts.find((ref) => ref.origin !== owner.origin);
|
|
195
199
|
if (foreign !== undefined && (count === '1' || count === '1..*')) {
|
|
196
|
-
diagnostics.push(diagnostic('DV_EDGE_FOREIGN_MINIMUM',
|
|
200
|
+
diagnostics.push(diagnostic('DV_EDGE_FOREIGN_MINIMUM', `${pointer}/${countField}`, `${formatDefinitionRef(owner)} ${side} accepts ${formatDefinitionRef(foreign)} of another Domain and cannot require ${count} Edges.`));
|
|
197
201
|
}
|
|
198
|
-
const
|
|
199
|
-
for (const ref of endpoint.accepts) {
|
|
200
|
-
const key = definitionRefIndex(ref);
|
|
201
|
-
if (seen.has(key)) {
|
|
202
|
-
diagnostics.push(diagnostic('DV_EDGE_ACCEPTS', '', `${formatDefinitionRef(owner)} ${side} repeats ${formatDefinitionRef(ref)}.`));
|
|
203
|
-
}
|
|
204
|
-
seen.add(key);
|
|
202
|
+
for (const [index, ref] of endpoint.accepts.entries()) {
|
|
205
203
|
const resolved = environment.resolve(ref);
|
|
206
204
|
if (resolved === undefined ||
|
|
207
205
|
resolved.ref.kind !== 'class' ||
|
|
208
206
|
resolved.declaration.kind !== 'node') {
|
|
209
|
-
diagnostics.push(diagnostic('DV_EDGE_ACCEPTS',
|
|
207
|
+
diagnostics.push(diagnostic('DV_EDGE_ACCEPTS', `${pointer}/accepts/${index}`, `${formatDefinitionRef(owner)} ${side} ref ${formatDefinitionRef(ref)} does not resolve to a Node Class.`));
|
|
210
208
|
}
|
|
211
209
|
}
|
|
212
210
|
}
|
|
@@ -223,63 +221,24 @@ function validateEdgeLifetimes(scope, owner, declaration, pointer, diagnostics)
|
|
|
223
221
|
diagnostics.push(diagnostic('DV_EDGE_LIFETIME', `${pointer}/endpoints/${keys[index]}/onDelete`, `${formatDefinitionRef(owner)} ${sides[index]} ${reason}`));
|
|
224
222
|
}
|
|
225
223
|
}
|
|
226
|
-
/**
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
224
|
+
/**
|
|
225
|
+
* Edge inheritance is refinement, not a second interface composition mechanism. A dependency
|
|
226
|
+
* Edge Class was judged against this same exact closure when it was accepted.
|
|
227
|
+
*/
|
|
228
|
+
function validateEdgeClassComposition(environment, owner, child, pointer, diagnostics) {
|
|
229
|
+
for (const [index, parentRef] of child.extends.entries()) {
|
|
230
|
+
const parent = environment.resolveAny(parentRef);
|
|
231
|
+
if (parent === undefined || parent.ref.kind !== 'class' || parent.declaration.kind !== 'edge') {
|
|
230
232
|
continue;
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
if (parent === undefined ||
|
|
235
|
-
parent.ref.kind !== 'class' ||
|
|
236
|
-
parent.declaration.kind !== 'edge') {
|
|
237
|
-
continue;
|
|
238
|
-
}
|
|
239
|
-
if (!edgeContractRefines(environment, child, parent.declaration)) {
|
|
240
|
-
diagnostics.push(diagnostic('DV_EDGE_CLASS_COMPOSITION', '', `${formatDefinitionRef(resolved.ref)} does not safely refine ${formatDefinitionRef(parentRef)}.`));
|
|
241
|
-
}
|
|
233
|
+
}
|
|
234
|
+
if (!edgeContractRefines(environment, child, parent.declaration)) {
|
|
235
|
+
diagnostics.push(diagnostic('DV_EDGE_CLASS_COMPOSITION', `${pointer}/extends/${index}`, `${formatDefinitionRef(owner)} does not safely refine ${formatDefinitionRef(parentRef)}.`));
|
|
242
236
|
}
|
|
243
237
|
}
|
|
244
238
|
}
|
|
245
239
|
/** Whether `child` safely refines its parent Edge contract `parent`. */
|
|
246
240
|
export function edgeContractRefines(environment, child, parent) {
|
|
247
|
-
|
|
248
|
-
return true;
|
|
249
|
-
if (child.orientation !== parent.orientation)
|
|
250
|
-
return false;
|
|
251
|
-
if (parent.constraints.noSelf === true && child.constraints.noSelf !== true)
|
|
252
|
-
return false;
|
|
253
|
-
if (parent.constraints.acyclic === true && child.constraints.acyclic !== true)
|
|
254
|
-
return false;
|
|
255
|
-
if (child.orientation === 'directed' && parent.orientation === 'directed') {
|
|
256
|
-
return (endpointRefines(environment, child.endpoints.source, parent.endpoints.source) &&
|
|
257
|
-
countRefines(child.endpoints.source.outgoing, parent.endpoints.source.outgoing) &&
|
|
258
|
-
endpointRefines(environment, child.endpoints.target, parent.endpoints.target) &&
|
|
259
|
-
countRefines(child.endpoints.target.incoming, parent.endpoints.target.incoming));
|
|
260
|
-
}
|
|
261
|
-
if (child.orientation !== 'undirected' || parent.orientation !== 'undirected')
|
|
262
|
-
return false;
|
|
263
|
-
const refines = (childIndex, parentIndex) => endpointRefines(environment, child.endpoints[childIndex], parent.endpoints[parentIndex]) &&
|
|
264
|
-
countRefines(child.endpoints[childIndex].incident, parent.endpoints[parentIndex].incident);
|
|
265
|
-
const direct = refines(0, 0) && refines(1, 1);
|
|
266
|
-
if (direct)
|
|
267
|
-
return true;
|
|
268
|
-
return refines(0, 1) && refines(1, 0);
|
|
269
|
-
}
|
|
270
|
-
function endpointRefines(environment, child, parent) {
|
|
271
|
-
return child.accepts.every((accepted) => classSatisfies(environment, accepted, parent.accepts));
|
|
272
|
-
}
|
|
273
|
-
function countRefines(child, parent) {
|
|
274
|
-
const bounds = {
|
|
275
|
-
'0..*': [0, Number.POSITIVE_INFINITY],
|
|
276
|
-
'1..*': [1, Number.POSITIVE_INFINITY],
|
|
277
|
-
'0..1': [0, 1],
|
|
278
|
-
'1': [1, 1],
|
|
279
|
-
};
|
|
280
|
-
const [childMinimum, childMaximum] = bounds[child];
|
|
281
|
-
const [parentMinimum, parentMaximum] = bounds[parent];
|
|
282
|
-
return childMinimum >= parentMinimum && childMaximum <= parentMaximum;
|
|
241
|
+
return refinesEdgeContract(child, parent, (candidate, accepts) => classSatisfies(environment, candidate, accepts));
|
|
283
242
|
}
|
|
284
243
|
/** Whether `candidate` is, or is a Node Class extending, one of `accepts`. */
|
|
285
244
|
export function classSatisfies(environment, candidate, accepts) {
|
|
@@ -294,12 +253,3 @@ export function classSatisfies(environment, candidate, accepts) {
|
|
|
294
253
|
return required !== undefined && satisfiesClass(actual, required, resolve);
|
|
295
254
|
});
|
|
296
255
|
}
|
|
297
|
-
function localRef(origin, name) {
|
|
298
|
-
return { origin, kind: 'class', name };
|
|
299
|
-
}
|
|
300
|
-
function sameRef(left, right) {
|
|
301
|
-
return left.origin === right.origin && left.kind === right.kind && left.name === right.name;
|
|
302
|
-
}
|
|
303
|
-
function escapePointer(value) {
|
|
304
|
-
return value.replaceAll('~', '~0').replaceAll('/', '~1');
|
|
305
|
-
}
|
|
@@ -1,10 +1,13 @@
|
|
|
1
|
+
import { equalDefinitionRefs } from '../../addressing/definition-ref.js';
|
|
1
2
|
import { Key } from '../../addressing/key.js';
|
|
2
3
|
import { compareUnicode, definitionRefIndex } from '../../addressing/ordering.js';
|
|
3
4
|
import { isMethodExecutable } from '../../language/callable/executable.js';
|
|
4
5
|
import { coveringEdgeUniqueTuples, coveringNodeUniqueTuples, edgeUniqueKeys, nodeUniqueKeys, } from '../../language/class/unique.js';
|
|
6
|
+
import { checkCoreEdgeSet, checkHeldKeys, coreEndpointsSatisfy, } from '../../language/core/facts.js';
|
|
5
7
|
import { policyObjectKey, policyObjectOrigin, projectedMemberClass, } from '../../language/policy/model/policy.js';
|
|
6
8
|
import { validateJsonInstance } from '../../language/value/evaluate/validate.js';
|
|
7
|
-
import {
|
|
9
|
+
import { escapePointerToken } from '../../language/value/syntax/pointer.js';
|
|
10
|
+
import { diagnostic, valueDiagnostic } from '../diagnostic.js';
|
|
8
11
|
import { classSatisfies } from './class.js';
|
|
9
12
|
const formatDefinitionRef = Key.of;
|
|
10
13
|
export function validateCore(environment, analysis, registry) {
|
|
@@ -15,38 +18,27 @@ export function validateCore(environment, analysis, registry) {
|
|
|
15
18
|
validateCoreEdgeSet(environment, edges, nodesByRef, diagnostics);
|
|
16
19
|
return Object.freeze(diagnostics);
|
|
17
20
|
}
|
|
18
|
-
/**
|
|
19
|
-
* No two Core Nodes hold one key under a tuple covering their Classes, whichever Class or ancestor
|
|
20
|
-
* declares it (DV-NODE-UNIQUE); Activation checks them against stored data.
|
|
21
|
-
*/
|
|
21
|
+
/** No two Core Nodes hold one key under a tuple covering their Classes, whichever declares it. */
|
|
22
22
|
function validateCoreNodeKeys(environment, nodesByRef, diagnostics) {
|
|
23
23
|
const resolve = (ref) => environment.resolveAny(ref)?.declaration;
|
|
24
|
-
|
|
25
|
-
for (const node of nodesByRef.values()) {
|
|
24
|
+
checkHeldKeys(nodesByRef.values(), (node) => {
|
|
26
25
|
const declaration = resolve(node.classRef);
|
|
27
26
|
if (declaration === undefined)
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
const keys = nodeUniqueKeys(coveringNodeUniqueTuples(declaration, resolve), {
|
|
27
|
+
return [];
|
|
28
|
+
return nodeUniqueKeys(coveringNodeUniqueTuples(declaration, resolve), {
|
|
31
29
|
props: environment.candidate.core.nodes[node.slug].properties,
|
|
32
30
|
});
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
for (const key of keys)
|
|
38
|
-
if (!holders.has(key))
|
|
39
|
-
holders.set(key, pointer);
|
|
40
|
-
}
|
|
31
|
+
}, (node, previous) => diagnostics.push(diagnostic('DV_CORE_NODE_UNIQUE', nodePointer(node), `Core Node holds a key the Core Node at ${nodePointer(previous)} holds.`)));
|
|
32
|
+
}
|
|
33
|
+
function nodePointer(node) {
|
|
34
|
+
return `/core/nodes/${escapePointerToken(node.slug)}`;
|
|
41
35
|
}
|
|
42
36
|
function resolveCoreNodes(environment, analysis, registry, diagnostics) {
|
|
43
37
|
const nodes = new Map();
|
|
44
38
|
for (const [slug, node] of Object.entries(environment.candidate.core.nodes)) {
|
|
45
|
-
const pointer = `/core/nodes/${
|
|
39
|
+
const pointer = `/core/nodes/${escapePointerToken(slug)}`;
|
|
46
40
|
const ref = { origin: environment.candidate.origin, kind: 'core', name: slug };
|
|
47
|
-
if (node.ref
|
|
48
|
-
node.ref.kind !== ref.kind ||
|
|
49
|
-
node.ref.name !== ref.name) {
|
|
41
|
+
if (!equalDefinitionRefs(node.ref, ref)) {
|
|
50
42
|
diagnostics.push(diagnostic('DV_REF_RESOLVES', `${pointer}/ref`, `Core ref must be exactly ${formatDefinitionRef(ref)}.`, formatDefinitionRef(node.ref)));
|
|
51
43
|
}
|
|
52
44
|
const resolved = environment.resolve(node.class);
|
|
@@ -89,13 +81,7 @@ function resolveCoreEdges(environment, analysis, registry, nodesByRef, diagnosti
|
|
|
89
81
|
const declaration = resolved.declaration;
|
|
90
82
|
if (source === undefined || target === undefined)
|
|
91
83
|
continue;
|
|
92
|
-
const endpointsSatisfy = declaration
|
|
93
|
-
? endpointSatisfies(environment, source, declaration.endpoints.source.accepts) &&
|
|
94
|
-
endpointSatisfies(environment, target, declaration.endpoints.target.accepts)
|
|
95
|
-
: (endpointSatisfies(environment, source, declaration.endpoints[0].accepts) &&
|
|
96
|
-
endpointSatisfies(environment, target, declaration.endpoints[1].accepts)) ||
|
|
97
|
-
(endpointSatisfies(environment, source, declaration.endpoints[1].accepts) &&
|
|
98
|
-
endpointSatisfies(environment, target, declaration.endpoints[0].accepts));
|
|
84
|
+
const endpointsSatisfy = coreEndpointsSatisfy(declaration, source, target, (endpoint, accepts) => endpointSatisfies(environment, endpoint, accepts));
|
|
99
85
|
if (!endpointsSatisfy) {
|
|
100
86
|
diagnostics.push(diagnostic('DV_CORE_EDGE_CLASS', pointer, `Core Edge endpoints do not satisfy ${formatDefinitionRef(edge.class)}.`, formatDefinitionRef(edge.class)));
|
|
101
87
|
continue;
|
|
@@ -153,190 +139,70 @@ function validateCreationProperties(definition, properties, registry, code, poin
|
|
|
153
139
|
.sort(compareUnicode),
|
|
154
140
|
additionalProperties: false,
|
|
155
141
|
};
|
|
156
|
-
for (const
|
|
157
|
-
diagnostics.push(
|
|
142
|
+
for (const refusal of validateJsonInstance(schema, properties, registry, pointer)) {
|
|
143
|
+
diagnostics.push(valueDiagnostic(code, refusal));
|
|
158
144
|
}
|
|
159
145
|
}
|
|
160
146
|
function validateCoreEdgeSet(environment, edges, nodesByRef, diagnostics) {
|
|
161
|
-
const
|
|
162
|
-
const edgeSlices = new Map();
|
|
163
|
-
for (const [name, candidate] of Object.entries(environment.candidate.classes)) {
|
|
164
|
-
if (candidate.kind !== 'edge')
|
|
165
|
-
continue;
|
|
166
|
-
const ref = {
|
|
167
|
-
origin: environment.candidate.origin,
|
|
168
|
-
kind: 'class',
|
|
169
|
-
name,
|
|
170
|
-
};
|
|
171
|
-
const resolved = environment.resolve(ref);
|
|
172
|
-
if (resolved === undefined || resolved.ref.kind !== 'class' || resolved.kind !== 'edge') {
|
|
173
|
-
continue;
|
|
174
|
-
}
|
|
175
|
-
edgeSlices.set(definitionRefIndex(ref), {
|
|
176
|
-
declaration: resolved.declaration,
|
|
177
|
-
edges: [],
|
|
178
|
-
});
|
|
179
|
-
}
|
|
180
|
-
for (const edge of edges) {
|
|
181
|
-
const classKey = definitionRefIndex(edge.value.class);
|
|
182
|
-
const [source, target] = edge.declaration.orientation === 'undirected' &&
|
|
183
|
-
compareUnicode(edge.source.key, edge.target.key) > 0
|
|
184
|
-
? [edge.target.key, edge.source.key]
|
|
185
|
-
: [edge.source.key, edge.target.key];
|
|
186
|
-
const key = `${classKey}\u0000${source}\u0000${target}`;
|
|
187
|
-
const previous = seen.get(key);
|
|
188
|
-
if (previous !== undefined) {
|
|
189
|
-
diagnostics.push(diagnostic('DV_CORE_EDGE_KEY', edge.pointer, `Core Edge duplicates the normalized EdgeKey declared at ${previous}.`));
|
|
190
|
-
}
|
|
191
|
-
else {
|
|
192
|
-
seen.set(key, edge.pointer);
|
|
193
|
-
}
|
|
194
|
-
const slice = edgeSlices.get(classKey) ?? {
|
|
195
|
-
declaration: edge.declaration,
|
|
196
|
-
edges: [],
|
|
197
|
-
};
|
|
198
|
-
slice.edges.push(edge);
|
|
199
|
-
edgeSlices.set(classKey, slice);
|
|
200
|
-
if (edge.declaration.constraints.noSelf === true && edge.source.key === edge.target.key) {
|
|
201
|
-
diagnostics.push(diagnostic('DV_CORE_EDGE_CONSTRAINTS', edge.pointer, 'Core Edge violates noSelf.'));
|
|
202
|
-
}
|
|
203
|
-
}
|
|
204
|
-
validateCoreEdgeKeys(environment, edges, diagnostics);
|
|
205
|
-
for (const { declaration, edges: classEdges } of edgeSlices.values()) {
|
|
206
|
-
if (declaration.constraints.acyclic === true && hasDirectedCycle(classEdges)) {
|
|
207
|
-
diagnostics.push(diagnostic('DV_CORE_EDGE_CONSTRAINTS', '/core/edges', 'Core Edge set violates exact-Class acyclicity.'));
|
|
208
|
-
}
|
|
209
|
-
validateCardinality(environment, declaration, classEdges, nodesByRef, diagnostics);
|
|
210
|
-
}
|
|
211
|
-
}
|
|
212
|
-
/**
|
|
213
|
-
* No two Core Edges hold one key under a tuple covering their Classes, whichever Class or ancestor
|
|
214
|
-
* declares it (DV-EDGE-UNIQUE). Core Edges have no slug, so only tuples of endpoints and
|
|
215
|
-
* Properties can bind them; Activation checks them against stored data.
|
|
216
|
-
*/
|
|
217
|
-
function validateCoreEdgeKeys(environment, edges, diagnostics) {
|
|
147
|
+
const { candidate } = environment;
|
|
218
148
|
const resolve = (ref) => environment.resolveAny(ref)?.declaration;
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
149
|
+
checkCoreEdgeSet({
|
|
150
|
+
edges: edges.map((edge) => ({
|
|
151
|
+
edge,
|
|
152
|
+
class: definitionRefIndex(edge.value.class),
|
|
153
|
+
contract: edge.declaration,
|
|
154
|
+
source: factEndpoint(edge.source, edge.pointer),
|
|
155
|
+
target: factEndpoint(edge.target, edge.pointer),
|
|
156
|
+
})),
|
|
157
|
+
classes: Object.entries(candidate.classes).flatMap(([name, declaration]) => {
|
|
158
|
+
if (declaration.kind !== 'edge')
|
|
159
|
+
return [];
|
|
160
|
+
const ref = { origin: candidate.origin, kind: 'class', name };
|
|
161
|
+
const resolved = environment.resolve(ref);
|
|
162
|
+
if (resolved === undefined || resolved.ref.kind !== 'class' || resolved.kind !== 'edge') {
|
|
163
|
+
return [];
|
|
164
|
+
}
|
|
165
|
+
return [
|
|
166
|
+
{ key: definitionRefIndex(ref), contract: resolved.declaration },
|
|
167
|
+
];
|
|
168
|
+
}),
|
|
169
|
+
nodes: [...nodesByRef.values()].map((node) => ({
|
|
170
|
+
key: node.key,
|
|
171
|
+
class: node.classRef,
|
|
172
|
+
pointer: nodePointer(node),
|
|
173
|
+
})),
|
|
174
|
+
satisfies: (ref, accepts) => classSatisfies(environment, ref, accepts),
|
|
175
|
+
keys: ({ edge }) => edgeUniqueKeys(coveringEdgeUniqueTuples(edge.declaration, resolve), {
|
|
222
176
|
source: edge.source.key,
|
|
223
177
|
target: edge.target.key,
|
|
224
178
|
props: edge.value.properties,
|
|
225
|
-
})
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
179
|
+
}),
|
|
180
|
+
sameIncident: sameIncidentEndpointContract,
|
|
181
|
+
report: (issue) => diagnostics.push(coreEdgeDiagnostic(issue)),
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
/** A projected member's degree is reported at the Edge that names it. */
|
|
185
|
+
function factEndpoint(endpoint, pointer) {
|
|
186
|
+
return endpoint.kind === 'member'
|
|
187
|
+
? { key: endpoint.key, member: { class: endpoint.classRef, pointer } }
|
|
188
|
+
: endpoint;
|
|
189
|
+
}
|
|
190
|
+
function coreEdgeDiagnostic(issue) {
|
|
191
|
+
switch (issue.kind) {
|
|
192
|
+
case 'identity':
|
|
193
|
+
return diagnostic('DV_CORE_EDGE_KEY', issue.edge.pointer, `Core Edge duplicates the normalized EdgeKey declared at ${issue.previous.pointer}.`);
|
|
194
|
+
case 'no-self':
|
|
195
|
+
return diagnostic('DV_CORE_EDGE_CONSTRAINTS', issue.edge.pointer, 'Core Edge violates noSelf.');
|
|
196
|
+
case 'key':
|
|
197
|
+
return diagnostic('DV_CORE_EDGE_UNIQUE', issue.edge.pointer, `Core Edge holds a key the Core Edge at ${issue.previous.pointer} holds.`);
|
|
198
|
+
case 'acyclic':
|
|
199
|
+
return diagnostic('DV_CORE_EDGE_CONSTRAINTS', '/core/edges', 'Core Edge set violates exact-Class acyclicity.');
|
|
200
|
+
case 'degree':
|
|
201
|
+
return diagnostic('DV_CORE_EDGE_CONSTRAINTS', issue.pointer, `Core degree ${issue.degree} violates ${issue.endpoint.role} Edge count ${issue.count}.`);
|
|
233
202
|
}
|
|
234
203
|
}
|
|
235
|
-
function validateCardinality(environment, declaration, edges, nodesByRef, diagnostics) {
|
|
236
|
-
const subjects = cardinalitySubjects(edges, nodesByRef);
|
|
237
|
-
if (declaration.orientation === 'undirected') {
|
|
238
|
-
const [first, second] = declaration.endpoints;
|
|
239
|
-
for (const subject of subjects) {
|
|
240
|
-
const matchesFirst = classSatisfies(environment, subject.classRef, first.accepts);
|
|
241
|
-
const matchesSecond = classSatisfies(environment, subject.classRef, second.accepts);
|
|
242
|
-
if (!matchesFirst && !matchesSecond)
|
|
243
|
-
continue;
|
|
244
|
-
const degree = edges.filter((edge) => edge.source.key === subject.key || edge.target.key === subject.key).length;
|
|
245
|
-
if (matchesFirst)
|
|
246
|
-
checkBound(first, first.incident, degree, subject, diagnostics);
|
|
247
|
-
if (matchesSecond && !sameIncidentEndpointContract(first, second)) {
|
|
248
|
-
checkBound(second, second.incident, degree, subject, diagnostics);
|
|
249
|
-
}
|
|
250
|
-
}
|
|
251
|
-
return;
|
|
252
|
-
}
|
|
253
|
-
for (const subject of subjects) {
|
|
254
|
-
if (classSatisfies(environment, subject.classRef, declaration.endpoints.source.accepts)) {
|
|
255
|
-
const degree = edges.filter((edge) => edge.source.key === subject.key).length;
|
|
256
|
-
checkBound(declaration.endpoints.source, declaration.endpoints.source.outgoing, degree, subject, diagnostics);
|
|
257
|
-
}
|
|
258
|
-
if (classSatisfies(environment, subject.classRef, declaration.endpoints.target.accepts)) {
|
|
259
|
-
const degree = edges.filter((edge) => edge.target.key === subject.key).length;
|
|
260
|
-
checkBound(declaration.endpoints.target, declaration.endpoints.target.incoming, degree, subject, diagnostics);
|
|
261
|
-
}
|
|
262
|
-
}
|
|
263
|
-
}
|
|
264
|
-
/**
|
|
265
|
-
* Every declared Core Node proves its own complete degree, so both bounds hold. A Definition
|
|
266
|
-
* endpoint is only named by the Edges that reach it: the Domain may also create ordinary Edges of
|
|
267
|
-
* the same Class against it, so a minimum is unprovable here and only the maximum is enforced.
|
|
268
|
-
*/
|
|
269
|
-
function cardinalitySubjects(edges, nodesByRef) {
|
|
270
|
-
const subjects = new Map();
|
|
271
|
-
for (const node of nodesByRef.values()) {
|
|
272
|
-
subjects.set(node.key, {
|
|
273
|
-
key: node.key,
|
|
274
|
-
classRef: node.classRef,
|
|
275
|
-
pointer: `/core/nodes/${escapePointer(node.slug)}`,
|
|
276
|
-
minimum: true,
|
|
277
|
-
});
|
|
278
|
-
}
|
|
279
|
-
for (const edge of edges) {
|
|
280
|
-
for (const endpoint of [edge.source, edge.target]) {
|
|
281
|
-
if (endpoint.kind !== 'member' || subjects.has(endpoint.key))
|
|
282
|
-
continue;
|
|
283
|
-
subjects.set(endpoint.key, {
|
|
284
|
-
key: endpoint.key,
|
|
285
|
-
classRef: endpoint.classRef,
|
|
286
|
-
pointer: edge.pointer,
|
|
287
|
-
minimum: false,
|
|
288
|
-
});
|
|
289
|
-
}
|
|
290
|
-
}
|
|
291
|
-
return Object.freeze([...subjects.values()]);
|
|
292
|
-
}
|
|
293
|
-
function checkBound(endpoint, count, degree, subject, diagnostics) {
|
|
294
|
-
const [minimum, maximum] = countBounds(count);
|
|
295
|
-
if (degree <= maximum && (degree >= minimum || !subject.minimum))
|
|
296
|
-
return;
|
|
297
|
-
diagnostics.push(diagnostic('DV_CORE_EDGE_CONSTRAINTS', subject.pointer, `Core degree ${degree} violates ${endpoint.role} Edge count ${count}.`));
|
|
298
|
-
}
|
|
299
204
|
function sameIncidentEndpointContract(left, right) {
|
|
300
205
|
return (left.incident === right.incident &&
|
|
301
206
|
left.accepts.length === right.accepts.length &&
|
|
302
207
|
left.accepts.every((ref, index) => definitionRefIndex(ref) === definitionRefIndex(right.accepts[index])));
|
|
303
208
|
}
|
|
304
|
-
function countBounds(value) {
|
|
305
|
-
switch (value) {
|
|
306
|
-
case '0..*':
|
|
307
|
-
return [0, Number.POSITIVE_INFINITY];
|
|
308
|
-
case '1..*':
|
|
309
|
-
return [1, Number.POSITIVE_INFINITY];
|
|
310
|
-
case '0..1':
|
|
311
|
-
return [0, 1];
|
|
312
|
-
case '1':
|
|
313
|
-
return [1, 1];
|
|
314
|
-
}
|
|
315
|
-
}
|
|
316
|
-
function hasDirectedCycle(edges) {
|
|
317
|
-
const outgoing = new Map();
|
|
318
|
-
for (const edge of edges) {
|
|
319
|
-
const targets = outgoing.get(edge.source.key) ?? [];
|
|
320
|
-
targets.push(edge.target.key);
|
|
321
|
-
outgoing.set(edge.source.key, targets);
|
|
322
|
-
}
|
|
323
|
-
const visiting = new Set();
|
|
324
|
-
const visited = new Set();
|
|
325
|
-
const visit = (key) => {
|
|
326
|
-
if (visiting.has(key))
|
|
327
|
-
return true;
|
|
328
|
-
if (visited.has(key))
|
|
329
|
-
return false;
|
|
330
|
-
visiting.add(key);
|
|
331
|
-
for (const target of outgoing.get(key) ?? [])
|
|
332
|
-
if (visit(target))
|
|
333
|
-
return true;
|
|
334
|
-
visiting.delete(key);
|
|
335
|
-
visited.add(key);
|
|
336
|
-
return false;
|
|
337
|
-
};
|
|
338
|
-
return [...outgoing.keys()].some(visit);
|
|
339
|
-
}
|
|
340
|
-
function escapePointer(value) {
|
|
341
|
-
return value.replaceAll('~', '~0').replaceAll('/', '~1');
|
|
342
|
-
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { orderAndDeduplicate } from '../../language/validation/diagnostic.js';
|
|
2
2
|
import { analyzeDefinitions } from '../analysis/class.js';
|
|
3
3
|
import { buildDomainResolutionContext } from '../resolution/context.js';
|
|
4
4
|
import { createValueSchemaRegistry } from '../resolution/value.registry.js';
|
|
@@ -27,12 +27,3 @@ export function validateDomainSchemaSemantics(schema, lookup) {
|
|
|
27
27
|
diagnostics: Object.freeze(orderAndDeduplicate(diagnostics)),
|
|
28
28
|
});
|
|
29
29
|
}
|
|
30
|
-
function orderAndDeduplicate(diagnostics) {
|
|
31
|
-
const unique = new Map();
|
|
32
|
-
for (const value of diagnostics) {
|
|
33
|
-
unique.set(`${value.pointer}\u0000${value.code}\u0000${value.ref ?? ''}\u0000${value.message}`, value);
|
|
34
|
-
}
|
|
35
|
-
return [...unique.values()].sort((left, right) => compareUnicode(left.pointer, right.pointer) ||
|
|
36
|
-
compareUnicode(left.code, right.code) ||
|
|
37
|
-
compareUnicode(left.message, right.message));
|
|
38
|
-
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { type core as zodCore } from 'zod';
|
|
2
|
+
export type JsonSchemaDocument = zodCore.JSONSchema.JSONSchema;
|
|
3
|
+
/**
|
|
4
|
+
* The JSON Schema of an authored Domain schema document, rendered from the structure the Kernel
|
|
5
|
+
* enforces. Zod derives what it reads; `documentJsonSchema` states each refinement it cannot read,
|
|
6
|
+
* or names the rule JSON Schema cannot state. A refinement neither stated nor named fails the
|
|
7
|
+
* rendering, so that no check of the structure is dropped silently. Every value schema is in the
|
|
8
|
+
* published dialect and states the profile rules JSON Schema can state; the others are named.
|
|
9
|
+
*/
|
|
10
|
+
export declare function domainSchemaJsonSchema(): JsonSchemaDocument;
|
|
11
|
+
/**
|
|
12
|
+
* Every refinement and custom part of the structure carries its statement in `documentJsonSchema`:
|
|
13
|
+
* the keywords that state it, or its named rule. Only parts zod renders take part.
|
|
14
|
+
*/
|
|
15
|
+
export declare function assertChecksStated(schema: zodCore.$ZodType, visited?: Set<zodCore.$ZodType<unknown, unknown, zodCore.$ZodTypeInternals<unknown, unknown>>>): void;
|