@nmnmcc/ability 2.1.3 → 2.1.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/Condition.d.ts +1 -1
- package/dist/HttpApi.js +1 -1
- package/dist/internal/authorizer.d.ts.map +1 -1
- package/dist/internal/authorizer.js +13 -0
- package/dist/internal/authorizer.js.map +1 -1
- package/dist/internal/bind.js +19 -15
- package/dist/internal/bind.js.map +1 -1
- package/dist/internal/catalog.d.ts +0 -31
- package/dist/internal/catalog.d.ts.map +1 -1
- package/dist/internal/catalog.js +26 -16
- package/dist/internal/catalog.js.map +1 -1
- package/dist/internal/decide.js +1 -1
- package/dist/internal/decide.js.map +1 -1
- package/dist/internal/domain.js +8 -5
- package/dist/internal/domain.js.map +1 -1
- package/dist/internal/json.js +42 -34
- package/dist/internal/json.js.map +1 -1
- package/dist/internal/patterns.js +1 -1
- package/dist/internal/policy-json.js +2 -2
- package/dist/internal/policy-json.js.map +1 -1
- package/dist/internal/policy.js +1 -1
- package/dist/internal/search.js +1 -1
- package/package.json +1 -1
- package/src/internal/authorizer.ts +33 -0
- package/src/internal/bind.ts +24 -7
- package/src/internal/catalog.ts +45 -33
- package/src/internal/decide.ts +5 -1
- package/src/internal/domain.ts +6 -11
- package/src/internal/json.ts +21 -8
- package/src/internal/policy-json.ts +24 -8
package/dist/Condition.d.ts
CHANGED
|
@@ -6,9 +6,9 @@ import * as Context from "effect/Context";
|
|
|
6
6
|
import * as Effect from "effect/Effect";
|
|
7
7
|
import * as HashMap from "effect/HashMap";
|
|
8
8
|
import * as HashSet from "effect/HashSet";
|
|
9
|
+
import * as Option from "effect/Option";
|
|
9
10
|
import * as Result from "effect/Result";
|
|
10
11
|
import * as Schema from "effect/Schema";
|
|
11
|
-
import * as Option from "effect/Option";
|
|
12
12
|
import * as Cause from "effect/Cause";
|
|
13
13
|
import * as Exit from "effect/Exit";
|
|
14
14
|
import * as Duration from "effect/Duration";
|
package/dist/HttpApi.js
CHANGED
|
@@ -3,9 +3,9 @@ import { Forbidden, Pending, PolicyUnavailable } from "./Authorizer.js";
|
|
|
3
3
|
import * as Context from "effect/Context";
|
|
4
4
|
import * as Effect from "effect/Effect";
|
|
5
5
|
import * as Layer from "effect/Layer";
|
|
6
|
+
import * as Option from "effect/Option";
|
|
6
7
|
import * as Schema from "effect/Schema";
|
|
7
8
|
import * as SchemaTransformation from "effect/SchemaTransformation";
|
|
8
|
-
import * as Option from "effect/Option";
|
|
9
9
|
import { HttpRouter } from "effect/http";
|
|
10
10
|
import { HttpApiSchema } from "effect/http-api";
|
|
11
11
|
// src/HttpApi.ts
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"authorizer.d.ts","names":[],"sources":["../../src/internal/authorizer.ts"],"mappings":";;;;;;;;;;;;;;;;
|
|
1
|
+
{"version":3,"file":"authorizer.d.ts","names":[],"sources":["../../src/internal/authorizer.ts"],"mappings":";;;;;;;;;;;;;;;;qBAqCa,kBAAkB;;;;;qBAUlB,0BAA0B;;;;;;qBAK1B,gBAAgB"}
|
|
@@ -15,6 +15,7 @@ import * as Hash from "effect/Hash";
|
|
|
15
15
|
import * as HashMap from "effect/HashMap";
|
|
16
16
|
import * as HashSet from "effect/HashSet";
|
|
17
17
|
import * as Layer from "effect/Layer";
|
|
18
|
+
import * as Option from "effect/Option";
|
|
18
19
|
import * as Result from "effect/Result";
|
|
19
20
|
import * as Schema from "effect/Schema";
|
|
20
21
|
// src/internal/authorizer.ts
|
|
@@ -96,6 +97,12 @@ var PrincipalKey = class PrincipalKey {
|
|
|
96
97
|
* part does not make the text very long. The numbers are in a native `Map`,
|
|
97
98
|
* because the walk must find an object by the object itself. The map belongs
|
|
98
99
|
* to one walk.
|
|
100
|
+
*
|
|
101
|
+
* A value that keeps its data inside, with no own fields, as a `Date`, a
|
|
102
|
+
* `URL` or a `RegExp` with the place of its next match, is in the text with
|
|
103
|
+
* its data, and so is an `Option`.
|
|
104
|
+
* For another such value, the walk can not see the data, so it throws, and
|
|
105
|
+
* the principal gets no key: it is not kept.
|
|
99
106
|
*/
|
|
100
107
|
const orderOf = (root) => {
|
|
101
108
|
const ids = /* @__PURE__ */ new Map();
|
|
@@ -105,9 +112,15 @@ const orderOf = (root) => {
|
|
|
105
112
|
if (known !== void 0) return R.succeed(`#${String(known)}`);
|
|
106
113
|
ids.set(value, ids.size);
|
|
107
114
|
const items = (list) => R.forEach(list, (item) => child(item, depth + 1));
|
|
115
|
+
if (value instanceof Date) return R.succeed(`date:${String(value.getTime())}`);
|
|
116
|
+
if (value instanceof URL) return R.succeed(`url:${JSON.stringify(value.href)}`);
|
|
117
|
+
if (value instanceof RegExp) return R.succeed(`regexp:${JSON.stringify(String(value))}:${String(value.lastIndex)}`);
|
|
118
|
+
if (Option.isOption(value)) return Option.isSome(value) ? R.map(child(value.value, depth + 1), (text) => `some(${text})`) : R.succeed("none");
|
|
108
119
|
if (Array.isArray(value)) return R.map(items(value), (texts) => `[${texts.join(",")}]`);
|
|
109
120
|
if (value instanceof Map || value instanceof Set || HashMap.isHashMap(value) || HashSet.isHashSet(value) || Chunk.isChunk(value)) return R.map(items(Array.from(value)), (texts) => `<${texts.join(",")}>`);
|
|
110
121
|
const names = Object.keys(value);
|
|
122
|
+
const prototype = Object.getPrototypeOf(value);
|
|
123
|
+
if (names.length === 0 && prototype !== Object.prototype && prototype !== null) throw new Error("The data of a part of the principal is not in fields");
|
|
111
124
|
return R.map(items(Arr.map(names, (name) => value[name])), (texts) => `{${Arr.map(names, (name, index) => `${JSON.stringify(name)}:${texts[index]}`).join(",")}}`);
|
|
112
125
|
})(root);
|
|
113
126
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"authorizer.js","names":["Trampoline.adaptive","Policies.provideContext","Decide.decideAny","ResourceData.kindOf","DomainData.descriptorOf","Decide.fitsKind","Analysis.decideKind","Decision.reason","Analysis.coverageAny","permittedFields","Decide.permittedFields"],"sources":["../../src/internal/authorizer.ts"],"sourcesContent":["/**\n * The values of authorizers: the errors, the service class and the layer.\n * The types are in `Authorizer.ts`.\n */\n// The service erases the error type and the service type of predicates,\n// principals and deciders to `unknown`. The public types keep them, so the\n// language service check for `unknown` in an Effect is off here.\n// @effect-diagnostics anyUnknownInErrorContext:off\nimport * as Arr from \"effect/Array\"\nimport * as Cache from \"effect/Cache\"\nimport * as Chunk from \"effect/Chunk\"\nimport * as Context from \"effect/Context\"\nimport type * as Duration from \"effect/Duration\"\nimport * as Effect from \"effect/Effect\"\nimport * as Equal from \"effect/Equal\"\nimport * as Hash from \"effect/Hash\"\nimport * as HashMap from \"effect/HashMap\"\nimport * as HashSet from \"effect/HashSet\"\nimport * as Layer from \"effect/Layer\"\nimport * as Result from \"effect/Result\"\nimport * as Schema from \"effect/Schema\"\nimport type * as Authorizer from \"../Authorizer.ts\"\nimport type * as Condition from \"../Condition.ts\"\nimport * as Decision from \"../Decision.ts\"\nimport type * as Domain from \"../Domain.ts\"\nimport type * as Policy from \"../Policy.ts\"\nimport * as Analysis from \"./analysis.ts\"\nimport * as Decide from \"./decide.ts\"\nimport * as DomainData from \"./domain.ts\"\nimport * as Policies from \"./policy.ts\"\nimport * as ResourceData from \"./resource.ts\"\nimport * as Trampoline from \"./trampoline.ts\"\n\ntype AnyServiceClass = Authorizer.AnyServiceClass\n\n/** The error when a policy says no. */\nexport class Forbidden extends Schema.TaggedError<Forbidden>(\n\t\"@nmnmcc/ability/Authorizer/Forbidden\",\n)(\"Forbidden\", {\n\taction: Schema.String,\n\tkind: Schema.String,\n\tfield: Schema.optionalKey(Schema.String),\n\treason: Schema.optionalKey(Schema.String),\n}) {}\n\n/** The error when the policy of the principal can not be loaded. */\nexport class PolicyUnavailable extends Schema.TaggedError<PolicyUnavailable>(\n\t\"@nmnmcc/ability/Authorizer/PolicyUnavailable\",\n)(\"PolicyUnavailable\", { cause: Schema.Defect() }) {}\n\n/** The error when a decider must wait for an answer. */\nexport class Pending extends Schema.TaggedError<Pending>(\n\t\"@nmnmcc/ability/Authorizer/Pending\",\n)(\"Pending\", {\n\tticket: Schema.String,\n\treason: Schema.optionalKey(Schema.String),\n}) {}\n\n/** Makes an authorizer service class for a domain. */\nexport const Service =\n\t() =>\n\t(\n\t\tid: string,\n\t\toptions: {\n\t\t\treadonly domain: Domain.Any\n\t\t\treadonly principal: Effect.Effect<any, any, any>\n\t\t},\n\t\t// The class has no name, and its type uses types of Effect that a\n\t\t// declaration file can not name. So this function gives a `Context.Key`.\n\t\t// The public module gives the full type.\n\t): Context.Key<any, any> => {\n\t\tconst Base = Context.Service<any, any>()(id)\n\t\treturn class extends Base {\n\t\t\tstatic readonly domain = options.domain\n\t\t\tstatic readonly principal = options.principal\n\t\t\tstatic readonly decide = (target: AnyTarget) =>\n\t\t\t\tthis.use((service: any) => service.decide(target))\n\t\t\tstatic readonly check = (target: AnyTarget) =>\n\t\t\t\tthis.use((service: any) => service.check(target))\n\t\t\tstatic readonly guard = (target: AnyTarget, options: unknown) =>\n\t\t\t\tthis.use((service: any) => service.guard(target, options))\n\t\t\tstatic readonly filter = (target: AnyTarget) =>\n\t\t\t\tthis.use((service: any) => service.filter(target))\n\t\t\tstatic readonly coverage = (target: AnyTarget) =>\n\t\t\t\tthis.use((service: any) => service.coverage(target))\n\t\t\tstatic readonly permittedFields = (target: AnyTarget) =>\n\t\t\t\tthis.use((service: any) => service.permittedFields(target))\n\t\t}\n\t}\n\ntype AnyTarget = {\n\treadonly action: string\n\treadonly resource?: object\n\treadonly kind?: string\n\treadonly field?: string\n\treadonly within?: unknown\n}\n\n/**\n * A cache key for a principal. Two keys are equal when they have the same\n * text, so two principals with the same key share one policy. A key from the\n * JSON form also needs equal decoded principals: a Schema may make data when\n * it decodes, as `expired` from a time, so one text may give two principals.\n * The key also keeps the principal, so the cache can load its policy. A plain\n * `Data` value can not do all this, so the key gives its own equality with\n * `Equal.Equal`.\n */\nclass PrincipalKey implements Equal.Equal {\n\treadonly principal: object\n\treadonly text: string\n\t/**\n\t * The order of the parts of the decoded principal, from {@link orderOf},\n\t * when the key is the JSON form. The decoded principals must be equal\n\t * too, with their parts in the same order. `undefined` for a key from\n\t * `cache.key`.\n\t */\n\treadonly order: string | undefined\n\tconstructor(principal: object, text: string, order: string | undefined) {\n\t\tthis.principal = principal\n\t\tthis.text = text\n\t\tthis.order = order\n\t}\n\t[Equal.symbol](that: unknown): boolean {\n\t\treturn (\n\t\t\tthat instanceof PrincipalKey &&\n\t\t\tthat.text === this.text &&\n\t\t\tthat.order === this.order &&\n\t\t\t(this.order === undefined || Equal.equals(that.principal, this.principal))\n\t\t)\n\t}\n\t[Hash.symbol](): number {\n\t\treturn Hash.string(this.text)\n\t}\n}\n\n/**\n * The text of the order of the parts of a value: the names of the fields of\n * each object, and the items of each list, set and map, with each scalar, in\n * the order that a loop sees them. `Equal` does not see the order of the\n * fields of an object or of the items of a map, but a loader may read it, as\n * `Object.values` does. So two keys need the same order too. A scalar is in\n * the text with its type, so the items of a set or a map show their order,\n * and `-0` is not `0`: `Equal` takes them as one, but a loader may not.\n *\n * Each object gets a number at its first visit, and a later visit of it, as\n * of a part that the value shares or of a loop back to a part above, writes\n * `#` and the number. So the walk ends for a value with a loop, and a shared\n * part does not make the text very long. The numbers are in a native `Map`,\n * because the walk must find an object by the object itself. The map belongs\n * to one walk.\n */\nconst orderOf = (root: unknown): string => {\n\tconst ids = new Map<object, number>()\n\treturn Trampoline.adaptive<unknown, string>((R, child) => (value, depth) => {\n\t\tif (typeof value !== \"object\" || value === null) {\n\t\t\treturn R.succeed(\n\t\t\t\ttypeof value === \"string\"\n\t\t\t\t\t? JSON.stringify(value)\n\t\t\t\t\t: Object.is(value, -0)\n\t\t\t\t\t\t? \"number:-0\"\n\t\t\t\t\t\t: `${typeof value}:${String(value)}`,\n\t\t\t)\n\t\t}\n\t\tconst known = ids.get(value)\n\t\tif (known !== undefined) {\n\t\t\treturn R.succeed(`#${String(known)}`)\n\t\t}\n\t\tids.set(value, ids.size)\n\t\tconst items = (list: ReadonlyArray<unknown>) =>\n\t\t\tR.forEach(list, (item) => child(item, depth + 1))\n\t\tif (Array.isArray(value)) {\n\t\t\treturn R.map(items(value), (texts) => `[${texts.join(\",\")}]`)\n\t\t}\n\t\tif (\n\t\t\tvalue instanceof Map ||\n\t\t\tvalue instanceof Set ||\n\t\t\tHashMap.isHashMap(value) ||\n\t\t\tHashSet.isHashSet(value) ||\n\t\t\tChunk.isChunk(value)\n\t\t) {\n\t\t\treturn R.map(\n\t\t\t\titems(Array.from(value as Iterable<unknown>)),\n\t\t\t\t(texts) => `<${texts.join(\",\")}>`,\n\t\t\t)\n\t\t}\n\t\tconst names = Object.keys(value)\n\t\treturn R.map(\n\t\t\titems(Arr.map(names, (name) => (value as Record<string, unknown>)[name])),\n\t\t\t(texts) =>\n\t\t\t\t`{${Arr.map(names, (name, index) => `${JSON.stringify(name)}:${texts[index]!}`).join(\",\")}}`,\n\t\t)\n\t})(root)\n}\n\n/**\n * Gives the JSON codec of a Schema, or `undefined` when the Schema has no JSON\n * form, as a struct with a symbol key has none.\n */\nconst jsonCodecOf = (\n\tschema: Schema.Top,\n): Schema.Codec<unknown, unknown> | undefined => {\n\ttry {\n\t\treturn Schema.toCodecJson(schema) as unknown as Schema.Codec<\n\t\t\tunknown,\n\t\t\tunknown\n\t\t>\n\t} catch {\n\t\treturn undefined\n\t}\n}\n\n/**\n * Makes the cache key of a principal from its JSON form. The key keeps the\n * principal that the Schema decodes from that form, so the loader sees only\n * the data that the key shows. A principal gets no key when it does not\n * encode, or when the decoded value is not `Equal` to it. Two classes, or a\n * string and a number, may have one JSON form, as in a union, and `Equal`\n * tells them apart. A Schema may make data when it decodes, as the order of\n * the fields of a record, so the key keeps that order too. So two\n * principals with one key are equal, and the cache can not give one the\n * policy of the other.\n */\nconst jsonKey = (\n\tschema: Schema.Top,\n): ((principal: object) => PrincipalKey | undefined) => {\n\tconst codec = jsonCodecOf(schema)\n\t// A principal with no JSON form is never kept.\n\tif (codec === undefined) {\n\t\treturn () => undefined\n\t}\n\tconst encode = Schema.encodeUnknownResult(codec)\n\tconst decode = Schema.decodeUnknownResult(codec)\n\tconst keyOf = (principal: object): PrincipalKey | undefined =>\n\t\tResult.match(encode(principal), {\n\t\t\tonFailure: () => undefined,\n\t\t\tonSuccess: (json) => {\n\t\t\t\t// The key is the text, so the check decodes the text. The encoded\n\t\t\t\t// value may hold more than its text shows, as an object of a\n\t\t\t\t// `Schema.Any` field does: `JSON.stringify` leaves out a map.\n\t\t\t\tconst text = JSON.stringify(json)\n\t\t\t\treturn Result.match(decode(JSON.parse(text)), {\n\t\t\t\t\tonFailure: () => undefined,\n\t\t\t\t\tonSuccess: (decoded) =>\n\t\t\t\t\t\tEqual.equals(decoded, principal)\n\t\t\t\t\t\t\t? new PrincipalKey(decoded as object, text, orderOf(decoded))\n\t\t\t\t\t\t\t: undefined,\n\t\t\t\t})\n\t\t\t},\n\t\t})\n\treturn (principal) => {\n\t\t// A very deep principal may fill the stack of the encoder or of\n\t\t// `Equal`. Such a principal is not kept.\n\t\ttry {\n\t\t\treturn keyOf(principal)\n\t\t} catch {\n\t\t\treturn undefined\n\t\t}\n\t}\n}\n\nconst forbidden = (\n\ttarget: AnyTarget,\n\tkind: string,\n\treason: string | undefined,\n): Forbidden =>\n\tForbidden.make({\n\t\taction: target.action,\n\t\tkind,\n\t\t...(target.field === undefined ? {} : { field: target.field }),\n\t\t...(reason === undefined ? {} : { reason }),\n\t})\n\n/** Makes the layer of an authorizer. */\nexport const makeLayer = (\n\tservice: AnyServiceClass,\n\toptions: {\n\t\treadonly policy: (\n\t\t\tprincipal: object,\n\t\t) => Effect.Effect<Policy.Any, unknown, unknown>\n\t\treadonly decider?: (\n\t\t\trequest: object,\n\t\t) => Effect.Effect<Decision.Decision, unknown, unknown>\n\t\treadonly cache?:\n\t\t\t| {\n\t\t\t\t\treadonly capacity?: number\n\t\t\t\t\treadonly timeToLive?: Duration.Input\n\t\t\t\t\treadonly key?: (principal: object) => string | undefined\n\t\t\t }\n\t\t\t| false\n\t},\n) => {\n\tconst domain: Domain.Any = service.domain\n\t// The JSON form of a principal, and the copy that the Schema decodes from\n\t// it. The loader always gets the copy, with a cache or not, so a cache can\n\t// not change an answer: a form may lose data that `Equal` does not see,\n\t// as `-0` becomes `0`. A principal with no such form gives `undefined`.\n\tconst canonicalOf = jsonKey(domain.principal)\n\tconst custom = options.cache === false ? undefined : options.cache?.key\n\t// It gives `undefined` when the principal has no key. Such a principal does\n\t// not use the cache.\n\tconst keyOf = (\n\t\tprincipal: object,\n\t\tcanonical: PrincipalKey | undefined,\n\t): PrincipalKey | undefined => {\n\t\tif (custom === undefined) {\n\t\t\treturn canonical\n\t\t}\n\t\tconst text = custom(principal)\n\t\treturn text === undefined\n\t\t\t? undefined\n\t\t\t: new PrincipalKey(canonical?.principal ?? principal, text, undefined)\n\t}\n\treturn Layer.effect(\n\t\tservice,\n\t\tEffect.gen(function* () {\n\t\t\t// The services of the policy loader and of the predicates come from the\n\t\t\t// layer, so a check does not need them.\n\t\t\tconst services = yield* Effect.context<never>()\n\n\t\t\t// The predicates of the policy get the services of the layer, so the\n\t\t\t// policy and its residual conditions need no services.\n\t\t\t// A policy of another domain may have the same types, but its resources\n\t\t\t// may have other paths. So it fails closed, as a policy that can not be\n\t\t\t// loaded.\n\t\t\tconst load = (principal: object) =>\n\t\t\t\toptions.policy(principal).pipe(\n\t\t\t\t\tEffect.provideContext(services),\n\t\t\t\t\tEffect.filterOrFail(\n\t\t\t\t\t\t(value) => value.domain === domain,\n\t\t\t\t\t\t() =>\n\t\t\t\t\t\t\tnew globalThis.Error(\n\t\t\t\t\t\t\t\t\"The loaded policy is not a policy of this domain\",\n\t\t\t\t\t\t\t),\n\t\t\t\t\t),\n\t\t\t\t\tEffect.mapError((cause) => PolicyUnavailable.make({ cause })),\n\t\t\t\t\tEffect.map((value) =>\n\t\t\t\t\t\tPolicies.provideContext(value, services as never),\n\t\t\t\t\t),\n\t\t\t\t)\n\n\t\t\tconst cache =\n\t\t\t\toptions.cache === false\n\t\t\t\t\t? undefined\n\t\t\t\t\t: yield* Cache.make({\n\t\t\t\t\t\t\tcapacity: options.cache?.capacity ?? 1000,\n\t\t\t\t\t\t\ttimeToLive: options.cache?.timeToLive ?? \"30 seconds\",\n\t\t\t\t\t\t\tlookup: (key: PrincipalKey) => load(key.principal),\n\t\t\t\t\t\t})\n\n\t\t\tconst policyFor = (principal: object) => {\n\t\t\t\tconst canonical = canonicalOf(principal)\n\t\t\t\tconst key =\n\t\t\t\t\tcache === undefined ? undefined : keyOf(principal, canonical)\n\t\t\t\tif (cache === undefined || key === undefined) {\n\t\t\t\t\treturn load(canonical?.principal ?? principal)\n\t\t\t\t}\n\t\t\t\t// A failed lookup is kept in the cache. Remove it, so the next check\n\t\t\t\t// tries again.\n\t\t\t\treturn Cache.get(cache, key).pipe(\n\t\t\t\t\tEffect.tapError(() => Cache.invalidate(cache, key)),\n\t\t\t\t)\n\t\t\t}\n\n\t\t\tconst withServices = <A, E>(\n\t\t\t\teffect: Effect.Effect<A, E, unknown>,\n\t\t\t): Effect.Effect<A, E, unknown> => Effect.provideContext(effect, services)\n\n\t\t\tconst current = Effect.gen(function* () {\n\t\t\t\tconst principal = yield* service.principal\n\t\t\t\tconst value = yield* policyFor(principal)\n\t\t\t\treturn { principal, policy: value }\n\t\t\t})\n\n\t\t\t// The principal and its policy, loaded one time. A check that asks\n\t\t\t// the policy two times, such as `guard`, uses one snapshot for both,\n\t\t\t// so the two answers come from one principal.\n\t\t\ttype Snapshot = Effect.Success<typeof current>\n\n\t\t\tconst decideWith = Effect.fnUntraced(function* (\n\t\t\t\t{ principal, policy: value }: Snapshot,\n\t\t\t\ttarget: AnyTarget,\n\t\t\t) {\n\t\t\t\tconst decision: Decision.Decision = yield* withServices(\n\t\t\t\t\tDecide.decideAny(value, { ...target, principal } as never),\n\t\t\t\t)\n\t\t\t\treturn decision\n\t\t\t})\n\n\t\t\tconst decide = (target: AnyTarget) =>\n\t\t\t\tEffect.flatMap(current, (snapshot) => decideWith(snapshot, target))\n\n\t\t\tconst checkWith = Effect.fnUntraced(function* (\n\t\t\t\t{ principal, policy: value }: Snapshot,\n\t\t\t\ttarget: AnyTarget,\n\t\t\t) {\n\t\t\t\tconst kind =\n\t\t\t\t\ttarget.resource === undefined\n\t\t\t\t\t\t? (target.kind ?? \"\")\n\t\t\t\t\t\t: (ResourceData.kindOf(target.resource) ?? \"\")\n\t\t\t\t// A request that does not fit its kind gets a \"no\" at once. The\n\t\t\t\t// decider must not say yes to an action that the kind does not have.\n\t\t\t\tconst descriptor = DomainData.descriptorOf(domain, kind)\n\t\t\t\tif (\n\t\t\t\t\tdescriptor === undefined ||\n\t\t\t\t\t!Decide.fitsKind(descriptor, target, domain.limits.fieldPathParts)\n\t\t\t\t) {\n\t\t\t\t\treturn yield* forbidden(target, kind, undefined)\n\t\t\t\t}\n\t\t\t\tconst fromPolicy: Decision.Decision =\n\t\t\t\t\ttarget.resource === undefined\n\t\t\t\t\t\t? Analysis.decideKind(value, { ...target, principal } as never)\n\t\t\t\t\t\t: yield* withServices(\n\t\t\t\t\t\t\t\tDecide.decideAny(value, { ...target, principal } as never),\n\t\t\t\t\t\t\t)\n\t\t\t\t// When no rule gives an answer, the decider gives it.\n\t\t\t\tconst decision: Decision.Decision =\n\t\t\t\t\tfromPolicy._tag === \"NotApplicable\" && options.decider !== undefined\n\t\t\t\t\t\t? yield* options.decider({ ...target, principal })\n\t\t\t\t\t\t: fromPolicy\n\t\t\t\tif (decision._tag !== \"Allow\") {\n\t\t\t\t\treturn yield* forbidden(target, kind, Decision.reason(decision))\n\t\t\t\t}\n\t\t\t})\n\n\t\t\tconst check = (target: AnyTarget) =>\n\t\t\t\tEffect.flatMap(current, (snapshot) => checkWith(snapshot, target))\n\n\t\t\tconst guard = Effect.fnUntraced(function* (\n\t\t\t\ttarget: AnyTarget,\n\t\t\t\tguardOptions: {\n\t\t\t\t\treadonly read?: string\n\t\t\t\t\treadonly hide: () => unknown\n\t\t\t\t},\n\t\t\t) {\n\t\t\t\tconst snapshot = yield* current\n\t\t\t\tconst read = guardOptions.read ?? \"read\"\n\t\t\t\tconst { field: _field, ...rest } = target\n\t\t\t\tconst visible: Decision.Decision = yield* decideWith(snapshot, {\n\t\t\t\t\t...rest,\n\t\t\t\t\taction: read,\n\t\t\t\t})\n\t\t\t\tif (visible._tag !== \"Allow\") {\n\t\t\t\t\treturn yield* Effect.fail(guardOptions.hide())\n\t\t\t\t}\n\t\t\t\tif (target.action !== read || target.field !== undefined) {\n\t\t\t\t\tyield* checkWith(snapshot, target)\n\t\t\t\t}\n\t\t\t})\n\n\t\t\t// The filter is the condition of the coverage: the scope for `All`,\n\t\t\t// `False` for `None`, and else the residual condition in the scope.\n\t\t\t// The residual throws an error when it is too large, so the filter\n\t\t\t// fails.\n\t\t\tconst filter = (\n\t\t\t\ttarget: AnyTarget,\n\t\t\t): Effect.Effect<Condition.Ast, unknown, unknown> =>\n\t\t\t\tEffect.map(\n\t\t\t\t\tcurrent,\n\t\t\t\t\t({ principal, policy: value }): Condition.Ast =>\n\t\t\t\t\t\tAnalysis.coverageAny(value, { ...target, principal } as never)\n\t\t\t\t\t\t\t.condition as Condition.Ast,\n\t\t\t\t)\n\n\t\t\tconst coverage = (\n\t\t\t\ttarget: AnyTarget,\n\t\t\t): Effect.Effect<Analysis.AnyCoverage, unknown, unknown> =>\n\t\t\t\tEffect.map(current, ({ principal, policy: value }) =>\n\t\t\t\t\tAnalysis.coverageAny(value, { ...target, principal } as never),\n\t\t\t\t)\n\n\t\t\tconst permittedFields = Effect.fnUntraced(function* (target: AnyTarget) {\n\t\t\t\tconst { principal, policy: value } = yield* current\n\t\t\t\treturn yield* withServices(\n\t\t\t\t\tDecide.permittedFields(value, { ...target, principal } as never),\n\t\t\t\t)\n\t\t\t})\n\n\t\t\tconst invalidate = (principal?: object) => {\n\t\t\t\tif (cache === undefined) {\n\t\t\t\t\treturn Effect.void\n\t\t\t\t}\n\t\t\t\tif (principal === undefined) {\n\t\t\t\t\treturn Cache.invalidateAll(cache)\n\t\t\t\t}\n\t\t\t\tconst key = keyOf(principal, canonicalOf(principal))\n\t\t\t\treturn key === undefined ? Effect.void : Cache.invalidate(cache, key)\n\t\t\t}\n\n\t\t\treturn {\n\t\t\t\tdomain,\n\t\t\t\tprincipal: service.principal,\n\t\t\t\tpolicy: Effect.map(current, ({ policy: value }) => value),\n\t\t\t\tdecide,\n\t\t\t\tcheck,\n\t\t\t\tguard,\n\t\t\t\tfilter,\n\t\t\t\tcoverage,\n\t\t\t\tpermittedFields,\n\t\t\t\tinvalidate,\n\t\t\t} as never\n\t\t}),\n\t)\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"authorizer.js","names":["Trampoline.adaptive","Policies.provideContext","Decide.decideAny","ResourceData.kindOf","DomainData.descriptorOf","Decide.fitsKind","Analysis.decideKind","Decision.reason","Analysis.coverageAny","permittedFields","Decide.permittedFields"],"sources":["../../src/internal/authorizer.ts"],"sourcesContent":["/**\n * The values of authorizers: the errors, the service class and the layer.\n * The types are in `Authorizer.ts`.\n */\n// The service erases the error type and the service type of predicates,\n// principals and deciders to `unknown`. The public types keep them, so the\n// language service check for `unknown` in an Effect is off here.\n// @effect-diagnostics anyUnknownInErrorContext:off\nimport * as Arr from \"effect/Array\"\nimport * as Cache from \"effect/Cache\"\nimport * as Chunk from \"effect/Chunk\"\nimport * as Context from \"effect/Context\"\nimport type * as Duration from \"effect/Duration\"\nimport * as Effect from \"effect/Effect\"\nimport * as Equal from \"effect/Equal\"\nimport * as Hash from \"effect/Hash\"\nimport * as HashMap from \"effect/HashMap\"\nimport * as HashSet from \"effect/HashSet\"\nimport * as Layer from \"effect/Layer\"\nimport * as Option from \"effect/Option\"\nimport * as Result from \"effect/Result\"\nimport * as Schema from \"effect/Schema\"\nimport type * as Authorizer from \"../Authorizer.ts\"\nimport type * as Condition from \"../Condition.ts\"\nimport * as Decision from \"../Decision.ts\"\nimport type * as Domain from \"../Domain.ts\"\nimport type * as Policy from \"../Policy.ts\"\nimport * as Analysis from \"./analysis.ts\"\nimport * as Decide from \"./decide.ts\"\nimport * as DomainData from \"./domain.ts\"\nimport * as Policies from \"./policy.ts\"\nimport * as ResourceData from \"./resource.ts\"\nimport * as Trampoline from \"./trampoline.ts\"\n\ntype AnyServiceClass = Authorizer.AnyServiceClass\n\n/** The error when a policy says no. */\nexport class Forbidden extends Schema.TaggedError<Forbidden>(\n\t\"@nmnmcc/ability/Authorizer/Forbidden\",\n)(\"Forbidden\", {\n\taction: Schema.String,\n\tkind: Schema.String,\n\tfield: Schema.optionalKey(Schema.String),\n\treason: Schema.optionalKey(Schema.String),\n}) {}\n\n/** The error when the policy of the principal can not be loaded. */\nexport class PolicyUnavailable extends Schema.TaggedError<PolicyUnavailable>(\n\t\"@nmnmcc/ability/Authorizer/PolicyUnavailable\",\n)(\"PolicyUnavailable\", { cause: Schema.Defect() }) {}\n\n/** The error when a decider must wait for an answer. */\nexport class Pending extends Schema.TaggedError<Pending>(\n\t\"@nmnmcc/ability/Authorizer/Pending\",\n)(\"Pending\", {\n\tticket: Schema.String,\n\treason: Schema.optionalKey(Schema.String),\n}) {}\n\n/** Makes an authorizer service class for a domain. */\nexport const Service =\n\t() =>\n\t(\n\t\tid: string,\n\t\toptions: {\n\t\t\treadonly domain: Domain.Any\n\t\t\treadonly principal: Effect.Effect<any, any, any>\n\t\t},\n\t\t// The class has no name, and its type uses types of Effect that a\n\t\t// declaration file can not name. So this function gives a `Context.Key`.\n\t\t// The public module gives the full type.\n\t): Context.Key<any, any> => {\n\t\tconst Base = Context.Service<any, any>()(id)\n\t\treturn class extends Base {\n\t\t\tstatic readonly domain = options.domain\n\t\t\tstatic readonly principal = options.principal\n\t\t\tstatic readonly decide = (target: AnyTarget) =>\n\t\t\t\tthis.use((service: any) => service.decide(target))\n\t\t\tstatic readonly check = (target: AnyTarget) =>\n\t\t\t\tthis.use((service: any) => service.check(target))\n\t\t\tstatic readonly guard = (target: AnyTarget, options: unknown) =>\n\t\t\t\tthis.use((service: any) => service.guard(target, options))\n\t\t\tstatic readonly filter = (target: AnyTarget) =>\n\t\t\t\tthis.use((service: any) => service.filter(target))\n\t\t\tstatic readonly coverage = (target: AnyTarget) =>\n\t\t\t\tthis.use((service: any) => service.coverage(target))\n\t\t\tstatic readonly permittedFields = (target: AnyTarget) =>\n\t\t\t\tthis.use((service: any) => service.permittedFields(target))\n\t\t}\n\t}\n\ntype AnyTarget = {\n\treadonly action: string\n\treadonly resource?: object\n\treadonly kind?: string\n\treadonly field?: string\n\treadonly within?: unknown\n}\n\n/**\n * A cache key for a principal. Two keys are equal when they have the same\n * text, so two principals with the same key share one policy. A key from the\n * JSON form also needs equal decoded principals: a Schema may make data when\n * it decodes, as `expired` from a time, so one text may give two principals.\n * The key also keeps the principal, so the cache can load its policy. A plain\n * `Data` value can not do all this, so the key gives its own equality with\n * `Equal.Equal`.\n */\nclass PrincipalKey implements Equal.Equal {\n\treadonly principal: object\n\treadonly text: string\n\t/**\n\t * The order of the parts of the decoded principal, from {@link orderOf},\n\t * when the key is the JSON form. The decoded principals must be equal\n\t * too, with their parts in the same order. `undefined` for a key from\n\t * `cache.key`.\n\t */\n\treadonly order: string | undefined\n\tconstructor(principal: object, text: string, order: string | undefined) {\n\t\tthis.principal = principal\n\t\tthis.text = text\n\t\tthis.order = order\n\t}\n\t[Equal.symbol](that: unknown): boolean {\n\t\treturn (\n\t\t\tthat instanceof PrincipalKey &&\n\t\t\tthat.text === this.text &&\n\t\t\tthat.order === this.order &&\n\t\t\t(this.order === undefined || Equal.equals(that.principal, this.principal))\n\t\t)\n\t}\n\t[Hash.symbol](): number {\n\t\treturn Hash.string(this.text)\n\t}\n}\n\n/**\n * The text of the order of the parts of a value: the names of the fields of\n * each object, and the items of each list, set and map, with each scalar, in\n * the order that a loop sees them. `Equal` does not see the order of the\n * fields of an object or of the items of a map, but a loader may read it, as\n * `Object.values` does. So two keys need the same order too. A scalar is in\n * the text with its type, so the items of a set or a map show their order,\n * and `-0` is not `0`: `Equal` takes them as one, but a loader may not.\n *\n * Each object gets a number at its first visit, and a later visit of it, as\n * of a part that the value shares or of a loop back to a part above, writes\n * `#` and the number. So the walk ends for a value with a loop, and a shared\n * part does not make the text very long. The numbers are in a native `Map`,\n * because the walk must find an object by the object itself. The map belongs\n * to one walk.\n *\n * A value that keeps its data inside, with no own fields, as a `Date`, a\n * `URL` or a `RegExp` with the place of its next match, is in the text with\n * its data, and so is an `Option`.\n * For another such value, the walk can not see the data, so it throws, and\n * the principal gets no key: it is not kept.\n */\nconst orderOf = (root: unknown): string => {\n\tconst ids = new Map<object, number>()\n\treturn Trampoline.adaptive<unknown, string>((R, child) => (value, depth) => {\n\t\tif (typeof value !== \"object\" || value === null) {\n\t\t\treturn R.succeed(\n\t\t\t\ttypeof value === \"string\"\n\t\t\t\t\t? JSON.stringify(value)\n\t\t\t\t\t: Object.is(value, -0)\n\t\t\t\t\t\t? \"number:-0\"\n\t\t\t\t\t\t: `${typeof value}:${String(value)}`,\n\t\t\t)\n\t\t}\n\t\tconst known = ids.get(value)\n\t\tif (known !== undefined) {\n\t\t\treturn R.succeed(`#${String(known)}`)\n\t\t}\n\t\tids.set(value, ids.size)\n\t\tconst items = (list: ReadonlyArray<unknown>) =>\n\t\t\tR.forEach(list, (item) => child(item, depth + 1))\n\t\tif (value instanceof Date) {\n\t\t\treturn R.succeed(`date:${String(value.getTime())}`)\n\t\t}\n\t\tif (value instanceof URL) {\n\t\t\treturn R.succeed(`url:${JSON.stringify(value.href)}`)\n\t\t}\n\t\tif (value instanceof RegExp) {\n\t\t\t// A `RegExp` with the flag `g` or `y` keeps the place of its next\n\t\t\t// match in `lastIndex`, which `Equal` does not see.\n\t\t\treturn R.succeed(\n\t\t\t\t`regexp:${JSON.stringify(String(value))}:${String(value.lastIndex)}`,\n\t\t\t)\n\t\t}\n\t\tif (Option.isOption(value)) {\n\t\t\treturn Option.isSome(value)\n\t\t\t\t? R.map(child(value.value, depth + 1), (text) => `some(${text})`)\n\t\t\t\t: R.succeed(\"none\")\n\t\t}\n\t\tif (Array.isArray(value)) {\n\t\t\treturn R.map(items(value), (texts) => `[${texts.join(\",\")}]`)\n\t\t}\n\t\tif (\n\t\t\tvalue instanceof Map ||\n\t\t\tvalue instanceof Set ||\n\t\t\tHashMap.isHashMap(value) ||\n\t\t\tHashSet.isHashSet(value) ||\n\t\t\tChunk.isChunk(value)\n\t\t) {\n\t\t\treturn R.map(\n\t\t\t\titems(Array.from(value as Iterable<unknown>)),\n\t\t\t\t(texts) => `<${texts.join(\",\")}>`,\n\t\t\t)\n\t\t}\n\t\tconst names = Object.keys(value)\n\t\tconst prototype: unknown = Object.getPrototypeOf(value)\n\t\tif (\n\t\t\tnames.length === 0 &&\n\t\t\tprototype !== Object.prototype &&\n\t\t\tprototype !== null\n\t\t) {\n\t\t\tthrow new Error(\"The data of a part of the principal is not in fields\")\n\t\t}\n\t\treturn R.map(\n\t\t\titems(Arr.map(names, (name) => (value as Record<string, unknown>)[name])),\n\t\t\t(texts) =>\n\t\t\t\t`{${Arr.map(names, (name, index) => `${JSON.stringify(name)}:${texts[index]!}`).join(\",\")}}`,\n\t\t)\n\t})(root)\n}\n\n/**\n * Gives the JSON codec of a Schema, or `undefined` when the Schema has no JSON\n * form, as a struct with a symbol key has none.\n */\nconst jsonCodecOf = (\n\tschema: Schema.Top,\n): Schema.Codec<unknown, unknown> | undefined => {\n\ttry {\n\t\treturn Schema.toCodecJson(schema) as unknown as Schema.Codec<\n\t\t\tunknown,\n\t\t\tunknown\n\t\t>\n\t} catch {\n\t\treturn undefined\n\t}\n}\n\n/**\n * Makes the cache key of a principal from its JSON form. The key keeps the\n * principal that the Schema decodes from that form, so the loader sees only\n * the data that the key shows. A principal gets no key when it does not\n * encode, or when the decoded value is not `Equal` to it. Two classes, or a\n * string and a number, may have one JSON form, as in a union, and `Equal`\n * tells them apart. A Schema may make data when it decodes, as the order of\n * the fields of a record, so the key keeps that order too. So two\n * principals with one key are equal, and the cache can not give one the\n * policy of the other.\n */\nconst jsonKey = (\n\tschema: Schema.Top,\n): ((principal: object) => PrincipalKey | undefined) => {\n\tconst codec = jsonCodecOf(schema)\n\t// A principal with no JSON form is never kept.\n\tif (codec === undefined) {\n\t\treturn () => undefined\n\t}\n\tconst encode = Schema.encodeUnknownResult(codec)\n\tconst decode = Schema.decodeUnknownResult(codec)\n\tconst keyOf = (principal: object): PrincipalKey | undefined =>\n\t\tResult.match(encode(principal), {\n\t\t\tonFailure: () => undefined,\n\t\t\tonSuccess: (json) => {\n\t\t\t\t// The key is the text, so the check decodes the text. The encoded\n\t\t\t\t// value may hold more than its text shows, as an object of a\n\t\t\t\t// `Schema.Any` field does: `JSON.stringify` leaves out a map.\n\t\t\t\tconst text = JSON.stringify(json)\n\t\t\t\treturn Result.match(decode(JSON.parse(text)), {\n\t\t\t\t\tonFailure: () => undefined,\n\t\t\t\t\tonSuccess: (decoded) =>\n\t\t\t\t\t\tEqual.equals(decoded, principal)\n\t\t\t\t\t\t\t? new PrincipalKey(decoded as object, text, orderOf(decoded))\n\t\t\t\t\t\t\t: undefined,\n\t\t\t\t})\n\t\t\t},\n\t\t})\n\treturn (principal) => {\n\t\t// A very deep principal may fill the stack of the encoder or of\n\t\t// `Equal`. Such a principal is not kept.\n\t\ttry {\n\t\t\treturn keyOf(principal)\n\t\t} catch {\n\t\t\treturn undefined\n\t\t}\n\t}\n}\n\nconst forbidden = (\n\ttarget: AnyTarget,\n\tkind: string,\n\treason: string | undefined,\n): Forbidden =>\n\tForbidden.make({\n\t\taction: target.action,\n\t\tkind,\n\t\t...(target.field === undefined ? {} : { field: target.field }),\n\t\t...(reason === undefined ? {} : { reason }),\n\t})\n\n/** Makes the layer of an authorizer. */\nexport const makeLayer = (\n\tservice: AnyServiceClass,\n\toptions: {\n\t\treadonly policy: (\n\t\t\tprincipal: object,\n\t\t) => Effect.Effect<Policy.Any, unknown, unknown>\n\t\treadonly decider?: (\n\t\t\trequest: object,\n\t\t) => Effect.Effect<Decision.Decision, unknown, unknown>\n\t\treadonly cache?:\n\t\t\t| {\n\t\t\t\t\treadonly capacity?: number\n\t\t\t\t\treadonly timeToLive?: Duration.Input\n\t\t\t\t\treadonly key?: (principal: object) => string | undefined\n\t\t\t }\n\t\t\t| false\n\t},\n) => {\n\tconst domain: Domain.Any = service.domain\n\t// The JSON form of a principal, and the copy that the Schema decodes from\n\t// it. The loader always gets the copy, with a cache or not, so a cache can\n\t// not change an answer: a form may lose data that `Equal` does not see,\n\t// as `-0` becomes `0`. A principal with no such form gives `undefined`.\n\tconst canonicalOf = jsonKey(domain.principal)\n\tconst custom = options.cache === false ? undefined : options.cache?.key\n\t// It gives `undefined` when the principal has no key. Such a principal does\n\t// not use the cache.\n\tconst keyOf = (\n\t\tprincipal: object,\n\t\tcanonical: PrincipalKey | undefined,\n\t): PrincipalKey | undefined => {\n\t\tif (custom === undefined) {\n\t\t\treturn canonical\n\t\t}\n\t\tconst text = custom(principal)\n\t\treturn text === undefined\n\t\t\t? undefined\n\t\t\t: new PrincipalKey(canonical?.principal ?? principal, text, undefined)\n\t}\n\treturn Layer.effect(\n\t\tservice,\n\t\tEffect.gen(function* () {\n\t\t\t// The services of the policy loader and of the predicates come from the\n\t\t\t// layer, so a check does not need them.\n\t\t\tconst services = yield* Effect.context<never>()\n\n\t\t\t// The predicates of the policy get the services of the layer, so the\n\t\t\t// policy and its residual conditions need no services.\n\t\t\t// A policy of another domain may have the same types, but its resources\n\t\t\t// may have other paths. So it fails closed, as a policy that can not be\n\t\t\t// loaded.\n\t\t\tconst load = (principal: object) =>\n\t\t\t\toptions.policy(principal).pipe(\n\t\t\t\t\tEffect.provideContext(services),\n\t\t\t\t\tEffect.filterOrFail(\n\t\t\t\t\t\t(value) => value.domain === domain,\n\t\t\t\t\t\t() =>\n\t\t\t\t\t\t\tnew globalThis.Error(\n\t\t\t\t\t\t\t\t\"The loaded policy is not a policy of this domain\",\n\t\t\t\t\t\t\t),\n\t\t\t\t\t),\n\t\t\t\t\tEffect.mapError((cause) => PolicyUnavailable.make({ cause })),\n\t\t\t\t\tEffect.map((value) =>\n\t\t\t\t\t\tPolicies.provideContext(value, services as never),\n\t\t\t\t\t),\n\t\t\t\t)\n\n\t\t\tconst cache =\n\t\t\t\toptions.cache === false\n\t\t\t\t\t? undefined\n\t\t\t\t\t: yield* Cache.make({\n\t\t\t\t\t\t\tcapacity: options.cache?.capacity ?? 1000,\n\t\t\t\t\t\t\ttimeToLive: options.cache?.timeToLive ?? \"30 seconds\",\n\t\t\t\t\t\t\tlookup: (key: PrincipalKey) => load(key.principal),\n\t\t\t\t\t\t})\n\n\t\t\tconst policyFor = (principal: object) => {\n\t\t\t\tconst canonical = canonicalOf(principal)\n\t\t\t\tconst key =\n\t\t\t\t\tcache === undefined ? undefined : keyOf(principal, canonical)\n\t\t\t\tif (cache === undefined || key === undefined) {\n\t\t\t\t\treturn load(canonical?.principal ?? principal)\n\t\t\t\t}\n\t\t\t\t// A failed lookup is kept in the cache. Remove it, so the next check\n\t\t\t\t// tries again.\n\t\t\t\treturn Cache.get(cache, key).pipe(\n\t\t\t\t\tEffect.tapError(() => Cache.invalidate(cache, key)),\n\t\t\t\t)\n\t\t\t}\n\n\t\t\tconst withServices = <A, E>(\n\t\t\t\teffect: Effect.Effect<A, E, unknown>,\n\t\t\t): Effect.Effect<A, E, unknown> => Effect.provideContext(effect, services)\n\n\t\t\tconst current = Effect.gen(function* () {\n\t\t\t\tconst principal = yield* service.principal\n\t\t\t\tconst value = yield* policyFor(principal)\n\t\t\t\treturn { principal, policy: value }\n\t\t\t})\n\n\t\t\t// The principal and its policy, loaded one time. A check that asks\n\t\t\t// the policy two times, such as `guard`, uses one snapshot for both,\n\t\t\t// so the two answers come from one principal.\n\t\t\ttype Snapshot = Effect.Success<typeof current>\n\n\t\t\tconst decideWith = Effect.fnUntraced(function* (\n\t\t\t\t{ principal, policy: value }: Snapshot,\n\t\t\t\ttarget: AnyTarget,\n\t\t\t) {\n\t\t\t\tconst decision: Decision.Decision = yield* withServices(\n\t\t\t\t\tDecide.decideAny(value, { ...target, principal } as never),\n\t\t\t\t)\n\t\t\t\treturn decision\n\t\t\t})\n\n\t\t\tconst decide = (target: AnyTarget) =>\n\t\t\t\tEffect.flatMap(current, (snapshot) => decideWith(snapshot, target))\n\n\t\t\tconst checkWith = Effect.fnUntraced(function* (\n\t\t\t\t{ principal, policy: value }: Snapshot,\n\t\t\t\ttarget: AnyTarget,\n\t\t\t) {\n\t\t\t\tconst kind =\n\t\t\t\t\ttarget.resource === undefined\n\t\t\t\t\t\t? (target.kind ?? \"\")\n\t\t\t\t\t\t: (ResourceData.kindOf(target.resource) ?? \"\")\n\t\t\t\t// A request that does not fit its kind gets a \"no\" at once. The\n\t\t\t\t// decider must not say yes to an action that the kind does not have.\n\t\t\t\tconst descriptor = DomainData.descriptorOf(domain, kind)\n\t\t\t\tif (\n\t\t\t\t\tdescriptor === undefined ||\n\t\t\t\t\t!Decide.fitsKind(descriptor, target, domain.limits.fieldPathParts)\n\t\t\t\t) {\n\t\t\t\t\treturn yield* forbidden(target, kind, undefined)\n\t\t\t\t}\n\t\t\t\tconst fromPolicy: Decision.Decision =\n\t\t\t\t\ttarget.resource === undefined\n\t\t\t\t\t\t? Analysis.decideKind(value, { ...target, principal } as never)\n\t\t\t\t\t\t: yield* withServices(\n\t\t\t\t\t\t\t\tDecide.decideAny(value, { ...target, principal } as never),\n\t\t\t\t\t\t\t)\n\t\t\t\t// When no rule gives an answer, the decider gives it.\n\t\t\t\tconst decision: Decision.Decision =\n\t\t\t\t\tfromPolicy._tag === \"NotApplicable\" && options.decider !== undefined\n\t\t\t\t\t\t? yield* options.decider({ ...target, principal })\n\t\t\t\t\t\t: fromPolicy\n\t\t\t\tif (decision._tag !== \"Allow\") {\n\t\t\t\t\treturn yield* forbidden(target, kind, Decision.reason(decision))\n\t\t\t\t}\n\t\t\t})\n\n\t\t\tconst check = (target: AnyTarget) =>\n\t\t\t\tEffect.flatMap(current, (snapshot) => checkWith(snapshot, target))\n\n\t\t\tconst guard = Effect.fnUntraced(function* (\n\t\t\t\ttarget: AnyTarget,\n\t\t\t\tguardOptions: {\n\t\t\t\t\treadonly read?: string\n\t\t\t\t\treadonly hide: () => unknown\n\t\t\t\t},\n\t\t\t) {\n\t\t\t\tconst snapshot = yield* current\n\t\t\t\tconst read = guardOptions.read ?? \"read\"\n\t\t\t\tconst { field: _field, ...rest } = target\n\t\t\t\tconst visible: Decision.Decision = yield* decideWith(snapshot, {\n\t\t\t\t\t...rest,\n\t\t\t\t\taction: read,\n\t\t\t\t})\n\t\t\t\tif (visible._tag !== \"Allow\") {\n\t\t\t\t\treturn yield* Effect.fail(guardOptions.hide())\n\t\t\t\t}\n\t\t\t\tif (target.action !== read || target.field !== undefined) {\n\t\t\t\t\tyield* checkWith(snapshot, target)\n\t\t\t\t}\n\t\t\t})\n\n\t\t\t// The filter is the condition of the coverage: the scope for `All`,\n\t\t\t// `False` for `None`, and else the residual condition in the scope.\n\t\t\t// The residual throws an error when it is too large, so the filter\n\t\t\t// fails.\n\t\t\tconst filter = (\n\t\t\t\ttarget: AnyTarget,\n\t\t\t): Effect.Effect<Condition.Ast, unknown, unknown> =>\n\t\t\t\tEffect.map(\n\t\t\t\t\tcurrent,\n\t\t\t\t\t({ principal, policy: value }): Condition.Ast =>\n\t\t\t\t\t\tAnalysis.coverageAny(value, { ...target, principal } as never)\n\t\t\t\t\t\t\t.condition as Condition.Ast,\n\t\t\t\t)\n\n\t\t\tconst coverage = (\n\t\t\t\ttarget: AnyTarget,\n\t\t\t): Effect.Effect<Analysis.AnyCoverage, unknown, unknown> =>\n\t\t\t\tEffect.map(current, ({ principal, policy: value }) =>\n\t\t\t\t\tAnalysis.coverageAny(value, { ...target, principal } as never),\n\t\t\t\t)\n\n\t\t\tconst permittedFields = Effect.fnUntraced(function* (target: AnyTarget) {\n\t\t\t\tconst { principal, policy: value } = yield* current\n\t\t\t\treturn yield* withServices(\n\t\t\t\t\tDecide.permittedFields(value, { ...target, principal } as never),\n\t\t\t\t)\n\t\t\t})\n\n\t\t\tconst invalidate = (principal?: object) => {\n\t\t\t\tif (cache === undefined) {\n\t\t\t\t\treturn Effect.void\n\t\t\t\t}\n\t\t\t\tif (principal === undefined) {\n\t\t\t\t\treturn Cache.invalidateAll(cache)\n\t\t\t\t}\n\t\t\t\tconst key = keyOf(principal, canonicalOf(principal))\n\t\t\t\treturn key === undefined ? Effect.void : Cache.invalidate(cache, key)\n\t\t\t}\n\n\t\t\treturn {\n\t\t\t\tdomain,\n\t\t\t\tprincipal: service.principal,\n\t\t\t\tpolicy: Effect.map(current, ({ policy: value }) => value),\n\t\t\t\tdecide,\n\t\t\t\tcheck,\n\t\t\t\tguard,\n\t\t\t\tfilter,\n\t\t\t\tcoverage,\n\t\t\t\tpermittedFields,\n\t\t\t\tinvalidate,\n\t\t\t} as never\n\t\t}),\n\t)\n}\n"],"mappings":"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"}
|
package/dist/internal/bind.js
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import { adaptive, run } from "./trampoline.js";
|
|
2
2
|
import { isNode } from "./nodes.js";
|
|
3
|
-
import { accepts, anyKinds, lookup } from "./catalog.js";
|
|
3
|
+
import { accepts, anyKinds, fieldsOf, lookup } from "./catalog.js";
|
|
4
4
|
import { at, pathOf } from "./place.js";
|
|
5
5
|
import { fitsCatalog, parse } from "./patterns.js";
|
|
6
6
|
import { withSuggestion } from "./suggest.js";
|
|
7
7
|
import * as Arr from "effect/Array";
|
|
8
8
|
import * as HashMap from "effect/HashMap";
|
|
9
|
-
import * as Result from "effect/Result";
|
|
10
9
|
import * as Option from "effect/Option";
|
|
10
|
+
import * as Result from "effect/Result";
|
|
11
11
|
// src/internal/bind.ts
|
|
12
12
|
/**
|
|
13
13
|
* The binding of rules to a domain. It is the one check of a rule, for a rule
|
|
@@ -64,8 +64,12 @@ const hasNumbers = (kinds) => kinds.number !== "none" || Arr.some(kinds.literals
|
|
|
64
64
|
const isOrdered = (kinds) => hasStrings(kinds) || hasNumbers(kinds);
|
|
65
65
|
/** Tests if two fields may hold values of one ordered type. */
|
|
66
66
|
const orderedMeet = (left, right) => hasStrings(left) && hasStrings(right) || hasNumbers(left) && hasNumbers(right);
|
|
67
|
-
/**
|
|
68
|
-
|
|
67
|
+
/**
|
|
68
|
+
* The texts of the fields of a catalog that have only names, for
|
|
69
|
+
* suggestions, with the limits of the domain. The domain checked the size of
|
|
70
|
+
* the list when it was made.
|
|
71
|
+
*/
|
|
72
|
+
const namesOf = (catalog, limits) => Arr.map(Arr.filter(fieldsOf(catalog, limits.fieldPathParts, limits.catalogFields), (field) => Arr.every(field.path, (segment) => typeof segment === "string")), (field) => field.text);
|
|
69
73
|
/** The issue of a field that is not a field of every resource of the rule. */
|
|
70
74
|
const fieldIssue = (context, field, at) => {
|
|
71
75
|
const parts = context.memo.limits.fieldPathParts;
|
|
@@ -81,7 +85,7 @@ const fieldIssue = (context, field, at) => {
|
|
|
81
85
|
const without = Arr.filter(context.named, (descriptor) => lookup(descriptor.catalog, field, parts) === void 0);
|
|
82
86
|
return without.length === 0 ? [] : [{
|
|
83
87
|
path: at,
|
|
84
|
-
issue: suggestIn(context.memo, message, field, without[0].kind, () => namesOf(without[0].catalog))
|
|
88
|
+
issue: suggestIn(context.memo, message, field, without[0].kind, () => namesOf(without[0].catalog, context.memo.limits))
|
|
85
89
|
}];
|
|
86
90
|
};
|
|
87
91
|
/**
|
|
@@ -140,7 +144,7 @@ const principalField = (context, ref, at) => {
|
|
|
140
144
|
};
|
|
141
145
|
return found !== void 0 ? found : {
|
|
142
146
|
path: at,
|
|
143
|
-
issue: suggestIn(context.memo, `The principal has no field "${ref}"`, ref, "~principal", () => namesOf(context.domain.principalCatalog))
|
|
147
|
+
issue: suggestIn(context.memo, `The principal has no field "${ref}"`, ref, "~principal", () => namesOf(context.domain.principalCatalog, context.memo.limits))
|
|
144
148
|
};
|
|
145
149
|
};
|
|
146
150
|
const isIssue = (value) => "issue" in value;
|
|
@@ -283,18 +287,18 @@ const conditionIssues = (context, root, rootAt, seen) => run(root.depth, (R) =>
|
|
|
283
287
|
return here(Arr.flatMap(node.values, (operand, index) => operandIssues(context, node.field, fields, false, operand, ["values", index])));
|
|
284
288
|
}
|
|
285
289
|
case "Exists": return room(0) ? here(readIssues(context, node.field, ["field"])) : [];
|
|
286
|
-
case "Under": return here(underIssues(context, node.prefix, []));
|
|
290
|
+
case "Under": return spendChecks(context.memo, 1 + node.prefix.length, context.paths.length) ? here(underIssues(context, node.prefix, [])) : [];
|
|
287
291
|
default: return [];
|
|
288
292
|
}
|
|
289
293
|
};
|
|
290
|
-
const go = (node, at$
|
|
294
|
+
const go = (node, at$3) => {
|
|
291
295
|
if (seen.has(node)) return R.succeed([]);
|
|
292
296
|
seen.add(node);
|
|
293
297
|
switch (node._tag) {
|
|
294
298
|
case "And":
|
|
295
|
-
case "Or": return R.map(R.forEach(node.conditions, (child, index) => R.suspend(() => go(child, at(at$
|
|
296
|
-
case "Not": return R.suspend(() => go(node.condition, at(at$
|
|
297
|
-
default: return R.succeed(leaf(node, at$
|
|
299
|
+
case "Or": return R.map(R.forEach(node.conditions, (child, index) => R.suspend(() => go(child, at(at$3, "conditions", index)))), Arr.flatten);
|
|
300
|
+
case "Not": return R.suspend(() => go(node.condition, at(at$3, "condition")));
|
|
301
|
+
default: return R.succeed(leaf(node, at$3));
|
|
298
302
|
}
|
|
299
303
|
};
|
|
300
304
|
return go(root, rootAt);
|
|
@@ -306,8 +310,8 @@ const hasAction = (descriptor, actions) => actions.includes("*") || Arr.some(act
|
|
|
306
310
|
* full path is made only for an issue that the binding keeps: a rule with
|
|
307
311
|
* very many problems deep in a tree does not make a full path for each one.
|
|
308
312
|
*/
|
|
309
|
-
const ruleIssues = (domain, rule, at$
|
|
310
|
-
const here = (issues) => under(memo, at$
|
|
313
|
+
const ruleIssues = (domain, rule, at$1, memo = makeMemo(domain.limits)) => {
|
|
314
|
+
const here = (issues) => under(memo, at$1, issues);
|
|
311
315
|
const all = rule.resources.includes("*");
|
|
312
316
|
const names = Array.from(new Set(Arr.filter(rule.resources, (kind) => kind !== "*")));
|
|
313
317
|
const kinds = Array.from(HashMap.keys(domain.resources));
|
|
@@ -328,7 +332,7 @@ const ruleIssues = (domain, rule, at$3, memo = makeMemo(domain.limits)) => {
|
|
|
328
332
|
const whenIssues = rule.when === void 0 ? [] : !isNode(rule.when) ? here([notWhole(["when"])]) : rule.when.weight > conditionNodes ? here([{
|
|
329
333
|
path: ["when"],
|
|
330
334
|
issue: `A condition has ${String(rule.when.weight)} nodes and values. The most is ${String(conditionNodes)}.`
|
|
331
|
-
}]) : conditionIssues(context, rule.when, at(at$
|
|
335
|
+
}]) : conditionIssues(context, rule.when, at(at$1, "when"), /* @__PURE__ */ new Set());
|
|
332
336
|
const work = memo.work.steps > memo.limits.searchSteps && !memo.work.noted;
|
|
333
337
|
memo.work.noted ||= work;
|
|
334
338
|
return [
|
|
@@ -343,7 +347,7 @@ const ruleIssues = (domain, rule, at$3, memo = makeMemo(domain.limits)) => {
|
|
|
343
347
|
...fieldIssues,
|
|
344
348
|
...whenIssues,
|
|
345
349
|
...work ? [{
|
|
346
|
-
path: pathOf(at$
|
|
350
|
+
path: pathOf(at$1),
|
|
347
351
|
issue: `The policy takes more than ${String(memo.limits.searchSteps)} steps to check: its field patterns, and the fields and values of its tests on each resource. The most is ${String(memo.limits.searchSteps)}.`
|
|
348
352
|
}] : []
|
|
349
353
|
];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bind.js","names":["Catalog.accepts","Catalog.lookup","Patterns.parse","Patterns.fitsCatalog","Catalog.anyKinds","Place.pathOf","Trampoline.run","Place.at","at","Nodes.isNode","Trampoline.adaptive"],"sources":["../../src/internal/bind.ts"],"sourcesContent":["/**\n * The binding of rules to a domain. It is the one check of a rule, for a rule\n * from the builder, from `Policy.rule`, and from JSON alike. It reads the\n * catalogs of the resources and of the principal, and it gives one issue for\n * each problem, with the path of the problem and, for a name with a mistake,\n * the name that it may mean.\n */\nimport * as Arr from \"effect/Array\"\nimport * as HashMap from \"effect/HashMap\"\nimport * as Option from \"effect/Option\"\nimport * as Result from \"effect/Result\"\nimport type * as Domain from \"../Domain.ts\"\nimport type * as Resource from \"../Resource.ts\"\nimport * as Catalog from \"./catalog.ts\"\nimport * as Limits from \"./limits.ts\"\nimport * as Nodes from \"./nodes.ts\"\nimport * as Patterns from \"./patterns.ts\"\nimport * as Place from \"./place.ts\"\nimport { withSuggestion } from \"./suggest.ts\"\nimport * as Trampoline from \"./trampoline.ts\"\n\n/** One problem of a rule or a policy, with its path. */\nexport interface Issue {\n\treadonly path: ReadonlyArray<PropertyKey>\n\treadonly issue: string\n}\n\n/** The data of a rule that the binding reads. */\nexport interface RuleData {\n\treadonly actions: ReadonlyArray<string>\n\treadonly resources: ReadonlyArray<string>\n\treadonly when?: unknown\n\treadonly fields?: ReadonlyArray<string>\n}\n\n/**\n * The memo of one binding, for all its rules. A policy may name one wrong\n * field or one pattern in very many places, so the work on each is done one\n * time. It goes away when the binding ends.\n */\ninterface Memo {\n\t/**\n\t * The suggestion for each name, by the place where it was looked for. The\n\t * names come from a policy, so the map is native, as `fits` is.\n\t */\n\treadonly suggestions: Map<string, string>\n\t/**\n\t * If a pattern matches a field of a kind, by the kind and the pattern. The\n\t * patterns come from a policy, so the map is native: the hash of a string\n\t * in Effect has no random seed, and a policy could hold very many\n\t * patterns with one hash. The engine hashes a string with a random seed.\n\t */\n\treadonly fits: Map<string, boolean>\n\t/** The count of the issues of conditions so far. It belongs to one binding. */\n\treadonly issues: { count: number; noted: boolean }\n\t/** The steps of the checks of patterns so far. It belongs to one binding. */\n\treadonly work: { steps: number; noted: boolean }\n\t/** The limits of the domain. */\n\treadonly limits: Limits.Limits\n}\n\nconst makeMemo = (limits: Limits.Limits): Memo => ({\n\tsuggestions: new Map(),\n\tfits: new Map(),\n\tissues: { count: 0, noted: false },\n\twork: { steps: 0, noted: false },\n\tlimits,\n})\n\n/**\n * The most names that one binding makes a suggestion for. A suggestion\n * compares the name with every name of a catalog, so a policy with very many\n * wrong names would take a long time.\n */\nconst maxSuggestions = 32\n\n/** Adds a suggestion to a message, with the memo of the binding. */\nconst suggestIn = (\n\tmemo: Memo,\n\tmessage: string,\n\tname: string,\n\tscope: string,\n\tnames: () => ReadonlyArray<string>,\n): string => {\n\tconst key = `${scope}\\u0000${name}`\n\tconst known = memo.suggestions.get(key)\n\tif (known !== undefined) {\n\t\treturn known\n\t}\n\tif (memo.suggestions.size >= maxSuggestions) {\n\t\treturn message\n\t}\n\tconst made = withSuggestion(message, name, names())\n\tmemo.suggestions.set(key, made)\n\treturn made\n}\n\n/** The kinds that a rule may run on, from the view of its checks. */\ninterface Context {\n\treadonly domain: Domain.Any\n\treadonly memo: Memo\n\t/** The rule is for every resource (`\"*\"`). */\n\treadonly all: boolean\n\t/** The resources that the rule names and that the domain has. */\n\treadonly named: ReadonlyArray<Resource.Descriptor>\n\t/** The named resources that have one of the actions of the rule. */\n\treadonly applicable: ReadonlyArray<Resource.Descriptor>\n\t/**\n\t * The resources whose paths a path test may read: every resource with one\n\t * of the actions, for a rule for every resource.\n\t */\n\treadonly paths: ReadonlyArray<Resource.Descriptor>\n}\n\ntype Operand = Nodes.Operand\n\n/**\n * Tests if a field may hold a value of the type of a fixed value. A test with\n * `null` is a test that the field is missing, so `null` always fits. A number\n * fits any field of numbers: a test of an integer field with `1.5` or\n * `Infinity` has a meaning, as `lt` shows.\n */\nconst fitsLiteral = (kinds: Catalog.Kinds, value: unknown): boolean =>\n\tvalue === null ||\n\t(typeof value === \"number\" && kinds.number !== \"none\") ||\n\tCatalog.accepts(kinds, value)\n\n/**\n * Tests if two fields may hold one scalar. A principal field that is `null`\n * or that holds an object is a missing value, and a test with a missing value\n * is false, so only scalars count.\n */\nconst meet = (left: Catalog.Kinds, right: Catalog.Kinds): boolean =>\n\t(left.string && right.string) ||\n\t(left.boolean && right.boolean) ||\n\t(left.number !== \"none\" && right.number !== \"none\") ||\n\tArr.some(left.literals, (value) => Catalog.accepts(right, value)) ||\n\tArr.some(right.literals, (value) => Catalog.accepts(left, value))\n\nconst hasStrings = (kinds: Catalog.Kinds): boolean =>\n\tkinds.string || Arr.some(kinds.literals, (value) => typeof value === \"string\")\n\nconst hasNumbers = (kinds: Catalog.Kinds): boolean =>\n\tkinds.number !== \"none\" ||\n\tArr.some(kinds.literals, (value) => typeof value === \"number\")\n\n/** Tests if a field may hold a value with an order: a number or a string. */\nconst isOrdered = (kinds: Catalog.Kinds): boolean =>\n\thasStrings(kinds) || hasNumbers(kinds)\n\n/** Tests if two fields may hold values of one ordered type. */\nconst orderedMeet = (left: Catalog.Kinds, right: Catalog.Kinds): boolean =>\n\t(hasStrings(left) && hasStrings(right)) ||\n\t(hasNumbers(left) && hasNumbers(right))\n\n/** The texts of the fields of a catalog that have only names, for suggestions. */\nconst namesOf = (catalog: Catalog.Catalog): ReadonlyArray<string> =>\n\tArr.map(\n\t\tArr.filter(catalog.fields, (field) =>\n\t\t\tArr.every(field.path, (segment) => typeof segment === \"string\"),\n\t\t),\n\t\t(field) => field.text,\n\t)\n\n/** The issue of a field that is not a field of every resource of the rule. */\nconst fieldIssue = (\n\tcontext: Context,\n\tfield: string,\n\tat: ReadonlyArray<PropertyKey>,\n): ReadonlyArray<Issue> => {\n\tconst parts = context.memo.limits.fieldPathParts\n\tif (field.split(\".\", parts + 1).length > parts) {\n\t\treturn [\n\t\t\t{\n\t\t\t\tpath: at,\n\t\t\t\tissue: `The field path \"${field}\" has more than ${String(parts)} parts. The most is ${String(parts)}.`,\n\t\t\t},\n\t\t]\n\t}\n\tconst message = `The field \"${field}\" is not a field of every resource of the rule`\n\tif (context.all || context.named.length === 0) {\n\t\treturn [{ path: at, issue: message }]\n\t}\n\tconst without = Arr.filter(\n\t\tcontext.named,\n\t\t(descriptor) =>\n\t\t\tCatalog.lookup(descriptor.catalog, field, parts) === undefined,\n\t)\n\treturn without.length === 0\n\t\t? []\n\t\t: [\n\t\t\t\t{\n\t\t\t\t\tpath: at,\n\t\t\t\t\tissue: suggestIn(context.memo, message, field, without[0]!.kind, () =>\n\t\t\t\t\t\tnamesOf(without[0]!.catalog),\n\t\t\t\t\t),\n\t\t\t\t},\n\t\t\t]\n}\n\n/**\n * The issues of a field that a condition reads: a field that is not a field\n * of every resource of the rule, or a field that is not plain data in one of\n * them. A read of a value that is not plain data, as a getter of a class, is\n * a defect in a decision. A residual may leave the read out, so the residual\n * would say \"yes\" where the decision fails. So the binding does not take\n * such a read: see `Catalog.Field.plain`.\n */\nconst readIssues = (\n\tcontext: Context,\n\tfield: string,\n\tat: ReadonlyArray<PropertyKey>,\n): ReadonlyArray<Issue> => {\n\tconst exists = fieldIssue(context, field, at)\n\tif (exists.length > 0) {\n\t\treturn exists\n\t}\n\treturn Arr.some(\n\t\tcontext.named,\n\t\t(descriptor) =>\n\t\t\t!(\n\t\t\t\tCatalog.lookup(\n\t\t\t\t\tdescriptor.catalog,\n\t\t\t\t\tfield,\n\t\t\t\t\tcontext.memo.limits.fieldPathParts,\n\t\t\t\t)?.plain ?? true\n\t\t\t),\n\t)\n\t\t? [\n\t\t\t\t{\n\t\t\t\t\tpath: at,\n\t\t\t\t\tissue: `A condition may not read the field \"${field}\". A value above it may be an object that does not have it as a data field, as a class with a getter, a date or a list. Use a predicate for it.`,\n\t\t\t\t},\n\t\t\t]\n\t\t: []\n}\n\n/**\n * The issues of a field pattern: a pattern that does not parse, or that\n * matches no field of the resources of the rule, as a pattern with a mistake\n * in a name does.\n */\nconst patternIssues = (\n\tmemo: Memo,\n\tknown: ReadonlyArray<Resource.Descriptor>,\n\tpattern: string,\n\tat: ReadonlyArray<PropertyKey>,\n): ReadonlyArray<Issue> =>\n\tResult.match(Patterns.parse(pattern, memo.limits.patternParts), {\n\t\tonFailure: (issue) => [{ path: at, issue }],\n\t\tonSuccess: (parsed) => {\n\t\t\t// A rule with no known resource has an issue for that already. The\n\t\t\t// pattern is checked on every kind, not only up to the first that it\n\t\t\t// fits: a decision on a kind does this check, so the limit of the\n\t\t\t// work below is a limit of the work of a decision too.\n\t\t\tconst fits =\n\t\t\t\tknown.length === 0 ||\n\t\t\t\tArr.reduce(known, false, (before, descriptor) => {\n\t\t\t\t\tconst key = `${descriptor.kind}\\u0000${pattern}`\n\t\t\t\t\treturn (\n\t\t\t\t\t\tOption.getOrElse(Option.fromUndefinedOr(memo.fits.get(key)), () => {\n\t\t\t\t\t\t\t// Each check of a pattern reads the steps of a catalog. The\n\t\t\t\t\t\t\t// work of one binding has a limit, so very many patterns stop\n\t\t\t\t\t\t\t// with one issue, and the policy does not load.\n\t\t\t\t\t\t\tmemo.work.steps +=\n\t\t\t\t\t\t\t\tMath.max(descriptor.catalog.edges, 1) *\n\t\t\t\t\t\t\t\tMath.max(parsed.length, 1)\n\t\t\t\t\t\t\tif (memo.work.steps > memo.limits.searchSteps) {\n\t\t\t\t\t\t\t\treturn true\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tconst found = Patterns.fitsCatalog(\n\t\t\t\t\t\t\t\tparsed,\n\t\t\t\t\t\t\t\tdescriptor.catalog,\n\t\t\t\t\t\t\t\tmemo.limits.fieldPathParts,\n\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\tmemo.fits.set(key, found)\n\t\t\t\t\t\t\treturn found\n\t\t\t\t\t\t}) || before\n\t\t\t\t\t)\n\t\t\t\t})\n\t\t\t// Past the limit of the work, `ruleIssues` gives one issue for it.\n\t\t\treturn fits || memo.work.steps > memo.limits.searchSteps\n\t\t\t\t? []\n\t\t\t\t: [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tpath: at,\n\t\t\t\t\t\t\tissue: `The field pattern \"${pattern}\" matches no field of the resources of the rule`,\n\t\t\t\t\t\t},\n\t\t\t\t\t]\n\t\t},\n\t})\n\n/** The fields of the resources that a test of the rule may read. */\nconst fieldsAt = (\n\tcontext: Context,\n\tfield: string,\n): ReadonlyArray<Catalog.Field> =>\n\tArr.flatMap(context.applicable, (descriptor) => {\n\t\tconst found = Catalog.lookup(\n\t\t\tdescriptor.catalog,\n\t\t\tfield,\n\t\t\tcontext.memo.limits.fieldPathParts,\n\t\t)\n\t\treturn found === undefined ? [] : [found]\n\t})\n\n/** The field of the principal that an operand names, or the issue when there is none. */\nconst principalField = (\n\tcontext: Context,\n\tref: string,\n\tat: ReadonlyArray<PropertyKey>,\n): Catalog.Field | Issue => {\n\tconst found = Catalog.lookup(\n\t\tcontext.domain.principalCatalog,\n\t\tref,\n\t\tcontext.memo.limits.fieldPathParts,\n\t)\n\t// A read of the principal that is not plain data is a defect, as a read\n\t// of a resource is: see `readIssues`.\n\tif (found !== undefined && !found.plain) {\n\t\treturn {\n\t\t\tpath: at,\n\t\t\tissue: `A condition may not read the field \"${ref}\" of the principal. A value above it may be an object that does not have it as a data field, as a class with a getter, a date or a list. Use a predicate for it.`,\n\t\t}\n\t}\n\treturn found !== undefined\n\t\t? found\n\t\t: {\n\t\t\t\tpath: at,\n\t\t\t\tissue: suggestIn(\n\t\t\t\t\tcontext.memo,\n\t\t\t\t\t`The principal has no field \"${ref}\"`,\n\t\t\t\t\tref,\n\t\t\t\t\t\"~principal\",\n\t\t\t\t\t() => namesOf(context.domain.principalCatalog),\n\t\t\t\t),\n\t\t\t}\n}\n\nconst isIssue = (value: Catalog.Field | Issue): value is Issue =>\n\t\"issue\" in value\n\n/**\n * The fields that a test reads, with one field for each distinct kind of\n * value. A rule for very many kinds may have one field type in all of them,\n * so each value of the test is checked one time for each distinct kind, not\n * one time for each resource. The kinds are found by their text in a native\n * `Map`, which belongs to this call.\n */\nconst distinctFields = (\n\tcontext: Context,\n\tfield: string,\n): ReadonlyArray<Catalog.Field> =>\n\tArray.from(\n\t\tnew Map(\n\t\t\tArr.map(\n\t\t\t\tfieldsAt(context, field),\n\t\t\t\t(found) => [JSON.stringify(found.kinds), found] as const,\n\t\t\t),\n\t\t).values(),\n\t)\n\n/**\n * Counts the work of the checks of the values of a test, as the work of the\n * field patterns counts. Past the limit, the values are not checked, and\n * `ruleIssues` gives one issue for the work.\n */\nconst spendChecks = (memo: Memo, values: number, fields: number): boolean => {\n\tmemo.work.steps += values * Math.max(fields, 1)\n\treturn memo.work.steps <= memo.limits.searchSteps\n}\n\n/** The issues of one operand of a test of a field, with its distinct fields. */\nconst operandIssues = (\n\tcontext: Context,\n\tfield: string,\n\tfields: ReadonlyArray<Catalog.Field>,\n\tordered: boolean,\n\toperand: Operand,\n\tat: ReadonlyArray<PropertyKey>,\n): ReadonlyArray<Issue> => {\n\tif (operand._tag === \"Literal\") {\n\t\tconst value = operand.value\n\t\tif (ordered && typeof value !== \"string\" && typeof value !== \"number\") {\n\t\t\treturn [\n\t\t\t\t{\n\t\t\t\t\tpath: at,\n\t\t\t\t\tissue: `The field \"${field}\" has an ordered test with a value that has no order`,\n\t\t\t\t},\n\t\t\t]\n\t\t}\n\t\treturn [\n\t\t\t...(Arr.some(fields, (found) => !fitsLiteral(found.kinds, value))\n\t\t\t\t? [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tpath: at,\n\t\t\t\t\t\t\tissue: `The field \"${field}\" can not hold the value ${JSON.stringify(value)}`,\n\t\t\t\t\t\t},\n\t\t\t\t\t]\n\t\t\t\t: []),\n\t\t\t...(ordered && Arr.some(fields, (found) => !isOrdered(found.kinds))\n\t\t\t\t? [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tpath: at,\n\t\t\t\t\t\t\tissue: `The field \"${field}\" has an ordered test with a value that has no order`,\n\t\t\t\t\t\t},\n\t\t\t\t\t]\n\t\t\t\t: []),\n\t\t]\n\t}\n\tconst principal = principalField(context, operand.path, at)\n\tif (isIssue(principal)) {\n\t\treturn [principal]\n\t}\n\treturn [\n\t\t...(Arr.some(fields, (found) => !meet(found.kinds, principal.kinds))\n\t\t\t? [\n\t\t\t\t\t{\n\t\t\t\t\t\tpath: at,\n\t\t\t\t\t\tissue: `The field \"${field}\" can not hold a value of the principal field \"${operand.path}\"`,\n\t\t\t\t\t},\n\t\t\t\t]\n\t\t\t: []),\n\t\t...(ordered &&\n\t\tArr.some(fields, (found) => !orderedMeet(found.kinds, principal.kinds))\n\t\t\t? [\n\t\t\t\t\t{\n\t\t\t\t\t\tpath: at,\n\t\t\t\t\t\tissue: `The field \"${field}\" has an ordered test with a value that has no order`,\n\t\t\t\t\t},\n\t\t\t\t]\n\t\t\t: []),\n\t]\n}\n\n/**\n * Tests if the path of a resource may start with a prefix: it is as long as\n * the prefix, and each fixed string of the prefix is the string of the path\n * at its place, when the path has a string there.\n */\nconst mayStartWith = (\n\tdescriptor: Resource.Descriptor,\n\tstrings: ReadonlyArray<string | null>,\n): boolean =>\n\t(descriptor.path?.length ?? 0) >= strings.length &&\n\tArr.every(strings, (string, index) => {\n\t\tconst segment = descriptor.path?.[index]\n\t\treturn string === null || typeof segment !== \"string\" || segment === string\n\t})\n\n/** The kinds of the part of the path of a resource at a place. */\nconst pathPartKinds = (\n\tdescriptor: Resource.Descriptor,\n\tindex: number,\n): Catalog.Kinds | string | undefined => {\n\tconst segment = descriptor.path?.[index]\n\treturn segment === undefined || typeof segment === \"string\"\n\t\t? segment\n\t\t: (Catalog.lookup(descriptor.catalog, segment.field)?.kinds ??\n\t\t\t\tCatalog.anyKinds)\n}\n\n/** The issues of the prefix of a path test. */\nconst underIssues = (\n\tcontext: Context,\n\tprefix: ReadonlyArray<Operand>,\n\tat: ReadonlyArray<PropertyKey>,\n): ReadonlyArray<Issue> => {\n\tconst strings = Arr.map(prefix, (operand) =>\n\t\toperand._tag === \"Literal\" && typeof operand.value === \"string\"\n\t\t\t? operand.value\n\t\t\t: null,\n\t)\n\t// The test can match only a path that starts with the fixed strings of\n\t// the prefix, so the other resources are left out.\n\tconst candidates = Arr.filter(context.paths, (descriptor) =>\n\t\tmayStartWith(descriptor, strings),\n\t)\n\treturn Arr.flatMap(prefix, (operand, index): ReadonlyArray<Issue> => {\n\t\tconst place = [...at, \"prefix\", index]\n\t\tif (operand._tag === \"Literal\") {\n\t\t\treturn Arr.some(candidates, (descriptor) => {\n\t\t\t\tconst kinds = pathPartKinds(descriptor, index)\n\t\t\t\treturn typeof kinds === \"object\" && !fitsLiteral(kinds, operand.value)\n\t\t\t})\n\t\t\t\t? [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tpath: place,\n\t\t\t\t\t\t\tissue: `The path part ${String(index)} can not hold the value ${JSON.stringify(operand.value)}`,\n\t\t\t\t\t\t},\n\t\t\t\t\t]\n\t\t\t\t: []\n\t\t}\n\t\tconst principal = principalField(context, operand.path, place)\n\t\tif (isIssue(principal)) {\n\t\t\treturn [principal]\n\t\t}\n\t\treturn Arr.some(candidates, (descriptor) => {\n\t\t\tconst kinds = pathPartKinds(descriptor, index)\n\t\t\treturn kinds === undefined\n\t\t\t\t? true\n\t\t\t\t: typeof kinds === \"string\"\n\t\t\t\t\t? !Catalog.accepts(principal.kinds, kinds)\n\t\t\t\t\t: !meet(kinds, principal.kinds)\n\t\t})\n\t\t\t? [\n\t\t\t\t\t{\n\t\t\t\t\t\tpath: place,\n\t\t\t\t\t\tissue: `The principal field \"${operand.path}\" can not hold a part of the path of the resource`,\n\t\t\t\t\t},\n\t\t\t\t]\n\t\t\t: []\n\t})\n}\n\nconst notWhole = (at: ReadonlyArray<PropertyKey>): Issue => ({\n\tpath: at,\n\tissue: \"The condition is not a whole condition tree\",\n})\n\n/**\n * The most issues that the walk of the conditions of one binding gives. A\n * policy may have very many wrong tests, and each issue keeps its path.\n */\nconst maxIssues = 100\n\n/**\n * The place of a part of a policy, which a walk turns into a full path only\n * for an issue: see `Place`.\n */\ntype At = Place.Place | undefined\n\n/**\n * Puts the path of a node before the paths of its issues. A walk keeps no\n * path for a node with no issue, so a deep and wide condition costs little.\n */\nconst under = (\n\tmemo: Memo,\n\tat: At,\n\tissues: ReadonlyArray<Issue>,\n): ReadonlyArray<Issue> => {\n\tif (issues.length === 0) {\n\t\treturn issues\n\t}\n\tconst room = maxIssues - memo.issues.count\n\tmemo.issues.count += issues.length\n\tconst kept = Arr.map(Arr.take(issues, Math.max(room, 0)), (issue) => ({\n\t\tpath: [...Place.pathOf(at), ...issue.path],\n\t\tissue: issue.issue,\n\t}))\n\tconst first = memo.issues.count > maxIssues && !memo.issues.noted\n\tmemo.issues.noted ||= first\n\treturn first\n\t\t? [\n\t\t\t\t...kept,\n\t\t\t\t{\n\t\t\t\t\tpath: [],\n\t\t\t\t\tissue: `The policy has more problems. The first ${String(maxIssues)} are here.`,\n\t\t\t\t},\n\t\t\t]\n\t\t: kept\n}\n\n/**\n * The issues of a condition. The constructors of the nodes checked the shape\n * of each node, so this checks the names and the types against the domain. A\n * node that the condition uses in many places is checked one time. The nodes\n * it has seen are in a native `Set`, because it must find a node by the node\n * itself: `Equal` compares nodes by structure.\n */\nconst conditionIssues = (\n\tcontext: Context,\n\troot: Nodes.Node,\n\trootAt: At,\n\tseen: Set<Nodes.Node>,\n): ReadonlyArray<Issue> =>\n\t// The walk takes the steps of a trampoline, so a deep condition does not\n\t// fill the stack.\n\tTrampoline.run(root.depth, (R) => {\n\t\t// A test looks its field up in each resource of the rule. A rule for\n\t\t// very many kinds with very many tests is very much work, so it counts.\n\t\t// Past the limit, `ruleIssues` gives one issue for it, and the policy\n\t\t// does not load.\n\t\tconst room = (values: number) =>\n\t\t\tspendChecks(context.memo, 1 + values, context.named.length)\n\t\tconst leaf = (node: Nodes.Node, at: At): ReadonlyArray<Issue> => {\n\t\t\tconst here = (issues: ReadonlyArray<Issue>) =>\n\t\t\t\tunder(context.memo, at, issues)\n\t\t\tswitch (node._tag) {\n\t\t\t\tcase \"Compare\": {\n\t\t\t\t\tif (!room(1)) {\n\t\t\t\t\t\treturn []\n\t\t\t\t\t}\n\t\t\t\t\tconst exists = readIssues(context, node.field, [\"field\"])\n\t\t\t\t\treturn here(\n\t\t\t\t\t\texists.length > 0\n\t\t\t\t\t\t\t? exists\n\t\t\t\t\t\t\t: operandIssues(\n\t\t\t\t\t\t\t\t\tcontext,\n\t\t\t\t\t\t\t\t\tnode.field,\n\t\t\t\t\t\t\t\t\tdistinctFields(context, node.field),\n\t\t\t\t\t\t\t\t\tnode.op !== \"eq\" && node.op !== \"ne\",\n\t\t\t\t\t\t\t\t\tnode.value,\n\t\t\t\t\t\t\t\t\t[\"value\"],\n\t\t\t\t\t\t\t\t),\n\t\t\t\t\t)\n\t\t\t\t}\n\t\t\t\tcase \"In\": {\n\t\t\t\t\tif (!room(0)) {\n\t\t\t\t\t\treturn []\n\t\t\t\t\t}\n\t\t\t\t\tconst exists = readIssues(context, node.field, [\"field\"])\n\t\t\t\t\tif (exists.length > 0) {\n\t\t\t\t\t\treturn here(exists)\n\t\t\t\t\t}\n\t\t\t\t\tconst fields = distinctFields(context, node.field)\n\t\t\t\t\tif (!spendChecks(context.memo, node.values.length, fields.length)) {\n\t\t\t\t\t\treturn []\n\t\t\t\t\t}\n\t\t\t\t\treturn here(\n\t\t\t\t\t\tArr.flatMap(node.values, (operand, index) =>\n\t\t\t\t\t\t\toperandIssues(context, node.field, fields, false, operand, [\n\t\t\t\t\t\t\t\t\"values\",\n\t\t\t\t\t\t\t\tindex,\n\t\t\t\t\t\t\t]),\n\t\t\t\t\t\t),\n\t\t\t\t\t)\n\t\t\t\t}\n\t\t\t\tcase \"Exists\":\n\t\t\t\t\treturn room(0) ? here(readIssues(context, node.field, [\"field\"])) : []\n\t\t\t\tcase \"Under\":\n\t\t\t\t\treturn here(underIssues(context, node.prefix, []))\n\t\t\t\tdefault:\n\t\t\t\t\treturn []\n\t\t\t}\n\t\t}\n\t\tconst go = (\n\t\t\tnode: Nodes.Node,\n\t\t\tat: At,\n\t\t): Trampoline.Step<ReadonlyArray<Issue>> => {\n\t\t\tif (seen.has(node)) {\n\t\t\t\treturn R.succeed([])\n\t\t\t}\n\t\t\tseen.add(node)\n\t\t\tswitch (node._tag) {\n\t\t\t\tcase \"And\":\n\t\t\t\tcase \"Or\":\n\t\t\t\t\treturn R.map(\n\t\t\t\t\t\tR.forEach(node.conditions, (child, index) =>\n\t\t\t\t\t\t\tR.suspend(() => go(child, Place.at(at, \"conditions\", index))),\n\t\t\t\t\t\t),\n\t\t\t\t\t\tArr.flatten,\n\t\t\t\t\t)\n\t\t\t\tcase \"Not\":\n\t\t\t\t\treturn R.suspend(() => go(node.condition, Place.at(at, \"condition\")))\n\t\t\t\tdefault:\n\t\t\t\t\treturn R.succeed(leaf(node, at))\n\t\t\t}\n\t\t}\n\t\treturn go(root, rootAt)\n\t})\n\nconst hasAction = (\n\tdescriptor: Resource.Descriptor,\n\tactions: ReadonlyArray<string>,\n): boolean =>\n\tactions.includes(\"*\") ||\n\tArr.some(actions, (action) => descriptor.actions.includes(action))\n\n/**\n * Checks one rule against a domain. It gives one issue for each problem, up\n * to the most issues of the binding. `at` is the place of the rule, and its\n * full path is made only for an issue that the binding keeps: a rule with\n * very many problems deep in a tree does not make a full path for each one.\n */\nexport const ruleIssues = (\n\tdomain: Domain.Any,\n\trule: RuleData,\n\tat: At,\n\tmemo: Memo = makeMemo(domain.limits),\n): ReadonlyArray<Issue> => {\n\tconst here = (issues: ReadonlyArray<Issue>) => under(memo, at, issues)\n\tconst all = rule.resources.includes(\"*\")\n\t// Each kind is checked one time, also when the list names it again. The\n\t// names come from a policy, so a native `Set` finds the copies:\n\t// `Arr.dedupe` puts the names in groups by the hash of Effect, which has\n\t// no random seed, and very many names with one hash make one long group.\n\tconst names = Array.from(\n\t\tnew Set(Arr.filter(rule.resources, (kind) => kind !== \"*\")),\n\t)\n\tconst kinds = Array.from(HashMap.keys(domain.resources))\n\tconst named = Arr.flatMap(names, (kind) =>\n\t\tOption.toArray(HashMap.get(domain.resources, kind)),\n\t)\n\tconst known = all ? Array.from(HashMap.values(domain.resources)) : named\n\t// The rule never runs on a kind without one of its actions, so the types\n\t// of its tests are checked only on the kinds with one.\n\tconst applicable = Arr.filter(named, (descriptor) =>\n\t\thasAction(descriptor, rule.actions),\n\t)\n\tconst context: Context = {\n\t\tdomain,\n\t\tmemo,\n\t\tall,\n\t\tnamed,\n\t\tapplicable,\n\t\tpaths: all\n\t\t\t? Arr.filter(known, (descriptor) => hasAction(descriptor, rule.actions))\n\t\t\t: applicable,\n\t}\n\t// A native `Set` finds copies about ten times as fast as `Arr.dedupe`.\n\tconst actions = Array.from(\n\t\tnew Set(Arr.flatMap(known, (descriptor) => descriptor.actions)),\n\t)\n\tconst fieldIssues = Arr.flatMap(rule.fields ?? [], (field, index) =>\n\t\there(\n\t\t\tfield.includes(\"*\")\n\t\t\t\t? patternIssues(memo, known, field, [\"fields\", index])\n\t\t\t\t: fieldIssue(context, field, [\"fields\", index]),\n\t\t),\n\t)\n\tconst conditionNodes = memo.limits.conditionNodes\n\tconst whenIssues =\n\t\trule.when === undefined\n\t\t\t? []\n\t\t\t: !Nodes.isNode(rule.when)\n\t\t\t\t? here([notWhole([\"when\"])])\n\t\t\t\t: // The constructors of the nodes check the ceiling, so the binding\n\t\t\t\t\t// checks the limit of the domain.\n\t\t\t\t\trule.when.weight > conditionNodes\n\t\t\t\t\t? here([\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tpath: [\"when\"],\n\t\t\t\t\t\t\t\tissue: `A condition has ${String(rule.when.weight)} nodes and values. The most is ${String(conditionNodes)}.`,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t])\n\t\t\t\t\t: conditionIssues(context, rule.when, Place.at(at, \"when\"), new Set())\n\t// The work is known only after every check of the rule. Past the limit, a\n\t// check gives no issue, so this issue must come, or the policy would load\n\t// with checks left out.\n\tconst work = memo.work.steps > memo.limits.searchSteps && !memo.work.noted\n\tmemo.work.noted ||= work\n\treturn [\n\t\t...Arr.flatMap(\n\t\t\tArr.filter(names, (kind) => !HashMap.has(domain.resources, kind)),\n\t\t\t(kind: string) =>\n\t\t\t\there([\n\t\t\t\t\t{\n\t\t\t\t\t\tpath: [\"resources\"],\n\t\t\t\t\t\tissue: suggestIn(\n\t\t\t\t\t\t\tmemo,\n\t\t\t\t\t\t\t`There is no resource with the kind \"${kind}\"`,\n\t\t\t\t\t\t\tkind,\n\t\t\t\t\t\t\t\"~kinds\",\n\t\t\t\t\t\t\t() => kinds,\n\t\t\t\t\t\t),\n\t\t\t\t\t},\n\t\t\t\t]),\n\t\t),\n\t\t...Arr.flatMap(\n\t\t\tArr.filter(\n\t\t\t\trule.actions,\n\t\t\t\t(action) => action !== \"*\" && !actions.includes(action),\n\t\t\t),\n\t\t\t(action: string) =>\n\t\t\t\there([\n\t\t\t\t\t{\n\t\t\t\t\t\tpath: [\"actions\"],\n\t\t\t\t\t\tissue: suggestIn(\n\t\t\t\t\t\t\tmemo,\n\t\t\t\t\t\t\t`The action \"${action}\" is not an action of these resources`,\n\t\t\t\t\t\t\taction,\n\t\t\t\t\t\t\t`~actions\\u0000${names.join(\",\")}`,\n\t\t\t\t\t\t\t() => actions,\n\t\t\t\t\t\t),\n\t\t\t\t\t},\n\t\t\t\t]),\n\t\t),\n\t\t...fieldIssues,\n\t\t...whenIssues,\n\t\t// The issue of the work is not one of the most issues of the binding,\n\t\t// so a policy past the limit always says why.\n\t\t...(work\n\t\t\t? [\n\t\t\t\t\t{\n\t\t\t\t\t\tpath: Place.pathOf(at),\n\t\t\t\t\t\tissue: `The policy takes more than ${String(memo.limits.searchSteps)} steps to check: its field patterns, and the fields and values of its tests on each resource. The most is ${String(memo.limits.searchSteps)}.`,\n\t\t\t\t\t},\n\t\t\t\t]\n\t\t\t: []),\n\t]\n}\n\n/** The JSON form of a policy tree, or a policy tree, as the binding reads it. */\ntype TreeNode =\n\t| { readonly _tag: \"Rules\"; readonly rules: ReadonlyArray<RuleData> }\n\t| {\n\t\t\treadonly _tag: \"All\" | \"Any\" | \"FirstOf\"\n\t\t\treadonly policies: ReadonlyArray<TreeNode>\n\t }\n\t| {\n\t\t\treadonly _tag: \"Ceiling\"\n\t\t\treadonly cap: TreeNode\n\t\t\treadonly policy: TreeNode\n\t }\n\n/** Checks each rule of a policy tree against a domain. */\nexport const treeIssues = (\n\tdomain: Domain.Any,\n\troot: TreeNode,\n): ReadonlyArray<Issue> => {\n\t// The rules come out of the tree first, and each one is checked after.\n\t// A check of a condition goes down its levels, so a check inside the walk\n\t// of the tree would go down the levels of both. The place of each rule\n\t// shares the path of its parent. The tree may be the JSON form, with no\n\t// depth that a node knows, so the walk is a walk of the trampoline for an\n\t// input of an unknown depth.\n\t// The rules go into one list, in order: a walk that joined the lists of\n\t// the children at each node would copy the list of a deep tree at each\n\t// level. The list belongs to this walk.\n\tconst found: Array<readonly [RuleData, Place.Place]> = []\n\tconst rulesOf = Trampoline.adaptive<\n\t\t{ readonly node: TreeNode; readonly at: At },\n\t\tundefined\n\t>((R, child) => ({ node, at }, level) => {\n\t\tconst below = (\n\t\t\tparts: ReadonlyArray<{ readonly node: TreeNode; readonly at: At }>,\n\t\t) =>\n\t\t\tR.map(\n\t\t\t\tR.forEach(parts, (part) => child(part, level + 1)),\n\t\t\t\t() => undefined,\n\t\t\t)\n\t\tswitch (node._tag) {\n\t\t\tcase \"Rules\":\n\t\t\t\tArr.forEach(node.rules, (rule, index) => {\n\t\t\t\t\tfound.push([rule, Place.at(at, \"rules\", index)])\n\t\t\t\t})\n\t\t\t\treturn R.succeed(undefined)\n\t\t\tcase \"All\":\n\t\t\tcase \"Any\":\n\t\t\tcase \"FirstOf\":\n\t\t\t\treturn below(\n\t\t\t\t\tArr.map(node.policies, (inner, index) => ({\n\t\t\t\t\t\tnode: inner,\n\t\t\t\t\t\tat: Place.at(at, \"policies\", index),\n\t\t\t\t\t})),\n\t\t\t\t)\n\t\t\tcase \"Ceiling\":\n\t\t\t\treturn below([\n\t\t\t\t\t{ node: node.cap, at: Place.at(at, \"cap\") },\n\t\t\t\t\t{ node: node.policy, at: Place.at(at, \"policy\") },\n\t\t\t\t])\n\t\t}\n\t})\n\trulesOf({ node: root, at: undefined })\n\tconst memo = makeMemo(domain.limits)\n\treturn Arr.flatMap(found, ([rule, place]) =>\n\t\truleIssues(domain, rule, place, memo),\n\t)\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"bind.js","names":["Catalog.accepts","Catalog.fieldsOf","Catalog.lookup","Patterns.parse","Patterns.fitsCatalog","Catalog.anyKinds","Place.pathOf","Trampoline.run","Place.at","at","Nodes.isNode","Trampoline.adaptive"],"sources":["../../src/internal/bind.ts"],"sourcesContent":["/**\n * The binding of rules to a domain. It is the one check of a rule, for a rule\n * from the builder, from `Policy.rule`, and from JSON alike. It reads the\n * catalogs of the resources and of the principal, and it gives one issue for\n * each problem, with the path of the problem and, for a name with a mistake,\n * the name that it may mean.\n */\nimport * as Arr from \"effect/Array\"\nimport * as HashMap from \"effect/HashMap\"\nimport * as Option from \"effect/Option\"\nimport * as Result from \"effect/Result\"\nimport type * as Domain from \"../Domain.ts\"\nimport type * as Resource from \"../Resource.ts\"\nimport * as Catalog from \"./catalog.ts\"\nimport * as Limits from \"./limits.ts\"\nimport * as Nodes from \"./nodes.ts\"\nimport * as Patterns from \"./patterns.ts\"\nimport * as Place from \"./place.ts\"\nimport { withSuggestion } from \"./suggest.ts\"\nimport * as Trampoline from \"./trampoline.ts\"\n\n/** One problem of a rule or a policy, with its path. */\nexport interface Issue {\n\treadonly path: ReadonlyArray<PropertyKey>\n\treadonly issue: string\n}\n\n/** The data of a rule that the binding reads. */\nexport interface RuleData {\n\treadonly actions: ReadonlyArray<string>\n\treadonly resources: ReadonlyArray<string>\n\treadonly when?: unknown\n\treadonly fields?: ReadonlyArray<string>\n}\n\n/**\n * The memo of one binding, for all its rules. A policy may name one wrong\n * field or one pattern in very many places, so the work on each is done one\n * time. It goes away when the binding ends.\n */\ninterface Memo {\n\t/**\n\t * The suggestion for each name, by the place where it was looked for. The\n\t * names come from a policy, so the map is native, as `fits` is.\n\t */\n\treadonly suggestions: Map<string, string>\n\t/**\n\t * If a pattern matches a field of a kind, by the kind and the pattern. The\n\t * patterns come from a policy, so the map is native: the hash of a string\n\t * in Effect has no random seed, and a policy could hold very many\n\t * patterns with one hash. The engine hashes a string with a random seed.\n\t */\n\treadonly fits: Map<string, boolean>\n\t/** The count of the issues of conditions so far. It belongs to one binding. */\n\treadonly issues: { count: number; noted: boolean }\n\t/** The steps of the checks of patterns so far. It belongs to one binding. */\n\treadonly work: { steps: number; noted: boolean }\n\t/** The limits of the domain. */\n\treadonly limits: Limits.Limits\n}\n\nconst makeMemo = (limits: Limits.Limits): Memo => ({\n\tsuggestions: new Map(),\n\tfits: new Map(),\n\tissues: { count: 0, noted: false },\n\twork: { steps: 0, noted: false },\n\tlimits,\n})\n\n/**\n * The most names that one binding makes a suggestion for. A suggestion\n * compares the name with every name of a catalog, so a policy with very many\n * wrong names would take a long time.\n */\nconst maxSuggestions = 32\n\n/** Adds a suggestion to a message, with the memo of the binding. */\nconst suggestIn = (\n\tmemo: Memo,\n\tmessage: string,\n\tname: string,\n\tscope: string,\n\tnames: () => ReadonlyArray<string>,\n): string => {\n\tconst key = `${scope}\\u0000${name}`\n\tconst known = memo.suggestions.get(key)\n\tif (known !== undefined) {\n\t\treturn known\n\t}\n\tif (memo.suggestions.size >= maxSuggestions) {\n\t\treturn message\n\t}\n\tconst made = withSuggestion(message, name, names())\n\tmemo.suggestions.set(key, made)\n\treturn made\n}\n\n/** The kinds that a rule may run on, from the view of its checks. */\ninterface Context {\n\treadonly domain: Domain.Any\n\treadonly memo: Memo\n\t/** The rule is for every resource (`\"*\"`). */\n\treadonly all: boolean\n\t/** The resources that the rule names and that the domain has. */\n\treadonly named: ReadonlyArray<Resource.Descriptor>\n\t/** The named resources that have one of the actions of the rule. */\n\treadonly applicable: ReadonlyArray<Resource.Descriptor>\n\t/**\n\t * The resources whose paths a path test may read: every resource with one\n\t * of the actions, for a rule for every resource.\n\t */\n\treadonly paths: ReadonlyArray<Resource.Descriptor>\n}\n\ntype Operand = Nodes.Operand\n\n/**\n * Tests if a field may hold a value of the type of a fixed value. A test with\n * `null` is a test that the field is missing, so `null` always fits. A number\n * fits any field of numbers: a test of an integer field with `1.5` or\n * `Infinity` has a meaning, as `lt` shows.\n */\nconst fitsLiteral = (kinds: Catalog.Kinds, value: unknown): boolean =>\n\tvalue === null ||\n\t(typeof value === \"number\" && kinds.number !== \"none\") ||\n\tCatalog.accepts(kinds, value)\n\n/**\n * Tests if two fields may hold one scalar. A principal field that is `null`\n * or that holds an object is a missing value, and a test with a missing value\n * is false, so only scalars count.\n */\nconst meet = (left: Catalog.Kinds, right: Catalog.Kinds): boolean =>\n\t(left.string && right.string) ||\n\t(left.boolean && right.boolean) ||\n\t(left.number !== \"none\" && right.number !== \"none\") ||\n\tArr.some(left.literals, (value) => Catalog.accepts(right, value)) ||\n\tArr.some(right.literals, (value) => Catalog.accepts(left, value))\n\nconst hasStrings = (kinds: Catalog.Kinds): boolean =>\n\tkinds.string || Arr.some(kinds.literals, (value) => typeof value === \"string\")\n\nconst hasNumbers = (kinds: Catalog.Kinds): boolean =>\n\tkinds.number !== \"none\" ||\n\tArr.some(kinds.literals, (value) => typeof value === \"number\")\n\n/** Tests if a field may hold a value with an order: a number or a string. */\nconst isOrdered = (kinds: Catalog.Kinds): boolean =>\n\thasStrings(kinds) || hasNumbers(kinds)\n\n/** Tests if two fields may hold values of one ordered type. */\nconst orderedMeet = (left: Catalog.Kinds, right: Catalog.Kinds): boolean =>\n\t(hasStrings(left) && hasStrings(right)) ||\n\t(hasNumbers(left) && hasNumbers(right))\n\n/**\n * The texts of the fields of a catalog that have only names, for\n * suggestions, with the limits of the domain. The domain checked the size of\n * the list when it was made.\n */\nconst namesOf = (\n\tcatalog: Catalog.Catalog,\n\tlimits: Limits.Limits,\n): ReadonlyArray<string> =>\n\tArr.map(\n\t\tArr.filter(\n\t\t\tCatalog.fieldsOf(catalog, limits.fieldPathParts, limits.catalogFields),\n\t\t\t(field) =>\n\t\t\t\tArr.every(field.path, (segment) => typeof segment === \"string\"),\n\t\t),\n\t\t(field) => field.text,\n\t)\n\n/** The issue of a field that is not a field of every resource of the rule. */\nconst fieldIssue = (\n\tcontext: Context,\n\tfield: string,\n\tat: ReadonlyArray<PropertyKey>,\n): ReadonlyArray<Issue> => {\n\tconst parts = context.memo.limits.fieldPathParts\n\tif (field.split(\".\", parts + 1).length > parts) {\n\t\treturn [\n\t\t\t{\n\t\t\t\tpath: at,\n\t\t\t\tissue: `The field path \"${field}\" has more than ${String(parts)} parts. The most is ${String(parts)}.`,\n\t\t\t},\n\t\t]\n\t}\n\tconst message = `The field \"${field}\" is not a field of every resource of the rule`\n\tif (context.all || context.named.length === 0) {\n\t\treturn [{ path: at, issue: message }]\n\t}\n\tconst without = Arr.filter(\n\t\tcontext.named,\n\t\t(descriptor) =>\n\t\t\tCatalog.lookup(descriptor.catalog, field, parts) === undefined,\n\t)\n\treturn without.length === 0\n\t\t? []\n\t\t: [\n\t\t\t\t{\n\t\t\t\t\tpath: at,\n\t\t\t\t\tissue: suggestIn(context.memo, message, field, without[0]!.kind, () =>\n\t\t\t\t\t\tnamesOf(without[0]!.catalog, context.memo.limits),\n\t\t\t\t\t),\n\t\t\t\t},\n\t\t\t]\n}\n\n/**\n * The issues of a field that a condition reads: a field that is not a field\n * of every resource of the rule, or a field that is not plain data in one of\n * them. A read of a value that is not plain data, as a getter of a class, is\n * a defect in a decision. A residual may leave the read out, so the residual\n * would say \"yes\" where the decision fails. So the binding does not take\n * such a read: see `Catalog.Field.plain`.\n */\nconst readIssues = (\n\tcontext: Context,\n\tfield: string,\n\tat: ReadonlyArray<PropertyKey>,\n): ReadonlyArray<Issue> => {\n\tconst exists = fieldIssue(context, field, at)\n\tif (exists.length > 0) {\n\t\treturn exists\n\t}\n\treturn Arr.some(\n\t\tcontext.named,\n\t\t(descriptor) =>\n\t\t\t!(\n\t\t\t\tCatalog.lookup(\n\t\t\t\t\tdescriptor.catalog,\n\t\t\t\t\tfield,\n\t\t\t\t\tcontext.memo.limits.fieldPathParts,\n\t\t\t\t)?.plain ?? true\n\t\t\t),\n\t)\n\t\t? [\n\t\t\t\t{\n\t\t\t\t\tpath: at,\n\t\t\t\t\tissue: `A condition may not read the field \"${field}\". A value above it may be an object that does not have it as a data field, as a class with a getter, a date or a list. Use a predicate for it.`,\n\t\t\t\t},\n\t\t\t]\n\t\t: []\n}\n\n/**\n * The issues of a field pattern: a pattern that does not parse, or that\n * matches no field of the resources of the rule, as a pattern with a mistake\n * in a name does.\n */\nconst patternIssues = (\n\tmemo: Memo,\n\tknown: ReadonlyArray<Resource.Descriptor>,\n\tpattern: string,\n\tat: ReadonlyArray<PropertyKey>,\n): ReadonlyArray<Issue> =>\n\tResult.match(Patterns.parse(pattern, memo.limits.patternParts), {\n\t\tonFailure: (issue) => [{ path: at, issue }],\n\t\tonSuccess: (parsed) => {\n\t\t\t// A rule with no known resource has an issue for that already. The\n\t\t\t// pattern is checked on every kind, not only up to the first that it\n\t\t\t// fits: a decision on a kind does this check, so the limit of the\n\t\t\t// work below is a limit of the work of a decision too.\n\t\t\tconst fits =\n\t\t\t\tknown.length === 0 ||\n\t\t\t\tArr.reduce(known, false, (before, descriptor) => {\n\t\t\t\t\tconst key = `${descriptor.kind}\\u0000${pattern}`\n\t\t\t\t\treturn (\n\t\t\t\t\t\tOption.getOrElse(Option.fromUndefinedOr(memo.fits.get(key)), () => {\n\t\t\t\t\t\t\t// Each check of a pattern reads the steps of a catalog. The\n\t\t\t\t\t\t\t// work of one binding has a limit, so very many patterns stop\n\t\t\t\t\t\t\t// with one issue, and the policy does not load.\n\t\t\t\t\t\t\tmemo.work.steps +=\n\t\t\t\t\t\t\t\tMath.max(descriptor.catalog.edges, 1) *\n\t\t\t\t\t\t\t\tMath.max(parsed.length, 1)\n\t\t\t\t\t\t\tif (memo.work.steps > memo.limits.searchSteps) {\n\t\t\t\t\t\t\t\treturn true\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tconst found = Patterns.fitsCatalog(\n\t\t\t\t\t\t\t\tparsed,\n\t\t\t\t\t\t\t\tdescriptor.catalog,\n\t\t\t\t\t\t\t\tmemo.limits.fieldPathParts,\n\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\tmemo.fits.set(key, found)\n\t\t\t\t\t\t\treturn found\n\t\t\t\t\t\t}) || before\n\t\t\t\t\t)\n\t\t\t\t})\n\t\t\t// Past the limit of the work, `ruleIssues` gives one issue for it.\n\t\t\treturn fits || memo.work.steps > memo.limits.searchSteps\n\t\t\t\t? []\n\t\t\t\t: [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tpath: at,\n\t\t\t\t\t\t\tissue: `The field pattern \"${pattern}\" matches no field of the resources of the rule`,\n\t\t\t\t\t\t},\n\t\t\t\t\t]\n\t\t},\n\t})\n\n/** The fields of the resources that a test of the rule may read. */\nconst fieldsAt = (\n\tcontext: Context,\n\tfield: string,\n): ReadonlyArray<Catalog.Field> =>\n\tArr.flatMap(context.applicable, (descriptor) => {\n\t\tconst found = Catalog.lookup(\n\t\t\tdescriptor.catalog,\n\t\t\tfield,\n\t\t\tcontext.memo.limits.fieldPathParts,\n\t\t)\n\t\treturn found === undefined ? [] : [found]\n\t})\n\n/** The field of the principal that an operand names, or the issue when there is none. */\nconst principalField = (\n\tcontext: Context,\n\tref: string,\n\tat: ReadonlyArray<PropertyKey>,\n): Catalog.Field | Issue => {\n\tconst found = Catalog.lookup(\n\t\tcontext.domain.principalCatalog,\n\t\tref,\n\t\tcontext.memo.limits.fieldPathParts,\n\t)\n\t// A read of the principal that is not plain data is a defect, as a read\n\t// of a resource is: see `readIssues`.\n\tif (found !== undefined && !found.plain) {\n\t\treturn {\n\t\t\tpath: at,\n\t\t\tissue: `A condition may not read the field \"${ref}\" of the principal. A value above it may be an object that does not have it as a data field, as a class with a getter, a date or a list. Use a predicate for it.`,\n\t\t}\n\t}\n\treturn found !== undefined\n\t\t? found\n\t\t: {\n\t\t\t\tpath: at,\n\t\t\t\tissue: suggestIn(\n\t\t\t\t\tcontext.memo,\n\t\t\t\t\t`The principal has no field \"${ref}\"`,\n\t\t\t\t\tref,\n\t\t\t\t\t\"~principal\",\n\t\t\t\t\t() => namesOf(context.domain.principalCatalog, context.memo.limits),\n\t\t\t\t),\n\t\t\t}\n}\n\nconst isIssue = (value: Catalog.Field | Issue): value is Issue =>\n\t\"issue\" in value\n\n/**\n * The fields that a test reads, with one field for each distinct kind of\n * value. A rule for very many kinds may have one field type in all of them,\n * so each value of the test is checked one time for each distinct kind, not\n * one time for each resource. The kinds are found by their text in a native\n * `Map`, which belongs to this call.\n */\nconst distinctFields = (\n\tcontext: Context,\n\tfield: string,\n): ReadonlyArray<Catalog.Field> =>\n\tArray.from(\n\t\tnew Map(\n\t\t\tArr.map(\n\t\t\t\tfieldsAt(context, field),\n\t\t\t\t(found) => [JSON.stringify(found.kinds), found] as const,\n\t\t\t),\n\t\t).values(),\n\t)\n\n/**\n * Counts the work of the checks of the values of a test, as the work of the\n * field patterns counts. Past the limit, the values are not checked, and\n * `ruleIssues` gives one issue for the work.\n */\nconst spendChecks = (memo: Memo, values: number, fields: number): boolean => {\n\tmemo.work.steps += values * Math.max(fields, 1)\n\treturn memo.work.steps <= memo.limits.searchSteps\n}\n\n/** The issues of one operand of a test of a field, with its distinct fields. */\nconst operandIssues = (\n\tcontext: Context,\n\tfield: string,\n\tfields: ReadonlyArray<Catalog.Field>,\n\tordered: boolean,\n\toperand: Operand,\n\tat: ReadonlyArray<PropertyKey>,\n): ReadonlyArray<Issue> => {\n\tif (operand._tag === \"Literal\") {\n\t\tconst value = operand.value\n\t\tif (ordered && typeof value !== \"string\" && typeof value !== \"number\") {\n\t\t\treturn [\n\t\t\t\t{\n\t\t\t\t\tpath: at,\n\t\t\t\t\tissue: `The field \"${field}\" has an ordered test with a value that has no order`,\n\t\t\t\t},\n\t\t\t]\n\t\t}\n\t\treturn [\n\t\t\t...(Arr.some(fields, (found) => !fitsLiteral(found.kinds, value))\n\t\t\t\t? [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tpath: at,\n\t\t\t\t\t\t\tissue: `The field \"${field}\" can not hold the value ${JSON.stringify(value)}`,\n\t\t\t\t\t\t},\n\t\t\t\t\t]\n\t\t\t\t: []),\n\t\t\t...(ordered && Arr.some(fields, (found) => !isOrdered(found.kinds))\n\t\t\t\t? [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tpath: at,\n\t\t\t\t\t\t\tissue: `The field \"${field}\" has an ordered test with a value that has no order`,\n\t\t\t\t\t\t},\n\t\t\t\t\t]\n\t\t\t\t: []),\n\t\t]\n\t}\n\tconst principal = principalField(context, operand.path, at)\n\tif (isIssue(principal)) {\n\t\treturn [principal]\n\t}\n\treturn [\n\t\t...(Arr.some(fields, (found) => !meet(found.kinds, principal.kinds))\n\t\t\t? [\n\t\t\t\t\t{\n\t\t\t\t\t\tpath: at,\n\t\t\t\t\t\tissue: `The field \"${field}\" can not hold a value of the principal field \"${operand.path}\"`,\n\t\t\t\t\t},\n\t\t\t\t]\n\t\t\t: []),\n\t\t...(ordered &&\n\t\tArr.some(fields, (found) => !orderedMeet(found.kinds, principal.kinds))\n\t\t\t? [\n\t\t\t\t\t{\n\t\t\t\t\t\tpath: at,\n\t\t\t\t\t\tissue: `The field \"${field}\" has an ordered test with a value that has no order`,\n\t\t\t\t\t},\n\t\t\t\t]\n\t\t\t: []),\n\t]\n}\n\n/**\n * Tests if the path of a resource may start with a prefix: it is as long as\n * the prefix, and each fixed string of the prefix is the string of the path\n * at its place, when the path has a string there.\n */\nconst mayStartWith = (\n\tdescriptor: Resource.Descriptor,\n\tstrings: ReadonlyArray<string | null>,\n): boolean =>\n\t(descriptor.path?.length ?? 0) >= strings.length &&\n\tArr.every(strings, (string, index) => {\n\t\tconst segment = descriptor.path?.[index]\n\t\treturn string === null || typeof segment !== \"string\" || segment === string\n\t})\n\n/** The kinds of the part of the path of a resource at a place. */\nconst pathPartKinds = (\n\tdescriptor: Resource.Descriptor,\n\tindex: number,\n): Catalog.Kinds | string | undefined => {\n\tconst segment = descriptor.path?.[index]\n\treturn segment === undefined || typeof segment === \"string\"\n\t\t? segment\n\t\t: (Catalog.lookup(descriptor.catalog, segment.field)?.kinds ??\n\t\t\t\tCatalog.anyKinds)\n}\n\n/** The issues of the prefix of a path test. */\nconst underIssues = (\n\tcontext: Context,\n\tprefix: ReadonlyArray<Operand>,\n\tat: ReadonlyArray<PropertyKey>,\n): ReadonlyArray<Issue> => {\n\tconst strings = Arr.map(prefix, (operand) =>\n\t\toperand._tag === \"Literal\" && typeof operand.value === \"string\"\n\t\t\t? operand.value\n\t\t\t: null,\n\t)\n\t// The test can match only a path that starts with the fixed strings of\n\t// the prefix, so the other resources are left out.\n\tconst candidates = Arr.filter(context.paths, (descriptor) =>\n\t\tmayStartWith(descriptor, strings),\n\t)\n\treturn Arr.flatMap(prefix, (operand, index): ReadonlyArray<Issue> => {\n\t\tconst place = [...at, \"prefix\", index]\n\t\tif (operand._tag === \"Literal\") {\n\t\t\treturn Arr.some(candidates, (descriptor) => {\n\t\t\t\tconst kinds = pathPartKinds(descriptor, index)\n\t\t\t\treturn typeof kinds === \"object\" && !fitsLiteral(kinds, operand.value)\n\t\t\t})\n\t\t\t\t? [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tpath: place,\n\t\t\t\t\t\t\tissue: `The path part ${String(index)} can not hold the value ${JSON.stringify(operand.value)}`,\n\t\t\t\t\t\t},\n\t\t\t\t\t]\n\t\t\t\t: []\n\t\t}\n\t\tconst principal = principalField(context, operand.path, place)\n\t\tif (isIssue(principal)) {\n\t\t\treturn [principal]\n\t\t}\n\t\treturn Arr.some(candidates, (descriptor) => {\n\t\t\tconst kinds = pathPartKinds(descriptor, index)\n\t\t\treturn kinds === undefined\n\t\t\t\t? true\n\t\t\t\t: typeof kinds === \"string\"\n\t\t\t\t\t? !Catalog.accepts(principal.kinds, kinds)\n\t\t\t\t\t: !meet(kinds, principal.kinds)\n\t\t})\n\t\t\t? [\n\t\t\t\t\t{\n\t\t\t\t\t\tpath: place,\n\t\t\t\t\t\tissue: `The principal field \"${operand.path}\" can not hold a part of the path of the resource`,\n\t\t\t\t\t},\n\t\t\t\t]\n\t\t\t: []\n\t})\n}\n\nconst notWhole = (at: ReadonlyArray<PropertyKey>): Issue => ({\n\tpath: at,\n\tissue: \"The condition is not a whole condition tree\",\n})\n\n/**\n * The most issues that the walk of the conditions of one binding gives. A\n * policy may have very many wrong tests, and each issue keeps its path.\n */\nconst maxIssues = 100\n\n/**\n * The place of a part of a policy, which a walk turns into a full path only\n * for an issue: see `Place`.\n */\ntype At = Place.Place | undefined\n\n/**\n * Puts the path of a node before the paths of its issues. A walk keeps no\n * path for a node with no issue, so a deep and wide condition costs little.\n */\nconst under = (\n\tmemo: Memo,\n\tat: At,\n\tissues: ReadonlyArray<Issue>,\n): ReadonlyArray<Issue> => {\n\tif (issues.length === 0) {\n\t\treturn issues\n\t}\n\tconst room = maxIssues - memo.issues.count\n\tmemo.issues.count += issues.length\n\tconst kept = Arr.map(Arr.take(issues, Math.max(room, 0)), (issue) => ({\n\t\tpath: [...Place.pathOf(at), ...issue.path],\n\t\tissue: issue.issue,\n\t}))\n\tconst first = memo.issues.count > maxIssues && !memo.issues.noted\n\tmemo.issues.noted ||= first\n\treturn first\n\t\t? [\n\t\t\t\t...kept,\n\t\t\t\t{\n\t\t\t\t\tpath: [],\n\t\t\t\t\tissue: `The policy has more problems. The first ${String(maxIssues)} are here.`,\n\t\t\t\t},\n\t\t\t]\n\t\t: kept\n}\n\n/**\n * The issues of a condition. The constructors of the nodes checked the shape\n * of each node, so this checks the names and the types against the domain. A\n * node that the condition uses in many places is checked one time. The nodes\n * it has seen are in a native `Set`, because it must find a node by the node\n * itself: `Equal` compares nodes by structure.\n */\nconst conditionIssues = (\n\tcontext: Context,\n\troot: Nodes.Node,\n\trootAt: At,\n\tseen: Set<Nodes.Node>,\n): ReadonlyArray<Issue> =>\n\t// The walk takes the steps of a trampoline, so a deep condition does not\n\t// fill the stack.\n\tTrampoline.run(root.depth, (R) => {\n\t\t// A test looks its field up in each resource of the rule. A rule for\n\t\t// very many kinds with very many tests is very much work, so it counts.\n\t\t// Past the limit, `ruleIssues` gives one issue for it, and the policy\n\t\t// does not load.\n\t\tconst room = (values: number) =>\n\t\t\tspendChecks(context.memo, 1 + values, context.named.length)\n\t\tconst leaf = (node: Nodes.Node, at: At): ReadonlyArray<Issue> => {\n\t\t\tconst here = (issues: ReadonlyArray<Issue>) =>\n\t\t\t\tunder(context.memo, at, issues)\n\t\t\tswitch (node._tag) {\n\t\t\t\tcase \"Compare\": {\n\t\t\t\t\tif (!room(1)) {\n\t\t\t\t\t\treturn []\n\t\t\t\t\t}\n\t\t\t\t\tconst exists = readIssues(context, node.field, [\"field\"])\n\t\t\t\t\treturn here(\n\t\t\t\t\t\texists.length > 0\n\t\t\t\t\t\t\t? exists\n\t\t\t\t\t\t\t: operandIssues(\n\t\t\t\t\t\t\t\t\tcontext,\n\t\t\t\t\t\t\t\t\tnode.field,\n\t\t\t\t\t\t\t\t\tdistinctFields(context, node.field),\n\t\t\t\t\t\t\t\t\tnode.op !== \"eq\" && node.op !== \"ne\",\n\t\t\t\t\t\t\t\t\tnode.value,\n\t\t\t\t\t\t\t\t\t[\"value\"],\n\t\t\t\t\t\t\t\t),\n\t\t\t\t\t)\n\t\t\t\t}\n\t\t\t\tcase \"In\": {\n\t\t\t\t\tif (!room(0)) {\n\t\t\t\t\t\treturn []\n\t\t\t\t\t}\n\t\t\t\t\tconst exists = readIssues(context, node.field, [\"field\"])\n\t\t\t\t\tif (exists.length > 0) {\n\t\t\t\t\t\treturn here(exists)\n\t\t\t\t\t}\n\t\t\t\t\tconst fields = distinctFields(context, node.field)\n\t\t\t\t\tif (!spendChecks(context.memo, node.values.length, fields.length)) {\n\t\t\t\t\t\treturn []\n\t\t\t\t\t}\n\t\t\t\t\treturn here(\n\t\t\t\t\t\tArr.flatMap(node.values, (operand, index) =>\n\t\t\t\t\t\t\toperandIssues(context, node.field, fields, false, operand, [\n\t\t\t\t\t\t\t\t\"values\",\n\t\t\t\t\t\t\t\tindex,\n\t\t\t\t\t\t\t]),\n\t\t\t\t\t\t),\n\t\t\t\t\t)\n\t\t\t\t}\n\t\t\t\tcase \"Exists\":\n\t\t\t\t\treturn room(0) ? here(readIssues(context, node.field, [\"field\"])) : []\n\t\t\t\tcase \"Under\":\n\t\t\t\t\t// The check reads the path of each resource of the rule for each\n\t\t\t\t\t// part of the prefix, so it counts that work first.\n\t\t\t\t\treturn spendChecks(\n\t\t\t\t\t\tcontext.memo,\n\t\t\t\t\t\t1 + node.prefix.length,\n\t\t\t\t\t\tcontext.paths.length,\n\t\t\t\t\t)\n\t\t\t\t\t\t? here(underIssues(context, node.prefix, []))\n\t\t\t\t\t\t: []\n\t\t\t\tdefault:\n\t\t\t\t\treturn []\n\t\t\t}\n\t\t}\n\t\tconst go = (\n\t\t\tnode: Nodes.Node,\n\t\t\tat: At,\n\t\t): Trampoline.Step<ReadonlyArray<Issue>> => {\n\t\t\tif (seen.has(node)) {\n\t\t\t\treturn R.succeed([])\n\t\t\t}\n\t\t\tseen.add(node)\n\t\t\tswitch (node._tag) {\n\t\t\t\tcase \"And\":\n\t\t\t\tcase \"Or\":\n\t\t\t\t\treturn R.map(\n\t\t\t\t\t\tR.forEach(node.conditions, (child, index) =>\n\t\t\t\t\t\t\tR.suspend(() => go(child, Place.at(at, \"conditions\", index))),\n\t\t\t\t\t\t),\n\t\t\t\t\t\tArr.flatten,\n\t\t\t\t\t)\n\t\t\t\tcase \"Not\":\n\t\t\t\t\treturn R.suspend(() => go(node.condition, Place.at(at, \"condition\")))\n\t\t\t\tdefault:\n\t\t\t\t\treturn R.succeed(leaf(node, at))\n\t\t\t}\n\t\t}\n\t\treturn go(root, rootAt)\n\t})\n\nconst hasAction = (\n\tdescriptor: Resource.Descriptor,\n\tactions: ReadonlyArray<string>,\n): boolean =>\n\tactions.includes(\"*\") ||\n\tArr.some(actions, (action) => descriptor.actions.includes(action))\n\n/**\n * Checks one rule against a domain. It gives one issue for each problem, up\n * to the most issues of the binding. `at` is the place of the rule, and its\n * full path is made only for an issue that the binding keeps: a rule with\n * very many problems deep in a tree does not make a full path for each one.\n */\nexport const ruleIssues = (\n\tdomain: Domain.Any,\n\trule: RuleData,\n\tat: At,\n\tmemo: Memo = makeMemo(domain.limits),\n): ReadonlyArray<Issue> => {\n\tconst here = (issues: ReadonlyArray<Issue>) => under(memo, at, issues)\n\tconst all = rule.resources.includes(\"*\")\n\t// Each kind is checked one time, also when the list names it again. The\n\t// names come from a policy, so a native `Set` finds the copies:\n\t// `Arr.dedupe` puts the names in groups by the hash of Effect, which has\n\t// no random seed, and very many names with one hash make one long group.\n\tconst names = Array.from(\n\t\tnew Set(Arr.filter(rule.resources, (kind) => kind !== \"*\")),\n\t)\n\tconst kinds = Array.from(HashMap.keys(domain.resources))\n\tconst named = Arr.flatMap(names, (kind) =>\n\t\tOption.toArray(HashMap.get(domain.resources, kind)),\n\t)\n\tconst known = all ? Array.from(HashMap.values(domain.resources)) : named\n\t// The rule never runs on a kind without one of its actions, so the types\n\t// of its tests are checked only on the kinds with one.\n\tconst applicable = Arr.filter(named, (descriptor) =>\n\t\thasAction(descriptor, rule.actions),\n\t)\n\tconst context: Context = {\n\t\tdomain,\n\t\tmemo,\n\t\tall,\n\t\tnamed,\n\t\tapplicable,\n\t\tpaths: all\n\t\t\t? Arr.filter(known, (descriptor) => hasAction(descriptor, rule.actions))\n\t\t\t: applicable,\n\t}\n\t// A native `Set` finds copies about ten times as fast as `Arr.dedupe`.\n\tconst actions = Array.from(\n\t\tnew Set(Arr.flatMap(known, (descriptor) => descriptor.actions)),\n\t)\n\tconst fieldIssues = Arr.flatMap(rule.fields ?? [], (field, index) =>\n\t\there(\n\t\t\tfield.includes(\"*\")\n\t\t\t\t? patternIssues(memo, known, field, [\"fields\", index])\n\t\t\t\t: fieldIssue(context, field, [\"fields\", index]),\n\t\t),\n\t)\n\tconst conditionNodes = memo.limits.conditionNodes\n\tconst whenIssues =\n\t\trule.when === undefined\n\t\t\t? []\n\t\t\t: !Nodes.isNode(rule.when)\n\t\t\t\t? here([notWhole([\"when\"])])\n\t\t\t\t: // The constructors of the nodes check the ceiling, so the binding\n\t\t\t\t\t// checks the limit of the domain.\n\t\t\t\t\trule.when.weight > conditionNodes\n\t\t\t\t\t? here([\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tpath: [\"when\"],\n\t\t\t\t\t\t\t\tissue: `A condition has ${String(rule.when.weight)} nodes and values. The most is ${String(conditionNodes)}.`,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t])\n\t\t\t\t\t: conditionIssues(context, rule.when, Place.at(at, \"when\"), new Set())\n\t// The work is known only after every check of the rule. Past the limit, a\n\t// check gives no issue, so this issue must come, or the policy would load\n\t// with checks left out.\n\tconst work = memo.work.steps > memo.limits.searchSteps && !memo.work.noted\n\tmemo.work.noted ||= work\n\treturn [\n\t\t...Arr.flatMap(\n\t\t\tArr.filter(names, (kind) => !HashMap.has(domain.resources, kind)),\n\t\t\t(kind: string) =>\n\t\t\t\there([\n\t\t\t\t\t{\n\t\t\t\t\t\tpath: [\"resources\"],\n\t\t\t\t\t\tissue: suggestIn(\n\t\t\t\t\t\t\tmemo,\n\t\t\t\t\t\t\t`There is no resource with the kind \"${kind}\"`,\n\t\t\t\t\t\t\tkind,\n\t\t\t\t\t\t\t\"~kinds\",\n\t\t\t\t\t\t\t() => kinds,\n\t\t\t\t\t\t),\n\t\t\t\t\t},\n\t\t\t\t]),\n\t\t),\n\t\t...Arr.flatMap(\n\t\t\tArr.filter(\n\t\t\t\trule.actions,\n\t\t\t\t(action) => action !== \"*\" && !actions.includes(action),\n\t\t\t),\n\t\t\t(action: string) =>\n\t\t\t\there([\n\t\t\t\t\t{\n\t\t\t\t\t\tpath: [\"actions\"],\n\t\t\t\t\t\tissue: suggestIn(\n\t\t\t\t\t\t\tmemo,\n\t\t\t\t\t\t\t`The action \"${action}\" is not an action of these resources`,\n\t\t\t\t\t\t\taction,\n\t\t\t\t\t\t\t`~actions\\u0000${names.join(\",\")}`,\n\t\t\t\t\t\t\t() => actions,\n\t\t\t\t\t\t),\n\t\t\t\t\t},\n\t\t\t\t]),\n\t\t),\n\t\t...fieldIssues,\n\t\t...whenIssues,\n\t\t// The issue of the work is not one of the most issues of the binding,\n\t\t// so a policy past the limit always says why.\n\t\t...(work\n\t\t\t? [\n\t\t\t\t\t{\n\t\t\t\t\t\tpath: Place.pathOf(at),\n\t\t\t\t\t\tissue: `The policy takes more than ${String(memo.limits.searchSteps)} steps to check: its field patterns, and the fields and values of its tests on each resource. The most is ${String(memo.limits.searchSteps)}.`,\n\t\t\t\t\t},\n\t\t\t\t]\n\t\t\t: []),\n\t]\n}\n\n/** The JSON form of a policy tree, or a policy tree, as the binding reads it. */\ntype TreeNode =\n\t| { readonly _tag: \"Rules\"; readonly rules: ReadonlyArray<RuleData> }\n\t| {\n\t\t\treadonly _tag: \"All\" | \"Any\" | \"FirstOf\"\n\t\t\treadonly policies: ReadonlyArray<TreeNode>\n\t }\n\t| {\n\t\t\treadonly _tag: \"Ceiling\"\n\t\t\treadonly cap: TreeNode\n\t\t\treadonly policy: TreeNode\n\t }\n\n/** Checks each rule of a policy tree against a domain. */\nexport const treeIssues = (\n\tdomain: Domain.Any,\n\troot: TreeNode,\n): ReadonlyArray<Issue> => {\n\t// The rules come out of the tree first, and each one is checked after.\n\t// A check of a condition goes down its levels, so a check inside the walk\n\t// of the tree would go down the levels of both. The place of each rule\n\t// shares the path of its parent. The tree may be the JSON form, with no\n\t// depth that a node knows, so the walk is a walk of the trampoline for an\n\t// input of an unknown depth.\n\t// The rules go into one list, in order: a walk that joined the lists of\n\t// the children at each node would copy the list of a deep tree at each\n\t// level. The list belongs to this walk.\n\tconst found: Array<readonly [RuleData, Place.Place]> = []\n\tconst rulesOf = Trampoline.adaptive<\n\t\t{ readonly node: TreeNode; readonly at: At },\n\t\tundefined\n\t>((R, child) => ({ node, at }, level) => {\n\t\tconst below = (\n\t\t\tparts: ReadonlyArray<{ readonly node: TreeNode; readonly at: At }>,\n\t\t) =>\n\t\t\tR.map(\n\t\t\t\tR.forEach(parts, (part) => child(part, level + 1)),\n\t\t\t\t() => undefined,\n\t\t\t)\n\t\tswitch (node._tag) {\n\t\t\tcase \"Rules\":\n\t\t\t\tArr.forEach(node.rules, (rule, index) => {\n\t\t\t\t\tfound.push([rule, Place.at(at, \"rules\", index)])\n\t\t\t\t})\n\t\t\t\treturn R.succeed(undefined)\n\t\t\tcase \"All\":\n\t\t\tcase \"Any\":\n\t\t\tcase \"FirstOf\":\n\t\t\t\treturn below(\n\t\t\t\t\tArr.map(node.policies, (inner, index) => ({\n\t\t\t\t\t\tnode: inner,\n\t\t\t\t\t\tat: Place.at(at, \"policies\", index),\n\t\t\t\t\t})),\n\t\t\t\t)\n\t\t\tcase \"Ceiling\":\n\t\t\t\treturn below([\n\t\t\t\t\t{ node: node.cap, at: Place.at(at, \"cap\") },\n\t\t\t\t\t{ node: node.policy, at: Place.at(at, \"policy\") },\n\t\t\t\t])\n\t\t}\n\t})\n\trulesOf({ node: root, at: undefined })\n\tconst memo = makeMemo(domain.limits)\n\treturn Arr.flatMap(found, ([rule, place]) =>\n\t\truleIssues(domain, rule, place, memo),\n\t)\n}\n"],"mappings":"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"}
|
|
@@ -20,31 +20,6 @@ export interface Key {
|
|
|
20
20
|
/** The Schemas of the keys. A key must fit one of them. */
|
|
21
21
|
readonly parameters: ReadonlyArray<SchemaAST.AST>;
|
|
22
22
|
}
|
|
23
|
-
/** One part of a field path: a name, or any key of a record. */
|
|
24
|
-
export type Segment = string | Key;
|
|
25
|
-
/** One field of a catalog. */
|
|
26
|
-
export interface Field {
|
|
27
|
-
readonly path: ReadonlyArray<Segment>;
|
|
28
|
-
/** The path with dots, and `*` for a key of a record. */
|
|
29
|
-
readonly text: string;
|
|
30
|
-
readonly kinds: Kinds;
|
|
31
|
-
/**
|
|
32
|
-
* The field holds a value that a list of fields shows as one field: a
|
|
33
|
-
* scalar, a record, an array, or a struct at the last part a path may have.
|
|
34
|
-
*/
|
|
35
|
-
readonly listed: boolean;
|
|
36
|
-
/** The field may hold parts that the Schema does not name, as a record may. */
|
|
37
|
-
readonly open: boolean;
|
|
38
|
-
/**
|
|
39
|
-
* A read of the field finds only plain data. It is `false` when a value
|
|
40
|
-
* above the field may be an object that does not have it as a data field:
|
|
41
|
-
* a class without the field, which may have a getter of that name, a
|
|
42
|
-
* value such as a date or a list, or any object, as of `Schema.Unknown`.
|
|
43
|
-
* A struct of a union without the field makes it not plain too, since a
|
|
44
|
-
* value made by hand may have it with any value.
|
|
45
|
-
*/
|
|
46
|
-
readonly plain: boolean;
|
|
47
|
-
}
|
|
48
23
|
/**
|
|
49
24
|
* A place in the graph of a catalog. A path of a field leads to a place, and
|
|
50
25
|
* many paths may lead to one place, as in a recursive Schema: `next` and
|
|
@@ -76,12 +51,6 @@ export interface Branch {
|
|
|
76
51
|
}
|
|
77
52
|
/** The catalog of the fields of a Schema. */
|
|
78
53
|
export interface Catalog {
|
|
79
|
-
/**
|
|
80
|
-
* The fields that a list shows, a field before the fields below it. For a
|
|
81
|
-
* recursive Schema, the list goes into each recursive place one time on
|
|
82
|
-
* each path, so it is finite.
|
|
83
|
-
*/
|
|
84
|
-
readonly fields: ReadonlyArray<Field>;
|
|
85
54
|
/** The places, by id. The root is the place with the id 0. */
|
|
86
55
|
readonly branches: ReadonlyArray<Branch>;
|
|
87
56
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"catalog.d.ts","names":[],"sources":["../../src/internal/catalog.ts"],"mappings":";;;iBAuBiB;;WAEP;WACA;;WAEA;;WAEA;;WAEA;;WAEA,UAAU;;;iBA8EH;WACP;;WAEA,YAAY,cAAc,UAAU
|
|
1
|
+
{"version":3,"file":"catalog.d.ts","names":[],"sources":["../../src/internal/catalog.ts"],"mappings":";;;iBAuBiB;;WAEP;WACA;;WAEA;;WAEA;;WAEA;;WAEA,UAAU;;;iBA8EH;WACP;;WAEA,YAAY,cAAc,UAAU;;;;;;;;iBAoC7B;WACP;;WAEA,OAAO;;WAEP;;WAEA;WACA;WACA;;WAEA,UAAU;;;;;;;WAOV,OAAO,QAAQ;;WAEf,KAAK;WACL;;;iBAIO;;WAEP,UAAU,cAAc;;;;;WAKxB;WACA,MAAM"}
|