@astrale-os/kernel-dsl 1.0.0-beta.12 → 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.
- package/dist/v1/addressing/definition-ref.js +21 -37
- package/dist/v1/addressing/ordering.d.ts +2 -0
- package/dist/v1/addressing/ordering.js +4 -0
- package/dist/v1/addressing/patterns.d.ts +0 -1
- package/dist/v1/addressing/patterns.js +0 -5
- package/dist/v1/builder/class/authoring.js +5 -28
- package/dist/v1/builder/lowering/schema/context.js +2 -4
- package/dist/v1/builder/lowering/value/path.js +2 -4
- package/dist/v1/builder/lowering/value/zod.js +1 -3
- package/dist/v1/builder/schema/define.d.ts +0 -1
- package/dist/v1/builder/schema/diagnostic.d.ts +2 -1
- package/dist/v1/builder/schema/diagnostic.js +5 -10
- package/dist/v1/builder/schema/projection.d.ts +1 -1
- package/dist/v1/builder/value/full-match.d.ts +0 -1
- package/dist/v1/builder/value/full-match.js +0 -1
- package/dist/v1/bundle/codec.js +2 -4
- package/dist/v1/bundle/diagnostic.js +3 -7
- package/dist/v1/bundle/structure.js +2 -4
- package/dist/v1/compiled/admission/diagnostic.d.ts +1 -1
- package/dist/v1/compiled/admission/diagnostic.js +1 -3
- package/dist/v1/compiled/admission/revision.d.ts +9 -1
- package/dist/v1/compiled/admission/revision.js +6 -3
- package/dist/v1/compiled/admission/semantics/class.d.ts +4 -1
- package/dist/v1/compiled/admission/semantics/class.js +4 -8
- package/dist/v1/compiled/admission/semantics/core.d.ts +1 -24
- package/dist/v1/compiled/admission/semantics/core.js +54 -34
- package/dist/v1/compiled/admission/semantics/edge.d.ts +2 -4
- package/dist/v1/compiled/admission/semantics/edge.js +6 -65
- package/dist/v1/compiled/admission/semantics/policy/callable.js +0 -2
- package/dist/v1/compiled/admission/semantics/policy/contracts.d.ts +5 -20
- package/dist/v1/compiled/admission/semantics/policy/contracts.js +8 -21
- package/dist/v1/compiled/admission/semantics/policy/parity.js +13 -22
- package/dist/v1/compiled/admission/semantics/policy/pattern.d.ts +4 -1
- package/dist/v1/compiled/admission/semantics/policy/pattern.js +4 -16
- package/dist/v1/compiled/admission/semantics/policy/typing.d.ts +2 -24
- package/dist/v1/compiled/admission/semantics/policy/typing.js +19 -201
- package/dist/v1/compiled/compiler/compile.js +6 -8
- package/dist/v1/compiled/diagnostic.js +2 -3
- package/dist/v1/compiled/loader/load.d.ts +3 -2
- package/dist/v1/compiled/loader/load.js +10 -44
- package/dist/v1/domain/create.js +3 -5
- package/dist/v1/domain/namespace/member-collection.js +4 -6
- package/dist/v1/domain/validation/create.d.ts +0 -1
- package/dist/v1/domain/validation/create.js +0 -35
- package/dist/v1/language/class/inheritance.d.ts +18 -0
- package/dist/v1/language/class/inheritance.js +73 -0
- package/dist/v1/language/class/property-values.js +2 -4
- package/dist/v1/language/core/facts.d.ts +72 -0
- package/dist/v1/language/core/facts.js +95 -0
- package/dist/v1/language/policy/analysis/branches.d.ts +8 -0
- package/dist/v1/language/policy/analysis/branches.js +2 -1
- package/dist/v1/language/policy/analysis/budgets.d.ts +13 -1
- package/dist/v1/language/policy/analysis/budgets.js +104 -0
- package/dist/v1/language/policy/analysis/typing.d.ts +48 -0
- package/dist/v1/language/policy/analysis/typing.js +338 -0
- package/dist/v1/language/validation/diagnostic.d.ts +14 -0
- package/dist/v1/language/validation/diagnostic.js +21 -0
- package/dist/v1/language/value/compile/fields.js +4 -8
- package/dist/v1/language/value/profile/rules.js +5 -7
- package/dist/v1/language/value/profile/validate.js +1 -10
- package/dist/v1/language/value/reference/recursion.js +2 -22
- package/dist/v1/language/value/syntax/pointer.d.ts +2 -0
- package/dist/v1/language/value/syntax/pointer.js +5 -2
- package/dist/v1/language/value/syntax/walk.d.ts +2 -0
- package/dist/v1/language/value/syntax/walk.js +19 -20
- package/dist/v1/schema/compatibility/diff.js +2 -4
- package/dist/v1/schema/compatibility/meaning/footprint.js +16 -50
- package/dist/v1/schema/compatibility/structure/requirements.js +26 -58
- package/dist/v1/schema/compatibility/walk.d.ts +17 -0
- package/dist/v1/schema/compatibility/walk.js +41 -0
- package/dist/v1/schema/diagnostic.js +3 -11
- package/dist/v1/schema/resolution/context.js +3 -5
- package/dist/v1/schema/validation/callable.js +3 -5
- package/dist/v1/schema/validation/class.js +10 -52
- package/dist/v1/schema/validation/core.js +65 -199
- package/dist/v1/schema/validation/domain.js +1 -10
- package/dist/v1/schema/validation/domain.structure.js +2 -4
- package/dist/v1/schema/validation/policy/budgets.js +5 -112
- package/dist/v1/schema/validation/policy/context.d.ts +5 -3
- package/dist/v1/schema/validation/policy/context.js +5 -3
- package/dist/v1/schema/validation/policy/objects.d.ts +1 -1
- package/dist/v1/schema/validation/policy/objects.js +6 -14
- package/dist/v1/schema/validation/policy/references.js +5 -6
- package/dist/v1/schema/validation/policy/typing.d.ts +3 -18
- package/dist/v1/schema/validation/policy/typing.js +22 -356
- package/dist/v1/schema/validation/policy/uses.d.ts +1 -1
- package/dist/v1/schema/validation/policy/uses.js +9 -22
- package/dist/v1/schema/validation/policy/validate.js +3 -2
- package/dist/v1/schema/validation/value-api.js +2 -2
- package/dist/v1/schema/validation/value.js +5 -7
- package/dist/v1/schema/validation/view.js +4 -8
- package/package.json +1 -1
- package/dist/v1/compiled/admission/semantics/cardinality.d.ts +0 -5
- package/dist/v1/compiled/admission/semantics/cardinality.js +0 -128
- package/dist/v1/compiled/admission/semantics/policy/equality.d.ts +0 -8
- package/dist/v1/compiled/admission/semantics/policy/equality.js +0 -27
- package/dist/v1/compiled/admission/semantics/policy/measure.d.ts +0 -9
- package/dist/v1/compiled/admission/semantics/policy/measure.js +0 -81
- package/dist/v1/compiled/admission/semantics/policy/variants.d.ts +0 -10
- package/dist/v1/compiled/admission/semantics/policy/variants.js +0 -65
- package/dist/v1/language/class/index.d.ts +0 -8
- package/dist/v1/language/class/index.js +0 -1
- package/dist/v1/language/core/index.d.ts +0 -2
- package/dist/v1/language/core/index.js +0 -1
- package/dist/v1/language/value/index.d.ts +0 -11
- package/dist/v1/language/value/index.js +0 -8
|
@@ -1,366 +1,32 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
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
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
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 {
|
|
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/${
|
|
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
|
-
|
|
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/${
|
|
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}/${
|
|
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
|
|
148
|
-
|
|
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,
|
|
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 {
|
|
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
|
|
19
|
+
validatePolicyTyping(context, typing);
|
|
19
20
|
validateReadUses(context, typing);
|
|
20
21
|
validateTraverseUses(context, typing);
|
|
21
22
|
validateCallableUses(context, typing);
|
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
import { diagnostic } from '../../language/validation/diagnostic.js';
|
|
3
3
|
import { validateJsonInstance } from '../../language/value/evaluate/validate.js';
|
|
4
4
|
import { classifyOrdering as classifyOrderingWithRegistry, classifyValueKind as classifyValueKindWithRegistry, } from '../../language/value/ordering.js';
|
|
5
|
+
import { escapePointerToken } from '../../language/value/syntax/pointer.js';
|
|
5
6
|
import { createDomainValueSchemaRegistry } from '../resolution/value.registry.js';
|
|
6
|
-
import { escapePointer } from './policy/context.js';
|
|
7
7
|
const registries = new WeakMap();
|
|
8
8
|
export { VALUE_SCHEMA_DIALECT as DIALECT, VALUE_SCHEMA_VOCABULARY as VOCABULARY, } from '../../language/value/model/schema.js';
|
|
9
9
|
export { blob, isBlobValueSchema as isBlob } from '../../language/value/model/blob.js';
|
|
@@ -31,7 +31,7 @@ export function valueKindWarnings(owner) {
|
|
|
31
31
|
for (const [name, property] of Object.entries(definition.properties)) {
|
|
32
32
|
if (classifyValueKind(owner, property.schema) !== 'json')
|
|
33
33
|
continue;
|
|
34
|
-
warnings.push(diagnostic('VS_VALUE_KIND_JSON', `/classes/${
|
|
34
|
+
warnings.push(diagnostic('VS_VALUE_KIND_JSON', `/classes/${escapePointerToken(className)}/properties/${escapePointerToken(name)}`, `The value kind of ${className}.${name} is json: a Query filters it by equality only.`));
|
|
35
35
|
}
|
|
36
36
|
}
|
|
37
37
|
return warnings;
|
|
@@ -1,23 +1,24 @@
|
|
|
1
1
|
import { isBinaryInput } from '../../language/callable/model/callable.js';
|
|
2
2
|
import { validateValueSchema } from '../../language/value/profile/validate.js';
|
|
3
|
+
import { escapePointerToken } from '../../language/value/syntax/pointer.js';
|
|
3
4
|
import { diagnostic } from '../diagnostic.js';
|
|
4
5
|
export function validateEmbeddedValueSchemas(schema, registry) {
|
|
5
6
|
const diagnostics = [];
|
|
6
7
|
for (const [name, declaration] of Object.entries(schema.classes)) {
|
|
7
|
-
validateDefinition(declaration, `/classes/${
|
|
8
|
+
validateDefinition(declaration, `/classes/${escapePointerToken(name)}`, registry, diagnostics);
|
|
8
9
|
}
|
|
9
10
|
for (const [name, declaration] of Object.entries(schema.functions)) {
|
|
10
|
-
validateCallable(declaration, `/functions/${
|
|
11
|
+
validateCallable(declaration, `/functions/${escapePointerToken(name)}`, registry, diagnostics);
|
|
11
12
|
}
|
|
12
13
|
return Object.freeze(diagnostics);
|
|
13
14
|
}
|
|
14
15
|
function validateDefinition(declaration, pointer, registry, diagnostics) {
|
|
15
16
|
for (const [name, property] of Object.entries(declaration.properties)) {
|
|
16
|
-
validate(property.schema, 'property-facet', `${pointer}/properties/${
|
|
17
|
+
validate(property.schema, 'property-facet', `${pointer}/properties/${escapePointerToken(name)}/schema`, registry, diagnostics);
|
|
17
18
|
}
|
|
18
19
|
if ('methods' in declaration) {
|
|
19
20
|
for (const [name, method] of Object.entries(declaration.methods)) {
|
|
20
|
-
validateCallable(method, `${pointer}/methods/${
|
|
21
|
+
validateCallable(method, `${pointer}/methods/${escapePointerToken(name)}`, registry, diagnostics);
|
|
21
22
|
}
|
|
22
23
|
}
|
|
23
24
|
}
|
|
@@ -40,6 +41,3 @@ function validate(schema, context, pointer, registry, diagnostics) {
|
|
|
40
41
|
diagnostics.push(diagnostic('DV_VALUE_SCHEMA', failure.pointer, `[${failure.code}] ${failure.message}`, failure.ref));
|
|
41
42
|
}
|
|
42
43
|
}
|
|
43
|
-
function escapePointer(value) {
|
|
44
|
-
return value.replaceAll('~', '~0').replaceAll('/', '~1');
|
|
45
|
-
}
|
|
@@ -1,12 +1,14 @@
|
|
|
1
|
+
import { equalDefinitionRefs } from '../../addressing/definition-ref.js';
|
|
1
2
|
import { Key } from '../../addressing/key.js';
|
|
3
|
+
import { escapePointerToken } from '../../language/value/syntax/pointer.js';
|
|
2
4
|
import { diagnostic } from '../diagnostic.js';
|
|
3
5
|
const formatDefinitionRef = Key.of;
|
|
4
6
|
export function validateViews(environment) {
|
|
5
7
|
const diagnostics = [];
|
|
6
8
|
for (const [name, view] of Object.entries(environment.candidate.views)) {
|
|
7
|
-
const pointer = `/views/${
|
|
9
|
+
const pointer = `/views/${escapePointerToken(name)}`;
|
|
8
10
|
const ref = { origin: environment.candidate.origin, kind: 'view', name };
|
|
9
|
-
if (!
|
|
11
|
+
if (!equalDefinitionRefs(view.ref, ref)) {
|
|
10
12
|
diagnostics.push(diagnostic('DV_REF_RESOLVES', `${pointer}/ref`, `View ref must be exactly ${formatDefinitionRef(ref)}.`, formatDefinitionRef(view.ref)));
|
|
11
13
|
}
|
|
12
14
|
}
|
|
@@ -26,9 +28,3 @@ function validateEntrypoint(environment, diagnostics) {
|
|
|
26
28
|
diagnostics.push(diagnostic('DV_ENTRYPOINT', '/entrypoint', `Entrypoint ${formatDefinitionRef(entrypoint)} is not a Domain-targeted View of ${origin}.`, formatDefinitionRef(entrypoint)));
|
|
27
29
|
}
|
|
28
30
|
}
|
|
29
|
-
function sameRef(left, right) {
|
|
30
|
-
return left.origin === right.origin && left.kind === right.kind && left.name === right.name;
|
|
31
|
-
}
|
|
32
|
-
function escapePointer(value) {
|
|
33
|
-
return value.replaceAll('~', '~0').replaceAll('/', '~1');
|
|
34
|
-
}
|
package/package.json
CHANGED
|
@@ -1,5 +0,0 @@
|
|
|
1
|
-
import { type CompiledSchemaEntry } from '../../model/schema.js';
|
|
2
|
-
import { type SemanticIndex } from './context.js';
|
|
3
|
-
import { type CoreNode, type CoreEdgeValue } from './core.js';
|
|
4
|
-
/** Check normalized Edge identity, keys, noSelf, cycles and complete Core degrees. */
|
|
5
|
-
export declare function validateCoreEdgeSet(context: SemanticIndex, entry: CompiledSchemaEntry, entryIndex: number, nodes: ReadonlyMap<string, CoreNode>, edges: readonly CoreEdgeValue[]): void;
|