@astrale-os/kernel-dsl 1.0.0-beta.1 → 1.0.0-beta.11

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 (81) hide show
  1. package/dist/v1/addressing/class-key.js +3 -3
  2. package/dist/v1/addressing/key.d.ts +4 -1
  3. package/dist/v1/addressing/key.js +21 -2
  4. package/dist/v1/builder/callable/authoring.d.ts +10 -4
  5. package/dist/v1/builder/callable/authoring.js +2 -0
  6. package/dist/v1/builder/class/authoring.d.ts +36 -1
  7. package/dist/v1/builder/class/authoring.js +25 -5
  8. package/dist/v1/builder/index.d.ts +1 -1
  9. package/dist/v1/builder/lowering/callable.js +1 -0
  10. package/dist/v1/builder/lowering/class.js +18 -1
  11. package/dist/v1/builder/lowering/property-keys.d.ts +27 -0
  12. package/dist/v1/builder/lowering/property-keys.js +108 -0
  13. package/dist/v1/builder/lowering/schema/compile.js +8 -3
  14. package/dist/v1/builder/lowering/schema/context.d.ts +2 -0
  15. package/dist/v1/builder/lowering/schema/context.js +12 -0
  16. package/dist/v1/builder/lowering/schema/lifetime.d.ts +11 -0
  17. package/dist/v1/builder/lowering/schema/lifetime.js +55 -0
  18. package/dist/v1/builder/schema/diagnostic.d.ts +1 -1
  19. package/dist/v1/builder/schema/projection/callable.d.ts +7 -5
  20. package/dist/v1/builder/schema/projection/model.d.ts +4 -2
  21. package/dist/v1/builder/schema/projection/resolved-class.d.ts +2 -1
  22. package/dist/v1/compiled/admission/semantics/cardinality.d.ts +1 -1
  23. package/dist/v1/compiled/admission/semantics/cardinality.js +16 -1
  24. package/dist/v1/compiled/admission/semantics/class.js +31 -1
  25. package/dist/v1/compiled/admission/semantics/core.js +16 -0
  26. package/dist/v1/compiled/admission/semantics/policy/parity.js +3 -2
  27. package/dist/v1/compiled/admission/wire/callable.js +11 -1
  28. package/dist/v1/compiled/admission/wire/class.js +50 -3
  29. package/dist/v1/compiled/compiler/compile.js +2 -0
  30. package/dist/v1/domain/create.js +1 -0
  31. package/dist/v1/domain/projection/classes.js +3 -0
  32. package/dist/v1/index.d.ts +2 -1
  33. package/dist/v1/language/callable/admission.d.ts +15 -0
  34. package/dist/v1/language/callable/admission.js +43 -0
  35. package/dist/v1/language/callable/model/callable.d.ts +10 -0
  36. package/dist/v1/language/callable/resolved.d.ts +5 -1
  37. package/dist/v1/language/class/inheritance.d.ts +18 -5
  38. package/dist/v1/language/class/inheritance.js +14 -1
  39. package/dist/v1/language/class/lifetime.d.ts +50 -11
  40. package/dist/v1/language/class/lifetime.js +89 -20
  41. package/dist/v1/language/class/model/class.d.ts +27 -3
  42. package/dist/v1/language/class/model/edge.d.ts +30 -1
  43. package/dist/v1/language/class/unique.d.ts +143 -0
  44. package/dist/v1/language/class/unique.js +264 -0
  45. package/dist/v1/language/index.d.ts +6 -2
  46. package/dist/v1/language/index.js +4 -1
  47. package/dist/v1/language/value/evaluate/validate.js +4 -0
  48. package/dist/v1/language/value/evaluate/validity.optimization.d.ts +14 -0
  49. package/dist/v1/language/value/evaluate/validity.optimization.js +342 -0
  50. package/dist/v1/language/value/profile/canonicalize.js +6 -5
  51. package/dist/v1/schema/compatibility/api.d.ts +1 -0
  52. package/dist/v1/schema/compatibility/api.js +1 -0
  53. package/dist/v1/schema/compatibility/comparators/classes.d.ts +9 -4
  54. package/dist/v1/schema/compatibility/comparators/classes.js +25 -4
  55. package/dist/v1/schema/compatibility/comparators/index.d.ts +1 -1
  56. package/dist/v1/schema/compatibility/extension.d.ts +11 -0
  57. package/dist/v1/schema/compatibility/extension.js +22 -0
  58. package/dist/v1/schema/compatibility/fields.d.ts +20 -4
  59. package/dist/v1/schema/compatibility/fields.js +13 -3
  60. package/dist/v1/schema/compatibility/model.d.ts +11 -3
  61. package/dist/v1/schema/compatibility/scope.d.ts +2 -0
  62. package/dist/v1/schema/compatibility/scope.js +4 -1
  63. package/dist/v1/schema/compatibility/structure/index.js +6 -4
  64. package/dist/v1/schema/compatibility/structure/requirements.js +32 -1
  65. package/dist/v1/schema/compatibility/versioning/surface.d.ts +1 -1
  66. package/dist/v1/schema/compatibility/versioning/surface.js +30 -2
  67. package/dist/v1/schema/diagnostic.d.ts +1 -1
  68. package/dist/v1/schema/normalization/domain.js +18 -0
  69. package/dist/v1/schema/validation/callable.d.ts +1 -1
  70. package/dist/v1/schema/validation/callable.js +17 -2
  71. package/dist/v1/schema/validation/class.js +80 -2
  72. package/dist/v1/schema/validation/core.js +50 -0
  73. package/dist/v1/schema/validation/domain.structure.js +45 -10
  74. package/dist/v1/schema/validation/policy/uses.js +5 -5
  75. package/dist/value/canonical.d.ts +5 -0
  76. package/dist/value/canonical.js +12 -0
  77. package/dist/value/json.d.ts +1 -1
  78. package/dist/value/json.js +1 -1
  79. package/package.json +1 -1
  80. package/dist/v1/builder/lowering/core-properties.d.ts +0 -10
  81. package/dist/v1/builder/lowering/core-properties.js +0 -76
