@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.
@@ -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, MethodInheritance } from '../../language/callable/model/callable.js';
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
- export type CallableInput = CallableInputBase & ({
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 = CallableInput & {
57
+ export type FunctionInput = ConcreteCallableInput & {
60
58
  readonly static?: never;
61
- readonly inheritance?: never;
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 MethodInput = CallableInput;
64
- export interface CallableProjection<out Input = unknown, out Output = unknown, out Auth extends FunctionAuth = FunctionAuth, out Static extends boolean = boolean, out Inheritance extends MethodInheritance = MethodInheritance> {
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 inheritance: Inheritance;
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, 'default'>>;
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, Inheritance extends MethodInheritance = Config extends {
100
- readonly inheritance: infer Value extends MethodInheritance;
101
- } ? Value : 'default'>(config: Config): Method<CallableProjection<Input, Output, Auth, Static, Inheritance>>;
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
- 'inheritance',
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
- 'inheritance',
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 EffectiveMethodsOfReference<Value, Static extends boolean> = ResolveCompactLazy<Value> extends infer Resolved ? Resolved extends NodeClass<infer Config> ? Config extends {
143
- readonly effectiveInstanceMethods: infer InstanceMethods;
144
- readonly effectiveStaticMethods: infer StaticMethods;
145
- } ? BindMethodOwners<Static extends true ? StaticMethods : InstanceMethods, ClassOwnerProjectionOf<Resolved>> : never : Resolved extends ResolvedClass<'node'> ? Static extends true ? Resolved['static']['methods']['byKey'][keyof Resolved['static']['methods']['byKey']] : Resolved['methods']['byKey'][keyof Resolved['methods']['byKey']] : never : never;
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
- } ? EffectiveMethodsOfReference<Parent, Static> : never;
149
- type EffectiveMethodProjectionsOf<Input, Static extends boolean> = OwnMethodProjectionsOf<Input, Static> | (ParentMethodProjectionsOf<Input, Static> extends infer Entry ? Entry extends {
159
+ } ? AbstractMethodsOfReference<Parent, Static> : never;
160
+ type MethodNames<Entry> = Entry extends {
150
161
  readonly name: infer Name;
151
- } ? Name extends OwnMethodProjectionsOf<Input, Static>['name'] ? never : Entry : never : never);
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 Inheritance, infer Auth, infer _Policy, infer Key> & {
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 inheritance: Inheritance;
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.inheritance !== undefined)) {
24
- fail('BUILDER_CALLABLE_PLACEMENT', placement.pointer, 'A standalone Function cannot declare Method staticness or inheritance.');
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 inheritance = method ? (config.inheritance ?? 'default') : undefined;
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
- inheritance: inheritance,
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.inheritance !== 'abstract';
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, AuthoredMethodProjection, Class, ClassIdentityProjection, ClassIdentityProjectionOf, ClassOwnerProjection, ClassOwnerProjectionOf, ClassReferenceProjection, ClassReferenceProjectionOf, DirectedEdgeClass, NodeClass, ProjectionOfClass, UndirectedEdgeClass } from '../class/authoring.js';
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
- export interface MethodProjection<Input = unknown, Output = unknown, Static extends boolean = boolean, Inheritance extends language.MethodInheritance = language.MethodInheritance, Auth extends language.FunctionAuth = language.FunctionAuth, Name extends string = string, Key extends language.MethodKey = language.MethodKey> {
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 inheritance: Inheritance;
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 CallableInheritanceOf<Value extends Method> = CallableConfig<Value> extends {
255
- readonly inheritance: infer Inheritance extends language.MethodInheritance;
256
- } ? Inheritance : 'default';
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<unknown, infer Name, infer Value> ? MethodProjection<CallableInputValue<Value>, CallableOutputValue<Value>, CallableStaticOf<Value>, CallableInheritanceOf<Value>, CallableAuthOf<Value>, Name, language.MethodKey<Origin, OwnerName, Name>> : never;
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 Inheritance, infer Auth, infer _Policy, infer Key> & {
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, Inheritance, Auth, Name, Key> : never;
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>, EffectiveMethodProjectionsOf<Origin, Name, Value, Root, false>, EffectiveMethodProjectionsOf<Origin, Name, Value, Root, true>, 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> & {
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>, EffectiveMethodProjectionsOf<Origin, Name, Value, Root, false>, EffectiveMethodProjectionsOf<Origin, Name, Value, Root, true>, 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
+ }, 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 Inheritance, infer Auth, infer Name, infer Key> ? language.ResolvedMethod<Input, Output, Static, Inheritance, Auth, language.PolicyCheckExpression, Key> & {
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', 'inheritance', 'input', 'output']
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
- if (value.inheritance !== 'abstract' &&
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' || (method && value.inheritance === 'abstract')) {
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 { normalizePolicyChecks, normalizePolicyExpression, } from '../../language/policy/normalization/normalize.js';
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 inherited = new Map();
108
- for (const parent of parents) {
109
- for (const [name, method] of parent.methods) {
110
- const values = inherited.get(name) ?? [];
111
- if (!values.some(({ key }) => key === method.key))
112
- values.push(method);
113
- inherited.set(name, values);
114
- }
115
- }
116
- const methods = new Map();
117
- for (const [name, values] of inherited)
118
- if (values.length === 1)
119
- methods.set(name, values[0]);
120
- if (located.value.kind === 'node') {
121
- for (const own of located.value.methods) {
122
- const inheritedMethods = inherited.get(own.name) ?? [];
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 (method.inheritance === 'abstract' && method.policy !== undefined) {
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.inheritance === 'abstract')
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
- inheritance: method.inheritance,
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
- inheritance: methodDefinition.inheritance,
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(instanceMethods),
99
- static: Object.freeze({ methods: createMethodCollection(staticMethods) }),
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),
@@ -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, MethodInheritance, MethodOwnerKeyOf, PolicyCheckExpression, PolicyDefinition, PropertyInputOf, PropertyAccessOf, PropertyOutputOf, PropertyShortcutsOf, PropertyValuesOf, ResolvedClass, ResolvedCoreDefinition, ResolvedData, ResolvedFunction, ResolvedMethod, ResolvedMethodCollection, ResolvedPolicy, ResolvedProperty, ResolvedView, MethodAccessOf, SelfMethodsOf, SelfPropertiesOf, SelfStaticMethodsOf, StaticMethodsOf, ViewDefinition, } from './language/index.js';
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
+ }
@@ -0,0 +1,6 @@
1
+ /** A local Method is executable unless both its contract and its owner are abstract. */
2
+ export declare function isMethodExecutable(owner: {
3
+ readonly abstract: boolean;
4
+ }, method: {
5
+ readonly abstract: boolean;
6
+ }): boolean;
@@ -0,0 +1,4 @@
1
+ /** A local Method is executable unless both its contract and its owner are abstract. */
2
+ export function isMethodExecutable(owner, method) {
3
+ return !method.abstract || !owner.abstract;
4
+ }
@@ -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 inheritance: MethodInheritance;
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 Inheritance extends MethodDefinition['inheritance'] = MethodDefinition['inheritance'], out Auth extends FunctionAuth = FunctionAuth, out Policy extends PolicyCheckExpression = PolicyCheckExpression, out ExactKey extends MethodKey = MethodKey> {
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 inheritance: Inheritance;
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 inheritance: Inheritance;
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, infer Inheritance> ? Inheritance extends 'abstract' ? never : Method : never;
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 identity: string;
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 { normalizePolicyChecks } from '../../language/policy/normalization/normalize.js';
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
- if (!('methods' in declaration))
111
- return new Map();
112
- const inherited = new Map();
113
- for (const parent of parents) {
114
- for (const [name, method] of parent.methods) {
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
- }
@@ -107,7 +107,7 @@ function canonicalizeMethod(value) {
107
107
  return {
108
108
  ...canonicalizeCallable(value),
109
109
  static: value.static,
110
- inheritance: value.inheritance,
110
+ abstract: value.abstract,
111
111
  };
112
112
  }
113
113
  function canonicalizeCallableOutput(value) {
@@ -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.inheritance === 'abstract' && method.policy !== undefined) {
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
- inheritance: enumeration(['abstract', 'default', 'sealed']),
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.inheritance === 'abstract'
30
+ return method === undefined || !isMethodExecutable(declaringClass, method)
30
31
  ? invalidPolicyObjectRef(validation, ref, pointer)
31
32
  : [{ kind: 'exact', ref: PROJECTION_FUNCTION }];
32
33
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@astrale-os/kernel-dsl",
3
- "version": "0.2.0-beta.31",
3
+ "version": "0.2.0-beta.33",
4
4
  "description": "Astrale Kernel DSL - Schema authoring, inference, and IR serialization",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {