@astrale-os/kernel-dsl 1.0.0-beta.15 → 1.0.0-beta.17
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/class/authoring.js +3 -2
- package/dist/v1/builder/class/input.d.ts +45 -14
- package/dist/v1/builder/index.d.ts +2 -1
- package/dist/v1/builder/lowering/class.d.ts +8 -0
- package/dist/v1/builder/lowering/class.js +40 -16
- package/dist/v1/builder/lowering/schema/compile.js +3 -3
- package/dist/v1/builder/policy/authoring.js +97 -88
- package/dist/v1/bundle/model.js +1 -1
- package/dist/v1/compiled/admission/accept.js +1 -7
- package/dist/v1/compiled/admission/semantics/class.js +2 -3
- package/dist/v1/compiled/admission/semantics/policy/parity.js +25 -13
- package/dist/v1/compiled/admission/semantics/policy/typing.js +2 -2
- package/dist/v1/compiled/admission/wire/class.js +23 -18
- package/dist/v1/compiled/admission/wire/policy.d.ts +4 -3
- package/dist/v1/compiled/admission/wire/policy.js +8 -7
- package/dist/v1/compiled/loader/load.js +1 -1
- package/dist/v1/domain/create.js +1 -1
- package/dist/v1/domain/model/input.d.ts +2 -1
- package/dist/v1/domain/projection/classes.js +1 -1
- package/dist/v1/domain/validation/create.d.ts +2 -1
- package/dist/v1/domain/validation/create.js +1 -1
- package/dist/v1/index.d.ts +1 -1
- package/dist/v1/language/.spec/schemas/draft-2020-12.schema.json +462 -0
- package/dist/v1/language/callable/resolved.d.ts +11 -2
- package/dist/v1/language/class/model/class.d.ts +17 -5
- package/dist/v1/language/freeze.d.ts +5 -0
- package/dist/v1/{schema/canonical → language}/freeze.js +4 -0
- package/dist/v1/language/policy/analysis/typing.d.ts +5 -2
- package/dist/v1/language/policy/analysis/typing.js +158 -42
- package/dist/v1/language/policy/model/policy.d.ts +44 -4
- package/dist/v1/language/policy/model/policy.js +14 -0
- package/dist/v1/language/policy/normalization/normalize.d.ts +6 -6
- package/dist/v1/language/policy/normalization/normalize.js +5 -5
- package/dist/v1/language/validation/rule.d.ts +22 -6
- package/dist/v1/language/validation/rule.js +4 -15
- package/dist/v1/language/value/evaluate/program.d.ts +2 -1
- package/dist/v1/language/value/evaluate/program.js +2 -2
- package/dist/v1/language/value/evaluate/validate.d.ts +2 -2
- package/dist/v1/language/value/evaluate/validate.js +2 -2
- package/dist/v1/language/value/ordering.d.ts +5 -0
- package/dist/v1/language/value/ordering.js +14 -0
- package/dist/v1/language/value/profile/json.validate.d.ts +3 -1
- package/dist/v1/language/value/profile/json.validate.js +3 -2
- 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/schema/.spec/schemas/domain-schema.schema.json +210 -27
- package/dist/v1/schema/admission/callable.js +1 -1
- package/dist/v1/schema/canonical/revision.js +4 -4
- package/dist/v1/schema/compatibility/comparators/callables.js +1 -1
- package/dist/v1/schema/compatibility/comparators/classes.js +4 -4
- package/dist/v1/schema/compatibility/comparators/members.js +1 -1
- package/dist/v1/schema/compatibility/comparators/values.js +2 -2
- package/dist/v1/schema/compatibility/report.js +1 -1
- package/dist/v1/schema/compatibility/versioning/index.js +1 -1
- package/dist/v1/schema/model/schema.js +1 -1
- package/dist/v1/schema/normalization/domain.js +9 -8
- package/dist/v1/schema/resolution/domain.js +1 -1
- package/dist/v1/schema/resolution/origins.js +3 -2
- package/dist/v1/schema/rule.d.ts +8 -14
- package/dist/v1/schema/rule.js +14 -2
- package/dist/v1/schema/validation/class.d.ts +6 -2
- package/dist/v1/schema/validation/class.js +20 -25
- package/dist/v1/schema/validation/domain.js +1 -1
- package/dist/v1/schema/validation/domain.json-schema.d.ts +2 -2
- package/dist/v1/schema/validation/domain.json-schema.js +20 -6
- package/dist/v1/schema/validation/domain.structure.d.ts +4765 -1005
- package/dist/v1/schema/validation/domain.structure.js +21 -9
- package/dist/v1/schema/validation/json.json-schema.d.ts +18 -2
- package/dist/v1/schema/validation/json.json-schema.js +14 -4
- package/dist/v1/schema/validation/policy/budgets.d.ts +1 -1
- package/dist/v1/schema/validation/policy/budgets.js +4 -4
- 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/uses.d.ts +10 -2
- package/dist/v1/schema/validation/policy/uses.js +26 -13
- package/dist/v1/schema/validation/policy/validate.js +2 -3
- 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.json-schema.d.ts +7 -9
- package/dist/v1/schema/validation/value.json-schema.js +38 -27
- package/package.json +2 -1
- package/dist/v1/schema/canonical/domain.d.ts +0 -3
- package/dist/v1/schema/canonical/domain.digest.d.ts +0 -3
- package/dist/v1/schema/canonical/domain.digest.js +0 -5
- package/dist/v1/schema/canonical/domain.js +0 -5
- package/dist/v1/schema/canonical/freeze.d.ts +0 -1
|
@@ -277,7 +277,7 @@ const policyCheckObjectSchema = discriminatedUnion('kind', [
|
|
|
277
277
|
}),
|
|
278
278
|
refObjectSchema,
|
|
279
279
|
]).register(documentJsonSchema, { id: 'policy-check-object' });
|
|
280
|
-
/** A
|
|
280
|
+
/** A Class rule checks the observed or written instance or one exact member: it has no input. */
|
|
281
281
|
const classRuleObjectSchema = discriminatedUnion('kind', [
|
|
282
282
|
selfObjectSchema,
|
|
283
283
|
refObjectSchema,
|
|
@@ -294,6 +294,8 @@ function policyChecksSchema(objectSchema, id) {
|
|
|
294
294
|
}
|
|
295
295
|
const policyCheckExpressionSchema = policyChecksSchema(policyCheckObjectSchema, 'policy-check-expr');
|
|
296
296
|
const classRuleChecksSchema = policyChecksSchema(classRuleObjectSchema, 'class-rule-check-expr');
|
|
297
|
+
/** A Node create rule has no Node to check yet: its objects are exact members only. */
|
|
298
|
+
const createRuleChecksSchema = policyChecksSchema(refObjectSchema, 'create-rule-check-expr');
|
|
297
299
|
const callableOutputSchema = discriminatedUnion('mode', [
|
|
298
300
|
strictObject({
|
|
299
301
|
mode: literal('value'),
|
|
@@ -353,17 +355,27 @@ const methodDeclarationSchema = strictObject({
|
|
|
353
355
|
const methodRecordSchema = record(memberNameSchema, methodDeclarationSchema).register(documentJsonSchema, {
|
|
354
356
|
id: 'method-record',
|
|
355
357
|
});
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
358
|
+
// Each slot spells one constant: the other one is what its absence already means.
|
|
359
|
+
const observationPolicySchema = union([
|
|
360
|
+
literal(false),
|
|
361
|
+
policyRefSchema,
|
|
362
|
+
classRuleChecksSchema,
|
|
363
|
+
]).register(documentJsonSchema, { id: 'observation-policy' });
|
|
364
|
+
const writePolicySchema = union([literal(true), policyRefSchema, classRuleChecksSchema]).register(documentJsonSchema, { id: 'write-policy' });
|
|
365
|
+
const createPolicySchema = union([literal(true), createRuleChecksSchema]).register(documentJsonSchema, { id: 'create-policy' });
|
|
360
366
|
const nodeClassPoliciesSchema = strictObject({
|
|
361
|
-
read:
|
|
362
|
-
traverse:
|
|
367
|
+
read: observationPolicySchema.optional(),
|
|
368
|
+
traverse: observationPolicySchema.optional(),
|
|
369
|
+
create: createPolicySchema.optional(),
|
|
370
|
+
update: writePolicySchema.optional(),
|
|
371
|
+
delete: writePolicySchema.optional(),
|
|
363
372
|
});
|
|
364
373
|
const edgeClassPoliciesSchema = strictObject({
|
|
365
|
-
read:
|
|
366
|
-
traverse:
|
|
374
|
+
read: observationPolicySchema.optional(),
|
|
375
|
+
traverse: observationPolicySchema.optional(),
|
|
376
|
+
create: writePolicySchema.optional(),
|
|
377
|
+
update: writePolicySchema.optional(),
|
|
378
|
+
delete: writePolicySchema.optional(),
|
|
367
379
|
});
|
|
368
380
|
// The default `'internal'` is the absence of the field: one spelling per meaning, one revision.
|
|
369
381
|
const inheritanceSchema = enumeration(['open', 'final']);
|
|
@@ -1,5 +1,22 @@
|
|
|
1
1
|
import type { core as zodCore } from 'zod';
|
|
2
|
-
type
|
|
2
|
+
import type { DiagnosticCode as ValueDiagnosticCode } from '../../language/validation/diagnostic.js';
|
|
3
|
+
import type { SchemaDiagnosticCode } from '../diagnostic.js';
|
|
4
|
+
/** A JSON Schema of the published document, or a part of one. */
|
|
5
|
+
export type JsonSchema = zodCore.JSONSchema.JSONSchema;
|
|
6
|
+
/**
|
|
7
|
+
* A check JSON Schema cannot state, which the published document names: the code the Kernel
|
|
8
|
+
* reports, with its value code for a check on value schemas, and the rule in one plain sentence.
|
|
9
|
+
*/
|
|
10
|
+
export interface NamedRuleEntry {
|
|
11
|
+
readonly code: SchemaDiagnosticCode;
|
|
12
|
+
readonly valueCode?: ValueDiagnosticCode;
|
|
13
|
+
readonly statement: string;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* `schema` with the `x-astrale-rule` marker naming the checks of `Rule` it leaves to the Kernel.
|
|
17
|
+
* `Rule` is given at each call, so that a name outside it does not compile.
|
|
18
|
+
*/
|
|
19
|
+
export declare function named<Rule extends string = never>(schema: JsonSchema, ...rules: [NoInfer<Rule>, ...NoInfer<Rule>[]]): JsonSchema;
|
|
3
20
|
/**
|
|
4
21
|
* The check that a document is portable JSON, which `schema.decode` and `schema.accept` make first.
|
|
5
22
|
* JSON Schema reads a parsed value and compares numbers by their value: it states that no string or
|
|
@@ -21,4 +38,3 @@ export declare function jsonValueJsonSchema(): {
|
|
|
21
38
|
readonly root: JsonSchema;
|
|
22
39
|
readonly definitions: Readonly<Record<string, JsonSchema>>;
|
|
23
40
|
};
|
|
24
|
-
export {};
|
|
@@ -1,4 +1,11 @@
|
|
|
1
1
|
import { RULES } from '../rule.js';
|
|
2
|
+
/**
|
|
3
|
+
* `schema` with the `x-astrale-rule` marker naming the checks of `Rule` it leaves to the Kernel.
|
|
4
|
+
* `Rule` is given at each call, so that a name outside it does not compile.
|
|
5
|
+
*/
|
|
6
|
+
export function named(schema, ...rules) {
|
|
7
|
+
return { ...schema, 'x-astrale-rule': rules };
|
|
8
|
+
}
|
|
2
9
|
/**
|
|
3
10
|
* The check that a document is portable JSON, which `schema.decode` and `schema.accept` make first.
|
|
4
11
|
* JSON Schema reads a parsed value and compares numbers by their value: it states that no string or
|
|
@@ -18,17 +25,20 @@ export function jsonValueJsonSchema() {
|
|
|
18
25
|
return {
|
|
19
26
|
root: { $ref: VALUE },
|
|
20
27
|
definitions: {
|
|
21
|
-
'portable-json': {
|
|
28
|
+
'portable-json': named({
|
|
22
29
|
anyOf: [
|
|
23
30
|
{ type: 'null' },
|
|
24
31
|
{ type: 'boolean' },
|
|
25
32
|
{ type: 'number' },
|
|
26
33
|
{ $ref: TEXT },
|
|
27
34
|
{ type: 'array', items: { $ref: VALUE } },
|
|
28
|
-
{
|
|
35
|
+
{
|
|
36
|
+
type: 'object',
|
|
37
|
+
propertyNames: { $ref: TEXT },
|
|
38
|
+
additionalProperties: { $ref: VALUE },
|
|
39
|
+
},
|
|
29
40
|
],
|
|
30
|
-
|
|
31
|
-
},
|
|
41
|
+
}, 'portable-json'),
|
|
32
42
|
// The pattern is the character itself, which every regular-expression dialect reads.
|
|
33
43
|
'portable-string': { type: 'string', not: { pattern: '\u0000' } },
|
|
34
44
|
},
|
|
@@ -13,7 +13,7 @@ export interface PolicyMeasures {
|
|
|
13
13
|
readonly unmeasured: readonly string[];
|
|
14
14
|
/**
|
|
15
15
|
* Each check tree by the JSON Pointer of its position: the Policy of a Function or a Method, or
|
|
16
|
-
* the
|
|
16
|
+
* the rule of a Class slot.
|
|
17
17
|
*/
|
|
18
18
|
readonly checks: Readonly<Record<string, CheckMeasure>>;
|
|
19
19
|
}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
+
import { deepFreeze } from '../../../language/freeze.js';
|
|
1
2
|
import { limits } from '../../../language/limits.js';
|
|
2
3
|
import { exceededLimits, measureChecks, policyMeasurer, } from '../../../language/policy/analysis/budgets.js';
|
|
3
|
-
import { isClassRuleChecks } from '../../../language/policy/model/policy.js';
|
|
4
|
+
import { CLASS_OPERATIONS, isClassRuleChecks } from '../../../language/policy/model/policy.js';
|
|
4
5
|
import { normalizePolicyChecks, normalizePolicyExpression, } from '../../../language/policy/normalization/normalize.js';
|
|
5
6
|
import { escapePointerToken } from '../../../language/value/syntax/pointer.js';
|
|
6
|
-
import { deepFreeze } from '../../canonical/freeze.js';
|
|
7
7
|
import { structureDomainSchema } from '../domain.route.js';
|
|
8
8
|
import { SITE } from './uses.js';
|
|
9
9
|
/**
|
|
@@ -55,7 +55,7 @@ export function validateCheckBudget(context, pointer, measure) {
|
|
|
55
55
|
if (exceededLimits(measure).length === 0)
|
|
56
56
|
return;
|
|
57
57
|
const { checkDepth, checkBranches, checkLeaves } = limits.policy;
|
|
58
|
-
// A callable holds its checks at `policy`; a
|
|
58
|
+
// A callable holds its checks at `policy`; a Class rule at `policies/{operation}`.
|
|
59
59
|
const site = SITE[pointer.endsWith('/policy') ? 'callable' : 'class'];
|
|
60
60
|
context.add('PL_BUDGET', pointer, `${site.checks} require depth <= ${checkDepth}, branches <= ${checkBranches}, and leaves <= ${checkLeaves}; got ${measure.checkDepth}, ${measure.checkBranches}, ${measure.checkLeaves}.`);
|
|
61
61
|
}
|
|
@@ -76,7 +76,7 @@ function normalizedPolicies(schema) {
|
|
|
76
76
|
function* checkTrees(schema) {
|
|
77
77
|
for (const [name, declaration] of Object.entries(schema.classes)) {
|
|
78
78
|
const owner = `/classes/${escapePointerToken(name)}`;
|
|
79
|
-
for (const operation of
|
|
79
|
+
for (const operation of CLASS_OPERATIONS) {
|
|
80
80
|
const rule = declaration.policies[operation];
|
|
81
81
|
if (typeof rule === 'object' && isClassRuleChecks(rule)) {
|
|
82
82
|
yield [`${owner}/policies/${operation}`, rule];
|
|
@@ -22,8 +22,7 @@ export declare class PolicyValidationContext implements PolicyTypingScope {
|
|
|
22
22
|
edgeDeclaration(ref: ClassRef): Extract<ClassDefinition, {
|
|
23
23
|
readonly kind: 'edge';
|
|
24
24
|
}> | undefined;
|
|
25
|
-
|
|
26
|
-
classExtends(candidate: ClassRef, required: ClassRef): boolean;
|
|
25
|
+
resolveClass(ref: ClassRef): ClassDefinition | undefined;
|
|
27
26
|
/** The contract a Policy pattern reference types, reporting an invalid one under `/policies`. */
|
|
28
27
|
referenceContract(ref: PolicyObjectRef): ObjectContract | undefined;
|
|
29
28
|
add(code: SchemaDiagnosticCode, pointer: string, message: string, ref?: string): void;
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { Key } from '../../../addressing/index.js';
|
|
2
2
|
import { definitionRefIndex } from '../../../addressing/ordering.js';
|
|
3
|
-
import { classSatisfies } from '../../../language/class/inheritance.js';
|
|
4
3
|
import { diagnostic } from '../../diagnostic.js';
|
|
5
4
|
import { referenceContracts } from './objects.js';
|
|
6
5
|
const formatDefinitionRef = Key.of;
|
|
@@ -63,12 +62,8 @@ export class PolicyValidationContext {
|
|
|
63
62
|
}
|
|
64
63
|
return resolved.declaration;
|
|
65
64
|
}
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
const resolve = (ref) => this.environment.resolveAny(ref)?.declaration;
|
|
69
|
-
const actual = resolve(candidate);
|
|
70
|
-
const target = resolve(required);
|
|
71
|
-
return actual !== undefined && target !== undefined && classSatisfies(actual, target, resolve);
|
|
65
|
+
resolveClass(ref) {
|
|
66
|
+
return this.environment.resolveAny(ref)?.declaration;
|
|
72
67
|
}
|
|
73
68
|
/** The contract a Policy pattern reference types, reporting an invalid one under `/policies`. */
|
|
74
69
|
referenceContract(ref) {
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { CallableDefinition } from '../../../language/callable/model/callable.js';
|
|
2
|
+
import type { EdgeContract } from '../../../language/class/model/edge.js';
|
|
2
3
|
import type { PolicyTyping } from '../../../language/policy/analysis/typing.js';
|
|
3
4
|
import type { PolicyCheckExpression } from '../../../language/policy/model/policy.js';
|
|
4
5
|
import type { JsonSchema } from '../../../language/value/model/schema.js';
|
|
@@ -10,6 +11,13 @@ interface CheckContext {
|
|
|
10
11
|
readonly input: JsonSchema;
|
|
11
12
|
/** The Node Class that receives the call, which the checks name `self`; none, and none is named. */
|
|
12
13
|
readonly receiver?: ClassRef;
|
|
14
|
+
/** In an Edge Class rule, the Edge `self` names, in place of a receiver. */
|
|
15
|
+
readonly edge?: EdgeUse;
|
|
16
|
+
}
|
|
17
|
+
/** One Edge Class, whose declaration fixes the concrete Node Classes of its endpoints. */
|
|
18
|
+
interface EdgeUse {
|
|
19
|
+
readonly ref: ClassRef;
|
|
20
|
+
readonly declaration: EdgeContract;
|
|
13
21
|
}
|
|
14
22
|
/** How a refusal names the checks of each site and the contract of their objects. */
|
|
15
23
|
export declare const SITE: {
|
|
@@ -22,8 +30,8 @@ export declare const SITE: {
|
|
|
22
30
|
readonly objects: 'the Class rule object contract';
|
|
23
31
|
};
|
|
24
32
|
};
|
|
25
|
-
|
|
26
|
-
export declare function
|
|
33
|
+
/** Type every Class slot: its Policy against the instance it decides, or its Policy checks. */
|
|
34
|
+
export declare function validateClassUses(context: PolicyValidationContext, typing: PolicyTyping): void;
|
|
27
35
|
export declare function validateCallableUses(context: PolicyValidationContext, typing: PolicyTyping): void;
|
|
28
36
|
/**
|
|
29
37
|
* Validate the Policy of one callable contract written at `pointer`, wherever it is declared:
|
|
@@ -2,7 +2,7 @@ import { Key } from '../../../addressing/index.js';
|
|
|
2
2
|
import { definitionRefIndex } from '../../../addressing/ordering.js';
|
|
3
3
|
import { isBinaryInput } from '../../../language/callable/model/callable.js';
|
|
4
4
|
import { neverAdmittedClasses, } from '../../../language/policy/analysis/checks.js';
|
|
5
|
-
import { checksNameTheCaller, isClassRuleChecks } from '../../../language/policy/model/policy.js';
|
|
5
|
+
import { CLASS_OPERATIONS, checksNameTheCaller, isClassRuleChecks, } from '../../../language/policy/model/policy.js';
|
|
6
6
|
import { normalizePolicyChecks } from '../../../language/policy/normalization/normalize.js';
|
|
7
7
|
import { GRAPH_NODE_PATH_SCHEMA_ID } from '../../../language/value/syntax/node-path/schema.js';
|
|
8
8
|
import { escapePointerToken } from '../../../language/value/syntax/pointer.js';
|
|
@@ -15,11 +15,10 @@ export const SITE = {
|
|
|
15
15
|
callable: { checks: 'Callable checks', objects: 'the callable object contract' },
|
|
16
16
|
class: { checks: 'Class rule checks', objects: 'the Class rule object contract' },
|
|
17
17
|
};
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
validateClassPolicyUses(context, typing, 'traverse');
|
|
18
|
+
/** Type every Class slot: its Policy against the instance it decides, or its Policy checks. */
|
|
19
|
+
export function validateClassUses(context, typing) {
|
|
20
|
+
for (const operation of CLASS_OPERATIONS)
|
|
21
|
+
validateClassPolicyUses(context, typing, operation);
|
|
23
22
|
}
|
|
24
23
|
function validateClassPolicyUses(context, typing, operation) {
|
|
25
24
|
for (const [name, declaration] of Object.entries(context.environment.candidate.classes)) {
|
|
@@ -27,23 +26,23 @@ function validateClassPolicyUses(context, typing, operation) {
|
|
|
27
26
|
if (typeof ref !== 'object')
|
|
28
27
|
continue;
|
|
29
28
|
const pointer = `/classes/${escapePointerToken(name)}/policies/${operation}`;
|
|
29
|
+
const self = { origin: context.environment.candidate.origin, kind: 'class', name };
|
|
30
30
|
if (isClassRuleChecks(ref)) {
|
|
31
|
-
// Structure admits
|
|
31
|
+
// Structure admits `self` everywhere but in a Node create rule: there is no Node yet.
|
|
32
32
|
validateChecks(context, typing, ref, {
|
|
33
33
|
site: 'class',
|
|
34
34
|
pointer,
|
|
35
35
|
input: {},
|
|
36
|
-
|
|
36
|
+
...(declaration.kind === 'node'
|
|
37
|
+
? { receiver: self }
|
|
38
|
+
: { edge: { ref: self, declaration } }),
|
|
37
39
|
});
|
|
38
40
|
continue;
|
|
39
41
|
}
|
|
40
42
|
if (!context.resolvePolicy(ref, pointer))
|
|
41
43
|
continue;
|
|
42
44
|
if (declaration.kind === 'node') {
|
|
43
|
-
const object = {
|
|
44
|
-
kind: 'exact',
|
|
45
|
-
ref: { origin: context.environment.candidate.origin, kind: 'class', name },
|
|
46
|
-
};
|
|
45
|
+
const object = { kind: 'exact', ref: self };
|
|
47
46
|
if (typing.policyAccepts(ref.name, object))
|
|
48
47
|
continue;
|
|
49
48
|
context.add('PL_USE', pointer, `${formatDefinitionRef(ref)} is not valid for every normalized branch of ${operation} Node Class ${name}.`, formatDefinitionRef(ref));
|
|
@@ -98,7 +97,11 @@ function visitChecks(validation, typing, expression, context, pointer, leaves) {
|
|
|
98
97
|
if ('sameNode' in expression) {
|
|
99
98
|
for (const [side, object] of Object.entries(expression.sameNode)) {
|
|
100
99
|
const objectPointer = `${pointer}/sameNode/${side}`;
|
|
101
|
-
if (object.kind === 'self')
|
|
100
|
+
if (object.kind === 'self' && context.edge !== undefined) {
|
|
101
|
+
// `self` is an Edge there, and sameNode compares Nodes.
|
|
102
|
+
validation.add('PL_USE', objectPointer, 'sameNode compares Nodes: self is an Edge here.');
|
|
103
|
+
}
|
|
104
|
+
else if (object.kind === 'self')
|
|
102
105
|
selfContracts(validation, context, objectPointer);
|
|
103
106
|
else if (object.kind === 'input')
|
|
104
107
|
inputContracts(validation, context, object.field, objectPointer);
|
|
@@ -110,6 +113,16 @@ function visitChecks(validation, typing, expression, context, pointer, leaves) {
|
|
|
110
113
|
if ('check' in expression) {
|
|
111
114
|
if (!validation.resolvePolicy(expression.check, `${pointer}/check`))
|
|
112
115
|
return;
|
|
116
|
+
if (expression.object.kind === 'self' && context.edge !== undefined) {
|
|
117
|
+
// An Edge is checked as an Edge Class Policy is: over every concrete endpoint pair.
|
|
118
|
+
if (!typing.policyAcceptsEdgeClass(expression.check.name, context.edge.declaration)) {
|
|
119
|
+
validation.add('PL_USE', pointer, `${formatDefinitionRef(expression.check)} is not valid for every concrete source/target pair of Edge Class ${context.edge.ref.name}.`, formatDefinitionRef(expression.check));
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
const edge = formatDefinitionRef(context.edge.ref);
|
|
123
|
+
leaves.set(expression, { accepted: [edge], admitted: new Set([edge]) });
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
113
126
|
const objectPointer = `${pointer}/object`;
|
|
114
127
|
const contracts = expression.object.kind === 'self'
|
|
115
128
|
? selfContracts(validation, context, objectPointer)
|
|
@@ -3,7 +3,7 @@ import { validatePolicyBudgets } from './budgets.js';
|
|
|
3
3
|
import { PolicyValidationContext } from './context.js';
|
|
4
4
|
import { validatePolicyReferences } from './references.js';
|
|
5
5
|
import { validatePolicyTyping } from './typing.js';
|
|
6
|
-
import { validateCallableUses,
|
|
6
|
+
import { validateCallableUses, validateClassUses } from './uses.js';
|
|
7
7
|
/**
|
|
8
8
|
* Validates the closed V1 Policy language after structural parsing.
|
|
9
9
|
*
|
|
@@ -17,8 +17,7 @@ export function validatePolicies(environment, analysis) {
|
|
|
17
17
|
validatePolicyBudgets(context);
|
|
18
18
|
const typing = new PolicyTyping(context);
|
|
19
19
|
validatePolicyTyping(context, typing);
|
|
20
|
-
|
|
21
|
-
validateTraverseUses(context, typing);
|
|
20
|
+
validateClassUses(context, typing);
|
|
22
21
|
validateCallableUses(context, typing);
|
|
23
22
|
return Object.freeze(context.diagnostics);
|
|
24
23
|
}
|
|
@@ -10,6 +10,8 @@ export type { BlobOptions, BlobReceipt, BlobRef, BlobSchema, } from '../../langu
|
|
|
10
10
|
export type { JsonSchema as Schema, JsonSchemaObject as SchemaObject, PathAnnotation, PropertyObjectSchema as PropertySchema, ValueSchemaContext as Context, } from '../../language/value/model/schema.js';
|
|
11
11
|
export { instant } from '../../language/value/model/instant.js';
|
|
12
12
|
export type { OrderingKind } from '../../language/value/ordering.js';
|
|
13
|
+
export { VALUE_KINDS } from '../../language/value/ordering.js';
|
|
14
|
+
export { GRAPH_NODE_PATH_SCHEMA_ID as NODE_PATH } from '../../language/value/syntax/node-path/schema.js';
|
|
13
15
|
export { DECLARED_VALUE_KINDS as DECLARED_KINDS, FORBIDDEN_KEYWORDS, SUBSCHEMA_KEYWORDS, VALUE_KEYWORDS as KEYWORDS, } from '../../language/value/profile/vocabulary.js';
|
|
14
16
|
export type { DeclaredValueKind as DeclaredKind, ValueKeyword as Keyword, } from '../../language/value/profile/vocabulary.js';
|
|
15
17
|
export { VALUE_RULES as RULES } from '../../language/validation/rule.js';
|
|
@@ -8,6 +8,8 @@ const registries = new WeakMap();
|
|
|
8
8
|
export { VALUE_SCHEMA_DIALECT as DIALECT, VALUE_SCHEMA_VOCABULARY as VOCABULARY, } from '../../language/value/model/schema.js';
|
|
9
9
|
export { blob, isBlobValueSchema as isBlob } from '../../language/value/model/blob.js';
|
|
10
10
|
export { instant } from '../../language/value/model/instant.js';
|
|
11
|
+
export { VALUE_KINDS } from '../../language/value/ordering.js';
|
|
12
|
+
export { GRAPH_NODE_PATH_SCHEMA_ID as NODE_PATH } from '../../language/value/syntax/node-path/schema.js';
|
|
11
13
|
export { DECLARED_VALUE_KINDS as DECLARED_KINDS, FORBIDDEN_KEYWORDS, SUBSCHEMA_KEYWORDS, VALUE_KEYWORDS as KEYWORDS, } from '../../language/value/profile/vocabulary.js';
|
|
12
14
|
export { VALUE_RULES as RULES } from '../../language/validation/rule.js';
|
|
13
15
|
/** Validate one complete JSON value under the exact accepted schema that owns the contract. */
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import
|
|
2
|
-
type JsonSchema = zodCore.JSONSchema.JSONSchema;
|
|
1
|
+
import { type JsonSchema } from './json.json-schema.js';
|
|
3
2
|
/**
|
|
4
3
|
* The checks on value schemas JSON Schema cannot state, which the published document names with
|
|
5
4
|
* the positions it constrains. The Kernel reports each as `DV_VALUE_SCHEMA`, with its value code.
|
|
@@ -13,17 +12,17 @@ export declare const valueSchemaRules: {
|
|
|
13
12
|
readonly 'value-path': {
|
|
14
13
|
readonly code: 'DV_VALUE_SCHEMA';
|
|
15
14
|
readonly valueCode: 'VS_PATH_EXTENSION';
|
|
16
|
-
readonly statement: 'x-astrale-path annotates only a direct Property of a callable input object, beside exactly the $ref of the graph NodePath schema
|
|
15
|
+
readonly statement: 'x-astrale-path annotates only a direct Property of a callable input object, beside exactly the $ref of the graph NodePath schema.';
|
|
17
16
|
};
|
|
18
17
|
readonly 'value-recursion': {
|
|
19
18
|
readonly code: 'DV_VALUE_SCHEMA';
|
|
20
19
|
readonly valueCode: 'VS_RECURSION_PRODUCTIVE';
|
|
21
|
-
readonly statement:
|
|
20
|
+
readonly statement: string;
|
|
22
21
|
};
|
|
23
22
|
readonly 'value-reference-form': {
|
|
24
23
|
readonly code: 'DV_VALUE_SCHEMA';
|
|
25
24
|
readonly valueCode: 'VS_REFERENCE_FORM';
|
|
26
|
-
readonly statement:
|
|
25
|
+
readonly statement: string;
|
|
27
26
|
};
|
|
28
27
|
readonly 'value-reference-resolution': {
|
|
29
28
|
readonly code: 'DV_VALUE_SCHEMA';
|
|
@@ -33,19 +32,18 @@ export declare const valueSchemaRules: {
|
|
|
33
32
|
readonly 'value-regexp': {
|
|
34
33
|
readonly code: 'DV_VALUE_SCHEMA';
|
|
35
34
|
readonly valueCode: 'VS_REGEX_PORTABLE';
|
|
36
|
-
readonly statement:
|
|
35
|
+
readonly statement: string;
|
|
37
36
|
};
|
|
38
37
|
};
|
|
39
38
|
export type ValueSchemaRule = keyof typeof valueSchemaRules;
|
|
40
39
|
/**
|
|
41
40
|
* The value language in JSON Schema, for the document that embeds value schemas: `root` constrains
|
|
42
41
|
* the `value-schema` definition with the published dialect and what the profile adds to it,
|
|
43
|
-
* `definitions` holds the locations it reaches
|
|
44
|
-
* definition. A named check leaves `x-astrale-rule` at the positions it constrains.
|
|
42
|
+
* `definitions` holds the locations it reaches and the members of the Path annotation, and
|
|
43
|
+
* `nodeProperty` constrains a Node Property definition. A named check leaves `x-astrale-rule` at the positions it constrains.
|
|
45
44
|
*/
|
|
46
45
|
export declare function valueSchemaJsonSchema(): {
|
|
47
46
|
readonly root: readonly JsonSchema[];
|
|
48
47
|
readonly definitions: Readonly<Record<string, JsonSchema>>;
|
|
49
48
|
readonly nodeProperty: JsonSchema;
|
|
50
49
|
};
|
|
51
|
-
export {};
|
|
@@ -1,5 +1,7 @@
|
|
|
1
|
+
import { VALUE_RULES } from '../../language/validation/rule.js';
|
|
1
2
|
import { VALUE_SCHEMA_DIALECT } from '../../language/value/model/schema.js';
|
|
2
3
|
import { FORBIDDEN_KEYWORDS, SUBSCHEMA_KEYWORDS, VALUE_KEYWORDS, } from '../../language/value/profile/vocabulary.js';
|
|
4
|
+
import { named } from './json.json-schema.js';
|
|
3
5
|
/**
|
|
4
6
|
* The checks on value schemas JSON Schema cannot state, which the published document names with
|
|
5
7
|
* the positions it constrains. The Kernel reports each as `DV_VALUE_SCHEMA`, with its value code.
|
|
@@ -10,20 +12,21 @@ export const valueSchemaRules = {
|
|
|
10
12
|
valueCode: 'VS_VALUE_KIND',
|
|
11
13
|
statement: 'A blob is declared only by the whole schema of a Property, exactly as values.blob writes it.',
|
|
12
14
|
},
|
|
15
|
+
// Its members are stated (`path-annotation`); where it stands is named.
|
|
13
16
|
'value-path': {
|
|
14
17
|
code: 'DV_VALUE_SCHEMA',
|
|
15
18
|
valueCode: 'VS_PATH_EXTENSION',
|
|
16
|
-
statement: 'x-astrale-path annotates only a direct Property of a callable input object, beside exactly the $ref of the graph NodePath schema
|
|
19
|
+
statement: 'x-astrale-path annotates only a direct Property of a callable input object, beside exactly the $ref of the graph NodePath schema.',
|
|
17
20
|
},
|
|
18
21
|
'value-recursion': {
|
|
19
22
|
code: 'DV_VALUE_SCHEMA',
|
|
20
23
|
valueCode: 'VS_RECURSION_PRODUCTIVE',
|
|
21
|
-
statement:
|
|
24
|
+
statement: VALUE_RULES.VS_RECURSION_PRODUCTIVE.statement,
|
|
22
25
|
},
|
|
23
26
|
'value-reference-form': {
|
|
24
27
|
code: 'DV_VALUE_SCHEMA',
|
|
25
28
|
valueCode: 'VS_REFERENCE_FORM',
|
|
26
|
-
statement:
|
|
29
|
+
statement: VALUE_RULES.VS_REFERENCE_FORM.statement,
|
|
27
30
|
},
|
|
28
31
|
'value-reference-resolution': {
|
|
29
32
|
code: 'DV_VALUE_SCHEMA',
|
|
@@ -33,17 +36,18 @@ export const valueSchemaRules = {
|
|
|
33
36
|
'value-regexp': {
|
|
34
37
|
code: 'DV_VALUE_SCHEMA',
|
|
35
38
|
valueCode: 'VS_REGEX_PORTABLE',
|
|
36
|
-
statement:
|
|
39
|
+
statement: VALUE_RULES.VS_REGEX_PORTABLE.statement,
|
|
37
40
|
},
|
|
38
41
|
};
|
|
39
42
|
const KIND = 'x-astrale-value-kind';
|
|
40
43
|
const LOCATION = '#/$defs/value-location';
|
|
41
44
|
const SUBSCHEMA = '#/$defs/value-subschema';
|
|
45
|
+
const PATH = '#/$defs/path-annotation';
|
|
42
46
|
/**
|
|
43
47
|
* The value language in JSON Schema, for the document that embeds value schemas: `root` constrains
|
|
44
48
|
* the `value-schema` definition with the published dialect and what the profile adds to it,
|
|
45
|
-
* `definitions` holds the locations it reaches
|
|
46
|
-
* definition. A named check leaves `x-astrale-rule` at the positions it constrains.
|
|
49
|
+
* `definitions` holds the locations it reaches and the members of the Path annotation, and
|
|
50
|
+
* `nodeProperty` constrains a Node Property definition. A named check leaves `x-astrale-rule` at the positions it constrains.
|
|
47
51
|
*/
|
|
48
52
|
export function valueSchemaJsonSchema() {
|
|
49
53
|
return {
|
|
@@ -53,10 +57,7 @@ export function valueSchemaJsonSchema() {
|
|
|
53
57
|
objectOrBoolean({
|
|
54
58
|
properties: {
|
|
55
59
|
$schema: { const: VALUE_SCHEMA_DIALECT },
|
|
56
|
-
[KIND]: {
|
|
57
|
-
enum: ['instant', 'blob'],
|
|
58
|
-
'x-astrale-rule': 'value-blob',
|
|
59
|
-
},
|
|
60
|
+
[KIND]: named({ enum: ['instant', 'blob'] }, 'value-blob'),
|
|
60
61
|
},
|
|
61
62
|
}),
|
|
62
63
|
],
|
|
@@ -68,37 +69,47 @@ export function valueSchemaJsonSchema() {
|
|
|
68
69
|
},
|
|
69
70
|
properties: {
|
|
70
71
|
$id: false,
|
|
71
|
-
$ref: {
|
|
72
|
-
type: 'string',
|
|
73
|
-
'x-astrale-rule': [
|
|
74
|
-
'value-recursion',
|
|
75
|
-
'value-reference-form',
|
|
76
|
-
'value-reference-resolution',
|
|
77
|
-
],
|
|
78
|
-
},
|
|
72
|
+
$ref: named({ type: 'string' }, 'value-recursion', 'value-reference-form', 'value-reference-resolution'),
|
|
79
73
|
enum: { type: 'array', uniqueItems: true },
|
|
80
|
-
fullMatch: { type: 'string', '
|
|
81
|
-
pattern: { type: 'string', '
|
|
82
|
-
'x-astrale-path': {
|
|
74
|
+
fullMatch: named({ type: 'string' }, 'value-regexp'),
|
|
75
|
+
pattern: named({ type: 'string' }, 'value-regexp'),
|
|
76
|
+
'x-astrale-path': named({ $ref: PATH }, 'value-path'),
|
|
83
77
|
...Object.fromEntries(SUBSCHEMA_KEYWORDS.single.map((keyword) => [keyword, { $ref: SUBSCHEMA }])),
|
|
84
78
|
...Object.fromEntries(SUBSCHEMA_KEYWORDS.array.map((keyword) => [
|
|
85
79
|
keyword,
|
|
86
80
|
{ type: 'array', items: { $ref: SUBSCHEMA } },
|
|
87
81
|
])),
|
|
88
|
-
...Object.fromEntries(SUBSCHEMA_KEYWORDS.map.map((keyword) =>
|
|
89
|
-
|
|
90
|
-
{
|
|
82
|
+
...Object.fromEntries(SUBSCHEMA_KEYWORDS.map.map((keyword) => {
|
|
83
|
+
const members = {
|
|
91
84
|
type: 'object',
|
|
92
85
|
additionalProperties: { $ref: SUBSCHEMA },
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
86
|
+
};
|
|
87
|
+
if (keyword !== 'patternProperties')
|
|
88
|
+
return [keyword, members];
|
|
89
|
+
return [keyword, named(members, 'value-regexp')];
|
|
90
|
+
})),
|
|
96
91
|
},
|
|
97
92
|
// An instant is a string.
|
|
98
93
|
if: { required: [KIND], properties: { [KIND]: { const: 'instant' } } },
|
|
99
94
|
// oxlint-disable-next-line unicorn/no-thenable -- the JSON Schema keyword
|
|
100
95
|
then: { required: ['type'], properties: { type: { const: 'string' } } },
|
|
101
96
|
}),
|
|
97
|
+
// The Path annotation: the one Node a Path field resolves, of a Node Class it accepts.
|
|
98
|
+
'path-annotation': {
|
|
99
|
+
type: 'object',
|
|
100
|
+
properties: {
|
|
101
|
+
target: { type: 'string', const: 'node' },
|
|
102
|
+
cardinality: { type: 'string', const: 'one' },
|
|
103
|
+
accepts: {
|
|
104
|
+
type: 'array',
|
|
105
|
+
minItems: 1,
|
|
106
|
+
uniqueItems: true,
|
|
107
|
+
items: named({ $ref: '#/$defs/class-ref' }, 'value-reference-resolution'),
|
|
108
|
+
},
|
|
109
|
+
},
|
|
110
|
+
required: ['target', 'cardinality', 'accepts'],
|
|
111
|
+
additionalProperties: false,
|
|
112
|
+
},
|
|
102
113
|
// A location below the root: no $schema, and no blob.
|
|
103
114
|
'value-subschema': {
|
|
104
115
|
allOf: [
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@astrale-os/kernel-dsl",
|
|
3
|
-
"version": "1.0.0-beta.
|
|
3
|
+
"version": "1.0.0-beta.17",
|
|
4
4
|
"description": "Astrale Kernel DSL - Schema authoring, inference, and IR serialization",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"repository": {
|
|
@@ -81,6 +81,7 @@
|
|
|
81
81
|
},
|
|
82
82
|
"./v1/schema/domain-schema.schema.json": "./dist/v1/schema/.spec/schemas/domain-schema.schema.json",
|
|
83
83
|
"./v1/schema/value-schema.schema.json": "./dist/v1/language/.spec/schemas/value-schema.schema.json",
|
|
84
|
+
"./v1/schema/draft-2020-12.schema.json": "./dist/v1/language/.spec/schemas/draft-2020-12.schema.json",
|
|
84
85
|
"./v1/value/full-match": {
|
|
85
86
|
"types": "./dist/v1/value/full-match.d.ts",
|
|
86
87
|
"import": "./dist/v1/value/full-match.js"
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export declare function deepFreeze<T>(value: T): Readonly<T>;
|