@astrale-os/kernel-dsl 1.0.0-beta.4 → 1.0.0-beta.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/v1/builder/callable/authoring.d.ts +10 -4
- package/dist/v1/builder/callable/authoring.js +2 -0
- package/dist/v1/builder/class/authoring.d.ts +11 -1
- package/dist/v1/builder/class/authoring.js +11 -1
- package/dist/v1/builder/lowering/callable.js +1 -0
- package/dist/v1/builder/lowering/class.js +7 -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 +8 -1
- package/dist/v1/compiled/admission/wire/callable.js +11 -1
- package/dist/v1/compiled/admission/wire/class.js +24 -2
- 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 -1
- 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/class/inheritance.d.ts +9 -5
- package/dist/v1/language/class/inheritance.js +4 -0
- package/dist/v1/language/class/model/class.d.ts +9 -0
- package/dist/v1/language/class/model/edge.d.ts +11 -0
- package/dist/v1/language/index.d.ts +2 -0
- package/dist/v1/language/index.js +2 -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/language/value/profile/canonicalize.js +6 -5
- package/dist/v1/schema/compatibility/comparators/classes.d.ts +3 -0
- package/dist/v1/schema/compatibility/comparators/classes.js +4 -0
- package/dist/v1/schema/compatibility/comparators/index.d.ts +1 -1
- package/dist/v1/schema/compatibility/fields.d.ts +20 -4
- package/dist/v1/schema/compatibility/fields.js +12 -3
- package/dist/v1/schema/compatibility/model.d.ts +4 -3
- package/dist/v1/schema/compatibility/structure/index.js +6 -4
- package/dist/v1/schema/compatibility/structure/requirements.js +29 -1
- package/dist/v1/schema/compatibility/versioning/surface.d.ts +1 -1
- package/dist/v1/schema/compatibility/versioning/surface.js +25 -2
- package/dist/v1/schema/diagnostic.d.ts +1 -1
- package/dist/v1/schema/normalization/domain.js +4 -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 +10 -1
- package/dist/v1/schema/validation/domain.structure.js +29 -8
- 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
|
@@ -46,7 +46,7 @@ export interface Usage {
|
|
|
46
46
|
readonly key?: Key;
|
|
47
47
|
readonly relation: ReferenceSite | 'member' | 'abstract' | 'selection' | 'capability';
|
|
48
48
|
}
|
|
49
|
-
export type Facet = 'class.reference' | 'class.inherit' | 'class.composition' | 'class.properties' | 'class.endpoints' | 'property' | 'property.required' | 'method.existence' | 'method.object' | 'method.inheritance' | 'method.signature' | 'function.existence' | 'function.signature' | 'policy' | 'view' | 'core';
|
|
49
|
+
export type Facet = 'class.reference' | 'class.inherit' | 'class.composition' | 'class.properties' | 'class.endpoints' | 'class.inheritance' | 'property' | 'property.required' | 'method.existence' | 'method.object' | 'method.inheritance' | 'method.signature' | 'function.existence' | 'function.signature' | 'policy' | 'view' | 'core';
|
|
50
50
|
/** A fact about a compared facet, including unchanged facets. No admission decision is implied. */
|
|
51
51
|
export interface Observation {
|
|
52
52
|
readonly id: string;
|
|
@@ -81,8 +81,9 @@ export interface Assessment {
|
|
|
81
81
|
* meaning evidence never records whether capabilities were declared. A referenced or inherited Class
|
|
82
82
|
* that loses an effective Property (`class.properties`) raises structure to version 4 and meaning to
|
|
83
83
|
* version 2, and structure is version 4 too when it records that a dependent Policy lost what it
|
|
84
|
-
* relies on (`method.object`, `class.endpoints`)
|
|
85
|
-
*
|
|
84
|
+
* relies on (`method.object`, `class.endpoints`) or that a Class the dependent extends became final
|
|
85
|
+
* (`class.inheritance`). Those facts are recorded only on such a loss, so every other comparison,
|
|
86
|
+
* every compatible one included, keeps its bytes.
|
|
86
87
|
*/
|
|
87
88
|
export interface ComparisonEvidence {
|
|
88
89
|
readonly kind: 'structure' | 'meaning';
|
|
@@ -12,8 +12,9 @@ export function analyzeStructure(context) {
|
|
|
12
12
|
...result,
|
|
13
13
|
compatible: result.assessments.every(({ satisfied }) => satisfied),
|
|
14
14
|
// Each version adds one requirement policy and applies only when its fact is present: 3 a
|
|
15
|
-
// declared capability root, 4 a lost effective Property of a reached Class
|
|
16
|
-
// reliance, facts recorded only when they fail. Without one, evidence
|
|
15
|
+
// declared capability root, 4 a lost effective Property of a reached Class, a failed Policy
|
|
16
|
+
// reliance or a refused extension, facts recorded only when they fail. Without one, evidence
|
|
17
|
+
// keeps its version-2 bytes.
|
|
17
18
|
evidence: evidenceOf({
|
|
18
19
|
kind: 'structure',
|
|
19
20
|
version: recordsFailureOnlyFact(result.assessments)
|
|
@@ -27,13 +28,14 @@ export function analyzeStructure(context) {
|
|
|
27
28
|
}
|
|
28
29
|
/**
|
|
29
30
|
* The facts structure records only when they fail: a referenced or inherited Class that lost an
|
|
30
|
-
* effective Property,
|
|
31
|
-
* endpoints).
|
|
31
|
+
* effective Property, what a dependent Policy relies on (a Method object, a matched Edge's
|
|
32
|
+
* endpoints), and a Class the dependent extends that became final.
|
|
32
33
|
*/
|
|
33
34
|
const FAILURE_ONLY_FACETS = new Set([
|
|
34
35
|
'class.properties',
|
|
35
36
|
'method.object',
|
|
36
37
|
'class.endpoints',
|
|
38
|
+
'class.inheritance',
|
|
37
39
|
]);
|
|
38
40
|
function recordsFailureOnlyFact(assessments) {
|
|
39
41
|
return assessments.some(({ requirement }) => FAILURE_ONLY_FACETS.has(requirement.kind));
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { Key } from '../../../addressing/index.js';
|
|
2
|
-
import {
|
|
2
|
+
import { inheritanceOf } from '../../../language/class/inheritance.js';
|
|
3
|
+
import { compareAddedProperties, compareLostProperties, inheritanceMeaning, } from '../comparators/classes.js';
|
|
3
4
|
import { compareFacet } from '../comparators/index.js';
|
|
4
5
|
import { fingerprintMeaning } from '../comparators/values.js';
|
|
5
6
|
import { contextInvalid } from '../context.js';
|
|
@@ -73,6 +74,7 @@ export function compareRequirements({ context, report, }) {
|
|
|
73
74
|
compareLostProperties({ pair, report });
|
|
74
75
|
}
|
|
75
76
|
compareReliance({ context, report });
|
|
77
|
+
compareExtension({ context, report });
|
|
76
78
|
}
|
|
77
79
|
/**
|
|
78
80
|
* What a dependent Policy relies on is recorded only when it fails: a Method it names that is gone
|
|
@@ -95,3 +97,29 @@ function compareReliance({ context, report, }) {
|
|
|
95
97
|
report.record({ pair, facet, before, after, requirement: { kind: facet } });
|
|
96
98
|
}
|
|
97
99
|
}
|
|
100
|
+
/**
|
|
101
|
+
* A Class one of the dependent's own Classes extends must still admit it: the dependent is refused
|
|
102
|
+
* once the compared origin declares that Class `'final'`. The fact is recorded only when it fails,
|
|
103
|
+
* like a Policy reliance, so a compatible comparison keeps its evidence bytes.
|
|
104
|
+
*/
|
|
105
|
+
function compareExtension({ context, report, }) {
|
|
106
|
+
for (const reference of context.roots) {
|
|
107
|
+
if (reference.usage.relation !== 'extends')
|
|
108
|
+
continue;
|
|
109
|
+
if (Key.origin(reference.coordinate.key) !== context.source.origin)
|
|
110
|
+
continue;
|
|
111
|
+
const pair = context.pair(reference.coordinate);
|
|
112
|
+
if (pair.source?.kind !== 'class' || pair.target?.kind !== 'class')
|
|
113
|
+
continue;
|
|
114
|
+
if (pair.target.analysis.declaration.inheritance !== 'final')
|
|
115
|
+
continue;
|
|
116
|
+
report.note(reference);
|
|
117
|
+
report.record({
|
|
118
|
+
pair,
|
|
119
|
+
facet: 'class.inheritance',
|
|
120
|
+
before: inheritanceMeaning(inheritanceOf(pair.source.analysis.declaration)),
|
|
121
|
+
after: inheritanceMeaning('final'),
|
|
122
|
+
requirement: { kind: 'class.inheritance' },
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { Report } from '../report.js';
|
|
2
2
|
import type { ComparisonContext } from '../scope.js';
|
|
3
3
|
/**
|
|
4
|
-
* Compare inherited contracts
|
|
4
|
+
* Compare inherited contracts, newly imposed obligations and who may extend existing Classes.
|
|
5
5
|
* Obligations are reported at their declaring Method: changed signatures retain their comparison;
|
|
6
6
|
* an unchanged obligation newly inherited by a Class is added and unsatisfied.
|
|
7
7
|
*/
|
|
@@ -1,10 +1,11 @@
|
|
|
1
|
+
import { inheritanceOf } from '../../../language/class/inheritance.js';
|
|
1
2
|
import { methodInheritance } from '../comparators/callables.js';
|
|
2
|
-
import { compareAddedProperties } from '../comparators/classes.js';
|
|
3
|
+
import { compareAddedProperties, inheritanceMeaning } from '../comparators/classes.js';
|
|
3
4
|
import { compareFacet } from '../comparators/index.js';
|
|
4
5
|
import { fingerprintMeaning } from '../comparators/values.js';
|
|
5
6
|
import { classCoordinate, methodCoordinate } from '../coordinate.js';
|
|
6
7
|
/**
|
|
7
|
-
* Compare inherited contracts
|
|
8
|
+
* Compare inherited contracts, newly imposed obligations and who may extend existing Classes.
|
|
8
9
|
* Obligations are reported at their declaring Method: changed signatures retain their comparison;
|
|
9
10
|
* an unchanged obligation newly inherited by a Class is added and unsatisfied.
|
|
10
11
|
*/
|
|
@@ -25,6 +26,7 @@ export function compareSurface({ context, report, }) {
|
|
|
25
26
|
requirement: { kind: 'class.inherit' },
|
|
26
27
|
});
|
|
27
28
|
compareAddedProperties({ context, pair, report });
|
|
29
|
+
compareExtension({ pair, report });
|
|
28
30
|
for (const [key, method] of source.analysis.requirements)
|
|
29
31
|
if (!established.has(key))
|
|
30
32
|
established.set(key, method);
|
|
@@ -54,6 +56,27 @@ export function compareSurface({ context, report, }) {
|
|
|
54
56
|
});
|
|
55
57
|
}
|
|
56
58
|
}
|
|
59
|
+
/**
|
|
60
|
+
* A changed inheritance is observed on its Class, and is major when the Class was `'open'` or
|
|
61
|
+
* becomes `'final'`. Closing an `'open'` Class withdraws what its declaration granted other Domains;
|
|
62
|
+
* acceptance refuses a subclass of a `'final'` Class of any Domain and admits one of another
|
|
63
|
+
* Domain's `'internal'` Class (DV-CLASS-INHERITANCE), so finalizing that Class can refuse a dependent.
|
|
64
|
+
*/
|
|
65
|
+
function compareExtension({ pair, report, }) {
|
|
66
|
+
if (pair.source?.kind !== 'class' || pair.target?.kind !== 'class')
|
|
67
|
+
return;
|
|
68
|
+
const before = inheritanceOf(pair.source.analysis.declaration);
|
|
69
|
+
const after = inheritanceOf(pair.target.analysis.declaration);
|
|
70
|
+
if (before === after)
|
|
71
|
+
return;
|
|
72
|
+
report.record({
|
|
73
|
+
pair,
|
|
74
|
+
facet: 'class.inheritance',
|
|
75
|
+
before: inheritanceMeaning(before),
|
|
76
|
+
after: inheritanceMeaning(after),
|
|
77
|
+
requirement: { kind: 'class.inheritance', satisfied: before !== 'open' && after !== 'final' },
|
|
78
|
+
});
|
|
79
|
+
}
|
|
57
80
|
function changed({ before, after }) {
|
|
58
81
|
return (before === undefined ||
|
|
59
82
|
after === undefined ||
|
|
@@ -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_INHERITANCE' | '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;
|
|
@@ -61,6 +61,7 @@ function canonicalizeClass(value) {
|
|
|
61
61
|
ref: value.ref,
|
|
62
62
|
kind: value.kind,
|
|
63
63
|
abstract: value.abstract,
|
|
64
|
+
...(value.inheritance === undefined ? {} : { inheritance: value.inheritance }),
|
|
64
65
|
extends: [...value.extends].sort(compareDefinitionRefs),
|
|
65
66
|
properties: mapRecord(value.properties, canonicalizeProperty),
|
|
66
67
|
};
|
|
@@ -80,6 +81,7 @@ function canonicalizeClass(value) {
|
|
|
80
81
|
endpoints: canonicalizeDirectedEndpoints(value.endpoints),
|
|
81
82
|
policies: value.policies,
|
|
82
83
|
constraints: value.constraints,
|
|
84
|
+
...(value.unique === undefined ? {} : { unique: value.unique }),
|
|
83
85
|
}
|
|
84
86
|
: {
|
|
85
87
|
...base,
|
|
@@ -115,6 +117,8 @@ function canonicalizeCallable(value) {
|
|
|
115
117
|
output: canonicalizeCallableOutput(value.output),
|
|
116
118
|
// Ordinary execution has one canonical form, so existing revisions never move.
|
|
117
119
|
...(value.execution?.durable === true ? { execution: { durable: true } } : {}),
|
|
120
|
+
// Absence is the one spelling of a call admitted afresh; validation refuses any other.
|
|
121
|
+
...(value.admission === undefined ? {} : { admission: { maxAge: value.admission.maxAge } }),
|
|
118
122
|
};
|
|
119
123
|
return value.auth === 'authorized'
|
|
120
124
|
? {
|
|
@@ -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) {
|
|
@@ -103,6 +109,9 @@ function validateParents(environment, owner, ownerKind, parents, diagnostics) {
|
|
|
103
109
|
if (target.ref.kind !== 'class' || target.declaration.kind !== ownerKind) {
|
|
104
110
|
diagnostics.push(diagnostic('DV_CLASS_KIND', pointer, `${formatDefinitionRef(owner)} cannot extend different-kind Class ${formatDefinitionRef(parent)}.`, formatDefinitionRef(parent)));
|
|
105
111
|
}
|
|
112
|
+
else if (target.declaration.inheritance === 'final') {
|
|
113
|
+
diagnostics.push(diagnostic('DV_CLASS_INHERITANCE', pointer, `${formatDefinitionRef(owner)} cannot extend final Class ${formatDefinitionRef(parent)}.`, formatDefinitionRef(parent)));
|
|
114
|
+
}
|
|
106
115
|
}
|
|
107
116
|
}
|
|
108
117
|
function validateEdgeContract(environment, owner, declaration, diagnostics) {
|
|
@@ -98,6 +98,9 @@ const edgeConstraintsSchema = strictObject({
|
|
|
98
98
|
noSelf: literal(true).optional(),
|
|
99
99
|
acyclic: literal(true).optional(),
|
|
100
100
|
});
|
|
101
|
+
// A tuple names the source and the slug. No tuple is spelled by the absence of the field, never by
|
|
102
|
+
// an empty list: one spelling per meaning, one revision.
|
|
103
|
+
const edgeUniqueSchema = uniqueArray(tuple([literal('source'), literal('slug')])).min(1);
|
|
101
104
|
const policyPatternSubjectSchema = strictObject({ kind: literal('subject') });
|
|
102
105
|
const policyPatternObjectSchema = strictObject({ kind: literal('object') });
|
|
103
106
|
const policyPatternSourceSchema = strictObject({ kind: literal('source') });
|
|
@@ -195,11 +198,16 @@ const valueFunctionInputSchema = jsonSchemaSchema.refine((value) => typeof value
|
|
|
195
198
|
const callableExecutionSchema = strictObject({
|
|
196
199
|
durable: boolean(),
|
|
197
200
|
});
|
|
201
|
+
// Its range is language meaning, refused by callable validation with DV_CALLABLE_ADMISSION.
|
|
202
|
+
const callableAdmissionSchema = strictObject({
|
|
203
|
+
maxAge: number(),
|
|
204
|
+
});
|
|
198
205
|
const callableShape = {
|
|
199
206
|
description: descriptionSchema.optional(),
|
|
200
207
|
input: jsonSchemaSchema,
|
|
201
208
|
output: callableOutputSchema,
|
|
202
209
|
execution: callableExecutionSchema.optional(),
|
|
210
|
+
admission: callableAdmissionSchema.optional(),
|
|
203
211
|
auth: enumeration(['anonymous', 'authenticated', 'authorized']),
|
|
204
212
|
policy: union([literal(true), policyCheckExpressionSchema]).optional(),
|
|
205
213
|
};
|
|
@@ -228,10 +236,13 @@ const edgeClassPoliciesSchema = strictObject({
|
|
|
228
236
|
read: classPolicySchema.optional(),
|
|
229
237
|
traverse: classPolicySchema.optional(),
|
|
230
238
|
});
|
|
239
|
+
// The default `'internal'` is the absence of the field: one spelling per meaning, one revision.
|
|
240
|
+
const inheritanceSchema = enumeration(['open', 'final']);
|
|
231
241
|
const nodeClassSchema = strictObject({
|
|
232
242
|
ref: classRefSchema,
|
|
233
243
|
kind: literal('node'),
|
|
234
244
|
abstract: boolean(),
|
|
245
|
+
inheritance: inheritanceSchema.optional(),
|
|
235
246
|
description: descriptionSchema.optional(),
|
|
236
247
|
extends: uniqueArray(classRefSchema),
|
|
237
248
|
properties: propertyRecordSchema,
|
|
@@ -244,10 +255,12 @@ const edgeClassShape = {
|
|
|
244
255
|
ref: classRefSchema,
|
|
245
256
|
kind: literal('edge'),
|
|
246
257
|
abstract: boolean(),
|
|
258
|
+
inheritance: inheritanceSchema.optional(),
|
|
247
259
|
description: descriptionSchema.optional(),
|
|
248
260
|
extends: uniqueArray(classRefSchema),
|
|
249
261
|
properties: propertyRecordSchema,
|
|
250
262
|
constraints: edgeConstraintsSchema,
|
|
263
|
+
unique: edgeUniqueSchema.optional(),
|
|
251
264
|
};
|
|
252
265
|
const edgeClassSchema = strictObject({
|
|
253
266
|
...edgeClassShape,
|
|
@@ -257,14 +270,22 @@ const edgeClassSchema = strictObject({
|
|
|
257
270
|
}).superRefine(validateEdgeOrientationShape);
|
|
258
271
|
const classDeclarationSchema = discriminatedUnion('kind', [nodeClassSchema, edgeClassSchema]);
|
|
259
272
|
function validateEdgeOrientationShape(value, context) {
|
|
260
|
-
if ((value.orientation === 'undirected')
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
}
|
|
273
|
+
if ((value.orientation === 'undirected') !== Array.isArray(value.endpoints)) {
|
|
274
|
+
context.addIssue({
|
|
275
|
+
code: 'custom',
|
|
276
|
+
path: ['endpoints'],
|
|
277
|
+
message: `${value.orientation} Edge endpoints have the wrong structural shape.`,
|
|
278
|
+
params: { structureCode: 'STRUCTURE_EDGE_ORIENTATION' },
|
|
279
|
+
});
|
|
280
|
+
}
|
|
281
|
+
if (value.orientation === 'undirected' && value.unique !== undefined) {
|
|
282
|
+
context.addIssue({
|
|
283
|
+
code: 'custom',
|
|
284
|
+
path: ['unique'],
|
|
285
|
+
message: 'Only a directed Edge Class declares unique tuples.',
|
|
286
|
+
params: { structureCode: 'STRUCTURE_EDGE_ORIENTATION' },
|
|
287
|
+
});
|
|
288
|
+
}
|
|
268
289
|
}
|
|
269
290
|
const functionDefinitionSchema = strictObject({
|
|
270
291
|
ref: functionRefSchema,
|
|
@@ -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