@nmnmcc/ability 2.0.4 → 2.0.6
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 +4 -4
- package/dist/Condition.d.ts.map +1 -1
- package/dist/Condition.js.map +1 -1
- package/dist/Domain.d.ts +1 -1
- package/dist/Policy.d.ts +2 -2
- package/dist/Policy.d.ts.map +1 -1
- package/dist/Policy.js.map +1 -1
- package/dist/internal/algebra.js +85 -10
- package/dist/internal/algebra.js.map +1 -1
- package/dist/internal/authorizer.d.ts.map +1 -1
- package/dist/internal/authorizer.js +37 -14
- package/dist/internal/authorizer.js.map +1 -1
- package/dist/internal/bind.js +15 -12
- package/dist/internal/bind.js.map +1 -1
- package/dist/internal/catalog.d.ts +6 -0
- package/dist/internal/catalog.d.ts.map +1 -1
- package/dist/internal/catalog.js +12 -9
- package/dist/internal/catalog.js.map +1 -1
- package/dist/internal/decide.js +1 -1
- package/dist/internal/domain.js +1 -1
- package/dist/internal/json.js +2 -10
- package/dist/internal/json.js.map +1 -1
- package/dist/internal/nodes.d.ts +4 -1
- package/dist/internal/nodes.d.ts.map +1 -1
- package/dist/internal/nodes.js +17 -7
- package/dist/internal/nodes.js.map +1 -1
- package/dist/internal/patterns.js +1 -1
- package/dist/internal/patterns.js.map +1 -1
- package/dist/internal/policy-json.js +2 -8
- package/dist/internal/policy-json.js.map +1 -1
- package/dist/internal/policy-nodes.d.ts +4 -1
- package/dist/internal/policy-nodes.d.ts.map +1 -1
- package/dist/internal/policy-nodes.js +13 -4
- package/dist/internal/policy-nodes.js.map +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/Condition.ts +5 -2
- package/src/Policy.ts +1 -2
- package/src/internal/algebra.ts +118 -21
- package/src/internal/authorizer.ts +56 -14
- package/src/internal/bind.ts +39 -18
- package/src/internal/catalog.ts +40 -27
- package/src/internal/json.ts +2 -11
- package/src/internal/nodes.ts +21 -11
- package/src/internal/patterns.ts +7 -3
- package/src/internal/policy-json.ts +2 -9
- package/src/internal/policy-nodes.ts +14 -6
- package/src/internal/rule.ts +13 -9
- package/src/internal/search.ts +81 -75
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"nodes.js","names":["Limits.exceeded","Limits.conditionNodes"],"sources":["../../src/internal/nodes.ts"],"sourcesContent":["/**\n * The nodes of a condition. Each node is a class, and only its constructor\n * makes one, also when a condition comes from JSON. The constructor checks the\n * data of the node and works out three facts once:\n * - `size`: the number of nodes, with a shared child counted for each place;\n * - `depth`: the number of levels;\n * - `opaque`: the node has a predicate.\n *\n * The constructor stops a node that goes past `conditionNodes` or\n * `conditionDepth`. So a walk over any condition visits at most\n * `conditionNodes` nodes and goes at most `conditionDepth` levels down, and it\n * needs no memo.\n *\n * The facts are private fields, so `Equal` and `Hash` do not read them: two\n * nodes with the same data are equal.\n */\n// A predicate gives an Effect with an error type and a service type that the\n// nodes do not know, so they are `unknown` here.\n// @effect-diagnostics anyUnknownInErrorContext:off\nimport * as Arr from \"effect/Array\"\nimport * as Data from \"effect/Data\"\nimport type * as Effect from \"effect/Effect\"\nimport { hasProperty } from \"effect/Predicate\"\nimport * as Limits from \"./limits.ts\"\n\n/** A value that a condition can compare. */\nexport type Scalar = string | number | boolean | null\n\nexport type CompareOperator = \"eq\" | \"ne\" | \"lt\" | \"lte\" | \"gt\" | \"gte\"\n\n/** A fixed value. */\nexport interface Literal {\n\treadonly _tag: \"Literal\"\n\treadonly value: Scalar\n}\n\n/** A field of the principal. */\nexport interface PrincipalRef {\n\treadonly _tag: \"Principal\"\n\treadonly path: string\n}\n\nexport type Operand = Literal | PrincipalRef\n\nconst operands = Data.taggedEnum<Operand>()\n\nexport const literal = (value: Scalar): Literal => operands.Literal({ value })\n\nexport const principalRef = (path: string): PrincipalRef =>\n\toperands.Principal({ path })\n\nconst compareOperators: ReadonlyArray<string> = [\n\t\"eq\",\n\t\"ne\",\n\t\"lt\",\n\t\"lte\",\n\t\"gt\",\n\t\"gte\",\n]\n\n/** Tests if a value is a scalar. Any number is one, also `NaN`. */\nexport const isScalar = (value: unknown): value is Scalar =>\n\tvalue === null ||\n\ttypeof value === \"string\" ||\n\ttypeof value === \"number\" ||\n\ttypeof value === \"boolean\"\n\n/**\n * Checks a field path: a string with at most `fieldPathParts` parts. A longer\n * path is never a field of a resource or of the principal.\n */\nconst checkPath = (path: unknown): string => {\n\tif (typeof path !== \"string\") {\n\t\tthrow new Error(\"A field path must be a string\")\n\t}\n\tconst parts = path.split(\".\", Limits.fieldPathParts + 1).length\n\tif (parts > Limits.fieldPathParts) {\n\t\tthrow Limits.exceeded(\n\t\t\t\"A field path\",\n\t\t\t\"fieldPathParts\",\n\t\t\t\"parts\",\n\t\t\tparts,\n\t\t\tLimits.fieldPathParts,\n\t\t)\n\t}\n\treturn path\n}\n\n/** Checks an operand, and gives a new one with only its data. */\nconst checkOperand = (operand: unknown): Operand => {\n\tif (hasProperty(operand, \"_tag\")) {\n\t\tif (\n\t\t\toperand._tag === \"Literal\" &&\n\t\t\thasProperty(operand, \"value\") &&\n\t\t\tisScalar(operand.value)\n\t\t) {\n\t\t\treturn literal(operand.value)\n\t\t}\n\t\tif (operand._tag === \"Principal\" && hasProperty(operand, \"path\")) {\n\t\t\treturn principalRef(checkPath(operand.path))\n\t\t}\n\t}\n\tthrow new Error(\n\t\t\"An operand must be a fixed scalar value or a field of the principal\",\n\t)\n}\n\nconst checkOperands = (values: unknown): ReadonlyArray<Operand> => {\n\tif (!Array.isArray(values)) {\n\t\tthrow new Error(\"A list of operands must be an array\")\n\t}\n\treturn Object.freeze(Arr.map(Array.from(values), checkOperand))\n}\n\ninterface Facts {\n\treadonly size: number\n\treadonly depth: number\n\treadonly opaque: boolean\n}\n\nconst leaf: Facts = { size: 1, depth: 1, opaque: false }\n\n/** The facts of a node with children. It stops a node past the limits. */\nconst factsOf = (children: ReadonlyArray<Node>): Facts => {\n\tconst size = Arr.reduce(children, 1, (total, child) => total + child.size)\n\tif (size > Limits.conditionNodes) {\n\t\tthrow Limits.exceeded(\n\t\t\t\"A condition\",\n\t\t\t\"conditionNodes\",\n\t\t\t\"nodes\",\n\t\t\tsize,\n\t\t\tLimits.conditionNodes,\n\t\t)\n\t}\n\tconst depth =\n\t\t1 + Arr.reduce(children, 0, (top, child) => Math.max(top, child.depth))\n\tif (depth > Limits.conditionDepth) {\n\t\tthrow Limits.exceeded(\n\t\t\t\"A condition\",\n\t\t\t\"conditionDepth\",\n\t\t\t\"levels\",\n\t\t\tdepth,\n\t\t\tLimits.conditionDepth,\n\t\t)\n\t}\n\treturn {\n\t\tsize,\n\t\tdepth,\n\t\topaque: Arr.some(children, (child) => child.opaque),\n\t}\n}\n\n/** The base of every node. It keeps the facts. */\nabstract class Base extends Data.Class {\n\t/**\n\t * The JSON form of the node, for `JSON.stringify`. A number that JSON can\n\t * not hold, as `Infinity`, becomes an object that no decoder takes, in\n\t * place of `null`, which would change the test.\n\t */\n\ttoJSON(): unknown {\n\t\treturn toJson(this as unknown as Node)\n\t}\n\n\treadonly #facts: Facts\n\n\tconstructor(props: object, facts: Facts) {\n\t\tsuper(props as never)\n\t\tthis.#facts = facts\n\t}\n\n\t/** The number of nodes, with a shared child counted for each place. */\n\tget size(): number {\n\t\treturn this.#facts.size\n\t}\n\n\t/** The number of levels. */\n\tget depth(): number {\n\t\treturn this.#facts.depth\n\t}\n\n\t/** The node has a predicate. */\n\tget opaque(): boolean {\n\t\treturn this.#facts.opaque\n\t}\n}\n\n/** Always true. */\nexport class True extends Base {\n\treadonly _tag = \"True\"\n\tconstructor() {\n\t\tsuper({}, leaf)\n\t}\n}\n\n/** Always false. */\nexport class False extends Base {\n\treadonly _tag = \"False\"\n\tconstructor() {\n\t\tsuper({}, leaf)\n\t}\n}\n\n/** Compares a field with a value. */\nexport class Compare extends Base {\n\treadonly _tag = \"Compare\"\n\tdeclare readonly op: CompareOperator\n\tdeclare readonly field: string\n\tdeclare readonly value: Operand\n\tconstructor(props: {\n\t\treadonly op: CompareOperator\n\t\treadonly field: string\n\t\treadonly value: Operand\n\t}) {\n\t\tif (!compareOperators.includes(props.op)) {\n\t\t\tthrow new Error(`\"${String(props.op)}\" is not a compare operator`)\n\t\t}\n\t\tsuper(\n\t\t\t{\n\t\t\t\top: props.op,\n\t\t\t\tfield: checkPath(props.field),\n\t\t\t\tvalue: checkOperand(props.value),\n\t\t\t},\n\t\t\tleaf,\n\t\t)\n\t}\n}\n\n/** True when a field is equal to one of the values. */\nexport class In extends Base {\n\treadonly _tag = \"In\"\n\tdeclare readonly field: string\n\tdeclare readonly values: ReadonlyArray<Operand>\n\tconstructor(props: {\n\t\treadonly field: string\n\t\treadonly values: ReadonlyArray<Operand>\n\t}) {\n\t\tsuper(\n\t\t\t{ field: checkPath(props.field), values: checkOperands(props.values) },\n\t\t\tleaf,\n\t\t)\n\t}\n}\n\n/** True when a field is not `null` and not missing. */\nexport class Exists extends Base {\n\treadonly _tag = \"Exists\"\n\tdeclare readonly field: string\n\tconstructor(props: { readonly field: string }) {\n\t\tsuper({ field: checkPath(props.field) }, leaf)\n\t}\n}\n\n/** True when the path of the resource starts with the prefix. */\nexport class Under extends Base {\n\treadonly _tag = \"Under\"\n\tdeclare readonly prefix: ReadonlyArray<Operand>\n\tconstructor(props: { readonly prefix: ReadonlyArray<Operand> }) {\n\t\tsuper({ prefix: checkOperands(props.prefix) }, leaf)\n\t}\n}\n\nconst checkChildren = (conditions: unknown): ReadonlyArray<Node> => {\n\tif (!Array.isArray(conditions)) {\n\t\tthrow new Error(\"The children of a node must be an array\")\n\t}\n\tconst children = Array.from(conditions as ReadonlyArray<unknown>)\n\tif (!Arr.every(children, isNode)) {\n\t\tthrow notNode()\n\t}\n\treturn Object.freeze(children)\n}\n\nexport class And extends Base {\n\treadonly _tag = \"And\"\n\tdeclare readonly conditions: ReadonlyArray<Node>\n\tconstructor(conditions: ReadonlyArray<Node>) {\n\t\tconst children = checkChildren(conditions)\n\t\tsuper({ conditions: children }, factsOf(children))\n\t}\n}\n\nexport class Or extends Base {\n\treadonly _tag = \"Or\"\n\tdeclare readonly conditions: ReadonlyArray<Node>\n\tconstructor(conditions: ReadonlyArray<Node>) {\n\t\tconst children = checkChildren(conditions)\n\t\tsuper({ conditions: children }, factsOf(children))\n\t}\n}\n\nexport class Not extends Base {\n\treadonly _tag = \"Not\"\n\tdeclare readonly condition: Node\n\tconstructor(condition: Node) {\n\t\tif (!isNode(condition)) {\n\t\t\tthrow notNode()\n\t\t}\n\t\tsuper({ condition }, factsOf([condition]))\n\t}\n}\n\n/** The test of a predicate, with no types. */\nexport type Run = (\n\tvalue: unknown,\n\tprincipal: unknown,\n) => Effect.Effect<boolean, unknown, unknown>\n\n/**\n * A test that runs an `Effect`. Its test takes inputs of the type `never`, so\n * no code can call it with a value of the wrong type: `Condition.evaluate`\n * checks the input. Use {@link runOf} inside the library.\n */\nexport class Predicate extends Base {\n\treadonly _tag = \"Predicate\"\n\tdeclare readonly name: string\n\tdeclare readonly run: (\n\t\tvalue: never,\n\t\tprincipal: never,\n\t) => Effect.Effect<boolean, unknown, unknown>\n\tconstructor(props: { readonly name: string; readonly run: Run }) {\n\t\tif (typeof props.name !== \"string\" || typeof props.run !== \"function\") {\n\t\t\tthrow new Error(\"A predicate needs a name and a test\")\n\t\t}\n\t\tsuper(\n\t\t\t{ name: props.name, run: props.run },\n\t\t\t{ size: 1, depth: 1, opaque: true },\n\t\t)\n\t}\n}\n\n/** Gives the test of a predicate, with no types. */\nexport const runOf = (node: Predicate): Run => node.run as Run\n\n/** A node of a condition. */\nexport type Node =\n\tTrue | False | Compare | In | Exists | Under | And | Or | Not | Predicate\n\nconst operandJson = (operand: Operand): unknown =>\n\toperand._tag === \"Literal\"\n\t\t? {\n\t\t\t\t_tag: \"Literal\",\n\t\t\t\t// JSON has no `Infinity`, `-Infinity` or `NaN`. Such a number\n\t\t\t\t// becomes an object that the decoder does not take.\n\t\t\t\tvalue:\n\t\t\t\t\ttypeof operand.value === \"number\" && !Number.isFinite(operand.value)\n\t\t\t\t\t\t? { nonFinite: String(operand.value) }\n\t\t\t\t\t\t: operand.value,\n\t\t\t}\n\t\t: { _tag: \"Principal\", path: operand.path }\n\n/** Gives the JSON form of a condition with no predicate. */\nexport const toJson = (node: Node): unknown => {\n\tswitch (node._tag) {\n\t\tcase \"True\":\n\t\tcase \"False\":\n\t\t\treturn { _tag: node._tag }\n\t\tcase \"Compare\":\n\t\t\treturn {\n\t\t\t\t_tag: \"Compare\",\n\t\t\t\top: node.op,\n\t\t\t\tfield: node.field,\n\t\t\t\tvalue: operandJson(node.value),\n\t\t\t}\n\t\tcase \"In\":\n\t\t\treturn {\n\t\t\t\t_tag: \"In\",\n\t\t\t\tfield: node.field,\n\t\t\t\tvalues: Arr.map(node.values, operandJson),\n\t\t\t}\n\t\tcase \"Exists\":\n\t\t\treturn { _tag: \"Exists\", field: node.field }\n\t\tcase \"Under\":\n\t\t\treturn { _tag: \"Under\", prefix: Arr.map(node.prefix, operandJson) }\n\t\tcase \"And\":\n\t\tcase \"Or\":\n\t\t\treturn { _tag: node._tag, conditions: Arr.map(node.conditions, toJson) }\n\t\tcase \"Not\":\n\t\t\treturn { _tag: \"Not\", condition: toJson(node.condition) }\n\t\tcase \"Predicate\":\n\t\t\tthrow new Error(\n\t\t\t\t`The predicate \"${node.name}\" can not become JSON. A condition with a predicate has no JSON form.`,\n\t\t\t)\n\t}\n}\n\n/** Tests if a value is a node. Only a constructor of a node makes one. */\nexport const isNode = (u: unknown): u is Node => u instanceof Base\n\nconst notNode = (): Error =>\n\tnew Error(\n\t\t\"A condition must be made with a builder or a function of Condition. A plain object is not a condition\",\n\t)\n\n/** Gives a value as a node, or throws when it is not a node. */\nexport const toNode = (u: unknown): Node => {\n\tif (!isNode(u)) {\n\t\tthrow notNode()\n\t}\n\treturn u\n}\n\nexport const always: Node = new True()\n\nexport const never: Node = new False()\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"nodes.js","names":["Limits.exceeded","Limits.conditionNodes"],"sources":["../../src/internal/nodes.ts"],"sourcesContent":["/**\n * The nodes of a condition. Each node is a class, and only its constructor\n * makes one, also when a condition comes from JSON. The constructor checks the\n * data of the node and works out three facts once:\n * - `size`: the number of nodes, with a shared child counted for each place;\n * - `depth`: the number of levels;\n * - `opaque`: the node has a predicate.\n *\n * The constructor stops a node that goes past `conditionNodes` or\n * `conditionDepth`. So a walk over any condition visits at most\n * `conditionNodes` nodes and goes at most `conditionDepth` levels down, and it\n * needs no memo.\n *\n * The facts are private fields, so `Equal` and `Hash` do not read them: two\n * nodes with the same data are equal.\n */\n// A predicate gives an Effect with an error type and a service type that the\n// nodes do not know, so they are `unknown` here.\n// @effect-diagnostics anyUnknownInErrorContext:off\nimport * as Arr from \"effect/Array\"\nimport * as Data from \"effect/Data\"\nimport type * as Effect from \"effect/Effect\"\nimport { hasProperty } from \"effect/Predicate\"\nimport * as Schema from \"effect/Schema\"\nimport * as Limits from \"./limits.ts\"\n\n/** A value that a condition can compare. */\nexport type Scalar = string | number | boolean | null\n\n/** The operators of a `Compare` test. The decoders read this list too. */\nexport const CompareOperator = Schema.Literals([\n\t\"eq\",\n\t\"ne\",\n\t\"lt\",\n\t\"lte\",\n\t\"gt\",\n\t\"gte\",\n])\n\nexport type CompareOperator = typeof CompareOperator.Type\n\n/**\n * Tests if a value is an operator. It reads the list of the Schema with\n * `includes`: for a short list, this is about twice as fast as\n * `Schema.is`, and a decoder tests each node of a large input.\n */\nexport const isCompareOperator = (u: unknown): u is CompareOperator =>\n\t(CompareOperator.literals as ReadonlyArray<unknown>).includes(u)\n\n/** A fixed value. */\nexport interface Literal {\n\treadonly _tag: \"Literal\"\n\treadonly value: Scalar\n}\n\n/** A field of the principal. */\nexport interface PrincipalRef {\n\treadonly _tag: \"Principal\"\n\treadonly path: string\n}\n\nexport type Operand = Literal | PrincipalRef\n\nconst operands = Data.taggedEnum<Operand>()\n\nexport const literal = (value: Scalar): Literal => operands.Literal({ value })\n\nexport const principalRef = (path: string): PrincipalRef =>\n\toperands.Principal({ path })\n\n/** Tests if a value is a scalar. Any number is one, also `NaN`. */\nexport const isScalar = (value: unknown): value is Scalar =>\n\tvalue === null ||\n\ttypeof value === \"string\" ||\n\ttypeof value === \"number\" ||\n\ttypeof value === \"boolean\"\n\n/**\n * Checks a field path: a string with at most `fieldPathParts` parts. A longer\n * path is never a field of a resource or of the principal.\n */\nconst checkPath = (path: unknown): string => {\n\tif (typeof path !== \"string\") {\n\t\tthrow new Error(\"A field path must be a string\")\n\t}\n\tconst parts = path.split(\".\", Limits.fieldPathParts + 1).length\n\tif (parts > Limits.fieldPathParts) {\n\t\tthrow Limits.exceeded(\n\t\t\t\"A field path\",\n\t\t\t\"fieldPathParts\",\n\t\t\t\"parts\",\n\t\t\tparts,\n\t\t\tLimits.fieldPathParts,\n\t\t)\n\t}\n\treturn path\n}\n\n/** Checks an operand, and gives a new one with only its data. */\nconst checkOperand = (operand: unknown): Operand => {\n\tif (hasProperty(operand, \"_tag\")) {\n\t\tif (\n\t\t\toperand._tag === \"Literal\" &&\n\t\t\thasProperty(operand, \"value\") &&\n\t\t\tisScalar(operand.value)\n\t\t) {\n\t\t\treturn literal(operand.value)\n\t\t}\n\t\tif (operand._tag === \"Principal\" && hasProperty(operand, \"path\")) {\n\t\t\treturn principalRef(checkPath(operand.path))\n\t\t}\n\t}\n\tthrow new Error(\n\t\t\"An operand must be a fixed scalar value or a field of the principal\",\n\t)\n}\n\nconst checkOperands = (values: unknown): ReadonlyArray<Operand> => {\n\tif (!Array.isArray(values)) {\n\t\tthrow new Error(\"A list of operands must be an array\")\n\t}\n\treturn Object.freeze(Arr.map(Array.from(values), checkOperand))\n}\n\ninterface Facts {\n\treadonly size: number\n\treadonly depth: number\n\treadonly opaque: boolean\n}\n\nconst leaf: Facts = { size: 1, depth: 1, opaque: false }\n\n/** The facts of a node with children. It stops a node past the limits. */\nconst factsOf = (children: ReadonlyArray<Node>): Facts => {\n\tconst size = Arr.reduce(children, 1, (total, child) => total + child.size)\n\tif (size > Limits.conditionNodes) {\n\t\tthrow Limits.exceeded(\n\t\t\t\"A condition\",\n\t\t\t\"conditionNodes\",\n\t\t\t\"nodes\",\n\t\t\tsize,\n\t\t\tLimits.conditionNodes,\n\t\t)\n\t}\n\tconst depth =\n\t\t1 + Arr.reduce(children, 0, (top, child) => Math.max(top, child.depth))\n\tif (depth > Limits.conditionDepth) {\n\t\tthrow Limits.exceeded(\n\t\t\t\"A condition\",\n\t\t\t\"conditionDepth\",\n\t\t\t\"levels\",\n\t\t\tdepth,\n\t\t\tLimits.conditionDepth,\n\t\t)\n\t}\n\treturn {\n\t\tsize,\n\t\tdepth,\n\t\topaque: Arr.some(children, (child) => child.opaque),\n\t}\n}\n\n/** The base of every node. It keeps the facts. */\nabstract class Base extends Data.Class {\n\t/**\n\t * The JSON form of the node, for `JSON.stringify`. A number that JSON can\n\t * not hold, as `Infinity`, becomes an object that no decoder takes, in\n\t * place of `null`, which would change the test.\n\t */\n\ttoJSON(): unknown {\n\t\treturn toJson(this as unknown as Node)\n\t}\n\n\treadonly #facts: Facts\n\n\tconstructor(props: object, facts: Facts) {\n\t\tsuper(props as never)\n\t\tthis.#facts = facts\n\t}\n\n\t/** The number of nodes, with a shared child counted for each place. */\n\tget size(): number {\n\t\treturn this.#facts.size\n\t}\n\n\t/** The number of levels. */\n\tget depth(): number {\n\t\treturn this.#facts.depth\n\t}\n\n\t/** The node has a predicate. */\n\tget opaque(): boolean {\n\t\treturn this.#facts.opaque\n\t}\n}\n\n/** Always true. */\nexport class True extends Base {\n\treadonly _tag = \"True\"\n\tconstructor() {\n\t\tsuper({}, leaf)\n\t}\n}\n\n/** Always false. */\nexport class False extends Base {\n\treadonly _tag = \"False\"\n\tconstructor() {\n\t\tsuper({}, leaf)\n\t}\n}\n\n/** Compares a field with a value. */\nexport class Compare extends Base {\n\treadonly _tag = \"Compare\"\n\tdeclare readonly op: CompareOperator\n\tdeclare readonly field: string\n\tdeclare readonly value: Operand\n\tconstructor(props: {\n\t\treadonly op: CompareOperator\n\t\treadonly field: string\n\t\treadonly value: Operand\n\t}) {\n\t\tif (!isCompareOperator(props.op)) {\n\t\t\tthrow new Error(`\"${String(props.op)}\" is not a compare operator`)\n\t\t}\n\t\tsuper(\n\t\t\t{\n\t\t\t\top: props.op,\n\t\t\t\tfield: checkPath(props.field),\n\t\t\t\tvalue: checkOperand(props.value),\n\t\t\t},\n\t\t\tleaf,\n\t\t)\n\t}\n}\n\n/** True when a field is equal to one of the values. */\nexport class In extends Base {\n\treadonly _tag = \"In\"\n\tdeclare readonly field: string\n\tdeclare readonly values: ReadonlyArray<Operand>\n\tconstructor(props: {\n\t\treadonly field: string\n\t\treadonly values: ReadonlyArray<Operand>\n\t}) {\n\t\tsuper(\n\t\t\t{ field: checkPath(props.field), values: checkOperands(props.values) },\n\t\t\tleaf,\n\t\t)\n\t}\n}\n\n/** True when a field is not `null` and not missing. */\nexport class Exists extends Base {\n\treadonly _tag = \"Exists\"\n\tdeclare readonly field: string\n\tconstructor(props: { readonly field: string }) {\n\t\tsuper({ field: checkPath(props.field) }, leaf)\n\t}\n}\n\n/** True when the path of the resource starts with the prefix. */\nexport class Under extends Base {\n\treadonly _tag = \"Under\"\n\tdeclare readonly prefix: ReadonlyArray<Operand>\n\tconstructor(props: { readonly prefix: ReadonlyArray<Operand> }) {\n\t\tsuper({ prefix: checkOperands(props.prefix) }, leaf)\n\t}\n}\n\nconst checkChildren = (conditions: unknown): ReadonlyArray<Node> => {\n\tif (!Array.isArray(conditions)) {\n\t\tthrow new Error(\"The children of a node must be an array\")\n\t}\n\tconst children = Array.from(conditions as ReadonlyArray<unknown>)\n\tif (!Arr.every(children, isNode)) {\n\t\tthrow notNode()\n\t}\n\treturn Object.freeze(children)\n}\n\nexport class And extends Base {\n\treadonly _tag = \"And\"\n\tdeclare readonly conditions: ReadonlyArray<Node>\n\tconstructor(conditions: ReadonlyArray<Node>) {\n\t\tconst children = checkChildren(conditions)\n\t\tsuper({ conditions: children }, factsOf(children))\n\t}\n}\n\nexport class Or extends Base {\n\treadonly _tag = \"Or\"\n\tdeclare readonly conditions: ReadonlyArray<Node>\n\tconstructor(conditions: ReadonlyArray<Node>) {\n\t\tconst children = checkChildren(conditions)\n\t\tsuper({ conditions: children }, factsOf(children))\n\t}\n}\n\nexport class Not extends Base {\n\treadonly _tag = \"Not\"\n\tdeclare readonly condition: Node\n\tconstructor(condition: Node) {\n\t\tif (!isNode(condition)) {\n\t\t\tthrow notNode()\n\t\t}\n\t\tsuper({ condition }, factsOf([condition]))\n\t}\n}\n\n/** The test of a predicate, with no types. */\nexport type Run = (\n\tvalue: unknown,\n\tprincipal: unknown,\n) => Effect.Effect<boolean, unknown, unknown>\n\n/**\n * A test that runs an `Effect`. Its test takes inputs of the type `never`, so\n * no code can call it with a value of the wrong type: `Condition.evaluate`\n * checks the input. Use {@link runOf} inside the library.\n */\nexport class Predicate extends Base {\n\treadonly _tag = \"Predicate\"\n\tdeclare readonly name: string\n\tdeclare readonly run: (\n\t\tvalue: never,\n\t\tprincipal: never,\n\t) => Effect.Effect<boolean, unknown, unknown>\n\tconstructor(props: { readonly name: string; readonly run: Run }) {\n\t\tif (typeof props.name !== \"string\" || typeof props.run !== \"function\") {\n\t\t\tthrow new Error(\"A predicate needs a name and a test\")\n\t\t}\n\t\tsuper(\n\t\t\t{ name: props.name, run: props.run },\n\t\t\t{ size: 1, depth: 1, opaque: true },\n\t\t)\n\t}\n}\n\n/** Gives the test of a predicate, with no types. */\nexport const runOf = (node: Predicate): Run => node.run as Run\n\n/** A node of a condition. */\nexport type Node =\n\tTrue | False | Compare | In | Exists | Under | And | Or | Not | Predicate\n\nconst operandJson = (operand: Operand): unknown =>\n\toperand._tag === \"Literal\"\n\t\t? {\n\t\t\t\t_tag: \"Literal\",\n\t\t\t\t// JSON has no `Infinity`, `-Infinity` or `NaN`. Such a number\n\t\t\t\t// becomes an object that the decoder does not take.\n\t\t\t\tvalue:\n\t\t\t\t\ttypeof operand.value === \"number\" && !Number.isFinite(operand.value)\n\t\t\t\t\t\t? { nonFinite: String(operand.value) }\n\t\t\t\t\t\t: operand.value,\n\t\t\t}\n\t\t: { _tag: \"Principal\", path: operand.path }\n\n/** Gives the JSON form of a condition with no predicate. */\nexport const toJson = (node: Node): unknown => {\n\tswitch (node._tag) {\n\t\tcase \"True\":\n\t\tcase \"False\":\n\t\t\treturn { _tag: node._tag }\n\t\tcase \"Compare\":\n\t\t\treturn {\n\t\t\t\t_tag: \"Compare\",\n\t\t\t\top: node.op,\n\t\t\t\tfield: node.field,\n\t\t\t\tvalue: operandJson(node.value),\n\t\t\t}\n\t\tcase \"In\":\n\t\t\treturn {\n\t\t\t\t_tag: \"In\",\n\t\t\t\tfield: node.field,\n\t\t\t\tvalues: Arr.map(node.values, operandJson),\n\t\t\t}\n\t\tcase \"Exists\":\n\t\t\treturn { _tag: \"Exists\", field: node.field }\n\t\tcase \"Under\":\n\t\t\treturn { _tag: \"Under\", prefix: Arr.map(node.prefix, operandJson) }\n\t\tcase \"And\":\n\t\tcase \"Or\":\n\t\t\treturn { _tag: node._tag, conditions: Arr.map(node.conditions, toJson) }\n\t\tcase \"Not\":\n\t\t\treturn { _tag: \"Not\", condition: toJson(node.condition) }\n\t\tcase \"Predicate\":\n\t\t\tthrow new Error(\n\t\t\t\t`The predicate \"${node.name}\" can not become JSON. A condition with a predicate has no JSON form.`,\n\t\t\t)\n\t}\n}\n\n/** Tests if a value is a node. Only a constructor of a node makes one. */\nexport const isNode = (u: unknown): u is Node => u instanceof Base\n\nconst notNode = (): Error =>\n\tnew Error(\n\t\t\"A condition must be made with a builder or a function of Condition. A plain object is not a condition\",\n\t)\n\n/** Gives a value as a node, or throws when it is not a node. */\nexport const toNode = (u: unknown): Node => {\n\tif (!isNode(u)) {\n\t\tthrow notNode()\n\t}\n\treturn u\n}\n\nexport const always: Node = new True()\n\nexport const never: Node = new False()\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA8BA,MAAa,kBAAkB,OAAO,SAAS;CAC9C;CACA;CACA;CACA;CACA;CACA;AACD,CAAC;;;;;;AASD,MAAa,qBAAqB,MAChC,gBAAgB,SAAoC,SAAS,CAAC;AAgBhE,MAAM,WAAW,KAAK,WAAoB;AAE1C,MAAa,WAAW,UAA2B,SAAS,QAAQ,EAAE,MAAM,CAAC;AAE7E,MAAa,gBAAgB,SAC5B,SAAS,UAAU,EAAE,KAAK,CAAC;;AAG5B,MAAa,YAAY,UACxB,UAAU,QACV,OAAO,UAAU,YACjB,OAAO,UAAU,YACjB,OAAO,UAAU;;;;;AAMlB,MAAM,aAAa,SAA0B;CAC5C,IAAI,OAAO,SAAS,UACnB,MAAM,IAAI,MAAM,+BAA+B;CAEhD,MAAM,QAAQ,KAAK,MAAM,KAAK,CAAyB,CAAC,CAAC;CACzD,IAAI,QAAQ,GACX,MAAMA,SACL,gBACA,kBACA,SACA,OACA,CACD;CAED,OAAO;AACR;;AAGA,MAAM,gBAAgB,YAA8B;CACnD,IAAI,YAAY,SAAS,MAAM,GAAG;EACjC,IACC,QAAQ,SAAS,aACjB,YAAY,SAAS,OAAO,KAC5B,SAAS,QAAQ,KAAK,GAEtB,OAAO,QAAQ,QAAQ,KAAK;EAE7B,IAAI,QAAQ,SAAS,eAAe,YAAY,SAAS,MAAM,GAC9D,OAAO,aAAa,UAAU,QAAQ,IAAI,CAAC;CAE7C;CACA,MAAM,IAAI,MACT,qEACD;AACD;AAEA,MAAM,iBAAiB,WAA4C;CAClE,IAAI,CAAC,MAAM,QAAQ,MAAM,GACxB,MAAM,IAAI,MAAM,qCAAqC;CAEtD,OAAO,OAAO,OAAO,IAAI,IAAI,MAAM,KAAK,MAAM,GAAG,YAAY,CAAC;AAC/D;AAQA,MAAM,OAAc;CAAE,MAAM;CAAG,OAAO;CAAG,QAAQ;AAAM;;AAGvD,MAAM,WAAW,aAAyC;CACzD,MAAM,OAAO,IAAI,OAAO,UAAU,IAAI,OAAO,UAAU,QAAQ,MAAM,IAAI;CACzE,IAAI,OAAO,KACV,MAAMA,SACL,eACA,kBACA,SACA,MACAC,cACD;CAED,MAAM,QACL,IAAI,IAAI,OAAO,UAAU,IAAI,KAAK,UAAU,KAAK,IAAI,KAAK,MAAM,KAAK,CAAC;CACvE,IAAI,QAAQ,KACX,MAAMD,SACL,eACA,kBACA,UACA,OACA,GACD;CAED,OAAO;EACN;EACA;EACA,QAAQ,IAAI,KAAK,WAAW,UAAU,MAAM,MAAM;CACnD;AACD;;AAGA,IAAe,OAAf,cAA4B,KAAK,MAAM;;;;;;CAMtC,SAAkB;EACjB,OAAO,OAAO,IAAuB;CACtC;CAEA;CAEA,YAAY,OAAe,OAAc;EACxC,MAAM,KAAc;EACpB,KAAK,SAAS;CACf;;CAGA,IAAI,OAAe;EAClB,OAAO,KAAK,OAAO;CACpB;;CAGA,IAAI,QAAgB;EACnB,OAAO,KAAK,OAAO;CACpB;;CAGA,IAAI,SAAkB;EACrB,OAAO,KAAK,OAAO;CACpB;AACD;;AAGA,IAAa,OAAb,cAA0B,KAAK;CAC9B,OAAgB;CAChB,cAAc;EACb,MAAM,CAAC,GAAG,IAAI;CACf;AACD;;AAGA,IAAa,QAAb,cAA2B,KAAK;CAC/B,OAAgB;CAChB,cAAc;EACb,MAAM,CAAC,GAAG,IAAI;CACf;AACD;;AAGA,IAAa,UAAb,cAA6B,KAAK;CACjC,OAAgB;CAIhB,YAAY,OAIT;EACF,IAAI,CAAC,kBAAkB,MAAM,EAAE,GAC9B,MAAM,IAAI,MAAM,IAAI,OAAO,MAAM,EAAE,EAAE,4BAA4B;EAElE,MACC;GACC,IAAI,MAAM;GACV,OAAO,UAAU,MAAM,KAAK;GAC5B,OAAO,aAAa,MAAM,KAAK;EAChC,GACA,IACD;CACD;AACD;;AAGA,IAAa,KAAb,cAAwB,KAAK;CAC5B,OAAgB;CAGhB,YAAY,OAGT;EACF,MACC;GAAE,OAAO,UAAU,MAAM,KAAK;GAAG,QAAQ,cAAc,MAAM,MAAM;EAAE,GACrE,IACD;CACD;AACD;;AAGA,IAAa,SAAb,cAA4B,KAAK;CAChC,OAAgB;CAEhB,YAAY,OAAmC;EAC9C,MAAM,EAAE,OAAO,UAAU,MAAM,KAAK,EAAE,GAAG,IAAI;CAC9C;AACD;;AAGA,IAAa,QAAb,cAA2B,KAAK;CAC/B,OAAgB;CAEhB,YAAY,OAAoD;EAC/D,MAAM,EAAE,QAAQ,cAAc,MAAM,MAAM,EAAE,GAAG,IAAI;CACpD;AACD;AAEA,MAAM,iBAAiB,eAA6C;CACnE,IAAI,CAAC,MAAM,QAAQ,UAAU,GAC5B,MAAM,IAAI,MAAM,yCAAyC;CAE1D,MAAM,WAAW,MAAM,KAAK,UAAoC;CAChE,IAAI,CAAC,IAAI,MAAM,UAAU,MAAM,GAC9B,MAAM,QAAQ;CAEf,OAAO,OAAO,OAAO,QAAQ;AAC9B;AAEA,IAAa,MAAb,cAAyB,KAAK;CAC7B,OAAgB;CAEhB,YAAY,YAAiC;EAC5C,MAAM,WAAW,cAAc,UAAU;EACzC,MAAM,EAAE,YAAY,SAAS,GAAG,QAAQ,QAAQ,CAAC;CAClD;AACD;AAEA,IAAa,KAAb,cAAwB,KAAK;CAC5B,OAAgB;CAEhB,YAAY,YAAiC;EAC5C,MAAM,WAAW,cAAc,UAAU;EACzC,MAAM,EAAE,YAAY,SAAS,GAAG,QAAQ,QAAQ,CAAC;CAClD;AACD;AAEA,IAAa,MAAb,cAAyB,KAAK;CAC7B,OAAgB;CAEhB,YAAY,WAAiB;EAC5B,IAAI,CAAC,OAAO,SAAS,GACpB,MAAM,QAAQ;EAEf,MAAM,EAAE,UAAU,GAAG,QAAQ,CAAC,SAAS,CAAC,CAAC;CAC1C;AACD;;;;;;AAaA,IAAa,YAAb,cAA+B,KAAK;CACnC,OAAgB;CAMhB,YAAY,OAAqD;EAChE,IAAI,OAAO,MAAM,SAAS,YAAY,OAAO,MAAM,QAAQ,YAC1D,MAAM,IAAI,MAAM,qCAAqC;EAEtD,MACC;GAAE,MAAM,MAAM;GAAM,KAAK,MAAM;EAAI,GACnC;GAAE,MAAM;GAAG,OAAO;GAAG,QAAQ;EAAK,CACnC;CACD;AACD;;AAGA,MAAa,SAAS,SAAyB,KAAK;AAMpD,MAAM,eAAe,YACpB,QAAQ,SAAS,YACd;CACA,MAAM;CAGN,OACC,OAAO,QAAQ,UAAU,YAAY,CAAC,OAAO,SAAS,QAAQ,KAAK,IAChE,EAAE,WAAW,OAAO,QAAQ,KAAK,EAAE,IACnC,QAAQ;AACb,IACC;CAAE,MAAM;CAAa,MAAM,QAAQ;AAAK;;AAG5C,MAAa,UAAU,SAAwB;CAC9C,QAAQ,KAAK,MAAb;EACC,KAAK;EACL,KAAK,SACJ,OAAO,EAAE,MAAM,KAAK,KAAK;EAC1B,KAAK,WACJ,OAAO;GACN,MAAM;GACN,IAAI,KAAK;GACT,OAAO,KAAK;GACZ,OAAO,YAAY,KAAK,KAAK;EAC9B;EACD,KAAK,MACJ,OAAO;GACN,MAAM;GACN,OAAO,KAAK;GACZ,QAAQ,IAAI,IAAI,KAAK,QAAQ,WAAW;EACzC;EACD,KAAK,UACJ,OAAO;GAAE,MAAM;GAAU,OAAO,KAAK;EAAM;EAC5C,KAAK,SACJ,OAAO;GAAE,MAAM;GAAS,QAAQ,IAAI,IAAI,KAAK,QAAQ,WAAW;EAAE;EACnE,KAAK;EACL,KAAK,MACJ,OAAO;GAAE,MAAM,KAAK;GAAM,YAAY,IAAI,IAAI,KAAK,YAAY,MAAM;EAAE;EACxE,KAAK,OACJ,OAAO;GAAE,MAAM;GAAO,WAAW,OAAO,KAAK,SAAS;EAAE;EACzD,KAAK,aACJ,MAAM,IAAI,MACT,kBAAkB,KAAK,KAAK,sEAC7B;CACF;AACD;;AAGA,MAAa,UAAU,MAA0B,aAAa;AAE9D,MAAM,gCACL,IAAI,MACH,uGACD;;AAGD,MAAa,UAAU,MAAqB;CAC3C,IAAI,CAAC,OAAO,CAAC,GACZ,MAAM,QAAQ;CAEf,OAAO;AACR;AAEA,MAAa,SAAe,IAAI,KAAK;AAErC,MAAa,QAAc,IAAI,MAAM"}
|
|
@@ -66,7 +66,7 @@ const partFits = (part, segment) => {
|
|
|
66
66
|
}
|
|
67
67
|
};
|
|
68
68
|
/** Adds the place after each `**`, since a `**` may match no names. */
|
|
69
|
-
const close = (pattern, states) =>
|
|
69
|
+
const close = (pattern, states) => Array.from(new Set(Arr.flatMap(states, (state) => pattern[state]?._tag === "Globstar" ? [state, state + 1] : [state])));
|
|
70
70
|
const start = (pattern) => close(pattern, [0]);
|
|
71
71
|
/** Gives the places after one more segment. A `**` that takes it stays. */
|
|
72
72
|
const step = (pattern, states, segment) => close(pattern, Arr.flatMap(states, (state) => {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"patterns.js","names":["Catalog.keyFits"],"sources":["../../src/internal/patterns.ts"],"sourcesContent":["/**\n * Field patterns, as in `settings.*`, `secret*` or `**.secret`. A pattern is\n * a list of parts with dots between them:\n * - A name with no star matches that name.\n * - A part with stars matches a name, and each star matches any text with no\n * dot, as `secret*` matches `secretKey`.\n * - `**` matches any number of names, also none. It must be a whole part.\n *\n * A pattern that matches a field matches each field below it too. So `pay`\n * matches `pay.amount`, as `pay.**` does.\n *\n * The match goes one name at a time, and it keeps the set of places in the\n * pattern that it may be at. It does not go back, so the time is at most the\n * number of names times the number of parts.\n */\nimport * as Arr from \"effect/Array\"\nimport * as Result from \"effect/Result\"\nimport * as Catalog from \"./catalog.ts\"\nimport * as Limits from \"./limits.ts\"\n\n/** One part of a pattern. */\nexport type Part =\n\t| { readonly _tag: \"Name\"; readonly name: string }\n\t/** The texts between the stars of the part. */\n\t| { readonly _tag: \"Glob\"; readonly pieces: ReadonlyArray<string> }\n\t| { readonly _tag: \"Globstar\" }\n\n/** A pattern, as a list of parts. Two `**` parts are never next to each other. */\nexport type Pattern = ReadonlyArray<Part>\n\nconst globstar: Part = { _tag: \"Globstar\" }\n\nconst partOf = (text: string): Part =>\n\ttext === \"**\"\n\t\t? globstar\n\t\t: text.includes(\"*\")\n\t\t\t? { _tag: \"Glob\", pieces: text.split(\"*\") }\n\t\t\t: { _tag: \"Name\", name: text }\n\n/**\n * Reads a pattern. It fails with the problem when the pattern has `**`\n * inside a part, or has more parts than `patternParts`.\n */\nexport const parse = (text: string): Result.Result<Pattern, string> => {\n\tconst texts = text.split(\".\", Limits.patternParts + 1)\n\tif (texts.length > Limits.patternParts) {\n\t\treturn Result.fail(\n\t\t\t`The field pattern \"${text}\" has more than ${String(Limits.patternParts)} parts. The most is ${String(Limits.patternParts)}.`,\n\t\t)\n\t}\n\tif (Arr.some(texts, (part) => part !== \"**\" && part.includes(\"**\"))) {\n\t\treturn Result.fail(\n\t\t\t`The field pattern \"${text}\" has \"**\" inside a part. \"**\" must be a whole part`,\n\t\t)\n\t}\n\t// `a.**.**` matches what `a.**` matches.\n\treturn Result.succeed(\n\t\tArr.dedupeAdjacentWith(\n\t\t\tArr.map(texts, partOf),\n\t\t\t(left, right) => left._tag === \"Globstar\" && right._tag === \"Globstar\",\n\t\t),\n\t)\n}\n\n/**\n * Reads a pattern of a bound rule. The binding of the rule took the pattern,\n * so a problem here is a defect.\n */\nexport const parseBound = (text: string): Pattern =>\n\tResult.getOrThrowWith(parse(text), (problem) => new Error(problem))\n\n/** Tests if a name matches the pieces of a part with stars. */\nconst globMatches = (pieces: ReadonlyArray<string>, name: string): boolean => {\n\tconst first = pieces[0]!\n\tconst last = pieces[pieces.length - 1]!\n\tconst middle = pieces.slice(1, -1)\n\tif (\n\t\tname.length < first.length + last.length ||\n\t\t!name.startsWith(first) ||\n\t\t!name.endsWith(last)\n\t) {\n\t\treturn false\n\t}\n\t// Each piece in the middle is found as early as it can be. An earlier\n\t// place never leaves less room for the pieces after it.\n\tconst end = name.length - last.length\n\tconst place = Arr.reduce(middle, first.length as number, (at, piece) => {\n\t\tif (at < 0) {\n\t\t\treturn at\n\t\t}\n\t\tconst found = name.indexOf(piece, at)\n\t\treturn found < 0 || found + piece.length > end ? -1 : found + piece.length\n\t})\n\treturn place >= 0\n}\n\n/**\n * Tests if a part matches one segment of a path. A part with stars matches\n * any key of a record. A name matches a key of a record when the Schema of\n * the keys takes it.\n */\nconst partFits = (part: Part, segment: Catalog.Segment): boolean => {\n\tswitch (part._tag) {\n\t\tcase \"Globstar\":\n\t\t\treturn true\n\t\tcase \"Glob\":\n\t\t\treturn typeof segment !== \"string\" || globMatches(part.pieces, segment)\n\t\tcase \"Name\":\n\t\t\treturn typeof segment === \"string\"\n\t\t\t\t? part.name === segment\n\t\t\t\t: Catalog.keyFits(segment, part.name)\n\t}\n}\n\n/** The places of a match: the parts that it may take next. */\ntype States = ReadonlyArray<number>\n\n/** Adds the place after each `**`, since a `**` may match no names. */\nconst close = (pattern: Pattern, states: States): States =>\n\
|
|
1
|
+
{"version":3,"file":"patterns.js","names":["Catalog.keyFits"],"sources":["../../src/internal/patterns.ts"],"sourcesContent":["/**\n * Field patterns, as in `settings.*`, `secret*` or `**.secret`. A pattern is\n * a list of parts with dots between them:\n * - A name with no star matches that name.\n * - A part with stars matches a name, and each star matches any text with no\n * dot, as `secret*` matches `secretKey`.\n * - `**` matches any number of names, also none. It must be a whole part.\n *\n * A pattern that matches a field matches each field below it too. So `pay`\n * matches `pay.amount`, as `pay.**` does.\n *\n * The match goes one name at a time, and it keeps the set of places in the\n * pattern that it may be at. It does not go back, so the time is at most the\n * number of names times the number of parts.\n */\nimport * as Arr from \"effect/Array\"\nimport * as Result from \"effect/Result\"\nimport * as Catalog from \"./catalog.ts\"\nimport * as Limits from \"./limits.ts\"\n\n/** One part of a pattern. */\nexport type Part =\n\t| { readonly _tag: \"Name\"; readonly name: string }\n\t/** The texts between the stars of the part. */\n\t| { readonly _tag: \"Glob\"; readonly pieces: ReadonlyArray<string> }\n\t| { readonly _tag: \"Globstar\" }\n\n/** A pattern, as a list of parts. Two `**` parts are never next to each other. */\nexport type Pattern = ReadonlyArray<Part>\n\nconst globstar: Part = { _tag: \"Globstar\" }\n\nconst partOf = (text: string): Part =>\n\ttext === \"**\"\n\t\t? globstar\n\t\t: text.includes(\"*\")\n\t\t\t? { _tag: \"Glob\", pieces: text.split(\"*\") }\n\t\t\t: { _tag: \"Name\", name: text }\n\n/**\n * Reads a pattern. It fails with the problem when the pattern has `**`\n * inside a part, or has more parts than `patternParts`.\n */\nexport const parse = (text: string): Result.Result<Pattern, string> => {\n\tconst texts = text.split(\".\", Limits.patternParts + 1)\n\tif (texts.length > Limits.patternParts) {\n\t\treturn Result.fail(\n\t\t\t`The field pattern \"${text}\" has more than ${String(Limits.patternParts)} parts. The most is ${String(Limits.patternParts)}.`,\n\t\t)\n\t}\n\tif (Arr.some(texts, (part) => part !== \"**\" && part.includes(\"**\"))) {\n\t\treturn Result.fail(\n\t\t\t`The field pattern \"${text}\" has \"**\" inside a part. \"**\" must be a whole part`,\n\t\t)\n\t}\n\t// `a.**.**` matches what `a.**` matches.\n\treturn Result.succeed(\n\t\tArr.dedupeAdjacentWith(\n\t\t\tArr.map(texts, partOf),\n\t\t\t(left, right) => left._tag === \"Globstar\" && right._tag === \"Globstar\",\n\t\t),\n\t)\n}\n\n/**\n * Reads a pattern of a bound rule. The binding of the rule took the pattern,\n * so a problem here is a defect.\n */\nexport const parseBound = (text: string): Pattern =>\n\tResult.getOrThrowWith(parse(text), (problem) => new Error(problem))\n\n/** Tests if a name matches the pieces of a part with stars. */\nconst globMatches = (pieces: ReadonlyArray<string>, name: string): boolean => {\n\tconst first = pieces[0]!\n\tconst last = pieces[pieces.length - 1]!\n\tconst middle = pieces.slice(1, -1)\n\tif (\n\t\tname.length < first.length + last.length ||\n\t\t!name.startsWith(first) ||\n\t\t!name.endsWith(last)\n\t) {\n\t\treturn false\n\t}\n\t// Each piece in the middle is found as early as it can be. An earlier\n\t// place never leaves less room for the pieces after it.\n\tconst end = name.length - last.length\n\tconst place = Arr.reduce(middle, first.length as number, (at, piece) => {\n\t\tif (at < 0) {\n\t\t\treturn at\n\t\t}\n\t\tconst found = name.indexOf(piece, at)\n\t\treturn found < 0 || found + piece.length > end ? -1 : found + piece.length\n\t})\n\treturn place >= 0\n}\n\n/**\n * Tests if a part matches one segment of a path. A part with stars matches\n * any key of a record. A name matches a key of a record when the Schema of\n * the keys takes it.\n */\nconst partFits = (part: Part, segment: Catalog.Segment): boolean => {\n\tswitch (part._tag) {\n\t\tcase \"Globstar\":\n\t\t\treturn true\n\t\tcase \"Glob\":\n\t\t\treturn typeof segment !== \"string\" || globMatches(part.pieces, segment)\n\t\tcase \"Name\":\n\t\t\treturn typeof segment === \"string\"\n\t\t\t\t? part.name === segment\n\t\t\t\t: Catalog.keyFits(segment, part.name)\n\t}\n}\n\n/** The places of a match: the parts that it may take next. */\ntype States = ReadonlyArray<number>\n\n/** Adds the place after each `**`, since a `**` may match no names. */\nconst close = (pattern: Pattern, states: States): States =>\n\t// A native `Set` keeps the first copy of each place, in order. It is\n\t// faster than `Arr.dedupe`, and this is on the path of each match.\n\tArray.from(\n\t\tnew Set(\n\t\t\tArr.flatMap(states, (state) =>\n\t\t\t\tpattern[state]?._tag === \"Globstar\" ? [state, state + 1] : [state],\n\t\t\t),\n\t\t),\n\t)\n\nconst start = (pattern: Pattern): States => close(pattern, [0])\n\n/** Gives the places after one more segment. A `**` that takes it stays. */\nconst step = (\n\tpattern: Pattern,\n\tstates: States,\n\tsegment: Catalog.Segment,\n): States =>\n\tclose(\n\t\tpattern,\n\t\tArr.flatMap(states, (state) => {\n\t\t\tconst part = pattern[state]\n\t\t\treturn part === undefined\n\t\t\t\t? []\n\t\t\t\t: part._tag === \"Globstar\"\n\t\t\t\t\t? [state]\n\t\t\t\t\t: partFits(part, segment)\n\t\t\t\t\t\t? [state + 1]\n\t\t\t\t\t\t: []\n\t\t}),\n\t)\n\nconst isDone = (pattern: Pattern, states: States): boolean =>\n\tstates.includes(pattern.length)\n\n/**\n * Tests if a pattern matches a field, or a field above it. The field is a\n * list of names.\n */\nexport const matches = (\n\tpattern: Pattern,\n\tnames: ReadonlyArray<string>,\n): boolean => {\n\tconst found = Arr.reduce(\n\t\tnames,\n\t\t{ states: start(pattern), done: isDone(pattern, start(pattern)) },\n\t\t(before, name) => {\n\t\t\tif (before.done || before.states.length === 0) {\n\t\t\t\treturn before\n\t\t\t}\n\t\t\tconst states = step(pattern, before.states, name)\n\t\t\treturn { states, done: isDone(pattern, states) }\n\t\t},\n\t)\n\treturn found.done\n}\n\n/**\n * Tests if a pattern may match a field below a field, with more names after\n * the names of the field.\n */\nexport const matchesBelow = (\n\tpattern: Pattern,\n\tnames: ReadonlyArray<string>,\n): boolean =>\n\tArr.some(\n\t\tArr.reduce(names, start(pattern), (states, name) =>\n\t\t\tstep(pattern, states, name),\n\t\t),\n\t\t(state) => state < pattern.length,\n\t)\n\n/** Tests if a pattern matches some field of a catalog. */\nexport const fitsCatalog = (\n\tpattern: Pattern,\n\tcatalog: Catalog.Catalog,\n): boolean => {\n\tconst below = (branch: Catalog.Branch, states: States): boolean =>\n\t\tstates.length > 0 &&\n\t\t(Arr.some(branch.children, ([name, child]) => visit(child, name, states)) ||\n\t\t\t(branch.key !== undefined &&\n\t\t\t\tbranch.keyBranch !== undefined &&\n\t\t\t\tvisit(branch.keyBranch, branch.key, states)))\n\tconst visit = (\n\t\tbranch: Catalog.Branch,\n\t\tsegment: Catalog.Segment,\n\t\tbefore: States,\n\t): boolean => {\n\t\tconst states = step(pattern, before, segment)\n\t\treturn (\n\t\t\t(branch.field !== undefined && isDone(pattern, states)) ||\n\t\t\tbelow(branch, states)\n\t\t)\n\t}\n\treturn below(catalog.root, start(pattern))\n}\n"],"mappings":"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"}
|
|
@@ -2,7 +2,7 @@ import { LimitExceeded, policyNodes } from "./limits.js";
|
|
|
2
2
|
import { Problem, decodeAt, problem, read, readList, readString, spend, toJson as toJson$1 } from "./json.js";
|
|
3
3
|
import { at, pathOf } from "./place.js";
|
|
4
4
|
import { make } from "./rule.js";
|
|
5
|
-
import { Ceiling, Rules, policy_nodes_exports, rulesOf } from "./policy-nodes.js";
|
|
5
|
+
import { Ceiling, Rules, isAlgorithm, policy_nodes_exports, rulesOf } from "./policy-nodes.js";
|
|
6
6
|
import * as Effect from "effect/Effect";
|
|
7
7
|
import * as Schema from "effect/Schema";
|
|
8
8
|
import * as Arr from "effect/Array";
|
|
@@ -81,12 +81,6 @@ const encode = (node, meta) => Effect.gen(function* () {
|
|
|
81
81
|
const forms = new Map(encoded);
|
|
82
82
|
return toJson(node, (rule) => forms.get(rule));
|
|
83
83
|
});
|
|
84
|
-
const algorithms = [
|
|
85
|
-
"denyOverrides",
|
|
86
|
-
"permitOverrides",
|
|
87
|
-
"firstApplicable",
|
|
88
|
-
"lastApplicable"
|
|
89
|
-
];
|
|
90
84
|
/** Reads a list of strings. */
|
|
91
85
|
const readStrings = (input, name, path, budget) => Arr.map(readList(input, name, path, budget), (value, index) => {
|
|
92
86
|
if (typeof value !== "string") throw problem([
|
|
@@ -175,7 +169,7 @@ const readTree = (input, path, place, depth, budget, rules) => {
|
|
|
175
169
|
switch (tag) {
|
|
176
170
|
case "Rules": {
|
|
177
171
|
const algorithm = readString(input, "algorithm", path);
|
|
178
|
-
if (!
|
|
172
|
+
if (!isAlgorithm(algorithm)) throw problem([...path, "algorithm"], `"${algorithm}" is not a combining algorithm`);
|
|
179
173
|
const found = Arr.map(readList(input, "rules", path, budget), (rule, index) => rules.push({
|
|
180
174
|
input: rule,
|
|
181
175
|
place: at(place, "rules", index)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"policy-json.js","names":["PolicyNodes.rulesOf","Json.toJson","Json.readList","Json.problem","read","Json.read","Place.pathOf","Json.Problem","Json.decodeAt","Json.readString","Rule.make","Place.at","Limits.policyNodes","PolicyNodes.Rules","PolicyNodes","PolicyNodes.Ceiling","Limits.LimitExceeded"],"sources":["../../src/internal/policy-json.ts"],"sourcesContent":["/**\n * The JSON form of a policy tree. The decoder is a plain walk, as the decoder\n * of a condition is, and the two share one budget of `jsonNodes` objects and\n * items. It makes each node with its constructor, so the limits of the nodes\n * hold for a decoded tree too. The binding of the rules to the domain comes\n * after, in the Schema of the policy.\n */\nimport * as Arr from \"effect/Array\"\nimport * as Effect from \"effect/Effect\"\nimport * as Schema from \"effect/Schema\"\nimport type * as Domain from \"../Domain.ts\"\nimport * as Rule from \"./rule.ts\"\nimport * as Json from \"./json.ts\"\nimport * as Limits from \"./limits.ts\"\nimport * as Place from \"./place.ts\"\nimport * as PolicyNodes from \"./policy-nodes.ts\"\n\ntype Node = PolicyNodes.Node\n\n/**\n * The codec of the `meta` Schema of a domain to and from JSON. The JSON form\n * of a policy must stay the same through `JSON.stringify` and `JSON.parse`, so\n * a `Date` in `meta`, for one, becomes a string in it, and a string again\n * when it is decoded.\n */\nconst metaCodec = (meta: Schema.Top): Schema.Codec<unknown, unknown> =>\n\tSchema.toCodecJson(meta) as unknown as Schema.Codec<unknown, unknown>\n\n/** Gives the JSON form of the `meta` of a rule, or `undefined` for none. */\nexport type MetaJson = (rule: Rule.Rule) => unknown\n\n/**\n * Gives the JSON form of the `meta` of each rule at once, with the `meta`\n * Schema of a domain. `JSON.stringify` needs it at once, so the Schema must\n * not need an async step there.\n */\nexport const syncMeta =\n\t(meta: Schema.Top | undefined): MetaJson =>\n\t(rule) =>\n\t\trule.meta === undefined || meta === undefined\n\t\t\t? undefined\n\t\t\t: Schema.encodeUnknownSync(metaCodec(meta))(rule.meta)\n\n/** The JSON form of the condition of each rule, by the rule. */\ntype WhenJson = (rule: Rule.Rule) => unknown\n\n/** Gives the JSON form of a rule. The domain of a rule is not in it. */\nconst ruleJson = (\n\trule: Rule.Rule,\n\tmetaJson: MetaJson,\n\twhenJson: WhenJson,\n): unknown => {\n\tconst meta = metaJson(rule)\n\treturn {\n\t\t_tag: rule._tag,\n\t\tactions: rule.actions,\n\t\tresources: rule.resources,\n\t\t...(rule.when === undefined ? {} : { when: whenJson(rule) }),\n\t\t...(rule.fields === undefined ? {} : { fields: rule.fields }),\n\t\t...(rule.reason === undefined ? {} : { reason: rule.reason }),\n\t\t...(rule.id === undefined ? {} : { id: rule.id }),\n\t\t// The `meta` of a rule is a decoded value. Its JSON form comes from the\n\t\t// `meta` Schema of the domain.\n\t\t...(meta === undefined ? {} : { meta }),\n\t}\n}\n\n/**\n * Gives the JSON form of a policy tree with no predicate. A condition with a\n * predicate has no JSON form, so it throws an error. The conditions are\n * written first, from a flat list of the rules, so the walk does not go down\n * the levels of the tree and of a condition at once.\n */\nexport const toJson = (node: Node, meta: MetaJson): unknown => {\n\t// A native map, because it must find a rule by the rule itself: two rules\n\t// with the same data are `Equal`.\n\tconst whens = new Map<Rule.Rule, unknown>(\n\t\tArr.map(PolicyNodes.rulesOf(node), (rule) => [\n\t\t\trule,\n\t\t\trule.when === undefined ? undefined : Json.toJson(rule.when as never),\n\t\t]),\n\t)\n\tconst tree = (current: Node): unknown => {\n\t\tswitch (current._tag) {\n\t\t\tcase \"Rules\":\n\t\t\t\treturn {\n\t\t\t\t\t_tag: \"Rules\",\n\t\t\t\t\talgorithm: current.algorithm,\n\t\t\t\t\trules: Arr.map(current.rules, (rule) =>\n\t\t\t\t\t\truleJson(rule, meta, (found) => whens.get(found)),\n\t\t\t\t\t),\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 {\n\t\t\t\t\t_tag: current._tag,\n\t\t\t\t\tpolicies: Arr.map(current.policies, tree),\n\t\t\t\t}\n\t\t\tcase \"Ceiling\":\n\t\t\t\treturn {\n\t\t\t\t\t_tag: \"Ceiling\",\n\t\t\t\t\tcap: tree(current.cap),\n\t\t\t\t\tpolicy: tree(current.policy),\n\t\t\t\t}\n\t\t}\n\t}\n\treturn tree(node)\n}\n\n/**\n * Gives the JSON form of a policy tree as an Effect. The `meta` Schema of the\n * domain may need an async step, so each `meta` is encoded as an Effect.\n */\nexport const encode = (\n\tnode: Node,\n\tmeta: Schema.Top | undefined,\n): Effect.Effect<unknown, Schema.SchemaError> =>\n\tEffect.gen(function* () {\n\t\tconst rules = Arr.filter(\n\t\t\tPolicyNodes.rulesOf(node),\n\t\t\t(rule) => rule.meta !== undefined,\n\t\t)\n\t\tconst encoded =\n\t\t\tmeta === undefined\n\t\t\t\t? []\n\t\t\t\t: yield* Effect.forEach(rules, (rule) =>\n\t\t\t\t\t\tEffect.map(\n\t\t\t\t\t\t\tSchema.encodeUnknownEffect(metaCodec(meta))(rule.meta),\n\t\t\t\t\t\t\t(json) => [rule, json] as const,\n\t\t\t\t\t\t),\n\t\t\t\t\t)\n\t\t// A native map, because it must find a rule by the rule itself: two\n\t\t// rules with the same data are `Equal`, and each keeps its own form.\n\t\tconst forms = new Map<Rule.Rule, unknown>(encoded)\n\t\treturn toJson(node, (rule) => forms.get(rule))\n\t})\n\nconst algorithms: ReadonlyArray<string> = [\n\t\"denyOverrides\",\n\t\"permitOverrides\",\n\t\"firstApplicable\",\n\t\"lastApplicable\",\n]\n\n/** Reads a list of strings. */\nconst readStrings = (\n\tinput: object,\n\tname: string,\n\tpath: ReadonlyArray<PropertyKey>,\n\tbudget: Json.Budget,\n): ReadonlyArray<string> =>\n\tArr.map(Json.readList(input, name, path, budget), (value, index) => {\n\t\tif (typeof value !== \"string\") {\n\t\t\tthrow Json.problem([...path, name, index], \"The item must be a string\")\n\t\t}\n\t\treturn value\n\t})\n\n/**\n * Reads a field that a rule may leave out. A field that the input has only\n * from the prototype of a class is not plain data, so it is a problem: a rule\n * that loses its condition would allow more.\n */\nconst readOptional = <A>(\n\tinput: object,\n\tname: string,\n\tpath: ReadonlyArray<PropertyKey>,\n\tread: () => A,\n): A | undefined => {\n\tif (Object.hasOwn(input, name)) {\n\t\treturn read()\n\t}\n\t// A read of the field throws a problem when the field is not data.\n\tJson.read(input, name, path)\n\treturn undefined\n}\n\n/** Counts the objects and items of a value, as the other parts count. */\nconst spendValue = (\n\tvalue: unknown,\n\tpath: ReadonlyArray<PropertyKey>,\n\tdepth: number,\n\tbudget: Json.Budget,\n): void => {\n\tif (typeof value !== \"object\" || value === null) {\n\t\treturn\n\t}\n\tif (depth > Limits.conditionDepth) {\n\t\tthrow Json.problem(\n\t\t\tpath,\n\t\t\t`The value has more than ${String(Limits.conditionDepth)} levels. The most is ${String(Limits.conditionDepth)}.`,\n\t\t)\n\t}\n\t// The length of an array counts before its items are read, so a sparse\n\t// array with a very large length stops at once.\n\tconst list = Array.isArray(value)\n\tif (list) {\n\t\tJson.spend(budget, 1 + value.length, path)\n\t}\n\tconst names = list\n\t\t? Array.from({ length: value.length }, (_, index) => String(index))\n\t\t: Object.keys(value)\n\tif (!list) {\n\t\tJson.spend(budget, 1 + names.length, path)\n\t}\n\tArr.forEach(names, (name) =>\n\t\tspendValue(\n\t\t\tJson.read(value, name, path),\n\t\t\t[...path, name],\n\t\t\tdepth + 1,\n\t\t\tbudget,\n\t\t),\n\t)\n}\n\n/**\n * A rule in the input, with its place, to decode after the tree. The place\n * shares the path of its parent: see `Place`.\n */\ninterface RuleInput {\n\treadonly input: unknown\n\treadonly place: Place.Place\n}\n\n/**\n * The shape of a tree, with the place of each rule in a flat list. `size`\n * counts the nodes and the rules, as the nodes do, so a shared input stops\n * at `policyNodes` before the decoder reads all of it.\n */\ntype Plan =\n\t| {\n\t\t\treadonly _tag: \"Rules\"\n\t\t\treadonly place: Place.Place | undefined\n\t\t\treadonly size: number\n\t\t\treadonly algorithm: PolicyNodes.Algorithm\n\t\t\treadonly rules: ReadonlyArray<number>\n\t }\n\t| {\n\t\t\treadonly _tag: \"All\" | \"Any\" | \"FirstOf\"\n\t\t\treadonly place: Place.Place | undefined\n\t\t\treadonly size: number\n\t\t\treadonly policies: ReadonlyArray<Plan>\n\t }\n\t| {\n\t\t\treadonly _tag: \"Ceiling\"\n\t\t\treadonly place: Place.Place | undefined\n\t\t\treadonly size: number\n\t\t\treadonly cap: Plan\n\t\t\treadonly policy: Plan\n\t }\n\n/**\n * Decodes the `meta` of a rule with the `meta` Schema of the domain, as an\n * Effect, since the Schema may need an async step. The Schema reads all of\n * the value, so its size counts in the budget.\n */\nconst decodeMeta = (\n\trule: RuleInput,\n\tdomain: Domain.Any,\n\tbudget: Json.Budget,\n): Effect.Effect<unknown, Json.Problem> => {\n\tconst { input } = rule\n\tconst path = Place.pathOf(rule.place)\n\treturn Effect.flatMap(\n\t\tattempt(() =>\n\t\t\ttypeof input !== \"object\" || input === null\n\t\t\t\t? undefined\n\t\t\t\t: readOptional(input, \"meta\", path, () => {\n\t\t\t\t\t\tconst value = Json.read(input, \"meta\", path)\n\t\t\t\t\t\tif (domain.meta === undefined) {\n\t\t\t\t\t\t\tthrow Json.problem(\n\t\t\t\t\t\t\t\t[...path, \"meta\"],\n\t\t\t\t\t\t\t\t\"The domain has no Schema for meta\",\n\t\t\t\t\t\t\t)\n\t\t\t\t\t\t}\n\t\t\t\t\t\tspendValue(value, [...path, \"meta\"], 1, budget)\n\t\t\t\t\t\treturn { value, schema: domain.meta }\n\t\t\t\t\t}),\n\t\t),\n\t\t(found) =>\n\t\t\tfound === undefined\n\t\t\t\t? Effect.void\n\t\t\t\t: Effect.mapError(\n\t\t\t\t\t\tSchema.decodeUnknownEffect(metaCodec(found.schema))(found.value),\n\t\t\t\t\t\t(error) =>\n\t\t\t\t\t\t\tnew Json.Problem({\n\t\t\t\t\t\t\t\tpath: [...path, \"meta\"],\n\t\t\t\t\t\t\t\tmessage: error.message,\n\t\t\t\t\t\t\t\tissue: error.issue,\n\t\t\t\t\t\t\t}),\n\t\t\t\t\t),\n\t)\n}\n\nconst decodeRule = (\n\trule: RuleInput,\n\tmeta: unknown,\n\tbudget: Json.Budget,\n\tdomain: Domain.Any,\n): Rule.Rule => {\n\tconst { input } = rule\n\tconst path = Place.pathOf(rule.place)\n\tif (typeof input !== \"object\" || input === null) {\n\t\tthrow Json.problem(path, \"A rule must be an object\")\n\t}\n\tJson.spend(budget, 1, path)\n\tconst tag = Json.read(input, \"_tag\", path)\n\tif (tag !== \"Allow\" && tag !== \"Deny\") {\n\t\tthrow Json.problem([...path, \"_tag\"], 'A rule must be \"Allow\" or \"Deny\"')\n\t}\n\tconst when = readOptional(input, \"when\", path, () =>\n\t\tJson.decodeAt(Json.read(input, \"when\", path), [...path, \"when\"], budget),\n\t)\n\tconst fields = readOptional(input, \"fields\", path, () =>\n\t\treadStrings(input, \"fields\", path, budget),\n\t)\n\tconst reason = readOptional(input, \"reason\", path, () =>\n\t\tJson.readString(input, \"reason\", path),\n\t)\n\tconst id = readOptional(input, \"id\", path, () =>\n\t\tJson.readString(input, \"id\", path),\n\t)\n\t// Each rule gets the domain of the Schema, as a rule from the builder of\n\t// the domain has it.\n\treturn Rule.make({\n\t\t_tag: tag,\n\t\tactions: readStrings(input, \"actions\", path, budget),\n\t\tresources: readStrings(input, \"resources\", path, budget),\n\t\t...(when === undefined ? {} : { when: when as never }),\n\t\t...(fields === undefined ? {} : { fields }),\n\t\t...(reason === undefined ? {} : { reason }),\n\t\t...(id === undefined ? {} : { id }),\n\t\t...(meta === undefined ? {} : { meta }),\n\t\tdomain,\n\t})\n}\n\n/**\n * Reads the shape of a tree, and puts each rule in `rules`. The rules are\n * decoded after the walk: a condition has levels of its own, and a decode\n * inside the walk would go down the levels of both. `path` is the path of the\n * node for a problem here, and `place` keeps it for later.\n */\nconst readTree = (\n\tinput: unknown,\n\tpath: ReadonlyArray<PropertyKey>,\n\tplace: Place.Place | undefined,\n\tdepth: number,\n\tbudget: Json.Budget,\n\t// The rules of one decode. The list belongs to that decode.\n\trules: Array<RuleInput>,\n): Plan => {\n\tif (depth > Limits.policyDepth) {\n\t\tthrow Json.problem(\n\t\t\tpath,\n\t\t\t`A policy tree has more than ${String(Limits.policyDepth)} levels. The most is ${String(Limits.policyDepth)}.`,\n\t\t)\n\t}\n\tif (typeof input !== \"object\" || input === null) {\n\t\tthrow Json.problem(path, \"A node of a policy must be an object\")\n\t}\n\tJson.spend(budget, 1, path)\n\tconst child = (value: unknown, ...parts: ReadonlyArray<PropertyKey>) =>\n\t\treadTree(\n\t\t\tvalue,\n\t\t\t[...path, ...parts],\n\t\t\tPlace.at(place, ...parts),\n\t\t\tdepth + 1,\n\t\t\tbudget,\n\t\t\trules,\n\t\t)\n\tconst tag = Json.read(input, \"_tag\", path)\n\tswitch (tag) {\n\t\tcase \"Rules\": {\n\t\t\tconst algorithm = Json.readString(input, \"algorithm\", path)\n\t\t\tif (!algorithms.includes(algorithm)) {\n\t\t\t\tthrow Json.problem(\n\t\t\t\t\t[...path, \"algorithm\"],\n\t\t\t\t\t`\"${algorithm}\" is not a combining algorithm`,\n\t\t\t\t)\n\t\t\t}\n\t\t\tconst found = Arr.map(\n\t\t\t\tJson.readList(input, \"rules\", path, budget),\n\t\t\t\t(rule, index) =>\n\t\t\t\t\trules.push({\n\t\t\t\t\t\tinput: rule,\n\t\t\t\t\t\tplace: Place.at(place, \"rules\", index),\n\t\t\t\t\t}) - 1,\n\t\t\t)\n\t\t\treturn sized(path, {\n\t\t\t\t_tag: \"Rules\",\n\t\t\t\tplace,\n\t\t\t\tsize: 1 + found.length,\n\t\t\t\talgorithm: algorithm as PolicyNodes.Algorithm,\n\t\t\t\trules: found,\n\t\t\t})\n\t\t}\n\t\tcase \"All\":\n\t\tcase \"Any\":\n\t\tcase \"FirstOf\": {\n\t\t\tconst policies = Arr.map(\n\t\t\t\tJson.readList(input, \"policies\", path, budget),\n\t\t\t\t(value, index) => child(value, \"policies\", index),\n\t\t\t)\n\t\t\treturn sized(path, {\n\t\t\t\t_tag: tag,\n\t\t\t\tplace,\n\t\t\t\tsize: Arr.reduce(policies, 1, (total, plan) => total + plan.size),\n\t\t\t\tpolicies,\n\t\t\t})\n\t\t}\n\t\tcase \"Ceiling\": {\n\t\t\tconst cap = child(Json.read(input, \"cap\", path), \"cap\")\n\t\t\tconst policy = child(Json.read(input, \"policy\", path), \"policy\")\n\t\t\treturn sized(path, {\n\t\t\t\t_tag: \"Ceiling\",\n\t\t\t\tplace,\n\t\t\t\tsize: 1 + cap.size + policy.size,\n\t\t\t\tcap,\n\t\t\t\tpolicy,\n\t\t\t})\n\t\t}\n\t\tdefault:\n\t\t\tthrow Json.problem([...path, \"_tag\"], \"The node has no known tag\")\n\t}\n}\n\n/** Stops a plan past `policyNodes`, with the message of the nodes. */\nconst sized = (path: ReadonlyArray<PropertyKey>, plan: Plan): Plan => {\n\tif (plan.size > Limits.policyNodes) {\n\t\tthrow Json.problem(\n\t\t\tpath,\n\t\t\t`A policy tree has ${String(plan.size)} nodes. The most is ${String(Limits.policyNodes)}.`,\n\t\t)\n\t}\n\treturn plan\n}\n\n/** Makes the nodes of a plan, with the decoded rules. */\nconst build = (plan: Plan, rules: ReadonlyArray<Rule.Rule>): Node => {\n\ttry {\n\t\tswitch (plan._tag) {\n\t\t\tcase \"Rules\":\n\t\t\t\treturn new PolicyNodes.Rules({\n\t\t\t\t\talgorithm: plan.algorithm,\n\t\t\t\t\trules: Arr.map(plan.rules, (index) => rules[index]!),\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 new PolicyNodes[plan._tag](\n\t\t\t\t\tArr.map(plan.policies, (child) => build(child, rules)),\n\t\t\t\t)\n\t\t\tcase \"Ceiling\":\n\t\t\t\treturn new PolicyNodes.Ceiling({\n\t\t\t\t\tcap: build(plan.cap, rules),\n\t\t\t\t\tpolicy: build(plan.policy, rules),\n\t\t\t\t})\n\t\t}\n\t} catch (error) {\n\t\t// A node past a limit is a problem at its place.\n\t\tif (error instanceof Limits.LimitExceeded) {\n\t\t\tthrow Json.problem(Place.pathOf(plan.place), error.message)\n\t\t}\n\t\tthrow error\n\t}\n}\n\n/** Runs a step that may throw a problem, and puts the problem in the error. */\nconst attempt = <A>(step: () => A): Effect.Effect<A, Json.Problem> =>\n\tEffect.try({\n\t\ttry: step,\n\t\tcatch: (error) => {\n\t\t\tif (error instanceof Json.Problem) {\n\t\t\t\treturn error\n\t\t\t}\n\t\t\tthrow error\n\t\t},\n\t})\n\n/**\n * Decodes the JSON form of a policy tree for a domain. It reads the tree\n * first, then the `meta` of each rule as an Effect, then the rules, and it\n * makes the nodes last.\n */\nexport const decode = (\n\tinput: unknown,\n\tdomain: Domain.Any,\n): Effect.Effect<Node, Json.Problem> =>\n\tEffect.gen(function* () {\n\t\tconst budget: Json.Budget = { used: 0 }\n\t\t// The rules of this decode. The list belongs to this decode.\n\t\tconst inputs: Array<RuleInput> = []\n\t\tconst plan = yield* attempt(() =>\n\t\t\treadTree(input, [], undefined, 1, budget, inputs),\n\t\t)\n\t\tconst metas = yield* Effect.forEach(inputs, (rule) =>\n\t\t\tdecodeMeta(rule, domain, budget),\n\t\t)\n\t\tconst rules = yield* attempt(() =>\n\t\t\tArr.map(inputs, (rule, index) =>\n\t\t\t\tdecodeRule(rule, metas[index], budget, domain),\n\t\t\t),\n\t\t)\n\t\treturn yield* attempt(() => build(plan, rules))\n\t})\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"policy-json.js","names":["PolicyNodes.rulesOf","Json.toJson","Json.readList","Json.problem","read","Json.read","Place.pathOf","Json.Problem","Json.decodeAt","Json.readString","Rule.make","Place.at","PolicyNodes.isAlgorithm","Limits.policyNodes","PolicyNodes.Rules","PolicyNodes","PolicyNodes.Ceiling","Limits.LimitExceeded"],"sources":["../../src/internal/policy-json.ts"],"sourcesContent":["/**\n * The JSON form of a policy tree. The decoder is a plain walk, as the decoder\n * of a condition is, and the two share one budget of `jsonNodes` objects and\n * items. It makes each node with its constructor, so the limits of the nodes\n * hold for a decoded tree too. The binding of the rules to the domain comes\n * after, in the Schema of the policy.\n */\nimport * as Arr from \"effect/Array\"\nimport * as Effect from \"effect/Effect\"\nimport * as Schema from \"effect/Schema\"\nimport type * as Domain from \"../Domain.ts\"\nimport * as Rule from \"./rule.ts\"\nimport * as Json from \"./json.ts\"\nimport * as Limits from \"./limits.ts\"\nimport * as Place from \"./place.ts\"\nimport * as PolicyNodes from \"./policy-nodes.ts\"\n\ntype Node = PolicyNodes.Node\n\n/**\n * The codec of the `meta` Schema of a domain to and from JSON. The JSON form\n * of a policy must stay the same through `JSON.stringify` and `JSON.parse`, so\n * a `Date` in `meta`, for one, becomes a string in it, and a string again\n * when it is decoded.\n */\nconst metaCodec = (meta: Schema.Top): Schema.Codec<unknown, unknown> =>\n\tSchema.toCodecJson(meta) as unknown as Schema.Codec<unknown, unknown>\n\n/** Gives the JSON form of the `meta` of a rule, or `undefined` for none. */\nexport type MetaJson = (rule: Rule.Rule) => unknown\n\n/**\n * Gives the JSON form of the `meta` of each rule at once, with the `meta`\n * Schema of a domain. `JSON.stringify` needs it at once, so the Schema must\n * not need an async step there.\n */\nexport const syncMeta =\n\t(meta: Schema.Top | undefined): MetaJson =>\n\t(rule) =>\n\t\trule.meta === undefined || meta === undefined\n\t\t\t? undefined\n\t\t\t: Schema.encodeUnknownSync(metaCodec(meta))(rule.meta)\n\n/** The JSON form of the condition of each rule, by the rule. */\ntype WhenJson = (rule: Rule.Rule) => unknown\n\n/** Gives the JSON form of a rule. The domain of a rule is not in it. */\nconst ruleJson = (\n\trule: Rule.Rule,\n\tmetaJson: MetaJson,\n\twhenJson: WhenJson,\n): unknown => {\n\tconst meta = metaJson(rule)\n\treturn {\n\t\t_tag: rule._tag,\n\t\tactions: rule.actions,\n\t\tresources: rule.resources,\n\t\t...(rule.when === undefined ? {} : { when: whenJson(rule) }),\n\t\t...(rule.fields === undefined ? {} : { fields: rule.fields }),\n\t\t...(rule.reason === undefined ? {} : { reason: rule.reason }),\n\t\t...(rule.id === undefined ? {} : { id: rule.id }),\n\t\t// The `meta` of a rule is a decoded value. Its JSON form comes from the\n\t\t// `meta` Schema of the domain.\n\t\t...(meta === undefined ? {} : { meta }),\n\t}\n}\n\n/**\n * Gives the JSON form of a policy tree with no predicate. A condition with a\n * predicate has no JSON form, so it throws an error. The conditions are\n * written first, from a flat list of the rules, so the walk does not go down\n * the levels of the tree and of a condition at once.\n */\nexport const toJson = (node: Node, meta: MetaJson): unknown => {\n\t// A native map, because it must find a rule by the rule itself: two rules\n\t// with the same data are `Equal`.\n\tconst whens = new Map<Rule.Rule, unknown>(\n\t\tArr.map(PolicyNodes.rulesOf(node), (rule) => [\n\t\t\trule,\n\t\t\trule.when === undefined ? undefined : Json.toJson(rule.when as never),\n\t\t]),\n\t)\n\tconst tree = (current: Node): unknown => {\n\t\tswitch (current._tag) {\n\t\t\tcase \"Rules\":\n\t\t\t\treturn {\n\t\t\t\t\t_tag: \"Rules\",\n\t\t\t\t\talgorithm: current.algorithm,\n\t\t\t\t\trules: Arr.map(current.rules, (rule) =>\n\t\t\t\t\t\truleJson(rule, meta, (found) => whens.get(found)),\n\t\t\t\t\t),\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 {\n\t\t\t\t\t_tag: current._tag,\n\t\t\t\t\tpolicies: Arr.map(current.policies, tree),\n\t\t\t\t}\n\t\t\tcase \"Ceiling\":\n\t\t\t\treturn {\n\t\t\t\t\t_tag: \"Ceiling\",\n\t\t\t\t\tcap: tree(current.cap),\n\t\t\t\t\tpolicy: tree(current.policy),\n\t\t\t\t}\n\t\t}\n\t}\n\treturn tree(node)\n}\n\n/**\n * Gives the JSON form of a policy tree as an Effect. The `meta` Schema of the\n * domain may need an async step, so each `meta` is encoded as an Effect.\n */\nexport const encode = (\n\tnode: Node,\n\tmeta: Schema.Top | undefined,\n): Effect.Effect<unknown, Schema.SchemaError> =>\n\tEffect.gen(function* () {\n\t\tconst rules = Arr.filter(\n\t\t\tPolicyNodes.rulesOf(node),\n\t\t\t(rule) => rule.meta !== undefined,\n\t\t)\n\t\tconst encoded =\n\t\t\tmeta === undefined\n\t\t\t\t? []\n\t\t\t\t: yield* Effect.forEach(rules, (rule) =>\n\t\t\t\t\t\tEffect.map(\n\t\t\t\t\t\t\tSchema.encodeUnknownEffect(metaCodec(meta))(rule.meta),\n\t\t\t\t\t\t\t(json) => [rule, json] as const,\n\t\t\t\t\t\t),\n\t\t\t\t\t)\n\t\t// A native map, because it must find a rule by the rule itself: two\n\t\t// rules with the same data are `Equal`, and each keeps its own form.\n\t\tconst forms = new Map<Rule.Rule, unknown>(encoded)\n\t\treturn toJson(node, (rule) => forms.get(rule))\n\t})\n\n/** Reads a list of strings. */\nconst readStrings = (\n\tinput: object,\n\tname: string,\n\tpath: ReadonlyArray<PropertyKey>,\n\tbudget: Json.Budget,\n): ReadonlyArray<string> =>\n\tArr.map(Json.readList(input, name, path, budget), (value, index) => {\n\t\tif (typeof value !== \"string\") {\n\t\t\tthrow Json.problem([...path, name, index], \"The item must be a string\")\n\t\t}\n\t\treturn value\n\t})\n\n/**\n * Reads a field that a rule may leave out. A field that the input has only\n * from the prototype of a class is not plain data, so it is a problem: a rule\n * that loses its condition would allow more.\n */\nconst readOptional = <A>(\n\tinput: object,\n\tname: string,\n\tpath: ReadonlyArray<PropertyKey>,\n\tread: () => A,\n): A | undefined => {\n\tif (Object.hasOwn(input, name)) {\n\t\treturn read()\n\t}\n\t// A read of the field throws a problem when the field is not data.\n\tJson.read(input, name, path)\n\treturn undefined\n}\n\n/** Counts the objects and items of a value, as the other parts count. */\nconst spendValue = (\n\tvalue: unknown,\n\tpath: ReadonlyArray<PropertyKey>,\n\tdepth: number,\n\tbudget: Json.Budget,\n): void => {\n\tif (typeof value !== \"object\" || value === null) {\n\t\treturn\n\t}\n\tif (depth > Limits.conditionDepth) {\n\t\tthrow Json.problem(\n\t\t\tpath,\n\t\t\t`The value has more than ${String(Limits.conditionDepth)} levels. The most is ${String(Limits.conditionDepth)}.`,\n\t\t)\n\t}\n\t// The length of an array counts before its items are read, so a sparse\n\t// array with a very large length stops at once.\n\tconst list = Array.isArray(value)\n\tif (list) {\n\t\tJson.spend(budget, 1 + value.length, path)\n\t}\n\tconst names = list\n\t\t? Array.from({ length: value.length }, (_, index) => String(index))\n\t\t: Object.keys(value)\n\tif (!list) {\n\t\tJson.spend(budget, 1 + names.length, path)\n\t}\n\tArr.forEach(names, (name) =>\n\t\tspendValue(\n\t\t\tJson.read(value, name, path),\n\t\t\t[...path, name],\n\t\t\tdepth + 1,\n\t\t\tbudget,\n\t\t),\n\t)\n}\n\n/**\n * A rule in the input, with its place, to decode after the tree. The place\n * shares the path of its parent: see `Place`.\n */\ninterface RuleInput {\n\treadonly input: unknown\n\treadonly place: Place.Place\n}\n\n/**\n * The shape of a tree, with the place of each rule in a flat list. `size`\n * counts the nodes and the rules, as the nodes do, so a shared input stops\n * at `policyNodes` before the decoder reads all of it.\n */\ntype Plan =\n\t| {\n\t\t\treadonly _tag: \"Rules\"\n\t\t\treadonly place: Place.Place | undefined\n\t\t\treadonly size: number\n\t\t\treadonly algorithm: PolicyNodes.Algorithm\n\t\t\treadonly rules: ReadonlyArray<number>\n\t }\n\t| {\n\t\t\treadonly _tag: \"All\" | \"Any\" | \"FirstOf\"\n\t\t\treadonly place: Place.Place | undefined\n\t\t\treadonly size: number\n\t\t\treadonly policies: ReadonlyArray<Plan>\n\t }\n\t| {\n\t\t\treadonly _tag: \"Ceiling\"\n\t\t\treadonly place: Place.Place | undefined\n\t\t\treadonly size: number\n\t\t\treadonly cap: Plan\n\t\t\treadonly policy: Plan\n\t }\n\n/**\n * Decodes the `meta` of a rule with the `meta` Schema of the domain, as an\n * Effect, since the Schema may need an async step. The Schema reads all of\n * the value, so its size counts in the budget.\n */\nconst decodeMeta = (\n\trule: RuleInput,\n\tdomain: Domain.Any,\n\tbudget: Json.Budget,\n): Effect.Effect<unknown, Json.Problem> => {\n\tconst { input } = rule\n\tconst path = Place.pathOf(rule.place)\n\treturn Effect.flatMap(\n\t\tattempt(() =>\n\t\t\ttypeof input !== \"object\" || input === null\n\t\t\t\t? undefined\n\t\t\t\t: readOptional(input, \"meta\", path, () => {\n\t\t\t\t\t\tconst value = Json.read(input, \"meta\", path)\n\t\t\t\t\t\tif (domain.meta === undefined) {\n\t\t\t\t\t\t\tthrow Json.problem(\n\t\t\t\t\t\t\t\t[...path, \"meta\"],\n\t\t\t\t\t\t\t\t\"The domain has no Schema for meta\",\n\t\t\t\t\t\t\t)\n\t\t\t\t\t\t}\n\t\t\t\t\t\tspendValue(value, [...path, \"meta\"], 1, budget)\n\t\t\t\t\t\treturn { value, schema: domain.meta }\n\t\t\t\t\t}),\n\t\t),\n\t\t(found) =>\n\t\t\tfound === undefined\n\t\t\t\t? Effect.void\n\t\t\t\t: Effect.mapError(\n\t\t\t\t\t\tSchema.decodeUnknownEffect(metaCodec(found.schema))(found.value),\n\t\t\t\t\t\t(error) =>\n\t\t\t\t\t\t\tnew Json.Problem({\n\t\t\t\t\t\t\t\tpath: [...path, \"meta\"],\n\t\t\t\t\t\t\t\tmessage: error.message,\n\t\t\t\t\t\t\t\tissue: error.issue,\n\t\t\t\t\t\t\t}),\n\t\t\t\t\t),\n\t)\n}\n\nconst decodeRule = (\n\trule: RuleInput,\n\tmeta: unknown,\n\tbudget: Json.Budget,\n\tdomain: Domain.Any,\n): Rule.Rule => {\n\tconst { input } = rule\n\tconst path = Place.pathOf(rule.place)\n\tif (typeof input !== \"object\" || input === null) {\n\t\tthrow Json.problem(path, \"A rule must be an object\")\n\t}\n\tJson.spend(budget, 1, path)\n\tconst tag = Json.read(input, \"_tag\", path)\n\tif (tag !== \"Allow\" && tag !== \"Deny\") {\n\t\tthrow Json.problem([...path, \"_tag\"], 'A rule must be \"Allow\" or \"Deny\"')\n\t}\n\tconst when = readOptional(input, \"when\", path, () =>\n\t\tJson.decodeAt(Json.read(input, \"when\", path), [...path, \"when\"], budget),\n\t)\n\tconst fields = readOptional(input, \"fields\", path, () =>\n\t\treadStrings(input, \"fields\", path, budget),\n\t)\n\tconst reason = readOptional(input, \"reason\", path, () =>\n\t\tJson.readString(input, \"reason\", path),\n\t)\n\tconst id = readOptional(input, \"id\", path, () =>\n\t\tJson.readString(input, \"id\", path),\n\t)\n\t// Each rule gets the domain of the Schema, as a rule from the builder of\n\t// the domain has it.\n\treturn Rule.make({\n\t\t_tag: tag,\n\t\tactions: readStrings(input, \"actions\", path, budget),\n\t\tresources: readStrings(input, \"resources\", path, budget),\n\t\t...(when === undefined ? {} : { when: when as never }),\n\t\t...(fields === undefined ? {} : { fields }),\n\t\t...(reason === undefined ? {} : { reason }),\n\t\t...(id === undefined ? {} : { id }),\n\t\t...(meta === undefined ? {} : { meta }),\n\t\tdomain,\n\t})\n}\n\n/**\n * Reads the shape of a tree, and puts each rule in `rules`. The rules are\n * decoded after the walk: a condition has levels of its own, and a decode\n * inside the walk would go down the levels of both. `path` is the path of the\n * node for a problem here, and `place` keeps it for later.\n */\nconst readTree = (\n\tinput: unknown,\n\tpath: ReadonlyArray<PropertyKey>,\n\tplace: Place.Place | undefined,\n\tdepth: number,\n\tbudget: Json.Budget,\n\t// The rules of one decode. The list belongs to that decode.\n\trules: Array<RuleInput>,\n): Plan => {\n\tif (depth > Limits.policyDepth) {\n\t\tthrow Json.problem(\n\t\t\tpath,\n\t\t\t`A policy tree has more than ${String(Limits.policyDepth)} levels. The most is ${String(Limits.policyDepth)}.`,\n\t\t)\n\t}\n\tif (typeof input !== \"object\" || input === null) {\n\t\tthrow Json.problem(path, \"A node of a policy must be an object\")\n\t}\n\tJson.spend(budget, 1, path)\n\tconst child = (value: unknown, ...parts: ReadonlyArray<PropertyKey>) =>\n\t\treadTree(\n\t\t\tvalue,\n\t\t\t[...path, ...parts],\n\t\t\tPlace.at(place, ...parts),\n\t\t\tdepth + 1,\n\t\t\tbudget,\n\t\t\trules,\n\t\t)\n\tconst tag = Json.read(input, \"_tag\", path)\n\tswitch (tag) {\n\t\tcase \"Rules\": {\n\t\t\tconst algorithm = Json.readString(input, \"algorithm\", path)\n\t\t\tif (!PolicyNodes.isAlgorithm(algorithm)) {\n\t\t\t\tthrow Json.problem(\n\t\t\t\t\t[...path, \"algorithm\"],\n\t\t\t\t\t`\"${algorithm}\" is not a combining algorithm`,\n\t\t\t\t)\n\t\t\t}\n\t\t\tconst found = Arr.map(\n\t\t\t\tJson.readList(input, \"rules\", path, budget),\n\t\t\t\t(rule, index) =>\n\t\t\t\t\trules.push({\n\t\t\t\t\t\tinput: rule,\n\t\t\t\t\t\tplace: Place.at(place, \"rules\", index),\n\t\t\t\t\t}) - 1,\n\t\t\t)\n\t\t\treturn sized(path, {\n\t\t\t\t_tag: \"Rules\",\n\t\t\t\tplace,\n\t\t\t\tsize: 1 + found.length,\n\t\t\t\talgorithm,\n\t\t\t\trules: found,\n\t\t\t})\n\t\t}\n\t\tcase \"All\":\n\t\tcase \"Any\":\n\t\tcase \"FirstOf\": {\n\t\t\tconst policies = Arr.map(\n\t\t\t\tJson.readList(input, \"policies\", path, budget),\n\t\t\t\t(value, index) => child(value, \"policies\", index),\n\t\t\t)\n\t\t\treturn sized(path, {\n\t\t\t\t_tag: tag,\n\t\t\t\tplace,\n\t\t\t\tsize: Arr.reduce(policies, 1, (total, plan) => total + plan.size),\n\t\t\t\tpolicies,\n\t\t\t})\n\t\t}\n\t\tcase \"Ceiling\": {\n\t\t\tconst cap = child(Json.read(input, \"cap\", path), \"cap\")\n\t\t\tconst policy = child(Json.read(input, \"policy\", path), \"policy\")\n\t\t\treturn sized(path, {\n\t\t\t\t_tag: \"Ceiling\",\n\t\t\t\tplace,\n\t\t\t\tsize: 1 + cap.size + policy.size,\n\t\t\t\tcap,\n\t\t\t\tpolicy,\n\t\t\t})\n\t\t}\n\t\tdefault:\n\t\t\tthrow Json.problem([...path, \"_tag\"], \"The node has no known tag\")\n\t}\n}\n\n/** Stops a plan past `policyNodes`, with the message of the nodes. */\nconst sized = (path: ReadonlyArray<PropertyKey>, plan: Plan): Plan => {\n\tif (plan.size > Limits.policyNodes) {\n\t\tthrow Json.problem(\n\t\t\tpath,\n\t\t\t`A policy tree has ${String(plan.size)} nodes. The most is ${String(Limits.policyNodes)}.`,\n\t\t)\n\t}\n\treturn plan\n}\n\n/** Makes the nodes of a plan, with the decoded rules. */\nconst build = (plan: Plan, rules: ReadonlyArray<Rule.Rule>): Node => {\n\ttry {\n\t\tswitch (plan._tag) {\n\t\t\tcase \"Rules\":\n\t\t\t\treturn new PolicyNodes.Rules({\n\t\t\t\t\talgorithm: plan.algorithm,\n\t\t\t\t\trules: Arr.map(plan.rules, (index) => rules[index]!),\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 new PolicyNodes[plan._tag](\n\t\t\t\t\tArr.map(plan.policies, (child) => build(child, rules)),\n\t\t\t\t)\n\t\t\tcase \"Ceiling\":\n\t\t\t\treturn new PolicyNodes.Ceiling({\n\t\t\t\t\tcap: build(plan.cap, rules),\n\t\t\t\t\tpolicy: build(plan.policy, rules),\n\t\t\t\t})\n\t\t}\n\t} catch (error) {\n\t\t// A node past a limit is a problem at its place.\n\t\tif (error instanceof Limits.LimitExceeded) {\n\t\t\tthrow Json.problem(Place.pathOf(plan.place), error.message)\n\t\t}\n\t\tthrow error\n\t}\n}\n\n/** Runs a step that may throw a problem, and puts the problem in the error. */\nconst attempt = <A>(step: () => A): Effect.Effect<A, Json.Problem> =>\n\tEffect.try({\n\t\ttry: step,\n\t\tcatch: (error) => {\n\t\t\tif (error instanceof Json.Problem) {\n\t\t\t\treturn error\n\t\t\t}\n\t\t\tthrow error\n\t\t},\n\t})\n\n/**\n * Decodes the JSON form of a policy tree for a domain. It reads the tree\n * first, then the `meta` of each rule as an Effect, then the rules, and it\n * makes the nodes last.\n */\nexport const decode = (\n\tinput: unknown,\n\tdomain: Domain.Any,\n): Effect.Effect<Node, Json.Problem> =>\n\tEffect.gen(function* () {\n\t\tconst budget: Json.Budget = { used: 0 }\n\t\t// The rules of this decode. The list belongs to this decode.\n\t\tconst inputs: Array<RuleInput> = []\n\t\tconst plan = yield* attempt(() =>\n\t\t\treadTree(input, [], undefined, 1, budget, inputs),\n\t\t)\n\t\tconst metas = yield* Effect.forEach(inputs, (rule) =>\n\t\t\tdecodeMeta(rule, domain, budget),\n\t\t)\n\t\tconst rules = yield* attempt(() =>\n\t\t\tArr.map(inputs, (rule, index) =>\n\t\t\t\tdecodeRule(rule, metas[index], budget, domain),\n\t\t\t),\n\t\t)\n\t\treturn yield* attempt(() => build(plan, rules))\n\t})\n"],"mappings":"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"}
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import { Rule } from "./rule.js";
|
|
2
|
+
import * as Schema from "effect/Schema";
|
|
2
3
|
import * as Data from "effect/Data";
|
|
3
|
-
|
|
4
|
+
/** The combining algorithms of a list of rules. The decoder reads this list too. */
|
|
5
|
+
export declare const Algorithm: Schema.Literals<readonly ["denyOverrides", "permitOverrides", "firstApplicable", "lastApplicable"]>;
|
|
6
|
+
export type Algorithm = typeof Algorithm.Type;
|
|
4
7
|
interface Facts {
|
|
5
8
|
readonly size: number;
|
|
6
9
|
readonly depth: number;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"policy-nodes.d.ts","names":["Rule.Rule"],"sources":["../../src/internal/policy-nodes.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"policy-nodes.d.ts","names":["Rule.Rule"],"sources":["../../src/internal/policy-nodes.ts"],"mappings":";;;;qBAmBa,WAAS,OAAA;YAOV,mBAAmB,UAAU;UAS/B;WACA;WACA;WACA;;;uBAmCK,aAAa,KAAK;;EAGhC,YAAY,eAAe,OAAO;;MAM9B;;MAKA;;MAKA;;;qBAaQ,cAAc;WACjB;WACQ,WAAW;WACX,OAAO,cAAcA;EACtC,YAAY;aACF,WAAW;aACX,OAAO,cAAcA;;;;qBAyCnB,YAAY;WACf;WACQ,UAAU,cAAc;EACzC,YAAY,UAAU,cAAc;;;qBAOxB,YAAY;WACf;WACQ,UAAU,cAAc;EACzC,YAAY,UAAU,cAAc;;;qBAOxB,gBAAgB;WACnB;WACQ,UAAU,cAAc;EACzC,YAAY,UAAU,cAAc;;;qBAOxB,gBAAgB;WACnB;WACQ,KAAK;WACL,QAAQ;EACzB,YAAY;aAAkB,KAAK;aAAe,QAAQ;;;;YAO/C,OAAO,QAAQ,MAAM,MAAM,UAAU"}
|
|
@@ -2,6 +2,7 @@ import { __exportAll } from "../_virtual/_rolldown/runtime.js";
|
|
|
2
2
|
import { exceeded, policyNodes } from "./limits.js";
|
|
3
3
|
import { isNode as isNode$1 } from "./nodes.js";
|
|
4
4
|
import { isRule } from "./rule.js";
|
|
5
|
+
import * as Schema from "effect/Schema";
|
|
5
6
|
import * as Data from "effect/Data";
|
|
6
7
|
import * as Arr from "effect/Array";
|
|
7
8
|
// src/internal/policy-nodes.ts
|
|
@@ -17,20 +18,28 @@ import * as Arr from "effect/Array";
|
|
|
17
18
|
* over any tree, such as a decision, needs no memo.
|
|
18
19
|
*/
|
|
19
20
|
var policy_nodes_exports = /* @__PURE__ */ __exportAll({
|
|
21
|
+
Algorithm: () => Algorithm,
|
|
20
22
|
All: () => All,
|
|
21
23
|
Any: () => Any,
|
|
22
24
|
Ceiling: () => Ceiling,
|
|
23
25
|
FirstOf: () => FirstOf,
|
|
24
26
|
Rules: () => Rules,
|
|
27
|
+
isAlgorithm: () => isAlgorithm,
|
|
25
28
|
isNode: () => isNode,
|
|
26
29
|
rulesOf: () => rulesOf
|
|
27
30
|
});
|
|
28
|
-
|
|
31
|
+
/** The combining algorithms of a list of rules. The decoder reads this list too. */
|
|
32
|
+
const Algorithm = Schema.Literals([
|
|
29
33
|
"denyOverrides",
|
|
30
34
|
"permitOverrides",
|
|
31
35
|
"firstApplicable",
|
|
32
36
|
"lastApplicable"
|
|
33
|
-
];
|
|
37
|
+
]);
|
|
38
|
+
/**
|
|
39
|
+
* Tests if a value is a combining algorithm. It reads the list of the Schema
|
|
40
|
+
* with `includes`, as `Nodes.isCompareOperator` does.
|
|
41
|
+
*/
|
|
42
|
+
const isAlgorithm = (u) => Algorithm.literals.includes(u);
|
|
34
43
|
/** Checks the facts of a node against the limits. */
|
|
35
44
|
const checked = (facts) => {
|
|
36
45
|
if (facts.size > 1e5) throw exceeded("A policy tree", "policyNodes", "nodes", facts.size, policyNodes);
|
|
@@ -71,7 +80,7 @@ const whenOf = (rule) => isNode$1(rule.when) ? rule.when : void 0;
|
|
|
71
80
|
var Rules = class extends Base {
|
|
72
81
|
_tag = "Rules";
|
|
73
82
|
constructor(props) {
|
|
74
|
-
if (!
|
|
83
|
+
if (!isAlgorithm(props.algorithm)) throw new Error(`"${String(props.algorithm)}" is not a combining algorithm`);
|
|
75
84
|
const rules = Object.freeze(Array.from(props.rules));
|
|
76
85
|
if (!Arr.every(rules, isRule)) throw new Error("A value in the list is not a rule");
|
|
77
86
|
const conditions = Arr.map(rules, whenOf);
|
|
@@ -141,6 +150,6 @@ const rulesOf = (node) => {
|
|
|
141
150
|
case "Ceiling": return [...rulesOf(node.cap), ...rulesOf(node.policy)];
|
|
142
151
|
}
|
|
143
152
|
};
|
|
144
|
-
export { All, Any, Ceiling, FirstOf, Rules, isNode, policy_nodes_exports, rulesOf };
|
|
153
|
+
export { Algorithm, All, Any, Ceiling, FirstOf, Rules, isAlgorithm, isNode, policy_nodes_exports, rulesOf };
|
|
145
154
|
|
|
146
155
|
//# sourceMappingURL=policy-nodes.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"policy-nodes.js","names":["Limits.exceeded","Limits.policyNodes","Nodes.isNode","Rule.isRule"],"sources":["../../src/internal/policy-nodes.ts"],"sourcesContent":["/**\n * The nodes of a policy tree. As with the nodes of a condition, each node is\n * a class, only its constructor makes one, and it works out three facts once:\n * - `size`: the number of nodes, with each rule and each node of its condition,\n * and a shared child counted for each place;\n * - `depth`: the number of levels of the tree;\n * - `opaque`: a rule of the tree has a predicate.\n *\n * The constructor stops a tree past `policyNodes` or `policyDepth`. So a walk\n * over any tree, such as a decision, needs no memo.\n */\nimport * as Arr from \"effect/Array\"\nimport * as Data from \"effect/Data\"\nimport * as Rule from \"./rule.ts\"\nimport * as Limits from \"./limits.ts\"\nimport * as Nodes from \"./nodes.ts\"\n\
|
|
1
|
+
{"version":3,"file":"policy-nodes.js","names":["Limits.exceeded","Limits.policyNodes","Nodes.isNode","Rule.isRule"],"sources":["../../src/internal/policy-nodes.ts"],"sourcesContent":["/**\n * The nodes of a policy tree. As with the nodes of a condition, each node is\n * a class, only its constructor makes one, and it works out three facts once:\n * - `size`: the number of nodes, with each rule and each node of its condition,\n * and a shared child counted for each place;\n * - `depth`: the number of levels of the tree;\n * - `opaque`: a rule of the tree has a predicate.\n *\n * The constructor stops a tree past `policyNodes` or `policyDepth`. So a walk\n * over any tree, such as a decision, needs no memo.\n */\nimport * as Arr from \"effect/Array\"\nimport * as Data from \"effect/Data\"\nimport * as Schema from \"effect/Schema\"\nimport * as Rule from \"./rule.ts\"\nimport * as Limits from \"./limits.ts\"\nimport * as Nodes from \"./nodes.ts\"\n\n/** The combining algorithms of a list of rules. The decoder reads this list too. */\nexport const Algorithm = Schema.Literals([\n\t\"denyOverrides\",\n\t\"permitOverrides\",\n\t\"firstApplicable\",\n\t\"lastApplicable\",\n])\n\nexport type Algorithm = typeof Algorithm.Type\n\n/**\n * Tests if a value is a combining algorithm. It reads the list of the Schema\n * with `includes`, as `Nodes.isCompareOperator` does.\n */\nexport const isAlgorithm = (u: unknown): u is Algorithm =>\n\t(Algorithm.literals as ReadonlyArray<unknown>).includes(u)\n\ninterface Facts {\n\treadonly size: number\n\treadonly depth: number\n\treadonly opaque: boolean\n}\n\n/** Checks the facts of a node against the limits. */\nconst checked = (facts: Facts): Facts => {\n\tif (facts.size > Limits.policyNodes) {\n\t\tthrow Limits.exceeded(\n\t\t\t\"A policy tree\",\n\t\t\t\"policyNodes\",\n\t\t\t\"nodes\",\n\t\t\tfacts.size,\n\t\t\tLimits.policyNodes,\n\t\t)\n\t}\n\tif (facts.depth > Limits.policyDepth) {\n\t\tthrow Limits.exceeded(\n\t\t\t\"A policy tree\",\n\t\t\t\"policyDepth\",\n\t\t\t\"levels\",\n\t\t\tfacts.depth,\n\t\t\tLimits.policyDepth,\n\t\t)\n\t}\n\treturn facts\n}\n\nconst factsOf = (children: ReadonlyArray<Node>): Facts =>\n\tchecked({\n\t\tsize: Arr.reduce(children, 1, (total, child) => total + child.size),\n\t\tdepth:\n\t\t\t1 + Arr.reduce(children, 0, (top, child) => Math.max(top, child.depth)),\n\t\topaque: Arr.some(children, (child) => child.opaque),\n\t})\n\n/** The base of every node. It keeps the facts. */\nabstract class Base extends Data.Class {\n\treadonly #facts: Facts\n\n\tconstructor(props: object, facts: Facts) {\n\t\tsuper(props as never)\n\t\tthis.#facts = facts\n\t}\n\n\t/** The number of nodes, with the rules and their conditions. */\n\tget size(): number {\n\t\treturn this.#facts.size\n\t}\n\n\t/** The number of levels of the tree. */\n\tget depth(): number {\n\t\treturn this.#facts.depth\n\t}\n\n\t/** A rule of the tree has a predicate. */\n\tget opaque(): boolean {\n\t\treturn this.#facts.opaque\n\t}\n}\n\n/**\n * The condition of a rule. A value that is not a node counts as one node\n * here: the binding of the rule gives the issue for it, before any walk.\n */\nconst whenOf = (rule: Rule.Rule): Nodes.Node | undefined =>\n\tNodes.isNode(rule.when) ? rule.when : undefined\n\n/** A list of rules with a combining algorithm. */\nexport class Rules extends Base {\n\treadonly _tag = \"Rules\"\n\tdeclare readonly algorithm: Algorithm\n\tdeclare readonly rules: ReadonlyArray<Rule.Rule>\n\tconstructor(props: {\n\t\treadonly algorithm: Algorithm\n\t\treadonly rules: ReadonlyArray<Rule.Rule>\n\t}) {\n\t\tif (!isAlgorithm(props.algorithm)) {\n\t\t\tthrow new Error(\n\t\t\t\t`\"${String(props.algorithm)}\" is not a combining algorithm`,\n\t\t\t)\n\t\t}\n\t\tconst rules = Object.freeze(Array.from(props.rules))\n\t\t// A value that is not a rule, such as a copy of a rule made with a\n\t\t// spread, may keep the type fields of another rule, and it does not\n\t\t// work in a generator. It is a mistake in the code, so it is a defect.\n\t\tif (!Arr.every(rules, Rule.isRule)) {\n\t\t\tthrow new Error(\"A value in the list is not a rule\")\n\t\t}\n\t\tconst conditions = Arr.map(rules, whenOf)\n\t\tsuper(\n\t\t\t{ algorithm: props.algorithm, rules },\n\t\t\tchecked({\n\t\t\t\tsize: Arr.reduce(\n\t\t\t\t\tconditions,\n\t\t\t\t\t1,\n\t\t\t\t\t(total, when) => total + 1 + (when?.size ?? 0),\n\t\t\t\t),\n\t\t\t\tdepth: 1,\n\t\t\t\topaque: Arr.some(conditions, (when) => when?.opaque ?? false),\n\t\t\t}),\n\t\t)\n\t}\n}\n\nconst checkChildren = (\n\tpolicies: ReadonlyArray<unknown>,\n): ReadonlyArray<Node> => {\n\tconst children = Array.from(policies)\n\tif (!Arr.every(children, isNode)) {\n\t\tthrow new Error(\"A child of a policy tree is not a node of a policy\")\n\t}\n\treturn Object.freeze(children)\n}\n\n/** Every policy must allow, and one deny is enough to deny. */\nexport class All extends Base {\n\treadonly _tag = \"All\"\n\tdeclare readonly policies: ReadonlyArray<Node>\n\tconstructor(policies: ReadonlyArray<Node>) {\n\t\tconst children = checkChildren(policies)\n\t\tsuper({ policies: children }, factsOf(children))\n\t}\n}\n\n/** One allow is enough. Without an allow, one deny gives a deny. */\nexport class Any extends Base {\n\treadonly _tag = \"Any\"\n\tdeclare readonly policies: ReadonlyArray<Node>\n\tconstructor(policies: ReadonlyArray<Node>) {\n\t\tconst children = checkChildren(policies)\n\t\tsuper({ policies: children }, factsOf(children))\n\t}\n}\n\n/** The first policy that allows or denies gives the answer. */\nexport class FirstOf extends Base {\n\treadonly _tag = \"FirstOf\"\n\tdeclare readonly policies: ReadonlyArray<Node>\n\tconstructor(policies: ReadonlyArray<Node>) {\n\t\tconst children = checkChildren(policies)\n\t\tsuper({ policies: children }, factsOf(children))\n\t}\n}\n\n/** A deny from `cap` always wins. An allow from `cap` does nothing. */\nexport class Ceiling extends Base {\n\treadonly _tag = \"Ceiling\"\n\tdeclare readonly cap: Node\n\tdeclare readonly policy: Node\n\tconstructor(props: { readonly cap: Node; readonly policy: Node }) {\n\t\tconst [cap, policy] = checkChildren([props.cap, props.policy])\n\t\tsuper({ cap, policy }, factsOf([cap!, policy!]))\n\t}\n}\n\n/** A node of a policy tree. */\nexport type Node = Rules | All | Any | FirstOf | Ceiling\n\n/** Tests if a value is a node of a policy tree. */\nexport const isNode = (u: unknown): u is Node => u instanceof Base\n\n/**\n * The rules of a tree, in order, once for each place. A walk that also goes\n * into the condition of each rule takes the rules from this list first, so it\n * does not go down the levels of the tree and of a condition at once.\n */\nexport const rulesOf = (node: Node): ReadonlyArray<Rule.Rule> => {\n\tswitch (node._tag) {\n\t\tcase \"Rules\":\n\t\t\treturn node.rules\n\t\tcase \"All\":\n\t\tcase \"Any\":\n\t\tcase \"FirstOf\":\n\t\t\treturn Arr.flatMap(node.policies, rulesOf)\n\t\tcase \"Ceiling\":\n\t\t\treturn [...rulesOf(node.cap), ...rulesOf(node.policy)]\n\t}\n}\n"],"mappings":"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"}
|
|
@@ -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":";;;;;;;;;;;;;;;;;;;;;;AA2CA,IAAa,OAAb,cAA6D,KAAK,MAEhE;CAUD,QAAiB;CAEjB,EAAE,OAAO,YAA8D;EACtE,MAAM;EACN,OAAO;CACR;;CAGA,OAAO,GAAG,GAAuB;EAChC,OAAO,OAAO,MAAM,YAAY,MAAM,QAAQ,SAAS;CACxD;AACD;;AAGA,MAAa,QAAQ,SAGpB,IAAI,KAAK;CACR,MAAM,KAAK;CACX,SAAS,CAAC,GAAG,KAAK,OAAO;CACzB,WAAW,CAAC,GAAG,KAAK,SAAS;CAC7B,GAAI,KAAK,SAAS,KAAA,IAAY,CAAC,IAAI,EAAE,MAAM,KAAK,KAAK;CACrD,GAAI,KAAK,WAAW,KAAA,IAAY,CAAC,IAAI,EAAE,QAAQ,CAAC,GAAG,KAAK,MAAM,EAAE;CAChE,GAAI,KAAK,WAAW,KAAA,IAAY,CAAC,IAAI,EAAE,QAAQ,KAAK,OAAO;CAC3D,GAAI,KAAK,OAAO,KAAA,IAAY,CAAC,IAAI,EAAE,IAAI,KAAK,GAAG;CAC/C,GAAI,KAAK,SAAS,KAAA,IAAY,CAAC,IAAI,EAAE,MAAM,KAAK,KAAK;CACrD,GAAI,KAAK,WAAW,KAAA,IAAY,CAAC,IAAI,EAAE,QAAQ,KAAK,OAAO;AAC5D,CAAU;;;;;;AAOX,MAAa,aAAa,SAAsB;CAC/C,IAAI,KAAK,SAAS,KAAA,KAAaA,OAAa,KAAK,IAAI,KAAK,KAAK,KAAK,QACnE,MAAM,IAAI,MACT,qFACD;CAED,OAAO,KAAK,IAAI;AACjB;;AAGA,MAAa,UAAU,MAA0B,KAAK,GAAG,CAAC;AAE1D,MAAM,WAAc,UACnB,MAAM,QAAQ,KAAK,IAAI,QAAQ,CAAC,KAAU;AAE3C,MAAM,YAAY,WACjBC,WAAwB,MAAM,IAC3BC,WAAwB,MAAM,CAAC,CAAC,OAC/B;AAWL,MAAM,eACJ,KAAuB,YAEvB,QACA,UACA,YAEA,KAAK;CACJ,MAAM;CACN,SAAS,QAAQ,MAAM;CACvB,WAAW,IAAI,IAAI,QAAQ,QAAQ,GAAG,QAAQ;CAC9C,GAAI,SAAS,SAAS,KAAA,IACnB,CAAC,IACD,EAAE,MAAM,QAAQ,KAAKC,UAAkB,CAAU,EAAE;CACtD,GAAI,SAAS,WAAW,KAAA,IAAY,CAAC,IAAI,EAAE,QAAQ,QAAQ,OAAO;CAClE,GAAI,SAAS,WAAW,KAAA,IAAY,CAAC,IAAI,EAAE,QAAQ,QAAQ,OAAO;CAClE,GAAI,SAAS,OAAO,KAAA,IAAY,CAAC,IAAI,EAAE,IAAI,QAAQ,GAAG;CACtD,GAAI,SAAS,SAAS,KAAA,IAAY,CAAC,IAAI,EAAE,MAAM,QAAQ,KAAK;CAC5D;AACD,CAAC;;AAGH,MAAa,WAAW,YAAwB;CAC/C,OAAO,YAAY,SAAS,MAAM;CAClC,MAAM,YAAY,QAAQ,MAAM;AACjC;;;;;;AAOA,MAAa,gBAAgB,SAAiB,UAC7CC,QAAiBC,WAAoB,OAAO,GAAG,MAAM,MAAM,GAAG,CAAC;;;;;AAMhE,MAAa,iBAAiB,SAAiB,UAC9CC,aAAsBD,WAAoB,OAAO,GAAG,MAAM,MAAM,GAAG,CAAC;;;;;;;;AASrE,MAAa,UACZ,YACoC;CAIpC,MAAM,uBAAO,IAAI,IAAqB;CACtC,QAAQ,YAAY;EACnB,MAAM,QAAQ,KAAK,IAAI,OAAO;EAC9B,IAAI,UAAU,KAAA,GACb,OAAO;EAER,MAAM,QAAQE,YAAqBF,WAAoB,OAAO,GAAG,OAAO;EACxE,KAAK,IAAI,SAAS,KAAK;EACvB,OAAO;CACR;AACD;;;;;;;;;;;;;;AAeA,MAAa,iBACZ,MACA,WAQa;CACb,IAAI,CAAC,KAAK,QAAQ,SAAS,GAAG,KAAK,CAAC,KAAK,QAAQ,SAAS,OAAO,MAAM,GACtE,OAAO;CAER,IAAI,CAAC,KAAK,UAAU,SAAS,GAAG,KAAK,CAAC,KAAK,UAAU,SAAS,OAAO,IAAI,GACxE,OAAO;CAER,IAAI,KAAK,WAAW,KAAA,GACnB,OAAO;CAIR,IAAI,KAAK,OAAO,WAAW,GAC1B,OAAO;CAER,IAAI,OAAO,UAAU,KAAA,GAAW;EAC/B,MAAM,aAAa,OAAO;EAC1B,OACC,KAAK,SAAS,YACb,eAAe,KAAA,KACf,IAAI,KAAK,KAAK,QAAQ,OAAO,QAAQ,OAAO,WAAW,OAAO,CAAC;CAElE;CACA,MAAM,QAAQ,OAAO;CAIrB,MAAM,OACL,KAAK,SAAS,UACd,OAAO,eAAe,KAAA,MACrBG,OAAe,OAAO,WAAW,SAAS,KAAK,CAAC,EAAE,QAAQ;CAC5D,OAAO,KAAK,OAAO,MACjB,YACA,aAAa,SAAS,KAAK,KAAM,QAAQ,cAAc,SAAS,KAAK,CACvE;AACD"}
|
|
@@ -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"}
|