@nmnmcc/ability 2.1.2 → 2.1.3

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.
@@ -1 +1 @@
1
- {"version":3,"file":"catalog.js","names":["Trampoline.adaptive","Limits.exceeded","Limits.fieldPathParts"],"sources":["../../src/internal/catalog.ts"],"sourcesContent":["/**\n * The catalog of the fields of a Schema. One walk over the Schema makes it,\n * and every question about the fields of a resource or of the principal reads\n * it: which names a rule may use, which values a field may hold, which fields\n * a pattern fits, and which fields a list of permitted fields shows.\n *\n * The catalog describes the shape of the values, not the checks of the\n * Schema. A check, such as a minimum length, only keeps some values out, so a\n * catalog that leaves the checks out holds every real value and more. An\n * answer that holds for all of these values, as `All` and `None` of a\n * coverage do, then holds for the real values too.\n */\nimport * as Arr from \"effect/Array\"\nimport * as HashMap from \"effect/HashMap\"\nimport * as HashSet from \"effect/HashSet\"\nimport * as Option from \"effect/Option\"\nimport { hasProperty } from \"effect/Predicate\"\nimport * as Schema from \"effect/Schema\"\nimport * as SchemaAST from \"effect/SchemaAST\"\nimport * as Limits from \"./limits.ts\"\nimport * as Trampoline from \"./trampoline.ts\"\n\n/** The kinds of values that a field may hold. */\nexport interface Kinds {\n\t/** The field may be `null`, `undefined` or missing. */\n\treadonly null: boolean\n\treadonly boolean: boolean\n\t/** The field may hold any string. */\n\treadonly string: boolean\n\t/** The numbers that the field may hold. */\n\treadonly number: \"none\" | \"integer\" | \"finite\" | \"any\"\n\t/** The field may hold an object, or a value that is not a scalar. */\n\treadonly object: boolean\n\t/** Single values that the field may hold, other than the ones above. */\n\treadonly literals: ReadonlyArray<string | number | boolean>\n}\n\n/** No value: the kinds of a field of `Schema.Never`. */\nexport const noKinds: Kinds = {\n\tnull: false,\n\tboolean: false,\n\tstring: false,\n\tnumber: \"none\",\n\tobject: false,\n\tliterals: [],\n}\n\n/** Any value. */\nexport const anyKinds: Kinds = {\n\tnull: true,\n\tboolean: true,\n\tstring: true,\n\tnumber: \"any\",\n\tobject: true,\n\tliterals: [],\n}\n\nconst nullKinds: Kinds = { ...noKinds, null: true }\nconst objectKinds: Kinds = { ...noKinds, object: true }\n\nconst numberOrder = [\"none\", \"integer\", \"finite\", \"any\"] as const\n\n/** The kinds of a field that may hold a value of `left` or of `right`. */\nexport const joinKinds = (left: Kinds, right: Kinds): Kinds => ({\n\tnull: left.null || right.null,\n\tboolean: left.boolean || right.boolean,\n\tstring: left.string || right.string,\n\tnumber:\n\t\tnumberOrder.indexOf(left.number) >= numberOrder.indexOf(right.number)\n\t\t\t? left.number\n\t\t\t: right.number,\n\tobject: left.object || right.object,\n\t// A native `Set` finds copies about ten times as fast as `Arr.dedupe`. It\n\t// takes `0` and `-0` as one, as `includes` does when a test reads them.\n\tliterals: Array.from(new Set([...left.literals, ...right.literals])),\n})\n\n/** Tests if a number fits the numbers of a field. */\nexport const fitsNumber = (number: Kinds[\"number\"], value: number): boolean => {\n\tswitch (number) {\n\t\tcase \"none\":\n\t\t\treturn false\n\t\tcase \"integer\":\n\t\t\treturn Number.isInteger(value)\n\t\tcase \"finite\":\n\t\t\treturn Number.isFinite(value)\n\t\tcase \"any\":\n\t\t\treturn true\n\t}\n}\n\n/**\n * Tests if a field with these kinds may hold a value. A missing value and\n * `undefined` are `null`.\n */\nexport const accepts = (kinds: Kinds, value: unknown): boolean => {\n\tif (value === null || value === undefined) {\n\t\treturn kinds.null\n\t}\n\tswitch (typeof value) {\n\t\tcase \"boolean\":\n\t\t\treturn kinds.boolean || kinds.literals.includes(value)\n\t\tcase \"string\":\n\t\t\treturn kinds.string || kinds.literals.includes(value)\n\t\tcase \"number\":\n\t\t\treturn fitsNumber(kinds.number, value) || kinds.literals.includes(value)\n\t\tdefault:\n\t\t\treturn kinds.object\n\t}\n}\n\n/** A part of a field path that stands for any key of a record. */\nexport interface Key {\n\treadonly _tag: \"Key\"\n\t/** The Schemas of the keys. A key must fit one of them. */\n\treadonly parameters: ReadonlyArray<SchemaAST.AST>\n}\n\n/** One part of a field path: a name, or any key of a record. */\nexport type Segment = string | Key\n\n/** One field of a catalog. */\nexport interface Field {\n\treadonly path: ReadonlyArray<Segment>\n\t/** The path with dots, and `*` for a key of a record. */\n\treadonly text: string\n\treadonly kinds: Kinds\n\t/**\n\t * The field holds a value that a list of fields shows as one field: a\n\t * scalar, a record, an array, or a struct at the last part a path may have.\n\t */\n\treadonly listed: boolean\n\t/** The field may hold parts that the Schema does not name, as a record may. */\n\treadonly open: boolean\n\t/**\n\t * A read of the field finds only plain data. It is `false` when a value\n\t * above the field may be an object that does not have it as a data field:\n\t * a class without the field, which may have a getter of that name, a\n\t * value such as a date or a list, or any object, as of `Schema.Unknown`.\n\t * A struct of a union without the field makes it not plain too, since a\n\t * value made by hand may have it with any value.\n\t */\n\treadonly plain: boolean\n}\n\n/**\n * A place in the graph of a catalog. A path of a field leads to a place, and\n * many paths may lead to one place, as in a recursive Schema: `next` and\n * `next.next` of a list of links are one place. A place keeps the ids of the\n * places below it, so the graph has no loop of objects.\n */\nexport interface Branch {\n\treadonly id: number\n\t/** The kinds of the values of the field at this place. */\n\treadonly kinds: Kinds\n\t/** The field holds a scalar, a record or a list: see `Field.listed`. */\n\treadonly values: boolean\n\t/** The field may hold a struct. */\n\treadonly structs: boolean\n\treadonly open: boolean\n\treadonly plain: boolean\n\t/** The ids of the places below, by name, in the order of the Schema. */\n\treadonly children: ReadonlyArray<readonly [string, number]>\n\t/**\n\t * The places below, by name. A lookup gets each name from `split`, as a\n\t * new string, and a `HashMap` finds a new string about 20% faster than a\n\t * native `Map`. The names come from the Schema, so a name from a policy\n\t * can not make a long list of names with one hash here.\n\t */\n\treadonly names: HashMap.HashMap<string, number>\n\t/** The place for the keys of a record, when the value may be a record. */\n\treadonly key: Key | undefined\n\treadonly keyBranch: number | undefined\n}\n\n/** The catalog of the fields of a Schema. */\nexport interface Catalog {\n\t/**\n\t * The fields that a list shows, a field before the fields below it. For a\n\t * recursive Schema, the list goes into each recursive place one time on\n\t * each path, so it is finite.\n\t */\n\treadonly fields: ReadonlyArray<Field>\n\t/** The places, by id. The root is the place with the id 0. */\n\treadonly branches: ReadonlyArray<Branch>\n\t/**\n\t * The number of steps from a place to a place below it. A search of the\n\t * catalog, as for a field pattern, reads each one, so this is its work.\n\t */\n\treadonly edges: number\n\treadonly root: Branch\n}\n\n/**\n * Tests if a key of an object fits the Schemas of the keys of a record. A key\n * of an object is a string, also a key that a number Schema takes: `{ 1: \"a\" }`\n * has the key `\"1\"`. So a key that is the text of a number is tried as that\n * number too.\n */\nexport const keyFits = (key: Key, name: string): boolean =>\n\tArr.some(key.parameters, (parameter) => {\n\t\tconst is = Schema.is(Schema.make(parameter))\n\t\treturn (\n\t\t\tis(name) ||\n\t\t\t(name !== \"\" && String(Number(name)) === name && is(Number(name)))\n\t\t)\n\t})\n\n/**\n * Gives the struct of a `Schema.Class`. A class is a declaration with one\n * struct as its type parameter, and Effect marks it with a `\"~constructor\"`\n * annotation, also through more codec steps, as in\n * `Schema.fromJsonString(User)`. Other declarations, such as `Schema.Option`\n * or `Schema.ReadonlyMap`, have no such mark, and the fields of their type\n * parameter are not fields of the value.\n */\nconst classStruct = (\n\tast: SchemaAST.Declaration,\n): SchemaAST.Objects | undefined => {\n\tconst [inner] = ast.typeParameters\n\treturn ast.annotations?.[\"~constructor\"] !== undefined &&\n\t\tast.typeParameters.length === 1 &&\n\t\tinner?._tag === \"Objects\"\n\t\t? inner\n\t\t: undefined\n}\n\n/**\n * Gives the Schemas that a value at one place may fit: it opens unions and\n * `Schema.suspend`. A class stays a class: see {@link structOf}. The open suspend nodes are in a native `Set`,\n * because the walk must find a node by the node itself: `Equal` compares\n * Schema nodes by structure, and a recursive Schema has no end. A suspend\n * node that gives itself again, with no step on the path, gives nothing.\n */\nconst alternatives = (\n\tast: SchemaAST.AST,\n\topen: ReadonlySet<SchemaAST.AST>,\n): ReadonlyArray<SchemaAST.AST> => alternativesOf({ ast, open })\n\n/**\n * The walk of {@link alternatives}. A union may hold a union, very many\n * levels down, so it is a walk of the trampoline for an input of an unknown\n * depth.\n */\nconst alternativesOf = Trampoline.adaptive<\n\t{ readonly ast: SchemaAST.AST; readonly open: ReadonlySet<SchemaAST.AST> },\n\tReadonlyArray<SchemaAST.AST>\n>((R, child) => ({ ast, open }, depth) => {\n\tswitch (ast._tag) {\n\t\tcase \"Union\":\n\t\t\treturn R.map(\n\t\t\t\tR.forEach(ast.types, (type) => child({ ast: type, open }, depth + 1)),\n\t\t\t\tArr.flatten,\n\t\t\t)\n\t\tcase \"Suspend\":\n\t\t\treturn open.has(ast)\n\t\t\t\t? R.succeed([])\n\t\t\t\t: child({ ast: ast.thunk(), open: new Set([...open, ast]) }, depth + 1)\n\t\tdefault:\n\t\t\treturn R.succeed([ast])\n\t}\n})\n\n/** The struct of a Schema: a struct, or the struct of a class. */\nconst structOf = (ast: SchemaAST.AST): SchemaAST.Objects | undefined =>\n\tast._tag === \"Objects\"\n\t\t? ast\n\t\t: ast._tag === \"Declaration\"\n\t\t\t? classStruct(ast)\n\t\t\t: undefined\n\nconst hasCheck = (\n\tchecks: ReadonlyArray<SchemaAST.Check<never>>,\n\tid: string,\n): boolean =>\n\tArr.some(\n\t\tchecks,\n\t\t(check) =>\n\t\t\tcheck.annotations?.representation?.id === id ||\n\t\t\t(check._tag === \"FilterGroup\" && hasCheck(check.checks, id)),\n\t)\n\n/** A struct with named fields, or with the keys of a record. */\nconst isStruct = (\n\tast: SchemaAST.Objects | undefined,\n): ast is SchemaAST.Objects =>\n\tast !== undefined &&\n\t(ast.propertySignatures.length > 0 || ast.indexSignatures.length > 0)\n\n/** The kinds of the values of a Schema that is not a struct. */\nconst kindsOf = (ast: SchemaAST.AST): Kinds => {\n\tswitch (ast._tag) {\n\t\tcase \"Null\":\n\t\tcase \"Undefined\":\n\t\t\treturn nullKinds\n\t\tcase \"Never\":\n\t\t\treturn noKinds\n\t\tcase \"String\":\n\t\tcase \"TemplateLiteral\":\n\t\t\treturn { ...noKinds, string: true }\n\t\tcase \"Boolean\":\n\t\t\treturn { ...noKinds, boolean: true }\n\t\tcase \"Number\": {\n\t\t\t// The two checks that a search uses. Effect names a number with\n\t\t\t// them, as `Schema.Int` and `Schema.Finite`; other checks are left\n\t\t\t// out, as all checks are.\n\t\t\tconst checks = ast.checks ?? []\n\t\t\treturn {\n\t\t\t\t...noKinds,\n\t\t\t\tnumber: hasCheck(checks, \"effect/schema/isInt\")\n\t\t\t\t\t? \"integer\"\n\t\t\t\t\t: hasCheck(checks, \"effect/schema/isFinite\")\n\t\t\t\t\t\t? \"finite\"\n\t\t\t\t\t\t: \"any\",\n\t\t\t}\n\t\t}\n\t\tcase \"Literal\":\n\t\t\treturn typeof ast.literal === \"bigint\"\n\t\t\t\t? objectKinds\n\t\t\t\t: { ...noKinds, literals: [ast.literal] }\n\t\tcase \"Enum\":\n\t\t\treturn { ...noKinds, literals: Arr.map(ast.enums, ([, value]) => value) }\n\t\tcase \"Arrays\":\n\t\tcase \"ObjectKeyword\":\n\t\tcase \"BigInt\":\n\t\tcase \"Symbol\":\n\t\tcase \"UniqueSymbol\":\n\t\t\treturn objectKinds\n\t\tcase \"Declaration\":\n\t\t\t// A declaration of Effect in `objectDeclarations` holds an object,\n\t\t\t// such as a `Date`. Another declaration may hold any value, as\n\t\t\t// `Schema.Json` or one from `Schema.declare` may.\n\t\t\treturn HashSet.has(objectDeclarations, declarationId(ast))\n\t\t\t\t? objectKinds\n\t\t\t\t: anyKinds\n\t\tdefault:\n\t\t\t// `Schema.Unknown`, `Schema.Void`, and `Struct({})`, which takes a\n\t\t\t// string too: these may hold any value.\n\t\t\treturn anyKinds\n\t}\n}\n\n/**\n * The ids of the declarations of Effect that hold only objects. Effect puts\n * the id in the `representation` annotation. A declaration that is not in\n * this list may hold a scalar, as `Schema.Json` may, so its kinds are any\n * value.\n */\nconst objectDeclarations = HashSet.make(\n\t\"effect/schema/Date\",\n\t\"effect/schema/URL\",\n\t\"effect/schema/Uint8Array\",\n\t\"effect/schema/Duration\",\n\t\"effect/schema/DateTimeUtc\",\n\t\"effect/schema/DateTimeZoned\",\n\t\"effect/schema/BigDecimal\",\n\t\"effect/schema/Redacted\",\n\t\"effect/schema/Cause\",\n\t\"effect/schema/Chunk\",\n\t\"effect/schema/HashMap\",\n\t\"effect/schema/HashSet\",\n\t\"effect/schema/ReadonlyMap\",\n\t\"effect/schema/ReadonlySet\",\n\t\"effect/schema/Option\",\n\t\"effect/schema/Result\",\n\t\"effect/schema/Exit\",\n\t\"effect/schema/File\",\n\t\"effect/schema/FormData\",\n\t\"effect/schema/RegExp\",\n\t\"effect/schema/URLSearchParams\",\n)\n\nconst declarationId = (ast: SchemaAST.Declaration): string | undefined => {\n\tconst representation: unknown = ast.annotations?.[\"representation\"]\n\treturn hasProperty(representation, \"id\") &&\n\t\ttypeof representation.id === \"string\"\n\t\t? representation.id\n\t\t: undefined\n}\n\n/** The things that the walk of a catalog knows at one place. */\ninterface Place {\n\treadonly schemas: ReadonlyArray<SchemaAST.AST>\n\t/** The value may be missing. */\n\treadonly missing: boolean\n\t/** The value may be anything, as an extra field of a struct may be. */\n\treadonly top: boolean\n\t/** A read here finds only plain data: see {@link Field.plain}. */\n\treadonly plain: boolean\n}\n\nconst textOf = (path: ReadonlyArray<Segment>): string =>\n\tArr.map(path, (segment) =>\n\t\ttypeof segment === \"string\" ? segment : \"*\",\n\t).join(\".\")\n\n/**\n * The name of a property as a part of a path. A key of an object is a string,\n * so a number key, as from `Schema.Record(Schema.Literal(1), ...)`, has the\n * name `\"1\"`. A symbol key is not a field.\n */\nconst nameOf = (signature: SchemaAST.PropertySignature): string | undefined =>\n\ttypeof signature.name === \"symbol\" ? undefined : String(signature.name)\n\n/**\n * The properties of a struct by name. A walk makes it one time for each\n * struct of a place, so a struct with very many fields does not take a\n * search for each name. It is a native `Map`, as `Branch.names` is.\n */\nconst propertiesOf = (\n\tstruct: SchemaAST.Objects,\n): ReadonlyMap<string, SchemaAST.PropertySignature> =>\n\tnew Map(\n\t\tArr.flatMap(struct.propertySignatures, (signature) => {\n\t\t\tconst name = nameOf(signature)\n\t\t\treturn name === undefined ? [] : [[name, signature] as const]\n\t\t}),\n\t)\n\n/** The names of the fields of the structs, in the order that they first come. */\nconst namesOf = (\n\tstructs: ReadonlyArray<SchemaAST.Objects>,\n): ReadonlyArray<string> =>\n\t// A native `Set` keeps the first copy of each name, in order, about ten\n\t// times as fast as `Arr.dedupe`.\n\tArray.from(\n\t\tnew Set(\n\t\t\tArr.flatMap(structs, (struct) =>\n\t\t\t\tArr.flatMap(struct.propertySignatures, (signature) => {\n\t\t\t\t\tconst name = nameOf(signature)\n\t\t\t\t\tif (name === undefined) {\n\t\t\t\t\t\treturn []\n\t\t\t\t\t}\n\t\t\t\t\t// A dot in a path is the step into a nested field. A field name with\n\t\t\t\t\t// a dot would make two fields have one path, so it is a defect.\n\t\t\t\t\tif (name.includes(\".\")) {\n\t\t\t\t\t\tthrow new Error(`A field name may not have a dot: \"${name}\"`)\n\t\t\t\t\t}\n\t\t\t\t\t// An empty name would make an empty part in a path, such as\n\t\t\t\t\t// `.secret`.\n\t\t\t\t\tif (name === \"\") {\n\t\t\t\t\t\tthrow new Error(\"A field name may not be empty\")\n\t\t\t\t\t}\n\t\t\t\t\treturn [name]\n\t\t\t\t}),\n\t\t\t),\n\t\t),\n\t)\n\n/** A place of the walk, and the places below it, before they have ids. */\ninterface Made {\n\treadonly branch: Omit<Branch, \"id\" | \"children\" | \"names\" | \"keyBranch\">\n\treadonly children: ReadonlyArray<readonly [string, Place]>\n\treadonly keyPlace: Place | undefined\n}\n\n/** Works out the facts of a place, and the places below it. */\nconst placeOf = (place: Place): Made => {\n\tconst structs = Arr.filter(Arr.map(place.schemas, structOf), isStruct)\n\tconst values = Arr.filter(\n\t\tplace.schemas,\n\t\t(schema) => !isStruct(structOf(schema)),\n\t)\n\tconst valueKinds = Arr.reduce(values, noKinds, (kinds, schema) =>\n\t\tjoinKinds(kinds, kindsOf(structOf(schema) ?? schema)),\n\t)\n\tconst kinds = place.top\n\t\t? anyKinds\n\t\t: joinKinds(\n\t\t\t\tjoinKinds(valueKinds, structs.length > 0 ? objectKinds : noKinds),\n\t\t\t\tplace.missing ? nullKinds : noKinds,\n\t\t\t)\n\tconst records = Arr.filter(\n\t\tstructs,\n\t\t(struct) => struct.indexSignatures.length > 0,\n\t)\n\tconst structProperties = Arr.map(structs, propertiesOf)\n\tconst children = Arr.map(namesOf(structs), (name) => {\n\t\tconst properties = Arr.flatMap(structProperties, (named) => {\n\t\t\tconst property = named.get(name)\n\t\t\treturn property === undefined ? [] : [property]\n\t\t})\n\t\t// A struct without the name may still have it as an extra field, with\n\t\t// any value: only a decode or a `make` takes extra fields away, and a\n\t\t// decision may get a value made by hand. A value that may be any\n\t\t// object, as one of `Schema.Unknown` may, may have the name with any\n\t\t// value too.\n\t\tconst top =\n\t\t\tplace.top || valueKinds.object || properties.length < structs.length\n\t\tconst below: Place = {\n\t\t\tschemas: Arr.flatMap(properties, (property) =>\n\t\t\t\talternatives(property.type, new Set()),\n\t\t\t),\n\t\t\t// A field below a value that is not a struct reads `null`, and so\n\t\t\t// does a field that may be missing.\n\t\t\tmissing:\n\t\t\t\tplace.missing ||\n\t\t\t\tvalues.length > 0 ||\n\t\t\t\tArr.some(properties, (property) => SchemaAST.isOptional(property.type)),\n\t\t\ttop,\n\t\t\t// A value that may be anything may be a list, or have a getter, so\n\t\t\t// a read below it may not find plain data.\n\t\t\tplain: place.plain && !top,\n\t\t}\n\t\treturn [name, below] as const\n\t})\n\tconst signatures = Arr.flatMap(records, (struct) => struct.indexSignatures)\n\tconst keyTop =\n\t\tplace.top || valueKinds.object || records.length < structs.length\n\treturn {\n\t\tbranch: {\n\t\t\tkinds,\n\t\t\tvalues: values.length > 0 || records.length > 0,\n\t\t\tstructs: structs.length > 0,\n\t\t\t// A field that is a scalar in one branch of a union and a struct in\n\t\t\t// another holds parts too.\n\t\t\topen:\n\t\t\t\trecords.length > 0 ||\n\t\t\t\tstructs.length > 0 ||\n\t\t\t\tvalueKinds.object ||\n\t\t\t\tplace.top,\n\t\t\tplain: place.plain,\n\t\t\tkey:\n\t\t\t\tsignatures.length === 0\n\t\t\t\t\t? undefined\n\t\t\t\t\t: {\n\t\t\t\t\t\t\t_tag: \"Key\",\n\t\t\t\t\t\t\tparameters: Arr.map(\n\t\t\t\t\t\t\t\tsignatures,\n\t\t\t\t\t\t\t\t(signature) => signature.parameter,\n\t\t\t\t\t\t\t),\n\t\t\t\t\t\t},\n\t\t},\n\t\tchildren,\n\t\tkeyPlace:\n\t\t\tsignatures.length === 0\n\t\t\t\t? undefined\n\t\t\t\t: {\n\t\t\t\t\t\tschemas: Arr.flatMap(signatures, (signature) =>\n\t\t\t\t\t\t\talternatives(signature.type, new Set()),\n\t\t\t\t\t\t),\n\t\t\t\t\t\t// A record may not have a given key.\n\t\t\t\t\t\tmissing: true,\n\t\t\t\t\t\ttop: keyTop,\n\t\t\t\t\t\tplain: place.plain && !keyTop,\n\t\t\t\t\t},\n\t}\n}\n\n/**\n * Makes the places of a Schema. Each place comes one time: two paths with the\n * same Schemas and the same marks lead to one place. The walk keeps a queue\n * of the places that it has found, and it makes them in order, so a deep\n * Schema does not fill the stack.\n */\nconst branchesOf = (root: Place): ReadonlyArray<Branch> => {\n\t// The id of each Schema node, and of each place, for this walk. The ids\n\t// are in native maps, because the walk must find a Schema node by the node\n\t// itself: `Equal` compares Schema nodes by structure, and a recursive\n\t// Schema has no end. The maps and the queue belong to this walk.\n\tconst astIds = new Map<SchemaAST.AST, number>()\n\tconst placeIds = new Map<string, number>()\n\tconst queue: Array<Place> = []\n\tconst idOf = (place: Place): number => {\n\t\tconst ids = Arr.map(place.schemas, (schema) => {\n\t\t\tconst known = astIds.get(schema)\n\t\t\tif (known !== undefined) {\n\t\t\t\treturn known\n\t\t\t}\n\t\t\tastIds.set(schema, astIds.size)\n\t\t\treturn astIds.size - 1\n\t\t})\n\t\tconst key = `${Array.from(new Set(ids))\n\t\t\t.sort((a, b) => a - b)\n\t\t\t.join(\n\t\t\t\t\",\",\n\t\t\t)}|${String(place.missing)}|${String(place.top)}|${String(place.plain)}`\n\t\tconst known = placeIds.get(key)\n\t\tif (known !== undefined) {\n\t\t\treturn known\n\t\t}\n\t\tif (queue.length >= Limits.ceilings.catalogFields) {\n\t\t\tthrow Limits.exceeded(\n\t\t\t\t\"A Schema\",\n\t\t\t\t\"catalogFields\",\n\t\t\t\t\"places\",\n\t\t\t\tqueue.length + 1,\n\t\t\t\tLimits.ceilings.catalogFields,\n\t\t\t)\n\t\t}\n\t\tplaceIds.set(key, queue.length)\n\t\tqueue.push(place)\n\t\treturn queue.length - 1\n\t}\n\tidOf(root)\n\treturn Arr.unfold(0, (id) => {\n\t\tconst place = queue[id]\n\t\tif (place === undefined) {\n\t\t\treturn Option.none()\n\t\t}\n\t\tconst made = placeOf(place)\n\t\tconst children = Arr.map(\n\t\t\tmade.children,\n\t\t\t([name, below]) => [name, idOf(below)] as const,\n\t\t)\n\t\tconst branch: Branch = {\n\t\t\t...made.branch,\n\t\t\tid,\n\t\t\tchildren,\n\t\t\tnames: HashMap.fromIterable(children),\n\t\t\tkeyBranch: made.keyPlace === undefined ? undefined : idOf(made.keyPlace),\n\t\t}\n\t\treturn Option.some([branch, id + 1] as const)\n\t})\n}\n\n/** The field at a place, for a path. */\nconst fieldAt = (\n\tbranch: Branch,\n\tpath: ReadonlyArray<Segment>,\n\tparts: number,\n): Field => ({\n\tpath,\n\ttext: textOf(path),\n\tkinds: branch.kinds,\n\t// A struct at the last part that a path may have is one field, since a\n\t// path does not go below it.\n\tlisted: branch.values || (path.length === parts && branch.structs),\n\topen: branch.open,\n\tplain: branch.plain,\n})\n\n/**\n * The fields that a list shows, a field before the fields below it, down to\n * `parts` parts. A path goes into a place that is on it already one time\n * only, so the list of a recursive Schema is finite. The walk keeps a stack\n * of the places to visit, so a deep Schema does not fill the stack. The\n * stack belongs to this walk.\n */\nconst listOf = (\n\tbranches: ReadonlyArray<Branch>,\n\tparts: number,\n): ReadonlyArray<Field> => {\n\tinterface Visit {\n\t\treadonly branch: Branch\n\t\treadonly path: ReadonlyArray<Segment>\n\t\t/** The ids of the places on the path, as a set of Effect that shares its parts. */\n\t\treadonly above: HashSet.HashSet<number>\n\t}\n\tconst below = (visit: Visit): ReadonlyArray<Visit> => {\n\t\tconst { branch, path } = visit\n\t\tif (\n\t\t\tpath.length === parts ||\n\t\t\t(path.length > 0 && HashSet.has(visit.above, branch.id))\n\t\t) {\n\t\t\treturn []\n\t\t}\n\t\tconst above = HashSet.add(visit.above, branch.id)\n\t\tconst named = Arr.map(branch.children, ([name, id]) => ({\n\t\t\tbranch: branches[id]!,\n\t\t\tpath: [...path, name],\n\t\t\tabove,\n\t\t}))\n\t\treturn branch.key === undefined || branch.keyBranch === undefined\n\t\t\t? named\n\t\t\t: [\n\t\t\t\t\t...named,\n\t\t\t\t\t{\n\t\t\t\t\t\tbranch: branches[branch.keyBranch]!,\n\t\t\t\t\t\tpath: [...path, branch.key],\n\t\t\t\t\t\tabove,\n\t\t\t\t\t},\n\t\t\t\t]\n\t}\n\tconst stack: Array<Visit> = [\n\t\t{ branch: branches[0]!, path: [], above: HashSet.empty() },\n\t]\n\treturn Arr.flatten(\n\t\tArr.unfold(stack, (rest) => {\n\t\t\tconst visit = rest.pop()\n\t\t\tif (visit === undefined) {\n\t\t\t\treturn Option.none()\n\t\t\t}\n\t\t\t// The visits go onto the stack one at a time: a spread into a call\n\t\t\t// would pass one argument for each field, past the most arguments\n\t\t\t// of a call.\n\t\t\tArr.forEach(Arr.reverse(below(visit)), (next) => {\n\t\t\t\trest.push(next)\n\t\t\t})\n\t\t\tconst field =\n\t\t\t\tvisit.path.length === 0\n\t\t\t\t\t? []\n\t\t\t\t\t: [fieldAt(visit.branch, visit.path, parts)]\n\t\t\tif (field.length > 0 && rest.length + 1 > Limits.ceilings.catalogFields) {\n\t\t\t\tthrow Limits.exceeded(\n\t\t\t\t\t\"A Schema\",\n\t\t\t\t\t\"catalogFields\",\n\t\t\t\t\t\"fields\",\n\t\t\t\t\trest.length + 1,\n\t\t\t\t\tLimits.ceilings.catalogFields,\n\t\t\t\t)\n\t\t\t}\n\t\t\treturn Option.some([field, rest] as const)\n\t\t}),\n\t)\n}\n\n/**\n * The fields that a list shows for paths of at most `parts` parts, as for a\n * domain with that limit. See `Catalog.fields`.\n */\nexport const fieldsOf = (\n\tcatalog: Catalog,\n\tparts: number,\n): ReadonlyArray<Field> =>\n\tparts === Limits.fieldPathParts\n\t\t? catalog.fields\n\t\t: listOf(catalog.branches, parts)\n\n/**\n * Makes the catalog of a Schema, with the list of its fields for the default\n * limit of the parts of a path. It throws when a field name has a dot or is\n * empty, and when the Schema has more places or fields than the ceiling of\n * `catalogFields`. A domain checks the size of the list with its own limit.\n */\nexport const catalogOf = (ast: SchemaAST.AST): Catalog => {\n\tconst branches = branchesOf({\n\t\tschemas: alternatives(ast, new Set()),\n\t\tmissing: false,\n\t\ttop: false,\n\t\tplain: true,\n\t})\n\treturn {\n\t\tfields: listOf(branches, Limits.fieldPathParts),\n\t\tbranches,\n\t\tedges: Arr.reduce(\n\t\t\tbranches,\n\t\t\t0,\n\t\t\t(total, branch) =>\n\t\t\t\ttotal +\n\t\t\t\tbranch.children.length +\n\t\t\t\t(branch.keyBranch === undefined ? 0 : 1),\n\t\t),\n\t\troot: branches[0]!,\n\t}\n}\n\n/**\n * Gives the field of a catalog at a path with dots, if the catalog has it.\n * A path has at most `parts` parts. The lookup follows one place for each\n * part, in a loop.\n */\nexport const lookup = (\n\tcatalog: Catalog,\n\tpath: string,\n\tparts: number = Limits.fieldPathParts,\n): Field | undefined => {\n\tconst names = path.split(\".\", parts + 1)\n\tif (names.length > parts) {\n\t\treturn undefined\n\t}\n\tconst found = Arr.reduce(\n\t\tnames,\n\t\t{ branch: catalog.root, path: [] } as\n\t\t\t| { readonly branch: Branch; readonly path: ReadonlyArray<Segment> }\n\t\t\t| undefined,\n\t\t(current, name) => {\n\t\t\tif (current === undefined) {\n\t\t\t\treturn undefined\n\t\t\t}\n\t\t\tconst named = HashMap.get(current.branch.names, name)\n\t\t\tif (Option.isSome(named)) {\n\t\t\t\treturn {\n\t\t\t\t\tbranch: catalog.branches[named.value]!,\n\t\t\t\t\tpath: [...current.path, name],\n\t\t\t\t}\n\t\t\t}\n\t\t\tconst key = current.branch.key\n\t\t\tconst keyBranch = current.branch.keyBranch\n\t\t\treturn key !== undefined && keyBranch !== undefined && keyFits(key, name)\n\t\t\t\t? { branch: catalog.branches[keyBranch]!, path: [...current.path, key] }\n\t\t\t\t: undefined\n\t\t},\n\t)\n\treturn found === undefined\n\t\t? undefined\n\t\t: fieldAt(found.branch, found.path, parts)\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"catalog.js","names":["Trampoline.adaptive","Limits.exceeded","Limits.fieldPathParts"],"sources":["../../src/internal/catalog.ts"],"sourcesContent":["/**\n * The catalog of the fields of a Schema. One walk over the Schema makes it,\n * and every question about the fields of a resource or of the principal reads\n * it: which names a rule may use, which values a field may hold, which fields\n * a pattern fits, and which fields a list of permitted fields shows.\n *\n * The catalog describes the shape of the values, not the checks of the\n * Schema. A check, such as a minimum length, only keeps some values out, so a\n * catalog that leaves the checks out holds every real value and more. An\n * answer that holds for all of these values, as `All` and `None` of a\n * coverage do, then holds for the real values too.\n */\nimport * as Arr from \"effect/Array\"\nimport * as HashMap from \"effect/HashMap\"\nimport * as HashSet from \"effect/HashSet\"\nimport * as Option from \"effect/Option\"\nimport { hasProperty } from \"effect/Predicate\"\nimport * as Schema from \"effect/Schema\"\nimport * as SchemaAST from \"effect/SchemaAST\"\nimport * as Limits from \"./limits.ts\"\nimport * as Trampoline from \"./trampoline.ts\"\n\n/** The kinds of values that a field may hold. */\nexport interface Kinds {\n\t/** The field may be `null`, `undefined` or missing. */\n\treadonly null: boolean\n\treadonly boolean: boolean\n\t/** The field may hold any string. */\n\treadonly string: boolean\n\t/** The numbers that the field may hold. */\n\treadonly number: \"none\" | \"integer\" | \"finite\" | \"any\"\n\t/** The field may hold an object, or a value that is not a scalar. */\n\treadonly object: boolean\n\t/** Single values that the field may hold, other than the ones above. */\n\treadonly literals: ReadonlyArray<string | number | boolean>\n}\n\n/** No value: the kinds of a field of `Schema.Never`. */\nexport const noKinds: Kinds = {\n\tnull: false,\n\tboolean: false,\n\tstring: false,\n\tnumber: \"none\",\n\tobject: false,\n\tliterals: [],\n}\n\n/** Any value. */\nexport const anyKinds: Kinds = {\n\tnull: true,\n\tboolean: true,\n\tstring: true,\n\tnumber: \"any\",\n\tobject: true,\n\tliterals: [],\n}\n\nconst nullKinds: Kinds = { ...noKinds, null: true }\nconst objectKinds: Kinds = { ...noKinds, object: true }\n\nconst numberOrder = [\"none\", \"integer\", \"finite\", \"any\"] as const\n\n/** The kinds of a field that may hold a value of `left` or of `right`. */\nexport const joinKinds = (left: Kinds, right: Kinds): Kinds => ({\n\tnull: left.null || right.null,\n\tboolean: left.boolean || right.boolean,\n\tstring: left.string || right.string,\n\tnumber:\n\t\tnumberOrder.indexOf(left.number) >= numberOrder.indexOf(right.number)\n\t\t\t? left.number\n\t\t\t: right.number,\n\tobject: left.object || right.object,\n\t// A native `Set` finds copies about ten times as fast as `Arr.dedupe`. It\n\t// takes `0` and `-0` as one, as `includes` does when a test reads them.\n\tliterals: Array.from(new Set([...left.literals, ...right.literals])),\n})\n\n/** Tests if a number fits the numbers of a field. */\nexport const fitsNumber = (number: Kinds[\"number\"], value: number): boolean => {\n\tswitch (number) {\n\t\tcase \"none\":\n\t\t\treturn false\n\t\tcase \"integer\":\n\t\t\treturn Number.isInteger(value)\n\t\tcase \"finite\":\n\t\t\treturn Number.isFinite(value)\n\t\tcase \"any\":\n\t\t\treturn true\n\t}\n}\n\n/**\n * Tests if a field with these kinds may hold a value. A missing value and\n * `undefined` are `null`.\n */\nexport const accepts = (kinds: Kinds, value: unknown): boolean => {\n\tif (value === null || value === undefined) {\n\t\treturn kinds.null\n\t}\n\tswitch (typeof value) {\n\t\tcase \"boolean\":\n\t\t\treturn kinds.boolean || kinds.literals.includes(value)\n\t\tcase \"string\":\n\t\t\treturn kinds.string || kinds.literals.includes(value)\n\t\tcase \"number\":\n\t\t\treturn fitsNumber(kinds.number, value) || kinds.literals.includes(value)\n\t\tdefault:\n\t\t\treturn kinds.object\n\t}\n}\n\n/** A part of a field path that stands for any key of a record. */\nexport interface Key {\n\treadonly _tag: \"Key\"\n\t/** The Schemas of the keys. A key must fit one of them. */\n\treadonly parameters: ReadonlyArray<SchemaAST.AST>\n}\n\n/** One part of a field path: a name, or any key of a record. */\nexport type Segment = string | Key\n\n/** One field of a catalog. */\nexport interface Field {\n\treadonly path: ReadonlyArray<Segment>\n\t/** The path with dots, and `*` for a key of a record. */\n\treadonly text: string\n\treadonly kinds: Kinds\n\t/**\n\t * The field holds a value that a list of fields shows as one field: a\n\t * scalar, a record, an array, or a struct at the last part a path may have.\n\t */\n\treadonly listed: boolean\n\t/** The field may hold parts that the Schema does not name, as a record may. */\n\treadonly open: boolean\n\t/**\n\t * A read of the field finds only plain data. It is `false` when a value\n\t * above the field may be an object that does not have it as a data field:\n\t * a class without the field, which may have a getter of that name, a\n\t * value such as a date or a list, or any object, as of `Schema.Unknown`.\n\t * A struct of a union without the field makes it not plain too, since a\n\t * value made by hand may have it with any value.\n\t */\n\treadonly plain: boolean\n}\n\n/**\n * A place in the graph of a catalog. A path of a field leads to a place, and\n * many paths may lead to one place, as in a recursive Schema: `next` and\n * `next.next` of a list of links are one place. A place keeps the ids of the\n * places below it, so the graph has no loop of objects.\n */\nexport interface Branch {\n\treadonly id: number\n\t/** The kinds of the values of the field at this place. */\n\treadonly kinds: Kinds\n\t/** The field holds a scalar, a record or a list: see `Field.listed`. */\n\treadonly values: boolean\n\t/** The field may hold a struct. */\n\treadonly structs: boolean\n\treadonly open: boolean\n\treadonly plain: boolean\n\t/** The ids of the places below, by name, in the order of the Schema. */\n\treadonly children: ReadonlyArray<readonly [string, number]>\n\t/**\n\t * The places below, by name. A lookup gets each name from `split`, as a\n\t * new string, and a `HashMap` finds a new string about 20% faster than a\n\t * native `Map`. The names come from the Schema, so a name from a policy\n\t * can not make a long list of names with one hash here.\n\t */\n\treadonly names: HashMap.HashMap<string, number>\n\t/** The place for the keys of a record, when the value may be a record. */\n\treadonly key: Key | undefined\n\treadonly keyBranch: number | undefined\n}\n\n/** The catalog of the fields of a Schema. */\nexport interface Catalog {\n\t/**\n\t * The fields that a list shows, a field before the fields below it. For a\n\t * recursive Schema, the list goes into each recursive place one time on\n\t * each path, so it is finite.\n\t */\n\treadonly fields: ReadonlyArray<Field>\n\t/** The places, by id. The root is the place with the id 0. */\n\treadonly branches: ReadonlyArray<Branch>\n\t/**\n\t * The number of steps from a place to a place below it. A search of the\n\t * catalog, as for a field pattern, reads each one, so this is its work.\n\t */\n\treadonly edges: number\n\treadonly root: Branch\n}\n\n/**\n * Tests if a key of an object fits the Schemas of the keys of a record. A key\n * of an object is a string, also a key that a number Schema takes: `{ 1: \"a\" }`\n * has the key `\"1\"`. So a key that is the text of a number is tried as that\n * number too.\n */\nexport const keyFits = (key: Key, name: string): boolean =>\n\tArr.some(key.parameters, (parameter) => {\n\t\tconst is = Schema.is(Schema.make(parameter))\n\t\treturn (\n\t\t\tis(name) ||\n\t\t\t(name !== \"\" && String(Number(name)) === name && is(Number(name)))\n\t\t)\n\t})\n\n/**\n * Gives the struct of a `Schema.Class`. A class is a declaration with one\n * struct as its type parameter, and Effect marks it with a `\"~constructor\"`\n * annotation, also through more codec steps, as in\n * `Schema.fromJsonString(User)`. Other declarations, such as `Schema.Option`\n * or `Schema.ReadonlyMap`, have no such mark, and the fields of their type\n * parameter are not fields of the value.\n */\nconst classStruct = (\n\tast: SchemaAST.Declaration,\n): SchemaAST.Objects | undefined => {\n\tconst [inner] = ast.typeParameters\n\treturn ast.annotations?.[\"~constructor\"] !== undefined &&\n\t\tast.typeParameters.length === 1 &&\n\t\tinner?._tag === \"Objects\"\n\t\t? inner\n\t\t: undefined\n}\n\n/**\n * Gives the Schemas that a value at one place may fit: it opens unions and\n * `Schema.suspend`. A class stays a class: see {@link structOf}. The open suspend nodes are in a native `Set`,\n * because the walk must find a node by the node itself: `Equal` compares\n * Schema nodes by structure, and a recursive Schema has no end. A suspend\n * node that gives itself again, with no step on the path, gives nothing.\n */\nconst alternatives = (\n\tast: SchemaAST.AST,\n\topen: ReadonlySet<SchemaAST.AST>,\n): ReadonlyArray<SchemaAST.AST> => alternativesOf({ ast, open })\n\n/**\n * The walk of {@link alternatives}. A union may hold a union, very many\n * levels down, so it is a walk of the trampoline for an input of an unknown\n * depth.\n */\nconst alternativesOf = Trampoline.adaptive<\n\t{ readonly ast: SchemaAST.AST; readonly open: ReadonlySet<SchemaAST.AST> },\n\tReadonlyArray<SchemaAST.AST>\n>((R, child) => ({ ast, open }, depth) => {\n\tswitch (ast._tag) {\n\t\tcase \"Union\":\n\t\t\treturn R.map(\n\t\t\t\tR.forEach(ast.types, (type) => child({ ast: type, open }, depth + 1)),\n\t\t\t\tArr.flatten,\n\t\t\t)\n\t\tcase \"Suspend\":\n\t\t\treturn open.has(ast)\n\t\t\t\t? R.succeed([])\n\t\t\t\t: child({ ast: ast.thunk(), open: new Set([...open, ast]) }, depth + 1)\n\t\tdefault:\n\t\t\treturn R.succeed([ast])\n\t}\n})\n\n/** The struct of a Schema: a struct, or the struct of a class. */\nconst structOf = (ast: SchemaAST.AST): SchemaAST.Objects | undefined =>\n\tast._tag === \"Objects\"\n\t\t? ast\n\t\t: ast._tag === \"Declaration\"\n\t\t\t? classStruct(ast)\n\t\t\t: undefined\n\nconst hasCheck = (\n\tchecks: ReadonlyArray<SchemaAST.Check<never>>,\n\tid: string,\n): boolean => hasCheckIn({ checks, id })\n\n/**\n * The walk of {@link hasCheck}. A group of checks may hold a group, very many\n * levels down, as `.and(...)` many times makes, so it is a walk of the\n * trampoline for an input of an unknown depth.\n */\nconst hasCheckIn = Trampoline.adaptive<\n\t{\n\t\treadonly checks: ReadonlyArray<SchemaAST.Check<never>>\n\t\treadonly id: string\n\t},\n\tboolean\n>(\n\t(R, child) =>\n\t\t({ checks, id }, depth) =>\n\t\t\tR.some(checks, (check) =>\n\t\t\t\tcheck.annotations?.representation?.id === id\n\t\t\t\t\t? R.succeed(true)\n\t\t\t\t\t: check._tag === \"FilterGroup\"\n\t\t\t\t\t\t? child({ checks: check.checks, id }, depth + 1)\n\t\t\t\t\t\t: R.succeed(false),\n\t\t\t),\n)\n\n/** A struct with named fields, or with the keys of a record. */\nconst isStruct = (\n\tast: SchemaAST.Objects | undefined,\n): ast is SchemaAST.Objects =>\n\tast !== undefined &&\n\t(ast.propertySignatures.length > 0 || ast.indexSignatures.length > 0)\n\n/** The kinds of the values of a Schema that is not a struct. */\nconst kindsOf = (ast: SchemaAST.AST): Kinds => {\n\tswitch (ast._tag) {\n\t\tcase \"Null\":\n\t\tcase \"Undefined\":\n\t\t\treturn nullKinds\n\t\tcase \"Never\":\n\t\t\treturn noKinds\n\t\tcase \"String\":\n\t\tcase \"TemplateLiteral\":\n\t\t\treturn { ...noKinds, string: true }\n\t\tcase \"Boolean\":\n\t\t\treturn { ...noKinds, boolean: true }\n\t\tcase \"Number\": {\n\t\t\t// The two checks that a search uses. Effect names a number with\n\t\t\t// them, as `Schema.Int` and `Schema.Finite`; other checks are left\n\t\t\t// out, as all checks are.\n\t\t\tconst checks = ast.checks ?? []\n\t\t\treturn {\n\t\t\t\t...noKinds,\n\t\t\t\tnumber: hasCheck(checks, \"effect/schema/isInt\")\n\t\t\t\t\t? \"integer\"\n\t\t\t\t\t: hasCheck(checks, \"effect/schema/isFinite\")\n\t\t\t\t\t\t? \"finite\"\n\t\t\t\t\t\t: \"any\",\n\t\t\t}\n\t\t}\n\t\tcase \"Literal\":\n\t\t\treturn typeof ast.literal === \"bigint\"\n\t\t\t\t? objectKinds\n\t\t\t\t: { ...noKinds, literals: [ast.literal] }\n\t\tcase \"Enum\":\n\t\t\treturn { ...noKinds, literals: Arr.map(ast.enums, ([, value]) => value) }\n\t\tcase \"Arrays\":\n\t\tcase \"ObjectKeyword\":\n\t\tcase \"BigInt\":\n\t\tcase \"Symbol\":\n\t\tcase \"UniqueSymbol\":\n\t\t\treturn objectKinds\n\t\tcase \"Declaration\":\n\t\t\t// A declaration of Effect in `objectDeclarations` holds an object,\n\t\t\t// such as a `Date`. Another declaration may hold any value, as\n\t\t\t// `Schema.Json` or one from `Schema.declare` may.\n\t\t\treturn HashSet.has(objectDeclarations, declarationId(ast))\n\t\t\t\t? objectKinds\n\t\t\t\t: anyKinds\n\t\tdefault:\n\t\t\t// `Schema.Unknown`, `Schema.Void`, and `Struct({})`, which takes a\n\t\t\t// string too: these may hold any value.\n\t\t\treturn anyKinds\n\t}\n}\n\n/**\n * The ids of the declarations of Effect that hold only objects. Effect puts\n * the id in the `representation` annotation. A declaration that is not in\n * this list may hold a scalar, as `Schema.Json` may, so its kinds are any\n * value.\n */\nconst objectDeclarations = HashSet.make(\n\t\"effect/schema/Date\",\n\t\"effect/schema/URL\",\n\t\"effect/schema/Uint8Array\",\n\t\"effect/schema/Duration\",\n\t\"effect/schema/DateTimeUtc\",\n\t\"effect/schema/DateTimeZoned\",\n\t\"effect/schema/BigDecimal\",\n\t\"effect/schema/Redacted\",\n\t\"effect/schema/Cause\",\n\t\"effect/schema/Chunk\",\n\t\"effect/schema/HashMap\",\n\t\"effect/schema/HashSet\",\n\t\"effect/schema/ReadonlyMap\",\n\t\"effect/schema/ReadonlySet\",\n\t\"effect/schema/Option\",\n\t\"effect/schema/Result\",\n\t\"effect/schema/Exit\",\n\t\"effect/schema/File\",\n\t\"effect/schema/FormData\",\n\t\"effect/schema/RegExp\",\n\t\"effect/schema/URLSearchParams\",\n)\n\nconst declarationId = (ast: SchemaAST.Declaration): string | undefined => {\n\tconst representation: unknown = ast.annotations?.[\"representation\"]\n\treturn hasProperty(representation, \"id\") &&\n\t\ttypeof representation.id === \"string\"\n\t\t? representation.id\n\t\t: undefined\n}\n\n/** The things that the walk of a catalog knows at one place. */\ninterface Place {\n\treadonly schemas: ReadonlyArray<SchemaAST.AST>\n\t/** The value may be missing. */\n\treadonly missing: boolean\n\t/** The value may be anything, as an extra field of a struct may be. */\n\treadonly top: boolean\n\t/** A read here finds only plain data: see {@link Field.plain}. */\n\treadonly plain: boolean\n}\n\nconst textOf = (path: ReadonlyArray<Segment>): string =>\n\tArr.map(path, (segment) =>\n\t\ttypeof segment === \"string\" ? segment : \"*\",\n\t).join(\".\")\n\n/**\n * The name of a property as a part of a path. A key of an object is a string,\n * so a number key, as from `Schema.Record(Schema.Literal(1), ...)`, has the\n * name `\"1\"`. A symbol key is not a field.\n */\nconst nameOf = (signature: SchemaAST.PropertySignature): string | undefined =>\n\ttypeof signature.name === \"symbol\" ? undefined : String(signature.name)\n\n/**\n * The properties of a struct by name. A walk makes it one time for each\n * struct of a place, so a struct with very many fields does not take a\n * search for each name. It is a native `Map`, as `Branch.names` is.\n */\nconst propertiesOf = (\n\tstruct: SchemaAST.Objects,\n): ReadonlyMap<string, SchemaAST.PropertySignature> =>\n\tnew Map(\n\t\tArr.flatMap(struct.propertySignatures, (signature) => {\n\t\t\tconst name = nameOf(signature)\n\t\t\treturn name === undefined ? [] : [[name, signature] as const]\n\t\t}),\n\t)\n\n/** The names of the fields of the structs, in the order that they first come. */\nconst namesOf = (\n\tstructs: ReadonlyArray<SchemaAST.Objects>,\n): ReadonlyArray<string> =>\n\t// A native `Set` keeps the first copy of each name, in order, about ten\n\t// times as fast as `Arr.dedupe`.\n\tArray.from(\n\t\tnew Set(\n\t\t\tArr.flatMap(structs, (struct) =>\n\t\t\t\tArr.flatMap(struct.propertySignatures, (signature) => {\n\t\t\t\t\tconst name = nameOf(signature)\n\t\t\t\t\tif (name === undefined) {\n\t\t\t\t\t\treturn []\n\t\t\t\t\t}\n\t\t\t\t\t// A dot in a path is the step into a nested field. A field name with\n\t\t\t\t\t// a dot would make two fields have one path, so it is a defect.\n\t\t\t\t\tif (name.includes(\".\")) {\n\t\t\t\t\t\tthrow new Error(`A field name may not have a dot: \"${name}\"`)\n\t\t\t\t\t}\n\t\t\t\t\t// An empty name would make an empty part in a path, such as\n\t\t\t\t\t// `.secret`.\n\t\t\t\t\tif (name === \"\") {\n\t\t\t\t\t\tthrow new Error(\"A field name may not be empty\")\n\t\t\t\t\t}\n\t\t\t\t\treturn [name]\n\t\t\t\t}),\n\t\t\t),\n\t\t),\n\t)\n\n/** A place of the walk, and the places below it, before they have ids. */\ninterface Made {\n\treadonly branch: Omit<Branch, \"id\" | \"children\" | \"names\" | \"keyBranch\">\n\treadonly children: ReadonlyArray<readonly [string, Place]>\n\treadonly keyPlace: Place | undefined\n}\n\n/** Works out the facts of a place, and the places below it. */\nconst placeOf = (place: Place): Made => {\n\tconst structs = Arr.filter(Arr.map(place.schemas, structOf), isStruct)\n\tconst values = Arr.filter(\n\t\tplace.schemas,\n\t\t(schema) => !isStruct(structOf(schema)),\n\t)\n\tconst valueKinds = Arr.reduce(values, noKinds, (kinds, schema) =>\n\t\tjoinKinds(kinds, kindsOf(structOf(schema) ?? schema)),\n\t)\n\tconst kinds = place.top\n\t\t? anyKinds\n\t\t: joinKinds(\n\t\t\t\tjoinKinds(valueKinds, structs.length > 0 ? objectKinds : noKinds),\n\t\t\t\tplace.missing ? nullKinds : noKinds,\n\t\t\t)\n\tconst records = Arr.filter(\n\t\tstructs,\n\t\t(struct) => struct.indexSignatures.length > 0,\n\t)\n\tconst structProperties = Arr.map(structs, propertiesOf)\n\tconst children = Arr.map(namesOf(structs), (name) => {\n\t\tconst properties = Arr.flatMap(structProperties, (named) => {\n\t\t\tconst property = named.get(name)\n\t\t\treturn property === undefined ? [] : [property]\n\t\t})\n\t\t// A struct without the name may still have it as an extra field, with\n\t\t// any value: only a decode or a `make` takes extra fields away, and a\n\t\t// decision may get a value made by hand. A value that may be any\n\t\t// object, as one of `Schema.Unknown` may, may have the name with any\n\t\t// value too.\n\t\tconst top =\n\t\t\tplace.top || valueKinds.object || properties.length < structs.length\n\t\tconst below: Place = {\n\t\t\tschemas: Arr.flatMap(properties, (property) =>\n\t\t\t\talternatives(property.type, new Set()),\n\t\t\t),\n\t\t\t// A field below a value that is not a struct reads `null`, and so\n\t\t\t// does a field that may be missing.\n\t\t\tmissing:\n\t\t\t\tplace.missing ||\n\t\t\t\tvalues.length > 0 ||\n\t\t\t\tArr.some(properties, (property) => SchemaAST.isOptional(property.type)),\n\t\t\ttop,\n\t\t\t// A value that may be anything may be a list, or have a getter, so\n\t\t\t// a read below it may not find plain data.\n\t\t\tplain: place.plain && !top,\n\t\t}\n\t\treturn [name, below] as const\n\t})\n\tconst signatures = Arr.flatMap(records, (struct) => struct.indexSignatures)\n\tconst keyTop =\n\t\tplace.top || valueKinds.object || records.length < structs.length\n\treturn {\n\t\tbranch: {\n\t\t\tkinds,\n\t\t\tvalues: values.length > 0 || records.length > 0,\n\t\t\tstructs: structs.length > 0,\n\t\t\t// A field that is a scalar in one branch of a union and a struct in\n\t\t\t// another holds parts too.\n\t\t\topen:\n\t\t\t\trecords.length > 0 ||\n\t\t\t\tstructs.length > 0 ||\n\t\t\t\tvalueKinds.object ||\n\t\t\t\tplace.top,\n\t\t\tplain: place.plain,\n\t\t\tkey:\n\t\t\t\tsignatures.length === 0\n\t\t\t\t\t? undefined\n\t\t\t\t\t: {\n\t\t\t\t\t\t\t_tag: \"Key\",\n\t\t\t\t\t\t\tparameters: Arr.map(\n\t\t\t\t\t\t\t\tsignatures,\n\t\t\t\t\t\t\t\t(signature) => signature.parameter,\n\t\t\t\t\t\t\t),\n\t\t\t\t\t\t},\n\t\t},\n\t\tchildren,\n\t\tkeyPlace:\n\t\t\tsignatures.length === 0\n\t\t\t\t? undefined\n\t\t\t\t: {\n\t\t\t\t\t\tschemas: Arr.flatMap(signatures, (signature) =>\n\t\t\t\t\t\t\talternatives(signature.type, new Set()),\n\t\t\t\t\t\t),\n\t\t\t\t\t\t// A record may not have a given key.\n\t\t\t\t\t\tmissing: true,\n\t\t\t\t\t\ttop: keyTop,\n\t\t\t\t\t\tplain: place.plain && !keyTop,\n\t\t\t\t\t},\n\t}\n}\n\n/**\n * Makes the places of a Schema. Each place comes one time: two paths with the\n * same Schemas and the same marks lead to one place. The walk keeps a queue\n * of the places that it has found, and it makes them in order, so a deep\n * Schema does not fill the stack.\n */\nconst branchesOf = (root: Place): ReadonlyArray<Branch> => {\n\t// The id of each Schema node, and of each place, for this walk. The ids\n\t// are in native maps, because the walk must find a Schema node by the node\n\t// itself: `Equal` compares Schema nodes by structure, and a recursive\n\t// Schema has no end. The maps and the queue belong to this walk.\n\tconst astIds = new Map<SchemaAST.AST, number>()\n\tconst placeIds = new Map<string, number>()\n\tconst queue: Array<Place> = []\n\tconst idOf = (place: Place): number => {\n\t\tconst ids = Arr.map(place.schemas, (schema) => {\n\t\t\tconst known = astIds.get(schema)\n\t\t\tif (known !== undefined) {\n\t\t\t\treturn known\n\t\t\t}\n\t\t\tastIds.set(schema, astIds.size)\n\t\t\treturn astIds.size - 1\n\t\t})\n\t\tconst key = `${Array.from(new Set(ids))\n\t\t\t.sort((a, b) => a - b)\n\t\t\t.join(\n\t\t\t\t\",\",\n\t\t\t)}|${String(place.missing)}|${String(place.top)}|${String(place.plain)}`\n\t\tconst known = placeIds.get(key)\n\t\tif (known !== undefined) {\n\t\t\treturn known\n\t\t}\n\t\tif (queue.length >= Limits.ceilings.catalogFields) {\n\t\t\tthrow Limits.exceeded(\n\t\t\t\t\"A Schema\",\n\t\t\t\t\"catalogFields\",\n\t\t\t\t\"places\",\n\t\t\t\tqueue.length + 1,\n\t\t\t\tLimits.ceilings.catalogFields,\n\t\t\t)\n\t\t}\n\t\tplaceIds.set(key, queue.length)\n\t\tqueue.push(place)\n\t\treturn queue.length - 1\n\t}\n\tidOf(root)\n\treturn Arr.unfold(0, (id) => {\n\t\tconst place = queue[id]\n\t\tif (place === undefined) {\n\t\t\treturn Option.none()\n\t\t}\n\t\tconst made = placeOf(place)\n\t\tconst children = Arr.map(\n\t\t\tmade.children,\n\t\t\t([name, below]) => [name, idOf(below)] as const,\n\t\t)\n\t\tconst branch: Branch = {\n\t\t\t...made.branch,\n\t\t\tid,\n\t\t\tchildren,\n\t\t\tnames: HashMap.fromIterable(children),\n\t\t\tkeyBranch: made.keyPlace === undefined ? undefined : idOf(made.keyPlace),\n\t\t}\n\t\treturn Option.some([branch, id + 1] as const)\n\t})\n}\n\n/** The field at a place, for a path. */\nconst fieldAt = (\n\tbranch: Branch,\n\tpath: ReadonlyArray<Segment>,\n\tparts: number,\n): Field => ({\n\tpath,\n\ttext: textOf(path),\n\tkinds: branch.kinds,\n\t// A struct at the last part that a path may have is one field, since a\n\t// path does not go below it.\n\tlisted: branch.values || (path.length === parts && branch.structs),\n\topen: branch.open,\n\tplain: branch.plain,\n})\n\n/**\n * The fields that a list shows, a field before the fields below it, down to\n * `parts` parts. A path goes into a place that is on it already one time\n * only, so the list of a recursive Schema is finite. The walk keeps a stack\n * of the places to visit, so a deep Schema does not fill the stack. The\n * stack belongs to this walk.\n */\nconst listOf = (\n\tbranches: ReadonlyArray<Branch>,\n\tparts: number,\n): ReadonlyArray<Field> => {\n\tinterface Visit {\n\t\treadonly branch: Branch\n\t\treadonly path: ReadonlyArray<Segment>\n\t\t/** The ids of the places on the path, as a set of Effect that shares its parts. */\n\t\treadonly above: HashSet.HashSet<number>\n\t}\n\tconst below = (visit: Visit): ReadonlyArray<Visit> => {\n\t\tconst { branch, path } = visit\n\t\tif (\n\t\t\tpath.length === parts ||\n\t\t\t(path.length > 0 && HashSet.has(visit.above, branch.id))\n\t\t) {\n\t\t\treturn []\n\t\t}\n\t\tconst above = HashSet.add(visit.above, branch.id)\n\t\tconst named = Arr.map(branch.children, ([name, id]) => ({\n\t\t\tbranch: branches[id]!,\n\t\t\tpath: [...path, name],\n\t\t\tabove,\n\t\t}))\n\t\treturn branch.key === undefined || branch.keyBranch === undefined\n\t\t\t? named\n\t\t\t: [\n\t\t\t\t\t...named,\n\t\t\t\t\t{\n\t\t\t\t\t\tbranch: branches[branch.keyBranch]!,\n\t\t\t\t\t\tpath: [...path, branch.key],\n\t\t\t\t\t\tabove,\n\t\t\t\t\t},\n\t\t\t\t]\n\t}\n\tconst stack: Array<Visit> = [\n\t\t{ branch: branches[0]!, path: [], above: HashSet.empty() },\n\t]\n\treturn Arr.flatten(\n\t\tArr.unfold(stack, (rest) => {\n\t\t\tconst visit = rest.pop()\n\t\t\tif (visit === undefined) {\n\t\t\t\treturn Option.none()\n\t\t\t}\n\t\t\t// The visits go onto the stack one at a time: a spread into a call\n\t\t\t// would pass one argument for each field, past the most arguments\n\t\t\t// of a call.\n\t\t\tArr.forEach(Arr.reverse(below(visit)), (next) => {\n\t\t\t\trest.push(next)\n\t\t\t})\n\t\t\tconst field =\n\t\t\t\tvisit.path.length === 0\n\t\t\t\t\t? []\n\t\t\t\t\t: [fieldAt(visit.branch, visit.path, parts)]\n\t\t\tif (field.length > 0 && rest.length + 1 > Limits.ceilings.catalogFields) {\n\t\t\t\tthrow Limits.exceeded(\n\t\t\t\t\t\"A Schema\",\n\t\t\t\t\t\"catalogFields\",\n\t\t\t\t\t\"fields\",\n\t\t\t\t\trest.length + 1,\n\t\t\t\t\tLimits.ceilings.catalogFields,\n\t\t\t\t)\n\t\t\t}\n\t\t\treturn Option.some([field, rest] as const)\n\t\t}),\n\t)\n}\n\n/**\n * The fields that a list shows for paths of at most `parts` parts, as for a\n * domain with that limit. See `Catalog.fields`.\n */\nexport const fieldsOf = (\n\tcatalog: Catalog,\n\tparts: number,\n): ReadonlyArray<Field> =>\n\tparts === Limits.fieldPathParts\n\t\t? catalog.fields\n\t\t: listOf(catalog.branches, parts)\n\n/**\n * Makes the catalog of a Schema, with the list of its fields for the default\n * limit of the parts of a path. It throws when a field name has a dot or is\n * empty, and when the Schema has more places or fields than the ceiling of\n * `catalogFields`. A domain checks the size of the list with its own limit.\n */\nexport const catalogOf = (ast: SchemaAST.AST): Catalog => {\n\tconst branches = branchesOf({\n\t\tschemas: alternatives(ast, new Set()),\n\t\tmissing: false,\n\t\ttop: false,\n\t\tplain: true,\n\t})\n\treturn {\n\t\tfields: listOf(branches, Limits.fieldPathParts),\n\t\tbranches,\n\t\tedges: Arr.reduce(\n\t\t\tbranches,\n\t\t\t0,\n\t\t\t(total, branch) =>\n\t\t\t\ttotal +\n\t\t\t\tbranch.children.length +\n\t\t\t\t(branch.keyBranch === undefined ? 0 : 1),\n\t\t),\n\t\troot: branches[0]!,\n\t}\n}\n\n/**\n * Gives the field of a catalog at a path with dots, if the catalog has it.\n * A path has at most `parts` parts. The lookup follows one place for each\n * part, in a loop.\n */\nexport const lookup = (\n\tcatalog: Catalog,\n\tpath: string,\n\tparts: number = Limits.fieldPathParts,\n): Field | undefined => {\n\tconst names = path.split(\".\", parts + 1)\n\tif (names.length > parts) {\n\t\treturn undefined\n\t}\n\tconst found = Arr.reduce(\n\t\tnames,\n\t\t{ branch: catalog.root, path: [] } as\n\t\t\t| { readonly branch: Branch; readonly path: ReadonlyArray<Segment> }\n\t\t\t| undefined,\n\t\t(current, name) => {\n\t\t\tif (current === undefined) {\n\t\t\t\treturn undefined\n\t\t\t}\n\t\t\tconst named = HashMap.get(current.branch.names, name)\n\t\t\tif (Option.isSome(named)) {\n\t\t\t\treturn {\n\t\t\t\t\tbranch: catalog.branches[named.value]!,\n\t\t\t\t\tpath: [...current.path, name],\n\t\t\t\t}\n\t\t\t}\n\t\t\tconst key = current.branch.key\n\t\t\tconst keyBranch = current.branch.keyBranch\n\t\t\treturn key !== undefined && keyBranch !== undefined && keyFits(key, name)\n\t\t\t\t? { branch: catalog.branches[keyBranch]!, path: [...current.path, key] }\n\t\t\t\t: undefined\n\t\t},\n\t)\n\treturn found === undefined\n\t\t? undefined\n\t\t: fieldAt(found.branch, found.path, parts)\n}\n"],"mappings":"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"}
@@ -141,18 +141,27 @@ const isNode = (u) => u instanceof Base;
141
141
  * into the condition of each rule takes the rules from this list first, so it