@@ -0,0 +1,11 @@
1
+ import type { DomainSchemaEntry } from '../../../schema/dependency/lookup.js';
2
+ import type { DomainSchema } from '../../../schema/model/schema.js';
3
+ /**
4
+ * Authoring refuses two Edge Classes that acceptance keeps, because a stored Schema must decode
5
+ * again with the lifetimes it derives (BUILDER-EDGE-LIFETIMES):
6
+ * - one with an endpoint that can hold a Node of another Domain while an endpoint requires an Edge.
7
+ * It can neither restrict that Domain's deletions nor detach below the required count, so the
8
+ * required endpoint must become optional;
9
+ * - one whose lifetimes differ from those of an Edge Class it extends, the root Edge aside.
10
+ */
11
+ export declare function assertAuthoredLifetimes(schema: DomainSchema, dependencies: readonly DomainSchemaEntry[]): void;
@@ -0,0 +1,55 @@
1
+ import { Key } from '../../../addressing/key.js';
2
+ import { definitionRefIndex } from '../../../addressing/ordering.js';
3
+ import { isGraphRoot } from '../../../language/class/inheritance.js';
4
+ import { contractEndpoints, edgeLifetimes, lifetimeScope, reachesAnotherDomain, requiredEndpoint, } from '../../../language/class/lifetime.js';
5
+ import { BuilderError, diagnostic, escapeToken } from '../../schema/diagnostic.js';
6
+ /**
7
+ * Authoring refuses two Edge Classes that acceptance keeps, because a stored Schema must decode
8
+ * again with the lifetimes it derives (BUILDER-EDGE-LIFETIMES):
9
+ * - one with an endpoint that can hold a Node of another Domain while an endpoint requires an Edge.
10
+ * It can neither restrict that Domain's deletions nor detach below the required count, so the
11
+ * required endpoint must become optional;
12
+ * - one whose lifetimes differ from those of an Edge Class it extends, the root Edge aside.
13
+ */
14
+ export function assertAuthoredLifetimes(schema, dependencies) {
15
+ const classes = [
16
+ ...Object.values(schema.classes),
17
+ ...dependencies.flatMap((entry) => Object.values(entry.schema.classes)),
18
+ ];
19
+ const scope = lifetimeScope(classes);
20
+ const declarations = new Map(classes.map((declaration) => [definitionRefIndex(declaration.ref), declaration]));
21
+ const diagnostics = Object.entries(schema.classes).flatMap(([name, declaration]) => {
22
+ if (declaration.kind !== 'edge')
23
+ return [];
24
+ const pointer = `/classes/${escapeToken(name)}`;
25
+ const endpoints = contractEndpoints(declaration);
26
+ const keys = declaration.orientation === 'directed' ? ['source', 'target'] : ['0', '1'];
27
+ const counts = declaration.orientation === 'directed' ? ['outgoing', 'incoming'] : ['incident', 'incident'];
28
+ const found = [];
29
+ const required = requiredEndpoint(declaration);
30
+ const reaching = endpoints.find(({ endpoint }) => reachesAnotherDomain(schema.origin, endpoint, scope));
31
+ if (required !== undefined && reaching !== undefined)
32
+ found.push(diagnostic('BUILDER_EDGE_LIFETIME', `${pointer}/endpoints/${keys[required.index]}/${counts[required.index]}`, `${Key.of(declaration.ref)} endpoint ${reaching.endpoint.role} can hold Nodes of another ` +
33
+ `Domain, so it cannot restrict their deletion, and endpoint ${required.endpoint.role} ` +
34
+ `requires ${required.count} Edges, so it cannot detach: make ${required.endpoint.role} optional.`));
35
+ const lifetimes = edgeLifetimes(declaration, scope);
36
+ declaration.extends.forEach((ref, index) => {
37
+ const parent = declarations.get(definitionRefIndex(ref));
38
+ if (parent?.kind !== 'edge' || isGraphRoot(parent))
39
+ return;
40
+ const inherited = edgeLifetimes(parent, scope);
41
+ const same = (order) => order.every((at, position) => lifetimes[position] === inherited[at]);
42
+ if (same([0, 1]) || (declaration.orientation === 'undirected' && same([1, 0])))
43
+ return;
44
+ found.push(diagnostic('BUILDER_EDGE_LIFETIME', `${pointer}/extends/${index}`, `${Key.of(declaration.ref)} has lifetimes ${spell(endpoints, lifetimes)}, and ` +
45
+ `${Key.of(parent.ref)} it extends has ${spell(contractEndpoints(parent), inherited)}: ` +
46
+ `an Edge Class keeps the lifetimes of the Classes it extends.`));
47
+ });
48
+ return found;
49
+ });
50
+ if (diagnostics.length > 0)
51
+ throw new BuilderError(diagnostics);
52
+ }
53
+ function spell(endpoints, lifetimes) {
54
+ return `${endpoints[0].endpoint.role} ${lifetimes[0]} and ${endpoints[1].endpoint.role} ${lifetimes[1]}`;
55
+ }
@@ -1,5 +1,5 @@
1
1
  import type { DiagnosticCode as LanguageDiagnosticCode } from '../../language/validation/diagnostic.js';
