@astrale-os/kernel-dsl 1.0.0-beta.4 → 1.0.0-beta.6

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 (51) hide show
  1. package/dist/v1/builder/callable/authoring.d.ts +10 -4
  2. package/dist/v1/builder/callable/authoring.js +2 -0
  3. package/dist/v1/builder/class/authoring.d.ts +11 -1
  4. package/dist/v1/builder/class/authoring.js +11 -1
  5. package/dist/v1/builder/lowering/callable.js +1 -0
  6. package/dist/v1/builder/lowering/class.js +7 -0
  7. package/dist/v1/builder/schema/projection/callable.d.ts +7 -5
  8. package/dist/v1/builder/schema/projection/model.d.ts +4 -2
  9. package/dist/v1/builder/schema/projection/resolved-class.d.ts +2 -1
  10. package/dist/v1/compiled/admission/semantics/class.js +8 -1
  11. package/dist/v1/compiled/admission/wire/callable.js +11 -1
  12. package/dist/v1/compiled/admission/wire/class.js +24 -2
  13. package/dist/v1/compiled/compiler/compile.js +2 -0
  14. package/dist/v1/domain/create.js +1 -0
  15. package/dist/v1/domain/projection/classes.js +3 -0
  16. package/dist/v1/index.d.ts +1 -1
  17. package/dist/v1/language/callable/admission.d.ts +15 -0
  18. package/dist/v1/language/callable/admission.js +43 -0
  19. package/dist/v1/language/callable/model/callable.d.ts +10 -0
  20. package/dist/v1/language/callable/resolved.d.ts +5 -1
  21. package/dist/v1/language/class/inheritance.d.ts +9 -5
  22. package/dist/v1/language/class/inheritance.js +4 -0
  23. package/dist/v1/language/class/model/class.d.ts +9 -0
  24. package/dist/v1/language/class/model/edge.d.ts +11 -0
  25. package/dist/v1/language/index.d.ts +2 -0
  26. package/dist/v1/language/index.js +2 -0
  27. package/dist/v1/language/value/evaluate/validate.js +4 -0
  28. package/dist/v1/language/value/evaluate/validity.optimization.d.ts +14 -0
  29. package/dist/v1/language/value/evaluate/validity.optimization.js +342 -0
  30. package/dist/v1/language/value/profile/canonicalize.js +6 -5
  31. package/dist/v1/schema/compatibility/comparators/classes.d.ts +3 -0
  32. package/dist/v1/schema/compatibility/comparators/classes.js +4 -0
  33. package/dist/v1/schema/compatibility/comparators/index.d.ts +1 -1
  34. package/dist/v1/schema/compatibility/fields.d.ts +20 -4
  35. package/dist/v1/schema/compatibility/fields.js +12 -3
  36. package/dist/v1/schema/compatibility/model.d.ts +4 -3
  37. package/dist/v1/schema/compatibility/structure/index.js +6 -4
  38. package/dist/v1/schema/compatibility/structure/requirements.js +29 -1
  39. package/dist/v1/schema/compatibility/versioning/surface.d.ts +1 -1
  40. package/dist/v1/schema/compatibility/versioning/surface.js +25 -2
  41. package/dist/v1/schema/diagnostic.d.ts +1 -1
  42. package/dist/v1/schema/normalization/domain.js +4 -0
  43. package/dist/v1/schema/validation/callable.d.ts +1 -1
  44. package/dist/v1/schema/validation/callable.js +17 -2
  45. package/dist/v1/schema/validation/class.js +10 -1
  46. package/dist/v1/schema/validation/domain.structure.js +29 -8
  47. package/dist/value/canonical.d.ts +5 -0
  48. package/dist/value/canonical.js +12 -0
  49. package/dist/value/json.d.ts +1 -1
  50. package/dist/value/json.js +1 -1
  51. package/package.json +1 -1
@@ -1,7 +1,7 @@
1
1
  import type { Value as PortableValue } from '@astrale-os/kernel-dsl/value';
2
2
  import type { z } from 'zod';
3
3
  import type { PolicyRef } from '../../addressing/index.js';
4
- import type { BinaryInputProjection, CallableExecution, FunctionAuth } from '../../language/callable/model/callable.js';
4
+ import type { BinaryInputProjection, CallableAdmission, CallableExecution, FunctionAuth } from '../../language/callable/model/callable.js';
5
5
  import type { PolicyCheckExpression, PolicyCheckObject, PolicyObjectRef } from '../../language/policy/model/policy.js';
6
6
  import type { Class } from '../class/authoring.js';
7
7
  import type { ValueSchemaInput } from '../class/property.js';
@@ -62,6 +62,8 @@ export interface CallableInputBase<Input extends FunctionInputSchema = ValueSche
62
62
  readonly output: CallableOutputInput;
63
63
  /** `{ durable: true }` makes an invocation return an Execution reference. */
64
64
  readonly execution?: CallableExecution;
65
+ /** `{ maxAge }`: how old, in seconds, the admission a call is served under may be. */
66
+ readonly admission?: CallableAdmission;
65
67
  }