142
142
  * does not go down the levels of the tree and of a condition at once.
143
143
  */
144
- const rulesOf = (root) => run(root.depth, (R) => {
145
- const go = (node) => {
146
- switch (node._tag) {
147
- case "Rules": return R.succeed(node.rules);
148
- case "All":
149
- case "Any":
150
- case "FirstOf": return R.map(R.forEach(node.policies, (child) => R.suspend(() => go(child))), Arr.flatten);
151
- case "Ceiling": return R.map(R.forEach([node.cap, node.policy], (child) => R.suspend(() => go(child))), Arr.flatten);
152
- }
153
- };
154
- return go(root);
155
- });
144
+ const rulesOf = (root) => {
145
+ const rules = [];
146
+ run(root.depth, (R) => {
147
+ const children = (nodes) => R.map(R.forEach(nodes, (child) => R.suspend(() => go(child))), () => void 0);
148
+ const go = (node) => {
149
+ switch (node._tag) {
150
+ case "Rules":
151
+ Arr.forEach(node.rules, (rule) => {
152
+ rules.push(rule);
153
+ });
154
+ return R.succeed(void 0);
155
+ case "All":
156
+ case "Any":
157
+ case "FirstOf": return children(node.policies);
158
+ case "Ceiling": return children([node.cap, node.policy]);
159
+ }
160
+ };
161
+ return go(root);
162
+ });
163
+ return rules;
164
+ };
156
165
  export { Algorithm, All, Any, Ceiling, FirstOf, Rules, isAlgorithm, isNode, policy_nodes_exports, rulesOf };