2
- export type BuilderDiagnosticCode = LanguageDiagnosticCode | 'BUILDER_ABSTRACT_POLICY' | 'BUILDER_AMBIGUOUS_OWNERSHIP' | 'BUILDER_AUTO_EVENT_INVALID' | 'BUILDER_AUTO_IMMUTABLE' | 'BUILDER_AUTO_NESTED' | 'BUILDER_AUTO_SET_INVALID' | 'BUILDER_CALLABLE_AUTH' | 'BUILDER_CALLABLE_PLACEMENT' | 'BUILDER_CALLABLE_POLICY_AUTH' | 'BUILDER_CORE_CLASS' | 'BUILDER_CORE_EDGE' | 'BUILDER_CORE_NODE' | 'BUILDER_CORE_PROPERTY' | 'BUILDER_CORE_REF' | 'BUILDER_DEFINITION' | 'BUILDER_DEPENDENCY_CONFLICT' | 'BUILDER_DEPENDENCY_ALIAS' | 'BUILDER_DEPENDENCY_DIRECT' | 'BUILDER_DUPLICATE_MEMBER' | 'BUILDER_FOREIGN_POLICY' | 'BUILDER_FULL_MATCH_INVALID' | 'BUILDER_FUNCTION' | 'BUILDER_METHOD' | 'BUILDER_PATH_DUPLICATE' | 'BUILDER_PATH_EMPTY' | 'BUILDER_POLICY' | 'BUILDER_POLICY_COMPOSITION' | 'BUILDER_POLICY_CYCLE' | 'BUILDER_POLICY_EMPTY_COMPOSITION' | 'BUILDER_POLICY_EMPTY_EXISTS' | 'BUILDER_POLICY_INPUT' | 'BUILDER_POLICY_REF' | 'BUILDER_POLICY_SELF' | 'BUILDER_PROPERTY_IMMUTABLE_INVALID' | 'BUILDER_REFERENCE_CYCLE' | 'BUILDER_REFERENCE_KIND' | 'BUILDER_REFERENCE_MISSING' | 'BUILDER_UNKNOWN_FIELD' | 'BUILDER_UNOWNED_REFERENCE' | 'BUILDER_VIEW' | 'ZOD_SCHEMA_NOT_LOSSLESS';
2
+ export type BuilderDiagnosticCode = LanguageDiagnosticCode | 'BUILDER_ABSTRACT_POLICY' | 'BUILDER_AMBIGUOUS_OWNERSHIP' | 'BUILDER_AUTO_EVENT_INVALID' | 'BUILDER_AUTO_IMMUTABLE' | 'BUILDER_AUTO_NESTED' | 'BUILDER_AUTO_SET_INVALID' | 'BUILDER_CALLABLE_AUTH' | 'BUILDER_CALLABLE_PLACEMENT' | 'BUILDER_CALLABLE_POLICY_AUTH' | 'BUILDER_CLASS_INHERITANCE' | 'BUILDER_CORE_CLASS' | 'BUILDER_CORE_EDGE' | 'BUILDER_CORE_NODE' | 'BUILDER_CORE_PROPERTY' | 'BUILDER_CORE_REF' | 'BUILDER_DEFINITION' | 'BUILDER_DEPENDENCY_CONFLICT' | 'BUILDER_DEPENDENCY_ALIAS' | 'BUILDER_DEPENDENCY_DIRECT' | 'BUILDER_DUPLICATE_MEMBER' | 'BUILDER_EDGE_LIFETIME' | 'BUILDER_FOREIGN_POLICY' | 'BUILDER_FULL_MATCH_INVALID' | 'BUILDER_FUNCTION' | 'BUILDER_METHOD' | 'BUILDER_PATH_DUPLICATE' | 'BUILDER_PATH_EMPTY' | 'BUILDER_POLICY' | 'BUILDER_POLICY_COMPOSITION' | 'BUILDER_POLICY_CYCLE' | 'BUILDER_POLICY_EMPTY_COMPOSITION' | 'BUILDER_POLICY_EMPTY_EXISTS' | 'BUILDER_POLICY_INPUT' | 'BUILDER_POLICY_REF' | 'BUILDER_POLICY_SELF' | 'BUILDER_PROPERTY_IMMUTABLE_INVALID' | 'BUILDER_REFERENCE_CYCLE' | 'BUILDER_REFERENCE_KIND' | 'BUILDER_REFERENCE_MISSING' | 'BUILDER_UNIQUE_PROPERTY' | 'BUILDER_UNKNOWN_FIELD' | 'BUILDER_UNOWNED_REFERENCE' | 'BUILDER_VIEW' | 'ZOD_SCHEMA_NOT_LOSSLESS';
3
3
  export interface BuilderDiagnostic<out Code extends BuilderDiagnosticCode = BuilderDiagnosticCode> {
4
4
  readonly code: Code;
5
5
  readonly pointer: string;
@@ -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, {
@@ -1,5 +1,5 @@
1
1
  import { type CompiledSchemaEntry } from '../../model/schema.js';
2
2
  import { type SemanticIndex } from './context.js';
3
3
  import { type CoreNode, type CoreEdgeValue } from './core.js';
4
- /** Check normalized Edge uniqueness, noSelf, cycles and complete Core degrees. */
4
+ /** Check normalized Edge identity, keys, noSelf, cycles and complete Core degrees. */
5
5
  export declare function validateCoreEdgeSet(context: SemanticIndex, entry: CompiledSchemaEntry, entryIndex: number, nodes: ReadonlyMap<string, CoreNode>, edges: readonly CoreEdgeValue[]): void;
@@ -1,12 +1,13 @@
1
1
  import { Key } from '../../../addressing/index.js';
2
2
  import { compareUnicode } from '../../../addressing/ordering.js';
3
3
  import {} from '../../../language/class/model/edge.js';
4
+ import { coveringEdgeUniqueTuples, edgeUniqueKeys } from '../../../language/class/unique.js';
4
5
  import {} from '../../model/schema.js';
5
6
  import { fail } from '../diagnostic.js';
6
7
  import { isCompiledEdge, classSatisfiesAny, } from './context.js';
7
8
  import {} from './core.js';
8
9
  import { sameIncidentEndpoint, countBounds } from './edge.js';
9
- /** Check normalized Edge uniqueness, noSelf, cycles and complete Core degrees. */
10
+ /** Check normalized Edge identity, keys, noSelf, cycles and complete Core degrees. */
10
11
  export function validateCoreEdgeSet(context, entry, entryIndex, nodes, edges) {
11
12
  const seen = new Set();
12
13
  const slices = new Map();
@@ -26,6 +27,20 @@ export function validateCoreEdgeSet(context, entry, entryIndex, nodes, edges) {
26
27
  if (edge.class.value.constraints.noSelf === true && edge.source.key === edge.target.key)
27
28
  fail(edge.pointer, 'Core Edge violates noSelf.');
28
29
  }
30
+ // No two Core Edges hold one key under a tuple covering their Classes (DV-EDGE-UNIQUE).
31
+ const resolve = (ref) => context.classes.get(Key.of(ref))?.value;
32
+ const holders = new Set();
33
+ for (const edge of edges) {
34
+ const keys = edgeUniqueKeys(coveringEdgeUniqueTuples(edge.class.value, resolve), {
35
+ source: edge.source.key,
36
+ target: edge.target.key,
37
+ props: edge.value.properties,
38
+ });
39
+ if (keys.some((key) => holders.has(key)))
40
+ fail(edge.pointer, 'Core Edge holds a key another Core Edge holds.');
41
+ for (const key of keys)
42
+ holders.add(key);
43
+ }
29
44
  for (const edgeClass of entry.definitions.classes) {
30
45
  if (!isCompiledEdge(edgeClass))
31
46
  continue;
@@ -2,6 +2,7 @@ import { json } from '@astrale-os/kernel-dsl/value';
2
2
  import { Key, PropertyKey as PropertyKeyValue, } from '../../../addressing/index.js';
3
3
  import { composeMethods } from '../../../language/callable/composition.js';
4
4
  import { isMethodExecutable } from '../../../language/callable/executable.js';
5
+ import { keyableProperty } from '../../../language/class/unique.js';
5
6
  import { bindValueProgram } from '../../../language/value/compile/load.js';
6
7
  import {} from '../../model/schema.js';
7
8
  import { fail } from '../diagnostic.js';
@@ -22,6 +23,8 @@ export function analyzeClasses(classes) {
22
23
  const parent = classes.get(Key.of(ref));
23
24
  if (parent === undefined)
24
25
  fail(`${located.pointer}/extends/${index}`, 'Parent Class is absent.');
26
+ if (parent.value.inheritance === 'final')
27
+ fail(`${located.pointer}/extends/${index}`, 'A final Class has no subclass.');
25
28
  return analyze(parent);
26
29
  });
27
30
  const properties = new Map();
@@ -114,11 +117,38 @@ export function validateClassFacets(context) {
114
117
  }
115
118
  }
116
119
  }
120
+ if (located.value.unique !== undefined) {
121
+ const properties = context.analyses.get(Key.of(located.value.ref))?.properties;
122
+ const keyable = (key, at) => {
123
+ const property = properties?.get(key)?.declaration;
124
+ if (property === undefined || !keyableProperty(property.ordering, property.auto)) {
125
+ fail(at, 'A key Property is a keyable Property of the Class.');
126
+ }
127
+ };
128
+ // A Node tuple names Properties only; an Edge tuple names them as `{ property }`.
129
+ const value = located.value;
130
+ if (value.kind === 'node') {
131
+ for (const [t, tuple] of (value.unique ?? []).entries())
132
+ for (const [f, key] of tuple.entries())
133
+ keyable(key, `${located.pointer}/unique/${t}/${f}`);
134
+ }
135
+ else {
136
+ for (const [t, tuple] of (value.unique ?? []).entries())
137
+ for (const [f, field] of tuple.entries())
138
+ if (typeof field !== 'string')
139
+ keyable(field.property, `${located.pointer}/unique/${t}/${f}/property`);
140
+ }
141
+ }
117
142
  if (located.value.kind === 'node') {
118
143
  for (const [index, method] of located.value.methods.entries()) {
119
- if (!isMethodExecutable(located.value, method) && method.policy !== undefined) {
144
+ if (isMethodExecutable(located.value, method))
145
+ continue;
146
+ if (method.policy !== undefined) {
120
147
  fail(`${located.pointer}/methods/${index}/policy`, 'An abstract Method cannot have a Policy.');
121
148
  }
149
+ if (method.admission !== undefined) {
150
+ fail(`${located.pointer}/methods/${index}/admission`, 'An abstract Method cannot have an admission age.');
151
+ }
122
152
  }
123
153
  }
124
154
  }
@@ -1,5 +1,6 @@
1
1
  import { Key } from '../../../addressing/index.js';
2
2
  import { isMethodExecutable } from '../../../language/callable/executable.js';
3
+ import { coveringNodeUniqueTuples, nodeUniqueKeys } from '../../../language/class/unique.js';
3
4
  import {} from '../../../language/core/model/core.js';
4
5
  import { policyObjectOrigin, policyObjectKey, projectedMemberClass, } from '../../../language/index.js';
5
6
  import { bindValueProgram } from '../../../language/value/compile/load.js';
@@ -31,6 +32,7 @@ export function validateCore(candidate, context) {
31
32
  pointer: path,
32
33
  });