66
68
  type ConcreteCallableInput<Input extends FunctionInputSchema = ValueSchemaInput> = CallableInputBase<Input> & ({
67
69
  readonly auth: 'anonymous';
@@ -83,7 +85,7 @@ export type MethodInput = ConcreteCallableInput & {
83
85
  readonly abstract?: boolean;
84
86
  };
85
87
  export type CallableInput = FunctionInput | MethodInput;
86
- export interface CallableProjection<out Input = unknown, out Output = unknown, out Auth extends FunctionAuth = FunctionAuth, out Static extends boolean = boolean, out Abstract extends boolean = boolean, out Execution extends CallableExecution | undefined = CallableExecution | undefined> {
88
+ export interface CallableProjection<out Input = unknown, out Output = unknown, out Auth extends FunctionAuth = FunctionAuth, out Static extends boolean = boolean, out Abstract extends boolean = boolean, out Execution extends CallableExecution | undefined = CallableExecution | undefined, out Admission extends CallableAdmission | undefined = CallableAdmission | undefined> {
87
89
  readonly [CALLABLE_PROJECTION_VALUE]: true;
88
90
  readonly input: Input;
89
91
  readonly output: Output;
@@ -91,6 +93,7 @@ export interface CallableProjection<out Input = unknown, out Output = unknown, o
91
93
  readonly static: Static;
92
94
  readonly abstract: Abstract;
93
95
  readonly execution: Execution;
96
+ readonly admission: Admission;
94
97
  }
95
98
  type ValueInputOf<Schema> = Schema extends TypedValueSchema<infer Value> ? Value : Schema extends z.ZodType ? z.input<Schema> : PortableValue;
96
99
  type FunctionInputOf<Schema> = Schema extends BinaryInputAuthoring<infer Metadata> ? BinaryInputProjection<ValueInputOf<Metadata>> : ValueInputOf<Schema>;
@@ -98,6 +101,9 @@ type ValueOutputOf<Schema> = Schema extends TypedValueSchema<infer Value> ? Valu
98
101
  type ExecutionOfConfig<Config extends CallableInput> = Config extends {
99
102
  readonly execution: infer Execution extends CallableExecution;
100
103
  } ? Execution : undefined;
104
+ type AdmissionOfConfig<Config extends CallableInput> = Config extends {
105
+ readonly admission: infer Admission extends CallableAdmission;
106
+ } ? Admission : undefined;
101
107
  type CallableOutputOf<Input extends CallableOutputInput> = Input extends StreamOutput<infer Item> ? {
102
108
  readonly mode: 'stream';
103
109
  readonly item: ValueOutputOf<Item>;
@@ -120,12 +126,12 @@ export type ProjectionOfCallable<Value extends Function | Method> = Value extend
120
126
  export declare function callableConfiguration(value: Function | Method): CallableInput;
121
127
  type PolicyObjectAuthoring = Class | Function | Method | Policy | View | CoreNode;
122
128
  export type PolicyObjectInput = Lazy<PolicyObjectAuthoring | PolicyObjectRef>;
123
- export declare function func<const Config extends FunctionInput, Input = FunctionInputOf<Config['input']>, Output = CallableOutputOf<Config['output']>, Auth extends FunctionAuth = Config['auth'], Execution extends CallableExecution | undefined = ExecutionOfConfig<Config>>(config: Config): Function<CallableProjection<Input, Output, Auth, false, false, Execution>>;
129
+ export declare function func<const Config extends FunctionInput, Input = FunctionInputOf<Config['input']>, Output = CallableOutputOf<Config['output']>, Auth extends FunctionAuth = Config['auth'], Execution extends CallableExecution | undefined = ExecutionOfConfig<Config>, Admission extends CallableAdmission | undefined = AdmissionOfConfig<Config>>(config: Config): Function<CallableProjection<Input, Output, Auth, false, false, Execution, Admission>>;
124
130
  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 {
125
131
  readonly static: true;
126
132
  } ? true : false, Abstract extends boolean = Config extends {
127
133
  readonly abstract: infer Value extends boolean;
128
- } ? Value : false, Execution extends CallableExecution | undefined = ExecutionOfConfig<Config>>(config: Config): Method<CallableProjection<Input, Output, Auth, Static, Abstract, Execution>>;
134
+ } ? Value : false, Execution extends CallableExecution | undefined = ExecutionOfConfig<Config>, Admission extends CallableAdmission | undefined = AdmissionOfConfig<Config>>(config: Config): Method<CallableProjection<Input, Output, Auth, Static, Abstract, Execution, Admission>>;
129
135
  export declare function callableInputField(name: string): Extract<PolicyCheckObject, {
130
136
  readonly kind: 'input';
131
137
  }>;
@@ -18,6 +18,7 @@ export function func(config) {
18
18
  'static',
19
19
  'abstract',
20
20
  'execution',
21
+ 'admission',
21
22
  ]);
22
23
  const value = authoringValue('function', {});
23
24
  callableInputs.set(value, { ...config });
@@ -33,6 +34,7 @@ export function method(config) {
33
34
  'static',
34
35
  'abstract',
35
36
  'execution',
37
+ 'admission',
36
38
  ]);
37
39
  const value = authoringValue('method', {});
38
40
  callableInputs.set(value, { ...config });
@@ -3,8 +3,9 @@ import type { z } from 'zod';
3
3
  import type { ClassRef } from '../../addressing/index.js';
4
4
  import type { MethodRecord } from '../../language/callable/model/callable.js';
5
5
  import type { ResolvedMethod } from '../../language/callable/resolved.js';
6
+ import type { Inheritance } from '../../language/class/model/class.js';
6
7
  import type { MediaType } from '../../language/class/model/data.js';
7
- import type { EdgeCount } from '../../language/class/model/edge.js';
8
+ import type { EdgeCount, EdgeUniqueTuple } from '../../language/class/model/edge.js';
8
9
  import type { ResolvedClass, ResolvedProperty } from '../../language/class/resolved.js';
9
10
  import type { ClassRuleChecks, ClassRuleObject } from '../../language/policy/model/policy.js';
10
11
  import type { StateTopology, StateTopologyOf } from '../../language/state/topology.js';
@@ -58,6 +59,8 @@ export interface NodeClassPoliciesInput {
58
59
  }
59
60
  export interface NodeClassInput {
60
61
  readonly abstract?: boolean;
62
+ /** Which Classes may extend this one; omitted, those of its own Domain only (`'internal'`). */
63
+ readonly inheritance?: Inheritance;
61
64
  readonly description?: string;
62
65
  readonly extends?: readonly NodeClassReferenceInput[];
63
66
  readonly properties?: PropertyRecordInput;
@@ -69,6 +72,8 @@ export interface NodeClassInput {
69
72
  }
70
73
  export interface EdgeClassOptions {
71
74
  readonly abstract?: boolean;
75
+ /** Which Classes may extend this one; omitted, those of its own Domain only (`'internal'`). */
76
+ readonly inheritance?: Inheritance;
72
77
  readonly description?: string;
73
78
  readonly extends?: readonly EdgeClassReferenceInput[];
74
79
  readonly properties?: PropertyRecordInput;
@@ -83,6 +88,11 @@ export interface DirectedEdgeInput extends EdgeClassOptions {
83
88
  readonly orientation: 'directed';
84
89
  readonly source: DirectedSourceInput;
85
90
  readonly target: DirectedTargetInput;
91
+ /**
92
+ * The tuples no two Edges of this Class and of its active concrete descendants share; omitted,
93
+ * none. A descendant is covered without declaring them again.
94
+ */
95
+ readonly unique?: readonly EdgeUniqueTuple[];
86
96
  }
87
97
  export interface UndirectedEdgeInput extends Omit<EdgeClassOptions, 'constraints'> {
88
98
  readonly orientation: 'undirected';
@@ -17,6 +17,7 @@ export function nodeClass(config) {
17
17
  const admitted = config ?? {};
18
18
  assertKnownKeys(admitted, [
19
19
  'abstract',
20
+ 'inheritance',
20
21
  'description',
21
22
  'extends',
22
23
  'properties',
@@ -39,15 +40,17 @@ export const edgeClass = Object.freeze({
39
40
  'source',
40
41
  'target',
41
42
  'abstract',
43
+ 'inheritance',
42
44
  'description',
43
45
  'extends',
44
46
  'properties',
45
47
  'policies',
46
48
  'constraints',
49
+ 'unique',
47
50
  ]);
48
51
  validateDirectedSourceInput(input.source);
49
52
  validateDirectedTargetInput(input.target);
50
- const { source, target, ...options } = input;
53
+ const { source, target, unique, ...options } = input;
51
54
  validateEdgeClassOptions(options);
52
55
  const value = authoringValue('definition', {
53
56
  kind: 'edge',
@@ -56,6 +59,10 @@ export const edgeClass = Object.freeze({
56
59
  classInputs.set(value, {
57
60
  source: { ...source },
58
61
  target: { ...target },
62
+ // Copied, so the declaration does not follow later changes to the author's arrays.
63
+ ...(unique === undefined
64
+ ? {}
65
+ : { unique: unique.map((tuple) => [...tuple]) }),
59
66
  ...options,
60
67
  abstract: options.abstract ?? false,
61
68
  });
@@ -65,6 +72,7 @@ export const edgeClass = Object.freeze({
65
72
  assertKnownKeys(input, [
66
73
  'endpoints',
67
74
  'abstract',
75
+ 'inheritance',
68
76
  'description',
69
77
  'extends',
70
78
  'properties',
@@ -107,6 +115,7 @@ function validateIncidentEndpointInput(value) {
107
115
  function validateEdgeClassOptions(value) {
108
116
  assertKnownKeys(value, [
109
117
  'abstract',
118
+ 'inheritance',
110
119
  'description',
111
120
  'extends',
112
121
  'properties',
@@ -121,6 +130,7 @@ function validateEdgeClassOptions(value) {
121
130
  function validateUndirectedEdgeClassOptions(value) {
122
131
  assertKnownKeys(value, [
123
132
  'abstract',
133
+ 'inheritance',
124
134
  'description',
125
135
  'extends',
126
136
  'properties',
@@ -42,6 +42,7 @@ export class V1CallableCompiler {
42
42
  input,
43
43
  output,
44
44
  ...(config.execution === undefined ? {} : { execution: config.execution }),
45
+ ...(config.admission === undefined ? {} : { admission: config.admission }),
45
46
  };
46
47
  const contract = auth === 'authorized'
47
48
  ? { ...base, auth, ...(policy === undefined ? {} : { policy }) }
@@ -31,6 +31,7 @@ export class V1DefinitionCompiler {
31
31
  ref: { origin: this.context.origin, kind: 'class', name },
32
32
  kind: 'node',
33
33
  abstract: input.abstract ?? false,
34
+ ...declaredInheritance(input.inheritance),
34
35
  ...(input.description === undefined ? {} : { description: input.description }),
35
36
  extends: this.parents(input.extends, 'node', `${pointer}/extends`),
36
37
  properties: facet,
@@ -53,6 +54,7 @@ export class V1DefinitionCompiler {
53
54
  target: this.directedTarget(directed.target, `${pointer}/endpoints/target`),
54
55
  },
55
56
  constraints: directedConstraints(directed),
57
+ ...(directed.unique === undefined ? {} : { unique: directed.unique }),
56
58
  };
57
59
  }
58
60
  const undirected = classConfiguration(value);
@@ -69,6 +71,7 @@ export class V1DefinitionCompiler {
69
71
  ref: { origin: this.context.origin, kind: 'class', name },
70
72
  kind: 'edge',
71
73
  abstract: input.abstract ?? false,
74
+ ...declaredInheritance(input.inheritance),
72
75
  ...(input.description === undefined ? {} : { description: input.description }),
73
76
  extends: this.parents(input.extends, 'edge', `${pointer}/extends`),
74
77
  properties: facet,
@@ -158,6 +161,10 @@ export class V1DefinitionCompiler {
158
161
  }));
159
162
  }
160
163
  }
164
+ /** The default `'internal'` is never declared, so a Class that keeps it keeps its revision. */
165
+ function declaredInheritance(inheritance) {
166
+ return inheritance === undefined || inheritance === 'internal' ? {} : { inheritance };
167
+ }
161
168
  function dataDeclaration(mediaType) {
162
169
  return mediaType === undefined ? undefined : { mediaType };
163
170
  }
@@ -18,6 +18,7 @@ type CallableConfig<Value> = Value extends Method<infer Config> ? Config extends
18
18
  type CallableInputValue<Value extends Method | Function> = CallableConfig<Value>['input'];
19
19
  type CallableOutputValue<Value extends Method | Function> = CallableConfig<Value>['output'];
20
20
  type CallableExecutionOf<Value extends Method | Function> = CallableConfig<Value>['execution'];
21
+ type CallableAdmissionOf<Value extends Method | Function> = CallableConfig<Value>['admission'];
21
22
  type CallableStaticOf<Value extends Method> = CallableConfig<Value> extends {
22
23
  readonly static: true;
23
24
  } ? true : false;
@@ -25,11 +26,11 @@ type CallableAbstractOf<Value extends Method> = CallableConfig<Value> extends {
25
26
  readonly abstract: infer Abstract extends boolean;
26
27
  } ? Abstract : false;
27
28
  type CallableAuthOf<Value extends Method | Function> = CallableConfig<Value>['auth'];
28
- 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>>, CallableExecutionOf<Value>> : never;
29
+ 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>>, CallableExecutionOf<Value>, CallableAdmissionOf<Value>> : never;
29
30
  type MethodProjectionOfEntries<Origin extends string, OwnerName extends string, Entries> = Entries extends unknown ? MethodProjectionOfEntry<Origin, OwnerName, Entries> : never;
30
31
  type MethodProjectionOfResolved<Value> = Value extends language.ResolvedMethod<infer Input, infer Output, infer Static, infer Abstract, infer Auth, infer _Policy, infer Key> & {
31
32
  readonly name: infer Name extends string;
32
- } ? MethodProjection<Input, Output, Static, Abstract, Auth, Name, Key, Value['executable'], Value['execution']> : never;
33
+ } ? MethodProjection<Input, Output, Static, Abstract, Auth, Name, Key, Value['executable'], Value['execution'], Value['admission']> : never;
33
34
  /** Capture each Method through its qualified lookup key. */
34
35
  export type MethodsOfCollection<Value> = Value extends {
35
36
  readonly byKey: infer ByKey extends Readonly<Record<language.MethodKey, language.ResolvedMethod>>;
@@ -41,11 +42,12 @@ export type OwnStaticMethodProjectionsOf<Origin extends string, Name extends str
41
42
  /** Bind an authored Function to its exact Domain coordinates. */
42
43
  export type FunctionProjectionOfAuthoring<Origin extends string, Name extends string, Value> = Value extends Function ? FunctionProjection<CallableInputValue<Value>, CallableOutputValue<Value>, CallableAuthOf<Value>, language.FunctionRef<Name> & {
43
44
  readonly origin: Origin;
44
- }, language.DefinitionKey<Origin, 'function', Name>, CallableExecutionOf<Value>> : never;
45
- /** Restore a resolved Function while preserving its execution modifier. */
46
- export type ResolvedFunctionOfProjection<Value extends FunctionProjection> = Value extends FunctionProjection<infer Input, infer Output, infer Auth, infer Ref, infer Key, infer Execution> ? language.ResolvedFunction<Input, Output, Auth, language.CallablePolicy> & {
45
+ }, language.DefinitionKey<Origin, 'function', Name>, CallableExecutionOf<Value>, CallableAdmissionOf<Value>> : never;
46
+ /** Restore a resolved Function while preserving its execution modifier and admission age. */
47
+ export type ResolvedFunctionOfProjection<Value extends FunctionProjection> = Value extends FunctionProjection<infer Input, infer Output, infer Auth, infer Ref, infer Key, infer Execution, infer Admission> ? language.ResolvedFunction<Input, Output, Auth, language.CallablePolicy> & {
47
48
  readonly ref: Ref;
48
49
  readonly key: Key;
49
50
  readonly execution: Execution;
51
+ readonly admission: Admission;
50
52
  } : never;
51
53
  export {};
@@ -10,7 +10,7 @@ export interface PropertyProjection<Value = unknown, Required extends boolean =
10
10
  readonly input: Input;
11
11
  }
12
12
  /** A Method’s exact callable, ownership and executable contracts. */
13
- 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, Execution extends language.CallableExecution | undefined = language.CallableExecution | undefined> {
13
+ 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, Execution extends language.CallableExecution | undefined = language.CallableExecution | undefined, Admission extends language.CallableAdmission | undefined = language.CallableAdmission | undefined> {
14
14
  readonly input: Input;
15
15
  readonly output: Output;
16
16
  readonly static: Static;
@@ -20,15 +20,17 @@ export interface MethodProjection<Input = unknown, Output = unknown, Static exte
20
20
  readonly name: Name;
21
21
  readonly key: Key;
22
22
  readonly execution: Execution;
23
+ readonly admission: Admission;
23
24
  }
24
25
  /** A Function’s exact callable and declaration contracts. */
25
- export interface FunctionProjection<Input = unknown, Output = unknown, Auth extends language.FunctionAuth = language.FunctionAuth, Ref extends language.FunctionRef = language.FunctionRef, Key extends language.DefinitionKey<string, 'function'> = language.DefinitionKey<string, 'function'>, Execution extends language.CallableExecution | undefined = language.CallableExecution | undefined> {
26
+ export interface FunctionProjection<Input = unknown, Output = unknown, Auth extends language.FunctionAuth = language.FunctionAuth, Ref extends language.FunctionRef = language.FunctionRef, Key extends language.DefinitionKey<string, 'function'> = language.DefinitionKey<string, 'function'>, Execution extends language.CallableExecution | undefined = language.CallableExecution | undefined, Admission extends language.CallableAdmission | undefined = language.CallableAdmission | undefined> {
26
27
  readonly input: Input;
27
28
  readonly output: Output;
28
29
  readonly auth: Auth;
29
30
  readonly ref: Ref;
30
31
  readonly key: Key;
31
32
  readonly execution: Execution;
33
+ readonly admission: Admission;
32
34
  }
33
35
  /** A compact Class witness with effective members and exact self facets. */
34
36
  export interface ClassProjection<Kind extends language.ClassKind = language.ClassKind, Definition extends Extract<language.ClassDefinition, {
@@ -56,10 +56,11 @@ type ProjectionPropertyByName<Entries extends PropertyProjection> = Readonly<{
56
56
  /** Restore qualified lookup and unambiguous named Property access. */
57
57
  type PropertyCollectionOfProjection<Entries extends PropertyProjection> = language.ResolvedPropertyCollection<ProjectionPropertyByKey<Entries>, ProjectionPropertyByName<Entries>>;
58
58
  type MethodCollectionOperation = 'byKey' | 'get';
59
- type ResolvedMethodOfProjection<Value> = Value extends MethodProjection<infer Input, infer Output, infer Static, infer Abstract, infer Auth, infer Name, infer Key, infer Executable, infer Execution> ? language.ResolvedMethod<Input, Output, Static, Abstract, Auth, language.CallablePolicy, Key> & {
59
+ type ResolvedMethodOfProjection<Value> = Value extends MethodProjection<infer Input, infer Output, infer Static, infer Abstract, infer Auth, infer Name, infer Key, infer Executable, infer Execution, infer Admission> ? language.ResolvedMethod<Input, Output, Static, Abstract, Auth, language.CallablePolicy, Key> & {
60
60
  readonly name: Name;
61
61
  readonly executable: Executable;
62
62
  readonly execution: Execution;
63
+ readonly admission: Admission;
63
64
  } : never;
64
65
  type ProjectionMethodByKey<Entries extends MethodProjection> = Readonly<{
65
66
  [Key in Entries['key']]: ResolvedMethodOfProjection<Extract<Entries, {
@@ -22,6 +22,8 @@ export function analyzeClasses(classes) {
22
22
  const parent = classes.get(Key.of(ref));
23
23
  if (parent === undefined)
24
24
  fail(`${located.pointer}/extends/${index}`, 'Parent Class is absent.');
25
+ if (parent.value.inheritance === 'final')
26
+ fail(`${located.pointer}/extends/${index}`, 'A final Class has no subclass.');
25
27
  return analyze(parent);
26
28
  });
27
29
  const properties = new Map();
@@ -116,9 +118,14 @@ export function validateClassFacets(context) {
116
118
  }
117
119
  if (located.value.kind === 'node') {
118
120
  for (const [index, method] of located.value.methods.entries()) {
119
- if (!isMethodExecutable(located.value, method) && method.policy !== undefined) {
121
+ if (isMethodExecutable(located.value, method))
122
+ continue;
123
+ if (method.policy !== undefined) {
120
124
  fail(`${located.pointer}/methods/${index}/policy`, 'An abstract Method cannot have a Policy.');
121
125
  }
126
+ if (method.admission !== undefined) {
127
+ fail(`${located.pointer}/methods/${index}/admission`, 'An abstract Method cannot have an admission age.');
128
+ }
122
129
  }
123
130
  }
124
131
  }
@@ -1,4 +1,5 @@
1
1
  import {} from '@astrale-os/kernel-dsl/value';
2
+ import { isAdmissionMaxAge } from '../../../language/callable/admission.js';
2
3
  import {} from '../../model/schema.js';
3
4
  import { invalid, pointer } from '../diagnostic.js';
4
5
  import { callablePolicy } from './policy.js';
@@ -16,7 +17,7 @@ export function compiledCallable(input, values, method, path) {
16
17
  const value = object(input, 'Compiled Callable', path);
17
18
  exact(value, method
18
19
  ? ['name', 'auth', 'static', 'abstract', 'input', 'output']
19
- : ['ref', 'auth', 'input', 'output'], 'Compiled Callable', path, ['policy', 'description', 'execution', 'fields']);
20
+ : ['ref', 'auth', 'input', 'output'], 'Compiled Callable', path, ['policy', 'description', 'execution', 'admission', 'fields']);
20
21
  if (method) {
21
22
  memberName(value.name, 'Method name', `${path}/name`);
22
23
  boolean(value.static, 'Method static', `${path}/static`);
@@ -87,4 +88,13 @@ export function compiledCallable(input, values, method, path) {
87
88
  invalid('durable Callable output', 'COMPILED_SOURCE_INVALID', `${outputPath}/mode`);
88
89
  }
89
90
  }
91
+ if (value.admission !== undefined) {
92
+ const admissionPath = `${path}/admission`;
93
+ const admission = object(value.admission, 'Callable admission', admissionPath);
94
+ exact(admission, ['maxAge'], 'Callable admission', admissionPath);
95
+ // An absent admission age is the only spelling of a call admitted afresh.
96
+ if (!isAdmissionMaxAge(admission.maxAge)) {
97
+ invalid('Callable admission age', 'COMPILED_SOURCE_INVALID', `${admissionPath}/maxAge`);
98
+ }
99
+ }
90
100
  }
@@ -25,9 +25,15 @@ export function compiledClass(input, values, path) {
25
25
  'constraints',
26
26
  'properties',
27
27
  'policies',
28
- ], 'Compiled Class', path, value.kind === 'node' ? ['data', 'description', 'icon'] : ['description']);
28
+ ], 'Compiled Class', path, value.kind === 'node'
29
+ ? ['data', 'description', 'icon', 'inheritance']
30
+ : ['description', 'inheritance', 'unique']);
29
31
  definitionRef(value.ref, 'class', 'Class ref', `${path}/ref`);
30
32
  boolean(value.abstract, 'Class abstract', `${path}/abstract`);
33
+ if (value.inheritance !== undefined &&
34
+ value.inheritance !== 'open' &&
35
+ value.inheritance !== 'final')
36
+ invalid('Class inheritance', undefined, `${path}/inheritance`);
31
37
  description(value.description, 'Class description', `${path}/description`);
32
38
  const extendsPath = `${path}/extends`;
33
39
  const parents = array(value.extends, 'Class extends', extendsPath).map((parent, index) => definitionRef(parent, 'class', 'Class parent', `${extendsPath}/${index}`));
@@ -93,9 +99,13 @@ function edgeContract(value, path) {
93
99
  object(endpoints.target, 'directed Edge target', `${endpointsPath}/target`).role) {
94
100
  invalid('Edge endpoint roles', undefined, endpointsPath);
95
101
  }
102
+ if (value.unique !== undefined)
103
+ uniqueTuples(value.unique, `${path}/unique`);
96
104
  return;
97
105
  }
98
- if (value.orientation !== 'undirected' || constraints.acyclic !== undefined) {
106
+ if (value.orientation !== 'undirected' ||
107
+ constraints.acyclic !== undefined ||
108
+ value.unique !== undefined) {
99
109
  invalid('Edge orientation', undefined, `${path}/orientation`);
100
110
  }
101
111
  const endpointsPath = `${path}/endpoints`;
@@ -109,6 +119,18 @@ function edgeContract(value, path) {
109
119
  invalid('Edge endpoint roles', undefined, endpointsPath);
110
120
  }
111
121
  }
122
+ /** A nonempty list of distinct tuples, each naming the source and then the slug. */
123
+ function uniqueTuples(input, path) {
124
+ const tuples = array(input, 'Edge unique tuples', path);
125
+ if (tuples.length === 0)
126
+ invalid('Edge unique tuples', undefined, path);
127
+ for (const [index, tuple] of tuples.entries()) {
128
+ const fields = array(tuple, 'Edge unique tuple', `${path}/${index}`);
129
+ if (fields.length !== 2 || fields[0] !== 'source' || fields[1] !== 'slug')
130
+ invalid('Edge unique tuple', undefined, `${path}/${index}`);
131
+ }
132
+ unique(tuples.map((tuple) => JSON.stringify(tuple)), 'Edge unique tuples', path);
133
+ }
112
134
  function edgeEndpoint(input, count, label, path) {
113
135
  const value = object(input, label, path);
114
136
  exact(value, ['role', 'accepts', count], label, path);
@@ -89,6 +89,7 @@ function compileEntry(schema, lookup) {
89
89
  static: method.static,
90
90
  abstract: method.abstract,
91
91
  ...(method.execution === undefined ? {} : { execution: method.execution }),
92
+ ...(method.admission === undefined ? {} : { admission: method.admission }),
92
93
  input,
93
94
  ...(fields === undefined ? {} : { fields }),
94
95
  output: output(method.output, `${pointer}/methods/${name}/output`),
@@ -117,6 +118,7 @@ function compileEntry(schema, lookup) {
117
118
  ...(definition.policy === undefined ? {} : { policy: definition.policy }),
118
119
  ...(definition.description === undefined ? {} : { description: definition.description }),
119
120
  ...(definition.execution === undefined ? {} : { execution: definition.execution }),
121
+ ...(definition.admission === undefined ? {} : { admission: definition.admission }),
120
122
  input,
121
123
  ...(fields === undefined ? {} : { fields }),
122
124
  output: output(definition.output, `/functions/${definition.ref.name}/output`),
@@ -130,6 +130,7 @@ function createDefinitions(declarations, classesByKey, input) {
130
130
  input: definition.input,
131
131
  output: definition.output,
132
132
  ...(definition.execution === undefined ? {} : { execution: definition.execution }),
133
+ ...(definition.admission === undefined ? {} : { admission: definition.admission }),
133
134
  auth: definition.auth,
134
135
  policy: definition.policy,
135
136
  ...(inputFields === undefined ? {} : { inputFields }),
@@ -70,6 +70,9 @@ export function createClasses(definitions, validation) {
70
70
  ...(methodDefinition.execution === undefined
71
71
  ? {}
72
72
  : { execution: methodDefinition.execution }),
73
+ ...(methodDefinition.admission === undefined
74
+ ? {}
75
+ : { admission: methodDefinition.admission }),
73
76
  auth: methodDefinition.auth,
74
77
  policy: methodDefinition.policy,
75
78
  static: methodDefinition.static,
@@ -5,7 +5,7 @@ export { isBinaryInput } from './language/callable/model/callable.js';
5
5
  export type { EventOf, StateMachine, StateOf } from './language/state/index.js';
6
6
  export { PropertyValues } from './language/index.js';
7
7
  export * as metadata from './language/definition/metadata.js';
8
- export type { AnyResolvedClass, BoundMethodOf, BoundMethodShortcutsOf, CallableResultOf, CallableInputOf, CallableInputModeOf, CallableOutputOf, ClassSatisfactionKeysOf, ClassDefinition, CoreDefinition, CoreEdge, FunctionAuth, FunctionDefinition, BinaryInput, BinaryInputProjection, CallableInputContract, MethodDefinition, MethodOwnerKeyOf, CallablePolicy, ClassPolicy, ClassRuleChecks, ClassRuleObject, NodeClassPolicy, PolicyCheckExpression, PolicyDefinition, PropertyImmutability, PropertyInputOf, PropertyAccessOf, PropertyOutputOf, PropertyShortcutsOf, PropertyValuesOf, ResolvedClass, ResolvedCoreDefinition, ResolvedCoreEndpoint, ResolvedData, ResolvedFunction, ExecutableMethod, ResolvedMethod, ResolvedMethodCollection, ResolvedPolicy, ResolvedProperty, ResolvedView, MethodAccessOf, SelfMethodsOf, SelfPropertiesOf, SelfStaticMethodsOf, StaticMethodsOf, ViewDefinition, } from './language/index.js';
8
+ export type { AnyResolvedClass, BoundMethodOf, BoundMethodShortcutsOf, CallableResultOf, CallableInputOf, CallableInputModeOf, CallableOutputOf, ClassSatisfactionKeysOf, ClassDefinition, CoreDefinition, CoreEdge, FunctionAuth, FunctionDefinition, Inheritance, BinaryInput, BinaryInputProjection, CallableInputContract, MethodDefinition, MethodOwnerKeyOf, CallablePolicy, ClassPolicy, ClassRuleChecks, ClassRuleObject, NodeClassPolicy, PolicyCheckExpression, PolicyDefinition, PropertyImmutability, PropertyInputOf, PropertyAccessOf, PropertyOutputOf, PropertyShortcutsOf, PropertyValuesOf, ResolvedClass, ResolvedCoreDefinition, ResolvedCoreEndpoint, ResolvedData, ResolvedFunction, ExecutableMethod, ResolvedMethod, ResolvedMethodCollection, ResolvedPolicy, ResolvedProperty, ResolvedView, MethodAccessOf, SelfMethodsOf, SelfPropertiesOf, SelfStaticMethodsOf, StaticMethodsOf, ViewDefinition, } from './language/index.js';
9
9
  export { acceptDefinitionRef, callableAddress, ClassKey, equalDefinitionRefs, isDefinitionRef, Key, MethodKey, PropertyKey, } from './addressing/index.js';
10
10
  export type { ClassRef, CoreRef, DefinitionBase, DefinitionKey, DefinitionRef, FunctionRef, MethodAddress, PolicyRef, Revision, ViewRef, } from './addressing/index.js';
11
11
  export type { Definition } from './language/index.js';
@@ -0,0 +1,15 @@
1
+ import type { CallablePolicy } from '../policy/model/policy.js';
2
+ /**
3
+ * The language ceiling of a declared admission age, in seconds. Raising it later widens the
4
+ * language; lowering it would refuse accepted Schemas.
5
+ */
6
+ export declare const MAXIMUM_ADMISSION_AGE_SECONDS = 300;
7
+ /** Whether `value` is a declarable admission age: an integer from 1 to the language ceiling. */
8
+ export declare function isAdmissionMaxAge(value: unknown): value is number;
9
+ /**
10
+ * The input fields an admission of this callable reads: the `field` of every `input` check object
11
+ * of its Policy, unique and in Unicode code-point order. A callable without Policy checks reads no
12
+ * input field. Two calls that agree on these fields, and on everything else the admission reads,
13
+ * receive the same admission.
14
+ */
15
+ export declare function admissionInputFields(policy: CallablePolicy | undefined): readonly string[];
@@ -0,0 +1,43 @@
1
+ import { compareUnicode } from '../../addressing/ordering.js';
2
+ /**
3
+ * The language ceiling of a declared admission age, in seconds. Raising it later widens the
4
+ * language; lowering it would refuse accepted Schemas.
5
+ */
6
+ export const MAXIMUM_ADMISSION_AGE_SECONDS = 300;
7
+ /** Whether `value` is a declarable admission age: an integer from 1 to the language ceiling. */
8
+ export function isAdmissionMaxAge(value) {
9
+ return (typeof value === 'number' &&
10
+ Number.isInteger(value) &&
11
+ value >= 1 &&
12
+ value <= MAXIMUM_ADMISSION_AGE_SECONDS);
13
+ }
14
+ /**
15
+ * The input fields an admission of this callable reads: the `field` of every `input` check object
16
+ * of its Policy, unique and in Unicode code-point order. A callable without Policy checks reads no
17
+ * input field. Two calls that agree on these fields, and on everything else the admission reads,
18
+ * receive the same admission.
19
+ */
20
+ export function admissionInputFields(policy) {
21
+ if (policy === undefined || policy === true)
22
+ return Object.freeze([]);
23
+ const fields = new Set();
24
+ collectInputFields(policy, fields);
25
+ return Object.freeze([...fields].sort(compareUnicode));
26
+ }
27
+ function collectInputFields(expression, fields) {
28
+ if ('check' in expression) {
29
+ if (expression.object.kind === 'input')
30
+ fields.add(expression.object.field);
31
+ return;
32
+ }
33
+ if ('sameNode' in expression) {
34
+ for (const object of [expression.sameNode.left, expression.sameNode.right]) {
35
+ if (object.kind === 'input')
36
+ fields.add(object.field);
37
+ }
38
+ return;
39
+ }
40
+ for (const child of 'allOf' in expression ? expression.allOf : expression.anyOf) {
41
+ collectInputFields(child, fields);
42
+ }
43
+ }
@@ -21,6 +21,15 @@ export type OutputMode = CallableOutput['mode'];
21
21
  export interface CallableExecution {
22
22
  readonly durable: boolean;
23
23
  }
24
+ /**
25
+ * Admission age of a callable: how old, in whole seconds, the admission decision used for one of its
26
+ * calls may be. A destination may reuse an admission it received within that age; a Runtime that
27
+ * never reuses one conforms. Absent means every call is admitted afresh, and `maxAge` is an integer
28
+ * from 1 to `MAXIMUM_ADMISSION_AGE_SECONDS`.
29
+ */
30
+ export interface CallableAdmission {
31
+ readonly maxAge: number;
32
+ }
24
33
  /** A binary Function receives portable metadata and one required byte stream. */
25
34
  export interface BinaryInput {
26
35
  readonly mode: 'binary';
@@ -39,6 +48,7 @@ export interface CallableDefinitionBase<Input extends CallableInputContract = Ca
39
48
  readonly input: Input;
40
49
  readonly output: CallableOutput;
41
50
  readonly execution?: CallableExecution;
51
+ readonly admission?: CallableAdmission;
42
52
  }
43
53
  export type CallableDefinition<Input extends CallableInputContract = CallableInputContract> = CallableDefinitionBase<Input> & ({
44
54
  readonly auth: 'anonymous' | 'authenticated';
@@ -2,7 +2,7 @@ import type { DefinitionKey, FunctionRef, Key, MethodKey } from '../../addressin
2
2
  import type { ResolvedClass } from '../class/resolved.js';
3
3
  import type { CallablePolicy } from '../policy/model/policy.js';
4
4
  import type { Diagnostic } from '../validation/diagnostic.js';
5
- import type { BinaryInputProjection, CallableExecution, FunctionDefinition, MethodDefinition } from './model/callable.js';
5
+ import type { BinaryInputProjection, CallableAdmission, CallableExecution, FunctionDefinition, MethodDefinition } from './model/callable.js';
6
6
  import type { FunctionAuth } from './model/callable.js';
7
7
  type CallableDefinitionOf<Definition extends FunctionDefinition | MethodDefinition, Auth extends FunctionAuth, Policy extends CallablePolicy> = Omit<Definition, 'auth' | 'policy'> & (Auth extends FunctionAuth ? {
8
8
  readonly auth: Auth;
@@ -22,6 +22,8 @@ export interface ResolvedMethod<out Input = unknown, out Output = unknown, out S
22
22
  readonly output: MethodDefinition['output'];
23
23
  /** Present only for a durable Method. */
24
24
  readonly execution?: CallableExecution;
25
+ /** Present only when the Method declares an admission age. */
26
+ readonly admission?: CallableAdmission;
25
27
  readonly auth: Auth;
26
28
  readonly policy?: Auth extends 'authorized' ? Policy : never;
27
29
  readonly static: Static;
@@ -53,6 +55,8 @@ export interface ResolvedFunction<out Input = unknown, out Output = unknown, out
53
55
  readonly output: FunctionDefinition['output'];
54
56
  /** Present only for a durable Function. */
55
57
  readonly execution?: CallableExecution;
58
+ /** Present only when the Function declares an admission age. */
59
+ readonly admission?: CallableAdmission;
56
60
  readonly auth: Auth;
57
61
  readonly policy?: Auth extends 'authorized' ? Policy : never;
58
62
  /** Present only when the callable input is a safely enumerable closed object. */
@@ -1,12 +1,16 @@
1
1
  import type { ClassRef } from '../../addressing/index.js';
2
- import type { ClassKind } from './model/class.js';
3
- /** The representation shared by source, compiled and resolved Class admission. */
4
- export interface ClassInheritance {
2
+ import type { ClassKind, Inheritance } from './model/class.js';
3
+ /** A Class as satisfaction reads it, shared by source, compiled and resolved Class admission. */
4
+ export interface ClassAncestry {
5
5
  readonly ref: ClassRef;
6
6
  readonly kind: ClassKind;
7
7
  readonly extends: readonly ClassRef[];
8
8
  }
9
9
  /** Kernel graph roots are intrinsic supertypes, not authored structural Edge contracts. */
10
- export declare function isGraphRoot(value: Pick<ClassInheritance, 'ref' | 'kind'>): boolean;
10
+ export declare function isGraphRoot(value: Pick<ClassAncestry, 'ref' | 'kind'>): boolean;
11
11
  /** Same-kind satisfaction includes the intrinsic root and the declared parent closure. */
12
- export declare function classSatisfies(candidate: ClassInheritance, required: ClassInheritance, resolve: (ref: ClassRef) => ClassInheritance | undefined): boolean;
12
+ export declare function classSatisfies(candidate: ClassAncestry, required: ClassAncestry, resolve: (ref: ClassRef) => ClassAncestry | undefined): boolean;
13
+ /** The inheritance a Class declares, `'internal'` when it declares none. */
14
+ export declare function inheritanceOf(declaration: {
15
+ readonly inheritance?: Inheritance;
16
+ }): Inheritance;
@@ -23,3 +23,7 @@ export function classSatisfies(candidate, required, resolve) {
23
23
  };
24
24
  return visit(candidate);
25
25
  }
26
+ /** The inheritance a Class declares, `'internal'` when it declares none. */
27
+ export function inheritanceOf(declaration) {
28
+ return declaration.inheritance ?? 'internal';
29
+ }