157
166
 
158
167
  //# sourceMappingURL=policy-nodes.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"policy-nodes.js","names":["Limits.exceeded","Nodes.isNode","Rule.isRule","Trampoline.run"],"sources":["../../src/internal/policy-nodes.ts"],"sourcesContent":["/**\n * The nodes of a policy tree. As with the nodes of a condition, each node is\n * a class, only its constructor makes one, and it works out three facts once:\n * - `size`: the number of nodes, with each rule and each node of its condition,\n * and a shared child counted for each place;\n * - `depth`: the number of levels of the tree;\n * - `opaque`: a rule of the tree has a predicate.\n *\n * The constructor stops a tree past `policyNodes` or `policyDepth`. So a walk\n * over any tree, such as a decision, needs no memo.\n */\nimport * as Arr from \"effect/Array\"\nimport * as Data from \"effect/Data\"\nimport * as Schema from \"effect/Schema\"\nimport * as Rule from \"./rule.ts\"\nimport * as Limits from \"./limits.ts\"\nimport * as Nodes from \"./nodes.ts\"\nimport * as Trampoline from \"./trampoline.ts\"\n\n/** The combining algorithms of a list of rules. The decoder reads this list too. */\nexport const Algorithm = Schema.Literals([\n\t\"denyOverrides\",\n\t\"permitOverrides\",\n\t\"firstApplicable\",\n\t\"lastApplicable\",\n])\n\nexport type Algorithm = typeof Algorithm.Type\n\n/**\n * Tests if a value is a combining algorithm. It reads the list of the Schema\n * with `includes`, as `Nodes.isCompareOperator` does.\n */\nexport const isAlgorithm = (u: unknown): u is Algorithm =>\n\t(Algorithm.literals as ReadonlyArray<unknown>).includes(u)\n\ninterface Facts {\n\treadonly size: number\n\treadonly depth: number\n\treadonly opaque: boolean\n}\n\n/** Checks the facts of a node against the limits. */\nconst checked = (facts: Facts): Facts => {\n\t// A node does not know its domain, so it checks the ceiling. A policy\n\t// checks the limit of its domain.\n\tif (facts.size > Limits.ceilings.policyNodes) {\n\t\tthrow Limits.exceeded(\n\t\t\t\"A policy tree\",\n\t\t\t\"policyNodes\",\n\t\t\t\"nodes\",\n\t\t\tfacts.size,\n\t\t\tLimits.ceilings.policyNodes,\n\t\t)\n\t}\n\treturn facts\n}\n\nconst factsOf = (children: ReadonlyArray<Node>): Facts =>\n\tchecked({\n\t\tsize: Arr.reduce(children, 1, (total, child) => total + child.size),\n\t\tdepth:\n\t\t\t1 + Arr.reduce(children, 0, (top, child) => Math.max(top, child.depth)),\n\t\topaque: Arr.some(children, (child) => child.opaque),\n\t})\n\n/** The base of every node. It keeps the facts. */\nabstract class Base extends Data.Class {\n\treadonly #facts: Facts\n\n\tconstructor(props: object, facts: Facts) {\n\t\tsuper(props as never)\n\t\tthis.#facts = facts\n\t}\n\n\t/** The number of nodes, with the rules and their conditions. */\n\tget size(): number {\n\t\treturn this.#facts.size\n\t}\n\n\t/** The number of levels of the tree. */\n\tget depth(): number {\n\t\treturn this.#facts.depth\n\t}\n\n\t/** A rule of the tree has a predicate. */\n\tget opaque(): boolean {\n\t\treturn this.#facts.opaque\n\t}\n}\n\n/**\n * The condition of a rule. A value that is not a node counts as one node\n * here: the binding of the rule gives the issue for it, before any walk.\n */\nconst whenOf = (rule: Rule.Rule): Nodes.Node | undefined =>\n\tNodes.isNode(rule.when) ? rule.when : undefined\n\n/** A list of rules with a combining algorithm. */\nexport class Rules extends Base {\n\treadonly _tag = \"Rules\"\n\tdeclare readonly algorithm: Algorithm\n\tdeclare readonly rules: ReadonlyArray<Rule.Rule>\n\tconstructor(props: {\n\t\treadonly algorithm: Algorithm\n\t\treadonly rules: ReadonlyArray<Rule.Rule>\n\t}) {\n\t\tif (!isAlgorithm(props.algorithm)) {\n\t\t\tthrow new Error(\n\t\t\t\t`\"${String(props.algorithm)}\" is not a combining algorithm`,\n\t\t\t)\n\t\t}\n\t\tconst rules = Object.freeze(Array.from(props.rules))\n\t\t// A value that is not a rule, such as a copy of a rule made with a\n\t\t// spread, may keep the type fields of another rule, and it does not\n\t\t// work in a generator. It is a mistake in the code, so it is a defect.\n\t\tif (!Arr.every(rules, Rule.isRule)) {\n\t\t\tthrow new Error(\"A value in the list is not a rule\")\n\t\t}\n\t\tconst conditions = Arr.map(rules, whenOf)\n\t\tsuper(\n\t\t\t{ algorithm: props.algorithm, rules },\n\t\t\tchecked({\n\t\t\t\tsize: Arr.reduce(\n\t\t\t\t\tconditions,\n\t\t\t\t\t1,\n\t\t\t\t\t// A condition counts its nodes and the values of its tests.\n\t\t\t\t\t(total, when) => total + 1 + (when?.weight ?? 0),\n\t\t\t\t),\n\t\t\t\tdepth: 1,\n\t\t\t\topaque: Arr.some(conditions, (when) => when?.opaque ?? false),\n\t\t\t}),\n\t\t)\n\t}\n}\n\nconst checkChildren = (\n\tpolicies: ReadonlyArray<unknown>,\n): ReadonlyArray<Node> => {\n\tconst children = Array.from(policies)\n\tif (!Arr.every(children, isNode)) {\n\t\tthrow new Error(\"A child of a policy tree is not a node of a policy\")\n\t}\n\treturn Object.freeze(children)\n}\n\n/** Every policy must allow, and one deny is enough to deny. */\nexport class All extends Base {\n\treadonly _tag = \"All\"\n\tdeclare readonly policies: ReadonlyArray<Node>\n\tconstructor(policies: ReadonlyArray<Node>) {\n\t\tconst children = checkChildren(policies)\n\t\tsuper({ policies: children }, factsOf(children))\n\t}\n}\n\n/** One allow is enough. Without an allow, one deny gives a deny. */\nexport class Any extends Base {\n\treadonly _tag = \"Any\"\n\tdeclare readonly policies: ReadonlyArray<Node>\n\tconstructor(policies: ReadonlyArray<Node>) {\n\t\tconst children = checkChildren(policies)\n\t\tsuper({ policies: children }, factsOf(children))\n\t}\n}\n\n/** The first policy that allows or denies gives the answer. */\nexport class FirstOf extends Base {\n\treadonly _tag = \"FirstOf\"\n\tdeclare readonly policies: ReadonlyArray<Node>\n\tconstructor(policies: ReadonlyArray<Node>) {\n\t\tconst children = checkChildren(policies)\n\t\tsuper({ policies: children }, factsOf(children))\n\t}\n}\n\n/** A deny from `cap` always wins. An allow from `cap` does nothing. */\nexport class Ceiling extends Base {\n\treadonly _tag = \"Ceiling\"\n\tdeclare readonly cap: Node\n\tdeclare readonly policy: Node\n\tconstructor(props: { readonly cap: Node; readonly policy: Node }) {\n\t\tconst [cap, policy] = checkChildren([props.cap, props.policy])\n\t\tsuper({ cap, policy }, factsOf([cap!, policy!]))\n\t}\n}\n\n/** A node of a policy tree. */\nexport type Node = Rules | All | Any | FirstOf | Ceiling\n\n/** Tests if a value is a node of a policy tree. */\nexport const isNode = (u: unknown): u is Node => u instanceof Base\n\n/**\n * The rules of a tree, in order, once for each place. A walk that also goes\n * into the condition of each rule takes the rules from this list first, so it\n * does not go down the levels of the tree and of a condition at once.\n */\nexport const rulesOf = (root: Node): ReadonlyArray<Rule.Rule> =>\n\t// The walk takes the steps of a trampoline, so a deep tree does not fill\n\t// the stack.\n\tTrampoline.run(root.depth, (R) => {\n\t\tconst go = (node: Node): Trampoline.Step<ReadonlyArray<Rule.Rule>> => {\n\t\t\tswitch (node._tag) {\n\t\t\t\tcase \"Rules\":\n\t\t\t\t\treturn R.succeed(node.rules)\n\t\t\t\tcase \"All\":\n\t\t\t\tcase \"Any\":\n\t\t\t\tcase \"FirstOf\":\n\t\t\t\t\treturn R.map(\n\t\t\t\t\t\tR.forEach(node.policies, (child) => R.suspend(() => go(child))),\n\t\t\t\t\t\tArr.flatten,\n\t\t\t\t\t)\n\t\t\t\tcase \"Ceiling\":\n\t\t\t\t\treturn R.map(\n\t\t\t\t\t\tR.forEach([node.cap, node.policy], (child) =>\n\t\t\t\t\t\t\tR.suspend(() => go(child)),\n\t\t\t\t\t\t),\n\t\t\t\t\t\tArr.flatten,\n\t\t\t\t\t)\n\t\t\t}\n\t\t}\n\t\treturn go(root)\n\t})\n"],"mappings":"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"}
