@astrale-os/kernel-dsl 0.2.0-beta.30 → 0.2.0-beta.32

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.
Files changed (37) hide show
  1. package/dist/v1/builder/callable/authoring.d.ts +21 -14
  2. package/dist/v1/builder/callable/authoring.js +2 -2
  3. package/dist/v1/builder/class/authoring.d.ts +27 -9
  4. package/dist/v1/builder/lowering/callable.js +5 -5
  5. package/dist/v1/builder/lowering/schema/capture.js +7 -2
  6. package/dist/v1/builder/lowering/schema/context.d.ts +2 -2
  7. package/dist/v1/builder/lowering/schema/context.js +17 -7
  8. package/dist/v1/builder/lowering/schema/ownership.d.ts +1 -1
  9. package/dist/v1/builder/lowering/schema/ownership.js +2 -1
  10. package/dist/v1/builder/schema/projection.d.ts +10 -10
  11. package/dist/v1/compiled/admission/accept.js +16 -9
  12. package/dist/v1/compiled/admission/semantics.js +38 -63
  13. package/dist/v1/compiled/compiler/compile.js +1 -1
  14. package/dist/v1/domain/projection/classes.js +9 -6
  15. package/dist/v1/index.d.ts +1 -1
  16. package/dist/v1/language/callable/composition.d.ts +15 -0
  17. package/dist/v1/language/callable/composition.js +42 -0
  18. package/dist/v1/language/callable/model/callable.d.ts +1 -2
  19. package/dist/v1/language/callable/resolved.d.ts +5 -5
  20. package/dist/v1/language/index.d.ts +1 -1
  21. package/dist/v1/language/index.js +1 -1
  22. package/dist/v1/language/policy/analysis/branches.js +2 -2
  23. package/dist/v1/language/policy/index.d.ts +1 -1
  24. package/dist/v1/language/policy/index.js +1 -1
  25. package/dist/v1/language/policy/model/policy.d.ts +5 -2
  26. package/dist/v1/language/policy/model/policy.js +9 -0
  27. package/dist/v1/language/policy/normalization/normalize.js +2 -2
  28. package/dist/v1/schema/analysis/class.d.ts +3 -3
  29. package/dist/v1/schema/analysis/class.js +7 -56
  30. package/dist/v1/schema/normalization/domain.js +1 -1
  31. package/dist/v1/schema/resolution/origins.js +6 -4
  32. package/dist/v1/schema/validation/class.js +1 -1
  33. package/dist/v1/schema/validation/domain.structure.js +3 -2
  34. package/dist/v1/schema/validation/policy/budgets.js +3 -3
  35. package/dist/v1/schema/validation/policy/objects.d.ts +1 -1
  36. package/dist/v1/schema/validation/policy/objects.js +12 -6
  37. 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, 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,27 @@ 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;
62
60
  };
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> {
61
+ export type MethodInput = (ConcreteCallableInput & {
62
+ readonly static?: boolean;
63
+ readonly abstract?: false;
64
+ }) | (CallableInputBase & {
65
+ readonly static?: boolean;
66
+ readonly abstract: true;
67
+ readonly auth: FunctionAuth;
68
+ readonly policy?: never;
69
+ });
70
+ export type CallableInput = FunctionInput | MethodInput;
71
+ 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
72
  readonly [CALLABLE_PROJECTION_VALUE]: true;
66
73
  readonly input: Input;
67
74
  readonly output: Output;
68
75
  readonly auth: Auth;
69
76
  readonly static: Static;
70
- readonly inheritance: Inheritance;
77
+ readonly abstract: Abstract;
71
78
  }
72
79
  type ValueInputOf<Schema> = Schema extends TypedValueSchema<infer Value> ? Value : Schema extends z.ZodType ? z.input<Schema> : PortableValue;
73
80
  type ValueOutputOf<Schema> = Schema extends TypedValueSchema<infer Value> ? Value : Schema extends z.ZodType ? z.output<Schema> : PortableValue;
@@ -91,14 +98,14 @@ export interface Method<out Projection = unknown> {
91
98
  export type ProjectionOfCallable<Value extends Function | Method> = Value extends Function<infer Projection> ? Projection : Value extends Method<infer Projection> ? Projection : never;
92
99
  /** Internal lowering access; Callable authoring handles expose no public configuration object. */
93
100
  export declare function callableConfiguration(value: Function | Method): CallableInput;
94
- export type PolicyObjectAuthoring = Class | Function | Policy | View | CoreNode;
101
+ export type PolicyObjectAuthoring = Class | Function | Method | Policy | View | CoreNode;
95
102
  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'>>;
103
+ 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
104
  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
105
  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>>;
106
+ } ? true : false, Abstract extends boolean = Config extends {
107
+ readonly abstract: infer Value extends boolean;
108
+ } ? Value : false>(config: Config): Method<CallableProjection<Input, Output, Auth, Static, Abstract>>;
102
109
  export declare function callableInputField(name: string): Extract<PolicyCheckObject, {
103
110
  readonly kind: 'input';
104
111
  }>;
