@astrale-os/kernel-dsl 0.2.0-beta.42 → 0.2.0-beta.44
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/compiled/admission/accept.js +10 -27
- package/dist/v1/compiled/admission/semantics.js +19 -4
- package/dist/v1/compiled/loader/load.d.ts +2 -11
- package/dist/v1/compiled/loader/load.js +1 -3
- package/dist/v1/language/policy/analysis/checks.d.ts +28 -0
- package/dist/v1/language/policy/analysis/checks.js +80 -0
- package/dist/v1/language/value/evaluate/evaluator.js +29 -11
- package/dist/v1/language/value/profile/rules.js +25 -0
- package/dist/v1/language/value/regexp/compile.d.ts +6 -0
- package/dist/v1/language/value/regexp/compile.js +18 -0
- package/dist/v1/schema/compatibility/api.d.ts +2 -1
- package/dist/v1/schema/compatibility/api.js +1 -0
- package/dist/v1/schema/compatibility/evidence.d.ts +6 -2
- package/dist/v1/schema/compatibility/evidence.js +5 -3
- package/dist/v1/schema/compatibility/index.d.ts +1 -3
- package/dist/v1/schema/compatibility/index.js +0 -1
- package/dist/v1/schema/compatibility/meaning/index.js +1 -1
- package/dist/v1/schema/compatibility/model.d.ts +33 -3
- package/dist/v1/schema/compatibility/scope.d.ts +8 -0
- package/dist/v1/schema/compatibility/scope.js +50 -21
- package/dist/v1/schema/compatibility/structure/index.js +6 -1
- package/dist/v1/schema/compatibility/structure/requirements.js +5 -3
- package/dist/v1/schema/compatibility/subjects.d.ts +5 -0
- package/dist/v1/schema/compatibility/subjects.js +39 -0
- package/dist/v1/schema/compatibility/versioning/index.d.ts +8 -0
- package/dist/v1/schema/compatibility/versioning/index.js +50 -0
- package/dist/v1/schema/compatibility/versioning/members.d.ts +12 -0
- package/dist/v1/schema/compatibility/versioning/members.js +47 -0
- package/dist/v1/schema/compatibility/versioning/surface.d.ts +11 -0
- package/dist/v1/schema/compatibility/versioning/surface.js +61 -0
- package/dist/v1/schema/diagnostic.d.ts +1 -1
- package/dist/v1/schema/index.d.ts +2 -4
- package/dist/v1/schema/index.js +1 -2
- package/dist/v1/schema/validation/policy/typing.d.ts +2 -0
- package/dist/v1/schema/validation/policy/typing.js +5 -1
- package/dist/v1/schema/validation/policy/uses.js +20 -8
- package/package.json +5 -1
- package/dist/v1/schema/compatibility/legacy/index.d.ts +0 -58
- package/dist/v1/schema/compatibility/legacy/index.js +0 -145
|
@@ -4,38 +4,66 @@ import { knownDomainClosure } from '../resolution/closure.js';
|
|
|
4
4
|
import { resolve } from '../resolution/domain.js';
|
|
5
5
|
import { contextOf } from './context.js';
|
|
6
6
|
import { rootReferences, schemaIdentity, usageOf } from './references.js';
|
|
7
|
-
import { coordinateForKey, resolveSubject } from './subjects.js';
|
|
7
|
+
import { capabilityCoordinates, coordinateForKey, resolveSubject } from './subjects.js';
|
|
8
8
|
export function prepare(request) {
|
|
9
|
-
const { target } = request;
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
let roots;
|
|
13
|
-
const dependent = request.scope.kind === 'dependency' ? request.scope.dependent : undefined;
|
|
14
|
-
if (request.scope.kind === 'dependency') {
|
|
15
|
-
const retained = retainedContext(request.scope.dependent, target.origin, '/target/origin');
|
|
16
|
-
source = retained.source;
|
|
17
|
-
before = retained.context;
|
|
18
|
-
roots = rootReferences(request.scope.dependent);
|
|
19
|
-
}
|
|
20
|
-
else {
|
|
21
|
-
source = request.scope.source;
|
|
9
|
+
const { scope, target } = request;
|
|
10
|
+
if (scope.kind === 'members') {
|
|
11
|
+
const { source, roots: keys } = scope;
|
|
22
12
|
if (source.origin !== target.origin)
|
|
23
13
|
throw new SchemaError([
|
|
24
14
|
diagnostic('DM_MEMBER_ORIGIN_MISMATCH', '/target/origin', `Member comparison target ${target.origin} does not match source ${source.origin}.`),
|
|
25
15
|
]);
|
|
26
|
-
const keys = request.scope.roots;
|
|
27
16
|
if (!Array.isArray(keys) || keys.length === 0 || new Set(keys).size !== keys.length) {
|
|
28
17
|
throw new SchemaError([
|
|
29
18
|
diagnostic('DM_MEMBER_ROOT_INVALID', '/roots', 'Member comparison roots must be one non-empty duplicate-free Key set.'),
|
|
30
19
|
]);
|
|
31
20
|
}
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
21
|
+
return selectMembers(source, target, keys);
|
|
22
|
+
}
|
|
23
|
+
const { dependent } = scope;
|
|
24
|
+
const { source, context: before } = retainedContext(dependent, target.origin, '/target/origin');
|
|
25
|
+
const keys = scope.capabilities;
|
|
26
|
+
if (keys !== undefined && !Array.isArray(keys)) {
|
|
27
|
+
throw new SchemaError([
|
|
28
|
+
diagnostic('DM_CAPABILITY_INVALID', '/capabilities', 'Declared capabilities must be one Key list.'),
|
|
29
|
+
]);
|
|
30
|
+
}
|
|
31
|
+
const usage = usageOf(dependent, 'capability');
|
|
32
|
+
const capabilities = capabilityCoordinates(before, source, keys ?? []).map((coordinate) => ({
|
|
33
|
+
coordinate,
|
|
34
|
+
usage,
|
|
35
|
+
}));
|
|
36
|
+
return comparisonContext({
|
|
37
|
+
kind: 'dependency',
|
|
38
|
+
source,
|
|
39
|
+
target,
|
|
40
|
+
dependent,
|
|
41
|
+
before,
|
|
42
|
+
roots: [...rootReferences(dependent), ...capabilities],
|
|
43
|
+
capabilities,
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Select distinct Keys of the source against a target the caller checked to be of the same origin.
|
|
48
|
+
* The selection may be empty, which a public request cannot be: `requiredBump` selects every member
|
|
49
|
+
* of a version that may declare none.
|
|
50
|
+
*/
|
|
51
|
+
export function selectMembers(source, target, keys) {
|
|
52
|
+
resolve(source);
|
|
53
|
+
const before = contextOf(source);
|
|
54
|
+
return comparisonContext({
|
|
55
|
+
kind: 'members',
|
|
56
|
+
source,
|
|
57
|
+
target,
|
|
58
|
+
before,
|
|
59
|
+
roots: keys.map((key, index) => ({
|
|
35
60
|
coordinate: coordinateForKey(before, source, key, index),
|
|
36
61
|
usage: usageOf(source, 'selection', key),
|
|
37
|
-
}))
|
|
38
|
-
|
|
62
|
+
})),
|
|
63
|
+
capabilities: [],
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
function comparisonContext({ kind, source, target, dependent, before, roots, capabilities, }) {
|
|
39
67
|
resolve(target);
|
|
40
68
|
const after = contextOf(target);
|
|
41
69
|
const pairs = new Map();
|
|
@@ -46,8 +74,9 @@ export function prepare(request) {
|
|
|
46
74
|
before,
|
|
47
75
|
after,
|
|
48
76
|
roots,
|
|
77
|
+
capabilities,
|
|
49
78
|
scope: Object.freeze({
|
|
50
|
-
kind
|
|
79
|
+
kind,
|
|
51
80
|
source: schemaIdentity(source),
|
|
52
81
|
target: schemaIdentity(target),
|
|
53
82
|
...(dependent === undefined ? {} : { dependent: schemaIdentity(dependent) }),
|
|
@@ -11,6 +11,11 @@ export function analyzeStructure(context) {
|
|
|
11
11
|
kind: 'structure',
|
|
12
12
|
...result,
|
|
13
13
|
compatible: result.assessments.every(({ satisfied }) => satisfied),
|
|
14
|
-
|
|
14
|
+
// Version 3 adds declared capability roots; without one, evidence keeps its version-2 bytes.
|
|
15
|
+
evidence: evidenceOf({
|
|
16
|
+
kind: 'structure',
|
|
17
|
+
version: context.capabilities.length > 0 ? 3 : 2,
|
|
18
|
+
...result,
|
|
19
|
+
}),
|
|
15
20
|
});
|
|
16
21
|
}
|
|
@@ -55,9 +55,11 @@ export function compareRequirements({ context, report, }) {
|
|
|
55
55
|
}
|
|
56
56
|
if (Key.origin(coordinate.key) !== context.source.origin)
|
|
57
57
|
continue;
|
|
58
|
-
// Class membership does not establish an invocation dependency. Explicit member selection
|
|
59
|
-
//
|
|
60
|
-
const required = facet !== 'method.existence' ||
|
|
58
|
+
// Class membership does not establish an invocation dependency. Explicit member selection,
|
|
59
|
+
// a Method the dependent declares as a capability, and inherited abstract obligations do.
|
|
60
|
+
const required = facet !== 'method.existence' ||
|
|
61
|
+
usage.relation === 'selection' ||
|
|
62
|
+
usage.relation === 'capability';
|
|
61
63
|
report.record({
|
|
62
64
|
pair,
|
|
63
65
|
facet,
|
|
@@ -32,3 +32,8 @@ export declare function resolveSubject({ context, coordinate, }: {
|
|
|
32
32
|
export declare function identityOf(coordinate: Coordinate): Subject;
|
|
33
33
|
/** Resolve one caller-supplied Key of the source into an exact coordinate that exists there. */
|
|
34
34
|
export declare function coordinateForKey(context: Context, source: DomainSchema, input: SemanticKey, index: number): Coordinate;
|
|
35
|
+
/**
|
|
36
|
+
* Resolve the Function, Method and Class Keys a dependent declares as capabilities into exact
|
|
37
|
+
* coordinates of its retained source. Keys of other origins belong to other comparisons.
|
|
38
|
+
*/
|
|
39
|
+
export declare function capabilityCoordinates(context: Context, source: DomainSchema, capabilities: readonly SemanticKey[]): readonly Coordinate[];
|
|
@@ -79,6 +79,45 @@ export function coordinateForKey(context, source, input, index) {
|
|
|
79
79
|
}
|
|
80
80
|
return coordinate;
|
|
81
81
|
}
|
|
82
|
+
/**
|
|
83
|
+
* Resolve the Function, Method and Class Keys a dependent declares as capabilities into exact
|
|
84
|
+
* coordinates of its retained source. Keys of other origins belong to other comparisons.
|
|
85
|
+
*/
|
|
86
|
+
export function capabilityCoordinates(context, source, capabilities) {
|
|
87
|
+
return capabilities.flatMap((key, index) => {
|
|
88
|
+
if (!Key.is(key))
|
|
89
|
+
return invalidCapability(key, index, 'is not a canonical semantic Key');
|
|
90
|
+
if (Key.origin(key) !== source.origin)
|
|
91
|
+
return [];
|
|
92
|
+
const coordinate = capabilityCoordinate(source, key);
|
|
93
|
+
if (coordinate === undefined) {
|
|
94
|
+
return invalidCapability(key, index, 'is not a Function, Method or Class');
|
|
95
|
+
}
|
|
96
|
+
if (resolveSubject({ context, coordinate }) === undefined) {
|
|
97
|
+
return invalidCapability(key, index, `does not exist in the retained ${source.origin}`);
|
|
98
|
+
}
|
|
99
|
+
return [coordinate];
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
/** A capability names a Function, a Method by its declaring Class, or a Class. */
|
|
103
|
+
function capabilityCoordinate(source, key) {
|
|
104
|
+
const { kind, name, member, ownerName } = keyReference(key);
|
|
105
|
+
const { origin } = source;
|
|
106
|
+
if (member === 'method')
|
|
107
|
+
return methodCoordinate({ origin, kind: 'class', name: ownerName }, name);
|
|
108
|
+
if (member !== undefined)
|
|
109
|
+
return undefined;
|
|
110
|
+
if (kind === 'class')
|
|
111
|
+
return classCoordinate({ origin, kind: 'class', name });
|
|
112
|
+
if (kind === 'function')
|
|
113
|
+
return memberCoordinate({ origin, kind: 'function', name });
|
|
114
|
+
return undefined;
|
|
115
|
+
}
|
|
116
|
+
function invalidCapability(key, index, reason) {
|
|
117
|
+
throw new SchemaError([
|
|
118
|
+
diagnostic('DM_CAPABILITY_INVALID', `/capabilities/${index}`, `Capability ${JSON.stringify(key)} ${reason}.`, typeof key === 'string' ? key : undefined),
|
|
119
|
+
]);
|
|
120
|
+
}
|
|
82
121
|
function invalidMemberRoot(key, index, reason) {
|
|
83
122
|
throw new SchemaError([
|
|
84
123
|
diagnostic('DM_MEMBER_ROOT_INVALID', `/roots/${index}`, `Key ${JSON.stringify(key)} ${reason}.`, typeof key === 'string' ? key : undefined),
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { DomainSchema } from '../../model/schema.js';
|
|
2
|
+
import type { RequiredBump } from '../model.js';
|
|
3
|
+
/**
|
|
4
|
+
* Minimum schema bump: unsatisfied requirements are major, additions minor, other changes patch.
|
|
5
|
+
* Concrete callable authorization and code behavior are outside this floor; abstract Method auth
|
|
6
|
+
* belongs to the inherited signature. Version arithmetic stays with the caller.
|
|
7
|
+
*/
|
|
8
|
+
export declare function requiredBump(previous: DomainSchema, next: DomainSchema): RequiredBump;
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { deepFreeze } from '../../canonical/freeze.js';
|
|
2
|
+
import { SchemaError, diagnostic } from '../../diagnostic.js';
|
|
3
|
+
import { compareFootprint } from '../meaning/footprint.js';
|
|
4
|
+
import { schemaIdentity } from '../references.js';
|
|
5
|
+
import { createReport } from '../report.js';
|
|
6
|
+
import { selectMembers } from '../scope.js';
|
|
7
|
+
import { compareRequirements } from '../structure/requirements.js';
|
|
8
|
+
import { compareAdditions, memberCoordinates } from './members.js';
|
|
9
|
+
import { compareSurface } from './surface.js';
|
|
10
|
+
const LEVELS = ['patch', 'minor', 'major'];
|
|
11
|
+
/**
|
|
12
|
+
* Minimum schema bump: unsatisfied requirements are major, additions minor, other changes patch.
|
|
13
|
+
* Concrete callable authorization and code behavior are outside this floor; abstract Method auth
|
|
14
|
+
* belongs to the inherited signature. Version arithmetic stays with the caller.
|
|
15
|
+
*/
|
|
16
|
+
export function requiredBump(previous, next) {
|
|
17
|
+
if (previous.origin !== next.origin)
|
|
18
|
+
throw new SchemaError([
|
|
19
|
+
diagnostic('DM_MEMBER_ORIGIN_MISMATCH', '/next/origin', `Next version ${next.origin} does not match previous version ${previous.origin}.`),
|
|
20
|
+
]);
|
|
21
|
+
const identities = { previous: schemaIdentity(previous), next: schemaIdentity(next) };
|
|
22
|
+
if (identities.previous.revision === identities.next.revision)
|
|
23
|
+
return deepFreeze({ level: 'patch', ...identities, changes: [] });
|
|
24
|
+
const members = memberCoordinates(previous);
|
|
25
|
+
const context = selectMembers(previous, next, members.map(({ key }) => key));
|
|
26
|
+
// Surface observations must win when an existing obligation becomes newly imposed on a Class.
|
|
27
|
+
const report = createReport();
|
|
28
|
+
compareSurface({ context, report });
|
|
29
|
+
compareRequirements({ context, report });
|
|
30
|
+
compareFootprint({ context, report });
|
|
31
|
+
compareAdditions({ context, report, previous: members });
|
|
32
|
+
const { entries, assessments } = report.finish();
|
|
33
|
+
const unsatisfied = new Set(assessments.filter(({ satisfied }) => !satisfied).map(({ observation }) => observation.id));
|
|
34
|
+
const changes = entries.flatMap(({ subject, observations }) => observations
|
|
35
|
+
.filter(({ state }) => state !== 'equal')
|
|
36
|
+
.map((observation) => ({
|
|
37
|
+
subject,
|
|
38
|
+
observation,
|
|
39
|
+
bump: unsatisfied.has(observation.id)
|
|
40
|
+
? 'major'
|
|
41
|
+
: observation.state === 'added'
|
|
42
|
+
? 'minor'
|
|
43
|
+
: 'patch',
|
|
44
|
+
})));
|
|
45
|
+
return deepFreeze({
|
|
46
|
+
level: changes.reduce((level, { bump }) => (LEVELS.indexOf(bump) > LEVELS.indexOf(level) ? bump : level), 'patch'),
|
|
47
|
+
...identities,
|
|
48
|
+
changes,
|
|
49
|
+
});
|
|
50
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { DomainSchema } from '../../model/schema.js';
|
|
2
|
+
import type { Coordinate } from '../coordinate.js';
|
|
3
|
+
import type { Report } from '../report.js';
|
|
4
|
+
import type { ComparisonContext } from '../scope.js';
|
|
5
|
+
/** Inherited members count once, at their declaring Class. */
|
|
6
|
+
export declare function memberCoordinates(schema: DomainSchema): readonly Coordinate[];
|
|
7
|
+
/** New declarations impose no requirements here; the surface pass checks obligations on existing Classes. */
|
|
8
|
+
export declare function compareAdditions({ context, report, previous, }: {
|
|
9
|
+
readonly context: ComparisonContext;
|
|
10
|
+
readonly report: Report;
|
|
11
|
+
readonly previous: readonly Coordinate[];
|
|
12
|
+
}): void;
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { compareFacet } from '../comparators/index.js';
|
|
2
|
+
import { classCoordinate, memberCoordinate, methodCoordinate, propertyCoordinate, } from '../coordinate.js';
|
|
3
|
+
/** Inherited members count once, at their declaring Class. */
|
|
4
|
+
export function memberCoordinates(schema) {
|
|
5
|
+
const result = [];
|
|
6
|
+
for (const declaration of Object.values(schema.classes)) {
|
|
7
|
+
result.push(classCoordinate(declaration.ref));
|
|
8
|
+
for (const name of Object.keys(declaration.properties))
|
|
9
|
+
result.push(propertyCoordinate(declaration.ref, name));
|
|
10
|
+
if (declaration.kind === 'node')
|
|
11
|
+
for (const name of Object.keys(declaration.methods))
|
|
12
|
+
result.push(methodCoordinate(declaration.ref, name));
|
|
13
|
+
}
|
|
14
|
+
for (const { ref } of [
|
|
15
|
+
...Object.values(schema.functions),
|
|
16
|
+
...Object.values(schema.policies),
|
|
17
|
+
...Object.values(schema.views),
|
|
18
|
+
...Object.values(schema.core.nodes),
|
|
19
|
+
])
|
|
20
|
+
result.push(memberCoordinate(ref));
|
|
21
|
+
return result;
|
|
22
|
+
}
|
|
23
|
+
/** New declarations impose no requirements here; the surface pass checks obligations on existing Classes. */
|
|
24
|
+
export function compareAdditions({ context, report, previous, }) {
|
|
25
|
+
const known = new Set(previous.map(({ key }) => key));
|
|
26
|
+
for (const coordinate of memberCoordinates(context.target)) {
|
|
27
|
+
if (known.has(coordinate.key))
|
|
28
|
+
continue;
|
|
29
|
+
const pair = context.pair(coordinate);
|
|
30
|
+
for (const facet of selectedFacets(coordinate))
|
|
31
|
+
report.record({ pair, facet, ...compareFacet({ context, pair, facet }) });
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
function selectedFacets(coordinate) {
|
|
35
|
+
switch (coordinate.kind) {
|
|
36
|
+
case 'class':
|
|
37
|
+
return ['class.reference'];
|
|
38
|
+
case 'property':
|
|
39
|
+
return ['property'];
|
|
40
|
+
case 'method':
|
|
41
|
+
return ['method.existence', 'method.signature'];
|
|
42
|
+
case 'member':
|
|
43
|
+
return coordinate.ref.kind === 'function'
|
|
44
|
+
? ['function.existence', 'function.signature']
|
|
45
|
+
: [coordinate.ref.kind];
|
|
46
|
+
}
|
|
47
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { Report } from '../report.js';
|
|
2
|
+
import type { ComparisonContext } from '../scope.js';
|
|
3
|
+
/**
|
|
4
|
+
* Compare inherited contracts and newly imposed obligations on existing Classes.
|
|
5
|
+
* Obligations are reported at their declaring Method: changed signatures retain their comparison;
|
|
6
|
+
* an unchanged obligation newly inherited by a Class is added and unsatisfied.
|
|
7
|
+
*/
|
|
8
|
+
export declare function compareSurface({ context, report, }: {
|
|
9
|
+
readonly context: ComparisonContext;
|
|
10
|
+
readonly report: Report;
|
|
11
|
+
}): void;
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { methodInheritance } from '../comparators/callables.js';
|
|
2
|
+
import { compareAddedProperties } from '../comparators/classes.js';
|
|
3
|
+
import { compareFacet } from '../comparators/index.js';
|
|
4
|
+
import { fingerprintMeaning } from '../comparators/values.js';
|
|
5
|
+
import { classCoordinate, methodCoordinate } from '../coordinate.js';
|
|
6
|
+
/**
|
|
7
|
+
* Compare inherited contracts and newly imposed obligations on existing Classes.
|
|
8
|
+
* Obligations are reported at their declaring Method: changed signatures retain their comparison;
|
|
9
|
+
* an unchanged obligation newly inherited by a Class is added and unsatisfied.
|
|
10
|
+
*/
|
|
11
|
+
export function compareSurface({ context, report, }) {
|
|
12
|
+
const established = new Map();
|
|
13
|
+
const imposed = new Map();
|
|
14
|
+
for (const declaration of Object.values(context.source.classes)) {
|
|
15
|
+
if (context.target.classes[declaration.ref.name] === undefined)
|
|
16
|
+
continue;
|
|
17
|
+
const pair = context.pair(classCoordinate(declaration.ref));
|
|
18
|
+
const { source, target } = pair;
|
|
19
|
+
if (source?.kind !== 'class' || target?.kind !== 'class')
|
|
20
|
+
continue;
|
|
21
|
+
report.record({
|
|
22
|
+
pair,
|
|
23
|
+
facet: 'class.inherit',
|
|
24
|
+
...compareFacet({ context, pair, facet: 'class.inherit' }),
|
|
25
|
+
requirement: { kind: 'class.inherit' },
|
|
26
|
+
});
|
|
27
|
+
compareAddedProperties({ context, pair, report });
|
|
28
|
+
for (const [key, method] of source.analysis.requirements)
|
|
29
|
+
if (!established.has(key))
|
|
30
|
+
established.set(key, method);
|
|
31
|
+
for (const [key, method] of target.analysis.requirements)
|
|
32
|
+
if (!source.analysis.requirements.has(key) && !imposed.has(key))
|
|
33
|
+
imposed.set(key, method);
|
|
34
|
+
}
|
|
35
|
+
for (const key of new Set([...established.keys(), ...imposed.keys()])) {
|
|
36
|
+
const added = imposed.get(key);
|
|
37
|
+
const method = established.get(key) ?? added;
|
|
38
|
+
const pair = context.pair(methodCoordinate(method.owner, method.name));
|
|
39
|
+
const comparison = established.has(key)
|
|
40
|
+
? compareFacet({ context, pair, facet: 'method.inheritance' })
|
|
41
|
+
: undefined;
|
|
42
|
+
report.record(comparison !== undefined && (added === undefined || changed(comparison))
|
|
43
|
+
? {
|
|
44
|
+
pair,
|
|
45
|
+
facet: 'method.inheritance',
|
|
46
|
+
...comparison,
|
|
47
|
+
requirement: { kind: 'method.inheritance' },
|
|
48
|
+
}
|
|
49
|
+
: {
|
|
50
|
+
pair,
|
|
51
|
+
facet: 'method.inheritance',
|
|
52
|
+
after: methodInheritance(added),
|
|
53
|
+
requirement: { kind: 'method.inheritance', satisfied: false },
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
function changed({ before, after }) {
|
|
58
|
+
return (before === undefined ||
|
|
59
|
+
after === undefined ||
|
|
60
|
+
fingerprintMeaning(before) !== fingerprintMeaning(after));
|
|
61
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export type SchemaDiagnosticCode = 'DM_MEMBER_ROOT_INVALID' | 'DM_DEPENDENCY_CONTEXT_INVALID' | 'DM_DEPENDENCY_NOT_RETAINED' | 'DM_MEANING_INCONSISTENT' | 'DM_MEMBER_ORIGIN_MISMATCH' | 'DV_ACCEPTANCE_CONTEXT' | 'DV_CALLABLE_AUTH' | 'DV_CALLABLE_IO' | 'DV_CALLABLE_POLICY_AUTH' | 'DV_CLASS_ABSTRACT' | 'DV_CLASS_ACYCLIC' | 'DV_CLASS_KIND' | 'DV_CORE_EDGE_CLASS' | 'DV_CORE_EDGE_CONSTRAINTS' | 'DV_CORE_EDGE_KEY' | 'DV_CORE_EDGE_PROPERTIES' | 'DV_CORE_NODE_CLASS' | 'DV_CORE_NODE_PROPERTIES' | 'DV_CORE_REF' | 'DV_DATA_COMPOSITION' | 'DV_DEPENDENCY_ACYCLIC' | 'DV_DEPENDENCY_DIRECT' | 'DV_DEPENDENCY_EXACT' | 'DV_DEPENDENCY_LIMITS' | 'DV_DEPENDENCY_REVISION_COHERENCE' | 'DV_DEPENDENCY_UNIQUE' | 'DV_EDGE_ACCEPTS' | 'DV_EDGE_CLASS_COMPOSITION' | 'DV_EDGE_CONSTRAINT' | 'DV_EDGE_ROLE' | 'DV_EDGE_UNDIRECTED' | 'DV_JSON_VALUE' | 'DV_METHOD_ABSTRACT' | 'DV_METHOD_COMPOSITION' | 'DV_PROPERTY_AUTO' | 'DV_PROPERTY_COMPOSITION' | 'DV_PROPERTY_STATE' | 'DV_REF_RESOLVES' | 'DV_VALUE_SCHEMA' | 'DV_VIEW_TARGET' | 'PL_BUDGET' | 'PL_COMPOSITION' | 'PL_CONNECTIVITY' | 'PL_REF' | 'PL_REPEAT' | 'PL_TARGET' | 'PL_TYPING' | 'PL_USE' | 'PL_VARIABLE_SCOPE' | 'STRUCTURE_ABSTRACT_POLICY' | 'STRUCTURE_CALLABLE_POLICY_AUTH' | 'STRUCTURE_DUPLICATE_ITEM' | 'STRUCTURE_EDGE_ORIENTATION' | 'STRUCTURE_FORMAT' | 'STRUCTURE_INVALID' | 'STRUCTURE_KEY' | 'STRUCTURE_MAXIMUM' | 'STRUCTURE_MINIMUM' | 'STRUCTURE_MULTIPLE' | 'STRUCTURE_REQUIRED' | 'STRUCTURE_TYPE' | 'STRUCTURE_UNION' | 'STRUCTURE_UNKNOWN_FIELD' | 'STRUCTURE_VALUE' | 'UNSUPPORTED_DOCUMENT_FORMAT' | 'UNSUPPORTED_DSL_VERSION';
|
|
1
|
+
export type SchemaDiagnosticCode = 'DM_CAPABILITY_INVALID' | 'DM_MEMBER_ROOT_INVALID' | 'DM_DEPENDENCY_CONTEXT_INVALID' | 'DM_DEPENDENCY_NOT_RETAINED' | 'DM_MEANING_INCONSISTENT' | 'DM_MEMBER_ORIGIN_MISMATCH' | 'DV_ACCEPTANCE_CONTEXT' | 'DV_CALLABLE_AUTH' | 'DV_CALLABLE_IO' | 'DV_CALLABLE_POLICY_AUTH' | 'DV_CLASS_ABSTRACT' | 'DV_CLASS_ACYCLIC' | 'DV_CLASS_KIND' | 'DV_CORE_EDGE_CLASS' | 'DV_CORE_EDGE_CONSTRAINTS' | 'DV_CORE_EDGE_KEY' | 'DV_CORE_EDGE_PROPERTIES' | 'DV_CORE_NODE_CLASS' | 'DV_CORE_NODE_PROPERTIES' | 'DV_CORE_REF' | 'DV_DATA_COMPOSITION' | 'DV_DEPENDENCY_ACYCLIC' | 'DV_DEPENDENCY_DIRECT' | 'DV_DEPENDENCY_EXACT' | 'DV_DEPENDENCY_LIMITS' | 'DV_DEPENDENCY_REVISION_COHERENCE' | 'DV_DEPENDENCY_UNIQUE' | 'DV_EDGE_ACCEPTS' | 'DV_EDGE_CLASS_COMPOSITION' | 'DV_EDGE_CONSTRAINT' | 'DV_EDGE_ROLE' | 'DV_EDGE_UNDIRECTED' | 'DV_JSON_VALUE' | 'DV_METHOD_ABSTRACT' | 'DV_METHOD_COMPOSITION' | 'DV_PROPERTY_AUTO' | 'DV_PROPERTY_COMPOSITION' | 'DV_PROPERTY_STATE' | 'DV_REF_RESOLVES' | 'DV_VALUE_SCHEMA' | 'DV_VIEW_TARGET' | 'PL_BUDGET' | 'PL_COMPOSITION' | 'PL_CONNECTIVITY' | 'PL_REF' | 'PL_REPEAT' | 'PL_TARGET' | 'PL_TYPING' | 'PL_USE' | 'PL_VARIABLE_SCOPE' | 'STRUCTURE_ABSTRACT_POLICY' | 'STRUCTURE_CALLABLE_POLICY_AUTH' | 'STRUCTURE_DUPLICATE_ITEM' | 'STRUCTURE_EDGE_ORIENTATION' | 'STRUCTURE_FORMAT' | 'STRUCTURE_INVALID' | 'STRUCTURE_KEY' | 'STRUCTURE_MAXIMUM' | 'STRUCTURE_MINIMUM' | 'STRUCTURE_MULTIPLE' | 'STRUCTURE_REQUIRED' | 'STRUCTURE_TYPE' | 'STRUCTURE_UNION' | 'STRUCTURE_UNKNOWN_FIELD' | 'STRUCTURE_VALUE' | 'UNSUPPORTED_DOCUMENT_FORMAT' | 'UNSUPPORTED_DSL_VERSION';
|
|
2
2
|
export interface SchemaDiagnostic<out Code extends SchemaDiagnosticCode = SchemaDiagnosticCode> {
|
|
3
3
|
readonly code: Code;
|
|
4
4
|
readonly pointer: string;
|
|
@@ -11,8 +11,8 @@ export type { SchemaDiagnosticCode as DiagnosticCode } from './diagnostic.js';
|
|
|
11
11
|
export { acceptDomainSchema as accept } from './admission/accept.js';
|
|
12
12
|
export { decodeDomainSchema as decode, encodeDomainSchema as encode, serializeDomainSchema as serialize, } from './codec/schema.js';
|
|
13
13
|
export { revisionOfDomainSchema as revision } from './canonical/revision.js';
|
|
14
|
-
export { ANNOTATION_KEYWORDS, FIELD_ROLES, callableFingerprint
|
|
15
|
-
export type {
|
|
14
|
+
export { ANNOTATION_KEYWORDS, FIELD_ROLES, callableFingerprint } from './compatibility/index.js';
|
|
15
|
+
export type { FieldRole, MeaningDifference, MeaningFingerprint } from './compatibility/index.js';
|
|
16
16
|
export * as compatibility from './compatibility/api.js';
|
|
17
17
|
export { header } from './codec/header.js';
|
|
18
18
|
export { DOMAIN_FORMAT as FORMAT, DOMAIN_VERSION as VERSION } from './model/format.js';
|
|
@@ -25,5 +25,3 @@ export type { DomainSchemaHeader as Header } from './codec/header.js';
|
|
|
25
25
|
export type { DomainDependency } from '../addressing/index.js';
|
|
26
26
|
/** The versioned value-schema grammar embedded in declarations. */
|
|
27
27
|
export * as values from './validation/value-api.js';
|
|
28
|
-
export { compareDependencyStructure } from './compatibility/index.js';
|
|
29
|
-
export type { DependencyStructureComparison } from './compatibility/index.js';
|
package/dist/v1/schema/index.js
CHANGED
|
@@ -4,7 +4,7 @@ export const resolve = resolveDomainSchema;
|
|
|
4
4
|
export { acceptDomainSchema as accept } from './admission/accept.js';
|
|
5
5
|
export { decodeDomainSchema as decode, encodeDomainSchema as encode, serializeDomainSchema as serialize, } from './codec/schema.js';
|
|
6
6
|
export { revisionOfDomainSchema as revision } from './canonical/revision.js';
|
|
7
|
-
export { ANNOTATION_KEYWORDS, FIELD_ROLES, callableFingerprint
|
|
7
|
+
export { ANNOTATION_KEYWORDS, FIELD_ROLES, callableFingerprint } from './compatibility/index.js';
|
|
8
8
|
export * as compatibility from './compatibility/api.js';
|
|
9
9
|
export { header } from './codec/header.js';
|
|
10
10
|
export { DOMAIN_FORMAT as FORMAT, DOMAIN_VERSION as VERSION } from './model/format.js';
|
|
@@ -12,4 +12,3 @@ export { compile, load } from '../compiled/index.js';
|
|
|
12
12
|
export { SchemaError as Error } from './diagnostic.js';
|
|
13
13
|
/** The versioned value-schema grammar embedded in declarations. */
|
|
14
14
|
export * as values from './validation/value-api.js';
|
|
15
|
-
export { compareDependencyStructure } from './compatibility/index.js';
|
|
@@ -12,6 +12,8 @@ export declare class PolicyTyping {
|
|
|
12
12
|
constructor(context: PolicyValidationContext);
|
|
13
13
|
validatePolicies(): void;
|
|
14
14
|
policyAccepts(name: string, object: ObjectContract): boolean;
|
|
15
|
+
/** Whether each normalized branch of the Policy is typed for an object of this contract. */
|
|
16
|
+
policyBranchTyping(name: string, object: ObjectContract): readonly boolean[] | undefined;
|
|
15
17
|
policyAcceptsEdge(name: string, source: ObjectContract, target: ObjectContract): boolean;
|
|
16
18
|
concreteEndpointClasses(accepts: readonly ClassRef[]): readonly ClassRef[];
|
|
17
19
|
endpointsOverlap(left: readonly ClassRef[], right: readonly ClassRef[]): boolean;
|
|
@@ -35,9 +35,13 @@ export class PolicyTyping {
|
|
|
35
35
|
}
|
|
36
36
|
}
|
|
37
37
|
policyAccepts(name, object) {
|
|
38
|
+
return this.policyBranchTyping(name, object)?.every(Boolean) ?? false;
|
|
39
|
+
}
|
|
40
|
+
/** Whether each normalized branch of the Policy is typed for an object of this contract. */
|
|
41
|
+
policyBranchTyping(name, object) {
|
|
38
42
|
const branches = this.#expandedBranches(name, new Set(), `$use:${name}`);
|
|
39
43
|
const use = { kind: 'object', object };
|
|
40
|
-
return branches
|
|
44
|
+
return branches?.map((branch) => this.#branchIsTyped(branch, use));
|
|
41
45
|
}
|
|
42
46
|
policyAcceptsEdge(name, source, target) {
|
|
43
47
|
const branches = this.#expandedBranches(name, new Set(), `$edge-use:${name}`);
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Key } from '../../../addressing/index.js';
|
|
2
2
|
import { definitionRefIndex } from '../../../addressing/ordering.js';
|
|
3
3
|
import { isBinaryInput } from '../../../language/callable/model/callable.js';
|
|
4
|
+
import { admittedCheckClasses, neverAdmittedClasses, } from '../../../language/policy/analysis/checks.js';
|
|
4
5
|
import { normalizePolicyChecks } from '../../../language/policy/normalization/normalize.js';
|
|
5
6
|
import { GRAPH_NODE_PATH_SCHEMA_ID } from '../../../language/value/syntax/node-path/schema.js';
|
|
6
7
|
import { MAX_BRANCHES, MAX_CHECK_DEPTH, MAX_CHECK_LEAVES, measureChecks } from './budgets.js';
|
|
@@ -89,9 +90,13 @@ function validateChecks(validation, typing, expression, context) {
|
|
|
89
90
|
measure.leaves > MAX_CHECK_LEAVES) {
|
|
90
91
|
validation.add('PL_BUDGET', context.pointer, `Callable checks require depth <= ${MAX_CHECK_DEPTH}, branches <= ${MAX_BRANCHES}, and leaves <= ${MAX_CHECK_LEAVES}; got ${measure.depth}, ${measure.branches}, ${measure.leaves}.`);
|
|
91
92
|
}
|
|
92
|
-
|
|
93
|
+
const leaves = new Map();
|
|
94
|
+
visitChecks(validation, typing, normalized, context, context.pointer, leaves);
|
|
95
|
+
for (const [object, className] of neverAdmittedClasses(normalized, leaves)) {
|
|
96
|
+
validation.add('PL_USE', context.pointer, `Callable checks can never admit ${object} of Class ${className}.`, className);
|
|
97
|
+
}
|
|
93
98
|
}
|
|
94
|
-
function visitChecks(validation, typing, expression, context, pointer) {
|
|
99
|
+
function visitChecks(validation, typing, expression, context, pointer, leaves) {
|
|
95
100
|
if ('sameNode' in expression) {
|
|
96
101
|
for (const [side, object] of Object.entries(expression.sameNode)) {
|
|
97
102
|
const objectPointer = `${pointer}/sameNode/${side}`;
|
|
@@ -115,19 +120,26 @@ function visitChecks(validation, typing, expression, context, pointer) {
|
|
|
115
120
|
: referenceContracts(validation, expression.object.ref, objectPointer);
|
|
116
121
|
if (contracts === undefined)
|
|
117
122
|
return;
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
+
const typings = new Map(contracts.map((contract) => [
|
|
124
|
+
contractName(contract),
|
|
125
|
+
typing.policyBranchTyping(expression.check.name, contract),
|
|
126
|
+
]));
|
|
127
|
+
const admitted = admittedCheckClasses(typings);
|
|
128
|
+
if (admitted === undefined) {
|
|
129
|
+
validation.add('PL_USE', pointer, `${formatDefinitionRef(expression.check)} is incompatible with the callable object contract.`, formatDefinitionRef(expression.check));
|
|
130
|
+
return;
|
|
123
131
|
}
|
|
132
|
+
leaves.set(expression, { accepted: [...typings.keys()], admitted });
|
|
124
133
|
return;
|
|
125
134
|
}
|
|
126
135
|
const { key, values } = checkOperands(expression);
|
|
127
136
|
if (values.length === 0) {
|
|
128
137
|
validation.add('PL_COMPOSITION', `${pointer}/${key}`, `${key} must be non-empty.`);
|
|
129
138
|
}
|
|
130
|
-
values.forEach((child, index) => visitChecks(validation, typing, child, context, `${pointer}/${key}/${index}
|
|
139
|
+
values.forEach((child, index) => visitChecks(validation, typing, child, context, `${pointer}/${key}/${index}`, leaves));
|
|
140
|
+
}
|
|
141
|
+
function contractName(contract) {
|
|
142
|
+
return contract.kind === 'exact' ? formatDefinitionRef(contract.ref) : 'default';
|
|
131
143
|
}
|
|
132
144
|
function selfContracts(validation, context, pointer) {
|
|
133
145
|
if (!context.instanceMethod || context.receiver === undefined) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@astrale-os/kernel-dsl",
|
|
3
|
-
"version": "0.2.0-beta.
|
|
3
|
+
"version": "0.2.0-beta.44",
|
|
4
4
|
"description": "Astrale Kernel DSL - Schema authoring, inference, and IR serialization",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"repository": {
|
|
@@ -67,6 +67,10 @@
|
|
|
67
67
|
"types": "./dist/v1/schema/index.d.ts",
|
|
68
68
|
"import": "./dist/v1/schema/index.js"
|
|
69
69
|
},
|
|
70
|
+
"./v1/schema/compatibility": {
|
|
71
|
+
"types": "./dist/v1/schema/compatibility/api.d.ts",
|
|
72
|
+
"import": "./dist/v1/schema/compatibility/api.js"
|
|
73
|
+
},
|
|
70
74
|
"./v1/schema/fingerprint": {
|
|
71
75
|
"types": "./dist/v1/schema/fingerprint.d.ts",
|
|
72
76
|
"import": "./dist/v1/schema/fingerprint.js"
|
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
/** Historical positional APIs and version-3 wire evidence. New analyses never depend on this adapter. */
|
|
2
|
-
import type { Value as JsonValue } from '@astrale-os/kernel-dsl/value';
|
|
3
|
-
import type { Definition, Key as SemanticKey, Revision } from '../../../addressing/index.js';
|
|
4
|
-
import type { DomainSchema } from '../../model/schema.js';
|
|
5
|
-
import type { MeaningDifference } from '../diff.js';
|
|
6
|
-
import type { DependencyMeaningLevel } from '../fields.js';
|
|
7
|
-
import type { Fingerprint as MeaningFingerprint } from '../model.js';
|
|
8
|
-
export type DependencyMeaningChangeKind = 'missing' | 'changed' | 'added';
|
|
9
|
-
export type DependencyMeaningChange = {
|
|
10
|
-
readonly key: SemanticKey;
|
|
11
|
-
readonly kind: 'missing';
|
|
12
|
-
readonly sourceFingerprint: MeaningFingerprint;
|
|
13
|
-
} | {
|
|
14
|
-
readonly key: SemanticKey;
|
|
15
|
-
readonly kind: 'changed';
|
|
16
|
-
readonly sourceFingerprint: MeaningFingerprint;
|
|
17
|
-
readonly targetFingerprint: MeaningFingerprint;
|
|
18
|
-
/** Every exact path of the meaning that differs, in JSON Pointer order. */
|
|
19
|
-
readonly differences: readonly MeaningDifference[];
|
|
20
|
-
} | {
|
|
21
|
-
/** A required, non-automatic Property the target adds to an inherited Class. */
|
|
22
|
-
readonly key: SemanticKey;
|
|
23
|
-
readonly kind: 'added';
|
|
24
|
-
readonly targetFingerprint: MeaningFingerprint;
|
|
25
|
-
readonly meaning: JsonValue;
|
|
26
|
-
};
|
|
27
|
-
export interface DependencyMeaningComparison {
|
|
28
|
-
readonly version: 3;
|
|
29
|
-
readonly dependency: SemanticKey.Origin;
|
|
30
|
-
readonly source: Revision;
|
|
31
|
-
readonly target: Revision;
|
|
32
|
-
readonly footprint: readonly SemanticKey[];
|
|
33
|
-
readonly changes: readonly DependencyMeaningChange[];
|
|
34
|
-
readonly fingerprint: MeaningFingerprint;
|
|
35
|
-
}
|
|
36
|
-
/** One footprint entry: the Key, what it is, and for a Class how strongly it is used. */
|
|
37
|
-
export interface FootprintEntry {
|
|
38
|
-
readonly key: SemanticKey;
|
|
39
|
-
readonly kind: Definition.Kind | 'property' | 'method';
|
|
40
|
-
readonly level?: DependencyMeaningLevel;
|
|
41
|
-
}
|
|
42
|
-
export interface DependencyStructureComparison {
|
|
43
|
-
readonly kind: 'structure';
|
|
44
|
-
readonly version: 1;
|
|
45
|
-
readonly dependency: SemanticKey.Origin;
|
|
46
|
-
readonly source: Revision;
|
|
47
|
-
readonly target: Revision;
|
|
48
|
-
readonly footprint: readonly SemanticKey[];
|
|
49
|
-
readonly changes: readonly DependencyMeaningChange[];
|
|
50
|
-
readonly fingerprint: MeaningFingerprint;
|
|
51
|
-
}
|
|
52
|
-
export declare function compareDependencyMeaning(dependent: DomainSchema, target: DomainSchema): DependencyMeaningComparison;
|
|
53
|
-
export declare function compareMemberMeaning(source: DomainSchema, target: DomainSchema, roots: readonly [SemanticKey, ...SemanticKey[]]): DependencyMeaningComparison;
|
|
54
|
-
export declare function compareDependencyStructure(dependent: DomainSchema, target: DomainSchema): DependencyStructureComparison;
|
|
55
|
-
export declare function footprintOf(dependent: DomainSchema, origin: SemanticKey.Origin): readonly FootprintEntry[];
|
|
56
|
-
export declare function meaningOf(schema: DomainSchema, key: SemanticKey, level?: DependencyMeaningLevel): JsonValue;
|
|
57
|
-
/** One line an operator can act on, naming the exact paths of a changed meaning. */
|
|
58
|
-
export declare function describeMeaningChange(change: DependencyMeaningChange): string;
|