1
+ {"version":3,"file":"policy-nodes.js","names":["Limits.exceeded","Nodes.isNode","Rule.isRule"],"sources":["../../src/internal/policy-nodes.ts"],"sourcesContent":["/**\n * The nodes of a policy tree. As with the nodes of a condition, each node is\n * a class, only its constructor makes one, and it works out three facts once:\n * - `size`: the number of nodes, with each rule and each node of its condition,\n * and a shared child counted for each place;\n * - `depth`: the number of levels of the tree;\n * - `opaque`: a rule of the tree has a predicate.\n *\n * The constructor stops a tree past `policyNodes` or `policyDepth`. So a walk\n * over any tree, such as a decision, needs no memo.\n */\nimport * as Arr from \"effect/Array\"\nimport * as Data from \"effect/Data\"\nimport * as Schema from \"effect/Schema\"\nimport * as Rule from \"./rule.ts\"\nimport * as Limits from \"./limits.ts\"\nimport * as Nodes from \"./nodes.ts\"\nimport * as Trampoline from \"./trampoline.ts\"\n\n/** The combining algorithms of a list of rules. The decoder reads this list too. */\nexport const Algorithm = Schema.Literals([\n\t\"denyOverrides\",\n\t\"permitOverrides\",\n\t\"firstApplicable\",\n\t\"lastApplicable\",\n])\n\nexport type Algorithm = typeof Algorithm.Type\n\n/**\n * Tests if a value is a combining algorithm. It reads the list of the Schema\n * with `includes`, as `Nodes.isCompareOperator` does.\n */\nexport const isAlgorithm = (u: unknown): u is Algorithm =>\n\t(Algorithm.literals as ReadonlyArray<unknown>).includes(u)\n\ninterface Facts {\n\treadonly size: number\n\treadonly depth: number\n\treadonly opaque: boolean\n}\n\n/** Checks the facts of a node against the limits. */\nconst checked = (facts: Facts): Facts => {\n\t// A node does not know its domain, so it checks the ceiling. A policy\n\t// checks the limit of its domain.\n\tif (facts.size > Limits.ceilings.policyNodes) {\n\t\tthrow Limits.exceeded(\n\t\t\t\"A policy tree\",\n\t\t\t\"policyNodes\",\n\t\t\t\"nodes\",\n\t\t\tfacts.size,\n\t\t\tLimits.ceilings.policyNodes,\n\t\t)\n\t}\n\treturn facts\n}\n\nconst factsOf = (children: ReadonlyArray<Node>): Facts =>\n\tchecked({\n\t\tsize: Arr.reduce(children, 1, (total, child) => total + child.size),\n\t\tdepth:\n\t\t\t1 + Arr.reduce(children, 0, (top, child) => Math.max(top, child.depth)),\n\t\topaque: Arr.some(children, (child) => child.opaque),\n\t})\n\n/** The base of every node. It keeps the facts. */\nabstract class Base extends Data.Class {\n\treadonly #facts: Facts\n\n\tconstructor(props: object, facts: Facts) {\n\t\tsuper(props as never)\n\t\tthis.#facts = facts\n\t}\n\n\t/** The number of nodes, with the rules and their conditions. */\n\tget size(): number {\n\t\treturn this.#facts.size\n\t}\n\n\t/** The number of levels of the tree. */\n\tget depth(): number {\n\t\treturn this.#facts.depth\n\t}\n\n\t/** A rule of the tree has a predicate. */\n\tget opaque(): boolean {\n\t\treturn this.#facts.opaque\n\t}\n}\n\n/**\n * The condition of a rule. A value that is not a node counts as one node\n * here: the binding of the rule gives the issue for it, before any walk.\n */\nconst whenOf = (rule: Rule.Rule): Nodes.Node | undefined =>\n\tNodes.isNode(rule.when) ? rule.when : undefined\n\n/** A list of rules with a combining algorithm. */\nexport class Rules extends Base {\n\treadonly _tag = \"Rules\"\n\tdeclare readonly algorithm: Algorithm\n\tdeclare readonly rules: ReadonlyArray<Rule.Rule>\n\tconstructor(props: {\n\t\treadonly algorithm: Algorithm\n\t\treadonly rules: ReadonlyArray<Rule.Rule>\n\t}) {\n\t\tif (!isAlgorithm(props.algorithm)) {\n\t\t\tthrow new Error(\n\t\t\t\t`\"${String(props.algorithm)}\" is not a combining algorithm`,\n\t\t\t)\n\t\t}\n\t\tconst rules = Object.freeze(Array.from(props.rules))\n\t\t// A value that is not a rule, such as a copy of a rule made with a\n\t\t// spread, may keep the type fields of another rule, and it does not\n\t\t// work in a generator. It is a mistake in the code, so it is a defect.\n\t\tif (!Arr.every(rules, Rule.isRule)) {\n\t\t\tthrow new Error(\"A value in the list is not a rule\")\n\t\t}\n\t\tconst conditions = Arr.map(rules, whenOf)\n\t\tsuper(\n\t\t\t{ algorithm: props.algorithm, rules },\n\t\t\tchecked({\n\t\t\t\tsize: Arr.reduce(\n\t\t\t\t\tconditions,\n\t\t\t\t\t1,\n\t\t\t\t\t// A condition counts its nodes and the values of its tests.\n\t\t\t\t\t(total, when) => total + 1 + (when?.weight ?? 0),\n\t\t\t\t),\n\t\t\t\tdepth: 1,\n\t\t\t\topaque: Arr.some(conditions, (when) => when?.opaque ?? false),\n\t\t\t}),\n\t\t)\n\t}\n}\n\nconst checkChildren = (\n\tpolicies: ReadonlyArray<unknown>,\n): ReadonlyArray<Node> => {\n\tconst children = Array.from(policies)\n\tif (!Arr.every(children, isNode)) {\n\t\tthrow new Error(\"A child of a policy tree is not a node of a policy\")\n\t}\n\treturn Object.freeze(children)\n}\n\n/** Every policy must allow, and one deny is enough to deny. */\nexport class All extends Base {\n\treadonly _tag = \"All\"\n\tdeclare readonly policies: ReadonlyArray<Node>\n\tconstructor(policies: ReadonlyArray<Node>) {\n\t\tconst children = checkChildren(policies)\n\t\tsuper({ policies: children }, factsOf(children))\n\t}\n}\n\n/** One allow is enough. Without an allow, one deny gives a deny. */\nexport class Any extends Base {\n\treadonly _tag = \"Any\"\n\tdeclare readonly policies: ReadonlyArray<Node>\n\tconstructor(policies: ReadonlyArray<Node>) {\n\t\tconst children = checkChildren(policies)\n\t\tsuper({ policies: children }, factsOf(children))\n\t}\n}\n\n/** The first policy that allows or denies gives the answer. */\nexport class FirstOf extends Base {\n\treadonly _tag = \"FirstOf\"\n\tdeclare readonly policies: ReadonlyArray<Node>\n\tconstructor(policies: ReadonlyArray<Node>) {\n\t\tconst children = checkChildren(policies)\n\t\tsuper({ policies: children }, factsOf(children))\n\t}\n}\n\n/** A deny from `cap` always wins. An allow from `cap` does nothing. */\nexport class Ceiling extends Base {\n\treadonly _tag = \"Ceiling\"\n\tdeclare readonly cap: Node\n\tdeclare readonly policy: Node\n\tconstructor(props: { readonly cap: Node; readonly policy: Node }) {\n\t\tconst [cap, policy] = checkChildren([props.cap, props.policy])\n\t\tsuper({ cap, policy }, factsOf([cap!, policy!]))\n\t}\n}\n\n/** A node of a policy tree. */\nexport type Node = Rules | All | Any | FirstOf | Ceiling\n\n/** Tests if a value is a node of a policy tree. */\nexport const isNode = (u: unknown): u is Node => u instanceof Base\n\n/**\n * The rules of a tree, in order, once for each place. A walk that also goes\n * into the condition of each rule takes the rules from this list first, so it\n * does not go down the levels of the tree and of a condition at once.\n */\nexport const rulesOf = (root: Node): ReadonlyArray<Rule.Rule> => {\n\t// The rules go into one list, in order. A walk that joined the lists of\n\t// the children at each node would copy the list of a deep tree at each\n\t// level. The list belongs to this walk.\n\tconst rules: Array<Rule.Rule> = []\n\t// The walk takes the steps of a trampoline, so a deep tree does not fill\n\t// the stack.\n\tTrampoline.run(root.depth, (R) => {\n\t\tconst children = (nodes: ReadonlyArray<Node>) =>\n\t\t\tR.map(\n\t\t\t\tR.forEach(nodes, (child) => R.suspend(() => go(child))),\n\t\t\t\t() => undefined,\n\t\t\t)\n\t\tconst go = (node: Node): Trampoline.Step<undefined> => {\n\t\t\tswitch (node._tag) {\n\t\t\t\tcase \"Rules\":\n\t\t\t\t\tArr.forEach(node.rules, (rule) => {\n\t\t\t\t\t\trules.push(rule)\n\t\t\t\t\t})\n\t\t\t\t\treturn R.succeed(undefined)\n\t\t\t\tcase \"All\":\n\t\t\t\tcase \"Any\":\n\t\t\t\tcase \"FirstOf\":\n\t\t\t\t\treturn children(node.policies)\n\t\t\t\tcase \"Ceiling\":\n\t\t\t\t\treturn children([node.cap, node.policy])\n\t\t\t}\n\t\t}\n\t\treturn go(root)\n\t})\n\treturn rules\n}\n"],"mappings":"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"}
@@ -145,20 +145,26 @@ const fix = (root, field, value, search) => run(root.depth, (R) => {
145
145
  return go(root);
146
146
  });
