@astrale-os/kernel-dsl 0.2.0-beta.40 → 0.2.0-beta.42
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/v1/builder/callable/authoring.d.ts +10 -4
- package/dist/v1/builder/callable/authoring.js +2 -0
- package/dist/v1/builder/lowering/callable.js +1 -0
- package/dist/v1/builder/schema/projection.d.ts +12 -7
- package/dist/v1/compiled/admission/accept.js +19 -1
- package/dist/v1/compiled/admission/semantics.js +2 -1
- package/dist/v1/compiled/compiler/compile.js +2 -0
- package/dist/v1/domain/create.js +1 -0
- package/dist/v1/domain/projection/classes.js +3 -0
- package/dist/v1/language/callable/model/callable.d.ts +10 -0
- package/dist/v1/language/callable/resolved.d.ts +5 -1
- package/dist/v1/language/policy/normalization/normalize.js +50 -33
- package/dist/v1/schema/analysis/class.d.ts +7 -1
- package/dist/v1/schema/analysis/class.js +5 -1
- package/dist/v1/schema/compatibility/comparators/callables.d.ts +12 -7
- package/dist/v1/schema/compatibility/comparators/callables.js +9 -5
- package/dist/v1/schema/compatibility/fields.d.ts +7 -1
- package/dist/v1/schema/compatibility/fields.js +6 -1
- package/dist/v1/schema/normalization/domain.js +2 -0
- package/dist/v1/schema/validation/callable.d.ts +4 -0
- package/dist/v1/schema/validation/callable.js +39 -0
- package/dist/v1/schema/validation/domain.js +2 -0
- package/dist/v1/schema/validation/domain.structure.js +4 -0
- 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 { BinaryInputProjection, FunctionAuth } from '../../language/callable/model/callable.js';
|
|
3
|
+
import type { BinaryInputProjection, CallableExecution, 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';
|
|
@@ -51,6 +51,8 @@ export interface CallableInputBase<Input extends FunctionInputSchema = ValueSche
|
|
|
51
51
|
readonly description?: string;
|
|
52
52
|
readonly input: Input;
|
|
53
53
|
readonly output: CallableOutputInput;
|
|
54
|
+
/** `{ durable: true }` makes an invocation return an Execution reference. */
|
|
55
|
+
readonly execution?: CallableExecution;
|
|
54
56
|
}
|
|
55
57
|
type ConcreteCallableInput<Input extends FunctionInputSchema = ValueSchemaInput> = CallableInputBase<Input> & ({
|
|
56
58
|
readonly auth: 'anonymous';
|
|
@@ -72,17 +74,21 @@ export type MethodInput = ConcreteCallableInput & {
|
|
|
72
74
|
readonly abstract?: boolean;
|
|
73
75
|
};
|
|
74
76
|
export type CallableInput = FunctionInput | MethodInput;
|
|
75
|
-
export interface CallableProjection<out Input = unknown, out Output = unknown, out Auth extends FunctionAuth = FunctionAuth, out Static extends boolean = boolean, out Abstract extends boolean = boolean> {
|
|
77
|
+
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> {
|
|
76
78
|
readonly [CALLABLE_PROJECTION_VALUE]: true;
|
|
77
79
|
readonly input: Input;
|
|
78
80
|
readonly output: Output;
|
|
79
81
|
readonly auth: Auth;
|
|
80
82
|
readonly static: Static;
|
|
81
83
|
readonly abstract: Abstract;
|
|
84
|
+
readonly execution: Execution;
|
|
82
85
|
}
|
|
83
86
|
type ValueInputOf<Schema> = Schema extends TypedValueSchema<infer Value> ? Value : Schema extends z.ZodType ? z.input<Schema> : PortableValue;
|
|
84
87
|
type FunctionInputOf<Schema> = Schema extends BinaryInputAuthoring<infer Metadata> ? BinaryInputProjection<ValueInputOf<Metadata>> : ValueInputOf<Schema>;
|
|
85
88
|
type ValueOutputOf<Schema> = Schema extends TypedValueSchema<infer Value> ? Value : Schema extends z.ZodType ? z.output<Schema> : PortableValue;
|
|
89
|
+
type ExecutionOfConfig<Config extends CallableInput> = Config extends {
|
|
90
|
+
readonly execution: infer Execution extends CallableExecution;
|
|
91
|
+
} ? Execution : undefined;
|
|
86
92
|
type CallableOutputOf<Input extends CallableOutputInput> = Input extends StreamOutput<infer Item> ? {
|
|
87
93
|
readonly mode: 'stream';
|
|
88
94
|
readonly item: ValueOutputOf<Item>;
|
|
@@ -105,12 +111,12 @@ export type ProjectionOfCallable<Value extends Function | Method> = Value extend
|
|
|
105
111
|
export declare function callableConfiguration(value: Function | Method): CallableInput;
|
|
106
112
|
export type PolicyObjectAuthoring = Class | Function | Method | Policy | View | CoreNode;
|
|
107
113
|
export type PolicyObjectInput = Lazy<PolicyObjectAuthoring | PolicyObjectRef>;
|
|
108
|
-
export declare function func<const Config extends FunctionInput, Input = FunctionInputOf<Config['input']>, Output = CallableOutputOf<Config['output']>, Auth extends FunctionAuth = Config['auth']
|
|
114
|
+
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>>;
|
|
109
115
|
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 {
|
|
110
116
|
readonly static: true;
|
|
111
117
|
} ? true : false, Abstract extends boolean = Config extends {
|
|
112
118
|
readonly abstract: infer Value extends boolean;
|
|
113
|
-
} ? Value : false
|
|
119
|
+
} ? Value : false, Execution extends CallableExecution | undefined = ExecutionOfConfig<Config>>(config: Config): Method<CallableProjection<Input, Output, Auth, Static, Abstract, Execution>>;
|
|
114
120
|
export declare function callableInputField(name: string): Extract<PolicyCheckObject, {
|
|
115
121
|
readonly kind: 'input';
|
|
116
122
|
}>;
|
|
@@ -17,6 +17,7 @@ export function func(config) {
|
|
|
17
17
|
'policy',
|
|
18
18
|
'static',
|
|
19
19
|
'abstract',
|
|
20
|
+
'execution',
|
|
20
21
|
]);
|
|
21
22
|
const value = authoringValue('function', {});
|
|
22
23
|
callableInputs.set(value, { ...config });
|
|
@@ -31,6 +32,7 @@ export function method(config) {
|
|
|
31
32
|
'policy',
|
|
32
33
|
'static',
|
|
33
34
|
'abstract',
|
|
35
|
+
'execution',
|
|
34
36
|
]);
|
|
35
37
|
const value = authoringValue('method', {});
|
|
36
38
|
callableInputs.set(value, { ...config });
|
|
@@ -42,6 +42,7 @@ export class V1CallableCompiler {
|
|
|
42
42
|
...(config.description === undefined ? {} : { description: config.description }),
|
|
43
43
|
input,
|
|
44
44
|
output,
|
|
45
|
+
...(config.execution === undefined ? {} : { execution: config.execution }),
|
|
45
46
|
};
|
|
46
47
|
const contract = auth === 'authorized'
|
|
47
48
|
? { ...base, auth, ...(policy === undefined ? {} : { policy }) }
|
|
@@ -16,7 +16,7 @@ export interface PropertyProjection<Value = unknown, Required extends boolean =
|
|
|
16
16
|
readonly input: Input;
|
|
17
17
|
}
|
|
18
18
|
type MethodExecutableOf<OwnerAbstract extends boolean, MethodAbstract extends boolean> = MethodAbstract extends false ? true : OwnerAbstract extends false ? true : false;
|
|
19
|
-
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> {
|
|
19
|
+
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> {
|
|
20
20
|
readonly input: Input;
|
|
21
21
|
readonly output: Output;
|
|
22
22
|
readonly static: Static;
|
|
@@ -25,13 +25,15 @@ export interface MethodProjection<Input = unknown, Output = unknown, Static exte
|
|
|
25
25
|
readonly auth: Auth;
|
|
26
26
|
readonly name: Name;
|
|
27
27
|
readonly key: Key;
|
|
28
|
+
readonly execution: Execution;
|
|
28
29
|
}
|
|
29
|
-
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'
|
|
30
|
+
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> {
|
|
30
31
|
readonly input: Input;
|
|
31
32
|
readonly output: Output;
|
|
32
33
|
readonly auth: Auth;
|
|
33
34
|
readonly ref: Ref;
|
|
34
35
|
readonly key: Key;
|
|
36
|
+
readonly execution: Execution;
|
|
35
37
|
}
|
|
36
38
|
export interface ClassProjection<Kind extends language.ClassKind = language.ClassKind, Definition extends Extract<language.ClassDefinition, {
|
|
37
39
|
readonly kind: Kind;
|
|
@@ -253,6 +255,7 @@ type OwnMethodEntries<Value, Static extends boolean> = {
|
|
|
253
255
|
type CallableConfig<Value> = Value extends Method<infer Config> ? Config extends CallableProjection ? Config : never : Value extends Function<infer Config> ? Config extends CallableProjection ? Config : never : never;
|
|
254
256
|
type CallableInputValue<Value extends Method | Function> = CallableConfig<Value>['input'];
|
|
255
257
|
type CallableOutputValue<Value extends Method | Function> = CallableConfig<Value>['output'];
|
|
258
|
+
type CallableExecutionOf<Value extends Method | Function> = CallableConfig<Value>['execution'];
|
|
256
259
|
type CallableStaticOf<Value extends Method> = CallableConfig<Value> extends {
|
|
257
260
|
readonly static: true;
|
|
258
261
|
} ? true : false;
|
|
@@ -260,7 +263,7 @@ type CallableAbstractOf<Value extends Method> = CallableConfig<Value> extends {
|
|
|
260
263
|
readonly abstract: infer Abstract extends boolean;
|
|
261
264
|
} ? Abstract : false;
|
|
262
265
|
type CallableAuthOf<Value extends Method | Function> = CallableConfig<Value>['auth'];
|
|
263
|
-
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
|
|
266
|
+
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;
|
|
264
267
|
type MethodProjectionOfEntries<Origin extends string, OwnerName extends string, Entries> = Entries extends unknown ? MethodProjectionOfEntry<Origin, OwnerName, Entries> : never;
|
|
265
268
|
type MethodCollectionOperation = 'byKey' | 'get';
|
|
266
269
|
type PropertyProjectionOfResolved<Value> = Value extends language.ResolvedProperty<infer PropertyValue, infer Required, infer State, infer Input> & {
|
|
@@ -269,7 +272,7 @@ type PropertyProjectionOfResolved<Value> = Value extends language.ResolvedProper
|
|
|
269
272
|
} ? PropertyProjection<PropertyValue, Required, Name, Key, State, Input> : never;
|
|
270
273
|
type MethodProjectionOfResolved<Value> = Value extends language.ResolvedMethod<infer Input, infer Output, infer Static, infer Abstract, infer Auth, infer _Policy, infer Key> & {
|
|
271
274
|
readonly name: infer Name extends string;
|
|
272
|
-
} ? MethodProjection<Input, Output, Static, Abstract, Auth, Name, Key, Value['executable']> : never;
|
|
275
|
+
} ? MethodProjection<Input, Output, Static, Abstract, Auth, Name, Key, Value['executable'], Value['execution']> : never;
|
|
273
276
|
type PropertiesOfCollection<Value> = Value extends {
|
|
274
277
|
readonly byKey: infer ByKey extends Readonly<Record<language.PropertyKey, language.ResolvedProperty<unknown, boolean>>>;
|
|
275
278
|
} ? PropertyProjectionOfResolved<ByKey[keyof ByKey]> : never;
|
|
@@ -316,7 +319,7 @@ type ClassProjectionOfAuthoring<Origin extends string, Name extends string, Valu
|
|
|
316
319
|
}, language.DefinitionKey<Origin, 'class', Name>, ParentClassProjectionTuple<Origin, ParentInputsOfConfig<ProjectionOfClass<Extract<Value, Class>>>, Root, Seen>, EffectivePropertyProjectionsOf<Origin, Name, Value, Root>, never, never, OwnPropertyProjectionsOf<Origin, Name, Value>, never, never> : never;
|
|
317
320
|
type FunctionProjectionOfAuthoring<Origin extends string, Name extends string, Value> = Value extends Function ? FunctionProjection<CallableInputValue<Value>, CallableOutputValue<Value>, CallableAuthOf<Value>, language.FunctionRef<Name> & {
|
|
318
321
|
readonly origin: Origin;
|
|
319
|
-
}, language.DefinitionKey<Origin, 'function', Name>> : never;
|
|
322
|
+
}, language.DefinitionKey<Origin, 'function', Name>, CallableExecutionOf<Value>> : never;
|
|
320
323
|
type ResolvedPropertyOfProjection<Value> = Value extends PropertyProjection<infer PropertyValue, infer Required, infer Name, infer Key, infer State, infer Input> ? language.ResolvedProperty<PropertyValue, Required, State, Input> & {
|
|
321
324
|
readonly name: Name;
|
|
322
325
|
readonly key: Key;
|
|
@@ -334,9 +337,10 @@ type ProjectionPropertyByName<Entries extends PropertyProjection> = Readonly<{
|
|
|
334
337
|
}>>;
|
|
335
338
|
}>;
|
|
336
339
|
type PropertyCollectionOfProjection<Entries extends PropertyProjection> = language.ResolvedPropertyCollection<ProjectionPropertyByKey<Entries>, ProjectionPropertyByName<Entries>>;
|
|
337
|
-
type ResolvedMethodOfProjection<Value> = Value extends MethodProjection<infer Input, infer Output, infer Static, infer Abstract, infer Auth, infer Name, infer Key, infer Executable> ? language.ResolvedMethod<Input, Output, Static, Abstract, Auth, language.PolicyCheckExpression, Key> & {
|
|
340
|
+
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.PolicyCheckExpression, Key> & {
|
|
338
341
|
readonly name: Name;
|
|
339
342
|
readonly executable: Executable;
|
|
343
|
+
readonly execution: Execution;
|
|
340
344
|
} : never;
|
|
341
345
|
type ProjectionMethodByKey<Entries extends MethodProjection> = Readonly<{
|
|
342
346
|
[Key in Entries['key']]: ResolvedMethodOfProjection<Extract<Entries, {
|
|
@@ -363,8 +367,9 @@ type ResolvedClassWithKind<Kind extends language.ClassKind, Definition extends E
|
|
|
363
367
|
readonly ref: Ref;
|
|
364
368
|
readonly key: Key;
|
|
365
369
|
} : never;
|
|
366
|
-
type ResolvedFunctionOfProjection<Value extends FunctionProjection> = Value extends FunctionProjection<infer Input, infer Output, infer Auth, infer Ref, infer Key> ? language.ResolvedFunction<Input, Output, Auth, language.PolicyCheckExpression> & {
|
|
370
|
+
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.PolicyCheckExpression> & {
|
|
367
371
|
readonly ref: Ref;
|
|
368
372
|
readonly key: Key;
|
|
373
|
+
readonly execution: Execution;
|
|
369
374
|
} : never;
|
|
370
375
|
export {};
|
|
@@ -255,7 +255,7 @@ function compiledCallable(input, values, method, path) {
|
|
|
255
255
|
const value = object(input, 'Compiled Callable', path);
|
|
256
256
|
exact(value, method
|
|
257
257
|
? ['name', 'auth', 'static', 'abstract', 'input', 'output']
|
|
258
|
-
: ['ref', 'auth', 'input', 'output'], 'Compiled Callable', path, ['policy', 'description', 'fields']);
|
|
258
|
+
: ['ref', 'auth', 'input', 'output'], 'Compiled Callable', path, ['policy', 'description', 'execution', 'fields']);
|
|
259
259
|
if (method) {
|
|
260
260
|
memberName(value.name, 'Method name', `${path}/name`);
|
|
261
261
|
boolean(value.static, 'Method static', `${path}/static`);
|
|
@@ -308,6 +308,24 @@ function compiledCallable(input, values, method, path) {
|
|
|
308
308
|
exact(output, ['mode', 'value'], 'Callable output', outputPath);
|
|
309
309
|
index(output.value, values.length, 'Callable output value', `${outputPath}/value`);
|
|
310
310
|
}
|
|
311
|
+
if (value.execution !== undefined) {
|
|
312
|
+
const executionPath = `${path}/execution`;
|
|
313
|
+
const execution = object(value.execution, 'Callable execution', executionPath);
|
|
314
|
+
exact(execution, ['durable'], 'Callable execution', executionPath);
|
|
315
|
+
// Ordinary execution is canonically absent; only the durable modifier is retained.
|
|
316
|
+
if (!boolean(execution.durable, 'Callable execution durable', `${executionPath}/durable`)) {
|
|
317
|
+
invalid('Callable execution', 'COMPILED_CANONICAL_MISMATCH', `${executionPath}/durable`);
|
|
318
|
+
}
|
|
319
|
+
if (value.auth === 'anonymous') {
|
|
320
|
+
invalid('durable Callable auth', 'COMPILED_SOURCE_INVALID', `${path}/auth`);
|
|
321
|
+
}
|
|
322
|
+
if (typeof value.input === 'object' && value.input !== null) {
|
|
323
|
+
invalid('durable Callable input', 'COMPILED_SOURCE_INVALID', `${path}/input`);
|
|
324
|
+
}
|
|
325
|
+
if (output.mode !== 'value') {
|
|
326
|
+
invalid('durable Callable output', 'COMPILED_SOURCE_INVALID', `${outputPath}/mode`);
|
|
327
|
+
}
|
|
328
|
+
}
|
|
311
329
|
}
|
|
312
330
|
function compiledPolicy(input, path) {
|
|
313
331
|
const value = object(input, 'Policy', path);
|
|
@@ -135,7 +135,8 @@ function sameMethodSignature(leftEntry, left, rightEntry, right) {
|
|
|
135
135
|
return (programKey(leftEntry, left.input) === programKey(rightEntry, right.input) &&
|
|
136
136
|
outputKey(leftEntry, left.output) === outputKey(rightEntry, right.output) &&
|
|
137
137
|
left.auth === right.auth &&
|
|
138
|
-
left.static === right.static
|
|
138
|
+
left.static === right.static &&
|
|
139
|
+
left.execution?.durable === right.execution?.durable);
|
|
139
140
|
}
|
|
140
141
|
function outputKey(entry, output) {
|
|
141
142
|
return output.mode === 'binary' ? 'binary' : `${output.mode}:${programKey(entry, output.value)}`;
|
|
@@ -87,6 +87,7 @@ function compileEntry(schema, lookup) {
|
|
|
87
87
|
...(method.description === undefined ? {} : { description: method.description }),
|
|
88
88
|
static: method.static,
|
|
89
89
|
abstract: method.abstract,
|
|
90
|
+
...(method.execution === undefined ? {} : { execution: method.execution }),
|
|
90
91
|
input,
|
|
91
92
|
...(fields === undefined ? {} : { fields }),
|
|
92
93
|
output: output(method.output, `${pointer}/methods/${name}/output`),
|
|
@@ -114,6 +115,7 @@ function compileEntry(schema, lookup) {
|
|
|
114
115
|
auth: definition.auth,
|
|
115
116
|
...(definition.policy === undefined ? {} : { policy: definition.policy }),
|
|
116
117
|
...(definition.description === undefined ? {} : { description: definition.description }),
|
|
118
|
+
...(definition.execution === undefined ? {} : { execution: definition.execution }),
|
|
117
119
|
input,
|
|
118
120
|
...(fields === undefined ? {} : { fields }),
|
|
119
121
|
output: output(definition.output, `/functions/${definition.ref.name}/output`),
|
package/dist/v1/domain/create.js
CHANGED
|
@@ -147,6 +147,7 @@ function createDefinitions(declarations, classesByKey, input) {
|
|
|
147
147
|
description: definition.description,
|
|
148
148
|
input: definition.input,
|
|
149
149
|
output: definition.output,
|
|
150
|
+
...(definition.execution === undefined ? {} : { execution: definition.execution }),
|
|
150
151
|
auth: definition.auth,
|
|
151
152
|
policy: definition.policy,
|
|
152
153
|
...(inputFields === undefined ? {} : { inputFields }),
|
|
@@ -65,6 +65,9 @@ export function createClasses(definitions, validation) {
|
|
|
65
65
|
description: methodDefinition.description,
|
|
66
66
|
input: methodDefinition.input,
|
|
67
67
|
output: methodDefinition.output,
|
|
68
|
+
...(methodDefinition.execution === undefined
|
|
69
|
+
? {}
|
|
70
|
+
: { execution: methodDefinition.execution }),
|
|
68
71
|
auth: methodDefinition.auth,
|
|
69
72
|
policy: methodDefinition.policy,
|
|
70
73
|
static: methodDefinition.static,
|
|
@@ -12,6 +12,15 @@ export type CallableOutput = {
|
|
|
12
12
|
readonly mode: 'binary';
|
|
13
13
|
};
|
|
14
14
|
export type OutputMode = CallableOutput['mode'];
|
|
15
|
+
/**
|
|
16
|
+
* Execution modifier of a callable. Absent or `durable: false` means ordinary execution, and an
|
|
17
|
+
* accepted Schema keeps only `durable: true`. A durable invocation returns an Execution reference;
|
|
18
|
+
* the handler still returns `output`. Retry, retention and endpoint are never callable meaning. A
|
|
19
|
+
* durable callable takes a value input, which its Execution records so the run can be resubmitted.
|
|
20
|
+
*/
|
|
21
|
+
export interface CallableExecution {
|
|
22
|
+
readonly durable: boolean;
|
|
23
|
+
}
|
|
15
24
|
/** A binary Function receives portable metadata and one required byte stream. */
|
|
16
25
|
export interface BinaryInput {
|
|
17
26
|
readonly mode: 'binary';
|
|
@@ -29,6 +38,7 @@ export interface CallableDefinitionBase<Input extends CallableInputContract = Ca
|
|
|
29
38
|
readonly description?: string;
|
|
30
39
|
readonly input: Input;
|
|
31
40
|
readonly output: CallableOutput;
|
|
41
|
+
readonly execution?: CallableExecution;
|
|
32
42
|
}
|
|
33
43
|
export type CallableDefinition<Input extends CallableInputContract = CallableInputContract> = CallableDefinitionBase<Input> & ({
|
|
34
44
|
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 { PolicyCheckExpression } from '../policy/model/policy.js';
|
|
4
4
|
import type { Diagnostic } from '../validation/diagnostic.js';
|
|
5
|
-
import type { BinaryInputProjection, FunctionDefinition, MethodDefinition } from './model/callable.js';
|
|
5
|
+
import type { BinaryInputProjection, 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 PolicyCheckExpression> = Omit<Definition, 'auth' | 'policy'> & (Auth extends FunctionAuth ? {
|
|
8
8
|
readonly auth: Auth;
|
|
@@ -20,6 +20,8 @@ export interface ResolvedMethod<out Input = unknown, out Output = unknown, out S
|
|
|
20
20
|
readonly description?: MethodDefinition['description'];
|
|
21
21
|
readonly input: MethodDefinition['input'];
|
|
22
22
|
readonly output: MethodDefinition['output'];
|
|
23
|
+
/** Present only for a durable Method. */
|
|
24
|
+
readonly execution?: CallableExecution;
|
|
23
25
|
readonly auth: Auth;
|
|
24
26
|
readonly policy?: Auth extends 'authorized' ? Policy : never;
|
|
25
27
|
readonly static: Static;
|
|
@@ -49,6 +51,8 @@ export interface ResolvedFunction<out Input = unknown, out Output = unknown, out
|
|
|
49
51
|
readonly description?: FunctionDefinition['description'];
|
|
50
52
|
readonly input: FunctionDefinition['input'];
|
|
51
53
|
readonly output: FunctionDefinition['output'];
|
|
54
|
+
/** Present only for a durable Function. */
|
|
55
|
+
readonly execution?: CallableExecution;
|
|
52
56
|
readonly auth: Auth;
|
|
53
57
|
readonly policy?: Auth extends 'authorized' ? Policy : never;
|
|
54
58
|
/** Present only when the callable input is a safely enumerable closed object. */
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { json } from '@astrale-os/kernel-dsl/value';
|
|
2
2
|
import { definitionRefIndex, compareDefinitionRefs } from '../../../addressing/ordering.js';
|
|
3
3
|
import { policyObjectKey, policyVariableSelector } from '../model/policy.js';
|
|
4
|
-
const textEncoder = new TextEncoder();
|
|
5
4
|
export function normalizePolicyExpression(value) {
|
|
6
5
|
if ('match' in value) {
|
|
7
6
|
return { match: normalizePolicyPattern(value.match) };
|
|
@@ -73,20 +72,22 @@ function normalizePatternNode(value) {
|
|
|
73
72
|
function canonicalVariableIds(value) {
|
|
74
73
|
const declarations = new Map();
|
|
75
74
|
collectDeclarations(value, declarations);
|
|
76
|
-
const
|
|
75
|
+
const uses = new Map([...declarations.keys()].map((id) => [id, []]));
|
|
76
|
+
collectUses(value, uses);
|
|
77
|
+
let colors = declarations;
|
|
78
|
+
// Color refinement. Each round names a variable by its declaration and the sorted colors of its
|
|
79
|
+
// uses, then replaces every name by its fixed-width rank among the round's names. Ranks keep the
|
|
80
|
+
// names' byte order, so a round decides what it would decide over uncompressed names, while a
|
|
81
|
+
// name stays linear in its variable's degree instead of embedding every neighbour's name. A
|
|
82
|
+
// round that reproduces its input is a fixed point of the next ones.
|
|
77
83
|
for (let iteration = 0; iteration < declarations.size + 1; iteration++) {
|
|
78
|
-
const next = new Map()
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
uses.sort(compareUtf8Bytes);
|
|
83
|
-
next.set(id, `${declarations.get(id) ?? ''}|${uses.join('|')}`);
|
|
84
|
-
}
|
|
84
|
+
const next = rank(new Map([...uses].map(([id, list]) => {
|
|
85
|
+
const named = list.map(([prefix, term]) => `${prefix}${termColor(term, colors)}`);
|
|
86
|
+
return [id, `${declarations.get(id) ?? ''}|${named.sort(compareUtf8Bytes).join('|')}`];
|
|
87
|
+
})));
|
|
85
88
|
if ([...next].every(([id, color]) => colors.get(id) === color))
|
|
86
89
|
break;
|
|
87
|
-
colors
|
|
88
|
-
for (const [id, color] of next)
|
|
89
|
-
colors.set(id, color);
|
|
90
|
+
colors = next;
|
|
90
91
|
}
|
|
91
92
|
const firstUse = new Map();
|
|
92
93
|
collectFirstUseKeys(value, colors, firstUse);
|
|
@@ -118,29 +119,34 @@ function collectDeclarations(value, result) {
|
|
|
118
119
|
for (const child of value.anyOf)
|
|
119
120
|
collectDeclarations(child, result);
|
|
120
121
|
}
|
|
121
|
-
function collectUses(value,
|
|
122
|
+
function collectUses(value, result) {
|
|
122
123
|
if ('sameNode' in value) {
|
|
123
124
|
const { left, right } = value.sameNode;
|
|
124
|
-
if (left.kind === 'variable'
|
|
125
|
-
result.push(
|
|
126
|
-
if (right.kind === 'variable'
|
|
127
|
-
result.push(
|
|
125
|
+
if (left.kind === 'variable')
|
|
126
|
+
result.get(left.id)?.push(['same:', right]);
|
|
127
|
+
if (right.kind === 'variable')
|
|
128
|
+
result.get(right.id)?.push(['same:', left]);
|
|
128
129
|
return;
|
|
129
130
|
}
|
|
130
131
|
if (isEdge(value)) {
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
if (value.target.kind === 'variable'
|
|
135
|
-
result.push(`t:${
|
|
136
|
-
}
|
|
132
|
+
const edge = definitionRefIndex(value.class);
|
|
133
|
+
if (value.source.kind === 'variable')
|
|
134
|
+
result.get(value.source.id)?.push([`s:${edge}:`, value.target]);
|
|
135
|
+
if (value.target.kind === 'variable')
|
|
136
|
+
result.get(value.target.id)?.push([`t:${edge}:`, value.source]);
|
|
137
137
|
return;
|
|
138
138
|
}
|
|
139
139
|
if ('exists' in value)
|
|
140
|
-
return collectUses(value.exists.where,
|
|
140
|
+
return collectUses(value.exists.where, result);
|
|
141
141
|
const children = 'allOf' in value ? value.allOf : value.anyOf;
|
|
142
142
|
for (const child of children)
|
|
143
|
-
collectUses(child,
|
|
143
|
+
collectUses(child, result);
|
|
144
|
+
}
|
|
145
|
+
function rank(names) {
|
|
146
|
+
const distinct = [...new Set(names.values())].sort(compareUtf8Bytes);
|
|
147
|
+
const width = String(distinct.length).length;
|
|
148
|
+
const ranks = new Map(distinct.map((name, index) => [name, String(index).padStart(width, '0')]));
|
|
149
|
+
return new Map([...names].map(([id, name]) => [id, ranks.get(name) ?? '']));
|
|
144
150
|
}
|
|
145
151
|
function termColor(term, colors) {
|
|
146
152
|
if (term.kind === 'variable')
|
|
@@ -181,8 +187,10 @@ function collectFirstUseKeys(value, colors, result, prefix = '$') {
|
|
|
181
187
|
}
|
|
182
188
|
const key = 'allOf' in value ? 'allOf' : 'anyOf';
|
|
183
189
|
const operands = 'allOf' in value ? value.allOf : value.anyOf;
|
|
184
|
-
const children =
|
|
185
|
-
|
|
190
|
+
const children = operands
|
|
191
|
+
.map((child) => [patternSignature(child, colors), child])
|
|
192
|
+
.sort(([left], [right]) => compareUtf8Bytes(left, right));
|
|
193
|
+
children.forEach(([, child], index) => collectFirstUseKeys(child, colors, result, `${prefix}:${key}:${index}`));
|
|
186
194
|
}
|
|
187
195
|
function patternSignature(value, colors) {
|
|
188
196
|
if ('sameNode' in value)
|
|
@@ -255,15 +263,24 @@ function uniqueJson(values) {
|
|
|
255
263
|
.sort(([left], [right]) => compareUtf8Bytes(left, right))
|
|
256
264
|
.map(([, value]) => value);
|
|
257
265
|
}
|
|
266
|
+
/**
|
|
267
|
+
* UTF-8 byte order without encoding: it is code-point order, which UTF-16 code units already follow
|
|
268
|
+
* except that surrogates (U+10000 and above) sort after U+E000–U+FFFF.
|
|
269
|
+
*/
|
|
258
270
|
function compareUtf8Bytes(left, right) {
|
|
259
|
-
const
|
|
260
|
-
const b = textEncoder.encode(right);
|
|
261
|
-
const length = Math.min(a.length, b.length);
|
|
271
|
+
const length = Math.min(left.length, right.length);
|
|
262
272
|
for (let index = 0; index < length; index++) {
|
|
263
|
-
|
|
264
|
-
|
|
273
|
+
const a = left.charCodeAt(index);
|
|
274
|
+
const b = right.charCodeAt(index);
|
|
275
|
+
if (a !== b)
|
|
276
|
+
return codePointOrder(a) - codePointOrder(b);
|
|
265
277
|
}
|
|
266
|
-
return
|
|
278
|
+
return left.length - right.length;
|
|
279
|
+
}
|
|
280
|
+
function codePointOrder(unit) {
|
|
281
|
+
if (unit >= 0xd800 && unit <= 0xdfff)
|
|
282
|
+
return unit + 0x2000;
|
|
283
|
+
return unit >= 0xe000 ? unit - 0x800 : unit;
|
|
267
284
|
}
|
|
268
285
|
function isEdge(value) {
|
|
269
286
|
return 'source' in value;
|
|
@@ -42,7 +42,10 @@ export interface DefinitionAnalysisResult {
|
|
|
42
42
|
export declare function analyzeDefinitions(environment: DomainResolutionContext): DefinitionAnalysisResult;
|
|
43
43
|
/** The exact signature required by abstract Method composition. */
|
|
44
44
|
export declare function sameMethodSignature(left: MethodDefinition, right: MethodDefinition): boolean;
|
|
45
|
-
/**
|
|
45
|
+
/**
|
|
46
|
+
* Shared canonical signature used by admission and compatibility explanations. The execution
|
|
47
|
+
* modifier appears only when durable, so absent and `durable: false` are the same signature.
|
|
48
|
+
*/
|
|
46
49
|
export declare function methodSignature(value: MethodDefinition): {
|
|
47
50
|
input: JsonSchema;
|
|
48
51
|
output: {
|
|
@@ -60,4 +63,7 @@ export declare function methodSignature(value: MethodDefinition): {
|
|
|
60
63
|
};
|
|
61
64
|
auth: "anonymous" | "authenticated" | "authorized";
|
|
62
65
|
static: boolean;
|
|
66
|
+
execution?: {
|
|
67
|
+
durable: boolean;
|
|
68
|
+
} | undefined;
|
|
63
69
|
};
|
|
@@ -117,13 +117,17 @@ function mergeMethods(owner, declaration, parents, diagnostics) {
|
|
|
117
117
|
export function sameMethodSignature(left, right) {
|
|
118
118
|
return json.serialize(methodSignature(left)) === json.serialize(methodSignature(right));
|
|
119
119
|
}
|
|
120
|
-
/**
|
|
120
|
+
/**
|
|
121
|
+
* Shared canonical signature used by admission and compatibility explanations. The execution
|
|
122
|
+
* modifier appears only when durable, so absent and `durable: false` are the same signature.
|
|
123
|
+
*/
|
|
121
124
|
export function methodSignature(value) {
|
|
122
125
|
return {
|
|
123
126
|
input: canonicalizeValueSchema(value.input, 'callable-input'),
|
|
124
127
|
output: canonicalMethodOutput(value),
|
|
125
128
|
auth: value.auth,
|
|
126
129
|
static: value.static,
|
|
130
|
+
...(value.execution?.durable === true ? { execution: { durable: true } } : {}),
|
|
127
131
|
};
|
|
128
132
|
}
|
|
129
133
|
function canonicalMethodOutput(value) {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { Value } from '@astrale-os/kernel-dsl/value';
|
|
2
|
-
import type { CallableOutput, CallableInputContract, FunctionDefinition, MethodDefinition } from '../../../language/callable/model/callable.js';
|
|
2
|
+
import type { CallableExecution, CallableOutput, CallableInputContract, FunctionDefinition, MethodDefinition } from '../../../language/callable/model/callable.js';
|
|
3
3
|
import type { ResolvedFunction, ResolvedMethod } from '../../../language/callable/resolved.js';
|
|
4
4
|
import type { EffectiveMethod } from '../../analysis/class.js';
|
|
5
5
|
import type { Fingerprint } from '../model.js';
|
|
@@ -10,18 +10,23 @@ export declare function functionSignature(declaration: FunctionDefinition): Valu
|
|
|
10
10
|
export declare function functionExistence(): Value;
|
|
11
11
|
/**
|
|
12
12
|
* Fingerprint the contract a caller compiles against, independently of its Domain revision: the
|
|
13
|
-
* exact key, the owner, dispatch and abstractness of a Method,
|
|
14
|
-
* contracts
|
|
15
|
-
* instead of a Domain revision, so a
|
|
16
|
-
* keeps every bound invocation
|
|
13
|
+
* exact key, the owner, dispatch and abstractness of a Method, the semantic input and output
|
|
14
|
+
* contracts, and the durable execution modifier; authorization is excluded
|
|
15
|
+
* (DM-CALLABLE-FINGERPRINT). A static consumer pins this instead of a Domain revision, so a
|
|
16
|
+
* dependency release that leaves the callable contract untouched keeps every bound invocation
|
|
17
|
+
* valid.
|
|
17
18
|
*/
|
|
18
19
|
export declare function callableFingerprint(callable: ResolvedFunction | ResolvedMethod): Fingerprint;
|
|
19
20
|
type CallableSignature = {
|
|
20
21
|
readonly static?: boolean;
|
|
21
22
|
readonly abstract?: boolean;
|
|
23
|
+
readonly execution?: CallableExecution;
|
|
22
24
|
readonly input: CallableInputContract;
|
|
23
25
|
readonly output: CallableOutput;
|
|
24
26
|
};
|
|
25
|
-
/**
|
|
26
|
-
|
|
27
|
+
/**
|
|
28
|
+
* The `always` fields of CALLABLE_FIELDS: dispatch shape, the execution modifier, and the semantic
|
|
29
|
+
* input and output.
|
|
30
|
+
*/
|
|
31
|
+
export declare function callableSignature(declaration: Pick<FunctionDefinition, 'input' | 'output' | 'execution'> | Pick<MethodDefinition, 'static' | 'abstract' | 'input' | 'output' | 'execution'>): CallableSignature;
|
|
27
32
|
export {};
|
|
@@ -41,10 +41,11 @@ export function functionExistence() {
|
|
|
41
41
|
}
|
|
42
42
|
/**
|
|
43
43
|
* Fingerprint the contract a caller compiles against, independently of its Domain revision: the
|
|
44
|
-
* exact key, the owner, dispatch and abstractness of a Method,
|
|
45
|
-
* contracts
|
|
46
|
-
* instead of a Domain revision, so a
|
|
47
|
-
* keeps every bound invocation
|
|
44
|
+
* exact key, the owner, dispatch and abstractness of a Method, the semantic input and output
|
|
45
|
+
* contracts, and the durable execution modifier; authorization is excluded
|
|
46
|
+
* (DM-CALLABLE-FINGERPRINT). A static consumer pins this instead of a Domain revision, so a
|
|
47
|
+
* dependency release that leaves the callable contract untouched keeps every bound invocation
|
|
48
|
+
* valid.
|
|
48
49
|
*/
|
|
49
50
|
export function callableFingerprint(callable) {
|
|
50
51
|
// This digest input travels in every bound invocation and is recomputed by the Kernel; its
|
|
@@ -58,7 +59,10 @@ export function callableFingerprint(callable) {
|
|
|
58
59
|
}
|
|
59
60
|
: { kind: 'function', key: callable.key, ...callableSignature(callable) });
|
|
60
61
|
}
|
|
61
|
-
/**
|
|
62
|
+
/**
|
|
63
|
+
* The `always` fields of CALLABLE_FIELDS: dispatch shape, the execution modifier, and the semantic
|
|
64
|
+
* input and output.
|
|
65
|
+
*/
|
|
62
66
|
export function callableSignature(declaration) {
|
|
63
67
|
return {
|
|
64
68
|
...fieldsOf({ declaration, roles: CALLABLE_FIELDS }),
|
|
@@ -48,7 +48,11 @@ export declare const PROPERTY_FIELDS: Readonly<{
|
|
|
48
48
|
excluded: 'annotation';
|
|
49
49
|
}>;
|
|
50
50
|
}>;
|
|
51
|
-
/**
|
|
51
|
+
/**
|
|
52
|
+
* The auth level and Policy check are decided by the Runtime at invocation, never compiled against.
|
|
53
|
+
* The execution modifier decides what an invocation returns; an accepted Schema carries it only
|
|
54
|
+
* when durable, so ordinary callables keep their meaning unchanged.
|
|
55
|
+
*/
|
|
52
56
|
export declare const CALLABLE_FIELDS: Readonly<{
|
|
53
57
|
ref: Readonly<{
|
|
54
58
|
excluded: 'identity';
|
|
@@ -57,6 +61,7 @@ export declare const CALLABLE_FIELDS: Readonly<{
|
|
|
57
61
|
output: "always";
|
|
58
62
|
static: "always";
|
|
59
63
|
abstract: "always";
|
|
64
|
+
execution: "always";
|
|
60
65
|
auth: Readonly<{
|
|
61
66
|
excluded: 'authorization';
|
|
62
67
|
}>;
|
|
@@ -140,6 +145,7 @@ export declare const FIELD_ROLES: Readonly<{
|
|
|
140
145
|
output: "always";
|
|
141
146
|
static: "always";
|
|
142
147
|
abstract: "always";
|
|
148
|
+
execution: "always";
|
|
143
149
|
auth: Readonly<{
|
|
144
150
|
excluded: 'authorization';
|
|
145
151
|
}>;
|
|
@@ -25,13 +25,18 @@ export const PROPERTY_FIELDS = Object.freeze({
|
|
|
25
25
|
state: 'always',
|
|
26
26
|
description: ANNOTATION,
|
|
27
27
|
});
|
|
28
|
-
/**
|
|
28
|
+
/**
|
|
29
|
+
* The auth level and Policy check are decided by the Runtime at invocation, never compiled against.
|
|
30
|
+
* The execution modifier decides what an invocation returns; an accepted Schema carries it only
|
|
31
|
+
* when durable, so ordinary callables keep their meaning unchanged.
|
|
32
|
+
*/
|
|
29
33
|
export const CALLABLE_FIELDS = Object.freeze({
|
|
30
34
|
ref: IDENTITY,
|
|
31
35
|
input: 'always',
|
|
32
36
|
output: 'always',
|
|
33
37
|
static: 'always',
|
|
34
38
|
abstract: 'always',
|
|
39
|
+
execution: 'always',
|
|
35
40
|
auth: AUTHORIZATION,
|
|
36
41
|
policy: AUTHORIZATION,
|
|
37
42
|
description: ANNOTATION,
|
|
@@ -101,6 +101,8 @@ function canonicalizeCallable(value) {
|
|
|
101
101
|
}
|
|
102
102
|
: canonicalizeValueSchema(value.input, 'callable-input'),
|
|
103
103
|
output: canonicalizeCallableOutput(value.output),
|
|
104
|
+
// Ordinary execution has one canonical form, so existing revisions never move.
|
|
105
|
+
...(value.execution?.durable === true ? { execution: { durable: true } } : {}),
|
|
104
106
|
};
|
|
105
107
|
return value.auth === 'authorized'
|
|
106
108
|
? {
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { SchemaDiagnostic } from '../diagnostic.js';
|
|
2
|
+
import type { DomainSchemaIR } from '../model/schema.js';
|
|
3
|
+
/** Validate the execution modifier of every local Function and Node Method. */
|
|
4
|
+
export declare function validateCallables(schema: DomainSchemaIR): readonly SchemaDiagnostic[];
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { isBinaryInput } from '../../language/callable/model/callable.js';
|
|
2
|
+
import { diagnostic } from '../diagnostic.js';
|
|
3
|
+
/** Validate the execution modifier of every local Function and Node Method. */
|
|
4
|
+
export function validateCallables(schema) {
|
|
5
|
+
const diagnostics = [];
|
|
6
|
+
for (const [name, declaration] of Object.entries(schema.functions)) {
|
|
7
|
+
validateExecution(declaration, `/functions/${escapePointer(name)}`, diagnostics);
|
|
8
|
+
}
|
|
9
|
+
for (const [name, declaration] of Object.entries(schema.classes)) {
|
|
10
|
+
if (declaration.kind !== 'node')
|
|
11
|
+
continue;
|
|
12
|
+
for (const [method, value] of Object.entries(declaration.methods)) {
|
|
13
|
+
const pointer = `/classes/${escapePointer(name)}/methods/${escapePointer(method)}`;
|
|
14
|
+
validateExecution(value, pointer, diagnostics);
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
return Object.freeze(diagnostics);
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* An Execution records its initiating caller and its admitted input, from which the run is
|
|
21
|
+
* resubmitted, and retains one portable outcome. A durable callable therefore needs a known caller,
|
|
22
|
+
* a value input and a replayable value output.
|
|
23
|
+
*/
|
|
24
|
+
function validateExecution(declaration, pointer, diagnostics) {
|
|
25
|
+
if (declaration.execution?.durable !== true)
|
|
26
|
+
return;
|
|
27
|
+
if (declaration.auth === 'anonymous') {
|
|
28
|
+
diagnostics.push(diagnostic('DV_CALLABLE_AUTH', `${pointer}/auth`, 'A durable callable requires authenticated or authorized auth.'));
|
|
29
|
+
}
|
|
30
|
+
if (isBinaryInput(declaration.input)) {
|
|
31
|
+
diagnostics.push(diagnostic('DV_CALLABLE_IO', `${pointer}/input/mode`, 'A durable callable requires a value input.'));
|
|
32
|
+
}
|
|
33
|
+
if (declaration.output.mode !== 'value') {
|
|
34
|
+
diagnostics.push(diagnostic('DV_CALLABLE_IO', `${pointer}/output/mode`, 'A durable callable requires a value output.'));
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
function escapePointer(value) {
|
|
38
|
+
return value.replaceAll('~', '~0').replaceAll('/', '~1');
|
|
39
|
+
}
|
|
@@ -2,6 +2,7 @@ import { compareUnicode } from '../../addressing/ordering.js';
|
|
|
2
2
|
import { analyzeDefinitions } from '../analysis/class.js';
|
|
3
3
|
import { buildDomainResolutionContext } from '../resolution/context.js';
|
|
4
4
|
import { createValueSchemaRegistry } from '../resolution/value.registry.js';
|
|
5
|
+
import { validateCallables } from './callable.js';
|
|
5
6
|
import { validateDefinitions } from './class.js';
|
|
6
7
|
import { validateCore } from './core.js';
|
|
7
8
|
import { validatePolicies } from './policy/validate.js';
|
|
@@ -15,6 +16,7 @@ export function validateDomainSchemaSemantics(schema, lookup) {
|
|
|
15
16
|
...context.diagnostics,
|
|
16
17
|
...validateEmbeddedValueSchemas(schema, registry),
|
|
17
18
|
...validateDefinitions(context, analysis),
|
|
19
|
+
...validateCallables(schema),
|
|
18
20
|
...validatePolicies(context, analysis),
|
|
19
21
|
...validateViews(context),
|
|
20
22
|
...validateCore(context, analysis, registry),
|
|
@@ -189,10 +189,14 @@ const binaryInputSchema = strictObject({
|
|
|
189
189
|
maximumBytes: number().int().min(1).max(Number.MAX_SAFE_INTEGER),
|
|
190
190
|
});
|
|
191
191
|
const valueFunctionInputSchema = jsonSchemaSchema.refine((value) => typeof value !== 'object' || value === null || value.mode !== 'binary', 'Binary input must declare metadata and maximumBytes.');
|
|
192
|
+
const callableExecutionSchema = strictObject({
|
|
193
|
+
durable: boolean(),
|
|
194
|
+
});
|
|
192
195
|
const callableShape = {
|
|
193
196
|
description: descriptionSchema.optional(),
|
|
194
197
|
input: jsonSchemaSchema,
|
|
195
198
|
output: callableOutputSchema,
|
|
199
|
+
execution: callableExecutionSchema.optional(),
|
|
196
200
|
auth: enumeration(['anonymous', 'authenticated', 'authorized']),
|
|
197
201
|
policy: policyCheckExpressionSchema.optional(),
|
|
198
202
|
};
|