@nmnmcc/ability 1.0.1 → 2.0.0

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.
Files changed (127) hide show
  1. package/README.md +112 -69
  2. package/dist/Authorizer.d.ts +43 -47
  3. package/dist/Authorizer.d.ts.map +1 -1
  4. package/dist/Authorizer.js +5 -290
  5. package/dist/Authorizer.js.map +1 -1
  6. package/dist/Condition.d.ts +164 -332
  7. package/dist/Condition.d.ts.map +1 -1
  8. package/dist/Condition.js +75 -890
  9. package/dist/Condition.js.map +1 -1
  10. package/dist/Decision.d.ts +13 -13
  11. package/dist/Decision.d.ts.map +1 -1
  12. package/dist/Decision.js.map +1 -1
  13. package/dist/Domain.d.ts +10 -21
  14. package/dist/Domain.d.ts.map +1 -1
  15. package/dist/Domain.js +4 -62
  16. package/dist/Domain.js.map +1 -1
  17. package/dist/HttpApi.d.ts +31 -7
  18. package/dist/HttpApi.d.ts.map +1 -1
  19. package/dist/HttpApi.js +25 -8
  20. package/dist/HttpApi.js.map +1 -1
  21. package/dist/Limits.d.ts +114 -0
  22. package/dist/Limits.d.ts.map +1 -0
  23. package/dist/Limits.js +132 -0
  24. package/dist/Limits.js.map +1 -0
  25. package/dist/Policy.d.ts +271 -269
  26. package/dist/Policy.d.ts.map +1 -1
  27. package/dist/Policy.js +68 -1177
  28. package/dist/Policy.js.map +1 -1
  29. package/dist/Resource.d.ts +4 -177
  30. package/dist/Resource.d.ts.map +1 -1
  31. package/dist/Resource.js +4 -803
  32. package/dist/Resource.js.map +1 -1
  33. package/dist/index.d.ts +3 -4
  34. package/dist/index.js +4 -5
  35. package/dist/internal/algebra.js +267 -0
  36. package/dist/internal/algebra.js.map +1 -0
  37. package/dist/internal/analysis.js +245 -0
  38. package/dist/internal/analysis.js.map +1 -0
  39. package/dist/internal/authorizer.d.ts +30 -0
  40. package/dist/internal/authorizer.d.ts.map +1 -0
  41. package/dist/internal/authorizer.js +221 -0
  42. package/dist/internal/authorizer.js.map +1 -0
  43. package/dist/internal/bind.js +286 -0
  44. package/dist/internal/bind.js.map +1 -0
  45. package/dist/internal/catalog.d.ts +56 -0
  46. package/dist/internal/catalog.d.ts.map +1 -0
  47. package/dist/internal/catalog.js +292 -0
  48. package/dist/internal/catalog.js.map +1 -0
  49. package/dist/internal/decide.js +166 -0
  50. package/dist/internal/decide.js.map +1 -0
  51. package/dist/internal/domain.js +32 -0
  52. package/dist/internal/domain.js.map +1 -0
  53. package/dist/internal/json.js +134 -0
  54. package/dist/internal/json.js.map +1 -0
  55. package/dist/internal/limits.d.ts +14 -0
  56. package/dist/internal/limits.d.ts.map +1 -0
  57. package/dist/internal/limits.js +43 -0
  58. package/dist/internal/limits.js.map +1 -0
  59. package/dist/internal/nodes.d.ts +121 -0
  60. package/dist/internal/nodes.d.ts.map +1 -0
  61. package/dist/internal/nodes.js +277 -0
  62. package/dist/internal/nodes.js.map +1 -0
  63. package/dist/internal/patterns.js +110 -0
  64. package/dist/internal/patterns.js.map +1 -0
  65. package/dist/internal/policy-json.js +255 -0
  66. package/dist/internal/policy-json.js.map +1 -0
  67. package/dist/internal/policy-nodes.d.ts +61 -0
  68. package/dist/internal/policy-nodes.d.ts.map +1 -0
  69. package/dist/internal/policy-nodes.js +146 -0
  70. package/dist/internal/policy-nodes.js.map +1 -0
  71. package/dist/internal/policy.d.ts +42 -0
  72. package/dist/internal/policy.d.ts.map +1 -0
  73. package/dist/internal/policy.js +124 -0
  74. package/dist/internal/policy.js.map +1 -0
  75. package/dist/internal/read.js +39 -0
  76. package/dist/internal/read.js.map +1 -0
  77. package/dist/internal/resource.js +115 -0
  78. package/dist/internal/resource.js.map +1 -0
  79. package/dist/internal/rule.d.ts +26 -0
  80. package/dist/internal/rule.d.ts.map +1 -0
  81. package/dist/internal/rule.js +93 -0
  82. package/dist/internal/rule.js.map +1 -0
  83. package/dist/internal/search.d.ts +22 -0
  84. package/dist/internal/search.d.ts.map +1 -0
  85. package/dist/internal/search.js +274 -0
  86. package/dist/internal/search.js.map +1 -0
  87. package/dist/internal/suggest.js +46 -0
  88. package/dist/internal/suggest.js.map +1 -0
  89. package/package.json +3 -4
  90. package/src/Authorizer.ts +67 -583
  91. package/src/Condition.ts +339 -2095
  92. package/src/Decision.ts +6 -5
  93. package/src/Domain.ts +12 -69
  94. package/src/HttpApi.ts +72 -10
  95. package/src/Limits.ts +132 -0
  96. package/src/Policy.ts +386 -2772
  97. package/src/Resource.ts +7 -1548
  98. package/src/index.ts +3 -9
  99. package/src/internal/algebra.ts +482 -0
  100. package/src/internal/analysis.ts +478 -0
  101. package/src/internal/authorizer.ts +418 -0
  102. package/src/internal/bind.ts +596 -0
  103. package/src/internal/catalog.ts +534 -0
  104. package/src/internal/decide.ts +402 -0
  105. package/src/internal/domain.ts +66 -0
  106. package/src/internal/json.ts +260 -0
  107. package/src/internal/limits.ts +78 -0
  108. package/src/internal/nodes.ts +404 -0
  109. package/src/internal/patterns.ts +211 -0
  110. package/src/internal/policy-json.ts +484 -0
  111. package/src/internal/policy-nodes.ts +207 -0
  112. package/src/internal/policy.ts +264 -0
  113. package/src/internal/read.ts +55 -0
  114. package/src/internal/resource.ts +180 -0
  115. package/src/internal/rule.ts +181 -0
  116. package/src/internal/search.ts +541 -0
  117. package/src/internal/suggest.ts +104 -0
  118. package/dist/Principal.d.ts +0 -35
  119. package/dist/Principal.d.ts.map +0 -1
  120. package/dist/Principal.js +0 -37
  121. package/dist/Principal.js.map +0 -1
  122. package/dist/Rule.d.ts +0 -275
  123. package/dist/Rule.d.ts.map +0 -1
  124. package/dist/Rule.js +0 -257
  125. package/dist/Rule.js.map +0 -1
  126. package/src/Principal.ts +0 -41
  127. package/src/Rule.ts +0 -683
package/dist/Domain.js CHANGED
@@ -1,35 +1,7 @@
1
1
  import { __exportAll } from "./_virtual/_rolldown/runtime.js";
2
- import { descriptor, fieldPaths } from "./Resource.js";
3
- import * as Arr from "effect/Array";
4
- import * as Option from "effect/Option";
5
- import * as Data from "effect/Data";
6
- import * as HashMap from "effect/HashMap";
2
+ import { make as make$1 } from "./internal/domain.js";
7
3
  // src/Domain.ts
8
- /**
9
- * A domain puts together the principal and all the resources of an
10
- * application. All other modules get their types from the domain. A rule may
11
- * only use the actions of its resource. A condition may only use the fields of
12
- * its resource and of the principal.
13
- *
14
- * @example
15
- * ```ts
16
- * import { Domain } from "@nmnmcc/ability"
17
- *
18
- * const App = Domain.make({ principal: Member, resources: [Document, Comment] })
19
- * ```
20
- *
21
- * @stability stable
22
- * @since 0.3.0
23
- */
24
- var Domain_exports = /* @__PURE__ */ __exportAll({
25
- descriptorOf: () => descriptorOf,
26
- isDomain: () => isDomain,
27
- make: () => make
28
- });
29
- /**
30
- * The class of every domain value. {@link isDomain} uses it to know a domain.
31
- */
32
- var DomainData = class extends Data.Class {};
4
+ var Domain_exports = /* @__PURE__ */ __exportAll({ make: () => make });
33
5
  /**
34
6
  * Makes a domain. Each resource must have its own kind name. Give `meta` when
35
7
  * rules must keep extra data, such as the scope of a grant. The `meta` Schema
@@ -40,37 +12,7 @@ var DomainData = class extends Data.Class {};
40
12
  * @category constructors
41
13
  * @since 0.3.0
42
14
  */
43
- const make = (options) => {
44
- const descriptors = Arr.map(options.resources, descriptor);
45
- const kinds = Arr.map(descriptors, (descriptor) => descriptor.kind);
46
- const twice = Arr.findFirst(kinds, (kind, index) => kinds.indexOf(kind) !== index);
47
- if (Option.isSome(twice)) throw new Error(`Two resources have the kind name "${twice.value}"`);
48
- if (kinds.includes("*")) throw new Error(`A resource may not have the kind name "*"`);
49
- const resources = HashMap.fromIterable(Arr.map(descriptors, (descriptor) => [descriptor.kind, descriptor]));
50
- fieldPaths(options.principal);
51
- return new DomainData({
52
- principal: options.principal,
53
- resources,
54
- meta: options.meta
55
- });
56
- };
57
- /**
58
- * Gives the descriptor of the resource with a kind name, or `undefined` when
59
- * the domain has no such resource.
60
- *
61
- * @stability stable
62
- * @category getters
63
- * @since 1.0.0
64
- */
65
- const descriptorOf = (domain, kind) => Option.getOrUndefined(HashMap.get(domain.resources, kind));
66
- /**
67
- * Tests if a value is a domain.
68
- *
69
- * @stability stable
70
- * @category guards
71
- * @since 0.3.0
72
- */
73
- const isDomain = (u) => u instanceof DomainData;
74
- export { Domain_exports, descriptorOf, isDomain, make };
15
+ const make = make$1;
16
+ export { Domain_exports, make };
75
17
 
