@nmnmcc/ability 2.1.2 → 2.1.4
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/Authorizer.d.ts +11 -11
- package/dist/Authorizer.d.ts.map +1 -1
- package/dist/Authorizer.js.map +1 -1
- package/dist/Condition.d.ts +1 -1
- package/dist/HttpApi.js +1 -1
- package/dist/Policy.d.ts +16 -6
- package/dist/Policy.d.ts.map +1 -1
- package/dist/Policy.js.map +1 -1
- package/dist/internal/algebra.js +8 -5
- package/dist/internal/algebra.js.map +1 -1
- package/dist/internal/authorizer.d.ts.map +1 -1
- package/dist/internal/authorizer.js +35 -9
- package/dist/internal/authorizer.js.map +1 -1
- package/dist/internal/bind.js +37 -27
- 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 +39 -17
- 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/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-nodes.js +21 -12
- package/dist/internal/policy-nodes.js.map +1 -1
- package/dist/internal/policy.js +1 -1
- package/dist/internal/search.js +39 -27
- package/dist/internal/search.js.map +1 -1
- package/package.json +1 -1
- package/src/Authorizer.ts +10 -10
- package/src/Policy.ts +16 -5
- package/src/internal/algebra.ts +12 -8
- package/src/internal/authorizer.ts +77 -34
- package/src/internal/bind.ts +43 -36
- package/src/internal/catalog.ts +69 -40
- package/src/internal/decide.ts +5 -1
- package/src/internal/domain.ts +6 -11
- package/src/internal/policy-json.ts +24 -8
- package/src/internal/policy-nodes.ts +19 -13
- package/src/internal/search.ts +31 -21
|
@@ -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\ttype Found = Issue | readonly [RuleData, Place.Place]\n\tconst rulesOf = Trampoline.adaptive<\n\t\t{ readonly node: TreeNode; readonly at: At },\n\t\tReadonlyArray<Found>\n\t>((R, child) => ({ node, at }, level) => {\n\t\tswitch (node._tag) {\n\t\t\tcase \"Rules\":\n\t\t\t\treturn R.succeed(\n\t\t\t\t\tArr.map(\n\t\t\t\t\t\tnode.rules,\n\t\t\t\t\t\t(rule, index) => [rule, Place.at(at, \"rules\", index)] as const,\n\t\t\t\t\t),\n\t\t\t\t)\n\t\t\tcase \"All\":\n\t\t\tcase \"Any\":\n\t\t\tcase \"FirstOf\":\n\t\t\t\treturn R.map(\n\t\t\t\t\tR.forEach(node.policies, (inner, index) =>\n\t\t\t\t\t\tchild(\n\t\t\t\t\t\t\t{ node: inner, at: Place.at(at, \"policies\", index) },\n\t\t\t\t\t\t\tlevel + 1,\n\t\t\t\t\t\t),\n\t\t\t\t\t),\n\t\t\t\t\tArr.flatten,\n\t\t\t\t)\n\t\t\tcase \"Ceiling\":\n\t\t\t\treturn R.map(\n\t\t\t\t\tR.forEach(\n\t\t\t\t\t\t[\n\t\t\t\t\t\t\t{ node: node.cap, at: Place.at(at, \"cap\") },\n\t\t\t\t\t\t\t{ node: node.policy, at: Place.at(at, \"policy\") },\n\t\t\t\t\t\t],\n\t\t\t\t\t\t(part) => child(part, level + 1),\n\t\t\t\t\t),\n\t\t\t\t\tArr.flatten,\n\t\t\t\t)\n\t\t}\n\t})\n\tconst memo = makeMemo(domain.limits)\n\treturn Arr.flatMap(rulesOf({ node: root, at: undefined }), (found) =>\n\t\tArray.isArray(found)\n\t\t\t? ruleIssues(domain, found[0], found[1], memo)\n\t\t\t: [found as Issue],\n\t)\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"bind.js","names":["Catalog.accepts","Catalog.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\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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"}
|
|
@@ -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"}
|
package/dist/internal/catalog.js
CHANGED
|
@@ -3,9 +3,9 @@ import { adaptive } from "./trampoline.js";
|
|
|
3
3
|
import * as Arr from "effect/Array";
|
|
4
4
|
import * as HashMap from "effect/HashMap";
|
|
5
5
|
import * as HashSet from "effect/HashSet";
|
|
6
|
+
import * as Option from "effect/Option";
|
|
6
7
|
import * as Schema from "effect/Schema";
|
|
7
8
|
import { hasProperty } from "effect/Predicate";
|
|
8
|
-
import * as Option from "effect/Option";
|
|
9
9
|
import * as SchemaAST from "effect/SchemaAST";
|
|
10
10
|
// src/internal/catalog.ts
|
|
11
11
|
/**
|
|
@@ -136,9 +136,23 @@ const alternativesOf = adaptive((R, child) => ({ ast, open }, depth) => {
|
|
|
136
136
|
});
|
|
137
137
|
/** The struct of a Schema: a struct, or the struct of a class. */
|
|
138
138
|
const structOf = (ast) => ast._tag === "Objects" ? ast : ast._tag === "Declaration" ? classStruct(ast) : void 0;
|
|
139
|
-
const hasCheck = (checks, id) =>
|
|
139
|
+
const hasCheck = (checks, id) => hasCheckIn({
|
|
140
|
+
checks,
|
|
141
|
+
id
|
|
142
|
+
});
|
|
143
|
+
/**
|
|
144
|
+
* The walk of {@link hasCheck}. A group of checks may hold a group, very many
|
|
145
|
+
* levels down, as `.and(...)` many times makes, so it is a walk of the
|
|
146
|
+
* trampoline for an input of an unknown depth.
|
|
147
|
+
*/
|
|
148
|
+
const hasCheckIn = adaptive((R, child) => ({ checks, id }, depth) => R.some(checks, (check) => check.annotations?.representation?.id === id ? R.succeed(true) : check._tag === "FilterGroup" ? child({
|
|
149
|
+
checks: check.checks,
|
|
150
|
+
id
|
|
151
|
+
}, depth + 1) : R.succeed(false)));
|
|
140
152
|
/** A struct with named fields, or with the keys of a record. */
|
|
141
153
|
const isStruct = (ast) => ast !== void 0 && (ast.propertySignatures.length > 0 || ast.indexSignatures.length > 0);
|
|
154
|
+
/** Tests if a Schema holds only values that are not objects, as `bigint`. */
|
|
155
|
+
const isPrimitive = (ast) => ast._tag === "BigInt" || ast._tag === "Symbol" || ast._tag === "UniqueSymbol" || ast._tag === "Literal" && typeof ast.literal === "bigint";
|
|
142
156
|
/** The kinds of the values of a Schema that is not a struct. */
|
|
143
157
|
const kindsOf = (ast) => {
|
|
144
158
|
switch (ast._tag) {
|
|
@@ -218,6 +232,7 @@ const placeOf = (place) => {
|
|
|
218
232
|
const structs = Arr.filter(Arr.map(place.schemas, structOf), isStruct);
|
|
219
233
|
const values = Arr.filter(place.schemas, (schema) => !isStruct(structOf(schema)));
|
|
220
234
|
const valueKinds = Arr.reduce(values, noKinds, (kinds, schema) => joinKinds(kinds, kindsOf(structOf(schema) ?? schema)));
|
|
235
|
+
const objects = Arr.some(values, (schema) => !isPrimitive(schema) && kindsOf(structOf(schema) ?? schema).object);
|
|
221
236
|
const kinds = place.top ? anyKinds : joinKinds(joinKinds(valueKinds, structs.length > 0 ? objectKinds : noKinds), place.missing ? nullKinds : noKinds);
|
|
222
237
|
const records = Arr.filter(structs, (struct) => struct.indexSignatures.length > 0);
|
|
223
238
|
const structProperties = Arr.map(structs, propertiesOf);
|
|
@@ -226,7 +241,7 @@ const placeOf = (place) => {
|
|
|
226
241
|
const property = named.get(name);
|
|
227
242
|
return property === void 0 ? [] : [property];
|
|
228
243
|
});
|
|
229
|
-
const top = place.top ||
|
|
244
|
+
const top = place.top || objects || properties.length < structs.length;
|
|
230
245
|
return [name, {
|
|
231
246
|
schemas: Arr.flatMap(properties, (property) => alternatives(property.type, /* @__PURE__ */ new Set())),
|
|
232
247
|
missing: place.missing || values.length > 0 || Arr.some(properties, (property) => SchemaAST.isOptional(property.type)),
|
|
@@ -235,13 +250,13 @@ const placeOf = (place) => {
|
|
|
235
250
|
}];
|
|
236
251
|
});
|
|
237
252
|
const signatures = Arr.flatMap(records, (struct) => struct.indexSignatures);
|
|
238
|
-
const keyTop = place.top ||
|
|
253
|
+
const keyTop = place.top || objects || records.length < structs.length;
|
|
239
254
|
return {
|
|
240
255
|
branch: {
|
|
241
256
|
kinds,
|
|
242
257
|
values: values.length > 0 || records.length > 0,
|
|
243
258
|
structs: structs.length > 0,
|
|
244
|
-
open: records.length > 0 || structs.length > 0 ||
|
|
259
|
+
open: records.length > 0 || structs.length > 0 || objects || place.top,
|
|
245
260
|
plain: place.plain,
|
|
246
261
|
key: signatures.length === 0 ? void 0 : {
|
|
247
262
|
_tag: "Key",
|
|
@@ -311,10 +326,13 @@ const fieldAt = (branch, path, parts) => ({
|
|
|
311
326
|
* The fields that a list shows, a field before the fields below it, down to
|
|
312
327
|
* `parts` parts. A path goes into a place that is on it already one time
|
|
313
328
|
* only, so the list of a recursive Schema is finite. The walk keeps a stack
|
|
314
|
-
* of the places to visit, so a deep Schema does not fill the stack.
|
|
315
|
-
*
|
|
329
|
+
* of the places to visit, so a deep Schema does not fill the stack. A Schema
|
|
330
|
+
* that shares one part in many places may have very many fields, so the
|
|
331
|
+
* walk counts the fields that it makes, and it stops at the first one past
|
|
332
|
+
* `most`. The stack and the count belong to this walk.
|
|
316
333
|
*/
|
|
317
|
-
const listOf = (branches, parts) => {
|
|
334
|
+
const listOf = (branches, parts, most, thing) => {
|
|
335
|
+
const count = { fields: 0 };
|
|
318
336
|
const below = (visit) => {
|
|
319
337
|
const { branch, path } = visit;
|
|
320
338
|
if (path.length === parts || path.length > 0 && HashSet.has(visit.above, branch.id)) return [];
|
|
@@ -342,20 +360,25 @@ const listOf = (branches, parts) => {
|
|
|
342
360
|
rest.push(next);
|
|
343
361
|
});
|
|
344
362
|
const field = visit.path.length === 0 ? [] : [fieldAt(visit.branch, visit.path, parts)];
|
|
345
|
-
|
|
363
|
+
count.fields += field.length;
|
|
364
|
+
if (count.fields > most) throw exceeded(thing, "catalogFields", "fields", count.fields, most);
|
|
346
365
|
return Option.some([field, rest]);
|
|
347
366
|
}));
|
|
348
367
|
};
|
|
349
368
|
/**
|
|
350
|
-
* The fields that a list shows
|
|
351
|
-
*
|
|
369
|
+
* The fields that a list shows, a field before the fields below it, for
|
|
370
|
+
* paths of at most `parts` parts. For a recursive Schema, the list goes into
|
|
371
|
+
* each recursive place one time on each path, so it is finite. A list with
|
|
372
|
+
* more than `most` fields is an error, as for a domain with these limits.
|
|
373
|
+
* Each call makes the list again: a domain checks its size one time, and a
|
|
374
|
+
* list of permitted fields reads it.
|
|
352
375
|
*/
|
|
353
|
-
const fieldsOf = (catalog, parts
|
|
376
|
+
const fieldsOf = (catalog, parts = fieldPathParts, most = ceilings.catalogFields, thing = "A Schema") => listOf(catalog.branches, parts, most, thing);
|
|
354
377
|
/**
|
|
355
|
-
* Makes the catalog of a Schema
|
|
356
|
-
*
|
|
357
|
-
*
|
|
358
|
-
*
|
|
378
|
+
* Makes the catalog of a Schema. It throws when a field name has a dot or is
|
|
379
|
+
* empty, and when the Schema has more places than the ceiling of
|
|
380
|
+
* `catalogFields`. A domain checks the size of the list of the fields with
|
|
381
|
+
* its own limit: see {@link fieldsOf}.
|
|
359
382
|
*/
|
|
360
383
|
const catalogOf = (ast) => {
|
|
361
384
|
const branches = branchesOf({
|
|
@@ -365,7 +388,6 @@ const catalogOf = (ast) => {
|
|
|
365
388
|
plain: true
|
|
366
389
|
});
|
|
367
390
|
return {
|
|
368
|
-
fields: listOf(branches, fieldPathParts),
|
|
369
391
|
branches,
|
|
370
392
|
edges: Arr.reduce(branches, 0, (total, branch) => total + branch.children.length + (branch.keyBranch === void 0 ? 0 : 1)),
|
|
371
393
|
root: branches[0]
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"catalog.js","names":["Trampoline.adaptive","Limits.exceeded","Limits.fieldPathParts"],"sources":["../../src/internal/catalog.ts"],"sourcesContent":["/**\n * The catalog of the fields of a Schema. One walk over the Schema makes it,\n * and every question about the fields of a resource or of the principal reads\n * it: which names a rule may use, which values a field may hold, which fields\n * a pattern fits, and which fields a list of permitted fields shows.\n *\n * The catalog describes the shape of the values, not the checks of the\n * Schema. A check, such as a minimum length, only keeps some values out, so a\n * catalog that leaves the checks out holds every real value and more. An\n * answer that holds for all of these values, as `All` and `None` of a\n * coverage do, then holds for the real values too.\n */\nimport * as Arr from \"effect/Array\"\nimport * as HashMap from \"effect/HashMap\"\nimport * as HashSet from \"effect/HashSet\"\nimport * as Option from \"effect/Option\"\nimport { hasProperty } from \"effect/Predicate\"\nimport * as Schema from \"effect/Schema\"\nimport * as SchemaAST from \"effect/SchemaAST\"\nimport * as Limits from \"./limits.ts\"\nimport * as Trampoline from \"./trampoline.ts\"\n\n/** The kinds of values that a field may hold. */\nexport interface Kinds {\n\t/** The field may be `null`, `undefined` or missing. */\n\treadonly null: boolean\n\treadonly boolean: boolean\n\t/** The field may hold any string. */\n\treadonly string: boolean\n\t/** The numbers that the field may hold. */\n\treadonly number: \"none\" | \"integer\" | \"finite\" | \"any\"\n\t/** The field may hold an object, or a value that is not a scalar. */\n\treadonly object: boolean\n\t/** Single values that the field may hold, other than the ones above. */\n\treadonly literals: ReadonlyArray<string | number | boolean>\n}\n\n/** No value: the kinds of a field of `Schema.Never`. */\nexport const noKinds: Kinds = {\n\tnull: false,\n\tboolean: false,\n\tstring: false,\n\tnumber: \"none\",\n\tobject: false,\n\tliterals: [],\n}\n\n/** Any value. */\nexport const anyKinds: Kinds = {\n\tnull: true,\n\tboolean: true,\n\tstring: true,\n\tnumber: \"any\",\n\tobject: true,\n\tliterals: [],\n}\n\nconst nullKinds: Kinds = { ...noKinds, null: true }\nconst objectKinds: Kinds = { ...noKinds, object: true }\n\nconst numberOrder = [\"none\", \"integer\", \"finite\", \"any\"] as const\n\n/** The kinds of a field that may hold a value of `left` or of `right`. */\nexport const joinKinds = (left: Kinds, right: Kinds): Kinds => ({\n\tnull: left.null || right.null,\n\tboolean: left.boolean || right.boolean,\n\tstring: left.string || right.string,\n\tnumber:\n\t\tnumberOrder.indexOf(left.number) >= numberOrder.indexOf(right.number)\n\t\t\t? left.number\n\t\t\t: right.number,\n\tobject: left.object || right.object,\n\t// A native `Set` finds copies about ten times as fast as `Arr.dedupe`. It\n\t// takes `0` and `-0` as one, as `includes` does when a test reads them.\n\tliterals: Array.from(new Set([...left.literals, ...right.literals])),\n})\n\n/** Tests if a number fits the numbers of a field. */\nexport const fitsNumber = (number: Kinds[\"number\"], value: number): boolean => {\n\tswitch (number) {\n\t\tcase \"none\":\n\t\t\treturn false\n\t\tcase \"integer\":\n\t\t\treturn Number.isInteger(value)\n\t\tcase \"finite\":\n\t\t\treturn Number.isFinite(value)\n\t\tcase \"any\":\n\t\t\treturn true\n\t}\n}\n\n/**\n * Tests if a field with these kinds may hold a value. A missing value and\n * `undefined` are `null`.\n */\nexport const accepts = (kinds: Kinds, value: unknown): boolean => {\n\tif (value === null || value === undefined) {\n\t\treturn kinds.null\n\t}\n\tswitch (typeof value) {\n\t\tcase \"boolean\":\n\t\t\treturn kinds.boolean || kinds.literals.includes(value)\n\t\tcase \"string\":\n\t\t\treturn kinds.string || kinds.literals.includes(value)\n\t\tcase \"number\":\n\t\t\treturn fitsNumber(kinds.number, value) || kinds.literals.includes(value)\n\t\tdefault:\n\t\t\treturn kinds.object\n\t}\n}\n\n/** A part of a field path that stands for any key of a record. */\nexport interface Key {\n\treadonly _tag: \"Key\"\n\t/** The Schemas of the keys. A key must fit one of them. */\n\treadonly parameters: ReadonlyArray<SchemaAST.AST>\n}\n\n/** One part of a field path: a name, or any key of a record. */\nexport type Segment = string | Key\n\n/** One field of a catalog. */\nexport interface Field {\n\treadonly path: ReadonlyArray<Segment>\n\t/** The path with dots, and `*` for a key of a record. */\n\treadonly text: string\n\treadonly kinds: Kinds\n\t/**\n\t * The field holds a value that a list of fields shows as one field: a\n\t * scalar, a record, an array, or a struct at the last part a path may have.\n\t */\n\treadonly listed: boolean\n\t/** The field may hold parts that the Schema does not name, as a record may. */\n\treadonly open: boolean\n\t/**\n\t * A read of the field finds only plain data. It is `false` when a value\n\t * above the field may be an object that does not have it as a data field:\n\t * a class without the field, which may have a getter of that name, a\n\t * value such as a date or a list, or any object, as of `Schema.Unknown`.\n\t * A struct of a union without the field makes it not plain too, since a\n\t * value made by hand may have it with any value.\n\t */\n\treadonly plain: boolean\n}\n\n/**\n * A place in the graph of a catalog. A path of a field leads to a place, and\n * many paths may lead to one place, as in a recursive Schema: `next` and\n * `next.next` of a list of links are one place. A place keeps the ids of the\n * places below it, so the graph has no loop of objects.\n */\nexport interface Branch {\n\treadonly id: number\n\t/** The kinds of the values of the field at this place. */\n\treadonly kinds: Kinds\n\t/** The field holds a scalar, a record or a list: see `Field.listed`. */\n\treadonly values: boolean\n\t/** The field may hold a struct. */\n\treadonly structs: boolean\n\treadonly open: boolean\n\treadonly plain: boolean\n\t/** The ids of the places below, by name, in the order of the Schema. */\n\treadonly children: ReadonlyArray<readonly [string, number]>\n\t/**\n\t * The places below, by name. A lookup gets each name from `split`, as a\n\t * new string, and a `HashMap` finds a new string about 20% faster than a\n\t * native `Map`. The names come from the Schema, so a name from a policy\n\t * can not make a long list of names with one hash here.\n\t */\n\treadonly names: HashMap.HashMap<string, number>\n\t/** The place for the keys of a record, when the value may be a record. */\n\treadonly key: Key | undefined\n\treadonly keyBranch: number | undefined\n}\n\n/** The catalog of the fields of a Schema. */\nexport interface Catalog {\n\t/**\n\t * The fields that a list shows, a field before the fields below it. For a\n\t * recursive Schema, the list goes into each recursive place one time on\n\t * each path, so it is finite.\n\t */\n\treadonly fields: ReadonlyArray<Field>\n\t/** The places, by id. The root is the place with the id 0. */\n\treadonly branches: ReadonlyArray<Branch>\n\t/**\n\t * The number of steps from a place to a place below it. A search of the\n\t * catalog, as for a field pattern, reads each one, so this is its work.\n\t */\n\treadonly edges: number\n\treadonly root: Branch\n}\n\n/**\n * Tests if a key of an object fits the Schemas of the keys of a record. A key\n * of an object is a string, also a key that a number Schema takes: `{ 1: \"a\" }`\n * has the key `\"1\"`. So a key that is the text of a number is tried as that\n * number too.\n */\nexport const keyFits = (key: Key, name: string): boolean =>\n\tArr.some(key.parameters, (parameter) => {\n\t\tconst is = Schema.is(Schema.make(parameter))\n\t\treturn (\n\t\t\tis(name) ||\n\t\t\t(name !== \"\" && String(Number(name)) === name && is(Number(name)))\n\t\t)\n\t})\n\n/**\n * Gives the struct of a `Schema.Class`. A class is a declaration with one\n * struct as its type parameter, and Effect marks it with a `\"~constructor\"`\n * annotation, also through more codec steps, as in\n * `Schema.fromJsonString(User)`. Other declarations, such as `Schema.Option`\n * or `Schema.ReadonlyMap`, have no such mark, and the fields of their type\n * parameter are not fields of the value.\n */\nconst classStruct = (\n\tast: SchemaAST.Declaration,\n): SchemaAST.Objects | undefined => {\n\tconst [inner] = ast.typeParameters\n\treturn ast.annotations?.[\"~constructor\"] !== undefined &&\n\t\tast.typeParameters.length === 1 &&\n\t\tinner?._tag === \"Objects\"\n\t\t? inner\n\t\t: undefined\n}\n\n/**\n * Gives the Schemas that a value at one place may fit: it opens unions and\n * `Schema.suspend`. A class stays a class: see {@link structOf}. The open suspend nodes are in a native `Set`,\n * because the walk must find a node by the node itself: `Equal` compares\n * Schema nodes by structure, and a recursive Schema has no end. A suspend\n * node that gives itself again, with no step on the path, gives nothing.\n */\nconst alternatives = (\n\tast: SchemaAST.AST,\n\topen: ReadonlySet<SchemaAST.AST>,\n): ReadonlyArray<SchemaAST.AST> => alternativesOf({ ast, open })\n\n/**\n * The walk of {@link alternatives}. A union may hold a union, very many\n * levels down, so it is a walk of the trampoline for an input of an unknown\n * depth.\n */\nconst alternativesOf = Trampoline.adaptive<\n\t{ readonly ast: SchemaAST.AST; readonly open: ReadonlySet<SchemaAST.AST> },\n\tReadonlyArray<SchemaAST.AST>\n>((R, child) => ({ ast, open }, depth) => {\n\tswitch (ast._tag) {\n\t\tcase \"Union\":\n\t\t\treturn R.map(\n\t\t\t\tR.forEach(ast.types, (type) => child({ ast: type, open }, depth + 1)),\n\t\t\t\tArr.flatten,\n\t\t\t)\n\t\tcase \"Suspend\":\n\t\t\treturn open.has(ast)\n\t\t\t\t? R.succeed([])\n\t\t\t\t: child({ ast: ast.thunk(), open: new Set([...open, ast]) }, depth + 1)\n\t\tdefault:\n\t\t\treturn R.succeed([ast])\n\t}\n})\n\n/** The struct of a Schema: a struct, or the struct of a class. */\nconst structOf = (ast: SchemaAST.AST): SchemaAST.Objects | undefined =>\n\tast._tag === \"Objects\"\n\t\t? ast\n\t\t: ast._tag === \"Declaration\"\n\t\t\t? classStruct(ast)\n\t\t\t: undefined\n\nconst hasCheck = (\n\tchecks: ReadonlyArray<SchemaAST.Check<never>>,\n\tid: string,\n): boolean =>\n\tArr.some(\n\t\tchecks,\n\t\t(check) =>\n\t\t\tcheck.annotations?.representation?.id === id ||\n\t\t\t(check._tag === \"FilterGroup\" && hasCheck(check.checks, id)),\n\t)\n\n/** A struct with named fields, or with the keys of a record. */\nconst isStruct = (\n\tast: SchemaAST.Objects | undefined,\n): ast is SchemaAST.Objects =>\n\tast !== undefined &&\n\t(ast.propertySignatures.length > 0 || ast.indexSignatures.length > 0)\n\n/** The kinds of the values of a Schema that is not a struct. */\nconst kindsOf = (ast: SchemaAST.AST): Kinds => {\n\tswitch (ast._tag) {\n\t\tcase \"Null\":\n\t\tcase \"Undefined\":\n\t\t\treturn nullKinds\n\t\tcase \"Never\":\n\t\t\treturn noKinds\n\t\tcase \"String\":\n\t\tcase \"TemplateLiteral\":\n\t\t\treturn { ...noKinds, string: true }\n\t\tcase \"Boolean\":\n\t\t\treturn { ...noKinds, boolean: true }\n\t\tcase \"Number\": {\n\t\t\t// The two checks that a search uses. Effect names a number with\n\t\t\t// them, as `Schema.Int` and `Schema.Finite`; other checks are left\n\t\t\t// out, as all checks are.\n\t\t\tconst checks = ast.checks ?? []\n\t\t\treturn {\n\t\t\t\t...noKinds,\n\t\t\t\tnumber: hasCheck(checks, \"effect/schema/isInt\")\n\t\t\t\t\t? \"integer\"\n\t\t\t\t\t: hasCheck(checks, \"effect/schema/isFinite\")\n\t\t\t\t\t\t? \"finite\"\n\t\t\t\t\t\t: \"any\",\n\t\t\t}\n\t\t}\n\t\tcase \"Literal\":\n\t\t\treturn typeof ast.literal === \"bigint\"\n\t\t\t\t? objectKinds\n\t\t\t\t: { ...noKinds, literals: [ast.literal] }\n\t\tcase \"Enum\":\n\t\t\treturn { ...noKinds, literals: Arr.map(ast.enums, ([, value]) => value) }\n\t\tcase \"Arrays\":\n\t\tcase \"ObjectKeyword\":\n\t\tcase \"BigInt\":\n\t\tcase \"Symbol\":\n\t\tcase \"UniqueSymbol\":\n\t\t\treturn objectKinds\n\t\tcase \"Declaration\":\n\t\t\t// A declaration of Effect in `objectDeclarations` holds an object,\n\t\t\t// such as a `Date`. Another declaration may hold any value, as\n\t\t\t// `Schema.Json` or one from `Schema.declare` may.\n\t\t\treturn HashSet.has(objectDeclarations, declarationId(ast))\n\t\t\t\t? objectKinds\n\t\t\t\t: anyKinds\n\t\tdefault:\n\t\t\t// `Schema.Unknown`, `Schema.Void`, and `Struct({})`, which takes a\n\t\t\t// string too: these may hold any value.\n\t\t\treturn anyKinds\n\t}\n}\n\n/**\n * The ids of the declarations of Effect that hold only objects. Effect puts\n * the id in the `representation` annotation. A declaration that is not in\n * this list may hold a scalar, as `Schema.Json` may, so its kinds are any\n * value.\n */\nconst objectDeclarations = HashSet.make(\n\t\"effect/schema/Date\",\n\t\"effect/schema/URL\",\n\t\"effect/schema/Uint8Array\",\n\t\"effect/schema/Duration\",\n\t\"effect/schema/DateTimeUtc\",\n\t\"effect/schema/DateTimeZoned\",\n\t\"effect/schema/BigDecimal\",\n\t\"effect/schema/Redacted\",\n\t\"effect/schema/Cause\",\n\t\"effect/schema/Chunk\",\n\t\"effect/schema/HashMap\",\n\t\"effect/schema/HashSet\",\n\t\"effect/schema/ReadonlyMap\",\n\t\"effect/schema/ReadonlySet\",\n\t\"effect/schema/Option\",\n\t\"effect/schema/Result\",\n\t\"effect/schema/Exit\",\n\t\"effect/schema/File\",\n\t\"effect/schema/FormData\",\n\t\"effect/schema/RegExp\",\n\t\"effect/schema/URLSearchParams\",\n)\n\nconst declarationId = (ast: SchemaAST.Declaration): string | undefined => {\n\tconst representation: unknown = ast.annotations?.[\"representation\"]\n\treturn hasProperty(representation, \"id\") &&\n\t\ttypeof representation.id === \"string\"\n\t\t? representation.id\n\t\t: undefined\n}\n\n/** The things that the walk of a catalog knows at one place. */\ninterface Place {\n\treadonly schemas: ReadonlyArray<SchemaAST.AST>\n\t/** The value may be missing. */\n\treadonly missing: boolean\n\t/** The value may be anything, as an extra field of a struct may be. */\n\treadonly top: boolean\n\t/** A read here finds only plain data: see {@link Field.plain}. */\n\treadonly plain: boolean\n}\n\nconst textOf = (path: ReadonlyArray<Segment>): string =>\n\tArr.map(path, (segment) =>\n\t\ttypeof segment === \"string\" ? segment : \"*\",\n\t).join(\".\")\n\n/**\n * The name of a property as a part of a path. A key of an object is a string,\n * so a number key, as from `Schema.Record(Schema.Literal(1), ...)`, has the\n * name `\"1\"`. A symbol key is not a field.\n */\nconst nameOf = (signature: SchemaAST.PropertySignature): string | undefined =>\n\ttypeof signature.name === \"symbol\" ? undefined : String(signature.name)\n\n/**\n * The properties of a struct by name. A walk makes it one time for each\n * struct of a place, so a struct with very many fields does not take a\n * search for each name. It is a native `Map`, as `Branch.names` is.\n */\nconst propertiesOf = (\n\tstruct: SchemaAST.Objects,\n): ReadonlyMap<string, SchemaAST.PropertySignature> =>\n\tnew Map(\n\t\tArr.flatMap(struct.propertySignatures, (signature) => {\n\t\t\tconst name = nameOf(signature)\n\t\t\treturn name === undefined ? [] : [[name, signature] as const]\n\t\t}),\n\t)\n\n/** The names of the fields of the structs, in the order that they first come. */\nconst namesOf = (\n\tstructs: ReadonlyArray<SchemaAST.Objects>,\n): ReadonlyArray<string> =>\n\t// A native `Set` keeps the first copy of each name, in order, about ten\n\t// times as fast as `Arr.dedupe`.\n\tArray.from(\n\t\tnew Set(\n\t\t\tArr.flatMap(structs, (struct) =>\n\t\t\t\tArr.flatMap(struct.propertySignatures, (signature) => {\n\t\t\t\t\tconst name = nameOf(signature)\n\t\t\t\t\tif (name === undefined) {\n\t\t\t\t\t\treturn []\n\t\t\t\t\t}\n\t\t\t\t\t// A dot in a path is the step into a nested field. A field name with\n\t\t\t\t\t// a dot would make two fields have one path, so it is a defect.\n\t\t\t\t\tif (name.includes(\".\")) {\n\t\t\t\t\t\tthrow new Error(`A field name may not have a dot: \"${name}\"`)\n\t\t\t\t\t}\n\t\t\t\t\t// An empty name would make an empty part in a path, such as\n\t\t\t\t\t// `.secret`.\n\t\t\t\t\tif (name === \"\") {\n\t\t\t\t\t\tthrow new Error(\"A field name may not be empty\")\n\t\t\t\t\t}\n\t\t\t\t\treturn [name]\n\t\t\t\t}),\n\t\t\t),\n\t\t),\n\t)\n\n/** A place of the walk, and the places below it, before they have ids. */\ninterface Made {\n\treadonly branch: Omit<Branch, \"id\" | \"children\" | \"names\" | \"keyBranch\">\n\treadonly children: ReadonlyArray<readonly [string, Place]>\n\treadonly keyPlace: Place | undefined\n}\n\n/** Works out the facts of a place, and the places below it. */\nconst placeOf = (place: Place): Made => {\n\tconst structs = Arr.filter(Arr.map(place.schemas, structOf), isStruct)\n\tconst values = Arr.filter(\n\t\tplace.schemas,\n\t\t(schema) => !isStruct(structOf(schema)),\n\t)\n\tconst valueKinds = Arr.reduce(values, noKinds, (kinds, schema) =>\n\t\tjoinKinds(kinds, kindsOf(structOf(schema) ?? schema)),\n\t)\n\tconst kinds = place.top\n\t\t? anyKinds\n\t\t: joinKinds(\n\t\t\t\tjoinKinds(valueKinds, structs.length > 0 ? objectKinds : noKinds),\n\t\t\t\tplace.missing ? nullKinds : noKinds,\n\t\t\t)\n\tconst records = Arr.filter(\n\t\tstructs,\n\t\t(struct) => struct.indexSignatures.length > 0,\n\t)\n\tconst structProperties = Arr.map(structs, propertiesOf)\n\tconst children = Arr.map(namesOf(structs), (name) => {\n\t\tconst properties = Arr.flatMap(structProperties, (named) => {\n\t\t\tconst property = named.get(name)\n\t\t\treturn property === undefined ? [] : [property]\n\t\t})\n\t\t// A struct without the name may still have it as an extra field, with\n\t\t// any value: only a decode or a `make` takes extra fields away, and a\n\t\t// decision may get a value made by hand. A value that may be any\n\t\t// object, as one of `Schema.Unknown` may, may have the name with any\n\t\t// value too.\n\t\tconst top =\n\t\t\tplace.top || valueKinds.object || properties.length < structs.length\n\t\tconst below: Place = {\n\t\t\tschemas: Arr.flatMap(properties, (property) =>\n\t\t\t\talternatives(property.type, new Set()),\n\t\t\t),\n\t\t\t// A field below a value that is not a struct reads `null`, and so\n\t\t\t// does a field that may be missing.\n\t\t\tmissing:\n\t\t\t\tplace.missing ||\n\t\t\t\tvalues.length > 0 ||\n\t\t\t\tArr.some(properties, (property) => SchemaAST.isOptional(property.type)),\n\t\t\ttop,\n\t\t\t// A value that may be anything may be a list, or have a getter, so\n\t\t\t// a read below it may not find plain data.\n\t\t\tplain: place.plain && !top,\n\t\t}\n\t\treturn [name, below] as const\n\t})\n\tconst signatures = Arr.flatMap(records, (struct) => struct.indexSignatures)\n\tconst keyTop =\n\t\tplace.top || valueKinds.object || records.length < structs.length\n\treturn {\n\t\tbranch: {\n\t\t\tkinds,\n\t\t\tvalues: values.length > 0 || records.length > 0,\n\t\t\tstructs: structs.length > 0,\n\t\t\t// A field that is a scalar in one branch of a union and a struct in\n\t\t\t// another holds parts too.\n\t\t\topen:\n\t\t\t\trecords.length > 0 ||\n\t\t\t\tstructs.length > 0 ||\n\t\t\t\tvalueKinds.object ||\n\t\t\t\tplace.top,\n\t\t\tplain: place.plain,\n\t\t\tkey:\n\t\t\t\tsignatures.length === 0\n\t\t\t\t\t? undefined\n\t\t\t\t\t: {\n\t\t\t\t\t\t\t_tag: \"Key\",\n\t\t\t\t\t\t\tparameters: Arr.map(\n\t\t\t\t\t\t\t\tsignatures,\n\t\t\t\t\t\t\t\t(signature) => signature.parameter,\n\t\t\t\t\t\t\t),\n\t\t\t\t\t\t},\n\t\t},\n\t\tchildren,\n\t\tkeyPlace:\n\t\t\tsignatures.length === 0\n\t\t\t\t? undefined\n\t\t\t\t: {\n\t\t\t\t\t\tschemas: Arr.flatMap(signatures, (signature) =>\n\t\t\t\t\t\t\talternatives(signature.type, new Set()),\n\t\t\t\t\t\t),\n\t\t\t\t\t\t// A record may not have a given key.\n\t\t\t\t\t\tmissing: true,\n\t\t\t\t\t\ttop: keyTop,\n\t\t\t\t\t\tplain: place.plain && !keyTop,\n\t\t\t\t\t},\n\t}\n}\n\n/**\n * Makes the places of a Schema. Each place comes one time: two paths with the\n * same Schemas and the same marks lead to one place. The walk keeps a queue\n * of the places that it has found, and it makes them in order, so a deep\n * Schema does not fill the stack.\n */\nconst branchesOf = (root: Place): ReadonlyArray<Branch> => {\n\t// The id of each Schema node, and of each place, for this walk. The ids\n\t// are in native maps, because the walk must find a Schema node by the node\n\t// itself: `Equal` compares Schema nodes by structure, and a recursive\n\t// Schema has no end. The maps and the queue belong to this walk.\n\tconst astIds = new Map<SchemaAST.AST, number>()\n\tconst placeIds = new Map<string, number>()\n\tconst queue: Array<Place> = []\n\tconst idOf = (place: Place): number => {\n\t\tconst ids = Arr.map(place.schemas, (schema) => {\n\t\t\tconst known = astIds.get(schema)\n\t\t\tif (known !== undefined) {\n\t\t\t\treturn known\n\t\t\t}\n\t\t\tastIds.set(schema, astIds.size)\n\t\t\treturn astIds.size - 1\n\t\t})\n\t\tconst key = `${Array.from(new Set(ids))\n\t\t\t.sort((a, b) => a - b)\n\t\t\t.join(\n\t\t\t\t\",\",\n\t\t\t)}|${String(place.missing)}|${String(place.top)}|${String(place.plain)}`\n\t\tconst known = placeIds.get(key)\n\t\tif (known !== undefined) {\n\t\t\treturn known\n\t\t}\n\t\tif (queue.length >= Limits.ceilings.catalogFields) {\n\t\t\tthrow Limits.exceeded(\n\t\t\t\t\"A Schema\",\n\t\t\t\t\"catalogFields\",\n\t\t\t\t\"places\",\n\t\t\t\tqueue.length + 1,\n\t\t\t\tLimits.ceilings.catalogFields,\n\t\t\t)\n\t\t}\n\t\tplaceIds.set(key, queue.length)\n\t\tqueue.push(place)\n\t\treturn queue.length - 1\n\t}\n\tidOf(root)\n\treturn Arr.unfold(0, (id) => {\n\t\tconst place = queue[id]\n\t\tif (place === undefined) {\n\t\t\treturn Option.none()\n\t\t}\n\t\tconst made = placeOf(place)\n\t\tconst children = Arr.map(\n\t\t\tmade.children,\n\t\t\t([name, below]) => [name, idOf(below)] as const,\n\t\t)\n\t\tconst branch: Branch = {\n\t\t\t...made.branch,\n\t\t\tid,\n\t\t\tchildren,\n\t\t\tnames: HashMap.fromIterable(children),\n\t\t\tkeyBranch: made.keyPlace === undefined ? undefined : idOf(made.keyPlace),\n\t\t}\n\t\treturn Option.some([branch, id + 1] as const)\n\t})\n}\n\n/** The field at a place, for a path. */\nconst fieldAt = (\n\tbranch: Branch,\n\tpath: ReadonlyArray<Segment>,\n\tparts: number,\n): Field => ({\n\tpath,\n\ttext: textOf(path),\n\tkinds: branch.kinds,\n\t// A struct at the last part that a path may have is one field, since a\n\t// path does not go below it.\n\tlisted: branch.values || (path.length === parts && branch.structs),\n\topen: branch.open,\n\tplain: branch.plain,\n})\n\n/**\n * The fields that a list shows, a field before the fields below it, down to\n * `parts` parts. A path goes into a place that is on it already one time\n * only, so the list of a recursive Schema is finite. The walk keeps a stack\n * of the places to visit, so a deep Schema does not fill the stack. The\n * stack belongs to this walk.\n */\nconst listOf = (\n\tbranches: ReadonlyArray<Branch>,\n\tparts: number,\n): ReadonlyArray<Field> => {\n\tinterface Visit {\n\t\treadonly branch: Branch\n\t\treadonly path: ReadonlyArray<Segment>\n\t\t/** The ids of the places on the path, as a set of Effect that shares its parts. */\n\t\treadonly above: HashSet.HashSet<number>\n\t}\n\tconst below = (visit: Visit): ReadonlyArray<Visit> => {\n\t\tconst { branch, path } = visit\n\t\tif (\n\t\t\tpath.length === parts ||\n\t\t\t(path.length > 0 && HashSet.has(visit.above, branch.id))\n\t\t) {\n\t\t\treturn []\n\t\t}\n\t\tconst above = HashSet.add(visit.above, branch.id)\n\t\tconst named = Arr.map(branch.children, ([name, id]) => ({\n\t\t\tbranch: branches[id]!,\n\t\t\tpath: [...path, name],\n\t\t\tabove,\n\t\t}))\n\t\treturn branch.key === undefined || branch.keyBranch === undefined\n\t\t\t? named\n\t\t\t: [\n\t\t\t\t\t...named,\n\t\t\t\t\t{\n\t\t\t\t\t\tbranch: branches[branch.keyBranch]!,\n\t\t\t\t\t\tpath: [...path, branch.key],\n\t\t\t\t\t\tabove,\n\t\t\t\t\t},\n\t\t\t\t]\n\t}\n\tconst stack: Array<Visit> = [\n\t\t{ branch: branches[0]!, path: [], above: HashSet.empty() },\n\t]\n\treturn Arr.flatten(\n\t\tArr.unfold(stack, (rest) => {\n\t\t\tconst visit = rest.pop()\n\t\t\tif (visit === undefined) {\n\t\t\t\treturn Option.none()\n\t\t\t}\n\t\t\t// The visits go onto the stack one at a time: a spread into a call\n\t\t\t// would pass one argument for each field, past the most arguments\n\t\t\t// of a call.\n\t\t\tArr.forEach(Arr.reverse(below(visit)), (next) => {\n\t\t\t\trest.push(next)\n\t\t\t})\n\t\t\tconst field =\n\t\t\t\tvisit.path.length === 0\n\t\t\t\t\t? []\n\t\t\t\t\t: [fieldAt(visit.branch, visit.path, parts)]\n\t\t\tif (field.length > 0 && rest.length + 1 > Limits.ceilings.catalogFields) {\n\t\t\t\tthrow Limits.exceeded(\n\t\t\t\t\t\"A Schema\",\n\t\t\t\t\t\"catalogFields\",\n\t\t\t\t\t\"fields\",\n\t\t\t\t\trest.length + 1,\n\t\t\t\t\tLimits.ceilings.catalogFields,\n\t\t\t\t)\n\t\t\t}\n\t\t\treturn Option.some([field, rest] as const)\n\t\t}),\n\t)\n}\n\n/**\n * The fields that a list shows for paths of at most `parts` parts, as for a\n * domain with that limit. See `Catalog.fields`.\n */\nexport const fieldsOf = (\n\tcatalog: Catalog,\n\tparts: number,\n): ReadonlyArray<Field> =>\n\tparts === Limits.fieldPathParts\n\t\t? catalog.fields\n\t\t: listOf(catalog.branches, parts)\n\n/**\n * Makes the catalog of a Schema, with the list of its fields for the default\n * limit of the parts of a path. It throws when a field name has a dot or is\n * empty, and when the Schema has more places or fields than the ceiling of\n * `catalogFields`. A domain checks the size of the list with its own limit.\n */\nexport const catalogOf = (ast: SchemaAST.AST): Catalog => {\n\tconst branches = branchesOf({\n\t\tschemas: alternatives(ast, new Set()),\n\t\tmissing: false,\n\t\ttop: false,\n\t\tplain: true,\n\t})\n\treturn {\n\t\tfields: listOf(branches, Limits.fieldPathParts),\n\t\tbranches,\n\t\tedges: Arr.reduce(\n\t\t\tbranches,\n\t\t\t0,\n\t\t\t(total, branch) =>\n\t\t\t\ttotal +\n\t\t\t\tbranch.children.length +\n\t\t\t\t(branch.keyBranch === undefined ? 0 : 1),\n\t\t),\n\t\troot: branches[0]!,\n\t}\n}\n\n/**\n * Gives the field of a catalog at a path with dots, if the catalog has it.\n * A path has at most `parts` parts. The lookup follows one place for each\n * part, in a loop.\n */\nexport const lookup = (\n\tcatalog: Catalog,\n\tpath: string,\n\tparts: number = Limits.fieldPathParts,\n): Field | undefined => {\n\tconst names = path.split(\".\", parts + 1)\n\tif (names.length > parts) {\n\t\treturn undefined\n\t}\n\tconst found = Arr.reduce(\n\t\tnames,\n\t\t{ branch: catalog.root, path: [] } as\n\t\t\t| { readonly branch: Branch; readonly path: ReadonlyArray<Segment> }\n\t\t\t| undefined,\n\t\t(current, name) => {\n\t\t\tif (current === undefined) {\n\t\t\t\treturn undefined\n\t\t\t}\n\t\t\tconst named = HashMap.get(current.branch.names, name)\n\t\t\tif (Option.isSome(named)) {\n\t\t\t\treturn {\n\t\t\t\t\tbranch: catalog.branches[named.value]!,\n\t\t\t\t\tpath: [...current.path, name],\n\t\t\t\t}\n\t\t\t}\n\t\t\tconst key = current.branch.key\n\t\t\tconst keyBranch = current.branch.keyBranch\n\t\t\treturn key !== undefined && keyBranch !== undefined && keyFits(key, name)\n\t\t\t\t? { branch: catalog.branches[keyBranch]!, path: [...current.path, key] }\n\t\t\t\t: undefined\n\t\t},\n\t)\n\treturn found === undefined\n\t\t? undefined\n\t\t: fieldAt(found.branch, found.path, parts)\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"catalog.js","names":["Trampoline.adaptive","Limits.exceeded","Limits.fieldPathParts"],"sources":["../../src/internal/catalog.ts"],"sourcesContent":["/**\n * The catalog of the fields of a Schema. One walk over the Schema makes it,\n * and every question about the fields of a resource or of the principal reads\n * it: which names a rule may use, which values a field may hold, which fields\n * a pattern fits, and which fields a list of permitted fields shows.\n *\n * The catalog describes the shape of the values, not the checks of the\n * Schema. A check, such as a minimum length, only keeps some values out, so a\n * catalog that leaves the checks out holds every real value and more. An\n * answer that holds for all of these values, as `All` and `None` of a\n * coverage do, then holds for the real values too.\n */\nimport * as Arr from \"effect/Array\"\nimport * as HashMap from \"effect/HashMap\"\nimport * as HashSet from \"effect/HashSet\"\nimport * as Option from \"effect/Option\"\nimport { hasProperty } from \"effect/Predicate\"\nimport * as Schema from \"effect/Schema\"\nimport * as SchemaAST from \"effect/SchemaAST\"\nimport * as Limits from \"./limits.ts\"\nimport * as Trampoline from \"./trampoline.ts\"\n\n/** The kinds of values that a field may hold. */\nexport interface Kinds {\n\t/** The field may be `null`, `undefined` or missing. */\n\treadonly null: boolean\n\treadonly boolean: boolean\n\t/** The field may hold any string. */\n\treadonly string: boolean\n\t/** The numbers that the field may hold. */\n\treadonly number: \"none\" | \"integer\" | \"finite\" | \"any\"\n\t/** The field may hold an object, or a value that is not a scalar. */\n\treadonly object: boolean\n\t/** Single values that the field may hold, other than the ones above. */\n\treadonly literals: ReadonlyArray<string | number | boolean>\n}\n\n/** No value: the kinds of a field of `Schema.Never`. */\nexport const noKinds: Kinds = {\n\tnull: false,\n\tboolean: false,\n\tstring: false,\n\tnumber: \"none\",\n\tobject: false,\n\tliterals: [],\n}\n\n/** Any value. */\nexport const anyKinds: Kinds = {\n\tnull: true,\n\tboolean: true,\n\tstring: true,\n\tnumber: \"any\",\n\tobject: true,\n\tliterals: [],\n}\n\nconst nullKinds: Kinds = { ...noKinds, null: true }\nconst objectKinds: Kinds = { ...noKinds, object: true }\n\nconst numberOrder = [\"none\", \"integer\", \"finite\", \"any\"] as const\n\n/** The kinds of a field that may hold a value of `left` or of `right`. */\nexport const joinKinds = (left: Kinds, right: Kinds): Kinds => ({\n\tnull: left.null || right.null,\n\tboolean: left.boolean || right.boolean,\n\tstring: left.string || right.string,\n\tnumber:\n\t\tnumberOrder.indexOf(left.number) >= numberOrder.indexOf(right.number)\n\t\t\t? left.number\n\t\t\t: right.number,\n\tobject: left.object || right.object,\n\t// A native `Set` finds copies about ten times as fast as `Arr.dedupe`. It\n\t// takes `0` and `-0` as one, as `includes` does when a test reads them.\n\tliterals: Array.from(new Set([...left.literals, ...right.literals])),\n})\n\n/** Tests if a number fits the numbers of a field. */\nexport const fitsNumber = (number: Kinds[\"number\"], value: number): boolean => {\n\tswitch (number) {\n\t\tcase \"none\":\n\t\t\treturn false\n\t\tcase \"integer\":\n\t\t\treturn Number.isInteger(value)\n\t\tcase \"finite\":\n\t\t\treturn Number.isFinite(value)\n\t\tcase \"any\":\n\t\t\treturn true\n\t}\n}\n\n/**\n * Tests if a field with these kinds may hold a value. A missing value and\n * `undefined` are `null`.\n */\nexport const accepts = (kinds: Kinds, value: unknown): boolean => {\n\tif (value === null || value === undefined) {\n\t\treturn kinds.null\n\t}\n\tswitch (typeof value) {\n\t\tcase \"boolean\":\n\t\t\treturn kinds.boolean || kinds.literals.includes(value)\n\t\tcase \"string\":\n\t\t\treturn kinds.string || kinds.literals.includes(value)\n\t\tcase \"number\":\n\t\t\treturn fitsNumber(kinds.number, value) || kinds.literals.includes(value)\n\t\tdefault:\n\t\t\treturn kinds.object\n\t}\n}\n\n/** A part of a field path that stands for any key of a record. */\nexport interface Key {\n\treadonly _tag: \"Key\"\n\t/** The Schemas of the keys. A key must fit one of them. */\n\treadonly parameters: ReadonlyArray<SchemaAST.AST>\n}\n\n/** One part of a field path: a name, or any key of a record. */\nexport type Segment = string | Key\n\n/** One field of a catalog. */\nexport interface Field {\n\treadonly path: ReadonlyArray<Segment>\n\t/** The path with dots, and `*` for a key of a record. */\n\treadonly text: string\n\treadonly kinds: Kinds\n\t/**\n\t * The field holds a value that a list of fields shows as one field: a\n\t * scalar, a record, an array, or a struct at the last part a path may have.\n\t */\n\treadonly listed: boolean\n\t/** The field may hold parts that the Schema does not name, as a record may. */\n\treadonly open: boolean\n\t/**\n\t * A read of the field finds only plain data. It is `false` when a value\n\t * above the field may be an object that does not have it as a data field:\n\t * a class without the field, which may have a getter of that name, a\n\t * value such as a date or a list, or any object, as of `Schema.Unknown`.\n\t * A struct of a union without the field makes it not plain too, since a\n\t * value made by hand may have it with any value.\n\t */\n\treadonly plain: boolean\n}\n\n/**\n * A place in the graph of a catalog. A path of a field leads to a place, and\n * many paths may lead to one place, as in a recursive Schema: `next` and\n * `next.next` of a list of links are one place. A place keeps the ids of the\n * places below it, so the graph has no loop of objects.\n */\nexport interface Branch {\n\treadonly id: number\n\t/** The kinds of the values of the field at this place. */\n\treadonly kinds: Kinds\n\t/** The field holds a scalar, a record or a list: see `Field.listed`. */\n\treadonly values: boolean\n\t/** The field may hold a struct. */\n\treadonly structs: boolean\n\treadonly open: boolean\n\treadonly plain: boolean\n\t/** The ids of the places below, by name, in the order of the Schema. */\n\treadonly children: ReadonlyArray<readonly [string, number]>\n\t/**\n\t * The places below, by name. A lookup gets each name from `split`, as a\n\t * new string, and a `HashMap` finds a new string about 20% faster than a\n\t * native `Map`. The names come from the Schema, so a name from a policy\n\t * can not make a long list of names with one hash here.\n\t */\n\treadonly names: HashMap.HashMap<string, number>\n\t/** The place for the keys of a record, when the value may be a record. */\n\treadonly key: Key | undefined\n\treadonly keyBranch: number | undefined\n}\n\n/** The catalog of the fields of a Schema. */\nexport interface Catalog {\n\t/** The places, by id. The root is the place with the id 0. */\n\treadonly branches: ReadonlyArray<Branch>\n\t/**\n\t * The number of steps from a place to a place below it. A search of the\n\t * catalog, as for a field pattern, reads each one, so this is its work.\n\t */\n\treadonly edges: number\n\treadonly root: Branch\n}\n\n/**\n * Tests if a key of an object fits the Schemas of the keys of a record. A key\n * of an object is a string, also a key that a number Schema takes: `{ 1: \"a\" }`\n * has the key `\"1\"`. So a key that is the text of a number is tried as that\n * number too.\n */\nexport const keyFits = (key: Key, name: string): boolean =>\n\tArr.some(key.parameters, (parameter) => {\n\t\tconst is = Schema.is(Schema.make(parameter))\n\t\treturn (\n\t\t\tis(name) ||\n\t\t\t(name !== \"\" && String(Number(name)) === name && is(Number(name)))\n\t\t)\n\t})\n\n/**\n * Gives the struct of a `Schema.Class`. A class is a declaration with one\n * struct as its type parameter, and Effect marks it with a `\"~constructor\"`\n * annotation, also through more codec steps, as in\n * `Schema.fromJsonString(User)`. Other declarations, such as `Schema.Option`\n * or `Schema.ReadonlyMap`, have no such mark, and the fields of their type\n * parameter are not fields of the value.\n */\nconst classStruct = (\n\tast: SchemaAST.Declaration,\n): SchemaAST.Objects | undefined => {\n\tconst [inner] = ast.typeParameters\n\treturn ast.annotations?.[\"~constructor\"] !== undefined &&\n\t\tast.typeParameters.length === 1 &&\n\t\tinner?._tag === \"Objects\"\n\t\t? inner\n\t\t: undefined\n}\n\n/**\n * Gives the Schemas that a value at one place may fit: it opens unions and\n * `Schema.suspend`. A class stays a class: see {@link structOf}. The open suspend nodes are in a native `Set`,\n * because the walk must find a node by the node itself: `Equal` compares\n * Schema nodes by structure, and a recursive Schema has no end. A suspend\n * node that gives itself again, with no step on the path, gives nothing.\n */\nconst alternatives = (\n\tast: SchemaAST.AST,\n\topen: ReadonlySet<SchemaAST.AST>,\n): ReadonlyArray<SchemaAST.AST> => alternativesOf({ ast, open })\n\n/**\n * The walk of {@link alternatives}. A union may hold a union, very many\n * levels down, so it is a walk of the trampoline for an input of an unknown\n * depth.\n */\nconst alternativesOf = Trampoline.adaptive<\n\t{ readonly ast: SchemaAST.AST; readonly open: ReadonlySet<SchemaAST.AST> },\n\tReadonlyArray<SchemaAST.AST>\n>((R, child) => ({ ast, open }, depth) => {\n\tswitch (ast._tag) {\n\t\tcase \"Union\":\n\t\t\treturn R.map(\n\t\t\t\tR.forEach(ast.types, (type) => child({ ast: type, open }, depth + 1)),\n\t\t\t\tArr.flatten,\n\t\t\t)\n\t\tcase \"Suspend\":\n\t\t\treturn open.has(ast)\n\t\t\t\t? R.succeed([])\n\t\t\t\t: child({ ast: ast.thunk(), open: new Set([...open, ast]) }, depth + 1)\n\t\tdefault:\n\t\t\treturn R.succeed([ast])\n\t}\n})\n\n/** The struct of a Schema: a struct, or the struct of a class. */\nconst structOf = (ast: SchemaAST.AST): SchemaAST.Objects | undefined =>\n\tast._tag === \"Objects\"\n\t\t? ast\n\t\t: ast._tag === \"Declaration\"\n\t\t\t? classStruct(ast)\n\t\t\t: undefined\n\nconst hasCheck = (\n\tchecks: ReadonlyArray<SchemaAST.Check<never>>,\n\tid: string,\n): boolean => hasCheckIn({ checks, id })\n\n/**\n * The walk of {@link hasCheck}. A group of checks may hold a group, very many\n * levels down, as `.and(...)` many times makes, so it is a walk of the\n * trampoline for an input of an unknown depth.\n */\nconst hasCheckIn = Trampoline.adaptive<\n\t{\n\t\treadonly checks: ReadonlyArray<SchemaAST.Check<never>>\n\t\treadonly id: string\n\t},\n\tboolean\n>(\n\t(R, child) =>\n\t\t({ checks, id }, depth) =>\n\t\t\tR.some(checks, (check) =>\n\t\t\t\tcheck.annotations?.representation?.id === id\n\t\t\t\t\t? R.succeed(true)\n\t\t\t\t\t: check._tag === \"FilterGroup\"\n\t\t\t\t\t\t? child({ checks: check.checks, id }, depth + 1)\n\t\t\t\t\t\t: R.succeed(false),\n\t\t\t),\n)\n\n/** A struct with named fields, or with the keys of a record. */\nconst isStruct = (\n\tast: SchemaAST.Objects | undefined,\n): ast is SchemaAST.Objects =>\n\tast !== undefined &&\n\t(ast.propertySignatures.length > 0 || ast.indexSignatures.length > 0)\n\n/** Tests if a Schema holds only values that are not objects, as `bigint`. */\nconst isPrimitive = (ast: SchemaAST.AST): boolean =>\n\tast._tag === \"BigInt\" ||\n\tast._tag === \"Symbol\" ||\n\tast._tag === \"UniqueSymbol\" ||\n\t(ast._tag === \"Literal\" && typeof ast.literal === \"bigint\")\n\n/** The kinds of the values of a Schema that is not a struct. */\nconst kindsOf = (ast: SchemaAST.AST): Kinds => {\n\tswitch (ast._tag) {\n\t\tcase \"Null\":\n\t\tcase \"Undefined\":\n\t\t\treturn nullKinds\n\t\tcase \"Never\":\n\t\t\treturn noKinds\n\t\tcase \"String\":\n\t\tcase \"TemplateLiteral\":\n\t\t\treturn { ...noKinds, string: true }\n\t\tcase \"Boolean\":\n\t\t\treturn { ...noKinds, boolean: true }\n\t\tcase \"Number\": {\n\t\t\t// The two checks that a search uses. Effect names a number with\n\t\t\t// them, as `Schema.Int` and `Schema.Finite`; other checks are left\n\t\t\t// out, as all checks are.\n\t\t\tconst checks = ast.checks ?? []\n\t\t\treturn {\n\t\t\t\t...noKinds,\n\t\t\t\tnumber: hasCheck(checks, \"effect/schema/isInt\")\n\t\t\t\t\t? \"integer\"\n\t\t\t\t\t: hasCheck(checks, \"effect/schema/isFinite\")\n\t\t\t\t\t\t? \"finite\"\n\t\t\t\t\t\t: \"any\",\n\t\t\t}\n\t\t}\n\t\tcase \"Literal\":\n\t\t\treturn typeof ast.literal === \"bigint\"\n\t\t\t\t? objectKinds\n\t\t\t\t: { ...noKinds, literals: [ast.literal] }\n\t\tcase \"Enum\":\n\t\t\treturn { ...noKinds, literals: Arr.map(ast.enums, ([, value]) => value) }\n\t\tcase \"Arrays\":\n\t\tcase \"ObjectKeyword\":\n\t\tcase \"BigInt\":\n\t\tcase \"Symbol\":\n\t\tcase \"UniqueSymbol\":\n\t\t\treturn objectKinds\n\t\tcase \"Declaration\":\n\t\t\t// A declaration of Effect in `objectDeclarations` holds an object,\n\t\t\t// such as a `Date`. Another declaration may hold any value, as\n\t\t\t// `Schema.Json` or one from `Schema.declare` may.\n\t\t\treturn HashSet.has(objectDeclarations, declarationId(ast))\n\t\t\t\t? objectKinds\n\t\t\t\t: anyKinds\n\t\tdefault:\n\t\t\t// `Schema.Unknown`, `Schema.Void`, and `Struct({})`, which takes a\n\t\t\t// string too: these may hold any value.\n\t\t\treturn anyKinds\n\t}\n}\n\n/**\n * The ids of the declarations of Effect that hold only objects. Effect puts\n * the id in the `representation` annotation. A declaration that is not in\n * this list may hold a scalar, as `Schema.Json` may, so its kinds are any\n * value.\n */\nconst objectDeclarations = HashSet.make(\n\t\"effect/schema/Date\",\n\t\"effect/schema/URL\",\n\t\"effect/schema/Uint8Array\",\n\t\"effect/schema/Duration\",\n\t\"effect/schema/DateTimeUtc\",\n\t\"effect/schema/DateTimeZoned\",\n\t\"effect/schema/BigDecimal\",\n\t\"effect/schema/Redacted\",\n\t\"effect/schema/Cause\",\n\t\"effect/schema/Chunk\",\n\t\"effect/schema/HashMap\",\n\t\"effect/schema/HashSet\",\n\t\"effect/schema/ReadonlyMap\",\n\t\"effect/schema/ReadonlySet\",\n\t\"effect/schema/Option\",\n\t\"effect/schema/Result\",\n\t\"effect/schema/Exit\",\n\t\"effect/schema/File\",\n\t\"effect/schema/FormData\",\n\t\"effect/schema/RegExp\",\n\t\"effect/schema/URLSearchParams\",\n)\n\nconst declarationId = (ast: SchemaAST.Declaration): string | undefined => {\n\tconst representation: unknown = ast.annotations?.[\"representation\"]\n\treturn hasProperty(representation, \"id\") &&\n\t\ttypeof representation.id === \"string\"\n\t\t? representation.id\n\t\t: undefined\n}\n\n/** The things that the walk of a catalog knows at one place. */\ninterface Place {\n\treadonly schemas: ReadonlyArray<SchemaAST.AST>\n\t/** The value may be missing. */\n\treadonly missing: boolean\n\t/** The value may be anything, as an extra field of a struct may be. */\n\treadonly top: boolean\n\t/** A read here finds only plain data: see {@link Field.plain}. */\n\treadonly plain: boolean\n}\n\nconst textOf = (path: ReadonlyArray<Segment>): string =>\n\tArr.map(path, (segment) =>\n\t\ttypeof segment === \"string\" ? segment : \"*\",\n\t).join(\".\")\n\n/**\n * The name of a property as a part of a path. A key of an object is a string,\n * so a number key, as from `Schema.Record(Schema.Literal(1), ...)`, has the\n * name `\"1\"`. A symbol key is not a field.\n */\nconst nameOf = (signature: SchemaAST.PropertySignature): string | undefined =>\n\ttypeof signature.name === \"symbol\" ? undefined : String(signature.name)\n\n/**\n * The properties of a struct by name. A walk makes it one time for each\n * struct of a place, so a struct with very many fields does not take a\n * search for each name. It is a native `Map`, as `Branch.names` is.\n */\nconst propertiesOf = (\n\tstruct: SchemaAST.Objects,\n): ReadonlyMap<string, SchemaAST.PropertySignature> =>\n\tnew Map(\n\t\tArr.flatMap(struct.propertySignatures, (signature) => {\n\t\t\tconst name = nameOf(signature)\n\t\t\treturn name === undefined ? [] : [[name, signature] as const]\n\t\t}),\n\t)\n\n/** The names of the fields of the structs, in the order that they first come. */\nconst namesOf = (\n\tstructs: ReadonlyArray<SchemaAST.Objects>,\n): ReadonlyArray<string> =>\n\t// A native `Set` keeps the first copy of each name, in order, about ten\n\t// times as fast as `Arr.dedupe`.\n\tArray.from(\n\t\tnew Set(\n\t\t\tArr.flatMap(structs, (struct) =>\n\t\t\t\tArr.flatMap(struct.propertySignatures, (signature) => {\n\t\t\t\t\tconst name = nameOf(signature)\n\t\t\t\t\tif (name === undefined) {\n\t\t\t\t\t\treturn []\n\t\t\t\t\t}\n\t\t\t\t\t// A dot in a path is the step into a nested field. A field name with\n\t\t\t\t\t// a dot would make two fields have one path, so it is a defect.\n\t\t\t\t\tif (name.includes(\".\")) {\n\t\t\t\t\t\tthrow new Error(`A field name may not have a dot: \"${name}\"`)\n\t\t\t\t\t}\n\t\t\t\t\t// An empty name would make an empty part in a path, such as\n\t\t\t\t\t// `.secret`.\n\t\t\t\t\tif (name === \"\") {\n\t\t\t\t\t\tthrow new Error(\"A field name may not be empty\")\n\t\t\t\t\t}\n\t\t\t\t\treturn [name]\n\t\t\t\t}),\n\t\t\t),\n\t\t),\n\t)\n\n/** A place of the walk, and the places below it, before they have ids. */\ninterface Made {\n\treadonly branch: Omit<Branch, \"id\" | \"children\" | \"names\" | \"keyBranch\">\n\treadonly children: ReadonlyArray<readonly [string, Place]>\n\treadonly keyPlace: Place | undefined\n}\n\n/** Works out the facts of a place, and the places below it. */\nconst placeOf = (place: Place): Made => {\n\tconst structs = Arr.filter(Arr.map(place.schemas, structOf), isStruct)\n\tconst values = Arr.filter(\n\t\tplace.schemas,\n\t\t(schema) => !isStruct(structOf(schema)),\n\t)\n\tconst valueKinds = Arr.reduce(values, noKinds, (kinds, schema) =>\n\t\tjoinKinds(kinds, kindsOf(structOf(schema) ?? schema)),\n\t)\n\t// A value of a branch may be an object, with parts that the Schema does\n\t// not name. A `bigint` or a `symbol` is not a scalar that a test takes,\n\t// so its kinds say `object`, but it has no parts: a read below it gives\n\t// `null`, as below a string.\n\tconst objects = Arr.some(\n\t\tvalues,\n\t\t(schema) =>\n\t\t\t!isPrimitive(schema) && kindsOf(structOf(schema) ?? schema).object,\n\t)\n\tconst kinds = place.top\n\t\t? anyKinds\n\t\t: joinKinds(\n\t\t\t\tjoinKinds(valueKinds, structs.length > 0 ? objectKinds : noKinds),\n\t\t\t\tplace.missing ? nullKinds : noKinds,\n\t\t\t)\n\tconst records = Arr.filter(\n\t\tstructs,\n\t\t(struct) => struct.indexSignatures.length > 0,\n\t)\n\tconst structProperties = Arr.map(structs, propertiesOf)\n\tconst children = Arr.map(namesOf(structs), (name) => {\n\t\tconst properties = Arr.flatMap(structProperties, (named) => {\n\t\t\tconst property = named.get(name)\n\t\t\treturn property === undefined ? [] : [property]\n\t\t})\n\t\t// A struct without the name may still have it as an extra field, with\n\t\t// any value: only a decode or a `make` takes extra fields away, and a\n\t\t// decision may get a value made by hand. A value that may be any\n\t\t// object, as one of `Schema.Unknown` may, may have the name with any\n\t\t// value too.\n\t\tconst top = place.top || objects || properties.length < structs.length\n\t\tconst below: Place = {\n\t\t\tschemas: Arr.flatMap(properties, (property) =>\n\t\t\t\talternatives(property.type, new Set()),\n\t\t\t),\n\t\t\t// A field below a value that is not a struct reads `null`, and so\n\t\t\t// does a field that may be missing.\n\t\t\tmissing:\n\t\t\t\tplace.missing ||\n\t\t\t\tvalues.length > 0 ||\n\t\t\t\tArr.some(properties, (property) => SchemaAST.isOptional(property.type)),\n\t\t\ttop,\n\t\t\t// A value that may be anything may be a list, or have a getter, so\n\t\t\t// a read below it may not find plain data.\n\t\t\tplain: place.plain && !top,\n\t\t}\n\t\treturn [name, below] as const\n\t})\n\tconst signatures = Arr.flatMap(records, (struct) => struct.indexSignatures)\n\tconst keyTop = place.top || objects || records.length < structs.length\n\treturn {\n\t\tbranch: {\n\t\t\tkinds,\n\t\t\tvalues: values.length > 0 || records.length > 0,\n\t\t\tstructs: structs.length > 0,\n\t\t\t// A field that is a scalar in one branch of a union and a struct in\n\t\t\t// another holds parts too.\n\t\t\topen: records.length > 0 || structs.length > 0 || objects || place.top,\n\t\t\tplain: place.plain,\n\t\t\tkey:\n\t\t\t\tsignatures.length === 0\n\t\t\t\t\t? undefined\n\t\t\t\t\t: {\n\t\t\t\t\t\t\t_tag: \"Key\",\n\t\t\t\t\t\t\tparameters: Arr.map(\n\t\t\t\t\t\t\t\tsignatures,\n\t\t\t\t\t\t\t\t(signature) => signature.parameter,\n\t\t\t\t\t\t\t),\n\t\t\t\t\t\t},\n\t\t},\n\t\tchildren,\n\t\tkeyPlace:\n\t\t\tsignatures.length === 0\n\t\t\t\t? undefined\n\t\t\t\t: {\n\t\t\t\t\t\tschemas: Arr.flatMap(signatures, (signature) =>\n\t\t\t\t\t\t\talternatives(signature.type, new Set()),\n\t\t\t\t\t\t),\n\t\t\t\t\t\t// A record may not have a given key.\n\t\t\t\t\t\tmissing: true,\n\t\t\t\t\t\ttop: keyTop,\n\t\t\t\t\t\tplain: place.plain && !keyTop,\n\t\t\t\t\t},\n\t}\n}\n\n/**\n * Makes the places of a Schema. Each place comes one time: two paths with the\n * same Schemas and the same marks lead to one place. The walk keeps a queue\n * of the places that it has found, and it makes them in order, so a deep\n * Schema does not fill the stack.\n */\nconst branchesOf = (root: Place): ReadonlyArray<Branch> => {\n\t// The id of each Schema node, and of each place, for this walk. The ids\n\t// are in native maps, because the walk must find a Schema node by the node\n\t// itself: `Equal` compares Schema nodes by structure, and a recursive\n\t// Schema has no end. The maps and the queue belong to this walk.\n\tconst astIds = new Map<SchemaAST.AST, number>()\n\tconst placeIds = new Map<string, number>()\n\tconst queue: Array<Place> = []\n\tconst idOf = (place: Place): number => {\n\t\tconst ids = Arr.map(place.schemas, (schema) => {\n\t\t\tconst known = astIds.get(schema)\n\t\t\tif (known !== undefined) {\n\t\t\t\treturn known\n\t\t\t}\n\t\t\tastIds.set(schema, astIds.size)\n\t\t\treturn astIds.size - 1\n\t\t})\n\t\tconst key = `${Array.from(new Set(ids))\n\t\t\t.sort((a, b) => a - b)\n\t\t\t.join(\n\t\t\t\t\",\",\n\t\t\t)}|${String(place.missing)}|${String(place.top)}|${String(place.plain)}`\n\t\tconst known = placeIds.get(key)\n\t\tif (known !== undefined) {\n\t\t\treturn known\n\t\t}\n\t\tif (queue.length >= Limits.ceilings.catalogFields) {\n\t\t\tthrow Limits.exceeded(\n\t\t\t\t\"A Schema\",\n\t\t\t\t\"catalogFields\",\n\t\t\t\t\"places\",\n\t\t\t\tqueue.length + 1,\n\t\t\t\tLimits.ceilings.catalogFields,\n\t\t\t)\n\t\t}\n\t\tplaceIds.set(key, queue.length)\n\t\tqueue.push(place)\n\t\treturn queue.length - 1\n\t}\n\tidOf(root)\n\treturn Arr.unfold(0, (id) => {\n\t\tconst place = queue[id]\n\t\tif (place === undefined) {\n\t\t\treturn Option.none()\n\t\t}\n\t\tconst made = placeOf(place)\n\t\tconst children = Arr.map(\n\t\t\tmade.children,\n\t\t\t([name, below]) => [name, idOf(below)] as const,\n\t\t)\n\t\tconst branch: Branch = {\n\t\t\t...made.branch,\n\t\t\tid,\n\t\t\tchildren,\n\t\t\tnames: HashMap.fromIterable(children),\n\t\t\tkeyBranch: made.keyPlace === undefined ? undefined : idOf(made.keyPlace),\n\t\t}\n\t\treturn Option.some([branch, id + 1] as const)\n\t})\n}\n\n/** The field at a place, for a path. */\nconst fieldAt = (\n\tbranch: Branch,\n\tpath: ReadonlyArray<Segment>,\n\tparts: number,\n): Field => ({\n\tpath,\n\ttext: textOf(path),\n\tkinds: branch.kinds,\n\t// A struct at the last part that a path may have is one field, since a\n\t// path does not go below it.\n\tlisted: branch.values || (path.length === parts && branch.structs),\n\topen: branch.open,\n\tplain: branch.plain,\n})\n\n/**\n * The fields that a list shows, a field before the fields below it, down to\n * `parts` parts. A path goes into a place that is on it already one time\n * only, so the list of a recursive Schema is finite. The walk keeps a stack\n * of the places to visit, so a deep Schema does not fill the stack. A Schema\n * that shares one part in many places may have very many fields, so the\n * walk counts the fields that it makes, and it stops at the first one past\n * `most`. The stack and the count belong to this walk.\n */\nconst listOf = (\n\tbranches: ReadonlyArray<Branch>,\n\tparts: number,\n\tmost: number,\n\tthing: string,\n): ReadonlyArray<Field> => {\n\tconst count = { fields: 0 }\n\tinterface Visit {\n\t\treadonly branch: Branch\n\t\treadonly path: ReadonlyArray<Segment>\n\t\t/** The ids of the places on the path, as a set of Effect that shares its parts. */\n\t\treadonly above: HashSet.HashSet<number>\n\t}\n\tconst below = (visit: Visit): ReadonlyArray<Visit> => {\n\t\tconst { branch, path } = visit\n\t\tif (\n\t\t\tpath.length === parts ||\n\t\t\t(path.length > 0 && HashSet.has(visit.above, branch.id))\n\t\t) {\n\t\t\treturn []\n\t\t}\n\t\tconst above = HashSet.add(visit.above, branch.id)\n\t\tconst named = Arr.map(branch.children, ([name, id]) => ({\n\t\t\tbranch: branches[id]!,\n\t\t\tpath: [...path, name],\n\t\t\tabove,\n\t\t}))\n\t\treturn branch.key === undefined || branch.keyBranch === undefined\n\t\t\t? named\n\t\t\t: [\n\t\t\t\t\t...named,\n\t\t\t\t\t{\n\t\t\t\t\t\tbranch: branches[branch.keyBranch]!,\n\t\t\t\t\t\tpath: [...path, branch.key],\n\t\t\t\t\t\tabove,\n\t\t\t\t\t},\n\t\t\t\t]\n\t}\n\tconst stack: Array<Visit> = [\n\t\t{ branch: branches[0]!, path: [], above: HashSet.empty() },\n\t]\n\treturn Arr.flatten(\n\t\tArr.unfold(stack, (rest) => {\n\t\t\tconst visit = rest.pop()\n\t\t\tif (visit === undefined) {\n\t\t\t\treturn Option.none()\n\t\t\t}\n\t\t\t// The visits go onto the stack one at a time: a spread into a call\n\t\t\t// would pass one argument for each field, past the most arguments\n\t\t\t// of a call.\n\t\t\tArr.forEach(Arr.reverse(below(visit)), (next) => {\n\t\t\t\trest.push(next)\n\t\t\t})\n\t\t\tconst field =\n\t\t\t\tvisit.path.length === 0\n\t\t\t\t\t? []\n\t\t\t\t\t: [fieldAt(visit.branch, visit.path, parts)]\n\t\t\tcount.fields += field.length\n\t\t\tif (count.fields > most) {\n\t\t\t\tthrow Limits.exceeded(\n\t\t\t\t\tthing,\n\t\t\t\t\t\"catalogFields\",\n\t\t\t\t\t\"fields\",\n\t\t\t\t\tcount.fields,\n\t\t\t\t\tmost,\n\t\t\t\t)\n\t\t\t}\n\t\t\treturn Option.some([field, rest] as const)\n\t\t}),\n\t)\n}\n\n/**\n * The fields that a list shows, a field before the fields below it, for\n * paths of at most `parts` parts. For a recursive Schema, the list goes into\n * each recursive place one time on each path, so it is finite. A list with\n * more than `most` fields is an error, as for a domain with these limits.\n * Each call makes the list again: a domain checks its size one time, and a\n * list of permitted fields reads it.\n */\nexport const fieldsOf = (\n\tcatalog: Catalog,\n\tparts: number = Limits.fieldPathParts,\n\tmost: number = Limits.ceilings.catalogFields,\n\tthing: string = \"A Schema\",\n): ReadonlyArray<Field> => listOf(catalog.branches, parts, most, thing)\n\n/**\n * Makes the catalog of a Schema. It throws when a field name has a dot or is\n * empty, and when the Schema has more places than the ceiling of\n * `catalogFields`. A domain checks the size of the list of the fields with\n * its own limit: see {@link fieldsOf}.\n */\nexport const catalogOf = (ast: SchemaAST.AST): Catalog => {\n\tconst branches = branchesOf({\n\t\tschemas: alternatives(ast, new Set()),\n\t\tmissing: false,\n\t\ttop: false,\n\t\tplain: true,\n\t})\n\treturn {\n\t\tbranches,\n\t\tedges: Arr.reduce(\n\t\t\tbranches,\n\t\t\t0,\n\t\t\t(total, branch) =>\n\t\t\t\ttotal +\n\t\t\t\tbranch.children.length +\n\t\t\t\t(branch.keyBranch === undefined ? 0 : 1),\n\t\t),\n\t\troot: branches[0]!,\n\t}\n}\n\n/**\n * Gives the field of a catalog at a path with dots, if the catalog has it.\n * A path has at most `parts` parts. The lookup follows one place for each\n * part, in a loop.\n */\nexport const lookup = (\n\tcatalog: Catalog,\n\tpath: string,\n\tparts: number = Limits.fieldPathParts,\n): Field | undefined => {\n\tconst names = path.split(\".\", parts + 1)\n\tif (names.length > parts) {\n\t\treturn undefined\n\t}\n\tconst found = Arr.reduce(\n\t\tnames,\n\t\t{ branch: catalog.root, path: [] } as\n\t\t\t| { readonly branch: Branch; readonly path: ReadonlyArray<Segment> }\n\t\t\t| undefined,\n\t\t(current, name) => {\n\t\t\tif (current === undefined) {\n\t\t\t\treturn undefined\n\t\t\t}\n\t\t\tconst named = HashMap.get(current.branch.names, name)\n\t\t\tif (Option.isSome(named)) {\n\t\t\t\treturn {\n\t\t\t\t\tbranch: catalog.branches[named.value]!,\n\t\t\t\t\tpath: [...current.path, name],\n\t\t\t\t}\n\t\t\t}\n\t\t\tconst key = current.branch.key\n\t\t\tconst keyBranch = current.branch.keyBranch\n\t\t\treturn key !== undefined && keyBranch !== undefined && keyFits(key, name)\n\t\t\t\t? { branch: catalog.branches[keyBranch]!, path: [...current.path, key] }\n\t\t\t\t: undefined\n\t\t},\n\t)\n\treturn found === undefined\n\t\t? undefined\n\t\t: fieldAt(found.branch, found.path, parts)\n}\n"],"mappings":"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"}
|
package/dist/internal/decide.js
CHANGED
|
@@ -149,7 +149,7 @@ const permittedFields = (policy, request) => {
|
|
|
149
149
|
const descriptor = kind === void 0 ? void 0 : descriptorOf(policy.domain, kind);
|
|
150
150
|
if (descriptor === void 0) return Effect.succeed([]);
|
|
151
151
|
const rules = policy.pipe(nodeOf, rulesOf);
|
|
152
|
-
const listed = Arr.filter(fieldsOf(descriptor.catalog, policy.domain.limits.fieldPathParts), (field) => field.listed && Arr.every(field.path, (segment) => typeof segment === "string"));
|
|
152
|
+
const listed = Arr.filter(fieldsOf(descriptor.catalog, policy.domain.limits.fieldPathParts, policy.domain.limits.catalogFields), (field) => field.listed && Arr.every(field.path, (segment) => typeof segment === "string"));
|
|
153
153
|
return Effect.map(Effect.filter(listed, (field) => Effect.map(decideAny(policy, {
|
|
154
154
|
...request,
|
|
155
155
|
field: field.text
|