33
34
  }
35
+ validateCoreNodeKeys(context, nodes);
34
36
  const edges = [];
35
37
  for (const [index, value] of entry.facts.core.entries()) {
36
38
  const path = `/closure/${entryIndex}/facts/core/${index}`;
@@ -67,6 +69,20 @@ export function validateCore(candidate, context) {
67
69
  validateCoreEdgeSet(context, entry, entryIndex, nodes, edges);
68
70
  }
69
71
  }
72
+ /** No two Core Nodes hold one key under a tuple covering their Classes (DV-NODE-UNIQUE). */
73
+ function validateCoreNodeKeys(context, nodes) {
74
+ const resolve = (ref) => context.classes.get(Key.of(ref))?.value;
75
+ const holders = new Set();
76
+ for (const node of nodes.values()) {
77
+ const keys = nodeUniqueKeys(coveringNodeUniqueTuples(node.class.value, resolve), {
78
+ props: node.value.properties,
79
+ });
80
+ if (keys.some((key) => holders.has(key)))
81
+ fail(node.pointer, 'Core Node holds a key another Core Node holds.');
82
+ for (const key of keys)
83
+ holders.add(key);
84
+ }
85
+ }
70
86
  function validateCoreProperties(values, analysis, path) {
71
87
  for (const key of Object.keys(values))
72
88
  if (!analysis.properties.has(key))
@@ -57,8 +57,6 @@ export function validatePolicyParity(context, entry, entryIndex, policies, origi
57
57
  const path = `/closure/${entryIndex}/definitions/classes/${classIndex}/policies/${operation}`;
58
58
  if (isClassRuleChecks(policy)) {
59
59
  // The wire admits rules only on a Node Class and only over `self` and `ref` objects.
60
- if (!checksNameTheCaller(policy))
61
- fail(path, 'Every branch of a Class rule must check a Policy: sameNode alone names no caller.');
62
60
  validatePolicyChecks(context, typing, entry, policy, -1, definition.ref, path, 'class');
63
61
  continue;
64
62
  }
@@ -102,6 +100,9 @@ const SITE = {
102
100
  class: { checks: 'Class rule checks', objects: 'the Class rule object contract' },
103
101
  };
104
102
  function validatePolicyChecks(context, typing, entry, expression, input, receiver, path, site) {
103
+ if (!checksNameTheCaller(expression)) {
104
+ fail(path, `${SITE[site].checks} must check a Policy on every branch: sameNode alone names no caller.`);
105
+ }
105
106
  const measure = measureChecks(expression);
106
107
  if (measure.depth > MAX_CHECK_DEPTH ||
107
108
  measure.branches > MAX_BRANCHES ||
@@ -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
  }
@@ -1,6 +1,7 @@
1
1
  import {} from '@astrale-os/kernel-dsl/value';
2
2
  import { Key } from '../../../addressing/index.js';
3
3
  import { MEDIA_TYPE_PATTERN } from '../../../language/class/model/data.js';
4
+ import { compareEdgeUniqueTuples, compareNodeUniqueTuples, edgeUniqueTuple, nodeUniqueTuple, } from '../../../language/class/unique.js';
4
5
  import {} from '../../model/schema.js';
5
6
  import { invalid } from '../diagnostic.js';
6
7
  import { compiledCallable } from './callable.js';
@@ -25,9 +26,15 @@ export function compiledClass(input, values, path) {
25
26
  'constraints',
26
27
  'properties',
27
28
  'policies',
28
- ], 'Compiled Class', path, value.kind === 'node' ? ['data', 'description', 'icon'] : ['description']);
29
+ ], 'Compiled Class', path, value.kind === 'node'
30
+ ? ['data', 'description', 'icon', 'inheritance', 'unique']
31
+ : ['description', 'inheritance', 'unique']);
29
32
  definitionRef(value.ref, 'class', 'Class ref', `${path}/ref`);
30
33
  boolean(value.abstract, 'Class abstract', `${path}/abstract`);
34
+ if (value.inheritance !== undefined &&
35
+ value.inheritance !== 'open' &&
36
+ value.inheritance !== 'final')
37
+ invalid('Class inheritance', undefined, `${path}/inheritance`);
31
38
  description(value.description, 'Class description', `${path}/description`);
32
39
  const extendsPath = `${path}/extends`;
33
40
  const parents = array(value.extends, 'Class extends', extendsPath).map((parent, index) => definitionRef(parent, 'class', 'Class parent', `${extendsPath}/${index}`));
@@ -40,6 +47,8 @@ export function compiledClass(input, values, path) {
40
47
  dataDefinition(value.data, `${path}/data`);
41
48
  boundedString(value.icon, 1, 8000, 'Class icon', `${path}/icon`);
42
49
  classPolicies(value.policies, 'node', `${path}/policies`);
50
+ if (value.unique !== undefined)
51
+ nodeUniqueTuples(value.unique, `${path}/unique`);
43
52
  const methodsPath = `${path}/methods`;
44
53
  const methods = array(value.methods, 'Class methods', methodsPath);
45
54
  for (const [index, method] of methods.entries())
@@ -93,9 +102,13 @@ function edgeContract(value, path) {
93
102
  object(endpoints.target, 'directed Edge target', `${endpointsPath}/target`).role) {
94
103
  invalid('Edge endpoint roles', undefined, endpointsPath);
95
104
  }
105
+ if (value.unique !== undefined)
106
+ uniqueTuples(value.unique, `${path}/unique`);
96
107
  return;
97
108
  }
98
- if (value.orientation !== 'undirected' || constraints.acyclic !== undefined) {
109
+ if (value.orientation !== 'undirected' ||
110
+ constraints.acyclic !== undefined ||
111
+ value.unique !== undefined) {
99
112
  invalid('Edge orientation', undefined, `${path}/orientation`);
100
113
  }
101
114
  const endpointsPath = `${path}/endpoints`;
@@ -109,9 +122,41 @@ function edgeContract(value, path) {
109
122
  invalid('Edge endpoint roles', undefined, endpointsPath);
110
123
  }
111
124
  }
125
+ /** A nonempty list of canonical tuples (`edgeUniqueTuple`), distinct and in canonical order. */
126
+ function uniqueTuples(input, path) {
127
+ const tuples = array(input, 'Edge unique tuples', path);
128
+ if (tuples.length === 0)
129
+ invalid('Edge unique tuples', undefined, path);
130
+ const known = tuples.map((tuple, index) => {
131
+ const fields = edgeUniqueTuple(array(tuple, 'Edge unique tuple', `${path}/${index}`));
132
+ if (fields === undefined)
133
+ invalid('Edge unique tuple', undefined, `${path}/${index}`);
134
+ return fields;
135
+ });
136
+ for (const [index, tuple] of known.entries()) {
137
+ if (index > 0 && compareEdgeUniqueTuples(known[index - 1], tuple) >= 0)
138
+ invalid('Edge unique tuples', undefined, `${path}/${index}`);
139
+ }
140
+ }
141
+ /** A nonempty list of canonical Node tuples (`nodeUniqueTuple`), distinct and in canonical order. */
142
+ function nodeUniqueTuples(input, path) {
143
+ const tuples = array(input, 'Node unique tuples', path);
144
+ if (tuples.length === 0)
145
+ invalid('Node unique tuples', undefined, path);
146
+ const known = tuples.map((tuple, index) => {
147
+ const keys = nodeUniqueTuple(array(tuple, 'Node unique tuple', `${path}/${index}`));
148
+ if (keys === undefined)
149
+ invalid('Node unique tuple', undefined, `${path}/${index}`);
150
+ return keys;
151
+ });
152
+ for (const [index, tuple] of known.entries()) {
153
+ if (index > 0 && compareNodeUniqueTuples(known[index - 1], tuple) >= 0)
154
+ invalid('Node unique tuples', undefined, `${path}/${index}`);
155
+ }
156
+ }
112
157
  function edgeEndpoint(input, count, label, path) {
113
158
  const value = object(input, label, path);
114
- exact(value, ['role', 'accepts', count], label, path);
159
+ exact(value, ['role', 'accepts', count], label, path, ['onDelete']);
115
160
  memberName(value.role, `${label} role`, `${path}/role`);
116
161
  const acceptsPath = `${path}/accepts`;
117
162
  const accepts = array(value.accepts, `${label} accepts`, acceptsPath).map((item, index) => definitionRef(item, 'class', `${label} accepted Class`, `${acceptsPath}/${index}`));
@@ -124,6 +169,8 @@ function edgeEndpoint(input, count, label, path) {
124
169
  value[count] !== '1') {
125
170
  invalid(`${label} ${count}`, undefined, `${path}/${count}`);
126
171
  }
172
+ if (value.onDelete !== undefined && value.onDelete !== 'restrict' && value.onDelete !== 'detach')
173
+ invalid(`${label} onDelete`, undefined, `${path}/onDelete`);
127
174
  }
128
175
  function dataDefinition(input, path) {
129
176
  const value = object(input, 'Class data', 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';
@@ -14,6 +14,7 @@ export { isDomain } from './domain/index.js';
14
14
  export type * as builder from './builder/index.js';
15
15
  export type { PathAuthoring, TypedValueSchema } from './builder/index.js';
16
16
  export type { PropertyValues as PropertyValuesValue } from './language/index.js';
17
+ export type { BlobReceipt, BlobRef } from './language/value/model/blob.js';
17
18
  export type { CompiledSchema } from './compiled/index.js';
18
19
  export * as schema from './schema/index.js';
19
20
  export * as bundle from './bundle/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. */