@nmnmcc/ability 2.0.7 → 2.1.2
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 +52 -29
- package/dist/Authorizer.d.ts +21 -12
- package/dist/Authorizer.d.ts.map +1 -1
- package/dist/Authorizer.js.map +1 -1
- package/dist/Condition.d.ts +121 -19
- package/dist/Condition.d.ts.map +1 -1
- package/dist/Condition.js +32 -21
- package/dist/Condition.js.map +1 -1
- package/dist/Domain.d.ts +9 -0
- package/dist/Domain.d.ts.map +1 -1
- package/dist/Domain.js +5 -0
- package/dist/Domain.js.map +1 -1
- package/dist/Limits.d.ts +44 -13
- package/dist/Limits.d.ts.map +1 -1
- package/dist/Limits.js +52 -22
- package/dist/Limits.js.map +1 -1
- package/dist/Policy.d.ts +102 -18
- package/dist/Policy.d.ts.map +1 -1
- package/dist/Policy.js +7 -6
- package/dist/Policy.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/internal/algebra.js +2 -2
- package/dist/internal/analysis.js +81 -75
- package/dist/internal/analysis.js.map +1 -1
- package/dist/internal/authorizer.d.ts.map +1 -1
- package/dist/internal/authorizer.js +11 -7
- package/dist/internal/authorizer.js.map +1 -1
- package/dist/internal/bind.js +86 -64
- package/dist/internal/bind.js.map +1 -1
- package/dist/internal/catalog.d.ts +33 -10
- package/dist/internal/catalog.d.ts.map +1 -1
- package/dist/internal/catalog.js +170 -77
- package/dist/internal/catalog.js.map +1 -1
- package/dist/internal/decide.js +27 -31
- package/dist/internal/decide.js.map +1 -1
- package/dist/internal/domain.js +19 -5
- package/dist/internal/domain.js.map +1 -1
- package/dist/internal/json.js +120 -76
- package/dist/internal/json.js.map +1 -1
- package/dist/internal/limits.d.ts +28 -0
- package/dist/internal/limits.d.ts.map +1 -1
- package/dist/internal/limits.js +75 -16
- package/dist/internal/limits.js.map +1 -1
- package/dist/internal/nodes.d.ts.map +1 -1
- package/dist/internal/nodes.js +53 -44
- package/dist/internal/nodes.js.map +1 -1
- package/dist/internal/patterns.js +50 -15
- package/dist/internal/patterns.js.map +1 -1
- package/dist/internal/policy-json.js +169 -127
- package/dist/internal/policy-json.js.map +1 -1
- package/dist/internal/policy-nodes.d.ts.map +1 -1
- package/dist/internal/policy-nodes.js +16 -13
- package/dist/internal/policy-nodes.js.map +1 -1
- package/dist/internal/policy.d.ts.map +1 -1
- package/dist/internal/policy.js +38 -22
- package/dist/internal/policy.js.map +1 -1
- package/dist/internal/resource.js +1 -1
- package/dist/internal/rule.d.ts.map +1 -1
- package/dist/internal/rule.js +6 -5
- package/dist/internal/rule.js.map +1 -1
- package/dist/internal/search.d.ts.map +1 -1
- package/dist/internal/search.js +106 -92
- package/dist/internal/search.js.map +1 -1
- package/dist/internal/trampoline.js +19 -5
- package/dist/internal/trampoline.js.map +1 -1
- package/package.json +1 -1
- package/src/Authorizer.ts +30 -20
- package/src/Condition.ts +317 -86
- package/src/Domain.ts +9 -0
- package/src/Limits.ts +69 -29
- package/src/Policy.ts +143 -21
- package/src/internal/analysis.ts +154 -124
- package/src/internal/authorizer.ts +29 -14
- package/src/internal/bind.ts +193 -136
- package/src/internal/catalog.ts +320 -118
- package/src/internal/decide.ts +61 -50
- package/src/internal/domain.ts +39 -3
- package/src/internal/json.ts +212 -117
- package/src/internal/limits.ts +101 -31
- package/src/internal/nodes.ts +65 -47
- package/src/internal/patterns.ts +79 -25
- package/src/internal/policy-json.ts +266 -197
- package/src/internal/policy-nodes.ts +31 -23
- package/src/internal/policy.ts +63 -23
- package/src/internal/rule.ts +16 -3
- package/src/internal/search.ts +186 -136
- package/src/internal/trampoline.ts +51 -4
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"search.js","names":["Algebra.testCompare","Nodes.always","Nodes.never","Algebra.and","Algebra.or","Algebra.not","Algebra.keyOf","Catalog.lookup","Catalog.anyKinds","Limits.searchSteps","Algebra.simplify"],"sources":["../../src/internal/search.ts"],"sourcesContent":["/**\n * The search for a value that makes a condition true. Each test of a\n * condition compares one field with constants, so the constants cut the\n * values of each field into a few cells, and every test gives the same answer\n * for all the values of one cell. The search tries one value from each cell,\n * one field at a time.\n *\n * The values of a field come from its kinds in a catalog. The kinds hold every\n * real value, so a \"no\" holds for the real values too. A \"yes\" holds for the\n * kinds: a check of the Schema may still stop the example.\n */\nimport * as Arr from \"effect/Array\"\nimport * as Data from \"effect/Data\"\nimport * as Option from \"effect/Option\"\nimport { isObject } from \"effect/Predicate\"\nimport * as Algebra from \"./algebra.ts\"\nimport * as Catalog from \"./catalog.ts\"\nimport * as Limits from \"./limits.ts\"\nimport * as Nodes from \"./nodes.ts\"\n\ntype Node = Nodes.Node\n\n/**\n * Values of fields, as a nested object. A field that is not in it may have any\n * value. A value with parts that the search does not look into, such as a\n * date, is an empty object.\n */\nexport interface Example {\n\treadonly [field: string]: unknown\n}\n\n/** The answer of a search. */\nexport type Satisfiability = Data.TaggedEnum<{\n\tSatisfiable: { readonly example: Example }\n\tUnsatisfiable: {}\n\tUnknown: { readonly reason: \"predicate\" | \"budget\" }\n}>\n\nconst satisfiability = Data.taggedEnum<Satisfiability>()\n\n/** Stands for a value with parts, such as a struct or a date. */\nconst opaque: object = Object.freeze({})\n\n/** Marks a field that a step of the search did not fix. */\nconst free: unique symbol = Symbol(\"free\")\n\n/** The least number that is greater than `value`. */\nconst nextUp = (value: number): number => {\n\tif (Number.isNaN(value) || value === Infinity) {\n\t\treturn value\n\t}\n\tif (value === 0) {\n\t\treturn Number.MIN_VALUE\n\t}\n\tconst view = new DataView(new ArrayBuffer(8))\n\tview.setFloat64(0, value)\n\tconst bits = view.getBigUint64(0)\n\tview.setBigUint64(0, value > 0 ? bits + 1n : bits - 1n)\n\treturn view.getFloat64(0)\n}\n\n/**\n * Gives one number from each part of the number line that the constants\n * make: each constant, and one number between each two neighbours when there\n * is one. The ends of the domain are in the list too.\n */\nconst numberCells = (\n\tkind: \"integer\" | \"finite\" | \"any\",\n\tconstants: ReadonlyArray<number>,\n): ReadonlyArray<number> => {\n\tconst integer = kind === \"integer\"\n\tconst low = integer\n\t\t? Number.MIN_SAFE_INTEGER\n\t\t: kind === \"finite\"\n\t\t\t? -Number.MAX_VALUE\n\t\t\t: -Infinity\n\tconst high = integer\n\t\t? Number.MAX_SAFE_INTEGER\n\t\t: kind === \"finite\"\n\t\t\t? Number.MAX_VALUE\n\t\t\t: Infinity\n\tconst known: ReadonlyArray<number> = Arr.filter(\n\t\tconstants,\n\t\t(value: number) => !Number.isNaN(value),\n\t)\n\tconst points = Arr.filter(\n\t\tArr.flatMap(known, (value: number) =>\n\t\t\tinteger && Number.isFinite(value)\n\t\t\t\t? [Math.floor(value), Math.ceil(value)]\n\t\t\t\t: [value],\n\t\t),\n\t\t(value: number) => value >= low && value <= high,\n\t)\n\t// `0` and `-0` are one point: every test gives the same answer for both,\n\t// and `===` calls them equal.\n\tconst ordered: Array<number> = [low, high, ...points].sort((a, b) => a - b)\n\tconst sorted = Arr.dedupeAdjacentWith(\n\t\tordered,\n\t\t(a: number, b: number) => a === b,\n\t)\n\tconst cells = Arr.flatMap(sorted, (value, index) => {\n\t\tconst after = sorted[index + 1]\n\t\tconst between = integer ? value + 1 : nextUp(value)\n\t\treturn after !== undefined && between < after ? [value, between] : [value]\n\t})\n\treturn kind === \"any\" ? Arr.append(cells, Number.NaN) : cells\n}\n\n/**\n * Gives one string from each part that the constants make. Strings are in the\n * order of their UTF-16 code units, as `<` sees them. The least string after\n * `a` is `a + \"\\0\"`, so there is a string between `a` and `b` exactly when\n * `b` is not `a + \"\\0\"`.\n */\nconst stringCells = (\n\tconstants: ReadonlyArray<string>,\n): ReadonlyArray<string> => {\n\tconst ordered: Array<string> = [...constants].sort()\n\tconst sorted = Arr.dedupeAdjacentWith(\n\t\tordered,\n\t\t(a: string, b: string) => a === b,\n\t)\n\tif (sorted.length === 0) {\n\t\treturn [\"\"]\n\t}\n\treturn [\n\t\t...(sorted[0] === \"\" ? [] : [\"\"]),\n\t\t...Arr.flatMap(sorted, (value, index) => {\n\t\t\tconst next = `${value}\\0`\n\t\t\treturn next === sorted[index + 1] ? [value] : [value, next]\n\t\t}),\n\t]\n}\n\n/**\n * A text for a cell. Two cells get one text when every test gives the same\n * answer for them: `0` and `-0` are one, and `NaN` is one.\n */\nconst cellText = (cell: unknown): string =>\n\tcell === opaque\n\t\t? \"opaque\"\n\t\t: typeof cell === \"number\"\n\t\t\t? `number:${Object.is(cell, -0) ? \"0\" : String(cell)}`\n\t\t\t: `${typeof cell}:${String(cell)}`\n\n/**\n * Keeps the first cell of each text, in order. The texts come from the\n * values of a policy, so they go into a native `Set`: the hash of a string in\n * Effect has no random seed, and a policy could hold very many strings with\n * one hash. The engine hashes a string with a random seed.\n */\nconst dedupeCells = (cells: ReadonlyArray<unknown>): ReadonlyArray<unknown> => {\n\t// The set belongs to this call.\n\tconst seen = new Set<string>()\n\treturn Arr.filter(cells, (cell) => {\n\t\tconst text = cellText(cell)\n\t\tif (seen.has(text)) {\n\t\t\treturn false\n\t\t}\n\t\tseen.add(text)\n\t\treturn true\n\t})\n}\n\n/** Gives one value from each cell of a field. */\nconst cellsOf = (\n\tkinds: Catalog.Kinds,\n\tconstants: ReadonlyArray<Nodes.Scalar>,\n): ReadonlyArray<unknown> =>\n\tdedupeCells([\n\t\t...(kinds.null ? [null] : []),\n\t\t...(kinds.object ? [opaque] : []),\n\t\t...(kinds.boolean ? [true, false] : []),\n\t\t...(kinds.string\n\t\t\t? stringCells(Arr.filter(constants, (value) => typeof value === \"string\"))\n\t\t\t: []),\n\t\t...(kinds.number === \"none\"\n\t\t\t? []\n\t\t\t: numberCells(\n\t\t\t\t\tkinds.number,\n\t\t\t\t\tArr.filter(constants, (value) => typeof value === \"number\"),\n\t\t\t\t)),\n\t\t...kinds.literals,\n\t])\n\nclass OutOfBudget extends Data.Error {}\n\n/** What one search keeps for all its steps. It lives only as long as the search. */\ninterface Search {\n\treadonly cells: (field: string) => ReadonlyArray<unknown>\n\t/**\n\t * The texts of the values of each `In` test, so one test of a value does\n\t * not read every value of the test. It is a native `Map`, because it must\n\t * find a node by the node itself: a hash of a node reads all its values.\n\t * The texts are in a native `Set`, as in {@link dedupeCells}.\n\t */\n\treadonly members: Map<Nodes.In, Set<string>>\n\t/**\n\t * The keys of the conditions that the search found to be unsatisfiable:\n\t * see `Algebra.keyOf`. A condition with the same structure has the same\n\t * free fields, so its answer is the same. The set only grows, so the steps\n\t * of a search may share it. It is a native `Set`, as in\n\t * {@link dedupeCells}.\n\t */\n\treadonly failed: Set<string | Node>\n\treadonly budget: number\n}\n\n/** What a search has done up to now. Each step gives a new one. */\ninterface SearchState {\n\treadonly steps: number\n}\n\ninterface Found {\n\treadonly assignment: Option.Option<ReadonlyArray<readonly [string, unknown]>>\n\treadonly state: SearchState\n}\n\nconst literalOf = (operand: Nodes.Operand): Nodes.Scalar => {\n\tif (operand._tag !== \"Literal\") {\n\t\tthrow new Error(\n\t\t\t\"Condition.satisfiable needs a condition with no principal fields. Call Condition.partial first.\",\n\t\t)\n\t}\n\treturn operand.value\n}\n\n/**\n * The value of the field `name` after `field` gets `value`. A field below\n * `field` is `null` when `value` is not a value with parts.\n */\nconst fixedValue = (\n\tname: string,\n\tfield: string,\n\tvalue: unknown,\n): unknown | typeof free =>\n\tname === field\n\t\t? value\n\t\t: name.startsWith(`${field}.`)\n\t\t\t? value === opaque\n\t\t\t\t? free\n\t\t\t\t: null\n\t\t\t: free\n\nconst membersOf = (node: Nodes.In, search: Search): Set<string> => {\n\tconst known = search.members.get(node)\n\tif (known !== undefined) {\n\t\treturn known\n\t}\n\t// `===` finds no `NaN`, so the set has none, and `NaN` is in no test.\n\tconst members = new Set(\n\t\tArr.map(\n\t\t\tArr.filter(\n\t\t\t\tArr.map(node.values, literalOf),\n\t\t\t\t(literal) => !Number.isNaN(literal),\n\t\t\t),\n\t\t\tcellText,\n\t\t),\n\t)\n\tsearch.members.set(node, members)\n\treturn members\n}\n\nconst testFixed = (\n\tnode: Nodes.Compare | Nodes.In | Nodes.Exists,\n\tvalue: unknown,\n\tsearch: Search,\n): boolean => {\n\tswitch (node._tag) {\n\t\tcase \"Compare\":\n\t\t\treturn Algebra.testCompare(node.op, value, literalOf(node.value))\n\t\tcase \"In\":\n\t\t\treturn (\n\t\t\t\t!Number.isNaN(value) && membersOf(node, search).has(cellText(value))\n\t\t\t)\n\t\tcase \"Exists\":\n\t\t\treturn value !== null\n\t}\n}\n\n/** Puts one value into a field, and into the fields below it. */\nconst fix = (\n\tnode: Node,\n\tfield: string,\n\tvalue: unknown,\n\tsearch: Search,\n): Node => {\n\tconst fixChild = (child: Node) => fix(child, field, value, search)\n\tswitch (node._tag) {\n\t\tcase \"Compare\":\n\t\tcase \"In\":\n\t\tcase \"Exists\": {\n\t\t\tconst known = fixedValue(node.field, field, value)\n\t\t\treturn known === free\n\t\t\t\t? node\n\t\t\t\t: testFixed(node, known, search)\n\t\t\t\t\t? Nodes.always\n\t\t\t\t\t: Nodes.never\n\t\t}\n\t\tcase \"And\":\n\t\t\treturn Algebra.and(Arr.map(node.conditions, fixChild))\n\t\tcase \"Or\":\n\t\t\treturn Algebra.or(Arr.map(node.conditions, fixChild))\n\t\tcase \"Not\":\n\t\t\treturn node.condition.pipe(fixChild, Algebra.not)\n\t\tdefault:\n\t\t\treturn node\n\t}\n}\n\n/** The fields of the tests of a condition, once for each test. */\nconst testedFields = (node: Node): ReadonlyArray<string> => {\n\tswitch (node._tag) {\n\t\tcase \"Compare\":\n\t\tcase \"In\":\n\t\tcase \"Exists\":\n\t\t\treturn [node.field]\n\t\tcase \"And\":\n\t\tcase \"Or\":\n\t\t\treturn Arr.flatMap(node.conditions, testedFields)\n\t\tcase \"Not\":\n\t\t\treturn testedFields(node.condition)\n\t\tdefault:\n\t\t\treturn []\n\t}\n}\n\n/**\n * Picks the next field to split on. A field comes before the fields below it,\n * so a value of a field can fix them. Then a field in more tests comes first,\n * and then the first name in order, so the choice is the same each time.\n */\nconst nextField = (node: Node): string => {\n\t// A native `Map`, as in `dedupeCells`: a condition that is not bound to a\n\t// domain may have any field names. The map belongs to this call.\n\tconst counts = new Map<string, number>()\n\tArr.forEach(testedFields(node), (field) =>\n\t\tcounts.set(field, (counts.get(field) ?? 0) + 1),\n\t)\n\tconst depth = (field: string) => field.split(\".\").length\n\tconst [first, ...rest] = Array.from(counts)\n\treturn Arr.reduce(rest, first!, (best, next) =>\n\t\tdepth(next[0]) < depth(best[0]) ||\n\t\t(depth(next[0]) === depth(best[0]) &&\n\t\t\t(next[1] > best[1] || (next[1] === best[1] && next[0] < best[0])))\n\t\t\t? next\n\t\t\t: best,\n\t)[0]\n}\n\n/**\n * Finds values of the fields that make a simple condition true. It splits on\n * one field at a time, and tries one value from each cell of the field. Each\n * step reads the whole condition, so the budget counts its size.\n */\nconst searchNode = (\n\tnode: Node,\n\tsearch: Search,\n\tstate: SearchState,\n\tassignment: ReadonlyArray<readonly [string, unknown]>,\n): Found => {\n\tif (node._tag === \"True\") {\n\t\treturn { assignment: Option.some(assignment), state }\n\t}\n\tif (node._tag === \"False\") {\n\t\treturn { assignment: Option.none(), state }\n\t}\n\t// The key reads at most a small part of the node: a node with very many\n\t// nodes, or with very many values in its `In` tests, is its own key. A\n\t// step reads the nodes but not the values, which `membersOf` keeps, so the\n\t// budget counts the size.\n\tconst key = Algebra.keyOf(node)\n\tif (search.failed.has(key)) {\n\t\treturn { assignment: Option.none(), state }\n\t}\n\tif (state.steps + node.size > search.budget) {\n\t\tthrow new OutOfBudget()\n\t}\n\tconst field = nextField(node)\n\t// The cells are tried in order, until one gives values. A fold over the\n\t// cells does no work after a found answer, and it does not grow the stack\n\t// with the number of cells.\n\tconst result = Arr.reduce(\n\t\tsearch.cells(field),\n\t\t{\n\t\t\tassignment: Option.none(),\n\t\t\tstate: { ...state, steps: state.steps + node.size },\n\t\t} as Found,\n\t\t(before, cell): Found => {\n\t\t\tif (Option.isSome(before.assignment)) {\n\t\t\t\treturn before\n\t\t\t}\n\t\t\tconst steps = before.state.steps + node.size\n\t\t\tif (steps > search.budget) {\n\t\t\t\tthrow new OutOfBudget()\n\t\t\t}\n\t\t\treturn searchNode(\n\t\t\t\tfix(node, field, cell, search),\n\t\t\t\tsearch,\n\t\t\t\t{ ...before.state, steps },\n\t\t\t\t[...assignment, [field, cell]],\n\t\t\t)\n\t\t},\n\t)\n\tif (Option.isNone(result.assignment)) {\n\t\tsearch.failed.add(key)\n\t}\n\treturn result\n}\n\n/**\n * Puts a value at a path in an object, and gives a new object. A value with\n * parts (`opaque`) becomes an empty object, and it keeps an object that is\n * there.\n */\nconst setPath = (\n\troot: Readonly<Record<string, unknown>>,\n\tnames: ReadonlyArray<string>,\n\tvalue: unknown,\n): Readonly<Record<string, unknown>> => {\n\tconst [name, ...rest] = names\n\tif (name === undefined) {\n\t\treturn root\n\t}\n\t// Only an own field counts, as a condition reads it. An inherited one,\n\t// such as `__proto__`, is not a field of the example.\n\tconst inner = Object.hasOwn(root, name) ? root[name] : undefined\n\tif (rest.length === 0) {\n\t\treturn value === opaque && isObject(inner)\n\t\t\t? root\n\t\t\t: { ...root, [name]: value === opaque ? {} : value }\n\t}\n\tconst base =\n\t\tisObject(inner) && inner !== opaque\n\t\t\t? (inner as Readonly<Record<string, unknown>>)\n\t\t\t: {}\n\treturn { ...root, [name]: setPath(base, rest, value) }\n}\n\nconst exampleOf = (\n\tassignment: ReadonlyArray<readonly [string, unknown]>,\n): Example =>\n\tArr.reduce(\n\t\tassignment,\n\t\t{} as Readonly<Record<string, unknown>>,\n\t\t(root, [field, value]) => setPath(root, field.split(\".\"), value),\n\t)\n\n/**\n * The constants that the tests of each field use. They come from a policy, so\n * they go into a native `Map` and `Set`, as in {@link dedupeCells}. The map\n * belongs to one search.\n */\nconst constantsOf = (node: Node): Map<string, Set<Nodes.Scalar>> => {\n\tconst pairs = (\n\t\tcurrent: Node,\n\t): ReadonlyArray<readonly [string, ReadonlyArray<Nodes.Scalar>]> => {\n\t\tswitch (current._tag) {\n\t\t\tcase \"Compare\":\n\t\t\t\treturn [[current.field, [literalOf(current.value)]]]\n\t\t\tcase \"In\":\n\t\t\t\treturn [[current.field, Arr.map(current.values, literalOf)]]\n\t\t\tcase \"Exists\":\n\t\t\t\treturn [[current.field, []]]\n\t\t\tcase \"Under\":\n\t\t\t\tthrow new Error(\n\t\t\t\t\t\"Condition.satisfiable needs a condition with no Under nodes. Call Condition.partial first.\",\n\t\t\t\t)\n\t\t\tcase \"And\":\n\t\t\tcase \"Or\":\n\t\t\t\treturn Arr.flatMap(current.conditions, pairs)\n\t\t\tcase \"Not\":\n\t\t\t\treturn pairs(current.condition)\n\t\t\tdefault:\n\t\t\t\treturn []\n\t\t}\n\t}\n\tconst all = new Map<string, Set<Nodes.Scalar>>()\n\tArr.forEach(pairs(node), ([field, values]) => {\n\t\tconst known = all.get(field) ?? new Set<Nodes.Scalar>()\n\t\tArr.forEach(values, (value) => known.add(value))\n\t\tall.set(field, known)\n\t})\n\treturn all\n}\n\n/** Gives the kinds of each field of a catalog. A field that it does not have may hold any value. */\nexport const kindsIn =\n\t(catalog: Catalog.Catalog | undefined) =>\n\t(field: string): Catalog.Kinds =>\n\t\t(catalog === undefined ? undefined : Catalog.lookup(catalog, field))\n\t\t\t?.kinds ?? Catalog.anyKinds\n\n/**\n * Searches for values that make a condition true. `kindsOf` gives the kinds\n * of values of each field. The condition must have no principal fields and no\n * `Under` nodes.\n */\nexport const satisfiable = (\n\tnode: Node,\n\tkindsOf: (field: string) => Catalog.Kinds,\n\tbudget: number = Limits.searchSteps,\n): Satisfiability => {\n\tif (node.opaque) {\n\t\treturn satisfiability.Unknown({ reason: \"predicate\" })\n\t}\n\tconst constants = constantsOf(node)\n\t// The search calls itself one time for each field that it fixes.\n\tif (constants.size > Limits.searchFields) {\n\t\treturn satisfiability.Unknown({ reason: \"budget\" })\n\t}\n\t// The cells of each field, in a native `Map` as the constants are.\n\tconst cells = new Map<string, ReadonlyArray<unknown>>()\n\tconst search: Search = {\n\t\tmembers: new Map(),\n\t\tfailed: new Set(),\n\t\tcells: (field) => {\n\t\t\tconst made = cells.get(field)\n\t\t\tif (made !== undefined) {\n\t\t\t\treturn made\n\t\t\t}\n\t\t\t// The cells come from the constants, so many constants are work\n\t\t\t// that the budget must count before the cells are made.\n\t\t\tconst known = constants.get(field) ?? new Set<Nodes.Scalar>()\n\t\t\tif (known.size > budget) {\n\t\t\t\tthrow new OutOfBudget()\n\t\t\t}\n\t\t\tconst found = cellsOf(kindsOf(field), Array.from(known))\n\t\t\tcells.set(field, found)\n\t\t\treturn found\n\t\t},\n\t\tbudget,\n\t}\n\ttry {\n\t\tconst found = searchNode(Algebra.simplify(node), search, { steps: 0 }, [])\n\t\treturn Option.match(found.assignment, {\n\t\t\tonNone: () => satisfiability.Unsatisfiable(),\n\t\t\tonSome: (assignment) =>\n\t\t\t\tsatisfiability.Satisfiable({ example: exampleOf(assignment) }),\n\t\t})\n\t} catch (error) {\n\t\tif (error instanceof OutOfBudget) {\n\t\t\treturn satisfiability.Unknown({ reason: \"budget\" })\n\t\t}\n\t\tthrow error\n\t}\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAsCA,MAAM,iBAAiB,KAAK,WAA2B;;AAGvD,MAAM,SAAiB,OAAO,OAAO,CAAC,CAAC;;AAGvC,MAAM,OAAsB,OAAO,MAAM;;AAGzC,MAAM,UAAU,UAA0B;CACzC,IAAI,OAAO,MAAM,KAAK,KAAK,UAAU,UACpC,OAAO;CAER,IAAI,UAAU,GACb,OAAO,OAAO;CAEf,MAAM,uBAAO,IAAI,yBAAS,IAAI,YAAY,CAAC,CAAC;CAC5C,KAAK,WAAW,GAAG,KAAK;CACxB,MAAM,OAAO,KAAK,aAAa,CAAC;CAChC,KAAK,aAAa,GAAG,QAAQ,IAAI,OAAO,KAAK,OAAO,EAAE;CACtD,OAAO,KAAK,WAAW,CAAC;AACzB;;;;;;AAOA,MAAM,eACL,MACA,cAC2B;CAC3B,MAAM,UAAU,SAAS;CACzB,MAAM,MAAM,UACT,OAAO,mBACP,SAAS,WACR,CAAC,OAAO,YACR;CACJ,MAAM,OAAO,UACV,OAAO,mBACP,SAAS,WACR,OAAO,YACP;CACJ,MAAM,QAA+B,IAAI,OACxC,YACC,UAAkB,CAAC,OAAO,MAAM,KAAK,CACvC;CAWA,MAAM,UAAyB;EAAC;EAAK;EAAM,GAV5B,IAAI,OAClB,IAAI,QAAQ,QAAQ,UACnB,WAAW,OAAO,SAAS,KAAK,IAC7B,CAAC,KAAK,MAAM,KAAK,GAAG,KAAK,KAAK,KAAK,CAAC,IACpC,CAAC,KAAK,CACV,IACC,UAAkB,SAAS,OAAO,SAAS,IAIM;CAAC,CAAC,CAAC,MAAM,GAAG,MAAM,IAAI,CAAC;CAC1E,MAAM,SAAS,IAAI,mBAClB,UACC,GAAW,MAAc,MAAM,CACjC;CACA,MAAM,QAAQ,IAAI,QAAQ,SAAS,OAAO,UAAU;EACnD,MAAM,QAAQ,OAAO,QAAQ;EAC7B,MAAM,UAAU,UAAU,QAAQ,IAAI,OAAO,KAAK;EAClD,OAAO,UAAU,KAAA,KAAa,UAAU,QAAQ,CAAC,OAAO,OAAO,IAAI,CAAC,KAAK;CAC1E,CAAC;CACD,OAAO,SAAS,QAAQ,IAAI,OAAO,OAAO,GAAU,IAAI;AACzD;;;;;;;AAQA,MAAM,eACL,cAC2B;CAC3B,MAAM,UAAyB,CAAC,GAAG,SAAS,CAAC,CAAC,KAAK;CACnD,MAAM,SAAS,IAAI,mBAClB,UACC,GAAW,MAAc,MAAM,CACjC;CACA,IAAI,OAAO,WAAW,GACrB,OAAO,CAAC,EAAE;CAEX,OAAO,CACN,GAAI,OAAO,OAAO,KAAK,CAAC,IAAI,CAAC,EAAE,GAC/B,GAAG,IAAI,QAAQ,SAAS,OAAO,UAAU;EACxC,MAAM,OAAO,GAAG,MAAM;EACtB,OAAO,SAAS,OAAO,QAAQ,KAAK,CAAC,KAAK,IAAI,CAAC,OAAO,IAAI;CAC3D,CAAC,CACF;AACD;;;;;AAMA,MAAM,YAAY,SACjB,SAAS,SACN,WACA,OAAO,SAAS,WACf,UAAU,OAAO,GAAG,MAAM,EAAE,IAAI,MAAM,OAAO,IAAI,MACjD,GAAG,OAAO,KAAK,GAAG,OAAO,IAAI;;;;;;;AAQlC,MAAM,eAAe,UAA0D;CAE9E,MAAM,uBAAO,IAAI,IAAY;CAC7B,OAAO,IAAI,OAAO,QAAQ,SAAS;EAClC,MAAM,OAAO,SAAS,IAAI;EAC1B,IAAI,KAAK,IAAI,IAAI,GAChB,OAAO;EAER,KAAK,IAAI,IAAI;EACb,OAAO;CACR,CAAC;AACF;;AAGA,MAAM,WACL,OACA,cAEA,YAAY;CACX,GAAI,MAAM,OAAO,CAAC,IAAI,IAAI,CAAC;CAC3B,GAAI,MAAM,SAAS,CAAC,MAAM,IAAI,CAAC;CAC/B,GAAI,MAAM,UAAU,CAAC,MAAM,KAAK,IAAI,CAAC;CACrC,GAAI,MAAM,SACP,YAAY,IAAI,OAAO,YAAY,UAAU,OAAO,UAAU,QAAQ,CAAC,IACvE,CAAC;CACJ,GAAI,MAAM,WAAW,SAClB,CAAC,IACD,YACA,MAAM,QACN,IAAI,OAAO,YAAY,UAAU,OAAO,UAAU,QAAQ,CAC3D;CACF,GAAG,MAAM;AACV,CAAC;AAEF,IAAM,cAAN,cAA0B,KAAK,MAAM,CAAC;AAiCtC,MAAM,aAAa,YAAyC;CAC3D,IAAI,QAAQ,SAAS,WACpB,MAAM,IAAI,MACT,iGACD;CAED,OAAO,QAAQ;AAChB;;;;;AAMA,MAAM,cACL,MACA,OACA,UAEA,SAAS,QACN,QACA,KAAK,WAAW,GAAG,MAAM,EAAE,IAC1B,UAAU,SACT,OACA,OACD;AAEL,MAAM,aAAa,MAAgB,WAAgC;CAClE,MAAM,QAAQ,OAAO,QAAQ,IAAI,IAAI;CACrC,IAAI,UAAU,KAAA,GACb,OAAO;CAGR,MAAM,UAAU,IAAI,IACnB,IAAI,IACH,IAAI,OACH,IAAI,IAAI,KAAK,QAAQ,SAAS,IAC7B,YAAY,CAAC,OAAO,MAAM,OAAO,CACnC,GACA,QACD,CACD;CACA,OAAO,QAAQ,IAAI,MAAM,OAAO;CAChC,OAAO;AACR;AAEA,MAAM,aACL,MACA,OACA,WACa;CACb,QAAQ,KAAK,MAAb;EACC,KAAK,WACJ,OAAOA,YAAoB,KAAK,IAAI,OAAO,UAAU,KAAK,KAAK,CAAC;EACjE,KAAK,MACJ,OACC,CAAC,OAAO,MAAM,KAAK,KAAK,UAAU,MAAM,MAAM,CAAC,CAAC,IAAI,SAAS,KAAK,CAAC;EAErE,KAAK,UACJ,OAAO,UAAU;CACnB;AACD;;AAGA,MAAM,OACL,MACA,OACA,OACA,WACU;CACV,MAAM,YAAY,UAAgB,IAAI,OAAO,OAAO,OAAO,MAAM;CACjE,QAAQ,KAAK,MAAb;EACC,KAAK;EACL,KAAK;EACL,KAAK,UAAU;GACd,MAAM,QAAQ,WAAW,KAAK,OAAO,OAAO,KAAK;GACjD,OAAO,UAAU,OACd,OACA,UAAU,MAAM,OAAO,MAAM,IAC5BC,SACAC;EACL;EACA,KAAK,OACJ,OAAOC,IAAY,IAAI,IAAI,KAAK,YAAY,QAAQ,CAAC;EACtD,KAAK,MACJ,OAAOC,GAAW,IAAI,IAAI,KAAK,YAAY,QAAQ,CAAC;EACrD,KAAK,OACJ,OAAO,KAAK,UAAU,KAAK,UAAUC,GAAW;EACjD,SACC,OAAO;CACT;AACD;;AAGA,MAAM,gBAAgB,SAAsC;CAC3D,QAAQ,KAAK,MAAb;EACC,KAAK;EACL,KAAK;EACL,KAAK,UACJ,OAAO,CAAC,KAAK,KAAK;EACnB,KAAK;EACL,KAAK,MACJ,OAAO,IAAI,QAAQ,KAAK,YAAY,YAAY;EACjD,KAAK,OACJ,OAAO,aAAa,KAAK,SAAS;EACnC,SACC,OAAO,CAAC;CACV;AACD;;;;;;AAOA,MAAM,aAAa,SAAuB;CAGzC,MAAM,yBAAS,IAAI,IAAoB;CACvC,IAAI,QAAQ,aAAa,IAAI,IAAI,UAChC,OAAO,IAAI,QAAQ,OAAO,IAAI,KAAK,KAAK,KAAK,CAAC,CAC/C;CACA,MAAM,SAAS,UAAkB,MAAM,MAAM,GAAG,CAAC,CAAC;CAClD,MAAM,CAAC,OAAO,GAAG,QAAQ,MAAM,KAAK,MAAM;CAC1C,OAAO,IAAI,OAAO,MAAM,QAAS,MAAM,SACtC,MAAM,KAAK,EAAE,IAAI,MAAM,KAAK,EAAE,KAC7B,MAAM,KAAK,EAAE,MAAM,MAAM,KAAK,EAAE,MAC/B,KAAK,KAAK,KAAK,MAAO,KAAK,OAAO,KAAK,MAAM,KAAK,KAAK,KAAK,MAC3D,OACA,IACJ,CAAC,CAAC;AACH;;;;;;AAOA,MAAM,cACL,MACA,QACA,OACA,eACW;CACX,IAAI,KAAK,SAAS,QACjB,OAAO;EAAE,YAAY,OAAO,KAAK,UAAU;EAAG;CAAM;CAErD,IAAI,KAAK,SAAS,SACjB,OAAO;EAAE,YAAY,OAAO,KAAK;EAAG;CAAM;CAM3C,MAAM,MAAMC,MAAc,IAAI;CAC9B,IAAI,OAAO,OAAO,IAAI,GAAG,GACxB,OAAO;EAAE,YAAY,OAAO,KAAK;EAAG;CAAM;CAE3C,IAAI,MAAM,QAAQ,KAAK,OAAO,OAAO,QACpC,MAAM,IAAI,YAAY;CAEvB,MAAM,QAAQ,UAAU,IAAI;CAI5B,MAAM,SAAS,IAAI,OAClB,OAAO,MAAM,KAAK,GAClB;EACC,YAAY,OAAO,KAAK;EACxB,OAAO;GAAE,GAAG;GAAO,OAAO,MAAM,QAAQ,KAAK;EAAK;CACnD,IACC,QAAQ,SAAgB;EACxB,IAAI,OAAO,OAAO,OAAO,UAAU,GAClC,OAAO;EAER,MAAM,QAAQ,OAAO,MAAM,QAAQ,KAAK;EACxC,IAAI,QAAQ,OAAO,QAClB,MAAM,IAAI,YAAY;EAEvB,OAAO,WACN,IAAI,MAAM,OAAO,MAAM,MAAM,GAC7B,QACA;GAAE,GAAG,OAAO;GAAO;EAAM,GACzB,CAAC,GAAG,YAAY,CAAC,OAAO,IAAI,CAAC,CAC9B;CACD,CACD;CACA,IAAI,OAAO,OAAO,OAAO,UAAU,GAClC,OAAO,OAAO,IAAI,GAAG;CAEtB,OAAO;AACR;;;;;;AAOA,MAAM,WACL,MACA,OACA,UACuC;CACvC,MAAM,CAAC,MAAM,GAAG,QAAQ;CACxB,IAAI,SAAS,KAAA,GACZ,OAAO;CAIR,MAAM,QAAQ,OAAO,OAAO,MAAM,IAAI,IAAI,KAAK,QAAQ,KAAA;CACvD,IAAI,KAAK,WAAW,GACnB,OAAO,UAAU,UAAU,SAAS,KAAK,IACtC,OACA;EAAE,GAAG;GAAO,OAAO,UAAU,SAAS,CAAC,IAAI;CAAM;CAErD,MAAM,OACL,SAAS,KAAK,KAAK,UAAU,SACzB,QACD,CAAC;CACL,OAAO;EAAE,GAAG;GAAO,OAAO,QAAQ,MAAM,MAAM,KAAK;CAAE;AACtD;AAEA,MAAM,aACL,eAEA,IAAI,OACH,YACA,CAAC,IACA,MAAM,CAAC,OAAO,WAAW,QAAQ,MAAM,MAAM,MAAM,GAAG,GAAG,KAAK,CAChE;;;;;;AAOD,MAAM,eAAe,SAA+C;CACnE,MAAM,SACL,YACmE;EACnE,QAAQ,QAAQ,MAAhB;GACC,KAAK,WACJ,OAAO,CAAC,CAAC,QAAQ,OAAO,CAAC,UAAU,QAAQ,KAAK,CAAC,CAAC,CAAC;GACpD,KAAK,MACJ,OAAO,CAAC,CAAC,QAAQ,OAAO,IAAI,IAAI,QAAQ,QAAQ,SAAS,CAAC,CAAC;GAC5D,KAAK,UACJ,OAAO,CAAC,CAAC,QAAQ,OAAO,CAAC,CAAC,CAAC;GAC5B,KAAK,SACJ,MAAM,IAAI,MACT,4FACD;GACD,KAAK;GACL,KAAK,MACJ,OAAO,IAAI,QAAQ,QAAQ,YAAY,KAAK;GAC7C,KAAK,OACJ,OAAO,MAAM,QAAQ,SAAS;GAC/B,SACC,OAAO,CAAC;EACV;CACD;CACA,MAAM,sBAAM,IAAI,IAA+B;CAC/C,IAAI,QAAQ,MAAM,IAAI,IAAI,CAAC,OAAO,YAAY;EAC7C,MAAM,QAAQ,IAAI,IAAI,KAAK,qBAAK,IAAI,IAAkB;EACtD,IAAI,QAAQ,SAAS,UAAU,MAAM,IAAI,KAAK,CAAC;EAC/C,IAAI,IAAI,OAAO,KAAK;CACrB,CAAC;CACD,OAAO;AACR;;AAGA,MAAa,WACX,aACA,WACC,YAAY,KAAA,IAAY,KAAA,IAAYC,OAAe,SAAS,KAAK,EAAA,EAC/D,SAASC;;;;;;AAOd,MAAa,eACZ,MACA,SACA,SAAiBC,gBACG;CACpB,IAAI,KAAK,QACR,OAAO,eAAe,QAAQ,EAAE,QAAQ,YAAY,CAAC;CAEtD,MAAM,YAAY,YAAY,IAAI;CAElC,IAAI,UAAU,OAAO,KACpB,OAAO,eAAe,QAAQ,EAAE,QAAQ,SAAS,CAAC;CAGnD,MAAM,wBAAQ,IAAI,IAAoC;CACtD,MAAM,SAAiB;EACtB,yBAAS,IAAI,IAAI;EACjB,wBAAQ,IAAI,IAAI;EAChB,QAAQ,UAAU;GACjB,MAAM,OAAO,MAAM,IAAI,KAAK;GAC5B,IAAI,SAAS,KAAA,GACZ,OAAO;GAIR,MAAM,QAAQ,UAAU,IAAI,KAAK,qBAAK,IAAI,IAAkB;GAC5D,IAAI,MAAM,OAAO,QAChB,MAAM,IAAI,YAAY;GAEvB,MAAM,QAAQ,QAAQ,QAAQ,KAAK,GAAG,MAAM,KAAK,KAAK,CAAC;GACvD,MAAM,IAAI,OAAO,KAAK;GACtB,OAAO;EACR;EACA;CACD;CACA,IAAI;EACH,MAAM,QAAQ,WAAWC,SAAiB,IAAI,GAAG,QAAQ,EAAE,OAAO,EAAE,GAAG,CAAC,CAAC;EACzE,OAAO,OAAO,MAAM,MAAM,YAAY;GACrC,cAAc,eAAe,cAAc;GAC3C,SAAS,eACR,eAAe,YAAY,EAAE,SAAS,UAAU,UAAU,EAAE,CAAC;EAC/D,CAAC;CACF,SAAS,OAAO;EACf,IAAI,iBAAiB,aACpB,OAAO,eAAe,QAAQ,EAAE,QAAQ,SAAS,CAAC;EAEnD,MAAM;CACP;AACD"}
|
|
1
|
+
{"version":3,"file":"search.js","names":["Algebra.testCompare","Trampoline.run","Nodes.always","Nodes.never","Algebra.and","Algebra.or","Algebra.not","Algebra.keyOf","Catalog.lookup","Catalog.anyKinds","Limits.searchSteps","Limits.searchFields","Algebra.simplify"],"sources":["../../src/internal/search.ts"],"sourcesContent":["/**\n * The search for a value that makes a condition true. Each test of a\n * condition compares one field with constants, so the constants cut the\n * values of each field into a few cells, and every test gives the same answer\n * for all the values of one cell. The search tries one value from each cell,\n * one field at a time.\n *\n * The values of a field come from its kinds in a catalog. The kinds hold every\n * real value, so a \"no\" holds for the real values too. A \"yes\" holds for the\n * kinds: a check of the Schema may still stop the example.\n */\nimport * as Arr from \"effect/Array\"\nimport * as Data from \"effect/Data\"\nimport * as Option from \"effect/Option\"\nimport { isObject } from \"effect/Predicate\"\nimport * as Algebra from \"./algebra.ts\"\nimport * as Catalog from \"./catalog.ts\"\nimport * as Limits from \"./limits.ts\"\nimport * as Nodes from \"./nodes.ts\"\nimport * as Trampoline from \"./trampoline.ts\"\n\ntype Node = Nodes.Node\n\n/**\n * Values of fields, as a nested object. A field that is not in it may have any\n * value. A value with parts that the search does not look into, such as a\n * date, is an empty object.\n */\nexport interface Example {\n\treadonly [field: string]: unknown\n}\n\n/** The answer of a search. */\nexport type Satisfiability = Data.TaggedEnum<{\n\tSatisfiable: { readonly example: Example }\n\tUnsatisfiable: {}\n\tUnknown: { readonly reason: \"predicate\" | \"budget\" }\n}>\n\nconst satisfiability = Data.taggedEnum<Satisfiability>()\n\n/** Stands for a value with parts, such as a struct or a date. */\nconst opaque: object = Object.freeze({})\n\n/** Marks a field that a step of the search did not fix. */\nconst free: unique symbol = Symbol(\"free\")\n\n/** The least number that is greater than `value`. */\nconst nextUp = (value: number): number => {\n\tif (Number.isNaN(value) || value === Infinity) {\n\t\treturn value\n\t}\n\tif (value === 0) {\n\t\treturn Number.MIN_VALUE\n\t}\n\tconst view = new DataView(new ArrayBuffer(8))\n\tview.setFloat64(0, value)\n\tconst bits = view.getBigUint64(0)\n\tview.setBigUint64(0, value > 0 ? bits + 1n : bits - 1n)\n\treturn view.getFloat64(0)\n}\n\n/**\n * Gives one number from each part of the number line that the constants\n * make: each constant, and one number between each two neighbours when there\n * is one. The ends of the domain are in the list too.\n */\nconst numberCells = (\n\tkind: \"integer\" | \"finite\" | \"any\",\n\tconstants: ReadonlyArray<number>,\n): ReadonlyArray<number> => {\n\tconst integer = kind === \"integer\"\n\tconst low = integer\n\t\t? Number.MIN_SAFE_INTEGER\n\t\t: kind === \"finite\"\n\t\t\t? -Number.MAX_VALUE\n\t\t\t: -Infinity\n\tconst high = integer\n\t\t? Number.MAX_SAFE_INTEGER\n\t\t: kind === \"finite\"\n\t\t\t? Number.MAX_VALUE\n\t\t\t: Infinity\n\tconst known: ReadonlyArray<number> = Arr.filter(\n\t\tconstants,\n\t\t(value: number) => !Number.isNaN(value),\n\t)\n\tconst points = Arr.filter(\n\t\tArr.flatMap(known, (value: number) =>\n\t\t\tinteger && Number.isFinite(value)\n\t\t\t\t? [Math.floor(value), Math.ceil(value)]\n\t\t\t\t: [value],\n\t\t),\n\t\t(value: number) => value >= low && value <= high,\n\t)\n\t// `0` and `-0` are one point: every test gives the same answer for both,\n\t// and `===` calls them equal.\n\tconst ordered: Array<number> = [low, high, ...points].sort((a, b) => a - b)\n\tconst sorted = Arr.dedupeAdjacentWith(\n\t\tordered,\n\t\t(a: number, b: number) => a === b,\n\t)\n\tconst cells = Arr.flatMap(sorted, (value, index) => {\n\t\tconst after = sorted[index + 1]\n\t\tconst between = integer ? value + 1 : nextUp(value)\n\t\treturn after !== undefined && between < after ? [value, between] : [value]\n\t})\n\treturn kind === \"any\" ? Arr.append(cells, Number.NaN) : cells\n}\n\n/**\n * Gives one string from each part that the constants make. Strings are in the\n * order of their UTF-16 code units, as `<` sees them. The least string after\n * `a` is `a + \"\\0\"`, so there is a string between `a` and `b` exactly when\n * `b` is not `a + \"\\0\"`.\n */\nconst stringCells = (\n\tconstants: ReadonlyArray<string>,\n): ReadonlyArray<string> => {\n\tconst ordered: Array<string> = [...constants].sort()\n\tconst sorted = Arr.dedupeAdjacentWith(\n\t\tordered,\n\t\t(a: string, b: string) => a === b,\n\t)\n\tif (sorted.length === 0) {\n\t\treturn [\"\"]\n\t}\n\treturn [\n\t\t...(sorted[0] === \"\" ? [] : [\"\"]),\n\t\t...Arr.flatMap(sorted, (value, index) => {\n\t\t\tconst next = `${value}\\0`\n\t\t\treturn next === sorted[index + 1] ? [value] : [value, next]\n\t\t}),\n\t]\n}\n\n/**\n * A text for a cell. Two cells get one text when every test gives the same\n * answer for them: `0` and `-0` are one, and `NaN` is one.\n */\nconst cellText = (cell: unknown): string =>\n\tcell === opaque\n\t\t? \"opaque\"\n\t\t: typeof cell === \"number\"\n\t\t\t? `number:${Object.is(cell, -0) ? \"0\" : String(cell)}`\n\t\t\t: `${typeof cell}:${String(cell)}`\n\n/**\n * Keeps the first cell of each text, in order. The texts come from the\n * values of a policy, so they go into a native `Set`: the hash of a string in\n * Effect has no random seed, and a policy could hold very many strings with\n * one hash. The engine hashes a string with a random seed.\n */\nconst dedupeCells = (cells: ReadonlyArray<unknown>): ReadonlyArray<unknown> => {\n\t// The set belongs to this call.\n\tconst seen = new Set<string>()\n\treturn Arr.filter(cells, (cell) => {\n\t\tconst text = cellText(cell)\n\t\tif (seen.has(text)) {\n\t\t\treturn false\n\t\t}\n\t\tseen.add(text)\n\t\treturn true\n\t})\n}\n\n/** Gives one value from each cell of a field. */\nconst cellsOf = (\n\tkinds: Catalog.Kinds,\n\tconstants: ReadonlyArray<Nodes.Scalar>,\n): ReadonlyArray<unknown> =>\n\tdedupeCells([\n\t\t...(kinds.null ? [null] : []),\n\t\t...(kinds.object ? [opaque] : []),\n\t\t...(kinds.boolean ? [true, false] : []),\n\t\t...(kinds.string\n\t\t\t? stringCells(Arr.filter(constants, (value) => typeof value === \"string\"))\n\t\t\t: []),\n\t\t...(kinds.number === \"none\"\n\t\t\t? []\n\t\t\t: numberCells(\n\t\t\t\t\tkinds.number,\n\t\t\t\t\tArr.filter(constants, (value) => typeof value === \"number\"),\n\t\t\t\t)),\n\t\t...kinds.literals,\n\t])\n\nclass OutOfBudget extends Data.Error {}\n\n/** What one search keeps for all its steps. It lives only as long as the search. */\ninterface Search {\n\treadonly cells: (field: string) => ReadonlyArray<unknown>\n\t/**\n\t * The texts of the values of each `In` test, so one test of a value does\n\t * not read every value of the test. It is a native `Map`, because it must\n\t * find a node by the node itself: a hash of a node reads all its values.\n\t * The texts are in a native `Set`, as in {@link dedupeCells}.\n\t */\n\treadonly members: Map<Nodes.In, Set<string>>\n\t/**\n\t * The keys of the conditions that the search found to be unsatisfiable:\n\t * see `Algebra.keyOf`. A condition with the same structure has the same\n\t * free fields, so its answer is the same. The set only grows, so the steps\n\t * of a search may share it. It is a native `Set`, as in\n\t * {@link dedupeCells}.\n\t */\n\treadonly failed: Set<string | Node>\n\treadonly budget: number\n}\n\n/** What a search has done up to now. Each step gives a new one. */\ninterface SearchState {\n\treadonly steps: number\n}\n\ninterface Found {\n\treadonly assignment: Option.Option<ReadonlyArray<readonly [string, unknown]>>\n\treadonly state: SearchState\n}\n\nconst literalOf = (operand: Nodes.Operand): Nodes.Scalar => {\n\tif (operand._tag !== \"Literal\") {\n\t\tthrow new Error(\n\t\t\t\"Condition.satisfiable needs a condition with no principal fields. Call Condition.partial first.\",\n\t\t)\n\t}\n\treturn operand.value\n}\n\n/**\n * The value of the field `name` after `field` gets `value`. A field below\n * `field` is `null` when `value` is not a value with parts.\n */\nconst fixedValue = (\n\tname: string,\n\tfield: string,\n\tvalue: unknown,\n): unknown | typeof free =>\n\tname === field\n\t\t? value\n\t\t: name.startsWith(`${field}.`)\n\t\t\t? value === opaque\n\t\t\t\t? free\n\t\t\t\t: null\n\t\t\t: free\n\nconst membersOf = (node: Nodes.In, search: Search): Set<string> => {\n\tconst known = search.members.get(node)\n\tif (known !== undefined) {\n\t\treturn known\n\t}\n\t// `===` finds no `NaN`, so the set has none, and `NaN` is in no test.\n\tconst members = new Set(\n\t\tArr.map(\n\t\t\tArr.filter(\n\t\t\t\tArr.map(node.values, literalOf),\n\t\t\t\t(literal) => !Number.isNaN(literal),\n\t\t\t),\n\t\t\tcellText,\n\t\t),\n\t)\n\tsearch.members.set(node, members)\n\treturn members\n}\n\nconst testFixed = (\n\tnode: Nodes.Compare | Nodes.In | Nodes.Exists,\n\tvalue: unknown,\n\tsearch: Search,\n): boolean => {\n\tswitch (node._tag) {\n\t\tcase \"Compare\":\n\t\t\treturn Algebra.testCompare(node.op, value, literalOf(node.value))\n\t\tcase \"In\":\n\t\t\treturn (\n\t\t\t\t!Number.isNaN(value) && membersOf(node, search).has(cellText(value))\n\t\t\t)\n\t\tcase \"Exists\":\n\t\t\treturn value !== null\n\t}\n}\n\n/** Puts one value into a field, and into the fields below it. */\nconst fix = (root: Node, field: string, value: unknown, search: Search): Node =>\n\t// The walks of a condition take the steps of a trampoline, so a deep\n\t// condition does not fill the stack.\n\tTrampoline.run(root.depth, (R) => {\n\t\tconst go = (node: Node): Trampoline.Step<Node> => {\n\t\t\tconst children = (nodes: ReadonlyArray<Node>) =>\n\t\t\t\tR.forEach(nodes, (child) => R.suspend(() => go(child)))\n\t\t\tswitch (node._tag) {\n\t\t\t\tcase \"Compare\":\n\t\t\t\tcase \"In\":\n\t\t\t\tcase \"Exists\": {\n\t\t\t\t\tconst known = fixedValue(node.field, field, value)\n\t\t\t\t\treturn R.succeed(\n\t\t\t\t\t\tknown === free\n\t\t\t\t\t\t\t? node\n\t\t\t\t\t\t\t: testFixed(node, known, search)\n\t\t\t\t\t\t\t\t? Nodes.always\n\t\t\t\t\t\t\t\t: Nodes.never,\n\t\t\t\t\t)\n\t\t\t\t}\n\t\t\t\tcase \"And\":\n\t\t\t\t\treturn R.map(children(node.conditions), Algebra.and)\n\t\t\t\tcase \"Or\":\n\t\t\t\t\treturn R.map(children(node.conditions), Algebra.or)\n\t\t\t\tcase \"Not\":\n\t\t\t\t\treturn R.map(\n\t\t\t\t\t\tR.suspend(() => go(node.condition)),\n\t\t\t\t\t\tAlgebra.not,\n\t\t\t\t\t)\n\t\t\t\tdefault:\n\t\t\t\t\treturn R.succeed(node)\n\t\t\t}\n\t\t}\n\t\treturn go(root)\n\t})\n\n/** The fields of the tests of a condition, once for each test. */\nconst testedFields = (root: Node): ReadonlyArray<string> =>\n\tTrampoline.run(root.depth, (R) => {\n\t\tconst go = (node: Node): Trampoline.Step<ReadonlyArray<string>> => {\n\t\t\tswitch (node._tag) {\n\t\t\t\tcase \"Compare\":\n\t\t\t\tcase \"In\":\n\t\t\t\tcase \"Exists\":\n\t\t\t\t\treturn R.succeed([node.field])\n\t\t\t\tcase \"And\":\n\t\t\t\tcase \"Or\":\n\t\t\t\t\treturn R.map(\n\t\t\t\t\t\tR.forEach(node.conditions, (child) => R.suspend(() => go(child))),\n\t\t\t\t\t\tArr.flatten,\n\t\t\t\t\t)\n\t\t\t\tcase \"Not\":\n\t\t\t\t\treturn R.suspend(() => go(node.condition))\n\t\t\t\tdefault:\n\t\t\t\t\treturn R.succeed([])\n\t\t\t}\n\t\t}\n\t\treturn go(root)\n\t})\n\n/**\n * Picks the next field to split on. A field comes before the fields below it,\n * so a value of a field can fix them. Then a field in more tests comes first,\n * and then the first name in order, so the choice is the same each time.\n */\nconst nextField = (node: Node): string => {\n\t// A native `Map`, as in `dedupeCells`: a condition that is not bound to a\n\t// domain may have any field names. The map belongs to this call.\n\tconst counts = new Map<string, number>()\n\tArr.forEach(testedFields(node), (field) =>\n\t\tcounts.set(field, (counts.get(field) ?? 0) + 1),\n\t)\n\tconst depth = (field: string) => field.split(\".\").length\n\tconst [first, ...rest] = Array.from(counts)\n\treturn Arr.reduce(rest, first!, (best, next) =>\n\t\tdepth(next[0]) < depth(best[0]) ||\n\t\t(depth(next[0]) === depth(best[0]) &&\n\t\t\t(next[1] > best[1] || (next[1] === best[1] && next[0] < best[0])))\n\t\t\t? next\n\t\t\t: best,\n\t)[0]\n}\n\n/**\n * Finds values of the fields that make a simple condition true. It splits on\n * one field at a time, and tries one value from each cell of the field. Each\n * step reads the whole condition, so the budget counts its size.\n */\nconst searchNode = (search: Search) => (R: Trampoline.Runner) => {\n\tconst go = (\n\t\tnode: Node,\n\t\tstate: SearchState,\n\t\tassignment: ReadonlyArray<readonly [string, unknown]>,\n\t): Trampoline.Step<Found> => {\n\t\tif (node._tag === \"True\") {\n\t\t\treturn R.succeed({ assignment: Option.some(assignment), state })\n\t\t}\n\t\tif (node._tag === \"False\") {\n\t\t\treturn R.succeed({ assignment: Option.none(), state })\n\t\t}\n\t\t// The key reads at most a small part of the node: a node with very\n\t\t// many nodes, or with very many values in its `In` tests, is its own\n\t\t// key. A step reads the nodes but not the values, which `membersOf`\n\t\t// keeps, so the budget counts the size.\n\t\tconst key = Algebra.keyOf(node)\n\t\tif (search.failed.has(key)) {\n\t\t\treturn R.succeed({ assignment: Option.none(), state })\n\t\t}\n\t\tif (state.steps + node.size > search.budget) {\n\t\t\tthrow new OutOfBudget()\n\t\t}\n\t\tconst field = nextField(node)\n\t\t// The cells are tried in order, until one gives values. A fold over\n\t\t// the cells does no work after a found answer, and it does not grow\n\t\t// the stack with the number of cells. Each call for the next field\n\t\t// is a step of the runner, so very many fields do not fill the stack.\n\t\treturn R.map(\n\t\t\tR.reduce(\n\t\t\t\tsearch.cells(field),\n\t\t\t\t{\n\t\t\t\t\tassignment: Option.none(),\n\t\t\t\t\tstate: { ...state, steps: state.steps + node.size },\n\t\t\t\t} as Found,\n\t\t\t\t(before, cell): Trampoline.Step<Found> => {\n\t\t\t\t\tif (Option.isSome(before.assignment)) {\n\t\t\t\t\t\treturn R.succeed(before)\n\t\t\t\t\t}\n\t\t\t\t\tconst steps = before.state.steps + node.size\n\t\t\t\t\tif (steps > search.budget) {\n\t\t\t\t\t\tthrow new OutOfBudget()\n\t\t\t\t\t}\n\t\t\t\t\treturn R.suspend(() =>\n\t\t\t\t\t\tgo(fix(node, field, cell, search), { ...before.state, steps }, [\n\t\t\t\t\t\t\t...assignment,\n\t\t\t\t\t\t\t[field, cell],\n\t\t\t\t\t\t]),\n\t\t\t\t\t)\n\t\t\t\t},\n\t\t\t),\n\t\t\t(result) => {\n\t\t\t\tif (Option.isNone(result.assignment)) {\n\t\t\t\t\tsearch.failed.add(key)\n\t\t\t\t}\n\t\t\t\treturn result\n\t\t\t},\n\t\t)\n\t}\n\treturn go\n}\n\n/**\n * Puts a value at a path in an object, and gives a new object. A value with\n * parts (`opaque`) becomes an empty object, and it keeps an object that is\n * there.\n */\ntype Fields = Readonly<Record<string, unknown>>\n\nconst setPath = (\n\troot: Fields,\n\tnames: ReadonlyArray<string>,\n\tvalue: unknown,\n): Fields => {\n\tconst last = names[names.length - 1]\n\tif (last === undefined) {\n\t\treturn root\n\t}\n\t// Only an own field counts, as a condition reads it. An inherited one,\n\t// such as `__proto__`, is not a field of the example.\n\tconst own = (base: Fields, name: string): unknown =>\n\t\tObject.hasOwn(base, name) ? base[name] : undefined\n\t// The object at each part of the path, from the root down to the object\n\t// that gets the value. A part that is not an object becomes empty. The\n\t// path is walked in a loop, so a long path does not fill the stack.\n\tconst bases = Arr.scan(names.slice(0, -1), root, (base, name): Fields => {\n\t\tconst inner = own(base, name)\n\t\treturn isObject(inner) && inner !== opaque ? (inner as Fields) : {}\n\t})\n\tconst target = bases[bases.length - 1]!\n\tconst inner = own(target, last)\n\tconst placed: Fields =\n\t\tvalue === opaque && isObject(inner)\n\t\t\t? target\n\t\t\t: { ...target, [last]: value === opaque ? {} : value }\n\t// Each object above gets the new object below it, from the bottom up.\n\treturn Arr.reduceRight(\n\t\tnames.slice(0, -1),\n\t\tplaced,\n\t\t(below, name, index): Fields => ({ ...bases[index]!, [name]: below }),\n\t)\n}\n\nconst exampleOf = (\n\tassignment: ReadonlyArray<readonly [string, unknown]>,\n): Example =>\n\tArr.reduce(\n\t\tassignment,\n\t\t{} as Readonly<Record<string, unknown>>,\n\t\t(root, [field, value]) => setPath(root, field.split(\".\"), value),\n\t)\n\n/**\n * The constants that the tests of each field use. They come from a policy, so\n * they go into a native `Map` and `Set`, as in {@link dedupeCells}. The map\n * belongs to one search.\n */\nconst constantsOf = (node: Node): Map<string, Set<Nodes.Scalar>> => {\n\ttype Pair = readonly [string, ReadonlyArray<Nodes.Scalar>]\n\t// The walk takes the steps of a trampoline, so a deep condition does not\n\t// fill the stack.\n\tconst pairs = Trampoline.run(node.depth, (R) => {\n\t\tconst go = (current: Node): Trampoline.Step<ReadonlyArray<Pair>> => {\n\t\t\tswitch (current._tag) {\n\t\t\t\tcase \"Compare\":\n\t\t\t\t\treturn R.succeed([[current.field, [literalOf(current.value)]]])\n\t\t\t\tcase \"In\":\n\t\t\t\t\treturn R.succeed([\n\t\t\t\t\t\t[current.field, Arr.map(current.values, literalOf)],\n\t\t\t\t\t])\n\t\t\t\tcase \"Exists\":\n\t\t\t\t\treturn R.succeed([[current.field, []]])\n\t\t\t\tcase \"Under\":\n\t\t\t\t\tthrow new Error(\n\t\t\t\t\t\t\"Condition.satisfiable needs a condition with no Under nodes. Call Condition.partial first.\",\n\t\t\t\t\t)\n\t\t\t\tcase \"And\":\n\t\t\t\tcase \"Or\":\n\t\t\t\t\treturn R.map(\n\t\t\t\t\t\tR.forEach(current.conditions, (child) =>\n\t\t\t\t\t\t\tR.suspend(() => go(child)),\n\t\t\t\t\t\t),\n\t\t\t\t\t\tArr.flatten,\n\t\t\t\t\t)\n\t\t\t\tcase \"Not\":\n\t\t\t\t\treturn R.suspend(() => go(current.condition))\n\t\t\t\tdefault:\n\t\t\t\t\treturn R.succeed([])\n\t\t\t}\n\t\t}\n\t\treturn go(node)\n\t})\n\tconst all = new Map<string, Set<Nodes.Scalar>>()\n\tArr.forEach(pairs, ([field, values]) => {\n\t\tconst known = all.get(field) ?? new Set<Nodes.Scalar>()\n\t\tArr.forEach(values, (value) => known.add(value))\n\t\tall.set(field, known)\n\t})\n\treturn all\n}\n\n/** Gives the kinds of each field of a catalog. A field that it does not have may hold any value. */\nexport const kindsIn =\n\t(catalog: Catalog.Catalog | undefined, parts?: number) =>\n\t(field: string): Catalog.Kinds =>\n\t\t(catalog === undefined ? undefined : Catalog.lookup(catalog, field, parts))\n\t\t\t?.kinds ?? Catalog.anyKinds\n\n/**\n * Searches for values that make a condition true. `kindsOf` gives the kinds\n * of values of each field. The condition must have no principal fields and no\n * `Under` nodes.\n */\nexport const satisfiable = (\n\tnode: Node,\n\tkindsOf: (field: string) => Catalog.Kinds,\n\tbudget: number = Limits.searchSteps,\n\tfields: number = Limits.searchFields,\n): Satisfiability => {\n\tif (node.opaque) {\n\t\treturn satisfiability.Unknown({ reason: \"predicate\" })\n\t}\n\tconst constants = constantsOf(node)\n\t// The search calls itself one time for each field that it fixes.\n\tif (constants.size > fields) {\n\t\treturn satisfiability.Unknown({ reason: \"budget\" })\n\t}\n\t// The cells of each field, in a native `Map` as the constants are.\n\tconst cells = new Map<string, ReadonlyArray<unknown>>()\n\tconst search: Search = {\n\t\tmembers: new Map(),\n\t\tfailed: new Set(),\n\t\tcells: (field) => {\n\t\t\tconst made = cells.get(field)\n\t\t\tif (made !== undefined) {\n\t\t\t\treturn made\n\t\t\t}\n\t\t\t// The cells come from the constants, so many constants are work\n\t\t\t// that the budget must count before the cells are made.\n\t\t\tconst known = constants.get(field) ?? new Set<Nodes.Scalar>()\n\t\t\tif (known.size > budget) {\n\t\t\t\tthrow new OutOfBudget()\n\t\t\t}\n\t\t\tconst found = cellsOf(kindsOf(field), Array.from(known))\n\t\t\tcells.set(field, found)\n\t\t\treturn found\n\t\t},\n\t\tbudget,\n\t}\n\ttry {\n\t\t// The search goes one call deeper for each field, so the runner is the\n\t\t// one for that many levels.\n\t\tconst found = Trampoline.run(constants.size, (R) =>\n\t\t\tsearchNode(search)(R)(Algebra.simplify(node), { steps: 0 }, []),\n\t\t)\n\t\treturn Option.match(found.assignment, {\n\t\t\tonNone: () => satisfiability.Unsatisfiable(),\n\t\t\tonSome: (assignment) =>\n\t\t\t\tsatisfiability.Satisfiable({ example: exampleOf(assignment) }),\n\t\t})\n\t} catch (error) {\n\t\tif (error instanceof OutOfBudget) {\n\t\t\treturn satisfiability.Unknown({ reason: \"budget\" })\n\t\t}\n\t\tthrow error\n\t}\n}\n"],"mappings":"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"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import * as Effect from "effect/Effect";
|
|
2
1
|
import * as Arr from "effect/Array";
|
|
2
|
+
import * as Effect from "effect/Effect";
|
|
3
3
|
import * as Option from "effect/Option";
|
|
4
4
|
import * as Cause from "effect/Cause";
|
|
5
5
|
import * as Exit from "effect/Exit";
|
|
@@ -28,9 +28,10 @@ const direct = {
|
|
|
28
28
|
suspend: (make) => make(),
|
|
29
29
|
map: (from, f) => step(f(value(from))),
|
|
30
30
|
flatMap: (from, f) => f(value(from)),
|
|
31
|
-
forEach: (items, f) => step(Arr.map(items,
|
|
32
|
-
|
|
33
|
-
|
|
31
|
+
forEach: (items, f) => step(Arr.map(items, f)),
|
|
32
|
+
reduce: (items, initial, f) => step(Arr.reduce(items, initial, f)),
|
|
33
|
+
some: (items, f) => step(Arr.some(items, f)),
|
|
34
|
+
every: (items, f) => step(Arr.every(items, f))
|
|
34
35
|
};
|
|
35
36
|
const effect = (step) => step;
|
|
36
37
|
const fromEffect = (effect) => effect;
|
|
@@ -41,6 +42,7 @@ const loop = {
|
|
|
41
42
|
map: (from, f) => fromEffect(Effect.map(effect(from), f)),
|
|
42
43
|
flatMap: (from, f) => fromEffect(Effect.flatMap(effect(from), (value) => effect(f(value)))),
|
|
43
44
|
forEach: (items, f) => fromEffect(Effect.forEach(items, (item, index) => effect(f(item, index)))),
|
|
45
|
+
reduce: (items, initial, f) => fromEffect(Effect.reduce(items, () => initial, (before, item) => effect(f(before, item)))),
|
|
44
46
|
some: (items, f) => fromEffect(Effect.map(Effect.findFirst(items, (item) => effect(f(item))), Option.isSome)),
|
|
45
47
|
every: (items, f) => fromEffect(Effect.map(Effect.findFirst(items, (item) => Effect.map(effect(f(item)), (value) => !value)), Option.isNone))
|
|
46
48
|
};
|
|
@@ -66,10 +68,22 @@ const runWith = (runner, walk) => {
|
|
|
66
68
|
});
|
|
67
69
|
};
|
|
68
70
|
/**
|
|
71
|
+
* Makes a walk of an input with an unknown depth, as the input of a decoder.
|
|
72
|
+
* It runs with `direct` while the input is low. A part more than
|
|
73
|
+
* `directDepth` levels down runs with `loop`, and the walk stays in the loop
|
|
74
|
+
* below it, so it goes into the loop one time on each path. `walk` gets the
|
|
75
|
+
* runner, and the call for a child at the next level: `child(item, depth)`.
|
|
76
|
+
*/
|
|
77
|
+
const adaptive = (walk) => {
|
|
78
|
+
const inLoop = walk(loop, (item, depth) => loop.suspend(() => inLoop(item, depth)));
|
|
79
|
+
const onStack = walk(direct, (item, depth) => depth > 200 ? step(runWith(loop, () => inLoop(item, depth))) : onStack(item, depth));
|
|
80
|
+
return (item) => value(onStack(item, 1));
|
|
81
|
+
};
|
|
82
|
+
/**
|
|
69
83
|
* Gives the Effect of a step of `loop`, so a walk can go on in an Effect
|
|
70
84
|
* that runs a predicate.
|
|
71
85
|
*/
|
|
72
86
|
const toEffect = (step) => effect(step);
|
|
73
|
-
export { direct, loop, run, runWith, runnerFor, toEffect };
|
|
87
|
+
export { adaptive, direct, loop, run, runWith, runnerFor, toEffect };
|
|
74
88
|
|
|
75
89
|
//# sourceMappingURL=trampoline.js.map
|
|
@@ -1 +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)
|
|
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/**\n\t * Runs a step for each item, in order, with the value of the step before.\n\t * It does not grow the stack with the number of items.\n\t */\n\treadonly reduce: <A, B>(\n\t\titems: ReadonlyArray<A>,\n\t\tinitial: B,\n\t\tf: (before: B, item: A) => Step<B>,\n\t) => Step<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. */\n// A step of `direct` is its value, so a function that gives a step gives a\n// value too. The runner passes such a function on with no wrap: a wrap for\n// each item would be a call more for each node of a walk.\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) => step(Arr.map(items, f as never)),\n\treduce: (items, initial, f) => step(Arr.reduce(items, initial, f as never)),\n\tsome: (items, f) => step(Arr.some(items, f as never)),\n\tevery: (items, f) => step(Arr.every(items, f as never)),\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\treduce: (items, initial, f) =>\n\t\tfromEffect(\n\t\t\tEffect.reduce(\n\t\t\t\titems,\n\t\t\t\t() => initial,\n\t\t\t\t(before, item) => effect(f(before, item)),\n\t\t\t),\n\t\t),\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 * Makes a walk of an input with an unknown depth, as the input of a decoder.\n * It runs with `direct` while the input is low. A part more than\n * `directDepth` levels down runs with `loop`, and the walk stays in the loop\n * below it, so it goes into the loop one time on each path. `walk` gets the\n * runner, and the call for a child at the next level: `child(item, depth)`.\n */\nexport const adaptive = <I, A>(\n\twalk: (\n\t\tR: Runner,\n\t\tchild: (item: I, depth: number) => Step<A>,\n\t) => (item: I, depth: number) => Step<A>,\n): ((item: I) => A) => {\n\tconst inLoop: (item: I, depth: number) => Step<A> = walk(\n\t\tloop,\n\t\t(item, depth) => loop.suspend(() => inLoop(item, depth)),\n\t)\n\tconst onStack: (item: I, depth: number) => Step<A> = walk(\n\t\tdirect,\n\t\t(item, depth) =>\n\t\t\tdepth > directDepth\n\t\t\t\t? step(runWith(loop, () => inLoop(item, depth)))\n\t\t\t\t: onStack(item, depth),\n\t)\n\treturn (item) => value(onStack(item, 1))\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":";;;;;;;;;;;;;;;;;;;;;;AAiEA,MAAM,SAAY,SAAqB;AACvC,MAAM,QAAW,UAAsB;;AAMvC,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,MAAM,KAAK,IAAI,IAAI,OAAO,CAAU,CAAC;CACtD,SAAS,OAAO,SAAS,MAAM,KAAK,IAAI,OAAO,OAAO,SAAS,CAAU,CAAC;CAC1E,OAAO,OAAO,MAAM,KAAK,IAAI,KAAK,OAAO,CAAU,CAAC;CACpD,QAAQ,OAAO,MAAM,KAAK,IAAI,MAAM,OAAO,CAAU,CAAC;AACvD;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,SAAS,OAAO,SAAS,MACxB,WACC,OAAO,OACN,aACM,UACL,QAAQ,SAAS,OAAO,EAAE,QAAQ,IAAI,CAAC,CACzC,CACD;CACD,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;;;;;;;;AASA,MAAa,YACZ,SAIsB;CACtB,MAAM,SAA8C,KACnD,OACC,MAAM,UAAU,KAAK,cAAc,OAAO,MAAM,KAAK,CAAC,CACxD;CACA,MAAM,UAA+C,KACpD,SACC,MAAM,UACN,QAAA,MACG,KAAK,QAAQ,YAAY,OAAO,MAAM,KAAK,CAAC,CAAC,IAC7C,QAAQ,MAAM,KAAK,CACxB;CACA,QAAQ,SAAS,MAAM,QAAQ,MAAM,CAAC,CAAC;AACxC;;;;;AAMA,MAAa,YAAe,SAAoC,OAAO,IAAI"}
|
package/package.json
CHANGED
package/src/Authorizer.ts
CHANGED
|
@@ -107,6 +107,16 @@ export type Pending = Internal.Pending
|
|
|
107
107
|
*/
|
|
108
108
|
export type Target<D> = Policy.Target<D> | Policy.KindTarget<D>
|
|
109
109
|
|
|
110
|
+
/**
|
|
111
|
+
* A {@link Target} with a field of any length, which a check checks with
|
|
112
|
+
* `Policy.CheckField`.
|
|
113
|
+
*
|
|
114
|
+
* @stability stable
|
|
115
|
+
* @category models
|
|
116
|
+
* @since 2.1.0
|
|
117
|
+
*/
|
|
118
|
+
export type DeepTarget<D> = Policy.DeepTarget<D> | Policy.DeepKindTarget<D>
|
|
119
|
+
|
|
110
120
|
/**
|
|
111
121
|
* A request to the decider.
|
|
112
122
|
*
|
|
@@ -164,12 +174,12 @@ export interface Authorizer<D extends Domain.Any, E, R> {
|
|
|
164
174
|
R
|
|
165
175
|
>
|
|
166
176
|
/** Gives the decision of the policy. It does not ask the decider. */
|
|
167
|
-
readonly decide: (
|
|
168
|
-
target: Policy.
|
|
177
|
+
readonly decide: <const T extends Policy.DeepTarget<D>>(
|
|
178
|
+
target: T & Policy.CheckField<D, T>,
|
|
169
179
|
) => Effect.Effect<Decision.Decision, PolicyUnavailable | E, R>
|
|
170
180
|
/** Succeeds when the principal may do the act, and fails when it may not. */
|
|
171
|
-
readonly check: (
|
|
172
|
-
target:
|
|
181
|
+
readonly check: <const T extends DeepTarget<D>>(
|
|
182
|
+
target: T & Policy.CheckField<D, T>,
|
|
173
183
|
) => Effect.Effect<void, Forbidden | Pending | PolicyUnavailable | E, R>
|
|
174
184
|
/**
|
|
175
185
|
* Checks the read action first, and fails with the error of `hide` when the
|
|
@@ -177,8 +187,8 @@ export interface Authorizer<D extends Domain.Any, E, R> {
|
|
|
177
187
|
* answer "not found" to a principal that may not know that a resource
|
|
178
188
|
* exists.
|
|
179
189
|
*/
|
|
180
|
-
readonly guard: <const T extends Policy.
|
|
181
|
-
target: T,
|
|
190
|
+
readonly guard: <const T extends Policy.DeepTarget<D>, E2>(
|
|
191
|
+
target: T & Policy.CheckField<D, T>,
|
|
182
192
|
options: GuardOptions<D, E2, T["resource"]["_tag"]>,
|
|
183
193
|
) => Effect.Effect<void, E2 | Forbidden | Pending | PolicyUnavailable | E, R>
|
|
184
194
|
/**
|
|
@@ -187,8 +197,8 @@ export interface Authorizer<D extends Domain.Any, E, R> {
|
|
|
187
197
|
* with `Condition.fold`. It is `False` when the principal may act on no
|
|
188
198
|
* value, so the list is empty: it is not an error.
|
|
189
199
|
*/
|
|
190
|
-
readonly filter: <const T extends Policy.
|
|
191
|
-
target: T,
|
|
200
|
+
readonly filter: <const T extends Policy.DeepKindTarget<D>>(
|
|
201
|
+
target: T & Policy.CheckField<D, T>,
|
|
192
202
|
) => Effect.Effect<
|
|
193
203
|
Condition.Condition<
|
|
194
204
|
Policy.ResidualReads<D, T["kind"]>,
|
|
@@ -202,8 +212,8 @@ export interface Authorizer<D extends Domain.Any, E, R> {
|
|
|
202
212
|
* the scope of the target. See `Policy.coverage`. Use it to show a button
|
|
203
213
|
* only when the answer is not `None`.
|
|
204
214
|
*/
|
|
205
|
-
readonly coverage: <const T extends Policy.
|
|
206
|
-
target: T,
|
|
215
|
+
readonly coverage: <const T extends Policy.DeepKindTarget<D>>(
|
|
216
|
+
target: T & Policy.CheckField<D, T>,
|
|
207
217
|
) => Effect.Effect<
|
|
208
218
|
Policy.Coverage<Policy.ResidualReads<D, T["kind"]>, Condition.Opaque<E>>,
|
|
209
219
|
PolicyUnavailable | E,
|
|
@@ -238,26 +248,26 @@ export interface ServiceClass<
|
|
|
238
248
|
readonly domain: D
|
|
239
249
|
/** The Effect that gives the principal of the current request. */
|
|
240
250
|
readonly principal: Effect.Effect<Domain.Principal<D>, E, R>
|
|
241
|
-
readonly decide: (
|
|
242
|
-
target: Policy.
|
|
251
|
+
readonly decide: <const T extends Policy.DeepTarget<D>>(
|
|
252
|
+
target: T & Policy.CheckField<D, T>,
|
|
243
253
|
) => Effect.Effect<Decision.Decision, PolicyUnavailable | E, R | Self>
|
|
244
|
-
readonly check: (
|
|
245
|
-
target:
|
|
254
|
+
readonly check: <const T extends DeepTarget<D>>(
|
|
255
|
+
target: T & Policy.CheckField<D, T>,
|
|
246
256
|
) => Effect.Effect<
|
|
247
257
|
void,
|
|
248
258
|
Forbidden | Pending | PolicyUnavailable | E,
|
|
249
259
|
R | Self
|
|
250
260
|
>
|
|
251
|
-
readonly guard: <const T extends Policy.
|
|
252
|
-
target: T,
|
|
261
|
+
readonly guard: <const T extends Policy.DeepTarget<D>, E2>(
|
|
262
|
+
target: T & Policy.CheckField<D, T>,
|
|
253
263
|
options: GuardOptions<D, E2, T["resource"]["_tag"]>,
|
|
254
264
|
) => Effect.Effect<
|
|
255
265
|
void,
|
|
256
266
|
E2 | Forbidden | Pending | PolicyUnavailable | E,
|
|
257
267
|
R | Self
|
|
258
268
|
>
|
|
259
|
-
readonly filter: <const T extends Policy.
|
|
260
|
-
target: T,
|
|
269
|
+
readonly filter: <const T extends Policy.DeepKindTarget<D>>(
|
|
270
|
+
target: T & Policy.CheckField<D, T>,
|
|
261
271
|
) => Effect.Effect<
|
|
262
272
|
Condition.Condition<
|
|
263
273
|
Policy.ResidualReads<D, T["kind"]>,
|
|
@@ -266,8 +276,8 @@ export interface ServiceClass<
|
|
|
266
276
|
PolicyUnavailable | E,
|
|
267
277
|
R | Self
|
|
268
278
|
>
|
|
269
|
-
readonly coverage: <const T extends Policy.
|
|
270
|
-
target: T,
|
|
279
|
+
readonly coverage: <const T extends Policy.DeepKindTarget<D>>(
|
|
280
|
+
target: T & Policy.CheckField<D, T>,
|
|
271
281
|
) => Effect.Effect<
|
|
272
282
|
Policy.Coverage<Policy.ResidualReads<D, T["kind"]>, Condition.Opaque<E>>,
|
|
273
283
|
PolicyUnavailable | E,
|