147
147
  /** The fields of the tests of a condition, once for each test. */
148
- const testedFields = (root) => run(root.depth, (R) => {
149
- const go = (node) => {
150
- switch (node._tag) {
151
- case "Compare":
152
- case "In":
153
- case "Exists": return R.succeed([node.field]);
154
- case "And":
155
- case "Or": return R.map(R.forEach(node.conditions, (child) => R.suspend(() => go(child))), Arr.flatten);
156
- case "Not": return R.suspend(() => go(node.condition));
157
- default: return R.succeed([]);
158
- }
159
- };
160
- return go(root);
161
- });
148
+ const testedFields = (root) => {
149
+ const fields = [];
150
+ run(root.depth, (R) => {
151
+ const go = (node) => {
152
+ switch (node._tag) {
153
+ case "Compare":
154
+ case "In":
155
+ case "Exists":
156
+ fields.push(node.field);
157
+ return R.succeed(void 0);
158
+ case "And":
159
+ case "Or": return R.map(R.forEach(node.conditions, (child) => R.suspend(() => go(child))), () => void 0);
160
+ case "Not": return R.suspend(() => go(node.condition));
161
+ default: return R.succeed(void 0);
162
+ }
163
+ };
164
+ return go(root);
165
+ });
166
+ return fields;
167
+ };
162
168
  /**
163
169
  * Picks the next field to split on. A field comes before the fields below it,
164
170
  * so a value of a field can fix them. Then a field in more tests comes first,
@@ -240,27 +246,33 @@ const exampleOf = (assignment) => Arr.reduce(assignment, {}, (root, [field, valu
240
246
  * belongs to one search.
241
247
  */
