@astrale-os/kernel-dsl 1.0.0-beta.13 → 1.0.0-beta.15
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/addressing/key.js +2 -1
- package/dist/v1/addressing/patterns.d.ts +1 -0
- package/dist/v1/addressing/patterns.js +2 -1
- package/dist/v1/builder/class/authoring.d.ts +43 -0
- package/dist/v1/builder/class/property.d.ts +3 -9
- package/dist/v1/builder/class/property.js +1 -0
- package/dist/v1/builder/index.d.ts +1 -1
- package/dist/v1/builder/lowering/class.js +5 -1
- package/dist/v1/builder/lowering/policy.js +2 -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 +44 -14
- package/dist/v1/builder/schema/projection/class.d.ts +116 -14
- package/dist/v1/bundle/codec.js +9 -9
- package/dist/v1/bundle/diagnostic.d.ts +2 -0
- package/dist/v1/bundle/diagnostic.js +6 -0
- package/dist/v1/bundle/structure.js +2 -2
- 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/domain/validation/create.d.ts +4 -1
- package/dist/v1/domain/validation/create.js +21 -34
- package/dist/v1/language/.spec/schemas/value-schema.schema.json +22 -0
- package/dist/v1/language/class/model/class.d.ts +4 -4
- package/dist/v1/language/class/model/edge.d.ts +2 -2
- package/dist/v1/language/class/model/property.d.ts +16 -7
- package/dist/v1/language/core/facts.d.ts +17 -4
- package/dist/v1/language/core/facts.js +26 -5
- package/dist/v1/language/definition/metadata.d.ts +5 -0
- package/dist/v1/language/definition/metadata.js +7 -0
- 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.js +4 -3
- package/dist/v1/language/policy/model/policy.d.ts +5 -5
- package/dist/v1/language/policy/normalization/normalize.js +17 -16
- package/dist/v1/language/validation/diagnostic.d.ts +1 -1
- package/dist/v1/language/validation/rule.d.ts +29 -0
- package/dist/v1/language/validation/rule.js +120 -0
- package/dist/v1/language/value/profile/meta-schema.compiled.js +4 -1
- package/dist/v1/language/value/profile/rules.js +13 -37
- package/dist/v1/language/value/profile/vocabulary.d.ts +35 -2
- package/dist/v1/language/value/profile/vocabulary.js +52 -5
- package/dist/v1/language/value/reference/registry.index.js +2 -1
- 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/language/value/syntax/keywords.d.ts +3 -3
- package/dist/v1/language/value/syntax/keywords.js +4 -20
- package/dist/v1/language/value/syntax/node-path/validate.js +2 -3
- package/dist/v1/schema/.spec/schemas/domain-schema.schema.json +2336 -0
- 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/analysis/class.js +33 -15
- package/dist/v1/schema/compatibility/api.d.ts +1 -1
- package/dist/v1/schema/compatibility/coordinate.js +3 -3
- package/dist/v1/schema/compatibility/model.d.ts +5 -0
- package/dist/v1/schema/diagnostic.d.ts +17 -3
- package/dist/v1/schema/diagnostic.js +11 -0
- package/dist/v1/schema/index.d.ts +10 -1
- package/dist/v1/schema/index.js +4 -0
- package/dist/v1/schema/model/schema.d.ts +19 -1
- package/dist/v1/schema/normalization/domain.d.ts +2 -0
- package/dist/v1/schema/normalization/domain.js +2 -1
- package/dist/v1/schema/resolution/closure.js +2 -1
- package/dist/v1/schema/resolution/context.js +10 -8
- 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 +32 -0
- package/dist/v1/schema/rule.js +428 -0
- package/dist/v1/schema/validation/callable.d.ts +35 -2
- package/dist/v1/schema/validation/callable.js +51 -8
- package/dist/v1/schema/validation/class.js +36 -44
- package/dist/v1/schema/validation/core.js +19 -13
- package/dist/v1/schema/validation/domain.js +2 -2
- package/dist/v1/schema/validation/domain.json-schema.d.ts +15 -0
- package/dist/v1/schema/validation/domain.json-schema.js +183 -0
- 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 +4020 -0
- package/dist/v1/schema/validation/domain.structure.js +359 -153
- package/dist/v1/schema/validation/json.json-schema.d.ts +24 -0
- package/dist/v1/schema/validation/json.json-schema.js +36 -0
- 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/references.js +6 -21
- package/dist/v1/schema/validation/policy/uses.d.ts +29 -0
- package/dist/v1/schema/validation/policy/uses.js +28 -51
- package/dist/v1/schema/validation/value-api.d.ts +12 -1
- package/dist/v1/schema/validation/value-api.js +10 -1
- package/dist/v1/schema/validation/value.d.ts +3 -0
- package/dist/v1/schema/validation/value.js +6 -12
- package/dist/v1/schema/validation/value.json-schema.d.ts +51 -0
- package/dist/v1/schema/validation/value.json-schema.js +126 -0
- package/package.json +3 -1
|
@@ -1,5 +1,18 @@
|
|
|
1
1
|
import { compareUnicode } from '../../addressing/ordering.js';
|
|
2
|
-
import { countBounds, hasDirectedCycle } from '../class/inheritance.js';
|
|
2
|
+
import { countBounds, hasDirectedCycle, KERNEL_ORIGIN } from '../class/inheritance.js';
|
|
3
|
+
/*
|
|
4
|
+
* The Core fact rules that authored and compiled admission share. Each admission resolves its own
|
|
5
|
+
* Core Nodes and Edges, then reports every issue under its own codes, messages and pointers.
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* The Class of the owning Domain's Node: every Domain principal is a Node of the final Kernel
|
|
9
|
+
* `Domain` Class, so a Core Edge that names the Domain must accept that Class at that endpoint.
|
|
10
|
+
*/
|
|
11
|
+
export const DOMAIN_ENDPOINT_CLASS = Object.freeze({
|
|
12
|
+
origin: KERNEL_ORIGIN,
|
|
13
|
+
kind: 'class',
|
|
14
|
+
name: 'Domain',
|
|
15
|
+
});
|
|
3
16
|
/** Whether the endpoints satisfy the contract: in order when directed, in either order when not. */
|
|
4
17
|
export function coreEndpointsSatisfy(contract, source, target, satisfies) {
|
|
5
18
|
if (contract.orientation === 'directed') {
|
|
@@ -27,7 +40,10 @@ export function checkHeldKeys(facts, keys, report) {
|
|
|
27
40
|
holders.set(key, fact);
|
|
28
41
|
}
|
|
29
42
|
}
|
|
30
|
-
/**
|
|
43
|
+
/**
|
|
44
|
+
* Check normalized Edge identity, noSelf, keys, and the acyclicity and Core degrees of each exact
|
|
45
|
+
* concrete Edge Class over its own Edges.
|
|
46
|
+
*/
|
|
31
47
|
export function checkCoreEdgeSet(set) {
|
|
32
48
|
const identities = new Map();
|
|
33
49
|
const slices = new Map(set.classes.map(({ key }) => [key, []]));
|
|
@@ -48,6 +64,10 @@ export function checkCoreEdgeSet(set) {
|
|
|
48
64
|
}
|
|
49
65
|
checkHeldKeys(set.edges, set.keys, (fact, previous) => set.report({ kind: 'key', edge: fact.edge, previous: previous.edge }));
|
|
50
66
|
for (const { key, contract } of set.classes) {
|
|
67
|
+
// No Edge is of an abstract Class exactly, so it bounds no degree and closes no cycle of its
|
|
68
|
+
// own: each concrete descendant carries the counts it refines, over its own Edges.
|
|
69
|
+
if (contract.abstract)
|
|
70
|
+
continue;
|
|
51
71
|
const slice = slices.get(key);
|
|
52
72
|
if (contract.constraints.acyclic === true && hasDirectedCycle(slice))
|
|
53
73
|
set.report({ kind: 'acyclic', class: key });
|
|
@@ -78,9 +98,10 @@ function checkDegrees(set, contract, slice) {
|
|
|
78
98
|
}
|
|
79
99
|
}
|
|
80
100
|
/**
|
|
81
|
-
* Every declared Core Node proves its own complete degree, so both bounds hold. A projected member
|
|
82
|
-
* is only named by the Edges that reach it: the Domain may also create
|
|
83
|
-
* Class against it, so a minimum is unprovable here and only the maximum
|
|
101
|
+
* Every declared Core Node proves its own complete degree, so both bounds hold. A projected member,
|
|
102
|
+
* and the owning Domain, is only named by the Edges that reach it: the Domain may also create
|
|
103
|
+
* ordinary Edges of the same Class against it, so a minimum is unprovable here and only the maximum
|
|
104
|
+
* is enforced.
|
|
84
105
|
*/
|
|
85
106
|
function degreeSubjects(nodes, slice) {
|
|
86
107
|
const subjects = new Map(nodes.map((node) => [node.key, { ...node, minimum: true }]));
|
|
@@ -18,3 +18,8 @@ export declare const name: Readonly<{
|
|
|
18
18
|
}>;
|
|
19
19
|
/** The common source and compiled grammar for a name displayed exactly as authored. */
|
|
20
20
|
export declare function isDomainNameText(value: string): boolean;
|
|
21
|
+
/**
|
|
22
|
+
* The same grammar as a portable pattern: no control character, and neither first nor last a
|
|
23
|
+
* character `trim` removes, spelled out since `\s` differs between regular expression engines.
|
|
24
|
+
*/
|
|
25
|
+
export declare const DOMAIN_NAME_TEXT_PATTERN: RegExp;
|
|
@@ -22,3 +22,10 @@ const DOMAIN_NAME_CONTROL = /[\u0000-\u001f\u007f-\u009f\u2028\u2029]/u;
|
|
|
22
22
|
export function isDomainNameText(value) {
|
|
23
23
|
return value.trim() === value && !DOMAIN_NAME_CONTROL.test(value);
|
|
24
24
|
}
|
|
25
|
+
/**
|
|
26
|
+
* The same grammar as a portable pattern: no control character, and neither first nor last a
|
|
27
|
+
* character `trim` removes, spelled out since `\s` differs between regular expression engines.
|
|
28
|
+
*/
|
|
29
|
+
export const DOMAIN_NAME_TEXT_PATTERN =
|
|
30
|
+
// oxlint-disable-next-line no-control-regex
|
|
31
|
+
/^(?:[^\u0000-\u001f\u007f-\u009f\u0020\u00a0\u1680\u2000-\u200a\u2028\u2029\u202f\u205f\u3000\ufeff](?:[^\u0000-\u001f\u007f-\u009f\u2028\u2029]*[^\u0000-\u001f\u007f-\u009f\u0020\u00a0\u1680\u2000-\u200a\u2028\u2029\u202f\u205f\u3000\ufeff])?)?$/u;
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The limits of the V1 language, each defined once: every check of a source Schema, a Bundle or a
|
|
3
|
+
* compiled closure reads its bound here. Every limit is inclusive: a count equal to it is
|
|
4
|
+
* accepted, one more is refused. `schema.limits` publishes this object, and each rule of
|
|
5
|
+
* `schema.RULES` and `schema.values.RULES` that enforces some of them names them in `limits`.
|
|
6
|
+
*/
|
|
7
|
+
export declare const limits: Readonly<{
|
|
8
|
+
/** Bounds of the dependency graph of one Domain (`DV_DEPENDENCY_LIMITS`). */
|
|
9
|
+
dependency: Readonly<{
|
|
10
|
+
/** Direct dependencies one Domain declares. */
|
|
11
|
+
direct: 32;
|
|
12
|
+
/** Domains of a dependency closure, the Domain itself included. */
|
|
13
|
+
closure: 256;
|
|
14
|
+
/** Dependencies on the longest chain from a Domain: a direct dependency is at depth 1. */
|
|
15
|
+
depth: 32;
|
|
16
|
+
}>;
|
|
17
|
+
/** Bounds of one I-Regexp in a value schema (`VS_REGEX_PORTABLE`), as RFC 9485 §8 allows. */
|
|
18
|
+
regexp: Readonly<{
|
|
19
|
+
/** Code points of one source. */
|
|
20
|
+
maxCodePoints: 4096;
|
|
21
|
+
/** Groups nested in one another in one source. */
|
|
22
|
+
maxNesting: 32;
|
|
23
|
+
/** The greatest bound of a counted repetition `{n}` or `{n,m}`. */
|
|
24
|
+
maxRepeat: 1000;
|
|
25
|
+
/** States of one compiled machine; the counted-run form never expands and is not bounded. */
|
|
26
|
+
maxStates: 10000;
|
|
27
|
+
}>;
|
|
28
|
+
/**
|
|
29
|
+
* Bounds of the Policy language. A named Policy is measured once normalized and once each
|
|
30
|
+
* composition is expanded into the Policies it names; a check tree, the checks of a callable or
|
|
31
|
+
* of a Node Class rule, once normalized (`PL_BUDGET`). The structure bounds the others
|
|
32
|
+
* (`STRUCTURE_MAXIMUM`).
|
|
33
|
+
*/
|
|
34
|
+
policy: Readonly<{
|
|
35
|
+
/**
|
|
36
|
+
* Levels of a Policy pattern: an Edge or sameNode is one, and each allOf, anyOf, exists and
|
|
37
|
+
* composition adds one.
|
|
38
|
+
*/
|
|
39
|
+
patternDepth: 4;
|
|
40
|
+
/**
|
|
41
|
+
* Branches of a Policy's normal form: an anyOf adds those of its operands, an allOf multiplies
|
|
42
|
+
* them.
|
|
43
|
+
*/
|
|
44
|
+
normalizedBranches: 8;
|
|
45
|
+
/** Nodes one exists declares. */
|
|
46
|
+
variablesPerExists: 4;
|
|
47
|
+
/** Graph predicates, Edges and sameNode equalities, of one normalized branch. */
|
|
48
|
+
edgePredicates: 6;
|
|
49
|
+
/** The greatest bound of a repeated Edge. */
|
|
50
|
+
repetition: 3;
|
|
51
|
+
/** Domains a Policy names: its own, and the origin of every Class and object it references. */
|
|
52
|
+
referencedDomains: 4;
|
|
53
|
+
/** Levels of a check tree: a check or sameNode is one, and each allOf and anyOf adds one. */
|
|
54
|
+
checkDepth: 4;
|
|
55
|
+
/** Branches of a check tree: an anyOf adds those of its operands, an allOf multiplies them. */
|
|
56
|
+
checkBranches: 8;
|
|
57
|
+
/** Checks and sameNode equalities of a check tree. */
|
|
58
|
+
checkLeaves: 8;
|
|
59
|
+
}>;
|
|
60
|
+
}>;
|
|
61
|
+
/** A limit by its key in `limits`, `<group>.<name>`: `policy.patternDepth`. */
|
|
62
|
+
export type LimitKey = {
|
|
63
|
+
readonly [Group in keyof typeof limits & string]: `${Group}.${keyof (typeof limits)[Group] & string}`;
|
|
64
|
+
}[keyof typeof limits & string];
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The limits of the V1 language, each defined once: every check of a source Schema, a Bundle or a
|
|
3
|
+
* compiled closure reads its bound here. Every limit is inclusive: a count equal to it is
|
|
4
|
+
* accepted, one more is refused. `schema.limits` publishes this object, and each rule of
|
|
5
|
+
* `schema.RULES` and `schema.values.RULES` that enforces some of them names them in `limits`.
|
|
6
|
+
*/
|
|
7
|
+
export const limits = Object.freeze({
|
|
8
|
+
/** Bounds of the dependency graph of one Domain (`DV_DEPENDENCY_LIMITS`). */
|
|
9
|
+
dependency: Object.freeze({
|
|
10
|
+
/** Direct dependencies one Domain declares. */
|
|
11
|
+
direct: 32,
|
|
12
|
+
/** Domains of a dependency closure, the Domain itself included. */
|
|
13
|
+
closure: 256,
|
|
14
|
+
/** Dependencies on the longest chain from a Domain: a direct dependency is at depth 1. */
|
|
15
|
+
depth: 32,
|
|
16
|
+
}),
|
|
17
|
+
/** Bounds of one I-Regexp in a value schema (`VS_REGEX_PORTABLE`), as RFC 9485 §8 allows. */
|
|
18
|
+
regexp: Object.freeze({
|
|
19
|
+
/** Code points of one source. */
|
|
20
|
+
maxCodePoints: 4096,
|
|
21
|
+
/** Groups nested in one another in one source. */
|
|
22
|
+
maxNesting: 32,
|
|
23
|
+
/** The greatest bound of a counted repetition `{n}` or `{n,m}`. */
|
|
24
|
+
maxRepeat: 1000,
|
|
25
|
+
/** States of one compiled machine; the counted-run form never expands and is not bounded. */
|
|
26
|
+
maxStates: 10_000,
|
|
27
|
+
}),
|
|
28
|
+
/**
|
|
29
|
+
* Bounds of the Policy language. A named Policy is measured once normalized and once each
|
|
30
|
+
* composition is expanded into the Policies it names; a check tree, the checks of a callable or
|
|
31
|
+
* of a Node Class rule, once normalized (`PL_BUDGET`). The structure bounds the others
|
|
32
|
+
* (`STRUCTURE_MAXIMUM`).
|
|
33
|
+
*/
|
|
34
|
+
policy: Object.freeze({
|
|
35
|
+
/**
|
|
36
|
+
* Levels of a Policy pattern: an Edge or sameNode is one, and each allOf, anyOf, exists and
|
|
37
|
+
* composition adds one.
|
|
38
|
+
*/
|
|
39
|
+
patternDepth: 4,
|
|
40
|
+
/**
|
|
41
|
+
* Branches of a Policy's normal form: an anyOf adds those of its operands, an allOf multiplies
|
|
42
|
+
* them.
|
|
43
|
+
*/
|
|
44
|
+
normalizedBranches: 8,
|
|
45
|
+
/** Nodes one exists declares. */
|
|
46
|
+
variablesPerExists: 4,
|
|
47
|
+
/** Graph predicates, Edges and sameNode equalities, of one normalized branch. */
|
|
48
|
+
edgePredicates: 6,
|
|
49
|
+
/** The greatest bound of a repeated Edge. */
|
|
50
|
+
repetition: 3,
|
|
51
|
+
/** Domains a Policy names: its own, and the origin of every Class and object it references. */
|
|
52
|
+
referencedDomains: 4,
|
|
53
|
+
/** Levels of a check tree: a check or sameNode is one, and each allOf and anyOf adds one. */
|
|
54
|
+
checkDepth: 4,
|
|
55
|
+
/** Branches of a check tree: an anyOf adds those of its operands, an allOf multiplies them. */
|
|
56
|
+
checkBranches: 8,
|
|
57
|
+
/** Checks and sameNode equalities of a check tree. */
|
|
58
|
+
checkLeaves: 8,
|
|
59
|
+
}),
|
|
60
|
+
});
|
|
@@ -1,28 +1,49 @@
|
|
|
1
1
|
import type { PolicyRef } from '../../../addressing/index.js';
|
|
2
2
|
import type { PolicyCheckExpression, PolicyExpression } from '../model/policy.js';
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
export
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
export declare const MAX_CHECK_LEAVES = 8;
|
|
3
|
+
import { limits } from '../../limits.js';
|
|
4
|
+
/** A key of `limits.policy`. */
|
|
5
|
+
export type PolicyLimit = keyof typeof limits.policy;
|
|
6
|
+
/**
|
|
7
|
+
* Where a count of Policy branches stops: one past its limit. Each branch count is exact up to its
|
|
8
|
+
* limit plus one and reads as that once it is over, so expansion stays bounded.
|
|
9
|
+
*/
|
|
11
10
|
export declare const BRANCH_OVERFLOW: number;
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
11
|
+
/**
|
|
12
|
+
* What `PL_BUDGET` compares with `limits.policy` for one named Policy once normalized and each of
|
|
13
|
+
* its compositions expanded, every count under the key of its limit. The branch count stops one
|
|
14
|
+
* past its limit: it is exact up to that, and once it is over, `edgePredicates` reads the branches
|
|
15
|
+
* it counted only. The other counts are exact.
|
|
16
|
+
*/
|
|
17
|
+
export interface PolicyMeasure {
|
|
18
|
+
/** Levels of the expanded pattern. */
|
|
19
|
+
readonly patternDepth: number;
|
|
20
|
+
/** Branches of its normal form, at most `limits.policy.normalizedBranches + 1`. */
|
|
21
|
+
readonly normalizedBranches: number;
|
|
22
|
+
/** The most graph predicates, Edges and sameNode equalities, of one branch. */
|
|
23
|
+
readonly edgePredicates: number;
|
|
24
|
+
/** Domains the expanded pattern names, its own included. */
|
|
25
|
+
readonly referencedDomains: number;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* What `PL_BUDGET` compares with `limits.policy` for one check tree once normalized, every count
|
|
29
|
+
* under the key of its limit. The branch count stops one past its limit; the other counts are
|
|
30
|
+
* exact.
|
|
31
|
+
*/
|
|
32
|
+
export interface CheckMeasure {
|
|
33
|
+
/** Levels of the tree. */
|
|
34
|
+
readonly checkDepth: number;
|
|
35
|
+
/** Branches of its normal form, at most `limits.policy.checkBranches + 1`. */
|
|
36
|
+
readonly checkBranches: number;
|
|
37
|
+
/** Checks and sameNode equalities of the tree. */
|
|
38
|
+
readonly checkLeaves: number;
|
|
16
39
|
}
|
|
40
|
+
/** The limits of `limits.policy` a measure exceeds, in the order the measure lists them. */
|
|
41
|
+
export declare function exceededLimits<Measure extends PolicyMeasure | CheckMeasure>(measure: Measure): readonly (keyof Measure & PolicyLimit)[];
|
|
17
42
|
/**
|
|
18
43
|
* Measure named Policies by their expanded branches, depth and referenced Domains. A Policy has no
|
|
19
44
|
* measure when it is not normalized, when its expansion reaches itself, or when a composition names
|
|
20
45
|
* a Policy `isLocal` refuses or one without a measure.
|
|
21
46
|
*/
|
|
22
|
-
export declare function policyMeasurer(origin: string, normalized: ReadonlyMap<string, PolicyExpression>, isLocal: (ref: PolicyRef) => boolean): (name: string) =>
|
|
23
|
-
|
|
24
|
-
readonly depth: number;
|
|
25
|
-
readonly branches: number;
|
|
26
|
-
readonly leaves: number;
|
|
27
|
-
}
|
|
47
|
+
export declare function policyMeasurer(origin: string, normalized: ReadonlyMap<string, PolicyExpression>, isLocal: (ref: PolicyRef) => boolean): (name: string) => PolicyMeasure | undefined;
|
|
48
|
+
/** Measure one normalized check tree. */
|
|
28
49
|
export declare function measureChecks(expression: PolicyCheckExpression): CheckMeasure;
|
|
@@ -1,13 +1,15 @@
|
|
|
1
|
+
import { limits } from '../../limits.js';
|
|
1
2
|
import { policyObjectOrigin, policyVariableClass } from '../model/policy.js';
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
export const
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
export
|
|
10
|
-
|
|
3
|
+
/**
|
|
4
|
+
* Where a count of Policy branches stops: one past its limit. Each branch count is exact up to its
|
|
5
|
+
* limit plus one and reads as that once it is over, so expansion stays bounded.
|
|
6
|
+
*/
|
|
7
|
+
export const BRANCH_OVERFLOW = limits.policy.normalizedBranches + 1;
|
|
8
|
+
const CHECK_BRANCH_OVERFLOW = limits.policy.checkBranches + 1;
|
|
9
|
+
/** The limits of `limits.policy` a measure exceeds, in the order the measure lists them. */
|
|
10
|
+
export function exceededLimits(measure) {
|
|
11
|
+
return Object.keys(measure).filter((limit) => measure[limit] > limits.policy[limit]);
|
|
12
|
+
}
|
|
11
13
|
/**
|
|
12
14
|
* Measure named Policies by their expanded branches, depth and referenced Domains. A Policy has no
|
|
13
15
|
* measure when it is not normalized, when its expansion reaches itself, or when a composition names
|
|
@@ -48,7 +50,17 @@ export function policyMeasurer(origin, normalized, isLocal) {
|
|
|
48
50
|
return undefined;
|
|
49
51
|
return combineMeasures('allOf' in declaration ? 'allOf' : 'anyOf', children);
|
|
50
52
|
};
|
|
51
|
-
return (name) =>
|
|
53
|
+
return (name) => {
|
|
54
|
+
const measure = measurePolicy(name, new Set());
|
|
55
|
+
return measure === undefined
|
|
56
|
+
? undefined
|
|
57
|
+
: {
|
|
58
|
+
patternDepth: measure.depth,
|
|
59
|
+
normalizedBranches: measure.edgeCounts.length,
|
|
60
|
+
edgePredicates: Math.max(0, ...measure.edgeCounts),
|
|
61
|
+
referencedDomains: measure.domains.size,
|
|
62
|
+
};
|
|
63
|
+
};
|
|
52
64
|
}
|
|
53
65
|
function measurePattern(pattern, origin) {
|
|
54
66
|
if ('sameNode' in pattern)
|
|
@@ -111,19 +123,17 @@ function combineMeasures(kind, children) {
|
|
|
111
123
|
}
|
|
112
124
|
return { depth, edgeCounts, domains };
|
|
113
125
|
}
|
|
126
|
+
/** Measure one normalized check tree. */
|
|
114
127
|
export function measureChecks(expression) {
|
|
115
128
|
if ('check' in expression || 'sameNode' in expression)
|
|
116
|
-
return {
|
|
129
|
+
return { checkDepth: 1, checkBranches: 1, checkLeaves: 1 };
|
|
117
130
|
const children = ('allOf' in expression ? expression.allOf : expression.anyOf).map(measureChecks);
|
|
118
|
-
const
|
|
119
|
-
? children.reduce((value, child) =>
|
|
120
|
-
: children.reduce((value, child) => Math.min(
|
|
131
|
+
const checkBranches = 'allOf' in expression
|
|
132
|
+
? children.reduce((value, child) => Math.min(CHECK_BRANCH_OVERFLOW, value * child.checkBranches), 1)
|
|
133
|
+
: children.reduce((value, child) => Math.min(CHECK_BRANCH_OVERFLOW, value + child.checkBranches), 0);
|
|
121
134
|
return {
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
135
|
+
checkDepth: 1 + Math.max(0, ...children.map((child) => child.checkDepth)),
|
|
136
|
+
checkBranches,
|
|
137
|
+
checkLeaves: children.reduce((value, child) => value + child.checkLeaves, 0),
|
|
125
138
|
};
|
|
126
139
|
}
|
|
127
|
-
function saturatingProduct(left, right) {
|
|
128
|
-
return Math.min(BRANCH_OVERFLOW, left * right);
|
|
129
|
-
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
+
import { limits } from '../../limits.js';
|
|
1
2
|
import { policyObjectKey } from '../model/policy.js';
|
|
2
|
-
import {
|
|
3
|
+
import { measureChecks } from './budgets.js';
|
|
3
4
|
/** Names a callable check object. Every leaf over one object sees the same Node at runtime. */
|
|
4
5
|
function checkObjectName(object) {
|
|
5
6
|
if (object.kind === 'self')
|
|
@@ -36,7 +37,7 @@ export function admittedCheckClasses(typing) {
|
|
|
36
37
|
* check budget is already refused, so it is not expanded.
|
|
37
38
|
*/
|
|
38
39
|
export function neverAdmittedClasses(expression, leaves) {
|
|
39
|
-
if (measureChecks(expression).
|
|
40
|
+
if (measureChecks(expression).checkBranches > limits.policy.checkBranches)
|
|
40
41
|
return [];
|
|
41
42
|
const accepted = new Map();
|
|
42
43
|
for (const [leaf, classes] of leaves)
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Key } from '../../../addressing/index.js';
|
|
2
2
|
import { definitionRefIndex } from '../../../addressing/ordering.js';
|
|
3
|
+
import { limits } from '../../limits.js';
|
|
3
4
|
import { branchesOf, branchProduct, capBranches, targetMode, termKey, UnionFind, } from './branches.js';
|
|
4
|
-
import { MAX_REPEAT } from './budgets.js';
|
|
5
5
|
import { admittedCheckClasses } from './checks.js';
|
|
6
6
|
export class PolicyTyping {
|
|
7
7
|
scope;
|
|
@@ -12,7 +12,8 @@ export class PolicyTyping {
|
|
|
12
12
|
// independent of the reaching path up to the opaque term-namespace labels, which only ever
|
|
13
13
|
// disambiguate variables within one branch and leave targetMode and branch typing unchanged.
|
|
14
14
|
// Two occurrences of one Policy in a branch share labels only when a Policy reaches it by two
|
|
15
|
-
// paths; for that to merge distinct variables, the Policy expands deeper than
|
|
15
|
+
// paths; for that to merge distinct variables, the Policy expands deeper than
|
|
16
|
+
// `limits.policy.patternDepth`.
|
|
16
17
|
#expansions = new Map();
|
|
17
18
|
constructor(scope) {
|
|
18
19
|
this.scope = scope;
|
|
@@ -269,7 +270,7 @@ function variantsForEdge(use, typing) {
|
|
|
269
270
|
!Number.isInteger(bounds.max) ||
|
|
270
271
|
bounds.min < 0 ||
|
|
271
272
|
bounds.max < bounds.min ||
|
|
272
|
-
bounds.max >
|
|
273
|
+
bounds.max > limits.policy.repetition) {
|
|
273
274
|
return [];
|
|
274
275
|
}
|
|
275
276
|
const sourceKey = termKey(edge.source, use.namespace);
|
|
@@ -65,20 +65,20 @@ export interface PolicyEdgePattern {
|
|
|
65
65
|
}
|
|
66
66
|
export type PolicyPatternExpression = PolicyEdgePattern | PolicySameNode<PolicyPatternTerm> | {
|
|
67
67
|
readonly exists: {
|
|
68
|
-
readonly nodes: readonly PolicyVariable[];
|
|
68
|
+
readonly nodes: readonly [PolicyVariable, ...PolicyVariable[]];
|
|
69
69
|
readonly where: PolicyPatternExpression;
|
|
70
70
|
};
|
|
71
71
|
} | {
|
|
72
|
-
readonly allOf: readonly PolicyPatternExpression[];
|
|
72
|
+
readonly allOf: readonly [PolicyPatternExpression, ...PolicyPatternExpression[]];
|
|
73
73
|
} | {
|
|
74
|
-
readonly anyOf: readonly PolicyPatternExpression[];
|
|
74
|
+
readonly anyOf: readonly [PolicyPatternExpression, ...PolicyPatternExpression[]];
|
|
75
75
|
};
|
|
76
76
|
export type PolicyExpression = {
|
|
77
77
|
readonly match: PolicyPatternExpression;
|
|
78
78
|
} | {
|
|
79
|
-
readonly allOf: readonly PolicyRef[];
|
|
79
|
+
readonly allOf: readonly [PolicyRef, ...PolicyRef[]];
|
|
80
80
|
} | {
|
|
81
|
-
readonly anyOf: readonly PolicyRef[];
|
|
81
|
+
readonly anyOf: readonly [PolicyRef, ...PolicyRef[]];
|
|
82
82
|
};
|
|
83
83
|
export interface PolicyDefinition extends DefinitionBase<PolicyRef> {
|
|
84
84
|
readonly expression: PolicyExpression;
|
|
@@ -6,9 +6,9 @@ export function normalizePolicyExpression(value) {
|
|
|
6
6
|
return { match: normalizePolicyPattern(value.match) };
|
|
7
7
|
}
|
|
8
8
|
if ('allOf' in value) {
|
|
9
|
-
return { allOf: uniqueRefs(value.allOf) };
|
|
9
|
+
return { allOf: operands(uniqueRefs(value.allOf)) };
|
|
10
10
|
}
|
|
11
|
-
return { anyOf: uniqueRefs(value.anyOf) };
|
|
11
|
+
return { anyOf: operands(uniqueRefs(value.anyOf)) };
|
|
12
12
|
}
|
|
13
13
|
/**
|
|
14
14
|
* Passes after the first, while one still changes the Pattern. A pass numbers variables by the
|
|
@@ -66,8 +66,7 @@ function unwrapSingleOperands(value) {
|
|
|
66
66
|
}));
|
|
67
67
|
if (children.length === 1)
|
|
68
68
|
return children[0];
|
|
69
|
-
|
|
70
|
-
return all ? { allOf: operands } : { anyOf: operands };
|
|
69
|
+
return all ? { allOf: operands(children) } : { anyOf: operands(children) };
|
|
71
70
|
}
|
|
72
71
|
export function normalizePolicyChecks(value) {
|
|
73
72
|
if ('check' in value)
|
|
@@ -79,17 +78,19 @@ export function normalizePolicyChecks(value) {
|
|
|
79
78
|
const normalized = normalizePolicyChecks(child);
|
|
80
79
|
return 'allOf' in normalized ? normalized.allOf : [normalized];
|
|
81
80
|
});
|
|
82
|
-
return { allOf:
|
|
81
|
+
return { allOf: operands(uniqueJson(children)) };
|
|
83
82
|
}
|
|
84
83
|
const children = value.anyOf.flatMap((child) => {
|
|
85
84
|
const normalized = normalizePolicyChecks(child);
|
|
86
85
|
return 'anyOf' in normalized ? normalized.anyOf : [normalized];
|
|
87
86
|
});
|
|
88
|
-
return { anyOf:
|
|
87
|
+
return { anyOf: operands(uniqueJson(children)) };
|
|
89
88
|
}
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
89
|
+
/**
|
|
90
|
+
* The operands of a non-empty list, mapped, flattened, deduplicated or sorted one by one: still at
|
|
91
|
+
* least one. The structure and compiled admission refuse an empty list.
|
|
92
|
+
*/
|
|
93
|
+
function operands(values) {
|
|
93
94
|
return values;
|
|
94
95
|
}
|
|
95
96
|
function normalizePatternNode(value) {
|
|
@@ -106,7 +107,7 @@ function normalizePatternNode(value) {
|
|
|
106
107
|
if ('exists' in value) {
|
|
107
108
|
return {
|
|
108
109
|
exists: {
|
|
109
|
-
nodes: [...value.exists.nodes].sort((left, right) => left.variable.id - right.variable.id),
|
|
110
|
+
nodes: operands([...value.exists.nodes].sort((left, right) => left.variable.id - right.variable.id)),
|
|
110
111
|
where: normalizePatternNode(value.exists.where),
|
|
111
112
|
},
|
|
112
113
|
};
|
|
@@ -116,13 +117,13 @@ function normalizePatternNode(value) {
|
|
|
116
117
|
const normalized = normalizePatternNode(child);
|
|
117
118
|
return 'allOf' in normalized ? normalized.allOf : [normalized];
|
|
118
119
|
});
|
|
119
|
-
return { allOf: uniqueJson(children) };
|
|
120
|
+
return { allOf: operands(uniqueJson(children)) };
|
|
120
121
|
}
|
|
121
122
|
const children = value.anyOf.flatMap((child) => {
|
|
122
123
|
const normalized = normalizePatternNode(child);
|
|
123
124
|
return 'anyOf' in normalized ? normalized.anyOf : [normalized];
|
|
124
125
|
});
|
|
125
|
-
return { anyOf: uniqueJson(children) };
|
|
126
|
+
return { anyOf: operands(uniqueJson(children)) };
|
|
126
127
|
}
|
|
127
128
|
function canonicalVariableIds(value) {
|
|
128
129
|
const declarations = new Map();
|
|
@@ -297,7 +298,7 @@ function rewriteVariables(value, ids) {
|
|
|
297
298
|
if ('exists' in value) {
|
|
298
299
|
return {
|
|
299
300
|
exists: {
|
|
300
|
-
nodes: value.exists.nodes.map((node) => {
|
|
301
|
+
nodes: operands(value.exists.nodes.map((node) => {
|
|
301
302
|
const variable = {
|
|
302
303
|
kind: 'variable',
|
|
303
304
|
id: ids.get(node.variable.id) ?? node.variable.id,
|
|
@@ -305,14 +306,14 @@ function rewriteVariables(value, ids) {
|
|
|
305
306
|
return 'class' in node
|
|
306
307
|
? { variable, class: node.class }
|
|
307
308
|
: { variable, selector: node.selector };
|
|
308
|
-
}),
|
|
309
|
+
})),
|
|
309
310
|
where: rewriteVariables(value.exists.where, ids),
|
|
310
311
|
},
|
|
311
312
|
};
|
|
312
313
|
}
|
|
313
314
|
if ('allOf' in value)
|
|
314
|
-
return { allOf: value.allOf.map((child) => rewriteVariables(child, ids)) };
|
|
315
|
-
return { anyOf: value.anyOf.map((child) => rewriteVariables(child, ids)) };
|
|
315
|
+
return { allOf: operands(value.allOf.map((child) => rewriteVariables(child, ids))) };
|
|
316
|
+
return { anyOf: operands(value.anyOf.map((child) => rewriteVariables(child, ids))) };
|
|
316
317
|
}
|
|
317
318
|
function selectorSignature(node) {
|
|
318
319
|
const selector = policyVariableSelector(node);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export type DiagnosticCode = 'CALLABLE_INPUT_FIELD_UNKNOWN' | 'DOMAIN_CLASS_ABSTRACT' | 'DOMAIN_PROPERTY_INPUT_INVALID' | 'DOMAIN_PROPERTY_REQUIRED' | 'DOMAIN_PROPERTY_UNKNOWN' | '
|
|
1
|
+
export type DiagnosticCode = 'CALLABLE_INPUT_FIELD_UNKNOWN' | 'DOMAIN_CLASS_ABSTRACT' | 'DOMAIN_PROPERTY_INPUT_INVALID' | 'DOMAIN_PROPERTY_REQUIRED' | 'DOMAIN_PROPERTY_UNKNOWN' | 'VALUE_SCHEMA_INSTANCE_INVALID' | 'VS_DIALECT' | 'VS_DYNAMIC_FORBIDDEN' | 'VS_KEYWORD_CLOSED' | 'VS_META_SCHEMA' | 'VS_NUMBER_DOMAIN' | 'VS_PATH_EXTENSION' | 'VS_RECURSION_PRODUCTIVE' | 'VS_REFERENCE_FORM' | 'VS_REFERENCE_RESOLUTION' | 'VS_REGEX_PORTABLE' | 'VS_RESOURCE_ID' | 'VS_VALUE_KIND' | 'VS_VALUE_KIND_JSON';
|
|
2
2
|
export interface Diagnostic<out Code extends DiagnosticCode = DiagnosticCode> {
|
|
3
3
|
readonly code: Code;
|
|
4
4
|
readonly pointer: string;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { LimitKey } from '../limits.js';
|
|
2
|
+
import type { DiagnosticCode } from './diagnostic.js';
|
|
3
|
+
/**
|
|
4
|
+
* What a rule reads to decide. `schema`: a value schema alone, with its place in the declaration
|
|
5
|
+
* that holds it (a Property, a callable input or output). `context`: a value schema and what its
|
|
6
|
+
* Domain can reach, the Node Classes of the Domain and of its direct dependencies and the exact
|
|
7
|
+
* dependency closure. `value`: a value, under an accepted schema or a contract of the Domain.
|
|
8
|
+
* `warning`: a value schema, which the rule reports without refusing it.
|
|
9
|
+
*/
|
|
10
|
+
export type ValueRuleStage = 'schema' | 'context' | 'value' | 'warning';
|
|
11
|
+
/** The rule one value diagnostic code reports. */
|
|
12
|
+
export interface ValueRule {
|
|
13
|
+
readonly stage: ValueRuleStage;
|
|
14
|
+
/**
|
|
15
|
+
* The positions the rule constrains, which its diagnostics point at, relative to the value
|
|
16
|
+
* schema, the value or the Domain input it checks: JSON Pointer templates in which a `{name}`
|
|
17
|
+
* token stands for one token of the kind it names and `{/path*}` for any number of tokens.
|
|
18
|
+
*/
|
|
19
|
+
readonly positions: readonly [string, ...string[]];
|
|
20
|
+
/** The rule, in one plain sentence. */
|
|
21
|
+
readonly statement: string;
|
|
22
|
+
/** The limits of `schema.limits` the rule enforces, by key, where it enforces some. */
|
|
23
|
+
readonly limits?: readonly [LimitKey, ...LimitKey[]];
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Every value diagnostic code with the rule it reports. A code added to the language does not
|
|
27
|
+
* compile until it is described here.
|
|
28
|
+
*/
|
|
29
|
+
export declare const VALUE_RULES: Readonly<Record<DiagnosticCode, ValueRule>>;
|