@astrale-os/kernel-dsl 0.2.0-beta.27 → 0.2.0-beta.29
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/callable/authoring.d.ts +1 -1
- package/dist/v1/builder/class/authoring.d.ts +2 -0
- package/dist/v1/builder/lowering/schema/context.js +28 -9
- package/dist/v1/builder/lowering/schema/ownership.d.ts +3 -3
- package/dist/v1/compiled/admission/semantics.js +7 -25
- package/dist/v1/domain/projection/classes.js +2 -19
- package/dist/v1/language/class/resolved.d.ts +3 -10
- package/dist/v1/schema/validation/class.js +5 -21
- package/dist/v1/schema/validation/policy/uses.js +14 -7
- package/package.json +1 -1
|
@@ -91,7 +91,7 @@ export type ProjectionOfCallable<Value extends Function | Method> = Value extend
|
|
|
91
91
|
/** Internal lowering access; Callable authoring handles expose no public configuration object. */
|
|
92
92
|
export declare function callableConfiguration(value: Function | Method): CallableInput;
|
|
93
93
|
export type PolicyObjectAuthoring = Class | Function | Policy | View | CoreNode;
|
|
94
|
-
export type PolicyObjectInput = Lazy<PolicyObjectAuthoring>;
|
|
94
|
+
export type PolicyObjectInput = Lazy<PolicyObjectAuthoring | PolicyObjectRef>;
|
|
95
95
|
export declare function func<const Config extends FunctionInput, Input = ValueInputOf<Config['input']>, Output = CallableOutputOf<Config['output']>, Auth extends FunctionAuth = Config['auth']>(config: Config): Function<CallableProjection<Input, Output, Auth, false, 'default'>>;
|
|
96
96
|
export declare function method<const Config extends MethodInput, Input = ValueInputOf<Config['input']>, Output = CallableOutputOf<Config['output']>, Auth extends FunctionAuth = Config['auth'], Static extends boolean = Config extends {
|
|
97
97
|
readonly static: true;
|
|
@@ -43,6 +43,7 @@ export interface NodeClassInput {
|
|
|
43
43
|
readonly data?: MediaType;
|
|
44
44
|
readonly methods?: Readonly<Record<string, Method>>;
|
|
45
45
|
readonly icon?: string;
|
|
46
|
+
/** Local observation Policies; inherited by no descendant and forbidden on abstract Classes. */
|
|
46
47
|
readonly policies?: {
|
|
47
48
|
readonly read?: Lazy<Policy>;
|
|
48
49
|
readonly traverse?: Lazy<Policy>;
|
|
@@ -53,6 +54,7 @@ export interface EdgeClassOptions {
|
|
|
53
54
|
readonly description?: string;
|
|
54
55
|
readonly extends?: readonly EdgeClassReferenceInput[];
|
|
55
56
|
readonly properties?: PropertyRecordInput;
|
|
57
|
+
/** Local observation Policies; inherited by no descendant and forbidden on abstract Classes. */
|
|
56
58
|
readonly policies?: {
|
|
57
59
|
readonly read?: Lazy<Policy>;
|
|
58
60
|
readonly traverse?: Lazy<Policy>;
|
|
@@ -50,22 +50,22 @@ export class BuilderContext {
|
|
|
50
50
|
this.capturePolicyObject(value, ref);
|
|
51
51
|
}
|
|
52
52
|
capturePolicy(value, ref, pointer) {
|
|
53
|
-
this.ownership.
|
|
53
|
+
this.ownership.captureDefinition(value, ref, pointer);
|
|
54
54
|
this.policies.set(value, ref);
|
|
55
55
|
this.capturePolicyObject(value, ref);
|
|
56
56
|
}
|
|
57
57
|
captureCoreNode(value, slug, pointer) {
|
|
58
|
-
this.ownership.capture(value, `${this.origin}:core.${slug}`, pointer);
|
|
59
58
|
const ref = { origin: this.origin, kind: 'core', name: slug };
|
|
59
|
+
this.ownership.captureDefinition(value, ref, pointer);
|
|
60
60
|
this.coreNodes.set(value, ref);
|
|
61
61
|
this.capturePolicyObject(value, ref);
|
|
62
62
|
}
|
|
63
63
|
captureFunction(value, ref, pointer) {
|
|
64
|
-
this.ownership.
|
|
64
|
+
this.ownership.captureDefinition(value, ref, pointer);
|
|
65
65
|
this.capturePolicyObject(value, ref);
|
|
66
66
|
}
|
|
67
67
|
captureView(value, ref, pointer) {
|
|
68
|
-
this.ownership.
|
|
68
|
+
this.ownership.captureDefinition(value, ref, pointer);
|
|
69
69
|
this.capturePolicyObject(value, ref);
|
|
70
70
|
}
|
|
71
71
|
resolveClass(input, kind, pointer) {
|
|
@@ -112,13 +112,18 @@ export class BuilderContext {
|
|
|
112
112
|
const ref = isPolicyObjectRef(value)
|
|
113
113
|
? value
|
|
114
114
|
: typeof value === 'object' && value !== null
|
|
115
|
-
? this.policyObjects.get(value)
|
|
115
|
+
? (this.policyObjects.get(value) ?? this.ownership.definition(value))
|
|
116
116
|
: undefined;
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
117
|
+
const dependency = ref === undefined ? undefined : this.#dependencies.get(ref.origin)?.schema;
|
|
118
|
+
const admitted = ref !== undefined &&
|
|
119
|
+
(ref.origin === this.origin
|
|
120
|
+
? this.policyObjectRefs.has(definitionRefIndex(ref))
|
|
121
|
+
: dependency !== undefined && hasPolicyObject(dependency, ref));
|
|
122
|
+
if (!admitted || ref === undefined) {
|
|
123
|
+
fail('BUILDER_POLICY_REF', pointer, 'Policy ref(...) must name one registered local or direct-dependency Definition, Function, Policy, View, or Core Node.');
|
|
121
124
|
}
|
|
125
|
+
if (ref.origin !== this.origin)
|
|
126
|
+
this.usedForeignOrigins.add(ref.origin);
|
|
122
127
|
return { ...ref };
|
|
123
128
|
}
|
|
124
129
|
resolveCoreEndpoint(value, pointer) {
|
|
@@ -245,3 +250,17 @@ function definitionRefsOf(schema) {
|
|
|
245
250
|
}
|
|
246
251
|
return refs.sort(compareDefinitionRefs);
|
|
247
252
|
}
|
|
253
|
+
function hasPolicyObject(schema, ref) {
|
|
254
|
+
switch (ref.kind) {
|
|
255
|
+
case 'class':
|
|
256
|
+
return Object.hasOwn(schema.classes, ref.name);
|
|
257
|
+
case 'function':
|
|
258
|
+
return Object.hasOwn(schema.functions, ref.name);
|
|
259
|
+
case 'policy':
|
|
260
|
+
return Object.hasOwn(schema.policies, ref.name);
|
|
261
|
+
case 'view':
|
|
262
|
+
return Object.hasOwn(schema.views, ref.name);
|
|
263
|
+
case 'core':
|
|
264
|
+
return Object.hasOwn(schema.core.nodes, ref.name);
|
|
265
|
+
}
|
|
266
|
+
}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { PolicyObjectRef } from '../../../language/policy/model/policy.js';
|
|
2
2
|
export declare class OwnershipCapture {
|
|
3
3
|
#private;
|
|
4
|
-
captureDefinition(value: object, ref:
|
|
4
|
+
captureDefinition(value: object, ref: PolicyObjectRef, pointer: string): void;
|
|
5
5
|
capture(value: object, owner: string, pointer: string): void;
|
|
6
|
-
definition(value: object):
|
|
6
|
+
definition(value: object): PolicyObjectRef | undefined;
|
|
7
7
|
commit(): void;
|
|
8
8
|
}
|
|
@@ -178,6 +178,10 @@ function policyKey(value) {
|
|
|
178
178
|
}
|
|
179
179
|
function validateClassFacets(context) {
|
|
180
180
|
for (const located of context.classes.values()) {
|
|
181
|
+
if (located.value.abstract &&
|
|
182
|
+
(located.value.policies.read !== undefined || located.value.policies.traverse !== undefined)) {
|
|
183
|
+
fail(`${located.pointer}/policies`, 'Abstract Classes cannot declare observation Policies.');
|
|
184
|
+
}
|
|
181
185
|
for (const [index, property] of located.value.properties.entries()) {
|
|
182
186
|
const path = `${located.pointer}/properties/${index}`;
|
|
183
187
|
if (property.auto !== undefined) {
|
|
@@ -252,26 +256,7 @@ function edgeRefines(context, child, parent) {
|
|
|
252
256
|
const direct = refine(0, 0) && refine(1, 1);
|
|
253
257
|
if (direct)
|
|
254
258
|
return true;
|
|
255
|
-
|
|
256
|
-
return reverse && !hasEffectiveEdgePolicy(context, parent, new Set());
|
|
257
|
-
}
|
|
258
|
-
function hasEffectiveEdgePolicy(context, declaration, visited) {
|
|
259
|
-
if (declaration.policies.read !== undefined || declaration.policies.traverse !== undefined) {
|
|
260
|
-
return true;
|
|
261
|
-
}
|
|
262
|
-
for (const parentRef of declaration.extends) {
|
|
263
|
-
const key = Key.of(parentRef);
|
|
264
|
-
if (visited.has(key))
|
|
265
|
-
continue;
|
|
266
|
-
visited.add(key);
|
|
267
|
-
const parent = context.classes.get(key)?.value;
|
|
268
|
-
if (parent !== undefined &&
|
|
269
|
-
isCompiledEdge(parent) &&
|
|
270
|
-
hasEffectiveEdgePolicy(context, parent, visited)) {
|
|
271
|
-
return true;
|
|
272
|
-
}
|
|
273
|
-
}
|
|
274
|
-
return false;
|
|
259
|
+
return refine(0, 1) && refine(1, 0);
|
|
275
260
|
}
|
|
276
261
|
function endpointRefines(context, child, parent) {
|
|
277
262
|
return child.accepts.every((accepted) => classSatisfiesAny(context, accepted, parent.accepts));
|
|
@@ -438,11 +423,8 @@ function validateCallablePolicy(context, entry, expression, inputIndex, fields,
|
|
|
438
423
|
!isSingleNodePathField(context, entry, inputIndex, object.field))
|
|
439
424
|
fail(`${path}/object`, 'Callable Policy input target must be a direct required single-Node Path field.');
|
|
440
425
|
}
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
context.definitions.get(Key.of(object.ref)) === undefined)
|
|
444
|
-
fail(`${path}/object/ref`, 'Callable Policy object refs must name an exact local Definition.');
|
|
445
|
-
}
|
|
426
|
+
// validateClosure already establishes that object refs name existing local
|
|
427
|
+
// or exact direct-dependency Definitions. Named Policy execution stays local.
|
|
446
428
|
return;
|
|
447
429
|
}
|
|
448
430
|
const children = 'allOf' in expression ? expression.allOf : expression.anyOf;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Key, MethodKey as MethodKeyValue, PropertyKey as PropertyKeyValue, } from '../../addressing/index.js';
|
|
2
2
|
import { acceptPropertyValues } from '../../language/class/property-values.js';
|
|
3
3
|
import { appendPointer } from '../../language/value/syntax/pointer.js';
|
|
4
4
|
import { createMethodCollection, createPropertyCollection } from '../namespace/member-collection.js';
|
|
@@ -24,16 +24,6 @@ export function createClasses(definitions, validation) {
|
|
|
24
24
|
throw new TypeError(`Class ${key} extends a Class of another graph kind.`);
|
|
25
25
|
}
|
|
26
26
|
const shell = {};
|
|
27
|
-
const policies = Object.freeze({
|
|
28
|
-
read: Object.freeze(uniquePolicyRefs([
|
|
29
|
-
...parents.flatMap((parent) => [...parent.policies.read]),
|
|
30
|
-
...(definition.policies.read === undefined ? [] : [definition.policies.read]),
|
|
31
|
-
])),
|
|
32
|
-
traverse: Object.freeze(uniquePolicyRefs([
|
|
33
|
-
...parents.flatMap((parent) => [...parent.policies.traverse]),
|
|
34
|
-
...(definition.policies.traverse === undefined ? [] : [definition.policies.traverse]),
|
|
35
|
-
])),
|
|
36
|
-
});
|
|
37
27
|
const effectiveProperties = new Map();
|
|
38
28
|
const effectiveMethods = new Map();
|
|
39
29
|
let inheritedData;
|
|
@@ -102,7 +92,7 @@ export function createClasses(definitions, validation) {
|
|
|
102
92
|
abstract: definition.abstract,
|
|
103
93
|
definition,
|
|
104
94
|
extends: Object.freeze(parents),
|
|
105
|
-
policies,
|
|
95
|
+
policies: definition.policies,
|
|
106
96
|
data: definition.kind === 'node' ? (ownData ?? inheritedData) : undefined,
|
|
107
97
|
properties: createPropertyCollection(effectiveProperties.values()),
|
|
108
98
|
methods: createMethodCollection(instanceMethods),
|
|
@@ -110,7 +100,6 @@ export function createClasses(definitions, validation) {
|
|
|
110
100
|
self: Object.freeze({
|
|
111
101
|
data: definition.kind === 'node' ? ownData : undefined,
|
|
112
102
|
properties: createPropertyCollection(ownProperties),
|
|
113
|
-
policies: definition.policies,
|
|
114
103
|
methods: createMethodCollection(ownInstanceMethods),
|
|
115
104
|
static: Object.freeze({ methods: createMethodCollection(ownStaticMethods) }),
|
|
116
105
|
}),
|
|
@@ -138,12 +127,6 @@ export function createClasses(definitions, validation) {
|
|
|
138
127
|
resolve(definition.ref);
|
|
139
128
|
return resolved;
|
|
140
129
|
}
|
|
141
|
-
function uniquePolicyRefs(values) {
|
|
142
|
-
const result = new Map();
|
|
143
|
-
for (const value of values)
|
|
144
|
-
result.set(Key.of(value), value);
|
|
145
|
-
return [...result.values()].sort((left, right) => compareUnicode(Key.of(left), Key.of(right)));
|
|
146
|
-
}
|
|
147
130
|
function validateProperties(abstract, properties, input) {
|
|
148
131
|
if (abstract) {
|
|
149
132
|
return [
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { Value as PortableValue } from '@astrale-os/kernel-dsl/value';
|
|
2
|
-
import type { ClassRef, DefinitionKey, Key,
|
|
2
|
+
import type { ClassRef, DefinitionKey, Key, PropertyKey } from '../../addressing/index.js';
|
|
3
3
|
import type { ResolvedMethodCollection } from '../callable/resolved.js';
|
|
4
4
|
import type { StateTopology } from '../state/index.js';
|
|
5
5
|
import type { Diagnostic } from '../validation/diagnostic.js';
|
|
@@ -25,11 +25,6 @@ export interface ResolvedData {
|
|
|
25
25
|
readonly owner: ResolvedClass<'node'>;
|
|
26
26
|
readonly definition: DataDefinition;
|
|
27
27
|
}
|
|
28
|
-
export interface ResolvedClassPolicies {
|
|
29
|
-
/** Effective read Policies for this Node or Edge Class. */
|
|
30
|
-
readonly read: readonly PolicyRef[];
|
|
31
|
-
readonly traverse: readonly PolicyRef[];
|
|
32
|
-
}
|
|
33
28
|
export type ResolvedPropertyCollection<ByKey extends Readonly<Record<PropertyKey, ResolvedProperty<unknown>>> = Readonly<Record<PropertyKey, ResolvedProperty<unknown>>>, ByName extends Readonly<Record<string, ResolvedProperty<unknown>>> = Readonly<Record<never, never>>> = Readonly<ByName> & Iterable<ByKey[keyof ByKey]> & {
|
|
34
29
|
readonly byKey: ByKey;
|
|
35
30
|
get<Key extends keyof ByKey>(key: Key): ByKey[Key];
|
|
@@ -47,8 +42,8 @@ export interface ResolvedClass<Kind extends ClassKind = ClassKind, Properties ex
|
|
|
47
42
|
readonly abstract: boolean;
|
|
48
43
|
readonly definition: Definition;
|
|
49
44
|
readonly extends: Parents & readonly ResolvedClass<Kind>[];
|
|
50
|
-
/**
|
|
51
|
-
readonly policies:
|
|
45
|
+
/** Local observation Policies; Class inheritance never contributes an attachment. */
|
|
46
|
+
readonly policies: Definition['policies'];
|
|
52
47
|
readonly data: Kind extends 'node' ? ResolvedData | undefined : undefined;
|
|
53
48
|
readonly properties: Properties;
|
|
54
49
|
readonly methods: Kind extends 'node' ? InstanceMethods : ResolvedMethodCollection<Record<never, never>>;
|
|
@@ -58,8 +53,6 @@ export interface ResolvedClass<Kind extends ClassKind = ClassKind, Properties ex
|
|
|
58
53
|
readonly self: {
|
|
59
54
|
readonly data: Kind extends 'node' ? ResolvedData | undefined : undefined;
|
|
60
55
|
readonly properties: SelfProperties;
|
|
61
|
-
/** Policies declared by this exact Class only. */
|
|
62
|
-
readonly policies: Definition['policies'];
|
|
63
56
|
readonly methods: Kind extends 'node' ? SelfInstanceMethods : ResolvedMethodCollection<Record<never, never>>;
|
|
64
57
|
readonly static: {
|
|
65
58
|
readonly methods: Kind extends 'node' ? SelfStaticMethods : ResolvedMethodCollection<Record<never, never>>;
|
|
@@ -15,6 +15,10 @@ export function validateDefinitions(environment, analysis) {
|
|
|
15
15
|
if (!sameRef(declaration.ref, ref)) {
|
|
16
16
|
diagnostics.push(diagnostic('DV_REF_RESOLVES', `${pointer}/ref`, `Class ref must be exactly ${formatDefinitionRef(ref)}.`, formatDefinitionRef(declaration.ref)));
|
|
17
17
|
}
|
|
18
|
+
if (declaration.abstract &&
|
|
19
|
+
(declaration.policies.read !== undefined || declaration.policies.traverse !== undefined)) {
|
|
20
|
+
diagnostics.push(diagnostic('DV_CLASS_ABSTRACT', `${pointer}/policies`, 'Abstract Classes cannot declare observation Policies.'));
|
|
21
|
+
}
|
|
18
22
|
validateFacet(environment, declaration, pointer, diagnostics);
|
|
19
23
|
validateParents(environment, ref, declaration.kind, declaration.extends, diagnostics);
|
|
20
24
|
if (declaration.kind === 'edge') {
|
|
@@ -159,27 +163,7 @@ function edgeContractRefines(environment, child, parent) {
|
|
|
159
163
|
const direct = refines(0, 0) && refines(1, 1);
|
|
160
164
|
if (direct)
|
|
161
165
|
return true;
|
|
162
|
-
|
|
163
|
-
return reverse && !hasEffectiveEdgePolicy(environment, parent, new Set());
|
|
164
|
-
}
|
|
165
|
-
function hasEffectiveEdgePolicy(environment, declaration, visited) {
|
|
166
|
-
if (declaration.policies.read !== undefined || declaration.policies.traverse !== undefined) {
|
|
167
|
-
return true;
|
|
168
|
-
}
|
|
169
|
-
for (const parentRef of declaration.extends) {
|
|
170
|
-
const key = definitionRefIndex(parentRef);
|
|
171
|
-
if (visited.has(key))
|
|
172
|
-
continue;
|
|
173
|
-
visited.add(key);
|
|
174
|
-
const parent = environment.resolveAny(parentRef);
|
|
175
|
-
if (parent !== undefined &&
|
|
176
|
-
parent.ref.kind === 'class' &&
|
|
177
|
-
parent.declaration.kind === 'edge' &&
|
|
178
|
-
hasEffectiveEdgePolicy(environment, parent.declaration, visited)) {
|
|
179
|
-
return true;
|
|
180
|
-
}
|
|
181
|
-
}
|
|
182
|
-
return false;
|
|
166
|
+
return refines(0, 1) && refines(1, 0);
|
|
183
167
|
}
|
|
184
168
|
function endpointRefines(environment, child, parent) {
|
|
185
169
|
return child.accepts.every((accepted) => classSatisfies(environment, accepted, parent.accepts));
|
|
@@ -122,7 +122,14 @@ function visitChecks(validation, typing, expression, context, pointer) {
|
|
|
122
122
|
}
|
|
123
123
|
function referenceContracts(validation, ref, pointer) {
|
|
124
124
|
const candidate = validation.environment.candidate;
|
|
125
|
-
|
|
125
|
+
const local = ref.origin === candidate.origin;
|
|
126
|
+
const direct = Object.values(candidate.dependencies).some((entry) => entry.origin === ref.origin);
|
|
127
|
+
const owner = local
|
|
128
|
+
? candidate
|
|
129
|
+
: direct
|
|
130
|
+
? validation.environment.dependencies.get(ref.origin)
|
|
131
|
+
: undefined;
|
|
132
|
+
if (owner === undefined)
|
|
126
133
|
return invalidPolicyObjectRef(validation, ref, pointer);
|
|
127
134
|
switch (ref.kind) {
|
|
128
135
|
case 'class': {
|
|
@@ -132,26 +139,26 @@ function referenceContracts(validation, ref, pointer) {
|
|
|
132
139
|
: invalidPolicyObjectRef(validation, ref, pointer);
|
|
133
140
|
}
|
|
134
141
|
case 'function':
|
|
135
|
-
return
|
|
142
|
+
return owner.functions[ref.name] === undefined
|
|
136
143
|
? invalidPolicyObjectRef(validation, ref, pointer)
|
|
137
144
|
: [{ kind: 'exact', ref: PROJECTION_FUNCTION }];
|
|
138
145
|
case 'policy':
|
|
139
|
-
return
|
|
146
|
+
return owner.policies[ref.name] === undefined
|
|
140
147
|
? invalidPolicyObjectRef(validation, ref, pointer)
|
|
141
148
|
: [{ kind: 'exact', ref: PROJECTION_POLICY }];
|
|
142
149
|
case 'view':
|
|
143
|
-
return
|
|
150
|
+
return owner.views[ref.name] === undefined
|
|
144
151
|
? invalidPolicyObjectRef(validation, ref, pointer)
|
|
145
152
|
: [{ kind: 'exact', ref: PROJECTION_VIEW }];
|
|
146
153
|
case 'core': {
|
|
147
|
-
const node =
|
|
154
|
+
const node = owner.core.nodes[ref.name];
|
|
148
155
|
if (node === undefined)
|
|
149
156
|
return invalidPolicyObjectRef(validation, ref, pointer);
|
|
150
157
|
const resolved = validation.environment.resolve(node.class);
|
|
151
158
|
const analyzed = validation.analysis.definitions.get(definitionRefIndex(node.class));
|
|
152
159
|
// Core validation owns invalid Core Class diagnostics. Avoid adding Policy noise for
|
|
153
160
|
// a Node that cannot materialize in the first place.
|
|
154
|
-
if (node.class.origin !==
|
|
161
|
+
if (node.class.origin !== owner.origin ||
|
|
155
162
|
resolved?.ref.kind !== 'class' ||
|
|
156
163
|
resolved.kind !== 'node' ||
|
|
157
164
|
analyzed === undefined) {
|
|
@@ -163,7 +170,7 @@ function referenceContracts(validation, ref, pointer) {
|
|
|
163
170
|
return invalidPolicyObjectRef(validation, ref, pointer);
|
|
164
171
|
}
|
|
165
172
|
function invalidPolicyObjectRef(validation, ref, pointer) {
|
|
166
|
-
validation.add('PL_REF', `${pointer}/ref`, `${formatDefinitionRef(ref)} is not an exact local projected Policy object.`, formatDefinitionRef(ref));
|
|
173
|
+
validation.add('PL_REF', `${pointer}/ref`, `${formatDefinitionRef(ref)} is not an exact local or direct-dependency projected Policy object.`, formatDefinitionRef(ref));
|
|
167
174
|
return undefined;
|
|
168
175
|
}
|
|
169
176
|
function selfContracts(validation, context, pointer) {
|