76
18
  //# sourceMappingURL=Domain.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"Domain.js","names":["Resource.descriptor"],"sources":["../src/Domain.ts"],"sourcesContent":["/**\n * A domain puts together the principal and all the resources of an\n * application. All other modules get their types from the domain. A rule may\n * only use the actions of its resource. A condition may only use the fields of\n * its resource and of the principal.\n *\n * @example\n * ```ts\n * import { Domain } from \"@nmnmcc/ability\"\n *\n * const App = Domain.make({ principal: Member, resources: [Document, Comment] })\n * ```\n *\n * @stability stable\n * @since 0.3.0\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 PrincipalSchema from \"./Principal.ts\"\nimport * as Resource from \"./Resource.ts\"\n\n/**\n * A domain. `P` is the type of the principal, `Resources` is the union of the\n * resources, and `Meta` is the type of the extra data that a rule may keep.\n *\n * @stability stable\n * @category models\n * @since 0.3.0\n */\nexport interface Domain<\n\tP extends object,\n\tResources extends Resource.Any,\n\tMeta,\n> {\n\treadonly principal: PrincipalSchema.Any\n\t/** The descriptor of each resource, by its kind name. */\n\treadonly resources: HashMap.HashMap<string, Resource.Descriptor>\n\treadonly meta: Schema.Top | undefined\n\treadonly \"~Domain\"?: {\n\t\treadonly Principal: P\n\t\treadonly Resources: Resources\n\t\treadonly Meta: Meta\n\t}\n}\n\n/**\n * Any domain.\n *\n * @stability stable\n * @category models\n * @since 0.3.0\n */\nexport type Any = Domain<any, any, any>\n\n/**\n * The type of the principal of a domain.\n *\n * @stability stable\n * @category utility types\n * @since 0.3.0\n */\nexport type Principal<D> = D extends Domain<infer P, any, any> ? P : never\n\n/**\n * The union of the resources of a domain.\n *\n * @stability stable\n * @category utility types\n * @since 0.3.0\n */\nexport type Resources<D> = D extends Domain<any, infer R, any> ? R : never\n\n/**\n * The type of the extra data that a rule of the domain may keep.\n *\n * @stability stable\n * @category utility types\n * @since 0.3.0\n */\nexport type Meta<D> = D extends Domain<any, any, infer M> ? M : never\n\n/**\n * The kind names of a domain.\n *\n * @stability stable\n * @category utility types\n * @since 0.3.0\n */\nexport type Kind<D> = Resource.Kind<Resources<D>>\n\n/**\n * The resource of a domain with the kind `K`.\n *\n * @stability stable\n * @category utility types\n * @since 0.3.0\n */\nexport type ResourceOf<D, K extends string> =\n\tResources<D> extends infer R\n\t\t? R extends Resource.Resource<K, any, any, any>\n\t\t\t? R\n\t\t\t: never\n\t\t: never\n\n/**\n * The actions of the resource with the kind `K`.\n *\n * @stability stable\n * @category utility types\n * @since 0.3.0\n */\nexport type ActionOf<D, K extends string> = Resource.Action<ResourceOf<D, K>>\n\n/**\n * The actions that every kind in `K` has. For one kind, they are its actions.\n * For a union of kinds, an action of only some of them is left out, so a\n * value of the union type can not get an action that its kind does not have.\n *\n * @stability stable\n * @category utility types\n * @since 1.0.0\n */\nexport type CommonAction<D, K extends string> = (\n\tK extends unknown ? (action: ActionOf<D, K>) => void : never\n) extends (action: infer A) => void\n\t? A\n\t: never\n\n/**\n * All the actions of all the resources of a domain.\n *\n * @stability stable\n * @category utility types\n * @since 0.3.0\n */\nexport type AnyAction<D> = Resource.Action<Resources<D>>\n\n/**\n * The data type of the resource with the kind `K`.\n *\n * @stability stable\n * @category utility types\n * @since 0.3.0\n */\nexport type DataOf<D, K extends string> = Resource.Data<ResourceOf<D, K>>\n\n/**\n * The value type of the resource with the kind `K`.\n *\n * @stability stable\n * @category utility types\n * @since 0.3.0\n */\nexport type ValueOf<D, K extends string> = Resource.Value<ResourceOf<D, K>>\n\n/**\n * The union of the value types of all the resources of a domain.\n *\n * @stability stable\n * @category utility types\n * @since 0.3.0\n */\nexport type AnyValue<D> = Resource.Value<Resources<D>>\n\n/**\n * The class of every domain value. {@link isDomain} uses it to know a domain.\n */\nclass DomainData extends Data.Class<{\n\treadonly principal: PrincipalSchema.Any\n\treadonly resources: HashMap.HashMap<string, Resource.Descriptor>\n\treadonly meta: Schema.Top | undefined\n}> {}\n\n/**\n * Makes a domain. Each resource must have its own kind name. Give `meta` when\n * rules must keep extra data, such as the scope of a grant. The `meta` Schema\n * may not need services, because `Policy.schema` gives a Schema that needs\n * none.\n *\n * @stability stable\n * @category constructors\n * @since 0.3.0\n */\nexport const make: <\n\tP extends PrincipalSchema.Any,\n\tconst Resources extends ReadonlyArray<Resource.Any>,\n\tM extends Schema.Codec<any, any, never, never> = Schema.Never,\n>(options: {\n\treadonly principal: P\n\treadonly resources: Resources\n\treadonly meta?: M\n}) => Domain<P[\"Type\"], Resources[number], M[\"Type\"]> = (options) => {\n\tconst descriptors = Arr.map(options.resources, Resource.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// This checks the field names of the principal. See `Resource.fieldPaths`.\n\tResource.fieldPaths(options.principal)\n\treturn new DomainData({\n\t\tprincipal: options.principal,\n\t\tresources,\n\t\tmeta: options.meta,\n\t})\n}\n\n/**\n * Gives the descriptor of the resource with a kind name, or `undefined` when\n * the domain has no such resource.\n *\n * @stability stable\n * @category getters\n * @since 1.0.0\n */\nexport const descriptorOf = (\n\tdomain: Any,\n\tkind: string,\n): Resource.Descriptor | undefined =>\n\tOption.getOrUndefined(HashMap.get(domain.resources, kind))\n\n/**\n * Tests if a value is a domain.\n *\n * @stability stable\n * @category guards\n * @since 0.3.0\n */\nexport const isDomain = (u: unknown): u is Any => u instanceof DomainData\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA0KA,IAAM,aAAN,cAAyB,KAAK,MAI3B,CAAC;;;;;;;;;;;AAYJ,MAAa,QAQ4C,YAAY;CACpE,MAAM,cAAc,IAAI,IAAI,QAAQ,WAAWA,UAAmB;CAClE,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;CAEA,WAAoB,QAAQ,SAAS;CACrC,OAAO,IAAI,WAAW;EACrB,WAAW,QAAQ;EACnB;EACA,MAAM,QAAQ;CACf,CAAC;AACF;;;;;;;;;AAUA,MAAa,gBACZ,QACA,SAEA,OAAO,eAAe,QAAQ,IAAI,OAAO,WAAW,IAAI,CAAC;;;;;;;;AAS1D,MAAa,YAAY,MAAyB,aAAa"}
1
+ {"version":3,"file":"Domain.js","names":["Internal.make"],"sources":["../src/Domain.ts"],"sourcesContent":["/**\n * A domain puts together the principal and all the resources of an\n * application. All other modules get their types from the domain. A rule may\n * only use the actions of its resource. A condition may only use the fields of\n * its resource and of the principal.\n *\n * @example\n * ```ts\n * import { Domain } from \"@nmnmcc/ability\"\n *\n * const App = Domain.make({ principal: Member, resources: [Document, Comment] })\n * ```\n *\n * @stability stable\n * @since 0.3.0\n */\nimport type * as HashMap from \"effect/HashMap\"\nimport type * as Schema from \"effect/Schema\"\nimport type * as Catalog from \"./internal/catalog.ts\"\nimport * as Internal from \"./internal/domain.ts\"\nimport type * as Resource from \"./Resource.ts\"\n\n/** The Schema of a principal: a Schema of objects, such as a `Schema.Class`. */\ntype PrincipalSchema = Schema.Top & { readonly Type: object }\n\n/**\n * A domain. `P` is the type of the principal, `Resources` is the union of the\n * resources, and `Meta` is the type of the extra data that a rule may keep.\n *\n * @stability stable\n * @category models\n * @since 0.3.0\n */\nexport interface Domain<\n\tP extends object,\n\tResources extends Resource.Any,\n\tMeta,\n> {\n\treadonly principal: PrincipalSchema\n\t/** The catalog of the data fields of the principal. */\n\treadonly principalCatalog: Catalog.Catalog\n\t/** The descriptor of each resource, by its kind name. */\n\treadonly resources: HashMap.HashMap<string, Resource.Descriptor>\n\treadonly meta: Schema.Top | undefined\n\treadonly \"~Domain\"?: {\n\t\treadonly Principal: P\n\t\treadonly Resources: Resources\n\t\treadonly Meta: Meta\n\t}\n}\n\n/**\n * Any domain.\n *\n * @stability stable\n * @category models\n * @since 0.3.0\n */\nexport type Any = Domain<any, any, any>\n\n/**\n * The type of the principal of a domain.\n *\n * @stability stable\n * @category utility types\n * @since 0.3.0\n */\nexport type Principal<D> = D extends Domain<infer P, any, any> ? P : never\n\n/**\n * The union of the resources of a domain.\n *\n * @stability stable\n * @category utility types\n * @since 0.3.0\n */\nexport type Resources<D> = D extends Domain<any, infer R, any> ? R : never\n\n/**\n * The type of the extra data that a rule of the domain may keep.\n *\n * @stability stable\n * @category utility types\n * @since 0.3.0\n */\nexport type Meta<D> = D extends Domain<any, any, infer M> ? M : never\n\n/**\n * The kind names of a domain.\n *\n * @stability stable\n * @category utility types\n * @since 0.3.0\n */\nexport type Kind<D> = Resource.Kind<Resources<D>>\n\n/**\n * The resource of a domain with the kind `K`.\n *\n * @stability stable\n * @category utility types\n * @since 0.3.0\n */\nexport type ResourceOf<D, K extends string> =\n\tResources<D> extends infer R\n\t\t? R extends Resource.Resource<K, any, any, any>\n\t\t\t? R\n\t\t\t: never\n\t\t: never\n\n/**\n * The actions of the resource with the kind `K`.\n *\n * @stability stable\n * @category utility types\n * @since 0.3.0\n */\nexport type ActionOf<D, K extends string> = Resource.Action<ResourceOf<D, K>>\n\n/**\n * The actions that every kind in `K` has. For one kind, they are its actions.\n * For a union of kinds, an action of only some of them is left out, so a\n * value of the union type can not get an action that its kind does not have.\n *\n * @stability stable\n * @category utility types\n * @since 1.0.0\n */\nexport type CommonAction<D, K extends string> = (\n\tK extends unknown ? (action: ActionOf<D, K>) => void : never\n) extends (action: infer A) => void\n\t? A\n\t: never\n\n/**\n * All the actions of all the resources of a domain.\n *\n * @stability stable\n * @category utility types\n * @since 0.3.0\n */\nexport type AnyAction<D> = Resource.Action<Resources<D>>\n\n/**\n * The data type of the resource with the kind `K`.\n *\n * @stability stable\n * @category utility types\n * @since 0.3.0\n */\nexport type DataOf<D, K extends string> = Resource.Data<ResourceOf<D, K>>\n\n/**\n * The value type of the resource with the kind `K`.\n *\n * @stability stable\n * @category utility types\n * @since 0.3.0\n */\nexport type ValueOf<D, K extends string> = Resource.Value<ResourceOf<D, K>>\n\n/**\n * The union of the value types of all the resources of a domain.\n *\n * @stability stable\n * @category utility types\n * @since 0.3.0\n */\nexport type AnyValue<D> = Resource.Value<Resources<D>>\n\n/**\n * Makes a domain. Each resource must have its own kind name. Give `meta` when\n * rules must keep extra data, such as the scope of a grant. The `meta` Schema\n * may not need services, because `Policy.schema` gives a Schema that needs\n * none.\n *\n * @stability stable\n * @category constructors\n * @since 0.3.0\n */\nexport const make: <\n\tP extends PrincipalSchema,\n\tconst Resources extends ReadonlyArray<Resource.Any>,\n\tM extends Schema.Codec<any, any, never, never> = Schema.Never,\n>(options: {\n\treadonly principal: P\n\treadonly resources: Resources\n\treadonly meta?: M\n}) => Domain<P[\"Type\"], Resources[number], M[\"Type\"]> = Internal.make as never\n"],"mappings":";;;;;;;;;;;;;;AAoLA,MAAa,OAQ2CA"}
package/dist/HttpApi.d.ts CHANGED
@@ -1,10 +1,13 @@
1
+ import { Condition, Opaque } from "./Condition.js";
1
2
  import { Any, CommonAction, Kind, ValueOf } from "./Domain.js";