@@ -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,12 +20,12 @@ 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) {
27
+ const isAbstract = method ? (config.abstract ?? false) : undefined;
28
+ if (isAbstract === true && config.policy !== undefined) {
29
29
  fail('BUILDER_ABSTRACT_POLICY', `${placement.pointer}/policy`, 'An abstract Method cannot declare a Policy check.');
30
30
  }
31
31
  const input = this.values.schema(config.input, 'callable-input', `${placement.pointer}/input`);
@@ -46,7 +46,7 @@ export class V1CallableCompiler {
46
46
  return {
47
47
  ...contract,
48
48
  static: isStatic,
49
- inheritance: inheritance,
49
+ abstract: isAbstract,
50
50
  };
51
51
  }
52
52
  compileOutput(value, pointer) {
@@ -1,4 +1,3 @@
1
- import { MethodKey } from '../../../addressing/index.js';
2
1
  import { classConfiguration } from '../../class/authoring.js';
3
2
  import { escapeToken, fail } from '../../schema/diagnostic.js';
4
3
  import { isAuthoringValue } from '../../schema/handle.js';
@@ -42,7 +41,13 @@ function captureExecutables(input, context) {
42
41
  for (const [methodName, method] of Object.entries(methods ?? {})) {
43
42
  const pointer = `/classes/${escapeToken(definitionName)}/methods/${escapeToken(methodName)}`;
44
43
  assertMethod(method, pointer);
45
- context.ownership.capture(method, MethodKey.of({ origin: context.origin, kind: 'class', name: definitionName }, methodName), pointer);
44
+ const ref = {
45
+ kind: 'method',
46
+ owner: { origin: context.origin, kind: 'class', name: definitionName },
47
+ name: methodName,
48
+ };
49
+ context.ownership.captureDefinition(method, ref, pointer);
50
+ context.capturePolicyObject(method, ref);
46
51
  }
47
52
  }
48
53
  for (const [name, value] of Object.entries(input.views ?? {})) {
@@ -2,7 +2,7 @@ import type { CoreRef, ClassRef, DefinitionRef, FunctionRef, PolicyRef, ViewRef
2
2
  import type { DomainDependency } from '../../../addressing/index.js';
3
3
  import type { ClassKind } from '../../../language/class/model/class.js';
4
4
  import type { CoreEndpointRef } from '../../../language/core/model/core.js';
5
- import type { PolicyObjectRef } from '../../../language/policy/model/policy.js';
5
+ import type { PolicyObjectRef } from '../../../language/index.js';
6
6
  import type { ValueSchemaRegistry } from '../../../language/value/reference/registry.js';
7
7
  import type { DomainSchema, DomainSchemaIR } from '../../../schema/model/schema.js';
8
8
  import type { Function, PolicyObjectInput } from '../../callable/authoring.js';
@@ -40,5 +40,5 @@ export declare class BuilderContext {
40
40
  noteDomainEndpointReferences(schema: DomainSchemaIR): void;
41
41
  private assertVisible;
42
42
  private classKind;
43
- private capturePolicyObject;
43
+ capturePolicyObject(value: object, ref: PolicyObjectRef): void;
44
44
  }
@@ -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 { policyObjectKey, policyObjectOrigin } from '../../../language/index.js';
3
4
  import { graphNodePathSchema } from '../../../language/value/syntax/node-path/schema.js';
4
5
  import { revisionOfDomainSchema } from '../../../schema/canonical/revision.js';
5
6
  import { requireDomainClosure } from '../../../schema/resolution/closure.js';
@@ -114,16 +115,18 @@ export class BuilderContext {
114
115
  : typeof value === 'object' && value !== null
115
116
  ? (this.policyObjects.get(value) ?? this.ownership.definition(value))
116
117
  : undefined;
117
- const dependency = ref === undefined ? undefined : this.#dependencies.get(ref.origin)?.schema;
118
+ const origin = ref === undefined ? undefined : policyObjectOrigin(ref);
119
+ const dependency = origin === undefined ? undefined : this.#dependencies.get(origin)?.schema;
118
120
  const admitted = ref !== undefined &&
119
- (ref.origin === this.origin
120
- ? this.policyObjectRefs.has(definitionRefIndex(ref))
121
+ (origin === this.origin
122
+ ? this.policyObjectRefs.has(policyObjectKey(ref))
121
123
  : dependency !== undefined && hasPolicyObject(dependency, ref));
122
124
  if (!admitted || ref === undefined) {
123
- fail('BUILDER_POLICY_REF', pointer, 'Policy ref(...) must name one registered local or direct-dependency Definition, Function, Policy, View, or Core Node.');
125
+ fail('BUILDER_POLICY_REF', pointer, 'Policy ref(...) must name one registered local or direct-dependency Definition, Function, Method, Policy, View, or Core Node.');
124
126
  }
125
- if (ref.origin !== this.origin)
126
- this.usedForeignOrigins.add(ref.origin);
127
+ const admittedOrigin = policyObjectOrigin(ref);
128
+ if (admittedOrigin !== this.origin)
129
+ this.usedForeignOrigins.add(admittedOrigin);
127
130
  return { ...ref };
128
131
  }
129
132
  resolveCoreEndpoint(value, pointer) {
@@ -196,7 +199,7 @@ export class BuilderContext {
196
199
  }
197
200
  capturePolicyObject(value, ref) {
198
201
  this.policyObjects.set(value, ref);
199
- this.policyObjectRefs.add(definitionRefIndex(ref));
202
+ this.policyObjectRefs.add(policyObjectKey(ref));
200
203
  }
201
204
  }
202
205
  function escapePointer(value) {
@@ -234,6 +237,8 @@ function isPolicyObjectRef(value) {
234
237
  if (typeof value !== 'object' || value === null)
235
238
  return false;
236
239
  const candidate = value;
240
+ if (candidate.kind === 'method')
241
+ return isDefinitionRef(candidate.owner) && typeof candidate.name === 'string';
237
242
  return (typeof candidate.origin === 'string' &&
238
243
  typeof candidate.name === 'string' &&
239
244
  (candidate.kind === 'class' ||
@@ -252,6 +257,11 @@ function definitionRefsOf(schema) {
252
257
  }
253
258
  function hasPolicyObject(schema, ref) {
254
259
  switch (ref.kind) {
260
+ case 'method': {
261
+ const owner = schema.classes[ref.owner.name];
262
+ const method = owner?.kind === 'node' ? owner.methods[ref.name] : undefined;
263
+ return method !== undefined && !method.abstract;
264
+ }
255
265
  case 'class':
256
266
  return Object.hasOwn(schema.classes, ref.name);
257
267
  case 'function':
@@ -1,4 +1,4 @@
1
- import type { PolicyObjectRef } from '../../../language/policy/model/policy.js';
1
+ import type { PolicyObjectRef } from '../../../language/index.js';
2
2
  export declare class OwnershipCapture {
3
3
  #private;
4
4
  captureDefinition(value: object, ref: PolicyObjectRef, pointer: string): void;
@@ -1,3 +1,4 @@
1
+ import { policyObjectKey } from '../../../language/index.js';
1
2
  import { fail } from '../../schema/diagnostic.js';
2
3
  const definitionOwners = new WeakMap();
3
4
  const semanticOwners = new WeakMap();
@@ -5,7 +6,7 @@ export class OwnershipCapture {
5
6
  #definitions = new Map();
6
7
  #owners = new Map();
7
8
  captureDefinition(value, ref, pointer) {
8
- this.capture(value, `${ref.origin}:${ref.kind}.${ref.name}`, pointer);
9
+ this.capture(value, policyObjectKey(ref), pointer);
9
10
  this.#definitions.set(value, ref);
10
11
  }
11
12
  capture(value, owner, pointer) {
@@ -13,11 +13,11 @@ 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
+ 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> {
17
17
  readonly input: Input;
18
18
  readonly output: Output;
19
19
  readonly static: Static;
20
- readonly inheritance: Inheritance;
20
+ readonly abstract: Abstract;
21
21
  readonly auth: Auth;
22
22
  readonly name: Name;
23
23
  readonly key: Key;
@@ -251,20 +251,20 @@ type CallableOutputValue<Value extends Method | Function> = CallableConfig<Value
251
251
  type CallableStaticOf<Value extends Method> = CallableConfig<Value> extends {
252
252
  readonly static: true;
253
253
  } ? true : false;
254
- type CallableInheritanceOf<Value extends Method> = CallableConfig<Value> extends {
255
- readonly inheritance: infer Inheritance extends language.MethodInheritance;
256
- } ? Inheritance : 'default';
254
+ type CallableAbstractOf<Value extends Method> = CallableConfig<Value> extends {
255
+ readonly abstract: infer Abstract extends boolean;
256
+ } ? Abstract : false;
257
257
  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;
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>, CallableAbstractOf<Value>, CallableAuthOf<Value>, Name, language.MethodKey<Origin, OwnerName, Name>> : never;
259
259
  type MethodProjectionOfEntries<Origin extends string, OwnerName extends string, Entries> = Entries extends unknown ? MethodProjectionOfEntry<Origin, OwnerName, Entries> : never;
260
260
  type MethodCollectionOperation = 'byKey' | 'get';
261
261
  type PropertyProjectionOfResolved<Value> = Value extends language.ResolvedProperty<infer PropertyValue, infer Required, infer State> & {
262
262
  readonly name: infer Name extends string;
263
263
  readonly key: infer Key extends language.PropertyKey;
264
264
  } ? 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> & {
265
+ type MethodProjectionOfResolved<Value> = Value extends language.ResolvedMethod<infer Input, infer Output, infer Static, infer Abstract, infer Auth, infer _Policy, infer Key> & {
266
266
  readonly name: infer Name extends string;
267
- } ? MethodProjection<Input, Output, Static, Inheritance, Auth, Name, Key> : never;
267
+ } ? MethodProjection<Input, Output, Static, Abstract, Auth, Name, Key> : never;
268
268
  type PropertiesOfCollection<Value> = Value extends {
269
269
  readonly byKey: infer ByKey extends Readonly<Record<language.PropertyKey, language.ResolvedProperty<unknown, boolean>>>;
270
270
  } ? PropertyProjectionOfResolved<ByKey[keyof ByKey]> : never;
@@ -303,7 +303,7 @@ type EffectiveMethodEntriesOfClass<Value, Static extends boolean> = ClassInputOf
303
303
  readonly effectiveInstanceMethods: infer InstanceMethods;
304
304
  readonly effectiveStaticMethods: infer StaticMethods;
305
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;
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>, CallableAbstractOf<Value>, CallableAuthOf<Value>, MethodName, language.MethodKey<Origin, OwnerName, MethodName>> : never : never;
307
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
308
  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
309
  readonly origin: Origin;
@@ -335,7 +335,7 @@ type ProjectionPropertyByName<Entries extends PropertyProjection> = Readonly<{
335
335
  }>>;
336
336
  }>;
337
337
  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> & {
338
+ type ResolvedMethodOfProjection<Value> = Value extends MethodProjection<infer Input, infer Output, infer Static, infer Abstract, infer Auth, infer Name, infer Key> ? language.ResolvedMethod<Input, Output, Static, Abstract, Auth, language.PolicyCheckExpression, Key> & {
339
339
  readonly name: Name;
340
340
  } : never;
341
341
  type ProjectionMethodByKey<Entries extends MethodProjection> = Readonly<{
@@ -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' || (method && value.abstract === true)) {
257
253
  invalid('Callable Policy placement', undefined, `${path}/policy`);
258
254
  }
259
255
  policyCheck(value.policy, 0, `${path}/policy`);
@@ -494,7 +490,7 @@ function policyTerm(input, variableOnly, path) {
494
490
  const value = object(input, 'Policy pattern term', path);
495
491
  if (!variableOnly && value.kind === 'ref') {
496
492
  exact(value, ['kind', 'ref'], 'Policy reference term', path);
497
- definitionRef(value.ref, undefined, 'Policy reference term', `${path}/ref`);
493
+ policyObjectRef(value.ref, `${path}/ref`);
498
494
  return;
499
495
  }
500
496
  if (value.kind === 'variable') {
@@ -548,7 +544,7 @@ function policyCheckObject(input, objectPath) {
548
544
  }
549
545
  else if (target.kind === 'ref') {
550
546
  exact(target, ['kind', 'ref'], 'Callable Policy object ref', objectPath);
551
- definitionRef(target.ref, undefined, 'Callable Policy object ref', `${objectPath}/ref`);
547
+ policyObjectRef(target.ref, `${objectPath}/ref`);
552
548
  }
553
549
  else
554
550
  invalid('Callable Policy object', undefined, `${objectPath}/kind`);
@@ -560,6 +556,17 @@ function coreEndpoint(input, label, path) {
560
556
  else
561
557
  definitionRef(value, 'core', label, path);
562
558
  }
559
+ function policyObjectRef(input, path) {
560
+ const ref = object(input, 'Policy object ref', path);
561
+ if (ref.kind !== 'method') {
562
+ definitionRef(input, undefined, 'Policy object ref', path);
563
+ return;
564
+ }
565
+ exact(ref, ['kind', 'owner', 'name'], 'Policy Method ref', path);
566
+ definitionRef(ref.owner, 'class', 'Policy Method owner', `${path}/owner`);
567
+ if (typeof ref.name !== 'string' || !MEMBER_NAME_PATTERN.test(ref.name))
568
+ invalid('Policy Method name', undefined, `${path}/name`);
569
+ }
563
570
  function definitionRef(input, kind, label, path) {
564
571
  if (!isDefinitionRef(input) || (kind !== undefined && input.kind !== kind))
565
572
  invalid(label, undefined, path);
@@ -1,10 +1,11 @@
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 { policyObjectOrigin, policyVariableClass } from '../../language/index.js';
4
6
  import { branchesOf, branchProduct, capBranches, isConnected, targetMode, termKey, } from '../../language/policy/analysis/branches.js';
5
7
  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';
6
- import { policyVariableClass } from '../../language/policy/model/policy.js';
7
- import { normalizePolicyChecks, normalizePolicyExpression, } from '../../language/policy/normalization/normalize.js';
8
+ import { normalizePolicyExpression } from '../../language/policy/normalization/normalize.js';
8
9
  import { validateValueProgram } from '../../language/value/evaluate/program.js';
9
10
  import { GRAPH_NODE_PATH_SCHEMA_ID } from '../../language/value/syntax/node-path/schema.js';
10
11
  import { invalidCompiled } from '../diagnostic.js';
@@ -104,55 +105,22 @@ function analyzeClasses(classes) {
104
105
  fail(`${located.pointer}/data`, 'Data cannot redeclare an inherited contract.');
105
106
  dataOwner = located.value.ref;
106
107
  }
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 }) };
108
+ const own = new Map((located.value.kind === 'node' ? located.value.methods : []).map((method) => [
109
+ method.name,
110
+ {
111
+ key: `${classKey}.method.${method.name}`,
112
+ name: method.name,
113
+ owner: located.value.ref,
114
+ declaration: method,
115
+ entry: located.entry,
116
+ },
117
+ ]));
118
+ 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));
119
+ const analysis = {
120
+ properties,
121
+ ...composition,
122
+ ...(dataOwner === undefined ? {} : { dataOwner }),
123
+ };
156
124
  analyses.set(classKey, analysis);
157
125
  visiting.delete(classKey);
158
126
  return analysis;
@@ -173,9 +141,6 @@ function outputKey(entry, output) {
173
141
  function programKey(entry, value) {
174
142
  return json.serialize(entry.values[value]);
175
143
  }
176
- function policyKey(value) {
177
- return json.serialize((value === undefined ? null : normalizePolicyChecks(value)));
178
- }
179
144
  function validateClassFacets(context) {
180
145
  for (const located of context.classes.values()) {
181
146
  if (located.value.abstract &&
@@ -195,7 +160,7 @@ function validateClassFacets(context) {
195
160
  }
196
161
  if (located.value.kind === 'node') {
197
162
  for (const [index, method] of located.value.methods.entries()) {
198
- if (method.inheritance === 'abstract' && method.policy !== undefined) {
163
+ if (method.abstract === true && method.policy !== undefined) {
199
164
  fail(`${located.pointer}/methods/${index}/policy`, 'An abstract Method cannot have a Policy.');
200
165
  }
201
166
  }
@@ -348,15 +313,15 @@ function validatePolicyExpression(context, entry, expression, path, local) {
348
313
  }
349
314
  function validatePattern(context, expression, path, scope, declared) {
350
315
  if ('sameNode' in expression) {
351
- validatePolicyTerm(expression.sameNode.left, `${path}/sameNode/left`, scope);
352
- validatePolicyTerm(expression.sameNode.right, `${path}/sameNode/right`, scope);
316
+ validatePolicyTerm(context, expression.sameNode.left, `${path}/sameNode/left`, scope);
317
+ validatePolicyTerm(context, expression.sameNode.right, `${path}/sameNode/right`, scope);
353
318
  return;
354
319
  }
355
320
  if ('source' in expression) {
356
321
  if (context.classes.get(Key.of(expression.class))?.value.kind !== 'edge')
357
322
  fail(`${path}/class`, 'Policy pattern Class must resolve to an Edge Class.');
358
- validatePolicyTerm(expression.source, `${path}/source`, scope);
359
- validatePolicyTerm(expression.target, `${path}/target`, scope);
323
+ validatePolicyTerm(context, expression.source, `${path}/source`, scope);
324
+ validatePolicyTerm(context, expression.target, `${path}/target`, scope);
360
325
  if (expression.repeat !== undefined &&
361
326
  (!Number.isInteger(expression.repeat.min) ||
362
327
  !Number.isInteger(expression.repeat.max) ||
@@ -389,10 +354,18 @@ function validatePattern(context, expression, path, scope, declared) {
389
354
  fail(path, 'Policy pattern composition must be non-empty.');
390
355
  values.forEach((value, index) => validatePattern(context, value, `${path}/${index}`, scope, declared));
391
356
  }
392
- function validatePolicyTerm(term, path, scope) {
357
+ function validatePolicyTerm(context, term, path, scope) {
358
+ if (term.kind === 'ref' && term.ref.kind === 'method')
359
+ validatePolicyMethod(context, term.ref, `${path}/ref`);
393
360
  if (term.kind === 'variable' && !scope.has(term.id))
394
361
  fail(path, 'Policy variable is outside its declaring scope.');
395
362
  }
363
+ function validatePolicyMethod(context, ref, path) {
364
+ const owner = context.classes.get(Key.of(ref.owner))?.value;
365
+ const method = owner?.kind === 'node' ? owner.methods.find((method) => method.name === ref.name) : undefined;
366
+ if (method === undefined || method.abstract)
367
+ fail(path, 'Policy Method ref must name an exact concrete Method declaration.');
368
+ }
396
369
  function validatePolicyCycles(entry, local, entryIndex) {
397
370
  const visiting = new Set();
398
371
  const visited = new Set();
@@ -422,6 +395,8 @@ function validateCallablePolicy(context, entry, expression, inputIndex, fields,
422
395
  fail(`${path}/check`, 'Callable Policy must reference a local named Policy.');
423
396
  const objects = 'check' in expression ? { object: expression.object } : expression.sameNode;
424
397
  for (const [side, object] of Object.entries(objects)) {
398
+ if (object.kind === 'ref' && object.ref.kind === 'method')
399
+ validatePolicyMethod(context, object.ref, `${path}/${side}/ref`);
425
400
  if (object.kind === 'self' && !instanceMethod)
426
401
  fail(`${path}/${side}`, 'self is valid only for an instance Method.');
427
402
  if (object.kind === 'input' &&
@@ -590,7 +565,7 @@ function callableObjectContracts(context, entry, input, receiver, object) {
590
565
  }
591
566
  const projection = ref.kind === 'class'
592
567
  ? 'Class'
593
- : ref.kind === 'function'
568
+ : ref.kind === 'function' || ref.kind === 'method'
594
569
  ? 'Function'
595
570
  : ref.kind === 'policy'
596
571
  ? 'Policy'
@@ -893,7 +868,7 @@ function measureCompiledPattern(pattern, origin) {
893
868
  edgeCounts: [1],
894
869
  domains: new Set([
895
870
  origin,
896
- ...[pattern.sameNode.left, pattern.sameNode.right].flatMap((term) => term.kind === 'ref' ? [term.ref.origin] : []),
871
+ ...[pattern.sameNode.left, pattern.sameNode.right].flatMap((term) => term.kind === 'ref' ? [policyObjectOrigin(term.ref)] : []),
897
872
  ]),
898
873
  };
899
874
  if ('source' in pattern)
@@ -903,7 +878,7 @@ function measureCompiledPattern(pattern, origin) {
903
878
  domains: new Set([
904
879
  origin,
905
880
  pattern.class.origin,
906
- ...[pattern.source, pattern.target].flatMap((term) => term.kind === 'ref' ? [term.ref.origin] : []),
881
+ ...[pattern.source, pattern.target].flatMap((term) => term.kind === 'ref' ? [policyObjectOrigin(term.ref)] : []),
907
882
  ]),
908
883
  };
909
884
  if ('exists' in pattern) {
@@ -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`),
@@ -8,6 +8,7 @@ export function createClasses(definitions, validation) {
8
8
  .map((value) => [Key.of(value.ref), value]));
9
9
  const resolved = new Map();
10
10
  const visiting = new Set();
11
+ const frontiers = new Map();
11
12
  const resolve = (ref) => {
12
13
  const key = Key.of(ref);
13
14
  const cached = resolved.get(key);
@@ -25,19 +26,18 @@ export function createClasses(definitions, validation) {
25
26
  }
26
27
  const shell = {};
27
28
  const effectiveProperties = new Map();
28
- const effectiveMethods = new Map();
29
+ const frontier = new Map();
29
30
  let inheritedData;
30
31
  for (const parent of parents) {
31
32
  for (const property of parent.properties) {
32
33
  effectiveProperties.set(property.key, property);
33
34
  }
34
- for (const method of [...parent.methods, ...parent.static.methods]) {
35
- if (!effectiveMethods.has(method.name))
36
- effectiveMethods.set(method.name, method);
37
- }
35
+ for (const [name, method] of frontiers.get(parent.key))
36
+ frontier.set(name, method);
38
37
  if (parent.kind === 'node' && inheritedData === undefined)
39
38
  inheritedData = parent.data;
40
39
  }
40
+ const effectiveMethods = new Map(frontier);
41
41
  const ownProperties = [];
42
42
  for (const [name, propertyDefinition] of Object.entries(definition.properties)) {
43
43
  const propertyKey = PropertyKeyValue.of(ref, name);
@@ -72,13 +72,15 @@ export function createClasses(definitions, validation) {
72
72
  auth: methodDefinition.auth,
73
73
  policy: methodDefinition.policy,
74
74
  static: methodDefinition.static,
75
- inheritance: methodDefinition.inheritance,
75
+ abstract: methodDefinition.abstract,
76
76
  ...(inputFields === undefined ? {} : { inputFields }),
77
77
  definition: methodDefinition,
78
78
  validateInput: (input) => validation.callableInput(methodKey, input),
79
79
  validateInputField: (field, input) => validation.callableInputField(methodKey, field, input),
80
80
  validateOutput: (output) => validation.callableOutput(methodKey, output),
81
81
  });
82
+ if (method.abstract)
83
+ frontier.set(name, method);
82
84
  effectiveMethods.set(name, method);
83
85
  (methodDefinition.static ? ownStaticMethods : ownInstanceMethods).push(method);
84
86
  }
@@ -119,6 +121,7 @@ export function createClasses(definitions, validation) {
119
121
  },
120
122
  });
121
123
  const value = Object.freeze(shell);
124
+ frontiers.set(key, frontier);
122
125
  resolved.set(key, value);
123
126
  visiting.delete(key);
124
127
  return value;
@@ -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, 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 || local.declaration.abstract) && !missing.has(required.name)) {
36
+ missing.add(required.name);
37
+ report(`Concrete Class requires a local concrete implementation 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 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
  };
@@ -21,14 +21,14 @@ export interface ResolvedMethod<out Input = unknown, out Output = unknown, out S
21
21
  readonly input: MethodDefinition['input'];
22
22
  readonly output: MethodDefinition['output'];
23
23
  readonly auth: Auth;
24
- readonly policy?: Auth extends 'authorized' ? Policy : never;
24
+ readonly policy?: Abstract extends true ? never : Auth extends 'authorized' ? Policy : never;
25
25
  readonly static: Static;
26
- readonly inheritance: Inheritance;
26
+ readonly abstract: Abstract;
27
27
  /** Present only when the callable input is a safely enumerable closed object. */
28
28
  readonly inputFields?: readonly string[];
29
- readonly definition: CallableDefinitionOf<MethodDefinition, Auth, Policy> & {
29
+ readonly definition: CallableDefinitionOf<MethodDefinition, Auth, Abstract extends true ? never : Policy> & {
30
30
  readonly static: Static;
31
- readonly inheritance: Inheritance;
31
+ readonly abstract: Abstract;
32
32
  };
33
33
  validateInput(input: unknown): readonly Diagnostic[];
34
34
  validateInputField(field: string, input: unknown): readonly Diagnostic[];
@@ -20,7 +20,7 @@ export type { Definition } from './definition.js';
20
20
  export type { ResolvedDefinition, ResolvedDefinitionOf } from './resolved.js';
21
21
  export type { Diagnostic, DiagnosticCode } from './validation/diagnostic.js';
22
22
  export type * from './policy/model/policy.js';
23
- export { policyVariableClass, policyVariableSelector } from './policy/model/policy.js';
23
+ export { policyVariableClass, policyVariableSelector, policyObjectKey, policyObjectOrigin, } from './policy/model/policy.js';
24
24
  export type * from './policy/resolved.js';
25
25
  export type * from './view/model/view.js';
26
26
  export type * from './view/resolved.js';
@@ -1,3 +1,3 @@
1
1
  export { stateMachine } from './state/index.js';
2
2
  export { PropertyValues } from './class/property-values.js';
3
- export { policyVariableClass, policyVariableSelector } from './policy/model/policy.js';
3
+ export { policyVariableClass, policyVariableSelector, policyObjectKey, policyObjectOrigin, } from './policy/model/policy.js';
@@ -1,5 +1,5 @@
1
1
  import { definitionRefIndex } from '../../../addressing/ordering.js';
2
- import { policyVariableSelector } from '../model/policy.js';
2
+ import { policyObjectKey, policyVariableSelector } from '../model/policy.js';
3
3
  import { BRANCH_OVERFLOW } from './budgets.js';
4
4
  export function branchesOf(pattern, namespace) {
5
5
  const variables = collectVariableClasses(pattern, namespace);
@@ -62,7 +62,7 @@ export function termKey(term, namespace) {
62
62
  return term.kind === 'variable'
63
63
  ? variableKey(namespace, term.id)
64
64
  : term.kind === 'ref'
65
- ? `ref:${definitionRefIndex(term.ref)}`
65
+ ? `ref:${term.ref.kind === 'method' ? policyObjectKey(term.ref) : definitionRefIndex(term.ref)}`
66
66
  : term.kind;
67
67
  }
68
68
  function branchesForNode(pattern, namespace, variables) {
@@ -1,4 +1,4 @@
1
1
  export type * from './model/policy.js';
2
- export { policyVariableClass, policyVariableSelector } from './model/policy.js';
2
+ export { policyVariableClass, policyVariableSelector, policyObjectKey, policyObjectOrigin, } from './model/policy.js';
3
3
  export type * from './resolved.js';
4
4
  export * from './normalization/normalize.js';
@@ -1,2 +1,2 @@
1
- export { policyVariableClass, policyVariableSelector } from './model/policy.js';
1
+ export { policyVariableClass, policyVariableSelector, policyObjectKey, policyObjectOrigin, } from './model/policy.js';
2
2
  export * from './normalization/normalize.js';
@@ -1,4 +1,4 @@
1
- import type { CoreRef, ClassRef, FunctionRef, PolicyRef, ViewRef, DefinitionBase } from '../../../addressing/index.js';
1
+ import type { CoreRef, ClassRef, FunctionRef, PolicyRef, ViewRef, DefinitionBase, MethodAddress } from '../../../addressing/index.js';
2
2
  export type PolicyPatternTerm = {
3
3
  readonly kind: 'subject';
4
4
  } | {
@@ -85,7 +85,10 @@ export interface PolicyDefinition extends DefinitionBase<PolicyRef> {
85
85
  readonly description?: string;
86
86
  }
87
87
  /** Local Domain members whose installed projection resolves through one exact member Path. */
88
- export type PolicyObjectRef = ClassRef | FunctionRef | PolicyRef | ViewRef | CoreRef;
88
+ export type PolicyObjectRef = ClassRef | FunctionRef | PolicyRef | ViewRef | CoreRef | MethodAddress;
89
+ /** Exact projected identity, including Class-owned Method declarations. */
90
+ export declare function policyObjectKey(ref: PolicyObjectRef): string;
91
+ export declare function policyObjectOrigin(ref: PolicyObjectRef): string;
89
92
  export type PolicyCheckObject = {
90
93
  readonly kind: 'self';
91
94
  } | {
@@ -7,3 +7,12 @@ export function policyVariableClass(variable) {
7
7
  const selector = policyVariableSelector(variable);
8
8
  return selector.kind === 'any' ? undefined : selector.class;
9
9
  }
10
+ /** Exact projected identity, including Class-owned Method declarations. */
11
+ export function policyObjectKey(ref) {
12
+ return ref.kind === 'method'
13
+ ? `${ref.owner.origin}:class.${ref.owner.name}.method.${ref.name}`
14
+ : `${ref.origin}:${ref.kind}.${ref.name}`;
15
+ }
16
+ export function policyObjectOrigin(ref) {
17
+ return ref.kind === 'method' ? ref.owner.origin : ref.origin;
18
+ }
@@ -1,6 +1,6 @@
1
1
  import { json } from '@astrale-os/kernel-dsl/value';
2
2
  import { definitionRefIndex, compareDefinitionRefs } from '../../../addressing/ordering.js';
3
- import { policyVariableSelector } from '../model/policy.js';
3
+ import { policyObjectKey, policyVariableSelector } from '../model/policy.js';
4
4
  const textEncoder = new TextEncoder();
5
5
  export function normalizePolicyExpression(value) {
6
6
  if ('match' in value) {
@@ -146,7 +146,7 @@ function termColor(term, colors) {
146
146
  if (term.kind === 'variable')
147
147
  return `variable:${colors.get(term.id) ?? '?'}`;
148
148
  if (term.kind === 'ref')
149
- return `ref:${definitionRefIndex(term.ref)}`;
149
+ return `ref:${term.ref.kind === 'method' ? policyObjectKey(term.ref) : definitionRefIndex(term.ref)}`;
150
150
  return term.kind;
151
151
  }
152
152
  function collectFirstUseKeys(value, colors, result, prefix = '$') {
@@ -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) {
@@ -82,13 +82,13 @@ function collectPolicyPatternRefs(expression, add) {
82
82
  if ('sameNode' in expression) {
83
83
  for (const term of [expression.sameNode.left, expression.sameNode.right])
84
84
  if (term.kind === 'ref')
85
- add(term.ref);
85
+ add(term.ref.kind === 'method' ? term.ref.owner : term.ref);
86
86
  return;
87
87
  }
88
88
  if ('source' in expression) {
89
89
  for (const term of [expression.source, expression.target])
90
90
  if (term.kind === 'ref')
91
- add(term.ref);
91
+ add(term.ref.kind === 'method' ? term.ref.owner : term.ref);
92
92
  add(expression.class);
93
93
  return;
94
94
  }
@@ -108,13 +108,15 @@ function collectPolicyCheckRefs(expression, add) {
108
108
  if ('sameNode' in expression) {
109
109
  for (const term of [expression.sameNode.left, expression.sameNode.right])
110
110
  if (term.kind === 'ref')
111
- add(term.ref);
111
+ add(term.ref.kind === 'method' ? term.ref.owner : term.ref);
112
112
  return;
113
113
  }
114
114
  if ('check' in expression) {
115
115
  add(expression.check);
116
116
  if (expression.object.kind === 'ref')
117
- add(expression.object.ref);
117
+ add(expression.object.ref.kind === 'method'
118
+ ? expression.object.ref.owner
119
+ : expression.object.ref);
118
120
  return;
119
121
  }
120
122
  const children = 'allOf' in expression ? expression.allOf : expression.anyOf;
@@ -71,7 +71,7 @@ function validateFacet(environment, declaration, pointer, diagnostics) {
71
71
  return;
72
72
  for (const [name, method] of Object.entries(declaration.methods)) {
73
73
  const methodPointer = `${pointer}/methods/${escapePointer(name)}`;
74
- if (method.inheritance === 'abstract' && method.policy !== undefined) {
74
+ if (method.abstract === true && method.policy !== undefined) {
75
75
  diagnostics.push(diagnostic('DV_METHOD_ABSTRACT', `${methodPointer}/policy`, 'Abstract Methods cannot have a Policy.'));
76
76
  }
77
77
  }
@@ -63,6 +63,7 @@ const coreRefSchema = strictObject({
63
63
  const domainRefSchema = strictObject({ kind: literal('domain') });
64
64
  const coreEndpointRefSchema = discriminatedUnion('kind', [coreRefSchema, domainRefSchema]);
65
65
  const policyObjectRefSchema = discriminatedUnion('kind', [
66
+ strictObject({ kind: literal('method'), owner: classRefSchema, name: memberNameSchema }),
66
67
  classRefSchema,
67
68
  functionRefSchema,
68
69
  policyRefSchema,
@@ -187,9 +188,9 @@ const callableShape = {
187
188
  const methodDeclarationSchema = strictObject({
188
189
  ...callableShape,
189
190
  static: boolean(),
190
- inheritance: enumeration(['abstract', 'default', 'sealed']),
191
+ abstract: boolean(),
191
192
  }).superRefine((method, context) => {
192
- if (method.inheritance === 'abstract' && method.policy !== undefined) {
193
+ if (method.abstract === true && method.policy !== undefined) {
193
194
  context.addIssue({
194
195
  code: 'custom',
195
196
  path: ['policy'],
@@ -1,5 +1,5 @@
1
+ import { policyObjectOrigin, policyVariableClass } from '../../../language/index.js';
1
2
  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';
2
- import { policyVariableClass } from '../../../language/policy/model/policy.js';
3
3
  import { escapePointer } from './context.js';
4
4
  export { BRANCH_OVERFLOW, MAX_BRANCHES, MAX_CHECK_DEPTH, MAX_CHECK_LEAVES, MAX_DOMAINS, MAX_EDGES, MAX_PATTERN_DEPTH, MAX_REPEAT, MAX_VARIABLES, measureChecks, };
5
5
  export function validatePolicyBudgets(context) {
@@ -52,7 +52,7 @@ function measurePattern(pattern, ownOrigin) {
52
52
  edgeCounts: [1],
53
53
  domains: new Set([
54
54
  ownOrigin,
55
- ...[pattern.sameNode.left, pattern.sameNode.right].flatMap((term) => term.kind === 'ref' ? [term.ref.origin] : []),
55
+ ...[pattern.sameNode.left, pattern.sameNode.right].flatMap((term) => term.kind === 'ref' ? [policyObjectOrigin(term.ref)] : []),
56
56
  ]),
57
57
  };
58
58
  if (isEdge(pattern)) {
@@ -62,7 +62,7 @@ function measurePattern(pattern, ownOrigin) {
62
62
  domains: new Set([
63
63
  ownOrigin,
64
64
  pattern.class.origin,
65
- ...[pattern.source, pattern.target].flatMap((term) => term.kind === 'ref' ? [term.ref.origin] : []),
65
+ ...[pattern.source, pattern.target].flatMap((term) => term.kind === 'ref' ? [policyObjectOrigin(term.ref)] : []),
66
66
  ]),
67
67
  };
68
68
  }
@@ -1,4 +1,4 @@
1
- import type { PolicyObjectRef } from '../../../language/policy/model/policy.js';
1
+ import type { PolicyObjectRef } from '../../../language/index.js';
2
2
  import type { PolicyValidationContext } from './context.js';
3
3
  import type { ObjectContract } from './typing.js';
4
4
  export declare function referenceContracts(validation: PolicyValidationContext, ref: PolicyObjectRef, pointer: string): readonly ObjectContract[] | undefined;
@@ -1,6 +1,5 @@
1
- import { Key } from '../../../addressing/index.js';
2
1
  import { definitionRefIndex } from '../../../addressing/ordering.js';
3
- const formatDefinitionRef = Key.of;
2
+ import { policyObjectKey, policyObjectOrigin } from '../../../language/index.js';
4
3
  const KERNEL_ORIGIN = 'kernel.astrale.ai';
5
4
  const PROJECTION_CLASS = nodeClassRef('Class');
6
5
  const PROJECTION_FUNCTION = nodeClassRef('Function');
@@ -8,12 +7,12 @@ const PROJECTION_POLICY = nodeClassRef('Policy');
8
7
  const PROJECTION_VIEW = nodeClassRef('View');
9
8
  export function referenceContracts(validation, ref, pointer) {
10
9
  const candidate = validation.environment.candidate;
11
- const local = ref.origin === candidate.origin;
12
- const direct = Object.values(candidate.dependencies).some((entry) => entry.origin === ref.origin);
10
+ const local = policyObjectOrigin(ref) === candidate.origin;
11
+ const direct = Object.values(candidate.dependencies).some((entry) => entry.origin === policyObjectOrigin(ref));
13
12
  const owner = local
14
13
  ? candidate
15
14
  : direct
16
- ? validation.environment.dependencies.get(ref.origin)
15
+ ? validation.environment.dependencies.get(policyObjectOrigin(ref))
17
16
  : undefined;
18
17
  if (owner === undefined)
19
18
  return invalidPolicyObjectRef(validation, ref, pointer);
@@ -24,6 +23,13 @@ export function referenceContracts(validation, ref, pointer) {
24
23
  ? [{ kind: 'exact', ref: PROJECTION_CLASS }]
25
24
  : invalidPolicyObjectRef(validation, ref, pointer);
26
25
  }
26
+ case 'method': {
27
+ const declaringClass = owner.classes[ref.owner.name];
28
+ const method = declaringClass?.kind === 'node' ? declaringClass.methods[ref.name] : undefined;
29
+ return method === undefined || method.abstract
30
+ ? invalidPolicyObjectRef(validation, ref, pointer)
31
+ : [{ kind: 'exact', ref: PROJECTION_FUNCTION }];
32
+ }
27
33
  case 'function':
28
34
  return owner.functions[ref.name] === undefined
29
35
  ? invalidPolicyObjectRef(validation, ref, pointer)
@@ -56,7 +62,7 @@ export function referenceContracts(validation, ref, pointer) {
56
62
  return invalidPolicyObjectRef(validation, ref, pointer);
57
63
  }
58
64
  function invalidPolicyObjectRef(validation, ref, pointer) {
59
- validation.add('PL_REF', `${pointer}/ref`, `${formatDefinitionRef(ref)} is not an exact local or direct-dependency projected Policy object.`, formatDefinitionRef(ref));
65
+ validation.add('PL_REF', `${pointer}/ref`, `${policyObjectKey(ref)} is not an exact local or direct-dependency projected Policy object.`, policyObjectKey(ref));
60
66
  return undefined;
61
67
  }
62
68
  function nodeClassRef(name) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@astrale-os/kernel-dsl",
3
- "version": "0.2.0-beta.30",
3
+ "version": "0.2.0-beta.32",
4
4
  "description": "Astrale Kernel DSL - Schema authoring, inference, and IR serialization",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {