@astrale-os/kernel-dsl 1.0.0-beta.11 → 1.0.0-beta.13

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 (167) hide show
  1. package/dist/v1/addressing/definition-ref.js +21 -37
  2. package/dist/v1/addressing/ordering.d.ts +2 -0
  3. package/dist/v1/addressing/ordering.js +4 -0
  4. package/dist/v1/addressing/patterns.d.ts +0 -1
  5. package/dist/v1/addressing/patterns.js +0 -5
  6. package/dist/v1/builder/callable/authoring.d.ts +1 -1
  7. package/dist/v1/builder/class/authoring.d.ts +6 -256
  8. package/dist/v1/builder/class/authoring.js +5 -28
  9. package/dist/v1/builder/class/handle.d.ts +27 -0
  10. package/dist/v1/builder/class/handle.js +1 -0
  11. package/dist/v1/builder/class/input.d.ts +129 -0
  12. package/dist/v1/builder/class/projection.d.ts +129 -0
  13. package/dist/v1/builder/class/projection.js +1 -0
  14. package/dist/v1/builder/core/authoring.d.ts +1 -1
  15. package/dist/v1/builder/index.d.ts +3 -1
  16. package/dist/v1/builder/lowering/class.d.ts +1 -1
  17. package/dist/v1/builder/lowering/schema/context.d.ts +2 -1
  18. package/dist/v1/builder/lowering/schema/context.js +2 -4
  19. package/dist/v1/builder/lowering/value/path.d.ts +1 -1
  20. package/dist/v1/builder/lowering/value/path.js +2 -4
  21. package/dist/v1/builder/lowering/value/zod.js +1 -3
  22. package/dist/v1/builder/policy/authoring.d.ts +1 -1
  23. package/dist/v1/builder/schema/define.d.ts +0 -1
  24. package/dist/v1/builder/schema/diagnostic.d.ts +2 -1
  25. package/dist/v1/builder/schema/diagnostic.js +5 -10
  26. package/dist/v1/builder/schema/input.d.ts +1 -1
  27. package/dist/v1/builder/schema/projection/class.d.ts +2 -1
  28. package/dist/v1/builder/schema/projection/core-input.d.ts +2 -1
  29. package/dist/v1/builder/schema/projection/identity.d.ts +2 -1
  30. package/dist/v1/builder/schema/projection/property.d.ts +1 -1
  31. package/dist/v1/builder/schema/projection.d.ts +1 -1
  32. package/dist/v1/builder/value/full-match.d.ts +0 -1
  33. package/dist/v1/builder/value/full-match.js +0 -1
  34. package/dist/v1/builder/value/path-metadata.d.ts +1 -1
  35. package/dist/v1/builder/value/path.d.ts +1 -1
  36. package/dist/v1/bundle/codec.js +2 -4
  37. package/dist/v1/bundle/diagnostic.js +3 -7
  38. package/dist/v1/bundle/structure.js +2 -4
  39. package/dist/v1/compiled/admission/diagnostic.d.ts +1 -1
  40. package/dist/v1/compiled/admission/diagnostic.js +1 -3
  41. package/dist/v1/compiled/admission/revision.d.ts +9 -1
  42. package/dist/v1/compiled/admission/revision.js +6 -3
  43. package/dist/v1/compiled/admission/semantics/class.d.ts +4 -1
  44. package/dist/v1/compiled/admission/semantics/class.js +4 -8
  45. package/dist/v1/compiled/admission/semantics/core.d.ts +1 -24
  46. package/dist/v1/compiled/admission/semantics/core.js +54 -34
  47. package/dist/v1/compiled/admission/semantics/edge.d.ts +2 -4
  48. package/dist/v1/compiled/admission/semantics/edge.js +6 -65
  49. package/dist/v1/compiled/admission/semantics/policy/callable.js +0 -2
  50. package/dist/v1/compiled/admission/semantics/policy/contracts.d.ts +5 -20
  51. package/dist/v1/compiled/admission/semantics/policy/contracts.js +8 -21
  52. package/dist/v1/compiled/admission/semantics/policy/parity.js +13 -22
  53. package/dist/v1/compiled/admission/semantics/policy/pattern.d.ts +4 -1
  54. package/dist/v1/compiled/admission/semantics/policy/pattern.js +4 -16
  55. package/dist/v1/compiled/admission/semantics/policy/typing.d.ts +2 -24
  56. package/dist/v1/compiled/admission/semantics/policy/typing.js +19 -201
  57. package/dist/v1/compiled/compiler/compile.js +6 -8
  58. package/dist/v1/compiled/diagnostic.js +2 -3
  59. package/dist/v1/compiled/loader/load.d.ts +3 -2
  60. package/dist/v1/compiled/loader/load.js +10 -44
  61. package/dist/v1/domain/create.js +3 -5
  62. package/dist/v1/domain/namespace/member-collection.js +4 -6
  63. package/dist/v1/domain/validation/create.d.ts +0 -1
  64. package/dist/v1/domain/validation/create.js +0 -35
  65. package/dist/v1/language/class/inheritance.d.ts +18 -0
  66. package/dist/v1/language/class/inheritance.js +73 -0
  67. package/dist/v1/language/class/property-values.js +2 -4
  68. package/dist/v1/language/core/facts.d.ts +72 -0
  69. package/dist/v1/language/core/facts.js +95 -0
  70. package/dist/v1/language/index.d.ts +1 -0
  71. package/dist/v1/language/index.js +1 -0
  72. package/dist/v1/language/policy/analysis/branches.d.ts +8 -0
  73. package/dist/v1/language/policy/analysis/branches.js +2 -1
  74. package/dist/v1/language/policy/analysis/budgets.d.ts +13 -1
  75. package/dist/v1/language/policy/analysis/budgets.js +104 -0
  76. package/dist/v1/language/policy/analysis/typing.d.ts +48 -0
  77. package/dist/v1/language/policy/analysis/typing.js +338 -0
  78. package/dist/v1/language/validation/diagnostic.d.ts +15 -1
  79. package/dist/v1/language/validation/diagnostic.js +21 -0
  80. package/dist/v1/language/value/compile/fields.js +4 -8
  81. package/dist/v1/language/value/inclusion/arrays.d.ts +3 -0
  82. package/dist/v1/language/value/inclusion/arrays.js +85 -0
  83. package/dist/v1/language/value/inclusion/assertions.d.ts +18 -0
  84. package/dist/v1/language/value/inclusion/assertions.js +29 -0
  85. package/dist/v1/language/value/inclusion/combinators.d.ts +3 -0
  86. package/dist/v1/language/value/inclusion/combinators.js +67 -0
  87. package/dist/v1/language/value/inclusion/equivalence.d.ts +4 -0
  88. package/dist/v1/language/value/inclusion/equivalence.js +74 -0
  89. package/dist/v1/language/value/inclusion/evidence.d.ts +34 -0
  90. package/dist/v1/language/value/inclusion/evidence.js +138 -0
  91. package/dist/v1/language/value/inclusion/includes.d.ts +17 -0
  92. package/dist/v1/language/value/inclusion/includes.js +25 -719
  93. package/dist/v1/language/value/inclusion/keywords.d.ts +5 -0
  94. package/dist/v1/language/value/inclusion/keywords.js +31 -0
  95. package/dist/v1/language/value/inclusion/kinds.d.ts +17 -0
  96. package/dist/v1/language/value/inclusion/kinds.js +57 -0
  97. package/dist/v1/language/value/inclusion/location.d.ts +24 -0
  98. package/dist/v1/language/value/inclusion/location.js +63 -0
  99. package/dist/v1/language/value/inclusion/numbers.d.ts +3 -0
  100. package/dist/v1/language/value/inclusion/numbers.js +41 -0
  101. package/dist/v1/language/value/inclusion/objects.d.ts +3 -0
  102. package/dist/v1/language/value/inclusion/objects.js +190 -0
  103. package/dist/v1/language/value/inclusion/strings.d.ts +3 -0
  104. package/dist/v1/language/value/inclusion/strings.js +29 -0
  105. package/dist/v1/language/value/inclusion/types.d.ts +3 -0
  106. package/dist/v1/language/value/inclusion/types.js +11 -0
  107. package/dist/v1/language/value/ordering.d.ts +9 -0
  108. package/dist/v1/language/value/ordering.js +72 -9
  109. package/dist/v1/language/value/profile/rules.js +5 -7
  110. package/dist/v1/language/value/profile/validate.js +1 -10
  111. package/dist/v1/language/value/reference/recursion.js +2 -22
  112. package/dist/v1/language/value/syntax/pointer.d.ts +2 -0
  113. package/dist/v1/language/value/syntax/pointer.js +5 -2
  114. package/dist/v1/language/value/syntax/walk.d.ts +2 -0
  115. package/dist/v1/language/value/syntax/walk.js +19 -20
  116. package/dist/v1/schema/compatibility/diff.js +2 -4
  117. package/dist/v1/schema/compatibility/meaning/footprint.js +16 -50
  118. package/dist/v1/schema/compatibility/structure/requirements.js +26 -58
  119. package/dist/v1/schema/compatibility/walk.d.ts +17 -0
  120. package/dist/v1/schema/compatibility/walk.js +41 -0
  121. package/dist/v1/schema/diagnostic.js +3 -11
  122. package/dist/v1/schema/resolution/context.js +3 -5
  123. package/dist/v1/schema/validation/callable.js +3 -5
  124. package/dist/v1/schema/validation/class.js +10 -52
  125. package/dist/v1/schema/validation/core.js +65 -199
  126. package/dist/v1/schema/validation/domain.js +1 -10
  127. package/dist/v1/schema/validation/domain.structure.js +2 -4
  128. package/dist/v1/schema/validation/policy/budgets.js +5 -112
  129. package/dist/v1/schema/validation/policy/context.d.ts +5 -3
  130. package/dist/v1/schema/validation/policy/context.js +5 -3
  131. package/dist/v1/schema/validation/policy/objects.d.ts +1 -1
  132. package/dist/v1/schema/validation/policy/objects.js +6 -14
  133. package/dist/v1/schema/validation/policy/references.js +5 -6
  134. package/dist/v1/schema/validation/policy/typing.d.ts +3 -18
  135. package/dist/v1/schema/validation/policy/typing.js +22 -356
  136. package/dist/v1/schema/validation/policy/uses.d.ts +1 -1
  137. package/dist/v1/schema/validation/policy/uses.js +9 -22
  138. package/dist/v1/schema/validation/policy/validate.js +3 -2
  139. package/dist/v1/schema/validation/value-api.d.ts +9 -0
  140. package/dist/v1/schema/validation/value-api.js +23 -1
  141. package/dist/v1/schema/validation/value.js +5 -7
  142. package/dist/v1/schema/validation/view.js +4 -8
  143. package/dist/value/canonical.d.ts +3 -2
  144. package/dist/value/canonical.js +30 -6
  145. package/dist/value/decode.d.ts +7 -0
  146. package/dist/value/decode.js +159 -9
  147. package/dist/value/index.d.ts +1 -1
  148. package/dist/value/json.d.ts +1 -1
  149. package/dist/value/json.js +1 -1
  150. package/dist/value/types.d.ts +6 -0
  151. package/dist/value/unicode.d.ts +2 -1
  152. package/dist/value/unicode.js +2 -0
  153. package/package.json +1 -1
  154. package/dist/v1/compiled/admission/semantics/cardinality.d.ts +0 -5
  155. package/dist/v1/compiled/admission/semantics/cardinality.js +0 -128
  156. package/dist/v1/compiled/admission/semantics/policy/equality.d.ts +0 -8
  157. package/dist/v1/compiled/admission/semantics/policy/equality.js +0 -27
  158. package/dist/v1/compiled/admission/semantics/policy/measure.d.ts +0 -9
  159. package/dist/v1/compiled/admission/semantics/policy/measure.js +0 -81
  160. package/dist/v1/compiled/admission/semantics/policy/variants.d.ts +0 -10
  161. package/dist/v1/compiled/admission/semantics/policy/variants.js +0 -65
  162. package/dist/v1/language/class/index.d.ts +0 -8
  163. package/dist/v1/language/class/index.js +0 -1
  164. package/dist/v1/language/core/index.d.ts +0 -2
  165. package/dist/v1/language/value/index.d.ts +0 -11
  166. package/dist/v1/language/value/index.js +0 -8
  167. /package/dist/v1/{language/core/index.js → builder/class/input.js} +0 -0
@@ -1,11 +1,6 @@
1
1
  import { definitionRefIndex } from '../../../addressing/ordering.js';
2
2
  import { isMethodExecutable } from '../../../language/callable/executable.js';
3
- import { policyObjectKey, policyObjectOrigin } from '../../../language/index.js';
4
- const KERNEL_ORIGIN = 'kernel.astrale.ai';
5
- const PROJECTION_CLASS = nodeClassRef('Class');
6
- const PROJECTION_FUNCTION = nodeClassRef('Function');
7
- const PROJECTION_POLICY = nodeClassRef('Policy');
8
- const PROJECTION_VIEW = nodeClassRef('View');
3
+ import { policyObjectKey, policyObjectOrigin, projectedMemberClass, } from '../../../language/index.js';
9
4
  export function referenceContracts(validation, ref, pointer) {
10
5
  const candidate = validation.environment.candidate;
11
6
  const local = policyObjectOrigin(ref) === candidate.origin;
@@ -21,7 +16,7 @@ export function referenceContracts(validation, ref, pointer) {
21
16
  case 'class': {
22
17
  const resolved = validation.environment.resolve(ref);
23
18
  return resolved?.ref.kind === ref.kind
24
- ? [{ kind: 'exact', ref: PROJECTION_CLASS }]
19
+ ? [{ kind: 'exact', ref: projectedMemberClass(ref) }]
25
20
  : invalidPolicyObjectRef(validation, ref, pointer);
26
21
  }
27
22
  case 'method': {
@@ -29,20 +24,20 @@ export function referenceContracts(validation, ref, pointer) {
29
24
  const method = declaringClass?.kind === 'node' ? declaringClass.methods[ref.name] : undefined;
30
25
  return method === undefined || !isMethodExecutable(declaringClass, method)
31
26
  ? invalidPolicyObjectRef(validation, ref, pointer)
32
- : [{ kind: 'exact', ref: PROJECTION_FUNCTION }];
27
+ : [{ kind: 'exact', ref: projectedMemberClass(ref) }];
33
28
  }
34
29
  case 'function':
35
30
  return owner.functions[ref.name] === undefined
36
31
  ? invalidPolicyObjectRef(validation, ref, pointer)
37
- : [{ kind: 'exact', ref: PROJECTION_FUNCTION }];
32
+ : [{ kind: 'exact', ref: projectedMemberClass(ref) }];
38
33
  case 'policy':
39
34
  return owner.policies[ref.name] === undefined
40
35
  ? invalidPolicyObjectRef(validation, ref, pointer)
41
- : [{ kind: 'exact', ref: PROJECTION_POLICY }];
36
+ : [{ kind: 'exact', ref: projectedMemberClass(ref) }];
42
37
  case 'view':
43
38
  return owner.views[ref.name] === undefined
44
39
  ? invalidPolicyObjectRef(validation, ref, pointer)
45
- : [{ kind: 'exact', ref: PROJECTION_VIEW }];
40
+ : [{ kind: 'exact', ref: projectedMemberClass(ref) }];
46
41
  case 'core': {
47
42
  const node = owner.core.nodes[ref.name];
48
43
  if (node === undefined)
@@ -66,6 +61,3 @@ function invalidPolicyObjectRef(validation, ref, pointer) {
66
61
  validation.add('PL_REF', `${pointer}/ref`, `${policyObjectKey(ref)} is not an exact local or direct-dependency projected Policy object.`, policyObjectKey(ref));
67
62
  return undefined;
68
63
  }
69
- function nodeClassRef(name) {
70
- return { origin: KERNEL_ORIGIN, kind: 'class', name };
71
- }
@@ -1,16 +1,15 @@
1
+ import { equalDefinitionRefs } from '../../../addressing/definition-ref.js';
1
2
  import { branchesOf, isConnected, targetMode } from '../../../language/policy/analysis/branches.js';
2
3
  import { policyVariableClass } from '../../../language/policy/model/policy.js';
3
4
  import { normalizePolicyExpression, normalizePolicyPattern, } from '../../../language/policy/normalization/normalize.js';
5
+ import { escapePointerToken } from '../../../language/value/syntax/pointer.js';
4
6
  import { MAX_REPEAT, MAX_VARIABLES } from './budgets.js';
5
- import { escapePointer } from './context.js';
6
7
  import { referenceContracts } from './objects.js';
7
8
  export function validatePolicyReferences(context) {
8
9
  for (const [name, declaration] of Object.entries(context.environment.candidate.policies)) {
9
- const pointer = `/policies/${escapePointer(name)}`;
10
+ const pointer = `/policies/${escapePointerToken(name)}`;
10
11
  const expected = { origin: context.environment.candidate.origin, kind: 'policy', name };
11
- if (declaration.ref.origin !== expected.origin ||
12
- declaration.ref.kind !== expected.kind ||
13
- declaration.ref.name !== expected.name) {
12
+ if (!equalDefinitionRefs(declaration.ref, expected)) {
14
13
  context.add('DV_REF_RESOLVES', `${pointer}/ref`, `Policy ref must be exactly ${expected.origin}:policy.${expected.name}.`);
15
14
  }
16
15
  const expression = declaration.expression;
@@ -123,7 +122,7 @@ function validateCycles(context) {
123
122
  const visited = new Set();
124
123
  const visit = (name, stack) => {
125
124
  if (visiting.has(name)) {
126
- context.add('PL_COMPOSITION', `/policies/${escapePointer(name)}`, `Named Policy composition cycle: ${[...stack, name].join(' -> ')}.`);
125
+ context.add('PL_COMPOSITION', `/policies/${escapePointerToken(name)}`, `Named Policy composition cycle: ${[...stack, name].join(' -> ')}.`);
127
126
  return;
128
127
  }
129
128
  if (visited.has(name))
@@ -1,21 +1,6 @@
1
1
  import type { ClassRef } from '../../../addressing/index.js';
2
+ import type { ObjectContract, PolicyTyping } from '../../../language/policy/analysis/typing.js';
2
3
  import type { PolicyValidationContext } from './context.js';
3
- export type ObjectContract = {
4
- readonly kind: 'default';
5
- } | {
6
- readonly kind: 'exact';
7
- readonly ref: ClassRef;
8
- };
9
- export declare class PolicyTyping {
10
- #private;
11
- readonly context: PolicyValidationContext;
12
- constructor(context: PolicyValidationContext);
13
- validatePolicies(): void;
14
- policyAccepts(name: string, object: ObjectContract): boolean;
15
- /** Whether each normalized branch of the Policy is typed for an object of this contract. */
16
- policyBranchTyping(name: string, object: ObjectContract): readonly boolean[] | undefined;
17
- policyAcceptsEdge(name: string, source: ObjectContract, target: ObjectContract): boolean;
18
- concreteEndpointClasses(accepts: readonly ClassRef[]): readonly ClassRef[];
19
- endpointsOverlap(left: readonly ClassRef[], right: readonly ClassRef[]): boolean;
20
- }
4
+ /** Type every expanded branch of each named Policy at its own default use. */
5
+ export declare function validatePolicyTyping(context: PolicyValidationContext, typing: PolicyTyping): void;
21
6
  export declare function contractOfNodeDefinition(ref: ClassRef): ObjectContract;
@@ -1,366 +1,32 @@
1
- import { definitionRefIndex } from '../../../addressing/ordering.js';
2
- import { branchesOf, branchProduct, capBranches, targetMode, termKey, } from '../../../language/policy/analysis/branches.js';
3
- import { MAX_REPEAT } from './budgets.js';
4
- import { escapePointer } from './context.js';
5
- import { referenceContracts } from './objects.js';
6
- export class PolicyTyping {
7
- context;
8
- // Expanded branches of each named Policy, cached by node identity. A Policy's expansion is reused
9
- // across every composition path that reaches it (and across every use site and object contract,
10
- // none of which the expansion depends on), so a shared-child composition DAG is expanded in work
11
- // linear in the number of Policies instead of enumerating every path. The expansion result is
12
- // independent of the reaching path up to the opaque term-namespace labels, which only ever
13
- // disambiguate variables within one branch and leave targetMode and branch typing unchanged.
14
- #expansions = new Map();
15
- constructor(context) {
16
- this.context = context;
17
- }
18
- validatePolicies() {
19
- for (const name of Object.keys(this.context.environment.candidate.policies)) {
20
- const branches = this.#expandedBranches(name, new Set(), `$${name}`);
21
- if (branches === undefined)
22
- continue;
23
- for (const [index, branch] of branches.entries()) {
24
- const mode = targetMode(branch);
25
- if (mode === undefined) {
26
- if (!('match' in this.context.environment.candidate.policies[name].expression)) {
27
- this.context.add('PL_TARGET', `/policies/${escapePointer(name)}`, `Expanded normalized branch ${index} must use object or at least one of source and target, without mixing target modes.`);
28
- }
29
- continue;
30
- }
31
- const use = mode === 'object'
32
- ? { kind: 'object', object: { kind: 'default' } }
33
- : {
34
- kind: 'edge',
35
- source: { kind: 'default' },
36
- target: { kind: 'default' },
37
- };
38
- if (!this.#branchIsTyped(branch, use)) {
39
- this.context.add('PL_TYPING', `/policies/${escapePointer(name)}`, `Expanded normalized branch ${index} has contradictory endpoint or repeat contracts.`);
1
+ import { targetMode } from '../../../language/policy/analysis/branches.js';
2
+ import { escapePointerToken } from '../../../language/value/syntax/pointer.js';
3
+ /** Type every expanded branch of each named Policy at its own default use. */
4
+ export function validatePolicyTyping(context, typing) {
5
+ for (const name of Object.keys(context.environment.candidate.policies)) {
6
+ const branches = typing.expandedBranches(name, `$${name}`);
7
+ if (branches === undefined)
8
+ continue;
9
+ for (const [index, branch] of branches.entries()) {
10
+ const mode = targetMode(branch);
11
+ if (mode === undefined) {
12
+ if (!('match' in context.environment.candidate.policies[name].expression)) {
13
+ context.add('PL_TARGET', `/policies/${escapePointerToken(name)}`, `Expanded normalized branch ${index} must use object or at least one of source and target, without mixing target modes.`);
40
14
  }
41
- }
42
- }
43
- }
44
- policyAccepts(name, object) {
45
- return this.policyBranchTyping(name, object)?.every(Boolean) ?? false;
46
- }
47
- /** Whether each normalized branch of the Policy is typed for an object of this contract. */
48
- policyBranchTyping(name, object) {
49
- const branches = this.#expandedBranches(name, new Set(), `$use:${name}`);
50
- const use = { kind: 'object', object };
51
- return branches?.map((branch) => this.#branchIsTyped(branch, use));
52
- }
53
- policyAcceptsEdge(name, source, target) {
54
- const branches = this.#expandedBranches(name, new Set(), `$edge-use:${name}`);
55
- const use = { kind: 'edge', source, target };
56
- return branches !== undefined && branches.every((branch) => this.#branchIsTyped(branch, use));
57
- }
58
- concreteEndpointClasses(accepts) {
59
- return this.context.concreteNodeClasses.filter((candidate) => this.#classSatisfiesEndpoint(candidate, accepts));
60
- }
61
- endpointsOverlap(left, right) {
62
- const seed = [{ kind: 'open', requirements: [] }];
63
- const afterLeft = this.#applyEndpoint(seed, left);
64
- return this.#applyEndpoint(afterLeft, right).length > 0;
65
- }
66
- #expandedBranches(name, visiting, namespace) {
67
- if (visiting.has(name))
68
- return undefined;
69
- const cached = this.#expansions.get(name);
70
- if (cached !== undefined || this.#expansions.has(name))
71
- return cached;
72
- const expanded = this.#expandDeclaration(name, visiting, namespace);
73
- this.#expansions.set(name, expanded);
74
- return expanded;
75
- }
76
- #expandDeclaration(name, visiting, namespace) {
77
- const declaration = this.context.normalized.get(name);
78
- if (declaration === undefined)
79
- return undefined;
80
- if ('match' in declaration)
81
- return branchesOf(declaration.match, namespace);
82
- const nested = new Set(visiting);
83
- nested.add(name);
84
- const refs = 'allOf' in declaration ? declaration.allOf : declaration.anyOf;
85
- const children = [];
86
- for (const [index, ref] of refs.entries()) {
87
- if (!this.context.isLocalPolicy(ref))
88
- return undefined;
89
- const expanded = this.#expandedBranches(ref.name, nested, `${namespace}/${index}:${ref.name}`);
90
- if (expanded === undefined)
91
- return undefined;
92
- children.push(expanded);
93
- }
94
- return 'allOf' in declaration ? branchProduct(children) : capBranches(children.flat());
95
- }
96
- #branchIsTyped(branch, use) {
97
- if (targetMode(branch) !== use.kind)
98
- return false;
99
- const alternatives = edgeAlternatives(branch.edges, this);
100
- return alternatives.some((variants) => this.#constraintsAreSatisfiable(branch, variants, use));
101
- }
102
- #constraintsAreSatisfiable(branch, variants, use) {
103
- const terms = new Set(use.kind === 'object' ? ['subject', 'object'] : ['subject', 'source', 'target']);
104
- for (const key of branch.variables.keys())
105
- terms.add(key);
106
- for (const key of branch.terms.keys())
107
- terms.add(key);
108
- for (const edge of branch.edges) {
109
- terms.add(termKey(edge.pattern.source, edge.namespace));
110
- terms.add(termKey(edge.pattern.target, edge.namespace));
111
- }
112
- const union = new UnionFind(terms);
113
- for (const [left, right] of branch.equalities)
114
- union.join(left, right);
115
- for (const variant of variants) {
116
- for (const [left, right] of variant.equalities)
117
- union.join(left, right);
118
- }
119
- const constraints = new Map();
120
- for (const variant of variants) {
121
- for (const [term, accepts] of variant.constraints) {
122
- const root = union.find(term);
123
- const values = constraints.get(root) ?? [];
124
- values.push(accepts);
125
- constraints.set(root, values);
126
- }
127
- }
128
- const groups = new Map();
129
- for (const term of terms) {
130
- const root = union.find(term);
131
- const members = groups.get(root) ?? [];
132
- members.push(term);
133
- groups.set(root, members);
134
- }
135
- for (const [root, members] of groups) {
136
- const endpointConstraints = constraints.get(root) ?? [];
137
- let candidates = [{ kind: 'open', requirements: [] }];
138
- for (const member of members) {
139
- candidates = this.#applyInitial(candidates, member, branch, use);
140
- if (candidates.length === 0)
141
- return false;
142
- }
143
- for (const endpoint of endpointConstraints) {
144
- candidates = this.#applyEndpoint(candidates, endpoint);
145
- if (candidates.length === 0)
146
- return false;
147
- }
148
- }
149
- return true;
150
- }
151
- #applyInitial(candidates, term, branch, use) {
152
- if (term === 'subject')
153
- return this.#applyAnyNodeClass(candidates);
154
- const contract = targetContract(term, use);
155
- if (contract !== undefined) {
156
- if (contract.kind === 'exact')
157
- return this.#applyExact(candidates, contract.ref);
158
- if (contract.kind === 'default') {
159
- return uniqueCandidates(this.context.allNodeClasses.flatMap((ref) => this.#applyExact(candidates, ref)));
160
- }
161
- return candidates;
162
- }
163
- const referenced = branch.terms.get(term);
164
- if (referenced?.kind === 'ref') {
165
- const contract = referenceContracts(this.context, referenced.ref, '/policies')?.[0];
166
- return contract?.kind === 'exact' ? this.#applyExact(candidates, contract.ref) : [];
167
- }
168
- const selector = branch.variables.get(term);
169
- if (selector === undefined)
170
- return [];
171
- if (selector.kind === 'exact')
172
- return this.#applyExact(candidates, selector.class);
173
- if (selector.kind === 'any')
174
- return candidates;
175
- return this.#applyRequirement(candidates, selector.class);
176
- }
177
- #applyExact(candidates, exact) {
178
- const result = [];
179
- for (const candidate of candidates) {
180
- if (candidate.kind === 'exact') {
181
- if (definitionRefIndex(candidate.ref) === definitionRefIndex(exact))
182
- result.push(candidate);
183
- }
184
- else if (candidate.requirements.every((required) => this.context.classExtends(exact, required))) {
185
- result.push({ kind: 'exact', ref: exact });
186
- }
187
- }
188
- return uniqueCandidates(result);
189
- }
190
- #applyAnyNodeClass(candidates) {
191
- return uniqueCandidates(candidates.flatMap((candidate) => candidate.kind === 'exact'
192
- ? [candidate]
193
- : this.context.allNodeClasses
194
- .filter((ref) => candidate.requirements.every((required) => this.context.classExtends(ref, required)))
195
- .map((ref) => ({ kind: 'exact', ref }))));
196
- }
197
- #applyRequirement(candidates, required) {
198
- const result = [];
199
- for (const candidate of candidates) {
200
- if (candidate.kind === 'exact') {
201
- if (this.context.classExtends(candidate.ref, required))
202
- result.push(candidate);
203
- }
204
- else {
205
- result.push({
206
- kind: 'open',
207
- requirements: uniqueClasses([...candidate.requirements, required]),
208
- });
209
- }
210
- }
211
- return uniqueCandidates(result);
212
- }
213
- #applyEndpoint(candidates, accepts) {
214
- const result = [];
215
- for (const candidate of candidates) {
216
- if (candidate.kind === 'exact') {
217
- if (this.#classSatisfiesEndpoint(candidate.ref, accepts))
218
- result.push(candidate);
219
15
  continue;
220
16
  }
221
- for (const accept of accepts) {
222
- result.push({
223
- kind: 'open',
224
- requirements: uniqueClasses([...candidate.requirements, accept]),
225
- });
17
+ const use = mode === 'object'
18
+ ? { kind: 'object', object: { kind: 'default' } }
19
+ : {
20
+ kind: 'edge',
21
+ source: { kind: 'default' },
22
+ target: { kind: 'default' },
23
+ };
24
+ if (!typing.branchIsTyped(branch, use)) {
25
+ context.add('PL_TYPING', `/policies/${escapePointerToken(name)}`, `Expanded normalized branch ${index} has contradictory endpoint or repeat contracts.`);
226
26
  }
227
27
  }
228
- return uniqueCandidates(result);
229
28
  }
230
- #classSatisfiesEndpoint(candidate, accepts) {
231
- return accepts.some((accept) => this.context.classExtends(candidate, accept));
232
- }
233
- }
234
- function targetContract(term, use) {
235
- if (use.kind === 'object')
236
- return term === 'object' ? use.object : undefined;
237
- if (term === 'source')
238
- return use.source;
239
- if (term === 'target')
240
- return use.target;
241
- return undefined;
242
29
  }
243
30
  export function contractOfNodeDefinition(ref) {
244
31
  return { kind: 'exact', ref };
245
32
  }
246
- function edgeAlternatives(edges, typing) {
247
- let result = [[]];
248
- for (const edge of edges) {
249
- const variants = variantsForEdge(edge, typing);
250
- if (variants.length === 0)
251
- return [];
252
- const next = [];
253
- for (const prefix of result) {
254
- for (const variant of variants)
255
- next.push([...prefix, variant]);
256
- }
257
- result = next;
258
- }
259
- return result;
260
- }
261
- function variantsForEdge(use, typing) {
262
- const edge = use.pattern;
263
- const declaration = typing.context.edgeDeclaration(edge.class);
264
- if (declaration === undefined)
265
- return [];
266
- const endpoints = declaration.orientation === 'directed'
267
- ? [declaration.endpoints.source, declaration.endpoints.target]
268
- : declaration.endpoints;
269
- const bounds = edge.repeat ?? { min: 1, max: 1 };
270
- if (!Number.isInteger(bounds.min) ||
271
- !Number.isInteger(bounds.max) ||
272
- bounds.min < 0 ||
273
- bounds.max < bounds.min ||
274
- bounds.max > MAX_REPEAT) {
275
- return [];
276
- }
277
- const sourceKey = termKey(edge.source, use.namespace);
278
- const targetKey = termKey(edge.target, use.namespace);
279
- const variants = [];
280
- for (let length = bounds.min; length <= bounds.max; length++) {
281
- if (length === 0) {
282
- variants.push({ constraints: [], equalities: [[sourceKey, targetKey]] });
283
- continue;
284
- }
285
- const orientationCount = declaration.orientation === 'undirected' ? 2 ** length : 1;
286
- for (let mask = 0; mask < orientationCount; mask++) {
287
- const orientations = Array.from({ length }, (_, index) => declaration.orientation === 'undirected' && (mask & (1 << index)) !== 0);
288
- let transitionsValid = true;
289
- for (let index = 0; index + 1 < length; index++) {
290
- const incoming = endEndpoint(endpoints, orientations[index] ?? false);
291
- const outgoing = startEndpoint(endpoints, orientations[index + 1] ?? false);
292
- if (!typing.endpointsOverlap(incoming.accepts, outgoing.accepts)) {
293
- transitionsValid = false;
294
- break;
295
- }
296
- }
297
- if (!transitionsValid)
298
- continue;
299
- const first = startEndpoint(endpoints, orientations[0] ?? false);
300
- const last = endEndpoint(endpoints, orientations[length - 1] ?? false);
301
- variants.push({
302
- constraints: [
303
- [sourceKey, first.accepts],
304
- [targetKey, last.accepts],
305
- ],
306
- equalities: [],
307
- });
308
- }
309
- }
310
- return uniqueEdgeVariants(variants);
311
- }
312
- function startEndpoint(endpoints, reversed) {
313
- return reversed ? endpoints[1] : endpoints[0];
314
- }
315
- function endEndpoint(endpoints, reversed) {
316
- return reversed ? endpoints[0] : endpoints[1];
317
- }
318
- function uniqueEdgeVariants(values) {
319
- const result = new Map();
320
- for (const value of values) {
321
- const key = `${value.constraints
322
- .map(([term, accepts]) => `${term}:${accepts.map(definitionRefIndex).sort().join(',')}`)
323
- .join('|')}=${value.equalities.map(([left, right]) => `${left}:${right}`).join('|')}`;
324
- result.set(key, value);
325
- }
326
- return [...result.values()];
327
- }
328
- function uniqueCandidates(values) {
329
- const result = new Map();
330
- for (const value of values) {
331
- const key = value.kind === 'exact'
332
- ? `exact:${definitionRefIndex(value.ref)}`
333
- : `open:${value.requirements.map(definitionRefIndex).sort().join('|')}`;
334
- result.set(key, value);
335
- }
336
- return [...result.values()];
337
- }
338
- function uniqueClasses(values) {
339
- return [...new Map(values.map((value) => [definitionRefIndex(value), value])).values()];
340
- }
341
- class UnionFind {
342
- #parents = new Map();
343
- constructor(values) {
344
- for (const value of values)
345
- this.add(value);
346
- }
347
- add(value) {
348
- if (!this.#parents.has(value))
349
- this.#parents.set(value, value);
350
- }
351
- find(value) {
352
- this.add(value);
353
- const parent = this.#parents.get(value);
354
- if (parent === value)
355
- return value;
356
- const root = this.find(parent);
357
- this.#parents.set(value, root);
358
- return root;
359
- }
360
- join(left, right) {
361
- const a = this.find(left);
362
- const b = this.find(right);
363
- if (a !== b)
364
- this.#parents.set(b, a);
365
- }
366
- }
@@ -1,5 +1,5 @@
1
+ import type { PolicyTyping } from '../../../language/policy/analysis/typing.js';
1
2
  import type { PolicyValidationContext } from './context.js';
2
- import { type PolicyTyping } from './typing.js';
3
3
  export declare function validateReadUses(context: PolicyValidationContext, typing: PolicyTyping): void;
4
4
  export declare function validateTraverseUses(context: PolicyValidationContext, typing: PolicyTyping): void;
5
5
  export declare function validateCallableUses(context: PolicyValidationContext, typing: PolicyTyping): void;
@@ -1,12 +1,12 @@
1
1
  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
- import { admittedCheckClasses, neverAdmittedClasses, } from '../../../language/policy/analysis/checks.js';
4
+ import { neverAdmittedClasses, } from '../../../language/policy/analysis/checks.js';
5
5
  import { 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
+ import { escapePointerToken } from '../../../language/value/syntax/pointer.js';
8
9
  import { MAX_BRANCHES, MAX_CHECK_DEPTH, MAX_CHECK_LEAVES, measureChecks } from './budgets.js';
9
- import { escapePointer } from './context.js';
10
10
  import { referenceContracts } from './objects.js';
11
11
  import { contractOfNodeDefinition } from './typing.js';
12
12
  const formatDefinitionRef = Key.of;
@@ -26,7 +26,7 @@ function validateClassPolicyUses(context, typing, operation) {
26
26
  const ref = declaration.policies[operation];
27
27
  if (typeof ref !== 'object')
28
28
  continue;
29
- const pointer = `/classes/${escapePointer(name)}/policies/${operation}`;
29
+ const pointer = `/classes/${escapePointerToken(name)}/policies/${operation}`;
30
30
  if (isClassRuleChecks(ref)) {
31
31
  // Structure admits Policy checks only on a Node Class: `self` is the observed exact Class.
32
32
  validateChecks(context, typing, ref, {
@@ -50,13 +50,7 @@ function validateClassPolicyUses(context, typing, operation) {
50
50
  context.add('PL_USE', pointer, `${formatDefinitionRef(ref)} is not valid for every normalized branch of ${operation} Node Class ${name}.`, formatDefinitionRef(ref));
51
51
  continue;
52
52
  }
53
- const endpoints = declaration.orientation === 'directed'
54
- ? [declaration.endpoints.source, declaration.endpoints.target]
55
- : declaration.endpoints;
56
- const sources = typing.concreteEndpointClasses(endpoints[0].accepts);
57
- const targets = typing.concreteEndpointClasses(endpoints[1].accepts);
58
- const accepted = sources.every((source) => targets.every((target) => typing.policyAcceptsEdge(ref.name, { kind: 'exact', ref: source }, { kind: 'exact', ref: target })));
59
- if (!accepted) {
53
+ if (!typing.policyAcceptsEdgeClass(ref.name, declaration)) {
60
54
  context.add('PL_USE', pointer, `${formatDefinitionRef(ref)} is not valid for every concrete source/target pair of ${operation} Edge Class ${name}.`, formatDefinitionRef(ref));
61
55
  }
62
56
  }
@@ -67,7 +61,7 @@ export function validateCallableUses(context, typing) {
67
61
  continue;
68
62
  validateChecks(context, typing, declaration.policy, {
69
63
  site: 'callable',
70
- pointer: `/functions/${escapePointer(name)}/policy`,
64
+ pointer: `/functions/${escapePointerToken(name)}/policy`,
71
65
  input: isBinaryInput(declaration.input) ? declaration.input.metadata : declaration.input,
72
66
  instanceMethod: false,
73
67
  });
@@ -94,7 +88,7 @@ function validateDeclaredMethods(context, typing, namespace, name, methods, rece
94
88
  continue;
95
89
  validateChecks(context, typing, method.policy, {
96
90
  site: 'callable',
97
- pointer: `/${namespace}/${escapePointer(name)}/methods/${escapePointer(methodName)}/policy`,
91
+ pointer: `/${namespace}/${escapePointerToken(name)}/methods/${escapePointerToken(methodName)}/policy`,
98
92
  input: method.input,
99
93
  receiver,
100
94
  instanceMethod: !method.static,
@@ -144,16 +138,12 @@ function visitChecks(validation, typing, expression, context, pointer, leaves) {
144
138
  : referenceContracts(validation, expression.object.ref, objectPointer);
145
139
  if (contracts === undefined)
146
140
  return;
147
- const typings = new Map(contracts.map((contract) => [
148
- contractName(contract),
149
- typing.policyBranchTyping(expression.check.name, contract),
150
- ]));
151
- const admitted = admittedCheckClasses(typings);
152
- if (admitted === undefined) {
141
+ const classes = typing.checkLeafClasses(expression.check.name, contracts);
142
+ if (classes === undefined) {
153
143
  validation.add('PL_USE', pointer, `${formatDefinitionRef(expression.check)} is incompatible with ${SITE[context.site].objects}.`, formatDefinitionRef(expression.check));
154
144
  return;
155
145
  }
156
- leaves.set(expression, { accepted: [...typings.keys()], admitted });
146
+ leaves.set(expression, classes);
157
147
  return;
158
148
  }
159
149
  const { key, values } = checkOperands(expression);
@@ -162,9 +152,6 @@ function visitChecks(validation, typing, expression, context, pointer, leaves) {
162
152
  }
163
153
  values.forEach((child, index) => visitChecks(validation, typing, child, context, `${pointer}/${key}/${index}`, leaves));
164
154
  }
165
- function contractName(contract) {
166
- return contract.kind === 'exact' ? formatDefinitionRef(contract.ref) : 'default';
167
- }
168
155
  function selfContracts(validation, context, pointer) {
169
156
  if (!context.instanceMethod || context.receiver === undefined) {
170
157
  validation.add('PL_USE', pointer, 'self is valid only for a Node instance Method.');
@@ -1,7 +1,8 @@
1
+ import { PolicyTyping } from '../../../language/policy/analysis/typing.js';
1
2
  import { validatePolicyBudgets } from './budgets.js';
2
3
  import { PolicyValidationContext } from './context.js';
3
4
  import { validatePolicyReferences } from './references.js';
4
- import { PolicyTyping } from './typing.js';
5
+ import { validatePolicyTyping } from './typing.js';
5
6
  import { validateCallableUses, validateReadUses, validateTraverseUses } from './uses.js';
6
7
  /**
7
8
  * Validates the closed V1 Policy language after structural parsing.
@@ -15,7 +16,7 @@ export function validatePolicies(environment, analysis) {
15
16
  validatePolicyReferences(context);
16
17
  validatePolicyBudgets(context);
17
18
  const typing = new PolicyTyping(context);
18
- typing.validatePolicies();
19
+ validatePolicyTyping(context, typing);
19
20
  validateReadUses(context, typing);
20
21
  validateTraverseUses(context, typing);
21
22
  validateCallableUses(context, typing);
@@ -1,4 +1,5 @@
1
1
  /** The exact V1 value-schema grammar embedded in DomainSchema declarations. */
2
+ import type { ValueKind } from '@astrale-os/kernel-dsl/value';
2
3
  import type { Diagnostic } from '../../language/validation/diagnostic.js';
3
4
  import type { JsonSchema } from '../../language/value/model/schema.js';
4
5
  import type { OrderingKind } from '../../language/value/ordering.js';
@@ -13,3 +14,11 @@ export type { OrderingKind } from '../../language/value/ordering.js';
13
14
  export declare function validate(owner: DomainSchema, contract: JsonSchema, input: unknown, pointer?: string): readonly Diagnostic[];
14
15
  /** Return the one portable scalar ordering domain admitted by a property schema, if any. */
15
16
  export declare function classifyOrdering(owner: DomainSchema, contract: JsonSchema): OrderingKind | undefined;
17
+ /** The value kind of a property schema: what a graph language may test on the property (Y4). */
18
+ export declare function classifyValueKind(owner: DomainSchema, contract: JsonSchema): ValueKind;
19
+ /**
20
+ * A warning for each class property whose value kind is `json` (an ambiguous union, an object, an
21
+ * array with repetitions): a Query filters it by equality only. Nothing refuses such a property;
22
+ * the CLI shows the warning to its author.
23
+ */
24
+ export declare function valueKindWarnings(owner: DomainSchema): readonly Diagnostic<'VS_VALUE_KIND_JSON'>[];