@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.
Files changed (103) hide show
  1. package/dist/v1/addressing/domain-dependency.d.ts +0 -6
  2. package/dist/v1/addressing/domain-dependency.js +1 -6
  3. package/dist/v1/addressing/key.js +2 -1
  4. package/dist/v1/addressing/patterns.d.ts +1 -0
  5. package/dist/v1/addressing/patterns.js +2 -1
  6. package/dist/v1/builder/class/authoring.d.ts +43 -0
  7. package/dist/v1/builder/class/property.d.ts +3 -9
  8. package/dist/v1/builder/class/property.js +1 -0
  9. package/dist/v1/builder/index.d.ts +1 -1
  10. package/dist/v1/builder/lowering/class.js +5 -1
  11. package/dist/v1/builder/lowering/policy.js +2 -0
  12. package/dist/v1/builder/lowering/schema/context.d.ts +5 -0
  13. package/dist/v1/builder/lowering/schema/context.js +22 -0
  14. package/dist/v1/builder/policy/authoring.d.ts +84 -4
  15. package/dist/v1/builder/policy/authoring.js +44 -14
  16. package/dist/v1/builder/schema/projection/class.d.ts +116 -14
  17. package/dist/v1/bundle/codec.js +9 -9
  18. package/dist/v1/bundle/diagnostic.d.ts +2 -0
  19. package/dist/v1/bundle/diagnostic.js +6 -0
  20. package/dist/v1/bundle/structure.js +2 -2
  21. package/dist/v1/compiled/admission/accept.js +3 -3
  22. package/dist/v1/compiled/admission/closure.js +2 -2
  23. package/dist/v1/compiled/admission/semantics/core.js +3 -7
  24. package/dist/v1/compiled/admission/semantics/policy/parity.js +3 -10
  25. package/dist/v1/domain/validation/create.d.ts +4 -1
  26. package/dist/v1/domain/validation/create.js +21 -34
  27. package/dist/v1/language/.spec/schemas/value-schema.schema.json +22 -0
  28. package/dist/v1/language/class/model/class.d.ts +4 -4
  29. package/dist/v1/language/class/model/edge.d.ts +2 -2
  30. package/dist/v1/language/class/model/property.d.ts +16 -7
  31. package/dist/v1/language/core/facts.d.ts +17 -4
  32. package/dist/v1/language/core/facts.js +26 -5
  33. package/dist/v1/language/definition/metadata.d.ts +5 -0
  34. package/dist/v1/language/definition/metadata.js +7 -0
  35. package/dist/v1/language/limits.d.ts +64 -0
  36. package/dist/v1/language/limits.js +60 -0
  37. package/dist/v1/language/policy/analysis/budgets.d.ts +39 -18
  38. package/dist/v1/language/policy/analysis/budgets.js +30 -20
  39. package/dist/v1/language/policy/analysis/checks.js +3 -2
  40. package/dist/v1/language/policy/analysis/typing.js +4 -3
  41. package/dist/v1/language/policy/model/policy.d.ts +5 -5
  42. package/dist/v1/language/policy/normalization/normalize.js +17 -16
  43. package/dist/v1/language/validation/diagnostic.d.ts +1 -1
  44. package/dist/v1/language/validation/rule.d.ts +29 -0
  45. package/dist/v1/language/validation/rule.js +120 -0
  46. package/dist/v1/language/value/profile/meta-schema.compiled.js +4 -1
  47. package/dist/v1/language/value/profile/rules.js +13 -37
  48. package/dist/v1/language/value/profile/vocabulary.d.ts +35 -2
  49. package/dist/v1/language/value/profile/vocabulary.js +52 -5
  50. package/dist/v1/language/value/reference/registry.index.js +2 -1
  51. package/dist/v1/language/value/regexp/ast.js +6 -7
  52. package/dist/v1/language/value/regexp/compile.d.ts +5 -11
  53. package/dist/v1/language/value/regexp/compile.js +14 -12
  54. package/dist/v1/language/value/regexp/syntax.js +3 -2
  55. package/dist/v1/language/value/syntax/keywords.d.ts +3 -3
  56. package/dist/v1/language/value/syntax/keywords.js +4 -20
  57. package/dist/v1/language/value/syntax/node-path/validate.js +2 -3
  58. package/dist/v1/schema/.spec/schemas/domain-schema.schema.json +2336 -0
  59. package/dist/v1/schema/admission/accept.js +3 -14
  60. package/dist/v1/schema/admission/callable.d.ts +21 -0
  61. package/dist/v1/schema/admission/callable.js +96 -0
  62. package/dist/v1/schema/analysis/class.js +33 -15
  63. package/dist/v1/schema/compatibility/api.d.ts +1 -1
  64. package/dist/v1/schema/compatibility/coordinate.js +3 -3
  65. package/dist/v1/schema/compatibility/model.d.ts +5 -0
  66. package/dist/v1/schema/diagnostic.d.ts +17 -3
  67. package/dist/v1/schema/diagnostic.js +11 -0
  68. package/dist/v1/schema/index.d.ts +10 -1
  69. package/dist/v1/schema/index.js +4 -0
  70. package/dist/v1/schema/model/schema.d.ts +19 -1
  71. package/dist/v1/schema/normalization/domain.d.ts +2 -0
  72. package/dist/v1/schema/normalization/domain.js +2 -1
  73. package/dist/v1/schema/resolution/closure.js +2 -1
  74. package/dist/v1/schema/resolution/context.js +10 -8
  75. package/dist/v1/schema/resolution/domain.d.ts +5 -0
  76. package/dist/v1/schema/resolution/domain.js +1 -1
  77. package/dist/v1/schema/rule.d.ts +32 -0
  78. package/dist/v1/schema/rule.js +428 -0
  79. package/dist/v1/schema/validation/callable.d.ts +35 -2
  80. package/dist/v1/schema/validation/callable.js +51 -8
  81. package/dist/v1/schema/validation/class.js +36 -44
  82. package/dist/v1/schema/validation/core.js +19 -13
  83. package/dist/v1/schema/validation/domain.js +2 -2
  84. package/dist/v1/schema/validation/domain.json-schema.d.ts +15 -0
  85. package/dist/v1/schema/validation/domain.json-schema.js +183 -0
  86. package/dist/v1/schema/validation/domain.route.d.ts +6 -1
  87. package/dist/v1/schema/validation/domain.route.js +18 -0
  88. package/dist/v1/schema/validation/domain.structure.d.ts +4020 -0
  89. package/dist/v1/schema/validation/domain.structure.js +359 -153
  90. package/dist/v1/schema/validation/json.json-schema.d.ts +24 -0
  91. package/dist/v1/schema/validation/json.json-schema.js +36 -0
  92. package/dist/v1/schema/validation/policy/budgets.d.ts +33 -2
  93. package/dist/v1/schema/validation/policy/budgets.js +89 -16
  94. package/dist/v1/schema/validation/policy/references.js +6 -21
  95. package/dist/v1/schema/validation/policy/uses.d.ts +29 -0
  96. package/dist/v1/schema/validation/policy/uses.js +28 -51
  97. package/dist/v1/schema/validation/value-api.d.ts +12 -1
  98. package/dist/v1/schema/validation/value-api.js +10 -1
  99. package/dist/v1/schema/validation/value.d.ts +3 -0
  100. package/dist/v1/schema/validation/value.js +6 -12
  101. package/dist/v1/schema/validation/value.json-schema.d.ts +51 -0
  102. package/dist/v1/schema/validation/value.json-schema.js +126 -0
  103. 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
