@astrale-os/kernel-dsl 1.0.0-beta.8 → 1.0.0-beta.9
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/builder/lowering/class.js +1 -1
- package/dist/v1/builder/lowering/schema/context.d.ts +2 -0
- package/dist/v1/builder/lowering/schema/context.js +12 -0
- package/dist/v1/builder/schema/diagnostic.d.ts +1 -1
- package/dist/v1/language/class/inheritance.d.ts +9 -0
- package/dist/v1/language/class/inheritance.js +10 -1
- package/dist/v1/language/class/lifetime.d.ts +3 -1
- package/dist/v1/language/class/lifetime.js +5 -2
- package/dist/v1/language/index.d.ts +1 -1
- package/dist/v1/language/index.js +1 -1
- package/dist/v1/schema/compatibility/api.d.ts +1 -0
- package/dist/v1/schema/compatibility/api.js +1 -0
- package/dist/v1/schema/compatibility/extension.d.ts +11 -0
- package/dist/v1/schema/compatibility/extension.js +22 -0
- package/dist/v1/schema/compatibility/model.d.ts +7 -0
- package/dist/v1/schema/compatibility/scope.d.ts +2 -0
- package/dist/v1/schema/compatibility/scope.js +4 -1
- package/dist/v1/schema/compatibility/structure/requirements.js +14 -11
- package/dist/v1/schema/compatibility/versioning/surface.js +13 -8
- package/package.json +1 -1
|
@@ -152,7 +152,7 @@ export class V1DefinitionCompiler {
|
|
|
152
152
|
}
|
|
153
153
|
parents(values, kind, pointer) {
|
|
154
154
|
return (values ?? [])
|
|
155
|
-
.map((entry, index) => this.context.
|
|
155
|
+
.map((entry, index) => this.context.resolveParent(entry, kind, `${pointer}/${index}`))
|
|
156
156
|
.sort(compareDefinitionRefs);
|
|
157
157
|
}
|
|
158
158
|
methods(values, abstractOwner, pointer) {
|
|
@@ -29,6 +29,8 @@ export declare class BuilderContext {
|
|
|
29
29
|
captureFunction(value: Function, ref: FunctionRef, pointer: string): void;
|
|
30
30
|
captureView(value: View, ref: ViewRef, pointer: string): void;
|
|
31
31
|
resolveClass(input: ClassReferenceInput, kind: ClassKind, pointer: string): ClassRef;
|
|
32
|
+
/** A parent of another Domain must be open to it in the dependency revision authored against. */
|
|
33
|
+
resolveParent(input: ClassReferenceInput, kind: ClassKind, pointer: string): ClassRef;
|
|
32
34
|
resolveNodeClass(input: ClassReferenceInput, pointer: string): ClassRef;
|
|
33
35
|
resolveEdgeClass(input: ClassReferenceInput, pointer: string): ClassRef;
|
|
34
36
|
resolvePolicy(input: Lazy<unknown>, pointer: string): PolicyRef;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { compareDefinitionRefs, compareUnicode, definitionRefIndex, } from '../../../addressing/ordering.js';
|
|
2
2
|
import { DEPENDENCY_ALIAS_PATTERN } from '../../../addressing/patterns.js';
|
|
3
3
|
import { isMethodExecutable } from '../../../language/callable/executable.js';
|
|
4
|
+
import { admitsForeignSubclass, inheritanceOf } from '../../../language/class/inheritance.js';
|
|
4
5
|
import { policyObjectKey, policyObjectOrigin } from '../../../language/index.js';
|
|
5
6
|
import { graphNodePathSchema } from '../../../language/value/syntax/node-path/schema.js';
|
|
6
7
|
import { revisionOfDomainSchema } from '../../../schema/canonical/revision.js';
|
|
@@ -90,6 +91,17 @@ export class BuilderContext {
|
|
|
90
91
|
}
|
|
91
92
|
return { ...ref };
|
|
92
93
|
}
|
|
94
|
+
/** A parent of another Domain must be open to it in the dependency revision authored against. */
|
|
95
|
+
resolveParent(input, kind, pointer) {
|
|
96
|
+
const ref = this.resolveClass(input, kind, pointer);
|
|
97
|
+
if (ref.origin === this.origin)
|
|
98
|
+
return ref;
|
|
99
|
+
const parent = this.#dependencies.get(ref.origin).schema.classes[ref.name];
|
|
100
|
+
if (!admitsForeignSubclass(parent)) {
|
|
101
|
+
fail('BUILDER_CLASS_INHERITANCE', pointer, `Class ${ref.origin}:class.${ref.name} is ${inheritanceOf(parent)}: only an open Class admits a subclass of another Domain.`);
|
|
102
|
+
}
|
|
103
|
+
return ref;
|
|
104
|
+
}
|
|
93
105
|
resolveNodeClass(input, pointer) {
|
|
94
106
|
return this.resolveClass(input, 'node', pointer);
|
|
95
107
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { DiagnosticCode as LanguageDiagnosticCode } from '../../language/validation/diagnostic.js';
|
|
2
|
-
export type BuilderDiagnosticCode = LanguageDiagnosticCode | 'BUILDER_ABSTRACT_POLICY' | 'BUILDER_AMBIGUOUS_OWNERSHIP' | 'BUILDER_AUTO_EVENT_INVALID' | 'BUILDER_AUTO_IMMUTABLE' | 'BUILDER_AUTO_NESTED' | 'BUILDER_AUTO_SET_INVALID' | 'BUILDER_CALLABLE_AUTH' | 'BUILDER_CALLABLE_PLACEMENT' | 'BUILDER_CALLABLE_POLICY_AUTH' | 'BUILDER_CORE_CLASS' | 'BUILDER_CORE_EDGE' | 'BUILDER_CORE_NODE' | 'BUILDER_CORE_PROPERTY' | 'BUILDER_CORE_REF' | 'BUILDER_DEFINITION' | 'BUILDER_DEPENDENCY_CONFLICT' | 'BUILDER_DEPENDENCY_ALIAS' | 'BUILDER_DEPENDENCY_DIRECT' | 'BUILDER_DUPLICATE_MEMBER' | 'BUILDER_EDGE_LIFETIME' | 'BUILDER_FOREIGN_POLICY' | 'BUILDER_FULL_MATCH_INVALID' | 'BUILDER_FUNCTION' | 'BUILDER_METHOD' | 'BUILDER_PATH_DUPLICATE' | 'BUILDER_PATH_EMPTY' | 'BUILDER_POLICY' | 'BUILDER_POLICY_COMPOSITION' | 'BUILDER_POLICY_CYCLE' | 'BUILDER_POLICY_EMPTY_COMPOSITION' | 'BUILDER_POLICY_EMPTY_EXISTS' | 'BUILDER_POLICY_INPUT' | 'BUILDER_POLICY_REF' | 'BUILDER_POLICY_SELF' | 'BUILDER_PROPERTY_IMMUTABLE_INVALID' | 'BUILDER_REFERENCE_CYCLE' | 'BUILDER_REFERENCE_KIND' | 'BUILDER_REFERENCE_MISSING' | 'BUILDER_UNIQUE_PROPERTY' | 'BUILDER_UNKNOWN_FIELD' | 'BUILDER_UNOWNED_REFERENCE' | 'BUILDER_VIEW' | 'ZOD_SCHEMA_NOT_LOSSLESS';
|
|
2
|
+
export type BuilderDiagnosticCode = LanguageDiagnosticCode | 'BUILDER_ABSTRACT_POLICY' | 'BUILDER_AMBIGUOUS_OWNERSHIP' | 'BUILDER_AUTO_EVENT_INVALID' | 'BUILDER_AUTO_IMMUTABLE' | 'BUILDER_AUTO_NESTED' | 'BUILDER_AUTO_SET_INVALID' | 'BUILDER_CALLABLE_AUTH' | 'BUILDER_CALLABLE_PLACEMENT' | 'BUILDER_CALLABLE_POLICY_AUTH' | 'BUILDER_CLASS_INHERITANCE' | 'BUILDER_CORE_CLASS' | 'BUILDER_CORE_EDGE' | 'BUILDER_CORE_NODE' | 'BUILDER_CORE_PROPERTY' | 'BUILDER_CORE_REF' | 'BUILDER_DEFINITION' | 'BUILDER_DEPENDENCY_CONFLICT' | 'BUILDER_DEPENDENCY_ALIAS' | 'BUILDER_DEPENDENCY_DIRECT' | 'BUILDER_DUPLICATE_MEMBER' | 'BUILDER_EDGE_LIFETIME' | 'BUILDER_FOREIGN_POLICY' | 'BUILDER_FULL_MATCH_INVALID' | 'BUILDER_FUNCTION' | 'BUILDER_METHOD' | 'BUILDER_PATH_DUPLICATE' | 'BUILDER_PATH_EMPTY' | 'BUILDER_POLICY' | 'BUILDER_POLICY_COMPOSITION' | 'BUILDER_POLICY_CYCLE' | 'BUILDER_POLICY_EMPTY_COMPOSITION' | 'BUILDER_POLICY_EMPTY_EXISTS' | 'BUILDER_POLICY_INPUT' | 'BUILDER_POLICY_REF' | 'BUILDER_POLICY_SELF' | 'BUILDER_PROPERTY_IMMUTABLE_INVALID' | 'BUILDER_REFERENCE_CYCLE' | 'BUILDER_REFERENCE_KIND' | 'BUILDER_REFERENCE_MISSING' | 'BUILDER_UNIQUE_PROPERTY' | 'BUILDER_UNKNOWN_FIELD' | 'BUILDER_UNOWNED_REFERENCE' | 'BUILDER_VIEW' | 'ZOD_SCHEMA_NOT_LOSSLESS';
|
|
3
3
|
export interface BuilderDiagnostic<out Code extends BuilderDiagnosticCode = BuilderDiagnosticCode> {
|
|
4
4
|
readonly code: Code;
|
|
5
5
|
readonly pointer: string;
|
|
@@ -6,6 +6,8 @@ export interface ClassAncestry {
|
|
|
6
6
|
readonly kind: ClassKind;
|
|
7
7
|
readonly extends: readonly ClassRef[];
|
|
8
8
|
}
|
|
9
|
+
/** The origin of the Kernel, whose Schema every Domain depends on. */
|
|
10
|
+
export declare const KERNEL_ORIGIN = "kernel.astrale.ai";
|
|
9
11
|
/** Kernel graph roots are intrinsic supertypes, not authored structural Edge contracts. */
|
|
10
12
|
export declare function isGraphRoot(value: Pick<ClassAncestry, 'ref' | 'kind'>): boolean;
|
|
11
13
|
/** Same-kind satisfaction includes the intrinsic root and the declared parent closure. */
|
|
@@ -14,3 +16,10 @@ export declare function classSatisfies(candidate: ClassAncestry, required: Class
|
|
|
14
16
|
export declare function inheritanceOf(declaration: {
|
|
15
17
|
readonly inheritance?: Inheritance;
|
|
16
18
|
}): Inheritance;
|
|
19
|
+
/**
|
|
20
|
+
* Whether a Class of another Domain may extend this Class: only an `'open'` one admits it. Its own
|
|
21
|
+
* Domain may extend any Class that is not `'final'`.
|
|
22
|
+
*/
|
|
23
|
+
export declare function admitsForeignSubclass(declaration: {
|
|
24
|
+
readonly inheritance?: Inheritance;
|
|
25
|
+
}): boolean;
|
|
@@ -1,6 +1,8 @@
|
|
|
1
|
+
/** The origin of the Kernel, whose Schema every Domain depends on. */
|
|
2
|
+
export const KERNEL_ORIGIN = 'kernel.astrale.ai';
|
|
1
3
|
/** Kernel graph roots are intrinsic supertypes, not authored structural Edge contracts. */
|
|
2
4
|
export function isGraphRoot(value) {
|
|
3
|
-
return (value.ref.origin ===
|
|
5
|
+
return (value.ref.origin === KERNEL_ORIGIN &&
|
|
4
6
|
value.ref.name === (value.kind === 'node' ? 'Node' : 'Edge'));
|
|
5
7
|
}
|
|
6
8
|
/** Same-kind satisfaction includes the intrinsic root and the declared parent closure. */
|
|
@@ -27,3 +29,10 @@ export function classSatisfies(candidate, required, resolve) {
|
|
|
27
29
|
export function inheritanceOf(declaration) {
|
|
28
30
|
return declaration.inheritance ?? 'internal';
|
|
29
31
|
}
|
|
32
|
+
/**
|
|
33
|
+
* Whether a Class of another Domain may extend this Class: only an `'open'` one admits it. Its own
|
|
34
|
+
* Domain may extend any Class that is not `'final'`.
|
|
35
|
+
*/
|
|
36
|
+
export function admitsForeignSubclass(declaration) {
|
|
37
|
+
return inheritanceOf(declaration) === 'open';
|
|
38
|
+
}
|
|
@@ -21,7 +21,9 @@ export interface ContractEndpoint {
|
|
|
21
21
|
export declare function contractEndpoints(contract: EdgeContract): readonly [ContractEndpoint, ContractEndpoint];
|
|
22
22
|
/**
|
|
23
23
|
* Whether an endpoint of an Edge Class of `origin` can hold a Node of another Domain: it accepts a
|
|
24
|
-
* Class `origin` does not own, or one another Domain may extend.
|
|
24
|
+
* Class `origin` does not own, or one another Domain may extend. The Kernel's own relations are the
|
|
25
|
+
* exception: every Domain depends on the Kernel and extends its roots, so its relations bind every
|
|
26
|
+
* Node and keep the lifetimes they have as closed Classes, however open the Kernel's Classes are.
|
|
25
27
|
*/
|
|
26
28
|
export declare function reachesAnotherDomain(origin: ClassRef['origin'], endpoint: EdgeEndpointBase, scope: LifetimeScope): boolean;
|
|
27
29
|
/**
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { definitionRefIndex } from '../../addressing/ordering.js';
|
|
2
|
+
import { KERNEL_ORIGIN } from './inheritance.js';
|
|
2
3
|
/** The lifetime scope of `classes`. */
|
|
3
4
|
export function lifetimeScope(classes) {
|
|
4
5
|
const declarations = new Map();
|
|
@@ -35,10 +36,12 @@ export function contractEndpoints(contract) {
|
|
|
35
36
|
}
|
|
36
37
|
/**
|
|
37
38
|
* Whether an endpoint of an Edge Class of `origin` can hold a Node of another Domain: it accepts a
|
|
38
|
-
* Class `origin` does not own, or one another Domain may extend.
|
|
39
|
+
* Class `origin` does not own, or one another Domain may extend. The Kernel's own relations are the
|
|
40
|
+
* exception: every Domain depends on the Kernel and extends its roots, so its relations bind every
|
|
41
|
+
* Node and keep the lifetimes they have as closed Classes, however open the Kernel's Classes are.
|
|
39
42
|
*/
|
|
40
43
|
export function reachesAnotherDomain(origin, endpoint, scope) {
|
|
41
|
-
return endpoint.accepts.some((ref) => ref.origin !== origin || scope.extendable(ref));
|
|
44
|
+
return endpoint.accepts.some((ref) => ref.origin !== origin || (origin !== KERNEL_ORIGIN && scope.extendable(ref)));
|
|
42
45
|
}
|
|
43
46
|
/**
|
|
44
47
|
* The lifetime of each endpoint of `edge`, in endpoint order: the one it declares, else the derived
|
|
@@ -14,7 +14,7 @@ export type * from './callable/resolved.js';
|
|
|
14
14
|
export type * from './class/model/class.js';
|
|
15
15
|
export type { DataDefinition, DataFacet, MediaType } from './class/model/data.js';
|
|
16
16
|
export type * from './class/model/edge.js';
|
|
17
|
-
export { inheritanceOf } from './class/inheritance.js';
|
|
17
|
+
export { admitsForeignSubclass, inheritanceOf } from './class/inheritance.js';
|
|
18
18
|
export { edgeLifetimes, lifetimeScope } from './class/lifetime.js';
|
|
19
19
|
export type { LifetimeScope } from './class/lifetime.js';
|
|
20
20
|
export { EDGE_UNIQUE_FORMS, NODE_UNIQUE_FORMS, NO_UNIQUE_FORMS, UNIQUE_FORMS, compareEdgeUniqueFields, compareEdgeUniqueTuples, compareNodeUniqueTuples, coveringEdgeUniqueTuples, coveringNodeUniqueTuples, edgeUniqueForm, edgeUniqueFormOf, edgeUniqueKeys, edgeUniqueTuple, edgeUniqueValue, edgeUniqueValues, enforcesEdgeUniqueTuple, enforcesNodeUniqueTuple, keyableProperty, nodeUniqueForm, nodeUniqueKeys, nodeUniqueTuple, nodeUniqueValue, nodeUniqueValues, } from './class/unique.js';
|
|
@@ -3,7 +3,7 @@ export { isMethodExecutable } from './callable/executable.js';
|
|
|
3
3
|
export { isBinaryInput } from './callable/model/callable.js';
|
|
4
4
|
export { valueProgramIncludes } from './value/inclusion/includes.js';
|
|
5
5
|
export { stateMachine } from './state/index.js';
|
|
6
|
-
export { inheritanceOf } from './class/inheritance.js';
|
|
6
|
+
export { admitsForeignSubclass, inheritanceOf } from './class/inheritance.js';
|
|
7
7
|
export { edgeLifetimes, lifetimeScope } from './class/lifetime.js';
|
|
8
8
|
export { EDGE_UNIQUE_FORMS, NODE_UNIQUE_FORMS, NO_UNIQUE_FORMS, UNIQUE_FORMS, compareEdgeUniqueFields, compareEdgeUniqueTuples, compareNodeUniqueTuples, coveringEdgeUniqueTuples, coveringNodeUniqueTuples, edgeUniqueForm, edgeUniqueFormOf, edgeUniqueKeys, edgeUniqueTuple, edgeUniqueValue, edgeUniqueValues, enforcesEdgeUniqueTuple, enforcesNodeUniqueTuple, keyableProperty, nodeUniqueForm, nodeUniqueKeys, nodeUniqueTuple, nodeUniqueValue, nodeUniqueValues, } from './class/unique.js';
|
|
9
9
|
export { PropertyValues } from './class/property-values.js';
|
|
@@ -1,3 +1,4 @@
|
|
|
1
1
|
export { compare, compareMeaning, compareStructure } from './compare.js';
|
|
2
|
+
export { closedParents } from './extension.js';
|
|
2
3
|
export { requiredBump } from './versioning/index.js';
|
|
3
4
|
export type { Analysis, Assessment, Bump, Comparison, ComparisonEntry, ComparisonEvidence, ComparisonRequest, ComparisonScope, Facet, Fingerprint, MeaningComparison, Observation, RequiredBump, SchemaIdentity, StructureComparison, Subject, Usage, VersionChange, } from './model.js';
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { Key } from '../../addressing/index.js';
|
|
2
|
+
import type { DomainSchema } from '../model/schema.js';
|
|
3
|
+
/**
|
|
4
|
+
* The Classes of the target's origin that a Class of the dependent extends and the target does not
|
|
5
|
+
* open to it, as Keys in order (DM-CLASS-INHERITANCE). A parent the target no longer declares is
|
|
6
|
+
* not closed: losing it is an ancestry change.
|
|
7
|
+
*/
|
|
8
|
+
export declare function closedParents({ dependent, target, }: {
|
|
9
|
+
readonly dependent: DomainSchema;
|
|
10
|
+
readonly target: DomainSchema;
|
|
11
|
+
}): readonly Key[];
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { compareUnicode } from '../../addressing/ordering.js';
|
|
2
|
+
import { admitsForeignSubclass } from '../../language/class/inheritance.js';
|
|
3
|
+
import { keyForDefinitionRef } from './coordinate.js';
|
|
4
|
+
/**
|
|
5
|
+
* The Classes of the target's origin that a Class of the dependent extends and the target does not
|
|
6
|
+
* open to it, as Keys in order (DM-CLASS-INHERITANCE). A parent the target no longer declares is
|
|
7
|
+
* not closed: losing it is an ancestry change.
|
|
8
|
+
*/
|
|
9
|
+
export function closedParents({ dependent, target, }) {
|
|
10
|
+
const closed = new Set();
|
|
11
|
+
if (dependent.origin === target.origin)
|
|
12
|
+
return [];
|
|
13
|
+
for (const declaration of Object.values(dependent.classes))
|
|
14
|
+
for (const parent of declaration.extends) {
|
|
15
|
+
if (parent.origin !== target.origin)
|
|
16
|
+
continue;
|
|
17
|
+
const declared = target.classes[parent.name];
|
|
18
|
+
if (declared !== undefined && !admitsForeignSubclass(declared))
|
|
19
|
+
closed.add(keyForDefinitionRef(parent));
|
|
20
|
+
}
|
|
21
|
+
return Object.freeze([...closed].sort(compareUnicode));
|
|
22
|
+
}
|
|
@@ -17,6 +17,13 @@ export interface ComparisonRequest {
|
|
|
17
17
|
* unresolvable or unsupported Key of the target origin, is rejected with DM_CAPABILITY_INVALID.
|
|
18
18
|
*/
|
|
19
19
|
readonly capabilities?: readonly Key[];
|
|
20
|
+
/**
|
|
21
|
+
* `true` when the dependent already holds its binding to the target, as the installer holds
|
|
22
|
+
* one a Domain was installed with: the Classes of the target origin it extends are then not
|
|
23
|
+
* judged for inheritance (DM-CLASS-INHERITANCE). Otherwise each must be `'open'` in the
|
|
24
|
+
* target. It enters no evidence: a held comparison has the facts of a compatible judged one.
|
|
25
|
+
*/
|
|
26
|
+
readonly held?: boolean;
|
|
20
27
|
} | {
|
|
21
28
|
readonly kind: 'members';
|
|
22
29
|
readonly source: DomainSchema;
|
|
@@ -25,6 +25,8 @@ export interface ComparisonContext {
|
|
|
25
25
|
* structure records them only when they fail, so a compatible comparison keeps its bytes.
|
|
26
26
|
*/
|
|
27
27
|
readonly reliance: readonly Reference[];
|
|
28
|
+
/** Whether the dependent holds its binding to the target, so its extensions are not judged. */
|
|
29
|
+
readonly held: boolean;
|
|
28
30
|
pair(coordinate: Coordinate): SubjectPair;
|
|
29
31
|
}
|
|
30
32
|
export declare function prepare(request: ComparisonRequest): ComparisonContext;
|
|
@@ -42,6 +42,7 @@ export function prepare(request) {
|
|
|
42
42
|
roots: [...rootReferences(dependent), ...capabilities],
|
|
43
43
|
capabilities,
|
|
44
44
|
reliance: policyReliance(dependent, source.origin),
|
|
45
|
+
held: scope.held === true,
|
|
45
46
|
});
|
|
46
47
|
}
|
|
47
48
|
/**
|
|
@@ -63,9 +64,10 @@ export function selectMembers(source, target, keys) {
|
|
|
63
64
|
})),
|
|
64
65
|
capabilities: [],
|
|
65
66
|
reliance: [],
|
|
67
|
+
held: false,
|
|
66
68
|
});
|
|
67
69
|
}
|
|
68
|
-
function comparisonContext({ kind, source, target, dependent, before, roots, capabilities, reliance, }) {
|
|
70
|
+
function comparisonContext({ kind, source, target, dependent, before, roots, capabilities, reliance, held, }) {
|
|
69
71
|
resolve(target);
|
|
70
72
|
const after = contextOf(target);
|
|
71
73
|
const pairs = new Map();
|
|
@@ -78,6 +80,7 @@ function comparisonContext({ kind, source, target, dependent, before, roots, cap
|
|
|
78
80
|
roots,
|
|
79
81
|
capabilities,
|
|
80
82
|
reliance,
|
|
83
|
+
held,
|
|
81
84
|
scope: Object.freeze({
|
|
82
85
|
kind,
|
|
83
86
|
source: schemaIdentity(source),
|
|
@@ -5,6 +5,7 @@ import { compareFacet } from '../comparators/index.js';
|
|
|
5
5
|
import { fingerprintMeaning } from '../comparators/values.js';
|
|
6
6
|
import { contextInvalid } from '../context.js';
|
|
7
7
|
import { methodCoordinate } from '../coordinate.js';
|
|
8
|
+
import { closedParents } from '../extension.js';
|
|
8
9
|
import { callableReferences, classReferences, inherits, memberReferences, usageOf, valueReferences, } from '../references.js';
|
|
9
10
|
/** Structure follows data and abstract obligations; concrete IO never expands its requirements. */
|
|
10
11
|
export function compareRequirements({ context, report, }) {
|
|
@@ -98,27 +99,29 @@ function compareReliance({ context, report, }) {
|
|
|
98
99
|
}
|
|
99
100
|
}
|
|
100
101
|
/**
|
|
101
|
-
* A Class one of the dependent's own Classes extends must
|
|
102
|
-
*
|
|
103
|
-
* like a Policy reliance, so a compatible comparison keeps its evidence bytes
|
|
102
|
+
* A Class of the compared origin one of the dependent's own Classes extends must be `'open'` in the
|
|
103
|
+
* target, unless the dependent holds its binding to that target. The fact is recorded only when it
|
|
104
|
+
* fails, like a Policy reliance, so a compatible comparison keeps its evidence bytes: `'open'`, what
|
|
105
|
+
* a subclass of another Domain requires, before, and the target's inheritance after.
|
|
104
106
|
*/
|
|
105
107
|
function compareExtension({ context, report, }) {
|
|
108
|
+
if (context.held || context.dependent === undefined)
|
|
109
|
+
return;
|
|
110
|
+
const closed = new Set(closedParents({ dependent: context.dependent, target: context.target }));
|
|
111
|
+
if (closed.size === 0)
|
|
112
|
+
return;
|
|
106
113
|
for (const reference of context.roots) {
|
|
107
|
-
if (reference.usage.relation !== 'extends')
|
|
108
|
-
continue;
|
|
109
|
-
if (Key.origin(reference.coordinate.key) !== context.source.origin)
|
|
114
|
+
if (reference.usage.relation !== 'extends' || !closed.has(reference.coordinate.key))
|
|
110
115
|
continue;
|
|
111
116
|
const pair = context.pair(reference.coordinate);
|
|
112
|
-
if (pair.
|
|
113
|
-
continue;
|
|
114
|
-
if (pair.target.analysis.declaration.inheritance !== 'final')
|
|
117
|
+
if (pair.target?.kind !== 'class')
|
|
115
118
|
continue;
|
|
116
119
|
report.note(reference);
|
|
117
120
|
report.record({
|
|
118
121
|
pair,
|
|
119
122
|
facet: 'class.inheritance',
|
|
120
|
-
before: inheritanceMeaning(
|
|
121
|
-
after: inheritanceMeaning(
|
|
123
|
+
before: inheritanceMeaning('open'),
|
|
124
|
+
after: inheritanceMeaning(inheritanceOf(pair.target.analysis.declaration)),
|
|
122
125
|
requirement: { kind: 'class.inheritance' },
|
|
123
126
|
});
|
|
124
127
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { inheritanceOf } from '../../../language/class/inheritance.js';
|
|
1
|
+
import { admitsForeignSubclass, inheritanceOf } from '../../../language/class/inheritance.js';
|
|
2
2
|
import { methodInheritance } from '../comparators/callables.js';
|
|
3
3
|
import { compareAddedProperties, inheritanceMeaning } from '../comparators/classes.js';
|
|
4
4
|
import { compareFacet } from '../comparators/index.js';
|
|
@@ -57,16 +57,18 @@ export function compareSurface({ context, report, }) {
|
|
|
57
57
|
}
|
|
58
58
|
}
|
|
59
59
|
/**
|
|
60
|
-
* A changed inheritance is observed on its Class, and is major when
|
|
61
|
-
*
|
|
62
|
-
*
|
|
63
|
-
*
|
|
60
|
+
* A changed inheritance is observed on its Class, and is major when an `'open'` Class stops being
|
|
61
|
+
* open: only an `'open'` Class admits a subclass of another Domain (DM-CLASS-INHERITANCE). Closing
|
|
62
|
+
* or finalizing a Class no other Domain may extend takes nothing from a dependent; opening one
|
|
63
|
+
* grants an extension, which the floor, counting added members only, leaves at a patch.
|
|
64
64
|
*/
|
|
65
65
|
function compareExtension({ pair, report, }) {
|
|
66
66
|
if (pair.source?.kind !== 'class' || pair.target?.kind !== 'class')
|
|
67
67
|
return;
|
|
68
|
-
const
|
|
69
|
-
const
|
|
68
|
+
const source = pair.source.analysis.declaration;
|
|
69
|
+
const target = pair.target.analysis.declaration;
|
|
70
|
+
const before = inheritanceOf(source);
|
|
71
|
+
const after = inheritanceOf(target);
|
|
70
72
|
if (before === after)
|
|
71
73
|
return;
|
|
72
74
|
report.record({
|
|
@@ -74,7 +76,10 @@ function compareExtension({ pair, report, }) {
|
|
|
74
76
|
facet: 'class.inheritance',
|
|
75
77
|
before: inheritanceMeaning(before),
|
|
76
78
|
after: inheritanceMeaning(after),
|
|
77
|
-
requirement: {
|
|
79
|
+
requirement: {
|
|
80
|
+
kind: 'class.inheritance',
|
|
81
|
+
satisfied: !admitsForeignSubclass(source) || admitsForeignSubclass(target),
|
|
82
|
+
},
|
|
78
83
|
});
|
|
79
84
|
}
|
|
80
85
|
function changed({ before, after }) {
|