2
- import { Scope } from "./Policy.js";
3
- import { AnyServiceClass, Forbidden, Pending, PolicyUnavailable } from "./Authorizer.js";
3
+ import { ResidualReads, Scope } from "./Policy.js";
4
+ import { Forbidden, Pending, PolicyUnavailable } from "./internal/authorizer.js";
5
+ import { AnyServiceClass, Authorizer, PolicyUnavailable as PolicyUnavailable$1, ServiceClass } from "./Authorizer.js";
4
6
  import * as Context from "effect/Context";
5
7
  import * as Effect from "effect/Effect";
6
8
  import * as Layer from "effect/Layer";
7
9
  import * as Schema from "effect/Schema";
10
+ import { HttpRouter } from "effect/http";
8
11
  /**
9
12
  * The path parameters of a request.
10
13
  *
@@ -83,11 +86,34 @@ export declare const requires: <A extends {
83
86
  }, const K extends Kind<A["domain"]>>(authorizer: A, action: CommonAction<A["domain"], K>, kind: K, how?: ((params: Params) => Effect.Effect<ValueOf<A["domain"], K>, unknown, unknown>) | {
84
87
  readonly within: (params: Params) => Scope<A["domain"], K>;
85
88
  }) => Requirement;
89
+ /**
90
+ * A {@link Requirement} for an endpoint that gives a list, with the list query
91
+ * for the handler. `filter` takes the authorizer, which the handler gets when
92
+ * its group is built. It reads the path parameters of the request, and it
93
+ * gives the condition of `Authorizer.filter` in the scope of the requirement.
94
+ * So the scope is written one time, for the middleware and for the handler.
95
+ *
96
+ * @stability unstable
97
+ * @category models
98
+ * @since 2.0.0
99
+ */
100
+ export interface List<A, K extends string> extends Requirement {
101
+ readonly filter: A extends ServiceClass<any, any, infer D, infer E, infer R> ? (authorizer: Authorizer<D, E, R>) => Effect.Effect<Condition<ResidualReads<D, K>, Opaque<E>>, PolicyUnavailable$1 | E, R | HttpRouter.RouteContext> : never;
102
+ }
86
103
  /**
87
104
  * Makes a {@link Requirement} for an endpoint that gives a list. The middleware
88
105
  * does not check that the principal may act on every value, because the list
89
106
  * shows only the values that it may act on. The handler gets the list query
90
- * with `Authorizer.filter`, with the same scope.
107
+ * from `filter` of the result, with the same scope:
108
+ *
109
+ * ```ts
110
+ * const byAuthor = HttpApi.lists(Auth, "read", "Note", {
111
+ * within: (params) => ($) => $.eq("authorId", params.user ?? null),
112
+ * })
113
+ * // When the group is built: const auth = yield* Auth
114
+ * // In the handler:
115
+ * const where = yield* byAuthor.filter(auth)
116
+ * ```
91
117
  *
92
118
  * When the principal may act on no value, the list is empty: that is not an
93
119
  * error, so a client does not learn that hidden values are there. With
@@ -99,12 +125,10 @@ export declare const requires: <A extends {
99
125
  * @category constructors
100
126
  * @since 1.0.0
101
127
  */
102
- export declare const lists: <A extends {
103
- readonly domain: Any;
104
- }, const K extends Kind<A["domain"]>>(authorizer: A, action: CommonAction<A["domain"], K>, kind: K, options?: {
128
+ export declare const lists: <A extends AnyServiceClass, const K extends Kind<A["domain"]>>(authorizer: A, action: CommonAction<A["domain"], K>, kind: K, options?: {
105
129
  readonly within?: (params: Params) => Scope<A["domain"], K>;
106
130
  readonly nonEmpty?: boolean;
107
- }) => Requirement;
131
+ }) => List<A, K>;
108
132
  /**
109
133
  * The errors of the middleware, with HTTP status codes: 403 for
110
134
  * `Forbidden`, 503 for `PolicyUnavailable`, and 409 for `Pending`.
@@ -1 +1 @@
1
- {"version":3,"file":"HttpApi.d.ts","names":["Domain.Any","Domain.Kind","Domain.CommonAction","Domain.ValueOf","Policy.Scope","Authorizer.Forbidden","Authorizer.PolicyUnavailable","Authorizer.Pending","Authorizer.AnyServiceClass"],"sources":["../src/HttpApi.ts"],"mappings":";;;;;;;;;;;;;;YAyDY,SAAS,SAAS;;;;;;;;;;;;;;;;;iBAkBb;WACP;WACA;WACA,QAAQ,QAAQ,WAAW,OAAO;WAClC,UAAU,QAAQ;WAClB;aAAkB;;;;;;;;;;;qBAUf,iBAAiB;;WAgBb,MAAA,cAAc;WACb,OAAA,cAAc;;;;;;;;;;qBAJnB,sBAAsB;;;;;;;;;;;;;;;;;;;qBA0BtB,WACZ;WAAqB,QAAQA;SACvB,UAAUC,KAAY,cAE5B,YAAY,GACZ,QAAQC,aAAoB,aAAa,IACzC,MAAM,GACN,QAEG,QAAQ,WACH,OAAO,OAAOC,QAAe,aAAa;WACpC,SAAS,QAAQ,WAAWC,MAAa,aAAa;MAChE;;;;;;;;;;;;;;;;;qBAuBQ,QACZ;WAAqB,QAAQJ;SACvB,UAAUC,KAAY,cAE5B,YAAY,GACZ,QAAQC,aAAoB,aAAa,IACzC,MAAM,GACN;WACU,UAAU,QAAQ,WAAWE,MAAa,aAAa;WACvD;MAEN;;;;;;;;;;;;;qBAuCQ,kBAAM,OAAA,SAAA,OAAA,mBAAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;qBAyHN,QACZ,MACA,mBACA,UAAUG,iBACV,WAEA,YAAY,QAAQ,aAAa,MAAM,UACvC,YAAY,MACR,MAAM,MACV,cAEC,UAAU,QAAQ,UAAU,UAAU,aAAa"}
1
+ {"version":3,"file":"HttpApi.d.ts","names":["Domain.Any","Domain.Kind","Domain.CommonAction","Domain.ValueOf","Policy.Scope","Authorizer.ServiceClass","Authorizer.Authorizer","Policy.ResidualReads","Condition.Opaque","Condition.Condition","Authorizer.PolicyUnavailable","Authorizer.AnyServiceClass","_$__internal_authorizer_ts0.Forbidden","_$__internal_authorizer_ts0.PolicyUnavailable","_$__internal_authorizer_ts0.Pending"],"sources":["../src/HttpApi.ts"],"mappings":";;;;;;;;;;;;;;;;;YA0DY,SAAS,SAAS;;;;;;;;;;;;;;;;;iBAkBb;WACP;WACA;WACA,QAAQ,QAAQ,WAAW,OAAO;WAClC,UAAU,QAAQ;WAClB;aAAkB;;;;;;;;;;;qBAUf,iBAAiB;;WAgBb,MAAA,cAAc;WACb,OAAA,cAAc;;;;;;;;;;qBAJnB,sBAAsB;;;;;;;;;;;;;;;;;;;qBA0BtB,WACZ;WAAqB,QAAQA;SACvB,UAAUC,KAAY,cAE5B,YAAY,GACZ,QAAQC,aAAoB,aAAa,IACzC,MAAM,GACN,QAEG,QAAQ,WACH,OAAO,OAAOC,QAAe,aAAa;WACpC,SAAS,QAAQ,WAAWC,MAAa,aAAa;MAChE;;;;;;;;;;;;iBAkBY,KAAK,GAAG,0BAA0B;WACzC,QAAQ,UAAUC,6BAGpB,SACA,SACA,MAGJ,YAAYC,WAAsB,GAAG,GAAG,OACpC,OAAO,OACXG,UAAoBF,cAAqB,GAAG,IAAIC,OAAiB,KACjEE,sBAA+B,GAC/B,IAAI,WAAW;;;;;;;;;;;;;;;;;;;;;;;;;;;qBA8BN,QACZ,UAAUC,uBACJ,UAAUV,KAAY,cAE5B,YAAY,GACZ,QAAQC,aAAoB,aAAa,IACzC,MAAM,GACN;WACU,UAAU,QAAQ,WAAWE,MAAa,aAAa;WACvD;MAEN,KAAK,GAAG;;;;;;;;;;;;;qBA8DA,kBAAM,OAAA,SAAA,OAAA,mBAANQ;;;;;;;;;;;;;;;;;;;;;;mB,+BAAMC,mB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;qBAyHN,QACZ,MACA,mBACA,UAAUF,iBACV,WAEA,YAAY,QAAQ,aAAa,MAAM,UACvC,YAAY,MACR,MAAM,MACV,cAEC,UAAU,QAAQ,UAAU,UAAU,aAAa"}
package/dist/HttpApi.js CHANGED
@@ -1,11 +1,11 @@
1
- import { kindOf } from "./Resource.js";
1
+ import { kindOf } from "./internal/resource.js";
2
2
  import { Forbidden, Pending, PolicyUnavailable } from "./Authorizer.js";
3
3
  import * as Context from "effect/Context";
4
4
  import * as Effect from "effect/Effect";
5
5
  import * as Layer from "effect/Layer";
6
- import * as Option from "effect/Option";
7
6
  import * as Schema from "effect/Schema";
8
7
  import * as SchemaTransformation from "effect/SchemaTransformation";
8
+ import * as Option from "effect/Option";
9
9
  import { HttpRouter } from "effect/http";
10
10
  import { HttpApiSchema } from "effect/http-api";
11
11
  // src/HttpApi.ts
@@ -93,7 +93,16 @@ const requires = (_authorizer, action, kind, how) => how === void 0 ? {
93
93
  * Makes a {@link Requirement} for an endpoint that gives a list. The middleware
94
94
  * does not check that the principal may act on every value, because the list
95
95
  * shows only the values that it may act on. The handler gets the list query
96
- * with `Authorizer.filter`, with the same scope.
96
+ * from `filter` of the result, with the same scope:
97
+ *
98
+ * ```ts
99
+ * const byAuthor = HttpApi.lists(Auth, "read", "Note", {
100
+ * within: (params) => ($) => $.eq("authorId", params.user ?? null),
101
+ * })
102
+ * // When the group is built: const auth = yield* Auth
103
+ * // In the handler:
104
+ * const where = yield* byAuthor.filter(auth)
105
+ * ```
97
106
  *
98
107
  * When the principal may act on no value, the list is empty: that is not an
99
108
  * error, so a client does not learn that hidden values are there. With
@@ -105,11 +114,19 @@ const requires = (_authorizer, action, kind, how) => how === void 0 ? {
105
114
  * @category constructors
106
115
  * @since 1.0.0
107
116
  */
