@astrale-os/kernel-dsl 0.2.0-beta.38 → 0.2.0-beta.39
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/language/callable/composition.d.ts +13 -1
- package/dist/v1/language/callable/composition.js +13 -3
- package/dist/v1/language/value/evaluate/evaluator.js +66 -10
- package/dist/v1/schema/analysis/class.d.ts +21 -0
- package/dist/v1/schema/analysis/class.js +18 -9
- package/dist/v1/schema/compatibility/api.d.ts +2 -0
- package/dist/v1/schema/compatibility/api.js +1 -0
- package/dist/v1/schema/compatibility/comparators/callables.d.ts +28 -0
- package/dist/v1/schema/compatibility/comparators/callables.js +77 -0
- package/dist/v1/schema/compatibility/comparators/classes.d.ts +28 -0
- package/dist/v1/schema/compatibility/comparators/classes.js +96 -0
- package/dist/v1/schema/compatibility/comparators/index.d.ts +12 -0
- package/dist/v1/schema/compatibility/comparators/index.js +41 -0
- package/dist/v1/schema/compatibility/comparators/members.d.ts +4 -0
- package/dist/v1/schema/compatibility/comparators/members.js +49 -0
- package/dist/v1/schema/compatibility/comparators/values.d.ts +5 -0
- package/dist/v1/schema/compatibility/comparators/values.js +37 -0
- package/dist/v1/schema/compatibility/compare.d.ts +5 -57
- package/dist/v1/schema/compatibility/compare.js +19 -162
- package/dist/v1/schema/compatibility/coordinate.d.ts +2 -7
- package/dist/v1/schema/compatibility/coordinate.js +2 -6
- package/dist/v1/schema/compatibility/evidence.d.ts +5 -0
- package/dist/v1/schema/compatibility/evidence.js +51 -0
- package/dist/v1/schema/compatibility/fields.d.ts +6 -0
- package/dist/v1/schema/compatibility/fields.js +5 -0
- package/dist/v1/schema/compatibility/index.d.ts +5 -4
- package/dist/v1/schema/compatibility/index.js +3 -2
- package/dist/v1/schema/compatibility/legacy/index.d.ts +58 -0
- package/dist/v1/schema/compatibility/legacy/index.js +145 -0
- package/dist/v1/schema/compatibility/meaning/footprint.d.ts +7 -0
- package/dist/v1/schema/compatibility/meaning/footprint.js +57 -0
- package/dist/v1/schema/compatibility/meaning/index.d.ts +3 -0
- package/dist/v1/schema/compatibility/meaning/index.js +14 -0
- package/dist/v1/schema/compatibility/model.d.ts +96 -0
- package/dist/v1/schema/compatibility/model.js +1 -0
- package/dist/v1/schema/compatibility/references.d.ts +30 -0
- package/dist/v1/schema/compatibility/references.js +122 -0
- package/dist/v1/schema/compatibility/report.d.ts +26 -0
- package/dist/v1/schema/compatibility/report.js +100 -0
- package/dist/v1/schema/compatibility/scope.d.ts +27 -0
- package/dist/v1/schema/compatibility/scope.js +78 -0
- package/dist/v1/schema/compatibility/structure/index.d.ts +3 -0
- package/dist/v1/schema/compatibility/structure/index.js +16 -0
- package/dist/v1/schema/compatibility/structure/inheritance.d.ts +7 -0
- package/dist/v1/schema/compatibility/structure/inheritance.js +68 -0
- package/dist/v1/schema/compatibility/structure/requirements.d.ts +7 -0
- package/dist/v1/schema/compatibility/structure/requirements.js +70 -0
- package/dist/v1/schema/compatibility/subjects.d.ts +34 -0
- package/dist/v1/schema/compatibility/subjects.js +86 -0
- package/dist/v1/schema/fingerprint.d.ts +2 -2
- package/dist/v1/schema/fingerprint.js +1 -1
- package/dist/v1/schema/index.d.ts +3 -0
- package/dist/v1/schema/index.js +2 -0
- package/package.json +2 -2
- package/dist/v1/schema/compatibility/footprint.d.ts +0 -25
- package/dist/v1/schema/compatibility/footprint.js +0 -264
- package/dist/v1/schema/compatibility/meaning.d.ts +0 -44
- package/dist/v1/schema/compatibility/meaning.js +0 -234
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { Report } from '../report.js';
|
|
2
|
+
import type { ComparisonContext } from '../scope.js';
|
|
3
|
+
/** Structure follows data and abstract obligations; concrete IO never expands its requirements. */
|
|
4
|
+
export declare function compareRequirements({ context, report, }: {
|
|
5
|
+
readonly context: ComparisonContext;
|
|
6
|
+
readonly report: Report;
|
|
7
|
+
}): void;
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { Key } from '../../../addressing/index.js';
|
|
2
|
+
import { compareAddedProperties } from '../comparators/classes.js';
|
|
3
|
+
import { compareFacet } from '../comparators/index.js';
|
|
4
|
+
import { contextInvalid } from '../context.js';
|
|
5
|
+
import { methodCoordinate } from '../coordinate.js';
|
|
6
|
+
import { callableReferences, classReferences, inherits, memberReferences, usageOf, valueReferences, } from '../references.js';
|
|
7
|
+
/** Structure follows data and abstract obligations; concrete IO never expands its requirements. */
|
|
8
|
+
export function compareRequirements({ context, report, }) {
|
|
9
|
+
const queue = [...context.roots];
|
|
10
|
+
const visited = new Set();
|
|
11
|
+
for (const reference of queue) {
|
|
12
|
+
report.note(reference);
|
|
13
|
+
const { coordinate, usage } = reference;
|
|
14
|
+
const visit = `${coordinate.key}:${usage.relation}`;
|
|
15
|
+
if (visited.has(visit))
|
|
16
|
+
continue;
|
|
17
|
+
visited.add(visit);
|
|
18
|
+
const pair = context.pair(coordinate);
|
|
19
|
+
const subject = pair.source;
|
|
20
|
+
if (subject === undefined)
|
|
21
|
+
throw contextInvalid(`Retained acceptance context has no meaning for ${coordinate.key}.`, coordinate.key);
|
|
22
|
+
let facet;
|
|
23
|
+
switch (subject.kind) {
|
|
24
|
+
case 'class': {
|
|
25
|
+
facet = inherits(usage) ? 'class.inherit' : 'class.reference';
|
|
26
|
+
queue.push(...classReferences(subject, false));
|
|
27
|
+
if (inherits(usage)) {
|
|
28
|
+
queue.push(...classReferences(subject, true));
|
|
29
|
+
for (const method of subject.analysis.requirements.values())
|
|
30
|
+
queue.push({
|
|
31
|
+
coordinate: methodCoordinate(method.owner, method.name),
|
|
32
|
+
usage: usageOf(subject.schema, 'abstract', coordinate.key),
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
break;
|
|
36
|
+
}
|
|
37
|
+
case 'property':
|
|
38
|
+
facet = 'property';
|
|
39
|
+
queue.push(...valueReferences(subject));
|
|
40
|
+
break;
|
|
41
|
+
case 'method': {
|
|
42
|
+
facet = usage.relation === 'abstract' ? 'method.inheritance' : 'method.existence';
|
|
43
|
+
if (facet === 'method.inheritance')
|
|
44
|
+
queue.push(...callableReferences(subject));
|
|
45
|
+
break;
|
|
46
|
+
}
|
|
47
|
+
case 'member': {
|
|
48
|
+
facet =
|
|
49
|
+
subject.coordinate.ref.kind === 'function'
|
|
50
|
+
? 'function.existence'
|
|
51
|
+
: subject.coordinate.ref.kind;
|
|
52
|
+
if (facet !== 'function.existence')
|
|
53
|
+
queue.push(...memberReferences(subject));
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
if (Key.origin(coordinate.key) !== context.source.origin)
|
|
57
|
+
continue;
|
|
58
|
+
// Class membership does not establish an invocation dependency. Explicit member selection
|
|
59
|
+
// and inherited abstract obligations do establish requirements; call tracking can add others.
|
|
60
|
+
const required = facet !== 'method.existence' || usage.relation === 'selection';
|
|
61
|
+
report.record({
|
|
62
|
+
pair,
|
|
63
|
+
facet,
|
|
64
|
+
...compareFacet({ context, pair, facet }),
|
|
65
|
+
...(required ? { requirement: { kind: facet } } : {}),
|
|
66
|
+
});
|
|
67
|
+
if (facet === 'class.inherit')
|
|
68
|
+
compareAddedProperties({ context, pair, report });
|
|
69
|
+
}
|
|
70
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { Key as SemanticKey } from '../../addressing/index.js';
|
|
2
|
+
import type { DefinitionAnalysis, EffectiveField, EffectiveMethod } from '../analysis/class.js';
|
|
3
|
+
import type { DomainSchema } from '../model/schema.js';
|
|
4
|
+
import type { Context } from './context.js';
|
|
5
|
+
import type { ClassCoordinate, Coordinate, MemberCoordinate, MethodCoordinate, PropertyCoordinate } from './coordinate.js';
|
|
6
|
+
import type { Subject } from './model.js';
|
|
7
|
+
export type ResolvedSubject = {
|
|
8
|
+
readonly kind: 'class';
|
|
9
|
+
readonly coordinate: ClassCoordinate;
|
|
10
|
+
readonly schema: DomainSchema;
|
|
11
|
+
readonly analysis: DefinitionAnalysis;
|
|
12
|
+
} | {
|
|
13
|
+
readonly kind: 'property';
|
|
14
|
+
readonly coordinate: PropertyCoordinate;
|
|
15
|
+
readonly schema: DomainSchema;
|
|
16
|
+
readonly field: EffectiveField;
|
|
17
|
+
} | {
|
|
18
|
+
readonly kind: 'method';
|
|
19
|
+
readonly coordinate: MethodCoordinate;
|
|
20
|
+
readonly schema: DomainSchema;
|
|
21
|
+
readonly method: EffectiveMethod;
|
|
22
|
+
} | {
|
|
23
|
+
readonly kind: 'member';
|
|
24
|
+
readonly coordinate: MemberCoordinate;
|
|
25
|
+
readonly schema: DomainSchema;
|
|
26
|
+
};
|
|
27
|
+
/** Resolve a trusted coordinate within one exact accepted closure. */
|
|
28
|
+
export declare function resolveSubject({ context, coordinate, }: {
|
|
29
|
+
readonly context: Context;
|
|
30
|
+
readonly coordinate: Coordinate;
|
|
31
|
+
}): ResolvedSubject | undefined;
|
|
32
|
+
export declare function identityOf(coordinate: Coordinate): Subject;
|
|
33
|
+
/** Resolve one caller-supplied Key of the source into an exact coordinate that exists there. */
|
|
34
|
+
export declare function coordinateForKey(context: Context, source: DomainSchema, input: SemanticKey, index: number): Coordinate;
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { Key } from '../../addressing/index.js';
|
|
2
|
+
import { SchemaError, diagnostic } from '../diagnostic.js';
|
|
3
|
+
import { effectiveDefinition, effectiveMethod } from './context.js';
|
|
4
|
+
import { classCoordinate, keyReference, memberCoordinate, methodCoordinate, propertyCoordinate, } from './coordinate.js';
|
|
5
|
+
/** Resolve a trusted coordinate within one exact accepted closure. */
|
|
6
|
+
export function resolveSubject({ context, coordinate, }) {
|
|
7
|
+
const schema = context.schemas.get(Key.origin(coordinate.key));
|
|
8
|
+
if (schema === undefined)
|
|
9
|
+
return undefined;
|
|
10
|
+
if (coordinate.kind === 'member') {
|
|
11
|
+
const { ref } = coordinate;
|
|
12
|
+
const declaration = ref.kind === 'function'
|
|
13
|
+
? schema.functions[ref.name]
|
|
14
|
+
: ref.kind === 'policy'
|
|
15
|
+
? schema.policies[ref.name]
|
|
16
|
+
: ref.kind === 'view'
|
|
17
|
+
? schema.views[ref.name]
|
|
18
|
+
: schema.core.nodes[ref.name];
|
|
19
|
+
return declaration === undefined ? undefined : { kind: 'member', coordinate, schema };
|
|
20
|
+
}
|
|
21
|
+
const analysis = effectiveDefinition(context, schema, coordinate.kind === 'class' ? coordinate.ref : coordinate.owner);
|
|
22
|
+
if (analysis === undefined)
|
|
23
|
+
return undefined;
|
|
24
|
+
if (coordinate.kind === 'class')
|
|
25
|
+
return { kind: 'class', coordinate, schema, analysis };
|
|
26
|
+
if (coordinate.kind === 'property') {
|
|
27
|
+
const field = analysis.properties.get(coordinate.key);
|
|
28
|
+
return field === undefined ? undefined : { kind: 'property', coordinate, schema, field };
|
|
29
|
+
}
|
|
30
|
+
const method = effectiveMethod(analysis, coordinate.key);
|
|
31
|
+
return method === undefined ? undefined : { kind: 'method', coordinate, schema, method };
|
|
32
|
+
}
|
|
33
|
+
export function identityOf(coordinate) {
|
|
34
|
+
return Object.freeze({
|
|
35
|
+
key: coordinate.key,
|
|
36
|
+
kind: coordinate.kind === 'member' ? coordinate.ref.kind : coordinate.kind,
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
/** Resolve one caller-supplied Key of the source into an exact coordinate that exists there. */
|
|
40
|
+
export function coordinateForKey(context, source, input, index) {
|
|
41
|
+
let key;
|
|
42
|
+
try {
|
|
43
|
+
key = Key(input);
|
|
44
|
+
}
|
|
45
|
+
catch {
|
|
46
|
+
return invalidMemberRoot(input, index, 'is not a canonical semantic Key');
|
|
47
|
+
}
|
|
48
|
+
if (Key.origin(key) !== source.origin) {
|
|
49
|
+
return invalidMemberRoot(key, index, `belongs to ${Key.origin(key)}, not source ${source.origin}`);
|
|
50
|
+
}
|
|
51
|
+
const reference = keyReference(key);
|
|
52
|
+
let coordinate;
|
|
53
|
+
if (reference.member !== undefined && reference.ownerName !== undefined) {
|
|
54
|
+
if (source.classes[reference.ownerName] === undefined) {
|
|
55
|
+
return invalidMemberRoot(key, index, 'has no exact owning Definition');
|
|
56
|
+
}
|
|
57
|
+
const owner = { origin: source.origin, kind: 'class', name: reference.ownerName };
|
|
58
|
+
coordinate =
|
|
59
|
+
reference.member === 'property'
|
|
60
|
+
? propertyCoordinate(owner, reference.name)
|
|
61
|
+
: methodCoordinate(owner, reference.name);
|
|
62
|
+
}
|
|
63
|
+
else if (reference.kind === 'class') {
|
|
64
|
+
if (source.classes[reference.name] === undefined) {
|
|
65
|
+
return invalidMemberRoot(key, index, 'does not exist in the source');
|
|
66
|
+
}
|
|
67
|
+
// Explicit Class selection compares its surface without inferring inheritance.
|
|
68
|
+
coordinate = classCoordinate({ origin: source.origin, kind: 'class', name: reference.name });
|
|
69
|
+
}
|
|
70
|
+
else {
|
|
71
|
+
coordinate = memberCoordinate({
|
|
72
|
+
origin: source.origin,
|
|
73
|
+
kind: reference.kind,
|
|
74
|
+
name: reference.name,
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
if (resolveSubject({ context, coordinate }) === undefined) {
|
|
78
|
+
return invalidMemberRoot(key, index, 'does not exist in the source');
|
|
79
|
+
}
|
|
80
|
+
return coordinate;
|
|
81
|
+
}
|
|
82
|
+
function invalidMemberRoot(key, index, reason) {
|
|
83
|
+
throw new SchemaError([
|
|
84
|
+
diagnostic('DM_MEMBER_ROOT_INVALID', `/roots/${index}`, `Key ${JSON.stringify(key)} ${reason}.`, typeof key === 'string' ? key : undefined),
|
|
85
|
+
]);
|
|
86
|
+
}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export { callableFingerprint } from './compatibility/
|
|
2
|
-
export type { MeaningFingerprint } from './compatibility/
|
|
1
|
+
export { callableFingerprint } from './compatibility/comparators/callables.js';
|
|
2
|
+
export type { Fingerprint as MeaningFingerprint } from './compatibility/model.js';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export { callableFingerprint } from './compatibility/
|
|
1
|
+
export { callableFingerprint } from './compatibility/comparators/callables.js';
|
|
@@ -13,6 +13,7 @@ export { decodeDomainSchema as decode, encodeDomainSchema as encode, serializeDo
|
|
|
13
13
|
export { revisionOfDomainSchema as revision } from './canonical/revision.js';
|
|
14
14
|
export { ANNOTATION_KEYWORDS, FIELD_ROLES, callableFingerprint, compareDependencyMeaning, compareMemberMeaning, describeMeaningChange, footprintOf, meaningOf, } from './compatibility/index.js';
|
|
15
15
|
export type { DependencyMeaningChange, DependencyMeaningChangeKind, DependencyMeaningComparison, DependencyMeaningLevel, FieldRole, FootprintEntry, MeaningDifference, MeaningFingerprint, } from './compatibility/index.js';
|
|
16
|
+
export * as compatibility from './compatibility/api.js';
|
|
16
17
|
export { header } from './codec/header.js';
|
|
17
18
|
export { DOMAIN_FORMAT as FORMAT, DOMAIN_VERSION as VERSION } from './model/format.js';
|
|
18
19
|
export { compile, load } from '../compiled/index.js';
|
|
@@ -24,3 +25,5 @@ export type { DomainSchemaHeader as Header } from './codec/header.js';
|
|
|
24
25
|
export type { DomainDependency } from '../addressing/index.js';
|
|
25
26
|
/** The versioned value-schema grammar embedded in declarations. */
|
|
26
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
|
@@ -5,9 +5,11 @@ 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
7
|
export { ANNOTATION_KEYWORDS, FIELD_ROLES, callableFingerprint, compareDependencyMeaning, compareMemberMeaning, describeMeaningChange, footprintOf, meaningOf, } from './compatibility/index.js';
|
|
8
|
+
export * as compatibility from './compatibility/api.js';
|
|
8
9
|
export { header } from './codec/header.js';
|
|
9
10
|
export { DOMAIN_FORMAT as FORMAT, DOMAIN_VERSION as VERSION } from './model/format.js';
|
|
10
11
|
export { compile, load } from '../compiled/index.js';
|
|
11
12
|
export { SchemaError as Error } from './diagnostic.js';
|
|
12
13
|
/** The versioned value-schema grammar embedded in declarations. */
|
|
13
14
|
export * as values from './validation/value-api.js';
|
|
15
|
+
export { compareDependencyStructure } from './compatibility/index.js';
|
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.39",
|
|
4
4
|
"description": "Astrale Kernel DSL - Schema authoring, inference, and IR serialization",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"repository": {
|
|
@@ -99,7 +99,7 @@
|
|
|
99
99
|
"ajv": "^8.20.0",
|
|
100
100
|
"re2js": "2.8.6",
|
|
101
101
|
"typescript": "~6.0.3",
|
|
102
|
-
"vitest": "^
|
|
102
|
+
"vitest": "^5.0.1",
|
|
103
103
|
"yaml": "2.9.0"
|
|
104
104
|
},
|
|
105
105
|
"scripts": {
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
import type { Value as JsonValue } from '@astrale-os/kernel-dsl/value';
|
|
2
|
-
import type { Key as SemanticKey } from '../../addressing/index.js';
|
|
3
|
-
import type { DomainSchema } from '../model/schema.js';
|
|
4
|
-
import type { Context } from './context.js';
|
|
5
|
-
import type { Coordinate } from './coordinate.js';
|
|
6
|
-
import type { DependencyMeaningLevel } from './fields.js';
|
|
7
|
-
import type { ClassRequirement, MeaningFingerprint } from './meaning.js';
|
|
8
|
-
/** One footprint coordinate with the meaning the dependent was authored against. */
|
|
9
|
-
export interface SourceMeaning {
|
|
10
|
-
readonly coordinate: Coordinate;
|
|
11
|
-
readonly meaning: JsonValue;
|
|
12
|
-
readonly fingerprint: MeaningFingerprint;
|
|
13
|
-
readonly requirement?: ClassRequirement;
|
|
14
|
-
}
|
|
15
|
-
/** Every dependency Definition the dependent's own declarations name, at its strongest usage. */
|
|
16
|
-
export declare function collectSchemaRoots(owner: DomainSchema): readonly Coordinate[];
|
|
17
|
-
/**
|
|
18
|
-
* The transitive footprint: from the roots, every Class, Property, Method, Function, Policy, View
|
|
19
|
-
* and Core coordinate of the selected origin that the dependent's meaning reaches. A Class carries
|
|
20
|
-
* its strongest usage level; only inheritance traverses ancestors and Edge endpoint contracts, and
|
|
21
|
-
* a reached callable contributes its contract references but never its Policy check.
|
|
22
|
-
*/
|
|
23
|
-
export declare function collectReachableMeanings(roots: readonly Coordinate[], selectedOrigin: string, context: Context): ReadonlyMap<SemanticKey, SourceMeaning>;
|
|
24
|
-
/** Resolve one caller-supplied Key of the source into an exact coordinate that exists there. */
|
|
25
|
-
export declare function coordinateForKey(context: Context, source: DomainSchema, input: SemanticKey, index: number, level?: DependencyMeaningLevel): Coordinate;
|
|
@@ -1,264 +0,0 @@
|
|
|
1
|
-
import { Key } from '../../addressing/index.js';
|
|
2
|
-
import { compareUnicode, definitionRefIndex } from '../../addressing/ordering.js';
|
|
3
|
-
import { SchemaError, diagnostic } from '../diagnostic.js';
|
|
4
|
-
import { collectCallableContractRefs, collectCallableRefs, collectClassPolicyRefs, collectClassRefs, collectCoreRefs, collectEndpointRefs, collectPolicyDefinitionRefs, collectValueDefinitionRefs, coreEndpointDefinitionRef, } from '../resolution/origins.js';
|
|
5
|
-
import { contextInvalid, effectiveDefinition, effectiveMethod, requireOwner } from './context.js';
|
|
6
|
-
import { classCoordinate, keyReference, levelOf, memberCoordinate, methodCoordinate, propertyCoordinate, } from './coordinate.js';
|
|
7
|
-
import { classMeaning, fingerprintMeaning, meaningForCoordinate, memberMeaning, methodMeaning, propertyMeaning, } from './meaning.js';
|
|
8
|
-
/** Every dependency Definition the dependent's own declarations name, at its strongest usage. */
|
|
9
|
-
export function collectSchemaRoots(owner) {
|
|
10
|
-
const classes = new Map();
|
|
11
|
-
const members = new Map();
|
|
12
|
-
const add = (ref, site) => {
|
|
13
|
-
if (ref.kind === 'class') {
|
|
14
|
-
const index = definitionRefIndex(ref);
|
|
15
|
-
const level = levelOf(site);
|
|
16
|
-
const previous = classes.get(index);
|
|
17
|
-
if (previous === undefined || (previous.level === 'reference' && level === 'inherit')) {
|
|
18
|
-
classes.set(index, classCoordinate(ref, level));
|
|
19
|
-
}
|
|
20
|
-
return;
|
|
21
|
-
}
|
|
22
|
-
members.set(definitionRefIndex(ref), memberCoordinate(ref));
|
|
23
|
-
};
|
|
24
|
-
for (const declaration of Object.values(owner.classes))
|
|
25
|
-
collectClassRefs(declaration, add);
|
|
26
|
-
for (const declaration of Object.values(owner.functions))
|
|
27
|
-
collectCallableRefs(declaration, add);
|
|
28
|
-
for (const declaration of Object.values(owner.policies)) {
|
|
29
|
-
collectPolicyDefinitionRefs(declaration, add);
|
|
30
|
-
}
|
|
31
|
-
for (const declaration of Object.values(owner.views)) {
|
|
32
|
-
if (declaration.target.kind === 'definition') {
|
|
33
|
-
declaration.target.definitions.forEach((ref) => add(ref, 'view'));
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
collectCoreRefs(owner, add);
|
|
37
|
-
return [...classes.values(), ...members.values()].sort((left, right) => compareUnicode(left.key, right.key));
|
|
38
|
-
}
|
|
39
|
-
/**
|
|
40
|
-
* The transitive footprint: from the roots, every Class, Property, Method, Function, Policy, View
|
|
41
|
-
* and Core coordinate of the selected origin that the dependent's meaning reaches. A Class carries
|
|
42
|
-
* its strongest usage level; only inheritance traverses ancestors and Edge endpoint contracts, and
|
|
43
|
-
* a reached callable contributes its contract references but never its Policy check.
|
|
44
|
-
*/
|
|
45
|
-
export function collectReachableMeanings(roots, selectedOrigin, context) {
|
|
46
|
-
const meanings = new Map();
|
|
47
|
-
const classLevels = new Map();
|
|
48
|
-
const visitedMembers = new Set();
|
|
49
|
-
const pendingClasses = [];
|
|
50
|
-
const pendingMembers = [];
|
|
51
|
-
const push = (ref, site) => {
|
|
52
|
-
if (ref.kind === 'class')
|
|
53
|
-
pendingClasses.push(classCoordinate(ref, levelOf(site)));
|
|
54
|
-
else
|
|
55
|
-
pendingMembers.push(memberCoordinate(ref));
|
|
56
|
-
};
|
|
57
|
-
for (const coordinate of roots) {
|
|
58
|
-
if (coordinate.kind === 'class') {
|
|
59
|
-
pendingClasses.push(coordinate);
|
|
60
|
-
continue;
|
|
61
|
-
}
|
|
62
|
-
if (coordinate.kind === 'member') {
|
|
63
|
-
pendingMembers.push(coordinate);
|
|
64
|
-
continue;
|
|
65
|
-
}
|
|
66
|
-
const meaning = meaningForCoordinate(context, coordinate);
|
|
67
|
-
if (meaning === undefined) {
|
|
68
|
-
throw new SchemaError([
|
|
69
|
-
diagnostic('DM_MEMBER_ROOT_INVALID', '/roots', `Member comparison root ${coordinate.key} does not exist in its source context.`, coordinate.key),
|
|
70
|
-
]);
|
|
71
|
-
}
|
|
72
|
-
retainMeaning(meanings, selectedOrigin, coordinate, meaning);
|
|
73
|
-
collectCoordinateSemanticRefs(context, coordinate, push);
|
|
74
|
-
}
|
|
75
|
-
const visitClass = (coordinate) => {
|
|
76
|
-
const index = definitionRefIndex(coordinate.ref);
|
|
77
|
-
const previous = classLevels.get(index);
|
|
78
|
-
if (previous === 'inherit' || previous === coordinate.level)
|
|
79
|
-
return;
|
|
80
|
-
classLevels.set(index, coordinate.level);
|
|
81
|
-
const owner = requireOwner(context, coordinate.ref, coordinate.key);
|
|
82
|
-
const declaration = owner.classes[coordinate.ref.name];
|
|
83
|
-
const analysis = effectiveDefinition(context, owner, coordinate.ref);
|
|
84
|
-
const resolved = classMeaning(context, coordinate);
|
|
85
|
-
if (declaration === undefined || analysis === undefined || resolved === undefined) {
|
|
86
|
-
throw contextInvalid(`Retained acceptance context has no meaning for ${coordinate.key}.`, coordinate.key);
|
|
87
|
-
}
|
|
88
|
-
retainMeaning(meanings, selectedOrigin, coordinate, resolved.meaning, resolved.requirement);
|
|
89
|
-
if (previous === undefined) {
|
|
90
|
-
// The surface is level-independent: every effective Property and Method the dependent may
|
|
91
|
-
// read or call, with the Definitions their contracts and policies name.
|
|
92
|
-
for (const field of analysis.properties.values()) {
|
|
93
|
-
retainMeaning(meanings, selectedOrigin, propertyCoordinate(field.owner, field.name), propertyMeaning(field));
|
|
94
|
-
collectValueDefinitionRefs(field.schema, push);
|
|
95
|
-
}
|
|
96
|
-
for (const method of analysis.methods.values()) {
|
|
97
|
-
retainMeaning(meanings, selectedOrigin, methodCoordinate(method.owner, method.name), methodMeaning(method));
|
|
98
|
-
collectCallableContractRefs(method.declaration, push);
|
|
99
|
-
}
|
|
100
|
-
collectClassPolicyRefs(declaration, push);
|
|
101
|
-
}
|
|
102
|
-
if (coordinate.level === 'inherit') {
|
|
103
|
-
// Only inheritance takes on the ancestry and, for an Edge, the endpoint contract.
|
|
104
|
-
declaration.extends.forEach((parent) => push(parent, 'extends'));
|
|
105
|
-
if (declaration.kind === 'edge')
|
|
106
|
-
collectEndpointRefs(declaration, push);
|
|
107
|
-
}
|
|
108
|
-
};
|
|
109
|
-
const visitMember = (coordinate) => {
|
|
110
|
-
const index = definitionRefIndex(coordinate.ref);
|
|
111
|
-
if (visitedMembers.has(index))
|
|
112
|
-
return;
|
|
113
|
-
visitedMembers.add(index);
|
|
114
|
-
const owner = requireOwner(context, coordinate.ref, coordinate.key);
|
|
115
|
-
const meaning = memberMeaning(owner, coordinate.ref);
|
|
116
|
-
if (meaning === undefined) {
|
|
117
|
-
throw contextInvalid(`Retained acceptance context has no meaning for ${coordinate.key}.`, coordinate.key);
|
|
118
|
-
}
|
|
119
|
-
retainMeaning(meanings, selectedOrigin, coordinate, meaning);
|
|
120
|
-
collectMemberSemanticRefs(owner, coordinate.ref, push);
|
|
121
|
-
};
|
|
122
|
-
let classIndex = 0;
|
|
123
|
-
let memberIndex = 0;
|
|
124
|
-
while (classIndex < pendingClasses.length || memberIndex < pendingMembers.length) {
|
|
125
|
-
if (classIndex < pendingClasses.length)
|
|
126
|
-
visitClass(pendingClasses[classIndex++]);
|
|
127
|
-
else
|
|
128
|
-
visitMember(pendingMembers[memberIndex++]);
|
|
129
|
-
}
|
|
130
|
-
return meanings;
|
|
131
|
-
}
|
|
132
|
-
/** Resolve one caller-supplied Key of the source into an exact coordinate that exists there. */
|
|
133
|
-
export function coordinateForKey(context, source, input, index, level = 'reference') {
|
|
134
|
-
let key;
|
|
135
|
-
try {
|
|
136
|
-
key = Key(input);
|
|
137
|
-
}
|
|
138
|
-
catch {
|
|
139
|
-
return invalidMemberRoot(input, index, 'is not a canonical semantic Key');
|
|
140
|
-
}
|
|
141
|
-
if (Key.origin(key) !== source.origin) {
|
|
142
|
-
return invalidMemberRoot(key, index, `belongs to ${Key.origin(key)}, not source ${source.origin}`);
|
|
143
|
-
}
|
|
144
|
-
const reference = keyReference(key);
|
|
145
|
-
let coordinate;
|
|
146
|
-
if (reference.member !== undefined && reference.ownerName !== undefined) {
|
|
147
|
-
if (source.classes[reference.ownerName] === undefined) {
|
|
148
|
-
return invalidMemberRoot(key, index, 'has no exact owning Definition');
|
|
149
|
-
}
|
|
150
|
-
const owner = { origin: source.origin, kind: 'class', name: reference.ownerName };
|
|
151
|
-
coordinate =
|
|
152
|
-
reference.member === 'property'
|
|
153
|
-
? propertyCoordinate(owner, reference.name)
|
|
154
|
-
: methodCoordinate(owner, reference.name);
|
|
155
|
-
}
|
|
156
|
-
else if (reference.kind === 'class') {
|
|
157
|
-
if (source.classes[reference.name] === undefined) {
|
|
158
|
-
return invalidMemberRoot(key, index, 'does not exist in the source');
|
|
159
|
-
}
|
|
160
|
-
// A Class root takes the caller's level; compareMemberMeaning passes none and compares the
|
|
161
|
-
// surface at reference level without inferring an inheritance contract.
|
|
162
|
-
coordinate = classCoordinate({ origin: source.origin, kind: 'class', name: reference.name }, level);
|
|
163
|
-
}
|
|
164
|
-
else {
|
|
165
|
-
coordinate = memberCoordinate({
|
|
166
|
-
origin: source.origin,
|
|
167
|
-
kind: reference.kind,
|
|
168
|
-
name: reference.name,
|
|
169
|
-
});
|
|
170
|
-
}
|
|
171
|
-
if (meaningForCoordinate(context, coordinate) === undefined) {
|
|
172
|
-
return invalidMemberRoot(key, index, 'does not exist in the source');
|
|
173
|
-
}
|
|
174
|
-
return coordinate;
|
|
175
|
-
}
|
|
176
|
-
function collectCoordinateSemanticRefs(context, coordinate, add) {
|
|
177
|
-
const owner = context.schemas.get(coordinate.owner.origin);
|
|
178
|
-
const analysis = owner === undefined ? undefined : effectiveDefinition(context, owner, coordinate.owner);
|
|
179
|
-
if (analysis === undefined)
|
|
180
|
-
return;
|
|
181
|
-
if (coordinate.kind === 'property') {
|
|
182
|
-
const field = analysis.properties.get(coordinate.key);
|
|
183
|
-
if (field !== undefined)
|
|
184
|
-
collectValueDefinitionRefs(field.schema, add);
|
|
185
|
-
return;
|
|
186
|
-
}
|
|
187
|
-
const method = effectiveMethod(analysis, coordinate.key);
|
|
188
|
-
if (method !== undefined)
|
|
189
|
-
collectCallableContractRefs(method.declaration, add);
|
|
190
|
-
}
|
|
191
|
-
function collectMemberSemanticRefs(owner, ref, add) {
|
|
192
|
-
switch (ref.kind) {
|
|
193
|
-
case 'function': {
|
|
194
|
-
const declaration = owner.functions[ref.name];
|
|
195
|
-
if (declaration !== undefined)
|
|
196
|
-
collectCallableRefs(declaration, add);
|
|
197
|
-
return;
|
|
198
|
-
}
|
|
199
|
-
case 'policy': {
|
|
200
|
-
const declaration = owner.policies[ref.name];
|
|
201
|
-
if (declaration !== undefined)
|
|
202
|
-
collectPolicyDefinitionRefs(declaration, add);
|
|
203
|
-
return;
|
|
204
|
-
}
|
|
205
|
-
case 'view': {
|
|
206
|
-
const declaration = owner.views[ref.name];
|
|
207
|
-
if (declaration?.target.kind === 'definition') {
|
|
208
|
-
declaration.target.definitions.forEach((definition) => add(definition, 'view'));
|
|
209
|
-
}
|
|
210
|
-
return;
|
|
211
|
-
}
|
|
212
|
-
case 'core': {
|
|
213
|
-
// The dependency instantiates its own Core; the dependent only refers to it.
|
|
214
|
-
const node = owner.core.nodes[ref.name];
|
|
215
|
-
if (node === undefined)
|
|
216
|
-
return;
|
|
217
|
-
add(node.class);
|
|
218
|
-
for (const edge of owner.core.edges) {
|
|
219
|
-
const incident = (edge.source.kind === 'core' && edge.source.name === ref.name) ||
|
|
220
|
-
(edge.target.kind === 'core' && edge.target.name === ref.name);
|
|
221
|
-
if (!incident)
|
|
222
|
-
continue;
|
|
223
|
-
add(edge.class);
|
|
224
|
-
for (const endpoint of [edge.source, edge.target]) {
|
|
225
|
-
const endpointRef = coreEndpointDefinitionRef(endpoint);
|
|
226
|
-
if (endpointRef !== undefined)
|
|
227
|
-
add(endpointRef);
|
|
228
|
-
}
|
|
229
|
-
}
|
|
230
|
-
}
|
|
231
|
-
}
|
|
232
|
-
}
|
|
233
|
-
function retainMeaning(meanings, selectedOrigin, coordinate, meaning, requirement) {
|
|
234
|
-
if (Key.origin(coordinate.key) !== selectedOrigin)
|
|
235
|
-
return;
|
|
236
|
-
const fingerprint = fingerprintMeaning(meaning);
|
|
237
|
-
const previous = meanings.get(coordinate.key);
|
|
238
|
-
if (previous !== undefined) {
|
|
239
|
-
const upgraded = previous.coordinate.kind === 'class' &&
|
|
240
|
-
coordinate.kind === 'class' &&
|
|
241
|
-
previous.coordinate.level !== coordinate.level;
|
|
242
|
-
if (upgraded) {
|
|
243
|
-
// A stronger requirement replaces the weaker one for the same Class.
|
|
244
|
-
if (coordinate.level === 'reference')
|
|
245
|
-
return;
|
|
246
|
-
}
|
|
247
|
-
else if (previous.fingerprint !== fingerprint) {
|
|
248
|
-
throw new SchemaError([
|
|
249
|
-
diagnostic('DM_MEANING_INCONSISTENT', '', `Semantic coordinate ${coordinate.key} produced inconsistent effective meaning.`, coordinate.key),
|
|
250
|
-
]);
|
|
251
|
-
}
|
|
252
|
-
}
|
|
253
|
-
meanings.set(coordinate.key, Object.freeze({
|
|
254
|
-
coordinate,
|
|
255
|
-
meaning,
|
|
256
|
-
fingerprint,
|
|
257
|
-
...(requirement === undefined ? {} : { requirement }),
|
|
258
|
-
}));
|
|
259
|
-
}
|
|
260
|
-
function invalidMemberRoot(key, index, reason) {
|
|
261
|
-
throw new SchemaError([
|
|
262
|
-
diagnostic('DM_MEMBER_ROOT_INVALID', `/roots/${index}`, `Key ${JSON.stringify(key)} ${reason}.`, typeof key === 'string' ? key : undefined),
|
|
263
|
-
]);
|
|
264
|
-
}
|
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
import type { Value as JsonValue } from '@astrale-os/kernel-dsl/value';
|
|
2
|
-
import type { Key as SemanticKey, PropertyKey as SemanticPropertyKey } from '../../addressing/index.js';
|
|
3
|
-
import type { ResolvedFunction, ResolvedMethod } from '../../language/callable/resolved.js';
|
|
4
|
-
import type { JsonSchema } from '../../language/value/model/schema.js';
|
|
5
|
-
import type { EffectiveField, EffectiveMethod } from '../analysis/class.js';
|
|
6
|
-
import type { DomainSchema } from '../model/schema.js';
|
|
7
|
-
import type { Context } from './context.js';
|
|
8
|
-
import type { ClassCoordinate, Coordinate, MemberRef } from './coordinate.js';
|
|
9
|
-
export type MeaningFingerprint = `sha256:${string}`;
|
|
10
|
-
/** What the source Class projects onto the target before comparing an inherited Class. */
|
|
11
|
-
export interface ClassRequirement {
|
|
12
|
-
readonly ancestors: readonly SemanticKey[];
|
|
13
|
-
readonly properties: ReadonlySet<SemanticPropertyKey>;
|
|
14
|
-
}
|
|
15
|
-
/**
|
|
16
|
-
* The meaning of one coordinate is a portable value built from the fields `fields.ts` classifies
|
|
17
|
-
* as counting at the coordinate's usage level; `__tests__/fields.test.ts` proves the two agree.
|
|
18
|
-
* Comparison works on these values, so every difference can name its exact path.
|
|
19
|
-
*/
|
|
20
|
-
export declare function meaningForCoordinate(context: Context, coordinate: Coordinate, requirement?: ClassRequirement): JsonValue | undefined;
|
|
21
|
-
/**
|
|
22
|
-
* A reference needs the Class to exist with its kind, abstractness and orientation; inheritance
|
|
23
|
-
* additionally takes on the source ancestors, class policies, data contract and, for an Edge, the
|
|
24
|
-
* endpoint and constraint contracts. A target projected onto a source requirement keeps only the
|
|
25
|
-
* ancestors the source named, so a superset satisfies it while a removal changes it.
|
|
26
|
-
*/
|
|
27
|
-
export declare function classMeaning(context: Context, coordinate: ClassCoordinate, onto?: ClassRequirement): {
|
|
28
|
-
readonly meaning: JsonValue;
|
|
29
|
-
readonly requirement: ClassRequirement;
|
|
30
|
-
} | undefined;
|
|
31
|
-
export declare function memberMeaning(owner: DomainSchema, ref: MemberRef): JsonValue | undefined;
|
|
32
|
-
export declare function propertyMeaning(field: EffectiveField): JsonValue;
|
|
33
|
-
export declare function methodMeaning(method: EffectiveMethod): JsonValue;
|
|
34
|
-
/**
|
|
35
|
-
* Fingerprint the contract a caller compiles against, independently of its Domain revision: the
|
|
36
|
-
* exact key, the owner, dispatch and abstractness of a Method, and the semantic input and output
|
|
37
|
-
* contracts; authorization is excluded (DM-CALLABLE-FINGERPRINT). A static consumer pins this
|
|
38
|
-
* instead of a Domain revision, so a dependency release that leaves the callable contract untouched
|
|
39
|
-
* keeps every bound invocation valid.
|
|
40
|
-
*/
|
|
41
|
-
export declare function callableFingerprint(callable: ResolvedFunction | ResolvedMethod): MeaningFingerprint;
|
|
42
|
-
/** Drop annotation keywords wherever a schema keyword nests a schema; never inside data. */
|
|
43
|
-
export declare function semanticValueSchema(value: JsonSchema): JsonSchema;
|
|
44
|
-
export declare function fingerprintMeaning(value: unknown): MeaningFingerprint;
|