@astrale-os/kernel-dsl 1.0.0-beta.3 → 1.0.0-beta.5
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/callable.d.ts +7 -5
- package/dist/v1/builder/schema/projection/model.d.ts +4 -2
- package/dist/v1/builder/schema/projection/resolved-class.d.ts +2 -1
- package/dist/v1/compiled/admission/semantics/class.js +6 -1
- package/dist/v1/compiled/admission/wire/callable.js +11 -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/index.d.ts +1 -0
- package/dist/v1/language/callable/admission.d.ts +15 -0
- package/dist/v1/language/callable/admission.js +43 -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/index.d.ts +1 -0
- package/dist/v1/language/index.js +1 -0
- package/dist/v1/language/value/evaluate/validate.js +4 -0
- package/dist/v1/language/value/evaluate/validity.optimization.d.ts +14 -0
- package/dist/v1/language/value/evaluate/validity.optimization.js +342 -0
- package/dist/v1/schema/compatibility/fields.d.ts +11 -3
- package/dist/v1/schema/compatibility/fields.js +6 -3
- package/dist/v1/schema/diagnostic.d.ts +1 -1
- package/dist/v1/schema/normalization/domain.js +2 -0
- package/dist/v1/schema/validation/callable.d.ts +1 -1
- package/dist/v1/schema/validation/callable.js +17 -2
- package/dist/v1/schema/validation/class.js +7 -1
- package/dist/v1/schema/validation/domain.structure.js +5 -0
- package/dist/value/canonical.d.ts +5 -0
- package/dist/value/canonical.js +12 -0
- package/dist/value/json.d.ts +1 -1
- package/dist/value/json.js +1 -1
- package/package.json +1 -1
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { Value as PortableValue } from '@astrale-os/kernel-dsl/value';
|
|
2
2
|
import type { z } from 'zod';
|
|
3
3
|
import type { PolicyRef } from '../../addressing/index.js';
|
|
4
|
-
import type { BinaryInputProjection, CallableExecution, FunctionAuth } from '../../language/callable/model/callable.js';
|
|
4
|
+
import type { BinaryInputProjection, CallableAdmission, CallableExecution, FunctionAuth } from '../../language/callable/model/callable.js';
|
|
5
5
|
import type { PolicyCheckExpression, PolicyCheckObject, PolicyObjectRef } from '../../language/policy/model/policy.js';
|
|
6
6
|
import type { Class } from '../class/authoring.js';
|
|
7
7
|
import type { ValueSchemaInput } from '../class/property.js';
|
|
@@ -62,6 +62,8 @@ export interface CallableInputBase<Input extends FunctionInputSchema = ValueSche
|
|
|
62
62
|
readonly output: CallableOutputInput;
|
|
63
63
|
/** `{ durable: true }` makes an invocation return an Execution reference. */
|
|
64
64
|
readonly execution?: CallableExecution;
|
|
65
|
+
/** `{ maxAge }`: how old, in seconds, the admission a call is served under may be. */
|
|
66
|
+
readonly admission?: CallableAdmission;
|
|
65
67
|
}
|
|
66
68
|
type ConcreteCallableInput<Input extends FunctionInputSchema = ValueSchemaInput> = CallableInputBase<Input> & ({
|
|
67
69
|
readonly auth: 'anonymous';
|
|
@@ -83,7 +85,7 @@ export type MethodInput = ConcreteCallableInput & {
|
|
|
83
85
|
readonly abstract?: boolean;
|
|
84
86
|
};
|
|
85
87
|
export type CallableInput = FunctionInput | MethodInput;
|
|
86
|
-
export interface CallableProjection<out Input = unknown, out Output = unknown, out Auth extends FunctionAuth = FunctionAuth, out Static extends boolean = boolean, out Abstract extends boolean = boolean, out Execution extends CallableExecution | undefined = CallableExecution | undefined> {
|
|
88
|
+
export interface CallableProjection<out Input = unknown, out Output = unknown, out Auth extends FunctionAuth = FunctionAuth, out Static extends boolean = boolean, out Abstract extends boolean = boolean, out Execution extends CallableExecution | undefined = CallableExecution | undefined, out Admission extends CallableAdmission | undefined = CallableAdmission | undefined> {
|
|
87
89
|
readonly [CALLABLE_PROJECTION_VALUE]: true;
|
|
88
90
|
readonly input: Input;
|
|
89
91
|
readonly output: Output;
|
|
@@ -91,6 +93,7 @@ export interface CallableProjection<out Input = unknown, out Output = unknown, o
|
|
|
91
93
|
readonly static: Static;
|
|
92
94
|
readonly abstract: Abstract;
|
|
93
95
|
readonly execution: Execution;
|
|
96
|
+
readonly admission: Admission;
|
|
94
97
|
}
|
|
95
98
|
type ValueInputOf<Schema> = Schema extends TypedValueSchema<infer Value> ? Value : Schema extends z.ZodType ? z.input<Schema> : PortableValue;
|
|
96
99
|
type FunctionInputOf<Schema> = Schema extends BinaryInputAuthoring<infer Metadata> ? BinaryInputProjection<ValueInputOf<Metadata>> : ValueInputOf<Schema>;
|
|
@@ -98,6 +101,9 @@ type ValueOutputOf<Schema> = Schema extends TypedValueSchema<infer Value> ? Valu
|
|
|
98
101
|
type ExecutionOfConfig<Config extends CallableInput> = Config extends {
|
|
99
102
|
readonly execution: infer Execution extends CallableExecution;
|
|
100
103
|
} ? Execution : undefined;
|
|
104
|
+
type AdmissionOfConfig<Config extends CallableInput> = Config extends {
|
|
105
|
+
readonly admission: infer Admission extends CallableAdmission;
|
|
106
|
+
} ? Admission : undefined;
|
|
101
107
|
type CallableOutputOf<Input extends CallableOutputInput> = Input extends StreamOutput<infer Item> ? {
|
|
102
108
|
readonly mode: 'stream';
|
|
103
109
|
readonly item: ValueOutputOf<Item>;
|
|
@@ -120,12 +126,12 @@ export type ProjectionOfCallable<Value extends Function | Method> = Value extend
|
|
|
120
126
|
export declare function callableConfiguration(value: Function | Method): CallableInput;
|
|
121
127
|
type PolicyObjectAuthoring = Class | Function | Method | Policy | View | CoreNode;
|
|
122
128
|
export type PolicyObjectInput = Lazy<PolicyObjectAuthoring | PolicyObjectRef>;
|
|
123
|
-
export declare function func<const Config extends FunctionInput, Input = FunctionInputOf<Config['input']>, Output = CallableOutputOf<Config['output']>, Auth extends FunctionAuth = Config['auth'], Execution extends CallableExecution | undefined = ExecutionOfConfig<Config>>(config: Config): Function<CallableProjection<Input, Output, Auth, false, false, Execution>>;
|
|
129
|
+
export declare function func<const Config extends FunctionInput, Input = FunctionInputOf<Config['input']>, Output = CallableOutputOf<Config['output']>, Auth extends FunctionAuth = Config['auth'], Execution extends CallableExecution | undefined = ExecutionOfConfig<Config>, Admission extends CallableAdmission | undefined = AdmissionOfConfig<Config>>(config: Config): Function<CallableProjection<Input, Output, Auth, false, false, Execution, Admission>>;
|
|
124
130
|
export declare function method<const Config extends MethodInput, Input = ValueInputOf<Config['input']>, Output = CallableOutputOf<Config['output']>, Auth extends FunctionAuth = Config['auth'], Static extends boolean = Config extends {
|
|
125
131
|
readonly static: true;
|
|
126
132
|
} ? true : false, Abstract extends boolean = Config extends {
|
|
127
133
|
readonly abstract: infer Value extends boolean;
|
|
128
|
-
} ? Value : false, Execution extends CallableExecution | undefined = ExecutionOfConfig<Config>>(config: Config): Method<CallableProjection<Input, Output, Auth, Static, Abstract, Execution>>;
|
|
134
|
+
} ? Value : false, Execution extends CallableExecution | undefined = ExecutionOfConfig<Config>, Admission extends CallableAdmission | undefined = AdmissionOfConfig<Config>>(config: Config): Method<CallableProjection<Input, Output, Auth, Static, Abstract, Execution, Admission>>;
|
|
129
135
|
export declare function callableInputField(name: string): Extract<PolicyCheckObject, {
|
|
130
136
|
readonly kind: 'input';
|
|
131
137
|
}>;
|
|
@@ -18,6 +18,7 @@ export function func(config) {
|
|
|
18
18
|
'static',
|
|
19
19
|
'abstract',
|
|
20
20
|
'execution',
|
|
21
|
+
'admission',
|
|
21
22
|
]);
|
|
22
23
|
const value = authoringValue('function', {});
|
|
23
24
|
callableInputs.set(value, { ...config });
|
|
@@ -33,6 +34,7 @@ export function method(config) {
|
|
|
33
34
|
'static',
|
|
34
35
|
'abstract',
|
|
35
36
|
'execution',
|
|
37
|
+
'admission',
|
|
36
38
|
]);
|
|
37
39
|
const value = authoringValue('method', {});
|
|
38
40
|
callableInputs.set(value, { ...config });
|
|
@@ -42,6 +42,7 @@ export class V1CallableCompiler {
|
|
|
42
42
|
input,
|
|
43
43
|
output,
|
|
44
44
|
...(config.execution === undefined ? {} : { execution: config.execution }),
|
|
45
|
+
...(config.admission === undefined ? {} : { admission: config.admission }),
|
|
45
46
|
};
|
|
46
47
|
const contract = auth === 'authorized'
|
|
47
48
|
? { ...base, auth, ...(policy === undefined ? {} : { policy }) }
|
|
@@ -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, {
|
|
@@ -116,9 +116,14 @@ export function validateClassFacets(context) {
|
|
|
116
116
|
}
|
|
117
117
|
if (located.value.kind === 'node') {
|
|
118
118
|
for (const [index, method] of located.value.methods.entries()) {
|
|
119
|
-
if (
|
|
119
|
+
if (isMethodExecutable(located.value, method))
|
|
120
|
+
continue;
|
|
121
|
+
if (method.policy !== undefined) {
|
|
120
122
|
fail(`${located.pointer}/methods/${index}/policy`, 'An abstract Method cannot have a Policy.');
|
|
121
123
|
}
|
|
124
|
+
if (method.admission !== undefined) {
|
|
125
|
+
fail(`${located.pointer}/methods/${index}/admission`, 'An abstract Method cannot have an admission age.');
|
|
126
|
+
}
|
|
122
127
|
}
|
|
123
128
|
}
|
|
124
129
|
}
|
|
@@ -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
|
}
|
|
@@ -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`),
|
package/dist/v1/domain/create.js
CHANGED
|
@@ -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,
|
package/dist/v1/index.d.ts
CHANGED
|
@@ -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. */
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
export { admissionInputFields, isAdmissionMaxAge, MAXIMUM_ADMISSION_AGE_SECONDS, } from './callable/admission.js';
|
|
1
2
|
export { isMethodExecutable } from './callable/executable.js';
|
|
2
3
|
export { isBinaryInput } from './callable/model/callable.js';
|
|
3
4
|
export type { ClassRef, CoreRef, DefinitionBase, DefinitionKey, DefinitionRef, FunctionRef, MethodKey, PolicyRef, PropertyKey, ViewRef, } from '../addressing/index.js';
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
export { admissionInputFields, isAdmissionMaxAge, MAXIMUM_ADMISSION_AGE_SECONDS, } from './callable/admission.js';
|
|
1
2
|
export { isMethodExecutable } from './callable/executable.js';
|
|
2
3
|
export { isBinaryInput } from './callable/model/callable.js';
|
|
3
4
|
export { valueProgramIncludes } from './value/inclusion/includes.js';
|
|
@@ -2,6 +2,7 @@ import { diagnostic } from '../../validation/diagnostic.js';
|
|
|
2
2
|
import { validateCanonicalJsonDomain } from '../profile/json.validate.js';
|
|
3
3
|
import { evaluateInstance } from './evaluator.js';
|
|
4
4
|
import { prepareValueSchema, } from './preparation.js';
|
|
5
|
+
import { satisfies } from './validity.optimization.js';
|
|
5
6
|
const validatorCache = new WeakMap();
|
|
6
7
|
/**
|
|
7
8
|
* Validate one complete portable value with the admitted V1 Draft 2020-12 profile.
|
|
@@ -30,6 +31,9 @@ export function validatePreparedInstance(compiled, value) {
|
|
|
30
31
|
: instanceDiagnostics(compiled, value, '');
|
|
31
32
|
}
|
|
32
33
|
function instanceDiagnostics(compiled, value, pointer) {
|
|
34
|
+
// A value the compiled check finds valid has nothing to report; any other is evaluated.
|
|
35
|
+
if (satisfies(compiled, value) === true)
|
|
36
|
+
return [];
|
|
33
37
|
const result = evaluateInstance(compiled, value);
|
|
34
38
|
if (result.valid)
|
|
35
39
|
return [];
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { Value as JsonValue } from '@astrale-os/kernel-dsl/value';
|
|
2
|
+
import type { CompiledInstanceSchema } from './types.js';
|
|
3
|
+
/**
|
|
4
|
+
* Whether a value satisfies its prepared schema, as the evaluator decides it, without building a
|
|
5
|
+
* failure, an instance pointer or an annotation: the evaluator's verdict, or undefined when this
|
|
6
|
+
* check cannot give it. Only the evaluator reports why a value fails, so a caller keeps it for
|
|
7
|
+
* every value this check does not find valid.
|
|
8
|
+
*
|
|
9
|
+
* Each schema location is compiled once into a closure over its keywords, retained with the
|
|
10
|
+
* prepared schema. A schema whose closure holds an annotation-dependent keyword
|
|
11
|
+
* (`unevaluatedItems`, `unevaluatedProperties`) is left to the evaluator, as is a value nested
|
|
12
|
+
* deeper than this check follows, and any value whose check throws.
|
|
13
|
+
*/
|
|
14
|
+
export declare function satisfies(compiled: CompiledInstanceSchema, value: JsonValue): boolean | undefined;
|
|
@@ -0,0 +1,342 @@
|
|
|
1
|
+
import { isCanonicalBlobRef } from '../model/blob.js';
|
|
2
|
+
import { isInstantValue } from '../model/instant.js';
|
|
3
|
+
import { isValueObject as isJsonObject } from '../model/object.js';
|
|
4
|
+
import { codePointLength, isMultipleOf, jsonEqual, matchesType, unique } from './assertions.js';
|
|
5
|
+
/**
|
|
6
|
+
* Whether a value satisfies its prepared schema, as the evaluator decides it, without building a
|
|
7
|
+
* failure, an instance pointer or an annotation: the evaluator's verdict, or undefined when this
|
|
8
|
+
* check cannot give it. Only the evaluator reports why a value fails, so a caller keeps it for
|
|
9
|
+
* every value this check does not find valid.
|
|
10
|
+
*
|
|
11
|
+
* Each schema location is compiled once into a closure over its keywords, retained with the
|
|
12
|
+
* prepared schema. A schema whose closure holds an annotation-dependent keyword
|
|
13
|
+
* (`unevaluatedItems`, `unevaluatedProperties`) is left to the evaluator, as is a value nested
|
|
14
|
+
* deeper than this check follows, and any value whose check throws.
|
|
15
|
+
*/
|
|
16
|
+
export function satisfies(compiled, value) {
|
|
17
|
+
let checks = compilations.get(compiled);
|
|
18
|
+
if (checks === undefined) {
|
|
19
|
+
checks = annotationFree(compiled) ? new Map() : null;
|
|
20
|
+
compilations.set(compiled, checks);
|
|
21
|
+
}
|
|
22
|
+
if (checks === null)
|
|
23
|
+
return undefined;
|
|
24
|
+
const outer = verdicts;
|
|
25
|
+
verdicts = new Map();
|
|
26
|
+
try {
|
|
27
|
+
return checkOf(compiled, checks, compiled.root)(value, 0);
|
|
28
|
+
}
|
|
29
|
+
catch {
|
|
30
|
+
return undefined;
|
|
31
|
+
}
|
|
32
|
+
finally {
|
|
33
|
+
verdicts = outer;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
/** Per prepared schema: its checks by location, or null when its closure needs annotations. */
|
|
37
|
+
const compilations = new WeakMap();
|
|
38
|
+
/** Locations one evaluation follows before leaving the value to the evaluator. */
|
|
39
|
+
const MAXIMUM_DEPTH = 1_024;
|
|
40
|
+
const UNDECIDED = new Error('The value is left to the evaluator.');
|
|
41
|
+
/**
|
|
42
|
+
* The verdicts of the current evaluation by referenced location and value. Only a reference reaches
|
|
43
|
+
* one location from several places, so each referenced location is checked once per value, as the
|
|
44
|
+
* evaluator memoizes it, and a schema that references a location twice per level stays linear. A
|
|
45
|
+
* location entered again on the same value is a cycle, left to the evaluator.
|
|
46
|
+
*/
|
|
47
|
+
let verdicts = new Map();
|
|
48
|
+
function annotationFree(compiled) {
|
|
49
|
+
for (const { schema } of compiled.locations.values()) {
|
|
50
|
+
if (isJsonObject(schema) &&
|
|
51
|
+
(Object.hasOwn(schema, 'unevaluatedItems') || Object.hasOwn(schema, 'unevaluatedProperties'))) {
|
|
52
|
+
return false;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
return true;
|
|
56
|
+
}
|
|
57
|
+
/** The check of one location, compiled on its first use so that references may recur. */
|
|
58
|
+
function checkOf(compiled, checks, location) {
|
|
59
|
+
let check = checks.get(location);
|
|
60
|
+
if (check === undefined) {
|
|
61
|
+
let body;
|
|
62
|
+
check = (value, depth) => {
|
|
63
|
+
if (depth > MAXIMUM_DEPTH)
|
|
64
|
+
throw UNDECIDED;
|
|
65
|
+
body ??= compileLocation(compiled, checks, location);
|
|
66
|
+
return body(value, depth);
|
|
67
|
+
};
|
|
68
|
+
checks.set(location, check);
|
|
69
|
+
}
|
|
70
|
+
return check;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* The conjunction of the location's keywords, each as the evaluator applies it: an applicator
|
|
74
|
+
* checks the same value at its child location, a keyword of one type passes every other type.
|
|
75
|
+
*/
|
|
76
|
+
function compileLocation(compiled, checks, location) {
|
|
77
|
+
const schema = location.schema;
|
|
78
|
+
if (schema === true)
|
|
79
|
+
return () => true;
|
|
80
|
+
if (schema === false)
|
|
81
|
+
return () => false;
|
|
82
|
+
const child = (keyword, member) => checkOf(compiled, checks, compiled.child(location, keyword, member));
|
|
83
|
+
const parts = [];
|
|
84
|
+
if (Object.hasOwn(schema, '$ref')) {
|
|
85
|
+
const target = compiled.reference(location);
|
|
86
|
+
if (target === undefined)
|
|
87
|
+
throw UNDECIDED;
|
|
88
|
+
const check = checkOf(compiled, checks, target);
|
|
89
|
+
parts.push((value, depth) => {
|
|
90
|
+
let byValue = verdicts.get(target);
|
|
91
|
+
if (byValue === undefined)
|
|
92
|
+
verdicts.set(target, (byValue = new Map()));
|
|
93
|
+
const known = byValue.get(value);
|
|
94
|
+
if (known === null)
|
|
95
|
+
throw UNDECIDED;
|
|
96
|
+
if (known !== undefined)
|
|
97
|
+
return known;
|
|
98
|
+
byValue.set(value, null);
|
|
99
|
+
const verdict = check(value, depth + 1);
|
|
100
|
+
byValue.set(value, verdict);
|
|
101
|
+
return verdict;
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
compileCompositions(schema, child, parts);
|
|
105
|
+
compileCommon(schema, parts);
|
|
106
|
+
compileNumber(schema, parts);
|
|
107
|
+
compileString(compiled, schema, parts);
|
|
108
|
+
compileArray(schema, child, parts);
|
|
109
|
+
compileObject(compiled, schema, child, parts);
|
|
110
|
+
if (parts.length === 0)
|
|
111
|
+
return () => true;
|
|
112
|
+
if (parts.length === 1)
|
|
113
|
+
return parts[0];
|
|
114
|
+
return (value, depth) => {
|
|
115
|
+
for (const part of parts)
|
|
116
|
+
if (!part(value, depth))
|
|
117
|
+
return false;
|
|
118
|
+
return true;
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
function compileCompositions(schema, child, parts) {
|
|
122
|
+
const allOf = schemaList(schema.allOf)?.map((_, index) => child('allOf', index));
|
|
123
|
+
if (allOf !== undefined) {
|
|
124
|
+
parts.push((value, depth) => allOf.every((check) => check(value, depth + 1)));
|
|
125
|
+
}
|
|
126
|
+
const anyOf = schemaList(schema.anyOf)?.map((_, index) => child('anyOf', index));
|
|
127
|
+
if (anyOf !== undefined) {
|
|
128
|
+
parts.push((value, depth) => anyOf.some((check) => check(value, depth + 1)));
|
|
129
|
+
}
|
|
130
|
+
const oneOf = schemaList(schema.oneOf)?.map((_, index) => child('oneOf', index));
|
|
131
|
+
if (oneOf !== undefined) {
|
|
132
|
+
parts.push((value, depth) => {
|
|
133
|
+
let admitted = 0;
|
|
134
|
+
for (const check of oneOf)
|
|
135
|
+
if (check(value, depth + 1) && ++admitted > 1)
|
|
136
|
+
return false;
|
|
137
|
+
return admitted === 1;
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
if (isSchema(schema.not)) {
|
|
141
|
+
const not = child('not');
|
|
142
|
+
parts.push((value, depth) => !not(value, depth + 1));
|
|
143
|
+
}
|
|
144
|
+
if (isSchema(schema.if)) {
|
|
145
|
+
const condition = child('if');
|
|
146
|
+
const then = isSchema(schema.then) ? child('then') : undefined;
|
|
147
|
+
const otherwise = isSchema(schema.else) ? child('else') : undefined;
|
|
148
|
+
parts.push((value, depth) => {
|
|
149
|
+
const branch = condition(value, depth + 1) ? then : otherwise;
|
|
150
|
+
return branch === undefined || branch(value, depth + 1);
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
function compileCommon(schema, parts) {
|
|
155
|
+
const kind = schema['x-astrale-value-kind'];
|
|
156
|
+
if (kind === 'instant')
|
|
157
|
+
parts.push((value) => isInstantValue(value));
|
|
158
|
+
if (kind === 'blob')
|
|
159
|
+
parts.push((value) => isCanonicalBlobRef(value));
|
|
160
|
+
const type = schema.type;
|
|
161
|
+
if (typeof type === 'string')
|
|
162
|
+
parts.push((value) => matchesType(type, value));
|
|
163
|
+
else if (Array.isArray(type)) {
|
|
164
|
+
const types = type.filter((candidate) => typeof candidate === 'string');
|
|
165
|
+
parts.push((value) => types.some((candidate) => matchesType(candidate, value)));
|
|
166
|
+
}
|
|
167
|
+
if (Object.hasOwn(schema, 'const')) {
|
|
168
|
+
const expected = schema.const;
|
|
169
|
+
parts.push((value) => jsonEqual(value, expected));
|
|
170
|
+
}
|
|
171
|
+
const choices = schema.enum;
|
|
172
|
+
if (Array.isArray(choices)) {
|
|
173
|
+
parts.push((value) => choices.some((candidate) => jsonEqual(value, candidate)));
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
function compileNumber(schema, parts) {
|
|
177
|
+
const { multipleOf, maximum, exclusiveMaximum, minimum, exclusiveMinimum } = schema;
|
|
178
|
+
const bounded = typeof multipleOf === 'number' ||
|
|
179
|
+
typeof maximum === 'number' ||
|
|
180
|
+
typeof exclusiveMaximum === 'number' ||
|
|
181
|
+
typeof minimum === 'number' ||
|
|
182
|
+
typeof exclusiveMinimum === 'number';
|
|
183
|
+
if (!bounded)
|
|
184
|
+
return;
|
|
185
|
+
parts.push((value) => typeof value !== 'number' ||
|
|
186
|
+
((typeof multipleOf !== 'number' || isMultipleOf(value, multipleOf)) &&
|
|
187
|
+
(typeof maximum !== 'number' || value <= maximum) &&
|
|
188
|
+
(typeof exclusiveMaximum !== 'number' || value < exclusiveMaximum) &&
|
|
189
|
+
(typeof minimum !== 'number' || value >= minimum) &&
|
|
190
|
+
(typeof exclusiveMinimum !== 'number' || value > exclusiveMinimum)));
|
|
191
|
+
}
|
|
192
|
+
function compileString(compiled, schema, parts) {
|
|
193
|
+
const { maxLength, minLength, pattern, fullMatch } = schema;
|
|
194
|
+
if (typeof maxLength === 'number' || typeof minLength === 'number') {
|
|
195
|
+
// For n UTF-16 units, ceil(n / 2) <= code points <= n: count only when the bounds cannot
|
|
196
|
+
// decide, as the evaluator does.
|
|
197
|
+
parts.push((value) => {
|
|
198
|
+
if (typeof value !== 'string')
|
|
199
|
+
return true;
|
|
200
|
+
if ((typeof maxLength !== 'number' || value.length <= maxLength) &&
|
|
201
|
+
(typeof minLength !== 'number' || Math.ceil(value.length / 2) >= minLength)) {
|
|
202
|
+
return true;
|
|
203
|
+
}
|
|
204
|
+
const length = codePointLength(value);
|
|
205
|
+
return ((typeof maxLength !== 'number' || length <= maxLength) &&
|
|
206
|
+
(typeof minLength !== 'number' || length >= minLength));
|
|
207
|
+
});
|
|
208
|
+
}
|
|
209
|
+
if (typeof pattern === 'string') {
|
|
210
|
+
const matcher = compiled.matcher(pattern);
|
|
211
|
+
parts.push((value) => typeof value !== 'string' || matcher.test(value));
|
|
212
|
+
}
|
|
213
|
+
if (typeof fullMatch === 'string') {
|
|
214
|
+
const matcher = compiled.matcher(fullMatch);
|
|
215
|
+
parts.push((value) => typeof value !== 'string' || matcher.matches(value));
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
function compileArray(schema, child, parts) {
|
|
219
|
+
const { maxItems, minItems } = schema;
|
|
220
|
+
if (typeof maxItems === 'number' || typeof minItems === 'number' || schema.uniqueItems === true) {
|
|
221
|
+
const distinct = schema.uniqueItems === true;
|
|
222
|
+
parts.push((value) => !Array.isArray(value) ||
|
|
223
|
+
((typeof maxItems !== 'number' || value.length <= maxItems) &&
|
|
224
|
+
(typeof minItems !== 'number' || value.length >= minItems) &&
|
|
225
|
+
(!distinct || unique(value))));
|
|
226
|
+
}
|
|
227
|
+
const prefix = schemaList(schema.prefixItems)?.map((_, index) => child('prefixItems', index));
|
|
228
|
+
const items = isSchema(schema.items) ? child('items') : undefined;
|
|
229
|
+
if (prefix !== undefined || items !== undefined) {
|
|
230
|
+
const leading = prefix ?? [];
|
|
231
|
+
parts.push((value, depth) => {
|
|
232
|
+
if (!Array.isArray(value))
|
|
233
|
+
return true;
|
|
234
|
+
for (let index = 0; index < value.length; index++) {
|
|
235
|
+
const check = index < leading.length ? leading[index] : items;
|
|
236
|
+
if (check !== undefined && !check(value[index], depth + 1))
|
|
237
|
+
return false;
|
|
238
|
+
}
|
|
239
|
+
return true;
|
|
240
|
+
});
|
|
241
|
+
}
|
|
242
|
+
if (isSchema(schema.contains)) {
|
|
243
|
+
const contains = child('contains');
|
|
244
|
+
const least = typeof schema.minContains === 'number' ? schema.minContains : 1;
|
|
245
|
+
const most = typeof schema.maxContains === 'number' ? schema.maxContains : Number.POSITIVE_INFINITY;
|
|
246
|
+
parts.push((value, depth) => {
|
|
247
|
+
if (!Array.isArray(value))
|
|
248
|
+
return true;
|
|
249
|
+
let matches = 0;
|
|
250
|
+
for (const item of value)
|
|
251
|
+
if (contains(item, depth + 1))
|
|
252
|
+
matches++;
|
|
253
|
+
return matches >= least && matches <= most;
|
|
254
|
+
});
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
function compileObject(compiled, schema, child, parts) {
|
|
258
|
+
const { maxProperties, minProperties } = schema;
|
|
259
|
+
const required = Array.isArray(schema.required)
|
|
260
|
+
? schema.required.filter((name) => typeof name === 'string')
|
|
261
|
+
: [];
|
|
262
|
+
const dependentRequired = isJsonObject(schema.dependentRequired)
|
|
263
|
+
? Object.entries(schema.dependentRequired).flatMap(([name, names]) => Array.isArray(names)
|
|
264
|
+
? [[name, names.filter((other) => typeof other === 'string')]]
|
|
265
|
+
: [])
|
|
266
|
+
: [];
|
|
267
|
+
if (typeof maxProperties === 'number' ||
|
|
268
|
+
typeof minProperties === 'number' ||
|
|
269
|
+
required.length > 0 ||
|
|
270
|
+
dependentRequired.length > 0) {
|
|
271
|
+
parts.push((value) => {
|
|
272
|
+
if (!isJsonObject(value))
|
|
273
|
+
return true;
|
|
274
|
+
if (typeof maxProperties === 'number' || typeof minProperties === 'number') {
|
|
275
|
+
const count = Object.keys(value).length;
|
|
276
|
+
if (typeof maxProperties === 'number' && count > maxProperties)
|
|
277
|
+
return false;
|
|
278
|
+
if (typeof minProperties === 'number' && count < minProperties)
|
|
279
|
+
return false;
|
|
280
|
+
}
|
|
281
|
+
for (const name of required)
|
|
282
|
+
if (!Object.hasOwn(value, name))
|
|
283
|
+
return false;
|
|
284
|
+
for (const [name, names] of dependentRequired) {
|
|
285
|
+
if (Object.hasOwn(value, name) && !names.every((other) => Object.hasOwn(value, other))) {
|
|
286
|
+
return false;
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
return true;
|
|
290
|
+
});
|
|
291
|
+
}
|
|
292
|
+
// Each member is checked by every pattern it matches, by its declared schema, and by the
|
|
293
|
+
// additional schema when neither declares it.
|
|
294
|
+
const declared = isJsonObject(schema.properties) ? schema.properties : {};
|
|
295
|
+
const properties = new Map(Object.keys(declared).map((name) => [name, child('properties', name)]));
|
|
296
|
+
const patterns = isJsonObject(schema.patternProperties)
|
|
297
|
+
? Object.keys(schema.patternProperties).map((source) => [compiled.matcher(source), child('patternProperties', source)])
|
|
298
|
+
: [];
|
|
299
|
+
const additional = isSchema(schema.additionalProperties)
|
|
300
|
+
? child('additionalProperties')
|
|
301
|
+
: undefined;
|
|
302
|
+
const names = isSchema(schema.propertyNames) ? child('propertyNames') : undefined;
|
|
303
|
+
if (properties.size > 0 || patterns.length > 0 || additional !== undefined || names) {
|
|
304
|
+
parts.push((value, depth) => {
|
|
305
|
+
if (!isJsonObject(value))
|
|
306
|
+
return true;
|
|
307
|
+
for (const name of Object.keys(value)) {
|
|
308
|
+
const member = value[name];
|
|
309
|
+
let matched = false;
|
|
310
|
+
for (const [matcher, check] of patterns) {
|
|
311
|
+
if (!matcher.test(name))
|
|
312
|
+
continue;
|
|
313
|
+
matched = true;
|
|
314
|
+
if (!check(member, depth + 1))
|
|
315
|
+
return false;
|
|
316
|
+
}
|
|
317
|
+
const check = Object.hasOwn(declared, name) ? properties.get(name) : undefined;
|
|
318
|
+
if (check !== undefined) {
|
|
319
|
+
if (!check(member, depth + 1))
|
|
320
|
+
return false;
|
|
321
|
+
}
|
|
322
|
+
else if (!matched && additional !== undefined && !additional(member, depth + 1)) {
|
|
323
|
+
return false;
|
|
324
|
+
}
|
|
325
|
+
if (names !== undefined && !names(name, depth + 1))
|
|
326
|
+
return false;
|
|
327
|
+
}
|
|
328
|
+
return true;
|
|
329
|
+
});
|
|
330
|
+
}
|
|
331
|
+
if (isJsonObject(schema.dependentSchemas)) {
|
|
332
|
+
const dependent = Object.keys(schema.dependentSchemas).map((name) => [name, child('dependentSchemas', name)]);
|
|
333
|
+
parts.push((value, depth) => !isJsonObject(value) ||
|
|
334
|
+
dependent.every(([name, check]) => !Object.hasOwn(value, name) || check(value, depth + 1)));
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
function schemaList(input) {
|
|
338
|
+
return Array.isArray(input) ? input : undefined;
|
|
339
|
+
}
|
|
340
|
+
function isSchema(input) {
|
|
341
|
+
return typeof input === 'boolean' || isJsonObject(input);
|
|
342
|
+
}
|
|
@@ -50,9 +50,11 @@ export declare const PROPERTY_FIELDS: Readonly<{
|
|
|
50
50
|
}>;
|
|
51
51
|
}>;
|
|
52
52
|
/**
|
|
53
|
-
* The auth level
|
|
54
|
-
*
|
|
55
|
-
*
|
|
53
|
+
* The auth level, the Policy check and the admission age are authorization: decided by the Runtime
|
|
54
|
+
* at invocation, never compiled against. The admission age only bounds how old the admission a
|
|
55
|
+
* destination decides a call under may be. The execution modifier decides what an invocation
|
|
56
|
+
* returns; an accepted Schema carries it only when durable, so ordinary callables keep their meaning
|
|
57
|
+
* unchanged.
|
|
56
58
|
*/
|
|
57
59
|
export declare const CALLABLE_FIELDS: Readonly<{
|
|
58
60
|
ref: Readonly<{
|
|
@@ -63,6 +65,9 @@ export declare const CALLABLE_FIELDS: Readonly<{
|
|
|
63
65
|
static: "always";
|
|
64
66
|
abstract: "always";
|
|
65
67
|
execution: "always";
|
|
68
|
+
admission: Readonly<{
|
|
69
|
+
excluded: 'authorization';
|
|
70
|
+
}>;
|
|
66
71
|
auth: Readonly<{
|
|
67
72
|
excluded: 'authorization';
|
|
68
73
|
}>;
|
|
@@ -148,6 +153,9 @@ export declare const FIELD_ROLES: Readonly<{
|
|
|
148
153
|
static: "always";
|
|
149
154
|
abstract: "always";
|
|
150
155
|
execution: "always";
|
|
156
|
+
admission: Readonly<{
|
|
157
|
+
excluded: 'authorization';
|
|
158
|
+
}>;
|
|
151
159
|
auth: Readonly<{
|
|
152
160
|
excluded: 'authorization';
|
|
153
161
|
}>;
|
|
@@ -27,9 +27,11 @@ export const PROPERTY_FIELDS = Object.freeze({
|
|
|
27
27
|
description: ANNOTATION,
|
|
28
28
|
});
|
|
29
29
|
/**
|
|
30
|
-
* The auth level
|
|
31
|
-
*
|
|
32
|
-
*
|
|
30
|
+
* The auth level, the Policy check and the admission age are authorization: decided by the Runtime
|
|
31
|
+
* at invocation, never compiled against. The admission age only bounds how old the admission a
|
|
32
|
+
* destination decides a call under may be. The execution modifier decides what an invocation
|
|
33
|
+
* returns; an accepted Schema carries it only when durable, so ordinary callables keep their meaning
|
|
34
|
+
* unchanged.
|
|
33
35
|
*/
|
|
34
36
|
export const CALLABLE_FIELDS = Object.freeze({
|
|
35
37
|
ref: IDENTITY,
|
|
@@ -38,6 +40,7 @@ export const CALLABLE_FIELDS = Object.freeze({
|
|
|
38
40
|
static: 'always',
|
|
39
41
|
abstract: 'always',
|
|
40
42
|
execution: 'always',
|
|
43
|
+
admission: AUTHORIZATION,
|
|
41
44
|
auth: AUTHORIZATION,
|
|
42
45
|
policy: AUTHORIZATION,
|
|
43
46
|
description: ANNOTATION,
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export type SchemaDiagnosticCode = 'DM_CAPABILITY_INVALID' | 'DM_MEMBER_ROOT_INVALID' | 'DM_DEPENDENCY_CONTEXT_INVALID' | 'DM_DEPENDENCY_NOT_RETAINED' | 'DM_MEANING_INCONSISTENT' | 'DM_MEMBER_ORIGIN_MISMATCH' | 'DV_ACCEPTANCE_CONTEXT' | 'DV_CALLABLE_AUTH' | 'DV_CALLABLE_IO' | 'DV_CALLABLE_POLICY_AUTH' | 'DV_CLASS_ABSTRACT' | 'DV_CLASS_ACYCLIC' | 'DV_CLASS_KIND' | 'DV_CORE_EDGE_CLASS' | 'DV_CORE_EDGE_CONSTRAINTS' | 'DV_CORE_EDGE_KEY' | 'DV_CORE_EDGE_PROPERTIES' | 'DV_CORE_NODE_CLASS' | 'DV_CORE_NODE_PROPERTIES' | 'DV_CORE_REF' | 'DV_DATA_COMPOSITION' | 'DV_DEPENDENCY_ACYCLIC' | 'DV_DEPENDENCY_DIRECT' | 'DV_DEPENDENCY_EXACT' | 'DV_DEPENDENCY_LIMITS' | 'DV_DEPENDENCY_REVISION_COHERENCE' | 'DV_DEPENDENCY_UNIQUE' | 'DV_EDGE_ACCEPTS' | 'DV_EDGE_CLASS_COMPOSITION' | 'DV_EDGE_CONSTRAINT' | 'DV_EDGE_FOREIGN_MINIMUM' | 'DV_EDGE_ROLE' | 'DV_EDGE_UNDIRECTED' | 'DV_ENTRYPOINT' | 'DV_JSON_VALUE' | 'DV_METHOD_ABSTRACT' | 'DV_METHOD_COMPOSITION' | 'DV_PROPERTY_AUTO' | 'DV_PROPERTY_COMPOSITION' | 'DV_PROPERTY_IMMUTABLE' | 'DV_PROPERTY_STATE' | 'DV_REF_RESOLVES' | 'DV_VALUE_SCHEMA' | 'PL_BUDGET' | 'PL_COMPOSITION' | 'PL_CONNECTIVITY' | 'PL_REF' | 'PL_REPEAT' | 'PL_TARGET' | 'PL_TYPING' | 'PL_USE' | 'PL_VARIABLE_SCOPE' | 'STRUCTURE_ABSTRACT_POLICY' | 'STRUCTURE_CALLABLE_POLICY_AUTH' | 'STRUCTURE_DUPLICATE_ITEM' | 'STRUCTURE_EDGE_ORIENTATION' | 'STRUCTURE_FORMAT' | 'STRUCTURE_INVALID' | 'STRUCTURE_KEY' | 'STRUCTURE_MAXIMUM' | 'STRUCTURE_MINIMUM' | 'STRUCTURE_MULTIPLE' | 'STRUCTURE_REQUIRED' | 'STRUCTURE_TYPE' | 'STRUCTURE_UNION' | 'STRUCTURE_UNKNOWN_FIELD' | 'STRUCTURE_VALUE' | 'UNSUPPORTED_DOCUMENT_FORMAT' | 'UNSUPPORTED_DSL_VERSION';
|
|
1
|
+
export type SchemaDiagnosticCode = 'DM_CAPABILITY_INVALID' | 'DM_MEMBER_ROOT_INVALID' | 'DM_DEPENDENCY_CONTEXT_INVALID' | 'DM_DEPENDENCY_NOT_RETAINED' | 'DM_MEANING_INCONSISTENT' | 'DM_MEMBER_ORIGIN_MISMATCH' | 'DV_ACCEPTANCE_CONTEXT' | 'DV_CALLABLE_ADMISSION' | 'DV_CALLABLE_AUTH' | 'DV_CALLABLE_IO' | 'DV_CALLABLE_POLICY_AUTH' | 'DV_CLASS_ABSTRACT' | 'DV_CLASS_ACYCLIC' | 'DV_CLASS_KIND' | 'DV_CORE_EDGE_CLASS' | 'DV_CORE_EDGE_CONSTRAINTS' | 'DV_CORE_EDGE_KEY' | 'DV_CORE_EDGE_PROPERTIES' | 'DV_CORE_NODE_CLASS' | 'DV_CORE_NODE_PROPERTIES' | 'DV_CORE_REF' | 'DV_DATA_COMPOSITION' | 'DV_DEPENDENCY_ACYCLIC' | 'DV_DEPENDENCY_DIRECT' | 'DV_DEPENDENCY_EXACT' | 'DV_DEPENDENCY_LIMITS' | 'DV_DEPENDENCY_REVISION_COHERENCE' | 'DV_DEPENDENCY_UNIQUE' | 'DV_EDGE_ACCEPTS' | 'DV_EDGE_CLASS_COMPOSITION' | 'DV_EDGE_CONSTRAINT' | 'DV_EDGE_FOREIGN_MINIMUM' | 'DV_EDGE_ROLE' | 'DV_EDGE_UNDIRECTED' | 'DV_ENTRYPOINT' | 'DV_JSON_VALUE' | 'DV_METHOD_ABSTRACT' | 'DV_METHOD_COMPOSITION' | 'DV_PROPERTY_AUTO' | 'DV_PROPERTY_COMPOSITION' | 'DV_PROPERTY_IMMUTABLE' | 'DV_PROPERTY_STATE' | 'DV_REF_RESOLVES' | 'DV_VALUE_SCHEMA' | 'PL_BUDGET' | 'PL_COMPOSITION' | 'PL_CONNECTIVITY' | 'PL_REF' | 'PL_REPEAT' | 'PL_TARGET' | 'PL_TYPING' | 'PL_USE' | 'PL_VARIABLE_SCOPE' | 'STRUCTURE_ABSTRACT_POLICY' | 'STRUCTURE_CALLABLE_POLICY_AUTH' | 'STRUCTURE_DUPLICATE_ITEM' | 'STRUCTURE_EDGE_ORIENTATION' | 'STRUCTURE_FORMAT' | 'STRUCTURE_INVALID' | 'STRUCTURE_KEY' | 'STRUCTURE_MAXIMUM' | 'STRUCTURE_MINIMUM' | 'STRUCTURE_MULTIPLE' | 'STRUCTURE_REQUIRED' | 'STRUCTURE_TYPE' | 'STRUCTURE_UNION' | 'STRUCTURE_UNKNOWN_FIELD' | 'STRUCTURE_VALUE' | 'UNSUPPORTED_DOCUMENT_FORMAT' | 'UNSUPPORTED_DSL_VERSION';
|
|
2
2
|
export interface SchemaDiagnostic<out Code extends SchemaDiagnosticCode = SchemaDiagnosticCode> {
|
|
3
3
|
readonly code: Code;
|
|
4
4
|
readonly pointer: string;
|
|
@@ -115,6 +115,8 @@ function canonicalizeCallable(value) {
|
|
|
115
115
|
output: canonicalizeCallableOutput(value.output),
|
|
116
116
|
// Ordinary execution has one canonical form, so existing revisions never move.
|
|
117
117
|
...(value.execution?.durable === true ? { execution: { durable: true } } : {}),
|
|
118
|
+
// Absence is the one spelling of a call admitted afresh; validation refuses any other.
|
|
119
|
+
...(value.admission === undefined ? {} : { admission: { maxAge: value.admission.maxAge } }),
|
|
118
120
|
};
|
|
119
121
|
return value.auth === 'authorized'
|
|
120
122
|
? {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import type { SchemaDiagnostic } from '../diagnostic.js';
|
|
2
2
|
import type { DomainSchemaIR } from '../model/schema.js';
|
|
3
|
-
/** Validate the execution modifier of every local Function and Node Method. */
|
|
3
|
+
/** Validate the execution modifier and admission age of every local Function and Node Method. */
|
|
4
4
|
export declare function validateCallables(schema: DomainSchemaIR): readonly SchemaDiagnostic[];
|
|
@@ -1,10 +1,13 @@
|
|
|
1
|
+
import { isAdmissionMaxAge, MAXIMUM_ADMISSION_AGE_SECONDS, } from '../../language/callable/admission.js';
|
|
1
2
|
import { isBinaryInput } from '../../language/callable/model/callable.js';
|
|
2
3
|
import { diagnostic } from '../diagnostic.js';
|
|
3
|
-
/** Validate the execution modifier of every local Function and Node Method. */
|
|
4
|
+
/** Validate the execution modifier and admission age of every local Function and Node Method. */
|
|
4
5
|
export function validateCallables(schema) {
|
|
5
6
|
const diagnostics = [];
|
|
6
7
|
for (const [name, declaration] of Object.entries(schema.functions)) {
|
|
7
|
-
|
|
8
|
+
const pointer = `/functions/${escapePointer(name)}`;
|
|
9
|
+
validateExecution(declaration, pointer, diagnostics);
|
|
10
|
+
validateAdmission(declaration, pointer, diagnostics);
|
|
8
11
|
}
|
|
9
12
|
for (const [name, declaration] of Object.entries(schema.classes)) {
|
|
10
13
|
if (declaration.kind !== 'node')
|
|
@@ -12,10 +15,22 @@ export function validateCallables(schema) {
|
|
|
12
15
|
for (const [method, value] of Object.entries(declaration.methods)) {
|
|
13
16
|
const pointer = `/classes/${escapePointer(name)}/methods/${escapePointer(method)}`;
|
|
14
17
|
validateExecution(value, pointer, diagnostics);
|
|
18
|
+
validateAdmission(value, pointer, diagnostics);
|
|
15
19
|
}
|
|
16
20
|
}
|
|
17
21
|
return Object.freeze(diagnostics);
|
|
18
22
|
}
|
|
23
|
+
/**
|
|
24
|
+
* An admission age is a whole number of seconds within the language ceiling. It has no zero
|
|
25
|
+
* spelling: an absent admission age is the one canonical form of a call admitted afresh.
|
|
26
|
+
*/
|
|
27
|
+
function validateAdmission(declaration, pointer, diagnostics) {
|
|
28
|
+
if (declaration.admission === undefined)
|
|
29
|
+
return;
|
|
30
|
+
if (isAdmissionMaxAge(declaration.admission.maxAge))
|
|
31
|
+
return;
|
|
32
|
+
diagnostics.push(diagnostic('DV_CALLABLE_ADMISSION', `${pointer}/admission/maxAge`, `An admission age is an integer number of seconds from 1 to ${MAXIMUM_ADMISSION_AGE_SECONDS}; omit it to admit every call afresh.`));
|
|
33
|
+
}
|
|
19
34
|
/**
|
|
20
35
|
* An Execution records its initiating caller and its admitted input, from which the run is
|
|
21
36
|
* resubmitted, and retains one portable outcome. A durable callable therefore needs a known caller,
|
|
@@ -87,9 +87,15 @@ function validateFacet(environment, declaration, pointer, diagnostics) {
|
|
|
87
87
|
return;
|
|
88
88
|
for (const [name, method] of Object.entries(declaration.methods)) {
|
|
89
89
|
const methodPointer = `${pointer}/methods/${escapePointer(name)}`;
|
|
90
|
-
if (
|
|
90
|
+
if (isMethodExecutable(declaration, method))
|
|
91
|
+
continue;
|
|
92
|
+
if (method.policy !== undefined) {
|
|
91
93
|
diagnostics.push(diagnostic('DV_METHOD_ABSTRACT', `${methodPointer}/policy`, 'Abstract Methods cannot have a Policy.'));
|
|
92
94
|
}
|
|
95
|
+
// An admission age qualifies the admission of an executable declaration, like its Policy.
|
|
96
|
+
if (method.admission !== undefined) {
|
|
97
|
+
diagnostics.push(diagnostic('DV_METHOD_ABSTRACT', `${methodPointer}/admission`, 'Abstract Methods cannot have an admission age.'));
|
|
98
|
+
}
|
|
93
99
|
}
|
|
94
100
|
}
|
|
95
101
|
function validateParents(environment, owner, ownerKind, parents, diagnostics) {
|
|
@@ -195,11 +195,16 @@ const valueFunctionInputSchema = jsonSchemaSchema.refine((value) => typeof value
|
|
|
195
195
|
const callableExecutionSchema = strictObject({
|
|
196
196
|
durable: boolean(),
|
|
197
197
|
});
|
|
198
|
+
// Its range is language meaning, refused by callable validation with DV_CALLABLE_ADMISSION.
|
|
199
|
+
const callableAdmissionSchema = strictObject({
|
|
200
|
+
maxAge: number(),
|
|
201
|
+
});
|
|
198
202
|
const callableShape = {
|
|
199
203
|
description: descriptionSchema.optional(),
|
|
200
204
|
input: jsonSchemaSchema,
|
|
201
205
|
output: callableOutputSchema,
|
|
202
206
|
execution: callableExecutionSchema.optional(),
|
|
207
|
+
admission: callableAdmissionSchema.optional(),
|
|
203
208
|
auth: enumeration(['anonymous', 'authenticated', 'authorized']),
|
|
204
209
|
policy: union([literal(true), policyCheckExpressionSchema]).optional(),
|
|
205
210
|
};
|
|
@@ -5,3 +5,8 @@ export declare function serialize(input: unknown, options?: Options): string;
|
|
|
5
5
|
/** Serialize a trusted portable value whose default structural admission proof is still valid. */
|
|
6
6
|
export declare function serializeAdmitted(input: Value): string;
|
|
7
7
|
export declare function encode(input: unknown, options?: Options): Uint8Array;
|
|
8
|
+
/**
|
|
9
|
+
* The UTF-8 byte length of a trusted Value's canonical JSON, `encode(input).byteLength`: an
|
|
10
|
+
* admission-owned immutable root counted its bytes when it was admitted, so it is not serialized.
|
|
11
|
+
*/
|
|
12
|
+
export declare function byteLength(input: Value): number;
|
package/dist/value/canonical.js
CHANGED
|
@@ -31,6 +31,18 @@ function serializeSnapshot(input, maxBytes) {
|
|
|
31
31
|
export function encode(input, options) {
|
|
32
32
|
return encoder.encode(serialize(input, options));
|
|
33
33
|
}
|
|
34
|
+
/**
|
|
35
|
+
* The UTF-8 byte length of a trusted Value's canonical JSON, `encode(input).byteLength`: an
|
|
36
|
+
* admission-owned immutable root counted its bytes when it was admitted, so it is not serialized.
|
|
37
|
+
*/
|
|
38
|
+
export function byteLength(input) {
|
|
39
|
+
if (input !== null && typeof input === 'object') {
|
|
40
|
+
const bytes = immutableSnapshotBytes(input);
|
|
41
|
+
if (bytes !== undefined)
|
|
42
|
+
return bytes;
|
|
43
|
+
}
|
|
44
|
+
return utf8Length(serializeAdmitted(input));
|
|
45
|
+
}
|
|
34
46
|
function serializeValue(value) {
|
|
35
47
|
if (value === null)
|
|
36
48
|
return 'null';
|
package/dist/value/json.d.ts
CHANGED
package/dist/value/json.js
CHANGED