108
- const lists = (_authorizer, action, kind, options) => ({
109
- action,
110
- kind,
111
- list: { nonEmpty: options?.nonEmpty ?? false },
112
- ...options?.within === void 0 ? {} : { within: options.within }
117
+ const lists = ((_authorizer, action, kind, options) => {
118
+ const within = options?.within;
119
+ return {
120
+ action,
121
+ kind,
122
+ list: { nonEmpty: options?.nonEmpty ?? false },
123
+ ...within === void 0 ? {} : { within },
124
+ filter: (authorizer) => Effect.flatMap(HttpRouter.params, (params) => authorizer.filter({
125
+ action,
126
+ kind,
127
+ ...within === void 0 ? {} : { within: within(params) }
128
+ }))
129
+ };
113
130
  });
114
131
  /**
115
132
  * The Schema of a `PolicyUnavailable` response. It encodes only the `_tag`, so
@@ -1 +1 @@
1
- {"version":3,"file":"HttpApi.js","names":["Authorizer.PolicyUnavailable","Resource.kindOf"],"sources":["../src/HttpApi.ts"],"sourcesContent":["/**\n * Tools to check authorization in an `effect/http-api` server.\n *\n * Each endpoint says what it needs with the {@link Required} annotation. A\n * group can give a default with {@link GroupRequired}: one list for GET\n * requests and one list for all other methods. A middleware checks the\n * requirements before the handler runs, in each group that uses it. An\n * endpoint with no annotation is a defect, so a new endpoint is closed until\n * you say what it needs. An empty list means the endpoint is open. An endpoint\n * in a group without the middleware is not checked at all.\n *\n * ```ts\n * class Authorization extends HttpApiMiddleware.Service<\n * Authorization,\n * { requires: Identity }\n * >()(\"app/Authorization\", { error: HttpApi.errors }) {}\n *\n * const endpoint = HttpApiEndpoint.patch(\"update\", \"/notes/:id\", { ... })\n * .annotate(HttpApi.Required, [\n * HttpApi.requires(Auth, \"update\", \"Note\", (params) => Notes.find(params.id)),\n * ])\n *\n * const notes = HttpApiGroup.make(\"notes\").add(endpoint).middleware(Authorization)\n *\n * const AuthorizationLive = HttpApi.layer(Authorization, Auth)\n * ```\n *\n * This module is a separate entry point, `@nmnmcc/ability/HttpApi`, because\n * `effect/http-api` is not stable yet.\n *\n * @stability unstable\n * @since 0.3.0\n */\n// A `load` function of a requirement is kept in an annotation, so its error\n// type and its service type are erased to `unknown`. The language service\n// check for `unknown` in an Effect is off here.\n// @effect-diagnostics anyUnknownInErrorContext:off\nimport * as Context from \"effect/Context\"\nimport * as Effect from \"effect/Effect\"\nimport * as Layer from \"effect/Layer\"\nimport * as Option from \"effect/Option\"\nimport * as Schema from \"effect/Schema\"\nimport * as SchemaTransformation from \"effect/SchemaTransformation\"\nimport { HttpRouter } from \"effect/http\"\nimport { HttpApiSchema } from \"effect/http-api\"\nimport * as Authorizer from \"./Authorizer.ts\"\nimport type * as Domain from \"./Domain.ts\"\nimport type * as Policy from \"./Policy.ts\"\nimport * as Resource from \"./Resource.ts\"\n\n/**\n * The path parameters of a request.\n *\n * @stability unstable\n * @category models\n * @since 1.0.0\n */\nexport type Params = Readonly<Record<string, string | undefined>>\n\n/**\n * One check that an endpoint needs: an action on a kind of resource.\n * - With `load`, the middleware loads the resource value from the path\n * parameters and checks the action on that value.\n * - With `within`, it makes a scope from the path parameters and checks the\n * action on every value in the scope.\n * - With `list`, the endpoint gives a list. The handler gets the list query\n * with `Authorizer.filter`, so the middleware checks nothing, unless\n * `nonEmpty` is true: then it fails when the principal may act on no value\n * in the scope.\n * - With none of them, it checks the action on every value of the kind.\n *\n * @stability unstable\n * @category models\n * @since 0.3.0\n */\nexport interface Requirement {\n\treadonly action: string\n\treadonly kind: string\n\treadonly load?: (params: Params) => Effect.Effect<object, unknown, unknown>\n\treadonly within?: (params: Params) => unknown\n\treadonly list?: { readonly nonEmpty: boolean }\n}\n\n/**\n * The checks that an endpoint needs.\n *\n * @stability unstable\n * @category annotations\n * @since 0.3.0\n */\nexport class Required extends Context.Service<\n\tRequired,\n\tReadonlyArray<Requirement>\n>()(\"@nmnmcc/ability/HttpApi/Required\") {}\n\n/**\n * The checks for every endpoint of a group with no {@link Required}\n * annotation. `read` is for GET requests. `write` is for all other methods.\n *\n * @stability unstable\n * @category annotations\n * @since 0.3.0\n */\nexport class GroupRequired extends Context.Service<\n\tGroupRequired,\n\t{\n\t\treadonly read: ReadonlyArray<Requirement>\n\t\treadonly write: ReadonlyArray<Requirement>\n\t}\n>()(\"@nmnmcc/ability/HttpApi/GroupRequired\") {}\n\n/**\n * Makes a {@link Requirement} with the types of an authorizer. The action must\n * be an action of the kind, also when the kind has a union type.\n *\n * The last argument is one of:\n * - a `load` function, which must give a value of the kind. It may fail. Its\n * errors must be errors of the middleware, and the services that it needs\n * must be given to {@link layer}.\n * - `{ within }`, a function from the path parameters to a scope. Use it for\n * an act on a part of the kind, such as a new value with no id yet:\n * `{ within: (params) => ($) => $.under(\"org\", params.org ?? null) }`. A path\n * parameter may be `undefined`, and a scope with `undefined` in it is a\n * defect, so give a value for it.\n *\n * @stability unstable\n * @category constructors\n * @since 0.3.0\n */\nexport const requires: <\n\tA extends { readonly domain: Domain.Any },\n\tconst K extends Domain.Kind<A[\"domain\"]>,\n>(\n\tauthorizer: A,\n\taction: Domain.CommonAction<A[\"domain\"], K>,\n\tkind: K,\n\thow?:\n\t\t| ((\n\t\t\t\tparams: Params,\n\t\t ) => Effect.Effect<Domain.ValueOf<A[\"domain\"], K>, unknown, unknown>)\n\t\t| { readonly within: (params: Params) => Policy.Scope<A[\"domain\"], K> },\n) => Requirement = (_authorizer, action, kind, how) =>\n\thow === undefined\n\t\t? { action: action as string, kind }\n\t\t: typeof how === \"function\"\n\t\t\t? { action: action as string, kind, load: how }\n\t\t\t: { action: action as string, kind, within: how.within }\n\n/**\n * Makes a {@link Requirement} for an endpoint that gives a list. The middleware\n * does not check that the principal may act on every value, because the list\n * shows only the values that it may act on. The handler gets the list query\n * with `Authorizer.filter`, with the same scope.\n *\n * When the principal may act on no value, the list is empty: that is not an\n * error, so a client does not learn that hidden values are there. With\n * `nonEmpty: true`, the middleware fails with `Forbidden` in that case. It\n * also fails when the coverage is `Unknown`, because then it can not say that\n * the list has a value.\n *\n * @stability unstable\n * @category constructors\n * @since 1.0.0\n */\nexport const lists: <\n\tA extends { readonly domain: Domain.Any },\n\tconst K extends Domain.Kind<A[\"domain\"]>,\n>(\n\tauthorizer: A,\n\taction: Domain.CommonAction<A[\"domain\"], K>,\n\tkind: K,\n\toptions?: {\n\t\treadonly within?: (params: Params) => Policy.Scope<A[\"domain\"], K>\n\t\treadonly nonEmpty?: boolean\n\t},\n) => Requirement = (_authorizer, action, kind, options) => ({\n\taction: action as string,\n\tkind,\n\tlist: { nonEmpty: options?.nonEmpty ?? false },\n\t...(options?.within === undefined ? {} : { within: options.within }),\n})\n\n/**\n * The Schema of a `PolicyUnavailable` response. It encodes only the `_tag`, so\n * the cause does not go to the client.\n */\nconst PolicyUnavailableResponse = Schema.TaggedStruct(\n\t\"PolicyUnavailable\",\n\t{},\n).pipe(\n\tSchema.decodeTo(\n\t\tSchema.declare(\n\t\t\t(u): u is Authorizer.PolicyUnavailable =>\n\t\t\t\tu instanceof Authorizer.PolicyUnavailable,\n\t\t),\n\t\tSchemaTransformation.transform({\n\t\t\tdecode: () => Authorizer.PolicyUnavailable.make({ cause: undefined }),\n\t\t\tencode: () => ({ _tag: \"PolicyUnavailable\" as const }),\n\t\t}),\n\t),\n)\n\n/**\n * The errors of the middleware, with HTTP status codes: 403 for\n * `Forbidden`, 503 for `PolicyUnavailable`, and 409 for `Pending`.\n *\n * The body of a `PolicyUnavailable` response has only its `_tag`. The cause\n * may have private details, such as an error from the database, so it stays\n * on the server.\n *\n * @stability unstable\n * @category errors\n * @since 0.3.0\n */\nexport const errors = [\n\tAuthorizer.Forbidden.pipe(HttpApiSchema.status(403)),\n\tPolicyUnavailableResponse.pipe(HttpApiSchema.status(503)),\n\tAuthorizer.Pending.pipe(HttpApiSchema.status(409)),\n] as const\n\nconst makeLayer = (\n\tmiddleware: Context.ServiceClass<any, string, any>,\n\tauthorizer: Authorizer.AnyServiceClass,\n) =>\n\tLayer.effect(\n\t\tmiddleware,\n\t\tEffect.gen(function* () {\n\t\t\tconst service = yield* authorizer\n\t\t\tconst services = yield* Effect.context<never>()\n\t\t\treturn Effect.fnUntraced(function* (\n\t\t\t\thttpEffect: Effect.Effect<unknown, unknown, unknown>,\n\t\t\t\toptions: {\n\t\t\t\t\treadonly endpoint: {\n\t\t\t\t\t\treadonly name: string\n\t\t\t\t\t\treadonly method: string\n\t\t\t\t\t\treadonly annotations: Context.Context<never>\n\t\t\t\t\t}\n\t\t\t\t\treadonly group: {\n\t\t\t\t\t\treadonly identifier: string\n\t\t\t\t\t\treadonly annotations: Context.Context<never>\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t) {\n\t\t\t\tconst requirements = Option.orElse(\n\t\t\t\t\tContext.getOption(options.endpoint.annotations, Required),\n\t\t\t\t\t() =>\n\t\t\t\t\t\tOption.map(\n\t\t\t\t\t\t\tContext.getOption(options.group.annotations, GroupRequired),\n\t\t\t\t\t\t\t(rules) =>\n\t\t\t\t\t\t\t\toptions.endpoint.method === \"GET\" ? rules.read : rules.write,\n\t\t\t\t\t\t),\n\t\t\t\t)\n\t\t\t\tif (Option.isNone(requirements)) {\n\t\t\t\t\treturn yield* Effect.die(\n\t\t\t\t\t\tnew Error(\n\t\t\t\t\t\t\t`The endpoint ${options.group.identifier}.${options.endpoint.name} does not say what it needs. Give it the HttpApi.Required annotation.`,\n\t\t\t\t\t\t),\n\t\t\t\t\t)\n\t\t\t\t}\n\t\t\t\tconst endpoint = `${options.group.identifier}.${options.endpoint.name}`\n\t\t\t\t// The requirements are checked in order. The first one that fails\n\t\t\t\t// stops the request.\n\t\t\t\tyield* Effect.forEach(\n\t\t\t\t\trequirements.value,\n\t\t\t\t\t({ action, kind, load, within, list }) => {\n\t\t\t\t\t\tif (load !== undefined) {\n\t\t\t\t\t\t\treturn HttpRouter.params.pipe(\n\t\t\t\t\t\t\t\tEffect.flatMap((params) =>\n\t\t\t\t\t\t\t\t\tEffect.provideContext(load(params), services),\n\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t\t// The check is for the kind of the requirement. A value of\n\t\t\t\t\t\t\t\t// another kind is a mistake in `load`, and the check must\n\t\t\t\t\t\t\t\t// not pass with the rules of that other kind.\n\t\t\t\t\t\t\t\tEffect.filterOrElse(\n\t\t\t\t\t\t\t\t\t(value) => Resource.kindOf(value) === kind,\n\t\t\t\t\t\t\t\t\t() =>\n\t\t\t\t\t\t\t\t\t\tEffect.die(\n\t\t\t\t\t\t\t\t\t\t\tnew Error(\n\t\t\t\t\t\t\t\t\t\t\t\t`The load function of ${endpoint} gave a value that is not a ${kind}`,\n\t\t\t\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t\tEffect.flatMap((value) =>\n\t\t\t\t\t\t\t\t\tservice.check({ action, resource: value } as never),\n\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t)\n\t\t\t\t\t\t}\n\t\t\t\t\t\tconst target =\n\t\t\t\t\t\t\twithin === undefined\n\t\t\t\t\t\t\t\t? Effect.succeed({ action, kind } as never)\n\t\t\t\t\t\t\t\t: Effect.map(\n\t\t\t\t\t\t\t\t\t\tHttpRouter.params,\n\t\t\t\t\t\t\t\t\t\t(params) =>\n\t\t\t\t\t\t\t\t\t\t\t({ action, kind, within: within(params) }) as never,\n\t\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\tif (list === undefined) {\n\t\t\t\t\t\t\treturn Effect.flatMap(target, (value) => service.check(value))\n\t\t\t\t\t\t}\n\t\t\t\t\t\t// A list endpoint may give an empty list, unless it must not.\n\t\t\t\t\t\t// Only `All` and `Some` say that the list has a value. `Unknown`\n\t\t\t\t\t\t// is not a \"yes\".\n\t\t\t\t\t\treturn list.nonEmpty\n\t\t\t\t\t\t\t? target.pipe(\n\t\t\t\t\t\t\t\t\tEffect.flatMap((value) => service.coverage(value)),\n\t\t\t\t\t\t\t\t\tEffect.filterOrElse(\n\t\t\t\t\t\t\t\t\t\t(covered) =>\n\t\t\t\t\t\t\t\t\t\t\t(covered as { readonly _tag: string })._tag === \"All\" ||\n\t\t\t\t\t\t\t\t\t\t\t(covered as { readonly _tag: string })._tag === \"Some\",\n\t\t\t\t\t\t\t\t\t\t() =>\n\t\t\t\t\t\t\t\t\t\t\tEffect.fail(Authorizer.Forbidden.make({ action, kind })),\n\t\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t: Effect.void\n\t\t\t\t\t},\n\t\t\t\t\t{ discard: true },\n\t\t\t\t)\n\t\t\t\treturn yield* httpEffect\n\t\t\t})\n\t\t}),\n\t)\n\n/**\n * Makes the layer of an authorization middleware. The middleware reads the\n * {@link Required} and {@link GroupRequired} annotations and checks each\n * requirement with the authorizer.\n *\n * The layer takes the authorizer when it is built. The middleware must\n * require the services of the authorizer, such as the identity of the\n * caller. `R` is the type of the services that the `load` functions need. The\n * layer takes them when it is built too.\n *\n * @stability unstable\n * @category layers\n * @since 0.3.0\n */\nexport const layer: <\n\tSelf,\n\tId extends string,\n\tA extends Authorizer.AnyServiceClass,\n\tR = never,\n>(\n\tmiddleware: Context.ServiceClass<Self, Id, any>,\n\tauthorizer: A,\n) => Layer.Layer<\n\tSelf,\n\tnever,\n\t(A extends Context.Key<infer I, any> ? I : never) | R\n> = makeLayer as never\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA0FA,IAAa,WAAb,cAA8B,QAAQ,QAGpC,CAAC,CAAC,kCAAkC,CAAC,CAAC,CAAC;;;;;;;;;AAUzC,IAAa,gBAAb,cAAmC,QAAQ,QAMzC,CAAC,CAAC,uCAAuC,CAAC,CAAC,CAAC;;;;;;;;;;;;;;;;;;;AAoB9C,MAAa,YAYO,aAAa,QAAQ,MAAM,QAC9C,QAAQ,KAAA,IACL;CAAU;CAAkB;AAAK,IACjC,OAAO,QAAQ,aACd;CAAU;CAAkB;CAAM,MAAM;AAAI,IAC5C;CAAU;CAAkB;CAAM,QAAQ,IAAI;AAAO;;;;;;;;;;;;;;;;;AAkB1D,MAAa,SAWO,aAAa,QAAQ,MAAM,aAAa;CACnD;CACR;CACA,MAAM,EAAE,UAAU,SAAS,YAAY,MAAM;CAC7C,GAAI,SAAS,WAAW,KAAA,IAAY,CAAC,IAAI,EAAE,QAAQ,QAAQ,OAAO;AACnE;;;;;AAMA,MAAM,4BAA4B,OAAO,aACxC,qBACA,CAAC,CACF,CAAC,CAAC,KACD,OAAO,SACN,OAAO,SACL,MACA,aAAaA,iBACf,GACA,qBAAqB,UAAU;CAC9B,cAAA,kBAA2C,KAAK,EAAE,OAAO,KAAA,EAAU,CAAC;CACpE,eAAe,EAAE,MAAM,oBAA6B;AACrD,CAAC,CACF,CACD;;;;;;;;;;;;;AAcA,MAAa,SAAS;CACA,UAAA,KAAK,cAAc,OAAO,GAAG,CAAC;CACnD,0BAA0B,KAAK,cAAc,OAAO,GAAG,CAAC;CACrC,QAAA,KAAK,cAAc,OAAO,GAAG,CAAC;AAClD;AAEA,MAAM,aACL,YACA,eAEA,MAAM,OACL,YACA,OAAO,IAAI,aAAa;CACvB,MAAM,UAAU,OAAO;CACvB,MAAM,WAAW,OAAO,OAAO,QAAe;CAC9C,OAAO,OAAO,WAAW,WACxB,YACA,SAWC;EACD,MAAM,eAAe,OAAO,OAC3B,QAAQ,UAAU,QAAQ,SAAS,aAAa,QAAQ,SAEvD,OAAO,IACN,QAAQ,UAAU,QAAQ,MAAM,aAAa,aAAa,IACzD,UACA,QAAQ,SAAS,WAAW,QAAQ,MAAM,OAAO,MAAM,KACzD,CACF;EACA,IAAI,OAAO,OAAO,YAAY,GAC7B,OAAO,OAAO,OAAO,oBACpB,IAAI,MACH,gBAAgB,QAAQ,MAAM,WAAW,GAAG,QAAQ,SAAS,KAAK,sEACnE,CACD;EAED,MAAM,WAAW,GAAG,QAAQ,MAAM,WAAW,GAAG,QAAQ,SAAS;EAGjE,OAAO,OAAO,QACb,aAAa,QACZ,EAAE,QAAQ,MAAM,MAAM,QAAQ,WAAW;GACzC,IAAI,SAAS,KAAA,GACZ,OAAO,WAAW,OAAO,KACxB,OAAO,SAAS,WACf,OAAO,eAAe,KAAK,MAAM,GAAG,QAAQ,CAC7C,GAIA,OAAO,cACL,UAAUC,OAAgB,KAAK,MAAM,YAErC,OAAO,oBACN,IAAI,MACH,wBAAwB,SAAS,8BAA8B,MAChE,CACD,CACF,GACA,OAAO,SAAS,UACf,QAAQ,MAAM;IAAE;IAAQ,UAAU;GAAM,CAAU,CACnD,CACD;GAED,MAAM,SACL,WAAW,KAAA,IACR,OAAO,QAAQ;IAAE;IAAQ;GAAK,CAAU,IACxC,OAAO,IACP,WAAW,SACV,YACC;IAAE;IAAQ;IAAM,QAAQ,OAAO,MAAM;GAAE,EAC1C;GACH,IAAI,SAAS,KAAA,GACZ,OAAO,OAAO,QAAQ,SAAS,UAAU,QAAQ,MAAM,KAAK,CAAC;GAK9D,OAAO,KAAK,WACT,OAAO,KACP,OAAO,SAAS,UAAU,QAAQ,SAAS,KAAK,CAAC,GACjD,OAAO,cACL,YACC,QAAsC,SAAS,SAC/C,QAAsC,SAAS,cAEhD,OAAO,KAAA,UAA0B,KAAK;IAAE;IAAQ;GAAK,CAAC,CAAC,CACzD,CACD,IACC,OAAO;EACX,GACA,EAAE,SAAS,KAAK,CACjB;EACA,OAAO,OAAO;CACf,CAAC;AACF,CAAC,CACF;;;;;;;;;;;;;;;AAgBD,MAAa,QAYT"}
1
+ {"version":3,"file":"HttpApi.js","names":["Authorizer.PolicyUnavailable","ResourceData.kindOf"],"sources":["../src/HttpApi.ts"],"sourcesContent":["/**\n * Tools to check authorization in an `effect/http-api` server.\n *\n * Each endpoint says what it needs with the {@link Required} annotation. A\n * group can give a default with {@link GroupRequired}: one list for GET\n * requests and one list for all other methods. A middleware checks the\n * requirements before the handler runs, in each group that uses it. An\n * endpoint with no annotation is a defect, so a new endpoint is closed until\n * you say what it needs. An empty list means the endpoint is open. An endpoint\n * in a group without the middleware is not checked at all.\n *\n * ```ts\n * class Authorization extends HttpApiMiddleware.Service<\n * Authorization,\n * { requires: Identity }\n * >()(\"app/Authorization\", { error: HttpApi.errors }) {}\n *\n * const endpoint = HttpApiEndpoint.patch(\"update\", \"/notes/:id\", { ... })\n * .annotate(HttpApi.Required, [\n * HttpApi.requires(Auth, \"update\", \"Note\", (params) => Notes.find(params.id)),\n * ])\n *\n * const notes = HttpApiGroup.make(\"notes\").add(endpoint).middleware(Authorization)\n *\n * const AuthorizationLive = HttpApi.layer(Authorization, Auth)\n * ```\n *\n * This module is a separate entry point, `@nmnmcc/ability/HttpApi`, because\n * `effect/http-api` is not stable yet.\n *\n * @stability unstable\n * @since 0.3.0\n */\n// A `load` function of a requirement is kept in an annotation, so its error\n// type and its service type are erased to `unknown`. The language service\n// check for `unknown` in an Effect is off here.\n// @effect-diagnostics anyUnknownInErrorContext:off\nimport * as Context from \"effect/Context\"\nimport * as Effect from \"effect/Effect\"\nimport * as Layer from \"effect/Layer\"\nimport * as Option from \"effect/Option\"\nimport * as Schema from \"effect/Schema\"\nimport * as SchemaTransformation from \"effect/SchemaTransformation\"\nimport { HttpRouter } from \"effect/http\"\nimport { HttpApiSchema } from \"effect/http-api\"\nimport * as Authorizer from \"./Authorizer.ts\"\nimport type * as Condition from \"./Condition.ts\"\nimport type * as Domain from \"./Domain.ts\"\nimport type * as Policy from \"./Policy.ts\"\nimport * as ResourceData from \"./internal/resource.ts\"\n\n/**\n * The path parameters of a request.\n *\n * @stability unstable\n * @category models\n * @since 1.0.0\n */\nexport type Params = Readonly<Record<string, string | undefined>>\n\n/**\n * One check that an endpoint needs: an action on a kind of resource.\n * - With `load`, the middleware loads the resource value from the path\n * parameters and checks the action on that value.\n * - With `within`, it makes a scope from the path parameters and checks the\n * action on every value in the scope.\n * - With `list`, the endpoint gives a list. The handler gets the list query\n * with `Authorizer.filter`, so the middleware checks nothing, unless\n * `nonEmpty` is true: then it fails when the principal may act on no value\n * in the scope.\n * - With none of them, it checks the action on every value of the kind.\n *\n * @stability unstable\n * @category models\n * @since 0.3.0\n */\nexport interface Requirement {\n\treadonly action: string\n\treadonly kind: string\n\treadonly load?: (params: Params) => Effect.Effect<object, unknown, unknown>\n\treadonly within?: (params: Params) => unknown\n\treadonly list?: { readonly nonEmpty: boolean }\n}\n\n/**\n * The checks that an endpoint needs.\n *\n * @stability unstable\n * @category annotations\n * @since 0.3.0\n */\nexport class Required extends Context.Service<\n\tRequired,\n\tReadonlyArray<Requirement>\n>()(\"@nmnmcc/ability/HttpApi/Required\") {}\n\n/**\n * The checks for every endpoint of a group with no {@link Required}\n * annotation. `read` is for GET requests. `write` is for all other methods.\n *\n * @stability unstable\n * @category annotations\n * @since 0.3.0\n */\nexport class GroupRequired extends Context.Service<\n\tGroupRequired,\n\t{\n\t\treadonly read: ReadonlyArray<Requirement>\n\t\treadonly write: ReadonlyArray<Requirement>\n\t}\n>()(\"@nmnmcc/ability/HttpApi/GroupRequired\") {}\n\n/**\n * Makes a {@link Requirement} with the types of an authorizer. The action must\n * be an action of the kind, also when the kind has a union type.\n *\n * The last argument is one of:\n * - a `load` function, which must give a value of the kind. It may fail. Its\n * errors must be errors of the middleware, and the services that it needs\n * must be given to {@link layer}.\n * - `{ within }`, a function from the path parameters to a scope. Use it for\n * an act on a part of the kind, such as a new value with no id yet:\n * `{ within: (params) => ($) => $.under(\"org\", params.org ?? null) }`. A path\n * parameter may be `undefined`, and a scope with `undefined` in it is a\n * defect, so give a value for it.\n *\n * @stability unstable\n * @category constructors\n * @since 0.3.0\n */\nexport const requires: <\n\tA extends { readonly domain: Domain.Any },\n\tconst K extends Domain.Kind<A[\"domain\"]>,\n>(\n\tauthorizer: A,\n\taction: Domain.CommonAction<A[\"domain\"], K>,\n\tkind: K,\n\thow?:\n\t\t| ((\n\t\t\t\tparams: Params,\n\t\t ) => Effect.Effect<Domain.ValueOf<A[\"domain\"], K>, unknown, unknown>)\n\t\t| { readonly within: (params: Params) => Policy.Scope<A[\"domain\"], K> },\n) => Requirement = (_authorizer, action, kind, how) =>\n\thow === undefined\n\t\t? { action: action as string, kind }\n\t\t: typeof how === \"function\"\n\t\t\t? { action: action as string, kind, load: how }\n\t\t\t: { action: action as string, kind, within: how.within }\n\n/**\n * A {@link Requirement} for an endpoint that gives a list, with the list query\n * for the handler. `filter` takes the authorizer, which the handler gets when\n * its group is built. It reads the path parameters of the request, and it\n * gives the condition of `Authorizer.filter` in the scope of the requirement.\n * So the scope is written one time, for the middleware and for the handler.\n *\n * @stability unstable\n * @category models\n * @since 2.0.0\n */\nexport interface List<A, K extends string> extends Requirement {\n\treadonly filter: A extends Authorizer.ServiceClass<\n\t\tany,\n\t\tany,\n\t\tinfer D,\n\t\tinfer E,\n\t\tinfer R\n\t>\n\t\t? (\n\t\t\t\tauthorizer: Authorizer.Authorizer<D, E, R>,\n\t\t\t) => Effect.Effect<\n\t\t\t\tCondition.Condition<Policy.ResidualReads<D, K>, Condition.Opaque<E>>,\n\t\t\t\tAuthorizer.PolicyUnavailable | E,\n\t\t\t\tR | HttpRouter.RouteContext\n\t\t\t>\n\t\t: never\n}\n\n/**\n * Makes a {@link Requirement} for an endpoint that gives a list. The middleware\n * does not check that the principal may act on every value, because the list\n * shows only the values that it may act on. The handler gets the list query\n * from `filter` of the result, with the same scope:\n *\n * ```ts\n * const byAuthor = HttpApi.lists(Auth, \"read\", \"Note\", {\n * within: (params) => ($) => $.eq(\"authorId\", params.user ?? null),\n * })\n * // When the group is built: const auth = yield* Auth\n * // In the handler:\n * const where = yield* byAuthor.filter(auth)\n * ```\n *\n * When the principal may act on no value, the list is empty: that is not an\n * error, so a client does not learn that hidden values are there. With\n * `nonEmpty: true`, the middleware fails with `Forbidden` in that case. It\n * also fails when the coverage is `Unknown`, because then it can not say that\n * the list has a value.\n *\n * @stability unstable\n * @category constructors\n * @since 1.0.0\n */\nexport const lists: <\n\tA extends Authorizer.AnyServiceClass,\n\tconst K extends Domain.Kind<A[\"domain\"]>,\n>(\n\tauthorizer: A,\n\taction: Domain.CommonAction<A[\"domain\"], K>,\n\tkind: K,\n\toptions?: {\n\t\treadonly within?: (params: Params) => Policy.Scope<A[\"domain\"], K>\n\t\treadonly nonEmpty?: boolean\n\t},\n) => List<A, K> = ((\n\t_authorizer: Authorizer.AnyServiceClass,\n\taction: string,\n\tkind: string,\n\toptions?: {\n\t\treadonly within?: (params: Params) => unknown\n\t\treadonly nonEmpty?: boolean\n\t},\n) => {\n\tconst within = options?.within\n\treturn {\n\t\taction,\n\t\tkind,\n\t\tlist: { nonEmpty: options?.nonEmpty ?? false },\n\t\t...(within === undefined ? {} : { within }),\n\t\tfilter: (authorizer: {\n\t\t\treadonly filter: (\n\t\t\t\ttarget: object,\n\t\t\t) => Effect.Effect<unknown, unknown, unknown>\n\t\t}) =>\n\t\t\tEffect.flatMap(HttpRouter.params, (params) =>\n\t\t\t\tauthorizer.filter({\n\t\t\t\t\taction,\n\t\t\t\t\tkind,\n\t\t\t\t\t...(within === undefined ? {} : { within: within(params) }),\n\t\t\t\t}),\n\t\t\t),\n\t}\n}) as never\n\n/**\n * The Schema of a `PolicyUnavailable` response. It encodes only the `_tag`, so\n * the cause does not go to the client.\n */\nconst PolicyUnavailableResponse = Schema.TaggedStruct(\n\t\"PolicyUnavailable\",\n\t{},\n).pipe(\n\tSchema.decodeTo(\n\t\tSchema.declare(\n\t\t\t(u): u is Authorizer.PolicyUnavailable =>\n\t\t\t\tu instanceof Authorizer.PolicyUnavailable,\n\t\t),\n\t\tSchemaTransformation.transform({\n\t\t\tdecode: () => Authorizer.PolicyUnavailable.make({ cause: undefined }),\n\t\t\tencode: () => ({ _tag: \"PolicyUnavailable\" as const }),\n\t\t}),\n\t),\n)\n\n/**\n * The errors of the middleware, with HTTP status codes: 403 for\n * `Forbidden`, 503 for `PolicyUnavailable`, and 409 for `Pending`.\n *\n * The body of a `PolicyUnavailable` response has only its `_tag`. The cause\n * may have private details, such as an error from the database, so it stays\n * on the server.\n *\n * @stability unstable\n * @category errors\n * @since 0.3.0\n */\nexport const errors = [\n\tAuthorizer.Forbidden.pipe(HttpApiSchema.status(403)),\n\tPolicyUnavailableResponse.pipe(HttpApiSchema.status(503)),\n\tAuthorizer.Pending.pipe(HttpApiSchema.status(409)),\n] as const\n\nconst makeLayer = (\n\tmiddleware: Context.ServiceClass<any, string, any>,\n\tauthorizer: Authorizer.AnyServiceClass,\n) =>\n\tLayer.effect(\n\t\tmiddleware,\n\t\tEffect.gen(function* () {\n\t\t\tconst service = yield* authorizer\n\t\t\tconst services = yield* Effect.context<never>()\n\t\t\treturn Effect.fnUntraced(function* (\n\t\t\t\thttpEffect: Effect.Effect<unknown, unknown, unknown>,\n\t\t\t\toptions: {\n\t\t\t\t\treadonly endpoint: {\n\t\t\t\t\t\treadonly name: string\n\t\t\t\t\t\treadonly method: string\n\t\t\t\t\t\treadonly annotations: Context.Context<never>\n\t\t\t\t\t}\n\t\t\t\t\treadonly group: {\n\t\t\t\t\t\treadonly identifier: string\n\t\t\t\t\t\treadonly annotations: Context.Context<never>\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t) {\n\t\t\t\tconst requirements = Option.orElse(\n\t\t\t\t\tContext.getOption(options.endpoint.annotations, Required),\n\t\t\t\t\t() =>\n\t\t\t\t\t\tOption.map(\n\t\t\t\t\t\t\tContext.getOption(options.group.annotations, GroupRequired),\n\t\t\t\t\t\t\t(rules) =>\n\t\t\t\t\t\t\t\toptions.endpoint.method === \"GET\" ? rules.read : rules.write,\n\t\t\t\t\t\t),\n\t\t\t\t)\n\t\t\t\tif (Option.isNone(requirements)) {\n\t\t\t\t\treturn yield* Effect.die(\n\t\t\t\t\t\tnew Error(\n\t\t\t\t\t\t\t`The endpoint ${options.group.identifier}.${options.endpoint.name} does not say what it needs. Give it the HttpApi.Required annotation.`,\n\t\t\t\t\t\t),\n\t\t\t\t\t)\n\t\t\t\t}\n\t\t\t\tconst endpoint = `${options.group.identifier}.${options.endpoint.name}`\n\t\t\t\t// The requirements are checked in order. The first one that fails\n\t\t\t\t// stops the request.\n\t\t\t\tyield* Effect.forEach(\n\t\t\t\t\trequirements.value,\n\t\t\t\t\t({ action, kind, load, within, list }) => {\n\t\t\t\t\t\tif (load !== undefined) {\n\t\t\t\t\t\t\treturn HttpRouter.params.pipe(\n\t\t\t\t\t\t\t\tEffect.flatMap((params) =>\n\t\t\t\t\t\t\t\t\tEffect.provideContext(load(params), services),\n\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t\t// The check is for the kind of the requirement. A value of\n\t\t\t\t\t\t\t\t// another kind is a mistake in `load`, and the check must\n\t\t\t\t\t\t\t\t// not pass with the rules of that other kind.\n\t\t\t\t\t\t\t\tEffect.filterOrElse(\n\t\t\t\t\t\t\t\t\t(value) => ResourceData.kindOf(value) === kind,\n\t\t\t\t\t\t\t\t\t() =>\n\t\t\t\t\t\t\t\t\t\tEffect.die(\n\t\t\t\t\t\t\t\t\t\t\tnew Error(\n\t\t\t\t\t\t\t\t\t\t\t\t`The load function of ${endpoint} gave a value that is not a ${kind}`,\n\t\t\t\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t\tEffect.flatMap((value) =>\n\t\t\t\t\t\t\t\t\tservice.check({ action, resource: value } as never),\n\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t)\n\t\t\t\t\t\t}\n\t\t\t\t\t\tconst target =\n\t\t\t\t\t\t\twithin === undefined\n\t\t\t\t\t\t\t\t? Effect.succeed({ action, kind } as never)\n\t\t\t\t\t\t\t\t: Effect.map(\n\t\t\t\t\t\t\t\t\t\tHttpRouter.params,\n\t\t\t\t\t\t\t\t\t\t(params) =>\n\t\t\t\t\t\t\t\t\t\t\t({ action, kind, within: within(params) }) as never,\n\t\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\tif (list === undefined) {\n\t\t\t\t\t\t\treturn Effect.flatMap(target, (value) => service.check(value))\n\t\t\t\t\t\t}\n\t\t\t\t\t\t// A list endpoint may give an empty list, unless it must not.\n\t\t\t\t\t\t// Only `All` and `Some` say that the list has a value. `Unknown`\n\t\t\t\t\t\t// is not a \"yes\".\n\t\t\t\t\t\treturn list.nonEmpty\n\t\t\t\t\t\t\t? target.pipe(\n\t\t\t\t\t\t\t\t\tEffect.flatMap((value) => service.coverage(value)),\n\t\t\t\t\t\t\t\t\tEffect.filterOrElse(\n\t\t\t\t\t\t\t\t\t\t(covered) =>\n\t\t\t\t\t\t\t\t\t\t\t(covered as { readonly _tag: string })._tag === \"All\" ||\n\t\t\t\t\t\t\t\t\t\t\t(covered as { readonly _tag: string })._tag === \"Some\",\n\t\t\t\t\t\t\t\t\t\t() =>\n\t\t\t\t\t\t\t\t\t\t\tEffect.fail(Authorizer.Forbidden.make({ action, kind })),\n\t\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t: Effect.void\n\t\t\t\t\t},\n\t\t\t\t\t{ discard: true },\n\t\t\t\t)\n\t\t\t\treturn yield* httpEffect\n\t\t\t})\n\t\t}),\n\t)\n\n/**\n * Makes the layer of an authorization middleware. The middleware reads the\n * {@link Required} and {@link GroupRequired} annotations and checks each\n * requirement with the authorizer.\n *\n * The layer takes the authorizer when it is built. The middleware must\n * require the services of the authorizer, such as the identity of the\n * caller. `R` is the type of the services that the `load` functions need. The\n * layer takes them when it is built too.\n *\n * @stability unstable\n * @category layers\n * @since 0.3.0\n */\nexport const layer: <\n\tSelf,\n\tId extends string,\n\tA extends Authorizer.AnyServiceClass,\n\tR = never,\n>(\n\tmiddleware: Context.ServiceClass<Self, Id, any>,\n\tauthorizer: A,\n) => Layer.Layer<\n\tSelf,\n\tnever,\n\t(A extends Context.Key<infer I, any> ? I : never) | R\n> = makeLayer as never\n"],"mappings":"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"}
@@ -0,0 +1,114 @@
1
+ import { LimitExceeded as LimitExceeded$1 } from "./internal/limits.js";
2
+ declare namespace Limits_d_exports {
3
+ export { LimitExceeded, catalogFields, conditionDepth, conditionNodes, fieldPathParts, jsonNodes, patternParts, policyDepth, policyNodes, residualNodes, searchFields, searchSteps };
4
+ }
5
+ /**
6
+ * The most parts of a field path, as in `settings.model`. The types of the
7
+ * field paths go to the same depth.
8
+ *
9
+ * @stability stable
10
+ * @category constants
11
+ * @since 2.0.0
12
+ */
13
+ export declare const fieldPathParts: number;
14
+ /**
15
+ * The most parts of a field pattern, as in `**.secret`.
16
+ *
17
+ * @stability stable
18
+ * @category constants
19
+ * @since 2.0.0
20
+ */
21
+ export declare const patternParts: number;
22
+ /**
23
+ * The most nodes of one condition. A part that a condition uses in many
24
+ * places counts for each place.
25
+ *
26
+ * @stability stable
27
+ * @category constants
28
+ * @since 2.0.0
29
+ */
30
+ export declare const conditionNodes: number;
31
+ /**
32
+ * The most levels of one condition.
33
+ *
34
+ * @stability stable
35
+ * @category constants
36
+ * @since 2.0.0
37
+ */
38
+ export declare const conditionDepth: number;
39
+ /**
40
+ * The most nodes of one residual condition.
41
+ *
42
+ * @stability stable
43
+ * @category constants
44
+ * @since 2.0.0
45
+ */
46
+ export declare const residualNodes: number;
47
+ /**
48
+ * The most levels of a policy tree.
49
+ *
50
+ * @stability stable
51
+ * @category constants
52
+ * @since 2.0.0
53
+ */
54
+ export declare const policyDepth: number;
55
+ /**
56
+ * The most nodes of a policy tree, with its rules and the nodes of their
57
+ * conditions. A part that the tree uses in many places counts for each place.
58
+ *
59
+ * @stability stable
60
+ * @category constants
61
+ * @since 2.0.0
62
+ */
63
+ export declare const policyNodes: number;
64
+ /**
65
+ * The most objects and arrays in one input to the Schema of a policy or of a
66
+ * condition.
67
+ *
68
+ * @stability stable
69
+ * @category constants
70
+ * @since 2.0.0
71
+ */
72
+ export declare const jsonNodes: number;
73
+ /**
74
+ * The most fields in the catalog of one Schema.
75
+ *
76
+ * @stability stable
77
+ * @category constants
78
+ * @since 2.0.0
79
+ */
80
+ export declare const catalogFields: number;
81
+ /**
82
+ * The most steps of one search for a value, as in `Policy.coverage`. Past
83
+ * it, the answer is `Unknown`.
84
+ *
85
+ * @stability stable
86
+ * @category constants
87
+ * @since 2.0.0
88
+ */
89
+ export declare const searchSteps: number;
90
+ /**
91
+ * The most fields that one search for a value may test. Past it, the answer
92
+ * is `Unknown`.
93
+ *
94
+ * @stability stable
95
+ * @category constants
96
+ * @since 2.0.0
97
+ */
98
+ export declare const searchFields: number;
99
+ /**
100
+ * The error when an input goes past a limit.
101
+ *
102
+ * @stability stable
103
+ * @category errors
104
+ * @since 2.0.0
105
+ */
106
+ export declare const LimitExceeded: typeof LimitExceeded$1;
107
+ /**
108
+ * @stability stable
109
+ * @category errors
110
+ * @since 2.0.0
111
+ */
112
+ export type LimitExceeded = LimitExceeded$1;
113
+ export { Limits_d_exports };
114
+ //# sourceMappingURL=Limits.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Limits.d.ts","names":["Limits.LimitExceeded"],"sources":["../src/Limits.ts"],"mappings":";;;;;;;;;;;;qBAoBa;;;;;;;;qBASA;;;;;;;;;qBAUA;;;;;;;;qBASA;;;;;;;;qBASA;;;;;;;;qBASA;;;;;;;;;qBAUA;;;;;;;;;qBAUA;;;;;;;;qBASA;;;;;;;;;qBAUA;;;;;;;;;qBAUA;;;;;;;;qBASA,sBAAsBA;;;;;;YAOvB,gBAAgBA"}
package/dist/Limits.js ADDED
@@ -0,0 +1,132 @@
1
+ import { __exportAll } from "./_virtual/_rolldown/runtime.js";
2
+ import { LimitExceeded as LimitExceeded$1, catalogFields as catalogFields$1, conditionNodes as conditionNodes$1, jsonNodes as jsonNodes$1, policyNodes as policyNodes$1, residualNodes as residualNodes$1, searchSteps as searchSteps$1 } from "./internal/limits.js";
3
+ // src/Limits.ts
4
+ /**
5
+ * The limits of the library. Each one keeps a walk, a decoder or a search
6
+ * small, so no input can fill the stack or take a very long time. An input
7
+ * past a limit is an error, with one form of message, as in "A condition has
8
+ * 100001 nodes. The most is 100000.". In an `Authorizer`, such an error is
9
+ * never a "yes".
10
+ *
11
+ * @stability stable
12
+ * @since 2.0.0
13
+ */
14
+ var Limits_exports = /* @__PURE__ */ __exportAll({
15
+ LimitExceeded: () => LimitExceeded,
16
+ catalogFields: () => catalogFields,
17
+ conditionDepth: () => conditionDepth,
18
+ conditionNodes: () => conditionNodes,
19
+ fieldPathParts: () => fieldPathParts,
20
+ jsonNodes: () => jsonNodes,
21
+ patternParts: () => patternParts,
22
+ policyDepth: () => policyDepth,
23
+ policyNodes: () => policyNodes,
24
+ residualNodes: () => residualNodes,
25
+ searchFields: () => searchFields,
26
+ searchSteps: () => searchSteps
27
+ });
28
+ /**
29
+ * The most parts of a field path, as in `settings.model`. The types of the
30
+ * field paths go to the same depth.
31
+ *
32
+ * @stability stable
33
+ * @category constants
34
+ * @since 2.0.0
35
+ */
36
+ const fieldPathParts = 5;
37
+ /**
38
+ * The most parts of a field pattern, as in `**.secret`.
39
+ *
40
+ * @stability stable
41
+ * @category constants
42
+ * @since 2.0.0
43
+ */
44
+ const patternParts = 16;
45
+ /**
46
+ * The most nodes of one condition. A part that a condition uses in many
47
+ * places counts for each place.
48
+ *
49
+ * @stability stable
50
+ * @category constants
51
+ * @since 2.0.0
52
+ */
53
+ const conditionNodes = conditionNodes$1;
54
+ /**
55
+ * The most levels of one condition.
56
+ *
57
+ * @stability stable
58
+ * @category constants
59
+ * @since 2.0.0
60
+ */
61
+ const conditionDepth = 500;
62
+ /**
63
+ * The most nodes of one residual condition.
64
+ *
65
+ * @stability stable
66
+ * @category constants
67
+ * @since 2.0.0
68
+ */
69
+ const residualNodes = residualNodes$1;
70
+ /**
71
+ * The most levels of a policy tree.
72
+ *
73
+ * @stability stable
74
+ * @category constants
75
+ * @since 2.0.0
76
+ */
77
+ const policyDepth = 256;
78
+ /**
79
+ * The most nodes of a policy tree, with its rules and the nodes of their
80
+ * conditions. A part that the tree uses in many places counts for each place.
81
+ *
82
+ * @stability stable
83
+ * @category constants
84
+ * @since 2.0.0
85
+ */
86
+ const policyNodes = policyNodes$1;
87
+ /**
88
+ * The most objects and arrays in one input to the Schema of a policy or of a
89
+ * condition.
90
+ *
91
+ * @stability stable
92
+ * @category constants
93
+ * @since 2.0.0
94
+ */
95
+ const jsonNodes = jsonNodes$1;
96
+ /**
97
+ * The most fields in the catalog of one Schema.
98
+ *
99
+ * @stability stable
100
+ * @category constants
101
+ * @since 2.0.0
102
+ */
103
+ const catalogFields = catalogFields$1;
104
+ /**
105
+ * The most steps of one search for a value, as in `Policy.coverage`. Past
106
+ * it, the answer is `Unknown`.
107
+ *
108
+ * @stability stable
109
+ * @category constants
110
+ * @since 2.0.0
111
+ */
112
+ const searchSteps = searchSteps$1;
113
+ /**
114
+ * The most fields that one search for a value may test. Past it, the answer
115
+ * is `Unknown`.
116
+ *
117
+ * @stability stable
118
+ * @category constants
119
+ * @since 2.0.0
120
+ */
121
+ const searchFields = 256;
122
+ /**
123
+ * The error when an input goes past a limit.
124
+ *
125
+ * @stability stable
126
+ * @category errors
127
+ * @since 2.0.0
128
+ */
129
+ const LimitExceeded = LimitExceeded$1;
130
+ export { LimitExceeded, Limits_exports, catalogFields, conditionDepth, conditionNodes, fieldPathParts, jsonNodes, patternParts, policyDepth, policyNodes, residualNodes, searchFields, searchSteps };
131
+
132
+ //# sourceMappingURL=Limits.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Limits.js","names":["Limits.conditionNodes","Limits.residualNodes","Limits.policyNodes","Limits.jsonNodes","Limits.catalogFields","Limits.searchSteps","Limits.LimitExceeded"],"sources":["../src/Limits.ts"],"sourcesContent":["/**\n * The limits of the library. Each one keeps a walk, a decoder or a search\n * small, so no input can fill the stack or take a very long time. An input\n * past a limit is an error, with one form of message, as in \"A condition has\n * 100001 nodes. The most is 100000.\". In an `Authorizer`, such an error is\n * never a \"yes\".\n *\n * @stability stable\n * @since 2.0.0\n */\nimport * as Limits from \"./internal/limits.ts\"\n\n/**\n * The most parts of a field path, as in `settings.model`. The types of the\n * field paths go to the same depth.\n *\n * @stability stable\n * @category constants\n * @since 2.0.0\n */\nexport const fieldPathParts: number = Limits.fieldPathParts\n\n/**\n * The most parts of a field pattern, as in `**.secret`.\n *\n * @stability stable\n * @category constants\n * @since 2.0.0\n */\nexport const patternParts: number = Limits.patternParts\n\n/**\n * The most nodes of one condition. A part that a condition uses in many\n * places counts for each place.\n *\n * @stability stable\n * @category constants\n * @since 2.0.0\n */\nexport const conditionNodes: number = Limits.conditionNodes\n\n/**\n * The most levels of one condition.\n *\n * @stability stable\n * @category constants\n * @since 2.0.0\n */\nexport const conditionDepth: number = Limits.conditionDepth\n\n/**\n * The most nodes of one residual condition.\n *\n * @stability stable\n * @category constants\n * @since 2.0.0\n */\nexport const residualNodes: number = Limits.residualNodes\n\n/**\n * The most levels of a policy tree.\n *\n * @stability stable\n * @category constants\n * @since 2.0.0\n */\nexport const policyDepth: number = Limits.policyDepth\n\n/**\n * The most nodes of a policy tree, with its rules and the nodes of their\n * conditions. A part that the tree uses in many places counts for each place.\n *\n * @stability stable\n * @category constants\n * @since 2.0.0\n */\nexport const policyNodes: number = Limits.policyNodes\n\n/**\n * The most objects and arrays in one input to the Schema of a policy or of a\n * condition.\n *\n * @stability stable\n * @category constants\n * @since 2.0.0\n */\nexport const jsonNodes: number = Limits.jsonNodes\n\n/**\n * The most fields in the catalog of one Schema.\n *\n * @stability stable\n * @category constants\n * @since 2.0.0\n */\nexport const catalogFields: number = Limits.catalogFields\n\n/**\n * The most steps of one search for a value, as in `Policy.coverage`. Past\n * it, the answer is `Unknown`.\n *\n * @stability stable\n * @category constants\n * @since 2.0.0\n */\nexport const searchSteps: number = Limits.searchSteps\n\n/**\n * The most fields that one search for a value may test. Past it, the answer\n * is `Unknown`.\n *\n * @stability stable\n * @category constants\n * @since 2.0.0\n */\nexport const searchFields: number = Limits.searchFields\n\n/**\n * The error when an input goes past a limit.\n *\n * @stability stable\n * @category errors\n * @since 2.0.0\n */\nexport const LimitExceeded: typeof Limits.LimitExceeded = Limits.LimitExceeded\n\n/**\n * @stability stable\n * @category errors\n * @since 2.0.0\n */\nexport type LimitExceeded = Limits.LimitExceeded\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAoBA,MAAa,iBAAyB;;;;;;;;AAStC,MAAa,eAAuB;;;;;;;;;AAUpC,MAAa,iBAAyBA;;;;;;;;AAStC,MAAa,iBAAyB;;;;;;;;AAStC,MAAa,gBAAwBC;;;;;;;;AASrC,MAAa,cAAsB;;;;;;;;;AAUnC,MAAa,cAAsBC;;;;;;;;;AAUnC,MAAa,YAAoBC;;;;;;;;AASjC,MAAa,gBAAwBC;;;;;;;;;AAUrC,MAAa,cAAsBC;;;;;;;;;AAUnC,MAAa,eAAuB;;;;;;;;AASpC,MAAa,gBAA6CC"}