@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
|
@@ -1,57 +1,5 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
import type { MeaningFingerprint } from './meaning.js';
|
|
7
|
-
export type DependencyMeaningChangeKind = 'missing' | 'changed' | 'added';
|
|
8
|
-
export type DependencyMeaningChange = {
|
|
9
|
-
readonly key: SemanticKey;
|
|
10
|
-
readonly kind: 'missing';
|
|
11
|
-
readonly sourceFingerprint: MeaningFingerprint;
|
|
12
|
-
} | {
|
|
13
|
-
readonly key: SemanticKey;
|
|
14
|
-
readonly kind: 'changed';
|
|
15
|
-
readonly sourceFingerprint: MeaningFingerprint;
|
|
16
|
-
readonly targetFingerprint: MeaningFingerprint;
|
|
17
|
-
/** Every exact path of the meaning that differs, in JSON Pointer order. */
|
|
18
|
-
readonly differences: readonly MeaningDifference[];
|
|
19
|
-
} | {
|
|
20
|
-
/** A required, non-automatic Property the target adds to an inherited Class. */
|
|
21
|
-
readonly key: SemanticKey;
|
|
22
|
-
readonly kind: 'added';
|
|
23
|
-
readonly targetFingerprint: MeaningFingerprint;
|
|
24
|
-
readonly meaning: JsonValue;
|
|
25
|
-
};
|
|
26
|
-
export interface DependencyMeaningComparison {
|
|
27
|
-
readonly version: 3;
|
|
28
|
-
readonly dependency: SemanticKey.Origin;
|
|
29
|
-
readonly source: Revision;
|
|
30
|
-
readonly target: Revision;
|
|
31
|
-
readonly footprint: readonly SemanticKey[];
|
|
32
|
-
readonly changes: readonly DependencyMeaningChange[];
|
|
33
|
-
readonly fingerprint: MeaningFingerprint;
|
|
34
|
-
}
|
|
35
|
-
/** One footprint entry: the Key, what it is, and for a Class how strongly it is used. */
|
|
36
|
-
export interface FootprintEntry {
|
|
37
|
-
readonly key: SemanticKey;
|
|
38
|
-
readonly kind: Definition.Kind | 'property' | 'method';
|
|
39
|
-
readonly level?: DependencyMeaningLevel;
|
|
40
|
-
}
|
|
41
|
-
/**
|
|
42
|
-
* Compare the exact V1 meaning one accepted Domain used from a retained dependency with a
|
|
43
|
-
* candidate revision of that same dependency origin. This operation is pure and makes no
|
|
44
|
-
* lifecycle-compatibility decision.
|
|
45
|
-
*/
|
|
46
|
-
export declare function compareDependencyMeaning(dependent: DomainSchema, target: DomainSchema): DependencyMeaningComparison;
|
|
47
|
-
/** Compare one explicit non-empty source member surface and its reachable V1 meaning. */
|
|
48
|
-
export declare function compareMemberMeaning(source: DomainSchema, target: DomainSchema, roots: readonly [SemanticKey, ...SemanticKey[]]): DependencyMeaningComparison;
|
|
49
|
-
/** The footprint one accepted Domain takes from a retained dependency, Key-ordered. */
|
|
50
|
-
export declare function footprintOf(dependent: DomainSchema, origin: SemanticKey.Origin): readonly FootprintEntry[];
|
|
51
|
-
/**
|
|
52
|
-
* The meaning one Domain gives one of its own Keys at a usage level; a Key the Domain does not
|
|
53
|
-
* declare is rejected with DM_MEMBER_ROOT_INVALID.
|
|
54
|
-
*/
|
|
55
|
-
export declare function meaningOf(schema: DomainSchema, key: SemanticKey, level?: DependencyMeaningLevel): JsonValue;
|
|
56
|
-
/** One line an operator can act on, naming the exact paths of a changed meaning. */
|
|
57
|
-
export declare function describeMeaningChange(change: DependencyMeaningChange): string;
|
|
1
|
+
import type { Comparison, ComparisonRequest, MeaningComparison, StructureComparison } from './model.js';
|
|
2
|
+
/** Complete facts and independent conclusions, resolved against the same exact contexts. */
|
|
3
|
+
export declare function compare(request: ComparisonRequest): Comparison;
|
|
4
|
+
export declare function compareStructure(request: ComparisonRequest): StructureComparison;
|
|
5
|
+
export declare function compareMeaning(request: ComparisonRequest): MeaningComparison;
|
|
@@ -1,166 +1,23 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* Compare the exact V1 meaning one accepted Domain used from a retained dependency with a
|
|
12
|
-
* candidate revision of that same dependency origin. This operation is pure and makes no
|
|
13
|
-
* lifecycle-compatibility decision.
|
|
14
|
-
*/
|
|
15
|
-
export function compareDependencyMeaning(dependent, target) {
|
|
16
|
-
const { source, meanings } = retainedMeanings(dependent, target.origin, '/target/origin');
|
|
17
|
-
return compareSourceMeanings(source, target, meanings);
|
|
18
|
-
}
|
|
19
|
-
/** Compare one explicit non-empty source member surface and its reachable V1 meaning. */
|
|
20
|
-
export function compareMemberMeaning(source, target, roots) {
|
|
21
|
-
if (source.origin !== target.origin) {
|
|
22
|
-
throw new SchemaError([
|
|
23
|
-
diagnostic('DM_MEMBER_ORIGIN_MISMATCH', '/target/origin', `Member comparison target ${target.origin} does not match source ${source.origin}.`),
|
|
24
|
-
]);
|
|
25
|
-
}
|
|
26
|
-
if (!Array.isArray(roots) || roots.length === 0 || new Set(roots).size !== roots.length) {
|
|
27
|
-
throw new SchemaError([
|
|
28
|
-
diagnostic('DM_MEMBER_ROOT_INVALID', '/roots', 'Member comparison roots must be one non-empty duplicate-free Key set.'),
|
|
29
|
-
]);
|
|
30
|
-
}
|
|
31
|
-
resolve(source);
|
|
32
|
-
const context = contextOf(source);
|
|
33
|
-
const coordinates = roots.map((key, index) => coordinateForKey(context, source, key, index));
|
|
34
|
-
const meanings = collectReachableMeanings(coordinates, source.origin, context);
|
|
35
|
-
return compareSourceMeanings(source, target, meanings);
|
|
36
|
-
}
|
|
37
|
-
/** The footprint one accepted Domain takes from a retained dependency, Key-ordered. */
|
|
38
|
-
export function footprintOf(dependent, origin) {
|
|
39
|
-
const { meanings } = retainedMeanings(dependent, origin, '/origin');
|
|
40
|
-
return Object.freeze([...meanings.values()]
|
|
41
|
-
.map(({ coordinate }) => Object.freeze({
|
|
42
|
-
key: coordinate.key,
|
|
43
|
-
kind: coordinate.kind === 'member' ? coordinate.ref.kind : coordinate.kind,
|
|
44
|
-
...(coordinate.kind === 'class' ? { level: coordinate.level } : {}),
|
|
45
|
-
}))
|
|
46
|
-
.sort((left, right) => compareUnicode(left.key, right.key)));
|
|
47
|
-
}
|
|
48
|
-
/**
|
|
49
|
-
* The meaning one Domain gives one of its own Keys at a usage level; a Key the Domain does not
|
|
50
|
-
* declare is rejected with DM_MEMBER_ROOT_INVALID.
|
|
51
|
-
*/
|
|
52
|
-
export function meaningOf(schema, key, level = 'reference') {
|
|
53
|
-
resolve(schema);
|
|
54
|
-
const context = contextOf(schema);
|
|
55
|
-
const coordinate = coordinateForKey(context, schema, key, 0, level);
|
|
56
|
-
return meaningForCoordinate(context, coordinate);
|
|
57
|
-
}
|
|
58
|
-
/** One line an operator can act on, naming the exact paths of a changed meaning. */
|
|
59
|
-
export function describeMeaningChange(change) {
|
|
60
|
-
switch (change.kind) {
|
|
61
|
-
case 'missing':
|
|
62
|
-
return `${change.key} missing`;
|
|
63
|
-
case 'added':
|
|
64
|
-
return `${change.key} added as an obligation`;
|
|
65
|
-
case 'changed': {
|
|
66
|
-
const paths = change.differences.map(({ path }) => path);
|
|
67
|
-
const shown = paths.slice(0, 3).join(', ');
|
|
68
|
-
return `${change.key} changed at ${paths.length > 3 ? `${shown} and ${paths.length - 3} more` : shown}`;
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
/** The retained dependency at one origin and every meaning the dependent reaches in it. */
|
|
73
|
-
function retainedMeanings(dependent, origin, path) {
|
|
74
|
-
resolve(dependent);
|
|
75
|
-
const context = contextOf(dependent);
|
|
76
|
-
const source = (knownDomainClosure(dependent) ?? []).find((candidate) => candidate.schema.origin === origin)?.schema;
|
|
77
|
-
if (source === undefined) {
|
|
78
|
-
throw new SchemaError([
|
|
79
|
-
diagnostic('DM_DEPENDENCY_NOT_RETAINED', path, `Domain ${dependent.origin} was not admitted with a dependency at ${origin}.`),
|
|
80
|
-
]);
|
|
81
|
-
}
|
|
82
|
-
return {
|
|
83
|
-
source,
|
|
84
|
-
meanings: collectReachableMeanings(collectSchemaRoots(dependent), source.origin, context),
|
|
85
|
-
};
|
|
86
|
-
}
|
|
87
|
-
/**
|
|
88
|
-
* A footprint coordinate is unchanged only when the same Key exists in the target with the same
|
|
89
|
-
* meaning projected onto the source requirement of its usage level. Additions outside the
|
|
90
|
-
* footprint are free; a required non-automatic Property the target adds to an inherited Class is
|
|
91
|
-
* the one addition reported, as an obligation.
|
|
92
|
-
*/
|
|
93
|
-
function compareSourceMeanings(source, target, sourceMeanings) {
|
|
94
|
-
// Prove the candidate still has an exact accepted semantic context before comparing it.
|
|
95
|
-
resolve(target);
|
|
96
|
-
const context = contextOf(target);
|
|
97
|
-
const footprint = Object.freeze([...sourceMeanings.keys()].sort(compareUnicode));
|
|
98
|
-
const changes = [];
|
|
99
|
-
const comparisonCoordinates = [];
|
|
100
|
-
for (const key of footprint) {
|
|
101
|
-
const sourceMeaning = sourceMeanings.get(key);
|
|
102
|
-
const targetMeaning = meaningForCoordinate(context, sourceMeaning.coordinate, sourceMeaning.requirement);
|
|
103
|
-
const targetFingerprint = targetMeaning === undefined ? undefined : fingerprintMeaning(targetMeaning);
|
|
104
|
-
comparisonCoordinates.push([
|
|
105
|
-
key,
|
|
106
|
-
sourceMeaning.fingerprint,
|
|
107
|
-
targetFingerprint ?? null,
|
|
108
|
-
]);
|
|
109
|
-
if (targetMeaning === undefined || targetFingerprint === undefined) {
|
|
110
|
-
changes.push(Object.freeze({ key, kind: 'missing', sourceFingerprint: sourceMeaning.fingerprint }));
|
|
111
|
-
}
|
|
112
|
-
else if (targetFingerprint !== sourceMeaning.fingerprint) {
|
|
113
|
-
changes.push(Object.freeze({
|
|
114
|
-
key,
|
|
115
|
-
kind: 'changed',
|
|
116
|
-
sourceFingerprint: sourceMeaning.fingerprint,
|
|
117
|
-
targetFingerprint,
|
|
118
|
-
differences: differences(sourceMeaning.meaning, targetMeaning),
|
|
119
|
-
}));
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
const additions = addedObligations(context, sourceMeanings);
|
|
123
|
-
const sourceRevision = revisionOfDomainSchema(source);
|
|
124
|
-
const targetRevision = revisionOfDomainSchema(target);
|
|
125
|
-
const fingerprint = fingerprintMeaning({
|
|
126
|
-
version: 3,
|
|
127
|
-
dependency: source.origin,
|
|
128
|
-
source: sourceRevision,
|
|
129
|
-
target: targetRevision,
|
|
130
|
-
coordinates: comparisonCoordinates,
|
|
131
|
-
additions: additions.map(({ key, targetFingerprint }) => [key, targetFingerprint]),
|
|
132
|
-
});
|
|
1
|
+
import { analyzeMeaning } from './meaning/index.js';
|
|
2
|
+
import { mergeEntries } from './report.js';
|
|
3
|
+
import { prepare } from './scope.js';
|
|
4
|
+
import { analyzeStructure } from './structure/index.js';
|
|
5
|
+
/** Complete facts and independent conclusions, resolved against the same exact contexts. */
|
|
6
|
+
export function compare(request) {
|
|
7
|
+
const context = prepare(request);
|
|
8
|
+
const structure = analyzeStructure(context);
|
|
9
|
+
const meaning = analyzeMeaning(context);
|
|
133
10
|
return Object.freeze({
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
changes: Object.freeze([...changes, ...additions]),
|
|
140
|
-
fingerprint,
|
|
11
|
+
scope: context.scope,
|
|
12
|
+
entries: mergeEntries(structure.entries, meaning.entries),
|
|
13
|
+
structure,
|
|
14
|
+
meaning,
|
|
15
|
+
evidence: Object.freeze({ structure: structure.evidence, meaning: meaning.evidence }),
|
|
141
16
|
});
|
|
142
17
|
}
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
continue;
|
|
149
|
-
const owner = context.schemas.get(coordinate.ref.origin);
|
|
150
|
-
const analysis = owner === undefined ? undefined : effectiveDefinition(context, owner, coordinate.ref);
|
|
151
|
-
if (analysis === undefined)
|
|
152
|
-
continue;
|
|
153
|
-
for (const field of analysis.properties.values()) {
|
|
154
|
-
if (requirement.properties.has(field.identity) || !field.required || field.auto !== undefined)
|
|
155
|
-
continue;
|
|
156
|
-
const meaning = propertyMeaning(field);
|
|
157
|
-
additions.set(field.identity, Object.freeze({
|
|
158
|
-
key: field.identity,
|
|
159
|
-
kind: 'added',
|
|
160
|
-
targetFingerprint: fingerprintMeaning(meaning),
|
|
161
|
-
meaning,
|
|
162
|
-
}));
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
return [...additions.values()].sort((left, right) => compareUnicode(left.key, right.key));
|
|
18
|
+
export function compareStructure(request) {
|
|
19
|
+
return analyzeStructure(prepare(request));
|
|
20
|
+
}
|
|
21
|
+
export function compareMeaning(request) {
|
|
22
|
+
return analyzeMeaning(prepare(request));
|
|
166
23
|
}
|
|
@@ -1,12 +1,9 @@
|
|
|
1
1
|
import type { ClassRef, Definition, DefinitionRef, Key as SemanticKey, PropertyKey as SemanticPropertyKey } from '../../addressing/index.js';
|
|
2
|
-
import type { ReferenceSite } from '../resolution/origins.js';
|
|
3
|
-
import type { DependencyMeaningLevel } from './fields.js';
|
|
4
2
|
export type MemberRef = DefinitionRef<Exclude<Definition.Kind, 'class'>>;
|
|
5
3
|
export type ClassCoordinate = Readonly<{
|
|
6
4
|
kind: 'class';
|
|
7
5
|
key: SemanticKey;
|
|
8
6
|
ref: ClassRef;
|
|
9
|
-
level: DependencyMeaningLevel;
|
|
10
7
|
}>;
|
|
11
8
|
export type MemberCoordinate = Readonly<{
|
|
12
9
|
kind: 'member';
|
|
@@ -25,11 +22,9 @@ export type MethodCoordinate = Readonly<{
|
|
|
25
22
|
owner: ClassRef;
|
|
26
23
|
name: string;
|
|
27
24
|
}>;
|
|
28
|
-
/** One exact semantic location of a
|
|
25
|
+
/** One exact semantic location, independent of how a dependent uses it. */
|
|
29
26
|
export type Coordinate = ClassCoordinate | MemberCoordinate | PropertyCoordinate | MethodCoordinate;
|
|
30
|
-
|
|
31
|
-
export declare function levelOf(site: ReferenceSite | undefined): DependencyMeaningLevel;
|
|
32
|
-
export declare function classCoordinate(ref: ClassRef, level: DependencyMeaningLevel): ClassCoordinate;
|
|
27
|
+
export declare function classCoordinate(ref: ClassRef): ClassCoordinate;
|
|
33
28
|
export declare function memberCoordinate(ref: MemberRef): MemberCoordinate;
|
|
34
29
|
export declare function propertyCoordinate(owner: ClassRef, name: string): PropertyCoordinate;
|
|
35
30
|
export declare function methodCoordinate(owner: ClassRef, name: string): MethodCoordinate;
|
|
@@ -1,11 +1,7 @@
|
|
|
1
1
|
import { Key, MethodKey, PropertyKey } from '../../addressing/index.js';
|
|
2
2
|
import { SchemaError, diagnostic } from '../diagnostic.js';
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
return site === 'extends' || site === 'core' ? 'inherit' : 'reference';
|
|
6
|
-
}
|
|
7
|
-
export function classCoordinate(ref, level) {
|
|
8
|
-
return Object.freeze({ kind: 'class', key: keyForDefinitionRef(ref), ref, level });
|
|
3
|
+
export function classCoordinate(ref) {
|
|
4
|
+
return Object.freeze({ kind: 'class', key: keyForDefinitionRef(ref), ref });
|
|
9
5
|
}
|
|
10
6
|
export function memberCoordinate(ref) {
|
|
11
7
|
return Object.freeze({ kind: 'member', key: keyForDefinitionRef(ref), ref });
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { Analysis, ComparisonEvidence } from './model.js';
|
|
2
|
+
/** Only semantic facts and decisions are durable; provenance and diagnostic prose are not. */
|
|
3
|
+
export declare function evidenceOf({ kind, scope, entries, assessments, }: Omit<Analysis, 'evidence'> & {
|
|
4
|
+
readonly kind: ComparisonEvidence['kind'];
|
|
5
|
+
}): ComparisonEvidence;
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { compareUnicode } from '../../addressing/ordering.js';
|
|
2
|
+
import { fingerprintMeaning } from './comparators/values.js';
|
|
3
|
+
/** Only semantic facts and decisions are durable; provenance and diagnostic prose are not. */
|
|
4
|
+
export function evidenceOf({ kind, scope, entries, assessments, }) {
|
|
5
|
+
const version = kind === 'structure' ? 2 : 1;
|
|
6
|
+
return Object.freeze({
|
|
7
|
+
kind,
|
|
8
|
+
version,
|
|
9
|
+
fingerprint: fingerprintMeaning({
|
|
10
|
+
kind,
|
|
11
|
+
version,
|
|
12
|
+
scope: {
|
|
13
|
+
kind: scope.kind,
|
|
14
|
+
source: [scope.source.origin, scope.source.revision],
|
|
15
|
+
target: [scope.target.origin, scope.target.revision],
|
|
16
|
+
dependent: scope.dependent === undefined ? null : [scope.dependent.origin, scope.dependent.revision],
|
|
17
|
+
roots: scope.roots,
|
|
18
|
+
},
|
|
19
|
+
observations: entries.flatMap((entry) => [...entry.observations]
|
|
20
|
+
.sort((a, b) => compareUnicode(evidenceFacet(a.facet), evidenceFacet(b.facet)))
|
|
21
|
+
.map((observation) => [
|
|
22
|
+
entry.subject.key,
|
|
23
|
+
evidenceFacet(observation.facet),
|
|
24
|
+
observation.before === undefined ? null : fingerprintMeaning(observation.before),
|
|
25
|
+
observation.after === undefined ? null : fingerprintMeaning(observation.after),
|
|
26
|
+
])),
|
|
27
|
+
assessments: [...assessments]
|
|
28
|
+
.sort((a, b) => compareUnicode(`${a.subject.key}#${evidenceFacet(a.observation.facet)}`, `${b.subject.key}#${evidenceFacet(b.observation.facet)}`) ||
|
|
29
|
+
compareUnicode(evidenceFacet(a.requirement.kind), evidenceFacet(b.requirement.kind)))
|
|
30
|
+
.map((assessment) => [
|
|
31
|
+
assessment.subject.key,
|
|
32
|
+
evidenceFacet(assessment.observation.facet),
|
|
33
|
+
evidenceFacet(assessment.requirement.kind),
|
|
34
|
+
assessment.satisfied,
|
|
35
|
+
]),
|
|
36
|
+
}),
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
/** Version-1 evidence keeps its original tags independently of public facet names. */
|
|
40
|
+
function evidenceFacet(facet) {
|
|
41
|
+
switch (facet) {
|
|
42
|
+
case 'method.signature':
|
|
43
|
+
return 'method.contract';
|
|
44
|
+
case 'function.signature':
|
|
45
|
+
return 'function.contract';
|
|
46
|
+
case 'method.inheritance':
|
|
47
|
+
return 'method.signature';
|
|
48
|
+
default:
|
|
49
|
+
return facet;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
@@ -178,3 +178,9 @@ export declare const FIELD_ROLES: Readonly<{
|
|
|
178
178
|
target: "always";
|
|
179
179
|
}>;
|
|
180
180
|
}>;
|
|
181
|
+
/** Classification drives projection; special semantic normalization stays with each comparator. */
|
|
182
|
+
export declare function fieldsOf({ declaration, roles, level, }: {
|
|
183
|
+
readonly declaration: object;
|
|
184
|
+
readonly roles: Readonly<Record<string, FieldRole>>;
|
|
185
|
+
readonly level?: DependencyMeaningLevel;
|
|
186
|
+
}): Readonly<Record<string, unknown>>;
|
|
@@ -81,3 +81,8 @@ export const FIELD_ROLES = Object.freeze({
|
|
|
81
81
|
view: VIEW_FIELDS,
|
|
82
82
|
core: CORE_FIELDS,
|
|
83
83
|
});
|
|
84
|
+
/** Classification drives projection; special semantic normalization stays with each comparator. */
|
|
85
|
+
export function fieldsOf({ declaration, roles, level = 'reference', }) {
|
|
86
|
+
return Object.fromEntries(Object.entries(declaration).filter(([name, value]) => value !== undefined &&
|
|
87
|
+
(roles[name] === 'always' || (level === 'inherit' && roles[name] === 'inherit'))));
|
|
88
|
+
}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
export
|
|
2
|
-
export
|
|
1
|
+
export * from './api.js';
|
|
2
|
+
export { compareDependencyMeaning, compareDependencyStructure, compareMemberMeaning, describeMeaningChange, footprintOf, meaningOf, } from './legacy/index.js';
|
|
3
|
+
export type { DependencyMeaningChange, DependencyMeaningChangeKind, DependencyMeaningComparison, DependencyStructureComparison, FootprintEntry, } from './legacy/index.js';
|
|
3
4
|
export type { MeaningDifference } from './diff.js';
|
|
4
5
|
export { ANNOTATION_KEYWORDS, FIELD_ROLES } from './fields.js';
|
|
5
6
|
export type { DependencyMeaningLevel, FieldRole } from './fields.js';
|
|
6
|
-
export { callableFingerprint } from './
|
|
7
|
-
export type { MeaningFingerprint } from './
|
|
7
|
+
export { callableFingerprint } from './comparators/callables.js';
|
|
8
|
+
export type { Fingerprint as MeaningFingerprint } from './model.js';
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
-
export
|
|
1
|
+
export * from './api.js';
|
|
2
|
+
export { compareDependencyMeaning, compareDependencyStructure, compareMemberMeaning, describeMeaningChange, footprintOf, meaningOf, } from './legacy/index.js';
|
|
2
3
|
export { ANNOTATION_KEYWORDS, FIELD_ROLES } from './fields.js';
|
|
3
|
-
export { callableFingerprint } from './
|
|
4
|
+
export { callableFingerprint } from './comparators/callables.js';
|
|
@@ -0,0 +1,58 @@
|
|
|
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;
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
import { Key } from '../../../addressing/index.js';
|
|
2
|
+
import { compareUnicode } from '../../../addressing/ordering.js';
|
|
3
|
+
import { compareFacet } from '../comparators/index.js';
|
|
4
|
+
import { fingerprintMeaning } from '../comparators/values.js';
|
|
5
|
+
import { compareMeaning, compareStructure } from '../compare.js';
|
|
6
|
+
import { prepare, retainedContext } from '../scope.js';
|
|
7
|
+
export function compareDependencyMeaning(dependent, target) {
|
|
8
|
+
return legacyMeaning(compareMeaning({ scope: { kind: 'dependency', dependent }, target }));
|
|
9
|
+
}
|
|
10
|
+
export function compareMemberMeaning(source, target, roots) {
|
|
11
|
+
return legacyMeaning(compareMeaning({ scope: { kind: 'members', source, roots }, target }));
|
|
12
|
+
}
|
|
13
|
+
export function compareDependencyStructure(dependent, target) {
|
|
14
|
+
const report = compareStructure({ scope: { kind: 'dependency', dependent }, target });
|
|
15
|
+
const failed = new Set(report.assessments.filter((item) => !item.satisfied).map((item) => item.observation.id));
|
|
16
|
+
const changes = report.entries.flatMap((entry) => entry.observations.filter((item) => failed.has(item.id)).map((item) => changeOf(entry, item)));
|
|
17
|
+
return Object.freeze({
|
|
18
|
+
kind: 'structure',
|
|
19
|
+
version: 1,
|
|
20
|
+
dependency: report.scope.source.origin,
|
|
21
|
+
source: report.scope.source.revision,
|
|
22
|
+
target: report.scope.target.revision,
|
|
23
|
+
footprint: Object.freeze(report.entries
|
|
24
|
+
.filter((entry) => Key.origin(entry.subject.key) === report.scope.source.origin &&
|
|
25
|
+
entry.observations.some((item) => item.before !== undefined))
|
|
26
|
+
.map((entry) => entry.subject.key)),
|
|
27
|
+
changes: Object.freeze(changes),
|
|
28
|
+
fingerprint: report.evidence.fingerprint,
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
export function footprintOf(dependent, origin) {
|
|
32
|
+
const { source } = retainedContext(dependent, origin, '/origin');
|
|
33
|
+
const report = compareMeaning({ scope: { kind: 'dependency', dependent }, target: source });
|
|
34
|
+
return Object.freeze(report.entries.map((entry) => Object.freeze({
|
|
35
|
+
...entry.subject,
|
|
36
|
+
...(entry.subject.kind === 'class'
|
|
37
|
+
? {
|
|
38
|
+
level: strongest(entry).facet === 'class.inherit'
|
|
39
|
+
? 'inherit'
|
|
40
|
+
: 'reference',
|
|
41
|
+
}
|
|
42
|
+
: {}),
|
|
43
|
+
})));
|
|
44
|
+
}
|
|
45
|
+
export function meaningOf(schema, key, level = 'reference') {
|
|
46
|
+
const context = prepare({
|
|
47
|
+
scope: { kind: 'members', source: schema, roots: [key] },
|
|
48
|
+
target: schema,
|
|
49
|
+
});
|
|
50
|
+
const coordinate = context.roots[0].coordinate;
|
|
51
|
+
const facet = coordinate.kind === 'class'
|
|
52
|
+
? level === 'inherit'
|
|
53
|
+
? 'class.inherit'
|
|
54
|
+
: 'class.reference'
|
|
55
|
+
: coordinate.kind === 'method'
|
|
56
|
+
? 'method.signature'
|
|
57
|
+
: coordinate.kind === 'property'
|
|
58
|
+
? 'property'
|
|
59
|
+
: coordinate.ref.kind === 'function'
|
|
60
|
+
? 'function.signature'
|
|
61
|
+
: coordinate.ref.kind;
|
|
62
|
+
return compareFacet({ context, pair: context.pair(coordinate), facet }).before;
|
|
63
|
+
}
|
|
64
|
+
function strongest(entry) {
|
|
65
|
+
return (entry.observations.find((item) => item.facet === 'class.inherit') ??
|
|
66
|
+
entry.observations.find((item) => item.facet !== 'property.required') ??
|
|
67
|
+
entry.observations[0]);
|
|
68
|
+
}
|
|
69
|
+
function legacyMeaning(report) {
|
|
70
|
+
const coordinates = [];
|
|
71
|
+
const footprint = [];
|
|
72
|
+
const changes = [];
|
|
73
|
+
const additions = [];
|
|
74
|
+
for (const entry of report.entries) {
|
|
75
|
+
const added = entry.observations.find((item) => item.facet === 'property.required');
|
|
76
|
+
if (added !== undefined)
|
|
77
|
+
additions.push(changeOf(entry, added));
|
|
78
|
+
const observation = strongest(entry);
|
|
79
|
+
const change = changeOf(entry, observation);
|
|
80
|
+
if (observation.before === undefined) {
|
|
81
|
+
continue;
|
|
82
|
+
}
|
|
83
|
+
footprint.push(entry.subject.key);
|
|
84
|
+
coordinates.push([
|
|
85
|
+
entry.subject.key,
|
|
86
|
+
fingerprintMeaning(observation.before),
|
|
87
|
+
observation.after === undefined ? null : fingerprintMeaning(observation.after),
|
|
88
|
+
]);
|
|
89
|
+
if (change !== undefined)
|
|
90
|
+
changes.push(change);
|
|
91
|
+
}
|
|
92
|
+
additions.sort((a, b) => compareUnicode(a.key, b.key));
|
|
93
|
+
const identity = {
|
|
94
|
+
version: 3,
|
|
95
|
+
dependency: report.scope.source.origin,
|
|
96
|
+
source: report.scope.source.revision,
|
|
97
|
+
target: report.scope.target.revision,
|
|
98
|
+
};
|
|
99
|
+
return Object.freeze({
|
|
100
|
+
...identity,
|
|
101
|
+
footprint: Object.freeze(footprint),
|
|
102
|
+
changes: Object.freeze([...changes, ...additions]),
|
|
103
|
+
fingerprint: fingerprintMeaning({
|
|
104
|
+
...identity,
|
|
105
|
+
coordinates,
|
|
106
|
+
additions: additions.map(({ key, targetFingerprint }) => [key, targetFingerprint]),
|
|
107
|
+
}),
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
function changeOf(entry, observation) {
|
|
111
|
+
const key = entry.subject.key;
|
|
112
|
+
if (observation.state === 'equal')
|
|
113
|
+
return undefined;
|
|
114
|
+
if (observation.before === undefined)
|
|
115
|
+
return Object.freeze({
|
|
116
|
+
key,
|
|
117
|
+
kind: 'added',
|
|
118
|
+
targetFingerprint: fingerprintMeaning(observation.after),
|
|
119
|
+
meaning: observation.after,
|
|
120
|
+
});
|
|
121
|
+
const sourceFingerprint = fingerprintMeaning(observation.before);
|
|
122
|
+
if (observation.after === undefined)
|
|
123
|
+
return Object.freeze({ key, kind: 'missing', sourceFingerprint });
|
|
124
|
+
return Object.freeze({
|
|
125
|
+
key,
|
|
126
|
+
kind: 'changed',
|
|
127
|
+
sourceFingerprint,
|
|
128
|
+
targetFingerprint: fingerprintMeaning(observation.after),
|
|
129
|
+
differences: observation.differences,
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
/** One line an operator can act on, naming the exact paths of a changed meaning. */
|
|
133
|
+
export function describeMeaningChange(change) {
|
|
134
|
+
switch (change.kind) {
|
|
135
|
+
case 'missing':
|
|
136
|
+
return `${change.key} missing`;
|
|
137
|
+
case 'added':
|
|
138
|
+
return `${change.key} added as an obligation`;
|
|
139
|
+
case 'changed': {
|
|
140
|
+
const paths = change.differences.map(({ path }) => path);
|
|
141
|
+
const shown = paths.slice(0, 3).join(', ');
|
|
142
|
+
return `${change.key} changed at ${paths.length > 3 ? `${shown} and ${paths.length - 3} more` : shown}`;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { Report } from '../report.js';
|
|
2
|
+
import type { ComparisonContext } from '../scope.js';
|
|
3
|
+
/** Meaning follows complete callable contracts through the exact authored reference closure. */
|
|
4
|
+
export declare function compareFootprint({ context, report, }: {
|
|
5
|
+
readonly context: ComparisonContext;
|
|
6
|
+
readonly report: Report;
|
|
7
|
+
}): void;
|