@atlasauth/pca-analyzer 0.0.0-stage → 0.2.0
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/LICENSE +21 -0
- package/README.md +109 -2
- package/dist/caveats.d.ts +22 -0
- package/dist/caveats.js +88 -0
- package/dist/caveats.js.map +1 -0
- package/dist/enumerate.d.ts +68 -0
- package/dist/enumerate.js +375 -0
- package/dist/enumerate.js.map +1 -0
- package/dist/field.d.ts +42 -0
- package/dist/field.js +232 -0
- package/dist/field.js.map +1 -0
- package/dist/index.d.ts +84 -0
- package/dist/index.js +374 -0
- package/dist/index.js.map +1 -0
- package/dist/types.d.ts +109 -0
- package/dist/types.js +13 -0
- package/dist/types.js.map +1 -0
- package/package.json +53 -3
|
@@ -0,0 +1,375 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.DEFAULT_MAX_COMBINATIONS = void 0;
|
|
4
|
+
exports.normalizePolicy = normalizePolicy;
|
|
5
|
+
exports.toModel = toModel;
|
|
6
|
+
exports.collectProbes = collectProbes;
|
|
7
|
+
exports.productSize = productSize;
|
|
8
|
+
exports.representatives = representatives;
|
|
9
|
+
exports.admits = admits;
|
|
10
|
+
exports.admitsAction = admitsAction;
|
|
11
|
+
exports.repToAction = repToAction;
|
|
12
|
+
exports.search = search;
|
|
13
|
+
const pca_1 = require("@atlasauth/pca");
|
|
14
|
+
const caveats_1 = require("./caveats");
|
|
15
|
+
const field_1 = require("./field");
|
|
16
|
+
const types_1 = require("./types");
|
|
17
|
+
/**
|
|
18
|
+
* The bounded decision engine. It builds a finite set of representative actions ("the grid") from the
|
|
19
|
+
* literals, thresholds, prefixes, verbs and resource matchers that appear in the policies (and, for
|
|
20
|
+
* intent conformance, the intent). Because PCA's `where` theory is a boolean combination of
|
|
21
|
+
* threshold/membership/prefix tests, each policy's admitted region is a union of grid cells, so one
|
|
22
|
+
* representative per cell is DISTINGUISHING: any two policies that differ on some action also differ on
|
|
23
|
+
* some representative. Evaluating the REAL `evaluatePredicates` at each representative (plus the static
|
|
24
|
+
* caveat gate) therefore decides reachability, subsumption, disjointness and equivalence EXACTLY for
|
|
25
|
+
* the decided fragment, and yields a concrete counterexample whenever a relation fails.
|
|
26
|
+
*
|
|
27
|
+
* Out-of-fragment features (`re:` regex resources, cross-field `ref` conditions) and grids larger than
|
|
28
|
+
* the bound are detected and reported as `approximate`, and every verdict then fails safe.
|
|
29
|
+
*/
|
|
30
|
+
const ABSENT = Symbol('analyzer-absent-field');
|
|
31
|
+
const FRESH_VERB = '\u0000atlas-analyzer::fresh-verb\u0000';
|
|
32
|
+
const FRESH_RESOURCE = '\u0000atlas-analyzer::fresh-resource\u0000';
|
|
33
|
+
/** Default cap on the representative grid size (product of all dimensions). */
|
|
34
|
+
exports.DEFAULT_MAX_COMBINATIONS = 200_000;
|
|
35
|
+
function normalizePolicy(p) {
|
|
36
|
+
const predicates = Array.isArray(p.predicates) ? p.predicates : [];
|
|
37
|
+
const caveats = 'caveats' in p && Array.isArray(p.caveats) ? p.caveats : [];
|
|
38
|
+
return { predicates, caveats };
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Collect over-approximation reasons from one condition tree. Boolean groupings (all_of/any_of/not)
|
|
42
|
+
* are DECIDED EXACTLY as long as every leaf uses a decidable op — the admitted region stays a boolean
|
|
43
|
+
* combination of the same per-field cells, so the representative grid remains distinguishing — so the
|
|
44
|
+
* grouping itself is not undecidable; only its hard leaves are. The genuinely-hard leaf ops
|
|
45
|
+
* (`like`/`matches`/`is_a`/`member_of`), a cross-field `ref`, and a whole-`action.params` condition
|
|
46
|
+
* force a conservative over-approximation (verdict fails safe).
|
|
47
|
+
*/
|
|
48
|
+
function conditionUndecidable(c, reasons) {
|
|
49
|
+
if (c === null || typeof c !== 'object')
|
|
50
|
+
return;
|
|
51
|
+
if ((0, pca_1.isGroupCondition)(c)) {
|
|
52
|
+
if ('all_of' in c && Array.isArray(c.all_of))
|
|
53
|
+
for (const x of c.all_of)
|
|
54
|
+
conditionUndecidable(x, reasons);
|
|
55
|
+
else if ('any_of' in c && Array.isArray(c.any_of))
|
|
56
|
+
for (const x of c.any_of)
|
|
57
|
+
conditionUndecidable(x, reasons);
|
|
58
|
+
else if ('not' in c)
|
|
59
|
+
conditionUndecidable(c.not, reasons);
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
if (!(0, pca_1.isLeafCondition)(c))
|
|
63
|
+
return;
|
|
64
|
+
if (c.ref !== undefined) {
|
|
65
|
+
reasons.add('cross-field `ref` condition — the relation between two fields is not resolved');
|
|
66
|
+
}
|
|
67
|
+
if (c.field === 'action.params') {
|
|
68
|
+
reasons.add('condition on the whole `action.params` object is not enumerated precisely');
|
|
69
|
+
}
|
|
70
|
+
if (c.op === 'like' || c.op === 'matches' || c.op === 'is_a' || c.op === 'member_of') {
|
|
71
|
+
reasons.add(`\`${c.op}\` condition — matched by over-approximation, not enumerated exactly`);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
function detectUndecidable(predicates) {
|
|
75
|
+
const reasons = new Set();
|
|
76
|
+
for (const p of predicates) {
|
|
77
|
+
if (p === null || typeof p !== 'object')
|
|
78
|
+
continue;
|
|
79
|
+
const r = p.resource;
|
|
80
|
+
if (typeof r === 'string' && r.startsWith('re:')) {
|
|
81
|
+
reasons.add('re: regex resource matcher — resource dimension cannot be soundly enumerated');
|
|
82
|
+
}
|
|
83
|
+
if (Array.isArray(p.where))
|
|
84
|
+
for (const c of p.where)
|
|
85
|
+
conditionUndecidable(c, reasons);
|
|
86
|
+
}
|
|
87
|
+
return [...reasons];
|
|
88
|
+
}
|
|
89
|
+
function toModel(p) {
|
|
90
|
+
const { predicates, caveats } = normalizePolicy(p);
|
|
91
|
+
const ca = (0, caveats_1.analyzeCaveats)(caveats);
|
|
92
|
+
return {
|
|
93
|
+
predicates,
|
|
94
|
+
caveats,
|
|
95
|
+
caveatSat: ca.sat,
|
|
96
|
+
caveatReason: ca.reason,
|
|
97
|
+
revCap: ca.revCap,
|
|
98
|
+
undecidable: detectUndecidable(predicates),
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
function addVerbLiterals(c, into) {
|
|
102
|
+
const v = c.value;
|
|
103
|
+
if (c.op === 'eq' || c.op === 'ne' || c.op === 'prefix' || c.op === 'like' || c.op === 'is_a') {
|
|
104
|
+
if (typeof v === 'string')
|
|
105
|
+
into.add(v);
|
|
106
|
+
}
|
|
107
|
+
else if ((c.op === 'in' || c.op === 'nin') && Array.isArray(v)) {
|
|
108
|
+
for (const x of v)
|
|
109
|
+
if (typeof x === 'string')
|
|
110
|
+
into.add(x);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
function collectProbes(inputs) {
|
|
114
|
+
const verbLiterals = new Set(inputs.extraVerbs ?? []);
|
|
115
|
+
const resourceLiterals = new Set();
|
|
116
|
+
const fieldConds = new Map();
|
|
117
|
+
const addFieldCond = (path, c) => {
|
|
118
|
+
const arr = fieldConds.get(path) ?? [];
|
|
119
|
+
arr.push(c);
|
|
120
|
+
fieldConds.set(path, arr);
|
|
121
|
+
};
|
|
122
|
+
const classifyResource = (r) => {
|
|
123
|
+
if (r === undefined || r === '*')
|
|
124
|
+
return; // universal: FRESH_RESOURCE represents it
|
|
125
|
+
if (r.startsWith('re:'))
|
|
126
|
+
return; // undecidable: FRESH_RESOURCE stands in (flagged on the model)
|
|
127
|
+
if (r.endsWith('*')) {
|
|
128
|
+
const body = r.slice(0, -1);
|
|
129
|
+
resourceLiterals.add(body);
|
|
130
|
+
resourceLiterals.add(body + 'x');
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
resourceLiterals.add(r);
|
|
134
|
+
};
|
|
135
|
+
const handleCond = (c) => {
|
|
136
|
+
if (c === null || typeof c !== 'object')
|
|
137
|
+
return;
|
|
138
|
+
// Recurse through boolean groupings and bucket their leaves (keeps the grid distinguishing for
|
|
139
|
+
// nested boolean logic over decidable ops).
|
|
140
|
+
if ((0, pca_1.isGroupCondition)(c)) {
|
|
141
|
+
if ('all_of' in c && Array.isArray(c.all_of))
|
|
142
|
+
for (const x of c.all_of)
|
|
143
|
+
handleCond(x);
|
|
144
|
+
else if ('any_of' in c && Array.isArray(c.any_of))
|
|
145
|
+
for (const x of c.any_of)
|
|
146
|
+
handleCond(x);
|
|
147
|
+
else if ('not' in c)
|
|
148
|
+
handleCond(c.not);
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
151
|
+
if (!(0, pca_1.isLeafCondition)(c))
|
|
152
|
+
return;
|
|
153
|
+
if (c.field === 'action.verb') {
|
|
154
|
+
addVerbLiterals(c, verbLiterals);
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
157
|
+
if (c.field === 'action.resource') {
|
|
158
|
+
if (c.op === 'prefix' && typeof c.value === 'string')
|
|
159
|
+
classifyResource(c.value + '*');
|
|
160
|
+
else if (typeof c.value === 'string')
|
|
161
|
+
classifyResource(c.value);
|
|
162
|
+
else if (Array.isArray(c.value))
|
|
163
|
+
for (const x of c.value)
|
|
164
|
+
if (typeof x === 'string')
|
|
165
|
+
classifyResource(x);
|
|
166
|
+
return;
|
|
167
|
+
}
|
|
168
|
+
addFieldCond(c.field, c);
|
|
169
|
+
};
|
|
170
|
+
for (const list of inputs.predicateSets) {
|
|
171
|
+
for (const p of list) {
|
|
172
|
+
if (p === null || typeof p !== 'object')
|
|
173
|
+
continue;
|
|
174
|
+
if (typeof p.verb === 'string') {
|
|
175
|
+
if (p.verb !== '*')
|
|
176
|
+
verbLiterals.add(p.verb);
|
|
177
|
+
}
|
|
178
|
+
else if (Array.isArray(p.verb)) {
|
|
179
|
+
for (const v of p.verb)
|
|
180
|
+
if (typeof v === 'string' && v !== '*')
|
|
181
|
+
verbLiterals.add(v);
|
|
182
|
+
}
|
|
183
|
+
classifyResource(p.resource);
|
|
184
|
+
if (Array.isArray(p.where))
|
|
185
|
+
for (const c of p.where)
|
|
186
|
+
handleCond(c);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
for (const r of inputs.extraResources ?? [])
|
|
190
|
+
classifyResource(r);
|
|
191
|
+
for (const ec of inputs.extraFieldConditions ?? [])
|
|
192
|
+
handleCond({ ...ec.cond, field: ec.path });
|
|
193
|
+
const fields = [...fieldConds.entries()].map(([path, conds]) => ({
|
|
194
|
+
path,
|
|
195
|
+
values: [...(0, field_1.fieldProbes)(conds), ABSENT],
|
|
196
|
+
}));
|
|
197
|
+
return {
|
|
198
|
+
verbs: [...verbLiterals, FRESH_VERB],
|
|
199
|
+
resources: [...resourceLiterals, FRESH_RESOURCE],
|
|
200
|
+
revClasses: inputs.varyReversibility ? [...types_1.REVERSIBILITY_ORDER] : [undefined],
|
|
201
|
+
fields,
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
function productSize(pr) {
|
|
205
|
+
let n = pr.verbs.length * pr.resources.length * pr.revClasses.length;
|
|
206
|
+
for (const f of pr.fields)
|
|
207
|
+
n *= f.values.length;
|
|
208
|
+
return n;
|
|
209
|
+
}
|
|
210
|
+
function* enumerateFields(fields, i, acc) {
|
|
211
|
+
if (i >= fields.length) {
|
|
212
|
+
yield new Map(acc);
|
|
213
|
+
return;
|
|
214
|
+
}
|
|
215
|
+
const f = fields[i];
|
|
216
|
+
if (f === undefined) {
|
|
217
|
+
yield new Map(acc);
|
|
218
|
+
return;
|
|
219
|
+
}
|
|
220
|
+
for (const v of f.values) {
|
|
221
|
+
if (v === ABSENT) {
|
|
222
|
+
acc.delete(f.path);
|
|
223
|
+
}
|
|
224
|
+
else {
|
|
225
|
+
acc.set(f.path, v);
|
|
226
|
+
}
|
|
227
|
+
yield* enumerateFields(fields, i + 1, acc);
|
|
228
|
+
}
|
|
229
|
+
acc.delete(f.path);
|
|
230
|
+
}
|
|
231
|
+
function* representatives(pr) {
|
|
232
|
+
for (const verb of pr.verbs) {
|
|
233
|
+
for (const resource of pr.resources) {
|
|
234
|
+
for (const revClass of pr.revClasses) {
|
|
235
|
+
for (const assignments of enumerateFields(pr.fields, 0, new Map())) {
|
|
236
|
+
yield { verb, resource, revClass, assignments };
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
function branch() {
|
|
243
|
+
return { leaf: false, children: new Map() };
|
|
244
|
+
}
|
|
245
|
+
function insert(root, segs, value) {
|
|
246
|
+
let node = root;
|
|
247
|
+
for (let i = 0; i < segs.length - 1; i++) {
|
|
248
|
+
const key = segs[i];
|
|
249
|
+
if (key === undefined || node.leaf)
|
|
250
|
+
return;
|
|
251
|
+
const next = node.children.get(key);
|
|
252
|
+
if (next === undefined || next.leaf) {
|
|
253
|
+
const created = branch();
|
|
254
|
+
node.children.set(key, created);
|
|
255
|
+
node = created;
|
|
256
|
+
}
|
|
257
|
+
else {
|
|
258
|
+
node = next;
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
const last = segs[segs.length - 1];
|
|
262
|
+
if (last !== undefined && !node.leaf)
|
|
263
|
+
node.children.set(last, { leaf: true, value });
|
|
264
|
+
}
|
|
265
|
+
function materialize(node) {
|
|
266
|
+
const out = {};
|
|
267
|
+
if (node.leaf)
|
|
268
|
+
return out;
|
|
269
|
+
for (const [key, child] of node.children) {
|
|
270
|
+
out[key] = child.leaf ? child.value : materialize(child);
|
|
271
|
+
}
|
|
272
|
+
return out;
|
|
273
|
+
}
|
|
274
|
+
function buildRoots(rep) {
|
|
275
|
+
const roots = new Map([
|
|
276
|
+
['action', branch()],
|
|
277
|
+
['subject', branch()],
|
|
278
|
+
['env', branch()],
|
|
279
|
+
]);
|
|
280
|
+
for (const [path, value] of rep.assignments) {
|
|
281
|
+
const segs = path.split('.');
|
|
282
|
+
const first = segs[0];
|
|
283
|
+
if (first === undefined || segs.length < 2)
|
|
284
|
+
continue;
|
|
285
|
+
const root = roots.get(first);
|
|
286
|
+
if (root === undefined)
|
|
287
|
+
continue; // only action / subject / env roots are addressable
|
|
288
|
+
insert(root, segs.slice(1), value);
|
|
289
|
+
}
|
|
290
|
+
const actionTree = roots.get('action');
|
|
291
|
+
const subjectTree = roots.get('subject');
|
|
292
|
+
const envTree = roots.get('env');
|
|
293
|
+
const action = { verb: rep.verb, resource: rep.resource, params: {} };
|
|
294
|
+
if (actionTree !== undefined && !actionTree.leaf) {
|
|
295
|
+
for (const [key, child] of actionTree.children) {
|
|
296
|
+
if (key === 'verb' || key === 'resource')
|
|
297
|
+
continue; // fixed from the rep
|
|
298
|
+
if (key === 'params') {
|
|
299
|
+
// `action.params.*` branches materialize to a typed record; a whole-`params` leaf is flagged
|
|
300
|
+
// undecidable upstream, so it is safe to ignore here (params stays an empty, present object).
|
|
301
|
+
if (!child.leaf)
|
|
302
|
+
action.params = materialize(child);
|
|
303
|
+
}
|
|
304
|
+
else {
|
|
305
|
+
action[key] = child.leaf ? child.value : materialize(child);
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
if (rep.revClass !== undefined)
|
|
310
|
+
action.reversibility_class = rep.revClass;
|
|
311
|
+
const subject = subjectTree === undefined || subjectTree.leaf ? {} : materialize(subjectTree);
|
|
312
|
+
const env = envTree === undefined || envTree.leaf ? {} : materialize(envTree);
|
|
313
|
+
return { action, subject, env };
|
|
314
|
+
}
|
|
315
|
+
function contextFromRoots(roots) {
|
|
316
|
+
return { action: roots.action, subject: roots.subject, env: roots.env };
|
|
317
|
+
}
|
|
318
|
+
function reversibilityOk(model, revClass) {
|
|
319
|
+
if (model.revCap === undefined)
|
|
320
|
+
return true;
|
|
321
|
+
if (revClass === undefined)
|
|
322
|
+
return false; // a reversibility_max caveat denies an action with no class
|
|
323
|
+
const idx = types_1.REVERSIBILITY_ORDER.indexOf(revClass);
|
|
324
|
+
return idx >= 0 && idx <= model.revCap;
|
|
325
|
+
}
|
|
326
|
+
/** Does the policy admit this representative action? (predicate match AND static caveat gate.) */
|
|
327
|
+
function admits(model, rep) {
|
|
328
|
+
if (!model.caveatSat)
|
|
329
|
+
return false;
|
|
330
|
+
if (!reversibilityOk(model, rep.revClass))
|
|
331
|
+
return false;
|
|
332
|
+
const ctx = contextFromRoots(buildRoots(rep));
|
|
333
|
+
return (0, pca_1.evaluatePredicates)(model.predicates, ctx).allowed;
|
|
334
|
+
}
|
|
335
|
+
/** Concrete admission of a fully-specified action (used by reachable's fast path; exact). */
|
|
336
|
+
function admitsAction(model, action) {
|
|
337
|
+
if (!model.caveatSat)
|
|
338
|
+
return false;
|
|
339
|
+
if (!reversibilityOk(model, action.reversibilityClass))
|
|
340
|
+
return false;
|
|
341
|
+
const act = { verb: action.verb, resource: action.resource, params: { ...(action.params ?? {}) } };
|
|
342
|
+
if (action.reversibilityClass !== undefined)
|
|
343
|
+
act.reversibility_class = action.reversibilityClass;
|
|
344
|
+
const ctx = {
|
|
345
|
+
action: act,
|
|
346
|
+
subject: { ...(action.subject ?? {}) },
|
|
347
|
+
env: { ...(action.env ?? {}) },
|
|
348
|
+
};
|
|
349
|
+
return (0, pca_1.evaluatePredicates)(model.predicates, ctx).allowed;
|
|
350
|
+
}
|
|
351
|
+
function repToAction(rep) {
|
|
352
|
+
const roots = buildRoots(rep);
|
|
353
|
+
const action = { verb: rep.verb, resource: rep.resource, params: { ...roots.action.params } };
|
|
354
|
+
if (rep.revClass !== undefined)
|
|
355
|
+
action.reversibilityClass = rep.revClass;
|
|
356
|
+
if (Object.keys(roots.subject).length > 0)
|
|
357
|
+
action.subject = roots.subject;
|
|
358
|
+
if (Object.keys(roots.env).length > 0)
|
|
359
|
+
action.env = roots.env;
|
|
360
|
+
return action;
|
|
361
|
+
}
|
|
362
|
+
/**
|
|
363
|
+
* Walk the representative grid, stopping at the first rep for which `predicate` is true. Returns
|
|
364
|
+
* `overflow: true` (without walking) when the grid is larger than `max`.
|
|
365
|
+
*/
|
|
366
|
+
function search(pr, predicate, max) {
|
|
367
|
+
if (productSize(pr) > max)
|
|
368
|
+
return { overflow: true };
|
|
369
|
+
for (const rep of representatives(pr)) {
|
|
370
|
+
if (predicate(rep))
|
|
371
|
+
return { hit: rep, overflow: false };
|
|
372
|
+
}
|
|
373
|
+
return { overflow: false };
|
|
374
|
+
}
|
|
375
|
+
//# sourceMappingURL=enumerate.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"enumerate.js","sourceRoot":"","sources":["../src/enumerate.ts"],"names":[],"mappings":";;;AAyCA,0CAIC;AA2CD,0BAWC;AA8BD,sCAyEC;AAED,kCAIC;AAqCD,0CAUC;AAwGD,wBAKC;AAGD,oCAWC;AAED,kCAOC;AAeD,wBAMC;AAxZD,wCAAuF;AAEvF,uCAA2C;AAC3C,mCAAsC;AACtC,mCAA8C;AAG9C;;;;;;;;;;;;GAYG;AAEH,MAAM,MAAM,GAAkB,MAAM,CAAC,uBAAuB,CAAC,CAAC;AAG9D,MAAM,UAAU,GAAG,wCAAwC,CAAC;AAC5D,MAAM,cAAc,GAAG,4CAA4C,CAAC;AAEpE,+EAA+E;AAClE,QAAA,wBAAwB,GAAG,OAAO,CAAC;AAahD,SAAgB,eAAe,CAAC,CAAc;IAC5C,MAAM,UAAU,GAAG,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,EAAE,CAAC;IACnE,MAAM,OAAO,GAAG,SAAS,IAAI,CAAC,IAAI,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC;IAC5E,OAAO,EAAE,UAAU,EAAE,OAAO,EAAE,CAAC;AACjC,CAAC;AAED;;;;;;;GAOG;AACH,SAAS,oBAAoB,CAAC,CAAY,EAAE,OAAoB;IAC9D,IAAI,CAAC,KAAK,IAAI,IAAI,OAAO,CAAC,KAAK,QAAQ;QAAE,OAAO;IAChD,IAAI,IAAA,sBAAgB,EAAC,CAAC,CAAC,EAAE,CAAC;QACxB,IAAI,QAAQ,IAAI,CAAC,IAAI,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC;YAAE,KAAK,MAAM,CAAC,IAAI,CAAC,CAAC,MAAM;gBAAE,oBAAoB,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC;aACpG,IAAI,QAAQ,IAAI,CAAC,IAAI,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC;YAAE,KAAK,MAAM,CAAC,IAAI,CAAC,CAAC,MAAM;gBAAE,oBAAoB,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC;aACzG,IAAI,KAAK,IAAI,CAAC;YAAE,oBAAoB,CAAC,CAAC,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;QAC1D,OAAO;IACT,CAAC;IACD,IAAI,CAAC,IAAA,qBAAe,EAAC,CAAC,CAAC;QAAE,OAAO;IAChC,IAAI,CAAC,CAAC,GAAG,KAAK,SAAS,EAAE,CAAC;QACxB,OAAO,CAAC,GAAG,CAAC,+EAA+E,CAAC,CAAC;IAC/F,CAAC;IACD,IAAI,CAAC,CAAC,KAAK,KAAK,eAAe,EAAE,CAAC;QAChC,OAAO,CAAC,GAAG,CAAC,2EAA2E,CAAC,CAAC;IAC3F,CAAC;IACD,IAAI,CAAC,CAAC,EAAE,KAAK,MAAM,IAAI,CAAC,CAAC,EAAE,KAAK,SAAS,IAAI,CAAC,CAAC,EAAE,KAAK,MAAM,IAAI,CAAC,CAAC,EAAE,KAAK,WAAW,EAAE,CAAC;QACrF,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,sEAAsE,CAAC,CAAC;IAC/F,CAAC;AACH,CAAC;AAED,SAAS,iBAAiB,CAAC,UAAgC;IACzD,MAAM,OAAO,GAAG,IAAI,GAAG,EAAU,CAAC;IAClC,KAAK,MAAM,CAAC,IAAI,UAAU,EAAE,CAAC;QAC3B,IAAI,CAAC,KAAK,IAAI,IAAI,OAAO,CAAC,KAAK,QAAQ;YAAE,SAAS;QAClD,MAAM,CAAC,GAAG,CAAC,CAAC,QAAQ,CAAC;QACrB,IAAI,OAAO,CAAC,KAAK,QAAQ,IAAI,CAAC,CAAC,UAAU,CAAC,KAAK,CAAC,EAAE,CAAC;YACjD,OAAO,CAAC,GAAG,CAAC,8EAA8E,CAAC,CAAC;QAC9F,CAAC;QACD,IAAI,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC;YAAE,KAAK,MAAM,CAAC,IAAI,CAAC,CAAC,KAAK;gBAAE,oBAAoB,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC;IACxF,CAAC;IACD,OAAO,CAAC,GAAG,OAAO,CAAC,CAAC;AACtB,CAAC;AAED,SAAgB,OAAO,CAAC,CAAc;IACpC,MAAM,EAAE,UAAU,EAAE,OAAO,EAAE,GAAG,eAAe,CAAC,CAAC,CAAC,CAAC;IACnD,MAAM,EAAE,GAAG,IAAA,wBAAc,EAAC,OAAO,CAAC,CAAC;IACnC,OAAO;QACL,UAAU;QACV,OAAO;QACP,SAAS,EAAE,EAAE,CAAC,GAAG;QACjB,YAAY,EAAE,EAAE,CAAC,MAAM;QACvB,MAAM,EAAE,EAAE,CAAC,MAAM;QACjB,WAAW,EAAE,iBAAiB,CAAC,UAAU,CAAC;KAC3C,CAAC;AACJ,CAAC;AAqBD,SAAS,eAAe,CAAC,CAAgB,EAAE,IAAiB;IAC1D,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC;IAClB,IAAI,CAAC,CAAC,EAAE,KAAK,IAAI,IAAI,CAAC,CAAC,EAAE,KAAK,IAAI,IAAI,CAAC,CAAC,EAAE,KAAK,QAAQ,IAAI,CAAC,CAAC,EAAE,KAAK,MAAM,IAAI,CAAC,CAAC,EAAE,KAAK,MAAM,EAAE,CAAC;QAC9F,IAAI,OAAO,CAAC,KAAK,QAAQ;YAAE,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;IACzC,CAAC;SAAM,IAAI,CAAC,CAAC,CAAC,EAAE,KAAK,IAAI,IAAI,CAAC,CAAC,EAAE,KAAK,KAAK,CAAC,IAAI,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC;QACjE,KAAK,MAAM,CAAC,IAAI,CAAC;YAAE,IAAI,OAAO,CAAC,KAAK,QAAQ;gBAAE,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;IAC5D,CAAC;AACH,CAAC;AAED,SAAgB,aAAa,CAAC,MAAmB;IAC/C,MAAM,YAAY,GAAG,IAAI,GAAG,CAAS,MAAM,CAAC,UAAU,IAAI,EAAE,CAAC,CAAC;IAC9D,MAAM,gBAAgB,GAAG,IAAI,GAAG,EAAU,CAAC;IAC3C,MAAM,UAAU,GAAG,IAAI,GAAG,EAA2B,CAAC;IAEtD,MAAM,YAAY,GAAG,CAAC,IAAY,EAAE,CAAgB,EAAQ,EAAE;QAC5D,MAAM,GAAG,GAAG,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;QACvC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QACZ,UAAU,CAAC,GAAG,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;IAC5B,CAAC,CAAC;IAEF,MAAM,gBAAgB,GAAG,CAAC,CAAqB,EAAQ,EAAE;QACvD,IAAI,CAAC,KAAK,SAAS,IAAI,CAAC,KAAK,GAAG;YAAE,OAAO,CAAC,0CAA0C;QACpF,IAAI,CAAC,CAAC,UAAU,CAAC,KAAK,CAAC;YAAE,OAAO,CAAC,+DAA+D;QAChG,IAAI,CAAC,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;YACpB,MAAM,IAAI,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;YAC5B,gBAAgB,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;YAC3B,gBAAgB,CAAC,GAAG,CAAC,IAAI,GAAG,GAAG,CAAC,CAAC;YACjC,OAAO;QACT,CAAC;QACD,gBAAgB,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;IAC1B,CAAC,CAAC;IAEF,MAAM,UAAU,GAAG,CAAC,CAAY,EAAQ,EAAE;QACxC,IAAI,CAAC,KAAK,IAAI,IAAI,OAAO,CAAC,KAAK,QAAQ;YAAE,OAAO;QAChD,+FAA+F;QAC/F,4CAA4C;QAC5C,IAAI,IAAA,sBAAgB,EAAC,CAAC,CAAC,EAAE,CAAC;YACxB,IAAI,QAAQ,IAAI,CAAC,IAAI,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC;gBAAE,KAAK,MAAM,CAAC,IAAI,CAAC,CAAC,MAAM;oBAAE,UAAU,CAAC,CAAC,CAAC,CAAC;iBACjF,IAAI,QAAQ,IAAI,CAAC,IAAI,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC;gBAAE,KAAK,MAAM,CAAC,IAAI,CAAC,CAAC,MAAM;oBAAE,UAAU,CAAC,CAAC,CAAC,CAAC;iBACtF,IAAI,KAAK,IAAI,CAAC;gBAAE,UAAU,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;YACvC,OAAO;QACT,CAAC;QACD,IAAI,CAAC,IAAA,qBAAe,EAAC,CAAC,CAAC;YAAE,OAAO;QAChC,IAAI,CAAC,CAAC,KAAK,KAAK,aAAa,EAAE,CAAC;YAC9B,eAAe,CAAC,CAAC,EAAE,YAAY,CAAC,CAAC;YACjC,OAAO;QACT,CAAC;QACD,IAAI,CAAC,CAAC,KAAK,KAAK,iBAAiB,EAAE,CAAC;YAClC,IAAI,CAAC,CAAC,EAAE,KAAK,QAAQ,IAAI,OAAO,CAAC,CAAC,KAAK,KAAK,QAAQ;gBAAE,gBAAgB,CAAC,CAAC,CAAC,KAAK,GAAG,GAAG,CAAC,CAAC;iBACjF,IAAI,OAAO,CAAC,CAAC,KAAK,KAAK,QAAQ;gBAAE,gBAAgB,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC;iBAC3D,IAAI,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC;gBAAE,KAAK,MAAM,CAAC,IAAI,CAAC,CAAC,KAAK;oBAAE,IAAI,OAAO,CAAC,KAAK,QAAQ;wBAAE,gBAAgB,CAAC,CAAC,CAAC,CAAC;YACzG,OAAO;QACT,CAAC;QACD,YAAY,CAAC,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC;IAC3B,CAAC,CAAC;IAEF,KAAK,MAAM,IAAI,IAAI,MAAM,CAAC,aAAa,EAAE,CAAC;QACxC,KAAK,MAAM,CAAC,IAAI,IAAI,EAAE,CAAC;YACrB,IAAI,CAAC,KAAK,IAAI,IAAI,OAAO,CAAC,KAAK,QAAQ;gBAAE,SAAS;YAClD,IAAI,OAAO,CAAC,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;gBAC/B,IAAI,CAAC,CAAC,IAAI,KAAK,GAAG;oBAAE,YAAY,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;YAC/C,CAAC;iBAAM,IAAI,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC;gBACjC,KAAK,MAAM,CAAC,IAAI,CAAC,CAAC,IAAI;oBAAE,IAAI,OAAO,CAAC,KAAK,QAAQ,IAAI,CAAC,KAAK,GAAG;wBAAE,YAAY,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;YACtF,CAAC;YACD,gBAAgB,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC;YAC7B,IAAI,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC;gBAAE,KAAK,MAAM,CAAC,IAAI,CAAC,CAAC,KAAK;oBAAE,UAAU,CAAC,CAAC,CAAC,CAAC;QACrE,CAAC;IACH,CAAC;IACD,KAAK,MAAM,CAAC,IAAI,MAAM,CAAC,cAAc,IAAI,EAAE;QAAE,gBAAgB,CAAC,CAAC,CAAC,CAAC;IACjE,KAAK,MAAM,EAAE,IAAI,MAAM,CAAC,oBAAoB,IAAI,EAAE;QAAE,UAAU,CAAC,EAAE,GAAG,EAAE,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC,IAAI,EAAE,CAAC,CAAC;IAE/F,MAAM,MAAM,GAAG,CAAC,GAAG,UAAU,CAAC,OAAO,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,KAAK,CAAC,EAAE,EAAE,CAAC,CAAC;QAC/D,IAAI;QACJ,MAAM,EAAE,CAAC,GAAG,IAAA,mBAAW,EAAC,KAAK,CAAC,EAAE,MAAM,CAAW;KAClD,CAAC,CAAC,CAAC;IAEJ,OAAO;QACL,KAAK,EAAE,CAAC,GAAG,YAAY,EAAE,UAAU,CAAC;QACpC,SAAS,EAAE,CAAC,GAAG,gBAAgB,EAAE,cAAc,CAAC;QAChD,UAAU,EAAE,MAAM,CAAC,iBAAiB,CAAC,CAAC,CAAC,CAAC,GAAG,2BAAmB,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;QAC7E,MAAM;KACP,CAAC;AACJ,CAAC;AAED,SAAgB,WAAW,CAAC,EAAU;IACpC,IAAI,CAAC,GAAG,EAAE,CAAC,KAAK,CAAC,MAAM,GAAG,EAAE,CAAC,SAAS,CAAC,MAAM,GAAG,EAAE,CAAC,UAAU,CAAC,MAAM,CAAC;IACrE,KAAK,MAAM,CAAC,IAAI,EAAE,CAAC,MAAM;QAAE,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC;IAChD,OAAO,CAAC,CAAC;AACX,CAAC;AAYD,QAAQ,CAAC,CAAC,eAAe,CACvB,MAAwB,EACxB,CAAS,EACT,GAAyB;IAEzB,IAAI,CAAC,IAAI,MAAM,CAAC,MAAM,EAAE,CAAC;QACvB,MAAM,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC;QACnB,OAAO;IACT,CAAC;IACD,MAAM,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC;IACpB,IAAI,CAAC,KAAK,SAAS,EAAE,CAAC;QACpB,MAAM,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC;QACnB,OAAO;IACT,CAAC;IACD,KAAK,MAAM,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,CAAC;QACzB,IAAI,CAAC,KAAK,MAAM,EAAE,CAAC;YACjB,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;QACrB,CAAC;aAAM,CAAC;YACN,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;QACrB,CAAC;QACD,KAAK,CAAC,CAAC,eAAe,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,EAAE,GAAG,CAAC,CAAC;IAC7C,CAAC;IACD,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;AACrB,CAAC;AAED,QAAe,CAAC,CAAC,eAAe,CAAC,EAAU;IACzC,KAAK,MAAM,IAAI,IAAI,EAAE,CAAC,KAAK,EAAE,CAAC;QAC5B,KAAK,MAAM,QAAQ,IAAI,EAAE,CAAC,SAAS,EAAE,CAAC;YACpC,KAAK,MAAM,QAAQ,IAAI,EAAE,CAAC,UAAU,EAAE,CAAC;gBACrC,KAAK,MAAM,WAAW,IAAI,eAAe,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,EAAE,IAAI,GAAG,EAAE,CAAC,EAAE,CAAC;oBACnE,MAAM,EAAE,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE,WAAW,EAAE,CAAC;gBAClD,CAAC;YACH,CAAC;QACH,CAAC;IACH,CAAC;AACH,CAAC;AAoBD,SAAS,MAAM;IACb,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,QAAQ,EAAE,IAAI,GAAG,EAAE,EAAE,CAAC;AAC9C,CAAC;AAED,SAAS,MAAM,CAAC,IAAc,EAAE,IAAuB,EAAE,KAAc;IACrE,IAAI,IAAI,GAAG,IAAI,CAAC;IAChB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;QACzC,MAAM,GAAG,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;QACpB,IAAI,GAAG,KAAK,SAAS,IAAI,IAAI,CAAC,IAAI;YAAE,OAAO;QAC3C,MAAM,IAAI,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QACpC,IAAI,IAAI,KAAK,SAAS,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC;YACpC,MAAM,OAAO,GAAG,MAAM,EAAE,CAAC;YACzB,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;YAChC,IAAI,GAAG,OAAO,CAAC;QACjB,CAAC;aAAM,CAAC;YACN,IAAI,GAAG,IAAI,CAAC;QACd,CAAC;IACH,CAAC;IACD,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;IACnC,IAAI,IAAI,KAAK,SAAS,IAAI,CAAC,IAAI,CAAC,IAAI;QAAE,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;AACvF,CAAC;AAED,SAAS,WAAW,CAAC,IAAc;IACjC,MAAM,GAAG,GAA4B,EAAE,CAAC;IACxC,IAAI,IAAI,CAAC,IAAI;QAAE,OAAO,GAAG,CAAC;IAC1B,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;QACzC,GAAG,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC;IAC3D,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED,SAAS,UAAU,CAAC,GAAQ;IAC1B,MAAM,KAAK,GAAG,IAAI,GAAG,CAAmB;QACtC,CAAC,QAAQ,EAAE,MAAM,EAAE,CAAC;QACpB,CAAC,SAAS,EAAE,MAAM,EAAE,CAAC;QACrB,CAAC,KAAK,EAAE,MAAM,EAAE,CAAC;KAClB,CAAC,CAAC;IACH,KAAK,MAAM,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,GAAG,CAAC,WAAW,EAAE,CAAC;QAC5C,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QAC7B,MAAM,KAAK,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;QACtB,IAAI,KAAK,KAAK,SAAS,IAAI,IAAI,CAAC,MAAM,GAAG,CAAC;YAAE,SAAS;QACrD,MAAM,IAAI,GAAG,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;QAC9B,IAAI,IAAI,KAAK,SAAS;YAAE,SAAS,CAAC,oDAAoD;QACtF,MAAM,CAAC,IAAI,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC;IACrC,CAAC;IACD,MAAM,UAAU,GAAG,KAAK,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;IACvC,MAAM,WAAW,GAAG,KAAK,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;IACzC,MAAM,OAAO,GAAG,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;IAEjC,MAAM,MAAM,GAAkB,EAAE,IAAI,EAAE,GAAG,CAAC,IAAI,EAAE,QAAQ,EAAE,GAAG,CAAC,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC;IACrF,IAAI,UAAU,KAAK,SAAS,IAAI,CAAC,UAAU,CAAC,IAAI,EAAE,CAAC;QACjD,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,UAAU,CAAC,QAAQ,EAAE,CAAC;YAC/C,IAAI,GAAG,KAAK,MAAM,IAAI,GAAG,KAAK,UAAU;gBAAE,SAAS,CAAC,qBAAqB;YACzE,IAAI,GAAG,KAAK,QAAQ,EAAE,CAAC;gBACrB,6FAA6F;gBAC7F,8FAA8F;gBAC9F,IAAI,CAAC,KAAK,CAAC,IAAI;oBAAE,MAAM,CAAC,MAAM,GAAG,WAAW,CAAC,KAAK,CAAC,CAAC;YACtD,CAAC;iBAAM,CAAC;gBACN,MAAM,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC;YAC9D,CAAC;QACH,CAAC;IACH,CAAC;IACD,IAAI,GAAG,CAAC,QAAQ,KAAK,SAAS;QAAE,MAAM,CAAC,mBAAmB,GAAG,GAAG,CAAC,QAAQ,CAAC;IAC1E,MAAM,OAAO,GAAG,WAAW,KAAK,SAAS,IAAI,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,WAAW,CAAC,WAAW,CAAC,CAAC;IAC9F,MAAM,GAAG,GAAG,OAAO,KAAK,SAAS,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC;IAC9E,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,GAAG,EAAE,CAAC;AAClC,CAAC;AAED,SAAS,gBAAgB,CAAC,KAIzB;IACC,OAAO,EAAE,MAAM,EAAE,KAAK,CAAC,MAAM,EAAE,OAAO,EAAE,KAAK,CAAC,OAAO,EAAE,GAAG,EAAE,KAAK,CAAC,GAAG,EAAE,CAAC;AAC1E,CAAC;AAED,SAAS,eAAe,CAAC,KAAkB,EAAE,QAA4B;IACvE,IAAI,KAAK,CAAC,MAAM,KAAK,SAAS;QAAE,OAAO,IAAI,CAAC;IAC5C,IAAI,QAAQ,KAAK,SAAS;QAAE,OAAO,KAAK,CAAC,CAAC,4DAA4D;IACtG,MAAM,GAAG,GAAI,2BAAyC,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;IACzE,OAAO,GAAG,IAAI,CAAC,IAAI,GAAG,IAAI,KAAK,CAAC,MAAM,CAAC;AACzC,CAAC;AAED,kGAAkG;AAClG,SAAgB,MAAM,CAAC,KAAkB,EAAE,GAAQ;IACjD,IAAI,CAAC,KAAK,CAAC,SAAS;QAAE,OAAO,KAAK,CAAC;IACnC,IAAI,CAAC,eAAe,CAAC,KAAK,EAAE,GAAG,CAAC,QAAQ,CAAC;QAAE,OAAO,KAAK,CAAC;IACxD,MAAM,GAAG,GAAG,gBAAgB,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC;IAC9C,OAAO,IAAA,wBAAkB,EAAC,KAAK,CAAC,UAAU,EAAE,GAAG,CAAC,CAAC,OAAO,CAAC;AAC3D,CAAC;AAED,6FAA6F;AAC7F,SAAgB,YAAY,CAAC,KAAkB,EAAE,MAAsB;IACrE,IAAI,CAAC,KAAK,CAAC,SAAS;QAAE,OAAO,KAAK,CAAC;IACnC,IAAI,CAAC,eAAe,CAAC,KAAK,EAAE,MAAM,CAAC,kBAAkB,CAAC;QAAE,OAAO,KAAK,CAAC;IACrE,MAAM,GAAG,GAAkB,EAAE,IAAI,EAAE,MAAM,CAAC,IAAI,EAAE,QAAQ,EAAE,MAAM,CAAC,QAAQ,EAAE,MAAM,EAAE,EAAE,GAAG,CAAC,MAAM,CAAC,MAAM,IAAI,EAAE,CAAC,EAAE,EAAE,CAAC;IAClH,IAAI,MAAM,CAAC,kBAAkB,KAAK,SAAS;QAAE,GAAG,CAAC,mBAAmB,GAAG,MAAM,CAAC,kBAAkB,CAAC;IACjG,MAAM,GAAG,GAAkB;QACzB,MAAM,EAAE,GAAG;QACX,OAAO,EAAE,EAAE,GAAG,CAAC,MAAM,CAAC,OAAO,IAAI,EAAE,CAAC,EAAE;QACtC,GAAG,EAAE,EAAE,GAAG,CAAC,MAAM,CAAC,GAAG,IAAI,EAAE,CAAC,EAAE;KAC/B,CAAC;IACF,OAAO,IAAA,wBAAkB,EAAC,KAAK,CAAC,UAAU,EAAE,GAAG,CAAC,CAAC,OAAO,CAAC;AAC3D,CAAC;AAED,SAAgB,WAAW,CAAC,GAAQ;IAClC,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,CAAC,CAAC;IAC9B,MAAM,MAAM,GAAmB,EAAE,IAAI,EAAE,GAAG,CAAC,IAAI,EAAE,QAAQ,EAAE,GAAG,CAAC,QAAQ,EAAE,MAAM,EAAE,EAAE,GAAG,KAAK,CAAC,MAAM,CAAC,MAAM,EAAE,EAAE,CAAC;IAC9G,IAAI,GAAG,CAAC,QAAQ,KAAK,SAAS;QAAE,MAAM,CAAC,kBAAkB,GAAG,GAAG,CAAC,QAAQ,CAAC;IACzE,IAAI,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,MAAM,GAAG,CAAC;QAAE,MAAM,CAAC,OAAO,GAAG,KAAK,CAAC,OAAO,CAAC;IAC1E,IAAI,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,MAAM,GAAG,CAAC;QAAE,MAAM,CAAC,GAAG,GAAG,KAAK,CAAC,GAAG,CAAC;IAC9D,OAAO,MAAM,CAAC;AAChB,CAAC;AAWD;;;GAGG;AACH,SAAgB,MAAM,CAAC,EAAU,EAAE,SAAgC,EAAE,GAAW;IAC9E,IAAI,WAAW,CAAC,EAAE,CAAC,GAAG,GAAG;QAAE,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;IACrD,KAAK,MAAM,GAAG,IAAI,eAAe,CAAC,EAAE,CAAC,EAAE,CAAC;QACtC,IAAI,SAAS,CAAC,GAAG,CAAC;YAAE,OAAO,EAAE,GAAG,EAAE,GAAG,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAC;IAC3D,CAAC;IACD,OAAO,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAC;AAC7B,CAAC"}
|
package/dist/field.d.ts
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { type Condition, type ConditionOp } from '@atlasauth/pca';
|
|
2
|
+
/**
|
|
3
|
+
* The interval + finite-set engine for a single `where` field.
|
|
4
|
+
*
|
|
5
|
+
* PCA's `where` conditions over one field are a conjunction of threshold tests (lt/lte/gt/gte/eq on
|
|
6
|
+
* numbers), membership tests (eq/ne/in/nin over finite values) and string-prefix tests. Any such
|
|
7
|
+
* conjunction partitions the value line into finitely many CELLS on which every condition is constant,
|
|
8
|
+
* bounded by the literals that appear. Picking one representative value per cell is therefore a SOUND
|
|
9
|
+
* AND COMPLETE probe set for the decided fragment: two policies agree on all actions iff they agree on
|
|
10
|
+
* all actions drawn from these representatives (the finite-distinguishing-value property that makes the
|
|
11
|
+
* relational queries decidable by enumeration — see enumerate.ts).
|
|
12
|
+
*
|
|
13
|
+
* `ref` conditions (operand is another field) are NOT a single-field constraint; they are flagged
|
|
14
|
+
* upstream as undecidable and skipped here.
|
|
15
|
+
*/
|
|
16
|
+
/** A sentinel string guaranteed not to equal, be a prefix of, or be prefixed by any policy literal. */
|
|
17
|
+
export declare const FRESH_STRING = "\0atlas-analyzer::fresh-unmatched\0";
|
|
18
|
+
/**
|
|
19
|
+
* Compute the representative probe values for one field from the conjunction of `where` conditions on
|
|
20
|
+
* it. The result is finite and distinguishing for the decided fragment. The caller adds an `absent`
|
|
21
|
+
* representative separately (to probe `exists` and the params-absent fail-closed semantics).
|
|
22
|
+
*/
|
|
23
|
+
export declare function fieldProbes(conditions: readonly Condition[]): unknown[];
|
|
24
|
+
export interface NumInterval {
|
|
25
|
+
lo: number;
|
|
26
|
+
loInclusive: boolean;
|
|
27
|
+
hi: number;
|
|
28
|
+
hiInclusive: boolean;
|
|
29
|
+
}
|
|
30
|
+
export declare const FULL_INTERVAL: NumInterval;
|
|
31
|
+
/** Tighten an interval by one numeric condition. Returns the same interval for non-numeric operands. */
|
|
32
|
+
export declare function tightenInterval(iv: NumInterval, op: ConditionOp, value: unknown): NumInterval;
|
|
33
|
+
/** An interval is empty iff lo > hi, or lo == hi with either bound exclusive. */
|
|
34
|
+
export declare function intervalEmpty(iv: NumInterval): boolean;
|
|
35
|
+
/**
|
|
36
|
+
* Decide whether the numeric subset of a field's `where` conjunction is satisfiable. Detects, e.g.,
|
|
37
|
+
* `amount >= 100 AND amount <= 10` as unsatisfiable (empty interval). Conditions with non-numeric or
|
|
38
|
+
* `ref` operands, and boolean groupings (all_of/any_of/not), do not participate (they are decided by
|
|
39
|
+
* the finite-set / enumeration path); ignoring them only ever widens the interval, so a satisfiable
|
|
40
|
+
* verdict here is sound as a necessary condition.
|
|
41
|
+
*/
|
|
42
|
+
export declare function numericConjunctionSatisfiable(conditions: readonly Condition[]): boolean;
|
package/dist/field.js
ADDED
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.FULL_INTERVAL = exports.FRESH_STRING = void 0;
|
|
4
|
+
exports.fieldProbes = fieldProbes;
|
|
5
|
+
exports.tightenInterval = tightenInterval;
|
|
6
|
+
exports.intervalEmpty = intervalEmpty;
|
|
7
|
+
exports.numericConjunctionSatisfiable = numericConjunctionSatisfiable;
|
|
8
|
+
const pca_1 = require("@atlasauth/pca");
|
|
9
|
+
/**
|
|
10
|
+
* The interval + finite-set engine for a single `where` field.
|
|
11
|
+
*
|
|
12
|
+
* PCA's `where` conditions over one field are a conjunction of threshold tests (lt/lte/gt/gte/eq on
|
|
13
|
+
* numbers), membership tests (eq/ne/in/nin over finite values) and string-prefix tests. Any such
|
|
14
|
+
* conjunction partitions the value line into finitely many CELLS on which every condition is constant,
|
|
15
|
+
* bounded by the literals that appear. Picking one representative value per cell is therefore a SOUND
|
|
16
|
+
* AND COMPLETE probe set for the decided fragment: two policies agree on all actions iff they agree on
|
|
17
|
+
* all actions drawn from these representatives (the finite-distinguishing-value property that makes the
|
|
18
|
+
* relational queries decidable by enumeration — see enumerate.ts).
|
|
19
|
+
*
|
|
20
|
+
* `ref` conditions (operand is another field) are NOT a single-field constraint; they are flagged
|
|
21
|
+
* upstream as undecidable and skipped here.
|
|
22
|
+
*/
|
|
23
|
+
/** A sentinel string guaranteed not to equal, be a prefix of, or be prefixed by any policy literal. */
|
|
24
|
+
exports.FRESH_STRING = '\u0000atlas-analyzer::fresh-unmatched\u0000';
|
|
25
|
+
function isFiniteNumber(v) {
|
|
26
|
+
return typeof v === 'number' && Number.isFinite(v);
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Representatives of a numeric threshold arrangement: below the min, each threshold exactly, the open
|
|
30
|
+
* interval between each pair of consecutive thresholds (its midpoint), and above the max. This is the
|
|
31
|
+
* minimal set on which every `x {lt,lte,gt,gte,eq} t` built from these thresholds is constant per cell.
|
|
32
|
+
*/
|
|
33
|
+
function numericCellReps(thresholds) {
|
|
34
|
+
const sorted = [...new Set(thresholds)].filter(Number.isFinite).sort((a, b) => a - b);
|
|
35
|
+
if (sorted.length === 0)
|
|
36
|
+
return [];
|
|
37
|
+
const first = sorted[0];
|
|
38
|
+
const last = sorted[sorted.length - 1];
|
|
39
|
+
if (first === undefined || last === undefined)
|
|
40
|
+
return [];
|
|
41
|
+
const out = [first - 1];
|
|
42
|
+
for (let i = 0; i < sorted.length; i++) {
|
|
43
|
+
const ti = sorted[i];
|
|
44
|
+
if (ti === undefined)
|
|
45
|
+
continue;
|
|
46
|
+
out.push(ti);
|
|
47
|
+
const tn = sorted[i + 1];
|
|
48
|
+
if (tn !== undefined)
|
|
49
|
+
out.push((ti + tn) / 2);
|
|
50
|
+
}
|
|
51
|
+
out.push(last + 1);
|
|
52
|
+
return out;
|
|
53
|
+
}
|
|
54
|
+
/** A stable key for de-duplicating representative values of mixed type. */
|
|
55
|
+
function valueKey(x) {
|
|
56
|
+
if (x === exports.FRESH_STRING)
|
|
57
|
+
return 's:__fresh__';
|
|
58
|
+
try {
|
|
59
|
+
return typeof x + ':' + JSON.stringify(x);
|
|
60
|
+
}
|
|
61
|
+
catch {
|
|
62
|
+
return typeof x + ':<unserializable>';
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Compute the representative probe values for one field from the conjunction of `where` conditions on
|
|
67
|
+
* it. The result is finite and distinguishing for the decided fragment. The caller adds an `absent`
|
|
68
|
+
* representative separately (to probe `exists` and the params-absent fail-closed semantics).
|
|
69
|
+
*/
|
|
70
|
+
function fieldProbes(conditions) {
|
|
71
|
+
const literals = [];
|
|
72
|
+
const thresholds = [];
|
|
73
|
+
const prefixes = [];
|
|
74
|
+
let hasNumericOp = false;
|
|
75
|
+
for (const c of conditions) {
|
|
76
|
+
// Group conditions (all_of/any_of/not) are flattened to their leaves upstream by `collectProbes`;
|
|
77
|
+
// a cross-field `ref` relation is undecidable and also handled upstream. Skip either here.
|
|
78
|
+
if (!(0, pca_1.isLeafCondition)(c) || c.ref !== undefined)
|
|
79
|
+
continue;
|
|
80
|
+
const v = c.value;
|
|
81
|
+
switch (c.op) {
|
|
82
|
+
case 'eq':
|
|
83
|
+
case 'ne':
|
|
84
|
+
literals.push(v);
|
|
85
|
+
if (isFiniteNumber(v)) {
|
|
86
|
+
thresholds.push(v);
|
|
87
|
+
hasNumericOp = true;
|
|
88
|
+
}
|
|
89
|
+
break;
|
|
90
|
+
case 'in':
|
|
91
|
+
case 'nin':
|
|
92
|
+
if (Array.isArray(v)) {
|
|
93
|
+
for (const x of v) {
|
|
94
|
+
literals.push(x);
|
|
95
|
+
if (isFiniteNumber(x)) {
|
|
96
|
+
thresholds.push(x);
|
|
97
|
+
hasNumericOp = true;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
break;
|
|
102
|
+
case 'lt':
|
|
103
|
+
case 'lte':
|
|
104
|
+
case 'gt':
|
|
105
|
+
case 'gte':
|
|
106
|
+
if (isFiniteNumber(v)) {
|
|
107
|
+
thresholds.push(v);
|
|
108
|
+
hasNumericOp = true;
|
|
109
|
+
}
|
|
110
|
+
break;
|
|
111
|
+
case 'prefix':
|
|
112
|
+
case 'like':
|
|
113
|
+
case 'matches':
|
|
114
|
+
case 'is_a':
|
|
115
|
+
// The literal and a strictly-extending variant give the enumerator something to hit; the op
|
|
116
|
+
// itself is flagged undecidable, so the verdict fails safe regardless.
|
|
117
|
+
if (typeof v === 'string') {
|
|
118
|
+
literals.push(v);
|
|
119
|
+
if (c.op === 'prefix')
|
|
120
|
+
prefixes.push(v);
|
|
121
|
+
}
|
|
122
|
+
break;
|
|
123
|
+
case 'member_of':
|
|
124
|
+
if (typeof v === 'string')
|
|
125
|
+
literals.push(v);
|
|
126
|
+
else if (Array.isArray(v))
|
|
127
|
+
for (const x of v)
|
|
128
|
+
if (typeof x === 'string')
|
|
129
|
+
literals.push(x);
|
|
130
|
+
break;
|
|
131
|
+
case 'exists':
|
|
132
|
+
default:
|
|
133
|
+
break;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
const out = [];
|
|
137
|
+
const seen = new Set();
|
|
138
|
+
const add = (x) => {
|
|
139
|
+
const k = valueKey(x);
|
|
140
|
+
if (!seen.has(k)) {
|
|
141
|
+
seen.add(k);
|
|
142
|
+
out.push(x);
|
|
143
|
+
}
|
|
144
|
+
};
|
|
145
|
+
for (const l of literals)
|
|
146
|
+
add(l);
|
|
147
|
+
for (const n of numericCellReps(thresholds))
|
|
148
|
+
add(n);
|
|
149
|
+
for (const p of prefixes) {
|
|
150
|
+
add(p);
|
|
151
|
+
add(p + 'x'); // a value strictly extending the prefix (distinguishes prefix from exact)
|
|
152
|
+
}
|
|
153
|
+
add(exports.FRESH_STRING); // a value matching no eq/in/prefix literal
|
|
154
|
+
if (hasNumericOp && thresholds.length > 0) {
|
|
155
|
+
const mx = Math.max(...thresholds);
|
|
156
|
+
if (Number.isFinite(mx))
|
|
157
|
+
add(mx + 1); // a value above every numeric threshold
|
|
158
|
+
}
|
|
159
|
+
return out;
|
|
160
|
+
}
|
|
161
|
+
exports.FULL_INTERVAL = { lo: -Infinity, loInclusive: false, hi: Infinity, hiInclusive: false };
|
|
162
|
+
/** Tighten an interval by one numeric condition. Returns the same interval for non-numeric operands. */
|
|
163
|
+
function tightenInterval(iv, op, value) {
|
|
164
|
+
if (!isFiniteNumber(value))
|
|
165
|
+
return iv;
|
|
166
|
+
const next = { ...iv };
|
|
167
|
+
switch (op) {
|
|
168
|
+
case 'eq':
|
|
169
|
+
if (value > next.lo || (value === next.lo && !next.loInclusive)) {
|
|
170
|
+
next.lo = value;
|
|
171
|
+
next.loInclusive = true;
|
|
172
|
+
}
|
|
173
|
+
if (value < next.hi || (value === next.hi && !next.hiInclusive)) {
|
|
174
|
+
next.hi = value;
|
|
175
|
+
next.hiInclusive = true;
|
|
176
|
+
}
|
|
177
|
+
break;
|
|
178
|
+
case 'gte':
|
|
179
|
+
// A stricter existing lower bound (gt at the same value) is kept; only a higher value binds.
|
|
180
|
+
if (value > next.lo) {
|
|
181
|
+
next.lo = value;
|
|
182
|
+
next.loInclusive = true;
|
|
183
|
+
}
|
|
184
|
+
break;
|
|
185
|
+
case 'gt':
|
|
186
|
+
if (value > next.lo || (value === next.lo && next.loInclusive)) {
|
|
187
|
+
next.lo = value;
|
|
188
|
+
next.loInclusive = false;
|
|
189
|
+
}
|
|
190
|
+
break;
|
|
191
|
+
case 'lte':
|
|
192
|
+
if (value < next.hi) {
|
|
193
|
+
next.hi = value;
|
|
194
|
+
next.hiInclusive = true;
|
|
195
|
+
}
|
|
196
|
+
break;
|
|
197
|
+
case 'lt':
|
|
198
|
+
if (value < next.hi || (value === next.hi && next.hiInclusive)) {
|
|
199
|
+
next.hi = value;
|
|
200
|
+
next.hiInclusive = false;
|
|
201
|
+
}
|
|
202
|
+
break;
|
|
203
|
+
default:
|
|
204
|
+
break;
|
|
205
|
+
}
|
|
206
|
+
return next;
|
|
207
|
+
}
|
|
208
|
+
/** An interval is empty iff lo > hi, or lo == hi with either bound exclusive. */
|
|
209
|
+
function intervalEmpty(iv) {
|
|
210
|
+
if (iv.lo > iv.hi)
|
|
211
|
+
return true;
|
|
212
|
+
if (iv.lo === iv.hi)
|
|
213
|
+
return !(iv.loInclusive && iv.hiInclusive);
|
|
214
|
+
return false;
|
|
215
|
+
}
|
|
216
|
+
/**
|
|
217
|
+
* Decide whether the numeric subset of a field's `where` conjunction is satisfiable. Detects, e.g.,
|
|
218
|
+
* `amount >= 100 AND amount <= 10` as unsatisfiable (empty interval). Conditions with non-numeric or
|
|
219
|
+
* `ref` operands, and boolean groupings (all_of/any_of/not), do not participate (they are decided by
|
|
220
|
+
* the finite-set / enumeration path); ignoring them only ever widens the interval, so a satisfiable
|
|
221
|
+
* verdict here is sound as a necessary condition.
|
|
222
|
+
*/
|
|
223
|
+
function numericConjunctionSatisfiable(conditions) {
|
|
224
|
+
let iv = exports.FULL_INTERVAL;
|
|
225
|
+
for (const c of conditions) {
|
|
226
|
+
if (!(0, pca_1.isLeafCondition)(c) || c.ref !== undefined)
|
|
227
|
+
continue;
|
|
228
|
+
iv = tightenInterval(iv, c.op, c.value);
|
|
229
|
+
}
|
|
230
|
+
return !intervalEmpty(iv);
|
|
231
|
+
}
|
|
232
|
+
//# sourceMappingURL=field.js.map
|