@astrale-os/kernel-dsl 1.0.0-beta.11 → 1.0.0-beta.13
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/ordering.d.ts +2 -0
- package/dist/v1/addressing/ordering.js +4 -0
- package/dist/v1/addressing/patterns.d.ts +0 -1
- package/dist/v1/addressing/patterns.js +0 -5
- package/dist/v1/builder/callable/authoring.d.ts +1 -1
- package/dist/v1/builder/class/authoring.d.ts +6 -256
- package/dist/v1/builder/class/authoring.js +5 -28
- package/dist/v1/builder/class/handle.d.ts +27 -0
- package/dist/v1/builder/class/handle.js +1 -0
- package/dist/v1/builder/class/input.d.ts +129 -0
- package/dist/v1/builder/class/projection.d.ts +129 -0
- package/dist/v1/builder/class/projection.js +1 -0
- package/dist/v1/builder/core/authoring.d.ts +1 -1
- package/dist/v1/builder/index.d.ts +3 -1
- package/dist/v1/builder/lowering/class.d.ts +1 -1
- package/dist/v1/builder/lowering/schema/context.d.ts +2 -1
- package/dist/v1/builder/lowering/schema/context.js +2 -4
- package/dist/v1/builder/lowering/value/path.d.ts +1 -1
- 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.d.ts +1 -1
- 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/input.d.ts +1 -1
- package/dist/v1/builder/schema/projection/class.d.ts +2 -1
- package/dist/v1/builder/schema/projection/core-input.d.ts +2 -1
- package/dist/v1/builder/schema/projection/identity.d.ts +2 -1
- package/dist/v1/builder/schema/projection/property.d.ts +1 -1
- 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/builder/value/path-metadata.d.ts +1 -1
- package/dist/v1/builder/value/path.d.ts +1 -1
- package/dist/v1/bundle/codec.js +2 -4
- package/dist/v1/bundle/diagnostic.js +3 -7
- package/dist/v1/bundle/structure.js +2 -4
- 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 +0 -1
- package/dist/v1/domain/validation/create.js +0 -35
- package/dist/v1/language/class/inheritance.d.ts +18 -0
- package/dist/v1/language/class/inheritance.js +73 -0
- 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/index.d.ts +1 -0
- package/dist/v1/language/index.js +1 -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/validation/diagnostic.d.ts +15 -1
- package/dist/v1/language/validation/diagnostic.js +21 -0
- package/dist/v1/language/value/compile/fields.js +4 -8
- package/dist/v1/language/value/inclusion/arrays.d.ts +3 -0
- package/dist/v1/language/value/inclusion/arrays.js +85 -0
- package/dist/v1/language/value/inclusion/assertions.d.ts +18 -0
- package/dist/v1/language/value/inclusion/assertions.js +29 -0
- package/dist/v1/language/value/inclusion/combinators.d.ts +3 -0
- package/dist/v1/language/value/inclusion/combinators.js +67 -0
- package/dist/v1/language/value/inclusion/equivalence.d.ts +4 -0
- package/dist/v1/language/value/inclusion/equivalence.js +74 -0
- package/dist/v1/language/value/inclusion/evidence.d.ts +34 -0
- package/dist/v1/language/value/inclusion/evidence.js +138 -0
- package/dist/v1/language/value/inclusion/includes.d.ts +17 -0
- package/dist/v1/language/value/inclusion/includes.js +25 -719
- package/dist/v1/language/value/inclusion/keywords.d.ts +5 -0
- package/dist/v1/language/value/inclusion/keywords.js +31 -0
- package/dist/v1/language/value/inclusion/kinds.d.ts +17 -0
- package/dist/v1/language/value/inclusion/kinds.js +57 -0
- package/dist/v1/language/value/inclusion/location.d.ts +24 -0
- package/dist/v1/language/value/inclusion/location.js +63 -0
- package/dist/v1/language/value/inclusion/numbers.d.ts +3 -0
- package/dist/v1/language/value/inclusion/numbers.js +41 -0
- package/dist/v1/language/value/inclusion/objects.d.ts +3 -0
- package/dist/v1/language/value/inclusion/objects.js +190 -0
- package/dist/v1/language/value/inclusion/strings.d.ts +3 -0
- package/dist/v1/language/value/inclusion/strings.js +29 -0
- package/dist/v1/language/value/inclusion/types.d.ts +3 -0
- package/dist/v1/language/value/inclusion/types.js +11 -0
- package/dist/v1/language/value/ordering.d.ts +9 -0
- package/dist/v1/language/value/ordering.js +72 -9
- package/dist/v1/language/value/profile/rules.js +5 -7
- package/dist/v1/language/value/profile/validate.js +1 -10
- package/dist/v1/language/value/reference/recursion.js +2 -22
- 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/compatibility/diff.js +2 -4
- package/dist/v1/schema/compatibility/meaning/footprint.js +16 -50
- 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.js +3 -11
- package/dist/v1/schema/resolution/context.js +3 -5
- package/dist/v1/schema/validation/callable.js +3 -5
- package/dist/v1/schema/validation/class.js +10 -52
- package/dist/v1/schema/validation/core.js +65 -199
- package/dist/v1/schema/validation/domain.js +1 -10
- package/dist/v1/schema/validation/domain.structure.js +2 -4
- 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 +5 -6
- 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 -22
- package/dist/v1/schema/validation/policy/validate.js +3 -2
- package/dist/v1/schema/validation/value-api.d.ts +9 -0
- package/dist/v1/schema/validation/value-api.js +23 -1
- package/dist/v1/schema/validation/value.js +5 -7
- package/dist/v1/schema/validation/view.js +4 -8
- package/dist/value/canonical.d.ts +3 -2
- package/dist/value/canonical.js +30 -6
- package/dist/value/decode.d.ts +7 -0
- package/dist/value/decode.js +159 -9
- package/dist/value/index.d.ts +1 -1
- package/dist/value/json.d.ts +1 -1
- package/dist/value/json.js +1 -1
- package/dist/value/types.d.ts +6 -0
- package/dist/value/unicode.d.ts +2 -1
- package/dist/value/unicode.js +2 -0
- package/package.json +1 -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/value/index.d.ts +0 -11
- package/dist/v1/language/value/index.js +0 -8
- /package/dist/v1/{language/core/index.js → builder/class/input.js} +0 -0
|
@@ -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 &&
|
|
@@ -40,7 +42,7 @@ export function validateDefinitions(environment, analysis) {
|
|
|
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)}`;
|
|
44
46
|
if (isBlobValueSchema(property.schema) &&
|
|
45
47
|
(declaration.kind !== 'node' || property.auto !== undefined || property.state !== undefined)) {
|
|
46
48
|
diagnostics.push(diagnostic('DV_VALUE_SCHEMA', `${propertyPointer}/schema/x-astrale-value-kind`, 'Blob values are allowed only on non-automatic, non-state Node properties.'));
|
|
@@ -92,7 +94,7 @@ function validateFacet(environment, declaration, pointer, diagnostics) {
|
|
|
92
94
|
if (declaration.kind !== 'node')
|
|
93
95
|
return;
|
|
94
96
|
for (const [name, method] of Object.entries(declaration.methods)) {
|
|
95
|
-
const methodPointer = `${pointer}/methods/${
|
|
97
|
+
const methodPointer = `${pointer}/methods/${escapePointerToken(name)}`;
|
|
96
98
|
if (isMethodExecutable(declaration, method))
|
|
97
99
|
continue;
|
|
98
100
|
if (method.policy !== undefined) {
|
|
@@ -107,7 +109,7 @@ function validateFacet(environment, declaration, pointer, diagnostics) {
|
|
|
107
109
|
function validateParents(environment, owner, ownerKind, parents, diagnostics) {
|
|
108
110
|
for (const [index, parent] of parents.entries()) {
|
|
109
111
|
const target = environment.resolve(parent);
|
|
110
|
-
const pointer = `/classes/${
|
|
112
|
+
const pointer = `/classes/${escapePointerToken(owner.name)}/extends/${index}`;
|
|
111
113
|
if (target === undefined) {
|
|
112
114
|
diagnostics.push(diagnostic('DV_REF_RESOLVES', pointer, `Parent ${formatDefinitionRef(parent)} does not resolve exactly.`, formatDefinitionRef(parent)));
|
|
113
115
|
continue;
|
|
@@ -244,42 +246,7 @@ function validateEdgeClassComposition(environment, diagnostics) {
|
|
|
244
246
|
}
|
|
245
247
|
/** Whether `child` safely refines its parent Edge contract `parent`. */
|
|
246
248
|
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;
|
|
249
|
+
return refinesEdgeContract(child, parent, (candidate, accepts) => classSatisfies(environment, candidate, accepts));
|
|
283
250
|
}
|
|
284
251
|
/** Whether `candidate` is, or is a Node Class extending, one of `accepts`. */
|
|
285
252
|
export function classSatisfies(environment, candidate, accepts) {
|
|
@@ -294,12 +261,3 @@ export function classSatisfies(environment, candidate, accepts) {
|
|
|
294
261
|
return required !== undefined && satisfiesClass(actual, required, resolve);
|
|
295
262
|
});
|
|
296
263
|
}
|
|
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,9 +1,12 @@
|
|
|
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';
|
|
9
|
+
import { escapePointerToken } from '../../language/value/syntax/pointer.js';
|
|
7
10
|
import { diagnostic } from '../diagnostic.js';
|
|
8
11
|
import { classSatisfies } from './class.js';
|
|
9
12
|
const formatDefinitionRef = Key.of;
|
|
@@ -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;
|
|
@@ -158,185 +144,65 @@ function validateCreationProperties(definition, properties, registry, code, poin
|
|
|
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
|
-
}
|
|
@@ -3,6 +3,7 @@ import { PropertyKey as SemanticPropertyKey } from '../../addressing/index.js';
|
|
|
3
3
|
import { CORE_SLUG_PATTERN, DEPENDENCY_ALIAS_PATTERN, DOMAIN_ORIGIN_PATTERN, DOMAIN_REVISION_PATTERN, MEMBER_NAME_PATTERN, } from '../../addressing/patterns.js';
|
|
4
4
|
import { MEDIA_TYPE_PATTERN } from '../../language/class/model/data.js';
|
|
5
5
|
import * as metadata from '../../language/definition/metadata.js';
|
|
6
|
+
import { escapePointerToken } from '../../language/value/syntax/pointer.js';
|
|
6
7
|
import { SchemaError, diagnostic } from '../diagnostic.js';
|
|
7
8
|
import { DOMAIN_FORMAT, DOMAIN_VERSION } from '../model/format.js';
|
|
8
9
|
const originSchema = string().min(3).max(253).regex(DOMAIN_ORIGIN_PATTERN);
|
|
@@ -498,8 +499,5 @@ function valueAtPath(input, path) {
|
|
|
498
499
|
function pointer(path) {
|
|
499
500
|
if (path.length === 0)
|
|
500
501
|
return '';
|
|
501
|
-
return `/${path.map((segment) =>
|
|
502
|
-
}
|
|
503
|
-
function escapePointerSegment(segment) {
|
|
504
|
-
return segment.replaceAll('~', '~0').replaceAll('/', '~1');
|
|
502
|
+
return `/${path.map((segment) => escapePointerToken(String(segment))).join('/')}`;
|
|
505
503
|
}
|
|
@@ -1,19 +1,13 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import { escapePointer } from './context.js';
|
|
1
|
+
import { BRANCH_OVERFLOW, MAX_BRANCHES, MAX_CHECK_DEPTH, MAX_CHECK_LEAVES, MAX_DOMAINS, MAX_EDGES, MAX_PATTERN_DEPTH, MAX_REPEAT, MAX_VARIABLES, measureChecks, policyMeasurer, } from '../../../language/policy/analysis/budgets.js';
|
|
2
|
+
import { escapePointerToken } from '../../../language/value/syntax/pointer.js';
|
|
4
3
|
export { BRANCH_OVERFLOW, MAX_BRANCHES, MAX_CHECK_DEPTH, MAX_CHECK_LEAVES, MAX_DOMAINS, MAX_EDGES, MAX_PATTERN_DEPTH, MAX_REPEAT, MAX_VARIABLES, measureChecks, };
|
|
5
4
|
export function validatePolicyBudgets(context) {
|
|
6
|
-
|
|
7
|
-
// path that reaches it (the only path-sensitive outcome, the back-edge `undefined`, already holds
|
|
8
|
-
// for every node on or reaching a composition cycle, regardless of entry), so caching the
|
|
9
|
-
// completed result by node identity turns path enumeration over the composition DAG into work
|
|
10
|
-
// linear in the number of nodes while preserving the exact measure and PL_BUDGET decision.
|
|
11
|
-
const measures = new Map();
|
|
5
|
+
const measurePolicy = policyMeasurer(context.environment.candidate.origin, context.normalized, (ref) => context.isLocalPolicy(ref));
|
|
12
6
|
for (const name of Object.keys(context.environment.candidate.policies)) {
|
|
13
|
-
const measure = measurePolicy(
|
|
7
|
+
const measure = measurePolicy(name);
|
|
14
8
|
if (measure === undefined)
|
|
15
9
|
continue;
|
|
16
|
-
const pointer = `/policies/${
|
|
10
|
+
const pointer = `/policies/${escapePointerToken(name)}`;
|
|
17
11
|
if (measure.depth > MAX_PATTERN_DEPTH) {
|
|
18
12
|
context.add('PL_BUDGET', pointer, `Expanded Pattern depth ${measure.depth} exceeds ${MAX_PATTERN_DEPTH}.`);
|
|
19
13
|
}
|
|
@@ -29,104 +23,3 @@ export function validatePolicyBudgets(context) {
|
|
|
29
23
|
}
|
|
30
24
|
}
|
|
31
25
|
}
|
|
32
|
-
function measurePolicy(context, name, visiting, measures) {
|
|
33
|
-
if (visiting.has(name))
|
|
34
|
-
return undefined;
|
|
35
|
-
if (measures.has(name))
|
|
36
|
-
return measures.get(name);
|
|
37
|
-
const declaration = context.normalized.get(name);
|
|
38
|
-
if (declaration === undefined)
|
|
39
|
-
return undefined;
|
|
40
|
-
const nested = new Set(visiting);
|
|
41
|
-
nested.add(name);
|
|
42
|
-
const measure = measureDeclaration(context, declaration, nested, measures);
|
|
43
|
-
measures.set(name, measure);
|
|
44
|
-
return measure;
|
|
45
|
-
}
|
|
46
|
-
function measureDeclaration(context, declaration, nested, measures) {
|
|
47
|
-
if ('match' in declaration) {
|
|
48
|
-
return measurePattern(declaration.match, context.environment.candidate.origin);
|
|
49
|
-
}
|
|
50
|
-
const refs = 'allOf' in declaration ? declaration.allOf : declaration.anyOf;
|
|
51
|
-
const children = refs.flatMap((ref) => {
|
|
52
|
-
if (!context.isLocalPolicy(ref))
|
|
53
|
-
return [];
|
|
54
|
-
const value = measurePolicy(context, ref.name, nested, measures);
|
|
55
|
-
return value === undefined ? [] : [value];
|
|
56
|
-
});
|
|
57
|
-
if (children.length !== refs.length || children.length === 0)
|
|
58
|
-
return undefined;
|
|
59
|
-
return combineMeasures('allOf' in declaration ? 'allOf' : 'anyOf', children);
|
|
60
|
-
}
|
|
61
|
-
function measurePattern(pattern, ownOrigin) {
|
|
62
|
-
if ('sameNode' in pattern)
|
|
63
|
-
return {
|
|
64
|
-
depth: 1,
|
|
65
|
-
edgeCounts: [1],
|
|
66
|
-
domains: new Set([
|
|
67
|
-
ownOrigin,
|
|
68
|
-
...[pattern.sameNode.left, pattern.sameNode.right].flatMap((term) => term.kind === 'ref' ? [policyObjectOrigin(term.ref)] : []),
|
|
69
|
-
]),
|
|
70
|
-
};
|
|
71
|
-
if (isEdge(pattern)) {
|
|
72
|
-
return {
|
|
73
|
-
depth: 1,
|
|
74
|
-
edgeCounts: [1],
|
|
75
|
-
domains: new Set([
|
|
76
|
-
ownOrigin,
|
|
77
|
-
pattern.class.origin,
|
|
78
|
-
...[pattern.source, pattern.target].flatMap((term) => term.kind === 'ref' ? [policyObjectOrigin(term.ref)] : []),
|
|
79
|
-
]),
|
|
80
|
-
};
|
|
81
|
-
}
|
|
82
|
-
if ('exists' in pattern) {
|
|
83
|
-
const child = measurePattern(pattern.exists.where, ownOrigin);
|
|
84
|
-
return {
|
|
85
|
-
depth: child.depth + 1,
|
|
86
|
-
edgeCounts: child.edgeCounts,
|
|
87
|
-
domains: new Set([
|
|
88
|
-
...child.domains,
|
|
89
|
-
...pattern.exists.nodes.flatMap((node) => {
|
|
90
|
-
const ref = policyVariableClass(node);
|
|
91
|
-
return ref === undefined ? [] : [ref.origin];
|
|
92
|
-
}),
|
|
93
|
-
]),
|
|
94
|
-
};
|
|
95
|
-
}
|
|
96
|
-
const { key, values } = patternOperands(pattern);
|
|
97
|
-
return combineMeasures(key, values.map((child) => measurePattern(child, ownOrigin)));
|
|
98
|
-
}
|
|
99
|
-
function combineMeasures(key, children) {
|
|
100
|
-
const depth = 1 + Math.max(0, ...children.map((child) => child.depth));
|
|
101
|
-
const domains = new Set(children.flatMap((child) => [...child.domains]));
|
|
102
|
-
if (key === 'anyOf') {
|
|
103
|
-
return {
|
|
104
|
-
depth,
|
|
105
|
-
edgeCounts: children.flatMap((child) => child.edgeCounts).slice(0, BRANCH_OVERFLOW),
|
|
106
|
-
domains,
|
|
107
|
-
};
|
|
108
|
-
}
|
|
109
|
-
let edgeCounts = [0];
|
|
110
|
-
for (const child of children) {
|
|
111
|
-
const next = [];
|
|
112
|
-
for (const left of edgeCounts) {
|
|
113
|
-
for (const right of child.edgeCounts) {
|
|
114
|
-
next.push(left + right);
|
|
115
|
-
if (next.length >= BRANCH_OVERFLOW)
|
|
116
|
-
break;
|
|
117
|
-
}
|
|
118
|
-
if (next.length >= BRANCH_OVERFLOW)
|
|
119
|
-
break;
|
|
120
|
-
}
|
|
121
|
-
edgeCounts = next;
|
|
122
|
-
}
|
|
123
|
-
return { depth, edgeCounts, domains };
|
|
124
|
-
}
|
|
125
|
-
function patternOperands(value) {
|
|
126
|
-
return 'allOf' in value
|
|
127
|
-
? { key: 'allOf', values: value.allOf }
|
|
128
|
-
: { key: 'anyOf', values: value.anyOf };
|
|
129
|
-
}
|
|
130
|
-
function isEdge(value) {
|
|
131
|
-
return 'source' in value;
|
|
132
|
-
}
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import type { ClassDefinition } from '../../../language/class/model/class.js';
|
|
2
|
-
import type {
|
|
2
|
+
import type { ObjectContract, PolicyTypingScope } from '../../../language/policy/analysis/typing.js';
|
|
3
|
+
import type { PolicyExpression, PolicyObjectRef } from '../../../language/policy/model/policy.js';
|
|
3
4
|
import type { DefinitionAnalysisResult } from '../../analysis/class.js';
|
|
4
5
|
import type { SchemaDiagnostic, SchemaDiagnosticCode } from '../../diagnostic.js';
|
|
5
6
|
import type { DomainResolutionContext } from '../../resolution/context.js';
|
|
6
7
|
import type { IndexedClassDefinition } from '../../resolution/definition.index.js';
|
|
7
8
|
import { type ClassRef, type PolicyRef } from '../../../addressing/index.js';
|
|
8
|
-
export declare class PolicyValidationContext {
|
|
9
|
+
export declare class PolicyValidationContext implements PolicyTypingScope {
|
|
9
10
|
#private;
|
|
10
11
|
readonly environment: DomainResolutionContext;
|
|
11
12
|
readonly analysis: DefinitionAnalysisResult;
|
|
@@ -23,6 +24,7 @@ export declare class PolicyValidationContext {
|
|
|
23
24
|
}> | undefined;
|
|
24
25
|
/** Return true when candidate is the required Class or a same-kind descendant. */
|
|
25
26
|
classExtends(candidate: ClassRef, required: ClassRef): boolean;
|
|
27
|
+
/** The contract a Policy pattern reference types, reporting an invalid one under `/policies`. */
|
|
28
|
+
referenceContract(ref: PolicyObjectRef): ObjectContract | undefined;
|
|
26
29
|
add(code: SchemaDiagnosticCode, pointer: string, message: string, ref?: string): void;
|
|
27
30
|
}
|
|
28
|
-
export declare function escapePointer(value: string): string;
|
|
@@ -2,6 +2,7 @@ import { Key } from '../../../addressing/index.js';
|
|
|
2
2
|
import { definitionRefIndex } from '../../../addressing/ordering.js';
|
|
3
3
|
import { classSatisfies } from '../../../language/class/inheritance.js';
|
|
4
4
|
import { diagnostic } from '../../diagnostic.js';
|
|
5
|
+
import { referenceContracts } from './objects.js';
|
|
5
6
|
const formatDefinitionRef = Key.of;
|
|
6
7
|
export class PolicyValidationContext {
|
|
7
8
|
environment;
|
|
@@ -69,10 +70,11 @@ export class PolicyValidationContext {
|
|
|
69
70
|
const target = resolve(required);
|
|
70
71
|
return actual !== undefined && target !== undefined && classSatisfies(actual, target, resolve);
|
|
71
72
|
}
|
|
73
|
+
/** The contract a Policy pattern reference types, reporting an invalid one under `/policies`. */
|
|
74
|
+
referenceContract(ref) {
|
|
75
|
+
return referenceContracts(this, ref, '/policies')?.[0];
|
|
76
|
+
}
|
|
72
77
|
add(code, pointer, message, ref) {
|
|
73
78
|
this.diagnostics.push(diagnostic(code, pointer, message, ref));
|
|
74
79
|
}
|
|
75
80
|
}
|
|
76
|
-
export function escapePointer(value) {
|
|
77
|
-
return value.replaceAll('~', '~0').replaceAll('/', '~1');
|
|
78
|
-
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import type { PolicyObjectRef } from '../../../language/index.js';
|
|
2
|
+
import type { ObjectContract } from '../../../language/policy/analysis/typing.js';
|
|
2
3
|
import type { PolicyValidationContext } from './context.js';
|
|
3
|
-
import type { ObjectContract } from './typing.js';
|
|
4
4
|
export declare function referenceContracts(validation: PolicyValidationContext, ref: PolicyObjectRef, pointer: string): readonly ObjectContract[] | undefined;
|