- /** Check normalized Edge identity, noSelf, keys, exact-Class acyclicity and Core degrees. */
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 ordinary Edges of the same
83
- * Class against it, so a minimum is unprovable here and only the maximum is enforced.
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
- export declare const MAX_PATTERN_DEPTH = 4;
4
- export declare const MAX_BRANCHES = 8;
5
- export declare const MAX_VARIABLES = 4;
6
- export declare const MAX_EDGES = 6;
7
- export declare const MAX_REPEAT = 3;
8
- export declare const MAX_DOMAINS = 4;
9
- export declare const MAX_CHECK_DEPTH = 4;
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
- export interface PatternMeasure {
13
- readonly depth: number;
14
- readonly edgeCounts: readonly number[];
15
- readonly domains: ReadonlySet<string>;
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) => PatternMeasure | undefined;
23
- export interface CheckMeasure {
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
- export const MAX_PATTERN_DEPTH = 4;
3
- export const MAX_BRANCHES = 8;
4
- export const MAX_VARIABLES = 4;
5
- export const MAX_EDGES = 6;
6
- export const MAX_REPEAT = 3;
7
- export const MAX_DOMAINS = 4;
8
- export const MAX_CHECK_DEPTH = 4;
9
- export const MAX_CHECK_LEAVES = 8;
10
- export const BRANCH_OVERFLOW = MAX_BRANCHES + 1;
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) => measurePolicy(name, new Set());
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 { depth: 1, branches: 1, leaves: 1 };
129
+ return { checkDepth: 1, checkBranches: 1, checkLeaves: 1 };
117
130
  const children = ('allOf' in expression ? expression.allOf : expression.anyOf).map(measureChecks);
118
- const branches = 'allOf' in expression
119
- ? children.reduce((value, child) => saturatingProduct(value, child.branches), 1)
120
- : children.reduce((value, child) => Math.min(BRANCH_OVERFLOW, value + child.branches), 0);
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
- depth: 1 + Math.max(0, ...children.map((child) => child.depth)),
123
- branches,
124
- leaves: children.reduce((value, child) => value + child.leaves, 0),
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 { MAX_BRANCHES, measureChecks } from './budgets.js';
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).branches > MAX_BRANCHES)
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 MAX_PATTERN_DEPTH.
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 > MAX_REPEAT) {
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
- const operands = policyCheckComposition(children);
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: policyCheckComposition(uniqueJson(children)) };
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: policyCheckComposition(uniqueJson(children)) };
87
+ return { anyOf: operands(uniqueJson(children)) };
89
88
  }
90
- function policyCheckComposition(values) {
91
- // Structural routing may carry an invalid empty array into semantic validation;
92
- // validateChecks reports it before the DomainSchema can be accepted.
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' | 'DOMAIN_VALIDATION_TARGET_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';
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>>;