@nmnmcc/ability 2.1.3 → 2.1.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -3,9 +3,9 @@ import { adaptive } from "./trampoline.js";
3
3
  import * as Arr from "effect/Array";
4
4
  import * as HashMap from "effect/HashMap";
5
5
  import * as HashSet from "effect/HashSet";
6
+ import * as Option from "effect/Option";
6
7
  import * as Schema from "effect/Schema";
7
8
  import { hasProperty } from "effect/Predicate";
8
- import * as Option from "effect/Option";
9
9
  import * as SchemaAST from "effect/SchemaAST";
10
10
  // src/internal/catalog.ts
11
11
  /**
@@ -151,6 +151,8 @@ const hasCheckIn = adaptive((R, child) => ({ checks, id }, depth) => R.some(chec
151
151
  }, depth + 1) : R.succeed(false)));
152
152
  /** A struct with named fields, or with the keys of a record. */
153
153
  const isStruct = (ast) => ast !== void 0 && (ast.propertySignatures.length > 0 || ast.indexSignatures.length > 0);
154
+ /** Tests if a Schema holds only values that are not objects, as `bigint`. */
155
+ const isPrimitive = (ast) => ast._tag === "BigInt" || ast._tag === "Symbol" || ast._tag === "UniqueSymbol" || ast._tag === "Literal" && typeof ast.literal === "bigint";
154
156
  /** The kinds of the values of a Schema that is not a struct. */
