@nmnmcc/ability 2.0.4 → 2.0.5
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/README.md +7 -7
- package/dist/Condition.d.ts +1 -1
- package/dist/internal/algebra.js +61 -8
- package/dist/internal/algebra.js.map +1 -1
- package/dist/internal/bind.js +8 -4
- package/dist/internal/bind.js.map +1 -1
- package/dist/internal/catalog.js +1 -1
- package/dist/internal/rule.d.ts.map +1 -1
- package/dist/internal/rule.js +6 -6
- package/dist/internal/rule.js.map +1 -1
- package/dist/internal/search.d.ts.map +1 -1
- package/dist/internal/search.js +51 -34
- package/dist/internal/search.js.map +1 -1
- package/package.json +1 -1
- package/src/internal/algebra.ts +80 -19
- package/src/internal/bind.ts +24 -7
- package/src/internal/rule.ts +13 -9
- package/src/internal/search.ts +78 -75
package/README.md
CHANGED
|
@@ -211,13 +211,13 @@ resource of the rule. Did you mean "title"?
|
|
|
211
211
|
|
|
212
212
|
A condition reads only the data fields of a value: an own field that a loop over
|
|
213
213
|
the keys sees. A rule that names a getter of a class is a `PolicyError`. Use a
|
|
214
|
-
predicate for a value that comes from code. A condition may not read a field
|
|
215
|
-
below a value that may not be plain data: a
|
|
216
|
-
the field, a list, a date, or a value of
|
|
217
|
-
`PolicyError` too. A struct without the field
|
|
218
|
-
copies it into a plain object, so the field reads
|
|
219
|
-
fields that the simple form of its condition
|
|
220
|
-
`x AND False`, it does not read `x`.
|
|
214
|
+
predicate for a value that comes from code. A condition may not read a field, of
|
|
215
|
+
a resource or of the principal, below a value that may not be plain data: a
|
|
216
|
+
class in a union that does not have the field, a list, a date, or a value of
|
|
217
|
+
`Schema.Unknown`. Such a rule is a `PolicyError` too. A struct without the field
|
|
218
|
+
is fine: a decode or a `make` copies it into a plain object, so the field reads
|
|
219
|
+
`null`. A test reads only the fields that the simple form of its condition
|
|
220
|
+
needs, as the residual does: in `x AND False`, it does not read `x`.
|
|
221
221
|
|
|
222
222
|
## Conditions
|
|
223
223
|
|
package/dist/Condition.d.ts
CHANGED
|
@@ -5,9 +5,9 @@ import * as Context from "effect/Context";
|
|
|
5
5
|
import * as Effect from "effect/Effect";
|
|
6
6
|
import * as Result from "effect/Result";
|
|
7
7
|
import * as Schema from "effect/Schema";
|
|
8
|
-
import * as HashSet from "effect/HashSet";
|
|
9
8
|
import * as Option from "effect/Option";
|
|
10
9
|
import * as HashMap from "effect/HashMap";
|
|
10
|
+
import * as HashSet from "effect/HashSet";
|
|
11
11
|
import * as Chunk from "effect/Chunk";
|
|
12
12
|
import * as Duration from "effect/Duration";
|
|
13
13
|
import * as BigDecimal from "effect/BigDecimal";
|
package/dist/internal/algebra.js
CHANGED
|
@@ -2,7 +2,6 @@ import { And, Compare, In, Not, Or, Predicate, isScalar, literal, nodes_exports,
|
|
|
2
2
|
import { readPath } from "./read.js";
|
|
3
3
|
import * as Effect from "effect/Effect";
|
|
4
4
|
import * as Arr from "effect/Array";
|
|
5
|
-
import * as HashSet from "effect/HashSet";
|
|
6
5
|
import * as Option from "effect/Option";
|
|
7
6
|
// src/internal/algebra.ts
|
|
8
7
|
/**
|
|
@@ -14,18 +13,72 @@ import * as Option from "effect/Option";
|
|
|
14
13
|
* each place, and the limits of the nodes keep that work small.
|
|
15
14
|
*/
|
|
16
15
|
const { always, never } = nodes_exports;
|
|
16
|
+
/** The text of an operand. The type is in it, so `"1"` is not `1`. */
|
|
17
|
+
const operandText = (operand) => operand._tag === "Literal" ? [typeof operand.value, String(operand.value)] : ["principal", operand.path];
|
|
18
|
+
/** The parts of the text of a condition with no predicate. */
|
|
19
|
+
const shapeOf = (node) => {
|
|
20
|
+
switch (node._tag) {
|
|
21
|
+
case "True":
|
|
22
|
+
case "False": return [node._tag];
|
|
23
|
+
case "Compare": return [
|
|
24
|
+
node._tag,
|
|
25
|
+
node.op,
|
|
26
|
+
node.field,
|
|
27
|
+
operandText(node.value)
|
|
28
|
+
];
|
|
29
|
+
case "In": return [
|
|
30
|
+
node._tag,
|
|
31
|
+
node.field,
|
|
32
|
+
Arr.map(node.values, operandText)
|
|
33
|
+
];
|
|
34
|
+
case "Exists": return [node._tag, node.field];
|
|
35
|
+
case "Under": return [node._tag, Arr.map(node.prefix, operandText)];
|
|
36
|
+
case "And":
|
|
37
|
+
case "Or": return [node._tag, Arr.map(node.conditions, shapeOf)];
|
|
38
|
+
case "Not": return [node._tag, shapeOf(node.condition)];
|
|
39
|
+
case "Predicate": throw new Error("A predicate has no text of structure");
|
|
40
|
+
}
|
|
41
|
+
};
|
|
42
|
+
/**
|
|
43
|
+
* The text of the structure of a condition with no predicate. Two conditions
|
|
44
|
+
* with one text give the same answer for every value. It reads all of the
|
|
45
|
+
* condition, so a search counts its size in the budget.
|
|
46
|
+
*/
|
|
47
|
+
const structureText = (node) => JSON.stringify(shapeOf(node));
|
|
17
48
|
/**
|
|
18
|
-
*
|
|
19
|
-
*
|
|
49
|
+
* The most nodes of a condition that a smart constructor reads for its key.
|
|
50
|
+
* A larger child is its own key, so the work of a constructor stays small.
|
|
20
51
|
*/
|
|
21
|
-
const
|
|
52
|
+
const keyNodes = 64;
|
|
53
|
+
/**
|
|
54
|
+
* The key of a child in a smart constructor: the text of its structure, or
|
|
55
|
+
* the node itself when it is large. The keys go into a native `Set`, not a
|
|
56
|
+
* `HashSet` of Effect. The hash of a string in Effect has no random seed, so
|
|
57
|
+
* a policy could hold very many strings with one hash, and each test of the
|
|
58
|
+
* set would read all of them. A native `Set` finds a node by the node itself,
|
|
59
|
+
* and it hashes a string with the random seed of the engine.
|
|
60
|
+
*/
|
|
61
|
+
const keyOf = (node) => node.size > keyNodes ? node : structureText(node);
|
|
62
|
+
/**
|
|
63
|
+
* Keeps the first node of each structure, in order. A large node is a copy
|
|
64
|
+
* only of itself.
|
|
65
|
+
*/
|
|
66
|
+
const dedupe = (nodes) => {
|
|
67
|
+
const seen = /* @__PURE__ */ new Set();
|
|
68
|
+
return Arr.filter(nodes, (node) => {
|
|
69
|
+
const key = keyOf(node);
|
|
70
|
+
if (seen.has(key)) return false;
|
|
71
|
+
seen.add(key);
|
|
72
|
+
return true;
|
|
73
|
+
});
|
|
74
|
+
};
|
|
22
75
|
/**
|
|
23
76
|
* Removes the children that the other children make useless. In an `Or`,
|
|
24
77
|
* `a OR (a AND b)` is `a`. In an `And`, `a AND (a OR b)` is `a`.
|
|
25
78
|
*/
|
|
26
79
|
const absorb = (inner, nodes) => {
|
|
27
|
-
const all =
|
|
28
|
-
return Arr.filter(nodes, (node) => node._tag !== inner || !Arr.some(node.conditions, (part) =>
|
|
80
|
+
const all = new Set(Arr.map(nodes, keyOf));
|
|
81
|
+
return Arr.filter(nodes, (node) => node._tag !== inner || !Arr.some(node.conditions, (part) => all.has(keyOf(part))));
|
|
29
82
|
};
|
|
30
83
|
/**
|
|
31
84
|
* Gives the result of an `And` with a `False` child, or of an `Or` with a
|
|
@@ -270,8 +323,8 @@ const fields = (node) => {
|
|
|
270
323
|
default: return [];
|
|
271
324
|
}
|
|
272
325
|
};
|
|
273
|
-
return
|
|
326
|
+
return Array.from(new Set(all(node)));
|
|
274
327
|
};
|
|
275
|
-
export { and, evaluate, evaluateSync, fields, not, or, partial, provide, simplify, testCompare };
|
|
328
|
+
export { and, evaluate, evaluateSync, fields, not, or, partial, provide, simplify, structureText, testCompare };
|
|
276
329
|
|
|
277
330
|
//# sourceMappingURL=algebra.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"algebra.js","names":["Nodes","Nodes.And","Nodes.Or","Nodes.Compare","Nodes.Not","Nodes.isScalar","Nodes.runOf","Nodes.literal","Nodes.In","Nodes.Predicate"],"sources":["../../src/internal/algebra.ts"],"sourcesContent":["/**\n * The work on condition nodes: smart constructors, simplification, the step\n * that puts the principal in, tests on a value, and folds.\n *\n * The constructors of the nodes keep each condition small and low, so each\n * walk here is a plain walk with no memo. A shared child is walked again for\n * each place, and the limits of the nodes keep that work small.\n */\n// A predicate gives an Effect with an error type and a service type that this\n// code does not know, so they are `unknown` here.\n// @effect-diagnostics anyUnknownInErrorContext:off\nimport * as Arr from \"effect/Array\"\nimport type * as Context from \"effect/Context\"\nimport * as Effect from \"effect/Effect\"\nimport * as HashSet from \"effect/HashSet\"\nimport * as Option from \"effect/Option\"\nimport type * as Resource from \"../Resource.ts\"\nimport * as Nodes from \"./nodes.ts\"\nimport { readPath } from \"./read.ts\"\n\ntype Node = Nodes.Node\n\nconst { always, never } = Nodes\n\n// Smart constructors\n\n/**\n * Keeps the first node of each structure, in order. A node with a predicate is\n * equal only to itself, as its test is a function.\n */\nconst dedupe = (nodes: ReadonlyArray<Node>): ReadonlyArray<Node> =>\n\tArr.flatten(\n\t\tArr.mapAccum(nodes, HashSet.empty<Node>(), (seen, node) => [\n\t\t\tHashSet.add(seen, node),\n\t\t\tHashSet.has(seen, node) ? [] : [node],\n\t\t])[1],\n\t)\n\n/**\n * Removes the children that the other children make useless. In an `Or`,\n * `a OR (a AND b)` is `a`. In an `And`, `a AND (a OR b)` is `a`.\n */\nconst absorb = (\n\tinner: \"And\" | \"Or\",\n\tnodes: ReadonlyArray<Node>,\n): ReadonlyArray<Node> => {\n\tconst all = HashSet.fromIterable(nodes)\n\treturn Arr.filter(\n\t\tnodes,\n\t\t(node) =>\n\t\t\tnode._tag !== inner ||\n\t\t\t!Arr.some(node.conditions, (part) => HashSet.has(all, part)),\n\t)\n}\n\n/**\n * Gives the result of an `And` with a `False` child, or of an `Or` with a\n * `True` child. The children are tested in order, so a predicate before the\n * constant still runs, and it may fail. When there is such a predicate, the\n * children before the constant stay, so the failure is not lost.\n */\nconst shortCircuit = (\n\ttag: \"And\" | \"Or\",\n\tchildren: ReadonlyArray<Node>,\n\tindex: number,\n): Node => {\n\tconst constant = tag === \"And\" ? never : always\n\tconst neutral = tag === \"And\" ? \"True\" : \"False\"\n\tconst before = Arr.filter(\n\t\tArr.take(children, index),\n\t\t(child) => child._tag !== neutral,\n\t)\n\tif (!Arr.some(before, (child) => child.opaque)) {\n\t\treturn constant\n\t}\n\treturn tag === \"And\"\n\t\t? new Nodes.And([...before, constant])\n\t\t: new Nodes.Or([...before, constant])\n}\n\n/**\n * Makes an `And` or an `Or` of simple children, and makes it simple. It looks\n * one level down only:\n * - it puts the children of a child of the same kind into the node;\n * - it stops at a `False` in an `And`, or at a `True` in an `Or`;\n * - it removes `True` from an `And`, `False` from an `Or`, copies, and the\n * children that other children make useless.\n *\n * A predicate may fail, and it may give another answer when it runs again. So\n * when a child has a predicate, the children stay as they are, and each one\n * runs in its place. A child with a predicate is not put into the node either.\n */\nconst junction = (tag: \"And\" | \"Or\", children: ReadonlyArray<Node>): Node => {\n\tconst constant = tag === \"And\" ? \"False\" : \"True\"\n\tconst neutral = tag === \"And\" ? \"True\" : \"False\"\n\tconst flat = Arr.flatMap(children, (child) =>\n\t\tchild._tag === tag && !child.opaque ? child.conditions : [child],\n\t)\n\treturn Option.match(\n\t\tArr.findFirstIndex(flat, (child) => child._tag === constant),\n\t\t{\n\t\t\tonSome: (stop) => shortCircuit(tag, flat, stop),\n\t\t\tonNone: () => {\n\t\t\t\tconst kept = Arr.filter(flat, (child) => child._tag !== neutral)\n\t\t\t\tconst unique = Arr.some(kept, (child) => child.opaque)\n\t\t\t\t\t? kept\n\t\t\t\t\t: absorb(tag === \"And\" ? \"Or\" : \"And\", dedupe(kept))\n\t\t\t\treturn unique.length === 0\n\t\t\t\t\t? tag === \"And\"\n\t\t\t\t\t\t? always\n\t\t\t\t\t\t: never\n\t\t\t\t\t: unique.length === 1\n\t\t\t\t\t\t? unique[0]!\n\t\t\t\t\t\t: tag === \"And\"\n\t\t\t\t\t\t\t? new Nodes.And(unique)\n\t\t\t\t\t\t\t: new Nodes.Or(unique)\n\t\t\t},\n\t\t},\n\t)\n}\n\n/** An `And` of simple children, made simple. */\nexport const and = (children: ReadonlyArray<Node>): Node =>\n\tjunction(\"And\", children)\n\n/** An `Or` of simple children, made simple. */\nexport const or = (children: ReadonlyArray<Node>): Node =>\n\tjunction(\"Or\", children)\n\n/** A `Not` of a simple child, made simple. */\nexport const not = (child: Node): Node => {\n\tswitch (child._tag) {\n\t\tcase \"True\":\n\t\t\treturn never\n\t\tcase \"False\":\n\t\t\treturn always\n\t\tcase \"Not\":\n\t\t\treturn child.condition\n\t\tcase \"Compare\":\n\t\t\t// A principal field with no value makes `eq` and `ne` both false, so\n\t\t\t// only a fixed value lets `not eq` become `ne`.\n\t\t\treturn child.value._tag === \"Literal\" &&\n\t\t\t\t(child.op === \"eq\" || child.op === \"ne\")\n\t\t\t\t? new Nodes.Compare({\n\t\t\t\t\t\top: child.op === \"eq\" ? \"ne\" : \"eq\",\n\t\t\t\t\t\tfield: child.field,\n\t\t\t\t\t\tvalue: child.value,\n\t\t\t\t\t})\n\t\t\t\t: new Nodes.Not(child)\n\t\tdefault:\n\t\t\treturn new Nodes.Not(child)\n\t}\n}\n\n/** Makes a condition simple, from its leaves up. */\nexport const simplify = (node: Node): Node => {\n\tswitch (node._tag) {\n\t\tcase \"And\":\n\t\tcase \"Or\":\n\t\t\treturn junction(node._tag, Arr.map(node.conditions, simplify))\n\t\tcase \"Not\":\n\t\t\treturn node.condition.pipe(simplify, not)\n\t\tcase \"In\":\n\t\t\treturn node.values.length === 0 ? never : node\n\t\tdefault:\n\t\t\treturn node\n\t}\n}\n\n// Tests on a value\n\n/** The data that a condition needs for a test. */\nexport interface Input {\n\treadonly value: object\n\treadonly principal?: object | undefined\n\treadonly path?: ReadonlyArray<Resource.PathSegment> | undefined\n}\n\n/**\n * Gets a field of a value by a path with dots. It reads only plain data: see\n * `readPart`. A missing field, or a field under a value that is not an object,\n * gives `null`.\n */\nconst getField = (value: unknown, path: string): unknown =>\n\treadPath(value, path.split(\".\"))\n\n/**\n * Gives the value of an operand. A principal field that is `null`, missing, or\n * not a scalar gives `undefined`. No field of a resource is `undefined`, so a\n * test with it is never true. This keeps the check closed: a principal with no\n * id never matches a resource with no owner.\n */\nconst resolve = (operand: Nodes.Operand, principal: unknown): unknown => {\n\tif (operand._tag === \"Literal\") {\n\t\treturn operand.value\n\t}\n\tconst value = getField(principal, operand.path)\n\treturn value !== null && Nodes.isScalar(value) ? value : undefined\n}\n\nexport const testCompare = (\n\top: Nodes.CompareOperator,\n\tleft: unknown,\n\tright: unknown,\n): boolean => {\n\tif (right === undefined) {\n\t\treturn false\n\t}\n\tswitch (op) {\n\t\tcase \"eq\":\n\t\t\treturn left === right\n\t\tcase \"ne\":\n\t\t\treturn left !== right\n\t\tdefault: {\n\t\t\tconst ordered =\n\t\t\t\t(typeof left === \"number\" && typeof right === \"number\") ||\n\t\t\t\t(typeof left === \"string\" && typeof right === \"string\")\n\t\t\tif (!ordered) {\n\t\t\t\treturn false\n\t\t\t}\n\t\t\tconst a = left as number | string\n\t\t\tconst b = right as number | string\n\t\t\tswitch (op) {\n\t\t\t\tcase \"lt\":\n\t\t\t\t\treturn a < b\n\t\t\t\tcase \"lte\":\n\t\t\t\t\treturn a <= b\n\t\t\t\tcase \"gt\":\n\t\t\t\t\treturn a > b\n\t\t\t\tcase \"gte\":\n\t\t\t\t\treturn a >= b\n\t\t\t}\n\t\t}\n\t}\n}\n\n/** Gives the values of the path of a resource, or `undefined` for no path. */\nconst pathValues = (\n\tvalue: unknown,\n\tpath: ReadonlyArray<Resource.PathSegment> | undefined,\n): ReadonlyArray<unknown> | undefined =>\n\tpath?.map((segment) =>\n\t\ttypeof segment === \"string\" ? segment : getField(value, segment.field),\n\t)\n\nconst testUnder = (\n\tprefix: ReadonlyArray<Nodes.Operand>,\n\tinput: Input,\n): boolean => {\n\tconst values = pathValues(input.value, input.path)\n\tif (values === undefined || prefix.length > values.length) {\n\t\treturn false\n\t}\n\t// An operand with no value never matches, also a hole in a path that gives\n\t// `undefined` too.\n\treturn prefix.every((operand, index) => {\n\t\tconst value = resolve(operand, input.principal)\n\t\treturn value !== undefined && value === values[index]\n\t})\n}\n\n/** Tests a simple condition with no predicate on a value. */\nconst testSync = (node: Node, input: Input): boolean => {\n\tswitch (node._tag) {\n\t\tcase \"True\":\n\t\t\treturn true\n\t\tcase \"False\":\n\t\t\treturn false\n\t\tcase \"Compare\":\n\t\t\treturn testCompare(\n\t\t\t\tnode.op,\n\t\t\t\tgetField(input.value, node.field),\n\t\t\t\tresolve(node.value, input.principal),\n\t\t\t)\n\t\tcase \"In\": {\n\t\t\tconst field = getField(input.value, node.field)\n\t\t\treturn node.values.some(\n\t\t\t\t(operand) => resolve(operand, input.principal) === field,\n\t\t\t)\n\t\t}\n\t\tcase \"Exists\":\n\t\t\treturn getField(input.value, node.field) !== null\n\t\tcase \"Under\":\n\t\t\treturn testUnder(node.prefix, input)\n\t\tcase \"And\":\n\t\t\treturn Arr.every(node.conditions, (child) => testSync(child, input))\n\t\tcase \"Or\":\n\t\t\treturn Arr.some(node.conditions, (child) => testSync(child, input))\n\t\tcase \"Not\":\n\t\t\treturn !testSync(node.condition, input)\n\t\tcase \"Predicate\":\n\t\t\t// The types stop this. A predicate needs `evaluate`.\n\t\t\tthrow new Error(\n\t\t\t\t`The predicate \"${node.name}\" needs an Effect. Use Condition.evaluate.`,\n\t\t\t)\n\t}\n}\n\n/**\n * Tests a simple condition on a value. The children of `And` and `Or` run in\n * order, and they stop at the first child that gives the answer.\n */\nconst test = (\n\tnode: Node,\n\tinput: Input,\n): Effect.Effect<boolean, unknown, unknown> => {\n\tif (!node.opaque) {\n\t\treturn Effect.sync(() => testSync(node, input))\n\t}\n\tswitch (node._tag) {\n\t\tcase \"Predicate\":\n\t\t\treturn Nodes.runOf(node)(input.value, input.principal)\n\t\tcase \"Not\":\n\t\t\treturn Effect.map(test(node.condition, input), (value) => !value)\n\t\tcase \"And\":\n\t\t\treturn Effect.map(\n\t\t\t\tEffect.findFirst(node.conditions, (child) =>\n\t\t\t\t\tEffect.map(test(child, input), (value) => !value),\n\t\t\t\t),\n\t\t\t\tOption.isNone,\n\t\t\t)\n\t\tcase \"Or\":\n\t\t\treturn Effect.map(\n\t\t\t\tEffect.findFirst(node.conditions, (child) => test(child, input)),\n\t\t\t\tOption.isSome,\n\t\t\t)\n\t\tdefault:\n\t\t\treturn Effect.sync(() => testSync(node, input))\n\t}\n}\n\n/**\n * Tests a condition with no predicate on a value. The test runs on the\n * simple form of the condition with the principal in it, as a residual does.\n * So a test reads only the fields that the residual reads: a read of a field\n * that is not plain data is a defect, and a field that the answer does not\n * need, as in `x AND False`, is not read.\n */\nexport const evaluateSync = (node: Node, input: Input): boolean =>\n\ttestSync(partial(node, input), input)\n\n/** Tests a condition on a value, as {@link evaluateSync} does. */\nexport const evaluate = (\n\tnode: Node,\n\tinput: Input,\n): Effect.Effect<boolean, unknown, unknown> =>\n\tEffect.suspend(() => test(partial(node, input), input))\n\n// The principal and the path\n\n/**\n * Puts the value of the principal into an operand. It gives `undefined` for a\n * principal field with no value. See {@link resolve}.\n */\nconst bindOperand = (\n\toperand: Nodes.Operand,\n\tprincipal: unknown,\n): Nodes.Literal | undefined => {\n\tif (operand._tag === \"Literal\") {\n\t\treturn operand\n\t}\n\tconst value = resolve(operand, principal)\n\treturn value === undefined ? undefined : Nodes.literal(value as Nodes.Scalar)\n}\n\nconst expandUnder = (\n\tprefix: ReadonlyArray<Nodes.Operand>,\n\tprincipal: unknown,\n\tpath: ReadonlyArray<Resource.PathSegment> | undefined,\n): Node => {\n\tif (path === undefined || prefix.length > path.length) {\n\t\treturn never\n\t}\n\treturn and(\n\t\tArr.map(prefix, (operand, index): Node => {\n\t\t\tconst value = resolve(operand, principal)\n\t\t\tconst segment = path[index]!\n\t\t\treturn value === undefined\n\t\t\t\t? never\n\t\t\t\t: typeof segment === \"string\"\n\t\t\t\t\t? segment === value\n\t\t\t\t\t\t? always\n\t\t\t\t\t\t: never\n\t\t\t\t\t: new Nodes.Compare({\n\t\t\t\t\t\t\top: \"eq\",\n\t\t\t\t\t\t\tfield: segment.field,\n\t\t\t\t\t\t\tvalue: Nodes.literal(value as Nodes.Scalar),\n\t\t\t\t\t\t})\n\t\t}),\n\t)\n}\n\n/** The principal and the path of the resource, for {@link partial}. */\nexport interface PartialInput {\n\treadonly principal?: object | undefined\n\treadonly path?: ReadonlyArray<Resource.PathSegment> | undefined\n}\n\n/**\n * Puts the principal into a condition and expands `Under` with the path of the\n * resource. The result is simple. It has no principal fields and no `Under`\n * nodes. A predicate keeps the principal.\n */\nexport const partial = (node: Node, input: PartialInput): Node => {\n\tconst bind = (current: Node): Node => {\n\t\tswitch (current._tag) {\n\t\t\tcase \"Compare\": {\n\t\t\t\tconst value = bindOperand(current.value, input.principal)\n\t\t\t\treturn value === undefined\n\t\t\t\t\t? never\n\t\t\t\t\t: value === current.value\n\t\t\t\t\t\t? current\n\t\t\t\t\t\t: new Nodes.Compare({ op: current.op, field: current.field, value })\n\t\t\t}\n\t\t\tcase \"In\": {\n\t\t\t\tconst values = Arr.flatMap(current.values, (operand) =>\n\t\t\t\t\tOption.toArray(\n\t\t\t\t\t\tOption.fromUndefinedOr(bindOperand(operand, input.principal)),\n\t\t\t\t\t),\n\t\t\t\t)\n\t\t\t\treturn values.length === 0\n\t\t\t\t\t? never\n\t\t\t\t\t: new Nodes.In({ field: current.field, values })\n\t\t\t}\n\t\t\tcase \"Under\":\n\t\t\t\treturn expandUnder(current.prefix, input.principal, input.path)\n\t\t\tcase \"And\":\n\t\t\t\treturn and(Arr.map(current.conditions, bind))\n\t\t\tcase \"Or\":\n\t\t\t\treturn or(Arr.map(current.conditions, bind))\n\t\t\tcase \"Not\":\n\t\t\t\treturn current.condition.pipe(bind, not)\n\t\t\tcase \"Predicate\": {\n\t\t\t\tconst run = Nodes.runOf(current)\n\t\t\t\treturn new Nodes.Predicate({\n\t\t\t\t\tname: current.name,\n\t\t\t\t\trun: (value) => run(value, input.principal),\n\t\t\t\t})\n\t\t\t}\n\t\t\tdefault:\n\t\t\t\treturn current\n\t\t}\n\t}\n\treturn bind(node)\n}\n\n/** Gives the services in `context` to every predicate of a condition. */\nexport const provide = (node: Node, context: Context.Context<never>): Node => {\n\tif (!node.opaque) {\n\t\treturn node\n\t}\n\tswitch (node._tag) {\n\t\tcase \"Predicate\": {\n\t\t\tconst run = Nodes.runOf(node)\n\t\t\treturn new Nodes.Predicate({\n\t\t\t\tname: node.name,\n\t\t\t\trun: (value, principal) =>\n\t\t\t\t\tEffect.provideContext(run(value, principal), context),\n\t\t\t})\n\t\t}\n\t\tcase \"And\":\n\t\t\treturn new Nodes.And(\n\t\t\t\tArr.map(node.conditions, (child) => provide(child, context)),\n\t\t\t)\n\t\tcase \"Or\":\n\t\t\treturn new Nodes.Or(\n\t\t\t\tArr.map(node.conditions, (child) => provide(child, context)),\n\t\t\t)\n\t\tcase \"Not\":\n\t\t\treturn new Nodes.Not(provide(node.condition, context))\n\t\tdefault:\n\t\t\treturn node\n\t}\n}\n\n/** The fields that a condition tests, in the order that they first come. */\nexport const fields = (node: Node): ReadonlyArray<string> => {\n\tconst all = (current: Node): ReadonlyArray<string> => {\n\t\tswitch (current._tag) {\n\t\t\tcase \"Compare\":\n\t\t\tcase \"In\":\n\t\t\tcase \"Exists\":\n\t\t\t\treturn [current.field]\n\t\t\tcase \"And\":\n\t\t\tcase \"Or\":\n\t\t\t\treturn Arr.flatMap(current.conditions, all)\n\t\t\tcase \"Not\":\n\t\t\t\treturn all(current.condition)\n\t\t\tdefault:\n\t\t\t\treturn []\n\t\t}\n\t}\n\treturn Arr.flatten(\n\t\tArr.mapAccum(all(node), HashSet.empty<string>(), (seen, field) => [\n\t\t\tHashSet.add(seen, field),\n\t\t\tHashSet.has(seen, field) ? [] : [field],\n\t\t])[1],\n\t)\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"algebra.js","names":["Nodes","Nodes.And","Nodes.Or","Nodes.Compare","Nodes.Not","Nodes.isScalar","Nodes.runOf","Nodes.literal","Nodes.In","Nodes.Predicate"],"sources":["../../src/internal/algebra.ts"],"sourcesContent":["/**\n * The work on condition nodes: smart constructors, simplification, the step\n * that puts the principal in, tests on a value, and folds.\n *\n * The constructors of the nodes keep each condition small and low, so each\n * walk here is a plain walk with no memo. A shared child is walked again for\n * each place, and the limits of the nodes keep that work small.\n */\n// A predicate gives an Effect with an error type and a service type that this\n// code does not know, so they are `unknown` here.\n// @effect-diagnostics anyUnknownInErrorContext:off\nimport * as Arr from \"effect/Array\"\nimport type * as Context from \"effect/Context\"\nimport * as Effect from \"effect/Effect\"\nimport * as Option from \"effect/Option\"\nimport type * as Resource from \"../Resource.ts\"\nimport * as Nodes from \"./nodes.ts\"\nimport { readPath } from \"./read.ts\"\n\ntype Node = Nodes.Node\n\nconst { always, never } = Nodes\n\n// Keys of structure\n\n/** The text of an operand. The type is in it, so `\"1\"` is not `1`. */\nconst operandText = (operand: Nodes.Operand): ReadonlyArray<string> =>\n\toperand._tag === \"Literal\"\n\t\t? [typeof operand.value, String(operand.value)]\n\t\t: [\"principal\", operand.path]\n\n/** The parts of the text of a condition with no predicate. */\nconst shapeOf = (node: Node): unknown => {\n\tswitch (node._tag) {\n\t\tcase \"True\":\n\t\tcase \"False\":\n\t\t\treturn [node._tag]\n\t\tcase \"Compare\":\n\t\t\treturn [node._tag, node.op, node.field, operandText(node.value)]\n\t\tcase \"In\":\n\t\t\treturn [node._tag, node.field, Arr.map(node.values, operandText)]\n\t\tcase \"Exists\":\n\t\t\treturn [node._tag, node.field]\n\t\tcase \"Under\":\n\t\t\treturn [node._tag, Arr.map(node.prefix, operandText)]\n\t\tcase \"And\":\n\t\tcase \"Or\":\n\t\t\treturn [node._tag, Arr.map(node.conditions, shapeOf)]\n\t\tcase \"Not\":\n\t\t\treturn [node._tag, shapeOf(node.condition)]\n\t\tcase \"Predicate\":\n\t\t\t// A smart constructor and a search do not ask for the text of a\n\t\t\t// predicate: it is equal only to itself.\n\t\t\tthrow new Error(\"A predicate has no text of structure\")\n\t}\n}\n\n/**\n * The text of the structure of a condition with no predicate. Two conditions\n * with one text give the same answer for every value. It reads all of the\n * condition, so a search counts its size in the budget.\n */\nexport const structureText = (node: Node): string =>\n\tJSON.stringify(shapeOf(node))\n\n/**\n * The most nodes of a condition that a smart constructor reads for its key.\n * A larger child is its own key, so the work of a constructor stays small.\n */\nconst keyNodes = 64\n\n/**\n * The key of a child in a smart constructor: the text of its structure, or\n * the node itself when it is large. The keys go into a native `Set`, not a\n * `HashSet` of Effect. The hash of a string in Effect has no random seed, so\n * a policy could hold very many strings with one hash, and each test of the\n * set would read all of them. A native `Set` finds a node by the node itself,\n * and it hashes a string with the random seed of the engine.\n */\nconst keyOf = (node: Node): string | Node =>\n\tnode.size > keyNodes ? node : structureText(node)\n\n/**\n * Keeps the first node of each structure, in order. A large node is a copy\n * only of itself.\n */\nconst dedupe = (nodes: ReadonlyArray<Node>): ReadonlyArray<Node> => {\n\t// The set belongs to this call.\n\tconst seen = new Set<string | Node>()\n\treturn Arr.filter(nodes, (node) => {\n\t\tconst key = keyOf(node)\n\t\tif (seen.has(key)) {\n\t\t\treturn false\n\t\t}\n\t\tseen.add(key)\n\t\treturn true\n\t})\n}\n\n/**\n * Removes the children that the other children make useless. In an `Or`,\n * `a OR (a AND b)` is `a`. In an `And`, `a AND (a OR b)` is `a`.\n */\nconst absorb = (\n\tinner: \"And\" | \"Or\",\n\tnodes: ReadonlyArray<Node>,\n): ReadonlyArray<Node> => {\n\t// A native `Set`, as in `dedupe`.\n\tconst all = new Set(Arr.map(nodes, keyOf))\n\treturn Arr.filter(\n\t\tnodes,\n\t\t(node) =>\n\t\t\tnode._tag !== inner ||\n\t\t\t!Arr.some(node.conditions, (part) => all.has(keyOf(part))),\n\t)\n}\n\n// Smart constructors\n\n/**\n * Gives the result of an `And` with a `False` child, or of an `Or` with a\n * `True` child. The children are tested in order, so a predicate before the\n * constant still runs, and it may fail. When there is such a predicate, the\n * children before the constant stay, so the failure is not lost.\n */\nconst shortCircuit = (\n\ttag: \"And\" | \"Or\",\n\tchildren: ReadonlyArray<Node>,\n\tindex: number,\n): Node => {\n\tconst constant = tag === \"And\" ? never : always\n\tconst neutral = tag === \"And\" ? \"True\" : \"False\"\n\tconst before = Arr.filter(\n\t\tArr.take(children, index),\n\t\t(child) => child._tag !== neutral,\n\t)\n\tif (!Arr.some(before, (child) => child.opaque)) {\n\t\treturn constant\n\t}\n\treturn tag === \"And\"\n\t\t? new Nodes.And([...before, constant])\n\t\t: new Nodes.Or([...before, constant])\n}\n\n/**\n * Makes an `And` or an `Or` of simple children, and makes it simple. It looks\n * one level down only:\n * - it puts the children of a child of the same kind into the node;\n * - it stops at a `False` in an `And`, or at a `True` in an `Or`;\n * - it removes `True` from an `And`, `False` from an `Or`, copies, and the\n * children that other children make useless.\n *\n * A predicate may fail, and it may give another answer when it runs again. So\n * when a child has a predicate, the children stay as they are, and each one\n * runs in its place. A child with a predicate is not put into the node either.\n */\nconst junction = (tag: \"And\" | \"Or\", children: ReadonlyArray<Node>): Node => {\n\tconst constant = tag === \"And\" ? \"False\" : \"True\"\n\tconst neutral = tag === \"And\" ? \"True\" : \"False\"\n\tconst flat = Arr.flatMap(children, (child) =>\n\t\tchild._tag === tag && !child.opaque ? child.conditions : [child],\n\t)\n\treturn Option.match(\n\t\tArr.findFirstIndex(flat, (child) => child._tag === constant),\n\t\t{\n\t\t\tonSome: (stop) => shortCircuit(tag, flat, stop),\n\t\t\tonNone: () => {\n\t\t\t\tconst kept = Arr.filter(flat, (child) => child._tag !== neutral)\n\t\t\t\tconst unique = Arr.some(kept, (child) => child.opaque)\n\t\t\t\t\t? kept\n\t\t\t\t\t: absorb(tag === \"And\" ? \"Or\" : \"And\", dedupe(kept))\n\t\t\t\treturn unique.length === 0\n\t\t\t\t\t? tag === \"And\"\n\t\t\t\t\t\t? always\n\t\t\t\t\t\t: never\n\t\t\t\t\t: unique.length === 1\n\t\t\t\t\t\t? unique[0]!\n\t\t\t\t\t\t: tag === \"And\"\n\t\t\t\t\t\t\t? new Nodes.And(unique)\n\t\t\t\t\t\t\t: new Nodes.Or(unique)\n\t\t\t},\n\t\t},\n\t)\n}\n\n/** An `And` of simple children, made simple. */\nexport const and = (children: ReadonlyArray<Node>): Node =>\n\tjunction(\"And\", children)\n\n/** An `Or` of simple children, made simple. */\nexport const or = (children: ReadonlyArray<Node>): Node =>\n\tjunction(\"Or\", children)\n\n/** A `Not` of a simple child, made simple. */\nexport const not = (child: Node): Node => {\n\tswitch (child._tag) {\n\t\tcase \"True\":\n\t\t\treturn never\n\t\tcase \"False\":\n\t\t\treturn always\n\t\tcase \"Not\":\n\t\t\treturn child.condition\n\t\tcase \"Compare\":\n\t\t\t// A principal field with no value makes `eq` and `ne` both false, so\n\t\t\t// only a fixed value lets `not eq` become `ne`.\n\t\t\treturn child.value._tag === \"Literal\" &&\n\t\t\t\t(child.op === \"eq\" || child.op === \"ne\")\n\t\t\t\t? new Nodes.Compare({\n\t\t\t\t\t\top: child.op === \"eq\" ? \"ne\" : \"eq\",\n\t\t\t\t\t\tfield: child.field,\n\t\t\t\t\t\tvalue: child.value,\n\t\t\t\t\t})\n\t\t\t\t: new Nodes.Not(child)\n\t\tdefault:\n\t\t\treturn new Nodes.Not(child)\n\t}\n}\n\n/** Makes a condition simple, from its leaves up. */\nexport const simplify = (node: Node): Node => {\n\tswitch (node._tag) {\n\t\tcase \"And\":\n\t\tcase \"Or\":\n\t\t\treturn junction(node._tag, Arr.map(node.conditions, simplify))\n\t\tcase \"Not\":\n\t\t\treturn node.condition.pipe(simplify, not)\n\t\tcase \"In\":\n\t\t\treturn node.values.length === 0 ? never : node\n\t\tdefault:\n\t\t\treturn node\n\t}\n}\n\n// Tests on a value\n\n/** The data that a condition needs for a test. */\nexport interface Input {\n\treadonly value: object\n\treadonly principal?: object | undefined\n\treadonly path?: ReadonlyArray<Resource.PathSegment> | undefined\n}\n\n/**\n * Gets a field of a value by a path with dots. It reads only plain data: see\n * `readPart`. A missing field, or a field under a value that is not an object,\n * gives `null`.\n */\nconst getField = (value: unknown, path: string): unknown =>\n\treadPath(value, path.split(\".\"))\n\n/**\n * Gives the value of an operand. A principal field that is `null`, missing, or\n * not a scalar gives `undefined`. No field of a resource is `undefined`, so a\n * test with it is never true. This keeps the check closed: a principal with no\n * id never matches a resource with no owner.\n */\nconst resolve = (operand: Nodes.Operand, principal: unknown): unknown => {\n\tif (operand._tag === \"Literal\") {\n\t\treturn operand.value\n\t}\n\tconst value = getField(principal, operand.path)\n\treturn value !== null && Nodes.isScalar(value) ? value : undefined\n}\n\nexport const testCompare = (\n\top: Nodes.CompareOperator,\n\tleft: unknown,\n\tright: unknown,\n): boolean => {\n\tif (right === undefined) {\n\t\treturn false\n\t}\n\tswitch (op) {\n\t\tcase \"eq\":\n\t\t\treturn left === right\n\t\tcase \"ne\":\n\t\t\treturn left !== right\n\t\tdefault: {\n\t\t\tconst ordered =\n\t\t\t\t(typeof left === \"number\" && typeof right === \"number\") ||\n\t\t\t\t(typeof left === \"string\" && typeof right === \"string\")\n\t\t\tif (!ordered) {\n\t\t\t\treturn false\n\t\t\t}\n\t\t\tconst a = left as number | string\n\t\t\tconst b = right as number | string\n\t\t\tswitch (op) {\n\t\t\t\tcase \"lt\":\n\t\t\t\t\treturn a < b\n\t\t\t\tcase \"lte\":\n\t\t\t\t\treturn a <= b\n\t\t\t\tcase \"gt\":\n\t\t\t\t\treturn a > b\n\t\t\t\tcase \"gte\":\n\t\t\t\t\treturn a >= b\n\t\t\t}\n\t\t}\n\t}\n}\n\n/** Gives the values of the path of a resource, or `undefined` for no path. */\nconst pathValues = (\n\tvalue: unknown,\n\tpath: ReadonlyArray<Resource.PathSegment> | undefined,\n): ReadonlyArray<unknown> | undefined =>\n\tpath?.map((segment) =>\n\t\ttypeof segment === \"string\" ? segment : getField(value, segment.field),\n\t)\n\nconst testUnder = (\n\tprefix: ReadonlyArray<Nodes.Operand>,\n\tinput: Input,\n): boolean => {\n\tconst values = pathValues(input.value, input.path)\n\tif (values === undefined || prefix.length > values.length) {\n\t\treturn false\n\t}\n\t// An operand with no value never matches, also a hole in a path that gives\n\t// `undefined` too.\n\treturn prefix.every((operand, index) => {\n\t\tconst value = resolve(operand, input.principal)\n\t\treturn value !== undefined && value === values[index]\n\t})\n}\n\n/** Tests a simple condition with no predicate on a value. */\nconst testSync = (node: Node, input: Input): boolean => {\n\tswitch (node._tag) {\n\t\tcase \"True\":\n\t\t\treturn true\n\t\tcase \"False\":\n\t\t\treturn false\n\t\tcase \"Compare\":\n\t\t\treturn testCompare(\n\t\t\t\tnode.op,\n\t\t\t\tgetField(input.value, node.field),\n\t\t\t\tresolve(node.value, input.principal),\n\t\t\t)\n\t\tcase \"In\": {\n\t\t\tconst field = getField(input.value, node.field)\n\t\t\treturn node.values.some(\n\t\t\t\t(operand) => resolve(operand, input.principal) === field,\n\t\t\t)\n\t\t}\n\t\tcase \"Exists\":\n\t\t\treturn getField(input.value, node.field) !== null\n\t\tcase \"Under\":\n\t\t\treturn testUnder(node.prefix, input)\n\t\tcase \"And\":\n\t\t\treturn Arr.every(node.conditions, (child) => testSync(child, input))\n\t\tcase \"Or\":\n\t\t\treturn Arr.some(node.conditions, (child) => testSync(child, input))\n\t\tcase \"Not\":\n\t\t\treturn !testSync(node.condition, input)\n\t\tcase \"Predicate\":\n\t\t\t// The types stop this. A predicate needs `evaluate`.\n\t\t\tthrow new Error(\n\t\t\t\t`The predicate \"${node.name}\" needs an Effect. Use Condition.evaluate.`,\n\t\t\t)\n\t}\n}\n\n/**\n * Tests a simple condition on a value. The children of `And` and `Or` run in\n * order, and they stop at the first child that gives the answer.\n */\nconst test = (\n\tnode: Node,\n\tinput: Input,\n): Effect.Effect<boolean, unknown, unknown> => {\n\tif (!node.opaque) {\n\t\treturn Effect.sync(() => testSync(node, input))\n\t}\n\tswitch (node._tag) {\n\t\tcase \"Predicate\":\n\t\t\treturn Nodes.runOf(node)(input.value, input.principal)\n\t\tcase \"Not\":\n\t\t\treturn Effect.map(test(node.condition, input), (value) => !value)\n\t\tcase \"And\":\n\t\t\treturn Effect.map(\n\t\t\t\tEffect.findFirst(node.conditions, (child) =>\n\t\t\t\t\tEffect.map(test(child, input), (value) => !value),\n\t\t\t\t),\n\t\t\t\tOption.isNone,\n\t\t\t)\n\t\tcase \"Or\":\n\t\t\treturn Effect.map(\n\t\t\t\tEffect.findFirst(node.conditions, (child) => test(child, input)),\n\t\t\t\tOption.isSome,\n\t\t\t)\n\t\tdefault:\n\t\t\treturn Effect.sync(() => testSync(node, input))\n\t}\n}\n\n/**\n * Tests a condition with no predicate on a value. The test runs on the\n * simple form of the condition with the principal in it, as a residual does.\n * So a test reads only the fields that the residual reads: a read of a field\n * that is not plain data is a defect, and a field that the answer does not\n * need, as in `x AND False`, is not read.\n */\nexport const evaluateSync = (node: Node, input: Input): boolean =>\n\ttestSync(partial(node, input), input)\n\n/** Tests a condition on a value, as {@link evaluateSync} does. */\nexport const evaluate = (\n\tnode: Node,\n\tinput: Input,\n): Effect.Effect<boolean, unknown, unknown> =>\n\tEffect.suspend(() => test(partial(node, input), input))\n\n// The principal and the path\n\n/**\n * Puts the value of the principal into an operand. It gives `undefined` for a\n * principal field with no value. See {@link resolve}.\n */\nconst bindOperand = (\n\toperand: Nodes.Operand,\n\tprincipal: unknown,\n): Nodes.Literal | undefined => {\n\tif (operand._tag === \"Literal\") {\n\t\treturn operand\n\t}\n\tconst value = resolve(operand, principal)\n\treturn value === undefined ? undefined : Nodes.literal(value as Nodes.Scalar)\n}\n\nconst expandUnder = (\n\tprefix: ReadonlyArray<Nodes.Operand>,\n\tprincipal: unknown,\n\tpath: ReadonlyArray<Resource.PathSegment> | undefined,\n): Node => {\n\tif (path === undefined || prefix.length > path.length) {\n\t\treturn never\n\t}\n\treturn and(\n\t\tArr.map(prefix, (operand, index): Node => {\n\t\t\tconst value = resolve(operand, principal)\n\t\t\tconst segment = path[index]!\n\t\t\treturn value === undefined\n\t\t\t\t? never\n\t\t\t\t: typeof segment === \"string\"\n\t\t\t\t\t? segment === value\n\t\t\t\t\t\t? always\n\t\t\t\t\t\t: never\n\t\t\t\t\t: new Nodes.Compare({\n\t\t\t\t\t\t\top: \"eq\",\n\t\t\t\t\t\t\tfield: segment.field,\n\t\t\t\t\t\t\tvalue: Nodes.literal(value as Nodes.Scalar),\n\t\t\t\t\t\t})\n\t\t}),\n\t)\n}\n\n/** The principal and the path of the resource, for {@link partial}. */\nexport interface PartialInput {\n\treadonly principal?: object | undefined\n\treadonly path?: ReadonlyArray<Resource.PathSegment> | undefined\n}\n\n/**\n * Puts the principal into a condition and expands `Under` with the path of the\n * resource. The result is simple. It has no principal fields and no `Under`\n * nodes. A predicate keeps the principal.\n */\nexport const partial = (node: Node, input: PartialInput): Node => {\n\tconst bind = (current: Node): Node => {\n\t\tswitch (current._tag) {\n\t\t\tcase \"Compare\": {\n\t\t\t\tconst value = bindOperand(current.value, input.principal)\n\t\t\t\treturn value === undefined\n\t\t\t\t\t? never\n\t\t\t\t\t: value === current.value\n\t\t\t\t\t\t? current\n\t\t\t\t\t\t: new Nodes.Compare({ op: current.op, field: current.field, value })\n\t\t\t}\n\t\t\tcase \"In\": {\n\t\t\t\tconst values = Arr.flatMap(current.values, (operand) =>\n\t\t\t\t\tOption.toArray(\n\t\t\t\t\t\tOption.fromUndefinedOr(bindOperand(operand, input.principal)),\n\t\t\t\t\t),\n\t\t\t\t)\n\t\t\t\treturn values.length === 0\n\t\t\t\t\t? never\n\t\t\t\t\t: new Nodes.In({ field: current.field, values })\n\t\t\t}\n\t\t\tcase \"Under\":\n\t\t\t\treturn expandUnder(current.prefix, input.principal, input.path)\n\t\t\tcase \"And\":\n\t\t\t\treturn and(Arr.map(current.conditions, bind))\n\t\t\tcase \"Or\":\n\t\t\t\treturn or(Arr.map(current.conditions, bind))\n\t\t\tcase \"Not\":\n\t\t\t\treturn current.condition.pipe(bind, not)\n\t\t\tcase \"Predicate\": {\n\t\t\t\tconst run = Nodes.runOf(current)\n\t\t\t\treturn new Nodes.Predicate({\n\t\t\t\t\tname: current.name,\n\t\t\t\t\trun: (value) => run(value, input.principal),\n\t\t\t\t})\n\t\t\t}\n\t\t\tdefault:\n\t\t\t\treturn current\n\t\t}\n\t}\n\treturn bind(node)\n}\n\n/** Gives the services in `context` to every predicate of a condition. */\nexport const provide = (node: Node, context: Context.Context<never>): Node => {\n\tif (!node.opaque) {\n\t\treturn node\n\t}\n\tswitch (node._tag) {\n\t\tcase \"Predicate\": {\n\t\t\tconst run = Nodes.runOf(node)\n\t\t\treturn new Nodes.Predicate({\n\t\t\t\tname: node.name,\n\t\t\t\trun: (value, principal) =>\n\t\t\t\t\tEffect.provideContext(run(value, principal), context),\n\t\t\t})\n\t\t}\n\t\tcase \"And\":\n\t\t\treturn new Nodes.And(\n\t\t\t\tArr.map(node.conditions, (child) => provide(child, context)),\n\t\t\t)\n\t\tcase \"Or\":\n\t\t\treturn new Nodes.Or(\n\t\t\t\tArr.map(node.conditions, (child) => provide(child, context)),\n\t\t\t)\n\t\tcase \"Not\":\n\t\t\treturn new Nodes.Not(provide(node.condition, context))\n\t\tdefault:\n\t\t\treturn node\n\t}\n}\n\n/** The fields that a condition tests, in the order that they first come. */\nexport const fields = (node: Node): ReadonlyArray<string> => {\n\tconst all = (current: Node): ReadonlyArray<string> => {\n\t\tswitch (current._tag) {\n\t\t\tcase \"Compare\":\n\t\t\tcase \"In\":\n\t\t\tcase \"Exists\":\n\t\t\t\treturn [current.field]\n\t\t\tcase \"And\":\n\t\t\tcase \"Or\":\n\t\t\t\treturn Arr.flatMap(current.conditions, all)\n\t\t\tcase \"Not\":\n\t\t\t\treturn all(current.condition)\n\t\t\tdefault:\n\t\t\t\treturn []\n\t\t}\n\t}\n\t// A native `Set`, as in `dedupe`: a field of a condition that is not bound\n\t// to a domain may be any string.\n\treturn Array.from(new Set(all(node)))\n}\n"],"mappings":";;;;;;;;;;;;;;AAqBA,MAAM,EAAE,QAAQ,UAAUA;;AAK1B,MAAM,eAAe,YACpB,QAAQ,SAAS,YACd,CAAC,OAAO,QAAQ,OAAO,OAAO,QAAQ,KAAK,CAAC,IAC5C,CAAC,aAAa,QAAQ,IAAI;;AAG9B,MAAM,WAAW,SAAwB;CACxC,QAAQ,KAAK,MAAb;EACC,KAAK;EACL,KAAK,SACJ,OAAO,CAAC,KAAK,IAAI;EAClB,KAAK,WACJ,OAAO;GAAC,KAAK;GAAM,KAAK;GAAI,KAAK;GAAO,YAAY,KAAK,KAAK;EAAC;EAChE,KAAK,MACJ,OAAO;GAAC,KAAK;GAAM,KAAK;GAAO,IAAI,IAAI,KAAK,QAAQ,WAAW;EAAC;EACjE,KAAK,UACJ,OAAO,CAAC,KAAK,MAAM,KAAK,KAAK;EAC9B,KAAK,SACJ,OAAO,CAAC,KAAK,MAAM,IAAI,IAAI,KAAK,QAAQ,WAAW,CAAC;EACrD,KAAK;EACL,KAAK,MACJ,OAAO,CAAC,KAAK,MAAM,IAAI,IAAI,KAAK,YAAY,OAAO,CAAC;EACrD,KAAK,OACJ,OAAO,CAAC,KAAK,MAAM,QAAQ,KAAK,SAAS,CAAC;EAC3C,KAAK,aAGJ,MAAM,IAAI,MAAM,sCAAsC;CACxD;AACD;;;;;;AAOA,MAAa,iBAAiB,SAC7B,KAAK,UAAU,QAAQ,IAAI,CAAC;;;;;AAM7B,MAAM,WAAW;;;;;;;;;AAUjB,MAAM,SAAS,SACd,KAAK,OAAO,WAAW,OAAO,cAAc,IAAI;;;;;AAMjD,MAAM,UAAU,UAAoD;CAEnE,MAAM,uBAAO,IAAI,IAAmB;CACpC,OAAO,IAAI,OAAO,QAAQ,SAAS;EAClC,MAAM,MAAM,MAAM,IAAI;EACtB,IAAI,KAAK,IAAI,GAAG,GACf,OAAO;EAER,KAAK,IAAI,GAAG;EACZ,OAAO;CACR,CAAC;AACF;;;;;AAMA,MAAM,UACL,OACA,UACyB;CAEzB,MAAM,MAAM,IAAI,IAAI,IAAI,IAAI,OAAO,KAAK,CAAC;CACzC,OAAO,IAAI,OACV,QACC,SACA,KAAK,SAAS,SACd,CAAC,IAAI,KAAK,KAAK,aAAa,SAAS,IAAI,IAAI,MAAM,IAAI,CAAC,CAAC,CAC3D;AACD;;;;;;;AAUA,MAAM,gBACL,KACA,UACA,UACU;CACV,MAAM,WAAW,QAAQ,QAAQ,QAAQ;CACzC,MAAM,UAAU,QAAQ,QAAQ,SAAS;CACzC,MAAM,SAAS,IAAI,OAClB,IAAI,KAAK,UAAU,KAAK,IACvB,UAAU,MAAM,SAAS,OAC3B;CACA,IAAI,CAAC,IAAI,KAAK,SAAS,UAAU,MAAM,MAAM,GAC5C,OAAO;CAER,OAAO,QAAQ,QACZ,IAAIC,IAAU,CAAC,GAAG,QAAQ,QAAQ,CAAC,IACnC,IAAIC,GAAS,CAAC,GAAG,QAAQ,QAAQ,CAAC;AACtC;;;;;;;;;;;;;AAcA,MAAM,YAAY,KAAmB,aAAwC;CAC5E,MAAM,WAAW,QAAQ,QAAQ,UAAU;CAC3C,MAAM,UAAU,QAAQ,QAAQ,SAAS;CACzC,MAAM,OAAO,IAAI,QAAQ,WAAW,UACnC,MAAM,SAAS,OAAO,CAAC,MAAM,SAAS,MAAM,aAAa,CAAC,KAAK,CAChE;CACA,OAAO,OAAO,MACb,IAAI,eAAe,OAAO,UAAU,MAAM,SAAS,QAAQ,GAC3D;EACC,SAAS,SAAS,aAAa,KAAK,MAAM,IAAI;EAC9C,cAAc;GACb,MAAM,OAAO,IAAI,OAAO,OAAO,UAAU,MAAM,SAAS,OAAO;GAC/D,MAAM,SAAS,IAAI,KAAK,OAAO,UAAU,MAAM,MAAM,IAClD,OACA,OAAO,QAAQ,QAAQ,OAAO,OAAO,OAAO,IAAI,CAAC;GACpD,OAAO,OAAO,WAAW,IACtB,QAAQ,QACP,SACA,QACD,OAAO,WAAW,IACjB,OAAO,KACP,QAAQ,QACP,IAAID,IAAU,MAAM,IACpB,IAAIC,GAAS,MAAM;EACzB;CACD,CACD;AACD;;AAGA,MAAa,OAAO,aACnB,SAAS,OAAO,QAAQ;;AAGzB,MAAa,MAAM,aAClB,SAAS,MAAM,QAAQ;;AAGxB,MAAa,OAAO,UAAsB;CACzC,QAAQ,MAAM,MAAd;EACC,KAAK,QACJ,OAAO;EACR,KAAK,SACJ,OAAO;EACR,KAAK,OACJ,OAAO,MAAM;EACd,KAAK,WAGJ,OAAO,MAAM,MAAM,SAAS,cAC1B,MAAM,OAAO,QAAQ,MAAM,OAAO,QACjC,IAAIC,QAAc;GAClB,IAAI,MAAM,OAAO,OAAO,OAAO;GAC/B,OAAO,MAAM;GACb,OAAO,MAAM;EACd,CAAC,IACA,IAAIC,IAAU,KAAK;EACvB,SACC,OAAO,IAAIA,IAAU,KAAK;CAC5B;AACD;;AAGA,MAAa,YAAY,SAAqB;CAC7C,QAAQ,KAAK,MAAb;EACC,KAAK;EACL,KAAK,MACJ,OAAO,SAAS,KAAK,MAAM,IAAI,IAAI,KAAK,YAAY,QAAQ,CAAC;EAC9D,KAAK,OACJ,OAAO,KAAK,UAAU,KAAK,UAAU,GAAG;EACzC,KAAK,MACJ,OAAO,KAAK,OAAO,WAAW,IAAI,QAAQ;EAC3C,SACC,OAAO;CACT;AACD;;;;;;AAgBA,MAAM,YAAY,OAAgB,SACjC,SAAS,OAAO,KAAK,MAAM,GAAG,CAAC;;;;;;;AAQhC,MAAM,WAAW,SAAwB,cAAgC;CACxE,IAAI,QAAQ,SAAS,WACpB,OAAO,QAAQ;CAEhB,MAAM,QAAQ,SAAS,WAAW,QAAQ,IAAI;CAC9C,OAAO,UAAU,QAAQC,SAAe,KAAK,IAAI,QAAQ,KAAA;AAC1D;AAEA,MAAa,eACZ,IACA,MACA,UACa;CACb,IAAI,UAAU,KAAA,GACb,OAAO;CAER,QAAQ,IAAR;EACC,KAAK,MACJ,OAAO,SAAS;EACjB,KAAK,MACJ,OAAO,SAAS;EACjB,SAAS;GAIR,IAAI,EAFF,OAAO,SAAS,YAAY,OAAO,UAAU,YAC7C,OAAO,SAAS,YAAY,OAAO,UAAU,WAE9C,OAAO;GAER,MAAM,IAAI;GACV,MAAM,IAAI;GACV,QAAQ,IAAR;IACC,KAAK,MACJ,OAAO,IAAI;IACZ,KAAK,OACJ,OAAO,KAAK;IACb,KAAK,MACJ,OAAO,IAAI;IACZ,KAAK,OACJ,OAAO,KAAK;GACd;EACD;CACD;AACD;;AAGA,MAAM,cACL,OACA,SAEA,MAAM,KAAK,YACV,OAAO,YAAY,WAAW,UAAU,SAAS,OAAO,QAAQ,KAAK,CACtE;AAED,MAAM,aACL,QACA,UACa;CACb,MAAM,SAAS,WAAW,MAAM,OAAO,MAAM,IAAI;CACjD,IAAI,WAAW,KAAA,KAAa,OAAO,SAAS,OAAO,QAClD,OAAO;CAIR,OAAO,OAAO,OAAO,SAAS,UAAU;EACvC,MAAM,QAAQ,QAAQ,SAAS,MAAM,SAAS;EAC9C,OAAO,UAAU,KAAA,KAAa,UAAU,OAAO;CAChD,CAAC;AACF;;AAGA,MAAM,YAAY,MAAY,UAA0B;CACvD,QAAQ,KAAK,MAAb;EACC,KAAK,QACJ,OAAO;EACR,KAAK,SACJ,OAAO;EACR,KAAK,WACJ,OAAO,YACN,KAAK,IACL,SAAS,MAAM,OAAO,KAAK,KAAK,GAChC,QAAQ,KAAK,OAAO,MAAM,SAAS,CACpC;EACD,KAAK,MAAM;GACV,MAAM,QAAQ,SAAS,MAAM,OAAO,KAAK,KAAK;GAC9C,OAAO,KAAK,OAAO,MACjB,YAAY,QAAQ,SAAS,MAAM,SAAS,MAAM,KACpD;EACD;EACA,KAAK,UACJ,OAAO,SAAS,MAAM,OAAO,KAAK,KAAK,MAAM;EAC9C,KAAK,SACJ,OAAO,UAAU,KAAK,QAAQ,KAAK;EACpC,KAAK,OACJ,OAAO,IAAI,MAAM,KAAK,aAAa,UAAU,SAAS,OAAO,KAAK,CAAC;EACpE,KAAK,MACJ,OAAO,IAAI,KAAK,KAAK,aAAa,UAAU,SAAS,OAAO,KAAK,CAAC;EACnE,KAAK,OACJ,OAAO,CAAC,SAAS,KAAK,WAAW,KAAK;EACvC,KAAK,aAEJ,MAAM,IAAI,MACT,kBAAkB,KAAK,KAAK,2CAC7B;CACF;AACD;;;;;AAMA,MAAM,QACL,MACA,UAC8C;CAC9C,IAAI,CAAC,KAAK,QACT,OAAO,OAAO,WAAW,SAAS,MAAM,KAAK,CAAC;CAE/C,QAAQ,KAAK,MAAb;EACC,KAAK,aACJ,OAAOC,MAAY,IAAI,CAAC,CAAC,MAAM,OAAO,MAAM,SAAS;EACtD,KAAK,OACJ,OAAO,OAAO,IAAI,KAAK,KAAK,WAAW,KAAK,IAAI,UAAU,CAAC,KAAK;EACjE,KAAK,OACJ,OAAO,OAAO,IACb,OAAO,UAAU,KAAK,aAAa,UAClC,OAAO,IAAI,KAAK,OAAO,KAAK,IAAI,UAAU,CAAC,KAAK,CACjD,GACA,OAAO,MACR;EACD,KAAK,MACJ,OAAO,OAAO,IACb,OAAO,UAAU,KAAK,aAAa,UAAU,KAAK,OAAO,KAAK,CAAC,GAC/D,OAAO,MACR;EACD,SACC,OAAO,OAAO,WAAW,SAAS,MAAM,KAAK,CAAC;CAChD;AACD;;;;;;;;AASA,MAAa,gBAAgB,MAAY,UACxC,SAAS,QAAQ,MAAM,KAAK,GAAG,KAAK;;AAGrC,MAAa,YACZ,MACA,UAEA,OAAO,cAAc,KAAK,QAAQ,MAAM,KAAK,GAAG,KAAK,CAAC;;;;;AAQvD,MAAM,eACL,SACA,cAC+B;CAC/B,IAAI,QAAQ,SAAS,WACpB,OAAO;CAER,MAAM,QAAQ,QAAQ,SAAS,SAAS;CACxC,OAAO,UAAU,KAAA,IAAY,KAAA,IAAYC,QAAc,KAAqB;AAC7E;AAEA,MAAM,eACL,QACA,WACA,SACU;CACV,IAAI,SAAS,KAAA,KAAa,OAAO,SAAS,KAAK,QAC9C,OAAO;CAER,OAAO,IACN,IAAI,IAAI,SAAS,SAAS,UAAgB;EACzC,MAAM,QAAQ,QAAQ,SAAS,SAAS;EACxC,MAAM,UAAU,KAAK;EACrB,OAAO,UAAU,KAAA,IACd,QACA,OAAO,YAAY,WAClB,YAAY,QACX,SACA,QACD,IAAIJ,QAAc;GAClB,IAAI;GACJ,OAAO,QAAQ;GACf,OAAOI,QAAc,KAAqB;EAC3C,CAAC;CACL,CAAC,CACF;AACD;;;;;;AAaA,MAAa,WAAW,MAAY,UAA8B;CACjE,MAAM,QAAQ,YAAwB;EACrC,QAAQ,QAAQ,MAAhB;GACC,KAAK,WAAW;IACf,MAAM,QAAQ,YAAY,QAAQ,OAAO,MAAM,SAAS;IACxD,OAAO,UAAU,KAAA,IACd,QACA,UAAU,QAAQ,QACjB,UACA,IAAIJ,QAAc;KAAE,IAAI,QAAQ;KAAI,OAAO,QAAQ;KAAO;IAAM,CAAC;GACtE;GACA,KAAK,MAAM;IACV,MAAM,SAAS,IAAI,QAAQ,QAAQ,SAAS,YAC3C,OAAO,QACN,OAAO,gBAAgB,YAAY,SAAS,MAAM,SAAS,CAAC,CAC7D,CACD;IACA,OAAO,OAAO,WAAW,IACtB,QACA,IAAIK,GAAS;KAAE,OAAO,QAAQ;KAAO;IAAO,CAAC;GACjD;GACA,KAAK,SACJ,OAAO,YAAY,QAAQ,QAAQ,MAAM,WAAW,MAAM,IAAI;GAC/D,KAAK,OACJ,OAAO,IAAI,IAAI,IAAI,QAAQ,YAAY,IAAI,CAAC;GAC7C,KAAK,MACJ,OAAO,GAAG,IAAI,IAAI,QAAQ,YAAY,IAAI,CAAC;GAC5C,KAAK,OACJ,OAAO,QAAQ,UAAU,KAAK,MAAM,GAAG;GACxC,KAAK,aAAa;IACjB,MAAM,MAAMF,MAAY,OAAO;IAC/B,OAAO,IAAIG,UAAgB;KAC1B,MAAM,QAAQ;KACd,MAAM,UAAU,IAAI,OAAO,MAAM,SAAS;IAC3C,CAAC;GACF;GACA,SACC,OAAO;EACT;CACD;CACA,OAAO,KAAK,IAAI;AACjB;;AAGA,MAAa,WAAW,MAAY,YAA0C;CAC7E,IAAI,CAAC,KAAK,QACT,OAAO;CAER,QAAQ,KAAK,MAAb;EACC,KAAK,aAAa;GACjB,MAAM,MAAMH,MAAY,IAAI;GAC5B,OAAO,IAAIG,UAAgB;IAC1B,MAAM,KAAK;IACX,MAAM,OAAO,cACZ,OAAO,eAAe,IAAI,OAAO,SAAS,GAAG,OAAO;GACtD,CAAC;EACF;EACA,KAAK,OACJ,OAAO,IAAIR,IACV,IAAI,IAAI,KAAK,aAAa,UAAU,QAAQ,OAAO,OAAO,CAAC,CAC5D;EACD,KAAK,MACJ,OAAO,IAAIC,GACV,IAAI,IAAI,KAAK,aAAa,UAAU,QAAQ,OAAO,OAAO,CAAC,CAC5D;EACD,KAAK,OACJ,OAAO,IAAIE,IAAU,QAAQ,KAAK,WAAW,OAAO,CAAC;EACtD,SACC,OAAO;CACT;AACD;;AAGA,MAAa,UAAU,SAAsC;CAC5D,MAAM,OAAO,YAAyC;EACrD,QAAQ,QAAQ,MAAhB;GACC,KAAK;GACL,KAAK;GACL,KAAK,UACJ,OAAO,CAAC,QAAQ,KAAK;GACtB,KAAK;GACL,KAAK,MACJ,OAAO,IAAI,QAAQ,QAAQ,YAAY,GAAG;GAC3C,KAAK,OACJ,OAAO,IAAI,QAAQ,SAAS;GAC7B,SACC,OAAO,CAAC;EACV;CACD;CAGA,OAAO,MAAM,KAAK,IAAI,IAAI,IAAI,IAAI,CAAC,CAAC;AACrC"}
|
package/dist/internal/bind.js
CHANGED
|
@@ -19,7 +19,7 @@ import * as MutableHashMap from "effect/MutableHashMap";
|
|
|
19
19
|
*/
|
|
20
20
|
const makeMemo = () => ({
|
|
21
21
|
suggestions: MutableHashMap.empty(),
|
|
22
|
-
fits:
|
|
22
|
+
fits: /* @__PURE__ */ new Map(),
|
|
23
23
|
issues: {
|
|
24
24
|
count: 0,
|
|
25
25
|
noted: false
|
|
@@ -108,11 +108,11 @@ const patternIssues = (memo, known, pattern, at) => Result.match(parse(pattern),
|
|
|
108
108
|
onSuccess: (parsed) => {
|
|
109
109
|
const fits = known.length === 0 || Arr.reduce(known, false, (before, descriptor) => {
|
|
110
110
|
const key = `${descriptor.kind}\u0000${pattern}`;
|
|
111
|
-
return Option.getOrElse(
|
|
111
|
+
return Option.getOrElse(Option.fromUndefinedOr(memo.fits.get(key)), () => {
|
|
112
112
|
memo.work.steps += descriptor.catalog.fields.length * Math.max(parsed.length, 1);
|
|
113
113
|
if (memo.work.steps > 1e6) return true;
|
|
114
114
|
const found = fitsCatalog(parsed, descriptor.catalog);
|
|
115
|
-
|
|
115
|
+
memo.fits.set(key, found);
|
|
116
116
|
return found;
|
|
117
117
|
}) || before;
|
|
118
118
|
});
|
|
@@ -137,6 +137,10 @@ const fieldsAt = (context, field) => Arr.flatMap(context.applicable, (descriptor
|
|
|
137
137
|
/** The field of the principal that an operand names, or the issue when there is none. */
|
|
138
138
|
const principalField = (context, ref, at) => {
|
|
139
139
|
const found = lookup(context.domain.principalCatalog, ref);
|
|
140
|
+
if (found !== void 0 && !found.plain) return {
|
|
141
|
+
path: at,
|
|
142
|
+
issue: `A condition may not read the field "${ref}" of the principal. A value above it may be an object that does not have it as a data field, as a class with a getter, a date or a list. Use a predicate for it.`
|
|
143
|
+
};
|
|
140
144
|
return found !== void 0 ? found : {
|
|
141
145
|
path: at,
|
|
142
146
|
issue: suggestIn(context.memo, `The principal has no field "${ref}"`, ref, "~principal", () => namesOf(context.domain.principalCatalog))
|
|
@@ -279,7 +283,7 @@ const hasAction = (descriptor, actions) => actions.includes("*") || Arr.some(act
|
|
|
279
283
|
*/
|
|
280
284
|
const ruleIssues = (domain, rule, at, memo = makeMemo()) => {
|
|
281
285
|
const all = rule.resources.includes("*");
|
|
282
|
-
const names =
|
|
286
|
+
const names = Array.from(new Set(Arr.filter(rule.resources, (kind) => kind !== "*")));
|
|
283
287
|
const kinds = Array.from(HashMap.keys(domain.resources));
|
|
284
288
|
const named = Arr.flatMap(names, (kind) => Option.toArray(HashMap.get(domain.resources, kind)));
|
|
285
289
|
const known = all ? Array.from(HashMap.values(domain.resources)) : named;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bind.js","names":["Catalog.accepts","Catalog.lookup","Patterns.parse","Patterns.fitsCatalog","Limits.searchSteps","Catalog.anyKinds","Nodes.isNode","Place.pathOf","Place.at"],"sources":["../../src/internal/bind.ts"],"sourcesContent":["/**\n * The binding of rules to a domain. It is the one check of a rule, for a rule\n * from the builder, from `Policy.rule`, and from JSON alike. It reads the\n * catalogs of the resources and of the principal, and it gives one issue for\n * each problem, with the path of the problem and, for a name with a mistake,\n * the name that it may mean.\n */\nimport * as Arr from \"effect/Array\"\nimport * as HashMap from \"effect/HashMap\"\nimport * as MutableHashMap from \"effect/MutableHashMap\"\nimport * as Option from \"effect/Option\"\nimport * as Result from \"effect/Result\"\nimport type * as Domain from \"../Domain.ts\"\nimport type * as Resource from \"../Resource.ts\"\nimport * as Catalog from \"./catalog.ts\"\nimport * as Limits from \"./limits.ts\"\nimport * as Nodes from \"./nodes.ts\"\nimport * as Patterns from \"./patterns.ts\"\nimport * as Place from \"./place.ts\"\nimport { withSuggestion } from \"./suggest.ts\"\n\n/** One problem of a rule or a policy, with its path. */\nexport interface Issue {\n\treadonly path: ReadonlyArray<PropertyKey>\n\treadonly issue: string\n}\n\n/** The data of a rule that the binding reads. */\nexport interface RuleData {\n\treadonly actions: ReadonlyArray<string>\n\treadonly resources: ReadonlyArray<string>\n\treadonly when?: unknown\n\treadonly fields?: ReadonlyArray<string>\n}\n\n/**\n * The memo of one binding, for all its rules. A policy may name one wrong\n * field or one pattern in very many places, so the work on each is done one\n * time. It goes away when the binding ends.\n */\ninterface Memo {\n\t/** The suggestion for each name, by the place where it was looked for. */\n\treadonly suggestions: MutableHashMap.MutableHashMap<string, string>\n\t/** If a pattern matches a field of a kind, by the kind and the pattern. */\n\treadonly fits: MutableHashMap.MutableHashMap<string, boolean>\n\t/** The count of the issues of conditions so far. It belongs to one binding. */\n\treadonly issues: { count: number; noted: boolean }\n\t/** The steps of the checks of patterns so far. It belongs to one binding. */\n\treadonly work: { steps: number; noted: boolean }\n}\n\nconst makeMemo = (): Memo => ({\n\tsuggestions: MutableHashMap.empty(),\n\tfits: MutableHashMap.empty(),\n\tissues: { count: 0, noted: false },\n\twork: { steps: 0, noted: false },\n})\n\n/**\n * The most names that one binding makes a suggestion for. A suggestion\n * compares the name with every name of a catalog, so a policy with very many\n * wrong names would take a long time.\n */\nconst maxSuggestions = 32\n\n/** Adds a suggestion to a message, with the memo of the binding. */\nconst suggestIn = (\n\tmemo: Memo,\n\tmessage: string,\n\tname: string,\n\tscope: string,\n\tnames: () => ReadonlyArray<string>,\n): string => {\n\tconst key = `${scope}\\u0000${name}`\n\tconst known = MutableHashMap.get(memo.suggestions, key)\n\tif (Option.isSome(known)) {\n\t\treturn known.value\n\t}\n\tif (MutableHashMap.size(memo.suggestions) >= maxSuggestions) {\n\t\treturn message\n\t}\n\tconst made = withSuggestion(message, name, names())\n\tMutableHashMap.set(memo.suggestions, key, made)\n\treturn made\n}\n\n/** The kinds that a rule may run on, from the view of its checks. */\ninterface Context {\n\treadonly domain: Domain.Any\n\treadonly memo: Memo\n\t/** The rule is for every resource (`\"*\"`). */\n\treadonly all: boolean\n\t/** The resources that the rule names and that the domain has. */\n\treadonly named: ReadonlyArray<Resource.Descriptor>\n\t/** The named resources that have one of the actions of the rule. */\n\treadonly applicable: ReadonlyArray<Resource.Descriptor>\n\t/**\n\t * The resources whose paths a path test may read: every resource with one\n\t * of the actions, for a rule for every resource.\n\t */\n\treadonly paths: ReadonlyArray<Resource.Descriptor>\n}\n\ntype Operand = Nodes.Operand\n\n/**\n * Tests if a field may hold a value of the type of a fixed value. A test with\n * `null` is a test that the field is missing, so `null` always fits. A number\n * fits any field of numbers: a test of an integer field with `1.5` or\n * `Infinity` has a meaning, as `lt` shows.\n */\nconst fitsLiteral = (kinds: Catalog.Kinds, value: unknown): boolean =>\n\tvalue === null ||\n\t(typeof value === \"number\" && kinds.number !== \"none\") ||\n\tCatalog.accepts(kinds, value)\n\n/**\n * Tests if two fields may hold one scalar. A principal field that is `null`\n * or that holds an object is a missing value, and a test with a missing value\n * is false, so only scalars count.\n */\nconst meet = (left: Catalog.Kinds, right: Catalog.Kinds): boolean =>\n\t(left.string && right.string) ||\n\t(left.boolean && right.boolean) ||\n\t(left.number !== \"none\" && right.number !== \"none\") ||\n\tArr.some(left.literals, (value) => Catalog.accepts(right, value)) ||\n\tArr.some(right.literals, (value) => Catalog.accepts(left, value))\n\nconst hasStrings = (kinds: Catalog.Kinds): boolean =>\n\tkinds.string || Arr.some(kinds.literals, (value) => typeof value === \"string\")\n\nconst hasNumbers = (kinds: Catalog.Kinds): boolean =>\n\tkinds.number !== \"none\" ||\n\tArr.some(kinds.literals, (value) => typeof value === \"number\")\n\n/** Tests if a field may hold a value with an order: a number or a string. */\nconst isOrdered = (kinds: Catalog.Kinds): boolean =>\n\thasStrings(kinds) || hasNumbers(kinds)\n\n/** Tests if two fields may hold values of one ordered type. */\nconst orderedMeet = (left: Catalog.Kinds, right: Catalog.Kinds): boolean =>\n\t(hasStrings(left) && hasStrings(right)) ||\n\t(hasNumbers(left) && hasNumbers(right))\n\n/** The texts of the fields of a catalog that have only names, for suggestions. */\nconst namesOf = (catalog: Catalog.Catalog): ReadonlyArray<string> =>\n\tArr.map(\n\t\tArr.filter(catalog.fields, (field) =>\n\t\t\tArr.every(field.path, (segment) => typeof segment === \"string\"),\n\t\t),\n\t\t(field) => field.text,\n\t)\n\n/** The issue of a field that is not a field of every resource of the rule. */\nconst fieldIssue = (\n\tcontext: Context,\n\tfield: string,\n\tat: ReadonlyArray<PropertyKey>,\n): ReadonlyArray<Issue> => {\n\tconst message = `The field \"${field}\" is not a field of every resource of the rule`\n\tif (context.all || context.named.length === 0) {\n\t\treturn [{ path: at, issue: message }]\n\t}\n\tconst without = Arr.filter(\n\t\tcontext.named,\n\t\t(descriptor) => Catalog.lookup(descriptor.catalog, field) === undefined,\n\t)\n\treturn without.length === 0\n\t\t? []\n\t\t: [\n\t\t\t\t{\n\t\t\t\t\tpath: at,\n\t\t\t\t\tissue: suggestIn(context.memo, message, field, without[0]!.kind, () =>\n\t\t\t\t\t\tnamesOf(without[0]!.catalog),\n\t\t\t\t\t),\n\t\t\t\t},\n\t\t\t]\n}\n\n/**\n * The issues of a field that a condition reads: a field that is not a field\n * of every resource of the rule, or a field that is not plain data in one of\n * them. A read of a value that is not plain data, as a getter of a class, is\n * a defect in a decision. A residual may leave the read out, so the residual\n * would say \"yes\" where the decision fails. So the binding does not take\n * such a read: see `Catalog.Field.plain`.\n */\nconst readIssues = (\n\tcontext: Context,\n\tfield: string,\n\tat: ReadonlyArray<PropertyKey>,\n): ReadonlyArray<Issue> => {\n\tconst exists = fieldIssue(context, field, at)\n\tif (exists.length > 0) {\n\t\treturn exists\n\t}\n\treturn Arr.some(\n\t\tcontext.named,\n\t\t(descriptor) => !(Catalog.lookup(descriptor.catalog, field)?.plain ?? true),\n\t)\n\t\t? [\n\t\t\t\t{\n\t\t\t\t\tpath: at,\n\t\t\t\t\tissue: `A condition may not read the field \"${field}\". A value above it may be an object that does not have it as a data field, as a class with a getter, a date or a list. Use a predicate for it.`,\n\t\t\t\t},\n\t\t\t]\n\t\t: []\n}\n\n/**\n * The issues of a field pattern: a pattern that does not parse, or that\n * matches no field of the resources of the rule, as a pattern with a mistake\n * in a name does.\n */\nconst patternIssues = (\n\tmemo: Memo,\n\tknown: ReadonlyArray<Resource.Descriptor>,\n\tpattern: string,\n\tat: ReadonlyArray<PropertyKey>,\n): ReadonlyArray<Issue> =>\n\tResult.match(Patterns.parse(pattern), {\n\t\tonFailure: (issue) => [{ path: at, issue }],\n\t\tonSuccess: (parsed) => {\n\t\t\t// A rule with no known resource has an issue for that already. The\n\t\t\t// pattern is checked on every kind, not only up to the first that it\n\t\t\t// fits: a decision on a kind does this check, so the limit of the\n\t\t\t// work below is a limit of the work of a decision too.\n\t\t\tconst fits =\n\t\t\t\tknown.length === 0 ||\n\t\t\t\tArr.reduce(known, false, (before, descriptor) => {\n\t\t\t\t\tconst key = `${descriptor.kind}\\u0000${pattern}`\n\t\t\t\t\treturn (\n\t\t\t\t\t\tOption.getOrElse(MutableHashMap.get(memo.fits, key), () => {\n\t\t\t\t\t\t\t// Each check of a pattern reads the fields of a catalog. The\n\t\t\t\t\t\t\t// work of one binding has a limit, so very many patterns stop\n\t\t\t\t\t\t\t// with one issue, and the policy does not load.\n\t\t\t\t\t\t\tmemo.work.steps +=\n\t\t\t\t\t\t\t\tdescriptor.catalog.fields.length * Math.max(parsed.length, 1)\n\t\t\t\t\t\t\tif (memo.work.steps > Limits.searchSteps) {\n\t\t\t\t\t\t\t\treturn true\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tconst found = Patterns.fitsCatalog(parsed, descriptor.catalog)\n\t\t\t\t\t\t\tMutableHashMap.set(memo.fits, key, found)\n\t\t\t\t\t\t\treturn found\n\t\t\t\t\t\t}) || before\n\t\t\t\t\t)\n\t\t\t\t})\n\t\t\tif (memo.work.steps > Limits.searchSteps && !memo.work.noted) {\n\t\t\t\tmemo.work.noted = true\n\t\t\t\treturn [\n\t\t\t\t\t{\n\t\t\t\t\t\tpath: at,\n\t\t\t\t\t\tissue: `The field patterns of the policy take more than ${String(Limits.searchSteps)} steps to check. The most is ${String(Limits.searchSteps)}.`,\n\t\t\t\t\t},\n\t\t\t\t]\n\t\t\t}\n\t\t\treturn fits\n\t\t\t\t? []\n\t\t\t\t: [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tpath: at,\n\t\t\t\t\t\t\tissue: `The field pattern \"${pattern}\" matches no field of the resources of the rule`,\n\t\t\t\t\t\t},\n\t\t\t\t\t]\n\t\t},\n\t})\n\n/** The fields of the resources that a test of the rule may read. */\nconst fieldsAt = (\n\tcontext: Context,\n\tfield: string,\n): ReadonlyArray<Catalog.Field> =>\n\tArr.flatMap(context.applicable, (descriptor) => {\n\t\tconst found = Catalog.lookup(descriptor.catalog, field)\n\t\treturn found === undefined ? [] : [found]\n\t})\n\n/** The field of the principal that an operand names, or the issue when there is none. */\nconst principalField = (\n\tcontext: Context,\n\tref: string,\n\tat: ReadonlyArray<PropertyKey>,\n): Catalog.Field | Issue => {\n\tconst found = Catalog.lookup(context.domain.principalCatalog, ref)\n\treturn found !== undefined\n\t\t? found\n\t\t: {\n\t\t\t\tpath: at,\n\t\t\t\tissue: suggestIn(\n\t\t\t\t\tcontext.memo,\n\t\t\t\t\t`The principal has no field \"${ref}\"`,\n\t\t\t\t\tref,\n\t\t\t\t\t\"~principal\",\n\t\t\t\t\t() => namesOf(context.domain.principalCatalog),\n\t\t\t\t),\n\t\t\t}\n}\n\nconst isIssue = (value: Catalog.Field | Issue): value is Issue =>\n\t\"issue\" in value\n\n/** The issues of one operand of a test of a field. */\nconst operandIssues = (\n\tcontext: Context,\n\tfield: string,\n\tordered: boolean,\n\toperand: Operand,\n\tat: ReadonlyArray<PropertyKey>,\n): ReadonlyArray<Issue> => {\n\tconst fields = fieldsAt(context, field)\n\tif (operand._tag === \"Literal\") {\n\t\tconst value = operand.value\n\t\tif (ordered && typeof value !== \"string\" && typeof value !== \"number\") {\n\t\t\treturn [\n\t\t\t\t{\n\t\t\t\t\tpath: at,\n\t\t\t\t\tissue: `The field \"${field}\" has an ordered test with a value that has no order`,\n\t\t\t\t},\n\t\t\t]\n\t\t}\n\t\treturn [\n\t\t\t...(Arr.some(fields, (found) => !fitsLiteral(found.kinds, value))\n\t\t\t\t? [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tpath: at,\n\t\t\t\t\t\t\tissue: `The field \"${field}\" can not hold the value ${JSON.stringify(value)}`,\n\t\t\t\t\t\t},\n\t\t\t\t\t]\n\t\t\t\t: []),\n\t\t\t...(ordered && Arr.some(fields, (found) => !isOrdered(found.kinds))\n\t\t\t\t? [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tpath: at,\n\t\t\t\t\t\t\tissue: `The field \"${field}\" has an ordered test with a value that has no order`,\n\t\t\t\t\t\t},\n\t\t\t\t\t]\n\t\t\t\t: []),\n\t\t]\n\t}\n\tconst principal = principalField(context, operand.path, at)\n\tif (isIssue(principal)) {\n\t\treturn [principal]\n\t}\n\treturn [\n\t\t...(Arr.some(fields, (found) => !meet(found.kinds, principal.kinds))\n\t\t\t? [\n\t\t\t\t\t{\n\t\t\t\t\t\tpath: at,\n\t\t\t\t\t\tissue: `The field \"${field}\" can not hold a value of the principal field \"${operand.path}\"`,\n\t\t\t\t\t},\n\t\t\t\t]\n\t\t\t: []),\n\t\t...(ordered &&\n\t\tArr.some(fields, (found) => !orderedMeet(found.kinds, principal.kinds))\n\t\t\t? [\n\t\t\t\t\t{\n\t\t\t\t\t\tpath: at,\n\t\t\t\t\t\tissue: `The field \"${field}\" has an ordered test with a value that has no order`,\n\t\t\t\t\t},\n\t\t\t\t]\n\t\t\t: []),\n\t]\n}\n\n/**\n * Tests if the path of a resource may start with a prefix: it is as long as\n * the prefix, and each fixed string of the prefix is the string of the path\n * at its place, when the path has a string there.\n */\nconst mayStartWith = (\n\tdescriptor: Resource.Descriptor,\n\tstrings: ReadonlyArray<string | null>,\n): boolean =>\n\t(descriptor.path?.length ?? 0) >= strings.length &&\n\tArr.every(strings, (string, index) => {\n\t\tconst segment = descriptor.path?.[index]\n\t\treturn string === null || typeof segment !== \"string\" || segment === string\n\t})\n\n/** The kinds of the part of the path of a resource at a place. */\nconst pathPartKinds = (\n\tdescriptor: Resource.Descriptor,\n\tindex: number,\n): Catalog.Kinds | string | undefined => {\n\tconst segment = descriptor.path?.[index]\n\treturn segment === undefined || typeof segment === \"string\"\n\t\t? segment\n\t\t: (Catalog.lookup(descriptor.catalog, segment.field)?.kinds ??\n\t\t\t\tCatalog.anyKinds)\n}\n\n/** The issues of the prefix of a path test. */\nconst underIssues = (\n\tcontext: Context,\n\tprefix: ReadonlyArray<Operand>,\n\tat: ReadonlyArray<PropertyKey>,\n): ReadonlyArray<Issue> => {\n\tconst strings = Arr.map(prefix, (operand) =>\n\t\toperand._tag === \"Literal\" && typeof operand.value === \"string\"\n\t\t\t? operand.value\n\t\t\t: null,\n\t)\n\t// The test can match only a path that starts with the fixed strings of\n\t// the prefix, so the other resources are left out.\n\tconst candidates = Arr.filter(context.paths, (descriptor) =>\n\t\tmayStartWith(descriptor, strings),\n\t)\n\treturn Arr.flatMap(prefix, (operand, index): ReadonlyArray<Issue> => {\n\t\tconst place = [...at, \"prefix\", index]\n\t\tif (operand._tag === \"Literal\") {\n\t\t\treturn Arr.some(candidates, (descriptor) => {\n\t\t\t\tconst kinds = pathPartKinds(descriptor, index)\n\t\t\t\treturn typeof kinds === \"object\" && !fitsLiteral(kinds, operand.value)\n\t\t\t})\n\t\t\t\t? [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tpath: place,\n\t\t\t\t\t\t\tissue: `The path part ${String(index)} can not hold the value ${JSON.stringify(operand.value)}`,\n\t\t\t\t\t\t},\n\t\t\t\t\t]\n\t\t\t\t: []\n\t\t}\n\t\tconst principal = principalField(context, operand.path, place)\n\t\tif (isIssue(principal)) {\n\t\t\treturn [principal]\n\t\t}\n\t\treturn Arr.some(candidates, (descriptor) => {\n\t\t\tconst kinds = pathPartKinds(descriptor, index)\n\t\t\treturn kinds === undefined\n\t\t\t\t? true\n\t\t\t\t: typeof kinds === \"string\"\n\t\t\t\t\t? !Catalog.accepts(principal.kinds, kinds)\n\t\t\t\t\t: !meet(kinds, principal.kinds)\n\t\t})\n\t\t\t? [\n\t\t\t\t\t{\n\t\t\t\t\t\tpath: place,\n\t\t\t\t\t\tissue: `The principal field \"${operand.path}\" can not hold a part of the path of the resource`,\n\t\t\t\t\t},\n\t\t\t\t]\n\t\t\t: []\n\t})\n}\n\nconst notWhole = (at: ReadonlyArray<PropertyKey>): Issue => ({\n\tpath: at,\n\tissue: \"The condition is not a whole condition tree\",\n})\n\n/**\n * The most issues that the walk of the conditions of one binding gives. A\n * policy may have very many wrong tests, and each issue keeps its path.\n */\nconst maxIssues = 100\n\n/** A path that the walk makes only when it needs it, for an issue. */\ntype LazyPath = () => ReadonlyArray<PropertyKey>\n\n/**\n * Puts the path of a node before the paths of its issues. A walk keeps no\n * path for a node with no issue, so a deep and wide condition costs little.\n */\nconst under = (\n\tmemo: Memo,\n\tat: LazyPath,\n\tissues: ReadonlyArray<Issue>,\n): ReadonlyArray<Issue> => {\n\tif (issues.length === 0) {\n\t\treturn issues\n\t}\n\tconst room = maxIssues - memo.issues.count\n\tmemo.issues.count += issues.length\n\tconst kept = Arr.map(Arr.take(issues, Math.max(room, 0)), (issue) => ({\n\t\tpath: [...at(), ...issue.path],\n\t\tissue: issue.issue,\n\t}))\n\tconst first = memo.issues.count > maxIssues && !memo.issues.noted\n\tmemo.issues.noted ||= first\n\treturn first\n\t\t? [\n\t\t\t\t...kept,\n\t\t\t\t{\n\t\t\t\t\tpath: [],\n\t\t\t\t\tissue: `The policy has more problems. The first ${String(maxIssues)} are here.`,\n\t\t\t\t},\n\t\t\t]\n\t\t: kept\n}\n\n/**\n * The issues of a condition. The constructors of the nodes checked the shape\n * of each node, so this checks the names and the types against the domain. A\n * node that the condition uses in many places is checked one time. The nodes\n * it has seen are in a native `Set`, because it must find a node by the node\n * itself: `Equal` compares nodes by structure.\n */\nconst conditionIssues = (\n\tcontext: Context,\n\tnode: Nodes.Node,\n\tat: LazyPath,\n\tseen: Set<Nodes.Node>,\n): ReadonlyArray<Issue> => {\n\tif (seen.has(node)) {\n\t\treturn []\n\t}\n\tseen.add(node)\n\tconst here = (issues: ReadonlyArray<Issue>) => under(context.memo, at, issues)\n\tswitch (node._tag) {\n\t\tcase \"Compare\": {\n\t\t\tconst exists = readIssues(context, node.field, [\"field\"])\n\t\t\treturn here(\n\t\t\t\texists.length > 0\n\t\t\t\t\t? exists\n\t\t\t\t\t: operandIssues(\n\t\t\t\t\t\t\tcontext,\n\t\t\t\t\t\t\tnode.field,\n\t\t\t\t\t\t\tnode.op !== \"eq\" && node.op !== \"ne\",\n\t\t\t\t\t\t\tnode.value,\n\t\t\t\t\t\t\t[\"value\"],\n\t\t\t\t\t\t),\n\t\t\t)\n\t\t}\n\t\tcase \"In\": {\n\t\t\tconst exists = readIssues(context, node.field, [\"field\"])\n\t\t\treturn here(\n\t\t\t\texists.length > 0\n\t\t\t\t\t? exists\n\t\t\t\t\t: Arr.flatMap(node.values, (operand, index) =>\n\t\t\t\t\t\t\toperandIssues(context, node.field, false, operand, [\n\t\t\t\t\t\t\t\t\"values\",\n\t\t\t\t\t\t\t\tindex,\n\t\t\t\t\t\t\t]),\n\t\t\t\t\t\t),\n\t\t\t)\n\t\t}\n\t\tcase \"Exists\":\n\t\t\treturn here(readIssues(context, node.field, [\"field\"]))\n\t\tcase \"Under\":\n\t\t\treturn here(underIssues(context, node.prefix, []))\n\t\tcase \"And\":\n\t\tcase \"Or\":\n\t\t\treturn Arr.flatMap(node.conditions, (child, index) =>\n\t\t\t\tconditionIssues(\n\t\t\t\t\tcontext,\n\t\t\t\t\tchild,\n\t\t\t\t\t() => [...at(), \"conditions\", index],\n\t\t\t\t\tseen,\n\t\t\t\t),\n\t\t\t)\n\t\tcase \"Not\":\n\t\t\treturn conditionIssues(\n\t\t\t\tcontext,\n\t\t\t\tnode.condition,\n\t\t\t\t() => [...at(), \"condition\"],\n\t\t\t\tseen,\n\t\t\t)\n\t\tdefault:\n\t\t\treturn []\n\t}\n}\n\nconst hasAction = (\n\tdescriptor: Resource.Descriptor,\n\tactions: ReadonlyArray<string>,\n): boolean =>\n\tactions.includes(\"*\") ||\n\tArr.some(actions, (action) => descriptor.actions.includes(action))\n\n/**\n * Checks one rule against a domain. It gives one issue for each problem. `at`\n * is the path of the rule, for the paths of the issues.\n */\nexport const ruleIssues = (\n\tdomain: Domain.Any,\n\trule: RuleData,\n\tat: ReadonlyArray<PropertyKey>,\n\tmemo: Memo = makeMemo(),\n): ReadonlyArray<Issue> => {\n\tconst all = rule.resources.includes(\"*\")\n\t// Each kind is checked one time, also when the list names it again.\n\tconst names = Arr.dedupe(Arr.filter(rule.resources, (kind) => kind !== \"*\"))\n\tconst kinds = Array.from(HashMap.keys(domain.resources))\n\tconst named = Arr.flatMap(names, (kind) =>\n\t\tOption.toArray(HashMap.get(domain.resources, kind)),\n\t)\n\tconst known = all ? Array.from(HashMap.values(domain.resources)) : named\n\t// The rule never runs on a kind without one of its actions, so the types\n\t// of its tests are checked only on the kinds with one.\n\tconst applicable = Arr.filter(named, (descriptor) =>\n\t\thasAction(descriptor, rule.actions),\n\t)\n\tconst context: Context = {\n\t\tdomain,\n\t\tmemo,\n\t\tall,\n\t\tnamed,\n\t\tapplicable,\n\t\tpaths: all\n\t\t\t? Arr.filter(known, (descriptor) => hasAction(descriptor, rule.actions))\n\t\t\t: applicable,\n\t}\n\tconst actions = Arr.dedupe(\n\t\tArr.flatMap(known, (descriptor) => descriptor.actions),\n\t)\n\treturn [\n\t\t...Arr.map(\n\t\t\tArr.filter(names, (kind) => !HashMap.has(domain.resources, kind)),\n\t\t\t(kind): Issue => ({\n\t\t\t\tpath: [...at, \"resources\"],\n\t\t\t\tissue: suggestIn(\n\t\t\t\t\tmemo,\n\t\t\t\t\t`There is no resource with the kind \"${kind}\"`,\n\t\t\t\t\tkind,\n\t\t\t\t\t\"~kinds\",\n\t\t\t\t\t() => kinds,\n\t\t\t\t),\n\t\t\t}),\n\t\t),\n\t\t...Arr.map(\n\t\t\tArr.filter(\n\t\t\t\trule.actions,\n\t\t\t\t(action) => action !== \"*\" && !actions.includes(action),\n\t\t\t),\n\t\t\t(action): Issue => ({\n\t\t\t\tpath: [...at, \"actions\"],\n\t\t\t\tissue: suggestIn(\n\t\t\t\t\tmemo,\n\t\t\t\t\t`The action \"${action}\" is not an action of these resources`,\n\t\t\t\t\taction,\n\t\t\t\t\t`~actions\\u0000${names.join(\",\")}`,\n\t\t\t\t\t() => actions,\n\t\t\t\t),\n\t\t\t}),\n\t\t),\n\t\t...Arr.flatMap(rule.fields ?? [], (field, index) =>\n\t\t\tfield.includes(\"*\")\n\t\t\t\t? patternIssues(memo, known, field, [...at, \"fields\", index])\n\t\t\t\t: fieldIssue(context, field, [...at, \"fields\", index]),\n\t\t),\n\t\t...(rule.when === undefined\n\t\t\t? []\n\t\t\t: Nodes.isNode(rule.when)\n\t\t\t\t? conditionIssues(context, rule.when, () => [...at, \"when\"], new Set())\n\t\t\t\t: [notWhole([...at, \"when\"])]),\n\t]\n}\n\n/** The JSON form of a policy tree, or a policy tree, as the binding reads it. */\ntype TreeNode =\n\t| { readonly _tag: \"Rules\"; readonly rules: ReadonlyArray<RuleData> }\n\t| {\n\t\t\treadonly _tag: \"All\" | \"Any\" | \"FirstOf\"\n\t\t\treadonly policies: ReadonlyArray<TreeNode>\n\t }\n\t| {\n\t\t\treadonly _tag: \"Ceiling\"\n\t\t\treadonly cap: TreeNode\n\t\t\treadonly policy: TreeNode\n\t }\n\n/** Checks each rule of a policy tree against a domain. */\nexport const treeIssues = (\n\tdomain: Domain.Any,\n\troot: TreeNode,\n): ReadonlyArray<Issue> => {\n\t// The rules come out of the tree first, and each one is checked after.\n\t// A check of a condition goes down its levels, so a check inside the walk\n\t// of the tree would go down the levels of both.\n\t// The place of each rule shares the path of its parent, and the full path\n\t// is made only when its rule is checked. A deep tree with very many rules\n\t// would need very much memory for a full copy of the path of each rule.\n\tconst rulesOf = (\n\t\tnode: TreeNode,\n\t\tplace: Place.Place | undefined,\n\t\tlevel: number,\n\t): ReadonlyArray<Issue | readonly [RuleData, Place.Place]> => {\n\t\tif (level > Limits.policyDepth) {\n\t\t\treturn [\n\t\t\t\t{\n\t\t\t\t\tpath: Place.pathOf(place),\n\t\t\t\t\tissue: `A policy tree has more than ${String(Limits.policyDepth)} levels. The most is ${String(Limits.policyDepth)}.`,\n\t\t\t\t},\n\t\t\t]\n\t\t}\n\t\tswitch (node._tag) {\n\t\t\tcase \"Rules\":\n\t\t\t\treturn Arr.map(\n\t\t\t\t\tnode.rules,\n\t\t\t\t\t(rule, index) => [rule, Place.at(place, \"rules\", index)] as const,\n\t\t\t\t)\n\t\t\tcase \"All\":\n\t\t\tcase \"Any\":\n\t\t\tcase \"FirstOf\":\n\t\t\t\treturn Arr.flatMap(node.policies, (child, index) =>\n\t\t\t\t\trulesOf(child, Place.at(place, \"policies\", index), level + 1),\n\t\t\t\t)\n\t\t\tcase \"Ceiling\":\n\t\t\t\treturn [\n\t\t\t\t\t...rulesOf(node.cap, Place.at(place, \"cap\"), level + 1),\n\t\t\t\t\t...rulesOf(node.policy, Place.at(place, \"policy\"), level + 1),\n\t\t\t\t]\n\t\t}\n\t}\n\tconst memo = makeMemo()\n\treturn Arr.flatMap(rulesOf(root, undefined, 1), (found) =>\n\t\tArray.isArray(found)\n\t\t\t? ruleIssues(domain, found[0], Place.pathOf(found[1]), memo)\n\t\t\t: [found as Issue],\n\t)\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"bind.js","names":["Catalog.accepts","Catalog.lookup","Patterns.parse","Patterns.fitsCatalog","Limits.searchSteps","Catalog.anyKinds","Nodes.isNode","Place.pathOf","Place.at"],"sources":["../../src/internal/bind.ts"],"sourcesContent":["/**\n * The binding of rules to a domain. It is the one check of a rule, for a rule\n * from the builder, from `Policy.rule`, and from JSON alike. It reads the\n * catalogs of the resources and of the principal, and it gives one issue for\n * each problem, with the path of the problem and, for a name with a mistake,\n * the name that it may mean.\n */\nimport * as Arr from \"effect/Array\"\nimport * as HashMap from \"effect/HashMap\"\nimport * as MutableHashMap from \"effect/MutableHashMap\"\nimport * as Option from \"effect/Option\"\nimport * as Result from \"effect/Result\"\nimport type * as Domain from \"../Domain.ts\"\nimport type * as Resource from \"../Resource.ts\"\nimport * as Catalog from \"./catalog.ts\"\nimport * as Limits from \"./limits.ts\"\nimport * as Nodes from \"./nodes.ts\"\nimport * as Patterns from \"./patterns.ts\"\nimport * as Place from \"./place.ts\"\nimport { withSuggestion } from \"./suggest.ts\"\n\n/** One problem of a rule or a policy, with its path. */\nexport interface Issue {\n\treadonly path: ReadonlyArray<PropertyKey>\n\treadonly issue: string\n}\n\n/** The data of a rule that the binding reads. */\nexport interface RuleData {\n\treadonly actions: ReadonlyArray<string>\n\treadonly resources: ReadonlyArray<string>\n\treadonly when?: unknown\n\treadonly fields?: ReadonlyArray<string>\n}\n\n/**\n * The memo of one binding, for all its rules. A policy may name one wrong\n * field or one pattern in very many places, so the work on each is done one\n * time. It goes away when the binding ends.\n */\ninterface Memo {\n\t/** The suggestion for each name, by the place where it was looked for. */\n\treadonly suggestions: MutableHashMap.MutableHashMap<string, string>\n\t/**\n\t * If a pattern matches a field of a kind, by the kind and the pattern. The\n\t * patterns come from a policy, so the map is native: the hash of a string\n\t * in Effect has no random seed, and a policy could hold very many\n\t * patterns with one hash. The engine hashes a string with a random seed.\n\t */\n\treadonly fits: Map<string, boolean>\n\t/** The count of the issues of conditions so far. It belongs to one binding. */\n\treadonly issues: { count: number; noted: boolean }\n\t/** The steps of the checks of patterns so far. It belongs to one binding. */\n\treadonly work: { steps: number; noted: boolean }\n}\n\nconst makeMemo = (): Memo => ({\n\tsuggestions: MutableHashMap.empty(),\n\tfits: new Map(),\n\tissues: { count: 0, noted: false },\n\twork: { steps: 0, noted: false },\n})\n\n/**\n * The most names that one binding makes a suggestion for. A suggestion\n * compares the name with every name of a catalog, so a policy with very many\n * wrong names would take a long time.\n */\nconst maxSuggestions = 32\n\n/** Adds a suggestion to a message, with the memo of the binding. */\nconst suggestIn = (\n\tmemo: Memo,\n\tmessage: string,\n\tname: string,\n\tscope: string,\n\tnames: () => ReadonlyArray<string>,\n): string => {\n\tconst key = `${scope}\\u0000${name}`\n\tconst known = MutableHashMap.get(memo.suggestions, key)\n\tif (Option.isSome(known)) {\n\t\treturn known.value\n\t}\n\tif (MutableHashMap.size(memo.suggestions) >= maxSuggestions) {\n\t\treturn message\n\t}\n\tconst made = withSuggestion(message, name, names())\n\tMutableHashMap.set(memo.suggestions, key, made)\n\treturn made\n}\n\n/** The kinds that a rule may run on, from the view of its checks. */\ninterface Context {\n\treadonly domain: Domain.Any\n\treadonly memo: Memo\n\t/** The rule is for every resource (`\"*\"`). */\n\treadonly all: boolean\n\t/** The resources that the rule names and that the domain has. */\n\treadonly named: ReadonlyArray<Resource.Descriptor>\n\t/** The named resources that have one of the actions of the rule. */\n\treadonly applicable: ReadonlyArray<Resource.Descriptor>\n\t/**\n\t * The resources whose paths a path test may read: every resource with one\n\t * of the actions, for a rule for every resource.\n\t */\n\treadonly paths: ReadonlyArray<Resource.Descriptor>\n}\n\ntype Operand = Nodes.Operand\n\n/**\n * Tests if a field may hold a value of the type of a fixed value. A test with\n * `null` is a test that the field is missing, so `null` always fits. A number\n * fits any field of numbers: a test of an integer field with `1.5` or\n * `Infinity` has a meaning, as `lt` shows.\n */\nconst fitsLiteral = (kinds: Catalog.Kinds, value: unknown): boolean =>\n\tvalue === null ||\n\t(typeof value === \"number\" && kinds.number !== \"none\") ||\n\tCatalog.accepts(kinds, value)\n\n/**\n * Tests if two fields may hold one scalar. A principal field that is `null`\n * or that holds an object is a missing value, and a test with a missing value\n * is false, so only scalars count.\n */\nconst meet = (left: Catalog.Kinds, right: Catalog.Kinds): boolean =>\n\t(left.string && right.string) ||\n\t(left.boolean && right.boolean) ||\n\t(left.number !== \"none\" && right.number !== \"none\") ||\n\tArr.some(left.literals, (value) => Catalog.accepts(right, value)) ||\n\tArr.some(right.literals, (value) => Catalog.accepts(left, value))\n\nconst hasStrings = (kinds: Catalog.Kinds): boolean =>\n\tkinds.string || Arr.some(kinds.literals, (value) => typeof value === \"string\")\n\nconst hasNumbers = (kinds: Catalog.Kinds): boolean =>\n\tkinds.number !== \"none\" ||\n\tArr.some(kinds.literals, (value) => typeof value === \"number\")\n\n/** Tests if a field may hold a value with an order: a number or a string. */\nconst isOrdered = (kinds: Catalog.Kinds): boolean =>\n\thasStrings(kinds) || hasNumbers(kinds)\n\n/** Tests if two fields may hold values of one ordered type. */\nconst orderedMeet = (left: Catalog.Kinds, right: Catalog.Kinds): boolean =>\n\t(hasStrings(left) && hasStrings(right)) ||\n\t(hasNumbers(left) && hasNumbers(right))\n\n/** The texts of the fields of a catalog that have only names, for suggestions. */\nconst namesOf = (catalog: Catalog.Catalog): ReadonlyArray<string> =>\n\tArr.map(\n\t\tArr.filter(catalog.fields, (field) =>\n\t\t\tArr.every(field.path, (segment) => typeof segment === \"string\"),\n\t\t),\n\t\t(field) => field.text,\n\t)\n\n/** The issue of a field that is not a field of every resource of the rule. */\nconst fieldIssue = (\n\tcontext: Context,\n\tfield: string,\n\tat: ReadonlyArray<PropertyKey>,\n): ReadonlyArray<Issue> => {\n\tconst message = `The field \"${field}\" is not a field of every resource of the rule`\n\tif (context.all || context.named.length === 0) {\n\t\treturn [{ path: at, issue: message }]\n\t}\n\tconst without = Arr.filter(\n\t\tcontext.named,\n\t\t(descriptor) => Catalog.lookup(descriptor.catalog, field) === undefined,\n\t)\n\treturn without.length === 0\n\t\t? []\n\t\t: [\n\t\t\t\t{\n\t\t\t\t\tpath: at,\n\t\t\t\t\tissue: suggestIn(context.memo, message, field, without[0]!.kind, () =>\n\t\t\t\t\t\tnamesOf(without[0]!.catalog),\n\t\t\t\t\t),\n\t\t\t\t},\n\t\t\t]\n}\n\n/**\n * The issues of a field that a condition reads: a field that is not a field\n * of every resource of the rule, or a field that is not plain data in one of\n * them. A read of a value that is not plain data, as a getter of a class, is\n * a defect in a decision. A residual may leave the read out, so the residual\n * would say \"yes\" where the decision fails. So the binding does not take\n * such a read: see `Catalog.Field.plain`.\n */\nconst readIssues = (\n\tcontext: Context,\n\tfield: string,\n\tat: ReadonlyArray<PropertyKey>,\n): ReadonlyArray<Issue> => {\n\tconst exists = fieldIssue(context, field, at)\n\tif (exists.length > 0) {\n\t\treturn exists\n\t}\n\treturn Arr.some(\n\t\tcontext.named,\n\t\t(descriptor) => !(Catalog.lookup(descriptor.catalog, field)?.plain ?? true),\n\t)\n\t\t? [\n\t\t\t\t{\n\t\t\t\t\tpath: at,\n\t\t\t\t\tissue: `A condition may not read the field \"${field}\". A value above it may be an object that does not have it as a data field, as a class with a getter, a date or a list. Use a predicate for it.`,\n\t\t\t\t},\n\t\t\t]\n\t\t: []\n}\n\n/**\n * The issues of a field pattern: a pattern that does not parse, or that\n * matches no field of the resources of the rule, as a pattern with a mistake\n * in a name does.\n */\nconst patternIssues = (\n\tmemo: Memo,\n\tknown: ReadonlyArray<Resource.Descriptor>,\n\tpattern: string,\n\tat: ReadonlyArray<PropertyKey>,\n): ReadonlyArray<Issue> =>\n\tResult.match(Patterns.parse(pattern), {\n\t\tonFailure: (issue) => [{ path: at, issue }],\n\t\tonSuccess: (parsed) => {\n\t\t\t// A rule with no known resource has an issue for that already. The\n\t\t\t// pattern is checked on every kind, not only up to the first that it\n\t\t\t// fits: a decision on a kind does this check, so the limit of the\n\t\t\t// work below is a limit of the work of a decision too.\n\t\t\tconst fits =\n\t\t\t\tknown.length === 0 ||\n\t\t\t\tArr.reduce(known, false, (before, descriptor) => {\n\t\t\t\t\tconst key = `${descriptor.kind}\\u0000${pattern}`\n\t\t\t\t\treturn (\n\t\t\t\t\t\tOption.getOrElse(Option.fromUndefinedOr(memo.fits.get(key)), () => {\n\t\t\t\t\t\t\t// Each check of a pattern reads the fields of a catalog. The\n\t\t\t\t\t\t\t// work of one binding has a limit, so very many patterns stop\n\t\t\t\t\t\t\t// with one issue, and the policy does not load.\n\t\t\t\t\t\t\tmemo.work.steps +=\n\t\t\t\t\t\t\t\tdescriptor.catalog.fields.length * Math.max(parsed.length, 1)\n\t\t\t\t\t\t\tif (memo.work.steps > Limits.searchSteps) {\n\t\t\t\t\t\t\t\treturn true\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tconst found = Patterns.fitsCatalog(parsed, descriptor.catalog)\n\t\t\t\t\t\t\tmemo.fits.set(key, found)\n\t\t\t\t\t\t\treturn found\n\t\t\t\t\t\t}) || before\n\t\t\t\t\t)\n\t\t\t\t})\n\t\t\tif (memo.work.steps > Limits.searchSteps && !memo.work.noted) {\n\t\t\t\tmemo.work.noted = true\n\t\t\t\treturn [\n\t\t\t\t\t{\n\t\t\t\t\t\tpath: at,\n\t\t\t\t\t\tissue: `The field patterns of the policy take more than ${String(Limits.searchSteps)} steps to check. The most is ${String(Limits.searchSteps)}.`,\n\t\t\t\t\t},\n\t\t\t\t]\n\t\t\t}\n\t\t\treturn fits\n\t\t\t\t? []\n\t\t\t\t: [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tpath: at,\n\t\t\t\t\t\t\tissue: `The field pattern \"${pattern}\" matches no field of the resources of the rule`,\n\t\t\t\t\t\t},\n\t\t\t\t\t]\n\t\t},\n\t})\n\n/** The fields of the resources that a test of the rule may read. */\nconst fieldsAt = (\n\tcontext: Context,\n\tfield: string,\n): ReadonlyArray<Catalog.Field> =>\n\tArr.flatMap(context.applicable, (descriptor) => {\n\t\tconst found = Catalog.lookup(descriptor.catalog, field)\n\t\treturn found === undefined ? [] : [found]\n\t})\n\n/** The field of the principal that an operand names, or the issue when there is none. */\nconst principalField = (\n\tcontext: Context,\n\tref: string,\n\tat: ReadonlyArray<PropertyKey>,\n): Catalog.Field | Issue => {\n\tconst found = Catalog.lookup(context.domain.principalCatalog, ref)\n\t// A read of the principal that is not plain data is a defect, as a read\n\t// of a resource is: see `readIssues`.\n\tif (found !== undefined && !found.plain) {\n\t\treturn {\n\t\t\tpath: at,\n\t\t\tissue: `A condition may not read the field \"${ref}\" of the principal. A value above it may be an object that does not have it as a data field, as a class with a getter, a date or a list. Use a predicate for it.`,\n\t\t}\n\t}\n\treturn found !== undefined\n\t\t? found\n\t\t: {\n\t\t\t\tpath: at,\n\t\t\t\tissue: suggestIn(\n\t\t\t\t\tcontext.memo,\n\t\t\t\t\t`The principal has no field \"${ref}\"`,\n\t\t\t\t\tref,\n\t\t\t\t\t\"~principal\",\n\t\t\t\t\t() => namesOf(context.domain.principalCatalog),\n\t\t\t\t),\n\t\t\t}\n}\n\nconst isIssue = (value: Catalog.Field | Issue): value is Issue =>\n\t\"issue\" in value\n\n/** The issues of one operand of a test of a field. */\nconst operandIssues = (\n\tcontext: Context,\n\tfield: string,\n\tordered: boolean,\n\toperand: Operand,\n\tat: ReadonlyArray<PropertyKey>,\n): ReadonlyArray<Issue> => {\n\tconst fields = fieldsAt(context, field)\n\tif (operand._tag === \"Literal\") {\n\t\tconst value = operand.value\n\t\tif (ordered && typeof value !== \"string\" && typeof value !== \"number\") {\n\t\t\treturn [\n\t\t\t\t{\n\t\t\t\t\tpath: at,\n\t\t\t\t\tissue: `The field \"${field}\" has an ordered test with a value that has no order`,\n\t\t\t\t},\n\t\t\t]\n\t\t}\n\t\treturn [\n\t\t\t...(Arr.some(fields, (found) => !fitsLiteral(found.kinds, value))\n\t\t\t\t? [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tpath: at,\n\t\t\t\t\t\t\tissue: `The field \"${field}\" can not hold the value ${JSON.stringify(value)}`,\n\t\t\t\t\t\t},\n\t\t\t\t\t]\n\t\t\t\t: []),\n\t\t\t...(ordered && Arr.some(fields, (found) => !isOrdered(found.kinds))\n\t\t\t\t? [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tpath: at,\n\t\t\t\t\t\t\tissue: `The field \"${field}\" has an ordered test with a value that has no order`,\n\t\t\t\t\t\t},\n\t\t\t\t\t]\n\t\t\t\t: []),\n\t\t]\n\t}\n\tconst principal = principalField(context, operand.path, at)\n\tif (isIssue(principal)) {\n\t\treturn [principal]\n\t}\n\treturn [\n\t\t...(Arr.some(fields, (found) => !meet(found.kinds, principal.kinds))\n\t\t\t? [\n\t\t\t\t\t{\n\t\t\t\t\t\tpath: at,\n\t\t\t\t\t\tissue: `The field \"${field}\" can not hold a value of the principal field \"${operand.path}\"`,\n\t\t\t\t\t},\n\t\t\t\t]\n\t\t\t: []),\n\t\t...(ordered &&\n\t\tArr.some(fields, (found) => !orderedMeet(found.kinds, principal.kinds))\n\t\t\t? [\n\t\t\t\t\t{\n\t\t\t\t\t\tpath: at,\n\t\t\t\t\t\tissue: `The field \"${field}\" has an ordered test with a value that has no order`,\n\t\t\t\t\t},\n\t\t\t\t]\n\t\t\t: []),\n\t]\n}\n\n/**\n * Tests if the path of a resource may start with a prefix: it is as long as\n * the prefix, and each fixed string of the prefix is the string of the path\n * at its place, when the path has a string there.\n */\nconst mayStartWith = (\n\tdescriptor: Resource.Descriptor,\n\tstrings: ReadonlyArray<string | null>,\n): boolean =>\n\t(descriptor.path?.length ?? 0) >= strings.length &&\n\tArr.every(strings, (string, index) => {\n\t\tconst segment = descriptor.path?.[index]\n\t\treturn string === null || typeof segment !== \"string\" || segment === string\n\t})\n\n/** The kinds of the part of the path of a resource at a place. */\nconst pathPartKinds = (\n\tdescriptor: Resource.Descriptor,\n\tindex: number,\n): Catalog.Kinds | string | undefined => {\n\tconst segment = descriptor.path?.[index]\n\treturn segment === undefined || typeof segment === \"string\"\n\t\t? segment\n\t\t: (Catalog.lookup(descriptor.catalog, segment.field)?.kinds ??\n\t\t\t\tCatalog.anyKinds)\n}\n\n/** The issues of the prefix of a path test. */\nconst underIssues = (\n\tcontext: Context,\n\tprefix: ReadonlyArray<Operand>,\n\tat: ReadonlyArray<PropertyKey>,\n): ReadonlyArray<Issue> => {\n\tconst strings = Arr.map(prefix, (operand) =>\n\t\toperand._tag === \"Literal\" && typeof operand.value === \"string\"\n\t\t\t? operand.value\n\t\t\t: null,\n\t)\n\t// The test can match only a path that starts with the fixed strings of\n\t// the prefix, so the other resources are left out.\n\tconst candidates = Arr.filter(context.paths, (descriptor) =>\n\t\tmayStartWith(descriptor, strings),\n\t)\n\treturn Arr.flatMap(prefix, (operand, index): ReadonlyArray<Issue> => {\n\t\tconst place = [...at, \"prefix\", index]\n\t\tif (operand._tag === \"Literal\") {\n\t\t\treturn Arr.some(candidates, (descriptor) => {\n\t\t\t\tconst kinds = pathPartKinds(descriptor, index)\n\t\t\t\treturn typeof kinds === \"object\" && !fitsLiteral(kinds, operand.value)\n\t\t\t})\n\t\t\t\t? [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tpath: place,\n\t\t\t\t\t\t\tissue: `The path part ${String(index)} can not hold the value ${JSON.stringify(operand.value)}`,\n\t\t\t\t\t\t},\n\t\t\t\t\t]\n\t\t\t\t: []\n\t\t}\n\t\tconst principal = principalField(context, operand.path, place)\n\t\tif (isIssue(principal)) {\n\t\t\treturn [principal]\n\t\t}\n\t\treturn Arr.some(candidates, (descriptor) => {\n\t\t\tconst kinds = pathPartKinds(descriptor, index)\n\t\t\treturn kinds === undefined\n\t\t\t\t? true\n\t\t\t\t: typeof kinds === \"string\"\n\t\t\t\t\t? !Catalog.accepts(principal.kinds, kinds)\n\t\t\t\t\t: !meet(kinds, principal.kinds)\n\t\t})\n\t\t\t? [\n\t\t\t\t\t{\n\t\t\t\t\t\tpath: place,\n\t\t\t\t\t\tissue: `The principal field \"${operand.path}\" can not hold a part of the path of the resource`,\n\t\t\t\t\t},\n\t\t\t\t]\n\t\t\t: []\n\t})\n}\n\nconst notWhole = (at: ReadonlyArray<PropertyKey>): Issue => ({\n\tpath: at,\n\tissue: \"The condition is not a whole condition tree\",\n})\n\n/**\n * The most issues that the walk of the conditions of one binding gives. A\n * policy may have very many wrong tests, and each issue keeps its path.\n */\nconst maxIssues = 100\n\n/** A path that the walk makes only when it needs it, for an issue. */\ntype LazyPath = () => ReadonlyArray<PropertyKey>\n\n/**\n * Puts the path of a node before the paths of its issues. A walk keeps no\n * path for a node with no issue, so a deep and wide condition costs little.\n */\nconst under = (\n\tmemo: Memo,\n\tat: LazyPath,\n\tissues: ReadonlyArray<Issue>,\n): ReadonlyArray<Issue> => {\n\tif (issues.length === 0) {\n\t\treturn issues\n\t}\n\tconst room = maxIssues - memo.issues.count\n\tmemo.issues.count += issues.length\n\tconst kept = Arr.map(Arr.take(issues, Math.max(room, 0)), (issue) => ({\n\t\tpath: [...at(), ...issue.path],\n\t\tissue: issue.issue,\n\t}))\n\tconst first = memo.issues.count > maxIssues && !memo.issues.noted\n\tmemo.issues.noted ||= first\n\treturn first\n\t\t? [\n\t\t\t\t...kept,\n\t\t\t\t{\n\t\t\t\t\tpath: [],\n\t\t\t\t\tissue: `The policy has more problems. The first ${String(maxIssues)} are here.`,\n\t\t\t\t},\n\t\t\t]\n\t\t: kept\n}\n\n/**\n * The issues of a condition. The constructors of the nodes checked the shape\n * of each node, so this checks the names and the types against the domain. A\n * node that the condition uses in many places is checked one time. The nodes\n * it has seen are in a native `Set`, because it must find a node by the node\n * itself: `Equal` compares nodes by structure.\n */\nconst conditionIssues = (\n\tcontext: Context,\n\tnode: Nodes.Node,\n\tat: LazyPath,\n\tseen: Set<Nodes.Node>,\n): ReadonlyArray<Issue> => {\n\tif (seen.has(node)) {\n\t\treturn []\n\t}\n\tseen.add(node)\n\tconst here = (issues: ReadonlyArray<Issue>) => under(context.memo, at, issues)\n\tswitch (node._tag) {\n\t\tcase \"Compare\": {\n\t\t\tconst exists = readIssues(context, node.field, [\"field\"])\n\t\t\treturn here(\n\t\t\t\texists.length > 0\n\t\t\t\t\t? exists\n\t\t\t\t\t: operandIssues(\n\t\t\t\t\t\t\tcontext,\n\t\t\t\t\t\t\tnode.field,\n\t\t\t\t\t\t\tnode.op !== \"eq\" && node.op !== \"ne\",\n\t\t\t\t\t\t\tnode.value,\n\t\t\t\t\t\t\t[\"value\"],\n\t\t\t\t\t\t),\n\t\t\t)\n\t\t}\n\t\tcase \"In\": {\n\t\t\tconst exists = readIssues(context, node.field, [\"field\"])\n\t\t\treturn here(\n\t\t\t\texists.length > 0\n\t\t\t\t\t? exists\n\t\t\t\t\t: Arr.flatMap(node.values, (operand, index) =>\n\t\t\t\t\t\t\toperandIssues(context, node.field, false, operand, [\n\t\t\t\t\t\t\t\t\"values\",\n\t\t\t\t\t\t\t\tindex,\n\t\t\t\t\t\t\t]),\n\t\t\t\t\t\t),\n\t\t\t)\n\t\t}\n\t\tcase \"Exists\":\n\t\t\treturn here(readIssues(context, node.field, [\"field\"]))\n\t\tcase \"Under\":\n\t\t\treturn here(underIssues(context, node.prefix, []))\n\t\tcase \"And\":\n\t\tcase \"Or\":\n\t\t\treturn Arr.flatMap(node.conditions, (child, index) =>\n\t\t\t\tconditionIssues(\n\t\t\t\t\tcontext,\n\t\t\t\t\tchild,\n\t\t\t\t\t() => [...at(), \"conditions\", index],\n\t\t\t\t\tseen,\n\t\t\t\t),\n\t\t\t)\n\t\tcase \"Not\":\n\t\t\treturn conditionIssues(\n\t\t\t\tcontext,\n\t\t\t\tnode.condition,\n\t\t\t\t() => [...at(), \"condition\"],\n\t\t\t\tseen,\n\t\t\t)\n\t\tdefault:\n\t\t\treturn []\n\t}\n}\n\nconst hasAction = (\n\tdescriptor: Resource.Descriptor,\n\tactions: ReadonlyArray<string>,\n): boolean =>\n\tactions.includes(\"*\") ||\n\tArr.some(actions, (action) => descriptor.actions.includes(action))\n\n/**\n * Checks one rule against a domain. It gives one issue for each problem. `at`\n * is the path of the rule, for the paths of the issues.\n */\nexport const ruleIssues = (\n\tdomain: Domain.Any,\n\trule: RuleData,\n\tat: ReadonlyArray<PropertyKey>,\n\tmemo: Memo = makeMemo(),\n): ReadonlyArray<Issue> => {\n\tconst all = rule.resources.includes(\"*\")\n\t// Each kind is checked one time, also when the list names it again. A\n\t// native `Set` finds the copies in one pass: `Arr.dedupe` compares each\n\t// name with each name before it, and a stored rule may name very many.\n\tconst names = Array.from(\n\t\tnew Set(Arr.filter(rule.resources, (kind) => kind !== \"*\")),\n\t)\n\tconst kinds = Array.from(HashMap.keys(domain.resources))\n\tconst named = Arr.flatMap(names, (kind) =>\n\t\tOption.toArray(HashMap.get(domain.resources, kind)),\n\t)\n\tconst known = all ? Array.from(HashMap.values(domain.resources)) : named\n\t// The rule never runs on a kind without one of its actions, so the types\n\t// of its tests are checked only on the kinds with one.\n\tconst applicable = Arr.filter(named, (descriptor) =>\n\t\thasAction(descriptor, rule.actions),\n\t)\n\tconst context: Context = {\n\t\tdomain,\n\t\tmemo,\n\t\tall,\n\t\tnamed,\n\t\tapplicable,\n\t\tpaths: all\n\t\t\t? Arr.filter(known, (descriptor) => hasAction(descriptor, rule.actions))\n\t\t\t: applicable,\n\t}\n\tconst actions = Arr.dedupe(\n\t\tArr.flatMap(known, (descriptor) => descriptor.actions),\n\t)\n\treturn [\n\t\t...Arr.map(\n\t\t\tArr.filter(names, (kind) => !HashMap.has(domain.resources, kind)),\n\t\t\t(kind): Issue => ({\n\t\t\t\tpath: [...at, \"resources\"],\n\t\t\t\tissue: suggestIn(\n\t\t\t\t\tmemo,\n\t\t\t\t\t`There is no resource with the kind \"${kind}\"`,\n\t\t\t\t\tkind,\n\t\t\t\t\t\"~kinds\",\n\t\t\t\t\t() => kinds,\n\t\t\t\t),\n\t\t\t}),\n\t\t),\n\t\t...Arr.map(\n\t\t\tArr.filter(\n\t\t\t\trule.actions,\n\t\t\t\t(action) => action !== \"*\" && !actions.includes(action),\n\t\t\t),\n\t\t\t(action): Issue => ({\n\t\t\t\tpath: [...at, \"actions\"],\n\t\t\t\tissue: suggestIn(\n\t\t\t\t\tmemo,\n\t\t\t\t\t`The action \"${action}\" is not an action of these resources`,\n\t\t\t\t\taction,\n\t\t\t\t\t`~actions\\u0000${names.join(\",\")}`,\n\t\t\t\t\t() => actions,\n\t\t\t\t),\n\t\t\t}),\n\t\t),\n\t\t...Arr.flatMap(rule.fields ?? [], (field, index) =>\n\t\t\tfield.includes(\"*\")\n\t\t\t\t? patternIssues(memo, known, field, [...at, \"fields\", index])\n\t\t\t\t: fieldIssue(context, field, [...at, \"fields\", index]),\n\t\t),\n\t\t...(rule.when === undefined\n\t\t\t? []\n\t\t\t: Nodes.isNode(rule.when)\n\t\t\t\t? conditionIssues(context, rule.when, () => [...at, \"when\"], new Set())\n\t\t\t\t: [notWhole([...at, \"when\"])]),\n\t]\n}\n\n/** The JSON form of a policy tree, or a policy tree, as the binding reads it. */\ntype TreeNode =\n\t| { readonly _tag: \"Rules\"; readonly rules: ReadonlyArray<RuleData> }\n\t| {\n\t\t\treadonly _tag: \"All\" | \"Any\" | \"FirstOf\"\n\t\t\treadonly policies: ReadonlyArray<TreeNode>\n\t }\n\t| {\n\t\t\treadonly _tag: \"Ceiling\"\n\t\t\treadonly cap: TreeNode\n\t\t\treadonly policy: TreeNode\n\t }\n\n/** Checks each rule of a policy tree against a domain. */\nexport const treeIssues = (\n\tdomain: Domain.Any,\n\troot: TreeNode,\n): ReadonlyArray<Issue> => {\n\t// The rules come out of the tree first, and each one is checked after.\n\t// A check of a condition goes down its levels, so a check inside the walk\n\t// of the tree would go down the levels of both.\n\t// The place of each rule shares the path of its parent, and the full path\n\t// is made only when its rule is checked. A deep tree with very many rules\n\t// would need very much memory for a full copy of the path of each rule.\n\tconst rulesOf = (\n\t\tnode: TreeNode,\n\t\tplace: Place.Place | undefined,\n\t\tlevel: number,\n\t): ReadonlyArray<Issue | readonly [RuleData, Place.Place]> => {\n\t\tif (level > Limits.policyDepth) {\n\t\t\treturn [\n\t\t\t\t{\n\t\t\t\t\tpath: Place.pathOf(place),\n\t\t\t\t\tissue: `A policy tree has more than ${String(Limits.policyDepth)} levels. The most is ${String(Limits.policyDepth)}.`,\n\t\t\t\t},\n\t\t\t]\n\t\t}\n\t\tswitch (node._tag) {\n\t\t\tcase \"Rules\":\n\t\t\t\treturn Arr.map(\n\t\t\t\t\tnode.rules,\n\t\t\t\t\t(rule, index) => [rule, Place.at(place, \"rules\", index)] as const,\n\t\t\t\t)\n\t\t\tcase \"All\":\n\t\t\tcase \"Any\":\n\t\t\tcase \"FirstOf\":\n\t\t\t\treturn Arr.flatMap(node.policies, (child, index) =>\n\t\t\t\t\trulesOf(child, Place.at(place, \"policies\", index), level + 1),\n\t\t\t\t)\n\t\t\tcase \"Ceiling\":\n\t\t\t\treturn [\n\t\t\t\t\t...rulesOf(node.cap, Place.at(place, \"cap\"), level + 1),\n\t\t\t\t\t...rulesOf(node.policy, Place.at(place, \"policy\"), level + 1),\n\t\t\t\t]\n\t\t}\n\t}\n\tconst memo = makeMemo()\n\treturn Arr.flatMap(rulesOf(root, undefined, 1), (found) =>\n\t\tArray.isArray(found)\n\t\t\t? ruleIssues(domain, found[0], Place.pathOf(found[1]), memo)\n\t\t\t: [found as Issue],\n\t)\n}\n"],"mappings":"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"}
|
package/dist/internal/catalog.js
CHANGED
|
@@ -2,9 +2,9 @@ import { catalogFields, exceeded } from "./limits.js";
|
|
|
2
2
|
import * as Schema from "effect/Schema";
|
|
3
3
|
import * as Arr from "effect/Array";
|
|
4
4
|
import { hasProperty } from "effect/Predicate";
|
|
5
|
-
import * as HashSet from "effect/HashSet";
|
|
6
5
|
import * as Option from "effect/Option";
|
|
7
6
|
import * as HashMap from "effect/HashMap";
|
|
7
|
+
import * as HashSet from "effect/HashSet";
|
|
8
8
|
import * as SchemaAST from "effect/SchemaAST";
|
|
9
9
|
// src/internal/catalog.ts
|
|
10
10
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rule.d.ts","names":["Policy.RuleShape"],"sources":["../../src/internal/rule.ts"],"mappings":";;;;;;;;;;;;;
|
|
1
|
+
{"version":3,"file":"rule.d.ts","names":["Policy.RuleShape"],"sources":["../../src/internal/rule.ts"],"mappings":";;;;;;;;;;;;;qBA2Ca,KAAK,WAAW,WAAW,qBAAqB,KAAK,MACjEA,UAAiB,GAAG;;;4BAGc;;uBAEL,QAAQ,MAAM;GAQzC,OAAO,aAAa,UAAU,KAAK,GAAG,GAAG,IAAI,KAAK,GAAG,GAAG;;SAMnD,GAAG,aAAa,KAAK"}
|
package/dist/internal/rule.js
CHANGED
|
@@ -5,8 +5,6 @@ import { builder as builder$1 } from "../Condition.js";
|
|
|
5
5
|
import { fitsCatalog, matches, matchesBelow, parseBound } from "./patterns.js";
|
|
6
6
|
import * as Data from "effect/Data";
|
|
7
7
|
import * as Arr from "effect/Array";
|
|
8
|
-
import * as Option from "effect/Option";
|
|
9
|
-
import * as MutableHashMap from "effect/MutableHashMap";
|
|
10
8
|
// src/internal/rule.ts
|
|
11
9
|
/**
|
|
12
10
|
* The values of rules, with no types. The types are in `Policy.ts`.
|
|
@@ -93,12 +91,14 @@ const matchesInside = (pattern, field) => matchesBelow(parseBound(pattern), fiel
|
|
|
93
91
|
* patterns of a policy on each kind, so one walk does at most that work.
|
|
94
92
|
*/
|
|
95
93
|
const fitsOf = (catalog) => {
|
|
96
|
-
const memo =
|
|
97
|
-
return (pattern) =>
|
|
94
|
+
const memo = /* @__PURE__ */ new Map();
|
|
95
|
+
return (pattern) => {
|
|
96
|
+
const known = memo.get(pattern);
|
|
97
|
+
if (known !== void 0) return known;
|
|
98
98
|
const found = fitsCatalog(parseBound(pattern), catalog);
|
|
99
|
-
|
|
99
|
+
memo.set(pattern, found);
|
|
100
100
|
return found;
|
|
101
|
-
}
|
|
101
|
+
};
|
|
102
102
|
};
|
|
103
103
|
/**
|
|
104
104
|
* Tests the parts of a rule that do not need the resource value: the action,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rule.js","names":["Nodes.isNode","ResourceData.isResource","ResourceData.descriptor","Condition.builder","Patterns.matches","Patterns.parseBound","Patterns.matchesBelow","Patterns.fitsCatalog","Catalog.lookup"],"sources":["../../src/internal/rule.ts"],"sourcesContent":["/**\n * The values of rules, with no types. The types are in `Policy.ts`.\n *\n * Rules do not change, and two rules with the same data are equal. A rule\n * works in a generator: it gives itself, so `yield* _.allow(...)` gives the\n * rule to `Policy.define`.\n */\nimport * as Arr from \"effect/Array\"\nimport * as Data from \"effect/Data\"\nimport * as MutableHashMap from \"effect/MutableHashMap\"\nimport * as Option from \"effect/Option\"\nimport * as Condition from \"../Condition.ts\"\nimport type * as Domain from \"../Domain.ts\"\nimport type * as Policy from \"../Policy.ts\"\nimport type * as Resource from \"../Resource.ts\"\nimport * as Catalog from \"./catalog.ts\"\nimport * as Nodes from \"./nodes.ts\"\nimport * as ResourceData from \"./resource.ts\"\nimport * as Patterns from \"./patterns.ts\"\n\n/** The data of a rule. */\nexport interface Shape {\n\treadonly _tag: \"Allow\" | \"Deny\"\n\treadonly actions: ReadonlyArray<string>\n\treadonly resources: ReadonlyArray<string>\n\treadonly when?: Condition.Ast\n\treadonly fields?: ReadonlyArray<string>\n\treadonly reason?: string\n\treadonly id?: string\n\treadonly meta?: unknown\n\t/**\n\t * The domain of the builder that made the rule. Two domains may have the\n\t * same types but other paths, so the types can not tell them apart. It is\n\t * not in the JSON form of a rule.\n\t */\n\treadonly domain?: Domain.Any\n}\n\n/**\n * The class of every rule value. The types are those of `Policy.Rule`: `D` is\n * the domain, `I` is what the tests of the condition read, and `X` is the\n * mark of the condition. With no types, as in the nodes of a policy, a rule\n * fits no domain, its condition fits no other rule, and it may have a\n * predicate.\n */\nexport class Rule<D = never, I = never, X = unknown> extends Data.Class<\n\tPolicy.RuleShape<I, X>\n> {\n\t/** Only in the types. */\n\tdeclare readonly \"~Opaque\": () => X\n\t/** Only in the types. */\n\tdeclare readonly \"~Domain\": (domain: D) => D\n\t// A private field makes the type of a rule its own, as for a policy: a\n\t// spread of a rule, or an object made by hand, is not a rule. `Equal` does\n\t// not read it. It comes last, so its `;` stays: the generator method below\n\t// needs one after a field. Node.js takes the `declare` fields out of the\n\t// code, and a `;` after one of them would go with it.\n\treadonly #rule = true;\n\n\t*[Symbol.iterator](): Generator<Rule<D, I, X>, Rule<D, I, X>, unknown> {\n\t\tyield this\n\t\treturn this\n\t}\n\n\t/** Tests if a value is a rule, by its private field. */\n\tstatic is(u: unknown): u is Rule {\n\t\treturn typeof u === \"object\" && u !== null && #rule in u\n\t}\n}\n\n/** Makes a rule from its data. It does not check the data against a domain. */\nexport const make = (data: Shape): Rule =>\n\t// A missing option is left out, so two rules with the same data are equal.\n\t// The data has no types here, as the code below works with no types.\n\tnew Rule({\n\t\t_tag: data._tag,\n\t\tactions: [...data.actions],\n\t\tresources: [...data.resources],\n\t\t...(data.when === undefined ? {} : { when: data.when }),\n\t\t...(data.fields === undefined ? {} : { fields: [...data.fields] }),\n\t\t...(data.reason === undefined ? {} : { reason: data.reason }),\n\t\t...(data.id === undefined ? {} : { id: data.id }),\n\t\t...(data.meta === undefined ? {} : { meta: data.meta }),\n\t\t...(data.domain === undefined ? {} : { domain: data.domain }),\n\t} as never)\n\n/**\n * Makes a rule from its data, as `Policy.rule` does. A condition with a\n * predicate is a defect: the types of the predicate tie it to the kinds of\n * the rule of a builder, and the binding can not check it.\n */\nexport const makePlain = (data: Shape): Rule => {\n\tif (data.when !== undefined && Nodes.isNode(data.when) && data.when.opaque) {\n\t\tthrow new Error(\n\t\t\t\"Policy.rule takes no condition with a predicate. Make the rule with Policy.builder.\",\n\t\t)\n\t}\n\treturn make(data)\n}\n\n/** Tests if a value is a rule. */\nexport const isRule = (u: unknown): u is Rule => Rule.is(u)\n\nconst toArray = <A>(value: A | ReadonlyArray<A>): ReadonlyArray<A> =>\n\tArray.isArray(value) ? value : [value as A]\n\nconst kindName = (target: unknown): string =>\n\tResourceData.isResource(target)\n\t\t? ResourceData.descriptor(target).kind\n\t\t: (target as string)\n\n/** The options of a rule from a builder, with no types. */\ninterface Options {\n\treadonly when?: (builder: never) => Condition.Ast\n\treadonly fields?: ReadonlyArray<string>\n\treadonly reason?: string\n\treadonly id?: string\n\treadonly meta?: unknown\n}\n\nconst fromOptions =\n\t(tag: \"Allow\" | \"Deny\", domain: Domain.Any) =>\n\t(\n\t\taction: string | ReadonlyArray<string>,\n\t\tresource: unknown,\n\t\toptions?: Options,\n\t): Rule =>\n\t\tmake({\n\t\t\t_tag: tag,\n\t\t\tactions: toArray(action),\n\t\t\tresources: Arr.map(toArray(resource), kindName),\n\t\t\t...(options?.when === undefined\n\t\t\t\t? {}\n\t\t\t\t: { when: options.when(Condition.builder() as never) }),\n\t\t\t...(options?.fields === undefined ? {} : { fields: options.fields }),\n\t\t\t...(options?.reason === undefined ? {} : { reason: options.reason }),\n\t\t\t...(options?.id === undefined ? {} : { id: options.id }),\n\t\t\t...(options?.meta === undefined ? {} : { meta: options.meta }),\n\t\t\tdomain,\n\t\t})\n\n/** Makes the rule builder of a domain. */\nexport const builder = (domain: Domain.Any) => ({\n\tallow: fromOptions(\"Allow\", domain),\n\tdeny: fromOptions(\"Deny\", domain),\n})\n\n/**\n * Tests if a field name matches a field name or a field pattern of a rule. A\n * pattern matches the fields below a field that it matches too, so `settings`\n * matches `settings.model`.\n */\nexport const matchesField = (pattern: string, field: string): boolean =>\n\tPatterns.matches(Patterns.parseBound(pattern), field.split(\".\"))\n\n/**\n * Tests if a field pattern may match a field inside `field`, such as\n * `settings.secret` inside `settings`.\n */\nexport const matchesInside = (pattern: string, field: string): boolean =>\n\tPatterns.matchesBelow(Patterns.parseBound(pattern), field.split(\".\"))\n\n/**\n * Makes the test \"a field pattern matches some field of the catalog\" for one\n * walk, such as one decision. A policy may name one pattern very many times,\n * so the test keeps each answer by the text of the pattern, and the memo goes\n * away when the walk ends. The binding puts a limit on the work of the\n * patterns of a policy on each kind, so one walk does at most that work.\n */\nexport const fitsOf = (\n\tcatalog: Catalog.Catalog,\n): ((pattern: string) => boolean) => {\n\tconst memo = MutableHashMap.empty<string, boolean>()\n\treturn (pattern) =>\n\t\tOption.getOrElse(MutableHashMap.get(memo, pattern), () => {\n\t\t\tconst found = Patterns.fitsCatalog(Patterns.parseBound(pattern), catalog)\n\t\t\tMutableHashMap.set(memo, pattern, found)\n\t\t\treturn found\n\t\t})\n}\n\n/**\n * Tests the parts of a rule that do not need the resource value: the action,\n * the kind and the field.\n *\n * A rule with an empty list of fields covers no request. A rule with fields\n * covers a request with no field only when it is an allow rule. So \"may\n * update some fields\" is a yes. A deny rule for some fields does not stop a\n * request for the whole value. With the `descriptor` of the kind, such an\n * allow rule covers the request only when one of its patterns matches a field\n * of the kind, and a deny rule for a part of a field that holds parts covers\n * a request for the whole field. A grant of fields that the kind does not have is not a grant of\n * some fields.\n */\nexport const matchesTarget = (\n\trule: Shape,\n\ttarget: {\n\t\treadonly action: string\n\t\treadonly kind: string\n\t\treadonly field?: string | undefined\n\t\treadonly descriptor?: Resource.Descriptor | undefined\n\t\t/** The test of {@link fitsOf} for the catalog of the descriptor. */\n\t\treadonly fits?: ((pattern: string) => boolean) | undefined\n\t},\n): boolean => {\n\tif (!rule.actions.includes(\"*\") && !rule.actions.includes(target.action)) {\n\t\treturn false\n\t}\n\tif (!rule.resources.includes(\"*\") && !rule.resources.includes(target.kind)) {\n\t\treturn false\n\t}\n\tif (rule.fields === undefined) {\n\t\treturn true\n\t}\n\t// A rule for no fields is a rule for nothing. A grant with its last field\n\t// taken away must not cover the whole value.\n\tif (rule.fields.length === 0) {\n\t\treturn false\n\t}\n\tif (target.field === undefined) {\n\t\tconst descriptor = target.descriptor\n\t\treturn (\n\t\t\trule._tag === \"Allow\" &&\n\t\t\t(descriptor === undefined ||\n\t\t\t\tArr.some(rule.fields, target.fits ?? fitsOf(descriptor.catalog)))\n\t\t)\n\t}\n\tconst field = target.field\n\t// A field that holds parts, as a record does, shows its parts too. So a\n\t// deny rule for a part inside it, as `data.secret` inside `data`, also\n\t// covers a request for the whole field.\n\tconst open =\n\t\trule._tag === \"Deny\" &&\n\t\ttarget.descriptor !== undefined &&\n\t\t(Catalog.lookup(target.descriptor.catalog, field)?.open ?? false)\n\treturn rule.fields.some(\n\t\t(pattern) =>\n\t\t\tmatchesField(pattern, field) || (open && matchesInside(pattern, field)),\n\t)\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"rule.js","names":["Nodes.isNode","ResourceData.isResource","ResourceData.descriptor","Condition.builder","Patterns.matches","Patterns.parseBound","Patterns.matchesBelow","Patterns.fitsCatalog","Catalog.lookup"],"sources":["../../src/internal/rule.ts"],"sourcesContent":["/**\n * The values of rules, with no types. The types are in `Policy.ts`.\n *\n * Rules do not change, and two rules with the same data are equal. A rule\n * works in a generator: it gives itself, so `yield* _.allow(...)` gives the\n * rule to `Policy.define`.\n */\nimport * as Arr from \"effect/Array\"\nimport * as Data from \"effect/Data\"\nimport * as Condition from \"../Condition.ts\"\nimport type * as Domain from \"../Domain.ts\"\nimport type * as Policy from \"../Policy.ts\"\nimport type * as Resource from \"../Resource.ts\"\nimport * as Catalog from \"./catalog.ts\"\nimport * as Nodes from \"./nodes.ts\"\nimport * as ResourceData from \"./resource.ts\"\nimport * as Patterns from \"./patterns.ts\"\n\n/** The data of a rule. */\nexport interface Shape {\n\treadonly _tag: \"Allow\" | \"Deny\"\n\treadonly actions: ReadonlyArray<string>\n\treadonly resources: ReadonlyArray<string>\n\treadonly when?: Condition.Ast\n\treadonly fields?: ReadonlyArray<string>\n\treadonly reason?: string\n\treadonly id?: string\n\treadonly meta?: unknown\n\t/**\n\t * The domain of the builder that made the rule. Two domains may have the\n\t * same types but other paths, so the types can not tell them apart. It is\n\t * not in the JSON form of a rule.\n\t */\n\treadonly domain?: Domain.Any\n}\n\n/**\n * The class of every rule value. The types are those of `Policy.Rule`: `D` is\n * the domain, `I` is what the tests of the condition read, and `X` is the\n * mark of the condition. With no types, as in the nodes of a policy, a rule\n * fits no domain, its condition fits no other rule, and it may have a\n * predicate.\n */\nexport class Rule<D = never, I = never, X = unknown> extends Data.Class<\n\tPolicy.RuleShape<I, X>\n> {\n\t/** Only in the types. */\n\tdeclare readonly \"~Opaque\": () => X\n\t/** Only in the types. */\n\tdeclare readonly \"~Domain\": (domain: D) => D\n\t// A private field makes the type of a rule its own, as for a policy: a\n\t// spread of a rule, or an object made by hand, is not a rule. `Equal` does\n\t// not read it. It comes last, so its `;` stays: the generator method below\n\t// needs one after a field. Node.js takes the `declare` fields out of the\n\t// code, and a `;` after one of them would go with it.\n\treadonly #rule = true;\n\n\t*[Symbol.iterator](): Generator<Rule<D, I, X>, Rule<D, I, X>, unknown> {\n\t\tyield this\n\t\treturn this\n\t}\n\n\t/** Tests if a value is a rule, by its private field. */\n\tstatic is(u: unknown): u is Rule {\n\t\treturn typeof u === \"object\" && u !== null && #rule in u\n\t}\n}\n\n/** Makes a rule from its data. It does not check the data against a domain. */\nexport const make = (data: Shape): Rule =>\n\t// A missing option is left out, so two rules with the same data are equal.\n\t// The data has no types here, as the code below works with no types.\n\tnew Rule({\n\t\t_tag: data._tag,\n\t\tactions: [...data.actions],\n\t\tresources: [...data.resources],\n\t\t...(data.when === undefined ? {} : { when: data.when }),\n\t\t...(data.fields === undefined ? {} : { fields: [...data.fields] }),\n\t\t...(data.reason === undefined ? {} : { reason: data.reason }),\n\t\t...(data.id === undefined ? {} : { id: data.id }),\n\t\t...(data.meta === undefined ? {} : { meta: data.meta }),\n\t\t...(data.domain === undefined ? {} : { domain: data.domain }),\n\t} as never)\n\n/**\n * Makes a rule from its data, as `Policy.rule` does. A condition with a\n * predicate is a defect: the types of the predicate tie it to the kinds of\n * the rule of a builder, and the binding can not check it.\n */\nexport const makePlain = (data: Shape): Rule => {\n\tif (data.when !== undefined && Nodes.isNode(data.when) && data.when.opaque) {\n\t\tthrow new Error(\n\t\t\t\"Policy.rule takes no condition with a predicate. Make the rule with Policy.builder.\",\n\t\t)\n\t}\n\treturn make(data)\n}\n\n/** Tests if a value is a rule. */\nexport const isRule = (u: unknown): u is Rule => Rule.is(u)\n\nconst toArray = <A>(value: A | ReadonlyArray<A>): ReadonlyArray<A> =>\n\tArray.isArray(value) ? value : [value as A]\n\nconst kindName = (target: unknown): string =>\n\tResourceData.isResource(target)\n\t\t? ResourceData.descriptor(target).kind\n\t\t: (target as string)\n\n/** The options of a rule from a builder, with no types. */\ninterface Options {\n\treadonly when?: (builder: never) => Condition.Ast\n\treadonly fields?: ReadonlyArray<string>\n\treadonly reason?: string\n\treadonly id?: string\n\treadonly meta?: unknown\n}\n\nconst fromOptions =\n\t(tag: \"Allow\" | \"Deny\", domain: Domain.Any) =>\n\t(\n\t\taction: string | ReadonlyArray<string>,\n\t\tresource: unknown,\n\t\toptions?: Options,\n\t): Rule =>\n\t\tmake({\n\t\t\t_tag: tag,\n\t\t\tactions: toArray(action),\n\t\t\tresources: Arr.map(toArray(resource), kindName),\n\t\t\t...(options?.when === undefined\n\t\t\t\t? {}\n\t\t\t\t: { when: options.when(Condition.builder() as never) }),\n\t\t\t...(options?.fields === undefined ? {} : { fields: options.fields }),\n\t\t\t...(options?.reason === undefined ? {} : { reason: options.reason }),\n\t\t\t...(options?.id === undefined ? {} : { id: options.id }),\n\t\t\t...(options?.meta === undefined ? {} : { meta: options.meta }),\n\t\t\tdomain,\n\t\t})\n\n/** Makes the rule builder of a domain. */\nexport const builder = (domain: Domain.Any) => ({\n\tallow: fromOptions(\"Allow\", domain),\n\tdeny: fromOptions(\"Deny\", domain),\n})\n\n/**\n * Tests if a field name matches a field name or a field pattern of a rule. A\n * pattern matches the fields below a field that it matches too, so `settings`\n * matches `settings.model`.\n */\nexport const matchesField = (pattern: string, field: string): boolean =>\n\tPatterns.matches(Patterns.parseBound(pattern), field.split(\".\"))\n\n/**\n * Tests if a field pattern may match a field inside `field`, such as\n * `settings.secret` inside `settings`.\n */\nexport const matchesInside = (pattern: string, field: string): boolean =>\n\tPatterns.matchesBelow(Patterns.parseBound(pattern), field.split(\".\"))\n\n/**\n * Makes the test \"a field pattern matches some field of the catalog\" for one\n * walk, such as one decision. A policy may name one pattern very many times,\n * so the test keeps each answer by the text of the pattern, and the memo goes\n * away when the walk ends. The binding puts a limit on the work of the\n * patterns of a policy on each kind, so one walk does at most that work.\n */\nexport const fitsOf = (\n\tcatalog: Catalog.Catalog,\n): ((pattern: string) => boolean) => {\n\t// A native `Map`: the hash of a string in Effect has no random seed, and a\n\t// policy could hold very many patterns with one hash. The engine hashes a\n\t// string with a random seed.\n\tconst memo = new Map<string, boolean>()\n\treturn (pattern) => {\n\t\tconst known = memo.get(pattern)\n\t\tif (known !== undefined) {\n\t\t\treturn known\n\t\t}\n\t\tconst found = Patterns.fitsCatalog(Patterns.parseBound(pattern), catalog)\n\t\tmemo.set(pattern, found)\n\t\treturn found\n\t}\n}\n\n/**\n * Tests the parts of a rule that do not need the resource value: the action,\n * the kind and the field.\n *\n * A rule with an empty list of fields covers no request. A rule with fields\n * covers a request with no field only when it is an allow rule. So \"may\n * update some fields\" is a yes. A deny rule for some fields does not stop a\n * request for the whole value. With the `descriptor` of the kind, such an\n * allow rule covers the request only when one of its patterns matches a field\n * of the kind, and a deny rule for a part of a field that holds parts covers\n * a request for the whole field. A grant of fields that the kind does not have is not a grant of\n * some fields.\n */\nexport const matchesTarget = (\n\trule: Shape,\n\ttarget: {\n\t\treadonly action: string\n\t\treadonly kind: string\n\t\treadonly field?: string | undefined\n\t\treadonly descriptor?: Resource.Descriptor | undefined\n\t\t/** The test of {@link fitsOf} for the catalog of the descriptor. */\n\t\treadonly fits?: ((pattern: string) => boolean) | undefined\n\t},\n): boolean => {\n\tif (!rule.actions.includes(\"*\") && !rule.actions.includes(target.action)) {\n\t\treturn false\n\t}\n\tif (!rule.resources.includes(\"*\") && !rule.resources.includes(target.kind)) {\n\t\treturn false\n\t}\n\tif (rule.fields === undefined) {\n\t\treturn true\n\t}\n\t// A rule for no fields is a rule for nothing. A grant with its last field\n\t// taken away must not cover the whole value.\n\tif (rule.fields.length === 0) {\n\t\treturn false\n\t}\n\tif (target.field === undefined) {\n\t\tconst descriptor = target.descriptor\n\t\treturn (\n\t\t\trule._tag === \"Allow\" &&\n\t\t\t(descriptor === undefined ||\n\t\t\t\tArr.some(rule.fields, target.fits ?? fitsOf(descriptor.catalog)))\n\t\t)\n\t}\n\tconst field = target.field\n\t// A field that holds parts, as a record does, shows its parts too. So a\n\t// deny rule for a part inside it, as `data.secret` inside `data`, also\n\t// covers a request for the whole field.\n\tconst open =\n\t\trule._tag === \"Deny\" &&\n\t\ttarget.descriptor !== undefined &&\n\t\t(Catalog.lookup(target.descriptor.catalog, field)?.open ?? false)\n\treturn rule.fields.some(\n\t\t(pattern) =>\n\t\t\tmatchesField(pattern, field) || (open && matchesInside(pattern, field)),\n\t)\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"search.d.ts","names":[],"sources":["../../src/internal/search.ts"],"mappings":";;;;;;;;
|
|
1
|
+
{"version":3,"file":"search.d.ts","names":[],"sources":["../../src/internal/search.ts"],"mappings":";;;;;;;;iBA2BiB;YACN;;;YAIC,iBAAiB,KAAK;EACjC;aAAwB,SAAS;;EACjC;EACA;aAAoB"}
|