@nmnmcc/ability 2.0.6 → 2.0.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -5
- package/dist/Condition.d.ts +2 -2
- package/dist/internal/algebra.js +100 -73
- package/dist/internal/algebra.js.map +1 -1
- package/dist/internal/analysis.js +1 -1
- package/dist/internal/analysis.js.map +1 -1
- package/dist/internal/authorizer.js +6 -5
- package/dist/internal/authorizer.js.map +1 -1
- package/dist/internal/bind.js +52 -35
- package/dist/internal/bind.js.map +1 -1
- package/dist/internal/catalog.js +6 -6
- package/dist/internal/catalog.js.map +1 -1
- package/dist/internal/nodes.d.ts +7 -0
- package/dist/internal/nodes.d.ts.map +1 -1
- package/dist/internal/nodes.js +34 -4
- package/dist/internal/nodes.js.map +1 -1
- package/dist/internal/policy-nodes.d.ts.map +1 -1
- package/dist/internal/policy-nodes.js +1 -1
- package/dist/internal/policy-nodes.js.map +1 -1
- package/dist/internal/trampoline.js +75 -0
- package/dist/internal/trampoline.js.map +1 -0
- package/package.json +1 -1
- package/src/internal/algebra.ts +218 -135
- package/src/internal/analysis.ts +3 -1
- package/src/internal/authorizer.ts +8 -5
- package/src/internal/bind.ts +131 -57
- package/src/internal/catalog.ts +15 -24
- package/src/internal/nodes.ts +37 -13
- package/src/internal/policy-nodes.ts +2 -1
- package/src/internal/trampoline.ts +147 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"catalog.js","names":["Limits.exceeded","Limits.catalogFields"],"sources":["../../src/internal/catalog.ts"],"sourcesContent":["/**\n * The catalog of the fields of a Schema. One walk over the Schema makes it,\n * and every question about the fields of a resource or of the principal reads\n * it: which names a rule may use, which values a field may hold, which fields\n * a pattern fits, and which fields a list of permitted fields shows.\n *\n * The catalog describes the shape of the values, not the checks of the\n * Schema. A check, such as a minimum length, only keeps some values out, so a\n * catalog that leaves the checks out holds every real value and more. An\n * answer that holds for all of these values, as `All` and `None` of a\n * coverage do, then holds for the real values too.\n */\nimport * as Arr from \"effect/Array\"\nimport * as HashMap from \"effect/HashMap\"\nimport * as HashSet from \"effect/HashSet\"\nimport * as Option from \"effect/Option\"\nimport { hasProperty } from \"effect/Predicate\"\nimport * as Schema from \"effect/Schema\"\nimport * as SchemaAST from \"effect/SchemaAST\"\nimport * as Limits from \"./limits.ts\"\n\n/** The kinds of values that a field may hold. */\nexport interface Kinds {\n\t/** The field may be `null`, `undefined` or missing. */\n\treadonly null: boolean\n\treadonly boolean: boolean\n\t/** The field may hold any string. */\n\treadonly string: boolean\n\t/** The numbers that the field may hold. */\n\treadonly number: \"none\" | \"integer\" | \"finite\" | \"any\"\n\t/** The field may hold an object, or a value that is not a scalar. */\n\treadonly object: boolean\n\t/** Single values that the field may hold, other than the ones above. */\n\treadonly literals: ReadonlyArray<string | number | boolean>\n}\n\n/** No value: the kinds of a field of `Schema.Never`. */\nexport const noKinds: Kinds = {\n\tnull: false,\n\tboolean: false,\n\tstring: false,\n\tnumber: \"none\",\n\tobject: false,\n\tliterals: [],\n}\n\n/** Any value. */\nexport const anyKinds: Kinds = {\n\tnull: true,\n\tboolean: true,\n\tstring: true,\n\tnumber: \"any\",\n\tobject: true,\n\tliterals: [],\n}\n\nconst nullKinds: Kinds = { ...noKinds, null: true }\nconst objectKinds: Kinds = { ...noKinds, object: true }\n\nconst numberOrder = [\"none\", \"integer\", \"finite\", \"any\"] as const\n\n/** The kinds of a field that may hold a value of `left` or of `right`. */\nexport const joinKinds = (left: Kinds, right: Kinds): Kinds => ({\n\tnull: left.null || right.null,\n\tboolean: left.boolean || right.boolean,\n\tstring: left.string || right.string,\n\tnumber:\n\t\tnumberOrder.indexOf(left.number) >= numberOrder.indexOf(right.number)\n\t\t\t? left.number\n\t\t\t: right.number,\n\tobject: left.object || right.object,\n\t// A native `Set` finds copies about ten times as fast as `Arr.dedupe`. It\n\t// takes `0` and `-0` as one, as `includes` does when a test reads them.\n\tliterals: Array.from(new Set([...left.literals, ...right.literals])),\n})\n\n/** Tests if a number fits the numbers of a field. */\nexport const fitsNumber = (number: Kinds[\"number\"], value: number): boolean => {\n\tswitch (number) {\n\t\tcase \"none\":\n\t\t\treturn false\n\t\tcase \"integer\":\n\t\t\treturn Number.isInteger(value)\n\t\tcase \"finite\":\n\t\t\treturn Number.isFinite(value)\n\t\tcase \"any\":\n\t\t\treturn true\n\t}\n}\n\n/**\n * Tests if a field with these kinds may hold a value. A missing value and\n * `undefined` are `null`.\n */\nexport const accepts = (kinds: Kinds, value: unknown): boolean => {\n\tif (value === null || value === undefined) {\n\t\treturn kinds.null\n\t}\n\tswitch (typeof value) {\n\t\tcase \"boolean\":\n\t\t\treturn kinds.boolean || kinds.literals.includes(value)\n\t\tcase \"string\":\n\t\t\treturn kinds.string || kinds.literals.includes(value)\n\t\tcase \"number\":\n\t\t\treturn fitsNumber(kinds.number, value) || kinds.literals.includes(value)\n\t\tdefault:\n\t\t\treturn kinds.object\n\t}\n}\n\n/** A part of a field path that stands for any key of a record. */\nexport interface Key {\n\treadonly _tag: \"Key\"\n\t/** The Schemas of the keys. A key must fit one of them. */\n\treadonly parameters: ReadonlyArray<SchemaAST.AST>\n}\n\n/** One part of a field path: a name, or any key of a record. */\nexport type Segment = string | Key\n\n/** One field of a catalog. */\nexport interface Field {\n\treadonly path: ReadonlyArray<Segment>\n\t/** The path with dots, and `*` for a key of a record. */\n\treadonly text: string\n\treadonly kinds: Kinds\n\t/**\n\t * The field holds a value that a list of fields shows as one field: a\n\t * scalar, a record, an array, or a struct at the last part a path may have.\n\t */\n\treadonly listed: boolean\n\t/** The field may hold parts that the Schema does not name, as a record may. */\n\treadonly open: boolean\n\t/**\n\t * A read of the field finds only plain data. It is `false` when a value\n\t * above the field may be an object that does not have it as a data field:\n\t * a class without the field, which may have a getter of that name, a\n\t * value such as a date or a list, or any object, as of `Schema.Unknown`.\n\t * A decode or a `make` copies a struct or a record into a plain object,\n\t * so a struct without the field gives `null` for it.\n\t */\n\treadonly plain: boolean\n}\n\n/** A place in the tree of a catalog, with the places below it. */\nexport interface Branch {\n\t/** The field at this place. The root has none. */\n\treadonly field: Field | undefined\n\t/** The places below, by name, in the order of the Schema. */\n\treadonly children: ReadonlyArray<readonly [string, Branch]>\n\t/**\n\t * The places below, by name. A lookup gets each name from `split`, as a\n\t * new string, and a `HashMap` finds a new string about 20% faster than a\n\t * native `Map`. The names come from the Schema, so a name from a policy\n\t * can not make a long list of names with one hash here.\n\t */\n\treadonly names: HashMap.HashMap<string, Branch>\n\t/** The place for the keys of a record, when the value may be a record. */\n\treadonly key: Key | undefined\n\treadonly keyBranch: Branch | undefined\n}\n\n/** The catalog of the fields of a Schema. */\nexport interface Catalog {\n\t/** Every field, a field before the fields below it. */\n\treadonly fields: ReadonlyArray<Field>\n\treadonly root: Branch\n}\n\n/**\n * Tests if a key of an object fits the Schemas of the keys of a record. A key\n * of an object is a string, also a key that a number Schema takes: `{ 1: \"a\" }`\n * has the key `\"1\"`. So a key that is the text of a number is tried as that\n * number too.\n */\nexport const keyFits = (key: Key, name: string): boolean =>\n\tArr.some(key.parameters, (parameter) => {\n\t\tconst is = Schema.is(Schema.make(parameter))\n\t\treturn (\n\t\t\tis(name) ||\n\t\t\t(name !== \"\" && String(Number(name)) === name && is(Number(name)))\n\t\t)\n\t})\n\n/**\n * Gives the struct of a `Schema.Class`. A class is a declaration with one\n * struct as its type parameter, and Effect marks it with a `\"~constructor\"`\n * annotation, also through more codec steps, as in\n * `Schema.fromJsonString(User)`. Other declarations, such as `Schema.Option`\n * or `Schema.ReadonlyMap`, have no such mark, and the fields of their type\n * parameter are not fields of the value.\n */\nconst classStruct = (\n\tast: SchemaAST.Declaration,\n): SchemaAST.Objects | undefined => {\n\tconst [inner] = ast.typeParameters\n\treturn ast.annotations?.[\"~constructor\"] !== undefined &&\n\t\tast.typeParameters.length === 1 &&\n\t\tinner?._tag === \"Objects\"\n\t\t? inner\n\t\t: undefined\n}\n\n/**\n * Gives the Schemas that a value at one place may fit: it opens unions and\n * `Schema.suspend`. A class stays a class: see {@link structOf}. The open suspend nodes are in a native `Set`,\n * because the walk must find a node by the node itself: `Equal` compares\n * Schema nodes by structure, and a recursive Schema has no end. A suspend\n * node that gives itself again, with no step on the path, gives nothing.\n */\nconst alternatives = (\n\tast: SchemaAST.AST,\n\topen: ReadonlySet<SchemaAST.AST>,\n): ReadonlyArray<SchemaAST.AST> => {\n\tswitch (ast._tag) {\n\t\tcase \"Union\":\n\t\t\treturn Arr.flatMap(ast.types, (type) => alternatives(type, open))\n\t\tcase \"Suspend\":\n\t\t\treturn open.has(ast)\n\t\t\t\t? []\n\t\t\t\t: alternatives(ast.thunk(), new Set([...open, ast]))\n\t\tdefault:\n\t\t\treturn [ast]\n\t}\n}\n\n/** The struct of a Schema: a struct, or the struct of a class. */\nconst structOf = (ast: SchemaAST.AST): SchemaAST.Objects | undefined =>\n\tast._tag === \"Objects\"\n\t\t? ast\n\t\t: ast._tag === \"Declaration\"\n\t\t\t? classStruct(ast)\n\t\t\t: undefined\n\nconst hasCheck = (\n\tchecks: ReadonlyArray<SchemaAST.Check<never>>,\n\tid: string,\n): boolean =>\n\tArr.some(\n\t\tchecks,\n\t\t(check) =>\n\t\t\tcheck.annotations?.representation?.id === id ||\n\t\t\t(check._tag === \"FilterGroup\" && hasCheck(check.checks, id)),\n\t)\n\n/** A struct with named fields, or with the keys of a record. */\nconst isStruct = (\n\tast: SchemaAST.Objects | undefined,\n): ast is SchemaAST.Objects =>\n\tast !== undefined &&\n\t(ast.propertySignatures.length > 0 || ast.indexSignatures.length > 0)\n\n/** The kinds of the values of a Schema that is not a struct. */\nconst kindsOf = (ast: SchemaAST.AST): Kinds => {\n\tswitch (ast._tag) {\n\t\tcase \"Null\":\n\t\tcase \"Undefined\":\n\t\t\treturn nullKinds\n\t\tcase \"Never\":\n\t\t\treturn noKinds\n\t\tcase \"String\":\n\t\tcase \"TemplateLiteral\":\n\t\t\treturn { ...noKinds, string: true }\n\t\tcase \"Boolean\":\n\t\t\treturn { ...noKinds, boolean: true }\n\t\tcase \"Number\": {\n\t\t\t// The two checks that a search uses. Effect names a number with\n\t\t\t// them, as `Schema.Int` and `Schema.Finite`; other checks are left\n\t\t\t// out, as all checks are.\n\t\t\tconst checks = ast.checks ?? []\n\t\t\treturn {\n\t\t\t\t...noKinds,\n\t\t\t\tnumber: hasCheck(checks, \"effect/schema/isInt\")\n\t\t\t\t\t? \"integer\"\n\t\t\t\t\t: hasCheck(checks, \"effect/schema/isFinite\")\n\t\t\t\t\t\t? \"finite\"\n\t\t\t\t\t\t: \"any\",\n\t\t\t}\n\t\t}\n\t\tcase \"Literal\":\n\t\t\treturn typeof ast.literal === \"bigint\"\n\t\t\t\t? objectKinds\n\t\t\t\t: { ...noKinds, literals: [ast.literal] }\n\t\tcase \"Enum\":\n\t\t\treturn { ...noKinds, literals: Arr.map(ast.enums, ([, value]) => value) }\n\t\tcase \"Arrays\":\n\t\tcase \"ObjectKeyword\":\n\t\tcase \"BigInt\":\n\t\tcase \"Symbol\":\n\t\tcase \"UniqueSymbol\":\n\t\t\treturn objectKinds\n\t\tcase \"Declaration\":\n\t\t\t// A declaration of Effect in `objectDeclarations` holds an object,\n\t\t\t// such as a `Date`. Another declaration may hold any value, as\n\t\t\t// `Schema.Json` or one from `Schema.declare` may.\n\t\t\treturn HashSet.has(objectDeclarations, declarationId(ast))\n\t\t\t\t? objectKinds\n\t\t\t\t: anyKinds\n\t\tdefault:\n\t\t\t// `Schema.Unknown`, `Schema.Void`, and `Struct({})`, which takes a\n\t\t\t// string too: these may hold any value.\n\t\t\treturn anyKinds\n\t}\n}\n\n/**\n * The ids of the declarations of Effect that hold only objects. Effect puts\n * the id in the `representation` annotation. A declaration that is not in\n * this list may hold a scalar, as `Schema.Json` may, so its kinds are any\n * value.\n */\nconst objectDeclarations = HashSet.make(\n\t\"effect/schema/Date\",\n\t\"effect/schema/URL\",\n\t\"effect/schema/Uint8Array\",\n\t\"effect/schema/Duration\",\n\t\"effect/schema/DateTimeUtc\",\n\t\"effect/schema/DateTimeZoned\",\n\t\"effect/schema/BigDecimal\",\n\t\"effect/schema/Redacted\",\n\t\"effect/schema/Cause\",\n\t\"effect/schema/Chunk\",\n\t\"effect/schema/HashMap\",\n\t\"effect/schema/HashSet\",\n\t\"effect/schema/ReadonlyMap\",\n\t\"effect/schema/ReadonlySet\",\n\t\"effect/schema/Option\",\n\t\"effect/schema/Result\",\n\t\"effect/schema/Exit\",\n\t\"effect/schema/File\",\n\t\"effect/schema/FormData\",\n\t\"effect/schema/RegExp\",\n\t\"effect/schema/URLSearchParams\",\n)\n\nconst declarationId = (ast: SchemaAST.Declaration): string | undefined => {\n\tconst representation: unknown = ast.annotations?.[\"representation\"]\n\treturn hasProperty(representation, \"id\") &&\n\t\ttypeof representation.id === \"string\"\n\t\t? representation.id\n\t\t: undefined\n}\n\n/** The things that a walk of the catalog knows at one place. */\ninterface Place {\n\treadonly path: ReadonlyArray<Segment>\n\treadonly schemas: ReadonlyArray<SchemaAST.AST>\n\t/** The value may be missing. */\n\treadonly missing: boolean\n\t/** The value may be anything, as an extra field of a struct may be. */\n\treadonly top: boolean\n\t/** A read here finds only plain data: see {@link Field.plain}. */\n\treadonly plain: boolean\n}\n\nconst textOf = (path: ReadonlyArray<Segment>): string =>\n\tArr.map(path, (segment) =>\n\t\ttypeof segment === \"string\" ? segment : \"*\",\n\t).join(\".\")\n\n/**\n * The name of a property as a part of a path. A key of an object is a string,\n * so a number key, as from `Schema.Record(Schema.Literal(1), ...)`, has the\n * name `\"1\"`. A symbol key is not a field.\n */\nconst nameOf = (signature: SchemaAST.PropertySignature): string | undefined =>\n\ttypeof signature.name === \"symbol\" ? undefined : String(signature.name)\n\n/**\n * The properties of a struct by name. A walk makes it one time for each\n * struct of a place, so a struct with very many fields does not take a\n * search for each name. It is a native `Map`, as `Branch.names` is.\n */\nconst propertiesOf = (\n\tstruct: SchemaAST.Objects,\n): ReadonlyMap<string, SchemaAST.PropertySignature> =>\n\tnew Map(\n\t\tArr.flatMap(struct.propertySignatures, (signature) => {\n\t\t\tconst name = nameOf(signature)\n\t\t\treturn name === undefined ? [] : [[name, signature] as const]\n\t\t}),\n\t)\n\n/** The names of the fields of the structs, in the order that they first come. */\nconst namesOf = (\n\tstructs: ReadonlyArray<SchemaAST.Objects>,\n): ReadonlyArray<string> =>\n\t// A native `Set` keeps the first copy of each name, in order, about ten\n\t// times as fast as `Arr.dedupe`.\n\tArray.from(\n\t\tnew Set(\n\t\t\tArr.flatMap(structs, (struct) =>\n\t\t\t\tArr.flatMap(struct.propertySignatures, (signature) => {\n\t\t\t\t\tconst name = nameOf(signature)\n\t\t\t\t\tif (name === undefined) {\n\t\t\t\t\t\treturn []\n\t\t\t\t\t}\n\t\t\t\t\t// A dot in a path is the step into a nested field. A field name with\n\t\t\t\t\t// a dot would make two fields have one path, so it is a defect.\n\t\t\t\t\tif (name.includes(\".\")) {\n\t\t\t\t\t\tthrow new Error(`A field name may not have a dot: \"${name}\"`)\n\t\t\t\t\t}\n\t\t\t\t\t// An empty name would make an empty part in a path, such as\n\t\t\t\t\t// `.secret`.\n\t\t\t\t\tif (name === \"\") {\n\t\t\t\t\t\tthrow new Error(\"A field name may not be empty\")\n\t\t\t\t\t}\n\t\t\t\t\treturn [name]\n\t\t\t\t}),\n\t\t\t),\n\t\t),\n\t)\n\n/**\n * Makes the branch of a place, with the branches below it. `count` is the\n * number of fields so far. It belongs to one walk, and it stops a Schema with\n * very many fields before the walk is done.\n */\nconst branchOf = (place: Place, count: { size: number }): Branch => {\n\tconst structs = Arr.filter(Arr.map(place.schemas, structOf), isStruct)\n\t// The structs of classes. A value of a class is not copied into a plain\n\t// object, so it keeps its getters and methods.\n\tconst classes = Arr.filter(\n\t\tArr.map(\n\t\t\tArr.filter(place.schemas, (schema) => schema._tag === \"Declaration\"),\n\t\t\tstructOf,\n\t\t),\n\t\tisStruct,\n\t)\n\tconst values = Arr.filter(\n\t\tplace.schemas,\n\t\t(schema) => !isStruct(structOf(schema)),\n\t)\n\tconst valueKinds = Arr.reduce(values, noKinds, (kinds, schema) =>\n\t\tjoinKinds(kinds, kindsOf(structOf(schema) ?? schema)),\n\t)\n\tconst kinds = place.top\n\t\t? anyKinds\n\t\t: joinKinds(\n\t\t\t\tjoinKinds(valueKinds, structs.length > 0 ? objectKinds : noKinds),\n\t\t\t\tplace.missing ? nullKinds : noKinds,\n\t\t\t)\n\tconst records = Arr.filter(\n\t\tstructs,\n\t\t(struct) => struct.indexSignatures.length > 0,\n\t)\n\t// The last part that a path may have: the walk does not go below it.\n\tconst last = place.path.length === Limits.fieldPathParts\n\tconst field: Field | undefined =\n\t\tplace.path.length === 0\n\t\t\t? undefined\n\t\t\t: {\n\t\t\t\t\tpath: place.path,\n\t\t\t\t\ttext: textOf(place.path),\n\t\t\t\t\tkinds,\n\t\t\t\t\tlisted:\n\t\t\t\t\t\tvalues.length > 0 ||\n\t\t\t\t\t\trecords.length > 0 ||\n\t\t\t\t\t\t(last && structs.length > 0),\n\t\t\t\t\t// A field that is a scalar in one branch of a union and a struct\n\t\t\t\t\t// in another holds parts too.\n\t\t\t\t\topen:\n\t\t\t\t\t\trecords.length > 0 ||\n\t\t\t\t\t\tstructs.length > 0 ||\n\t\t\t\t\t\tvalueKinds.object ||\n\t\t\t\t\t\tplace.top,\n\t\t\t\t\tplain: place.plain,\n\t\t\t\t}\n\tif (field !== undefined) {\n\t\tcount.size += 1\n\t\tif (count.size > Limits.catalogFields) {\n\t\t\tthrow Limits.exceeded(\n\t\t\t\t\"A Schema\",\n\t\t\t\t\"catalogFields\",\n\t\t\t\t\"fields\",\n\t\t\t\tcount.size,\n\t\t\t\tLimits.catalogFields,\n\t\t\t)\n\t\t}\n\t}\n\tif (last) {\n\t\treturn {\n\t\t\tfield,\n\t\t\tchildren: [],\n\t\t\tnames: HashMap.empty(),\n\t\t\tkey: undefined,\n\t\t\tkeyBranch: undefined,\n\t\t}\n\t}\n\tconst structProperties = Arr.map(structs, propertiesOf)\n\tconst classProperties = Arr.map(classes, propertiesOf)\n\tconst children = Arr.map(namesOf(structs), (name) => {\n\t\tconst properties = Arr.flatMap(structProperties, (named) => {\n\t\t\tconst property = named.get(name)\n\t\t\treturn property === undefined ? [] : [property]\n\t\t})\n\t\tconst below: Place = {\n\t\t\tpath: [...place.path, name],\n\t\t\tschemas: Arr.flatMap(properties, (property) =>\n\t\t\t\talternatives(property.type, new Set()),\n\t\t\t),\n\t\t\t// A field below a value that is not a struct reads `null`, and so\n\t\t\t// does a field that may be missing.\n\t\t\tmissing:\n\t\t\t\tplace.missing ||\n\t\t\t\tvalues.length > 0 ||\n\t\t\t\tArr.some(properties, (property) => SchemaAST.isOptional(property.type)),\n\t\t\t// A struct without the name may still have it as an extra field,\n\t\t\t// with any value: only a decode takes extra fields away. A value\n\t\t\t// that may be any object, as one of `Schema.Unknown` may, may have\n\t\t\t// the name with any value too.\n\t\t\ttop: place.top || valueKinds.object || properties.length < structs.length,\n\t\t\tplain:\n\t\t\t\tplace.plain &&\n\t\t\t\t!valueKinds.object &&\n\t\t\t\tArr.every(classProperties, (named) => named.has(name)),\n\t\t}\n\t\treturn [name, branchOf(below, count)] as const\n\t})\n\tconst names = HashMap.fromIterable(children)\n\tconst signatures = Arr.flatMap(records, (struct) => struct.indexSignatures)\n\tif (signatures.length === 0) {\n\t\treturn { field, children, names, key: undefined, keyBranch: undefined }\n\t}\n\tconst key: Key = {\n\t\t_tag: \"Key\",\n\t\tparameters: Arr.map(signatures, (signature) => signature.parameter),\n\t}\n\tconst keyBranch = branchOf(\n\t\t{\n\t\t\tpath: [...place.path, key],\n\t\t\tschemas: Arr.flatMap(signatures, (signature) =>\n\t\t\t\talternatives(signature.type, new Set()),\n\t\t\t),\n\t\t\t// A record may not have a given key.\n\t\t\tmissing: true,\n\t\t\ttop: place.top || valueKinds.object || records.length < structs.length,\n\t\t\tplain:\n\t\t\t\tplace.plain &&\n\t\t\t\t!valueKinds.object &&\n\t\t\t\tArr.every(classes, (struct) => struct.indexSignatures.length > 0),\n\t\t},\n\t\tcount,\n\t)\n\treturn { field, children, names, key, keyBranch }\n}\n\nconst fieldsOf = (branch: Branch): ReadonlyArray<Field> => [\n\t...(branch.field === undefined ? [] : [branch.field]),\n\t...Arr.flatMap(branch.children, ([, child]) => fieldsOf(child)),\n\t...(branch.keyBranch === undefined ? [] : fieldsOf(branch.keyBranch)),\n]\n\n/**\n * Makes the catalog of a Schema. It throws when a field name has a dot or is\n * empty, and when the Schema has more fields than `catalogFields`.\n */\nexport const catalogOf = (ast: SchemaAST.AST): Catalog => {\n\tconst root = branchOf(\n\t\t{\n\t\t\tpath: [],\n\t\t\tschemas: alternatives(ast, new Set()),\n\t\t\tmissing: false,\n\t\t\ttop: false,\n\t\t\tplain: true,\n\t\t},\n\t\t{ size: 0 },\n\t)\n\treturn { fields: fieldsOf(root), root }\n}\n\n/** Gives the field of a catalog at a path with dots, if the catalog has it. */\nexport const lookup = (catalog: Catalog, path: string): Field | undefined => {\n\tconst names = path.split(\".\", Limits.fieldPathParts + 1)\n\tif (names.length > Limits.fieldPathParts) {\n\t\treturn undefined\n\t}\n\tconst branch = Arr.reduce(\n\t\tnames,\n\t\tcatalog.root as Branch | undefined,\n\t\t(current, name) => {\n\t\t\tif (current === undefined) {\n\t\t\t\treturn undefined\n\t\t\t}\n\t\t\tconst named = HashMap.get(current.names, name)\n\t\t\tif (Option.isSome(named)) {\n\t\t\t\treturn named.value\n\t\t\t}\n\t\t\treturn current.key !== undefined && keyFits(current.key, name)\n\t\t\t\t? current.keyBranch\n\t\t\t\t: undefined\n\t\t},\n\t)\n\treturn branch?.field\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"catalog.js","names":["Limits.exceeded","Limits.catalogFields"],"sources":["../../src/internal/catalog.ts"],"sourcesContent":["/**\n * The catalog of the fields of a Schema. One walk over the Schema makes it,\n * and every question about the fields of a resource or of the principal reads\n * it: which names a rule may use, which values a field may hold, which fields\n * a pattern fits, and which fields a list of permitted fields shows.\n *\n * The catalog describes the shape of the values, not the checks of the\n * Schema. A check, such as a minimum length, only keeps some values out, so a\n * catalog that leaves the checks out holds every real value and more. An\n * answer that holds for all of these values, as `All` and `None` of a\n * coverage do, then holds for the real values too.\n */\nimport * as Arr from \"effect/Array\"\nimport * as HashMap from \"effect/HashMap\"\nimport * as HashSet from \"effect/HashSet\"\nimport * as Option from \"effect/Option\"\nimport { hasProperty } from \"effect/Predicate\"\nimport * as Schema from \"effect/Schema\"\nimport * as SchemaAST from \"effect/SchemaAST\"\nimport * as Limits from \"./limits.ts\"\n\n/** The kinds of values that a field may hold. */\nexport interface Kinds {\n\t/** The field may be `null`, `undefined` or missing. */\n\treadonly null: boolean\n\treadonly boolean: boolean\n\t/** The field may hold any string. */\n\treadonly string: boolean\n\t/** The numbers that the field may hold. */\n\treadonly number: \"none\" | \"integer\" | \"finite\" | \"any\"\n\t/** The field may hold an object, or a value that is not a scalar. */\n\treadonly object: boolean\n\t/** Single values that the field may hold, other than the ones above. */\n\treadonly literals: ReadonlyArray<string | number | boolean>\n}\n\n/** No value: the kinds of a field of `Schema.Never`. */\nexport const noKinds: Kinds = {\n\tnull: false,\n\tboolean: false,\n\tstring: false,\n\tnumber: \"none\",\n\tobject: false,\n\tliterals: [],\n}\n\n/** Any value. */\nexport const anyKinds: Kinds = {\n\tnull: true,\n\tboolean: true,\n\tstring: true,\n\tnumber: \"any\",\n\tobject: true,\n\tliterals: [],\n}\n\nconst nullKinds: Kinds = { ...noKinds, null: true }\nconst objectKinds: Kinds = { ...noKinds, object: true }\n\nconst numberOrder = [\"none\", \"integer\", \"finite\", \"any\"] as const\n\n/** The kinds of a field that may hold a value of `left` or of `right`. */\nexport const joinKinds = (left: Kinds, right: Kinds): Kinds => ({\n\tnull: left.null || right.null,\n\tboolean: left.boolean || right.boolean,\n\tstring: left.string || right.string,\n\tnumber:\n\t\tnumberOrder.indexOf(left.number) >= numberOrder.indexOf(right.number)\n\t\t\t? left.number\n\t\t\t: right.number,\n\tobject: left.object || right.object,\n\t// A native `Set` finds copies about ten times as fast as `Arr.dedupe`. It\n\t// takes `0` and `-0` as one, as `includes` does when a test reads them.\n\tliterals: Array.from(new Set([...left.literals, ...right.literals])),\n})\n\n/** Tests if a number fits the numbers of a field. */\nexport const fitsNumber = (number: Kinds[\"number\"], value: number): boolean => {\n\tswitch (number) {\n\t\tcase \"none\":\n\t\t\treturn false\n\t\tcase \"integer\":\n\t\t\treturn Number.isInteger(value)\n\t\tcase \"finite\":\n\t\t\treturn Number.isFinite(value)\n\t\tcase \"any\":\n\t\t\treturn true\n\t}\n}\n\n/**\n * Tests if a field with these kinds may hold a value. A missing value and\n * `undefined` are `null`.\n */\nexport const accepts = (kinds: Kinds, value: unknown): boolean => {\n\tif (value === null || value === undefined) {\n\t\treturn kinds.null\n\t}\n\tswitch (typeof value) {\n\t\tcase \"boolean\":\n\t\t\treturn kinds.boolean || kinds.literals.includes(value)\n\t\tcase \"string\":\n\t\t\treturn kinds.string || kinds.literals.includes(value)\n\t\tcase \"number\":\n\t\t\treturn fitsNumber(kinds.number, value) || kinds.literals.includes(value)\n\t\tdefault:\n\t\t\treturn kinds.object\n\t}\n}\n\n/** A part of a field path that stands for any key of a record. */\nexport interface Key {\n\treadonly _tag: \"Key\"\n\t/** The Schemas of the keys. A key must fit one of them. */\n\treadonly parameters: ReadonlyArray<SchemaAST.AST>\n}\n\n/** One part of a field path: a name, or any key of a record. */\nexport type Segment = string | Key\n\n/** One field of a catalog. */\nexport interface Field {\n\treadonly path: ReadonlyArray<Segment>\n\t/** The path with dots, and `*` for a key of a record. */\n\treadonly text: string\n\treadonly kinds: Kinds\n\t/**\n\t * The field holds a value that a list of fields shows as one field: a\n\t * scalar, a record, an array, or a struct at the last part a path may have.\n\t */\n\treadonly listed: boolean\n\t/** The field may hold parts that the Schema does not name, as a record may. */\n\treadonly open: boolean\n\t/**\n\t * A read of the field finds only plain data. It is `false` when a value\n\t * above the field may be an object that does not have it as a data field:\n\t * a class without the field, which may have a getter of that name, a\n\t * value such as a date or a list, or any object, as of `Schema.Unknown`.\n\t * A decode or a `make` copies a struct or a record into a plain object,\n\t * so a struct without the field gives `null` for it.\n\t */\n\treadonly plain: boolean\n}\n\n/** A place in the tree of a catalog, with the places below it. */\nexport interface Branch {\n\t/** The field at this place. The root has none. */\n\treadonly field: Field | undefined\n\t/** The places below, by name, in the order of the Schema. */\n\treadonly children: ReadonlyArray<readonly [string, Branch]>\n\t/**\n\t * The places below, by name. A lookup gets each name from `split`, as a\n\t * new string, and a `HashMap` finds a new string about 20% faster than a\n\t * native `Map`. The names come from the Schema, so a name from a policy\n\t * can not make a long list of names with one hash here.\n\t */\n\treadonly names: HashMap.HashMap<string, Branch>\n\t/** The place for the keys of a record, when the value may be a record. */\n\treadonly key: Key | undefined\n\treadonly keyBranch: Branch | undefined\n}\n\n/** The catalog of the fields of a Schema. */\nexport interface Catalog {\n\t/** Every field, a field before the fields below it. */\n\treadonly fields: ReadonlyArray<Field>\n\treadonly root: Branch\n}\n\n/**\n * Tests if a key of an object fits the Schemas of the keys of a record. A key\n * of an object is a string, also a key that a number Schema takes: `{ 1: \"a\" }`\n * has the key `\"1\"`. So a key that is the text of a number is tried as that\n * number too.\n */\nexport const keyFits = (key: Key, name: string): boolean =>\n\tArr.some(key.parameters, (parameter) => {\n\t\tconst is = Schema.is(Schema.make(parameter))\n\t\treturn (\n\t\t\tis(name) ||\n\t\t\t(name !== \"\" && String(Number(name)) === name && is(Number(name)))\n\t\t)\n\t})\n\n/**\n * Gives the struct of a `Schema.Class`. A class is a declaration with one\n * struct as its type parameter, and Effect marks it with a `\"~constructor\"`\n * annotation, also through more codec steps, as in\n * `Schema.fromJsonString(User)`. Other declarations, such as `Schema.Option`\n * or `Schema.ReadonlyMap`, have no such mark, and the fields of their type\n * parameter are not fields of the value.\n */\nconst classStruct = (\n\tast: SchemaAST.Declaration,\n): SchemaAST.Objects | undefined => {\n\tconst [inner] = ast.typeParameters\n\treturn ast.annotations?.[\"~constructor\"] !== undefined &&\n\t\tast.typeParameters.length === 1 &&\n\t\tinner?._tag === \"Objects\"\n\t\t? inner\n\t\t: undefined\n}\n\n/**\n * Gives the Schemas that a value at one place may fit: it opens unions and\n * `Schema.suspend`. A class stays a class: see {@link structOf}. The open suspend nodes are in a native `Set`,\n * because the walk must find a node by the node itself: `Equal` compares\n * Schema nodes by structure, and a recursive Schema has no end. A suspend\n * node that gives itself again, with no step on the path, gives nothing.\n */\nconst alternatives = (\n\tast: SchemaAST.AST,\n\topen: ReadonlySet<SchemaAST.AST>,\n): ReadonlyArray<SchemaAST.AST> => {\n\tswitch (ast._tag) {\n\t\tcase \"Union\":\n\t\t\treturn Arr.flatMap(ast.types, (type) => alternatives(type, open))\n\t\tcase \"Suspend\":\n\t\t\treturn open.has(ast)\n\t\t\t\t? []\n\t\t\t\t: alternatives(ast.thunk(), new Set([...open, ast]))\n\t\tdefault:\n\t\t\treturn [ast]\n\t}\n}\n\n/** The struct of a Schema: a struct, or the struct of a class. */\nconst structOf = (ast: SchemaAST.AST): SchemaAST.Objects | undefined =>\n\tast._tag === \"Objects\"\n\t\t? ast\n\t\t: ast._tag === \"Declaration\"\n\t\t\t? classStruct(ast)\n\t\t\t: undefined\n\nconst hasCheck = (\n\tchecks: ReadonlyArray<SchemaAST.Check<never>>,\n\tid: string,\n): boolean =>\n\tArr.some(\n\t\tchecks,\n\t\t(check) =>\n\t\t\tcheck.annotations?.representation?.id === id ||\n\t\t\t(check._tag === \"FilterGroup\" && hasCheck(check.checks, id)),\n\t)\n\n/** A struct with named fields, or with the keys of a record. */\nconst isStruct = (\n\tast: SchemaAST.Objects | undefined,\n): ast is SchemaAST.Objects =>\n\tast !== undefined &&\n\t(ast.propertySignatures.length > 0 || ast.indexSignatures.length > 0)\n\n/** The kinds of the values of a Schema that is not a struct. */\nconst kindsOf = (ast: SchemaAST.AST): Kinds => {\n\tswitch (ast._tag) {\n\t\tcase \"Null\":\n\t\tcase \"Undefined\":\n\t\t\treturn nullKinds\n\t\tcase \"Never\":\n\t\t\treturn noKinds\n\t\tcase \"String\":\n\t\tcase \"TemplateLiteral\":\n\t\t\treturn { ...noKinds, string: true }\n\t\tcase \"Boolean\":\n\t\t\treturn { ...noKinds, boolean: true }\n\t\tcase \"Number\": {\n\t\t\t// The two checks that a search uses. Effect names a number with\n\t\t\t// them, as `Schema.Int` and `Schema.Finite`; other checks are left\n\t\t\t// out, as all checks are.\n\t\t\tconst checks = ast.checks ?? []\n\t\t\treturn {\n\t\t\t\t...noKinds,\n\t\t\t\tnumber: hasCheck(checks, \"effect/schema/isInt\")\n\t\t\t\t\t? \"integer\"\n\t\t\t\t\t: hasCheck(checks, \"effect/schema/isFinite\")\n\t\t\t\t\t\t? \"finite\"\n\t\t\t\t\t\t: \"any\",\n\t\t\t}\n\t\t}\n\t\tcase \"Literal\":\n\t\t\treturn typeof ast.literal === \"bigint\"\n\t\t\t\t? objectKinds\n\t\t\t\t: { ...noKinds, literals: [ast.literal] }\n\t\tcase \"Enum\":\n\t\t\treturn { ...noKinds, literals: Arr.map(ast.enums, ([, value]) => value) }\n\t\tcase \"Arrays\":\n\t\tcase \"ObjectKeyword\":\n\t\tcase \"BigInt\":\n\t\tcase \"Symbol\":\n\t\tcase \"UniqueSymbol\":\n\t\t\treturn objectKinds\n\t\tcase \"Declaration\":\n\t\t\t// A declaration of Effect in `objectDeclarations` holds an object,\n\t\t\t// such as a `Date`. Another declaration may hold any value, as\n\t\t\t// `Schema.Json` or one from `Schema.declare` may.\n\t\t\treturn HashSet.has(objectDeclarations, declarationId(ast))\n\t\t\t\t? objectKinds\n\t\t\t\t: anyKinds\n\t\tdefault:\n\t\t\t// `Schema.Unknown`, `Schema.Void`, and `Struct({})`, which takes a\n\t\t\t// string too: these may hold any value.\n\t\t\treturn anyKinds\n\t}\n}\n\n/**\n * The ids of the declarations of Effect that hold only objects. Effect puts\n * the id in the `representation` annotation. A declaration that is not in\n * this list may hold a scalar, as `Schema.Json` may, so its kinds are any\n * value.\n */\nconst objectDeclarations = HashSet.make(\n\t\"effect/schema/Date\",\n\t\"effect/schema/URL\",\n\t\"effect/schema/Uint8Array\",\n\t\"effect/schema/Duration\",\n\t\"effect/schema/DateTimeUtc\",\n\t\"effect/schema/DateTimeZoned\",\n\t\"effect/schema/BigDecimal\",\n\t\"effect/schema/Redacted\",\n\t\"effect/schema/Cause\",\n\t\"effect/schema/Chunk\",\n\t\"effect/schema/HashMap\",\n\t\"effect/schema/HashSet\",\n\t\"effect/schema/ReadonlyMap\",\n\t\"effect/schema/ReadonlySet\",\n\t\"effect/schema/Option\",\n\t\"effect/schema/Result\",\n\t\"effect/schema/Exit\",\n\t\"effect/schema/File\",\n\t\"effect/schema/FormData\",\n\t\"effect/schema/RegExp\",\n\t\"effect/schema/URLSearchParams\",\n)\n\nconst declarationId = (ast: SchemaAST.Declaration): string | undefined => {\n\tconst representation: unknown = ast.annotations?.[\"representation\"]\n\treturn hasProperty(representation, \"id\") &&\n\t\ttypeof representation.id === \"string\"\n\t\t? representation.id\n\t\t: undefined\n}\n\n/** The things that a walk of the catalog knows at one place. */\ninterface Place {\n\treadonly path: ReadonlyArray<Segment>\n\treadonly schemas: ReadonlyArray<SchemaAST.AST>\n\t/** The value may be missing. */\n\treadonly missing: boolean\n\t/** The value may be anything, as an extra field of a struct may be. */\n\treadonly top: boolean\n\t/** A read here finds only plain data: see {@link Field.plain}. */\n\treadonly plain: boolean\n}\n\nconst textOf = (path: ReadonlyArray<Segment>): string =>\n\tArr.map(path, (segment) =>\n\t\ttypeof segment === \"string\" ? segment : \"*\",\n\t).join(\".\")\n\n/**\n * The name of a property as a part of a path. A key of an object is a string,\n * so a number key, as from `Schema.Record(Schema.Literal(1), ...)`, has the\n * name `\"1\"`. A symbol key is not a field.\n */\nconst nameOf = (signature: SchemaAST.PropertySignature): string | undefined =>\n\ttypeof signature.name === \"symbol\" ? undefined : String(signature.name)\n\n/**\n * The properties of a struct by name. A walk makes it one time for each\n * struct of a place, so a struct with very many fields does not take a\n * search for each name. It is a native `Map`, as `Branch.names` is.\n */\nconst propertiesOf = (\n\tstruct: SchemaAST.Objects,\n): ReadonlyMap<string, SchemaAST.PropertySignature> =>\n\tnew Map(\n\t\tArr.flatMap(struct.propertySignatures, (signature) => {\n\t\t\tconst name = nameOf(signature)\n\t\t\treturn name === undefined ? [] : [[name, signature] as const]\n\t\t}),\n\t)\n\n/** The names of the fields of the structs, in the order that they first come. */\nconst namesOf = (\n\tstructs: ReadonlyArray<SchemaAST.Objects>,\n): ReadonlyArray<string> =>\n\t// A native `Set` keeps the first copy of each name, in order, about ten\n\t// times as fast as `Arr.dedupe`.\n\tArray.from(\n\t\tnew Set(\n\t\t\tArr.flatMap(structs, (struct) =>\n\t\t\t\tArr.flatMap(struct.propertySignatures, (signature) => {\n\t\t\t\t\tconst name = nameOf(signature)\n\t\t\t\t\tif (name === undefined) {\n\t\t\t\t\t\treturn []\n\t\t\t\t\t}\n\t\t\t\t\t// A dot in a path is the step into a nested field. A field name with\n\t\t\t\t\t// a dot would make two fields have one path, so it is a defect.\n\t\t\t\t\tif (name.includes(\".\")) {\n\t\t\t\t\t\tthrow new Error(`A field name may not have a dot: \"${name}\"`)\n\t\t\t\t\t}\n\t\t\t\t\t// An empty name would make an empty part in a path, such as\n\t\t\t\t\t// `.secret`.\n\t\t\t\t\tif (name === \"\") {\n\t\t\t\t\t\tthrow new Error(\"A field name may not be empty\")\n\t\t\t\t\t}\n\t\t\t\t\treturn [name]\n\t\t\t\t}),\n\t\t\t),\n\t\t),\n\t)\n\n/**\n * Makes the branch of a place, with the branches below it. `count` is the\n * number of fields so far. It belongs to one walk, and it stops a Schema with\n * very many fields before the walk is done.\n */\nconst branchOf = (place: Place, count: { size: number }): Branch => {\n\tconst structs = Arr.filter(Arr.map(place.schemas, structOf), isStruct)\n\tconst values = Arr.filter(\n\t\tplace.schemas,\n\t\t(schema) => !isStruct(structOf(schema)),\n\t)\n\tconst valueKinds = Arr.reduce(values, noKinds, (kinds, schema) =>\n\t\tjoinKinds(kinds, kindsOf(structOf(schema) ?? schema)),\n\t)\n\tconst kinds = place.top\n\t\t? anyKinds\n\t\t: joinKinds(\n\t\t\t\tjoinKinds(valueKinds, structs.length > 0 ? objectKinds : noKinds),\n\t\t\t\tplace.missing ? nullKinds : noKinds,\n\t\t\t)\n\tconst records = Arr.filter(\n\t\tstructs,\n\t\t(struct) => struct.indexSignatures.length > 0,\n\t)\n\t// The last part that a path may have: the walk does not go below it.\n\tconst last = place.path.length === Limits.fieldPathParts\n\tconst field: Field | undefined =\n\t\tplace.path.length === 0\n\t\t\t? undefined\n\t\t\t: {\n\t\t\t\t\tpath: place.path,\n\t\t\t\t\ttext: textOf(place.path),\n\t\t\t\t\tkinds,\n\t\t\t\t\tlisted:\n\t\t\t\t\t\tvalues.length > 0 ||\n\t\t\t\t\t\trecords.length > 0 ||\n\t\t\t\t\t\t(last && structs.length > 0),\n\t\t\t\t\t// A field that is a scalar in one branch of a union and a struct\n\t\t\t\t\t// in another holds parts too.\n\t\t\t\t\topen:\n\t\t\t\t\t\trecords.length > 0 ||\n\t\t\t\t\t\tstructs.length > 0 ||\n\t\t\t\t\t\tvalueKinds.object ||\n\t\t\t\t\t\tplace.top,\n\t\t\t\t\tplain: place.plain,\n\t\t\t\t}\n\tif (field !== undefined) {\n\t\tcount.size += 1\n\t\tif (count.size > Limits.catalogFields) {\n\t\t\tthrow Limits.exceeded(\n\t\t\t\t\"A Schema\",\n\t\t\t\t\"catalogFields\",\n\t\t\t\t\"fields\",\n\t\t\t\tcount.size,\n\t\t\t\tLimits.catalogFields,\n\t\t\t)\n\t\t}\n\t}\n\tif (last) {\n\t\treturn {\n\t\t\tfield,\n\t\t\tchildren: [],\n\t\t\tnames: HashMap.empty(),\n\t\t\tkey: undefined,\n\t\t\tkeyBranch: undefined,\n\t\t}\n\t}\n\tconst structProperties = Arr.map(structs, propertiesOf)\n\tconst 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\tpath: [...place.path, name],\n\t\t\tschemas: Arr.flatMap(properties, (property) =>\n\t\t\t\talternatives(property.type, new Set()),\n\t\t\t),\n\t\t\t// A field below a value that is not a struct reads `null`, and so\n\t\t\t// does a field that may be missing.\n\t\t\tmissing:\n\t\t\t\tplace.missing ||\n\t\t\t\tvalues.length > 0 ||\n\t\t\t\tArr.some(properties, (property) => SchemaAST.isOptional(property.type)),\n\t\t\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, branchOf(below, count)] as const\n\t})\n\tconst names = HashMap.fromIterable(children)\n\tconst signatures = Arr.flatMap(records, (struct) => struct.indexSignatures)\n\tif (signatures.length === 0) {\n\t\treturn { field, children, names, key: undefined, keyBranch: undefined }\n\t}\n\tconst keyTop =\n\t\tplace.top || valueKinds.object || records.length < structs.length\n\tconst key: Key = {\n\t\t_tag: \"Key\",\n\t\tparameters: Arr.map(signatures, (signature) => signature.parameter),\n\t}\n\tconst keyBranch = branchOf(\n\t\t{\n\t\t\tpath: [...place.path, key],\n\t\t\tschemas: Arr.flatMap(signatures, (signature) =>\n\t\t\t\talternatives(signature.type, new Set()),\n\t\t\t),\n\t\t\t// A record may not have a given key.\n\t\t\tmissing: true,\n\t\t\ttop: keyTop,\n\t\t\tplain: place.plain && !keyTop,\n\t\t},\n\t\tcount,\n\t)\n\treturn { field, children, names, key, keyBranch }\n}\n\nconst fieldsOf = (branch: Branch): ReadonlyArray<Field> => [\n\t...(branch.field === undefined ? [] : [branch.field]),\n\t...Arr.flatMap(branch.children, ([, child]) => fieldsOf(child)),\n\t...(branch.keyBranch === undefined ? [] : fieldsOf(branch.keyBranch)),\n]\n\n/**\n * Makes the catalog of a Schema. It throws when a field name has a dot or is\n * empty, and when the Schema has more fields than `catalogFields`.\n */\nexport const catalogOf = (ast: SchemaAST.AST): Catalog => {\n\tconst root = branchOf(\n\t\t{\n\t\t\tpath: [],\n\t\t\tschemas: alternatives(ast, new Set()),\n\t\t\tmissing: false,\n\t\t\ttop: false,\n\t\t\tplain: true,\n\t\t},\n\t\t{ size: 0 },\n\t)\n\treturn { fields: fieldsOf(root), root }\n}\n\n/** Gives the field of a catalog at a path with dots, if the catalog has it. */\nexport const lookup = (catalog: Catalog, path: string): Field | undefined => {\n\tconst names = path.split(\".\", Limits.fieldPathParts + 1)\n\tif (names.length > Limits.fieldPathParts) {\n\t\treturn undefined\n\t}\n\tconst branch = Arr.reduce(\n\t\tnames,\n\t\tcatalog.root as Branch | undefined,\n\t\t(current, name) => {\n\t\t\tif (current === undefined) {\n\t\t\t\treturn undefined\n\t\t\t}\n\t\t\tconst named = HashMap.get(current.names, name)\n\t\t\tif (Option.isSome(named)) {\n\t\t\t\treturn named.value\n\t\t\t}\n\t\t\treturn current.key !== undefined && keyFits(current.key, name)\n\t\t\t\t? current.keyBranch\n\t\t\t\t: undefined\n\t\t},\n\t)\n\treturn branch?.field\n}\n"],"mappings":"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"}
|
package/dist/internal/nodes.d.ts
CHANGED
|
@@ -19,6 +19,7 @@ export interface PrincipalRef {
|
|
|
19
19
|
export type Operand = Literal | PrincipalRef;
|
|
20
20
|
interface Facts {
|
|
21
21
|
readonly size: number;
|
|
22
|
+
readonly weight: number;
|
|
22
23
|
readonly depth: number;
|
|
23
24
|
readonly opaque: boolean;
|
|
24
25
|
}
|
|
@@ -34,6 +35,12 @@ declare abstract class Base extends Data.Class {
|
|
|
34
35
|
constructor(props: object, facts: Facts);
|
|
35
36
|
/** The number of nodes, with a shared child counted for each place. */
|
|
36
37
|
get size(): number;
|
|
38
|
+
/**
|
|
39
|
+
* The number of nodes and of the values of their `In` and `Under` tests,
|
|
40
|
+
* with a shared child counted for each place. A walk that reads the tests
|
|
41
|
+
* does this much work, so the limits of the nodes count it.
|
|
42
|
+
*/
|
|
43
|
+
get weight(): number;
|
|
37
44
|
/** The number of levels. */
|
|
38
45
|
get depth(): number;
|
|
39
46
|
/** The node has a predicate. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"nodes.d.ts","names":[],"sources":["../../src/internal/nodes.ts"],"mappings":";;;;YA2BY;;qBAGC,iBAAe,OAAA;YAShB,yBAAyB,gBAAgB;;iBAWpC;WACP;WACA,OAAO;;;iBAIA;WACP;WACA;;YAGE,UAAU,UAAU;UA+DtB;WACA;WACA;WACA;;;
|
|
1
|
+
{"version":3,"file":"nodes.d.ts","names":[],"sources":["../../src/internal/nodes.ts"],"mappings":";;;;YA2BY;;qBAGC,iBAAe,OAAA;YAShB,yBAAyB,gBAAgB;;iBAWpC;WACP;WACA,OAAO;;;iBAIA;WACP;WACA;;YAGE,UAAU,UAAU;UA+DtB;WACA;WACA;WACA;WACA;;;uBAmDK,aAAa,KAAK;;;;;;;EAMhC;EAMA,YAAY,eAAe,OAAO;;MAM9B;;;;;;MASA;;MAKA;;MAKA;;;qBAMQ,aAAa;WAChB;EACT;;;qBAMY,cAAc;WACjB;EACT;;;qBAMY,gBAAgB;WACnB;WACQ,IAAI;WACJ;WACA,OAAO;EACxB,YAAY;aACF,IAAI;aACJ;aACA,OAAO;;;;qBAiBL,WAAW;WACd;WACQ;WACA,QAAQ,cAAc;EACvC,YAAY;aACF;aACA,QAAQ,cAAc;;;;qBAQpB,eAAe;WAClB;WACQ;EACjB,YAAY;aAAkB;;;;qBAMlB,cAAc;WACjB;WACQ,QAAQ,cAAc;EACvC,YAAY;aAAkB,QAAQ,cAAc;;;qBAiBxC,YAAY;WACf;WACQ,YAAY,cAAc;EAC3C,YAAY,YAAY,cAAc;;qBAM1B,WAAW;WACd;WACQ,YAAY,cAAc;EAC3C,YAAY,YAAY,cAAc;;qBAM1B,YAAY;WACf;WACQ,WAAW;EAC5B,YAAY,WAAW;;;YASZ,OACX,gBACA,uBACI,OAAO;;;;;;qBAOC,kBAAkB;WACrB;WACQ;WACA,MAChB,cACA,qBACI,OAAO;EACZ,YAAY;aAAkB;aAAuB,KAAK;;;;YAe/C,OACX,OAAO,QAAQ,UAAU,KAAK,SAAS,QAAQ,MAAM,KAAK,MAAM"}
|
package/dist/internal/nodes.js
CHANGED
|
@@ -88,17 +88,36 @@ const checkOperands = (values) => {
|
|
|
88
88
|
};
|
|
89
89
|
const leaf = {
|
|
90
90
|
size: 1,
|
|
91
|
+
weight: 1,
|
|
91
92
|
depth: 1,
|
|
92
93
|
opaque: false
|
|
93
94
|
};
|
|
95
|
+
/**
|
|
96
|
+
* The facts of a test with values, as `In` and `Under`. Each value counts in
|
|
97
|
+
* the weight, since a walk that reads the test reads its values too. It stops
|
|
98
|
+
* a test past the limits.
|
|
99
|
+
*/
|
|
100
|
+
const valuesFacts = (values) => checkWeight({
|
|
101
|
+
...leaf,
|
|
102
|
+
weight: 1 + values.length
|
|
103
|
+
});
|
|
104
|
+
const checkWeight = (facts) => {
|
|
105
|
+
if (facts.weight > 1e5) throw exceeded("A condition", "conditionNodes", "nodes and values", facts.weight, conditionNodes);
|
|
106
|
+
return facts;
|
|
107
|
+
};
|
|
94
108
|
/** The facts of a node with children. It stops a node past the limits. */
|
|
95
109
|
const factsOf = (children) => {
|
|
96
110
|
const size = Arr.reduce(children, 1, (total, child) => total + child.size);
|
|
97
|
-
|
|
111
|
+
const weight = Arr.reduce(children, 1, (total, child) => total + child.weight);
|
|
112
|
+
checkWeight({
|
|
113
|
+
...leaf,
|
|
114
|
+
weight
|
|
115
|
+
});
|
|
98
116
|
const depth = 1 + Arr.reduce(children, 0, (top, child) => Math.max(top, child.depth));
|
|
99
117
|
if (depth > 500) throw exceeded("A condition", "conditionDepth", "levels", depth, 500);
|
|
100
118
|
return {
|
|
101
119
|
size,
|
|
120
|
+
weight,
|
|
102
121
|
depth,
|
|
103
122
|
opaque: Arr.some(children, (child) => child.opaque)
|
|
104
123
|
};
|
|
@@ -122,6 +141,14 @@ var Base = class extends Data.Class {
|
|
|
122
141
|
get size() {
|
|
123
142
|
return this.#facts.size;
|
|
124
143
|
}
|
|
144
|
+
/**
|
|
145
|
+
* The number of nodes and of the values of their `In` and `Under` tests,
|
|
146
|
+
* with a shared child counted for each place. A walk that reads the tests
|
|
147
|
+
* does this much work, so the limits of the nodes count it.
|
|
148
|
+
*/
|
|
149
|
+
get weight() {
|
|
150
|
+
return this.#facts.weight;
|
|
151
|
+
}
|
|
125
152
|
/** The number of levels. */
|
|
126
153
|
get depth() {
|
|
127
154
|
return this.#facts.depth;
|
|
@@ -161,10 +188,11 @@ var Compare = class extends Base {
|
|
|
161
188
|
var In = class extends Base {
|
|
162
189
|
_tag = "In";
|
|
163
190
|
constructor(props) {
|
|
191
|
+
const values = checkOperands(props.values);
|
|
164
192
|
super({
|
|
165
193
|
field: checkPath(props.field),
|
|
166
|
-
values
|
|
167
|
-
},
|
|
194
|
+
values
|
|
195
|
+
}, valuesFacts(values));
|
|
168
196
|
}
|
|
169
197
|
};
|
|
170
198
|
/** True when a field is not `null` and not missing. */
|
|
@@ -178,7 +206,8 @@ var Exists = class extends Base {
|
|
|
178
206
|
var Under = class extends Base {
|
|
179
207
|
_tag = "Under";
|
|
180
208
|
constructor(props) {
|
|
181
|
-
|
|
209
|
+
const prefix = checkOperands(props.prefix);
|
|
210
|
+
super({ prefix }, valuesFacts(prefix));
|
|
182
211
|
}
|
|
183
212
|
};
|
|
184
213
|
const checkChildren = (conditions) => {
|
|
@@ -222,6 +251,7 @@ var Predicate = class extends Base {
|
|
|
222
251
|
run: props.run
|
|
223
252
|
}, {
|
|
224
253
|
size: 1,
|
|
254
|
+
weight: 1,
|
|
225
255
|
depth: 1,
|
|
226
256
|
opaque: true
|
|
227
257
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"nodes.js","names":["Limits.exceeded","Limits.conditionNodes"],"sources":["../../src/internal/nodes.ts"],"sourcesContent":["/**\n * The nodes of a condition. Each node is a class, and only its constructor\n * makes one, also when a condition comes from JSON. The constructor checks the\n * data of the node and works out three facts once:\n * - `size`: the number of nodes, with a shared child counted for each place;\n * - `depth`: the number of levels;\n * - `opaque`: the node has a predicate.\n *\n * The constructor stops a node that goes past `conditionNodes` or\n * `conditionDepth`. So a walk over any condition visits at most\n * `conditionNodes` nodes and goes at most `conditionDepth` levels down, and it\n * needs no memo.\n *\n * The facts are private fields, so `Equal` and `Hash` do not read them: two\n * nodes with the same data are equal.\n */\n// A predicate gives an Effect with an error type and a service type that the\n// nodes do not know, so they are `unknown` here.\n// @effect-diagnostics anyUnknownInErrorContext:off\nimport * as Arr from \"effect/Array\"\nimport * as Data from \"effect/Data\"\nimport type * as Effect from \"effect/Effect\"\nimport { hasProperty } from \"effect/Predicate\"\nimport * as Schema from \"effect/Schema\"\nimport * as Limits from \"./limits.ts\"\n\n/** A value that a condition can compare. */\nexport type Scalar = string | number | boolean | null\n\n/** The operators of a `Compare` test. The decoders read this list too. */\nexport const CompareOperator = Schema.Literals([\n\t\"eq\",\n\t\"ne\",\n\t\"lt\",\n\t\"lte\",\n\t\"gt\",\n\t\"gte\",\n])\n\nexport type CompareOperator = typeof CompareOperator.Type\n\n/**\n * Tests if a value is an operator. It reads the list of the Schema with\n * `includes`: for a short list, this is about twice as fast as\n * `Schema.is`, and a decoder tests each node of a large input.\n */\nexport const isCompareOperator = (u: unknown): u is CompareOperator =>\n\t(CompareOperator.literals as ReadonlyArray<unknown>).includes(u)\n\n/** A fixed value. */\nexport interface Literal {\n\treadonly _tag: \"Literal\"\n\treadonly value: Scalar\n}\n\n/** A field of the principal. */\nexport interface PrincipalRef {\n\treadonly _tag: \"Principal\"\n\treadonly path: string\n}\n\nexport type Operand = Literal | PrincipalRef\n\nconst operands = Data.taggedEnum<Operand>()\n\nexport const literal = (value: Scalar): Literal => operands.Literal({ value })\n\nexport const principalRef = (path: string): PrincipalRef =>\n\toperands.Principal({ path })\n\n/** Tests if a value is a scalar. Any number is one, also `NaN`. */\nexport const isScalar = (value: unknown): value is Scalar =>\n\tvalue === null ||\n\ttypeof value === \"string\" ||\n\ttypeof value === \"number\" ||\n\ttypeof value === \"boolean\"\n\n/**\n * Checks a field path: a string with at most `fieldPathParts` parts. A longer\n * path is never a field of a resource or of the principal.\n */\nconst checkPath = (path: unknown): string => {\n\tif (typeof path !== \"string\") {\n\t\tthrow new Error(\"A field path must be a string\")\n\t}\n\tconst parts = path.split(\".\", Limits.fieldPathParts + 1).length\n\tif (parts > Limits.fieldPathParts) {\n\t\tthrow Limits.exceeded(\n\t\t\t\"A field path\",\n\t\t\t\"fieldPathParts\",\n\t\t\t\"parts\",\n\t\t\tparts,\n\t\t\tLimits.fieldPathParts,\n\t\t)\n\t}\n\treturn path\n}\n\n/** Checks an operand, and gives a new one with only its data. */\nconst checkOperand = (operand: unknown): Operand => {\n\tif (hasProperty(operand, \"_tag\")) {\n\t\tif (\n\t\t\toperand._tag === \"Literal\" &&\n\t\t\thasProperty(operand, \"value\") &&\n\t\t\tisScalar(operand.value)\n\t\t) {\n\t\t\treturn literal(operand.value)\n\t\t}\n\t\tif (operand._tag === \"Principal\" && hasProperty(operand, \"path\")) {\n\t\t\treturn principalRef(checkPath(operand.path))\n\t\t}\n\t}\n\tthrow new Error(\n\t\t\"An operand must be a fixed scalar value or a field of the principal\",\n\t)\n}\n\nconst checkOperands = (values: unknown): ReadonlyArray<Operand> => {\n\tif (!Array.isArray(values)) {\n\t\tthrow new Error(\"A list of operands must be an array\")\n\t}\n\treturn Object.freeze(Arr.map(Array.from(values), checkOperand))\n}\n\ninterface Facts {\n\treadonly size: number\n\treadonly depth: number\n\treadonly opaque: boolean\n}\n\nconst leaf: Facts = { size: 1, depth: 1, opaque: false }\n\n/** The facts of a node with children. It stops a node past the limits. */\nconst factsOf = (children: ReadonlyArray<Node>): Facts => {\n\tconst size = Arr.reduce(children, 1, (total, child) => total + child.size)\n\tif (size > Limits.conditionNodes) {\n\t\tthrow Limits.exceeded(\n\t\t\t\"A condition\",\n\t\t\t\"conditionNodes\",\n\t\t\t\"nodes\",\n\t\t\tsize,\n\t\t\tLimits.conditionNodes,\n\t\t)\n\t}\n\tconst depth =\n\t\t1 + Arr.reduce(children, 0, (top, child) => Math.max(top, child.depth))\n\tif (depth > Limits.conditionDepth) {\n\t\tthrow Limits.exceeded(\n\t\t\t\"A condition\",\n\t\t\t\"conditionDepth\",\n\t\t\t\"levels\",\n\t\t\tdepth,\n\t\t\tLimits.conditionDepth,\n\t\t)\n\t}\n\treturn {\n\t\tsize,\n\t\tdepth,\n\t\topaque: Arr.some(children, (child) => child.opaque),\n\t}\n}\n\n/** The base of every node. It keeps the facts. */\nabstract class Base extends Data.Class {\n\t/**\n\t * The JSON form of the node, for `JSON.stringify`. A number that JSON can\n\t * not hold, as `Infinity`, becomes an object that no decoder takes, in\n\t * place of `null`, which would change the test.\n\t */\n\ttoJSON(): unknown {\n\t\treturn toJson(this as unknown as Node)\n\t}\n\n\treadonly #facts: Facts\n\n\tconstructor(props: object, facts: Facts) {\n\t\tsuper(props as never)\n\t\tthis.#facts = facts\n\t}\n\n\t/** The number of nodes, with a shared child counted for each place. */\n\tget size(): number {\n\t\treturn this.#facts.size\n\t}\n\n\t/** The number of levels. */\n\tget depth(): number {\n\t\treturn this.#facts.depth\n\t}\n\n\t/** The node has a predicate. */\n\tget opaque(): boolean {\n\t\treturn this.#facts.opaque\n\t}\n}\n\n/** Always true. */\nexport class True extends Base {\n\treadonly _tag = \"True\"\n\tconstructor() {\n\t\tsuper({}, leaf)\n\t}\n}\n\n/** Always false. */\nexport class False extends Base {\n\treadonly _tag = \"False\"\n\tconstructor() {\n\t\tsuper({}, leaf)\n\t}\n}\n\n/** Compares a field with a value. */\nexport class Compare extends Base {\n\treadonly _tag = \"Compare\"\n\tdeclare readonly op: CompareOperator\n\tdeclare readonly field: string\n\tdeclare readonly value: Operand\n\tconstructor(props: {\n\t\treadonly op: CompareOperator\n\t\treadonly field: string\n\t\treadonly value: Operand\n\t}) {\n\t\tif (!isCompareOperator(props.op)) {\n\t\t\tthrow new Error(`\"${String(props.op)}\" is not a compare operator`)\n\t\t}\n\t\tsuper(\n\t\t\t{\n\t\t\t\top: props.op,\n\t\t\t\tfield: checkPath(props.field),\n\t\t\t\tvalue: checkOperand(props.value),\n\t\t\t},\n\t\t\tleaf,\n\t\t)\n\t}\n}\n\n/** True when a field is equal to one of the values. */\nexport class In extends Base {\n\treadonly _tag = \"In\"\n\tdeclare readonly field: string\n\tdeclare readonly values: ReadonlyArray<Operand>\n\tconstructor(props: {\n\t\treadonly field: string\n\t\treadonly values: ReadonlyArray<Operand>\n\t}) {\n\t\tsuper(\n\t\t\t{ field: checkPath(props.field), values: checkOperands(props.values) },\n\t\t\tleaf,\n\t\t)\n\t}\n}\n\n/** True when a field is not `null` and not missing. */\nexport class Exists extends Base {\n\treadonly _tag = \"Exists\"\n\tdeclare readonly field: string\n\tconstructor(props: { readonly field: string }) {\n\t\tsuper({ field: checkPath(props.field) }, leaf)\n\t}\n}\n\n/** True when the path of the resource starts with the prefix. */\nexport class Under extends Base {\n\treadonly _tag = \"Under\"\n\tdeclare readonly prefix: ReadonlyArray<Operand>\n\tconstructor(props: { readonly prefix: ReadonlyArray<Operand> }) {\n\t\tsuper({ prefix: checkOperands(props.prefix) }, leaf)\n\t}\n}\n\nconst checkChildren = (conditions: unknown): ReadonlyArray<Node> => {\n\tif (!Array.isArray(conditions)) {\n\t\tthrow new Error(\"The children of a node must be an array\")\n\t}\n\tconst children = Array.from(conditions as ReadonlyArray<unknown>)\n\tif (!Arr.every(children, isNode)) {\n\t\tthrow notNode()\n\t}\n\treturn Object.freeze(children)\n}\n\nexport class And extends Base {\n\treadonly _tag = \"And\"\n\tdeclare readonly conditions: ReadonlyArray<Node>\n\tconstructor(conditions: ReadonlyArray<Node>) {\n\t\tconst children = checkChildren(conditions)\n\t\tsuper({ conditions: children }, factsOf(children))\n\t}\n}\n\nexport class Or extends Base {\n\treadonly _tag = \"Or\"\n\tdeclare readonly conditions: ReadonlyArray<Node>\n\tconstructor(conditions: ReadonlyArray<Node>) {\n\t\tconst children = checkChildren(conditions)\n\t\tsuper({ conditions: children }, factsOf(children))\n\t}\n}\n\nexport class Not extends Base {\n\treadonly _tag = \"Not\"\n\tdeclare readonly condition: Node\n\tconstructor(condition: Node) {\n\t\tif (!isNode(condition)) {\n\t\t\tthrow notNode()\n\t\t}\n\t\tsuper({ condition }, factsOf([condition]))\n\t}\n}\n\n/** The test of a predicate, with no types. */\nexport type Run = (\n\tvalue: unknown,\n\tprincipal: unknown,\n) => Effect.Effect<boolean, unknown, unknown>\n\n/**\n * A test that runs an `Effect`. Its test takes inputs of the type `never`, so\n * no code can call it with a value of the wrong type: `Condition.evaluate`\n * checks the input. Use {@link runOf} inside the library.\n */\nexport class Predicate extends Base {\n\treadonly _tag = \"Predicate\"\n\tdeclare readonly name: string\n\tdeclare readonly run: (\n\t\tvalue: never,\n\t\tprincipal: never,\n\t) => Effect.Effect<boolean, unknown, unknown>\n\tconstructor(props: { readonly name: string; readonly run: Run }) {\n\t\tif (typeof props.name !== \"string\" || typeof props.run !== \"function\") {\n\t\t\tthrow new Error(\"A predicate needs a name and a test\")\n\t\t}\n\t\tsuper(\n\t\t\t{ name: props.name, run: props.run },\n\t\t\t{ size: 1, depth: 1, opaque: true },\n\t\t)\n\t}\n}\n\n/** Gives the test of a predicate, with no types. */\nexport const runOf = (node: Predicate): Run => node.run as Run\n\n/** A node of a condition. */\nexport type Node =\n\tTrue | False | Compare | In | Exists | Under | And | Or | Not | Predicate\n\nconst operandJson = (operand: Operand): unknown =>\n\toperand._tag === \"Literal\"\n\t\t? {\n\t\t\t\t_tag: \"Literal\",\n\t\t\t\t// JSON has no `Infinity`, `-Infinity` or `NaN`. Such a number\n\t\t\t\t// becomes an object that the decoder does not take.\n\t\t\t\tvalue:\n\t\t\t\t\ttypeof operand.value === \"number\" && !Number.isFinite(operand.value)\n\t\t\t\t\t\t? { nonFinite: String(operand.value) }\n\t\t\t\t\t\t: operand.value,\n\t\t\t}\n\t\t: { _tag: \"Principal\", path: operand.path }\n\n/** Gives the JSON form of a condition with no predicate. */\nexport const toJson = (node: Node): unknown => {\n\tswitch (node._tag) {\n\t\tcase \"True\":\n\t\tcase \"False\":\n\t\t\treturn { _tag: node._tag }\n\t\tcase \"Compare\":\n\t\t\treturn {\n\t\t\t\t_tag: \"Compare\",\n\t\t\t\top: node.op,\n\t\t\t\tfield: node.field,\n\t\t\t\tvalue: operandJson(node.value),\n\t\t\t}\n\t\tcase \"In\":\n\t\t\treturn {\n\t\t\t\t_tag: \"In\",\n\t\t\t\tfield: node.field,\n\t\t\t\tvalues: Arr.map(node.values, operandJson),\n\t\t\t}\n\t\tcase \"Exists\":\n\t\t\treturn { _tag: \"Exists\", field: node.field }\n\t\tcase \"Under\":\n\t\t\treturn { _tag: \"Under\", prefix: Arr.map(node.prefix, operandJson) }\n\t\tcase \"And\":\n\t\tcase \"Or\":\n\t\t\treturn { _tag: node._tag, conditions: Arr.map(node.conditions, toJson) }\n\t\tcase \"Not\":\n\t\t\treturn { _tag: \"Not\", condition: toJson(node.condition) }\n\t\tcase \"Predicate\":\n\t\t\tthrow new Error(\n\t\t\t\t`The predicate \"${node.name}\" can not become JSON. A condition with a predicate has no JSON form.`,\n\t\t\t)\n\t}\n}\n\n/** Tests if a value is a node. Only a constructor of a node makes one. */\nexport const isNode = (u: unknown): u is Node => u instanceof Base\n\nconst notNode = (): Error =>\n\tnew Error(\n\t\t\"A condition must be made with a builder or a function of Condition. A plain object is not a condition\",\n\t)\n\n/** Gives a value as a node, or throws when it is not a node. */\nexport const toNode = (u: unknown): Node => {\n\tif (!isNode(u)) {\n\t\tthrow notNode()\n\t}\n\treturn u\n}\n\nexport const always: Node = new True()\n\nexport const never: Node = new False()\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"nodes.js","names":["Limits.exceeded","Limits.conditionNodes"],"sources":["../../src/internal/nodes.ts"],"sourcesContent":["/**\n * The nodes of a condition. Each node is a class, and only its constructor\n * makes one, also when a condition comes from JSON. The constructor checks the\n * data of the node and works out three facts once:\n * - `size`: the number of nodes, with a shared child counted for each place;\n * - `depth`: the number of levels;\n * - `opaque`: the node has a predicate.\n *\n * The constructor stops a node that goes past `conditionNodes` or\n * `conditionDepth`. So a walk over any condition visits at most\n * `conditionNodes` nodes and goes at most `conditionDepth` levels down, and it\n * needs no memo.\n *\n * The facts are private fields, so `Equal` and `Hash` do not read them: two\n * nodes with the same data are equal.\n */\n// A predicate gives an Effect with an error type and a service type that the\n// nodes do not know, so they are `unknown` here.\n// @effect-diagnostics anyUnknownInErrorContext:off\nimport * as Arr from \"effect/Array\"\nimport * as Data from \"effect/Data\"\nimport type * as Effect from \"effect/Effect\"\nimport { hasProperty } from \"effect/Predicate\"\nimport * as Schema from \"effect/Schema\"\nimport * as Limits from \"./limits.ts\"\n\n/** A value that a condition can compare. */\nexport type Scalar = string | number | boolean | null\n\n/** The operators of a `Compare` test. The decoders read this list too. */\nexport const CompareOperator = Schema.Literals([\n\t\"eq\",\n\t\"ne\",\n\t\"lt\",\n\t\"lte\",\n\t\"gt\",\n\t\"gte\",\n])\n\nexport type CompareOperator = typeof CompareOperator.Type\n\n/**\n * Tests if a value is an operator. It reads the list of the Schema with\n * `includes`: for a short list, this is about twice as fast as\n * `Schema.is`, and a decoder tests each node of a large input.\n */\nexport const isCompareOperator = (u: unknown): u is CompareOperator =>\n\t(CompareOperator.literals as ReadonlyArray<unknown>).includes(u)\n\n/** A fixed value. */\nexport interface Literal {\n\treadonly _tag: \"Literal\"\n\treadonly value: Scalar\n}\n\n/** A field of the principal. */\nexport interface PrincipalRef {\n\treadonly _tag: \"Principal\"\n\treadonly path: string\n}\n\nexport type Operand = Literal | PrincipalRef\n\nconst operands = Data.taggedEnum<Operand>()\n\nexport const literal = (value: Scalar): Literal => operands.Literal({ value })\n\nexport const principalRef = (path: string): PrincipalRef =>\n\toperands.Principal({ path })\n\n/** Tests if a value is a scalar. Any number is one, also `NaN`. */\nexport const isScalar = (value: unknown): value is Scalar =>\n\tvalue === null ||\n\ttypeof value === \"string\" ||\n\ttypeof value === \"number\" ||\n\ttypeof value === \"boolean\"\n\n/**\n * Checks a field path: a string with at most `fieldPathParts` parts. A longer\n * path is never a field of a resource or of the principal.\n */\nconst checkPath = (path: unknown): string => {\n\tif (typeof path !== \"string\") {\n\t\tthrow new Error(\"A field path must be a string\")\n\t}\n\tconst parts = path.split(\".\", Limits.fieldPathParts + 1).length\n\tif (parts > Limits.fieldPathParts) {\n\t\tthrow Limits.exceeded(\n\t\t\t\"A field path\",\n\t\t\t\"fieldPathParts\",\n\t\t\t\"parts\",\n\t\t\tparts,\n\t\t\tLimits.fieldPathParts,\n\t\t)\n\t}\n\treturn path\n}\n\n/** Checks an operand, and gives a new one with only its data. */\nconst checkOperand = (operand: unknown): Operand => {\n\tif (hasProperty(operand, \"_tag\")) {\n\t\tif (\n\t\t\toperand._tag === \"Literal\" &&\n\t\t\thasProperty(operand, \"value\") &&\n\t\t\tisScalar(operand.value)\n\t\t) {\n\t\t\treturn literal(operand.value)\n\t\t}\n\t\tif (operand._tag === \"Principal\" && hasProperty(operand, \"path\")) {\n\t\t\treturn principalRef(checkPath(operand.path))\n\t\t}\n\t}\n\tthrow new Error(\n\t\t\"An operand must be a fixed scalar value or a field of the principal\",\n\t)\n}\n\nconst checkOperands = (values: unknown): ReadonlyArray<Operand> => {\n\tif (!Array.isArray(values)) {\n\t\tthrow new Error(\"A list of operands must be an array\")\n\t}\n\treturn Object.freeze(Arr.map(Array.from(values), checkOperand))\n}\n\ninterface Facts {\n\treadonly size: number\n\treadonly weight: number\n\treadonly depth: number\n\treadonly opaque: boolean\n}\n\nconst leaf: Facts = { size: 1, weight: 1, depth: 1, opaque: false }\n\n/**\n * The facts of a test with values, as `In` and `Under`. Each value counts in\n * the weight, since a walk that reads the test reads its values too. It stops\n * a test past the limits.\n */\nconst valuesFacts = (values: ReadonlyArray<Operand>): Facts =>\n\tcheckWeight({ ...leaf, weight: 1 + values.length })\n\nconst checkWeight = (facts: Facts): Facts => {\n\tif (facts.weight > Limits.conditionNodes) {\n\t\tthrow Limits.exceeded(\n\t\t\t\"A condition\",\n\t\t\t\"conditionNodes\",\n\t\t\t\"nodes and values\",\n\t\t\tfacts.weight,\n\t\t\tLimits.conditionNodes,\n\t\t)\n\t}\n\treturn facts\n}\n\n/** The facts of a node with children. It stops a node past the limits. */\nconst factsOf = (children: ReadonlyArray<Node>): Facts => {\n\tconst size = Arr.reduce(children, 1, (total, child) => total + child.size)\n\tconst weight = Arr.reduce(children, 1, (total, child) => total + child.weight)\n\tcheckWeight({ ...leaf, weight })\n\tconst depth =\n\t\t1 + Arr.reduce(children, 0, (top, child) => Math.max(top, child.depth))\n\tif (depth > Limits.conditionDepth) {\n\t\tthrow Limits.exceeded(\n\t\t\t\"A condition\",\n\t\t\t\"conditionDepth\",\n\t\t\t\"levels\",\n\t\t\tdepth,\n\t\t\tLimits.conditionDepth,\n\t\t)\n\t}\n\treturn {\n\t\tsize,\n\t\tweight,\n\t\tdepth,\n\t\topaque: Arr.some(children, (child) => child.opaque),\n\t}\n}\n\n/** The base of every node. It keeps the facts. */\nabstract class Base extends Data.Class {\n\t/**\n\t * The JSON form of the node, for `JSON.stringify`. A number that JSON can\n\t * not hold, as `Infinity`, becomes an object that no decoder takes, in\n\t * place of `null`, which would change the test.\n\t */\n\ttoJSON(): unknown {\n\t\treturn toJson(this as unknown as Node)\n\t}\n\n\treadonly #facts: Facts\n\n\tconstructor(props: object, facts: Facts) {\n\t\tsuper(props as never)\n\t\tthis.#facts = facts\n\t}\n\n\t/** The number of nodes, with a shared child counted for each place. */\n\tget size(): number {\n\t\treturn this.#facts.size\n\t}\n\n\t/**\n\t * The number of nodes and of the values of their `In` and `Under` tests,\n\t * with a shared child counted for each place. A walk that reads the tests\n\t * does this much work, so the limits of the nodes count it.\n\t */\n\tget weight(): number {\n\t\treturn this.#facts.weight\n\t}\n\n\t/** The number of levels. */\n\tget depth(): number {\n\t\treturn this.#facts.depth\n\t}\n\n\t/** The node has a predicate. */\n\tget opaque(): boolean {\n\t\treturn this.#facts.opaque\n\t}\n}\n\n/** Always true. */\nexport class True extends Base {\n\treadonly _tag = \"True\"\n\tconstructor() {\n\t\tsuper({}, leaf)\n\t}\n}\n\n/** Always false. */\nexport class False extends Base {\n\treadonly _tag = \"False\"\n\tconstructor() {\n\t\tsuper({}, leaf)\n\t}\n}\n\n/** Compares a field with a value. */\nexport class Compare extends Base {\n\treadonly _tag = \"Compare\"\n\tdeclare readonly op: CompareOperator\n\tdeclare readonly field: string\n\tdeclare readonly value: Operand\n\tconstructor(props: {\n\t\treadonly op: CompareOperator\n\t\treadonly field: string\n\t\treadonly value: Operand\n\t}) {\n\t\tif (!isCompareOperator(props.op)) {\n\t\t\tthrow new Error(`\"${String(props.op)}\" is not a compare operator`)\n\t\t}\n\t\tsuper(\n\t\t\t{\n\t\t\t\top: props.op,\n\t\t\t\tfield: checkPath(props.field),\n\t\t\t\tvalue: checkOperand(props.value),\n\t\t\t},\n\t\t\tleaf,\n\t\t)\n\t}\n}\n\n/** True when a field is equal to one of the values. */\nexport class In extends Base {\n\treadonly _tag = \"In\"\n\tdeclare readonly field: string\n\tdeclare readonly values: ReadonlyArray<Operand>\n\tconstructor(props: {\n\t\treadonly field: string\n\t\treadonly values: ReadonlyArray<Operand>\n\t}) {\n\t\tconst values = checkOperands(props.values)\n\t\tsuper({ field: checkPath(props.field), values }, valuesFacts(values))\n\t}\n}\n\n/** True when a field is not `null` and not missing. */\nexport class Exists extends Base {\n\treadonly _tag = \"Exists\"\n\tdeclare readonly field: string\n\tconstructor(props: { readonly field: string }) {\n\t\tsuper({ field: checkPath(props.field) }, leaf)\n\t}\n}\n\n/** True when the path of the resource starts with the prefix. */\nexport class Under extends Base {\n\treadonly _tag = \"Under\"\n\tdeclare readonly prefix: ReadonlyArray<Operand>\n\tconstructor(props: { readonly prefix: ReadonlyArray<Operand> }) {\n\t\tconst prefix = checkOperands(props.prefix)\n\t\tsuper({ prefix }, valuesFacts(prefix))\n\t}\n}\n\nconst checkChildren = (conditions: unknown): ReadonlyArray<Node> => {\n\tif (!Array.isArray(conditions)) {\n\t\tthrow new Error(\"The children of a node must be an array\")\n\t}\n\tconst children = Array.from(conditions as ReadonlyArray<unknown>)\n\tif (!Arr.every(children, isNode)) {\n\t\tthrow notNode()\n\t}\n\treturn Object.freeze(children)\n}\n\nexport class And extends Base {\n\treadonly _tag = \"And\"\n\tdeclare readonly conditions: ReadonlyArray<Node>\n\tconstructor(conditions: ReadonlyArray<Node>) {\n\t\tconst children = checkChildren(conditions)\n\t\tsuper({ conditions: children }, factsOf(children))\n\t}\n}\n\nexport class Or extends Base {\n\treadonly _tag = \"Or\"\n\tdeclare readonly conditions: ReadonlyArray<Node>\n\tconstructor(conditions: ReadonlyArray<Node>) {\n\t\tconst children = checkChildren(conditions)\n\t\tsuper({ conditions: children }, factsOf(children))\n\t}\n}\n\nexport class Not extends Base {\n\treadonly _tag = \"Not\"\n\tdeclare readonly condition: Node\n\tconstructor(condition: Node) {\n\t\tif (!isNode(condition)) {\n\t\t\tthrow notNode()\n\t\t}\n\t\tsuper({ condition }, factsOf([condition]))\n\t}\n}\n\n/** The test of a predicate, with no types. */\nexport type Run = (\n\tvalue: unknown,\n\tprincipal: unknown,\n) => Effect.Effect<boolean, unknown, unknown>\n\n/**\n * A test that runs an `Effect`. Its test takes inputs of the type `never`, so\n * no code can call it with a value of the wrong type: `Condition.evaluate`\n * checks the input. Use {@link runOf} inside the library.\n */\nexport class Predicate extends Base {\n\treadonly _tag = \"Predicate\"\n\tdeclare readonly name: string\n\tdeclare readonly run: (\n\t\tvalue: never,\n\t\tprincipal: never,\n\t) => Effect.Effect<boolean, unknown, unknown>\n\tconstructor(props: { readonly name: string; readonly run: Run }) {\n\t\tif (typeof props.name !== \"string\" || typeof props.run !== \"function\") {\n\t\t\tthrow new Error(\"A predicate needs a name and a test\")\n\t\t}\n\t\tsuper(\n\t\t\t{ name: props.name, run: props.run },\n\t\t\t{ size: 1, weight: 1, depth: 1, opaque: true },\n\t\t)\n\t}\n}\n\n/** Gives the test of a predicate, with no types. */\nexport const runOf = (node: Predicate): Run => node.run as Run\n\n/** A node of a condition. */\nexport type Node =\n\tTrue | False | Compare | In | Exists | Under | And | Or | Not | Predicate\n\nconst operandJson = (operand: Operand): unknown =>\n\toperand._tag === \"Literal\"\n\t\t? {\n\t\t\t\t_tag: \"Literal\",\n\t\t\t\t// JSON has no `Infinity`, `-Infinity` or `NaN`. Such a number\n\t\t\t\t// becomes an object that the decoder does not take.\n\t\t\t\tvalue:\n\t\t\t\t\ttypeof operand.value === \"number\" && !Number.isFinite(operand.value)\n\t\t\t\t\t\t? { nonFinite: String(operand.value) }\n\t\t\t\t\t\t: operand.value,\n\t\t\t}\n\t\t: { _tag: \"Principal\", path: operand.path }\n\n/** Gives the JSON form of a condition with no predicate. */\nexport const toJson = (node: Node): unknown => {\n\tswitch (node._tag) {\n\t\tcase \"True\":\n\t\tcase \"False\":\n\t\t\treturn { _tag: node._tag }\n\t\tcase \"Compare\":\n\t\t\treturn {\n\t\t\t\t_tag: \"Compare\",\n\t\t\t\top: node.op,\n\t\t\t\tfield: node.field,\n\t\t\t\tvalue: operandJson(node.value),\n\t\t\t}\n\t\tcase \"In\":\n\t\t\treturn {\n\t\t\t\t_tag: \"In\",\n\t\t\t\tfield: node.field,\n\t\t\t\tvalues: Arr.map(node.values, operandJson),\n\t\t\t}\n\t\tcase \"Exists\":\n\t\t\treturn { _tag: \"Exists\", field: node.field }\n\t\tcase \"Under\":\n\t\t\treturn { _tag: \"Under\", prefix: Arr.map(node.prefix, operandJson) }\n\t\tcase \"And\":\n\t\tcase \"Or\":\n\t\t\treturn { _tag: node._tag, conditions: Arr.map(node.conditions, toJson) }\n\t\tcase \"Not\":\n\t\t\treturn { _tag: \"Not\", condition: toJson(node.condition) }\n\t\tcase \"Predicate\":\n\t\t\tthrow new Error(\n\t\t\t\t`The predicate \"${node.name}\" can not become JSON. A condition with a predicate has no JSON form.`,\n\t\t\t)\n\t}\n}\n\n/** Tests if a value is a node. Only a constructor of a node makes one. */\nexport const isNode = (u: unknown): u is Node => u instanceof Base\n\nconst notNode = (): Error =>\n\tnew Error(\n\t\t\"A condition must be made with a builder or a function of Condition. A plain object is not a condition\",\n\t)\n\n/** Gives a value as a node, or throws when it is not a node. */\nexport const toNode = (u: unknown): Node => {\n\tif (!isNode(u)) {\n\t\tthrow notNode()\n\t}\n\treturn u\n}\n\nexport const always: Node = new True()\n\nexport const never: Node = new False()\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA8BA,MAAa,kBAAkB,OAAO,SAAS;CAC9C;CACA;CACA;CACA;CACA;CACA;AACD,CAAC;;;;;;AASD,MAAa,qBAAqB,MAChC,gBAAgB,SAAoC,SAAS,CAAC;AAgBhE,MAAM,WAAW,KAAK,WAAoB;AAE1C,MAAa,WAAW,UAA2B,SAAS,QAAQ,EAAE,MAAM,CAAC;AAE7E,MAAa,gBAAgB,SAC5B,SAAS,UAAU,EAAE,KAAK,CAAC;;AAG5B,MAAa,YAAY,UACxB,UAAU,QACV,OAAO,UAAU,YACjB,OAAO,UAAU,YACjB,OAAO,UAAU;;;;;AAMlB,MAAM,aAAa,SAA0B;CAC5C,IAAI,OAAO,SAAS,UACnB,MAAM,IAAI,MAAM,+BAA+B;CAEhD,MAAM,QAAQ,KAAK,MAAM,KAAK,CAAyB,CAAC,CAAC;CACzD,IAAI,QAAQ,GACX,MAAMA,SACL,gBACA,kBACA,SACA,OACA,CACD;CAED,OAAO;AACR;;AAGA,MAAM,gBAAgB,YAA8B;CACnD,IAAI,YAAY,SAAS,MAAM,GAAG;EACjC,IACC,QAAQ,SAAS,aACjB,YAAY,SAAS,OAAO,KAC5B,SAAS,QAAQ,KAAK,GAEtB,OAAO,QAAQ,QAAQ,KAAK;EAE7B,IAAI,QAAQ,SAAS,eAAe,YAAY,SAAS,MAAM,GAC9D,OAAO,aAAa,UAAU,QAAQ,IAAI,CAAC;CAE7C;CACA,MAAM,IAAI,MACT,qEACD;AACD;AAEA,MAAM,iBAAiB,WAA4C;CAClE,IAAI,CAAC,MAAM,QAAQ,MAAM,GACxB,MAAM,IAAI,MAAM,qCAAqC;CAEtD,OAAO,OAAO,OAAO,IAAI,IAAI,MAAM,KAAK,MAAM,GAAG,YAAY,CAAC;AAC/D;AASA,MAAM,OAAc;CAAE,MAAM;CAAG,QAAQ;CAAG,OAAO;CAAG,QAAQ;AAAM;;;;;;AAOlE,MAAM,eAAe,WACpB,YAAY;CAAE,GAAG;CAAM,QAAQ,IAAI,OAAO;AAAO,CAAC;AAEnD,MAAM,eAAe,UAAwB;CAC5C,IAAI,MAAM,SAAS,KAClB,MAAMA,SACL,eACA,kBACA,oBACA,MAAM,QACNC,cACD;CAED,OAAO;AACR;;AAGA,MAAM,WAAW,aAAyC;CACzD,MAAM,OAAO,IAAI,OAAO,UAAU,IAAI,OAAO,UAAU,QAAQ,MAAM,IAAI;CACzE,MAAM,SAAS,IAAI,OAAO,UAAU,IAAI,OAAO,UAAU,QAAQ,MAAM,MAAM;CAC7E,YAAY;EAAE,GAAG;EAAM;CAAO,CAAC;CAC/B,MAAM,QACL,IAAI,IAAI,OAAO,UAAU,IAAI,KAAK,UAAU,KAAK,IAAI,KAAK,MAAM,KAAK,CAAC;CACvE,IAAI,QAAQ,KACX,MAAMD,SACL,eACA,kBACA,UACA,OACA,GACD;CAED,OAAO;EACN;EACA;EACA;EACA,QAAQ,IAAI,KAAK,WAAW,UAAU,MAAM,MAAM;CACnD;AACD;;AAGA,IAAe,OAAf,cAA4B,KAAK,MAAM;;;;;;CAMtC,SAAkB;EACjB,OAAO,OAAO,IAAuB;CACtC;CAEA;CAEA,YAAY,OAAe,OAAc;EACxC,MAAM,KAAc;EACpB,KAAK,SAAS;CACf;;CAGA,IAAI,OAAe;EAClB,OAAO,KAAK,OAAO;CACpB;;;;;;CAOA,IAAI,SAAiB;EACpB,OAAO,KAAK,OAAO;CACpB;;CAGA,IAAI,QAAgB;EACnB,OAAO,KAAK,OAAO;CACpB;;CAGA,IAAI,SAAkB;EACrB,OAAO,KAAK,OAAO;CACpB;AACD;;AAGA,IAAa,OAAb,cAA0B,KAAK;CAC9B,OAAgB;CAChB,cAAc;EACb,MAAM,CAAC,GAAG,IAAI;CACf;AACD;;AAGA,IAAa,QAAb,cAA2B,KAAK;CAC/B,OAAgB;CAChB,cAAc;EACb,MAAM,CAAC,GAAG,IAAI;CACf;AACD;;AAGA,IAAa,UAAb,cAA6B,KAAK;CACjC,OAAgB;CAIhB,YAAY,OAIT;EACF,IAAI,CAAC,kBAAkB,MAAM,EAAE,GAC9B,MAAM,IAAI,MAAM,IAAI,OAAO,MAAM,EAAE,EAAE,4BAA4B;EAElE,MACC;GACC,IAAI,MAAM;GACV,OAAO,UAAU,MAAM,KAAK;GAC5B,OAAO,aAAa,MAAM,KAAK;EAChC,GACA,IACD;CACD;AACD;;AAGA,IAAa,KAAb,cAAwB,KAAK;CAC5B,OAAgB;CAGhB,YAAY,OAGT;EACF,MAAM,SAAS,cAAc,MAAM,MAAM;EACzC,MAAM;GAAE,OAAO,UAAU,MAAM,KAAK;GAAG;EAAO,GAAG,YAAY,MAAM,CAAC;CACrE;AACD;;AAGA,IAAa,SAAb,cAA4B,KAAK;CAChC,OAAgB;CAEhB,YAAY,OAAmC;EAC9C,MAAM,EAAE,OAAO,UAAU,MAAM,KAAK,EAAE,GAAG,IAAI;CAC9C;AACD;;AAGA,IAAa,QAAb,cAA2B,KAAK;CAC/B,OAAgB;CAEhB,YAAY,OAAoD;EAC/D,MAAM,SAAS,cAAc,MAAM,MAAM;EACzC,MAAM,EAAE,OAAO,GAAG,YAAY,MAAM,CAAC;CACtC;AACD;AAEA,MAAM,iBAAiB,eAA6C;CACnE,IAAI,CAAC,MAAM,QAAQ,UAAU,GAC5B,MAAM,IAAI,MAAM,yCAAyC;CAE1D,MAAM,WAAW,MAAM,KAAK,UAAoC;CAChE,IAAI,CAAC,IAAI,MAAM,UAAU,MAAM,GAC9B,MAAM,QAAQ;CAEf,OAAO,OAAO,OAAO,QAAQ;AAC9B;AAEA,IAAa,MAAb,cAAyB,KAAK;CAC7B,OAAgB;CAEhB,YAAY,YAAiC;EAC5C,MAAM,WAAW,cAAc,UAAU;EACzC,MAAM,EAAE,YAAY,SAAS,GAAG,QAAQ,QAAQ,CAAC;CAClD;AACD;AAEA,IAAa,KAAb,cAAwB,KAAK;CAC5B,OAAgB;CAEhB,YAAY,YAAiC;EAC5C,MAAM,WAAW,cAAc,UAAU;EACzC,MAAM,EAAE,YAAY,SAAS,GAAG,QAAQ,QAAQ,CAAC;CAClD;AACD;AAEA,IAAa,MAAb,cAAyB,KAAK;CAC7B,OAAgB;CAEhB,YAAY,WAAiB;EAC5B,IAAI,CAAC,OAAO,SAAS,GACpB,MAAM,QAAQ;EAEf,MAAM,EAAE,UAAU,GAAG,QAAQ,CAAC,SAAS,CAAC,CAAC;CAC1C;AACD;;;;;;AAaA,IAAa,YAAb,cAA+B,KAAK;CACnC,OAAgB;CAMhB,YAAY,OAAqD;EAChE,IAAI,OAAO,MAAM,SAAS,YAAY,OAAO,MAAM,QAAQ,YAC1D,MAAM,IAAI,MAAM,qCAAqC;EAEtD,MACC;GAAE,MAAM,MAAM;GAAM,KAAK,MAAM;EAAI,GACnC;GAAE,MAAM;GAAG,QAAQ;GAAG,OAAO;GAAG,QAAQ;EAAK,CAC9C;CACD;AACD;;AAGA,MAAa,SAAS,SAAyB,KAAK;AAMpD,MAAM,eAAe,YACpB,QAAQ,SAAS,YACd;CACA,MAAM;CAGN,OACC,OAAO,QAAQ,UAAU,YAAY,CAAC,OAAO,SAAS,QAAQ,KAAK,IAChE,EAAE,WAAW,OAAO,QAAQ,KAAK,EAAE,IACnC,QAAQ;AACb,IACC;CAAE,MAAM;CAAa,MAAM,QAAQ;AAAK;;AAG5C,MAAa,UAAU,SAAwB;CAC9C,QAAQ,KAAK,MAAb;EACC,KAAK;EACL,KAAK,SACJ,OAAO,EAAE,MAAM,KAAK,KAAK;EAC1B,KAAK,WACJ,OAAO;GACN,MAAM;GACN,IAAI,KAAK;GACT,OAAO,KAAK;GACZ,OAAO,YAAY,KAAK,KAAK;EAC9B;EACD,KAAK,MACJ,OAAO;GACN,MAAM;GACN,OAAO,KAAK;GACZ,QAAQ,IAAI,IAAI,KAAK,QAAQ,WAAW;EACzC;EACD,KAAK,UACJ,OAAO;GAAE,MAAM;GAAU,OAAO,KAAK;EAAM;EAC5C,KAAK,SACJ,OAAO;GAAE,MAAM;GAAS,QAAQ,IAAI,IAAI,KAAK,QAAQ,WAAW;EAAE;EACnE,KAAK;EACL,KAAK,MACJ,OAAO;GAAE,MAAM,KAAK;GAAM,YAAY,IAAI,IAAI,KAAK,YAAY,MAAM;EAAE;EACxE,KAAK,OACJ,OAAO;GAAE,MAAM;GAAO,WAAW,OAAO,KAAK,SAAS;EAAE;EACzD,KAAK,aACJ,MAAM,IAAI,MACT,kBAAkB,KAAK,KAAK,sEAC7B;CACF;AACD;;AAGA,MAAa,UAAU,MAA0B,aAAa;AAE9D,MAAM,gCACL,IAAI,MACH,uGACD;;AAGD,MAAa,UAAU,MAAqB;CAC3C,IAAI,CAAC,OAAO,CAAC,GACZ,MAAM,QAAQ;CAEf,OAAO;AACR;AAEA,MAAa,SAAe,IAAI,KAAK;AAErC,MAAa,QAAc,IAAI,MAAM"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"policy-nodes.d.ts","names":["Rule.Rule"],"sources":["../../src/internal/policy-nodes.ts"],"mappings":";;;;qBAmBa,WAAS,OAAA;YAOV,mBAAmB,UAAU;UAS/B;WACA;WACA;WACA;;;uBAmCK,aAAa,KAAK;;EAGhC,YAAY,eAAe,OAAO;;MAM9B;;MAKA;;MAKA;;;qBAaQ,cAAc;WACjB;WACQ,WAAW;WACX,OAAO,cAAcA;EACtC,YAAY;aACF,WAAW;aACX,OAAO,cAAcA;;;;
|
|
1
|
+
{"version":3,"file":"policy-nodes.d.ts","names":["Rule.Rule"],"sources":["../../src/internal/policy-nodes.ts"],"mappings":";;;;qBAmBa,WAAS,OAAA;YAOV,mBAAmB,UAAU;UAS/B;WACA;WACA;WACA;;;uBAmCK,aAAa,KAAK;;EAGhC,YAAY,eAAe,OAAO;;MAM9B;;MAKA;;MAKA;;;qBAaQ,cAAc;WACjB;WACQ,WAAW;WACX,OAAO,cAAcA;EACtC,YAAY;aACF,WAAW;aACX,OAAO,cAAcA;;;;qBA0CnB,YAAY;WACf;WACQ,UAAU,cAAc;EACzC,YAAY,UAAU,cAAc;;;qBAOxB,YAAY;WACf;WACQ,UAAU,cAAc;EACzC,YAAY,UAAU,cAAc;;;qBAOxB,gBAAgB;WACnB;WACQ,UAAU,cAAc;EACzC,YAAY,UAAU,cAAc;;;qBAOxB,gBAAgB;WACnB;WACQ,KAAK;WACL,QAAQ;EACzB,YAAY;aAAkB,KAAK;aAAe,QAAQ;;;;YAO/C,OAAO,QAAQ,MAAM,MAAM,UAAU"}
|
|
@@ -88,7 +88,7 @@ var Rules = class extends Base {
|
|
|
88
88
|
algorithm: props.algorithm,
|
|
89
89
|
rules
|
|
90
90
|
}, checked({
|
|
91
|
-
size: Arr.reduce(conditions, 1, (total, when) => total + 1 + (when?.
|
|
91
|
+
size: Arr.reduce(conditions, 1, (total, when) => total + 1 + (when?.weight ?? 0)),
|
|
92
92
|
depth: 1,
|
|
93
93
|
opaque: Arr.some(conditions, (when) => when?.opaque ?? false)
|
|
94
94
|
}));
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"policy-nodes.js","names":["Limits.exceeded","Limits.policyNodes","Nodes.isNode","Rule.isRule"],"sources":["../../src/internal/policy-nodes.ts"],"sourcesContent":["/**\n * The nodes of a policy tree. As with the nodes of a condition, each node is\n * a class, only its constructor makes one, and it works out three facts once:\n * - `size`: the number of nodes, with each rule and each node of its condition,\n * and a shared child counted for each place;\n * - `depth`: the number of levels of the tree;\n * - `opaque`: a rule of the tree has a predicate.\n *\n * The constructor stops a tree past `policyNodes` or `policyDepth`. So a walk\n * over any tree, such as a decision, needs no memo.\n */\nimport * as Arr from \"effect/Array\"\nimport * as Data from \"effect/Data\"\nimport * as Schema from \"effect/Schema\"\nimport * as Rule from \"./rule.ts\"\nimport * as Limits from \"./limits.ts\"\nimport * as Nodes from \"./nodes.ts\"\n\n/** The combining algorithms of a list of rules. The decoder reads this list too. */\nexport const Algorithm = Schema.Literals([\n\t\"denyOverrides\",\n\t\"permitOverrides\",\n\t\"firstApplicable\",\n\t\"lastApplicable\",\n])\n\nexport type Algorithm = typeof Algorithm.Type\n\n/**\n * Tests if a value is a combining algorithm. It reads the list of the Schema\n * with `includes`, as `Nodes.isCompareOperator` does.\n */\nexport const isAlgorithm = (u: unknown): u is Algorithm =>\n\t(Algorithm.literals as ReadonlyArray<unknown>).includes(u)\n\ninterface Facts {\n\treadonly size: number\n\treadonly depth: number\n\treadonly opaque: boolean\n}\n\n/** Checks the facts of a node against the limits. */\nconst checked = (facts: Facts): Facts => {\n\tif (facts.size > Limits.policyNodes) {\n\t\tthrow Limits.exceeded(\n\t\t\t\"A policy tree\",\n\t\t\t\"policyNodes\",\n\t\t\t\"nodes\",\n\t\t\tfacts.size,\n\t\t\tLimits.policyNodes,\n\t\t)\n\t}\n\tif (facts.depth > Limits.policyDepth) {\n\t\tthrow Limits.exceeded(\n\t\t\t\"A policy tree\",\n\t\t\t\"policyDepth\",\n\t\t\t\"levels\",\n\t\t\tfacts.depth,\n\t\t\tLimits.policyDepth,\n\t\t)\n\t}\n\treturn facts\n}\n\nconst factsOf = (children: ReadonlyArray<Node>): Facts =>\n\tchecked({\n\t\tsize: Arr.reduce(children, 1, (total, child) => total + child.size),\n\t\tdepth:\n\t\t\t1 + Arr.reduce(children, 0, (top, child) => Math.max(top, child.depth)),\n\t\topaque: Arr.some(children, (child) => child.opaque),\n\t})\n\n/** The base of every node. It keeps the facts. */\nabstract class Base extends Data.Class {\n\treadonly #facts: Facts\n\n\tconstructor(props: object, facts: Facts) {\n\t\tsuper(props as never)\n\t\tthis.#facts = facts\n\t}\n\n\t/** The number of nodes, with the rules and their conditions. */\n\tget size(): number {\n\t\treturn this.#facts.size\n\t}\n\n\t/** The number of levels of the tree. */\n\tget depth(): number {\n\t\treturn this.#facts.depth\n\t}\n\n\t/** A rule of the tree has a predicate. */\n\tget opaque(): boolean {\n\t\treturn this.#facts.opaque\n\t}\n}\n\n/**\n * The condition of a rule. A value that is not a node counts as one node\n * here: the binding of the rule gives the issue for it, before any walk.\n */\nconst whenOf = (rule: Rule.Rule): Nodes.Node | undefined =>\n\tNodes.isNode(rule.when) ? rule.when : undefined\n\n/** A list of rules with a combining algorithm. */\nexport class Rules extends Base {\n\treadonly _tag = \"Rules\"\n\tdeclare readonly algorithm: Algorithm\n\tdeclare readonly rules: ReadonlyArray<Rule.Rule>\n\tconstructor(props: {\n\t\treadonly algorithm: Algorithm\n\t\treadonly rules: ReadonlyArray<Rule.Rule>\n\t}) {\n\t\tif (!isAlgorithm(props.algorithm)) {\n\t\t\tthrow new Error(\n\t\t\t\t`\"${String(props.algorithm)}\" is not a combining algorithm`,\n\t\t\t)\n\t\t}\n\t\tconst rules = Object.freeze(Array.from(props.rules))\n\t\t// A value that is not a rule, such as a copy of a rule made with a\n\t\t// spread, may keep the type fields of another rule, and it does not\n\t\t// work in a generator. It is a mistake in the code, so it is a defect.\n\t\tif (!Arr.every(rules, Rule.isRule)) {\n\t\t\tthrow new Error(\"A value in the list is not a rule\")\n\t\t}\n\t\tconst conditions = Arr.map(rules, whenOf)\n\t\tsuper(\n\t\t\t{ algorithm: props.algorithm, rules },\n\t\t\tchecked({\n\t\t\t\tsize: Arr.reduce(\n\t\t\t\t\tconditions,\n\t\t\t\t\t1,\n\t\t\t\t\t(total, when) => total + 1 + (when?.
|
|
1
|
+
{"version":3,"file":"policy-nodes.js","names":["Limits.exceeded","Limits.policyNodes","Nodes.isNode","Rule.isRule"],"sources":["../../src/internal/policy-nodes.ts"],"sourcesContent":["/**\n * The nodes of a policy tree. As with the nodes of a condition, each node is\n * a class, only its constructor makes one, and it works out three facts once:\n * - `size`: the number of nodes, with each rule and each node of its condition,\n * and a shared child counted for each place;\n * - `depth`: the number of levels of the tree;\n * - `opaque`: a rule of the tree has a predicate.\n *\n * The constructor stops a tree past `policyNodes` or `policyDepth`. So a walk\n * over any tree, such as a decision, needs no memo.\n */\nimport * as Arr from \"effect/Array\"\nimport * as Data from \"effect/Data\"\nimport * as Schema from \"effect/Schema\"\nimport * as Rule from \"./rule.ts\"\nimport * as Limits from \"./limits.ts\"\nimport * as Nodes from \"./nodes.ts\"\n\n/** The combining algorithms of a list of rules. The decoder reads this list too. */\nexport const Algorithm = Schema.Literals([\n\t\"denyOverrides\",\n\t\"permitOverrides\",\n\t\"firstApplicable\",\n\t\"lastApplicable\",\n])\n\nexport type Algorithm = typeof Algorithm.Type\n\n/**\n * Tests if a value is a combining algorithm. It reads the list of the Schema\n * with `includes`, as `Nodes.isCompareOperator` does.\n */\nexport const isAlgorithm = (u: unknown): u is Algorithm =>\n\t(Algorithm.literals as ReadonlyArray<unknown>).includes(u)\n\ninterface Facts {\n\treadonly size: number\n\treadonly depth: number\n\treadonly opaque: boolean\n}\n\n/** Checks the facts of a node against the limits. */\nconst checked = (facts: Facts): Facts => {\n\tif (facts.size > Limits.policyNodes) {\n\t\tthrow Limits.exceeded(\n\t\t\t\"A policy tree\",\n\t\t\t\"policyNodes\",\n\t\t\t\"nodes\",\n\t\t\tfacts.size,\n\t\t\tLimits.policyNodes,\n\t\t)\n\t}\n\tif (facts.depth > Limits.policyDepth) {\n\t\tthrow Limits.exceeded(\n\t\t\t\"A policy tree\",\n\t\t\t\"policyDepth\",\n\t\t\t\"levels\",\n\t\t\tfacts.depth,\n\t\t\tLimits.policyDepth,\n\t\t)\n\t}\n\treturn facts\n}\n\nconst factsOf = (children: ReadonlyArray<Node>): Facts =>\n\tchecked({\n\t\tsize: Arr.reduce(children, 1, (total, child) => total + child.size),\n\t\tdepth:\n\t\t\t1 + Arr.reduce(children, 0, (top, child) => Math.max(top, child.depth)),\n\t\topaque: Arr.some(children, (child) => child.opaque),\n\t})\n\n/** The base of every node. It keeps the facts. */\nabstract class Base extends Data.Class {\n\treadonly #facts: Facts\n\n\tconstructor(props: object, facts: Facts) {\n\t\tsuper(props as never)\n\t\tthis.#facts = facts\n\t}\n\n\t/** The number of nodes, with the rules and their conditions. */\n\tget size(): number {\n\t\treturn this.#facts.size\n\t}\n\n\t/** The number of levels of the tree. */\n\tget depth(): number {\n\t\treturn this.#facts.depth\n\t}\n\n\t/** A rule of the tree has a predicate. */\n\tget opaque(): boolean {\n\t\treturn this.#facts.opaque\n\t}\n}\n\n/**\n * The condition of a rule. A value that is not a node counts as one node\n * here: the binding of the rule gives the issue for it, before any walk.\n */\nconst whenOf = (rule: Rule.Rule): Nodes.Node | undefined =>\n\tNodes.isNode(rule.when) ? rule.when : undefined\n\n/** A list of rules with a combining algorithm. */\nexport class Rules extends Base {\n\treadonly _tag = \"Rules\"\n\tdeclare readonly algorithm: Algorithm\n\tdeclare readonly rules: ReadonlyArray<Rule.Rule>\n\tconstructor(props: {\n\t\treadonly algorithm: Algorithm\n\t\treadonly rules: ReadonlyArray<Rule.Rule>\n\t}) {\n\t\tif (!isAlgorithm(props.algorithm)) {\n\t\t\tthrow new Error(\n\t\t\t\t`\"${String(props.algorithm)}\" is not a combining algorithm`,\n\t\t\t)\n\t\t}\n\t\tconst rules = Object.freeze(Array.from(props.rules))\n\t\t// A value that is not a rule, such as a copy of a rule made with a\n\t\t// spread, may keep the type fields of another rule, and it does not\n\t\t// work in a generator. It is a mistake in the code, so it is a defect.\n\t\tif (!Arr.every(rules, Rule.isRule)) {\n\t\t\tthrow new Error(\"A value in the list is not a rule\")\n\t\t}\n\t\tconst conditions = Arr.map(rules, whenOf)\n\t\tsuper(\n\t\t\t{ algorithm: props.algorithm, rules },\n\t\t\tchecked({\n\t\t\t\tsize: Arr.reduce(\n\t\t\t\t\tconditions,\n\t\t\t\t\t1,\n\t\t\t\t\t// 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 = (node: Node): ReadonlyArray<Rule.Rule> => {\n\tswitch (node._tag) {\n\t\tcase \"Rules\":\n\t\t\treturn node.rules\n\t\tcase \"All\":\n\t\tcase \"Any\":\n\t\tcase \"FirstOf\":\n\t\t\treturn Arr.flatMap(node.policies, rulesOf)\n\t\tcase \"Ceiling\":\n\t\t\treturn [...rulesOf(node.cap), ...rulesOf(node.policy)]\n\t}\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import * as Effect from "effect/Effect";
|
|
2
|
+
import * as Arr from "effect/Array";
|
|
3
|
+
import * as Option from "effect/Option";
|
|
4
|
+
import * as Cause from "effect/Cause";
|
|
5
|
+
import * as Exit from "effect/Exit";
|
|
6
|
+
// src/internal/trampoline.ts
|
|
7
|
+
/**
|
|
8
|
+
* Walks that do not fill the stack.
|
|
9
|
+
*
|
|
10
|
+
* A walk is written one time, with the steps of a {@link Runner}, and a
|
|
11
|
+
* runner runs it:
|
|
12
|
+
* - {@link direct} runs each step at once, as a plain call does. It is fast,
|
|
13
|
+
* and it is for an input with few levels.
|
|
14
|
+
* - {@link loop} puts each step into the run loop of an Effect. A step gives
|
|
15
|
+
* the next step in place of calling it, and the loop runs one step after
|
|
16
|
+
* the other, so the stack does not grow with the levels of the input. This
|
|
17
|
+
* is a trampoline.
|
|
18
|
+
*
|
|
19
|
+
* {@link run} takes `direct` for an input with at most `directDepth` levels,
|
|
20
|
+
* and `loop` for a deeper one. A walk makes each call for a child through
|
|
21
|
+
* `suspend`, so the loop makes the step of the child only when it runs it.
|
|
22
|
+
*/
|
|
23
|
+
const value = (step) => step;
|
|
24
|
+
const step = (value) => value;
|
|
25
|
+
/** Runs each step at once, on the stack. */
|
|
26
|
+
const direct = {
|
|
27
|
+
succeed: step,
|
|
28
|
+
suspend: (make) => make(),
|
|
29
|
+
map: (from, f) => step(f(value(from))),
|
|
30
|
+
flatMap: (from, f) => f(value(from)),
|
|
31
|
+
forEach: (items, f) => step(Arr.map(items, (item, index) => value(f(item, index)))),
|
|
32
|
+
some: (items, f) => step(Arr.some(items, (item) => value(f(item)))),
|
|
33
|
+
every: (items, f) => step(Arr.every(items, (item) => value(f(item))))
|
|
34
|
+
};
|
|
35
|
+
const effect = (step) => step;
|
|
36
|
+
const fromEffect = (effect) => effect;
|
|
37
|
+
/** Runs each step in the run loop of an Effect, so a deep walk does not fill the stack. */
|
|
38
|
+
const loop = {
|
|
39
|
+
succeed: (value) => fromEffect(Effect.succeed(value)),
|
|
40
|
+
suspend: (make) => fromEffect(Effect.suspend(() => effect(make()))),
|
|
41
|
+
map: (from, f) => fromEffect(Effect.map(effect(from), f)),
|
|
42
|
+
flatMap: (from, f) => fromEffect(Effect.flatMap(effect(from), (value) => effect(f(value)))),
|
|
43
|
+
forEach: (items, f) => fromEffect(Effect.forEach(items, (item, index) => effect(f(item, index)))),
|
|
44
|
+
some: (items, f) => fromEffect(Effect.map(Effect.findFirst(items, (item) => effect(f(item))), Option.isSome)),
|
|
45
|
+
every: (items, f) => fromEffect(Effect.map(Effect.findFirst(items, (item) => Effect.map(effect(f(item)), (value) => !value)), Option.isNone))
|
|
46
|
+
};
|
|
47
|
+
/**
|
|
48
|
+
* Gives the runner for an input with `depth` levels. The walks of a condition
|
|
49
|
+
* or a policy tree know the depth of the input from its node. A walk of an
|
|
50
|
+
* input with an unknown depth, as a decoder, takes `loop`.
|
|
51
|
+
*/
|
|
52
|
+
const runnerFor = (depth) => depth <= 200 ? direct : loop;
|
|
53
|
+
/**
|
|
54
|
+
* Runs a walk at once, with the runner for `depth` levels, and gives its
|
|
55
|
+
* value. An error that a step throws comes out as it was, also from `loop`.
|
|
56
|
+
*/
|
|
57
|
+
const run = (depth, walk) => runWith(runnerFor(depth), walk);
|
|
58
|
+
/** Runs a walk at once with a runner, as {@link run} does. */
|
|
59
|
+
const runWith = (runner, walk) => {
|
|
60
|
+
if (runner === direct) return value(walk(direct));
|
|
61
|
+
return Exit.match(Effect.runSyncExit(effect(walk(loop))), {
|
|
62
|
+
onSuccess: (result) => result,
|
|
63
|
+
onFailure: (cause) => {
|
|
64
|
+
throw Cause.squash(cause);
|
|
65
|
+
}
|
|
66
|
+
});
|
|
67
|
+
};
|
|
68
|
+
/**
|
|
69
|
+
* Gives the Effect of a step of `loop`, so a walk can go on in an Effect
|
|
70
|
+
* that runs a predicate.
|
|
71
|
+
*/
|
|
72
|
+
const toEffect = (step) => effect(step);
|
|
73
|
+
export { direct, loop, run, runWith, runnerFor, toEffect };
|
|
74
|
+
|
|
75
|
+
//# sourceMappingURL=trampoline.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"trampoline.js","names":[],"sources":["../../src/internal/trampoline.ts"],"sourcesContent":["/**\n * Walks that do not fill the stack.\n *\n * A walk is written one time, with the steps of a {@link Runner}, and a\n * runner runs it:\n * - {@link direct} runs each step at once, as a plain call does. It is fast,\n * and it is for an input with few levels.\n * - {@link loop} puts each step into the run loop of an Effect. A step gives\n * the next step in place of calling it, and the loop runs one step after\n * the other, so the stack does not grow with the levels of the input. This\n * is a trampoline.\n *\n * {@link run} takes `direct` for an input with at most `directDepth` levels,\n * and `loop` for a deeper one. A walk makes each call for a child through\n * `suspend`, so the loop makes the step of the child only when it runs it.\n */\nimport * as Arr from \"effect/Array\"\nimport * as Cause from \"effect/Cause\"\nimport * as Effect from \"effect/Effect\"\nimport * as Exit from \"effect/Exit\"\nimport * as Option from \"effect/Option\"\n\n/**\n * A step of a walk that gives an `A`. A runner of steps knows what a step\n * is: the value itself for `direct`, and an Effect for `loop`. The type is\n * only in the types.\n */\nexport interface Step<A> {\n\treadonly \"~Step\": (_: never) => A\n}\n\n/** The steps that a walk may take. */\nexport interface Runner {\n\treadonly succeed: <A>(value: A) => Step<A>\n\t/** The step that `step` makes, when the runner runs it. */\n\treadonly suspend: <A>(step: () => Step<A>) => Step<A>\n\treadonly map: <A, B>(step: Step<A>, f: (value: A) => B) => Step<B>\n\treadonly flatMap: <A, B>(step: Step<A>, f: (value: A) => Step<B>) => Step<B>\n\t/** Runs a step for each item, in order, and gives their values. */\n\treadonly forEach: <A, B>(\n\t\titems: ReadonlyArray<A>,\n\t\tf: (item: A, index: number) => Step<B>,\n\t) => Step<ReadonlyArray<B>>\n\t/** Runs a step for each item, in order, up to the first `true`. */\n\treadonly some: <A>(\n\t\titems: ReadonlyArray<A>,\n\t\tf: (item: A) => Step<boolean>,\n\t) => Step<boolean>\n\t/** Runs a step for each item, in order, up to the first `false`. */\n\treadonly every: <A>(\n\t\titems: ReadonlyArray<A>,\n\t\tf: (item: A) => Step<boolean>,\n\t) => Step<boolean>\n}\n\n// A step of `direct` is its value. The casts below only change the type.\nconst value = <A>(step: Step<A>): A => step as unknown as A\nconst step = <A>(value: A): Step<A> => value as unknown as Step<A>\n\n/** Runs each step at once, on the stack. */\nexport const direct: Runner = {\n\tsucceed: step,\n\tsuspend: (make) => make(),\n\tmap: (from, f) => step(f(value(from))),\n\tflatMap: (from, f) => f(value(from)),\n\tforEach: (items, f) =>\n\t\tstep(Arr.map(items, (item, index) => value(f(item, index)))),\n\tsome: (items, f) => step(Arr.some(items, (item) => value(f(item)))),\n\tevery: (items, f) => step(Arr.every(items, (item) => value(f(item)))),\n}\n\n// A step of `loop` is an Effect with no error and no services. A failure of a\n// walk is a thrown error, which `run` gives back as it was.\ntype Loop<A> = Effect.Effect<A>\nconst effect = <A>(step: Step<A>): Loop<A> => step as unknown as Loop<A>\nconst fromEffect = <A>(effect: Loop<A>): Step<A> => effect as unknown as Step<A>\n\n/** Runs each step in the run loop of an Effect, so a deep walk does not fill the stack. */\nexport const loop: Runner = {\n\tsucceed: (value) => fromEffect(Effect.succeed(value)),\n\tsuspend: (make) => fromEffect(Effect.suspend(() => effect(make()))),\n\tmap: (from, f) => fromEffect(Effect.map(effect(from), f)),\n\tflatMap: (from, f) =>\n\t\tfromEffect(Effect.flatMap(effect(from), (value) => effect(f(value)))),\n\tforEach: (items, f) =>\n\t\tfromEffect(Effect.forEach(items, (item, index) => effect(f(item, index)))),\n\tsome: (items, f) =>\n\t\tfromEffect(\n\t\t\tEffect.map(\n\t\t\t\tEffect.findFirst(items, (item) => effect(f(item))),\n\t\t\t\tOption.isSome,\n\t\t\t),\n\t\t),\n\tevery: (items, f) =>\n\t\tfromEffect(\n\t\t\tEffect.map(\n\t\t\t\tEffect.findFirst(items, (item) =>\n\t\t\t\t\tEffect.map(effect(f(item)), (value) => !value),\n\t\t\t\t),\n\t\t\t\tOption.isNone,\n\t\t\t),\n\t\t),\n}\n\n/**\n * The most levels of an input that `run` walks with `direct`. A level takes\n * a few calls of the walk and of the runner on the stack, so this keeps the\n * stack far from full, and most inputs take the fast way.\n */\nexport const directDepth = 200\n\n/**\n * Gives the runner for an input with `depth` levels. The walks of a condition\n * or a policy tree know the depth of the input from its node. A walk of an\n * input with an unknown depth, as a decoder, takes `loop`.\n */\nexport const runnerFor = (depth: number): Runner =>\n\tdepth <= directDepth ? direct : loop\n\n/**\n * Runs a walk at once, with the runner for `depth` levels, and gives its\n * value. An error that a step throws comes out as it was, also from `loop`.\n */\nexport const run = <A>(depth: number, walk: (runner: Runner) => Step<A>): A =>\n\trunWith(runnerFor(depth), walk)\n\n/** Runs a walk at once with a runner, as {@link run} does. */\nexport const runWith = <A>(\n\trunner: Runner,\n\twalk: (runner: Runner) => Step<A>,\n): A => {\n\tif (runner === direct) {\n\t\treturn value(walk(direct))\n\t}\n\treturn Exit.match(Effect.runSyncExit(effect(walk(loop))), {\n\t\tonSuccess: (result) => result,\n\t\tonFailure: (cause) => {\n\t\t\tthrow Cause.squash(cause)\n\t\t},\n\t})\n}\n\n/**\n * Gives the Effect of a step of `loop`, so a walk can go on in an Effect\n * that runs a predicate.\n */\nexport const toEffect = <A>(step: Step<A>): Effect.Effect<A> => effect(step)\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;AAwDA,MAAM,SAAY,SAAqB;AACvC,MAAM,QAAW,UAAsB;;AAGvC,MAAa,SAAiB;CAC7B,SAAS;CACT,UAAU,SAAS,KAAK;CACxB,MAAM,MAAM,MAAM,KAAK,EAAE,MAAM,IAAI,CAAC,CAAC;CACrC,UAAU,MAAM,MAAM,EAAE,MAAM,IAAI,CAAC;CACnC,UAAU,OAAO,MAChB,KAAK,IAAI,IAAI,QAAQ,MAAM,UAAU,MAAM,EAAE,MAAM,KAAK,CAAC,CAAC,CAAC;CAC5D,OAAO,OAAO,MAAM,KAAK,IAAI,KAAK,QAAQ,SAAS,MAAM,EAAE,IAAI,CAAC,CAAC,CAAC;CAClE,QAAQ,OAAO,MAAM,KAAK,IAAI,MAAM,QAAQ,SAAS,MAAM,EAAE,IAAI,CAAC,CAAC,CAAC;AACrE;AAKA,MAAM,UAAa,SAA2B;AAC9C,MAAM,cAAiB,WAA6B;;AAGpD,MAAa,OAAe;CAC3B,UAAU,UAAU,WAAW,OAAO,QAAQ,KAAK,CAAC;CACpD,UAAU,SAAS,WAAW,OAAO,cAAc,OAAO,KAAK,CAAC,CAAC,CAAC;CAClE,MAAM,MAAM,MAAM,WAAW,OAAO,IAAI,OAAO,IAAI,GAAG,CAAC,CAAC;CACxD,UAAU,MAAM,MACf,WAAW,OAAO,QAAQ,OAAO,IAAI,IAAI,UAAU,OAAO,EAAE,KAAK,CAAC,CAAC,CAAC;CACrE,UAAU,OAAO,MAChB,WAAW,OAAO,QAAQ,QAAQ,MAAM,UAAU,OAAO,EAAE,MAAM,KAAK,CAAC,CAAC,CAAC;CAC1E,OAAO,OAAO,MACb,WACC,OAAO,IACN,OAAO,UAAU,QAAQ,SAAS,OAAO,EAAE,IAAI,CAAC,CAAC,GACjD,OAAO,MACR,CACD;CACD,QAAQ,OAAO,MACd,WACC,OAAO,IACN,OAAO,UAAU,QAAQ,SACxB,OAAO,IAAI,OAAO,EAAE,IAAI,CAAC,IAAI,UAAU,CAAC,KAAK,CAC9C,GACA,OAAO,MACR,CACD;AACF;;;;;;AAcA,MAAa,aAAa,UACzB,SAAA,MAAuB,SAAS;;;;;AAMjC,MAAa,OAAU,OAAe,SACrC,QAAQ,UAAU,KAAK,GAAG,IAAI;;AAG/B,MAAa,WACZ,QACA,SACO;CACP,IAAI,WAAW,QACd,OAAO,MAAM,KAAK,MAAM,CAAC;CAE1B,OAAO,KAAK,MAAM,OAAO,YAAY,OAAO,KAAK,IAAI,CAAC,CAAC,GAAG;EACzD,YAAY,WAAW;EACvB,YAAY,UAAU;GACrB,MAAM,MAAM,OAAO,KAAK;EACzB;CACD,CAAC;AACF;;;;;AAMA,MAAa,YAAe,SAAoC,OAAO,IAAI"}
|