155
157
  const kindsOf = (ast) => {
156
158
  switch (ast._tag) {
@@ -230,6 +232,7 @@ const placeOf = (place) => {
230
232
  const structs = Arr.filter(Arr.map(place.schemas, structOf), isStruct);
231
233
  const values = Arr.filter(place.schemas, (schema) => !isStruct(structOf(schema)));
232
234
  const valueKinds = Arr.reduce(values, noKinds, (kinds, schema) => joinKinds(kinds, kindsOf(structOf(schema) ?? schema)));
235
+ const objects = Arr.some(values, (schema) => !isPrimitive(schema) && kindsOf(structOf(schema) ?? schema).object);
233
236
  const kinds = place.top ? anyKinds : joinKinds(joinKinds(valueKinds, structs.length > 0 ? objectKinds : noKinds), place.missing ? nullKinds : noKinds);
234
237
  const records = Arr.filter(structs, (struct) => struct.indexSignatures.length > 0);
235
238
  const structProperties = Arr.map(structs, propertiesOf);
@@ -238,7 +241,7 @@ const placeOf = (place) => {
238
241
  const property = named.get(name);
239
242
  return property === void 0 ? [] : [property];
240
243
  });
241
- const top = place.top || valueKinds.object || properties.length < structs.length;
244
+ const top = place.top || objects || properties.length < structs.length;
242
245
  return [name, {
243
246
  schemas: Arr.flatMap(properties, (property) => alternatives(property.type, /* @__PURE__ */ new Set())),
244
247
  missing: place.missing || values.length > 0 || Arr.some(properties, (property) => SchemaAST.isOptional(property.type)),
@@ -247,13 +250,13 @@ const placeOf = (place) => {
247
250
  }];
248
251
  });
249
252
  const signatures = Arr.flatMap(records, (struct) => struct.indexSignatures);
250
- const keyTop = place.top || valueKinds.object || records.length < structs.length;
253
+ const keyTop = place.top || objects || records.length < structs.length;
251
254
  return {
252
255
  branch: {
253
256
  kinds,
254
257
  values: values.length > 0 || records.length > 0,
255
258
  structs: structs.length > 0,
256
- open: records.length > 0 || structs.length > 0 || valueKinds.object || place.top,
259
+ open: records.length > 0 || structs.length > 0 || objects || place.top,
257
260
  plain: place.plain,
258
261
  key: signatures.length === 0 ? void 0 : {
259
262
  _tag: "Key",
@@ -323,10 +326,13 @@ const fieldAt = (branch, path, parts) => ({
323
326
  * The fields that a list shows, a field before the fields below it, down to
324
327
  * `parts` parts. A path goes into a place that is on it already one time
325
328
  * only, so the list of a recursive Schema is finite. The walk keeps a stack
326
- * of the places to visit, so a deep Schema does not fill the stack. The
327
- * stack belongs to this walk.
329
+ * of the places to visit, so a deep Schema does not fill the stack. A Schema
330
+ * that shares one part in many places may have very many fields, so the
331
+ * walk counts the fields that it makes, and it stops at the first one past
332
+ * `most`. The stack and the count belong to this walk.
328
333
  */
329
- const listOf = (branches, parts) => {
334
+ const listOf = (branches, parts, most, thing) => {
335
+ const count = { fields: 0 };
330
336
  const below = (visit) => {
331
337
  const { branch, path } = visit;
332
338
  if (path.length === parts || path.length > 0 && HashSet.has(visit.above, branch.id)) return [];
@@ -354,20 +360,25 @@ const listOf = (branches, parts) => {
354
360
  rest.push(next);
355
361
  });
356
362
  const field = visit.path.length === 0 ? [] : [fieldAt(visit.branch, visit.path, parts)];
357
- if (field.length > 0 && rest.length + 1 > ceilings.catalogFields) throw exceeded("A Schema", "catalogFields", "fields", rest.length + 1, ceilings.catalogFields);
363
+ count.fields += field.length;
364
+ if (count.fields > most) throw exceeded(thing, "catalogFields", "fields", count.fields, most);
358
365
  return Option.some([field, rest]);
359
366
  }));
360
367
  };
361
368
  /**
362
- * The fields that a list shows for paths of at most `parts` parts, as for a
363
- * domain with that limit. See `Catalog.fields`.
369
+ * The fields that a list shows, a field before the fields below it, for
370
+ * paths of at most `parts` parts. For a recursive Schema, the list goes into
371
+ * each recursive place one time on each path, so it is finite. A list with
372
+ * more than `most` fields is an error, as for a domain with these limits.
373
+ * Each call makes the list again: a domain checks its size one time, and a
374
+ * list of permitted fields reads it.
364
375
  */
365
- const fieldsOf = (catalog, parts) => parts === fieldPathParts ? catalog.fields : listOf(catalog.branches, parts);
376
+ const fieldsOf = (catalog, parts = fieldPathParts, most = ceilings.catalogFields, thing = "A Schema") => listOf(catalog.branches, parts, most, thing);
366
377
  /**
367
- * Makes the catalog of a Schema, with the list of its fields for the default
368
- * limit of the parts of a path. It throws when a field name has a dot or is
369
- * empty, and when the Schema has more places or fields than the ceiling of
370
- * `catalogFields`. A domain checks the size of the list with its own limit.
378
+ * Makes the catalog of a Schema. It throws when a field name has a dot or is
379
+ * empty, and when the Schema has more places than the ceiling of
380
+ * `catalogFields`. A domain checks the size of the list of the fields with
381
+ * its own limit: see {@link fieldsOf}.
371
382
  */
372
383
  const catalogOf = (ast) => {
373
384
  const branches = branchesOf({
@@ -377,7 +388,6 @@ const catalogOf = (ast) => {
377
388
  plain: true
378
389
  });
379
390
  return {
380
- fields: listOf(branches, fieldPathParts),
381
391
  branches,
382
392
  edges: Arr.reduce(branches, 0, (total, branch) => total + branch.children.length + (branch.keyBranch === void 0 ? 0 : 1)),
383
393
  root: branches[0]
@@ -1 +1 @@
1
- {"version":3,"file":"catalog.js","names":["Trampoline.adaptive","Limits.exceeded","Limits.fieldPathParts"],"sources":["../../src/internal/catalog.ts"],"sourcesContent":["/**\n * The catalog of the fields of a Schema. One walk over the Schema makes it,\n * and every question about the fields of a resource or of the principal reads\n * it: which names a rule may use, which values a field may hold, which fields\n * a pattern fits, and which fields a list of permitted fields shows.\n *\n * The catalog describes the shape of the values, not the checks of the\n * Schema. A check, such as a minimum length, only keeps some values out, so a\n * catalog that leaves the checks out holds every real value and more. An\n * answer that holds for all of these values, as `All` and `None` of a\n * coverage do, then holds for the real values too.\n */\nimport * as Arr from \"effect/Array\"\nimport * as HashMap from \"effect/HashMap\"\nimport * as HashSet from \"effect/HashSet\"\nimport * as Option from \"effect/Option\"\nimport { hasProperty } from \"effect/Predicate\"\nimport * as Schema from \"effect/Schema\"\nimport * as SchemaAST from \"effect/SchemaAST\"\nimport * as Limits from \"./limits.ts\"\nimport * as Trampoline from \"./trampoline.ts\"\n\n/** The kinds of values that a field may hold. */\nexport interface Kinds {\n\t/** The field may be `null`, `undefined` or missing. */\n\treadonly null: boolean\n\treadonly boolean: boolean\n\t/** The field may hold any string. */\n\treadonly string: boolean\n\t/** The numbers that the field may hold. */\n\treadonly number: \"none\" | \"integer\" | \"finite\" | \"any\"\n\t/** The field may hold an object, or a value that is not a scalar. */\n\treadonly object: boolean\n\t/** Single values that the field may hold, other than the ones above. */\n\treadonly literals: ReadonlyArray<string | number | boolean>\n}\n\n/** No value: the kinds of a field of `Schema.Never`. */\nexport const noKinds: Kinds = {\n\tnull: false,\n\tboolean: false,\n\tstring: false,\n\tnumber: \"none\",\n\tobject: false,\n\tliterals: [],\n}\n\n/** Any value. */\nexport const anyKinds: Kinds = {\n\tnull: true,\n\tboolean: true,\n\tstring: true,\n\tnumber: \"any\",\n\tobject: true,\n\tliterals: [],\n}\n\nconst nullKinds: Kinds = { ...noKinds, null: true }\nconst objectKinds: Kinds = { ...noKinds, object: true }\n\nconst numberOrder = [\"none\", \"integer\", \"finite\", \"any\"] as const\n\n/** The kinds of a field that may hold a value of `left` or of `right`. */\nexport const joinKinds = (left: Kinds, right: Kinds): Kinds => ({\n\tnull: left.null || right.null,\n\tboolean: left.boolean || right.boolean,\n\tstring: left.string || right.string,\n\tnumber:\n\t\tnumberOrder.indexOf(left.number) >= numberOrder.indexOf(right.number)\n\t\t\t? left.number\n\t\t\t: right.number,\n\tobject: left.object || right.object,\n\t// A native `Set` finds copies about ten times as fast as `Arr.dedupe`. It\n\t// takes `0` and `-0` as one, as `includes` does when a test reads them.\n\tliterals: Array.from(new Set([...left.literals, ...right.literals])),\n})\n\n/** Tests if a number fits the numbers of a field. */\nexport const fitsNumber = (number: Kinds[\"number\"], value: number): boolean => {\n\tswitch (number) {\n\t\tcase \"none\":\n\t\t\treturn false\n\t\tcase \"integer\":\n\t\t\treturn Number.isInteger(value)\n\t\tcase \"finite\":\n\t\t\treturn Number.isFinite(value)\n\t\tcase \"any\":\n\t\t\treturn true\n\t}\n}\n\n/**\n * Tests if a field with these kinds may hold a value. A missing value and\n * `undefined` are `null`.\n */\nexport const accepts = (kinds: Kinds, value: unknown): boolean => {\n\tif (value === null || value === undefined) {\n\t\treturn kinds.null\n\t}\n\tswitch (typeof value) {\n\t\tcase \"boolean\":\n\t\t\treturn kinds.boolean || kinds.literals.includes(value)\n\t\tcase \"string\":\n\t\t\treturn kinds.string || kinds.literals.includes(value)\n\t\tcase \"number\":\n\t\t\treturn fitsNumber(kinds.number, value) || kinds.literals.includes(value)\n\t\tdefault:\n\t\t\treturn kinds.object\n\t}\n}\n\n/** A part of a field path that stands for any key of a record. */\nexport interface Key {\n\treadonly _tag: \"Key\"\n\t/** The Schemas of the keys. A key must fit one of them. */\n\treadonly parameters: ReadonlyArray<SchemaAST.AST>\n}\n\n/** One part of a field path: a name, or any key of a record. */\nexport type Segment = string | Key\n\n/** One field of a catalog. */\nexport interface Field {\n\treadonly path: ReadonlyArray<Segment>\n\t/** The path with dots, and `*` for a key of a record. */\n\treadonly text: string\n\treadonly kinds: Kinds\n\t/**\n\t * The field holds a value that a list of fields shows as one field: a\n\t * scalar, a record, an array, or a struct at the last part a path may have.\n\t */\n\treadonly listed: boolean\n\t/** The field may hold parts that the Schema does not name, as a record may. */\n\treadonly open: boolean\n\t/**\n\t * A read of the field finds only plain data. It is `false` when a value\n\t * above the field may be an object that does not have it as a data field:\n\t * a class without the field, which may have a getter of that name, a\n\t * value such as a date or a list, or any object, as of `Schema.Unknown`.\n\t * A struct of a union without the field makes it not plain too, since a\n\t * value made by hand may have it with any value.\n\t */\n\treadonly plain: boolean\n}\n\n/**\n * A place in the graph of a catalog. A path of a field leads to a place, and\n * many paths may lead to one place, as in a recursive Schema: `next` and\n * `next.next` of a list of links are one place. A place keeps the ids of the\n * places below it, so the graph has no loop of objects.\n */\nexport interface Branch {\n\treadonly id: number\n\t/** The kinds of the values of the field at this place. */\n\treadonly kinds: Kinds\n\t/** The field holds a scalar, a record or a list: see `Field.listed`. */\n\treadonly values: boolean\n\t/** The field may hold a struct. */\n\treadonly structs: boolean\n\treadonly open: boolean\n\treadonly plain: boolean\n\t/** The ids of the places below, by name, in the order of the Schema. */\n\treadonly children: ReadonlyArray<readonly [string, number]>\n\t/**\n\t * The places below, by name. A lookup gets each name from `split`, as a\n\t * new string, and a `HashMap` finds a new string about 20% faster than a\n\t * native `Map`. The names come from the Schema, so a name from a policy\n\t * can not make a long list of names with one hash here.\n\t */\n\treadonly names: HashMap.HashMap<string, number>\n\t/** The place for the keys of a record, when the value may be a record. */\n\treadonly key: Key | undefined\n\treadonly keyBranch: number | undefined\n}\n\n/** The catalog of the fields of a Schema. */\nexport interface Catalog {\n\t/**\n\t * The fields that a list shows, a field before the fields below it. For a\n\t * recursive Schema, the list goes into each recursive place one time on\n\t * each path, so it is finite.\n\t */\n\treadonly fields: ReadonlyArray<Field>\n\t/** The places, by id. The root is the place with the id 0. */\n\treadonly branches: ReadonlyArray<Branch>\n\t/**\n\t * The number of steps from a place to a place below it. A search of the\n\t * catalog, as for a field pattern, reads each one, so this is its work.\n\t */\n\treadonly edges: number\n\treadonly root: Branch\n}\n\n/**\n * Tests if a key of an object fits the Schemas of the keys of a record. A key\n * of an object is a string, also a key that a number Schema takes: `{ 1: \"a\" }`\n * has the key `\"1\"`. So a key that is the text of a number is tried as that\n * number too.\n */\nexport const keyFits = (key: Key, name: string): boolean =>\n\tArr.some(key.parameters, (parameter) => {\n\t\tconst is = Schema.is(Schema.make(parameter))\n\t\treturn (\n\t\t\tis(name) ||\n\t\t\t(name !== \"\" && String(Number(name)) === name && is(Number(name)))\n\t\t)\n\t})\n\n/**\n * Gives the struct of a `Schema.Class`. A class is a declaration with one\n * struct as its type parameter, and Effect marks it with a `\"~constructor\"`\n * annotation, also through more codec steps, as in\n * `Schema.fromJsonString(User)`. Other declarations, such as `Schema.Option`\n * or `Schema.ReadonlyMap`, have no such mark, and the fields of their type\n * parameter are not fields of the value.\n */\nconst classStruct = (\n\tast: SchemaAST.Declaration,\n): SchemaAST.Objects | undefined => {\n\tconst [inner] = ast.typeParameters\n\treturn ast.annotations?.[\"~constructor\"] !== undefined &&\n\t\tast.typeParameters.length === 1 &&\n\t\tinner?._tag === \"Objects\"\n\t\t? inner\n\t\t: undefined\n}\n\n/**\n * Gives the Schemas that a value at one place may fit: it opens unions and\n * `Schema.suspend`. A class stays a class: see {@link structOf}. The open suspend nodes are in a native `Set`,\n * because the walk must find a node by the node itself: `Equal` compares\n * Schema nodes by structure, and a recursive Schema has no end. A suspend\n * node that gives itself again, with no step on the path, gives nothing.\n */\nconst alternatives = (\n\tast: SchemaAST.AST,\n\topen: ReadonlySet<SchemaAST.AST>,\n): ReadonlyArray<SchemaAST.AST> => alternativesOf({ ast, open })\n\n/**\n * The walk of {@link alternatives}. A union may hold a union, very many\n * levels down, so it is a walk of the trampoline for an input of an unknown\n * depth.\n */\nconst alternativesOf = Trampoline.adaptive<\n\t{ readonly ast: SchemaAST.AST; readonly open: ReadonlySet<SchemaAST.AST> },\n\tReadonlyArray<SchemaAST.AST>\n>((R, child) => ({ ast, open }, depth) => {\n\tswitch (ast._tag) {\n\t\tcase \"Union\":\n\t\t\treturn R.map(\n\t\t\t\tR.forEach(ast.types, (type) => child({ ast: type, open }, depth + 1)),\n\t\t\t\tArr.flatten,\n\t\t\t)\n\t\tcase \"Suspend\":\n\t\t\treturn open.has(ast)\n\t\t\t\t? R.succeed([])\n\t\t\t\t: child({ ast: ast.thunk(), open: new Set([...open, ast]) }, depth + 1)\n\t\tdefault:\n\t\t\treturn R.succeed([ast])\n\t}\n})\n\n/** The struct of a Schema: a struct, or the struct of a class. */\nconst structOf = (ast: SchemaAST.AST): SchemaAST.Objects | undefined =>\n\tast._tag === \"Objects\"\n\t\t? ast\n\t\t: ast._tag === \"Declaration\"\n\t\t\t? classStruct(ast)\n\t\t\t: undefined\n\nconst hasCheck = (\n\tchecks: ReadonlyArray<SchemaAST.Check<never>>,\n\tid: string,\n): boolean => hasCheckIn({ checks, id })\n\n/**\n * The walk of {@link hasCheck}. A group of checks may hold a group, very many\n * levels down, as `.and(...)` many times makes, so it is a walk of the\n * trampoline for an input of an unknown depth.\n */\nconst hasCheckIn = Trampoline.adaptive<\n\t{\n\t\treadonly checks: ReadonlyArray<SchemaAST.Check<never>>\n\t\treadonly id: string\n\t},\n\tboolean\n>(\n\t(R, child) =>\n\t\t({ checks, id }, depth) =>\n\t\t\tR.some(checks, (check) =>\n\t\t\t\tcheck.annotations?.representation?.id === id\n\t\t\t\t\t? R.succeed(true)\n\t\t\t\t\t: check._tag === \"FilterGroup\"\n\t\t\t\t\t\t? child({ checks: check.checks, id }, depth + 1)\n\t\t\t\t\t\t: R.succeed(false),\n\t\t\t),\n)\n\n/** A struct with named fields, or with the keys of a record. */\nconst isStruct = (\n\tast: SchemaAST.Objects | undefined,\n): ast is SchemaAST.Objects =>\n\tast !== undefined &&\n\t(ast.propertySignatures.length > 0 || ast.indexSignatures.length > 0)\n\n/** The kinds of the values of a Schema that is not a struct. */\nconst kindsOf = (ast: SchemaAST.AST): Kinds => {\n\tswitch (ast._tag) {\n\t\tcase \"Null\":\n\t\tcase \"Undefined\":\n\t\t\treturn nullKinds\n\t\tcase \"Never\":\n\t\t\treturn noKinds\n\t\tcase \"String\":\n\t\tcase \"TemplateLiteral\":\n\t\t\treturn { ...noKinds, string: true }\n\t\tcase \"Boolean\":\n\t\t\treturn { ...noKinds, boolean: true }\n\t\tcase \"Number\": {\n\t\t\t// The two checks that a search uses. Effect names a number with\n\t\t\t// them, as `Schema.Int` and `Schema.Finite`; other checks are left\n\t\t\t// out, as all checks are.\n\t\t\tconst checks = ast.checks ?? []\n\t\t\treturn {\n\t\t\t\t...noKinds,\n\t\t\t\tnumber: hasCheck(checks, \"effect/schema/isInt\")\n\t\t\t\t\t? \"integer\"\n\t\t\t\t\t: hasCheck(checks, \"effect/schema/isFinite\")\n\t\t\t\t\t\t? \"finite\"\n\t\t\t\t\t\t: \"any\",\n\t\t\t}\n\t\t}\n\t\tcase \"Literal\":\n\t\t\treturn typeof ast.literal === \"bigint\"\n\t\t\t\t? objectKinds\n\t\t\t\t: { ...noKinds, literals: [ast.literal] }\n\t\tcase \"Enum\":\n\t\t\treturn { ...noKinds, literals: Arr.map(ast.enums, ([, value]) => value) }\n\t\tcase \"Arrays\":\n\t\tcase \"ObjectKeyword\":\n\t\tcase \"BigInt\":\n\t\tcase \"Symbol\":\n\t\tcase \"UniqueSymbol\":\n\t\t\treturn objectKinds\n\t\tcase \"Declaration\":\n\t\t\t// A declaration of Effect in `objectDeclarations` holds an object,\n\t\t\t// such as a `Date`. Another declaration may hold any value, as\n\t\t\t// `Schema.Json` or one from `Schema.declare` may.\n\t\t\treturn HashSet.has(objectDeclarations, declarationId(ast))\n\t\t\t\t? objectKinds\n\t\t\t\t: anyKinds\n\t\tdefault:\n\t\t\t// `Schema.Unknown`, `Schema.Void`, and `Struct({})`, which takes a\n\t\t\t// string too: these may hold any value.\n\t\t\treturn anyKinds\n\t}\n}\n\n/**\n * The ids of the declarations of Effect that hold only objects. Effect puts\n * the id in the `representation` annotation. A declaration that is not in\n * this list may hold a scalar, as `Schema.Json` may, so its kinds are any\n * value.\n */\nconst objectDeclarations = HashSet.make(\n\t\"effect/schema/Date\",\n\t\"effect/schema/URL\",\n\t\"effect/schema/Uint8Array\",\n\t\"effect/schema/Duration\",\n\t\"effect/schema/DateTimeUtc\",\n\t\"effect/schema/DateTimeZoned\",\n\t\"effect/schema/BigDecimal\",\n\t\"effect/schema/Redacted\",\n\t\"effect/schema/Cause\",\n\t\"effect/schema/Chunk\",\n\t\"effect/schema/HashMap\",\n\t\"effect/schema/HashSet\",\n\t\"effect/schema/ReadonlyMap\",\n\t\"effect/schema/ReadonlySet\",\n\t\"effect/schema/Option\",\n\t\"effect/schema/Result\",\n\t\"effect/schema/Exit\",\n\t\"effect/schema/File\",\n\t\"effect/schema/FormData\",\n\t\"effect/schema/RegExp\",\n\t\"effect/schema/URLSearchParams\",\n)\n\nconst declarationId = (ast: SchemaAST.Declaration): string | undefined => {\n\tconst representation: unknown = ast.annotations?.[\"representation\"]\n\treturn hasProperty(representation, \"id\") &&\n\t\ttypeof representation.id === \"string\"\n\t\t? representation.id\n\t\t: undefined\n}\n\n/** The things that the walk of a catalog knows at one place. */\ninterface Place {\n\treadonly schemas: ReadonlyArray<SchemaAST.AST>\n\t/** The value may be missing. */\n\treadonly missing: boolean\n\t/** The value may be anything, as an extra field of a struct may be. */\n\treadonly top: boolean\n\t/** A read here finds only plain data: see {@link Field.plain}. */\n\treadonly plain: boolean\n}\n\nconst textOf = (path: ReadonlyArray<Segment>): string =>\n\tArr.map(path, (segment) =>\n\t\ttypeof segment === \"string\" ? segment : \"*\",\n\t).join(\".\")\n\n/**\n * The name of a property as a part of a path. A key of an object is a string,\n * so a number key, as from `Schema.Record(Schema.Literal(1), ...)`, has the\n * name `\"1\"`. A symbol key is not a field.\n */\nconst nameOf = (signature: SchemaAST.PropertySignature): string | undefined =>\n\ttypeof signature.name === \"symbol\" ? undefined : String(signature.name)\n\n/**\n * The properties of a struct by name. A walk makes it one time for each\n * struct of a place, so a struct with very many fields does not take a\n * search for each name. It is a native `Map`, as `Branch.names` is.\n */\nconst propertiesOf = (\n\tstruct: SchemaAST.Objects,\n): ReadonlyMap<string, SchemaAST.PropertySignature> =>\n\tnew Map(\n\t\tArr.flatMap(struct.propertySignatures, (signature) => {\n\t\t\tconst name = nameOf(signature)\n\t\t\treturn name === undefined ? [] : [[name, signature] as const]\n\t\t}),\n\t)\n\n/** The names of the fields of the structs, in the order that they first come. */\nconst namesOf = (\n\tstructs: ReadonlyArray<SchemaAST.Objects>,\n): ReadonlyArray<string> =>\n\t// A native `Set` keeps the first copy of each name, in order, about ten\n\t// times as fast as `Arr.dedupe`.\n\tArray.from(\n\t\tnew Set(\n\t\t\tArr.flatMap(structs, (struct) =>\n\t\t\t\tArr.flatMap(struct.propertySignatures, (signature) => {\n\t\t\t\t\tconst name = nameOf(signature)\n\t\t\t\t\tif (name === undefined) {\n\t\t\t\t\t\treturn []\n\t\t\t\t\t}\n\t\t\t\t\t// A dot in a path is the step into a nested field. A field name with\n\t\t\t\t\t// a dot would make two fields have one path, so it is a defect.\n\t\t\t\t\tif (name.includes(\".\")) {\n\t\t\t\t\t\tthrow new Error(`A field name may not have a dot: \"${name}\"`)\n\t\t\t\t\t}\n\t\t\t\t\t// An empty name would make an empty part in a path, such as\n\t\t\t\t\t// `.secret`.\n\t\t\t\t\tif (name === \"\") {\n\t\t\t\t\t\tthrow new Error(\"A field name may not be empty\")\n\t\t\t\t\t}\n\t\t\t\t\treturn [name]\n\t\t\t\t}),\n\t\t\t),\n\t\t),\n\t)\n\n/** A place of the walk, and the places below it, before they have ids. */\ninterface Made {\n\treadonly branch: Omit<Branch, \"id\" | \"children\" | \"names\" | \"keyBranch\">\n\treadonly children: ReadonlyArray<readonly [string, Place]>\n\treadonly keyPlace: Place | undefined\n}\n\n/** Works out the facts of a place, and the places below it. */\nconst placeOf = (place: Place): Made => {\n\tconst structs = Arr.filter(Arr.map(place.schemas, structOf), isStruct)\n\tconst values = Arr.filter(\n\t\tplace.schemas,\n\t\t(schema) => !isStruct(structOf(schema)),\n\t)\n\tconst valueKinds = Arr.reduce(values, noKinds, (kinds, schema) =>\n\t\tjoinKinds(kinds, kindsOf(structOf(schema) ?? schema)),\n\t)\n\tconst kinds = place.top\n\t\t? anyKinds\n\t\t: joinKinds(\n\t\t\t\tjoinKinds(valueKinds, structs.length > 0 ? objectKinds : noKinds),\n\t\t\t\tplace.missing ? nullKinds : noKinds,\n\t\t\t)\n\tconst records = Arr.filter(\n\t\tstructs,\n\t\t(struct) => struct.indexSignatures.length > 0,\n\t)\n\tconst structProperties = Arr.map(structs, propertiesOf)\n\tconst children = Arr.map(namesOf(structs), (name) => {\n\t\tconst properties = Arr.flatMap(structProperties, (named) => {\n\t\t\tconst property = named.get(name)\n\t\t\treturn property === undefined ? [] : [property]\n\t\t})\n\t\t// A struct without the name may still have it as an extra field, with\n\t\t// any value: only a decode or a `make` takes extra fields away, and a\n\t\t// decision may get a value made by hand. A value that may be any\n\t\t// object, as one of `Schema.Unknown` may, may have the name with any\n\t\t// value too.\n\t\tconst top =\n\t\t\tplace.top || valueKinds.object || properties.length < structs.length\n\t\tconst below: Place = {\n\t\t\tschemas: Arr.flatMap(properties, (property) =>\n\t\t\t\talternatives(property.type, new Set()),\n\t\t\t),\n\t\t\t// A field below a value that is not a struct reads `null`, and so\n\t\t\t// does a field that may be missing.\n\t\t\tmissing:\n\t\t\t\tplace.missing ||\n\t\t\t\tvalues.length > 0 ||\n\t\t\t\tArr.some(properties, (property) => SchemaAST.isOptional(property.type)),\n\t\t\ttop,\n\t\t\t// A value that may be anything may be a list, or have a getter, so\n\t\t\t// a read below it may not find plain data.\n\t\t\tplain: place.plain && !top,\n\t\t}\n\t\treturn [name, below] as const\n\t})\n\tconst signatures = Arr.flatMap(records, (struct) => struct.indexSignatures)\n\tconst keyTop =\n\t\tplace.top || valueKinds.object || records.length < structs.length\n\treturn {\n\t\tbranch: {\n\t\t\tkinds,\n\t\t\tvalues: values.length > 0 || records.length > 0,\n\t\t\tstructs: structs.length > 0,\n\t\t\t// A field that is a scalar in one branch of a union and a struct in\n\t\t\t// another holds parts too.\n\t\t\topen:\n\t\t\t\trecords.length > 0 ||\n\t\t\t\tstructs.length > 0 ||\n\t\t\t\tvalueKinds.object ||\n\t\t\t\tplace.top,\n\t\t\tplain: place.plain,\n\t\t\tkey:\n\t\t\t\tsignatures.length === 0\n\t\t\t\t\t? undefined\n\t\t\t\t\t: {\n\t\t\t\t\t\t\t_tag: \"Key\",\n\t\t\t\t\t\t\tparameters: Arr.map(\n\t\t\t\t\t\t\t\tsignatures,\n\t\t\t\t\t\t\t\t(signature) => signature.parameter,\n\t\t\t\t\t\t\t),\n\t\t\t\t\t\t},\n\t\t},\n\t\tchildren,\n\t\tkeyPlace:\n\t\t\tsignatures.length === 0\n\t\t\t\t? undefined\n\t\t\t\t: {\n\t\t\t\t\t\tschemas: Arr.flatMap(signatures, (signature) =>\n\t\t\t\t\t\t\talternatives(signature.type, new Set()),\n\t\t\t\t\t\t),\n\t\t\t\t\t\t// A record may not have a given key.\n\t\t\t\t\t\tmissing: true,\n\t\t\t\t\t\ttop: keyTop,\n\t\t\t\t\t\tplain: place.plain && !keyTop,\n\t\t\t\t\t},\n\t}\n}\n\n/**\n * Makes the places of a Schema. Each place comes one time: two paths with the\n * same Schemas and the same marks lead to one place. The walk keeps a queue\n * of the places that it has found, and it makes them in order, so a deep\n * Schema does not fill the stack.\n */\nconst branchesOf = (root: Place): ReadonlyArray<Branch> => {\n\t// The id of each Schema node, and of each place, for this walk. The ids\n\t// are in native maps, because the walk must find a Schema node by the node\n\t// itself: `Equal` compares Schema nodes by structure, and a recursive\n\t// Schema has no end. The maps and the queue belong to this walk.\n\tconst astIds = new Map<SchemaAST.AST, number>()\n\tconst placeIds = new Map<string, number>()\n\tconst queue: Array<Place> = []\n\tconst idOf = (place: Place): number => {\n\t\tconst ids = Arr.map(place.schemas, (schema) => {\n\t\t\tconst known = astIds.get(schema)\n\t\t\tif (known !== undefined) {\n\t\t\t\treturn known\n\t\t\t}\n\t\t\tastIds.set(schema, astIds.size)\n\t\t\treturn astIds.size - 1\n\t\t})\n\t\tconst key = `${Array.from(new Set(ids))\n\t\t\t.sort((a, b) => a - b)\n\t\t\t.join(\n\t\t\t\t\",\",\n\t\t\t)}|${String(place.missing)}|${String(place.top)}|${String(place.plain)}`\n\t\tconst known = placeIds.get(key)\n\t\tif (known !== undefined) {\n\t\t\treturn known\n\t\t}\n\t\tif (queue.length >= Limits.ceilings.catalogFields) {\n\t\t\tthrow Limits.exceeded(\n\t\t\t\t\"A Schema\",\n\t\t\t\t\"catalogFields\",\n\t\t\t\t\"places\",\n\t\t\t\tqueue.length + 1,\n\t\t\t\tLimits.ceilings.catalogFields,\n\t\t\t)\n\t\t}\n\t\tplaceIds.set(key, queue.length)\n\t\tqueue.push(place)\n\t\treturn queue.length - 1\n\t}\n\tidOf(root)\n\treturn Arr.unfold(0, (id) => {\n\t\tconst place = queue[id]\n\t\tif (place === undefined) {\n\t\t\treturn Option.none()\n\t\t}\n\t\tconst made = placeOf(place)\n\t\tconst children = Arr.map(\n\t\t\tmade.children,\n\t\t\t([name, below]) => [name, idOf(below)] as const,\n\t\t)\n\t\tconst branch: Branch = {\n\t\t\t...made.branch,\n\t\t\tid,\n\t\t\tchildren,\n\t\t\tnames: HashMap.fromIterable(children),\n\t\t\tkeyBranch: made.keyPlace === undefined ? undefined : idOf(made.keyPlace),\n\t\t}\n\t\treturn Option.some([branch, id + 1] as const)\n\t})\n}\n\n/** The field at a place, for a path. */\nconst fieldAt = (\n\tbranch: Branch,\n\tpath: ReadonlyArray<Segment>,\n\tparts: number,\n): Field => ({\n\tpath,\n\ttext: textOf(path),\n\tkinds: branch.kinds,\n\t// A struct at the last part that a path may have is one field, since a\n\t// path does not go below it.\n\tlisted: branch.values || (path.length === parts && branch.structs),\n\topen: branch.open,\n\tplain: branch.plain,\n})\n\n/**\n * The fields that a list shows, a field before the fields below it, down to\n * `parts` parts. A path goes into a place that is on it already one time\n * only, so the list of a recursive Schema is finite. The walk keeps a stack\n * of the places to visit, so a deep Schema does not fill the stack. The\n * stack belongs to this walk.\n */\nconst listOf = (\n\tbranches: ReadonlyArray<Branch>,\n\tparts: number,\n): ReadonlyArray<Field> => {\n\tinterface Visit {\n\t\treadonly branch: Branch\n\t\treadonly path: ReadonlyArray<Segment>\n\t\t/** The ids of the places on the path, as a set of Effect that shares its parts. */\n\t\treadonly above: HashSet.HashSet<number>\n\t}\n\tconst below = (visit: Visit): ReadonlyArray<Visit> => {\n\t\tconst { branch, path } = visit\n\t\tif (\n\t\t\tpath.length === parts ||\n\t\t\t(path.length > 0 && HashSet.has(visit.above, branch.id))\n\t\t) {\n\t\t\treturn []\n\t\t}\n\t\tconst above = HashSet.add(visit.above, branch.id)\n\t\tconst named = Arr.map(branch.children, ([name, id]) => ({\n\t\t\tbranch: branches[id]!,\n\t\t\tpath: [...path, name],\n\t\t\tabove,\n\t\t}))\n\t\treturn branch.key === undefined || branch.keyBranch === undefined\n\t\t\t? named\n\t\t\t: [\n\t\t\t\t\t...named,\n\t\t\t\t\t{\n\t\t\t\t\t\tbranch: branches[branch.keyBranch]!,\n\t\t\t\t\t\tpath: [...path, branch.key],\n\t\t\t\t\t\tabove,\n\t\t\t\t\t},\n\t\t\t\t]\n\t}\n\tconst stack: Array<Visit> = [\n\t\t{ branch: branches[0]!, path: [], above: HashSet.empty() },\n\t]\n\treturn Arr.flatten(\n\t\tArr.unfold(stack, (rest) => {\n\t\t\tconst visit = rest.pop()\n\t\t\tif (visit === undefined) {\n\t\t\t\treturn Option.none()\n\t\t\t}\n\t\t\t// The visits go onto the stack one at a time: a spread into a call\n\t\t\t// would pass one argument for each field, past the most arguments\n\t\t\t// of a call.\n\t\t\tArr.forEach(Arr.reverse(below(visit)), (next) => {\n\t\t\t\trest.push(next)\n\t\t\t})\n\t\t\tconst field =\n\t\t\t\tvisit.path.length === 0\n\t\t\t\t\t? []\n\t\t\t\t\t: [fieldAt(visit.branch, visit.path, parts)]\n\t\t\tif (field.length > 0 && rest.length + 1 > Limits.ceilings.catalogFields) {\n\t\t\t\tthrow Limits.exceeded(\n\t\t\t\t\t\"A Schema\",\n\t\t\t\t\t\"catalogFields\",\n\t\t\t\t\t\"fields\",\n\t\t\t\t\trest.length + 1,\n\t\t\t\t\tLimits.ceilings.catalogFields,\n\t\t\t\t)\n\t\t\t}\n\t\t\treturn Option.some([field, rest] as const)\n\t\t}),\n\t)\n}\n\n/**\n * The fields that a list shows for paths of at most `parts` parts, as for a\n * domain with that limit. See `Catalog.fields`.\n */\nexport const fieldsOf = (\n\tcatalog: Catalog,\n\tparts: number,\n): ReadonlyArray<Field> =>\n\tparts === Limits.fieldPathParts\n\t\t? catalog.fields\n\t\t: listOf(catalog.branches, parts)\n\n/**\n * Makes the catalog of a Schema, with the list of its fields for the default\n * limit of the parts of a path. It throws when a field name has a dot or is\n * empty, and when the Schema has more places or fields than the ceiling of\n * `catalogFields`. A domain checks the size of the list with its own limit.\n */\nexport const catalogOf = (ast: SchemaAST.AST): Catalog => {\n\tconst branches = branchesOf({\n\t\tschemas: alternatives(ast, new Set()),\n\t\tmissing: false,\n\t\ttop: false,\n\t\tplain: true,\n\t})\n\treturn {\n\t\tfields: listOf(branches, Limits.fieldPathParts),\n\t\tbranches,\n\t\tedges: Arr.reduce(\n\t\t\tbranches,\n\t\t\t0,\n\t\t\t(total, branch) =>\n\t\t\t\ttotal +\n\t\t\t\tbranch.children.length +\n\t\t\t\t(branch.keyBranch === undefined ? 0 : 1),\n\t\t),\n\t\troot: branches[0]!,\n\t}\n}\n\n/**\n * Gives the field of a catalog at a path with dots, if the catalog has it.\n * A path has at most `parts` parts. The lookup follows one place for each\n * part, in a loop.\n */\nexport const lookup = (\n\tcatalog: Catalog,\n\tpath: string,\n\tparts: number = Limits.fieldPathParts,\n): Field | undefined => {\n\tconst names = path.split(\".\", parts + 1)\n\tif (names.length > parts) {\n\t\treturn undefined\n\t}\n\tconst found = Arr.reduce(\n\t\tnames,\n\t\t{ branch: catalog.root, path: [] } as\n\t\t\t| { readonly branch: Branch; readonly path: ReadonlyArray<Segment> }\n\t\t\t| undefined,\n\t\t(current, name) => {\n\t\t\tif (current === undefined) {\n\t\t\t\treturn undefined\n\t\t\t}\n\t\t\tconst named = HashMap.get(current.branch.names, name)\n\t\t\tif (Option.isSome(named)) {\n\t\t\t\treturn {\n\t\t\t\t\tbranch: catalog.branches[named.value]!,\n\t\t\t\t\tpath: [...current.path, name],\n\t\t\t\t}\n\t\t\t}\n\t\t\tconst key = current.branch.key\n\t\t\tconst keyBranch = current.branch.keyBranch\n\t\t\treturn key !== undefined && keyBranch !== undefined && keyFits(key, name)\n\t\t\t\t? { branch: catalog.branches[keyBranch]!, path: [...current.path, key] }\n\t\t\t\t: undefined\n\t\t},\n\t)\n\treturn found === undefined\n\t\t? undefined\n\t\t: fieldAt(found.branch, found.path, parts)\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"catalog.js","names":["Trampoline.adaptive","Limits.exceeded","Limits.fieldPathParts"],"sources":["../../src/internal/catalog.ts"],"sourcesContent":["/**\n * The catalog of the fields of a Schema. One walk over the Schema makes it,\n * and every question about the fields of a resource or of the principal reads\n * it: which names a rule may use, which values a field may hold, which fields\n * a pattern fits, and which fields a list of permitted fields shows.\n *\n * The catalog describes the shape of the values, not the checks of the\n * Schema. A check, such as a minimum length, only keeps some values out, so a\n * catalog that leaves the checks out holds every real value and more. An\n * answer that holds for all of these values, as `All` and `None` of a\n * coverage do, then holds for the real values too.\n */\nimport * as Arr from \"effect/Array\"\nimport * as HashMap from \"effect/HashMap\"\nimport * as HashSet from \"effect/HashSet\"\nimport * as Option from \"effect/Option\"\nimport { hasProperty } from \"effect/Predicate\"\nimport * as Schema from \"effect/Schema\"\nimport * as SchemaAST from \"effect/SchemaAST\"\nimport * as Limits from \"./limits.ts\"\nimport * as Trampoline from \"./trampoline.ts\"\n\n/** The kinds of values that a field may hold. */\nexport interface Kinds {\n\t/** The field may be `null`, `undefined` or missing. */\n\treadonly null: boolean\n\treadonly boolean: boolean\n\t/** The field may hold any string. */\n\treadonly string: boolean\n\t/** The numbers that the field may hold. */\n\treadonly number: \"none\" | \"integer\" | \"finite\" | \"any\"\n\t/** The field may hold an object, or a value that is not a scalar. */\n\treadonly object: boolean\n\t/** Single values that the field may hold, other than the ones above. */\n\treadonly literals: ReadonlyArray<string | number | boolean>\n}\n\n/** No value: the kinds of a field of `Schema.Never`. */\nexport const noKinds: Kinds = {\n\tnull: false,\n\tboolean: false,\n\tstring: false,\n\tnumber: \"none\",\n\tobject: false,\n\tliterals: [],\n}\n\n/** Any value. */\nexport const anyKinds: Kinds = {\n\tnull: true,\n\tboolean: true,\n\tstring: true,\n\tnumber: \"any\",\n\tobject: true,\n\tliterals: [],\n}\n\nconst nullKinds: Kinds = { ...noKinds, null: true }\nconst objectKinds: Kinds = { ...noKinds, object: true }\n\nconst numberOrder = [\"none\", \"integer\", \"finite\", \"any\"] as const\n\n/** The kinds of a field that may hold a value of `left` or of `right`. */\nexport const joinKinds = (left: Kinds, right: Kinds): Kinds => ({\n\tnull: left.null || right.null,\n\tboolean: left.boolean || right.boolean,\n\tstring: left.string || right.string,\n\tnumber:\n\t\tnumberOrder.indexOf(left.number) >= numberOrder.indexOf(right.number)\n\t\t\t? left.number\n\t\t\t: right.number,\n\tobject: left.object || right.object,\n\t// A native `Set` finds copies about ten times as fast as `Arr.dedupe`. It\n\t// takes `0` and `-0` as one, as `includes` does when a test reads them.\n\tliterals: Array.from(new Set([...left.literals, ...right.literals])),\n})\n\n/** Tests if a number fits the numbers of a field. */\nexport const fitsNumber = (number: Kinds[\"number\"], value: number): boolean => {\n\tswitch (number) {\n\t\tcase \"none\":\n\t\t\treturn false\n\t\tcase \"integer\":\n\t\t\treturn Number.isInteger(value)\n\t\tcase \"finite\":\n\t\t\treturn Number.isFinite(value)\n\t\tcase \"any\":\n\t\t\treturn true\n\t}\n}\n\n/**\n * Tests if a field with these kinds may hold a value. A missing value and\n * `undefined` are `null`.\n */\nexport const accepts = (kinds: Kinds, value: unknown): boolean => {\n\tif (value === null || value === undefined) {\n\t\treturn kinds.null\n\t}\n\tswitch (typeof value) {\n\t\tcase \"boolean\":\n\t\t\treturn kinds.boolean || kinds.literals.includes(value)\n\t\tcase \"string\":\n\t\t\treturn kinds.string || kinds.literals.includes(value)\n\t\tcase \"number\":\n\t\t\treturn fitsNumber(kinds.number, value) || kinds.literals.includes(value)\n\t\tdefault:\n\t\t\treturn kinds.object\n\t}\n}\n\n/** A part of a field path that stands for any key of a record. */\nexport interface Key {\n\treadonly _tag: \"Key\"\n\t/** The Schemas of the keys. A key must fit one of them. */\n\treadonly parameters: ReadonlyArray<SchemaAST.AST>\n}\n\n/** One part of a field path: a name, or any key of a record. */\nexport type Segment = string | Key\n\n/** One field of a catalog. */\nexport interface Field {\n\treadonly path: ReadonlyArray<Segment>\n\t/** The path with dots, and `*` for a key of a record. */\n\treadonly text: string\n\treadonly kinds: Kinds\n\t/**\n\t * The field holds a value that a list of fields shows as one field: a\n\t * scalar, a record, an array, or a struct at the last part a path may have.\n\t */\n\treadonly listed: boolean\n\t/** The field may hold parts that the Schema does not name, as a record may. */\n\treadonly open: boolean\n\t/**\n\t * A read of the field finds only plain data. It is `false` when a value\n\t * above the field may be an object that does not have it as a data field:\n\t * a class without the field, which may have a getter of that name, a\n\t * value such as a date or a list, or any object, as of `Schema.Unknown`.\n\t * A struct of a union without the field makes it not plain too, since a\n\t * value made by hand may have it with any value.\n\t */\n\treadonly plain: boolean\n}\n\n/**\n * A place in the graph of a catalog. A path of a field leads to a place, and\n * many paths may lead to one place, as in a recursive Schema: `next` and\n * `next.next` of a list of links are one place. A place keeps the ids of the\n * places below it, so the graph has no loop of objects.\n */\nexport interface Branch {\n\treadonly id: number\n\t/** The kinds of the values of the field at this place. */\n\treadonly kinds: Kinds\n\t/** The field holds a scalar, a record or a list: see `Field.listed`. */\n\treadonly values: boolean\n\t/** The field may hold a struct. */\n\treadonly structs: boolean\n\treadonly open: boolean\n\treadonly plain: boolean\n\t/** The ids of the places below, by name, in the order of the Schema. */\n\treadonly children: ReadonlyArray<readonly [string, number]>\n\t/**\n\t * The places below, by name. A lookup gets each name from `split`, as a\n\t * new string, and a `HashMap` finds a new string about 20% faster than a\n\t * native `Map`. The names come from the Schema, so a name from a policy\n\t * can not make a long list of names with one hash here.\n\t */\n\treadonly names: HashMap.HashMap<string, number>\n\t/** The place for the keys of a record, when the value may be a record. */\n\treadonly key: Key | undefined\n\treadonly keyBranch: number | undefined\n}\n\n/** The catalog of the fields of a Schema. */\nexport interface Catalog {\n\t/** The places, by id. The root is the place with the id 0. */\n\treadonly branches: ReadonlyArray<Branch>\n\t/**\n\t * The number of steps from a place to a place below it. A search of the\n\t * catalog, as for a field pattern, reads each one, so this is its work.\n\t */\n\treadonly edges: number\n\treadonly root: Branch\n}\n\n/**\n * Tests if a key of an object fits the Schemas of the keys of a record. A key\n * of an object is a string, also a key that a number Schema takes: `{ 1: \"a\" }`\n * has the key `\"1\"`. So a key that is the text of a number is tried as that\n * number too.\n */\nexport const keyFits = (key: Key, name: string): boolean =>\n\tArr.some(key.parameters, (parameter) => {\n\t\tconst is = Schema.is(Schema.make(parameter))\n\t\treturn (\n\t\t\tis(name) ||\n\t\t\t(name !== \"\" && String(Number(name)) === name && is(Number(name)))\n\t\t)\n\t})\n\n/**\n * Gives the struct of a `Schema.Class`. A class is a declaration with one\n * struct as its type parameter, and Effect marks it with a `\"~constructor\"`\n * annotation, also through more codec steps, as in\n * `Schema.fromJsonString(User)`. Other declarations, such as `Schema.Option`\n * or `Schema.ReadonlyMap`, have no such mark, and the fields of their type\n * parameter are not fields of the value.\n */\nconst classStruct = (\n\tast: SchemaAST.Declaration,\n): SchemaAST.Objects | undefined => {\n\tconst [inner] = ast.typeParameters\n\treturn ast.annotations?.[\"~constructor\"] !== undefined &&\n\t\tast.typeParameters.length === 1 &&\n\t\tinner?._tag === \"Objects\"\n\t\t? inner\n\t\t: undefined\n}\n\n/**\n * Gives the Schemas that a value at one place may fit: it opens unions and\n * `Schema.suspend`. A class stays a class: see {@link structOf}. The open suspend nodes are in a native `Set`,\n * because the walk must find a node by the node itself: `Equal` compares\n * Schema nodes by structure, and a recursive Schema has no end. A suspend\n * node that gives itself again, with no step on the path, gives nothing.\n */\nconst alternatives = (\n\tast: SchemaAST.AST,\n\topen: ReadonlySet<SchemaAST.AST>,\n): ReadonlyArray<SchemaAST.AST> => alternativesOf({ ast, open })\n\n/**\n * The walk of {@link alternatives}. A union may hold a union, very many\n * levels down, so it is a walk of the trampoline for an input of an unknown\n * depth.\n */\nconst alternativesOf = Trampoline.adaptive<\n\t{ readonly ast: SchemaAST.AST; readonly open: ReadonlySet<SchemaAST.AST> },\n\tReadonlyArray<SchemaAST.AST>\n>((R, child) => ({ ast, open }, depth) => {\n\tswitch (ast._tag) {\n\t\tcase \"Union\":\n\t\t\treturn R.map(\n\t\t\t\tR.forEach(ast.types, (type) => child({ ast: type, open }, depth + 1)),\n\t\t\t\tArr.flatten,\n\t\t\t)\n\t\tcase \"Suspend\":\n\t\t\treturn open.has(ast)\n\t\t\t\t? R.succeed([])\n\t\t\t\t: child({ ast: ast.thunk(), open: new Set([...open, ast]) }, depth + 1)\n\t\tdefault:\n\t\t\treturn R.succeed([ast])\n\t}\n})\n\n/** The struct of a Schema: a struct, or the struct of a class. */\nconst structOf = (ast: SchemaAST.AST): SchemaAST.Objects | undefined =>\n\tast._tag === \"Objects\"\n\t\t? ast\n\t\t: ast._tag === \"Declaration\"\n\t\t\t? classStruct(ast)\n\t\t\t: undefined\n\nconst hasCheck = (\n\tchecks: ReadonlyArray<SchemaAST.Check<never>>,\n\tid: string,\n): boolean => hasCheckIn({ checks, id })\n\n/**\n * The walk of {@link hasCheck}. A group of checks may hold a group, very many\n * levels down, as `.and(...)` many times makes, so it is a walk of the\n * trampoline for an input of an unknown depth.\n */\nconst hasCheckIn = Trampoline.adaptive<\n\t{\n\t\treadonly checks: ReadonlyArray<SchemaAST.Check<never>>\n\t\treadonly id: string\n\t},\n\tboolean\n>(\n\t(R, child) =>\n\t\t({ checks, id }, depth) =>\n\t\t\tR.some(checks, (check) =>\n\t\t\t\tcheck.annotations?.representation?.id === id\n\t\t\t\t\t? R.succeed(true)\n\t\t\t\t\t: check._tag === \"FilterGroup\"\n\t\t\t\t\t\t? child({ checks: check.checks, id }, depth + 1)\n\t\t\t\t\t\t: R.succeed(false),\n\t\t\t),\n)\n\n/** A struct with named fields, or with the keys of a record. */\nconst isStruct = (\n\tast: SchemaAST.Objects | undefined,\n): ast is SchemaAST.Objects =>\n\tast !== undefined &&\n\t(ast.propertySignatures.length > 0 || ast.indexSignatures.length > 0)\n\n/** Tests if a Schema holds only values that are not objects, as `bigint`. */\nconst isPrimitive = (ast: SchemaAST.AST): boolean =>\n\tast._tag === \"BigInt\" ||\n\tast._tag === \"Symbol\" ||\n\tast._tag === \"UniqueSymbol\" ||\n\t(ast._tag === \"Literal\" && typeof ast.literal === \"bigint\")\n\n/** The kinds of the values of a Schema that is not a struct. */\nconst kindsOf = (ast: SchemaAST.AST): Kinds => {\n\tswitch (ast._tag) {\n\t\tcase \"Null\":\n\t\tcase \"Undefined\":\n\t\t\treturn nullKinds\n\t\tcase \"Never\":\n\t\t\treturn noKinds\n\t\tcase \"String\":\n\t\tcase \"TemplateLiteral\":\n\t\t\treturn { ...noKinds, string: true }\n\t\tcase \"Boolean\":\n\t\t\treturn { ...noKinds, boolean: true }\n\t\tcase \"Number\": {\n\t\t\t// The two checks that a search uses. Effect names a number with\n\t\t\t// them, as `Schema.Int` and `Schema.Finite`; other checks are left\n\t\t\t// out, as all checks are.\n\t\t\tconst checks = ast.checks ?? []\n\t\t\treturn {\n\t\t\t\t...noKinds,\n\t\t\t\tnumber: hasCheck(checks, \"effect/schema/isInt\")\n\t\t\t\t\t? \"integer\"\n\t\t\t\t\t: hasCheck(checks, \"effect/schema/isFinite\")\n\t\t\t\t\t\t? \"finite\"\n\t\t\t\t\t\t: \"any\",\n\t\t\t}\n\t\t}\n\t\tcase \"Literal\":\n\t\t\treturn typeof ast.literal === \"bigint\"\n\t\t\t\t? objectKinds\n\t\t\t\t: { ...noKinds, literals: [ast.literal] }\n\t\tcase \"Enum\":\n\t\t\treturn { ...noKinds, literals: Arr.map(ast.enums, ([, value]) => value) }\n\t\tcase \"Arrays\":\n\t\tcase \"ObjectKeyword\":\n\t\tcase \"BigInt\":\n\t\tcase \"Symbol\":\n\t\tcase \"UniqueSymbol\":\n\t\t\treturn objectKinds\n\t\tcase \"Declaration\":\n\t\t\t// A declaration of Effect in `objectDeclarations` holds an object,\n\t\t\t// such as a `Date`. Another declaration may hold any value, as\n\t\t\t// `Schema.Json` or one from `Schema.declare` may.\n\t\t\treturn HashSet.has(objectDeclarations, declarationId(ast))\n\t\t\t\t? objectKinds\n\t\t\t\t: anyKinds\n\t\tdefault:\n\t\t\t// `Schema.Unknown`, `Schema.Void`, and `Struct({})`, which takes a\n\t\t\t// string too: these may hold any value.\n\t\t\treturn anyKinds\n\t}\n}\n\n/**\n * The ids of the declarations of Effect that hold only objects. Effect puts\n * the id in the `representation` annotation. A declaration that is not in\n * this list may hold a scalar, as `Schema.Json` may, so its kinds are any\n * value.\n */\nconst objectDeclarations = HashSet.make(\n\t\"effect/schema/Date\",\n\t\"effect/schema/URL\",\n\t\"effect/schema/Uint8Array\",\n\t\"effect/schema/Duration\",\n\t\"effect/schema/DateTimeUtc\",\n\t\"effect/schema/DateTimeZoned\",\n\t\"effect/schema/BigDecimal\",\n\t\"effect/schema/Redacted\",\n\t\"effect/schema/Cause\",\n\t\"effect/schema/Chunk\",\n\t\"effect/schema/HashMap\",\n\t\"effect/schema/HashSet\",\n\t\"effect/schema/ReadonlyMap\",\n\t\"effect/schema/ReadonlySet\",\n\t\"effect/schema/Option\",\n\t\"effect/schema/Result\",\n\t\"effect/schema/Exit\",\n\t\"effect/schema/File\",\n\t\"effect/schema/FormData\",\n\t\"effect/schema/RegExp\",\n\t\"effect/schema/URLSearchParams\",\n)\n\nconst declarationId = (ast: SchemaAST.Declaration): string | undefined => {\n\tconst representation: unknown = ast.annotations?.[\"representation\"]\n\treturn hasProperty(representation, \"id\") &&\n\t\ttypeof representation.id === \"string\"\n\t\t? representation.id\n\t\t: undefined\n}\n\n/** The things that the walk of a catalog knows at one place. */\ninterface Place {\n\treadonly schemas: ReadonlyArray<SchemaAST.AST>\n\t/** The value may be missing. */\n\treadonly missing: boolean\n\t/** The value may be anything, as an extra field of a struct may be. */\n\treadonly top: boolean\n\t/** A read here finds only plain data: see {@link Field.plain}. */\n\treadonly plain: boolean\n}\n\nconst textOf = (path: ReadonlyArray<Segment>): string =>\n\tArr.map(path, (segment) =>\n\t\ttypeof segment === \"string\" ? segment : \"*\",\n\t).join(\".\")\n\n/**\n * The name of a property as a part of a path. A key of an object is a string,\n * so a number key, as from `Schema.Record(Schema.Literal(1), ...)`, has the\n * name `\"1\"`. A symbol key is not a field.\n */\nconst nameOf = (signature: SchemaAST.PropertySignature): string | undefined =>\n\ttypeof signature.name === \"symbol\" ? undefined : String(signature.name)\n\n/**\n * The properties of a struct by name. A walk makes it one time for each\n * struct of a place, so a struct with very many fields does not take a\n * search for each name. It is a native `Map`, as `Branch.names` is.\n */\nconst propertiesOf = (\n\tstruct: SchemaAST.Objects,\n): ReadonlyMap<string, SchemaAST.PropertySignature> =>\n\tnew Map(\n\t\tArr.flatMap(struct.propertySignatures, (signature) => {\n\t\t\tconst name = nameOf(signature)\n\t\t\treturn name === undefined ? [] : [[name, signature] as const]\n\t\t}),\n\t)\n\n/** The names of the fields of the structs, in the order that they first come. */\nconst namesOf = (\n\tstructs: ReadonlyArray<SchemaAST.Objects>,\n): ReadonlyArray<string> =>\n\t// A native `Set` keeps the first copy of each name, in order, about ten\n\t// times as fast as `Arr.dedupe`.\n\tArray.from(\n\t\tnew Set(\n\t\t\tArr.flatMap(structs, (struct) =>\n\t\t\t\tArr.flatMap(struct.propertySignatures, (signature) => {\n\t\t\t\t\tconst name = nameOf(signature)\n\t\t\t\t\tif (name === undefined) {\n\t\t\t\t\t\treturn []\n\t\t\t\t\t}\n\t\t\t\t\t// A dot in a path is the step into a nested field. A field name with\n\t\t\t\t\t// a dot would make two fields have one path, so it is a defect.\n\t\t\t\t\tif (name.includes(\".\")) {\n\t\t\t\t\t\tthrow new Error(`A field name may not have a dot: \"${name}\"`)\n\t\t\t\t\t}\n\t\t\t\t\t// An empty name would make an empty part in a path, such as\n\t\t\t\t\t// `.secret`.\n\t\t\t\t\tif (name === \"\") {\n\t\t\t\t\t\tthrow new Error(\"A field name may not be empty\")\n\t\t\t\t\t}\n\t\t\t\t\treturn [name]\n\t\t\t\t}),\n\t\t\t),\n\t\t),\n\t)\n\n/** A place of the walk, and the places below it, before they have ids. */\ninterface Made {\n\treadonly branch: Omit<Branch, \"id\" | \"children\" | \"names\" | \"keyBranch\">\n\treadonly children: ReadonlyArray<readonly [string, Place]>\n\treadonly keyPlace: Place | undefined\n}\n\n/** Works out the facts of a place, and the places below it. */\nconst placeOf = (place: Place): Made => {\n\tconst structs = Arr.filter(Arr.map(place.schemas, structOf), isStruct)\n\tconst values = Arr.filter(\n\t\tplace.schemas,\n\t\t(schema) => !isStruct(structOf(schema)),\n\t)\n\tconst valueKinds = Arr.reduce(values, noKinds, (kinds, schema) =>\n\t\tjoinKinds(kinds, kindsOf(structOf(schema) ?? schema)),\n\t)\n\t// A value of a branch may be an object, with parts that the Schema does\n\t// not name. A `bigint` or a `symbol` is not a scalar that a test takes,\n\t// so its kinds say `object`, but it has no parts: a read below it gives\n\t// `null`, as below a string.\n\tconst objects = Arr.some(\n\t\tvalues,\n\t\t(schema) =>\n\t\t\t!isPrimitive(schema) && kindsOf(structOf(schema) ?? schema).object,\n\t)\n\tconst kinds = place.top\n\t\t? anyKinds\n\t\t: joinKinds(\n\t\t\t\tjoinKinds(valueKinds, structs.length > 0 ? objectKinds : noKinds),\n\t\t\t\tplace.missing ? nullKinds : noKinds,\n\t\t\t)\n\tconst records = Arr.filter(\n\t\tstructs,\n\t\t(struct) => struct.indexSignatures.length > 0,\n\t)\n\tconst structProperties = Arr.map(structs, propertiesOf)\n\tconst children = Arr.map(namesOf(structs), (name) => {\n\t\tconst properties = Arr.flatMap(structProperties, (named) => {\n\t\t\tconst property = named.get(name)\n\t\t\treturn property === undefined ? [] : [property]\n\t\t})\n\t\t// A struct without the name may still have it as an extra field, with\n\t\t// any value: only a decode or a `make` takes extra fields away, and a\n\t\t// decision may get a value made by hand. A value that may be any\n\t\t// object, as one of `Schema.Unknown` may, may have the name with any\n\t\t// value too.\n\t\tconst top = place.top || objects || properties.length < structs.length\n\t\tconst below: Place = {\n\t\t\tschemas: Arr.flatMap(properties, (property) =>\n\t\t\t\talternatives(property.type, new Set()),\n\t\t\t),\n\t\t\t// A field below a value that is not a struct reads `null`, and so\n\t\t\t// does a field that may be missing.\n\t\t\tmissing:\n\t\t\t\tplace.missing ||\n\t\t\t\tvalues.length > 0 ||\n\t\t\t\tArr.some(properties, (property) => SchemaAST.isOptional(property.type)),\n\t\t\ttop,\n\t\t\t// A value that may be anything may be a list, or have a getter, so\n\t\t\t// a read below it may not find plain data.\n\t\t\tplain: place.plain && !top,\n\t\t}\n\t\treturn [name, below] as const\n\t})\n\tconst signatures = Arr.flatMap(records, (struct) => struct.indexSignatures)\n\tconst keyTop = place.top || objects || records.length < structs.length\n\treturn {\n\t\tbranch: {\n\t\t\tkinds,\n\t\t\tvalues: values.length > 0 || records.length > 0,\n\t\t\tstructs: structs.length > 0,\n\t\t\t// A field that is a scalar in one branch of a union and a struct in\n\t\t\t// another holds parts too.\n\t\t\topen: records.length > 0 || structs.length > 0 || objects || place.top,\n\t\t\tplain: place.plain,\n\t\t\tkey:\n\t\t\t\tsignatures.length === 0\n\t\t\t\t\t? undefined\n\t\t\t\t\t: {\n\t\t\t\t\t\t\t_tag: \"Key\",\n\t\t\t\t\t\t\tparameters: Arr.map(\n\t\t\t\t\t\t\t\tsignatures,\n\t\t\t\t\t\t\t\t(signature) => signature.parameter,\n\t\t\t\t\t\t\t),\n\t\t\t\t\t\t},\n\t\t},\n\t\tchildren,\n\t\tkeyPlace:\n\t\t\tsignatures.length === 0\n\t\t\t\t? undefined\n\t\t\t\t: {\n\t\t\t\t\t\tschemas: Arr.flatMap(signatures, (signature) =>\n\t\t\t\t\t\t\talternatives(signature.type, new Set()),\n\t\t\t\t\t\t),\n\t\t\t\t\t\t// A record may not have a given key.\n\t\t\t\t\t\tmissing: true,\n\t\t\t\t\t\ttop: keyTop,\n\t\t\t\t\t\tplain: place.plain && !keyTop,\n\t\t\t\t\t},\n\t}\n}\n\n/**\n * Makes the places of a Schema. Each place comes one time: two paths with the\n * same Schemas and the same marks lead to one place. The walk keeps a queue\n * of the places that it has found, and it makes them in order, so a deep\n * Schema does not fill the stack.\n */\nconst branchesOf = (root: Place): ReadonlyArray<Branch> => {\n\t// The id of each Schema node, and of each place, for this walk. The ids\n\t// are in native maps, because the walk must find a Schema node by the node\n\t// itself: `Equal` compares Schema nodes by structure, and a recursive\n\t// Schema has no end. The maps and the queue belong to this walk.\n\tconst astIds = new Map<SchemaAST.AST, number>()\n\tconst placeIds = new Map<string, number>()\n\tconst queue: Array<Place> = []\n\tconst idOf = (place: Place): number => {\n\t\tconst ids = Arr.map(place.schemas, (schema) => {\n\t\t\tconst known = astIds.get(schema)\n\t\t\tif (known !== undefined) {\n\t\t\t\treturn known\n\t\t\t}\n\t\t\tastIds.set(schema, astIds.size)\n\t\t\treturn astIds.size - 1\n\t\t})\n\t\tconst key = `${Array.from(new Set(ids))\n\t\t\t.sort((a, b) => a - b)\n\t\t\t.join(\n\t\t\t\t\",\",\n\t\t\t)}|${String(place.missing)}|${String(place.top)}|${String(place.plain)}`\n\t\tconst known = placeIds.get(key)\n\t\tif (known !== undefined) {\n\t\t\treturn known\n\t\t}\n\t\tif (queue.length >= Limits.ceilings.catalogFields) {\n\t\t\tthrow Limits.exceeded(\n\t\t\t\t\"A Schema\",\n\t\t\t\t\"catalogFields\",\n\t\t\t\t\"places\",\n\t\t\t\tqueue.length + 1,\n\t\t\t\tLimits.ceilings.catalogFields,\n\t\t\t)\n\t\t}\n\t\tplaceIds.set(key, queue.length)\n\t\tqueue.push(place)\n\t\treturn queue.length - 1\n\t}\n\tidOf(root)\n\treturn Arr.unfold(0, (id) => {\n\t\tconst place = queue[id]\n\t\tif (place === undefined) {\n\t\t\treturn Option.none()\n\t\t}\n\t\tconst made = placeOf(place)\n\t\tconst children = Arr.map(\n\t\t\tmade.children,\n\t\t\t([name, below]) => [name, idOf(below)] as const,\n\t\t)\n\t\tconst branch: Branch = {\n\t\t\t...made.branch,\n\t\t\tid,\n\t\t\tchildren,\n\t\t\tnames: HashMap.fromIterable(children),\n\t\t\tkeyBranch: made.keyPlace === undefined ? undefined : idOf(made.keyPlace),\n\t\t}\n\t\treturn Option.some([branch, id + 1] as const)\n\t})\n}\n\n/** The field at a place, for a path. */\nconst fieldAt = (\n\tbranch: Branch,\n\tpath: ReadonlyArray<Segment>,\n\tparts: number,\n): Field => ({\n\tpath,\n\ttext: textOf(path),\n\tkinds: branch.kinds,\n\t// A struct at the last part that a path may have is one field, since a\n\t// path does not go below it.\n\tlisted: branch.values || (path.length === parts && branch.structs),\n\topen: branch.open,\n\tplain: branch.plain,\n})\n\n/**\n * The fields that a list shows, a field before the fields below it, down to\n * `parts` parts. A path goes into a place that is on it already one time\n * only, so the list of a recursive Schema is finite. The walk keeps a stack\n * of the places to visit, so a deep Schema does not fill the stack. A Schema\n * that shares one part in many places may have very many fields, so the\n * walk counts the fields that it makes, and it stops at the first one past\n * `most`. The stack and the count belong to this walk.\n */\nconst listOf = (\n\tbranches: ReadonlyArray<Branch>,\n\tparts: number,\n\tmost: number,\n\tthing: string,\n): ReadonlyArray<Field> => {\n\tconst count = { fields: 0 }\n\tinterface Visit {\n\t\treadonly branch: Branch\n\t\treadonly path: ReadonlyArray<Segment>\n\t\t/** The ids of the places on the path, as a set of Effect that shares its parts. */\n\t\treadonly above: HashSet.HashSet<number>\n\t}\n\tconst below = (visit: Visit): ReadonlyArray<Visit> => {\n\t\tconst { branch, path } = visit\n\t\tif (\n\t\t\tpath.length === parts ||\n\t\t\t(path.length > 0 && HashSet.has(visit.above, branch.id))\n\t\t) {\n\t\t\treturn []\n\t\t}\n\t\tconst above = HashSet.add(visit.above, branch.id)\n\t\tconst named = Arr.map(branch.children, ([name, id]) => ({\n\t\t\tbranch: branches[id]!,\n\t\t\tpath: [...path, name],\n\t\t\tabove,\n\t\t}))\n\t\treturn branch.key === undefined || branch.keyBranch === undefined\n\t\t\t? named\n\t\t\t: [\n\t\t\t\t\t...named,\n\t\t\t\t\t{\n\t\t\t\t\t\tbranch: branches[branch.keyBranch]!,\n\t\t\t\t\t\tpath: [...path, branch.key],\n\t\t\t\t\t\tabove,\n\t\t\t\t\t},\n\t\t\t\t]\n\t}\n\tconst stack: Array<Visit> = [\n\t\t{ branch: branches[0]!, path: [], above: HashSet.empty() },\n\t]\n\treturn Arr.flatten(\n\t\tArr.unfold(stack, (rest) => {\n\t\t\tconst visit = rest.pop()\n\t\t\tif (visit === undefined) {\n\t\t\t\treturn Option.none()\n\t\t\t}\n\t\t\t// The visits go onto the stack one at a time: a spread into a call\n\t\t\t// would pass one argument for each field, past the most arguments\n\t\t\t// of a call.\n\t\t\tArr.forEach(Arr.reverse(below(visit)), (next) => {\n\t\t\t\trest.push(next)\n\t\t\t})\n\t\t\tconst field =\n\t\t\t\tvisit.path.length === 0\n\t\t\t\t\t? []\n\t\t\t\t\t: [fieldAt(visit.branch, visit.path, parts)]\n\t\t\tcount.fields += field.length\n\t\t\tif (count.fields > most) {\n\t\t\t\tthrow Limits.exceeded(\n\t\t\t\t\tthing,\n\t\t\t\t\t\"catalogFields\",\n\t\t\t\t\t\"fields\",\n\t\t\t\t\tcount.fields,\n\t\t\t\t\tmost,\n\t\t\t\t)\n\t\t\t}\n\t\t\treturn Option.some([field, rest] as const)\n\t\t}),\n\t)\n}\n\n/**\n * The fields that a list shows, a field before the fields below it, for\n * paths of at most `parts` parts. For a recursive Schema, the list goes into\n * each recursive place one time on each path, so it is finite. A list with\n * more than `most` fields is an error, as for a domain with these limits.\n * Each call makes the list again: a domain checks its size one time, and a\n * list of permitted fields reads it.\n */\nexport const fieldsOf = (\n\tcatalog: Catalog,\n\tparts: number = Limits.fieldPathParts,\n\tmost: number = Limits.ceilings.catalogFields,\n\tthing: string = \"A Schema\",\n): ReadonlyArray<Field> => listOf(catalog.branches, parts, most, thing)\n\n/**\n * Makes the catalog of a Schema. It throws when a field name has a dot or is\n * empty, and when the Schema has more places than the ceiling of\n * `catalogFields`. A domain checks the size of the list of the fields with\n * its own limit: see {@link fieldsOf}.\n */\nexport const catalogOf = (ast: SchemaAST.AST): Catalog => {\n\tconst branches = branchesOf({\n\t\tschemas: alternatives(ast, new Set()),\n\t\tmissing: false,\n\t\ttop: false,\n\t\tplain: true,\n\t})\n\treturn {\n\t\tbranches,\n\t\tedges: Arr.reduce(\n\t\t\tbranches,\n\t\t\t0,\n\t\t\t(total, branch) =>\n\t\t\t\ttotal +\n\t\t\t\tbranch.children.length +\n\t\t\t\t(branch.keyBranch === undefined ? 0 : 1),\n\t\t),\n\t\troot: branches[0]!,\n\t}\n}\n\n/**\n * Gives the field of a catalog at a path with dots, if the catalog has it.\n * A path has at most `parts` parts. The lookup follows one place for each\n * part, in a loop.\n */\nexport const lookup = (\n\tcatalog: Catalog,\n\tpath: string,\n\tparts: number = Limits.fieldPathParts,\n): Field | undefined => {\n\tconst names = path.split(\".\", parts + 1)\n\tif (names.length > parts) {\n\t\treturn undefined\n\t}\n\tconst found = Arr.reduce(\n\t\tnames,\n\t\t{ branch: catalog.root, path: [] } as\n\t\t\t| { readonly branch: Branch; readonly path: ReadonlyArray<Segment> }\n\t\t\t| undefined,\n\t\t(current, name) => {\n\t\t\tif (current === undefined) {\n\t\t\t\treturn undefined\n\t\t\t}\n\t\t\tconst named = HashMap.get(current.branch.names, name)\n\t\t\tif (Option.isSome(named)) {\n\t\t\t\treturn {\n\t\t\t\t\tbranch: catalog.branches[named.value]!,\n\t\t\t\t\tpath: [...current.path, name],\n\t\t\t\t}\n\t\t\t}\n\t\t\tconst key = current.branch.key\n\t\t\tconst keyBranch = current.branch.keyBranch\n\t\t\treturn key !== undefined && keyBranch !== undefined && keyFits(key, name)\n\t\t\t\t? { branch: catalog.branches[keyBranch]!, path: [...current.path, key] }\n\t\t\t\t: undefined\n\t\t},\n\t)\n\treturn found === undefined\n\t\t? undefined\n\t\t: fieldAt(found.branch, found.path, parts)\n}\n"],"mappings":"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"}
@@ -149,7 +149,7 @@ const permittedFields = (policy, request) => {
149
149
  const descriptor = kind === void 0 ? void 0 : descriptorOf(policy.domain, kind);
150
150
  if (descriptor === void 0) return Effect.succeed([]);
151
151
  const rules = policy.pipe(nodeOf, rulesOf);
152
- const listed = Arr.filter(fieldsOf(descriptor.catalog, policy.domain.limits.fieldPathParts), (field) => field.listed && Arr.every(field.path, (segment) => typeof segment === "string"));
152
+ const listed = Arr.filter(fieldsOf(descriptor.catalog, policy.domain.limits.fieldPathParts, policy.domain.limits.catalogFields), (field) => field.listed && Arr.every(field.path, (segment) => typeof segment === "string"));
153
153
  return Effect.map(Effect.filter(listed, (field) => Effect.map(decideAny(policy, {
154
154
  ...request,
155
155
  field: field.text
@@ -1 +1 @@
1
- {"version":3,"file":"decide.js","names":["RuleData.matchesTarget","Condition.evaluate","Place.pathOf","Decision.Allow","Decision.Deny","Place.at","at","Decision.NotApplicable","Catalog.lookup","ResourceData.kindOf","DomainData.descriptorOf","RuleData.fitsOf","RuleData.matchesInside","PolicyNodes.rulesOf","Catalog.fieldsOf"],"sources":["../../src/internal/decide.ts"],"sourcesContent":["/**\n * The decision walk of a policy: `decide`, `explain` and `permittedFields`.\n * The types are in `Policy.ts`.\n */\n// A policy keeps the error type and the service type of its predicates in the\n// public types. The code below works on the nodes with no types, where they\n// are `unknown`, so the language service check for `unknown` in an Effect is\n// off here.\n// @effect-diagnostics anyUnknownInErrorContext:off\nimport * as Arr from \"effect/Array\"\nimport * as Chunk from \"effect/Chunk\"\nimport * as Effect from \"effect/Effect\"\nimport * as Option from \"effect/Option\"\nimport * as Ref from \"effect/Ref\"\nimport * as Condition from \"../Condition.ts\"\nimport * as Decision from \"../Decision.ts\"\nimport type * as Domain from \"../Domain.ts\"\nimport type * as Policy from \"../Policy.ts\"\nimport type * as Resource from \"../Resource.ts\"\nimport * as Catalog from \"./catalog.ts\"\nimport * as DomainData from \"./domain.ts\"\nimport * as Place from \"./place.ts\"\nimport * as PolicyNodes from \"./policy-nodes.ts\"\nimport { nodeOf } from \"./policy.ts\"\nimport * as ResourceData from \"./resource.ts\"\nimport * as RuleData from \"./rule.ts\"\n\ntype Any = Policy.Any\ntype AnyRequest = Policy.AnyRequest\ntype AnyRule = Policy.AnyRule\ntype Explanation = Policy.Explanation\ntype Step = Policy.Step\n\ninterface DecideContext {\n\treadonly action: string\n\treadonly kind: string\n\treadonly field: string | undefined\n\treadonly input: Condition.Input\n\t/** The resource of the kind, for the fields that a rule may grant. */\n\treadonly descriptor: Resource.Descriptor\n\t/** The test of `RuleData.fitsOf` for the catalog, for this decision. */\n\treadonly fits: (pattern: string) => boolean\n\t/** The most parts of a field path in the domain. */\n\treadonly parts: number\n\t/** The rules that were tested, for {@link explain}. */\n\treadonly trace: Ref.Ref<Chunk.Chunk<Step>> | undefined\n}\n\n/**\n * The place of a node or a rule in a tree: the index of each child, then\n * the index of the rule. It shares the path of its parent, and a decision\n * makes the full path only for a step of `explain`: see `Place`.\n */\ntype At = Place.Place | undefined\n\nconst ruleApplies = (\n\tvalue: RuleData.Rule,\n\tcontext: DecideContext,\n\tat: Place.Place,\n): Effect.Effect<boolean, unknown, unknown> => {\n\tif (!RuleData.matchesTarget(value, context)) {\n\t\treturn Effect.succeed(false)\n\t}\n\tconst test =\n\t\tvalue.when === undefined\n\t\t\t? Effect.succeed(true)\n\t\t\t: // A rule of a node keeps no type of what it reads. The decision gives\n\t\t\t\t// it the value and the principal of the request.\n\t\t\t\tCondition.evaluate(value.when as Condition.Any, context.input)\n\tconst trace = context.trace\n\treturn trace === undefined\n\t\t? test\n\t\t: Effect.tap(test, (applies) =>\n\t\t\t\t// A chunk adds a step with no copy of the steps before.\n\t\t\t\tRef.update(\n\t\t\t\t\ttrace,\n\t\t\t\t\tChunk.append({\n\t\t\t\t\t\tpath: Place.pathOf(at) as ReadonlyArray<number>,\n\t\t\t\t\t\trule: typed(value),\n\t\t\t\t\t\tapplies,\n\t\t\t\t\t}),\n\t\t\t\t),\n\t\t\t)\n}\n\n/** A rule of a node, with the type of a rule of no domain. */\nconst typed = (value: RuleData.Rule): AnyRule => value as never\n\nconst fromRule = (value: RuleData.Rule): Decision.Decision =>\n\tvalue._tag === \"Allow\"\n\t\t? Decision.Allow({ rule: typed(value) })\n\t\t: value.reason === undefined\n\t\t\t? Decision.Deny({ rule: typed(value) })\n\t\t\t: Decision.Deny({ rule: typed(value), reason: value.reason })\n\n/** Gives the first rule that applies, in order. */\nconst firstApplying = (\n\tcandidates: ReadonlyArray<readonly [RuleData.Rule, number]>,\n\tcontext: DecideContext,\n\tat: At,\n): Effect.Effect<Option.Option<RuleData.Rule>, unknown, unknown> =>\n\tEffect.map(\n\t\tEffect.findFirst(candidates, ([value, index]) =>\n\t\t\truleApplies(value, context, Place.at(at, index)),\n\t\t),\n\t\tOption.map(([value]) => value),\n\t)\n\n/** Gives the first result that is found, and tests `next` only when none is. */\nconst orElse = <A, E, R>(\n\tfirst: Effect.Effect<Option.Option<A>, E, R>,\n\tnext: () => Effect.Effect<Option.Option<A>, E, R>,\n): Effect.Effect<Option.Option<A>, E, R> =>\n\tEffect.filterOrElse(first, Option.isSome, next)\n\nconst decideRules = (\n\tnode: PolicyNodes.Rules,\n\tcontext: DecideContext,\n\tat: At,\n): Effect.Effect<Decision.Decision, unknown, unknown> => {\n\tconst indexed = Arr.map(node.rules, (value, index) => [value, index] as const)\n\tconst denies = Arr.filter(indexed, ([value]) => value._tag === \"Deny\")\n\tconst allows = Arr.filter(indexed, ([value]) => value._tag === \"Allow\")\n\tconst first = (candidates: ReadonlyArray<readonly [RuleData.Rule, number]>) =>\n\t\tfirstApplying(candidates, context, at)\n\tconst found = (() => {\n\t\tswitch (node.algorithm) {\n\t\t\tcase \"denyOverrides\":\n\t\t\t\treturn orElse(first(denies), () => first(allows))\n\t\t\tcase \"permitOverrides\":\n\t\t\t\treturn orElse(first(allows), () => first(denies))\n\t\t\tcase \"firstApplicable\":\n\t\t\t\treturn first(indexed)\n\t\t\tcase \"lastApplicable\":\n\t\t\t\treturn first(Arr.reverse(indexed))\n\t\t}\n\t})()\n\treturn Effect.map(\n\t\tfound,\n\t\tOption.match({ onNone: () => Decision.NotApplicable, onSome: fromRule }),\n\t)\n}\n\n/** The next step of a walk over children: stop with a decision, or go on. */\ntype WalkStep<S> =\n\t| { readonly _tag: \"Stop\"; readonly decision: Decision.Decision }\n\t| { readonly _tag: \"Next\"; readonly state: S }\n\n/**\n * Decides the children in order. After each child, `step` stops with a\n * decision, or gives the state for the next child. After the last child,\n * `end` gives the decision. A child after a stop is not decided, so its\n * predicates do not run.\n */\nconst walkChildren = <S>(\n\tchildren: ReadonlyArray<PolicyNodes.Node>,\n\tcontext: DecideContext,\n\tat: At,\n\tstart: S,\n\tstep: (state: S, decision: Decision.Decision) => WalkStep<S>,\n\tend: (state: S) => Decision.Decision,\n): Effect.Effect<Decision.Decision, unknown, unknown> => {\n\tconst go = (\n\t\tindex: number,\n\t\tstate: S,\n\t): Effect.Effect<Decision.Decision, unknown, unknown> =>\n\t\tindex === children.length\n\t\t\t? Effect.succeed(end(state))\n\t\t\t: Effect.flatMap(\n\t\t\t\t\t// Each call for a child is a step of the Effect, so a deep tree\n\t\t\t\t\t// does not fill the stack.\n\t\t\t\t\tEffect.suspend(() =>\n\t\t\t\t\t\tdecideNode(children[index]!, context, Place.at(at, index)),\n\t\t\t\t\t),\n\t\t\t\t\t(decision) => {\n\t\t\t\t\t\tconst next = step(state, decision)\n\t\t\t\t\t\treturn next._tag === \"Stop\"\n\t\t\t\t\t\t\t? Effect.succeed(next.decision)\n\t\t\t\t\t\t\t: go(index + 1, next.state)\n\t\t\t\t\t},\n\t\t\t\t)\n\treturn go(0, start)\n}\n\nconst stop = (decision: Decision.Decision): WalkStep<never> => ({\n\t_tag: \"Stop\",\n\tdecision,\n})\n\nconst next = <S>(state: S): WalkStep<S> => ({ _tag: \"Next\", state })\n\n/**\n * Decides a node. A shared child is decided again for each place, so its\n * predicates run each time, as the policy says. The limits of the nodes keep\n * this work small.\n */\nexport const decideNode = (\n\tnode: PolicyNodes.Node,\n\tcontext: DecideContext,\n\tat: At,\n): Effect.Effect<Decision.Decision, unknown, unknown> => {\n\tswitch (node._tag) {\n\t\tcase \"Rules\":\n\t\t\treturn decideRules(node, context, at)\n\t\tcase \"All\":\n\t\t\t// One deny is enough to deny. Every child must allow.\n\t\t\treturn walkChildren(\n\t\t\t\tnode.policies,\n\t\t\t\tcontext,\n\t\t\t\tat,\n\t\t\t\t{\n\t\t\t\t\tfirstAllow: Option.none<Decision.Decision>(),\n\t\t\t\t\tallAllow: node.policies.length > 0,\n\t\t\t\t},\n\t\t\t\t(state, decision) =>\n\t\t\t\t\tdecision._tag === \"Deny\"\n\t\t\t\t\t\t? stop(decision)\n\t\t\t\t\t\t: next({\n\t\t\t\t\t\t\t\tfirstAllow: Option.orElse(state.firstAllow, () =>\n\t\t\t\t\t\t\t\t\tdecision._tag === \"Allow\"\n\t\t\t\t\t\t\t\t\t\t? Option.some(decision)\n\t\t\t\t\t\t\t\t\t\t: Option.none(),\n\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t\tallAllow: state.allAllow && decision._tag === \"Allow\",\n\t\t\t\t\t\t\t}),\n\t\t\t\t(state) =>\n\t\t\t\t\tstate.allAllow\n\t\t\t\t\t\t? Option.getOrElse(state.firstAllow, () => Decision.NotApplicable)\n\t\t\t\t\t\t: Decision.NotApplicable,\n\t\t\t)\n\t\tcase \"Any\":\n\t\t\t// One allow is enough. Without one, the first deny denies.\n\t\t\treturn walkChildren(\n\t\t\t\tnode.policies,\n\t\t\t\tcontext,\n\t\t\t\tat,\n\t\t\t\tOption.none<Decision.Decision>(),\n\t\t\t\t(firstDeny, decision) =>\n\t\t\t\t\tdecision._tag === \"Allow\"\n\t\t\t\t\t\t? stop(decision)\n\t\t\t\t\t\t: next(\n\t\t\t\t\t\t\t\tOption.orElse(firstDeny, () =>\n\t\t\t\t\t\t\t\t\tdecision._tag === \"Deny\"\n\t\t\t\t\t\t\t\t\t\t? Option.some(decision)\n\t\t\t\t\t\t\t\t\t\t: Option.none(),\n\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t),\n\t\t\t\t(firstDeny) =>\n\t\t\t\t\tOption.getOrElse(firstDeny, () => Decision.NotApplicable),\n\t\t\t)\n\t\tcase \"FirstOf\":\n\t\t\treturn walkChildren(\n\t\t\t\tnode.policies,\n\t\t\t\tcontext,\n\t\t\t\tat,\n\t\t\t\tundefined,\n\t\t\t\t(_, decision) =>\n\t\t\t\t\tdecision._tag === \"NotApplicable\" ? next(undefined) : stop(decision),\n\t\t\t\t() => Decision.NotApplicable,\n\t\t\t)\n\t\tcase \"Ceiling\":\n\t\t\t// A deny of the cap wins. Else the policy decides.\n\t\t\treturn Effect.filterOrElse(\n\t\t\t\tEffect.suspend(() => decideNode(node.cap, context, Place.at(at, 0))),\n\t\t\t\t(capped) => capped._tag === \"Deny\",\n\t\t\t\t() => decideNode(node.policy, context, Place.at(at, 1)),\n\t\t\t)\n\t}\n}\n\n/**\n * Tests if a request fits its kind: the action is an action of the kind, and\n * the field, if there is one, is a field of the kind.\n */\nexport const fitsKind = (\n\tdescriptor: Resource.Descriptor,\n\trequest: { readonly action: string; readonly field?: string | undefined },\n\tparts: number,\n): boolean =>\n\tdescriptor.actions.includes(request.action) &&\n\t(request.field === undefined ||\n\t\t(Catalog.lookup(descriptor.catalog, request.field, parts)?.listed ?? false))\n\nconst makeContext = (\n\tdomain: Domain.Any,\n\trequest: AnyRequest,\n\ttrace: Ref.Ref<Chunk.Chunk<Step>> | undefined,\n): DecideContext | undefined => {\n\tconst kind = ResourceData.kindOf(request.resource)\n\tconst descriptor =\n\t\tkind === undefined ? undefined : DomainData.descriptorOf(domain, kind)\n\tif (\n\t\tkind === undefined ||\n\t\tdescriptor === undefined ||\n\t\t!fitsKind(descriptor, request, domain.limits.fieldPathParts)\n\t) {\n\t\treturn undefined\n\t}\n\treturn {\n\t\taction: request.action,\n\t\tkind,\n\t\tfield: request.field,\n\t\tinput: {\n\t\t\tvalue: request.resource,\n\t\t\tprincipal: request.principal,\n\t\t\tpath: descriptor.path,\n\t\t},\n\t\tdescriptor,\n\t\tfits: RuleData.fitsOf(descriptor.catalog, domain.limits.fieldPathParts),\n\t\tparts: domain.limits.fieldPathParts,\n\t\ttrace,\n\t}\n}\n\nexport const decideAny = (\n\tpolicy: Any,\n\trequest: AnyRequest,\n): Effect.Effect<Decision.Decision, unknown, unknown> =>\n\t// The context reads the resource value, so it is made for each run.\n\tEffect.suspend(() => {\n\t\tconst context = makeContext(policy.domain, request, undefined)\n\t\treturn context === undefined\n\t\t\t? Effect.succeed(Decision.NotApplicable)\n\t\t\t: decideNode(nodeOf(policy), context, undefined)\n\t})\n\n/** Gives the decision of a policy with no predicates, at once. */\nexport const decideSync = (\n\tpolicy: Any,\n\trequest: AnyRequest,\n): Decision.Decision =>\n\tEffect.runSync(\n\t\t// The public type of `decideSync` takes only policies with no\n\t\t// predicates, so this Effect has no error and needs no services.\n\t\t// @effect-diagnostics-next-line unsafeEffectTypeAssertion:off\n\t\tdecideAny(policy, request) as Effect.Effect<Decision.Decision>,\n\t)\n\n/** Gives the decision of a policy and the rules that were tested to make it. */\nexport const explain = (\n\tpolicy: Any,\n\trequest: AnyRequest,\n): Effect.Effect<Explanation, unknown, unknown> =>\n\t// Each run of the Effect needs its own trace, so the trace is made inside\n\t// the Effect.\n\tEffect.gen(function* () {\n\t\tconst trace = yield* Ref.make(Chunk.empty<Step>())\n\t\tconst context = makeContext(policy.domain, request, trace)\n\t\tif (context === undefined) {\n\t\t\treturn { decision: Decision.NotApplicable, steps: [] }\n\t\t}\n\t\tconst decision = yield* decideNode(nodeOf(policy), context, undefined)\n\t\treturn { decision, steps: Chunk.toReadonlyArray(yield* Ref.get(trace)) }\n\t})\n\n/**\n * Tests if a deny rule of the request names a part inside `field`.\n */\nconst deniesInside = (\n\trules: ReadonlyArray<RuleData.Rule>,\n\ttarget: { readonly action: string; readonly kind: string },\n\tfield: string,\n): boolean =>\n\tArr.some(\n\t\trules,\n\t\t(value) =>\n\t\t\tvalue._tag === \"Deny\" &&\n\t\t\tvalue.fields !== undefined &&\n\t\t\tvalue.fields.some((pattern) => RuleData.matchesInside(pattern, field)) &&\n\t\t\t(value.actions.includes(\"*\") || value.actions.includes(target.action)) &&\n\t\t\t(value.resources.includes(\"*\") || value.resources.includes(target.kind)),\n\t)\n\n/** Gives the fields of a resource value that the policy lets a request use. */\nexport const permittedFields = (\n\tpolicy: Any,\n\trequest: Omit<AnyRequest, \"field\">,\n): Effect.Effect<ReadonlyArray<string>, unknown, unknown> => {\n\tconst kind = ResourceData.kindOf(request.resource)\n\tconst descriptor: Resource.Descriptor | undefined =\n\t\tkind === undefined\n\t\t\t? undefined\n\t\t\t: DomainData.descriptorOf(policy.domain, kind)\n\tif (descriptor === undefined) {\n\t\treturn Effect.succeed([])\n\t}\n\t// The rules of the tree, in one flat list for all the fields.\n\tconst rules = policy.pipe(nodeOf, PolicyNodes.rulesOf)\n\t// The fields with names only: a list can not name every key of a record.\n\tconst listed = Arr.filter(\n\t\t// The list for the limit of the parts of a path of the domain, as the\n\t\t// decision of each field reads it.\n\t\tCatalog.fieldsOf(descriptor.catalog, policy.domain.limits.fieldPathParts),\n\t\t(field) =>\n\t\t\tfield.listed &&\n\t\t\tArr.every(field.path, (segment) => typeof segment === \"string\"),\n\t)\n\treturn Effect.map(\n\t\tEffect.filter(listed, (field) =>\n\t\t\tEffect.map(\n\t\t\t\tdecideAny(policy, { ...request, field: field.text }),\n\t\t\t\t(result) =>\n\t\t\t\t\tresult._tag === \"Allow\" &&\n\t\t\t\t\t!(\n\t\t\t\t\t\tfield.open &&\n\t\t\t\t\t\tdeniesInside(\n\t\t\t\t\t\t\trules,\n\t\t\t\t\t\t\t{ ...request, kind: descriptor.kind },\n\t\t\t\t\t\t\tfield.text,\n\t\t\t\t\t\t)\n\t\t\t\t\t),\n\t\t\t),\n\t\t),\n\t\tArr.map((field) => field.text),\n\t)\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"decide.js","names":["RuleData.matchesTarget","Condition.evaluate","Place.pathOf","Decision.Allow","Decision.Deny","Place.at","at","Decision.NotApplicable","Catalog.lookup","ResourceData.kindOf","DomainData.descriptorOf","RuleData.fitsOf","RuleData.matchesInside","PolicyNodes.rulesOf","Catalog.fieldsOf"],"sources":["../../src/internal/decide.ts"],"sourcesContent":["/**\n * The decision walk of a policy: `decide`, `explain` and `permittedFields`.\n * The types are in `Policy.ts`.\n */\n// A policy keeps the error type and the service type of its predicates in the\n// public types. The code below works on the nodes with no types, where they\n// are `unknown`, so the language service check for `unknown` in an Effect is\n// off here.\n// @effect-diagnostics anyUnknownInErrorContext:off\nimport * as Arr from \"effect/Array\"\nimport * as Chunk from \"effect/Chunk\"\nimport * as Effect from \"effect/Effect\"\nimport * as Option from \"effect/Option\"\nimport * as Ref from \"effect/Ref\"\nimport * as Condition from \"../Condition.ts\"\nimport * as Decision from \"../Decision.ts\"\nimport type * as Domain from \"../Domain.ts\"\nimport type * as Policy from \"../Policy.ts\"\nimport type * as Resource from \"../Resource.ts\"\nimport * as Catalog from \"./catalog.ts\"\nimport * as DomainData from \"./domain.ts\"\nimport * as Place from \"./place.ts\"\nimport * as PolicyNodes from \"./policy-nodes.ts\"\nimport { nodeOf } from \"./policy.ts\"\nimport * as ResourceData from \"./resource.ts\"\nimport * as RuleData from \"./rule.ts\"\n\ntype Any = Policy.Any\ntype AnyRequest = Policy.AnyRequest\ntype AnyRule = Policy.AnyRule\ntype Explanation = Policy.Explanation\ntype Step = Policy.Step\n\ninterface DecideContext {\n\treadonly action: string\n\treadonly kind: string\n\treadonly field: string | undefined\n\treadonly input: Condition.Input\n\t/** The resource of the kind, for the fields that a rule may grant. */\n\treadonly descriptor: Resource.Descriptor\n\t/** The test of `RuleData.fitsOf` for the catalog, for this decision. */\n\treadonly fits: (pattern: string) => boolean\n\t/** The most parts of a field path in the domain. */\n\treadonly parts: number\n\t/** The rules that were tested, for {@link explain}. */\n\treadonly trace: Ref.Ref<Chunk.Chunk<Step>> | undefined\n}\n\n/**\n * The place of a node or a rule in a tree: the index of each child, then\n * the index of the rule. It shares the path of its parent, and a decision\n * makes the full path only for a step of `explain`: see `Place`.\n */\ntype At = Place.Place | undefined\n\nconst ruleApplies = (\n\tvalue: RuleData.Rule,\n\tcontext: DecideContext,\n\tat: Place.Place,\n): Effect.Effect<boolean, unknown, unknown> => {\n\tif (!RuleData.matchesTarget(value, context)) {\n\t\treturn Effect.succeed(false)\n\t}\n\tconst test =\n\t\tvalue.when === undefined\n\t\t\t? Effect.succeed(true)\n\t\t\t: // A rule of a node keeps no type of what it reads. The decision gives\n\t\t\t\t// it the value and the principal of the request.\n\t\t\t\tCondition.evaluate(value.when as Condition.Any, context.input)\n\tconst trace = context.trace\n\treturn trace === undefined\n\t\t? test\n\t\t: Effect.tap(test, (applies) =>\n\t\t\t\t// A chunk adds a step with no copy of the steps before.\n\t\t\t\tRef.update(\n\t\t\t\t\ttrace,\n\t\t\t\t\tChunk.append({\n\t\t\t\t\t\tpath: Place.pathOf(at) as ReadonlyArray<number>,\n\t\t\t\t\t\trule: typed(value),\n\t\t\t\t\t\tapplies,\n\t\t\t\t\t}),\n\t\t\t\t),\n\t\t\t)\n}\n\n/** A rule of a node, with the type of a rule of no domain. */\nconst typed = (value: RuleData.Rule): AnyRule => value as never\n\nconst fromRule = (value: RuleData.Rule): Decision.Decision =>\n\tvalue._tag === \"Allow\"\n\t\t? Decision.Allow({ rule: typed(value) })\n\t\t: value.reason === undefined\n\t\t\t? Decision.Deny({ rule: typed(value) })\n\t\t\t: Decision.Deny({ rule: typed(value), reason: value.reason })\n\n/** Gives the first rule that applies, in order. */\nconst firstApplying = (\n\tcandidates: ReadonlyArray<readonly [RuleData.Rule, number]>,\n\tcontext: DecideContext,\n\tat: At,\n): Effect.Effect<Option.Option<RuleData.Rule>, unknown, unknown> =>\n\tEffect.map(\n\t\tEffect.findFirst(candidates, ([value, index]) =>\n\t\t\truleApplies(value, context, Place.at(at, index)),\n\t\t),\n\t\tOption.map(([value]) => value),\n\t)\n\n/** Gives the first result that is found, and tests `next` only when none is. */\nconst orElse = <A, E, R>(\n\tfirst: Effect.Effect<Option.Option<A>, E, R>,\n\tnext: () => Effect.Effect<Option.Option<A>, E, R>,\n): Effect.Effect<Option.Option<A>, E, R> =>\n\tEffect.filterOrElse(first, Option.isSome, next)\n\nconst decideRules = (\n\tnode: PolicyNodes.Rules,\n\tcontext: DecideContext,\n\tat: At,\n): Effect.Effect<Decision.Decision, unknown, unknown> => {\n\tconst indexed = Arr.map(node.rules, (value, index) => [value, index] as const)\n\tconst denies = Arr.filter(indexed, ([value]) => value._tag === \"Deny\")\n\tconst allows = Arr.filter(indexed, ([value]) => value._tag === \"Allow\")\n\tconst first = (candidates: ReadonlyArray<readonly [RuleData.Rule, number]>) =>\n\t\tfirstApplying(candidates, context, at)\n\tconst found = (() => {\n\t\tswitch (node.algorithm) {\n\t\t\tcase \"denyOverrides\":\n\t\t\t\treturn orElse(first(denies), () => first(allows))\n\t\t\tcase \"permitOverrides\":\n\t\t\t\treturn orElse(first(allows), () => first(denies))\n\t\t\tcase \"firstApplicable\":\n\t\t\t\treturn first(indexed)\n\t\t\tcase \"lastApplicable\":\n\t\t\t\treturn first(Arr.reverse(indexed))\n\t\t}\n\t})()\n\treturn Effect.map(\n\t\tfound,\n\t\tOption.match({ onNone: () => Decision.NotApplicable, onSome: fromRule }),\n\t)\n}\n\n/** The next step of a walk over children: stop with a decision, or go on. */\ntype WalkStep<S> =\n\t| { readonly _tag: \"Stop\"; readonly decision: Decision.Decision }\n\t| { readonly _tag: \"Next\"; readonly state: S }\n\n/**\n * Decides the children in order. After each child, `step` stops with a\n * decision, or gives the state for the next child. After the last child,\n * `end` gives the decision. A child after a stop is not decided, so its\n * predicates do not run.\n */\nconst walkChildren = <S>(\n\tchildren: ReadonlyArray<PolicyNodes.Node>,\n\tcontext: DecideContext,\n\tat: At,\n\tstart: S,\n\tstep: (state: S, decision: Decision.Decision) => WalkStep<S>,\n\tend: (state: S) => Decision.Decision,\n): Effect.Effect<Decision.Decision, unknown, unknown> => {\n\tconst go = (\n\t\tindex: number,\n\t\tstate: S,\n\t): Effect.Effect<Decision.Decision, unknown, unknown> =>\n\t\tindex === children.length\n\t\t\t? Effect.succeed(end(state))\n\t\t\t: Effect.flatMap(\n\t\t\t\t\t// Each call for a child is a step of the Effect, so a deep tree\n\t\t\t\t\t// does not fill the stack.\n\t\t\t\t\tEffect.suspend(() =>\n\t\t\t\t\t\tdecideNode(children[index]!, context, Place.at(at, index)),\n\t\t\t\t\t),\n\t\t\t\t\t(decision) => {\n\t\t\t\t\t\tconst next = step(state, decision)\n\t\t\t\t\t\treturn next._tag === \"Stop\"\n\t\t\t\t\t\t\t? Effect.succeed(next.decision)\n\t\t\t\t\t\t\t: go(index + 1, next.state)\n\t\t\t\t\t},\n\t\t\t\t)\n\treturn go(0, start)\n}\n\nconst stop = (decision: Decision.Decision): WalkStep<never> => ({\n\t_tag: \"Stop\",\n\tdecision,\n})\n\nconst next = <S>(state: S): WalkStep<S> => ({ _tag: \"Next\", state })\n\n/**\n * Decides a node. A shared child is decided again for each place, so its\n * predicates run each time, as the policy says. The limits of the nodes keep\n * this work small.\n */\nexport const decideNode = (\n\tnode: PolicyNodes.Node,\n\tcontext: DecideContext,\n\tat: At,\n): Effect.Effect<Decision.Decision, unknown, unknown> => {\n\tswitch (node._tag) {\n\t\tcase \"Rules\":\n\t\t\treturn decideRules(node, context, at)\n\t\tcase \"All\":\n\t\t\t// One deny is enough to deny. Every child must allow.\n\t\t\treturn walkChildren(\n\t\t\t\tnode.policies,\n\t\t\t\tcontext,\n\t\t\t\tat,\n\t\t\t\t{\n\t\t\t\t\tfirstAllow: Option.none<Decision.Decision>(),\n\t\t\t\t\tallAllow: node.policies.length > 0,\n\t\t\t\t},\n\t\t\t\t(state, decision) =>\n\t\t\t\t\tdecision._tag === \"Deny\"\n\t\t\t\t\t\t? stop(decision)\n\t\t\t\t\t\t: next({\n\t\t\t\t\t\t\t\tfirstAllow: Option.orElse(state.firstAllow, () =>\n\t\t\t\t\t\t\t\t\tdecision._tag === \"Allow\"\n\t\t\t\t\t\t\t\t\t\t? Option.some(decision)\n\t\t\t\t\t\t\t\t\t\t: Option.none(),\n\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t\tallAllow: state.allAllow && decision._tag === \"Allow\",\n\t\t\t\t\t\t\t}),\n\t\t\t\t(state) =>\n\t\t\t\t\tstate.allAllow\n\t\t\t\t\t\t? Option.getOrElse(state.firstAllow, () => Decision.NotApplicable)\n\t\t\t\t\t\t: Decision.NotApplicable,\n\t\t\t)\n\t\tcase \"Any\":\n\t\t\t// One allow is enough. Without one, the first deny denies.\n\t\t\treturn walkChildren(\n\t\t\t\tnode.policies,\n\t\t\t\tcontext,\n\t\t\t\tat,\n\t\t\t\tOption.none<Decision.Decision>(),\n\t\t\t\t(firstDeny, decision) =>\n\t\t\t\t\tdecision._tag === \"Allow\"\n\t\t\t\t\t\t? stop(decision)\n\t\t\t\t\t\t: next(\n\t\t\t\t\t\t\t\tOption.orElse(firstDeny, () =>\n\t\t\t\t\t\t\t\t\tdecision._tag === \"Deny\"\n\t\t\t\t\t\t\t\t\t\t? Option.some(decision)\n\t\t\t\t\t\t\t\t\t\t: Option.none(),\n\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t),\n\t\t\t\t(firstDeny) =>\n\t\t\t\t\tOption.getOrElse(firstDeny, () => Decision.NotApplicable),\n\t\t\t)\n\t\tcase \"FirstOf\":\n\t\t\treturn walkChildren(\n\t\t\t\tnode.policies,\n\t\t\t\tcontext,\n\t\t\t\tat,\n\t\t\t\tundefined,\n\t\t\t\t(_, decision) =>\n\t\t\t\t\tdecision._tag === \"NotApplicable\" ? next(undefined) : stop(decision),\n\t\t\t\t() => Decision.NotApplicable,\n\t\t\t)\n\t\tcase \"Ceiling\":\n\t\t\t// A deny of the cap wins. Else the policy decides.\n\t\t\treturn Effect.filterOrElse(\n\t\t\t\tEffect.suspend(() => decideNode(node.cap, context, Place.at(at, 0))),\n\t\t\t\t(capped) => capped._tag === \"Deny\",\n\t\t\t\t() => decideNode(node.policy, context, Place.at(at, 1)),\n\t\t\t)\n\t}\n}\n\n/**\n * Tests if a request fits its kind: the action is an action of the kind, and\n * the field, if there is one, is a field of the kind.\n */\nexport const fitsKind = (\n\tdescriptor: Resource.Descriptor,\n\trequest: { readonly action: string; readonly field?: string | undefined },\n\tparts: number,\n): boolean =>\n\tdescriptor.actions.includes(request.action) &&\n\t(request.field === undefined ||\n\t\t(Catalog.lookup(descriptor.catalog, request.field, parts)?.listed ?? false))\n\nconst makeContext = (\n\tdomain: Domain.Any,\n\trequest: AnyRequest,\n\ttrace: Ref.Ref<Chunk.Chunk<Step>> | undefined,\n): DecideContext | undefined => {\n\tconst kind = ResourceData.kindOf(request.resource)\n\tconst descriptor =\n\t\tkind === undefined ? undefined : DomainData.descriptorOf(domain, kind)\n\tif (\n\t\tkind === undefined ||\n\t\tdescriptor === undefined ||\n\t\t!fitsKind(descriptor, request, domain.limits.fieldPathParts)\n\t) {\n\t\treturn undefined\n\t}\n\treturn {\n\t\taction: request.action,\n\t\tkind,\n\t\tfield: request.field,\n\t\tinput: {\n\t\t\tvalue: request.resource,\n\t\t\tprincipal: request.principal,\n\t\t\tpath: descriptor.path,\n\t\t},\n\t\tdescriptor,\n\t\tfits: RuleData.fitsOf(descriptor.catalog, domain.limits.fieldPathParts),\n\t\tparts: domain.limits.fieldPathParts,\n\t\ttrace,\n\t}\n}\n\nexport const decideAny = (\n\tpolicy: Any,\n\trequest: AnyRequest,\n): Effect.Effect<Decision.Decision, unknown, unknown> =>\n\t// The context reads the resource value, so it is made for each run.\n\tEffect.suspend(() => {\n\t\tconst context = makeContext(policy.domain, request, undefined)\n\t\treturn context === undefined\n\t\t\t? Effect.succeed(Decision.NotApplicable)\n\t\t\t: decideNode(nodeOf(policy), context, undefined)\n\t})\n\n/** Gives the decision of a policy with no predicates, at once. */\nexport const decideSync = (\n\tpolicy: Any,\n\trequest: AnyRequest,\n): Decision.Decision =>\n\tEffect.runSync(\n\t\t// The public type of `decideSync` takes only policies with no\n\t\t// predicates, so this Effect has no error and needs no services.\n\t\t// @effect-diagnostics-next-line unsafeEffectTypeAssertion:off\n\t\tdecideAny(policy, request) as Effect.Effect<Decision.Decision>,\n\t)\n\n/** Gives the decision of a policy and the rules that were tested to make it. */\nexport const explain = (\n\tpolicy: Any,\n\trequest: AnyRequest,\n): Effect.Effect<Explanation, unknown, unknown> =>\n\t// Each run of the Effect needs its own trace, so the trace is made inside\n\t// the Effect.\n\tEffect.gen(function* () {\n\t\tconst trace = yield* Ref.make(Chunk.empty<Step>())\n\t\tconst context = makeContext(policy.domain, request, trace)\n\t\tif (context === undefined) {\n\t\t\treturn { decision: Decision.NotApplicable, steps: [] }\n\t\t}\n\t\tconst decision = yield* decideNode(nodeOf(policy), context, undefined)\n\t\treturn { decision, steps: Chunk.toReadonlyArray(yield* Ref.get(trace)) }\n\t})\n\n/**\n * Tests if a deny rule of the request names a part inside `field`.\n */\nconst deniesInside = (\n\trules: ReadonlyArray<RuleData.Rule>,\n\ttarget: { readonly action: string; readonly kind: string },\n\tfield: string,\n): boolean =>\n\tArr.some(\n\t\trules,\n\t\t(value) =>\n\t\t\tvalue._tag === \"Deny\" &&\n\t\t\tvalue.fields !== undefined &&\n\t\t\tvalue.fields.some((pattern) => RuleData.matchesInside(pattern, field)) &&\n\t\t\t(value.actions.includes(\"*\") || value.actions.includes(target.action)) &&\n\t\t\t(value.resources.includes(\"*\") || value.resources.includes(target.kind)),\n\t)\n\n/** Gives the fields of a resource value that the policy lets a request use. */\nexport const permittedFields = (\n\tpolicy: Any,\n\trequest: Omit<AnyRequest, \"field\">,\n): Effect.Effect<ReadonlyArray<string>, unknown, unknown> => {\n\tconst kind = ResourceData.kindOf(request.resource)\n\tconst descriptor: Resource.Descriptor | undefined =\n\t\tkind === undefined\n\t\t\t? undefined\n\t\t\t: DomainData.descriptorOf(policy.domain, kind)\n\tif (descriptor === undefined) {\n\t\treturn Effect.succeed([])\n\t}\n\t// The rules of the tree, in one flat list for all the fields.\n\tconst rules = policy.pipe(nodeOf, PolicyNodes.rulesOf)\n\t// The fields with names only: a list can not name every key of a record.\n\tconst listed = Arr.filter(\n\t\t// The list for the limit of the parts of a path of the domain, as the\n\t\t// decision of each field reads it.\n\t\tCatalog.fieldsOf(\n\t\t\tdescriptor.catalog,\n\t\t\tpolicy.domain.limits.fieldPathParts,\n\t\t\tpolicy.domain.limits.catalogFields,\n\t\t),\n\t\t(field) =>\n\t\t\tfield.listed &&\n\t\t\tArr.every(field.path, (segment) => typeof segment === \"string\"),\n\t)\n\treturn Effect.map(\n\t\tEffect.filter(listed, (field) =>\n\t\t\tEffect.map(\n\t\t\t\tdecideAny(policy, { ...request, field: field.text }),\n\t\t\t\t(result) =>\n\t\t\t\t\tresult._tag === \"Allow\" &&\n\t\t\t\t\t!(\n\t\t\t\t\t\tfield.open &&\n\t\t\t\t\t\tdeniesInside(\n\t\t\t\t\t\t\trules,\n\t\t\t\t\t\t\t{ ...request, kind: descriptor.kind },\n\t\t\t\t\t\t\tfield.text,\n\t\t\t\t\t\t)\n\t\t\t\t\t),\n\t\t\t),\n\t\t),\n\t\tArr.map((field) => field.text),\n\t)\n}\n"],"mappings":"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"}
@@ -1,20 +1,23 @@
1
- import { exceeded, make as make$1 } from "./limits.js";
1
+ import { make as make$1 } from "./limits.js";
2
2
  import { catalogOf, fieldsOf } from "./catalog.js";
3
3
  import { descriptor } from "./resource.js";
4
4
  import * as Arr from "effect/Array";
5
5
  import * as HashMap from "effect/HashMap";
6
- import * as Data from "effect/Data";
7
6
  import * as Option from "effect/Option";
7
+ import * as Data from "effect/Data";
8
8
  // src/internal/domain.ts
9
9
  /**
10
10
  * The values of domains. The types are in `Domain.ts`.
11
11
  */
12
12
  /** The class of every domain value. `isDomain` uses it to know a domain. */
13
13
  var DomainData = class extends Data.Class {};
14
- /** Stops a catalog with more fields in its list than the domain takes. */
14
+ /**
15
+ * Stops a catalog with more fields in its list than the domain takes. The
16
+ * list stops at the first field past the limit, so a Schema with very many
17
+ * fields does not take very much memory first.
18
+ */
15
19
  const checkCatalog = (catalog, limits, name) => {
16
- const fields = fieldsOf(catalog, limits.fieldPathParts).length;
17
- if (fields > limits.catalogFields) throw exceeded(name, "catalogFields", "fields", fields, limits.catalogFields);
20
+ fieldsOf(catalog, limits.fieldPathParts, limits.catalogFields, name);
18
21
  };
19
22
  /**
20
23
  * Makes a domain. Each resource must have its own kind name. The limits that
@@ -1 +1 @@
1
- {"version":3,"file":"domain.js","names":["Catalog.fieldsOf","Limits.exceeded","Limits.make","ResourceData.descriptor","Catalog.catalogOf"],"sources":["../../src/internal/domain.ts"],"sourcesContent":["/**\n * The values of domains. The types are in `Domain.ts`.\n */\nimport * as Arr from \"effect/Array\"\nimport * as Data from \"effect/Data\"\nimport * as HashMap from \"effect/HashMap\"\nimport * as Option from \"effect/Option\"\nimport type * as Schema from \"effect/Schema\"\nimport type * as Domain from \"../Domain.ts\"\nimport type * as Resource from \"../Resource.ts\"\nimport * as Catalog from \"./catalog.ts\"\nimport * as Limits from \"./limits.ts\"\nimport * as ResourceData from \"./resource.ts\"\n\n/** The class of every domain value. `isDomain` uses it to know a domain. */\nclass DomainData extends Data.Class<{\n\treadonly principal: Schema.Top\n\treadonly principalCatalog: Catalog.Catalog\n\treadonly resources: HashMap.HashMap<string, Resource.Descriptor>\n\treadonly meta: Schema.Top | undefined\n\treadonly limits: Limits.Limits\n}> {}\n\n/** Stops a catalog with more fields in its list than the domain takes. */\nconst checkCatalog = (\n\tcatalog: Catalog.Catalog,\n\tlimits: Limits.Limits,\n\tname: string,\n): void => {\n\tconst fields = Catalog.fieldsOf(catalog, limits.fieldPathParts).length\n\tif (fields > limits.catalogFields) {\n\t\tthrow Limits.exceeded(\n\t\t\tname,\n\t\t\t\"catalogFields\",\n\t\t\t\"fields\",\n\t\t\tfields,\n\t\t\tlimits.catalogFields,\n\t\t)\n\t}\n}\n\n/**\n * Makes a domain. Each resource must have its own kind name. The limits that\n * the domain does not set have their defaults.\n */\nexport const make = (options: {\n\treadonly principal: Schema.Top\n\treadonly resources: ReadonlyArray<Resource.Any>\n\treadonly meta?: Schema.Top\n\treadonly limits?: Partial<Limits.Limits>\n}): DomainData => {\n\tconst limits = Limits.make(options.limits)\n\tconst descriptors = Arr.map(options.resources, ResourceData.descriptor)\n\tconst kinds = Arr.map(descriptors, (descriptor) => descriptor.kind)\n\t// Two resources with one kind name is a mistake in the code, not in the\n\t// data, so it is a defect.\n\tconst twice = Arr.findFirst(\n\t\tkinds,\n\t\t(kind, index) => kinds.indexOf(kind) !== index,\n\t)\n\tif (Option.isSome(twice)) {\n\t\tthrow new Error(`Two resources have the kind name \"${twice.value}\"`)\n\t}\n\tif (kinds.includes(\"*\")) {\n\t\tthrow new Error(`A resource may not have the kind name \"*\"`)\n\t}\n\tconst resources = HashMap.fromIterable(\n\t\tArr.map(\n\t\t\tdescriptors,\n\t\t\t(descriptor) => [descriptor.kind, descriptor] as const,\n\t\t),\n\t)\n\t// The catalog checks the field names of the principal, so a bad name is a\n\t// defect when the domain is made. A Schema with more fields than the\n\t// domain takes is a defect too.\n\tconst principalCatalog = Catalog.catalogOf(options.principal.ast)\n\tcheckCatalog(principalCatalog, limits, \"The Schema of the principal\")\n\tArr.forEach(descriptors, (descriptor) =>\n\t\tcheckCatalog(\n\t\t\tdescriptor.catalog,\n\t\t\tlimits,\n\t\t\t`The Schema of the resource \"${descriptor.kind}\"`,\n\t\t),\n\t)\n\treturn new DomainData({\n\t\tprincipal: options.principal,\n\t\tprincipalCatalog,\n\t\tresources,\n\t\tmeta: options.meta,\n\t\tlimits,\n\t})\n}\n\n/** Gives the descriptor of the resource with a kind name. */\nexport const descriptorOf = (\n\tdomain: Domain.Any,\n\tkind: string,\n): Resource.Descriptor | undefined =>\n\tOption.getOrUndefined(HashMap.get(domain.resources, kind))\n\n/** Tests if a value is a domain. */\nexport const isDomain = (u: unknown): u is Domain.Any => u instanceof DomainData\n"],"mappings":";;;;;;;;;;;;AAeA,IAAM,aAAN,cAAyB,KAAK,MAM3B,CAAC;;AAGJ,MAAM,gBACL,SACA,QACA,SACU;CACV,MAAM,SAASA,SAAiB,SAAS,OAAO,cAAc,CAAC,CAAC;CAChE,IAAI,SAAS,OAAO,eACnB,MAAMC,SACL,MACA,iBACA,UACA,QACA,OAAO,aACR;AAEF;;;;;AAMA,MAAa,QAAQ,YAKH;CACjB,MAAM,SAASC,OAAY,QAAQ,MAAM;CACzC,MAAM,cAAc,IAAI,IAAI,QAAQ,WAAWC,UAAuB;CACtE,MAAM,QAAQ,IAAI,IAAI,cAAc,eAAe,WAAW,IAAI;CAGlE,MAAM,QAAQ,IAAI,UACjB,QACC,MAAM,UAAU,MAAM,QAAQ,IAAI,MAAM,KAC1C;CACA,IAAI,OAAO,OAAO,KAAK,GACtB,MAAM,IAAI,MAAM,qCAAqC,MAAM,MAAM,EAAE;CAEpE,IAAI,MAAM,SAAS,GAAG,GACrB,MAAM,IAAI,MAAM,2CAA2C;CAE5D,MAAM,YAAY,QAAQ,aACzB,IAAI,IACH,cACC,eAAe,CAAC,WAAW,MAAM,UAAU,CAC7C,CACD;CAIA,MAAM,mBAAmBC,UAAkB,QAAQ,UAAU,GAAG;CAChE,aAAa,kBAAkB,QAAQ,6BAA6B;CACpE,IAAI,QAAQ,cAAc,eACzB,aACC,WAAW,SACX,QACA,+BAA+B,WAAW,KAAK,EAChD,CACD;CACA,OAAO,IAAI,WAAW;EACrB,WAAW,QAAQ;EACnB;EACA;EACA,MAAM,QAAQ;EACd;CACD,CAAC;AACF;;AAGA,MAAa,gBACZ,QACA,SAEA,OAAO,eAAe,QAAQ,IAAI,OAAO,WAAW,IAAI,CAAC"}
1
+ {"version":3,"file":"domain.js","names":["Limits.make","ResourceData.descriptor","Catalog.catalogOf"],"sources":["../../src/internal/domain.ts"],"sourcesContent":["/**\n * The values of domains. The types are in `Domain.ts`.\n */\nimport * as Arr from \"effect/Array\"\nimport * as Data from \"effect/Data\"\nimport * as HashMap from \"effect/HashMap\"\nimport * as Option from \"effect/Option\"\nimport type * as Schema from \"effect/Schema\"\nimport type * as Domain from \"../Domain.ts\"\nimport type * as Resource from \"../Resource.ts\"\nimport * as Catalog from \"./catalog.ts\"\nimport * as Limits from \"./limits.ts\"\nimport * as ResourceData from \"./resource.ts\"\n\n/** The class of every domain value. `isDomain` uses it to know a domain. */\nclass DomainData extends Data.Class<{\n\treadonly principal: Schema.Top\n\treadonly principalCatalog: Catalog.Catalog\n\treadonly resources: HashMap.HashMap<string, Resource.Descriptor>\n\treadonly meta: Schema.Top | undefined\n\treadonly limits: Limits.Limits\n}> {}\n\n/**\n * Stops a catalog with more fields in its list than the domain takes. The\n * list stops at the first field past the limit, so a Schema with very many\n * fields does not take very much memory first.\n */\nconst checkCatalog = (\n\tcatalog: Catalog.Catalog,\n\tlimits: Limits.Limits,\n\tname: string,\n): void => {\n\tCatalog.fieldsOf(catalog, limits.fieldPathParts, limits.catalogFields, name)\n}\n\n/**\n * Makes a domain. Each resource must have its own kind name. The limits that\n * the domain does not set have their defaults.\n */\nexport const make = (options: {\n\treadonly principal: Schema.Top\n\treadonly resources: ReadonlyArray<Resource.Any>\n\treadonly meta?: Schema.Top\n\treadonly limits?: Partial<Limits.Limits>\n}): DomainData => {\n\tconst limits = Limits.make(options.limits)\n\tconst descriptors = Arr.map(options.resources, ResourceData.descriptor)\n\tconst kinds = Arr.map(descriptors, (descriptor) => descriptor.kind)\n\t// Two resources with one kind name is a mistake in the code, not in the\n\t// data, so it is a defect.\n\tconst twice = Arr.findFirst(\n\t\tkinds,\n\t\t(kind, index) => kinds.indexOf(kind) !== index,\n\t)\n\tif (Option.isSome(twice)) {\n\t\tthrow new Error(`Two resources have the kind name \"${twice.value}\"`)\n\t}\n\tif (kinds.includes(\"*\")) {\n\t\tthrow new Error(`A resource may not have the kind name \"*\"`)\n\t}\n\tconst resources = HashMap.fromIterable(\n\t\tArr.map(\n\t\t\tdescriptors,\n\t\t\t(descriptor) => [descriptor.kind, descriptor] as const,\n\t\t),\n\t)\n\t// The catalog checks the field names of the principal, so a bad name is a\n\t// defect when the domain is made. A Schema with more fields than the\n\t// domain takes is a defect too.\n\tconst principalCatalog = Catalog.catalogOf(options.principal.ast)\n\tcheckCatalog(principalCatalog, limits, \"The Schema of the principal\")\n\tArr.forEach(descriptors, (descriptor) =>\n\t\tcheckCatalog(\n\t\t\tdescriptor.catalog,\n\t\t\tlimits,\n\t\t\t`The Schema of the resource \"${descriptor.kind}\"`,\n\t\t),\n\t)\n\treturn new DomainData({\n\t\tprincipal: options.principal,\n\t\tprincipalCatalog,\n\t\tresources,\n\t\tmeta: options.meta,\n\t\tlimits,\n\t})\n}\n\n/** Gives the descriptor of the resource with a kind name. */\nexport const descriptorOf = (\n\tdomain: Domain.Any,\n\tkind: string,\n): Resource.Descriptor | undefined =>\n\tOption.getOrUndefined(HashMap.get(domain.resources, kind))\n\n/** Tests if a value is a domain. */\nexport const isDomain = (u: unknown): u is Domain.Any => u instanceof DomainData\n"],"mappings":";;;;;;;;;;;;AAeA,IAAM,aAAN,cAAyB,KAAK,MAM3B,CAAC;;;;;;AAOJ,MAAM,gBACL,SACA,QACA,SACU;CACV,SAAiB,SAAS,OAAO,gBAAgB,OAAO,eAAe,IAAI;AAC5E;;;;;AAMA,MAAa,QAAQ,YAKH;CACjB,MAAM,SAASA,OAAY,QAAQ,MAAM;CACzC,MAAM,cAAc,IAAI,IAAI,QAAQ,WAAWC,UAAuB;CACtE,MAAM,QAAQ,IAAI,IAAI,cAAc,eAAe,WAAW,IAAI;CAGlE,MAAM,QAAQ,IAAI,UACjB,QACC,MAAM,UAAU,MAAM,QAAQ,IAAI,MAAM,KAC1C;CACA,IAAI,OAAO,OAAO,KAAK,GACtB,MAAM,IAAI,MAAM,qCAAqC,MAAM,MAAM,EAAE;CAEpE,IAAI,MAAM,SAAS,GAAG,GACrB,MAAM,IAAI,MAAM,2CAA2C;CAE5D,MAAM,YAAY,QAAQ,aACzB,IAAI,IACH,cACC,eAAe,CAAC,WAAW,MAAM,UAAU,CAC7C,CACD;CAIA,MAAM,mBAAmBC,UAAkB,QAAQ,UAAU,GAAG;CAChE,aAAa,kBAAkB,QAAQ,6BAA6B;CACpE,IAAI,QAAQ,cAAc,eACzB,aACC,WAAW,SACX,QACA,+BAA+B,WAAW,KAAK,EAChD,CACD;CACA,OAAO,IAAI,WAAW;EACrB,WAAW,QAAQ;EACnB;EACA;EACA,MAAM,QAAQ;EACd;CACD,CAAC;AACF;;AAGA,MAAa,gBACZ,QACA,SAEA,OAAO,eAAe,QAAQ,IAAI,OAAO,WAAW,IAAI,CAAC"}
@@ -1,8 +1,8 @@
1
1
  import { ceilings, fieldPathParts, patternParts } from "./limits.js";
2
2
  import { keyFits } from "./catalog.js";
3
3
  import * as Arr from "effect/Array";
4
- import * as Result from "effect/Result";
5
4
  import * as Option from "effect/Option";
5
+ import * as Result from "effect/Result";
6
6
  // src/internal/patterns.ts
7
7
  /**
8
8
  * Field patterns, as in `settings.*`, `secret*` or `**.secret`. A pattern is
@@ -136,11 +136,11 @@ const decodeMeta = (rule, domain, budget) => {
136
136
  value,
137
137
  schema: domain.meta
138
138
  };
139
- })), (found) => found === void 0 ? Effect.void : Effect.mapError(Schema.decodeUnknownEffect(metaCodec(found.schema))(found.value), (error) => new Problem({
139
+ })), (found) => found === void 0 ? Effect.void : Schema.decodeUnknownEffect(metaCodec(found.schema))(found.value).pipe(Effect.mapError((error) => new Problem({
140
140
  path: pathOf(at$4),
141
141
  message: error.message,
142
142
  issue: error.issue
143
- })));
143
+ })), Effect.catchDefect((defect) => defect instanceof RangeError ? Effect.fail(problemAt(at$4, "The meta of the rule has too many levels for the Schema of meta")) : Effect.die(defect))));
144
144
  };
145
145
  const decodeRule = (rule, meta, budget, domain) => {
146
146
  const { input, place } = rule;