@nmnmcc/ability 2.0.4 → 2.0.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +7 -7
- package/dist/Condition.d.ts +4 -4
- package/dist/Condition.d.ts.map +1 -1
- package/dist/Condition.js.map +1 -1
- package/dist/Domain.d.ts +1 -1
- package/dist/Policy.d.ts +2 -2
- package/dist/Policy.d.ts.map +1 -1
- package/dist/Policy.js.map +1 -1
- package/dist/internal/algebra.js +85 -10
- package/dist/internal/algebra.js.map +1 -1
- package/dist/internal/authorizer.d.ts.map +1 -1
- package/dist/internal/authorizer.js +37 -14
- package/dist/internal/authorizer.js.map +1 -1
- package/dist/internal/bind.js +15 -12
- package/dist/internal/bind.js.map +1 -1
- package/dist/internal/catalog.d.ts +6 -0
- package/dist/internal/catalog.d.ts.map +1 -1
- package/dist/internal/catalog.js +12 -9
- package/dist/internal/catalog.js.map +1 -1
- package/dist/internal/decide.js +1 -1
- package/dist/internal/domain.js +1 -1
- package/dist/internal/json.js +2 -10
- package/dist/internal/json.js.map +1 -1
- package/dist/internal/nodes.d.ts +4 -1
- package/dist/internal/nodes.d.ts.map +1 -1
- package/dist/internal/nodes.js +17 -7
- package/dist/internal/nodes.js.map +1 -1
- package/dist/internal/patterns.js +1 -1
- package/dist/internal/patterns.js.map +1 -1
- package/dist/internal/policy-json.js +2 -8
- package/dist/internal/policy-json.js.map +1 -1
- package/dist/internal/policy-nodes.d.ts +4 -1
- package/dist/internal/policy-nodes.d.ts.map +1 -1
- package/dist/internal/policy-nodes.js +13 -4
- package/dist/internal/policy-nodes.js.map +1 -1
- package/dist/internal/rule.d.ts.map +1 -1
- package/dist/internal/rule.js +6 -6
- package/dist/internal/rule.js.map +1 -1
- package/dist/internal/search.d.ts.map +1 -1
- package/dist/internal/search.js +51 -34
- package/dist/internal/search.js.map +1 -1
- package/package.json +1 -1
- package/src/Condition.ts +5 -2
- package/src/Policy.ts +1 -2
- package/src/internal/algebra.ts +118 -21
- package/src/internal/authorizer.ts +56 -14
- package/src/internal/bind.ts +39 -18
- package/src/internal/catalog.ts +40 -27
- package/src/internal/json.ts +2 -11
- package/src/internal/nodes.ts +21 -11
- package/src/internal/patterns.ts +7 -3
- package/src/internal/policy-json.ts +2 -9
- package/src/internal/policy-nodes.ts +14 -6
- package/src/internal/rule.ts +13 -9
- package/src/internal/search.ts +81 -75
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bind.js","names":["Catalog.accepts","Catalog.lookup","Patterns.parse","Patterns.fitsCatalog","Limits.searchSteps","Catalog.anyKinds","Nodes.isNode","Place.pathOf","Place.at"],"sources":["../../src/internal/bind.ts"],"sourcesContent":["/**\n * The binding of rules to a domain. It is the one check of a rule, for a rule\n * from the builder, from `Policy.rule`, and from JSON alike. It reads the\n * catalogs of the resources and of the principal, and it gives one issue for\n * each problem, with the path of the problem and, for a name with a mistake,\n * the name that it may mean.\n */\nimport * as Arr from \"effect/Array\"\nimport * as HashMap from \"effect/HashMap\"\nimport * as MutableHashMap from \"effect/MutableHashMap\"\nimport * as Option from \"effect/Option\"\nimport * as Result from \"effect/Result\"\nimport type * as Domain from \"../Domain.ts\"\nimport type * as Resource from \"../Resource.ts\"\nimport * as Catalog from \"./catalog.ts\"\nimport * as Limits from \"./limits.ts\"\nimport * as Nodes from \"./nodes.ts\"\nimport * as Patterns from \"./patterns.ts\"\nimport * as Place from \"./place.ts\"\nimport { withSuggestion } from \"./suggest.ts\"\n\n/** One problem of a rule or a policy, with its path. */\nexport interface Issue {\n\treadonly path: ReadonlyArray<PropertyKey>\n\treadonly issue: string\n}\n\n/** The data of a rule that the binding reads. */\nexport interface RuleData {\n\treadonly actions: ReadonlyArray<string>\n\treadonly resources: ReadonlyArray<string>\n\treadonly when?: unknown\n\treadonly fields?: ReadonlyArray<string>\n}\n\n/**\n * The memo of one binding, for all its rules. A policy may name one wrong\n * field or one pattern in very many places, so the work on each is done one\n * time. It goes away when the binding ends.\n */\ninterface Memo {\n\t/** The suggestion for each name, by the place where it was looked for. */\n\treadonly suggestions: MutableHashMap.MutableHashMap<string, string>\n\t/** If a pattern matches a field of a kind, by the kind and the pattern. */\n\treadonly fits: MutableHashMap.MutableHashMap<string, boolean>\n\t/** The count of the issues of conditions so far. It belongs to one binding. */\n\treadonly issues: { count: number; noted: boolean }\n\t/** The steps of the checks of patterns so far. It belongs to one binding. */\n\treadonly work: { steps: number; noted: boolean }\n}\n\nconst makeMemo = (): Memo => ({\n\tsuggestions: MutableHashMap.empty(),\n\tfits: MutableHashMap.empty(),\n\tissues: { count: 0, noted: false },\n\twork: { steps: 0, noted: false },\n})\n\n/**\n * The most names that one binding makes a suggestion for. A suggestion\n * compares the name with every name of a catalog, so a policy with very many\n * wrong names would take a long time.\n */\nconst maxSuggestions = 32\n\n/** Adds a suggestion to a message, with the memo of the binding. */\nconst suggestIn = (\n\tmemo: Memo,\n\tmessage: string,\n\tname: string,\n\tscope: string,\n\tnames: () => ReadonlyArray<string>,\n): string => {\n\tconst key = `${scope}\\u0000${name}`\n\tconst known = MutableHashMap.get(memo.suggestions, key)\n\tif (Option.isSome(known)) {\n\t\treturn known.value\n\t}\n\tif (MutableHashMap.size(memo.suggestions) >= maxSuggestions) {\n\t\treturn message\n\t}\n\tconst made = withSuggestion(message, name, names())\n\tMutableHashMap.set(memo.suggestions, key, made)\n\treturn made\n}\n\n/** The kinds that a rule may run on, from the view of its checks. */\ninterface Context {\n\treadonly domain: Domain.Any\n\treadonly memo: Memo\n\t/** The rule is for every resource (`\"*\"`). */\n\treadonly all: boolean\n\t/** The resources that the rule names and that the domain has. */\n\treadonly named: ReadonlyArray<Resource.Descriptor>\n\t/** The named resources that have one of the actions of the rule. */\n\treadonly applicable: ReadonlyArray<Resource.Descriptor>\n\t/**\n\t * The resources whose paths a path test may read: every resource with one\n\t * of the actions, for a rule for every resource.\n\t */\n\treadonly paths: ReadonlyArray<Resource.Descriptor>\n}\n\ntype Operand = Nodes.Operand\n\n/**\n * Tests if a field may hold a value of the type of a fixed value. A test with\n * `null` is a test that the field is missing, so `null` always fits. A number\n * fits any field of numbers: a test of an integer field with `1.5` or\n * `Infinity` has a meaning, as `lt` shows.\n */\nconst fitsLiteral = (kinds: Catalog.Kinds, value: unknown): boolean =>\n\tvalue === null ||\n\t(typeof value === \"number\" && kinds.number !== \"none\") ||\n\tCatalog.accepts(kinds, value)\n\n/**\n * Tests if two fields may hold one scalar. A principal field that is `null`\n * or that holds an object is a missing value, and a test with a missing value\n * is false, so only scalars count.\n */\nconst meet = (left: Catalog.Kinds, right: Catalog.Kinds): boolean =>\n\t(left.string && right.string) ||\n\t(left.boolean && right.boolean) ||\n\t(left.number !== \"none\" && right.number !== \"none\") ||\n\tArr.some(left.literals, (value) => Catalog.accepts(right, value)) ||\n\tArr.some(right.literals, (value) => Catalog.accepts(left, value))\n\nconst hasStrings = (kinds: Catalog.Kinds): boolean =>\n\tkinds.string || Arr.some(kinds.literals, (value) => typeof value === \"string\")\n\nconst hasNumbers = (kinds: Catalog.Kinds): boolean =>\n\tkinds.number !== \"none\" ||\n\tArr.some(kinds.literals, (value) => typeof value === \"number\")\n\n/** Tests if a field may hold a value with an order: a number or a string. */\nconst isOrdered = (kinds: Catalog.Kinds): boolean =>\n\thasStrings(kinds) || hasNumbers(kinds)\n\n/** Tests if two fields may hold values of one ordered type. */\nconst orderedMeet = (left: Catalog.Kinds, right: Catalog.Kinds): boolean =>\n\t(hasStrings(left) && hasStrings(right)) ||\n\t(hasNumbers(left) && hasNumbers(right))\n\n/** The texts of the fields of a catalog that have only names, for suggestions. */\nconst namesOf = (catalog: Catalog.Catalog): ReadonlyArray<string> =>\n\tArr.map(\n\t\tArr.filter(catalog.fields, (field) =>\n\t\t\tArr.every(field.path, (segment) => typeof segment === \"string\"),\n\t\t),\n\t\t(field) => field.text,\n\t)\n\n/** The issue of a field that is not a field of every resource of the rule. */\nconst fieldIssue = (\n\tcontext: Context,\n\tfield: string,\n\tat: ReadonlyArray<PropertyKey>,\n): ReadonlyArray<Issue> => {\n\tconst message = `The field \"${field}\" is not a field of every resource of the rule`\n\tif (context.all || context.named.length === 0) {\n\t\treturn [{ path: at, issue: message }]\n\t}\n\tconst without = Arr.filter(\n\t\tcontext.named,\n\t\t(descriptor) => Catalog.lookup(descriptor.catalog, field) === undefined,\n\t)\n\treturn without.length === 0\n\t\t? []\n\t\t: [\n\t\t\t\t{\n\t\t\t\t\tpath: at,\n\t\t\t\t\tissue: suggestIn(context.memo, message, field, without[0]!.kind, () =>\n\t\t\t\t\t\tnamesOf(without[0]!.catalog),\n\t\t\t\t\t),\n\t\t\t\t},\n\t\t\t]\n}\n\n/**\n * The issues of a field that a condition reads: a field that is not a field\n * of every resource of the rule, or a field that is not plain data in one of\n * them. A read of a value that is not plain data, as a getter of a class, is\n * a defect in a decision. A residual may leave the read out, so the residual\n * would say \"yes\" where the decision fails. So the binding does not take\n * such a read: see `Catalog.Field.plain`.\n */\nconst readIssues = (\n\tcontext: Context,\n\tfield: string,\n\tat: ReadonlyArray<PropertyKey>,\n): ReadonlyArray<Issue> => {\n\tconst exists = fieldIssue(context, field, at)\n\tif (exists.length > 0) {\n\t\treturn exists\n\t}\n\treturn Arr.some(\n\t\tcontext.named,\n\t\t(descriptor) => !(Catalog.lookup(descriptor.catalog, field)?.plain ?? true),\n\t)\n\t\t? [\n\t\t\t\t{\n\t\t\t\t\tpath: at,\n\t\t\t\t\tissue: `A condition may not read the field \"${field}\". A value above it may be an object that does not have it as a data field, as a class with a getter, a date or a list. Use a predicate for it.`,\n\t\t\t\t},\n\t\t\t]\n\t\t: []\n}\n\n/**\n * The issues of a field pattern: a pattern that does not parse, or that\n * matches no field of the resources of the rule, as a pattern with a mistake\n * in a name does.\n */\nconst patternIssues = (\n\tmemo: Memo,\n\tknown: ReadonlyArray<Resource.Descriptor>,\n\tpattern: string,\n\tat: ReadonlyArray<PropertyKey>,\n): ReadonlyArray<Issue> =>\n\tResult.match(Patterns.parse(pattern), {\n\t\tonFailure: (issue) => [{ path: at, issue }],\n\t\tonSuccess: (parsed) => {\n\t\t\t// A rule with no known resource has an issue for that already. The\n\t\t\t// pattern is checked on every kind, not only up to the first that it\n\t\t\t// fits: a decision on a kind does this check, so the limit of the\n\t\t\t// work below is a limit of the work of a decision too.\n\t\t\tconst fits =\n\t\t\t\tknown.length === 0 ||\n\t\t\t\tArr.reduce(known, false, (before, descriptor) => {\n\t\t\t\t\tconst key = `${descriptor.kind}\\u0000${pattern}`\n\t\t\t\t\treturn (\n\t\t\t\t\t\tOption.getOrElse(MutableHashMap.get(memo.fits, key), () => {\n\t\t\t\t\t\t\t// Each check of a pattern reads the fields of a catalog. The\n\t\t\t\t\t\t\t// work of one binding has a limit, so very many patterns stop\n\t\t\t\t\t\t\t// with one issue, and the policy does not load.\n\t\t\t\t\t\t\tmemo.work.steps +=\n\t\t\t\t\t\t\t\tdescriptor.catalog.fields.length * Math.max(parsed.length, 1)\n\t\t\t\t\t\t\tif (memo.work.steps > Limits.searchSteps) {\n\t\t\t\t\t\t\t\treturn true\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tconst found = Patterns.fitsCatalog(parsed, descriptor.catalog)\n\t\t\t\t\t\t\tMutableHashMap.set(memo.fits, key, found)\n\t\t\t\t\t\t\treturn found\n\t\t\t\t\t\t}) || before\n\t\t\t\t\t)\n\t\t\t\t})\n\t\t\tif (memo.work.steps > Limits.searchSteps && !memo.work.noted) {\n\t\t\t\tmemo.work.noted = true\n\t\t\t\treturn [\n\t\t\t\t\t{\n\t\t\t\t\t\tpath: at,\n\t\t\t\t\t\tissue: `The field patterns of the policy take more than ${String(Limits.searchSteps)} steps to check. The most is ${String(Limits.searchSteps)}.`,\n\t\t\t\t\t},\n\t\t\t\t]\n\t\t\t}\n\t\t\treturn fits\n\t\t\t\t? []\n\t\t\t\t: [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tpath: at,\n\t\t\t\t\t\t\tissue: `The field pattern \"${pattern}\" matches no field of the resources of the rule`,\n\t\t\t\t\t\t},\n\t\t\t\t\t]\n\t\t},\n\t})\n\n/** The fields of the resources that a test of the rule may read. */\nconst fieldsAt = (\n\tcontext: Context,\n\tfield: string,\n): ReadonlyArray<Catalog.Field> =>\n\tArr.flatMap(context.applicable, (descriptor) => {\n\t\tconst found = Catalog.lookup(descriptor.catalog, field)\n\t\treturn found === undefined ? [] : [found]\n\t})\n\n/** The field of the principal that an operand names, or the issue when there is none. */\nconst principalField = (\n\tcontext: Context,\n\tref: string,\n\tat: ReadonlyArray<PropertyKey>,\n): Catalog.Field | Issue => {\n\tconst found = Catalog.lookup(context.domain.principalCatalog, ref)\n\treturn found !== undefined\n\t\t? found\n\t\t: {\n\t\t\t\tpath: at,\n\t\t\t\tissue: suggestIn(\n\t\t\t\t\tcontext.memo,\n\t\t\t\t\t`The principal has no field \"${ref}\"`,\n\t\t\t\t\tref,\n\t\t\t\t\t\"~principal\",\n\t\t\t\t\t() => namesOf(context.domain.principalCatalog),\n\t\t\t\t),\n\t\t\t}\n}\n\nconst isIssue = (value: Catalog.Field | Issue): value is Issue =>\n\t\"issue\" in value\n\n/** The issues of one operand of a test of a field. */\nconst operandIssues = (\n\tcontext: Context,\n\tfield: string,\n\tordered: boolean,\n\toperand: Operand,\n\tat: ReadonlyArray<PropertyKey>,\n): ReadonlyArray<Issue> => {\n\tconst fields = fieldsAt(context, field)\n\tif (operand._tag === \"Literal\") {\n\t\tconst value = operand.value\n\t\tif (ordered && typeof value !== \"string\" && typeof value !== \"number\") {\n\t\t\treturn [\n\t\t\t\t{\n\t\t\t\t\tpath: at,\n\t\t\t\t\tissue: `The field \"${field}\" has an ordered test with a value that has no order`,\n\t\t\t\t},\n\t\t\t]\n\t\t}\n\t\treturn [\n\t\t\t...(Arr.some(fields, (found) => !fitsLiteral(found.kinds, value))\n\t\t\t\t? [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tpath: at,\n\t\t\t\t\t\t\tissue: `The field \"${field}\" can not hold the value ${JSON.stringify(value)}`,\n\t\t\t\t\t\t},\n\t\t\t\t\t]\n\t\t\t\t: []),\n\t\t\t...(ordered && Arr.some(fields, (found) => !isOrdered(found.kinds))\n\t\t\t\t? [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tpath: at,\n\t\t\t\t\t\t\tissue: `The field \"${field}\" has an ordered test with a value that has no order`,\n\t\t\t\t\t\t},\n\t\t\t\t\t]\n\t\t\t\t: []),\n\t\t]\n\t}\n\tconst principal = principalField(context, operand.path, at)\n\tif (isIssue(principal)) {\n\t\treturn [principal]\n\t}\n\treturn [\n\t\t...(Arr.some(fields, (found) => !meet(found.kinds, principal.kinds))\n\t\t\t? [\n\t\t\t\t\t{\n\t\t\t\t\t\tpath: at,\n\t\t\t\t\t\tissue: `The field \"${field}\" can not hold a value of the principal field \"${operand.path}\"`,\n\t\t\t\t\t},\n\t\t\t\t]\n\t\t\t: []),\n\t\t...(ordered &&\n\t\tArr.some(fields, (found) => !orderedMeet(found.kinds, principal.kinds))\n\t\t\t? [\n\t\t\t\t\t{\n\t\t\t\t\t\tpath: at,\n\t\t\t\t\t\tissue: `The field \"${field}\" has an ordered test with a value that has no order`,\n\t\t\t\t\t},\n\t\t\t\t]\n\t\t\t: []),\n\t]\n}\n\n/**\n * Tests if the path of a resource may start with a prefix: it is as long as\n * the prefix, and each fixed string of the prefix is the string of the path\n * at its place, when the path has a string there.\n */\nconst mayStartWith = (\n\tdescriptor: Resource.Descriptor,\n\tstrings: ReadonlyArray<string | null>,\n): boolean =>\n\t(descriptor.path?.length ?? 0) >= strings.length &&\n\tArr.every(strings, (string, index) => {\n\t\tconst segment = descriptor.path?.[index]\n\t\treturn string === null || typeof segment !== \"string\" || segment === string\n\t})\n\n/** The kinds of the part of the path of a resource at a place. */\nconst pathPartKinds = (\n\tdescriptor: Resource.Descriptor,\n\tindex: number,\n): Catalog.Kinds | string | undefined => {\n\tconst segment = descriptor.path?.[index]\n\treturn segment === undefined || typeof segment === \"string\"\n\t\t? segment\n\t\t: (Catalog.lookup(descriptor.catalog, segment.field)?.kinds ??\n\t\t\t\tCatalog.anyKinds)\n}\n\n/** The issues of the prefix of a path test. */\nconst underIssues = (\n\tcontext: Context,\n\tprefix: ReadonlyArray<Operand>,\n\tat: ReadonlyArray<PropertyKey>,\n): ReadonlyArray<Issue> => {\n\tconst strings = Arr.map(prefix, (operand) =>\n\t\toperand._tag === \"Literal\" && typeof operand.value === \"string\"\n\t\t\t? operand.value\n\t\t\t: null,\n\t)\n\t// The test can match only a path that starts with the fixed strings of\n\t// the prefix, so the other resources are left out.\n\tconst candidates = Arr.filter(context.paths, (descriptor) =>\n\t\tmayStartWith(descriptor, strings),\n\t)\n\treturn Arr.flatMap(prefix, (operand, index): ReadonlyArray<Issue> => {\n\t\tconst place = [...at, \"prefix\", index]\n\t\tif (operand._tag === \"Literal\") {\n\t\t\treturn Arr.some(candidates, (descriptor) => {\n\t\t\t\tconst kinds = pathPartKinds(descriptor, index)\n\t\t\t\treturn typeof kinds === \"object\" && !fitsLiteral(kinds, operand.value)\n\t\t\t})\n\t\t\t\t? [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tpath: place,\n\t\t\t\t\t\t\tissue: `The path part ${String(index)} can not hold the value ${JSON.stringify(operand.value)}`,\n\t\t\t\t\t\t},\n\t\t\t\t\t]\n\t\t\t\t: []\n\t\t}\n\t\tconst principal = principalField(context, operand.path, place)\n\t\tif (isIssue(principal)) {\n\t\t\treturn [principal]\n\t\t}\n\t\treturn Arr.some(candidates, (descriptor) => {\n\t\t\tconst kinds = pathPartKinds(descriptor, index)\n\t\t\treturn kinds === undefined\n\t\t\t\t? true\n\t\t\t\t: typeof kinds === \"string\"\n\t\t\t\t\t? !Catalog.accepts(principal.kinds, kinds)\n\t\t\t\t\t: !meet(kinds, principal.kinds)\n\t\t})\n\t\t\t? [\n\t\t\t\t\t{\n\t\t\t\t\t\tpath: place,\n\t\t\t\t\t\tissue: `The principal field \"${operand.path}\" can not hold a part of the path of the resource`,\n\t\t\t\t\t},\n\t\t\t\t]\n\t\t\t: []\n\t})\n}\n\nconst notWhole = (at: ReadonlyArray<PropertyKey>): Issue => ({\n\tpath: at,\n\tissue: \"The condition is not a whole condition tree\",\n})\n\n/**\n * The most issues that the walk of the conditions of one binding gives. A\n * policy may have very many wrong tests, and each issue keeps its path.\n */\nconst maxIssues = 100\n\n/** A path that the walk makes only when it needs it, for an issue. */\ntype LazyPath = () => ReadonlyArray<PropertyKey>\n\n/**\n * Puts the path of a node before the paths of its issues. A walk keeps no\n * path for a node with no issue, so a deep and wide condition costs little.\n */\nconst under = (\n\tmemo: Memo,\n\tat: LazyPath,\n\tissues: ReadonlyArray<Issue>,\n): ReadonlyArray<Issue> => {\n\tif (issues.length === 0) {\n\t\treturn issues\n\t}\n\tconst room = maxIssues - memo.issues.count\n\tmemo.issues.count += issues.length\n\tconst kept = Arr.map(Arr.take(issues, Math.max(room, 0)), (issue) => ({\n\t\tpath: [...at(), ...issue.path],\n\t\tissue: issue.issue,\n\t}))\n\tconst first = memo.issues.count > maxIssues && !memo.issues.noted\n\tmemo.issues.noted ||= first\n\treturn first\n\t\t? [\n\t\t\t\t...kept,\n\t\t\t\t{\n\t\t\t\t\tpath: [],\n\t\t\t\t\tissue: `The policy has more problems. The first ${String(maxIssues)} are here.`,\n\t\t\t\t},\n\t\t\t]\n\t\t: kept\n}\n\n/**\n * The issues of a condition. The constructors of the nodes checked the shape\n * of each node, so this checks the names and the types against the domain. A\n * node that the condition uses in many places is checked one time. The nodes\n * it has seen are in a native `Set`, because it must find a node by the node\n * itself: `Equal` compares nodes by structure.\n */\nconst conditionIssues = (\n\tcontext: Context,\n\tnode: Nodes.Node,\n\tat: LazyPath,\n\tseen: Set<Nodes.Node>,\n): ReadonlyArray<Issue> => {\n\tif (seen.has(node)) {\n\t\treturn []\n\t}\n\tseen.add(node)\n\tconst here = (issues: ReadonlyArray<Issue>) => under(context.memo, at, issues)\n\tswitch (node._tag) {\n\t\tcase \"Compare\": {\n\t\t\tconst exists = readIssues(context, node.field, [\"field\"])\n\t\t\treturn here(\n\t\t\t\texists.length > 0\n\t\t\t\t\t? exists\n\t\t\t\t\t: operandIssues(\n\t\t\t\t\t\t\tcontext,\n\t\t\t\t\t\t\tnode.field,\n\t\t\t\t\t\t\tnode.op !== \"eq\" && node.op !== \"ne\",\n\t\t\t\t\t\t\tnode.value,\n\t\t\t\t\t\t\t[\"value\"],\n\t\t\t\t\t\t),\n\t\t\t)\n\t\t}\n\t\tcase \"In\": {\n\t\t\tconst exists = readIssues(context, node.field, [\"field\"])\n\t\t\treturn here(\n\t\t\t\texists.length > 0\n\t\t\t\t\t? exists\n\t\t\t\t\t: Arr.flatMap(node.values, (operand, index) =>\n\t\t\t\t\t\t\toperandIssues(context, node.field, false, operand, [\n\t\t\t\t\t\t\t\t\"values\",\n\t\t\t\t\t\t\t\tindex,\n\t\t\t\t\t\t\t]),\n\t\t\t\t\t\t),\n\t\t\t)\n\t\t}\n\t\tcase \"Exists\":\n\t\t\treturn here(readIssues(context, node.field, [\"field\"]))\n\t\tcase \"Under\":\n\t\t\treturn here(underIssues(context, node.prefix, []))\n\t\tcase \"And\":\n\t\tcase \"Or\":\n\t\t\treturn Arr.flatMap(node.conditions, (child, index) =>\n\t\t\t\tconditionIssues(\n\t\t\t\t\tcontext,\n\t\t\t\t\tchild,\n\t\t\t\t\t() => [...at(), \"conditions\", index],\n\t\t\t\t\tseen,\n\t\t\t\t),\n\t\t\t)\n\t\tcase \"Not\":\n\t\t\treturn conditionIssues(\n\t\t\t\tcontext,\n\t\t\t\tnode.condition,\n\t\t\t\t() => [...at(), \"condition\"],\n\t\t\t\tseen,\n\t\t\t)\n\t\tdefault:\n\t\t\treturn []\n\t}\n}\n\nconst hasAction = (\n\tdescriptor: Resource.Descriptor,\n\tactions: ReadonlyArray<string>,\n): boolean =>\n\tactions.includes(\"*\") ||\n\tArr.some(actions, (action) => descriptor.actions.includes(action))\n\n/**\n * Checks one rule against a domain. It gives one issue for each problem. `at`\n * is the path of the rule, for the paths of the issues.\n */\nexport const ruleIssues = (\n\tdomain: Domain.Any,\n\trule: RuleData,\n\tat: ReadonlyArray<PropertyKey>,\n\tmemo: Memo = makeMemo(),\n): ReadonlyArray<Issue> => {\n\tconst all = rule.resources.includes(\"*\")\n\t// Each kind is checked one time, also when the list names it again.\n\tconst names = Arr.dedupe(Arr.filter(rule.resources, (kind) => kind !== \"*\"))\n\tconst kinds = Array.from(HashMap.keys(domain.resources))\n\tconst named = Arr.flatMap(names, (kind) =>\n\t\tOption.toArray(HashMap.get(domain.resources, kind)),\n\t)\n\tconst known = all ? Array.from(HashMap.values(domain.resources)) : named\n\t// The rule never runs on a kind without one of its actions, so the types\n\t// of its tests are checked only on the kinds with one.\n\tconst applicable = Arr.filter(named, (descriptor) =>\n\t\thasAction(descriptor, rule.actions),\n\t)\n\tconst context: Context = {\n\t\tdomain,\n\t\tmemo,\n\t\tall,\n\t\tnamed,\n\t\tapplicable,\n\t\tpaths: all\n\t\t\t? Arr.filter(known, (descriptor) => hasAction(descriptor, rule.actions))\n\t\t\t: applicable,\n\t}\n\tconst actions = Arr.dedupe(\n\t\tArr.flatMap(known, (descriptor) => descriptor.actions),\n\t)\n\treturn [\n\t\t...Arr.map(\n\t\t\tArr.filter(names, (kind) => !HashMap.has(domain.resources, kind)),\n\t\t\t(kind): Issue => ({\n\t\t\t\tpath: [...at, \"resources\"],\n\t\t\t\tissue: suggestIn(\n\t\t\t\t\tmemo,\n\t\t\t\t\t`There is no resource with the kind \"${kind}\"`,\n\t\t\t\t\tkind,\n\t\t\t\t\t\"~kinds\",\n\t\t\t\t\t() => kinds,\n\t\t\t\t),\n\t\t\t}),\n\t\t),\n\t\t...Arr.map(\n\t\t\tArr.filter(\n\t\t\t\trule.actions,\n\t\t\t\t(action) => action !== \"*\" && !actions.includes(action),\n\t\t\t),\n\t\t\t(action): Issue => ({\n\t\t\t\tpath: [...at, \"actions\"],\n\t\t\t\tissue: suggestIn(\n\t\t\t\t\tmemo,\n\t\t\t\t\t`The action \"${action}\" is not an action of these resources`,\n\t\t\t\t\taction,\n\t\t\t\t\t`~actions\\u0000${names.join(\",\")}`,\n\t\t\t\t\t() => actions,\n\t\t\t\t),\n\t\t\t}),\n\t\t),\n\t\t...Arr.flatMap(rule.fields ?? [], (field, index) =>\n\t\t\tfield.includes(\"*\")\n\t\t\t\t? patternIssues(memo, known, field, [...at, \"fields\", index])\n\t\t\t\t: fieldIssue(context, field, [...at, \"fields\", index]),\n\t\t),\n\t\t...(rule.when === undefined\n\t\t\t? []\n\t\t\t: Nodes.isNode(rule.when)\n\t\t\t\t? conditionIssues(context, rule.when, () => [...at, \"when\"], new Set())\n\t\t\t\t: [notWhole([...at, \"when\"])]),\n\t]\n}\n\n/** The JSON form of a policy tree, or a policy tree, as the binding reads it. */\ntype TreeNode =\n\t| { readonly _tag: \"Rules\"; readonly rules: ReadonlyArray<RuleData> }\n\t| {\n\t\t\treadonly _tag: \"All\" | \"Any\" | \"FirstOf\"\n\t\t\treadonly policies: ReadonlyArray<TreeNode>\n\t }\n\t| {\n\t\t\treadonly _tag: \"Ceiling\"\n\t\t\treadonly cap: TreeNode\n\t\t\treadonly policy: TreeNode\n\t }\n\n/** Checks each rule of a policy tree against a domain. */\nexport const treeIssues = (\n\tdomain: Domain.Any,\n\troot: TreeNode,\n): ReadonlyArray<Issue> => {\n\t// The rules come out of the tree first, and each one is checked after.\n\t// A check of a condition goes down its levels, so a check inside the walk\n\t// of the tree would go down the levels of both.\n\t// The place of each rule shares the path of its parent, and the full path\n\t// is made only when its rule is checked. A deep tree with very many rules\n\t// would need very much memory for a full copy of the path of each rule.\n\tconst rulesOf = (\n\t\tnode: TreeNode,\n\t\tplace: Place.Place | undefined,\n\t\tlevel: number,\n\t): ReadonlyArray<Issue | readonly [RuleData, Place.Place]> => {\n\t\tif (level > Limits.policyDepth) {\n\t\t\treturn [\n\t\t\t\t{\n\t\t\t\t\tpath: Place.pathOf(place),\n\t\t\t\t\tissue: `A policy tree has more than ${String(Limits.policyDepth)} levels. The most is ${String(Limits.policyDepth)}.`,\n\t\t\t\t},\n\t\t\t]\n\t\t}\n\t\tswitch (node._tag) {\n\t\t\tcase \"Rules\":\n\t\t\t\treturn Arr.map(\n\t\t\t\t\tnode.rules,\n\t\t\t\t\t(rule, index) => [rule, Place.at(place, \"rules\", index)] as const,\n\t\t\t\t)\n\t\t\tcase \"All\":\n\t\t\tcase \"Any\":\n\t\t\tcase \"FirstOf\":\n\t\t\t\treturn Arr.flatMap(node.policies, (child, index) =>\n\t\t\t\t\trulesOf(child, Place.at(place, \"policies\", index), level + 1),\n\t\t\t\t)\n\t\t\tcase \"Ceiling\":\n\t\t\t\treturn [\n\t\t\t\t\t...rulesOf(node.cap, Place.at(place, \"cap\"), level + 1),\n\t\t\t\t\t...rulesOf(node.policy, Place.at(place, \"policy\"), level + 1),\n\t\t\t\t]\n\t\t}\n\t}\n\tconst memo = makeMemo()\n\treturn Arr.flatMap(rulesOf(root, undefined, 1), (found) =>\n\t\tArray.isArray(found)\n\t\t\t? ruleIssues(domain, found[0], Place.pathOf(found[1]), memo)\n\t\t\t: [found as Issue],\n\t)\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;AAmDA,MAAM,kBAAwB;CAC7B,aAAa,eAAe,MAAM;CAClC,MAAM,eAAe,MAAM;CAC3B,QAAQ;EAAE,OAAO;EAAG,OAAO;CAAM;CACjC,MAAM;EAAE,OAAO;EAAG,OAAO;CAAM;AAChC;;;;;;AAOA,MAAM,iBAAiB;;AAGvB,MAAM,aACL,MACA,SACA,MACA,OACA,UACY;CACZ,MAAM,MAAM,GAAG,MAAM,QAAQ;CAC7B,MAAM,QAAQ,eAAe,IAAI,KAAK,aAAa,GAAG;CACtD,IAAI,OAAO,OAAO,KAAK,GACtB,OAAO,MAAM;CAEd,IAAI,eAAe,KAAK,KAAK,WAAW,KAAK,gBAC5C,OAAO;CAER,MAAM,OAAO,eAAe,SAAS,MAAM,MAAM,CAAC;CAClD,eAAe,IAAI,KAAK,aAAa,KAAK,IAAI;CAC9C,OAAO;AACR;;;;;;;AA2BA,MAAM,eAAe,OAAsB,UAC1C,UAAU,QACT,OAAO,UAAU,YAAY,MAAM,WAAW,UAC/CA,QAAgB,OAAO,KAAK;;;;;;AAO7B,MAAM,QAAQ,MAAqB,UACjC,KAAK,UAAU,MAAM,UACrB,KAAK,WAAW,MAAM,WACtB,KAAK,WAAW,UAAU,MAAM,WAAW,UAC5C,IAAI,KAAK,KAAK,WAAW,UAAUA,QAAgB,OAAO,KAAK,CAAC,KAChE,IAAI,KAAK,MAAM,WAAW,UAAUA,QAAgB,MAAM,KAAK,CAAC;AAEjE,MAAM,cAAc,UACnB,MAAM,UAAU,IAAI,KAAK,MAAM,WAAW,UAAU,OAAO,UAAU,QAAQ;AAE9E,MAAM,cAAc,UACnB,MAAM,WAAW,UACjB,IAAI,KAAK,MAAM,WAAW,UAAU,OAAO,UAAU,QAAQ;;AAG9D,MAAM,aAAa,UAClB,WAAW,KAAK,KAAK,WAAW,KAAK;;AAGtC,MAAM,eAAe,MAAqB,UACxC,WAAW,IAAI,KAAK,WAAW,KAAK,KACpC,WAAW,IAAI,KAAK,WAAW,KAAK;;AAGtC,MAAM,WAAW,YAChB,IAAI,IACH,IAAI,OAAO,QAAQ,SAAS,UAC3B,IAAI,MAAM,MAAM,OAAO,YAAY,OAAO,YAAY,QAAQ,CAC/D,IACC,UAAU,MAAM,IAClB;;AAGD,MAAM,cACL,SACA,OACA,OAC0B;CAC1B,MAAM,UAAU,cAAc,MAAM;CACpC,IAAI,QAAQ,OAAO,QAAQ,MAAM,WAAW,GAC3C,OAAO,CAAC;EAAE,MAAM;EAAI,OAAO;CAAQ,CAAC;CAErC,MAAM,UAAU,IAAI,OACnB,QAAQ,QACP,eAAeC,OAAe,WAAW,SAAS,KAAK,MAAM,KAAA,CAC/D;CACA,OAAO,QAAQ,WAAW,IACvB,CAAC,IACD,CACA;EACC,MAAM;EACN,OAAO,UAAU,QAAQ,MAAM,SAAS,OAAO,QAAQ,EAAE,CAAE,YAC1D,QAAQ,QAAQ,EAAE,CAAE,OAAO,CAC5B;CACD,CACD;AACH;;;;;;;;;AAUA,MAAM,cACL,SACA,OACA,OAC0B;CAC1B,MAAM,SAAS,WAAW,SAAS,OAAO,EAAE;CAC5C,IAAI,OAAO,SAAS,GACnB,OAAO;CAER,OAAO,IAAI,KACV,QAAQ,QACP,eAAe,EAAEA,OAAe,WAAW,SAAS,KAAK,CAAC,EAAE,SAAS,KACvE,IACG,CACA;EACC,MAAM;EACN,OAAO,uCAAuC,MAAM;CACrD,CACD,IACC,CAAC;AACL;;;;;;AAOA,MAAM,iBACL,MACA,OACA,SACA,OAEA,OAAO,MAAMC,MAAe,OAAO,GAAG;CACrC,YAAY,UAAU,CAAC;EAAE,MAAM;EAAI;CAAM,CAAC;CAC1C,YAAY,WAAW;EAKtB,MAAM,OACL,MAAM,WAAW,KACjB,IAAI,OAAO,OAAO,QAAQ,QAAQ,eAAe;GAChD,MAAM,MAAM,GAAG,WAAW,KAAK,QAAQ;GACvC,OACC,OAAO,UAAU,eAAe,IAAI,KAAK,MAAM,GAAG,SAAS;IAI1D,KAAK,KAAK,SACT,WAAW,QAAQ,OAAO,SAAS,KAAK,IAAI,OAAO,QAAQ,CAAC;IAC7D,IAAI,KAAK,KAAK,QAAQ,KACrB,OAAO;IAER,MAAM,QAAQC,YAAqB,QAAQ,WAAW,OAAO;IAC7D,eAAe,IAAI,KAAK,MAAM,KAAK,KAAK;IACxC,OAAO;GACR,CAAC,KAAK;EAER,CAAC;EACF,IAAI,KAAK,KAAK,QAAQ,OAAsB,CAAC,KAAK,KAAK,OAAO;GAC7D,KAAK,KAAK,QAAQ;GAClB,OAAO,CACN;IACC,MAAM;IACN,OAAO,mDAAmD,OAAOC,WAAkB,EAAE,+BAA+B,OAAOA,WAAkB,EAAE;GAChJ,CACD;EACD;EACA,OAAO,OACJ,CAAC,IACD,CACA;GACC,MAAM;GACN,OAAO,sBAAsB,QAAQ;EACtC,CACD;CACH;AACD,CAAC;;AAGF,MAAM,YACL,SACA,UAEA,IAAI,QAAQ,QAAQ,aAAa,eAAe;CAC/C,MAAM,QAAQH,OAAe,WAAW,SAAS,KAAK;CACtD,OAAO,UAAU,KAAA,IAAY,CAAC,IAAI,CAAC,KAAK;AACzC,CAAC;;AAGF,MAAM,kBACL,SACA,KACA,OAC2B;CAC3B,MAAM,QAAQA,OAAe,QAAQ,OAAO,kBAAkB,GAAG;CACjE,OAAO,UAAU,KAAA,IACd,QACA;EACA,MAAM;EACN,OAAO,UACN,QAAQ,MACR,+BAA+B,IAAI,IACnC,KACA,oBACM,QAAQ,QAAQ,OAAO,gBAAgB,CAC9C;CACD;AACH;AAEA,MAAM,WAAW,UAChB,WAAW;;AAGZ,MAAM,iBACL,SACA,OACA,SACA,SACA,OAC0B;CAC1B,MAAM,SAAS,SAAS,SAAS,KAAK;CACtC,IAAI,QAAQ,SAAS,WAAW;EAC/B,MAAM,QAAQ,QAAQ;EACtB,IAAI,WAAW,OAAO,UAAU,YAAY,OAAO,UAAU,UAC5D,OAAO,CACN;GACC,MAAM;GACN,OAAO,cAAc,MAAM;EAC5B,CACD;EAED,OAAO,CACN,GAAI,IAAI,KAAK,SAAS,UAAU,CAAC,YAAY,MAAM,OAAO,KAAK,CAAC,IAC7D,CACA;GACC,MAAM;GACN,OAAO,cAAc,MAAM,2BAA2B,KAAK,UAAU,KAAK;EAC3E,CACD,IACC,CAAC,GACJ,GAAI,WAAW,IAAI,KAAK,SAAS,UAAU,CAAC,UAAU,MAAM,KAAK,CAAC,IAC/D,CACA;GACC,MAAM;GACN,OAAO,cAAc,MAAM;EAC5B,CACD,IACC,CAAC,CACL;CACD;CACA,MAAM,YAAY,eAAe,SAAS,QAAQ,MAAM,EAAE;CAC1D,IAAI,QAAQ,SAAS,GACpB,OAAO,CAAC,SAAS;CAElB,OAAO,CACN,GAAI,IAAI,KAAK,SAAS,UAAU,CAAC,KAAK,MAAM,OAAO,UAAU,KAAK,CAAC,IAChE,CACA;EACC,MAAM;EACN,OAAO,cAAc,MAAM,iDAAiD,QAAQ,KAAK;CAC1F,CACD,IACC,CAAC,GACJ,GAAI,WACJ,IAAI,KAAK,SAAS,UAAU,CAAC,YAAY,MAAM,OAAO,UAAU,KAAK,CAAC,IACnE,CACA;EACC,MAAM;EACN,OAAO,cAAc,MAAM;CAC5B,CACD,IACC,CAAC,CACL;AACD;;;;;;AAOA,MAAM,gBACL,YACA,aAEC,WAAW,MAAM,UAAU,MAAM,QAAQ,UAC1C,IAAI,MAAM,UAAU,QAAQ,UAAU;CACrC,MAAM,UAAU,WAAW,OAAO;CAClC,OAAO,WAAW,QAAQ,OAAO,YAAY,YAAY,YAAY;AACtE,CAAC;;AAGF,MAAM,iBACL,YACA,UACwC;CACxC,MAAM,UAAU,WAAW,OAAO;CAClC,OAAO,YAAY,KAAA,KAAa,OAAO,YAAY,WAChD,UACCA,OAAe,WAAW,SAAS,QAAQ,KAAK,CAAC,EAAE,SACpDI;AACJ;;AAGA,MAAM,eACL,SACA,QACA,OAC0B;CAC1B,MAAM,UAAU,IAAI,IAAI,SAAS,YAChC,QAAQ,SAAS,aAAa,OAAO,QAAQ,UAAU,WACpD,QAAQ,QACR,IACJ;CAGA,MAAM,aAAa,IAAI,OAAO,QAAQ,QAAQ,eAC7C,aAAa,YAAY,OAAO,CACjC;CACA,OAAO,IAAI,QAAQ,SAAS,SAAS,UAAgC;EACpE,MAAM,QAAQ;GAAC,GAAG;GAAI;GAAU;EAAK;EACrC,IAAI,QAAQ,SAAS,WACpB,OAAO,IAAI,KAAK,aAAa,eAAe;GAC3C,MAAM,QAAQ,cAAc,YAAY,KAAK;GAC7C,OAAO,OAAO,UAAU,YAAY,CAAC,YAAY,OAAO,QAAQ,KAAK;EACtE,CAAC,IACE,CACA;GACC,MAAM;GACN,OAAO,iBAAiB,OAAO,KAAK,EAAE,0BAA0B,KAAK,UAAU,QAAQ,KAAK;EAC7F,CACD,IACC,CAAC;EAEL,MAAM,YAAY,eAAe,SAAS,QAAQ,MAAM,KAAK;EAC7D,IAAI,QAAQ,SAAS,GACpB,OAAO,CAAC,SAAS;EAElB,OAAO,IAAI,KAAK,aAAa,eAAe;GAC3C,MAAM,QAAQ,cAAc,YAAY,KAAK;GAC7C,OAAO,UAAU,KAAA,IACd,OACA,OAAO,UAAU,WAChB,CAACL,QAAgB,UAAU,OAAO,KAAK,IACvC,CAAC,KAAK,OAAO,UAAU,KAAK;EACjC,CAAC,IACE,CACA;GACC,MAAM;GACN,OAAO,wBAAwB,QAAQ,KAAK;EAC7C,CACD,IACC,CAAC;CACL,CAAC;AACF;AAEA,MAAM,YAAY,QAA2C;CAC5D,MAAM;CACN,OAAO;AACR;;;;;AAMA,MAAM,YAAY;;;;;AASlB,MAAM,SACL,MACA,IACA,WAC0B;CAC1B,IAAI,OAAO,WAAW,GACrB,OAAO;CAER,MAAM,OAAO,YAAY,KAAK,OAAO;CACrC,KAAK,OAAO,SAAS,OAAO;CAC5B,MAAM,OAAO,IAAI,IAAI,IAAI,KAAK,QAAQ,KAAK,IAAI,MAAM,CAAC,CAAC,IAAI,WAAW;EACrE,MAAM,CAAC,GAAG,GAAG,GAAG,GAAG,MAAM,IAAI;EAC7B,OAAO,MAAM;CACd,EAAE;CACF,MAAM,QAAQ,KAAK,OAAO,QAAQ,aAAa,CAAC,KAAK,OAAO;CAC5D,KAAK,OAAO,UAAU;CACtB,OAAO,QACJ,CACA,GAAG,MACH;EACC,MAAM,CAAC;EACP,OAAO,2CAA2C,OAAO,SAAS,EAAE;CACrE,CACD,IACC;AACJ;;;;;;;;AASA,MAAM,mBACL,SACA,MACA,IACA,SAC0B;CAC1B,IAAI,KAAK,IAAI,IAAI,GAChB,OAAO,CAAC;CAET,KAAK,IAAI,IAAI;CACb,MAAM,QAAQ,WAAiC,MAAM,QAAQ,MAAM,IAAI,MAAM;CAC7E,QAAQ,KAAK,MAAb;EACC,KAAK,WAAW;GACf,MAAM,SAAS,WAAW,SAAS,KAAK,OAAO,CAAC,OAAO,CAAC;GACxD,OAAO,KACN,OAAO,SAAS,IACb,SACA,cACA,SACA,KAAK,OACL,KAAK,OAAO,QAAQ,KAAK,OAAO,MAChC,KAAK,OACL,CAAC,OAAO,CACT,CACH;EACD;EACA,KAAK,MAAM;GACV,MAAM,SAAS,WAAW,SAAS,KAAK,OAAO,CAAC,OAAO,CAAC;GACxD,OAAO,KACN,OAAO,SAAS,IACb,SACA,IAAI,QAAQ,KAAK,SAAS,SAAS,UACnC,cAAc,SAAS,KAAK,OAAO,OAAO,SAAS,CAClD,UACA,KACD,CAAC,CACF,CACH;EACD;EACA,KAAK,UACJ,OAAO,KAAK,WAAW,SAAS,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;EACvD,KAAK,SACJ,OAAO,KAAK,YAAY,SAAS,KAAK,QAAQ,CAAC,CAAC,CAAC;EAClD,KAAK;EACL,KAAK,MACJ,OAAO,IAAI,QAAQ,KAAK,aAAa,OAAO,UAC3C,gBACC,SACA,aACM;GAAC,GAAG,GAAG;GAAG;GAAc;EAAK,GACnC,IACD,CACD;EACD,KAAK,OACJ,OAAO,gBACN,SACA,KAAK,iBACC,CAAC,GAAG,GAAG,GAAG,WAAW,GAC3B,IACD;EACD,SACC,OAAO,CAAC;CACV;AACD;AAEA,MAAM,aACL,YACA,YAEA,QAAQ,SAAS,GAAG,KACpB,IAAI,KAAK,UAAU,WAAW,WAAW,QAAQ,SAAS,MAAM,CAAC;;;;;AAMlE,MAAa,cACZ,QACA,MACA,IACA,OAAa,SAAS,MACI;CAC1B,MAAM,MAAM,KAAK,UAAU,SAAS,GAAG;CAEvC,MAAM,QAAQ,IAAI,OAAO,IAAI,OAAO,KAAK,YAAY,SAAS,SAAS,GAAG,CAAC;CAC3E,MAAM,QAAQ,MAAM,KAAK,QAAQ,KAAK,OAAO,SAAS,CAAC;CACvD,MAAM,QAAQ,IAAI,QAAQ,QAAQ,SACjC,OAAO,QAAQ,QAAQ,IAAI,OAAO,WAAW,IAAI,CAAC,CACnD;CACA,MAAM,QAAQ,MAAM,MAAM,KAAK,QAAQ,OAAO,OAAO,SAAS,CAAC,IAAI;CAGnE,MAAM,aAAa,IAAI,OAAO,QAAQ,eACrC,UAAU,YAAY,KAAK,OAAO,CACnC;CACA,MAAM,UAAmB;EACxB;EACA;EACA;EACA;EACA;EACA,OAAO,MACJ,IAAI,OAAO,QAAQ,eAAe,UAAU,YAAY,KAAK,OAAO,CAAC,IACrE;CACJ;CACA,MAAM,UAAU,IAAI,OACnB,IAAI,QAAQ,QAAQ,eAAe,WAAW,OAAO,CACtD;CACA,OAAO;EACN,GAAG,IAAI,IACN,IAAI,OAAO,QAAQ,SAAS,CAAC,QAAQ,IAAI,OAAO,WAAW,IAAI,CAAC,IAC/D,UAAiB;GACjB,MAAM,CAAC,GAAG,IAAI,WAAW;GACzB,OAAO,UACN,MACA,uCAAuC,KAAK,IAC5C,MACA,gBACM,KACP;EACD,EACD;EACA,GAAG,IAAI,IACN,IAAI,OACH,KAAK,UACJ,WAAW,WAAW,OAAO,CAAC,QAAQ,SAAS,MAAM,CACvD,IACC,YAAmB;GACnB,MAAM,CAAC,GAAG,IAAI,SAAS;GACvB,OAAO,UACN,MACA,eAAe,OAAO,wCACtB,QACA,iBAAiB,MAAM,KAAK,GAAG,WACzB,OACP;EACD,EACD;EACA,GAAG,IAAI,QAAQ,KAAK,UAAU,CAAC,IAAI,OAAO,UACzC,MAAM,SAAS,GAAG,IACf,cAAc,MAAM,OAAO,OAAO;GAAC,GAAG;GAAI;GAAU;EAAK,CAAC,IAC1D,WAAW,SAAS,OAAO;GAAC,GAAG;GAAI;GAAU;EAAK,CAAC,CACvD;EACA,GAAI,KAAK,SAAS,KAAA,IACf,CAAC,IACDM,OAAa,KAAK,IAAI,IACrB,gBAAgB,SAAS,KAAK,YAAY,CAAC,GAAG,IAAI,MAAM,mBAAG,IAAI,IAAI,CAAC,IACpE,CAAC,SAAS,CAAC,GAAG,IAAI,MAAM,CAAC,CAAC;CAC/B;AACD;;AAgBA,MAAa,cACZ,QACA,SAC0B;CAO1B,MAAM,WACL,MACA,OACA,UAC6D;EAC7D,IAAI,QAAQ,KACX,OAAO,CACN;GACC,MAAMC,OAAa,KAAK;GACxB,OAAO,+BAA+B,OAAO,GAAkB,EAAE,uBAAuB,OAAO,GAAkB,EAAE;EACpH,CACD;EAED,QAAQ,KAAK,MAAb;GACC,KAAK,SACJ,OAAO,IAAI,IACV,KAAK,QACJ,MAAM,UAAU,CAAC,MAAMC,GAAS,OAAO,SAAS,KAAK,CAAC,CACxD;GACD,KAAK;GACL,KAAK;GACL,KAAK,WACJ,OAAO,IAAI,QAAQ,KAAK,WAAW,OAAO,UACzC,QAAQ,OAAOA,GAAS,OAAO,YAAY,KAAK,GAAG,QAAQ,CAAC,CAC7D;GACD,KAAK,WACJ,OAAO,CACN,GAAG,QAAQ,KAAK,KAAKA,GAAS,OAAO,KAAK,GAAG,QAAQ,CAAC,GACtD,GAAG,QAAQ,KAAK,QAAQA,GAAS,OAAO,QAAQ,GAAG,QAAQ,CAAC,CAC7D;EACF;CACD;CACA,MAAM,OAAO,SAAS;CACtB,OAAO,IAAI,QAAQ,QAAQ,MAAM,KAAA,GAAW,CAAC,IAAI,UAChD,MAAM,QAAQ,KAAK,IAChB,WAAW,QAAQ,MAAM,IAAID,OAAa,MAAM,EAAE,GAAG,IAAI,IACzD,CAAC,KAAc,CACnB;AACD"}
|
|
1
|
+
{"version":3,"file":"bind.js","names":["Catalog.accepts","Catalog.lookup","Patterns.parse","Patterns.fitsCatalog","Limits.searchSteps","Catalog.anyKinds","Nodes.isNode","Place.pathOf","Place.at"],"sources":["../../src/internal/bind.ts"],"sourcesContent":["/**\n * The binding of rules to a domain. It is the one check of a rule, for a rule\n * from the builder, from `Policy.rule`, and from JSON alike. It reads the\n * catalogs of the resources and of the principal, and it gives one issue for\n * each problem, with the path of the problem and, for a name with a mistake,\n * the name that it may mean.\n */\nimport * as Arr from \"effect/Array\"\nimport * as HashMap from \"effect/HashMap\"\nimport * as Option from \"effect/Option\"\nimport * as Result from \"effect/Result\"\nimport type * as Domain from \"../Domain.ts\"\nimport type * as Resource from \"../Resource.ts\"\nimport * as Catalog from \"./catalog.ts\"\nimport * as Limits from \"./limits.ts\"\nimport * as Nodes from \"./nodes.ts\"\nimport * as Patterns from \"./patterns.ts\"\nimport * as Place from \"./place.ts\"\nimport { withSuggestion } from \"./suggest.ts\"\n\n/** One problem of a rule or a policy, with its path. */\nexport interface Issue {\n\treadonly path: ReadonlyArray<PropertyKey>\n\treadonly issue: string\n}\n\n/** The data of a rule that the binding reads. */\nexport interface RuleData {\n\treadonly actions: ReadonlyArray<string>\n\treadonly resources: ReadonlyArray<string>\n\treadonly when?: unknown\n\treadonly fields?: ReadonlyArray<string>\n}\n\n/**\n * The memo of one binding, for all its rules. A policy may name one wrong\n * field or one pattern in very many places, so the work on each is done one\n * time. It goes away when the binding ends.\n */\ninterface Memo {\n\t/**\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}\n\nconst makeMemo = (): Memo => ({\n\tsuggestions: new Map(),\n\tfits: new Map(),\n\tissues: { count: 0, noted: false },\n\twork: { steps: 0, noted: false },\n})\n\n/**\n * The most names that one binding makes a suggestion for. A suggestion\n * compares the name with every name of a catalog, so a policy with very many\n * wrong names would take a long time.\n */\nconst maxSuggestions = 32\n\n/** Adds a suggestion to a message, with the memo of the binding. */\nconst suggestIn = (\n\tmemo: Memo,\n\tmessage: string,\n\tname: string,\n\tscope: string,\n\tnames: () => ReadonlyArray<string>,\n): string => {\n\tconst key = `${scope}\\u0000${name}`\n\tconst known = 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 message = `The field \"${field}\" is not a field of every resource of the rule`\n\tif (context.all || context.named.length === 0) {\n\t\treturn [{ path: at, issue: message }]\n\t}\n\tconst without = Arr.filter(\n\t\tcontext.named,\n\t\t(descriptor) => Catalog.lookup(descriptor.catalog, field) === undefined,\n\t)\n\treturn without.length === 0\n\t\t? []\n\t\t: [\n\t\t\t\t{\n\t\t\t\t\tpath: at,\n\t\t\t\t\tissue: suggestIn(context.memo, message, field, without[0]!.kind, () =>\n\t\t\t\t\t\tnamesOf(without[0]!.catalog),\n\t\t\t\t\t),\n\t\t\t\t},\n\t\t\t]\n}\n\n/**\n * The issues of a field that a condition reads: a field that is not a field\n * of every resource of the rule, or a field that is not plain data in one of\n * them. A read of a value that is not plain data, as a getter of a class, is\n * a defect in a decision. A residual may leave the read out, so the residual\n * would say \"yes\" where the decision fails. So the binding does not take\n * such a read: see `Catalog.Field.plain`.\n */\nconst readIssues = (\n\tcontext: Context,\n\tfield: string,\n\tat: ReadonlyArray<PropertyKey>,\n): ReadonlyArray<Issue> => {\n\tconst exists = fieldIssue(context, field, at)\n\tif (exists.length > 0) {\n\t\treturn exists\n\t}\n\treturn Arr.some(\n\t\tcontext.named,\n\t\t(descriptor) => !(Catalog.lookup(descriptor.catalog, field)?.plain ?? true),\n\t)\n\t\t? [\n\t\t\t\t{\n\t\t\t\t\tpath: at,\n\t\t\t\t\tissue: `A condition may not read the field \"${field}\". A value above it may be an object that does not have it as a data field, as a class with a getter, a date or a list. Use a predicate for it.`,\n\t\t\t\t},\n\t\t\t]\n\t\t: []\n}\n\n/**\n * The issues of a field pattern: a pattern that does not parse, or that\n * matches no field of the resources of the rule, as a pattern with a mistake\n * in a name does.\n */\nconst patternIssues = (\n\tmemo: Memo,\n\tknown: ReadonlyArray<Resource.Descriptor>,\n\tpattern: string,\n\tat: ReadonlyArray<PropertyKey>,\n): ReadonlyArray<Issue> =>\n\tResult.match(Patterns.parse(pattern), {\n\t\tonFailure: (issue) => [{ path: at, issue }],\n\t\tonSuccess: (parsed) => {\n\t\t\t// A rule with no known resource has an issue for that already. The\n\t\t\t// pattern is checked on every kind, not only up to the first that it\n\t\t\t// fits: a decision on a kind does this check, so the limit of the\n\t\t\t// work below is a limit of the work of a decision too.\n\t\t\tconst fits =\n\t\t\t\tknown.length === 0 ||\n\t\t\t\tArr.reduce(known, false, (before, descriptor) => {\n\t\t\t\t\tconst key = `${descriptor.kind}\\u0000${pattern}`\n\t\t\t\t\treturn (\n\t\t\t\t\t\tOption.getOrElse(Option.fromUndefinedOr(memo.fits.get(key)), () => {\n\t\t\t\t\t\t\t// Each check of a pattern reads the fields of a catalog. The\n\t\t\t\t\t\t\t// work of one binding has a limit, so very many patterns stop\n\t\t\t\t\t\t\t// with one issue, and the policy does not load.\n\t\t\t\t\t\t\tmemo.work.steps +=\n\t\t\t\t\t\t\t\tdescriptor.catalog.fields.length * Math.max(parsed.length, 1)\n\t\t\t\t\t\t\tif (memo.work.steps > Limits.searchSteps) {\n\t\t\t\t\t\t\t\treturn true\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tconst found = Patterns.fitsCatalog(parsed, descriptor.catalog)\n\t\t\t\t\t\t\tmemo.fits.set(key, found)\n\t\t\t\t\t\t\treturn found\n\t\t\t\t\t\t}) || before\n\t\t\t\t\t)\n\t\t\t\t})\n\t\t\tif (memo.work.steps > Limits.searchSteps && !memo.work.noted) {\n\t\t\t\tmemo.work.noted = true\n\t\t\t\treturn [\n\t\t\t\t\t{\n\t\t\t\t\t\tpath: at,\n\t\t\t\t\t\tissue: `The field patterns of the policy take more than ${String(Limits.searchSteps)} steps to check. The most is ${String(Limits.searchSteps)}.`,\n\t\t\t\t\t},\n\t\t\t\t]\n\t\t\t}\n\t\t\treturn fits\n\t\t\t\t? []\n\t\t\t\t: [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tpath: at,\n\t\t\t\t\t\t\tissue: `The field pattern \"${pattern}\" matches no field of the resources of the rule`,\n\t\t\t\t\t\t},\n\t\t\t\t\t]\n\t\t},\n\t})\n\n/** The fields of the resources that a test of the rule may read. */\nconst fieldsAt = (\n\tcontext: Context,\n\tfield: string,\n): ReadonlyArray<Catalog.Field> =>\n\tArr.flatMap(context.applicable, (descriptor) => {\n\t\tconst found = Catalog.lookup(descriptor.catalog, field)\n\t\treturn found === undefined ? [] : [found]\n\t})\n\n/** The field of the principal that an operand names, or the issue when there is none. */\nconst principalField = (\n\tcontext: Context,\n\tref: string,\n\tat: ReadonlyArray<PropertyKey>,\n): Catalog.Field | Issue => {\n\tconst found = Catalog.lookup(context.domain.principalCatalog, ref)\n\t// A read of the principal that is not plain data is a defect, as a read\n\t// of a resource is: see `readIssues`.\n\tif (found !== undefined && !found.plain) {\n\t\treturn {\n\t\t\tpath: at,\n\t\t\tissue: `A condition may not read the field \"${ref}\" of the principal. A value above it may be an object that does not have it as a data field, as a class with a getter, a date or a list. Use a predicate for it.`,\n\t\t}\n\t}\n\treturn found !== undefined\n\t\t? found\n\t\t: {\n\t\t\t\tpath: at,\n\t\t\t\tissue: suggestIn(\n\t\t\t\t\tcontext.memo,\n\t\t\t\t\t`The principal has no field \"${ref}\"`,\n\t\t\t\t\tref,\n\t\t\t\t\t\"~principal\",\n\t\t\t\t\t() => namesOf(context.domain.principalCatalog),\n\t\t\t\t),\n\t\t\t}\n}\n\nconst isIssue = (value: Catalog.Field | Issue): value is Issue =>\n\t\"issue\" in value\n\n/** The issues of one operand of a test of a field. */\nconst operandIssues = (\n\tcontext: Context,\n\tfield: string,\n\tordered: boolean,\n\toperand: Operand,\n\tat: ReadonlyArray<PropertyKey>,\n): ReadonlyArray<Issue> => {\n\tconst fields = fieldsAt(context, field)\n\tif (operand._tag === \"Literal\") {\n\t\tconst value = operand.value\n\t\tif (ordered && typeof value !== \"string\" && typeof value !== \"number\") {\n\t\t\treturn [\n\t\t\t\t{\n\t\t\t\t\tpath: at,\n\t\t\t\t\tissue: `The field \"${field}\" has an ordered test with a value that has no order`,\n\t\t\t\t},\n\t\t\t]\n\t\t}\n\t\treturn [\n\t\t\t...(Arr.some(fields, (found) => !fitsLiteral(found.kinds, value))\n\t\t\t\t? [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tpath: at,\n\t\t\t\t\t\t\tissue: `The field \"${field}\" can not hold the value ${JSON.stringify(value)}`,\n\t\t\t\t\t\t},\n\t\t\t\t\t]\n\t\t\t\t: []),\n\t\t\t...(ordered && Arr.some(fields, (found) => !isOrdered(found.kinds))\n\t\t\t\t? [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tpath: at,\n\t\t\t\t\t\t\tissue: `The field \"${field}\" has an ordered test with a value that has no order`,\n\t\t\t\t\t\t},\n\t\t\t\t\t]\n\t\t\t\t: []),\n\t\t]\n\t}\n\tconst principal = principalField(context, operand.path, at)\n\tif (isIssue(principal)) {\n\t\treturn [principal]\n\t}\n\treturn [\n\t\t...(Arr.some(fields, (found) => !meet(found.kinds, principal.kinds))\n\t\t\t? [\n\t\t\t\t\t{\n\t\t\t\t\t\tpath: at,\n\t\t\t\t\t\tissue: `The field \"${field}\" can not hold a value of the principal field \"${operand.path}\"`,\n\t\t\t\t\t},\n\t\t\t\t]\n\t\t\t: []),\n\t\t...(ordered &&\n\t\tArr.some(fields, (found) => !orderedMeet(found.kinds, principal.kinds))\n\t\t\t? [\n\t\t\t\t\t{\n\t\t\t\t\t\tpath: at,\n\t\t\t\t\t\tissue: `The field \"${field}\" has an ordered test with a value that has no order`,\n\t\t\t\t\t},\n\t\t\t\t]\n\t\t\t: []),\n\t]\n}\n\n/**\n * Tests if the path of a resource may start with a prefix: it is as long as\n * the prefix, and each fixed string of the prefix is the string of the path\n * at its place, when the path has a string there.\n */\nconst mayStartWith = (\n\tdescriptor: Resource.Descriptor,\n\tstrings: ReadonlyArray<string | null>,\n): boolean =>\n\t(descriptor.path?.length ?? 0) >= strings.length &&\n\tArr.every(strings, (string, index) => {\n\t\tconst segment = descriptor.path?.[index]\n\t\treturn string === null || typeof segment !== \"string\" || segment === string\n\t})\n\n/** The kinds of the part of the path of a resource at a place. */\nconst pathPartKinds = (\n\tdescriptor: Resource.Descriptor,\n\tindex: number,\n): Catalog.Kinds | string | undefined => {\n\tconst segment = descriptor.path?.[index]\n\treturn segment === undefined || typeof segment === \"string\"\n\t\t? segment\n\t\t: (Catalog.lookup(descriptor.catalog, segment.field)?.kinds ??\n\t\t\t\tCatalog.anyKinds)\n}\n\n/** The issues of the prefix of a path test. */\nconst underIssues = (\n\tcontext: Context,\n\tprefix: ReadonlyArray<Operand>,\n\tat: ReadonlyArray<PropertyKey>,\n): ReadonlyArray<Issue> => {\n\tconst strings = Arr.map(prefix, (operand) =>\n\t\toperand._tag === \"Literal\" && typeof operand.value === \"string\"\n\t\t\t? operand.value\n\t\t\t: null,\n\t)\n\t// The test can match only a path that starts with the fixed strings of\n\t// the prefix, so the other resources are left out.\n\tconst candidates = Arr.filter(context.paths, (descriptor) =>\n\t\tmayStartWith(descriptor, strings),\n\t)\n\treturn Arr.flatMap(prefix, (operand, index): ReadonlyArray<Issue> => {\n\t\tconst place = [...at, \"prefix\", index]\n\t\tif (operand._tag === \"Literal\") {\n\t\t\treturn Arr.some(candidates, (descriptor) => {\n\t\t\t\tconst kinds = pathPartKinds(descriptor, index)\n\t\t\t\treturn typeof kinds === \"object\" && !fitsLiteral(kinds, operand.value)\n\t\t\t})\n\t\t\t\t? [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tpath: place,\n\t\t\t\t\t\t\tissue: `The path part ${String(index)} can not hold the value ${JSON.stringify(operand.value)}`,\n\t\t\t\t\t\t},\n\t\t\t\t\t]\n\t\t\t\t: []\n\t\t}\n\t\tconst principal = principalField(context, operand.path, place)\n\t\tif (isIssue(principal)) {\n\t\t\treturn [principal]\n\t\t}\n\t\treturn Arr.some(candidates, (descriptor) => {\n\t\t\tconst kinds = pathPartKinds(descriptor, index)\n\t\t\treturn kinds === undefined\n\t\t\t\t? true\n\t\t\t\t: typeof kinds === \"string\"\n\t\t\t\t\t? !Catalog.accepts(principal.kinds, kinds)\n\t\t\t\t\t: !meet(kinds, principal.kinds)\n\t\t})\n\t\t\t? [\n\t\t\t\t\t{\n\t\t\t\t\t\tpath: place,\n\t\t\t\t\t\tissue: `The principal field \"${operand.path}\" can not hold a part of the path of the resource`,\n\t\t\t\t\t},\n\t\t\t\t]\n\t\t\t: []\n\t})\n}\n\nconst notWhole = (at: ReadonlyArray<PropertyKey>): Issue => ({\n\tpath: at,\n\tissue: \"The condition is not a whole condition tree\",\n})\n\n/**\n * The most issues that the walk of the conditions of one binding gives. A\n * policy may have very many wrong tests, and each issue keeps its path.\n */\nconst maxIssues = 100\n\n/** A path that the walk makes only when it needs it, for an issue. */\ntype LazyPath = () => ReadonlyArray<PropertyKey>\n\n/**\n * Puts the path of a node before the paths of its issues. A walk keeps no\n * path for a node with no issue, so a deep and wide condition costs little.\n */\nconst under = (\n\tmemo: Memo,\n\tat: LazyPath,\n\tissues: ReadonlyArray<Issue>,\n): ReadonlyArray<Issue> => {\n\tif (issues.length === 0) {\n\t\treturn issues\n\t}\n\tconst room = maxIssues - memo.issues.count\n\tmemo.issues.count += issues.length\n\tconst kept = Arr.map(Arr.take(issues, Math.max(room, 0)), (issue) => ({\n\t\tpath: [...at(), ...issue.path],\n\t\tissue: issue.issue,\n\t}))\n\tconst first = memo.issues.count > maxIssues && !memo.issues.noted\n\tmemo.issues.noted ||= first\n\treturn first\n\t\t? [\n\t\t\t\t...kept,\n\t\t\t\t{\n\t\t\t\t\tpath: [],\n\t\t\t\t\tissue: `The policy has more problems. The first ${String(maxIssues)} are here.`,\n\t\t\t\t},\n\t\t\t]\n\t\t: kept\n}\n\n/**\n * The issues of a condition. The constructors of the nodes checked the shape\n * of each node, so this checks the names and the types against the domain. A\n * node that the condition uses in many places is checked one time. The nodes\n * it has seen are in a native `Set`, because it must find a node by the node\n * itself: `Equal` compares nodes by structure.\n */\nconst conditionIssues = (\n\tcontext: Context,\n\tnode: Nodes.Node,\n\tat: LazyPath,\n\tseen: Set<Nodes.Node>,\n): ReadonlyArray<Issue> => {\n\tif (seen.has(node)) {\n\t\treturn []\n\t}\n\tseen.add(node)\n\tconst here = (issues: ReadonlyArray<Issue>) => under(context.memo, at, issues)\n\tswitch (node._tag) {\n\t\tcase \"Compare\": {\n\t\t\tconst exists = readIssues(context, node.field, [\"field\"])\n\t\t\treturn here(\n\t\t\t\texists.length > 0\n\t\t\t\t\t? exists\n\t\t\t\t\t: operandIssues(\n\t\t\t\t\t\t\tcontext,\n\t\t\t\t\t\t\tnode.field,\n\t\t\t\t\t\t\tnode.op !== \"eq\" && node.op !== \"ne\",\n\t\t\t\t\t\t\tnode.value,\n\t\t\t\t\t\t\t[\"value\"],\n\t\t\t\t\t\t),\n\t\t\t)\n\t\t}\n\t\tcase \"In\": {\n\t\t\tconst exists = readIssues(context, node.field, [\"field\"])\n\t\t\treturn here(\n\t\t\t\texists.length > 0\n\t\t\t\t\t? exists\n\t\t\t\t\t: Arr.flatMap(node.values, (operand, index) =>\n\t\t\t\t\t\t\toperandIssues(context, node.field, false, operand, [\n\t\t\t\t\t\t\t\t\"values\",\n\t\t\t\t\t\t\t\tindex,\n\t\t\t\t\t\t\t]),\n\t\t\t\t\t\t),\n\t\t\t)\n\t\t}\n\t\tcase \"Exists\":\n\t\t\treturn here(readIssues(context, node.field, [\"field\"]))\n\t\tcase \"Under\":\n\t\t\treturn here(underIssues(context, node.prefix, []))\n\t\tcase \"And\":\n\t\tcase \"Or\":\n\t\t\treturn Arr.flatMap(node.conditions, (child, index) =>\n\t\t\t\tconditionIssues(\n\t\t\t\t\tcontext,\n\t\t\t\t\tchild,\n\t\t\t\t\t() => [...at(), \"conditions\", index],\n\t\t\t\t\tseen,\n\t\t\t\t),\n\t\t\t)\n\t\tcase \"Not\":\n\t\t\treturn conditionIssues(\n\t\t\t\tcontext,\n\t\t\t\tnode.condition,\n\t\t\t\t() => [...at(), \"condition\"],\n\t\t\t\tseen,\n\t\t\t)\n\t\tdefault:\n\t\t\treturn []\n\t}\n}\n\nconst hasAction = (\n\tdescriptor: Resource.Descriptor,\n\tactions: ReadonlyArray<string>,\n): boolean =>\n\tactions.includes(\"*\") ||\n\tArr.some(actions, (action) => descriptor.actions.includes(action))\n\n/**\n * Checks one rule against a domain. It gives one issue for each problem. `at`\n * is the path of the rule, for the paths of the issues.\n */\nexport const ruleIssues = (\n\tdomain: Domain.Any,\n\trule: RuleData,\n\tat: ReadonlyArray<PropertyKey>,\n\tmemo: Memo = makeMemo(),\n): ReadonlyArray<Issue> => {\n\tconst all = rule.resources.includes(\"*\")\n\t// Each kind is checked one time, also when the list names it again. 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\treturn [\n\t\t...Arr.map(\n\t\t\tArr.filter(names, (kind) => !HashMap.has(domain.resources, kind)),\n\t\t\t(kind): Issue => ({\n\t\t\t\tpath: [...at, \"resources\"],\n\t\t\t\tissue: suggestIn(\n\t\t\t\t\tmemo,\n\t\t\t\t\t`There is no resource with the kind \"${kind}\"`,\n\t\t\t\t\tkind,\n\t\t\t\t\t\"~kinds\",\n\t\t\t\t\t() => kinds,\n\t\t\t\t),\n\t\t\t}),\n\t\t),\n\t\t...Arr.map(\n\t\t\tArr.filter(\n\t\t\t\trule.actions,\n\t\t\t\t(action) => action !== \"*\" && !actions.includes(action),\n\t\t\t),\n\t\t\t(action): Issue => ({\n\t\t\t\tpath: [...at, \"actions\"],\n\t\t\t\tissue: suggestIn(\n\t\t\t\t\tmemo,\n\t\t\t\t\t`The action \"${action}\" is not an action of these resources`,\n\t\t\t\t\taction,\n\t\t\t\t\t`~actions\\u0000${names.join(\",\")}`,\n\t\t\t\t\t() => actions,\n\t\t\t\t),\n\t\t\t}),\n\t\t),\n\t\t...Arr.flatMap(rule.fields ?? [], (field, index) =>\n\t\t\tfield.includes(\"*\")\n\t\t\t\t? patternIssues(memo, known, field, [...at, \"fields\", index])\n\t\t\t\t: fieldIssue(context, field, [...at, \"fields\", index]),\n\t\t),\n\t\t...(rule.when === undefined\n\t\t\t? []\n\t\t\t: Nodes.isNode(rule.when)\n\t\t\t\t? conditionIssues(context, rule.when, () => [...at, \"when\"], new Set())\n\t\t\t\t: [notWhole([...at, \"when\"])]),\n\t]\n}\n\n/** The JSON form of a policy tree, or a policy tree, as the binding reads it. */\ntype TreeNode =\n\t| { readonly _tag: \"Rules\"; readonly rules: ReadonlyArray<RuleData> }\n\t| {\n\t\t\treadonly _tag: \"All\" | \"Any\" | \"FirstOf\"\n\t\t\treadonly policies: ReadonlyArray<TreeNode>\n\t }\n\t| {\n\t\t\treadonly _tag: \"Ceiling\"\n\t\t\treadonly cap: TreeNode\n\t\t\treadonly policy: TreeNode\n\t }\n\n/** Checks each rule of a policy tree against a domain. */\nexport const treeIssues = (\n\tdomain: Domain.Any,\n\troot: TreeNode,\n): ReadonlyArray<Issue> => {\n\t// The rules come out of the tree first, and each one is checked after.\n\t// A check of a condition goes down its levels, so a check inside the walk\n\t// of the tree would go down the levels of both.\n\t// The place of each rule shares the path of its parent, and the full path\n\t// is made only when its rule is checked. A deep tree with very many rules\n\t// would need very much memory for a full copy of the path of each rule.\n\tconst rulesOf = (\n\t\tnode: TreeNode,\n\t\tplace: Place.Place | undefined,\n\t\tlevel: number,\n\t): ReadonlyArray<Issue | readonly [RuleData, Place.Place]> => {\n\t\tif (level > Limits.policyDepth) {\n\t\t\treturn [\n\t\t\t\t{\n\t\t\t\t\tpath: Place.pathOf(place),\n\t\t\t\t\tissue: `A policy tree has more than ${String(Limits.policyDepth)} levels. The most is ${String(Limits.policyDepth)}.`,\n\t\t\t\t},\n\t\t\t]\n\t\t}\n\t\tswitch (node._tag) {\n\t\t\tcase \"Rules\":\n\t\t\t\treturn Arr.map(\n\t\t\t\t\tnode.rules,\n\t\t\t\t\t(rule, index) => [rule, Place.at(place, \"rules\", index)] as const,\n\t\t\t\t)\n\t\t\tcase \"All\":\n\t\t\tcase \"Any\":\n\t\t\tcase \"FirstOf\":\n\t\t\t\treturn Arr.flatMap(node.policies, (child, index) =>\n\t\t\t\t\trulesOf(child, Place.at(place, \"policies\", index), level + 1),\n\t\t\t\t)\n\t\t\tcase \"Ceiling\":\n\t\t\t\treturn [\n\t\t\t\t\t...rulesOf(node.cap, Place.at(place, \"cap\"), level + 1),\n\t\t\t\t\t...rulesOf(node.policy, Place.at(place, \"policy\"), level + 1),\n\t\t\t\t]\n\t\t}\n\t}\n\tconst memo = makeMemo()\n\treturn Arr.flatMap(rulesOf(root, undefined, 1), (found) =>\n\t\tArray.isArray(found)\n\t\t\t? ruleIssues(domain, found[0], Place.pathOf(found[1]), memo)\n\t\t\t: [found as Issue],\n\t)\n}\n"],"mappings":"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"}
|
|
@@ -51,6 +51,12 @@ export interface Branch {
|
|
|
51
51
|
readonly field: Field | undefined;
|
|
52
52
|
/** The places below, by name, in the order of the Schema. */
|
|
53
53
|
readonly children: ReadonlyArray<readonly [string, Branch]>;
|
|
54
|
+
/**
|
|
55
|
+
* The places below, by name. A lookup gets each name from `split`, as a
|
|
56
|
+
* new string, and a `HashMap` finds a new string about 20% faster than a
|
|
57
|
+
* native `Map`. The names come from the Schema, so a name from a policy
|
|
58
|
+
* can not make a long list of names with one hash here.
|
|
59
|
+
*/
|
|
54
60
|
readonly names: HashMap.HashMap<string, Branch>;
|
|
55
61
|
/** The place for the keys of a record, when the value may be a record. */
|
|
56
62
|
readonly key: Key | undefined;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"catalog.d.ts","names":[],"sources":["../../src/internal/catalog.ts"],"mappings":";;;iBAsBiB;;WAEP;WACA;;WAEA;;WAEA;;WAEA;;WAEA,UAAU;;;
|
|
1
|
+
{"version":3,"file":"catalog.d.ts","names":[],"sources":["../../src/internal/catalog.ts"],"mappings":";;;iBAsBiB;;WAEP;WACA;;WAEA;;WAEA;;WAEA;;WAEA,UAAU;;;iBA8EH;WACP;;WAEA,YAAY,cAAc,UAAU;;;YAIlC,mBAAmB;;iBAGd;WACP,MAAM,cAAc;;WAEpB;WACA,OAAO;;;;;WAKP;;WAEA;;;;;;;;;WASA;;;iBAIO;;WAEP,OAAO;;WAEP,UAAU,gCAAgC;;;;;;;WAO1C,OAAO,QAAQ,gBAAgB;;WAE/B,KAAK;WACL,WAAW;;;iBAIJ;;WAEP,QAAQ,cAAc;WACtB,MAAM"}
|
package/dist/internal/catalog.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { catalogFields, exceeded } from "./limits.js";
|
|
2
|
+
import * as HashMap from "effect/HashMap";
|
|
3
|
+
import * as HashSet from "effect/HashSet";
|
|
2
4
|
import * as Schema from "effect/Schema";
|
|
3
5
|
import * as Arr from "effect/Array";
|
|
4
6
|
import { hasProperty } from "effect/Predicate";
|
|
5
|
-
import * as HashSet from "effect/HashSet";
|
|
6
7
|
import * as Option from "effect/Option";
|
|
7
|
-
import * as HashMap from "effect/HashMap";
|
|
8
8
|
import * as SchemaAST from "effect/SchemaAST";
|
|
9
9
|
// src/internal/catalog.ts
|
|
10
10
|
/**
|
|
@@ -58,7 +58,7 @@ const joinKinds = (left, right) => ({
|
|
|
58
58
|
string: left.string || right.string,
|
|
59
59
|
number: numberOrder.indexOf(left.number) >= numberOrder.indexOf(right.number) ? left.number : right.number,
|
|
60
60
|
object: left.object || right.object,
|
|
61
|
-
literals:
|
|
61
|
+
literals: Array.from(/* @__PURE__ */ new Set([...left.literals, ...right.literals]))
|
|
62
62
|
});
|
|
63
63
|
/** Tests if a number fits the numbers of a field. */
|
|
64
64
|
const fitsNumber = (number, value) => {
|
|
@@ -183,20 +183,20 @@ const nameOf = (signature) => typeof signature.name === "symbol" ? void 0 : Stri
|
|
|
183
183
|
/**
|
|
184
184
|
* The properties of a struct by name. A walk makes it one time for each
|
|
185
185
|
* struct of a place, so a struct with very many fields does not take a
|
|
186
|
-
* search for each name.
|
|
186
|
+
* search for each name. It is a native `Map`, as `Branch.names` is.
|
|
187
187
|
*/
|
|
188
|
-
const propertiesOf = (struct) =>
|
|
188
|
+
const propertiesOf = (struct) => new Map(Arr.flatMap(struct.propertySignatures, (signature) => {
|
|
189
189
|
const name = nameOf(signature);
|
|
190
190
|
return name === void 0 ? [] : [[name, signature]];
|
|
191
191
|
}));
|
|
192
192
|
/** The names of the fields of the structs, in the order that they first come. */
|
|
193
|
-
const namesOf = (structs) =>
|
|
193
|
+
const namesOf = (structs) => Array.from(new Set(Arr.flatMap(structs, (struct) => Arr.flatMap(struct.propertySignatures, (signature) => {
|
|
194
194
|
const name = nameOf(signature);
|
|
195
195
|
if (name === void 0) return [];
|
|
196
196
|
if (name.includes(".")) throw new Error(`A field name may not have a dot: "${name}"`);
|
|
197
197
|
if (name === "") throw new Error("A field name may not be empty");
|
|
198
198
|
return [name];
|
|
199
|
-
})));
|
|
199
|
+
}))));
|
|
200
200
|
/**
|
|
201
201
|
* Makes the branch of a place, with the branches below it. `count` is the
|
|
202
202
|
* number of fields so far. It belongs to one walk, and it stops a Schema with
|
|
@@ -232,13 +232,16 @@ const branchOf = (place, count) => {
|
|
|
232
232
|
const structProperties = Arr.map(structs, propertiesOf);
|
|
233
233
|
const classProperties = Arr.map(classes, propertiesOf);
|
|
234
234
|
const children = Arr.map(namesOf(structs), (name) => {
|
|
235
|
-
const properties = Arr.flatMap(structProperties, (named) =>
|
|
235
|
+
const properties = Arr.flatMap(structProperties, (named) => {
|
|
236
|
+
const property = named.get(name);
|
|
237
|
+
return property === void 0 ? [] : [property];
|
|
238
|
+
});
|
|
236
239
|
const below = {
|
|
237
240
|
path: [...place.path, name],
|
|
238
241
|
schemas: Arr.flatMap(properties, (property) => alternatives(property.type, /* @__PURE__ */ new Set())),
|
|
239
242
|
missing: place.missing || values.length > 0 || Arr.some(properties, (property) => SchemaAST.isOptional(property.type)),
|
|
240
243
|
top: place.top || valueKinds.object || properties.length < structs.length,
|
|
241
|
-
plain: place.plain && !valueKinds.object && Arr.every(classProperties, (named) =>
|
|
244
|
+
plain: place.plain && !valueKinds.object && Arr.every(classProperties, (named) => named.has(name))
|
|
242
245
|
};
|
|
243
246
|
return [name, branchOf(below, count)];
|
|
244
247
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"catalog.js","names":["Limits.exceeded","Limits.catalogFields"],"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\"\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\tliterals: Arr.dedupe([...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 decode or a `make` copies a struct or a record into a plain object,\n\t * so a struct without the field gives `null` for it.\n\t */\n\treadonly plain: boolean\n}\n\n/** A place in the tree of a catalog, with the places below it. */\nexport interface Branch {\n\t/** The field at this place. The root has none. */\n\treadonly field: Field | undefined\n\t/** The places below, by name, in the order of the Schema. */\n\treadonly children: ReadonlyArray<readonly [string, Branch]>\n\treadonly names: HashMap.HashMap<string, Branch>\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: Branch | undefined\n}\n\n/** The catalog of the fields of a Schema. */\nexport interface Catalog {\n\t/** Every field, a field before the fields below it. */\n\treadonly fields: ReadonlyArray<Field>\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> => {\n\tswitch (ast._tag) {\n\t\tcase \"Union\":\n\t\t\treturn Arr.flatMap(ast.types, (type) => alternatives(type, open))\n\t\tcase \"Suspend\":\n\t\t\treturn open.has(ast)\n\t\t\t\t? []\n\t\t\t\t: alternatives(ast.thunk(), new Set([...open, ast]))\n\t\tdefault:\n\t\t\treturn [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 a walk of the catalog knows at one place. */\ninterface Place {\n\treadonly path: ReadonlyArray<Segment>\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.\n */\nconst propertiesOf = (\n\tstruct: SchemaAST.Objects,\n): HashMap.HashMap<string, SchemaAST.PropertySignature> =>\n\tHashMap.fromIterable(\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\tArr.dedupe(\n\t\tArr.flatMap(structs, (struct) =>\n\t\t\tArr.flatMap(struct.propertySignatures, (signature) => {\n\t\t\t\tconst name = nameOf(signature)\n\t\t\t\tif (name === undefined) {\n\t\t\t\t\treturn []\n\t\t\t\t}\n\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// a dot would make two fields have one path, so it is a defect.\n\t\t\t\tif (name.includes(\".\")) {\n\t\t\t\t\tthrow new Error(`A field name may not have a dot: \"${name}\"`)\n\t\t\t\t}\n\t\t\t\t// An empty name would make an empty part in a path, such as\n\t\t\t\t// `.secret`.\n\t\t\t\tif (name === \"\") {\n\t\t\t\t\tthrow new Error(\"A field name may not be empty\")\n\t\t\t\t}\n\t\t\t\treturn [name]\n\t\t\t}),\n\t\t),\n\t)\n\n/**\n * Makes the branch of a place, with the branches below it. `count` is the\n * number of fields so far. It belongs to one walk, and it stops a Schema with\n * very many fields before the walk is done.\n */\nconst branchOf = (place: Place, count: { size: number }): Branch => {\n\tconst structs = Arr.filter(Arr.map(place.schemas, structOf), isStruct)\n\t// The structs of classes. A value of a class is not copied into a plain\n\t// object, so it keeps its getters and methods.\n\tconst classes = Arr.filter(\n\t\tArr.map(\n\t\t\tArr.filter(place.schemas, (schema) => schema._tag === \"Declaration\"),\n\t\t\tstructOf,\n\t\t),\n\t\tisStruct,\n\t)\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\t// The last part that a path may have: the walk does not go below it.\n\tconst last = place.path.length === Limits.fieldPathParts\n\tconst field: Field | undefined =\n\t\tplace.path.length === 0\n\t\t\t? undefined\n\t\t\t: {\n\t\t\t\t\tpath: place.path,\n\t\t\t\t\ttext: textOf(place.path),\n\t\t\t\t\tkinds,\n\t\t\t\t\tlisted:\n\t\t\t\t\t\tvalues.length > 0 ||\n\t\t\t\t\t\trecords.length > 0 ||\n\t\t\t\t\t\t(last && structs.length > 0),\n\t\t\t\t\t// A field that is a scalar in one branch of a union and a struct\n\t\t\t\t\t// in another holds parts too.\n\t\t\t\t\topen:\n\t\t\t\t\t\trecords.length > 0 ||\n\t\t\t\t\t\tstructs.length > 0 ||\n\t\t\t\t\t\tvalueKinds.object ||\n\t\t\t\t\t\tplace.top,\n\t\t\t\t\tplain: place.plain,\n\t\t\t\t}\n\tif (field !== undefined) {\n\t\tcount.size += 1\n\t\tif (count.size > Limits.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\"fields\",\n\t\t\t\tcount.size,\n\t\t\t\tLimits.catalogFields,\n\t\t\t)\n\t\t}\n\t}\n\tif (last) {\n\t\treturn {\n\t\t\tfield,\n\t\t\tchildren: [],\n\t\t\tnames: HashMap.empty(),\n\t\t\tkey: undefined,\n\t\t\tkeyBranch: undefined,\n\t\t}\n\t}\n\tconst structProperties = Arr.map(structs, propertiesOf)\n\tconst classProperties = Arr.map(classes, propertiesOf)\n\tconst children = Arr.map(namesOf(structs), (name) => {\n\t\tconst properties = Arr.flatMap(structProperties, (named) =>\n\t\t\tOption.toArray(HashMap.get(named, name)),\n\t\t)\n\t\tconst below: Place = {\n\t\t\tpath: [...place.path, name],\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\t// A struct without the name may still have it as an extra field,\n\t\t\t// with any value: only a decode takes extra fields away. A value\n\t\t\t// that may be any object, as one of `Schema.Unknown` may, may have\n\t\t\t// the name with any value too.\n\t\t\ttop: place.top || valueKinds.object || properties.length < structs.length,\n\t\t\tplain:\n\t\t\t\tplace.plain &&\n\t\t\t\t!valueKinds.object &&\n\t\t\t\tArr.every(classProperties, (named) => HashMap.has(named, name)),\n\t\t}\n\t\treturn [name, branchOf(below, count)] as const\n\t})\n\tconst names = HashMap.fromIterable(children)\n\tconst signatures = Arr.flatMap(records, (struct) => struct.indexSignatures)\n\tif (signatures.length === 0) {\n\t\treturn { field, children, names, key: undefined, keyBranch: undefined }\n\t}\n\tconst key: Key = {\n\t\t_tag: \"Key\",\n\t\tparameters: Arr.map(signatures, (signature) => signature.parameter),\n\t}\n\tconst keyBranch = branchOf(\n\t\t{\n\t\t\tpath: [...place.path, key],\n\t\t\tschemas: Arr.flatMap(signatures, (signature) =>\n\t\t\t\talternatives(signature.type, new Set()),\n\t\t\t),\n\t\t\t// A record may not have a given key.\n\t\t\tmissing: true,\n\t\t\ttop: place.top || valueKinds.object || records.length < structs.length,\n\t\t\tplain:\n\t\t\t\tplace.plain &&\n\t\t\t\t!valueKinds.object &&\n\t\t\t\tArr.every(classes, (struct) => struct.indexSignatures.length > 0),\n\t\t},\n\t\tcount,\n\t)\n\treturn { field, children, names, key, keyBranch }\n}\n\nconst fieldsOf = (branch: Branch): ReadonlyArray<Field> => [\n\t...(branch.field === undefined ? [] : [branch.field]),\n\t...Arr.flatMap(branch.children, ([, child]) => fieldsOf(child)),\n\t...(branch.keyBranch === undefined ? [] : fieldsOf(branch.keyBranch)),\n]\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 fields than `catalogFields`.\n */\nexport const catalogOf = (ast: SchemaAST.AST): Catalog => {\n\tconst root = branchOf(\n\t\t{\n\t\t\tpath: [],\n\t\t\tschemas: alternatives(ast, new Set()),\n\t\t\tmissing: false,\n\t\t\ttop: false,\n\t\t\tplain: true,\n\t\t},\n\t\t{ size: 0 },\n\t)\n\treturn { fields: fieldsOf(root), root }\n}\n\n/** Gives the field of a catalog at a path with dots, if the catalog has it. */\nexport const lookup = (catalog: Catalog, path: string): Field | undefined => {\n\tconst names = path.split(\".\", Limits.fieldPathParts + 1)\n\tif (names.length > Limits.fieldPathParts) {\n\t\treturn undefined\n\t}\n\tconst branch = Arr.reduce(\n\t\tnames,\n\t\tcatalog.root as Branch | 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.names, name)\n\t\t\tif (Option.isSome(named)) {\n\t\t\t\treturn named.value\n\t\t\t}\n\t\t\treturn current.key !== undefined && keyFits(current.key, name)\n\t\t\t\t? current.keyBranch\n\t\t\t\t: undefined\n\t\t},\n\t)\n\treturn branch?.field\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"catalog.js","names":["Limits.exceeded","Limits.catalogFields"],"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\"\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 decode or a `make` copies a struct or a record into a plain object,\n\t * so a struct without the field gives `null` for it.\n\t */\n\treadonly plain: boolean\n}\n\n/** A place in the tree of a catalog, with the places below it. */\nexport interface Branch {\n\t/** The field at this place. The root has none. */\n\treadonly field: Field | undefined\n\t/** The places below, by name, in the order of the Schema. */\n\treadonly children: ReadonlyArray<readonly [string, Branch]>\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, Branch>\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: Branch | undefined\n}\n\n/** The catalog of the fields of a Schema. */\nexport interface Catalog {\n\t/** Every field, a field before the fields below it. */\n\treadonly fields: ReadonlyArray<Field>\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> => {\n\tswitch (ast._tag) {\n\t\tcase \"Union\":\n\t\t\treturn Arr.flatMap(ast.types, (type) => alternatives(type, open))\n\t\tcase \"Suspend\":\n\t\t\treturn open.has(ast)\n\t\t\t\t? []\n\t\t\t\t: alternatives(ast.thunk(), new Set([...open, ast]))\n\t\tdefault:\n\t\t\treturn [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 a walk of the catalog knows at one place. */\ninterface Place {\n\treadonly path: ReadonlyArray<Segment>\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/**\n * Makes the branch of a place, with the branches below it. `count` is the\n * number of fields so far. It belongs to one walk, and it stops a Schema with\n * very many fields before the walk is done.\n */\nconst branchOf = (place: Place, count: { size: number }): Branch => {\n\tconst structs = Arr.filter(Arr.map(place.schemas, structOf), isStruct)\n\t// The structs of classes. A value of a class is not copied into a plain\n\t// object, so it keeps its getters and methods.\n\tconst classes = Arr.filter(\n\t\tArr.map(\n\t\t\tArr.filter(place.schemas, (schema) => schema._tag === \"Declaration\"),\n\t\t\tstructOf,\n\t\t),\n\t\tisStruct,\n\t)\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\t// The last part that a path may have: the walk does not go below it.\n\tconst last = place.path.length === Limits.fieldPathParts\n\tconst field: Field | undefined =\n\t\tplace.path.length === 0\n\t\t\t? undefined\n\t\t\t: {\n\t\t\t\t\tpath: place.path,\n\t\t\t\t\ttext: textOf(place.path),\n\t\t\t\t\tkinds,\n\t\t\t\t\tlisted:\n\t\t\t\t\t\tvalues.length > 0 ||\n\t\t\t\t\t\trecords.length > 0 ||\n\t\t\t\t\t\t(last && structs.length > 0),\n\t\t\t\t\t// A field that is a scalar in one branch of a union and a struct\n\t\t\t\t\t// in another holds parts too.\n\t\t\t\t\topen:\n\t\t\t\t\t\trecords.length > 0 ||\n\t\t\t\t\t\tstructs.length > 0 ||\n\t\t\t\t\t\tvalueKinds.object ||\n\t\t\t\t\t\tplace.top,\n\t\t\t\t\tplain: place.plain,\n\t\t\t\t}\n\tif (field !== undefined) {\n\t\tcount.size += 1\n\t\tif (count.size > Limits.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\"fields\",\n\t\t\t\tcount.size,\n\t\t\t\tLimits.catalogFields,\n\t\t\t)\n\t\t}\n\t}\n\tif (last) {\n\t\treturn {\n\t\t\tfield,\n\t\t\tchildren: [],\n\t\t\tnames: HashMap.empty(),\n\t\t\tkey: undefined,\n\t\t\tkeyBranch: undefined,\n\t\t}\n\t}\n\tconst structProperties = Arr.map(structs, propertiesOf)\n\tconst classProperties = Arr.map(classes, 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\tconst below: Place = {\n\t\t\tpath: [...place.path, name],\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\t// A struct without the name may still have it as an extra field,\n\t\t\t// with any value: only a decode takes extra fields away. A value\n\t\t\t// that may be any object, as one of `Schema.Unknown` may, may have\n\t\t\t// the name with any value too.\n\t\t\ttop: place.top || valueKinds.object || properties.length < structs.length,\n\t\t\tplain:\n\t\t\t\tplace.plain &&\n\t\t\t\t!valueKinds.object &&\n\t\t\t\tArr.every(classProperties, (named) => named.has(name)),\n\t\t}\n\t\treturn [name, branchOf(below, count)] as const\n\t})\n\tconst names = HashMap.fromIterable(children)\n\tconst signatures = Arr.flatMap(records, (struct) => struct.indexSignatures)\n\tif (signatures.length === 0) {\n\t\treturn { field, children, names, key: undefined, keyBranch: undefined }\n\t}\n\tconst key: Key = {\n\t\t_tag: \"Key\",\n\t\tparameters: Arr.map(signatures, (signature) => signature.parameter),\n\t}\n\tconst keyBranch = branchOf(\n\t\t{\n\t\t\tpath: [...place.path, key],\n\t\t\tschemas: Arr.flatMap(signatures, (signature) =>\n\t\t\t\talternatives(signature.type, new Set()),\n\t\t\t),\n\t\t\t// A record may not have a given key.\n\t\t\tmissing: true,\n\t\t\ttop: place.top || valueKinds.object || records.length < structs.length,\n\t\t\tplain:\n\t\t\t\tplace.plain &&\n\t\t\t\t!valueKinds.object &&\n\t\t\t\tArr.every(classes, (struct) => struct.indexSignatures.length > 0),\n\t\t},\n\t\tcount,\n\t)\n\treturn { field, children, names, key, keyBranch }\n}\n\nconst fieldsOf = (branch: Branch): ReadonlyArray<Field> => [\n\t...(branch.field === undefined ? [] : [branch.field]),\n\t...Arr.flatMap(branch.children, ([, child]) => fieldsOf(child)),\n\t...(branch.keyBranch === undefined ? [] : fieldsOf(branch.keyBranch)),\n]\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 fields than `catalogFields`.\n */\nexport const catalogOf = (ast: SchemaAST.AST): Catalog => {\n\tconst root = branchOf(\n\t\t{\n\t\t\tpath: [],\n\t\t\tschemas: alternatives(ast, new Set()),\n\t\t\tmissing: false,\n\t\t\ttop: false,\n\t\t\tplain: true,\n\t\t},\n\t\t{ size: 0 },\n\t)\n\treturn { fields: fieldsOf(root), root }\n}\n\n/** Gives the field of a catalog at a path with dots, if the catalog has it. */\nexport const lookup = (catalog: Catalog, path: string): Field | undefined => {\n\tconst names = path.split(\".\", Limits.fieldPathParts + 1)\n\tif (names.length > Limits.fieldPathParts) {\n\t\treturn undefined\n\t}\n\tconst branch = Arr.reduce(\n\t\tnames,\n\t\tcatalog.root as Branch | 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.names, name)\n\t\t\tif (Option.isSome(named)) {\n\t\t\t\treturn named.value\n\t\t\t}\n\t\t\treturn current.key !== undefined && keyFits(current.key, name)\n\t\t\t\t? current.keyBranch\n\t\t\t\t: undefined\n\t\t},\n\t)\n\treturn branch?.field\n}\n"],"mappings":"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"}
|
package/dist/internal/decide.js
CHANGED
|
@@ -5,10 +5,10 @@ import { evaluate } from "../Condition.js";
|
|
|
5
5
|
import { descriptorOf } from "./domain.js";
|
|
6
6
|
import { fitsOf, matchesInside, matchesTarget } from "./rule.js";
|
|
7
7
|
import { nodeOf } from "./policy.js";
|
|
8
|
+
import * as Chunk from "effect/Chunk";
|
|
8
9
|
import * as Effect from "effect/Effect";
|
|
9
10
|
import * as Arr from "effect/Array";
|
|
10
11
|
import * as Option from "effect/Option";
|
|
11
|
-
import * as Chunk from "effect/Chunk";
|
|
12
12
|
import * as Ref from "effect/Ref";
|
|
13
13
|
// src/internal/decide.ts
|
|
14
14
|
/**
|
package/dist/internal/domain.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { catalogOf } from "./catalog.js";
|
|
2
2
|
import { descriptor } from "./resource.js";
|
|
3
|
+
import * as HashMap from "effect/HashMap";
|
|
3
4
|
import * as Data from "effect/Data";
|
|
4
5
|
import * as Arr from "effect/Array";
|
|
5
6
|
import * as Option from "effect/Option";
|
|
6
|
-
import * as HashMap from "effect/HashMap";
|
|
7
7
|
// src/internal/domain.ts
|
|
8
8
|
/**
|
|
9
9
|
* The values of domains. The types are in `Domain.ts`.
|
package/dist/internal/json.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { LimitExceeded, jsonNodes } from "./limits.js";
|
|
2
|
-
import { And, Compare, Exists, In, Not, Or, Under, always, isScalar, literal, never, principalRef, toJson as toJson$1 } from "./nodes.js";
|
|
2
|
+
import { And, Compare, Exists, In, Not, Or, Under, always, isCompareOperator, isScalar, literal, never, principalRef, toJson as toJson$1 } from "./nodes.js";
|
|
3
3
|
import { readPart } from "./read.js";
|
|
4
4
|
import * as Result from "effect/Result";
|
|
5
5
|
import * as Data from "effect/Data";
|
|
@@ -66,14 +66,6 @@ const decodeOperands = (input, name, path, budget) => Arr.map(readList(input, na
|
|
|
66
66
|
name,
|
|
67
67
|
index
|
|
68
68
|
]));
|
|
69
|
-
const compareOperators = [
|
|
70
|
-
"eq",
|
|
71
|
-
"ne",
|
|
72
|
-
"lt",
|
|
73
|
-
"lte",
|
|
74
|
-
"gt",
|
|
75
|
-
"gte"
|
|
76
|
-
];
|
|
77
69
|
const decodeNode = (input, path, depth, budget) => {
|
|
78
70
|
if (depth > 500) throw problem(path, `A condition has more than ${String(500)} levels. The most is ${String(500)}.`);
|
|
79
71
|
if (typeof input !== "object" || input === null) throw problem(path, "A condition node must be an object");
|
|
@@ -96,7 +88,7 @@ const makeNode = (input, path, depth, budget, children) => {
|
|
|
96
88
|
case "False": return never;
|
|
97
89
|
case "Compare": {
|
|
98
90
|
const op = readString(input, "op", path);
|
|
99
|
-
if (!
|
|
91
|
+
if (!isCompareOperator(op)) throw problem([...path, "op"], `"${op}" is not a compare operator`);
|
|
100
92
|
return new Compare({
|
|
101
93
|
op,
|
|
102
94
|
field: readString(input, "field", path),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"json.js","names":["Nodes.toJson","Limits.jsonNodes","Nodes.isScalar","Nodes.literal","Nodes.principalRef","Limits.LimitExceeded","Nodes.always","Nodes.never","Nodes.Compare","Nodes.In","Nodes.Exists","Nodes.Under","Nodes.And","Nodes.Or","Nodes.Not"],"sources":["../../src/internal/json.ts"],"sourcesContent":["/**\n * The JSON form of a condition. The decoder is a plain walk with two limits:\n * it goes at most `conditionDepth` levels down, and it reads at most\n * `jsonNodes` objects and array items, with a shared object counted for each\n * place. So no input can fill the stack or take a very long time. It makes\n * each node with its constructor, and it reads only the own data fields of\n * the input, as a condition reads a value.\n */\nimport * as Arr from \"effect/Array\"\nimport * as Data from \"effect/Data\"\nimport * as Result from \"effect/Result\"\nimport type * as SchemaIssue from \"effect/SchemaIssue\"\nimport * as Limits from \"./limits.ts\"\nimport * as Nodes from \"./nodes.ts\"\nimport { readPart } from \"./read.ts\"\n\ntype Node = Nodes.Node\n\n/** Gives the JSON form of a condition with no predicate. See `Nodes.toJson`. */\nexport const toJson = (node: Node): unknown => Nodes.toJson(node)\n\n/** A problem of an input, with the path of the problem. */\nexport class Problem extends Data.TaggedError(\"Problem\")<{\n\treadonly path: ReadonlyArray<PropertyKey>\n\treadonly message: string\n\t/** The issue of a Schema that the decode used, as for `meta`. */\n\treadonly issue?: SchemaIssue.Issue | undefined\n}> {}\n\n/** The count of the objects and items that one decode has read. It belongs to one decode. */\nexport interface Budget {\n\tused: number\n}\n\nexport const problem = (path: ReadonlyArray<PropertyKey>, message: string) =>\n\tnew Problem({ path, message })\n\n/** Counts `count` more objects or items, and stops past the limit. */\nexport const spend = (\n\tbudget: Budget,\n\tcount: number,\n\tpath: ReadonlyArray<PropertyKey>,\n): void => {\n\tbudget.used += count\n\tif (budget.used > Limits.jsonNodes) {\n\t\tthrow problem(\n\t\t\tpath,\n\t\t\t`The input has more than ${String(Limits.jsonNodes)} objects and items. The most is ${String(Limits.jsonNodes)}.`,\n\t\t)\n\t}\n}\n\n/** Reads a field of the input. A field that is not plain data is a problem. */\nexport const read = (\n\tinput: unknown,\n\tname: string,\n\tpath: ReadonlyArray<PropertyKey>,\n): unknown => {\n\ttry {\n\t\treturn readPart(input, name)\n\t} catch {\n\t\tthrow problem([...path, name], `The field \"${name}\" is not plain data`)\n\t}\n}\n\nexport const readString = (\n\tinput: unknown,\n\tname: string,\n\tpath: ReadonlyArray<PropertyKey>,\n): string => {\n\tconst value = read(input, name, path)\n\tif (typeof value !== \"string\") {\n\t\tthrow problem([...path, name], `The field \"${name}\" must be a string`)\n\t}\n\treturn value\n}\n\n/** Reads the items of an array. */\nexport const readList = (\n\tinput: unknown,\n\tname: string,\n\tpath: ReadonlyArray<PropertyKey>,\n\tbudget: Budget,\n): ReadonlyArray<unknown> => {\n\tconst value = read(input, name, path)\n\tconst at = [...path, name]\n\tif (!Array.isArray(value)) {\n\t\tthrow problem(at, `The field \"${name}\" must be an array`)\n\t}\n\tconst list: ReadonlyArray<unknown> = value\n\tspend(budget, list.length, at)\n\t// `Array.makeBy` of Effect gives at least one item, so it can not make an\n\t// empty list. An array-like object with a length makes one with no read\n\t// of the items before the read rule.\n\treturn Array.from({ length: list.length }, (_, index) =>\n\t\tread(list, String(index), at),\n\t)\n}\n\nconst isJsonScalar = (value: unknown): value is Nodes.Scalar =>\n\tNodes.isScalar(value) && (typeof value !== \"number\" || Number.isFinite(value))\n\nconst decodeOperand = (\n\tinput: unknown,\n\tpath: ReadonlyArray<PropertyKey>,\n): Nodes.Operand => {\n\tconst tag = read(input, \"_tag\", path)\n\tif (tag === \"Literal\") {\n\t\t// A missing value is not `null`: a test with `null` has its own\n\t\t// meaning, and a lost value must not become one.\n\t\tconst own =\n\t\t\ttypeof input === \"object\" && input !== null\n\t\t\t\t? Object.getOwnPropertyDescriptor(input, \"value\")\n\t\t\t\t: undefined\n\t\tif (own === undefined || (\"value\" in own && own.value === undefined)) {\n\t\t\tthrow problem([...path, \"value\"], \"A fixed value is missing\")\n\t\t}\n\t\tconst value = read(input, \"value\", path)\n\t\tif (!isJsonScalar(value)) {\n\t\t\tthrow problem(\n\t\t\t\t[...path, \"value\"],\n\t\t\t\t\"A fixed value must be a string, a finite number, a boolean or null\",\n\t\t\t)\n\t\t}\n\t\treturn Nodes.literal(value)\n\t}\n\tif (tag === \"Principal\") {\n\t\treturn Nodes.principalRef(readString(input, \"path\", path))\n\t}\n\tthrow problem(\n\t\t[...path, \"_tag\"],\n\t\t'An operand must have the tag \"Literal\" or \"Principal\"',\n\t)\n}\n\nconst decodeOperands = (\n\tinput: unknown,\n\tname: string,\n\tpath: ReadonlyArray<PropertyKey>,\n\tbudget: Budget,\n): ReadonlyArray<Nodes.Operand> =>\n\tArr.map(readList(input, name, path, budget), (operand, index) =>\n\t\tdecodeOperand(operand, [...path, name, index]),\n\t)\n\nconst compareOperators: ReadonlyArray<string> = [\n\t\"eq\",\n\t\"ne\",\n\t\"lt\",\n\t\"lte\",\n\t\"gt\",\n\t\"gte\",\n]\n\nconst decodeNode = (\n\tinput: unknown,\n\tpath: ReadonlyArray<PropertyKey>,\n\tdepth: number,\n\tbudget: Budget,\n): Node => {\n\tif (depth > Limits.conditionDepth) {\n\t\tthrow problem(\n\t\t\tpath,\n\t\t\t`A condition has more than ${String(Limits.conditionDepth)} levels. The most is ${String(Limits.conditionDepth)}.`,\n\t\t)\n\t}\n\tif (typeof input !== \"object\" || input === null) {\n\t\tthrow problem(path, \"A condition node must be an object\")\n\t}\n\tspend(budget, 1, path)\n\tconst children = (name: string) =>\n\t\tArr.map(readList(input, name, path, budget), (child, index) =>\n\t\t\tdecodeNode(child, [...path, name, index], depth + 1, budget),\n\t\t)\n\ttry {\n\t\treturn makeNode(input, path, depth, budget, children)\n\t} catch (error) {\n\t\t// A node past a limit, such as a field path with too many parts, is a\n\t\t// problem at its place.\n\t\tif (error instanceof Limits.LimitExceeded) {\n\t\t\tthrow problem(path, error.message)\n\t\t}\n\t\tthrow error\n\t}\n}\n\nconst makeNode = (\n\tinput: object,\n\tpath: ReadonlyArray<PropertyKey>,\n\tdepth: number,\n\tbudget: Budget,\n\tchildren: (name: string) => ReadonlyArray<Node>,\n): Node => {\n\tconst tag = read(input, \"_tag\", path)\n\tswitch (tag) {\n\t\tcase \"True\":\n\t\t\treturn Nodes.always\n\t\tcase \"False\":\n\t\t\treturn Nodes.never\n\t\tcase \"Compare\": {\n\t\t\tconst op = readString(input, \"op\", path)\n\t\t\tif (!compareOperators.includes(op)) {\n\t\t\t\tthrow problem([...path, \"op\"], `\"${op}\" is not a compare operator`)\n\t\t\t}\n\t\t\treturn new Nodes.Compare({\n\t\t\t\top: op as Nodes.CompareOperator,\n\t\t\t\tfield: readString(input, \"field\", path),\n\t\t\t\tvalue: decodeOperand(read(input, \"value\", path), [...path, \"value\"]),\n\t\t\t})\n\t\t}\n\t\tcase \"In\":\n\t\t\treturn new Nodes.In({\n\t\t\t\tfield: readString(input, \"field\", path),\n\t\t\t\tvalues: decodeOperands(input, \"values\", path, budget),\n\t\t\t})\n\t\tcase \"Exists\":\n\t\t\treturn new Nodes.Exists({ field: readString(input, \"field\", path) })\n\t\tcase \"Under\":\n\t\t\treturn new Nodes.Under({\n\t\t\t\tprefix: decodeOperands(input, \"prefix\", path, budget),\n\t\t\t})\n\t\tcase \"And\":\n\t\t\treturn new Nodes.And(children(\"conditions\"))\n\t\tcase \"Or\":\n\t\t\treturn new Nodes.Or(children(\"conditions\"))\n\t\tcase \"Not\":\n\t\t\treturn new Nodes.Not(\n\t\t\t\tdecodeNode(\n\t\t\t\t\tread(input, \"condition\", path),\n\t\t\t\t\t[...path, \"condition\"],\n\t\t\t\t\tdepth + 1,\n\t\t\t\t\tbudget,\n\t\t\t\t),\n\t\t\t)\n\t\tdefault:\n\t\t\tthrow problem([...path, \"_tag\"], \"The node has no known tag\")\n\t}\n}\n\n/**\n * Decodes the JSON form of a condition at a path, with the budget of a larger\n * decode. It throws a {@link Problem}.\n */\nexport const decodeAt = (\n\tinput: unknown,\n\tpath: ReadonlyArray<PropertyKey>,\n\tbudget: Budget,\n): Node => decodeNode(input, path, 1, budget)\n\n/** Decodes the JSON form of a condition. */\nexport const decode = (input: unknown): Result.Result<Node, Problem> => {\n\ttry {\n\t\treturn Result.succeed(decodeNode(input, [], 1, { used: 0 }))\n\t} catch (error) {\n\t\tif (error instanceof Problem) {\n\t\t\treturn Result.fail(error)\n\t\t}\n\t\tthrow error\n\t}\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"json.js","names":["Nodes.toJson","Limits.jsonNodes","Nodes.isScalar","Nodes.literal","Nodes.principalRef","Limits.LimitExceeded","Nodes.always","Nodes.never","Nodes.isCompareOperator","Nodes.Compare","Nodes.In","Nodes.Exists","Nodes.Under","Nodes.And","Nodes.Or","Nodes.Not"],"sources":["../../src/internal/json.ts"],"sourcesContent":["/**\n * The JSON form of a condition. The decoder is a plain walk with two limits:\n * it goes at most `conditionDepth` levels down, and it reads at most\n * `jsonNodes` objects and array items, with a shared object counted for each\n * place. So no input can fill the stack or take a very long time. It makes\n * each node with its constructor, and it reads only the own data fields of\n * the input, as a condition reads a value.\n */\nimport * as Arr from \"effect/Array\"\nimport * as Data from \"effect/Data\"\nimport * as Result from \"effect/Result\"\nimport type * as SchemaIssue from \"effect/SchemaIssue\"\nimport * as Limits from \"./limits.ts\"\nimport * as Nodes from \"./nodes.ts\"\nimport { readPart } from \"./read.ts\"\n\ntype Node = Nodes.Node\n\n/** Gives the JSON form of a condition with no predicate. See `Nodes.toJson`. */\nexport const toJson = (node: Node): unknown => Nodes.toJson(node)\n\n/** A problem of an input, with the path of the problem. */\nexport class Problem extends Data.TaggedError(\"Problem\")<{\n\treadonly path: ReadonlyArray<PropertyKey>\n\treadonly message: string\n\t/** The issue of a Schema that the decode used, as for `meta`. */\n\treadonly issue?: SchemaIssue.Issue | undefined\n}> {}\n\n/** The count of the objects and items that one decode has read. It belongs to one decode. */\nexport interface Budget {\n\tused: number\n}\n\nexport const problem = (path: ReadonlyArray<PropertyKey>, message: string) =>\n\tnew Problem({ path, message })\n\n/** Counts `count` more objects or items, and stops past the limit. */\nexport const spend = (\n\tbudget: Budget,\n\tcount: number,\n\tpath: ReadonlyArray<PropertyKey>,\n): void => {\n\tbudget.used += count\n\tif (budget.used > Limits.jsonNodes) {\n\t\tthrow problem(\n\t\t\tpath,\n\t\t\t`The input has more than ${String(Limits.jsonNodes)} objects and items. The most is ${String(Limits.jsonNodes)}.`,\n\t\t)\n\t}\n}\n\n/** Reads a field of the input. A field that is not plain data is a problem. */\nexport const read = (\n\tinput: unknown,\n\tname: string,\n\tpath: ReadonlyArray<PropertyKey>,\n): unknown => {\n\ttry {\n\t\treturn readPart(input, name)\n\t} catch {\n\t\tthrow problem([...path, name], `The field \"${name}\" is not plain data`)\n\t}\n}\n\nexport const readString = (\n\tinput: unknown,\n\tname: string,\n\tpath: ReadonlyArray<PropertyKey>,\n): string => {\n\tconst value = read(input, name, path)\n\tif (typeof value !== \"string\") {\n\t\tthrow problem([...path, name], `The field \"${name}\" must be a string`)\n\t}\n\treturn value\n}\n\n/** Reads the items of an array. */\nexport const readList = (\n\tinput: unknown,\n\tname: string,\n\tpath: ReadonlyArray<PropertyKey>,\n\tbudget: Budget,\n): ReadonlyArray<unknown> => {\n\tconst value = read(input, name, path)\n\tconst at = [...path, name]\n\tif (!Array.isArray(value)) {\n\t\tthrow problem(at, `The field \"${name}\" must be an array`)\n\t}\n\tconst list: ReadonlyArray<unknown> = value\n\tspend(budget, list.length, at)\n\t// `Array.makeBy` of Effect gives at least one item, so it can not make an\n\t// empty list. An array-like object with a length makes one with no read\n\t// of the items before the read rule.\n\treturn Array.from({ length: list.length }, (_, index) =>\n\t\tread(list, String(index), at),\n\t)\n}\n\nconst isJsonScalar = (value: unknown): value is Nodes.Scalar =>\n\tNodes.isScalar(value) && (typeof value !== \"number\" || Number.isFinite(value))\n\nconst decodeOperand = (\n\tinput: unknown,\n\tpath: ReadonlyArray<PropertyKey>,\n): Nodes.Operand => {\n\tconst tag = read(input, \"_tag\", path)\n\tif (tag === \"Literal\") {\n\t\t// A missing value is not `null`: a test with `null` has its own\n\t\t// meaning, and a lost value must not become one.\n\t\tconst own =\n\t\t\ttypeof input === \"object\" && input !== null\n\t\t\t\t? Object.getOwnPropertyDescriptor(input, \"value\")\n\t\t\t\t: undefined\n\t\tif (own === undefined || (\"value\" in own && own.value === undefined)) {\n\t\t\tthrow problem([...path, \"value\"], \"A fixed value is missing\")\n\t\t}\n\t\tconst value = read(input, \"value\", path)\n\t\tif (!isJsonScalar(value)) {\n\t\t\tthrow problem(\n\t\t\t\t[...path, \"value\"],\n\t\t\t\t\"A fixed value must be a string, a finite number, a boolean or null\",\n\t\t\t)\n\t\t}\n\t\treturn Nodes.literal(value)\n\t}\n\tif (tag === \"Principal\") {\n\t\treturn Nodes.principalRef(readString(input, \"path\", path))\n\t}\n\tthrow problem(\n\t\t[...path, \"_tag\"],\n\t\t'An operand must have the tag \"Literal\" or \"Principal\"',\n\t)\n}\n\nconst decodeOperands = (\n\tinput: unknown,\n\tname: string,\n\tpath: ReadonlyArray<PropertyKey>,\n\tbudget: Budget,\n): ReadonlyArray<Nodes.Operand> =>\n\tArr.map(readList(input, name, path, budget), (operand, index) =>\n\t\tdecodeOperand(operand, [...path, name, index]),\n\t)\n\nconst decodeNode = (\n\tinput: unknown,\n\tpath: ReadonlyArray<PropertyKey>,\n\tdepth: number,\n\tbudget: Budget,\n): Node => {\n\tif (depth > Limits.conditionDepth) {\n\t\tthrow problem(\n\t\t\tpath,\n\t\t\t`A condition has more than ${String(Limits.conditionDepth)} levels. The most is ${String(Limits.conditionDepth)}.`,\n\t\t)\n\t}\n\tif (typeof input !== \"object\" || input === null) {\n\t\tthrow problem(path, \"A condition node must be an object\")\n\t}\n\tspend(budget, 1, path)\n\tconst children = (name: string) =>\n\t\tArr.map(readList(input, name, path, budget), (child, index) =>\n\t\t\tdecodeNode(child, [...path, name, index], depth + 1, budget),\n\t\t)\n\ttry {\n\t\treturn makeNode(input, path, depth, budget, children)\n\t} catch (error) {\n\t\t// A node past a limit, such as a field path with too many parts, is a\n\t\t// problem at its place.\n\t\tif (error instanceof Limits.LimitExceeded) {\n\t\t\tthrow problem(path, error.message)\n\t\t}\n\t\tthrow error\n\t}\n}\n\nconst makeNode = (\n\tinput: object,\n\tpath: ReadonlyArray<PropertyKey>,\n\tdepth: number,\n\tbudget: Budget,\n\tchildren: (name: string) => ReadonlyArray<Node>,\n): Node => {\n\tconst tag = read(input, \"_tag\", path)\n\tswitch (tag) {\n\t\tcase \"True\":\n\t\t\treturn Nodes.always\n\t\tcase \"False\":\n\t\t\treturn Nodes.never\n\t\tcase \"Compare\": {\n\t\t\tconst op = readString(input, \"op\", path)\n\t\t\tif (!Nodes.isCompareOperator(op)) {\n\t\t\t\tthrow problem([...path, \"op\"], `\"${op}\" is not a compare operator`)\n\t\t\t}\n\t\t\treturn new Nodes.Compare({\n\t\t\t\top,\n\t\t\t\tfield: readString(input, \"field\", path),\n\t\t\t\tvalue: decodeOperand(read(input, \"value\", path), [...path, \"value\"]),\n\t\t\t})\n\t\t}\n\t\tcase \"In\":\n\t\t\treturn new Nodes.In({\n\t\t\t\tfield: readString(input, \"field\", path),\n\t\t\t\tvalues: decodeOperands(input, \"values\", path, budget),\n\t\t\t})\n\t\tcase \"Exists\":\n\t\t\treturn new Nodes.Exists({ field: readString(input, \"field\", path) })\n\t\tcase \"Under\":\n\t\t\treturn new Nodes.Under({\n\t\t\t\tprefix: decodeOperands(input, \"prefix\", path, budget),\n\t\t\t})\n\t\tcase \"And\":\n\t\t\treturn new Nodes.And(children(\"conditions\"))\n\t\tcase \"Or\":\n\t\t\treturn new Nodes.Or(children(\"conditions\"))\n\t\tcase \"Not\":\n\t\t\treturn new Nodes.Not(\n\t\t\t\tdecodeNode(\n\t\t\t\t\tread(input, \"condition\", path),\n\t\t\t\t\t[...path, \"condition\"],\n\t\t\t\t\tdepth + 1,\n\t\t\t\t\tbudget,\n\t\t\t\t),\n\t\t\t)\n\t\tdefault:\n\t\t\tthrow problem([...path, \"_tag\"], \"The node has no known tag\")\n\t}\n}\n\n/**\n * Decodes the JSON form of a condition at a path, with the budget of a larger\n * decode. It throws a {@link Problem}.\n */\nexport const decodeAt = (\n\tinput: unknown,\n\tpath: ReadonlyArray<PropertyKey>,\n\tbudget: Budget,\n): Node => decodeNode(input, path, 1, budget)\n\n/** Decodes the JSON form of a condition. */\nexport const decode = (input: unknown): Result.Result<Node, Problem> => {\n\ttry {\n\t\treturn Result.succeed(decodeNode(input, [], 1, { used: 0 }))\n\t} catch (error) {\n\t\tif (error instanceof Problem) {\n\t\t\treturn Result.fail(error)\n\t\t}\n\t\tthrow error\n\t}\n}\n"],"mappings":"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"}
|
package/dist/internal/nodes.d.ts
CHANGED
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
import * as Effect from "effect/Effect";
|
|
2
|
+
import * as Schema from "effect/Schema";
|
|
2
3
|
import * as Data from "effect/Data";
|
|
3
4
|
/** A value that a condition can compare. */
|
|
4
5
|
export type Scalar = string | number | boolean | null;
|
|
5
|
-
|
|
6
|
+
/** The operators of a `Compare` test. The decoders read this list too. */
|
|
7
|
+
export declare const CompareOperator: Schema.Literals<readonly ["eq", "ne", "lt", "lte", "gt", "gte"]>;
|
|
8
|
+
export type CompareOperator = typeof CompareOperator.Type;
|
|
6
9
|
/** A fixed value. */
|
|
7
10
|
export interface Literal {
|
|
8
11
|
readonly _tag: "Literal";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"nodes.d.ts","names":[],"sources":["../../src/internal/nodes.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"nodes.d.ts","names":[],"sources":["../../src/internal/nodes.ts"],"mappings":";;;;YA2BY;;qBAGC,iBAAe,OAAA;YAShB,yBAAyB,gBAAgB;;iBAWpC;WACP;WACA,OAAO;;;iBAIA;WACP;WACA;;YAGE,UAAU,UAAU;UA+DtB;WACA;WACA;WACA;;;uBAoCK,aAAa,KAAK;;;;;;;EAMhC;EAMA,YAAY,eAAe,OAAO;;MAM9B;;MAKA;;MAKA;;;qBAMQ,aAAa;WAChB;EACT;;;qBAMY,cAAc;WACjB;EACT;;;qBAMY,gBAAgB;WACnB;WACQ,IAAI;WACJ;WACA,OAAO;EACxB,YAAY;aACF,IAAI;aACJ;aACA,OAAO;;;;qBAiBL,WAAW;WACd;WACQ;WACA,QAAQ,cAAc;EACvC,YAAY;aACF;aACA,QAAQ,cAAc;;;;qBAUpB,eAAe;WAClB;WACQ;EACjB,YAAY;aAAkB;;;;qBAMlB,cAAc;WACjB;WACQ,QAAQ,cAAc;EACvC,YAAY;aAAkB,QAAQ,cAAc;;;qBAgBxC,YAAY;WACf;WACQ,YAAY,cAAc;EAC3C,YAAY,YAAY,cAAc;;qBAM1B,WAAW;WACd;WACQ,YAAY,cAAc;EAC3C,YAAY,YAAY,cAAc;;qBAM1B,YAAY;WACf;WACQ,WAAW;EAC5B,YAAY,WAAW;;;YASZ,OACX,gBACA,uBACI,OAAO;;;;;;qBAOC,kBAAkB;WACrB;WACQ;WACA,MAChB,cACA,qBACI,OAAO;EACZ,YAAY;aAAkB;aAAuB,KAAK;;;;YAe/C,OACX,OAAO,QAAQ,UAAU,KAAK,SAAS,QAAQ,MAAM,KAAK,MAAM"}
|
package/dist/internal/nodes.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { __exportAll } from "../_virtual/_rolldown/runtime.js";
|
|
2
2
|
import { conditionNodes, exceeded } from "./limits.js";
|
|
3
|
+
import * as Schema from "effect/Schema";
|
|
3
4
|
import * as Data from "effect/Data";
|
|
4
5
|
import * as Arr from "effect/Array";
|
|
5
6
|
import { hasProperty } from "effect/Predicate";
|
|
@@ -23,6 +24,7 @@ import { hasProperty } from "effect/Predicate";
|
|
|
23
24
|
var nodes_exports = /* @__PURE__ */ __exportAll({
|
|
24
25
|
And: () => And,
|
|
25
26
|
Compare: () => Compare,
|
|
27
|
+
CompareOperator: () => CompareOperator,
|
|
26
28
|
Exists: () => Exists,
|
|
27
29
|
False: () => False,
|
|
28
30
|
In: () => In,
|
|
@@ -32,6 +34,7 @@ var nodes_exports = /* @__PURE__ */ __exportAll({
|
|
|
32
34
|
True: () => True,
|
|
33
35
|
Under: () => Under,
|
|
34
36
|
always: () => always,
|
|
37
|
+
isCompareOperator: () => isCompareOperator,
|
|
35
38
|
isNode: () => isNode,
|
|
36
39
|
isScalar: () => isScalar,
|
|
37
40
|
literal: () => literal,
|
|
@@ -41,17 +44,24 @@ var nodes_exports = /* @__PURE__ */ __exportAll({
|
|
|
41
44
|
toJson: () => toJson,
|
|
42
45
|
toNode: () => toNode
|
|
43
46
|
});
|
|
44
|
-
|
|
45
|
-
const
|
|
46
|
-
const principalRef = (path) => operands.Principal({ path });
|
|
47
|
-
const compareOperators = [
|
|
47
|
+
/** The operators of a `Compare` test. The decoders read this list too. */
|
|
48
|
+
const CompareOperator = Schema.Literals([
|
|
48
49
|
"eq",
|
|
49
50
|
"ne",
|
|
50
51
|
"lt",
|
|
51
52
|
"lte",
|
|
52
53
|
"gt",
|
|
53
54
|
"gte"
|
|
54
|
-
];
|
|
55
|
+
]);
|
|
56
|
+
/**
|
|
57
|
+
* Tests if a value is an operator. It reads the list of the Schema with
|
|
58
|
+
* `includes`: for a short list, this is about twice as fast as
|
|
59
|
+
* `Schema.is`, and a decoder tests each node of a large input.
|
|
60
|
+
*/
|
|
61
|
+
const isCompareOperator = (u) => CompareOperator.literals.includes(u);
|
|
62
|
+
const operands = Data.taggedEnum();
|
|
63
|
+
const literal = (value) => operands.Literal({ value });
|
|
64
|
+
const principalRef = (path) => operands.Principal({ path });
|
|
55
65
|
/** Tests if a value is a scalar. Any number is one, also `NaN`. */
|
|
56
66
|
const isScalar = (value) => value === null || typeof value === "string" || typeof value === "number" || typeof value === "boolean";
|
|
57
67
|
/**
|
|
@@ -139,7 +149,7 @@ var False = class extends Base {
|
|
|
139
149
|
var Compare = class extends Base {
|
|
140
150
|
_tag = "Compare";
|
|
141
151
|
constructor(props) {
|
|
142
|
-
if (!
|
|
152
|
+
if (!isCompareOperator(props.op)) throw new Error(`"${String(props.op)}" is not a compare operator`);
|
|
143
153
|
super({
|
|
144
154
|
op: props.op,
|
|
145
155
|
field: checkPath(props.field),
|
|
@@ -272,6 +282,6 @@ const toNode = (u) => {
|
|
|
272
282
|
};
|
|
273
283
|
const always = new True();
|
|
274
284
|
const never = new False();
|
|
275
|
-
export { And, Compare, Exists, False, In, Not, Or, Predicate, True, Under, always, isNode, isScalar, literal, never, nodes_exports, principalRef, runOf, toJson, toNode };
|
|
285
|
+
export { And, Compare, CompareOperator, Exists, False, In, Not, Or, Predicate, True, Under, always, isCompareOperator, isNode, isScalar, literal, never, nodes_exports, principalRef, runOf, toJson, toNode };
|
|
276
286
|
|
|
277
287
|
//# sourceMappingURL=nodes.js.map
|