@astrale-os/kernel-dsl 1.0.0-beta.14 → 1.0.0-beta.16
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/addressing/domain-dependency.d.ts +0 -6
- package/dist/v1/addressing/domain-dependency.js +1 -6
- package/dist/v1/builder/class/authoring.d.ts +43 -0
- package/dist/v1/builder/index.d.ts +1 -1
- package/dist/v1/builder/lowering/policy.js +1 -0
- package/dist/v1/builder/lowering/schema/context.d.ts +5 -0
- package/dist/v1/builder/lowering/schema/context.js +22 -0
- package/dist/v1/builder/policy/authoring.d.ts +84 -4
- package/dist/v1/builder/policy/authoring.js +41 -12
- package/dist/v1/builder/schema/projection/class.d.ts +116 -14
- package/dist/v1/bundle/codec.js +7 -7
- package/dist/v1/compiled/admission/accept.js +3 -3
- package/dist/v1/compiled/admission/closure.js +2 -2
- package/dist/v1/compiled/admission/semantics/core.js +3 -7
- package/dist/v1/compiled/admission/semantics/policy/parity.js +3 -10
- package/dist/v1/compiled/admission/semantics/policy/typing.js +2 -2
- package/dist/v1/language/.spec/schemas/draft-2020-12.schema.json +462 -0
- package/dist/v1/language/core/facts.d.ts +17 -4
- package/dist/v1/language/core/facts.js +26 -5
- package/dist/v1/language/limits.d.ts +64 -0
- package/dist/v1/language/limits.js +60 -0
- package/dist/v1/language/policy/analysis/budgets.d.ts +39 -18
- package/dist/v1/language/policy/analysis/budgets.js +30 -20
- package/dist/v1/language/policy/analysis/checks.js +3 -2
- package/dist/v1/language/policy/analysis/typing.d.ts +4 -2
- package/dist/v1/language/policy/analysis/typing.js +136 -14
- package/dist/v1/language/validation/rule.d.ts +9 -0
- package/dist/v1/language/validation/rule.js +9 -7
- package/dist/v1/language/value/ordering.d.ts +5 -0
- package/dist/v1/language/value/ordering.js +14 -0
- package/dist/v1/language/value/reference/closure.js +3 -4
- package/dist/v1/language/value/reference/registry.index.d.ts +2 -0
- package/dist/v1/language/value/reference/registry.index.js +31 -15
- package/dist/v1/language/value/regexp/ast.js +6 -7
- package/dist/v1/language/value/regexp/compile.d.ts +5 -11
- package/dist/v1/language/value/regexp/compile.js +14 -12
- package/dist/v1/language/value/regexp/syntax.js +3 -2
- package/dist/v1/schema/.spec/schemas/domain-schema.schema.json +30 -2
- package/dist/v1/schema/admission/accept.js +3 -14
- package/dist/v1/schema/admission/callable.d.ts +21 -0
- package/dist/v1/schema/admission/callable.js +96 -0
- package/dist/v1/schema/index.d.ts +7 -0
- package/dist/v1/schema/index.js +3 -0
- package/dist/v1/schema/normalization/domain.d.ts +2 -0
- package/dist/v1/schema/normalization/domain.js +1 -1
- package/dist/v1/schema/resolution/context.js +7 -7
- package/dist/v1/schema/resolution/domain.d.ts +5 -0
- package/dist/v1/schema/resolution/domain.js +1 -1
- package/dist/v1/schema/rule.d.ts +9 -0
- package/dist/v1/schema/rule.js +29 -4
- package/dist/v1/schema/validation/callable.d.ts +33 -3
- package/dist/v1/schema/validation/callable.js +49 -15
- package/dist/v1/schema/validation/class.d.ts +6 -2
- package/dist/v1/schema/validation/class.js +18 -22
- package/dist/v1/schema/validation/core.js +16 -10
- package/dist/v1/schema/validation/domain.js +3 -3
- package/dist/v1/schema/validation/domain.route.d.ts +6 -1
- package/dist/v1/schema/validation/domain.route.js +18 -0
- package/dist/v1/schema/validation/domain.structure.d.ts +28 -26
- package/dist/v1/schema/validation/domain.structure.js +22 -6
- package/dist/v1/schema/validation/policy/budgets.d.ts +33 -2
- package/dist/v1/schema/validation/policy/budgets.js +89 -16
- package/dist/v1/schema/validation/policy/context.d.ts +1 -2
- package/dist/v1/schema/validation/policy/context.js +2 -7
- package/dist/v1/schema/validation/policy/references.js +2 -1
- package/dist/v1/schema/validation/policy/uses.d.ts +29 -0
- package/dist/v1/schema/validation/policy/uses.js +28 -48
- package/dist/v1/schema/validation/value-api.d.ts +2 -0
- package/dist/v1/schema/validation/value-api.js +2 -0
- package/dist/v1/schema/validation/value.d.ts +3 -0
- package/dist/v1/schema/validation/value.js +3 -9
- package/dist/v1/schema/validation/value.json-schema.d.ts +3 -3
- package/dist/v1/schema/validation/value.json-schema.js +22 -4
- package/package.json +2 -1
package/dist/v1/schema/rule.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { defaultLimits } from '@astrale-os/kernel-dsl/value';
|
|
2
|
+
import { limits } from '../language/limits.js';
|
|
2
3
|
import { deepFreeze } from './canonical/freeze.js';
|
|
3
4
|
const ANY = ['{/path*}'];
|
|
4
5
|
const CHECKS = [
|
|
@@ -8,6 +9,7 @@ const CHECKS = [
|
|
|
8
9
|
];
|
|
9
10
|
const DEPENDENCIES = ['/dependencies/{alias}', '/dependencies'];
|
|
10
11
|
const count = (value) => value.toLocaleString('en-US');
|
|
12
|
+
const { dependency, policy } = limits;
|
|
11
13
|
const at = (field) => [
|
|
12
14
|
`/functions/{function}${field}`,
|
|
13
15
|
`/classes/{class}/methods/{method}${field}`,
|
|
@@ -36,7 +38,8 @@ export const RULES = deepFreeze({
|
|
|
36
38
|
},
|
|
37
39
|
DV_CALLABLE_IO: {
|
|
38
40
|
stage: 'rule',
|
|
39
|
-
|
|
41
|
+
// A Method takes a value input only: its input has no mode.
|
|
42
|
+
positions: ['/functions/{function}/input/mode', ...at('/output/mode')],
|
|
40
43
|
statement: 'A durable Function or Method takes a value input and returns a value output.',
|
|
41
44
|
},
|
|
42
45
|
DV_CLASS_ABSTRACT: {
|
|
@@ -68,7 +71,7 @@ export const RULES = deepFreeze({
|
|
|
68
71
|
DV_CORE_EDGE_CONSTRAINTS: {
|
|
69
72
|
stage: 'context',
|
|
70
73
|
positions: ['/core/edges', '/core/edges/{index}', '/core/nodes/{slug}'],
|
|
71
|
-
statement: 'The Core Edges of a Schema hold the noSelf and acyclic constraints of their Classes, and every Core Node, and every Definition a Core Edge names, the Edge counts of the endpoints
|
|
74
|
+
statement: 'The Core Edges of a Schema hold the noSelf and acyclic constraints of their Classes, and every Core Node, and every Definition a Core Edge names, the Edge counts of the concrete Edge Classes whose endpoints accept it, each over its own Edges, a Definition its maximum only.',
|
|
72
75
|
},
|
|
73
76
|
DV_CORE_EDGE_KEY: {
|
|
74
77
|
stage: 'rule',
|
|
@@ -128,7 +131,8 @@ export const RULES = deepFreeze({
|
|
|
128
131
|
DV_DEPENDENCY_LIMITS: {
|
|
129
132
|
stage: 'context',
|
|
130
133
|
positions: ['/dependencies', '/dependencies/{alias}'],
|
|
131
|
-
statement:
|
|
134
|
+
statement: `A Domain has at most ${dependency.direct} direct dependencies, a closure of at most ${dependency.closure} Domains and a dependency depth of at most ${dependency.depth}.`,
|
|
135
|
+
limits: ['dependency.direct', 'dependency.closure', 'dependency.depth'],
|
|
132
136
|
},
|
|
133
137
|
DV_DEPENDENCY_REVISION_COHERENCE: {
|
|
134
138
|
stage: 'context',
|
|
@@ -182,6 +186,7 @@ export const RULES = deepFreeze({
|
|
|
182
186
|
stage: 'structure',
|
|
183
187
|
positions: ANY,
|
|
184
188
|
statement: `The document is portable JSON: as text, UTF-8 that parses, within ${count(defaultLimits.maxBytes)} bytes, with no byte-order mark and no repeated member name; as a value, null, booleans, finite numbers other than negative zero, strings, arrays and plain objects, with no string or member name that holds U+0000 or a lone surrogate or exceeds ${count(defaultLimits.maxStringBytes)} bytes of UTF-8, at most ${count(defaultLimits.maxDepth)} nested arrays and objects, and at most ${count(defaultLimits.maxMembers)} members and items in all.`,
|
|
189
|
+
conditions: ['#/$defs/portable-string/not'],
|
|
185
190
|
},
|
|
186
191
|
DV_METHOD_ABSTRACT: {
|
|
187
192
|
stage: 'rule',
|
|
@@ -244,11 +249,21 @@ export const RULES = deepFreeze({
|
|
|
244
249
|
...at('/output/item{/path*}'),
|
|
245
250
|
],
|
|
246
251
|
statement: 'Every embedded value schema is in the value language: its diagnostic names the refusal in `valueCode`, and the schema of an automatic or State Property is not a blob.',
|
|
252
|
+
conditions: ['#/$defs/node-property-definition/allOf/0/if'],
|
|
247
253
|
},
|
|
248
254
|
PL_BUDGET: {
|
|
249
255
|
stage: 'rule',
|
|
250
256
|
positions: ['/policies/{policy}', ...CHECKS],
|
|
251
|
-
statement:
|
|
257
|
+
statement: `A Policy stays within its budgets once expanded: pattern depth ${policy.patternDepth}, ${policy.normalizedBranches} normalized branches, ${policy.edgePredicates} Edge predicates a branch and ${policy.referencedDomains} referenced Domains; and checks within depth ${policy.checkDepth}, ${policy.checkBranches} branches and ${policy.checkLeaves} leaves.`,
|
|
258
|
+
limits: [
|
|
259
|
+
'policy.patternDepth',
|
|
260
|
+
'policy.normalizedBranches',
|
|
261
|
+
'policy.edgePredicates',
|
|
262
|
+
'policy.referencedDomains',
|
|
263
|
+
'policy.checkDepth',
|
|
264
|
+
'policy.checkBranches',
|
|
265
|
+
'policy.checkLeaves',
|
|
266
|
+
],
|
|
252
267
|
},
|
|
253
268
|
PL_COMPOSITION: {
|
|
254
269
|
stage: 'rule',
|
|
@@ -295,16 +310,19 @@ export const RULES = deepFreeze({
|
|
|
295
310
|
stage: 'structure',
|
|
296
311
|
positions: ['/functions/{function}/input'],
|
|
297
312
|
statement: 'A Function input in binary mode is a binary input: it declares its metadata and maximumBytes.',
|
|
313
|
+
conditions: ['#/$defs/function-definition/properties/input/anyOf/1/not'],
|
|
298
314
|
},
|
|
299
315
|
STRUCTURE_CALLABLE_POLICY_AUTH: {
|
|
300
316
|
stage: 'structure',
|
|
301
317
|
positions: at('/policy'),
|
|
302
318
|
statement: 'Only an authorized Function or Method declares a Policy.',
|
|
319
|
+
conditions: ['#/$defs/function-definition/if', '#/$defs/method-definition/if'],
|
|
303
320
|
},
|
|
304
321
|
STRUCTURE_DEPENDENCY_ALIAS: {
|
|
305
322
|
stage: 'structure',
|
|
306
323
|
positions: ['/dependencies/{alias}'],
|
|
307
324
|
statement: 'A dependency alias is neither prototype nor constructor.',
|
|
325
|
+
conditions: ['#/$defs/dependency-alias/not'],
|
|
308
326
|
},
|
|
309
327
|
STRUCTURE_DOMAIN_NAME: {
|
|
310
328
|
stage: 'structure',
|
|
@@ -326,6 +344,7 @@ export const RULES = deepFreeze({
|
|
|
326
344
|
stage: 'structure',
|
|
327
345
|
positions: ['/classes/{class}/properties/{property}/schema/x-astrale-value-kind'],
|
|
328
346
|
statement: 'The schema of an Edge Property is not a blob: only a Node holds Blob contents.',
|
|
347
|
+
conditions: ['#/$defs/edge-property-definition/properties/schema/not'],
|
|
329
348
|
},
|
|
330
349
|
STRUCTURE_EDGE_ORIENTATION: {
|
|
331
350
|
stage: 'structure',
|
|
@@ -336,6 +355,7 @@ export const RULES = deepFreeze({
|
|
|
336
355
|
],
|
|
337
356
|
statement: 'A directed Edge Class has source and target endpoints and an undirected one two incident endpoints, and only a directed one declares unique tuples or is acyclic.',
|
|
338
357
|
enforces: 'No Edges of an acyclic Class form a directed cycle.',
|
|
358
|
+
conditions: ['#/$defs/edge-class/if'],
|
|
339
359
|
},
|
|
340
360
|
STRUCTURE_FORMAT: {
|
|
341
361
|
stage: 'structure',
|
|
@@ -351,6 +371,7 @@ export const RULES = deepFreeze({
|
|
|
351
371
|
stage: 'structure',
|
|
352
372
|
positions: ANY,
|
|
353
373
|
statement: 'A string, list or number is no greater than the bound of its position.',
|
|
374
|
+
limits: ['policy.variablesPerExists', 'policy.repetition'],
|
|
354
375
|
},
|
|
355
376
|
STRUCTURE_MINIMUM: {
|
|
356
377
|
stage: 'structure',
|
|
@@ -371,6 +392,10 @@ export const RULES = deepFreeze({
|
|
|
371
392
|
stage: 'structure',
|
|
372
393
|
positions: ANY,
|
|
373
394
|
statement: 'No object of the document has a member named __proto__, except the transitions of a State.',
|
|
395
|
+
conditions: [
|
|
396
|
+
'#/$defs/json-value/anyOf/5/propertyNames/not',
|
|
397
|
+
'#/$defs/value-schema/anyOf/1/propertyNames/not',
|
|
398
|
+
],
|
|
374
399
|
},
|
|
375
400
|
STRUCTURE_REQUIRED: {
|
|
376
401
|
stage: 'structure',
|
|
@@ -1,7 +1,37 @@
|
|
|
1
|
+
import type { ClassRef } from '../../addressing/index.js';
|
|
2
|
+
import type { CallableDefinition } from '../../language/callable/model/callable.js';
|
|
3
|
+
import type { ValueSchemaRegistry } from '../../language/value/reference/registry.js';
|
|
1
4
|
import type { SchemaDiagnostic } from '../diagnostic.js';
|
|
2
5
|
import type { DomainSchemaIR } from '../model/schema.js';
|
|
3
6
|
/**
|
|
4
|
-
*
|
|
5
|
-
*
|
|
7
|
+
* One callable contract a Domain schema declares: where it is written, and the Node Class that
|
|
8
|
+
* receives its call, which its Policy names `self`. A contract is validated by the same rules
|
|
9
|
+
* wherever it is: in a Domain schema, at its declaration, or on the Node of a Function instance,
|
|
10
|
+
* which carries it (`acceptCallable`).
|
|
6
11
|
*/
|
|
7
|
-
export
|
|
12
|
+
export interface DeclaredContract {
|
|
13
|
+
readonly contract: CallableDefinition;
|
|
14
|
+
/** Its declaration's JSON Pointer, which its diagnostics point within. */
|
|
15
|
+
readonly pointer: string;
|
|
16
|
+
/** The owner of an instance Method; a Function and a static Method have none. */
|
|
17
|
+
readonly receiver?: ClassRef;
|
|
18
|
+
/** The Node Class and name of a Method; absent for a Function. */
|
|
19
|
+
readonly method?: {
|
|
20
|
+
readonly owner: ClassRef;
|
|
21
|
+
readonly name: string;
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
/** Every callable contract one Domain schema declares: each Function, then each Node Method. */
|
|
25
|
+
export declare function declaredContracts(schema: DomainSchemaIR): readonly DeclaredContract[];
|
|
26
|
+
/**
|
|
27
|
+
* Validate the ref of every Function declaration, then the value schemas, execution modifier and
|
|
28
|
+
* admission age of every declared contract.
|
|
29
|
+
*/
|
|
30
|
+
export declare function validateContracts(schema: DomainSchemaIR, registry: ValueSchemaRegistry): readonly SchemaDiagnostic[];
|
|
31
|
+
/**
|
|
32
|
+
* Validate one callable contract written at `pointer`, except its Policy (`validateContractPolicy`):
|
|
33
|
+
* its value schemas against the owner registry, its execution modifier and its admission age.
|
|
34
|
+
*/
|
|
35
|
+
export declare function validateContract(contract: CallableDefinition, pointer: string, registry: ValueSchemaRegistry, diagnostics: SchemaDiagnostic[]): void;
|
|
36
|
+
/** Validate the execution modifier and admission age of one callable contract at `pointer`. */
|
|
37
|
+
export declare function validateCallableModifiers(declaration: CallableDefinition, pointer: string, diagnostics: SchemaDiagnostic[]): void;
|
|
@@ -4,32 +4,66 @@ import { isAdmissionMaxAge, MAXIMUM_ADMISSION_AGE_SECONDS, } from '../../languag
|
|
|
4
4
|
import { isBinaryInput } from '../../language/callable/model/callable.js';
|
|
5
5
|
import { escapePointerToken } from '../../language/value/syntax/pointer.js';
|
|
6
6
|
import { diagnostic } from '../diagnostic.js';
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
export function validateCallables(schema) {
|
|
12
|
-
const diagnostics = [];
|
|
7
|
+
import { validateCallable } from './value.js';
|
|
8
|
+
/** Every callable contract one Domain schema declares: each Function, then each Node Method. */
|
|
9
|
+
export function declaredContracts(schema) {
|
|
10
|
+
const contracts = [];
|
|
13
11
|
for (const [name, declaration] of Object.entries(schema.functions)) {
|
|
14
|
-
|
|
15
|
-
const ref = { origin: schema.origin, kind: 'function', name };
|
|
16
|
-
if (!equalDefinitionRefs(declaration.ref, ref)) {
|
|
17
|
-
diagnostics.push(diagnostic('DV_REF_RESOLVES', `${pointer}/ref`, `Function ref must be exactly ${Key.of(ref)}.`, Key.of(declaration.ref)));
|
|
18
|
-
}
|
|
19
|
-
validateExecution(declaration, pointer, diagnostics);
|
|
20
|
-
validateAdmission(declaration, pointer, diagnostics);
|
|
12
|
+
contracts.push({ contract: declaration, pointer: `/functions/${escapePointerToken(name)}` });
|
|
21
13
|
}
|
|
22
14
|
for (const [name, declaration] of Object.entries(schema.classes)) {
|
|
23
15
|
if (declaration.kind !== 'node')
|
|
24
16
|
continue;
|
|
17
|
+
const owner = { origin: schema.origin, kind: 'class', name };
|
|
25
18
|
for (const [method, value] of Object.entries(declaration.methods)) {
|
|
26
19
|
const pointer = `/classes/${escapePointerToken(name)}/methods/${escapePointerToken(method)}`;
|
|
27
|
-
|
|
28
|
-
|
|
20
|
+
contracts.push({
|
|
21
|
+
contract: value,
|
|
22
|
+
pointer,
|
|
23
|
+
...(value.static ? {} : { receiver: owner }),
|
|
24
|
+
method: { owner, name: method },
|
|
25
|
+
});
|
|
29
26
|
}
|
|
30
27
|
}
|
|
28
|
+
return contracts;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Validate the ref of every Function declaration, then the value schemas, execution modifier and
|
|
32
|
+
* admission age of every declared contract.
|
|
33
|
+
*/
|
|
34
|
+
export function validateContracts(schema, registry) {
|
|
35
|
+
const diagnostics = [];
|
|
36
|
+
for (const [name, declaration] of Object.entries(schema.functions)) {
|
|
37
|
+
validateFunctionRef(schema.origin, name, declaration.ref, diagnostics);
|
|
38
|
+
}
|
|
39
|
+
for (const { contract, pointer } of declaredContracts(schema)) {
|
|
40
|
+
validateContract(contract, pointer, registry, diagnostics);
|
|
41
|
+
}
|
|
31
42
|
return Object.freeze(diagnostics);
|
|
32
43
|
}
|
|
44
|
+
/**
|
|
45
|
+
* A Function declaration names itself by its ref: its own key in its own Domain. The ref belongs to
|
|
46
|
+
* the declaration, not to its contract, which a Node carries without one.
|
|
47
|
+
*/
|
|
48
|
+
function validateFunctionRef(origin, name, declared, diagnostics) {
|
|
49
|
+
const ref = { origin, kind: 'function', name };
|
|
50
|
+
if (equalDefinitionRefs(declared, ref))
|
|
51
|
+
return;
|
|
52
|
+
diagnostics.push(diagnostic('DV_REF_RESOLVES', `/functions/${escapePointerToken(name)}/ref`, `Function ref must be exactly ${Key.of(ref)}.`, Key.of(declared)));
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Validate one callable contract written at `pointer`, except its Policy (`validateContractPolicy`):
|
|
56
|
+
* its value schemas against the owner registry, its execution modifier and its admission age.
|
|
57
|
+
*/
|
|
58
|
+
export function validateContract(contract, pointer, registry, diagnostics) {
|
|
59
|
+
validateCallable(contract, pointer, registry, diagnostics);
|
|
60
|
+
validateCallableModifiers(contract, pointer, diagnostics);
|
|
61
|
+
}
|
|
62
|
+
/** Validate the execution modifier and admission age of one callable contract at `pointer`. */
|
|
63
|
+
export function validateCallableModifiers(declaration, pointer, diagnostics) {
|
|
64
|
+
validateExecution(declaration, pointer, diagnostics);
|
|
65
|
+
validateAdmission(declaration, pointer, diagnostics);
|
|
66
|
+
}
|
|
33
67
|
/**
|
|
34
68
|
* An admission age is a whole number of seconds within the language ceiling. It has no zero
|
|
35
69
|
* spelling: an absent admission age is the one canonical form of a call admitted afresh.
|
|
@@ -1,10 +1,14 @@
|
|
|
1
1
|
import type { ClassRef } from '../../addressing/index.js';
|
|
2
2
|
import type { ClassDefinition } from '../../language/class/model/class.js';
|
|
3
|
+
import type { ValueSchemaRegistry } from '../../language/value/reference/registry.js';
|
|
3
4
|
import type { DefinitionAnalysisResult } from '../analysis/class.js';
|
|
4
5
|
import type { SchemaDiagnostic } from '../diagnostic.js';
|
|
5
6
|
import type { DomainResolutionContext } from '../resolution/context.js';
|
|
6
|
-
/**
|
|
7
|
-
|
|
7
|
+
/**
|
|
8
|
+
* Validate the semantic contracts owned by Class declarations. `registry` is the candidate's value
|
|
9
|
+
* registry; each dependency's is built at most once, so the Classes share them.
|
|
10
|
+
*/
|
|
11
|
+
export declare function validateDefinitions(environment: DomainResolutionContext, analysis: DefinitionAnalysisResult, registry: ValueSchemaRegistry): readonly SchemaDiagnostic[];
|
|
8
12
|
/** Whether `child` safely refines its parent Edge contract `parent`. */
|
|
9
13
|
export declare function edgeContractRefines(environment: DomainResolutionContext, child: Extract<ClassDefinition, {
|
|
10
14
|
readonly kind: 'edge';
|
|
@@ -10,13 +10,24 @@ import { isBlobValueSchema } from '../../language/value/model/blob.js';
|
|
|
10
10
|
import { classifyOrdering } from '../../language/value/ordering.js';
|
|
11
11
|
import { escapePointerToken } from '../../language/value/syntax/pointer.js';
|
|
12
12
|
import { diagnostic } from '../diagnostic.js';
|
|
13
|
-
import { createDomainValueSchemaRegistry
|
|
13
|
+
import { createDomainValueSchemaRegistry } from '../resolution/value.registry.js';
|
|
14
14
|
const formatDefinitionRef = Key.of;
|
|
15
|
-
/**
|
|
16
|
-
|
|
15
|
+
/**
|
|
16
|
+
* Validate the semantic contracts owned by Class declarations. `registry` is the candidate's value
|
|
17
|
+
* registry; each dependency's is built at most once, so the Classes share them.
|
|
18
|
+
*/
|
|
19
|
+
export function validateDefinitions(environment, analysis, registry) {
|
|
17
20
|
const diagnostics = [...analysis.diagnostics];
|
|
18
21
|
const schema = environment.candidate;
|
|
19
22
|
let scope;
|
|
23
|
+
const registries = new Map([[schema.origin, registry]]);
|
|
24
|
+
const registryOf = (origin) => {
|
|
25
|
+
if (!registries.has(origin)) {
|
|
26
|
+
const dependency = environment.dependencies.get(origin);
|
|
27
|
+
registries.set(origin, dependency === undefined ? undefined : createDomainValueSchemaRegistry(dependency));
|
|
28
|
+
}
|
|
29
|
+
return registries.get(origin);
|
|
30
|
+
};
|
|
20
31
|
for (const [name, declaration] of Object.entries(schema.classes)) {
|
|
21
32
|
const ref = { origin: schema.origin, kind: 'class', name };
|
|
22
33
|
const pointer = `/classes/${escapePointerToken(name)}`;
|
|
@@ -27,7 +38,7 @@ export function validateDefinitions(environment, analysis) {
|
|
|
27
38
|
(declaration.policies.read !== undefined || declaration.policies.traverse !== undefined)) {
|
|
28
39
|
diagnostics.push(diagnostic('DV_CLASS_ABSTRACT', `${pointer}/policies`, 'Abstract Classes cannot declare observation Policies.'));
|
|
29
40
|
}
|
|
30
|
-
validateFacet(
|
|
41
|
+
validateFacet(registry, declaration, pointer, diagnostics);
|
|
31
42
|
validateParents(environment, ref, declaration.kind, declaration.extends, diagnostics);
|
|
32
43
|
if (declaration.kind === 'edge') {
|
|
33
44
|
validateEdgeContract(environment, ref, declaration, pointer, diagnostics);
|
|
@@ -35,12 +46,11 @@ export function validateDefinitions(environment, analysis) {
|
|
|
35
46
|
validateEdgeLifetimes(scope, ref, declaration, pointer, diagnostics);
|
|
36
47
|
validateEdgeClassComposition(environment, ref, declaration, pointer, diagnostics);
|
|
37
48
|
}
|
|
38
|
-
validateUniqueProperties(
|
|
49
|
+
validateUniqueProperties(registryOf, analysis, ref, declaration, pointer, diagnostics);
|
|
39
50
|
}
|
|
40
51
|
return Object.freeze(diagnostics);
|
|
41
52
|
}
|
|
42
|
-
function validateFacet(
|
|
43
|
-
const valueRegistry = createValueSchemaRegistry(environment);
|
|
53
|
+
function validateFacet(valueRegistry, declaration, pointer, diagnostics) {
|
|
44
54
|
for (const [name, property] of Object.entries(declaration.properties)) {
|
|
45
55
|
const propertyPointer = `${pointer}/properties/${escapePointerToken(name)}`;
|
|
46
56
|
// The structure refuses a blob on an Edge Property and a State Property on an Edge Class.
|
|
@@ -147,7 +157,7 @@ function validateEdgeContract(environment, owner, declaration, pointer, diagnost
|
|
|
147
157
|
* inherit without redefinition, so the key reads one meaning on every descendant it covers. A Node
|
|
148
158
|
* tuple names Properties only; an Edge tuple names them as `{ property }` beside its other fields.
|
|
149
159
|
*/
|
|
150
|
-
function validateUniqueProperties(
|
|
160
|
+
function validateUniqueProperties(registryOf, analysis, owner, declaration, pointer, diagnostics) {
|
|
151
161
|
if (declaration.kind === 'edge' && declaration.orientation !== 'directed')
|
|
152
162
|
return;
|
|
153
163
|
if (declaration.unique === undefined)
|
|
@@ -156,20 +166,6 @@ function validateUniqueProperties(environment, analysis, owner, declaration, poi
|
|
|
156
166
|
if (properties === undefined)
|
|
157
167
|
return;
|
|
158
168
|
const code = declaration.kind === 'node' ? 'DV_NODE_UNIQUE' : 'DV_EDGE_UNIQUE';
|
|
159
|
-
const registries = new Map();
|
|
160
|
-
const registryOf = (origin) => {
|
|
161
|
-
if (!registries.has(origin)) {
|
|
162
|
-
const schema = environment.dependencies.get(origin);
|
|
163
|
-
const registry = origin === environment.candidate.origin
|
|
164
|
-
? createValueSchemaRegistry(environment)
|
|
165
|
-
: schema === undefined
|
|
166
|
-
? undefined
|
|
167
|
-
: createDomainValueSchemaRegistry(schema);
|
|
168
|
-
if (registry !== undefined)
|
|
169
|
-
registries.set(origin, registry);
|
|
170
|
-
}
|
|
171
|
-
return registries.get(origin);
|
|
172
|
-
};
|
|
173
169
|
const check = (key, at) => {
|
|
174
170
|
const property = properties.get(key);
|
|
175
171
|
if (property === undefined) {
|
|
@@ -3,7 +3,7 @@ import { Key } from '../../addressing/key.js';
|
|
|
3
3
|
import { compareUnicode, definitionRefIndex } from '../../addressing/ordering.js';
|
|
4
4
|
import { isMethodExecutable } from '../../language/callable/executable.js';
|
|
5
5
|
import { coveringEdgeUniqueTuples, coveringNodeUniqueTuples, edgeUniqueKeys, nodeUniqueKeys, } from '../../language/class/unique.js';
|
|
6
|
-
import { checkCoreEdgeSet, checkHeldKeys, coreEndpointsSatisfy, } from '../../language/core/facts.js';
|
|
6
|
+
import { checkCoreEdgeSet, checkHeldKeys, coreEndpointsSatisfy, DOMAIN_ENDPOINT_CLASS, } from '../../language/core/facts.js';
|
|
7
7
|
import { policyObjectKey, policyObjectOrigin, projectedMemberClass, } from '../../language/policy/model/policy.js';
|
|
8
8
|
import { validateJsonInstance } from '../../language/value/evaluate/validate.js';
|
|
9
9
|
import { escapePointerToken } from '../../language/value/syntax/pointer.js';
|
|
@@ -83,7 +83,9 @@ function resolveCoreEdges(environment, analysis, registry, nodesByRef, diagnosti
|
|
|
83
83
|
continue;
|
|
84
84
|
const endpointsSatisfy = coreEndpointsSatisfy(declaration, source, target, (endpoint, accepts) => endpointSatisfies(environment, endpoint, accepts));
|
|
85
85
|
if (!endpointsSatisfy) {
|
|
86
|
-
diagnostics.push(diagnostic('DV_CORE_EDGE_CLASS', pointer, `Core Edge endpoints do not satisfy ${formatDefinitionRef(edge.class)}
|
|
86
|
+
diagnostics.push(diagnostic('DV_CORE_EDGE_CLASS', pointer, `Core Edge endpoints do not satisfy ${formatDefinitionRef(edge.class)}.${source.kind === 'domain' || target.kind === 'domain'
|
|
87
|
+
? ` The owning Domain is a ${formatDefinitionRef(DOMAIN_ENDPOINT_CLASS)} Node: the endpoint that names it must accept that Class or one it extends.`
|
|
88
|
+
: ''}`, formatDefinitionRef(edge.class)));
|
|
87
89
|
continue;
|
|
88
90
|
}
|
|
89
91
|
edges.push({ source, target, declaration, value: edge, pointer });
|
|
@@ -92,7 +94,7 @@ function resolveCoreEdges(environment, analysis, registry, nodesByRef, diagnosti
|
|
|
92
94
|
}
|
|
93
95
|
function resolveCoreEndpoint(environment, ref, pointer, nodesByRef, diagnostics) {
|
|
94
96
|
if (ref.kind === 'domain')
|
|
95
|
-
return { kind: 'domain', ref, key: 'domain' };
|
|
97
|
+
return { kind: 'domain', ref, key: 'domain', classRef: DOMAIN_ENDPOINT_CLASS };
|
|
96
98
|
const undeclared = () => {
|
|
97
99
|
const key = policyObjectKey(ref);
|
|
98
100
|
diagnostics.push(diagnostic('DV_CORE_REF', pointer, `Core endpoint ${key} does not name a declared local Definition.`, key));
|
|
@@ -122,10 +124,14 @@ function declaresProjectedMember(environment, ref) {
|
|
|
122
124
|
return Object.hasOwn(candidate.policies, ref.name);
|
|
123
125
|
return Object.hasOwn(candidate.views, ref.name);
|
|
124
126
|
}
|
|
127
|
+
/**
|
|
128
|
+
* A Core Node is admitted by its own authored Class, a projected member by the Kernel declaration
|
|
129
|
+
* Class it projects as, and the owning Domain by the Kernel `Domain` Class of its Node: the write
|
|
130
|
+
* that projects the Edge binds exactly that Node, so an Edge Class that does not accept it can
|
|
131
|
+
* never be installed (DV-CORE-EDGE-CLASS).
|
|
132
|
+
*/
|
|
125
133
|
function endpointSatisfies(environment, endpoint, accepts) {
|
|
126
|
-
|
|
127
|
-
// Its concrete Class contract is therefore admitted by the projection compiler.
|
|
128
|
-
return endpoint.kind === 'domain' || classSatisfies(environment, endpoint.classRef, accepts);
|
|
134
|
+
return classSatisfies(environment, endpoint.classRef, accepts);
|
|
129
135
|
}
|
|
130
136
|
function validateCreationProperties(definition, properties, registry, code, pointer, diagnostics) {
|
|
131
137
|
const schema = {
|
|
@@ -181,11 +187,11 @@ function validateCoreEdgeSet(environment, edges, nodesByRef, diagnostics) {
|
|
|
181
187
|
report: (issue) => diagnostics.push(coreEdgeDiagnostic(issue)),
|
|
182
188
|
});
|
|
183
189
|
}
|
|
184
|
-
/** A projected member's degree is reported at the Edge that names it. */
|
|
190
|
+
/** A projected member's degree, and the owning Domain's, is reported at the Edge that names it. */
|
|
185
191
|
function factEndpoint(endpoint, pointer) {
|
|
186
|
-
return endpoint.kind === '
|
|
187
|
-
?
|
|
188
|
-
: endpoint;
|
|
192
|
+
return endpoint.kind === 'core'
|
|
193
|
+
? endpoint
|
|
194
|
+
: { key: endpoint.key, member: { class: endpoint.classRef, pointer } };
|
|
189
195
|
}
|
|
190
196
|
function coreEdgeDiagnostic(issue) {
|
|
191
197
|
switch (issue.kind) {
|
|
@@ -2,7 +2,7 @@ import { orderAndDeduplicate } from '../../language/validation/diagnostic.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 {
|
|
5
|
+
import { validateContracts } from './callable.js';
|
|
6
6
|
import { validateDefinitions } from './class.js';
|
|
7
7
|
import { validateCore } from './core.js';
|
|
8
8
|
import { validatePolicies } from './policy/validate.js';
|
|
@@ -15,8 +15,8 @@ export function validateDomainSchemaSemantics(schema, lookup) {
|
|
|
15
15
|
const diagnostics = [
|
|
16
16
|
...context.diagnostics,
|
|
17
17
|
...validateEmbeddedValueSchemas(schema, registry),
|
|
18
|
-
...validateDefinitions(context, analysis),
|
|
19
|
-
...
|
|
18
|
+
...validateDefinitions(context, analysis, registry),
|
|
19
|
+
...validateContracts(schema, registry),
|
|
20
20
|
...validatePolicies(context, analysis),
|
|
21
21
|
...validateViews(context),
|
|
22
22
|
...validateCore(context, analysis, registry),
|
|
@@ -1,4 +1,9 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { type Value as JsonValue } from '@astrale-os/kernel-dsl/value';
|
|
2
2
|
import type { DomainSchemaIR } from '../model/schema.js';
|
|
3
|
+
/**
|
|
4
|
+
* Read an unknown value as a Domain Schema document of its exact language contract: portable JSON
|
|
5
|
+
* (`DV_JSON_VALUE`), then the structure of its format and version. Throws `SchemaError`.
|
|
6
|
+
*/
|
|
7
|
+
export declare function structureDomainSchema(input: unknown): DomainSchemaIR;
|
|
3
8
|
/** Select the exact Domain language contract before entering its structural grammar. */
|
|
4
9
|
export declare function routeDomainSchema(input: JsonValue): DomainSchemaIR;
|
|
@@ -1,9 +1,27 @@
|
|
|
1
|
+
import { accept as acceptJsonValue, BoundaryError as JsonBoundaryError, } from '@astrale-os/kernel-dsl/value';
|
|
1
2
|
import { SchemaError, diagnostic } from '../diagnostic.js';
|
|
2
3
|
import { DOMAIN_FORMAT, DOMAIN_VERSION } from '../model/format.js';
|
|
3
4
|
import { parseDomainSchemaStructure } from './domain.structure.js';
|
|
4
5
|
const decoders = new Map([
|
|
5
6
|
[DOMAIN_FORMAT, new Map([[DOMAIN_VERSION, parseDomainSchemaStructure]])],
|
|
6
7
|
]);
|
|
8
|
+
/**
|
|
9
|
+
* Read an unknown value as a Domain Schema document of its exact language contract: portable JSON
|
|
10
|
+
* (`DV_JSON_VALUE`), then the structure of its format and version. Throws `SchemaError`.
|
|
11
|
+
*/
|
|
12
|
+
export function structureDomainSchema(input) {
|
|
13
|
+
let admitted;
|
|
14
|
+
try {
|
|
15
|
+
admitted = acceptJsonValue(input);
|
|
16
|
+
}
|
|
17
|
+
catch (error) {
|
|
18
|
+
if (error instanceof JsonBoundaryError) {
|
|
19
|
+
throw new SchemaError([diagnostic('DV_JSON_VALUE', error.pointer, error.message)]);
|
|
20
|
+
}
|
|
21
|
+
throw error;
|
|
22
|
+
}
|
|
23
|
+
return routeDomainSchema(admitted);
|
|
24
|
+
}
|
|
7
25
|
/** Select the exact Domain language contract before entering its structural grammar. */
|
|
8
26
|
export function routeDomainSchema(input) {
|
|
9
27
|
if (!isJsonObject(input))
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { Value as JsonValue } from '@astrale-os/kernel-dsl/value';
|
|
2
2
|
import { type ZodArray, type ZodType, type core as zodCore } from 'zod';
|
|
3
3
|
import type { PolicyRef } from '../../addressing/index.js';
|
|
4
|
-
import type { FunctionAuth } from '../../language/callable/model/callable.js';
|
|
4
|
+
import type { CallableDefinition, FunctionAuth } from '../../language/callable/model/callable.js';
|
|
5
5
|
import type { PolicyCheckExpression, PolicyPatternExpression } from '../../language/policy/model/policy.js';
|
|
6
6
|
import type { DomainSchemaIR } from '../model/schema.js';
|
|
7
7
|
/**
|
|
@@ -3641,16 +3641,16 @@ export declare const domainSchemaStructure: import("zod").ZodObject<{
|
|
|
3641
3641
|
name: string;
|
|
3642
3642
|
};
|
|
3643
3643
|
}>, unknown>>]>>;
|
|
3644
|
-
ref: import("zod").ZodObject<{
|
|
3645
|
-
origin: import("zod").ZodString;
|
|
3646
|
-
kind: import("zod").ZodLiteral<"function">;
|
|
3647
|
-
name: import("zod").ZodString;
|
|
3648
|
-
}, zodCore.$strict>;
|
|
3649
3644
|
input: import("zod").ZodUnion<readonly [import("zod").ZodObject<{
|
|
3650
3645
|
mode: import("zod").ZodLiteral<"binary">;
|
|
3651
3646
|
metadata: import("zod").ZodUnion<readonly [import("zod").ZodBoolean, import("zod").ZodRecord<import("zod").ZodString, ZodType<JsonValue, unknown, zodCore.$ZodTypeInternals<JsonValue, unknown>>>]>;
|
|
3652
3647
|
maximumBytes: import("zod").ZodNumber;
|
|
3653
3648
|
}, zodCore.$strict>, import("zod").ZodUnion<readonly [import("zod").ZodBoolean, import("zod").ZodRecord<import("zod").ZodString, ZodType<JsonValue, unknown, zodCore.$ZodTypeInternals<JsonValue, unknown>>>]>]>;
|
|
3649
|
+
ref: import("zod").ZodObject<{
|
|
3650
|
+
origin: import("zod").ZodString;
|
|
3651
|
+
kind: import("zod").ZodLiteral<"function">;
|
|
3652
|
+
name: import("zod").ZodString;
|
|
3653
|
+
}, zodCore.$strict>;
|
|
3654
3654
|
}, zodCore.$strict> & ZodType<{
|
|
3655
3655
|
description?: string | undefined;
|
|
3656
3656
|
output: {
|
|
@@ -3706,16 +3706,16 @@ export declare const domainSchemaStructure: import("zod").ZodObject<{
|
|
|
3706
3706
|
name: string;
|
|
3707
3707
|
};
|
|
3708
3708
|
}> | undefined;
|
|
3709
|
-
ref: {
|
|
3710
|
-
origin: string;
|
|
3711
|
-
kind: "function";
|
|
3712
|
-
name: string;
|
|
3713
|
-
};
|
|
3714
3709
|
input: boolean | Record<string, JsonValue> | {
|
|
3715
3710
|
mode: "binary";
|
|
3716
3711
|
metadata: boolean | Record<string, JsonValue>;
|
|
3717
3712
|
maximumBytes: number;
|
|
3718
3713
|
};
|
|
3714
|
+
ref: {
|
|
3715
|
+
origin: string;
|
|
3716
|
+
kind: "function";
|
|
3717
|
+
name: string;
|
|
3718
|
+
};
|
|
3719
3719
|
} & AuthorizedPolicy, {
|
|
3720
3720
|
description?: string | undefined;
|
|
3721
3721
|
output: {
|
|
@@ -3735,16 +3735,16 @@ export declare const domainSchemaStructure: import("zod").ZodObject<{
|
|
|
3735
3735
|
} | undefined;
|
|
3736
3736
|
auth: "anonymous" | "authenticated" | "authorized";
|
|
3737
3737
|
policy?: unknown;
|
|
3738
|
-
ref: {
|
|
3739
|
-
origin: string;
|
|
3740
|
-
kind: "function";
|
|
3741
|
-
name: string;
|
|
3742
|
-
};
|
|
3743
3738
|
input: boolean | Record<string, unknown> | {
|
|
3744
3739
|
mode: "binary";
|
|
3745
3740
|
metadata: boolean | Record<string, unknown>;
|
|
3746
3741
|
maximumBytes: number;
|
|
3747
3742
|
};
|
|
3743
|
+
ref: {
|
|
3744
|
+
origin: string;
|
|
3745
|
+
kind: "function";
|
|
3746
|
+
name: string;
|
|
3747
|
+
};
|
|
3748
3748
|
}, zodCore.$ZodTypeInternals<{
|
|
3749
3749
|
description?: string | undefined;
|
|
3750
3750
|
output: {
|
|
@@ -3800,16 +3800,16 @@ export declare const domainSchemaStructure: import("zod").ZodObject<{
|
|
|
3800
3800
|
name: string;
|
|
3801
3801
|
};
|
|
3802
3802
|
}> | undefined;
|
|
3803
|
-
ref: {
|
|
3804
|
-
origin: string;
|
|
3805
|
-
kind: "function";
|
|
3806
|
-
name: string;
|
|
3807
|
-
};
|
|
3808
3803
|
input: boolean | Record<string, JsonValue> | {
|
|
3809
3804
|
mode: "binary";
|
|
3810
3805
|
metadata: boolean | Record<string, JsonValue>;
|
|
3811
3806
|
maximumBytes: number;
|
|
3812
3807
|
};
|
|
3808
|
+
ref: {
|
|
3809
|
+
origin: string;
|
|
3810
|
+
kind: "function";
|
|
3811
|
+
name: string;
|
|
3812
|
+
};
|
|
3813
3813
|
} & AuthorizedPolicy, {
|
|
3814
3814
|
description?: string | undefined;
|
|
3815
3815
|
output: {
|
|
@@ -3829,16 +3829,16 @@ export declare const domainSchemaStructure: import("zod").ZodObject<{
|
|
|
3829
3829
|
} | undefined;
|
|
3830
3830
|
auth: "anonymous" | "authenticated" | "authorized";
|
|
3831
3831
|
policy?: unknown;
|
|
3832
|
-
ref: {
|
|
3833
|
-
origin: string;
|
|
3834
|
-
kind: "function";
|
|
3835
|
-
name: string;
|
|
3836
|
-
};
|
|
3837
3832
|
input: boolean | Record<string, unknown> | {
|
|
3838
3833
|
mode: "binary";
|
|
3839
3834
|
metadata: boolean | Record<string, unknown>;
|
|
3840
3835
|
maximumBytes: number;
|
|
3841
3836
|
};
|
|
3837
|
+
ref: {
|
|
3838
|
+
origin: string;
|
|
3839
|
+
kind: "function";
|
|
3840
|
+
name: string;
|
|
3841
|
+
};
|
|
3842
3842
|
}>>>;
|
|
3843
3843
|
policies: import("zod").ZodRecord<import("zod").ZodString, import("zod").ZodObject<{
|
|
3844
3844
|
ref: import("zod").ZodObject<{
|
|
@@ -4017,4 +4017,6 @@ export declare const domainSchemaStructure: import("zod").ZodObject<{
|
|
|
4017
4017
|
}, zodCore.$strict>;
|
|
4018
4018
|
}, zodCore.$strict>;
|
|
4019
4019
|
export declare function parseDomainSchemaStructure(input: unknown): DomainSchemaIR;
|
|
4020
|
+
/** Parse the structure of one callable contract: a Function declaration without its ref. */
|
|
4021
|
+
export declare function parseCallableContract(input: unknown): CallableDefinition;
|
|
4020
4022
|
export {};
|