@astrale-os/kernel-dsl 0.2.0-beta.31 → 0.2.0-beta.33
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 +16 -13
- package/dist/v1/builder/callable/authoring.js +2 -2
- package/dist/v1/builder/class/authoring.d.ts +27 -9
- package/dist/v1/builder/lowering/callable.js +4 -7
- package/dist/v1/builder/lowering/schema/context.js +2 -1
- package/dist/v1/builder/schema/projection.d.ts +15 -18
- package/dist/v1/compiled/admission/accept.js +3 -7
- package/dist/v1/compiled/admission/semantics.js +21 -55
- package/dist/v1/compiled/compiler/compile.js +1 -1
- package/dist/v1/domain/projection/classes.js +5 -11
- package/dist/v1/index.d.ts +1 -1
- package/dist/v1/language/callable/composition.d.ts +15 -0
- package/dist/v1/language/callable/composition.js +42 -0
- package/dist/v1/language/callable/executable.d.ts +6 -0
- package/dist/v1/language/callable/executable.js +4 -0
- package/dist/v1/language/callable/model/callable.d.ts +1 -2
- package/dist/v1/language/callable/resolved.d.ts +9 -3
- package/dist/v1/language/class/facets.d.ts +3 -1
- package/dist/v1/language/index.d.ts +1 -0
- package/dist/v1/language/index.js +1 -0
- package/dist/v1/schema/analysis/class.d.ts +3 -3
- package/dist/v1/schema/analysis/class.js +7 -56
- package/dist/v1/schema/normalization/domain.js +1 -1
- package/dist/v1/schema/validation/class.js +2 -1
- package/dist/v1/schema/validation/domain.structure.js +1 -9
- package/dist/v1/schema/validation/policy/objects.js +2 -1
- package/package.json +1 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { Value as PortableValue } from '@astrale-os/kernel-dsl/value';
|
|
2
2
|
import type { z } from 'zod';
|
|
3
|
-
import type { FunctionAuth
|
|
3
|
+
import type { FunctionAuth } from '../../language/callable/model/callable.js';
|
|
4
4
|
import type { PolicyCheckExpression, PolicyCheckObject, PolicyObjectRef } from '../../language/policy/model/policy.js';
|
|
5
5
|
import type { Class } from '../class/authoring.js';
|
|
6
6
|
import type { ValueSchemaInput } from '../class/property.js';
|
|
@@ -43,10 +43,8 @@ export interface CallableInputBase {
|
|
|
43
43
|
readonly description?: string;
|
|
44
44
|
readonly input: ValueSchemaInput;
|
|
45
45
|
readonly output: CallableOutputInput;
|
|
46
|
-
readonly static?: boolean;
|
|
47
|
-
readonly inheritance?: MethodInheritance;
|
|
48
46
|
}
|
|
49
|
-
|
|
47
|
+
type ConcreteCallableInput = CallableInputBase & ({
|
|
50
48
|
readonly auth: 'anonymous';
|
|
51
49
|
readonly policy?: never;
|
|
52
50
|
} | {
|
|
@@ -56,18 +54,23 @@ export type CallableInput = CallableInputBase & ({
|
|
|
56
54
|
readonly auth: 'authorized';
|
|
57
55
|
readonly policy?: CallablePolicyBuilder;
|
|
58
56
|
});
|
|
59
|
-
export type FunctionInput =
|
|
57
|
+
export type FunctionInput = ConcreteCallableInput & {
|
|
60
58
|
readonly static?: never;
|
|
61
|
-
readonly
|
|
59
|
+
readonly abstract?: never;
|
|
60
|
+
};
|
|
61
|
+
export type MethodInput = ConcreteCallableInput & {
|
|
62
|
+
readonly static?: boolean;
|
|
63
|
+
/** Require every concrete descendant to declare this contract locally. */
|
|
64
|
+
readonly abstract?: boolean;
|
|
62
65
|
};
|
|
63
|
-
export type
|
|
64
|
-
export interface CallableProjection<out Input = unknown, out Output = unknown, out Auth extends FunctionAuth = FunctionAuth, out Static extends boolean = boolean, out
|
|
66
|
+
export type CallableInput = FunctionInput | MethodInput;
|
|
67
|
+
export interface CallableProjection<out Input = unknown, out Output = unknown, out Auth extends FunctionAuth = FunctionAuth, out Static extends boolean = boolean, out Abstract extends boolean = boolean> {
|
|
65
68
|
readonly [CALLABLE_PROJECTION_VALUE]: true;
|
|
66
69
|
readonly input: Input;
|
|
67
70
|
readonly output: Output;
|
|
68
71
|
readonly auth: Auth;
|
|
69
72
|
readonly static: Static;
|
|
70
|
-
readonly
|
|
73
|
+
readonly abstract: Abstract;
|
|
71
74
|
}
|
|
72
75
|
type ValueInputOf<Schema> = Schema extends TypedValueSchema<infer Value> ? Value : Schema extends z.ZodType ? z.input<Schema> : PortableValue;
|
|
73
76
|
type ValueOutputOf<Schema> = Schema extends TypedValueSchema<infer Value> ? Value : Schema extends z.ZodType ? z.output<Schema> : PortableValue;
|
|
@@ -93,12 +96,12 @@ export type ProjectionOfCallable<Value extends Function | Method> = Value extend
|
|
|
93
96
|
export declare function callableConfiguration(value: Function | Method): CallableInput;
|
|
94
97
|
export type PolicyObjectAuthoring = Class | Function | Method | Policy | View | CoreNode;
|
|
95
98
|
export type PolicyObjectInput = Lazy<PolicyObjectAuthoring | PolicyObjectRef>;
|
|
96
|
-
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,
|
|
99
|
+
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, false>>;
|
|
97
100
|
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 {
|
|
98
101
|
readonly static: true;
|
|
99
|
-
} ? true : false,
|
|
100
|
-
readonly
|
|
101
|
-
} ? Value :
|
|
102
|
+
} ? true : false, Abstract extends boolean = Config extends {
|
|
103
|
+
readonly abstract: infer Value extends boolean;
|
|
104
|
+
} ? Value : false>(config: Config): Method<CallableProjection<Input, Output, Auth, Static, Abstract>>;
|
|
102
105
|
export declare function callableInputField(name: string): Extract<PolicyCheckObject, {
|
|
103
106
|
readonly kind: 'input';
|
|
104
107
|
}>;
|
|
@@ -16,7 +16,7 @@ export function func(config) {
|
|
|
16
16
|
'auth',
|
|
17
17
|
'policy',
|
|
18
18
|
'static',
|
|
19
|
-
'
|
|
19
|
+
'abstract',
|
|
20
20
|
]);
|
|
21
21
|
const value = authoringValue('function', {});
|
|
22
22
|
callableInputs.set(value, { ...config });
|
|
@@ -30,7 +30,7 @@ export function method(config) {
|
|
|
30
30
|
'auth',
|
|
31
31
|
'policy',
|
|
32
32
|
'static',
|
|
33
|
-
'
|
|
33
|
+
'abstract',
|
|
34
34
|
]);
|
|
35
35
|
const value = authoringValue('method', {});
|
|
36
36
|
callableInputs.set(value, { ...config });
|
|
@@ -139,16 +139,32 @@ type OwnMethodProjectionsOf<Input, Static extends boolean> = {
|
|
|
139
139
|
[Name in keyof MethodFieldsOf<Input>]: MethodFieldsOf<Input>[Name] extends infer Value extends Method ? MethodIsStatic<Value> extends Static ? AuthoredMethodProjection<Value, Extract<Name, string>> : never : never;
|
|
140
140
|
}[keyof MethodFieldsOf<Input>];
|
|
141
141
|
type BindMethodOwners<Entries, Owner> = Entries extends AuthoredMethodProjection<infer Value, infer Name, infer ExistingOwner> ? AuthoredMethodProjection<Value, Name, ExistingOwner extends 'self' ? Owner : ExistingOwner> : Entries;
|
|
142
|
-
type
|
|
143
|
-
readonly
|
|
144
|
-
|
|
145
|
-
|
|
142
|
+
type AbstractMethodEntries<Entry> = Entry extends AuthoredMethodProjection<infer Value> ? ProjectionOfCallable<Value> extends {
|
|
143
|
+
readonly abstract: true;
|
|
144
|
+
} ? Entry : never : Entry extends {
|
|
145
|
+
readonly abstract: true;
|
|
146
|
+
} ? Entry : never;
|
|
147
|
+
type AbstractMethodsOfReference<Value, Static extends boolean, Seen = never> = Value extends Seen ? never : ResolveCompactLazy<Value> extends infer Resolved ? Resolved extends NodeClass<infer Config> ? Config extends {
|
|
148
|
+
readonly abstractInstanceMethods: infer InstanceMethods;
|
|
149
|
+
readonly abstractStaticMethods: infer StaticMethods;
|
|
150
|
+
} ? BindMethodOwners<Static extends true ? StaticMethods : InstanceMethods, ClassOwnerProjectionOf<Resolved>> : never : Resolved extends ResolvedClass<'node'> ? ResolvedAbstractMethods<Resolved, Static, Seen | Value> : never : never;
|
|
151
|
+
type ResolvedOwnAbstractMethods<Value extends ResolvedClass<'node'>, Static extends boolean> = AbstractMethodEntries<Static extends true ? Value['self']['static']['methods']['byKey'][keyof Value['self']['static']['methods']['byKey']] : Value['self']['methods']['byKey'][keyof Value['self']['methods']['byKey']]>;
|
|
152
|
+
type ParentTupleMembers<Parents> = Parents extends readonly [infer Parent, ...infer Rest] ? Parent | ParentTupleMembers<Rest> : never;
|
|
153
|
+
type ResolvedParents<Value> = Value extends {
|
|
154
|
+
readonly extends: infer Parents;
|
|
155
|
+
} ? ParentTupleMembers<Parents> : never;
|
|
156
|
+
type ResolvedAbstractMethods<Value extends ResolvedClass<'node'>, Static extends boolean, Seen> = ResolvedOwnAbstractMethods<Value, Static> | WithoutMethodNames<AbstractMethodsOfReference<ResolvedParents<Value>, Static, Seen>, MethodNames<ResolvedOwnAbstractMethods<Value, Static>>>;
|
|
146
157
|
type ParentMethodProjectionsOf<Input, Static extends boolean> = Input extends {
|
|
147
158
|
readonly extends?: readonly (infer Parent)[];
|
|
148
|
-
} ?
|
|
149
|
-
type
|
|
159
|
+
} ? AbstractMethodsOfReference<Parent, Static> : never;
|
|
160
|
+
type MethodNames<Entry> = Entry extends {
|
|
150
161
|
readonly name: infer Name;
|
|
151
|
-
} ? Name
|
|
162
|
+
} ? Name : never;
|
|
163
|
+
type WithoutMethodNames<Entry, Names> = Entry extends {
|
|
164
|
+
readonly name: infer Name;
|
|
165
|
+
} ? Name extends Names ? never : Entry : never;
|
|
166
|
+
type AbstractMethodProjectionsOf<Input, Static extends boolean> = AbstractMethodEntries<OwnMethodProjectionsOf<Input, Static>> | WithoutMethodNames<ParentMethodProjectionsOf<Input, Static>, AbstractMethodEntries<OwnMethodProjectionsOf<Input, Static>>['name']>;
|
|
167
|
+
type EffectiveMethodProjectionsOf<Input, Static extends boolean> = OwnMethodProjectionsOf<Input, Static> | WithoutMethodNames<ParentMethodProjectionsOf<Input, Static>, keyof MethodFieldsOf<Input>>;
|
|
152
168
|
type ClassDepthOf<Value> = ResolveCompactLazy<Value> extends infer Resolved ? Resolved extends NodeClass<infer Config> ? Config extends {
|
|
153
169
|
readonly depth: infer Depth extends number;
|
|
154
170
|
} ? Depth : 0 : Resolved extends DirectedEdgeClass<infer Config> | UndirectedEdgeClass<infer Config> ? Config extends {
|
|
@@ -222,13 +238,13 @@ type OwnerErasedPropertyProjection<Entry> = Entry extends AuthoredPropertyProjec
|
|
|
222
238
|
readonly key: Key;
|
|
223
239
|
readonly state?: State;
|
|
224
240
|
} : never;
|
|
225
|
-
type OwnerErasedMethodProjection<Entry> = Entry extends AuthoredMethodProjection<infer Value, infer Name, unknown> ? AuthoredMethodProjection<Value, Name, unknown> : Entry extends ResolvedMethod<infer Input, infer Output, infer Static, infer
|
|
241
|
+
type OwnerErasedMethodProjection<Entry> = Entry extends AuthoredMethodProjection<infer Value, infer Name, unknown> ? AuthoredMethodProjection<Value, Name, unknown> : Entry extends ResolvedMethod<infer Input, infer Output, infer Static, infer Abstract, infer Auth, infer _Policy, infer Key> & {
|
|
226
242
|
readonly name: infer Name;
|
|
227
243
|
} ? {
|
|
228
244
|
readonly input: Input;
|
|
229
245
|
readonly output: Output;
|
|
230
246
|
readonly static: Static;
|
|
231
|
-
readonly
|
|
247
|
+
readonly abstract: Abstract;
|
|
232
248
|
readonly auth: Auth;
|
|
233
249
|
readonly name: Name;
|
|
234
250
|
readonly key: Key;
|
|
@@ -274,6 +290,8 @@ type CompactNodeInput<Input extends NodeClassInput> = {
|
|
|
274
290
|
readonly methods?: infer Value;
|
|
275
291
|
} ? NonNullable<Value> : Readonly<Record<never, never>> : Readonly<Record<never, never>>;
|
|
276
292
|
readonly effectiveProperties: EffectivePropertyProjectionsOf<Input>;
|
|
293
|
+
readonly abstractInstanceMethods: AbstractMethodProjectionsOf<Input, false>;
|
|
294
|
+
readonly abstractStaticMethods: AbstractMethodProjectionsOf<Input, true>;
|
|
277
295
|
readonly effectiveInstanceMethods: EffectiveMethodProjectionsOf<Input, false>;
|
|
278
296
|
readonly effectiveStaticMethods: EffectiveMethodProjectionsOf<Input, true>;
|
|
279
297
|
};
|
|
@@ -20,14 +20,11 @@ export class V1CallableCompiler {
|
|
|
20
20
|
fail('BUILDER_CALLABLE_POLICY_AUTH', `${placement.pointer}/policy`, 'Only an authorized Function or Method may declare a Policy check.');
|
|
21
21
|
}
|
|
22
22
|
const method = placement.kind !== 'function';
|
|
23
|
-
if (!method && (config.static !== undefined || config.
|
|
24
|
-
fail('BUILDER_CALLABLE_PLACEMENT', placement.pointer, 'A standalone Function cannot declare Method staticness or
|
|
23
|
+
if (!method && (config.static !== undefined || config.abstract !== undefined)) {
|
|
24
|
+
fail('BUILDER_CALLABLE_PLACEMENT', placement.pointer, 'A standalone Function cannot declare Method staticness or abstractness.');
|
|
25
25
|
}
|
|
26
26
|
const isStatic = method ? (config.static ?? false) : undefined;
|
|
27
|
-
const
|
|
28
|
-
if (inheritance === 'abstract' && config.policy !== undefined) {
|
|
29
|
-
fail('BUILDER_ABSTRACT_POLICY', `${placement.pointer}/policy`, 'An abstract Method cannot declare a Policy check.');
|
|
30
|
-
}
|
|
27
|
+
const isAbstract = method ? (config.abstract ?? false) : undefined;
|
|
31
28
|
const input = this.values.schema(config.input, 'callable-input', `${placement.pointer}/input`);
|
|
32
29
|
const output = this.compileOutput(config.output, `${placement.pointer}/output`);
|
|
33
30
|
const policy = config.policy === undefined
|
|
@@ -46,7 +43,7 @@ export class V1CallableCompiler {
|
|
|
46
43
|
return {
|
|
47
44
|
...contract,
|
|
48
45
|
static: isStatic,
|
|
49
|
-
|
|
46
|
+
abstract: isAbstract,
|
|
50
47
|
};
|
|
51
48
|
}
|
|
52
49
|
compileOutput(value, pointer) {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { compareDefinitionRefs, compareUnicode, definitionRefIndex, } from '../../../addressing/ordering.js';
|
|
2
2
|
import { DEPENDENCY_ALIAS_PATTERN } from '../../../addressing/patterns.js';
|
|
3
|
+
import { isMethodExecutable } from '../../../language/callable/executable.js';
|
|
3
4
|
import { policyObjectKey, policyObjectOrigin } from '../../../language/index.js';
|
|
4
5
|
import { graphNodePathSchema } from '../../../language/value/syntax/node-path/schema.js';
|
|
5
6
|
import { revisionOfDomainSchema } from '../../../schema/canonical/revision.js';
|
|
@@ -260,7 +261,7 @@ function hasPolicyObject(schema, ref) {
|
|
|
260
261
|
case 'method': {
|
|
261
262
|
const owner = schema.classes[ref.owner.name];
|
|
262
263
|
const method = owner?.kind === 'node' ? owner.methods[ref.name] : undefined;
|
|
263
|
-
return method !== undefined && method
|
|
264
|
+
return method !== undefined && isMethodExecutable(owner, method);
|
|
264
265
|
}
|
|
265
266
|
case 'class':
|
|
266
267
|
return Object.hasOwn(schema.classes, ref.name);
|
|
@@ -4,7 +4,7 @@ import type * as language from '../../language/index.js';
|
|
|
4
4
|
import type { StateTopology } from '../../language/state/index.js';
|
|
5
5
|
import type { DomainOfSchema, DomainSchema } from '../../schema/model/schema.js';
|
|
6
6
|
import type { CallableProjection, Function, Method } from '../callable/authoring.js';
|
|
7
|
-
import type { AuthoredPropertyProjection,
|
|
7
|
+
import type { AuthoredPropertyProjection, Class, ClassIdentityProjection, ClassIdentityProjectionOf, ClassOwnerProjection, ClassOwnerProjectionOf, ClassReferenceProjection, ClassReferenceProjectionOf, DirectedEdgeClass, NodeClass, ProjectionOfClass, UndirectedEdgeClass } from '../class/authoring.js';
|
|
8
8
|
import type { DomainSchemaInput } from './input.js';
|
|
9
9
|
export interface PropertyProjection<Value = unknown, Required extends boolean = boolean, Name extends string = string, Key extends language.PropertyKey = language.PropertyKey, State extends StateTopology | undefined = StateTopology | undefined> {
|
|
10
10
|
readonly value: Value;
|
|
@@ -13,11 +13,13 @@ export interface PropertyProjection<Value = unknown, Required extends boolean =
|
|
|
13
13
|
readonly key: Key;
|
|
14
14
|
readonly state?: State;
|
|
15
15
|
}
|
|
16
|
-
|
|
16
|
+
type MethodExecutableOf<OwnerAbstract extends boolean, MethodAbstract extends boolean> = MethodAbstract extends false ? true : OwnerAbstract extends false ? true : false;
|
|
17
|
+
export interface MethodProjection<Input = unknown, Output = unknown, Static extends boolean = boolean, Abstract extends boolean = boolean, Auth extends language.FunctionAuth = language.FunctionAuth, Name extends string = string, Key extends language.MethodKey = language.MethodKey, Executable extends boolean = boolean> {
|
|
17
18
|
readonly input: Input;
|
|
18
19
|
readonly output: Output;
|
|
19
20
|
readonly static: Static;
|
|
20
|
-
readonly
|
|
21
|
+
readonly abstract: Abstract;
|
|
22
|
+
readonly executable: Executable;
|
|
21
23
|
readonly auth: Auth;
|
|
22
24
|
readonly name: Name;
|
|
23
25
|
readonly key: Key;
|
|
@@ -251,20 +253,20 @@ type CallableOutputValue<Value extends Method | Function> = CallableConfig<Value
|
|
|
251
253
|
type CallableStaticOf<Value extends Method> = CallableConfig<Value> extends {
|
|
252
254
|
readonly static: true;
|
|
253
255
|
} ? true : false;
|
|
254
|
-
type
|
|
255
|
-
readonly
|
|
256
|
-
} ?
|
|
256
|
+
type CallableAbstractOf<Value extends Method> = CallableConfig<Value> extends {
|
|
257
|
+
readonly abstract: infer Abstract extends boolean;
|
|
258
|
+
} ? Abstract : false;
|
|
257
259
|
type CallableAuthOf<Value extends Method | Function> = CallableConfig<Value>['auth'];
|
|
258
|
-
type MethodProjectionOfEntry<Origin extends string, OwnerName extends string, Entry> = Entry extends language.ResolvedMethod ? MethodProjectionOfResolved<Entry> : Entry extends AuthoredMethodEntry<
|
|
260
|
+
type MethodProjectionOfEntry<Origin extends string, OwnerName extends string, Entry> = Entry extends language.ResolvedMethod ? MethodProjectionOfResolved<Entry> : Entry extends AuthoredMethodEntry<infer Owner, infer Name, infer Value> ? MethodProjection<CallableInputValue<Value>, CallableOutputValue<Value>, CallableStaticOf<Value>, CallableAbstractOf<Value>, CallableAuthOf<Value>, Name, language.MethodKey<Origin, OwnerName, Name>, MethodExecutableOf<AbstractOf<ClassInputOf<Owner>>, CallableAbstractOf<Value>>> : never;
|
|
259
261
|
type MethodProjectionOfEntries<Origin extends string, OwnerName extends string, Entries> = Entries extends unknown ? MethodProjectionOfEntry<Origin, OwnerName, Entries> : never;
|
|
260
262
|
type MethodCollectionOperation = 'byKey' | 'get';
|
|
261
263
|
type PropertyProjectionOfResolved<Value> = Value extends language.ResolvedProperty<infer PropertyValue, infer Required, infer State> & {
|
|
262
264
|
readonly name: infer Name extends string;
|
|
263
265
|
readonly key: infer Key extends language.PropertyKey;
|
|
264
266
|
} ? PropertyProjection<PropertyValue, Required, Name, Key, State> : never;
|
|
265
|
-
type MethodProjectionOfResolved<Value> = Value extends language.ResolvedMethod<infer Input, infer Output, infer Static, infer
|
|
267
|
+
type MethodProjectionOfResolved<Value> = Value extends language.ResolvedMethod<infer Input, infer Output, infer Static, infer Abstract, infer Auth, infer _Policy, infer Key> & {
|
|
266
268
|
readonly name: infer Name extends string;
|
|
267
|
-
} ? MethodProjection<Input, Output, Static,
|
|
269
|
+
} ? MethodProjection<Input, Output, Static, Abstract, Auth, Name, Key, Value['executable']> : never;
|
|
268
270
|
type PropertiesOfCollection<Value> = Value extends {
|
|
269
271
|
readonly byKey: infer ByKey extends Readonly<Record<language.PropertyKey, language.ResolvedProperty<unknown, boolean>>>;
|
|
270
272
|
} ? PropertyProjectionOfResolved<ByKey[keyof ByKey]> : never;
|
|
@@ -299,20 +301,14 @@ type EffectivePropertyProjectionOfEntry<Origin extends string, CurrentName exten
|
|
|
299
301
|
type EffectivePropertyProjectionsOf<Origin extends string, Name extends string, Value, Root extends DomainSchemaInput> = EffectivePropertyEntriesOfClass<Value> extends infer Entry ? Entry extends unknown ? EffectivePropertyProjectionOfEntry<Origin, Name, Root, Entry> : never : never;
|
|
300
302
|
type OwnInstanceMethodProjectionsOf<Origin extends string, Name extends string, Value> = MethodProjectionOfEntries<Origin, Name, OwnMethodEntries<Value, false>>;
|
|
301
303
|
type OwnStaticMethodProjectionsOf<Origin extends string, Name extends string, Value> = MethodProjectionOfEntries<Origin, Name, OwnMethodEntries<Value, true>>;
|
|
302
|
-
type EffectiveMethodEntriesOfClass<Value, Static extends boolean> = ClassInputOf<Value> extends {
|
|
303
|
-
readonly effectiveInstanceMethods: infer InstanceMethods;
|
|
304
|
-
readonly effectiveStaticMethods: infer StaticMethods;
|
|
305
|
-
} ? Static extends true ? StaticMethods : InstanceMethods : never;
|
|
306
|
-
type EffectiveMethodProjectionOfEntry<Origin extends string, CurrentName extends string, Root extends DomainSchemaInput, Entry> = Entry extends language.ResolvedMethod ? MethodProjectionOfResolved<Entry> : Entry extends AuthoredMethodProjection<infer Value, infer MethodName, infer Owner> ? OwnerNameOf<Root, CurrentName, Owner> extends infer OwnerName extends string ? MethodProjection<CallableInputValue<Value>, CallableOutputValue<Value>, CallableStaticOf<Value>, CallableInheritanceOf<Value>, CallableAuthOf<Value>, MethodName, language.MethodKey<Origin, OwnerName, MethodName>> : never : never;
|
|
307
|
-
type EffectiveMethodProjectionsOf<Origin extends string, Name extends string, Value, Root extends DomainSchemaInput, Static extends boolean> = EffectiveMethodEntriesOfClass<Value, Static> extends infer Entry ? Entry extends unknown ? EffectiveMethodProjectionOfEntry<Origin, Name, Root, Entry> : never : never;
|
|
308
304
|
type ShallowClassProjectionOfAuthoring<Origin extends string, Name extends string, Value, Root extends DomainSchemaInput> = Value extends NodeClass<infer _Config> ? ClassProjection<'node', Extract<ProjectedClassDefinition<Origin, Name, Value>, language.NodeClassDefinition>, language.ClassRef<Name> & {
|
|
309
305
|
readonly origin: Origin;
|
|
310
|
-
}, language.DefinitionKey<Origin, 'class', Name>, readonly language.AnyResolvedClass[], EffectivePropertyProjectionsOf<Origin, Name, Value, Root>,
|
|
306
|
+
}, language.DefinitionKey<Origin, 'class', Name>, readonly language.AnyResolvedClass[], EffectivePropertyProjectionsOf<Origin, Name, Value, Root>, OwnInstanceMethodProjectionsOf<Origin, Name, Value>, OwnStaticMethodProjectionsOf<Origin, Name, Value>, OwnPropertyProjectionsOf<Origin, Name, Value>, OwnInstanceMethodProjectionsOf<Origin, Name, Value>, OwnStaticMethodProjectionsOf<Origin, Name, Value>> : Value extends DirectedEdgeClass | UndirectedEdgeClass ? ClassProjection<'edge', Extract<ProjectedClassDefinition<Origin, Name, Value>, language.EdgeClassDefinition>, language.ClassRef<Name> & {
|
|
311
307
|
readonly origin: Origin;
|
|
312
308
|
}, language.DefinitionKey<Origin, 'class', Name>, readonly language.AnyResolvedClass[], EffectivePropertyProjectionsOf<Origin, Name, Value, Root>, never, never, OwnPropertyProjectionsOf<Origin, Name, Value>, never, never> : never;
|
|
313
309
|
type ClassProjectionOfAuthoring<Origin extends string, Name extends string, Value, Root extends DomainSchemaInput, Seen = Value> = Value extends NodeClass ? ClassProjection<'node', Extract<ProjectedClassDefinition<Origin, Name, Value>, language.NodeClassDefinition>, language.ClassRef<Name> & {
|
|
314
310
|
readonly origin: Origin;
|
|
315
|
-
}, language.DefinitionKey<Origin, 'class', Name>, ParentClassProjectionTuple<Origin, ParentInputsOfConfig<ProjectionOfClass<Extract<Value, Class>>>, Root, Seen>, EffectivePropertyProjectionsOf<Origin, Name, Value, Root>,
|
|
311
|
+
}, language.DefinitionKey<Origin, 'class', Name>, ParentClassProjectionTuple<Origin, ParentInputsOfConfig<ProjectionOfClass<Extract<Value, Class>>>, Root, Seen>, EffectivePropertyProjectionsOf<Origin, Name, Value, Root>, OwnInstanceMethodProjectionsOf<Origin, Name, Value>, OwnStaticMethodProjectionsOf<Origin, Name, Value>, OwnPropertyProjectionsOf<Origin, Name, Value>, OwnInstanceMethodProjectionsOf<Origin, Name, Value>, OwnStaticMethodProjectionsOf<Origin, Name, Value>> : Value extends DirectedEdgeClass | UndirectedEdgeClass ? ClassProjection<'edge', Extract<ProjectedClassDefinition<Origin, Name, Value>, language.EdgeClassDefinition>, language.ClassRef<Name> & {
|
|
316
312
|
readonly origin: Origin;
|
|
317
313
|
}, language.DefinitionKey<Origin, 'class', Name>, ParentClassProjectionTuple<Origin, ParentInputsOfConfig<ProjectionOfClass<Extract<Value, Class>>>, Root, Seen>, EffectivePropertyProjectionsOf<Origin, Name, Value, Root>, never, never, OwnPropertyProjectionsOf<Origin, Name, Value>, never, never> : never;
|
|
318
314
|
type FunctionProjectionOfAuthoring<Origin extends string, Name extends string, Value> = Value extends Function ? FunctionProjection<CallableInputValue<Value>, CallableOutputValue<Value>, CallableAuthOf<Value>, language.FunctionRef<Name> & {
|
|
@@ -335,8 +331,9 @@ type ProjectionPropertyByName<Entries extends PropertyProjection> = Readonly<{
|
|
|
335
331
|
}>>;
|
|
336
332
|
}>;
|
|
337
333
|
type PropertyCollectionOfProjection<Entries extends PropertyProjection> = language.ResolvedPropertyCollection<ProjectionPropertyByKey<Entries>, ProjectionPropertyByName<Entries>>;
|
|
338
|
-
type ResolvedMethodOfProjection<Value> = Value extends MethodProjection<infer Input, infer Output, infer Static, infer
|
|
334
|
+
type ResolvedMethodOfProjection<Value> = Value extends MethodProjection<infer Input, infer Output, infer Static, infer Abstract, infer Auth, infer Name, infer Key, infer Executable> ? language.ResolvedMethod<Input, Output, Static, Abstract, Auth, language.PolicyCheckExpression, Key> & {
|
|
339
335
|
readonly name: Name;
|
|
336
|
+
readonly executable: Executable;
|
|
340
337
|
} : never;
|
|
341
338
|
type ProjectionMethodByKey<Entries extends MethodProjection> = Readonly<{
|
|
342
339
|
[Key in Entries['key']]: ResolvedMethodOfProjection<Extract<Entries, {
|
|
@@ -237,23 +237,19 @@ function compiledProperty(input, values, node, path) {
|
|
|
237
237
|
function compiledCallable(input, values, method, path) {
|
|
238
238
|
const value = object(input, 'Compiled Callable', path);
|
|
239
239
|
exact(value, method
|
|
240
|
-
? ['name', 'auth', 'static', '
|
|
240
|
+
? ['name', 'auth', 'static', 'abstract', 'input', 'output']
|
|
241
241
|
: ['ref', 'auth', 'input', 'output'], 'Compiled Callable', path, ['policy', 'description', 'fields']);
|
|
242
242
|
if (method) {
|
|
243
243
|
memberName(value.name, 'Method name', `${path}/name`);
|
|
244
244
|
boolean(value.static, 'Method static', `${path}/static`);
|
|
245
|
-
|
|
246
|
-
value.inheritance !== 'default' &&
|
|
247
|
-
value.inheritance !== 'sealed') {
|
|
248
|
-
invalid('Method inheritance', undefined, `${path}/inheritance`);
|
|
249
|
-
}
|
|
245
|
+
boolean(value.abstract, 'Method abstract', `${path}/abstract`);
|
|
250
246
|
}
|
|
251
247
|
if (value.auth !== 'anonymous' && value.auth !== 'authenticated' && value.auth !== 'authorized') {
|
|
252
248
|
invalid('Callable auth', undefined, `${path}/auth`);
|
|
253
249
|
}
|
|
254
250
|
description(value.description, 'Callable description', `${path}/description`);
|
|
255
251
|
if (value.policy !== undefined) {
|
|
256
|
-
if (value.auth !== 'authorized'
|
|
252
|
+
if (value.auth !== 'authorized') {
|
|
257
253
|
invalid('Callable Policy placement', undefined, `${path}/policy`);
|
|
258
254
|
}
|
|
259
255
|
policyCheck(value.policy, 0, `${path}/policy`);
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import { json } from '@astrale-os/kernel-dsl/value';
|
|
2
2
|
import { Key, PropertyKey as PropertyKeyValue } from '../../addressing/index.js';
|
|
3
3
|
import { compareUnicode } from '../../addressing/ordering.js';
|
|
4
|
+
import { composeMethods } from '../../language/callable/composition.js';
|
|
5
|
+
import { isMethodExecutable } from '../../language/callable/executable.js';
|
|
4
6
|
import { policyObjectOrigin, policyVariableClass } from '../../language/index.js';
|
|
5
7
|
import { branchesOf, branchProduct, capBranches, isConnected, targetMode, termKey, } from '../../language/policy/analysis/branches.js';
|
|
6
8
|
import { BRANCH_OVERFLOW, MAX_BRANCHES, MAX_CHECK_DEPTH, MAX_CHECK_LEAVES, MAX_DOMAINS, MAX_EDGES, MAX_PATTERN_DEPTH, MAX_REPEAT, MAX_VARIABLES, measureChecks, } from '../../language/policy/analysis/budgets.js';
|
|
7
|
-
import {
|
|
9
|
+
import { normalizePolicyExpression } from '../../language/policy/normalization/normalize.js';
|
|
8
10
|
import { validateValueProgram } from '../../language/value/evaluate/program.js';
|
|
9
11
|
import { GRAPH_NODE_PATH_SCHEMA_ID } from '../../language/value/syntax/node-path/schema.js';
|
|
10
12
|
import { invalidCompiled } from '../diagnostic.js';
|
|
@@ -104,55 +106,22 @@ function analyzeClasses(classes) {
|
|
|
104
106
|
fail(`${located.pointer}/data`, 'Data cannot redeclare an inherited contract.');
|
|
105
107
|
dataOwner = located.value.ref;
|
|
106
108
|
}
|
|
107
|
-
const
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
for (const parent of inheritedMethods) {
|
|
124
|
-
if (parent.declaration.inheritance === 'sealed') {
|
|
125
|
-
fail(`${located.pointer}/methods`, `Method ${own.name} overrides a sealed Method.`);
|
|
126
|
-
}
|
|
127
|
-
if (!sameMethodSignature(located.entry, own, parent.entry, parent.declaration)) {
|
|
128
|
-
fail(`${located.pointer}/methods`, `Method ${own.name} changes its inherited signature.`);
|
|
129
|
-
}
|
|
130
|
-
if (parent.declaration.inheritance !== 'abstract' &&
|
|
131
|
-
policyKey(parent.declaration.policy) !== policyKey(own.policy)) {
|
|
132
|
-
fail(`${located.pointer}/methods`, `Method ${own.name} changes an executable inherited Policy.`);
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
methods.set(own.name, {
|
|
136
|
-
key: `${Key.of(located.value.ref)}.method.${own.name}`,
|
|
137
|
-
owner: located.value.ref,
|
|
138
|
-
declaration: own,
|
|
139
|
-
entry: located.entry,
|
|
140
|
-
});
|
|
141
|
-
inherited.delete(own.name);
|
|
142
|
-
}
|
|
143
|
-
for (const [name, values] of inherited) {
|
|
144
|
-
if (values.length > 1)
|
|
145
|
-
fail(`${located.pointer}/methods`, `Inherited Method ${name} is ambiguous.`);
|
|
146
|
-
}
|
|
147
|
-
if (!located.value.abstract) {
|
|
148
|
-
for (const [name, method] of methods) {
|
|
149
|
-
if (method.declaration.inheritance === 'abstract') {
|
|
150
|
-
fail(`${located.pointer}/methods`, `Concrete Class leaves Method ${name} abstract.`);
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
}
|
|
155
|
-
const analysis = { properties, methods, ...(dataOwner === undefined ? {} : { dataOwner }) };
|
|
109
|
+
const own = new Map((located.value.kind === 'node' ? located.value.methods : []).map((method) => [
|
|
110
|
+
method.name,
|
|
111
|
+
{
|
|
112
|
+
key: `${classKey}.method.${method.name}`,
|
|
113
|
+
name: method.name,
|
|
114
|
+
owner: located.value.ref,
|
|
115
|
+
declaration: method,
|
|
116
|
+
entry: located.entry,
|
|
117
|
+
},
|
|
118
|
+
]));
|
|
119
|
+
const composition = composeMethods(parents, own, located.value.abstract, (left, right) => sameMethodSignature(left.entry, left.declaration, right.entry, right.declaration), (message) => fail(`${located.pointer}/methods`, message));
|
|
120
|
+
const analysis = {
|
|
121
|
+
properties,
|
|
122
|
+
...composition,
|
|
123
|
+
...(dataOwner === undefined ? {} : { dataOwner }),
|
|
124
|
+
};
|
|
156
125
|
analyses.set(classKey, analysis);
|
|
157
126
|
visiting.delete(classKey);
|
|
158
127
|
return analysis;
|
|
@@ -173,9 +142,6 @@ function outputKey(entry, output) {
|
|
|
173
142
|
function programKey(entry, value) {
|
|
174
143
|
return json.serialize(entry.values[value]);
|
|
175
144
|
}
|
|
176
|
-
function policyKey(value) {
|
|
177
|
-
return json.serialize((value === undefined ? null : normalizePolicyChecks(value)));
|
|
178
|
-
}
|
|
179
145
|
function validateClassFacets(context) {
|
|
180
146
|
for (const located of context.classes.values()) {
|
|
181
147
|
if (located.value.abstract &&
|
|
@@ -195,7 +161,7 @@ function validateClassFacets(context) {
|
|
|
195
161
|
}
|
|
196
162
|
if (located.value.kind === 'node') {
|
|
197
163
|
for (const [index, method] of located.value.methods.entries()) {
|
|
198
|
-
if (
|
|
164
|
+
if (!isMethodExecutable(located.value, method) && method.policy !== undefined) {
|
|
199
165
|
fail(`${located.pointer}/methods/${index}/policy`, 'An abstract Method cannot have a Policy.');
|
|
200
166
|
}
|
|
201
167
|
}
|
|
@@ -398,7 +364,7 @@ function validatePolicyTerm(context, term, path, scope) {
|
|
|
398
364
|
function validatePolicyMethod(context, ref, path) {
|
|
399
365
|
const owner = context.classes.get(Key.of(ref.owner))?.value;
|
|
400
366
|
const method = owner?.kind === 'node' ? owner.methods.find((method) => method.name === ref.name) : undefined;
|
|
401
|
-
if (method === undefined || method
|
|
367
|
+
if (method === undefined || !isMethodExecutable(owner, method))
|
|
402
368
|
fail(path, 'Policy Method ref must name an exact concrete Method declaration.');
|
|
403
369
|
}
|
|
404
370
|
function validatePolicyCycles(entry, local, entryIndex) {
|
|
@@ -70,7 +70,7 @@ function compileEntry(schema, lookup) {
|
|
|
70
70
|
...(method.policy === undefined ? {} : { policy: method.policy }),
|
|
71
71
|
...(method.description === undefined ? {} : { description: method.description }),
|
|
72
72
|
static: method.static,
|
|
73
|
-
|
|
73
|
+
abstract: method.abstract,
|
|
74
74
|
input,
|
|
75
75
|
...(fields === undefined ? {} : { fields }),
|
|
76
76
|
output: output(method.output, `${pointer}/methods/${name}/output`),
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { Key, MethodKey as MethodKeyValue, PropertyKey as PropertyKeyValue, } from '../../addressing/index.js';
|
|
2
|
+
import { isMethodExecutable } from '../../language/callable/executable.js';
|
|
2
3
|
import { acceptPropertyValues } from '../../language/class/property-values.js';
|
|
3
4
|
import { appendPointer } from '../../language/value/syntax/pointer.js';
|
|
4
5
|
import { createMethodCollection, createPropertyCollection } from '../namespace/member-collection.js';
|
|
@@ -25,16 +26,11 @@ export function createClasses(definitions, validation) {
|
|
|
25
26
|
}
|
|
26
27
|
const shell = {};
|
|
27
28
|
const effectiveProperties = new Map();
|
|
28
|
-
const effectiveMethods = new Map();
|
|
29
29
|
let inheritedData;
|
|
30
30
|
for (const parent of parents) {
|
|
31
31
|
for (const property of parent.properties) {
|
|
32
32
|
effectiveProperties.set(property.key, property);
|
|
33
33
|
}
|
|
34
|
-
for (const method of [...parent.methods, ...parent.static.methods]) {
|
|
35
|
-
if (!effectiveMethods.has(method.name))
|
|
36
|
-
effectiveMethods.set(method.name, method);
|
|
37
|
-
}
|
|
38
34
|
if (parent.kind === 'node' && inheritedData === undefined)
|
|
39
35
|
inheritedData = parent.data;
|
|
40
36
|
}
|
|
@@ -72,19 +68,17 @@ export function createClasses(definitions, validation) {
|
|
|
72
68
|
auth: methodDefinition.auth,
|
|
73
69
|
policy: methodDefinition.policy,
|
|
74
70
|
static: methodDefinition.static,
|
|
75
|
-
|
|
71
|
+
abstract: methodDefinition.abstract,
|
|
72
|
+
executable: isMethodExecutable(definition, methodDefinition),
|
|
76
73
|
...(inputFields === undefined ? {} : { inputFields }),
|
|
77
74
|
definition: methodDefinition,
|
|
78
75
|
validateInput: (input) => validation.callableInput(methodKey, input),
|
|
79
76
|
validateInputField: (field, input) => validation.callableInputField(methodKey, field, input),
|
|
80
77
|
validateOutput: (output) => validation.callableOutput(methodKey, output),
|
|
81
78
|
});
|
|
82
|
-
effectiveMethods.set(name, method);
|
|
83
79
|
(methodDefinition.static ? ownStaticMethods : ownInstanceMethods).push(method);
|
|
84
80
|
}
|
|
85
81
|
}
|
|
86
|
-
const instanceMethods = [...effectiveMethods.values()].filter(({ definition }) => !definition.static);
|
|
87
|
-
const staticMethods = [...effectiveMethods.values()].filter(({ definition }) => definition.static);
|
|
88
82
|
Object.assign(shell, {
|
|
89
83
|
ref: definition.ref,
|
|
90
84
|
key,
|
|
@@ -95,8 +89,8 @@ export function createClasses(definitions, validation) {
|
|
|
95
89
|
policies: definition.policies,
|
|
96
90
|
data: definition.kind === 'node' ? (ownData ?? inheritedData) : undefined,
|
|
97
91
|
properties: createPropertyCollection(effectiveProperties.values()),
|
|
98
|
-
methods: createMethodCollection(
|
|
99
|
-
static: Object.freeze({ methods: createMethodCollection(
|
|
92
|
+
methods: createMethodCollection(ownInstanceMethods),
|
|
93
|
+
static: Object.freeze({ methods: createMethodCollection(ownStaticMethods) }),
|
|
100
94
|
self: Object.freeze({
|
|
101
95
|
data: definition.kind === 'node' ? ownData : undefined,
|
|
102
96
|
properties: createPropertyCollection(ownProperties),
|
package/dist/v1/index.d.ts
CHANGED
|
@@ -4,7 +4,7 @@ export { stateMachine } from './language/state/index.js';
|
|
|
4
4
|
export type { EventOf, StateMachine, StateOf } from './language/state/index.js';
|
|
5
5
|
export { PropertyValues } from './language/index.js';
|
|
6
6
|
export * as metadata from './language/definition/metadata.js';
|
|
7
|
-
export type { AnyResolvedClass, BoundMethodOf, BoundMethodShortcutsOf, CallableResultOf, CallableInputOf, CallableOutputOf, ClassSatisfactionKeysOf, ClassDefinition, CoreDefinition, CoreEdge, FunctionAuth, FunctionDefinition, MethodDefinition,
|
|
7
|
+
export type { AnyResolvedClass, BoundMethodOf, BoundMethodShortcutsOf, CallableResultOf, CallableInputOf, CallableOutputOf, ClassSatisfactionKeysOf, ClassDefinition, CoreDefinition, CoreEdge, FunctionAuth, FunctionDefinition, MethodDefinition, MethodOwnerKeyOf, PolicyCheckExpression, PolicyDefinition, PropertyInputOf, PropertyAccessOf, PropertyOutputOf, PropertyShortcutsOf, PropertyValuesOf, ResolvedClass, ResolvedCoreDefinition, ResolvedData, ResolvedFunction, ExecutableMethod, ResolvedMethod, ResolvedMethodCollection, ResolvedPolicy, ResolvedProperty, ResolvedView, MethodAccessOf, SelfMethodsOf, SelfPropertiesOf, SelfStaticMethodsOf, StaticMethodsOf, ViewDefinition, } from './language/index.js';
|
|
8
8
|
export { acceptDefinitionRef, callableAddress, ClassKey, equalDefinitionRefs, isDefinitionRef, Key, MethodKey, PropertyKey, } from './addressing/index.js';
|
|
9
9
|
export type { ClassRef, CoreRef, DefinitionBase, DefinitionKey, DefinitionRef, FunctionRef, MethodAddress, PolicyRef, Revision, ViewRef, } from './addressing/index.js';
|
|
10
10
|
export type { Definition } from './language/index.js';
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
interface MethodMember {
|
|
2
|
+
readonly key: string;
|
|
3
|
+
readonly name: string;
|
|
4
|
+
readonly declaration: {
|
|
5
|
+
readonly abstract: boolean;
|
|
6
|
+
};
|
|
7
|
+
}
|
|
8
|
+
export interface MethodComposition<Method> {
|
|
9
|
+
readonly requirements: ReadonlyMap<string, Method>;
|
|
10
|
+
readonly frontier: ReadonlyMap<string, Method>;
|
|
11
|
+
readonly methods: ReadonlyMap<string, Method>;
|
|
12
|
+
}
|
|
13
|
+
/** Concrete implementations never discharge a descendant's abstract obligations. */
|
|
14
|
+
export declare function composeMethods<Method extends MethodMember>(parents: readonly MethodComposition<Method>[], own: ReadonlyMap<string, Method>, abstract: boolean, sameSignature: (left: Method, right: Method) => boolean, report: (message: string) => void): MethodComposition<Method>;
|
|
15
|
+
export {};
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/** Concrete implementations never discharge a descendant's abstract obligations. */
|
|
2
|
+
export function composeMethods(parents, own, abstract, sameSignature, report) {
|
|
3
|
+
const requirements = new Map(parents.flatMap((parent) => [...parent.requirements]));
|
|
4
|
+
const frontier = new Map(parents.flatMap((parent) => [...parent.frontier]));
|
|
5
|
+
for (const required of requirements.values()) {
|
|
6
|
+
const local = own.get(required.name);
|
|
7
|
+
if (local !== undefined && !sameSignature(local, required)) {
|
|
8
|
+
report(`Method ${local.key} does not exactly preserve abstract signature ${required.key}.`);
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
for (const local of own.values()) {
|
|
12
|
+
if (!local.declaration.abstract)
|
|
13
|
+
continue;
|
|
14
|
+
requirements.set(local.key, local);
|
|
15
|
+
for (const [key, inherited] of frontier) {
|
|
16
|
+
if (inherited.name === local.name)
|
|
17
|
+
frontier.delete(key);
|
|
18
|
+
}
|
|
19
|
+
frontier.set(local.key, local);
|
|
20
|
+
}
|
|
21
|
+
const methods = new Map();
|
|
22
|
+
for (const inherited of frontier.values()) {
|
|
23
|
+
const prior = methods.get(inherited.name);
|
|
24
|
+
if (prior !== undefined && !own.has(inherited.name)) {
|
|
25
|
+
report(`Inherited Method ${inherited.name} is ambiguous: ${prior.key}, ${inherited.key}.`);
|
|
26
|
+
}
|
|
27
|
+
methods.set(inherited.name, inherited);
|
|
28
|
+
}
|
|
29
|
+
for (const [name, local] of own)
|
|
30
|
+
methods.set(name, local);
|
|
31
|
+
if (!abstract) {
|
|
32
|
+
const missing = new Set();
|
|
33
|
+
for (const required of requirements.values()) {
|
|
34
|
+
const local = own.get(required.name);
|
|
35
|
+
if (local === undefined && !missing.has(required.name)) {
|
|
36
|
+
missing.add(required.name);
|
|
37
|
+
report(`Concrete Class requires a local Method declaration of ${required.key}.`);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
return { requirements, frontier, methods };
|
|
42
|
+
}
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import type { DefinitionBase, FunctionRef } from '../../../addressing/index.js';
|
|
2
2
|
import type { PolicyCheckExpression } from '../../policy/model/policy.js';
|
|
3
3
|
import type { JsonSchema } from '../../value/model/schema.js';
|
|
4
|
-
export type MethodInheritance = 'abstract' | 'default' | 'sealed';
|
|
5
4
|
export type FunctionAuth = 'anonymous' | 'authenticated' | 'authorized';
|
|
6
5
|
export type CallableOutput = {
|
|
7
6
|
readonly mode: 'value';
|
|
@@ -28,6 +27,6 @@ export type CallableDefinition = CallableDefinitionBase & ({
|
|
|
28
27
|
export type FunctionDefinition = DefinitionBase<FunctionRef> & CallableDefinition;
|
|
29
28
|
export type MethodDefinition = CallableDefinition & {
|
|
30
29
|
readonly static: boolean;
|
|
31
|
-
readonly
|
|
30
|
+
readonly abstract: boolean;
|
|
32
31
|
};
|
|
33
32
|
export type MethodRecord = Readonly<Record<string, MethodDefinition>>;
|
|
@@ -11,7 +11,7 @@ type CallableDefinitionOf<Definition extends FunctionDefinition | MethodDefiniti
|
|
|
11
11
|
} : {
|
|
12
12
|
readonly policy?: never;
|
|
13
13
|
}) : never);
|
|
14
|
-
export interface ResolvedMethod<out Input = unknown, out Output = unknown, out Static extends boolean = boolean, out
|
|
14
|
+
export interface ResolvedMethod<out Input = unknown, out Output = unknown, out Static extends boolean = boolean, out Abstract extends MethodDefinition['abstract'] = MethodDefinition['abstract'], out Auth extends FunctionAuth = FunctionAuth, out Policy extends PolicyCheckExpression = PolicyCheckExpression, out ExactKey extends MethodKey = MethodKey> {
|
|
15
15
|
readonly owner: ResolvedClass<'node'> & {
|
|
16
16
|
readonly key: MethodOwnerKeyOfKey<ExactKey>;
|
|
17
17
|
};
|
|
@@ -23,12 +23,14 @@ export interface ResolvedMethod<out Input = unknown, out Output = unknown, out S
|
|
|
23
23
|
readonly auth: Auth;
|
|
24
24
|
readonly policy?: Auth extends 'authorized' ? Policy : never;
|
|
25
25
|
readonly static: Static;
|
|
26
|
-
readonly
|
|
26
|
+
readonly abstract: Abstract;
|
|
27
|
+
/** Whether this exact local declaration requires an implementation. */
|
|
28
|
+
readonly executable: Abstract extends false ? true : boolean;
|
|
27
29
|
/** Present only when the callable input is a safely enumerable closed object. */
|
|
28
30
|
readonly inputFields?: readonly string[];
|
|
29
31
|
readonly definition: CallableDefinitionOf<MethodDefinition, Auth, Policy> & {
|
|
30
32
|
readonly static: Static;
|
|
31
|
-
readonly
|
|
33
|
+
readonly abstract: Abstract;
|
|
32
34
|
};
|
|
33
35
|
validateInput(input: unknown): readonly Diagnostic[];
|
|
34
36
|
validateInputField(field: string, input: unknown): readonly Diagnostic[];
|
|
@@ -65,4 +67,8 @@ export type MethodOwnerKeyOf<Method> = Method extends {
|
|
|
65
67
|
type MethodOwnerKeyOfKey<ExactKey extends MethodKey> = ExactKey extends `${infer Origin}:class.${infer Owner}.method.${string}` ? DefinitionKey<Extract<Origin, Key.Origin>, 'class', Owner> : never;
|
|
66
68
|
export type CallableInputOf<Callable> = Callable extends ResolvedMethod<infer Input, unknown, boolean> ? Input : Callable extends ResolvedFunction<infer Input, unknown> ? Input : never;
|
|
67
69
|
export type CallableOutputOf<Callable> = Callable extends ResolvedMethod<unknown, infer Output, boolean> ? Output : Callable extends ResolvedFunction<unknown, infer Output> ? Output : never;
|
|
70
|
+
/** An exact local Method whose Schema requires an implementation. */
|
|
71
|
+
export type ExecutableMethod<Input = unknown, Output = unknown, Static extends boolean = boolean> = ResolvedMethod<Input, Output, Static> & {
|
|
72
|
+
readonly executable: true;
|
|
73
|
+
};
|
|
68
74
|
export {};
|
|
@@ -40,7 +40,9 @@ export type CallableResultOf<Callable, Binary = unknown> = CallableOutputOf<Call
|
|
|
40
40
|
} ? AsyncIterable<Item> : Output extends {
|
|
41
41
|
readonly mode: 'binary';
|
|
42
42
|
} ? Binary : never : never;
|
|
43
|
-
type ExecutableMethod<Method> = Method extends ResolvedMethod<unknown, unknown, false
|
|
43
|
+
type ExecutableMethod<Method> = Method extends ResolvedMethod<unknown, unknown, false> & {
|
|
44
|
+
readonly executable: true;
|
|
45
|
+
} ? Method : never;
|
|
44
46
|
export type BoundMethodOf<Method, Binary = unknown> = ExecutableMethod<Method> extends infer Callable ? [Callable] extends [never] ? never : (input: CallableInputOf<Callable>) => Promise<CallableResultOf<Callable, Binary>> : never;
|
|
45
47
|
/** Collision-safe direct bound Methods. Client supplies graph/wrapper reserved names. */
|
|
46
48
|
export type BoundMethodShortcutsOf<Class, Binary = unknown, Reserved extends MemberName = never> = Readonly<{
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
export { isMethodExecutable } from './callable/executable.js';
|
|
1
2
|
export type { ClassRef, CoreRef, DefinitionBase, DefinitionKey, DefinitionRef, FunctionRef, MethodKey, PolicyRef, PropertyKey, ViewRef, } from '../addressing/index.js';
|
|
2
3
|
export type { Value as PropertyValue } from '@astrale-os/kernel-dsl/value';
|
|
3
4
|
export type { JsonSchema as ValueSchema, JsonSchemaObject as ValueSchemaObject, } from './value/model/schema.js';
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
export { isMethodExecutable } from './callable/executable.js';
|
|
1
2
|
export { stateMachine } from './state/index.js';
|
|
2
3
|
export { PropertyValues } from './class/property-values.js';
|
|
3
4
|
export { policyVariableClass, policyVariableSelector, policyObjectKey, policyObjectOrigin, } from './policy/model/policy.js';
|
|
@@ -8,6 +8,7 @@ import type { JsonSchema } from '../../language/value/model/schema.js';
|
|
|
8
8
|
import type { SchemaDiagnostic } from '../diagnostic.js';
|
|
9
9
|
import type { DomainResolutionContext } from '../resolution/context.js';
|
|
10
10
|
import { PropertyKey } from '../../addressing/index.js';
|
|
11
|
+
import { type MethodComposition } from '../../language/callable/composition.js';
|
|
11
12
|
export interface EffectiveField {
|
|
12
13
|
readonly identity: PropertyKey;
|
|
13
14
|
readonly owner: ClassRef;
|
|
@@ -18,7 +19,7 @@ export interface EffectiveField {
|
|
|
18
19
|
readonly state?: StateTopology;
|
|
19
20
|
}
|
|
20
21
|
export interface EffectiveMethod {
|
|
21
|
-
readonly
|
|
22
|
+
readonly key: string;
|
|
22
23
|
readonly owner: ClassRef;
|
|
23
24
|
readonly name: string;
|
|
24
25
|
readonly declaration: MethodDefinition;
|
|
@@ -28,12 +29,11 @@ export interface EffectiveData {
|
|
|
28
29
|
readonly owner: ClassRef;
|
|
29
30
|
readonly declaration: DataDefinition;
|
|
30
31
|
}
|
|
31
|
-
export interface DefinitionAnalysis {
|
|
32
|
+
export interface DefinitionAnalysis extends MethodComposition<EffectiveMethod> {
|
|
32
33
|
readonly ref: ClassRef;
|
|
33
34
|
readonly declaration: ClassDefinition;
|
|
34
35
|
readonly properties: ReadonlyMap<PropertyKey, EffectiveField>;
|
|
35
36
|
readonly data?: EffectiveData;
|
|
36
|
-
readonly methods: ReadonlyMap<string, EffectiveMethod>;
|
|
37
37
|
}
|
|
38
38
|
export interface DefinitionAnalysisResult {
|
|
39
39
|
readonly definitions: ReadonlyMap<string, DefinitionAnalysis>;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { json } from '@astrale-os/kernel-dsl/value';
|
|
2
2
|
import { Key, PropertyKey } from '../../addressing/index.js';
|
|
3
3
|
import { definitionRefIndex } from '../../addressing/ordering.js';
|
|
4
|
-
import {
|
|
4
|
+
import { composeMethods } from '../../language/callable/composition.js';
|
|
5
5
|
import { canonicalizeValueSchema } from '../../language/value/profile/canonicalize.js';
|
|
6
6
|
import { diagnostic } from '../diagnostic.js';
|
|
7
7
|
const formatDefinitionRef = Key.of;
|
|
@@ -41,7 +41,7 @@ export function analyzeDefinitions(environment) {
|
|
|
41
41
|
declaration,
|
|
42
42
|
properties,
|
|
43
43
|
...(data === undefined ? {} : { data }),
|
|
44
|
-
methods,
|
|
44
|
+
...methods,
|
|
45
45
|
};
|
|
46
46
|
result.set(key, analysis);
|
|
47
47
|
visiting.delete(key);
|
|
@@ -107,57 +107,11 @@ function mergeData(owner, declaration, parents, diagnostics) {
|
|
|
107
107
|
};
|
|
108
108
|
}
|
|
109
109
|
function mergeMethods(owner, declaration, parents, diagnostics) {
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
const entries = inherited.get(name) ?? [];
|
|
116
|
-
if (!entries.some((entry) => entry.identity === method.identity))
|
|
117
|
-
entries.push(method);
|
|
118
|
-
inherited.set(name, entries);
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
const result = new Map();
|
|
122
|
-
for (const [name, entries] of inherited) {
|
|
123
|
-
if (entries.length === 1)
|
|
124
|
-
result.set(name, entries[0]);
|
|
125
|
-
}
|
|
126
|
-
for (const [name, ownMethod] of Object.entries(declaration.methods)) {
|
|
127
|
-
const parentsForName = inherited.get(name) ?? [];
|
|
128
|
-
for (const parent of parentsForName) {
|
|
129
|
-
if (parent.declaration.inheritance === 'sealed') {
|
|
130
|
-
diagnostics.push(diagnostic('DV_METHOD_COMPOSITION', '', `Method ${name} cannot override sealed ${formatDefinitionRef(parent.owner)}.${name}.`));
|
|
131
|
-
}
|
|
132
|
-
if (!sameMethodSignature(ownMethod, parent.declaration)) {
|
|
133
|
-
diagnostics.push(diagnostic('DV_METHOD_COMPOSITION', '', `Method ${name} does not exactly preserve its inherited signature.`));
|
|
134
|
-
}
|
|
135
|
-
if (parent.declaration.inheritance !== 'abstract' &&
|
|
136
|
-
canonicalPolicy(parent.declaration.policy) !== canonicalPolicy(ownMethod.policy)) {
|
|
137
|
-
diagnostics.push(diagnostic('DV_METHOD_COMPOSITION', '', `Method ${name} changes an executable inherited Policy.`));
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
result.set(name, {
|
|
141
|
-
identity: `${definitionRefIndex(owner)}\u0000method\u0000${name}`,
|
|
142
|
-
owner,
|
|
143
|
-
name,
|
|
144
|
-
declaration: ownMethod,
|
|
145
|
-
});
|
|
146
|
-
inherited.delete(name);
|
|
147
|
-
}
|
|
148
|
-
for (const [name, entries] of inherited) {
|
|
149
|
-
if (entries.length > 1) {
|
|
150
|
-
diagnostics.push(diagnostic('DV_METHOD_COMPOSITION', '', `Inherited method ${name} is ambiguous and has no local override.`));
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
if (declaration.kind === 'node' && !declaration.abstract) {
|
|
154
|
-
for (const [name, method] of result) {
|
|
155
|
-
if (method.declaration.inheritance === 'abstract') {
|
|
156
|
-
diagnostics.push(diagnostic('DV_METHOD_COMPOSITION', '', `Concrete Class ${owner.name} leaves method ${name} abstract.`));
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
return result;
|
|
110
|
+
const own = new Map(Object.entries('methods' in declaration ? declaration.methods : {}).map(([name, method]) => [
|
|
111
|
+
name,
|
|
112
|
+
{ key: `${Key.of(owner)}.method.${name}`, owner, name, declaration: method },
|
|
113
|
+
]));
|
|
114
|
+
return composeMethods(parents, own, declaration.abstract, (left, right) => sameMethodSignature(left.declaration, right.declaration), (message) => diagnostics.push(diagnostic('DV_METHOD_COMPOSITION', '', message, Key.of(owner))));
|
|
161
115
|
}
|
|
162
116
|
function sameMethodSignature(left, right) {
|
|
163
117
|
return (json.serialize(canonicalizeValueSchema(left.input, 'callable-input')) ===
|
|
@@ -176,6 +130,3 @@ function canonicalMethodOutput(value) {
|
|
|
176
130
|
return 'binary';
|
|
177
131
|
}
|
|
178
132
|
}
|
|
179
|
-
function canonicalPolicy(value) {
|
|
180
|
-
return json.serialize(value === undefined ? null : normalizePolicyChecks(value));
|
|
181
|
-
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { Key } from '../../addressing/key.js';
|
|
2
2
|
import { definitionRefIndex } from '../../addressing/ordering.js';
|
|
3
|
+
import { isMethodExecutable } from '../../language/callable/executable.js';
|
|
3
4
|
import { admitStateTopology } from '../../language/state/topology.js';
|
|
4
5
|
import { classifyOrdering } from '../../language/value/ordering.js';
|
|
5
6
|
import { diagnostic } from '../diagnostic.js';
|
|
@@ -71,7 +72,7 @@ function validateFacet(environment, declaration, pointer, diagnostics) {
|
|
|
71
72
|
return;
|
|
72
73
|
for (const [name, method] of Object.entries(declaration.methods)) {
|
|
73
74
|
const methodPointer = `${pointer}/methods/${escapePointer(name)}`;
|
|
74
|
-
if (method
|
|
75
|
+
if (!isMethodExecutable(declaration, method) && method.policy !== undefined) {
|
|
75
76
|
diagnostics.push(diagnostic('DV_METHOD_ABSTRACT', `${methodPointer}/policy`, 'Abstract Methods cannot have a Policy.'));
|
|
76
77
|
}
|
|
77
78
|
}
|
|
@@ -188,16 +188,8 @@ const callableShape = {
|
|
|
188
188
|
const methodDeclarationSchema = strictObject({
|
|
189
189
|
...callableShape,
|
|
190
190
|
static: boolean(),
|
|
191
|
-
|
|
191
|
+
abstract: boolean(),
|
|
192
192
|
}).superRefine((method, context) => {
|
|
193
|
-
if (method.inheritance === 'abstract' && method.policy !== undefined) {
|
|
194
|
-
context.addIssue({
|
|
195
|
-
code: 'custom',
|
|
196
|
-
path: ['policy'],
|
|
197
|
-
message: 'An abstract method cannot declare a policy.',
|
|
198
|
-
params: { structureCode: 'STRUCTURE_ABSTRACT_POLICY' },
|
|
199
|
-
});
|
|
200
|
-
}
|
|
201
193
|
if (method.auth !== 'authorized' && method.policy !== undefined) {
|
|
202
194
|
context.addIssue({
|
|
203
195
|
code: 'custom',
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { definitionRefIndex } from '../../../addressing/ordering.js';
|
|
2
|
+
import { isMethodExecutable } from '../../../language/callable/executable.js';
|
|
2
3
|
import { policyObjectKey, policyObjectOrigin } from '../../../language/index.js';
|
|
3
4
|
const KERNEL_ORIGIN = 'kernel.astrale.ai';
|
|
4
5
|
const PROJECTION_CLASS = nodeClassRef('Class');
|
|
@@ -26,7 +27,7 @@ export function referenceContracts(validation, ref, pointer) {
|
|
|
26
27
|
case 'method': {
|
|
27
28
|
const declaringClass = owner.classes[ref.owner.name];
|
|
28
29
|
const method = declaringClass?.kind === 'node' ? declaringClass.methods[ref.name] : undefined;
|
|
29
|
-
return method === undefined || method
|
|
30
|
+
return method === undefined || !isMethodExecutable(declaringClass, method)
|
|
30
31
|
? invalidPolicyObjectRef(validation, ref, pointer)
|
|
31
32
|
: [{ kind: 'exact', ref: PROJECTION_FUNCTION }];
|
|
32
33
|
}
|