242
248
  const constantsOf = (node) => {
243
- const pairs = run(node.depth, (R) => {
249
+ const all = /* @__PURE__ */ new Map();
250
+ const add = (field, values) => {
251
+ const known = all.get(field) ?? /* @__PURE__ */ new Set();
252
+ Arr.forEach(values, (value) => known.add(value));
253
+ all.set(field, known);
254
+ };
255
+ run(node.depth, (R) => {
244
256
  const go = (current) => {
245
257
  switch (current._tag) {
246
- case "Compare": return R.succeed([[current.field, [literalOf(current.value)]]]);
247
- case "In": return R.succeed([[current.field, Arr.map(current.values, literalOf)]]);
248
- case "Exists": return R.succeed([[current.field, []]]);
258
+ case "Compare":
259
+ add(current.field, [literalOf(current.value)]);
260
+ return R.succeed(void 0);
261
+ case "In":
262
+ add(current.field, Arr.map(current.values, literalOf));
263
+ return R.succeed(void 0);
264
+ case "Exists":
265
+ add(current.field, []);
266
+ return R.succeed(void 0);
249
267
  case "Under": throw new Error("Condition.satisfiable needs a condition with no Under nodes. Call Condition.partial first.");
250
268
  case "And":
251
- case "Or": return R.map(R.forEach(current.conditions, (child) => R.suspend(() => go(child))), Arr.flatten);
269
+ case "Or": return R.map(R.forEach(current.conditions, (child) => R.suspend(() => go(child))), () => void 0);
252
270
  case "Not": return R.suspend(() => go(current.condition));
253
- default: return R.succeed([]);
271
+ default: return R.succeed(void 0);
254
272
  }
255
273
  };
256
274
  return go(node);
257
275
  });
258
- const all = /* @__PURE__ */ new Map();
259
- Arr.forEach(pairs, ([field, values]) => {
260
- const known = all.get(field) ?? /* @__PURE__ */ new Set();
261
- Arr.forEach(values, (value) => known.add(value));
262
- all.set(field, known);
263
- });
264
276
  return all;
265
277
  };
266
278
  /** Gives the kinds of each field of a catalog. A field that it does not have may hold any value. */
@@ -1 +1 @@
1
- {"version":3,"file":"search.js","names":["Algebra.testCompare","Trampoline.run","Nodes.always","Nodes.never","Algebra.and","Algebra.or","Algebra.not","Algebra.keyOf","Catalog.lookup","Catalog.anyKinds","Limits.searchSteps","Limits.searchFields","Algebra.simplify"],"sources":["../../src/internal/search.ts"],"sourcesContent":["/**\n * The search for a value that makes a condition true. Each test of a\n * condition compares one field with constants, so the constants cut the\n * values of each field into a few cells, and every test gives the same answer\n * for all the values of one cell. The search tries one value from each cell,\n * one field at a time.\n *\n * The values of a field come from its kinds in a catalog. The kinds hold every\n * real value, so a \"no\" holds for the real values too. A \"yes\" holds for the\n * kinds: a check of the Schema may still stop the example.\n */\nimport * as Arr from \"effect/Array\"\nimport * as Data from \"effect/Data\"\nimport * as Option from \"effect/Option\"\nimport { isObject } from \"effect/Predicate\"\nimport * as Algebra from \"./algebra.ts\"\nimport * as Catalog from \"./catalog.ts\"\nimport * as Limits from \"./limits.ts\"\nimport * as Nodes from \"./nodes.ts\"\nimport * as Trampoline from \"./trampoline.ts\"\n\ntype Node = Nodes.Node\n\n/**\n * Values of fields, as a nested object. A field that is not in it may have any\n * value. A value with parts that the search does not look into, such as a\n * date, is an empty object.\n */\nexport interface Example {\n\treadonly [field: string]: unknown\n}\n\n/** The answer of a search. */\nexport type Satisfiability = Data.TaggedEnum<{\n\tSatisfiable: { readonly example: Example }\n\tUnsatisfiable: {}\n\tUnknown: { readonly reason: \"predicate\" | \"budget\" }\n}>\n\nconst satisfiability = Data.taggedEnum<Satisfiability>()\n\n/** Stands for a value with parts, such as a struct or a date. */\nconst opaque: object = Object.freeze({})\n\n/** Marks a field that a step of the search did not fix. */\nconst free: unique symbol = Symbol(\"free\")\n\n/** The least number that is greater than `value`. */\nconst nextUp = (value: number): number => {\n\tif (Number.isNaN(value) || value === Infinity) {\n\t\treturn value\n\t}\n\tif (value === 0) {\n\t\treturn Number.MIN_VALUE\n\t}\n\tconst view = new DataView(new ArrayBuffer(8))\n\tview.setFloat64(0, value)\n\tconst bits = view.getBigUint64(0)\n\tview.setBigUint64(0, value > 0 ? bits + 1n : bits - 1n)\n\treturn view.getFloat64(0)\n}\n\n/**\n * Gives one number from each part of the number line that the constants\n * make: each constant, and one number between each two neighbours when there\n * is one. The ends of the domain are in the list too.\n */\nconst numberCells = (\n\tkind: \"integer\" | \"finite\" | \"any\",\n\tconstants: ReadonlyArray<number>,\n): ReadonlyArray<number> => {\n\tconst integer = kind === \"integer\"\n\tconst low = integer\n\t\t? Number.MIN_SAFE_INTEGER\n\t\t: kind === \"finite\"\n\t\t\t? -Number.MAX_VALUE\n\t\t\t: -Infinity\n\tconst high = integer\n\t\t? Number.MAX_SAFE_INTEGER\n\t\t: kind === \"finite\"\n\t\t\t? Number.MAX_VALUE\n\t\t\t: Infinity\n\tconst known: ReadonlyArray<number> = Arr.filter(\n\t\tconstants,\n\t\t(value: number) => !Number.isNaN(value),\n\t)\n\tconst points = Arr.filter(\n\t\tArr.flatMap(known, (value: number) =>\n\t\t\tinteger && Number.isFinite(value)\n\t\t\t\t? [Math.floor(value), Math.ceil(value)]\n\t\t\t\t: [value],\n\t\t),\n\t\t(value: number) => value >= low && value <= high,\n\t)\n\t// `0` and `-0` are one point: every test gives the same answer for both,\n\t// and `===` calls them equal.\n\tconst ordered: Array<number> = [low, high, ...points].sort((a, b) => a - b)\n\tconst sorted = Arr.dedupeAdjacentWith(\n\t\tordered,\n\t\t(a: number, b: number) => a === b,\n\t)\n\tconst cells = Arr.flatMap(sorted, (value, index) => {\n\t\tconst after = sorted[index + 1]\n\t\tconst between = integer ? value + 1 : nextUp(value)\n\t\treturn after !== undefined && between < after ? [value, between] : [value]\n\t})\n\treturn kind === \"any\" ? Arr.append(cells, Number.NaN) : cells\n}\n\n/**\n * Gives one string from each part that the constants make. Strings are in the\n * order of their UTF-16 code units, as `<` sees them. The least string after\n * `a` is `a + \"\\0\"`, so there is a string between `a` and `b` exactly when\n * `b` is not `a + \"\\0\"`.\n */\nconst stringCells = (\n\tconstants: ReadonlyArray<string>,\n): ReadonlyArray<string> => {\n\tconst ordered: Array<string> = [...constants].sort()\n\tconst sorted = Arr.dedupeAdjacentWith(\n\t\tordered,\n\t\t(a: string, b: string) => a === b,\n\t)\n\tif (sorted.length === 0) {\n\t\treturn [\"\"]\n\t}\n\treturn [\n\t\t...(sorted[0] === \"\" ? [] : [\"\"]),\n\t\t...Arr.flatMap(sorted, (value, index) => {\n\t\t\tconst next = `${value}\\0`\n\t\t\treturn next === sorted[index + 1] ? [value] : [value, next]\n\t\t}),\n\t]\n}\n\n/**\n * A text for a cell. Two cells get one text when every test gives the same\n * answer for them: `0` and `-0` are one, and `NaN` is one.\n */\nconst cellText = (cell: unknown): string =>\n\tcell === opaque\n\t\t? \"opaque\"\n\t\t: typeof cell === \"number\"\n\t\t\t? `number:${Object.is(cell, -0) ? \"0\" : String(cell)}`\n\t\t\t: `${typeof cell}:${String(cell)}`\n\n/**\n * Keeps the first cell of each text, in order. The texts come from the\n * values of a policy, so they go into a native `Set`: the hash of a string in\n * Effect has no random seed, and a policy could hold very many strings with\n * one hash. The engine hashes a string with a random seed.\n */\nconst dedupeCells = (cells: ReadonlyArray<unknown>): ReadonlyArray<unknown> => {\n\t// The set belongs to this call.\n\tconst seen = new Set<string>()\n\treturn Arr.filter(cells, (cell) => {\n\t\tconst text = cellText(cell)\n\t\tif (seen.has(text)) {\n\t\t\treturn false\n\t\t}\n\t\tseen.add(text)\n\t\treturn true\n\t})\n}\n\n/** Gives one value from each cell of a field. */\nconst cellsOf = (\n\tkinds: Catalog.Kinds,\n\tconstants: ReadonlyArray<Nodes.Scalar>,\n): ReadonlyArray<unknown> =>\n\tdedupeCells([\n\t\t...(kinds.null ? [null] : []),\n\t\t...(kinds.object ? [opaque] : []),\n\t\t...(kinds.boolean ? [true, false] : []),\n\t\t...(kinds.string\n\t\t\t? stringCells(Arr.filter(constants, (value) => typeof value === \"string\"))\n\t\t\t: []),\n\t\t...(kinds.number === \"none\"\n\t\t\t? []\n\t\t\t: numberCells(\n\t\t\t\t\tkinds.number,\n\t\t\t\t\tArr.filter(constants, (value) => typeof value === \"number\"),\n\t\t\t\t)),\n\t\t...kinds.literals,\n\t])\n\nclass OutOfBudget extends Data.Error {}\n\n/** What one search keeps for all its steps. It lives only as long as the search. */\ninterface Search {\n\treadonly cells: (field: string) => ReadonlyArray<unknown>\n\t/**\n\t * The texts of the values of each `In` test, so one test of a value does\n\t * not read every value of the test. It is a native `Map`, because it must\n\t * find a node by the node itself: a hash of a node reads all its values.\n\t * The texts are in a native `Set`, as in {@link dedupeCells}.\n\t */\n\treadonly members: Map<Nodes.In, Set<string>>\n\t/**\n\t * The keys of the conditions that the search found to be unsatisfiable:\n\t * see `Algebra.keyOf`. A condition with the same structure has the same\n\t * free fields, so its answer is the same. The set only grows, so the steps\n\t * of a search may share it. It is a native `Set`, as in\n\t * {@link dedupeCells}.\n\t */\n\treadonly failed: Set<string | Node>\n\treadonly budget: number\n}\n\n/** What a search has done up to now. Each step gives a new one. */\ninterface SearchState {\n\treadonly steps: number\n}\n\ninterface Found {\n\treadonly assignment: Option.Option<ReadonlyArray<readonly [string, unknown]>>\n\treadonly state: SearchState\n}\n\nconst literalOf = (operand: Nodes.Operand): Nodes.Scalar => {\n\tif (operand._tag !== \"Literal\") {\n\t\tthrow new Error(\n\t\t\t\"Condition.satisfiable needs a condition with no principal fields. Call Condition.partial first.\",\n\t\t)\n\t}\n\treturn operand.value\n}\n\n/**\n * The value of the field `name` after `field` gets `value`. A field below\n * `field` is `null` when `value` is not a value with parts.\n */\nconst fixedValue = (\n\tname: string,\n\tfield: string,\n\tvalue: unknown,\n): unknown | typeof free =>\n\tname === field\n\t\t? value\n\t\t: name.startsWith(`${field}.`)\n\t\t\t? value === opaque\n\t\t\t\t? free\n\t\t\t\t: null\n\t\t\t: free\n\nconst membersOf = (node: Nodes.In, search: Search): Set<string> => {\n\tconst known = search.members.get(node)\n\tif (known !== undefined) {\n\t\treturn known\n\t}\n\t// `===` finds no `NaN`, so the set has none, and `NaN` is in no test.\n\tconst members = new Set(\n\t\tArr.map(\n\t\t\tArr.filter(\n\t\t\t\tArr.map(node.values, literalOf),\n\t\t\t\t(literal) => !Number.isNaN(literal),\n\t\t\t),\n\t\t\tcellText,\n\t\t),\n\t)\n\tsearch.members.set(node, members)\n\treturn members\n}\n\nconst testFixed = (\n\tnode: Nodes.Compare | Nodes.In | Nodes.Exists,\n\tvalue: unknown,\n\tsearch: Search,\n): boolean => {\n\tswitch (node._tag) {\n\t\tcase \"Compare\":\n\t\t\treturn Algebra.testCompare(node.op, value, literalOf(node.value))\n\t\tcase \"In\":\n\t\t\treturn (\n\t\t\t\t!Number.isNaN(value) && membersOf(node, search).has(cellText(value))\n\t\t\t)\n\t\tcase \"Exists\":\n\t\t\treturn value !== null\n\t}\n}\n\n/** Puts one value into a field, and into the fields below it. */\nconst fix = (root: Node, field: string, value: unknown, search: Search): Node =>\n\t// The walks of a condition take the steps of a trampoline, so a deep\n\t// condition does not fill the stack.\n\tTrampoline.run(root.depth, (R) => {\n\t\tconst go = (node: Node): Trampoline.Step<Node> => {\n\t\t\tconst children = (nodes: ReadonlyArray<Node>) =>\n\t\t\t\tR.forEach(nodes, (child) => R.suspend(() => go(child)))\n\t\t\tswitch (node._tag) {\n\t\t\t\tcase \"Compare\":\n\t\t\t\tcase \"In\":\n\t\t\t\tcase \"Exists\": {\n\t\t\t\t\tconst known = fixedValue(node.field, field, value)\n\t\t\t\t\treturn R.succeed(\n\t\t\t\t\t\tknown === free\n\t\t\t\t\t\t\t? node\n\t\t\t\t\t\t\t: testFixed(node, known, search)\n\t\t\t\t\t\t\t\t? Nodes.always\n\t\t\t\t\t\t\t\t: Nodes.never,\n\t\t\t\t\t)\n\t\t\t\t}\n\t\t\t\tcase \"And\":\n\t\t\t\t\treturn R.map(children(node.conditions), Algebra.and)\n\t\t\t\tcase \"Or\":\n\t\t\t\t\treturn R.map(children(node.conditions), Algebra.or)\n\t\t\t\tcase \"Not\":\n\t\t\t\t\treturn R.map(\n\t\t\t\t\t\tR.suspend(() => go(node.condition)),\n\t\t\t\t\t\tAlgebra.not,\n\t\t\t\t\t)\n\t\t\t\tdefault:\n\t\t\t\t\treturn R.succeed(node)\n\t\t\t}\n\t\t}\n\t\treturn go(root)\n\t})\n\n/** The fields of the tests of a condition, once for each test. */\nconst testedFields = (root: Node): ReadonlyArray<string> =>\n\tTrampoline.run(root.depth, (R) => {\n\t\tconst go = (node: Node): Trampoline.Step<ReadonlyArray<string>> => {\n\t\t\tswitch (node._tag) {\n\t\t\t\tcase \"Compare\":\n\t\t\t\tcase \"In\":\n\t\t\t\tcase \"Exists\":\n\t\t\t\t\treturn R.succeed([node.field])\n\t\t\t\tcase \"And\":\n\t\t\t\tcase \"Or\":\n\t\t\t\t\treturn R.map(\n\t\t\t\t\t\tR.forEach(node.conditions, (child) => R.suspend(() => go(child))),\n\t\t\t\t\t\tArr.flatten,\n\t\t\t\t\t)\n\t\t\t\tcase \"Not\":\n\t\t\t\t\treturn R.suspend(() => go(node.condition))\n\t\t\t\tdefault:\n\t\t\t\t\treturn R.succeed([])\n\t\t\t}\n\t\t}\n\t\treturn go(root)\n\t})\n\n/**\n * Picks the next field to split on. A field comes before the fields below it,\n * so a value of a field can fix them. Then a field in more tests comes first,\n * and then the first name in order, so the choice is the same each time.\n */\nconst nextField = (node: Node): string => {\n\t// A native `Map`, as in `dedupeCells`: a condition that is not bound to a\n\t// domain may have any field names. The map belongs to this call.\n\tconst counts = new Map<string, number>()\n\tArr.forEach(testedFields(node), (field) =>\n\t\tcounts.set(field, (counts.get(field) ?? 0) + 1),\n\t)\n\tconst depth = (field: string) => field.split(\".\").length\n\tconst [first, ...rest] = Array.from(counts)\n\treturn Arr.reduce(rest, first!, (best, next) =>\n\t\tdepth(next[0]) < depth(best[0]) ||\n\t\t(depth(next[0]) === depth(best[0]) &&\n\t\t\t(next[1] > best[1] || (next[1] === best[1] && next[0] < best[0])))\n\t\t\t? next\n\t\t\t: best,\n\t)[0]\n}\n\n/**\n * Finds values of the fields that make a simple condition true. It splits on\n * one field at a time, and tries one value from each cell of the field. Each\n * step reads the whole condition, so the budget counts its size.\n */\nconst searchNode = (search: Search) => (R: Trampoline.Runner) => {\n\tconst go = (\n\t\tnode: Node,\n\t\tstate: SearchState,\n\t\tassignment: ReadonlyArray<readonly [string, unknown]>,\n\t): Trampoline.Step<Found> => {\n\t\tif (node._tag === \"True\") {\n\t\t\treturn R.succeed({ assignment: Option.some(assignment), state })\n\t\t}\n\t\tif (node._tag === \"False\") {\n\t\t\treturn R.succeed({ assignment: Option.none(), state })\n\t\t}\n\t\t// The key reads at most a small part of the node: a node with very\n\t\t// many nodes, or with very many values in its `In` tests, is its own\n\t\t// key. A step reads the nodes but not the values, which `membersOf`\n\t\t// keeps, so the budget counts the size.\n\t\tconst key = Algebra.keyOf(node)\n\t\tif (search.failed.has(key)) {\n\t\t\treturn R.succeed({ assignment: Option.none(), state })\n\t\t}\n\t\tif (state.steps + node.size > search.budget) {\n\t\t\tthrow new OutOfBudget()\n\t\t}\n\t\tconst field = nextField(node)\n\t\t// The cells are tried in order, until one gives values. A fold over\n\t\t// the cells does no work after a found answer, and it does not grow\n\t\t// the stack with the number of cells. Each call for the next field\n\t\t// is a step of the runner, so very many fields do not fill the stack.\n\t\treturn R.map(\n\t\t\tR.reduce(\n\t\t\t\tsearch.cells(field),\n\t\t\t\t{\n\t\t\t\t\tassignment: Option.none(),\n\t\t\t\t\tstate: { ...state, steps: state.steps + node.size },\n\t\t\t\t} as Found,\n\t\t\t\t(before, cell): Trampoline.Step<Found> => {\n\t\t\t\t\tif (Option.isSome(before.assignment)) {\n\t\t\t\t\t\treturn R.succeed(before)\n\t\t\t\t\t}\n\t\t\t\t\tconst steps = before.state.steps + node.size\n\t\t\t\t\tif (steps > search.budget) {\n\t\t\t\t\t\tthrow new OutOfBudget()\n\t\t\t\t\t}\n\t\t\t\t\treturn R.suspend(() =>\n\t\t\t\t\t\tgo(fix(node, field, cell, search), { ...before.state, steps }, [\n\t\t\t\t\t\t\t...assignment,\n\t\t\t\t\t\t\t[field, cell],\n\t\t\t\t\t\t]),\n\t\t\t\t\t)\n\t\t\t\t},\n\t\t\t),\n\t\t\t(result) => {\n\t\t\t\tif (Option.isNone(result.assignment)) {\n\t\t\t\t\tsearch.failed.add(key)\n\t\t\t\t}\n\t\t\t\treturn result\n\t\t\t},\n\t\t)\n\t}\n\treturn go\n}\n\n/**\n * Puts a value at a path in an object, and gives a new object. A value with\n * parts (`opaque`) becomes an empty object, and it keeps an object that is\n * there.\n */\ntype Fields = Readonly<Record<string, unknown>>\n\nconst setPath = (\n\troot: Fields,\n\tnames: ReadonlyArray<string>,\n\tvalue: unknown,\n): Fields => {\n\tconst last = names[names.length - 1]\n\tif (last === undefined) {\n\t\treturn root\n\t}\n\t// Only an own field counts, as a condition reads it. An inherited one,\n\t// such as `__proto__`, is not a field of the example.\n\tconst own = (base: Fields, name: string): unknown =>\n\t\tObject.hasOwn(base, name) ? base[name] : undefined\n\t// The object at each part of the path, from the root down to the object\n\t// that gets the value. A part that is not an object becomes empty. The\n\t// path is walked in a loop, so a long path does not fill the stack.\n\tconst bases = Arr.scan(names.slice(0, -1), root, (base, name): Fields => {\n\t\tconst inner = own(base, name)\n\t\treturn isObject(inner) && inner !== opaque ? (inner as Fields) : {}\n\t})\n\tconst target = bases[bases.length - 1]!\n\tconst inner = own(target, last)\n\tconst placed: Fields =\n\t\tvalue === opaque && isObject(inner)\n\t\t\t? target\n\t\t\t: { ...target, [last]: value === opaque ? {} : value }\n\t// Each object above gets the new object below it, from the bottom up.\n\treturn Arr.reduceRight(\n\t\tnames.slice(0, -1),\n\t\tplaced,\n\t\t(below, name, index): Fields => ({ ...bases[index]!, [name]: below }),\n\t)\n}\n\nconst exampleOf = (\n\tassignment: ReadonlyArray<readonly [string, unknown]>,\n): Example =>\n\tArr.reduce(\n\t\tassignment,\n\t\t{} as Readonly<Record<string, unknown>>,\n\t\t(root, [field, value]) => setPath(root, field.split(\".\"), value),\n\t)\n\n/**\n * The constants that the tests of each field use. They come from a policy, so\n * they go into a native `Map` and `Set`, as in {@link dedupeCells}. The map\n * belongs to one search.\n */\nconst constantsOf = (node: Node): Map<string, Set<Nodes.Scalar>> => {\n\ttype Pair = readonly [string, ReadonlyArray<Nodes.Scalar>]\n\t// The walk takes the steps of a trampoline, so a deep condition does not\n\t// fill the stack.\n\tconst pairs = Trampoline.run(node.depth, (R) => {\n\t\tconst go = (current: Node): Trampoline.Step<ReadonlyArray<Pair>> => {\n\t\t\tswitch (current._tag) {\n\t\t\t\tcase \"Compare\":\n\t\t\t\t\treturn R.succeed([[current.field, [literalOf(current.value)]]])\n\t\t\t\tcase \"In\":\n\t\t\t\t\treturn R.succeed([\n\t\t\t\t\t\t[current.field, Arr.map(current.values, literalOf)],\n\t\t\t\t\t])\n\t\t\t\tcase \"Exists\":\n\t\t\t\t\treturn R.succeed([[current.field, []]])\n\t\t\t\tcase \"Under\":\n\t\t\t\t\tthrow new Error(\n\t\t\t\t\t\t\"Condition.satisfiable needs a condition with no Under nodes. Call Condition.partial first.\",\n\t\t\t\t\t)\n\t\t\t\tcase \"And\":\n\t\t\t\tcase \"Or\":\n\t\t\t\t\treturn R.map(\n\t\t\t\t\t\tR.forEach(current.conditions, (child) =>\n\t\t\t\t\t\t\tR.suspend(() => go(child)),\n\t\t\t\t\t\t),\n\t\t\t\t\t\tArr.flatten,\n\t\t\t\t\t)\n\t\t\t\tcase \"Not\":\n\t\t\t\t\treturn R.suspend(() => go(current.condition))\n\t\t\t\tdefault:\n\t\t\t\t\treturn R.succeed([])\n\t\t\t}\n\t\t}\n\t\treturn go(node)\n\t})\n\tconst all = new Map<string, Set<Nodes.Scalar>>()\n\tArr.forEach(pairs, ([field, values]) => {\n\t\tconst known = all.get(field) ?? new Set<Nodes.Scalar>()\n\t\tArr.forEach(values, (value) => known.add(value))\n\t\tall.set(field, known)\n\t})\n\treturn all\n}\n\n/** Gives the kinds of each field of a catalog. A field that it does not have may hold any value. */\nexport const kindsIn =\n\t(catalog: Catalog.Catalog | undefined, parts?: number) =>\n\t(field: string): Catalog.Kinds =>\n\t\t(catalog === undefined ? undefined : Catalog.lookup(catalog, field, parts))\n\t\t\t?.kinds ?? Catalog.anyKinds\n\n/**\n * Searches for values that make a condition true. `kindsOf` gives the kinds\n * of values of each field. The condition must have no principal fields and no\n * `Under` nodes.\n */\nexport const satisfiable = (\n\tnode: Node,\n\tkindsOf: (field: string) => Catalog.Kinds,\n\tbudget: number = Limits.searchSteps,\n\tfields: number = Limits.searchFields,\n): Satisfiability => {\n\tif (node.opaque) {\n\t\treturn satisfiability.Unknown({ reason: \"predicate\" })\n\t}\n\tconst constants = constantsOf(node)\n\t// The search calls itself one time for each field that it fixes.\n\tif (constants.size > fields) {\n\t\treturn satisfiability.Unknown({ reason: \"budget\" })\n\t}\n\t// The cells of each field, in a native `Map` as the constants are.\n\tconst cells = new Map<string, ReadonlyArray<unknown>>()\n\tconst search: Search = {\n\t\tmembers: new Map(),\n\t\tfailed: new Set(),\n\t\tcells: (field) => {\n\t\t\tconst made = cells.get(field)\n\t\t\tif (made !== undefined) {\n\t\t\t\treturn made\n\t\t\t}\n\t\t\t// The cells come from the constants, so many constants are work\n\t\t\t// that the budget must count before the cells are made.\n\t\t\tconst known = constants.get(field) ?? new Set<Nodes.Scalar>()\n\t\t\tif (known.size > budget) {\n\t\t\t\tthrow new OutOfBudget()\n\t\t\t}\n\t\t\tconst found = cellsOf(kindsOf(field), Array.from(known))\n\t\t\tcells.set(field, found)\n\t\t\treturn found\n\t\t},\n\t\tbudget,\n\t}\n\ttry {\n\t\t// The search goes one call deeper for each field, so the runner is the\n\t\t// one for that many levels.\n\t\tconst found = Trampoline.run(constants.size, (R) =>\n\t\t\tsearchNode(search)(R)(Algebra.simplify(node), { steps: 0 }, []),\n\t\t)\n\t\treturn Option.match(found.assignment, {\n\t\t\tonNone: () => satisfiability.Unsatisfiable(),\n\t\t\tonSome: (assignment) =>\n\t\t\t\tsatisfiability.Satisfiable({ example: exampleOf(assignment) }),\n\t\t})\n\t} catch (error) {\n\t\tif (error instanceof OutOfBudget) {\n\t\t\treturn satisfiability.Unknown({ reason: \"budget\" })\n\t\t}\n\t\tthrow error\n\t}\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"search.js","names":["Algebra.testCompare","Trampoline.run","Nodes.always","Nodes.never","Algebra.and","Algebra.or","Algebra.not","Algebra.keyOf","Catalog.lookup","Catalog.anyKinds","Limits.searchSteps","Limits.searchFields","Algebra.simplify"],"sources":["../../src/internal/search.ts"],"sourcesContent":["/**\n * The search for a value that makes a condition true. Each test of a\n * condition compares one field with constants, so the constants cut the\n * values of each field into a few cells, and every test gives the same answer\n * for all the values of one cell. The search tries one value from each cell,\n * one field at a time.\n *\n * The values of a field come from its kinds in a catalog. The kinds hold every\n * real value, so a \"no\" holds for the real values too. A \"yes\" holds for the\n * kinds: a check of the Schema may still stop the example.\n */\nimport * as Arr from \"effect/Array\"\nimport * as Data from \"effect/Data\"\nimport * as Option from \"effect/Option\"\nimport { isObject } from \"effect/Predicate\"\nimport * as Algebra from \"./algebra.ts\"\nimport * as Catalog from \"./catalog.ts\"\nimport * as Limits from \"./limits.ts\"\nimport * as Nodes from \"./nodes.ts\"\nimport * as Trampoline from \"./trampoline.ts\"\n\ntype Node = Nodes.Node\n\n/**\n * Values of fields, as a nested object. A field that is not in it may have any\n * value. A value with parts that the search does not look into, such as a\n * date, is an empty object.\n */\nexport interface Example {\n\treadonly [field: string]: unknown\n}\n\n/** The answer of a search. */\nexport type Satisfiability = Data.TaggedEnum<{\n\tSatisfiable: { readonly example: Example }\n\tUnsatisfiable: {}\n\tUnknown: { readonly reason: \"predicate\" | \"budget\" }\n}>\n\nconst satisfiability = Data.taggedEnum<Satisfiability>()\n\n/** Stands for a value with parts, such as a struct or a date. */\nconst opaque: object = Object.freeze({})\n\n/** Marks a field that a step of the search did not fix. */\nconst free: unique symbol = Symbol(\"free\")\n\n/** The least number that is greater than `value`. */\nconst nextUp = (value: number): number => {\n\tif (Number.isNaN(value) || value === Infinity) {\n\t\treturn value\n\t}\n\tif (value === 0) {\n\t\treturn Number.MIN_VALUE\n\t}\n\tconst view = new DataView(new ArrayBuffer(8))\n\tview.setFloat64(0, value)\n\tconst bits = view.getBigUint64(0)\n\tview.setBigUint64(0, value > 0 ? bits + 1n : bits - 1n)\n\treturn view.getFloat64(0)\n}\n\n/**\n * Gives one number from each part of the number line that the constants\n * make: each constant, and one number between each two neighbours when there\n * is one. The ends of the domain are in the list too.\n */\nconst numberCells = (\n\tkind: \"integer\" | \"finite\" | \"any\",\n\tconstants: ReadonlyArray<number>,\n): ReadonlyArray<number> => {\n\tconst integer = kind === \"integer\"\n\tconst low = integer\n\t\t? Number.MIN_SAFE_INTEGER\n\t\t: kind === \"finite\"\n\t\t\t? -Number.MAX_VALUE\n\t\t\t: -Infinity\n\tconst high = integer\n\t\t? Number.MAX_SAFE_INTEGER\n\t\t: kind === \"finite\"\n\t\t\t? Number.MAX_VALUE\n\t\t\t: Infinity\n\tconst known: ReadonlyArray<number> = Arr.filter(\n\t\tconstants,\n\t\t(value: number) => !Number.isNaN(value),\n\t)\n\tconst points = Arr.filter(\n\t\tArr.flatMap(known, (value: number) =>\n\t\t\tinteger && Number.isFinite(value)\n\t\t\t\t? [Math.floor(value), Math.ceil(value)]\n\t\t\t\t: [value],\n\t\t),\n\t\t(value: number) => value >= low && value <= high,\n\t)\n\t// `0` and `-0` are one point: every test gives the same answer for both,\n\t// and `===` calls them equal.\n\tconst ordered: Array<number> = [low, high, ...points].sort((a, b) => a - b)\n\tconst sorted = Arr.dedupeAdjacentWith(\n\t\tordered,\n\t\t(a: number, b: number) => a === b,\n\t)\n\tconst cells = Arr.flatMap(sorted, (value, index) => {\n\t\tconst after = sorted[index + 1]\n\t\tconst between = integer ? value + 1 : nextUp(value)\n\t\treturn after !== undefined && between < after ? [value, between] : [value]\n\t})\n\treturn kind === \"any\" ? Arr.append(cells, Number.NaN) : cells\n}\n\n/**\n * Gives one string from each part that the constants make. Strings are in the\n * order of their UTF-16 code units, as `<` sees them. The least string after\n * `a` is `a + \"\\0\"`, so there is a string between `a` and `b` exactly when\n * `b` is not `a + \"\\0\"`.\n */\nconst stringCells = (\n\tconstants: ReadonlyArray<string>,\n): ReadonlyArray<string> => {\n\tconst ordered: Array<string> = [...constants].sort()\n\tconst sorted = Arr.dedupeAdjacentWith(\n\t\tordered,\n\t\t(a: string, b: string) => a === b,\n\t)\n\tif (sorted.length === 0) {\n\t\treturn [\"\"]\n\t}\n\treturn [\n\t\t...(sorted[0] === \"\" ? [] : [\"\"]),\n\t\t...Arr.flatMap(sorted, (value, index) => {\n\t\t\tconst next = `${value}\\0`\n\t\t\treturn next === sorted[index + 1] ? [value] : [value, next]\n\t\t}),\n\t]\n}\n\n/**\n * A text for a cell. Two cells get one text when every test gives the same\n * answer for them: `0` and `-0` are one, and `NaN` is one.\n */\nconst cellText = (cell: unknown): string =>\n\tcell === opaque\n\t\t? \"opaque\"\n\t\t: typeof cell === \"number\"\n\t\t\t? `number:${Object.is(cell, -0) ? \"0\" : String(cell)}`\n\t\t\t: `${typeof cell}:${String(cell)}`\n\n/**\n * Keeps the first cell of each text, in order. The texts come from the\n * values of a policy, so they go into a native `Set`: the hash of a string in\n * Effect has no random seed, and a policy could hold very many strings with\n * one hash. The engine hashes a string with a random seed.\n */\nconst dedupeCells = (cells: ReadonlyArray<unknown>): ReadonlyArray<unknown> => {\n\t// The set belongs to this call.\n\tconst seen = new Set<string>()\n\treturn Arr.filter(cells, (cell) => {\n\t\tconst text = cellText(cell)\n\t\tif (seen.has(text)) {\n\t\t\treturn false\n\t\t}\n\t\tseen.add(text)\n\t\treturn true\n\t})\n}\n\n/** Gives one value from each cell of a field. */\nconst cellsOf = (\n\tkinds: Catalog.Kinds,\n\tconstants: ReadonlyArray<Nodes.Scalar>,\n): ReadonlyArray<unknown> =>\n\tdedupeCells([\n\t\t...(kinds.null ? [null] : []),\n\t\t...(kinds.object ? [opaque] : []),\n\t\t...(kinds.boolean ? [true, false] : []),\n\t\t...(kinds.string\n\t\t\t? stringCells(Arr.filter(constants, (value) => typeof value === \"string\"))\n\t\t\t: []),\n\t\t...(kinds.number === \"none\"\n\t\t\t? []\n\t\t\t: numberCells(\n\t\t\t\t\tkinds.number,\n\t\t\t\t\tArr.filter(constants, (value) => typeof value === \"number\"),\n\t\t\t\t)),\n\t\t...kinds.literals,\n\t])\n\nclass OutOfBudget extends Data.Error {}\n\n/** What one search keeps for all its steps. It lives only as long as the search. */\ninterface Search {\n\treadonly cells: (field: string) => ReadonlyArray<unknown>\n\t/**\n\t * The texts of the values of each `In` test, so one test of a value does\n\t * not read every value of the test. It is a native `Map`, because it must\n\t * find a node by the node itself: a hash of a node reads all its values.\n\t * The texts are in a native `Set`, as in {@link dedupeCells}.\n\t */\n\treadonly members: Map<Nodes.In, Set<string>>\n\t/**\n\t * The keys of the conditions that the search found to be unsatisfiable:\n\t * see `Algebra.keyOf`. A condition with the same structure has the same\n\t * free fields, so its answer is the same. The set only grows, so the steps\n\t * of a search may share it. It is a native `Set`, as in\n\t * {@link dedupeCells}.\n\t */\n\treadonly failed: Set<string | Node>\n\treadonly budget: number\n}\n\n/** What a search has done up to now. Each step gives a new one. */\ninterface SearchState {\n\treadonly steps: number\n}\n\ninterface Found {\n\treadonly assignment: Option.Option<ReadonlyArray<readonly [string, unknown]>>\n\treadonly state: SearchState\n}\n\nconst literalOf = (operand: Nodes.Operand): Nodes.Scalar => {\n\tif (operand._tag !== \"Literal\") {\n\t\tthrow new Error(\n\t\t\t\"Condition.satisfiable needs a condition with no principal fields. Call Condition.partial first.\",\n\t\t)\n\t}\n\treturn operand.value\n}\n\n/**\n * The value of the field `name` after `field` gets `value`. A field below\n * `field` is `null` when `value` is not a value with parts.\n */\nconst fixedValue = (\n\tname: string,\n\tfield: string,\n\tvalue: unknown,\n): unknown | typeof free =>\n\tname === field\n\t\t? value\n\t\t: name.startsWith(`${field}.`)\n\t\t\t? value === opaque\n\t\t\t\t? free\n\t\t\t\t: null\n\t\t\t: free\n\nconst membersOf = (node: Nodes.In, search: Search): Set<string> => {\n\tconst known = search.members.get(node)\n\tif (known !== undefined) {\n\t\treturn known\n\t}\n\t// `===` finds no `NaN`, so the set has none, and `NaN` is in no test.\n\tconst members = new Set(\n\t\tArr.map(\n\t\t\tArr.filter(\n\t\t\t\tArr.map(node.values, literalOf),\n\t\t\t\t(literal) => !Number.isNaN(literal),\n\t\t\t),\n\t\t\tcellText,\n\t\t),\n\t)\n\tsearch.members.set(node, members)\n\treturn members\n}\n\nconst testFixed = (\n\tnode: Nodes.Compare | Nodes.In | Nodes.Exists,\n\tvalue: unknown,\n\tsearch: Search,\n): boolean => {\n\tswitch (node._tag) {\n\t\tcase \"Compare\":\n\t\t\treturn Algebra.testCompare(node.op, value, literalOf(node.value))\n\t\tcase \"In\":\n\t\t\treturn (\n\t\t\t\t!Number.isNaN(value) && membersOf(node, search).has(cellText(value))\n\t\t\t)\n\t\tcase \"Exists\":\n\t\t\treturn value !== null\n\t}\n}\n\n/** Puts one value into a field, and into the fields below it. */\nconst fix = (root: Node, field: string, value: unknown, search: Search): Node =>\n\t// The walks of a condition take the steps of a trampoline, so a deep\n\t// condition does not fill the stack.\n\tTrampoline.run(root.depth, (R) => {\n\t\tconst go = (node: Node): Trampoline.Step<Node> => {\n\t\t\tconst children = (nodes: ReadonlyArray<Node>) =>\n\t\t\t\tR.forEach(nodes, (child) => R.suspend(() => go(child)))\n\t\t\tswitch (node._tag) {\n\t\t\t\tcase \"Compare\":\n\t\t\t\tcase \"In\":\n\t\t\t\tcase \"Exists\": {\n\t\t\t\t\tconst known = fixedValue(node.field, field, value)\n\t\t\t\t\treturn R.succeed(\n\t\t\t\t\t\tknown === free\n\t\t\t\t\t\t\t? node\n\t\t\t\t\t\t\t: testFixed(node, known, search)\n\t\t\t\t\t\t\t\t? Nodes.always\n\t\t\t\t\t\t\t\t: Nodes.never,\n\t\t\t\t\t)\n\t\t\t\t}\n\t\t\t\tcase \"And\":\n\t\t\t\t\treturn R.map(children(node.conditions), Algebra.and)\n\t\t\t\tcase \"Or\":\n\t\t\t\t\treturn R.map(children(node.conditions), Algebra.or)\n\t\t\t\tcase \"Not\":\n\t\t\t\t\treturn R.map(\n\t\t\t\t\t\tR.suspend(() => go(node.condition)),\n\t\t\t\t\t\tAlgebra.not,\n\t\t\t\t\t)\n\t\t\t\tdefault:\n\t\t\t\t\treturn R.succeed(node)\n\t\t\t}\n\t\t}\n\t\treturn go(root)\n\t})\n\n/** The fields of the tests of a condition, once for each test. */\nconst testedFields = (root: Node): ReadonlyArray<string> => {\n\t// The fields go into one list, in order. A walk that joined the lists of\n\t// the children would copy the list of a deep condition at each level. The\n\t// list belongs to this walk.\n\tconst fields: Array<string> = []\n\tTrampoline.run(root.depth, (R) => {\n\t\tconst go = (node: Node): Trampoline.Step<undefined> => {\n\t\t\tswitch (node._tag) {\n\t\t\t\tcase \"Compare\":\n\t\t\t\tcase \"In\":\n\t\t\t\tcase \"Exists\":\n\t\t\t\t\tfields.push(node.field)\n\t\t\t\t\treturn R.succeed(undefined)\n\t\t\t\tcase \"And\":\n\t\t\t\tcase \"Or\":\n\t\t\t\t\treturn R.map(\n\t\t\t\t\t\tR.forEach(node.conditions, (child) => R.suspend(() => go(child))),\n\t\t\t\t\t\t() => undefined,\n\t\t\t\t\t)\n\t\t\t\tcase \"Not\":\n\t\t\t\t\treturn R.suspend(() => go(node.condition))\n\t\t\t\tdefault:\n\t\t\t\t\treturn R.succeed(undefined)\n\t\t\t}\n\t\t}\n\t\treturn go(root)\n\t})\n\treturn fields\n}\n\n/**\n * Picks the next field to split on. A field comes before the fields below it,\n * so a value of a field can fix them. Then a field in more tests comes first,\n * and then the first name in order, so the choice is the same each time.\n */\nconst nextField = (node: Node): string => {\n\t// A native `Map`, as in `dedupeCells`: a condition that is not bound to a\n\t// domain may have any field names. The map belongs to this call.\n\tconst counts = new Map<string, number>()\n\tArr.forEach(testedFields(node), (field) =>\n\t\tcounts.set(field, (counts.get(field) ?? 0) + 1),\n\t)\n\tconst depth = (field: string) => field.split(\".\").length\n\tconst [first, ...rest] = Array.from(counts)\n\treturn Arr.reduce(rest, first!, (best, next) =>\n\t\tdepth(next[0]) < depth(best[0]) ||\n\t\t(depth(next[0]) === depth(best[0]) &&\n\t\t\t(next[1] > best[1] || (next[1] === best[1] && next[0] < best[0])))\n\t\t\t? next\n\t\t\t: best,\n\t)[0]\n}\n\n/**\n * Finds values of the fields that make a simple condition true. It splits on\n * one field at a time, and tries one value from each cell of the field. Each\n * step reads the whole condition, so the budget counts its size.\n */\nconst searchNode = (search: Search) => (R: Trampoline.Runner) => {\n\tconst go = (\n\t\tnode: Node,\n\t\tstate: SearchState,\n\t\tassignment: ReadonlyArray<readonly [string, unknown]>,\n\t): Trampoline.Step<Found> => {\n\t\tif (node._tag === \"True\") {\n\t\t\treturn R.succeed({ assignment: Option.some(assignment), state })\n\t\t}\n\t\tif (node._tag === \"False\") {\n\t\t\treturn R.succeed({ assignment: Option.none(), state })\n\t\t}\n\t\t// The key reads at most a small part of the node: a node with very\n\t\t// many nodes, or with very many values in its `In` tests, is its own\n\t\t// key. A step reads the nodes but not the values, which `membersOf`\n\t\t// keeps, so the budget counts the size.\n\t\tconst key = Algebra.keyOf(node)\n\t\tif (search.failed.has(key)) {\n\t\t\treturn R.succeed({ assignment: Option.none(), state })\n\t\t}\n\t\tif (state.steps + node.size > search.budget) {\n\t\t\tthrow new OutOfBudget()\n\t\t}\n\t\tconst field = nextField(node)\n\t\t// The cells are tried in order, until one gives values. A fold over\n\t\t// the cells does no work after a found answer, and it does not grow\n\t\t// the stack with the number of cells. Each call for the next field\n\t\t// is a step of the runner, so very many fields do not fill the stack.\n\t\treturn R.map(\n\t\t\tR.reduce(\n\t\t\t\tsearch.cells(field),\n\t\t\t\t{\n\t\t\t\t\tassignment: Option.none(),\n\t\t\t\t\tstate: { ...state, steps: state.steps + node.size },\n\t\t\t\t} as Found,\n\t\t\t\t(before, cell): Trampoline.Step<Found> => {\n\t\t\t\t\tif (Option.isSome(before.assignment)) {\n\t\t\t\t\t\treturn R.succeed(before)\n\t\t\t\t\t}\n\t\t\t\t\tconst steps = before.state.steps + node.size\n\t\t\t\t\tif (steps > search.budget) {\n\t\t\t\t\t\tthrow new OutOfBudget()\n\t\t\t\t\t}\n\t\t\t\t\treturn R.suspend(() =>\n\t\t\t\t\t\tgo(fix(node, field, cell, search), { ...before.state, steps }, [\n\t\t\t\t\t\t\t...assignment,\n\t\t\t\t\t\t\t[field, cell],\n\t\t\t\t\t\t]),\n\t\t\t\t\t)\n\t\t\t\t},\n\t\t\t),\n\t\t\t(result) => {\n\t\t\t\tif (Option.isNone(result.assignment)) {\n\t\t\t\t\tsearch.failed.add(key)\n\t\t\t\t}\n\t\t\t\treturn result\n\t\t\t},\n\t\t)\n\t}\n\treturn go\n}\n\n/**\n * Puts a value at a path in an object, and gives a new object. A value with\n * parts (`opaque`) becomes an empty object, and it keeps an object that is\n * there.\n */\ntype Fields = Readonly<Record<string, unknown>>\n\nconst setPath = (\n\troot: Fields,\n\tnames: ReadonlyArray<string>,\n\tvalue: unknown,\n): Fields => {\n\tconst last = names[names.length - 1]\n\tif (last === undefined) {\n\t\treturn root\n\t}\n\t// Only an own field counts, as a condition reads it. An inherited one,\n\t// such as `__proto__`, is not a field of the example.\n\tconst own = (base: Fields, name: string): unknown =>\n\t\tObject.hasOwn(base, name) ? base[name] : undefined\n\t// The object at each part of the path, from the root down to the object\n\t// that gets the value. A part that is not an object becomes empty. The\n\t// path is walked in a loop, so a long path does not fill the stack.\n\tconst bases = Arr.scan(names.slice(0, -1), root, (base, name): Fields => {\n\t\tconst inner = own(base, name)\n\t\treturn isObject(inner) && inner !== opaque ? (inner as Fields) : {}\n\t})\n\tconst target = bases[bases.length - 1]!\n\tconst inner = own(target, last)\n\tconst placed: Fields =\n\t\tvalue === opaque && isObject(inner)\n\t\t\t? target\n\t\t\t: { ...target, [last]: value === opaque ? {} : value }\n\t// Each object above gets the new object below it, from the bottom up.\n\treturn Arr.reduceRight(\n\t\tnames.slice(0, -1),\n\t\tplaced,\n\t\t(below, name, index): Fields => ({ ...bases[index]!, [name]: below }),\n\t)\n}\n\nconst exampleOf = (\n\tassignment: ReadonlyArray<readonly [string, unknown]>,\n): Example =>\n\tArr.reduce(\n\t\tassignment,\n\t\t{} as Readonly<Record<string, unknown>>,\n\t\t(root, [field, value]) => setPath(root, field.split(\".\"), value),\n\t)\n\n/**\n * The constants that the tests of each field use. They come from a policy, so\n * they go into a native `Map` and `Set`, as in {@link dedupeCells}. The map\n * belongs to one search.\n */\nconst constantsOf = (node: Node): Map<string, Set<Nodes.Scalar>> => {\n\t// The constants go into one map as the walk finds them. A walk that\n\t// joined the lists of the children would copy the list of a deep\n\t// condition at each level. The map belongs to one search.\n\tconst all = new Map<string, Set<Nodes.Scalar>>()\n\tconst add = (field: string, values: ReadonlyArray<Nodes.Scalar>): void => {\n\t\tconst known = all.get(field) ?? new Set<Nodes.Scalar>()\n\t\tArr.forEach(values, (value) => known.add(value))\n\t\tall.set(field, known)\n\t}\n\t// The walk takes the steps of a trampoline, so a deep condition does not\n\t// fill the stack.\n\tTrampoline.run(node.depth, (R) => {\n\t\tconst go = (current: Node): Trampoline.Step<undefined> => {\n\t\t\tswitch (current._tag) {\n\t\t\t\tcase \"Compare\":\n\t\t\t\t\tadd(current.field, [literalOf(current.value)])\n\t\t\t\t\treturn R.succeed(undefined)\n\t\t\t\tcase \"In\":\n\t\t\t\t\tadd(current.field, Arr.map(current.values, literalOf))\n\t\t\t\t\treturn R.succeed(undefined)\n\t\t\t\tcase \"Exists\":\n\t\t\t\t\tadd(current.field, [])\n\t\t\t\t\treturn R.succeed(undefined)\n\t\t\t\tcase \"Under\":\n\t\t\t\t\tthrow new Error(\n\t\t\t\t\t\t\"Condition.satisfiable needs a condition with no Under nodes. Call Condition.partial first.\",\n\t\t\t\t\t)\n\t\t\t\tcase \"And\":\n\t\t\t\tcase \"Or\":\n\t\t\t\t\treturn R.map(\n\t\t\t\t\t\tR.forEach(current.conditions, (child) =>\n\t\t\t\t\t\t\tR.suspend(() => go(child)),\n\t\t\t\t\t\t),\n\t\t\t\t\t\t() => undefined,\n\t\t\t\t\t)\n\t\t\t\tcase \"Not\":\n\t\t\t\t\treturn R.suspend(() => go(current.condition))\n\t\t\t\tdefault:\n\t\t\t\t\treturn R.succeed(undefined)\n\t\t\t}\n\t\t}\n\t\treturn go(node)\n\t})\n\treturn all\n}\n\n/** Gives the kinds of each field of a catalog. A field that it does not have may hold any value. */\nexport const kindsIn =\n\t(catalog: Catalog.Catalog | undefined, parts?: number) =>\n\t(field: string): Catalog.Kinds =>\n\t\t(catalog === undefined ? undefined : Catalog.lookup(catalog, field, parts))\n\t\t\t?.kinds ?? Catalog.anyKinds\n\n/**\n * Searches for values that make a condition true. `kindsOf` gives the kinds\n * of values of each field. The condition must have no principal fields and no\n * `Under` nodes.\n */\nexport const satisfiable = (\n\tnode: Node,\n\tkindsOf: (field: string) => Catalog.Kinds,\n\tbudget: number = Limits.searchSteps,\n\tfields: number = Limits.searchFields,\n): Satisfiability => {\n\tif (node.opaque) {\n\t\treturn satisfiability.Unknown({ reason: \"predicate\" })\n\t}\n\tconst constants = constantsOf(node)\n\t// The search calls itself one time for each field that it fixes.\n\tif (constants.size > fields) {\n\t\treturn satisfiability.Unknown({ reason: \"budget\" })\n\t}\n\t// The cells of each field, in a native `Map` as the constants are.\n\tconst cells = new Map<string, ReadonlyArray<unknown>>()\n\tconst search: Search = {\n\t\tmembers: new Map(),\n\t\tfailed: new Set(),\n\t\tcells: (field) => {\n\t\t\tconst made = cells.get(field)\n\t\t\tif (made !== undefined) {\n\t\t\t\treturn made\n\t\t\t}\n\t\t\t// The cells come from the constants, so many constants are work\n\t\t\t// that the budget must count before the cells are made.\n\t\t\tconst known = constants.get(field) ?? new Set<Nodes.Scalar>()\n\t\t\tif (known.size > budget) {\n\t\t\t\tthrow new OutOfBudget()\n\t\t\t}\n\t\t\tconst found = cellsOf(kindsOf(field), Array.from(known))\n\t\t\tcells.set(field, found)\n\t\t\treturn found\n\t\t},\n\t\tbudget,\n\t}\n\ttry {\n\t\t// The search goes one call deeper for each field, so the runner is the\n\t\t// one for that many levels.\n\t\tconst found = Trampoline.run(constants.size, (R) =>\n\t\t\tsearchNode(search)(R)(Algebra.simplify(node), { steps: 0 }, []),\n\t\t)\n\t\treturn Option.match(found.assignment, {\n\t\t\tonNone: () => satisfiability.Unsatisfiable(),\n\t\t\tonSome: (assignment) =>\n\t\t\t\tsatisfiability.Satisfiable({ example: exampleOf(assignment) }),\n\t\t})\n\t} catch (error) {\n\t\tif (error instanceof OutOfBudget) {\n\t\t\treturn satisfiability.Unknown({ reason: \"budget\" })\n\t\t}\n\t\tthrow error\n\t}\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nmnmcc/ability",
3
- "version": "2.1.2",
3
+ "version": "2.1.3",
4
4
  "description": "Typed, composable authorization policies for Effect programs.",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",
package/src/Authorizer.ts CHANGED
@@ -175,11 +175,11 @@ export interface Authorizer<D extends Domain.Any, E, R> {
175
175
  >
176
176
  /** Gives the decision of the policy. It does not ask the decider. */
177
177
  readonly decide: <const T extends Policy.DeepTarget<D>>(
178
- target: T & Policy.CheckField<D, T>,
178
+ target: Policy.Checked<D, T>,
179
179
  ) => Effect.Effect<Decision.Decision, PolicyUnavailable | E, R>
180
180
  /** Succeeds when the principal may do the act, and fails when it may not. */
181
181
  readonly check: <const T extends DeepTarget<D>>(
182
- target: T & Policy.CheckField<D, T>,
182
+ target: Policy.Checked<D, T>,
183
183
  ) => Effect.Effect<void, Forbidden | Pending | PolicyUnavailable | E, R>
184
184
  /**
185
185
  * Checks the read action first, and fails with the error of `hide` when the
@@ -188,7 +188,7 @@ export interface Authorizer<D extends Domain.Any, E, R> {
188
188
  * exists.
189
189
  */
190
190
  readonly guard: <const T extends Policy.DeepTarget<D>, E2>(
191
- target: T & Policy.CheckField<D, T>,
191
+ target: Policy.Checked<D, T>,
192
192
  options: GuardOptions<D, E2, T["resource"]["_tag"]>,
193
193
  ) => Effect.Effect<void, E2 | Forbidden | Pending | PolicyUnavailable | E, R>
194
194
  /**
@@ -198,7 +198,7 @@ export interface Authorizer<D extends Domain.Any, E, R> {
198
198
  * value, so the list is empty: it is not an error.
199
199
  */
200
200
  readonly filter: <const T extends Policy.DeepKindTarget<D>>(
201
- target: T & Policy.CheckField<D, T>,
201
+ target: Policy.Checked<D, T>,
202
202
  ) => Effect.Effect<
203
203
  Condition.Condition<
204
204
  Policy.ResidualReads<D, T["kind"]>,
@@ -213,7 +213,7 @@ export interface Authorizer<D extends Domain.Any, E, R> {
213
213
  * only when the answer is not `None`.
214
214
  */
215
215
  readonly coverage: <const T extends Policy.DeepKindTarget<D>>(
216
- target: T & Policy.CheckField<D, T>,
216
+ target: Policy.Checked<D, T>,
217
217
  ) => Effect.Effect<
218
218
  Policy.Coverage<Policy.ResidualReads<D, T["kind"]>, Condition.Opaque<E>>,
219
219
  PolicyUnavailable | E,
@@ -249,17 +249,17 @@ export interface ServiceClass<
249
249
  /** The Effect that gives the principal of the current request. */
250
250
  readonly principal: Effect.Effect<Domain.Principal<D>, E, R>
251
251
  readonly decide: <const T extends Policy.DeepTarget<D>>(
252
- target: T & Policy.CheckField<D, T>,
252
+ target: Policy.Checked<D, T>,
253
253
  ) => Effect.Effect<Decision.Decision, PolicyUnavailable | E, R | Self>
254
254
  readonly check: <const T extends DeepTarget<D>>(
255
- target: T & Policy.CheckField<D, T>,
255
+ target: Policy.Checked<D, T>,
256
256
  ) => Effect.Effect<
257
257
  void,
258
258
  Forbidden | Pending | PolicyUnavailable | E,
259
259
  R | Self
260
260
  >
261
261
  readonly guard: <const T extends Policy.DeepTarget<D>, E2>(
262
- target: T & Policy.CheckField<D, T>,
262
+ target: Policy.Checked<D, T>,
263
263
  options: GuardOptions<D, E2, T["resource"]["_tag"]>,
264
264
  ) => Effect.Effect<
265
265
  void,
@@ -267,7 +267,7 @@ export interface ServiceClass<
267
267
  R | Self
268
268
  >
269
269
  readonly filter: <const T extends Policy.DeepKindTarget<D>>(
270
- target: T & Policy.CheckField<D, T>,
270
+ target: Policy.Checked<D, T>,
271
271
  ) => Effect.Effect<
272
272
  Condition.Condition<
273
273
  Policy.ResidualReads<D, T["kind"]>,
@@ -277,7 +277,7 @@ export interface ServiceClass<
277
277
  R | Self
278
278
  >
279
279
  readonly coverage: <const T extends Policy.DeepKindTarget<D>>(
280
- target: T & Policy.CheckField<D, T>,
280
+ target: Policy.Checked<D, T>,
281
281
  ) => Effect.Effect<
282
282
  Policy.Coverage<Policy.ResidualReads<D, T["kind"]>, Condition.Opaque<E>>,
283
283
  PolicyUnavailable | E,