@nmnmcc/ability 2.0.6 → 2.1.1

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 (89) hide show
  1. package/README.md +56 -32
  2. package/dist/Authorizer.d.ts +21 -12
  3. package/dist/Authorizer.d.ts.map +1 -1
  4. package/dist/Authorizer.js.map +1 -1
  5. package/dist/Condition.d.ts +118 -18
  6. package/dist/Condition.d.ts.map +1 -1
  7. package/dist/Condition.js +27 -18
  8. package/dist/Condition.js.map +1 -1
  9. package/dist/Domain.d.ts +9 -0
  10. package/dist/Domain.d.ts.map +1 -1
  11. package/dist/Domain.js +5 -0
  12. package/dist/Domain.js.map +1 -1
  13. package/dist/Limits.d.ts +44 -13
  14. package/dist/Limits.d.ts.map +1 -1
  15. package/dist/Limits.js +52 -22
  16. package/dist/Limits.js.map +1 -1
  17. package/dist/Policy.d.ts +95 -12
  18. package/dist/Policy.d.ts.map +1 -1
  19. package/dist/Policy.js.map +1 -1
  20. package/dist/index.d.ts +1 -1
  21. package/dist/internal/algebra.js +101 -74
  22. package/dist/internal/algebra.js.map +1 -1
  23. package/dist/internal/analysis.js +81 -75
  24. package/dist/internal/analysis.js.map +1 -1
  25. package/dist/internal/authorizer.d.ts.map +1 -1
  26. package/dist/internal/authorizer.js +16 -11
  27. package/dist/internal/authorizer.js.map +1 -1
  28. package/dist/internal/bind.js +119 -80
  29. package/dist/internal/bind.js.map +1 -1
  30. package/dist/internal/catalog.d.ts +33 -10
  31. package/dist/internal/catalog.d.ts.map +1 -1
  32. package/dist/internal/catalog.js +153 -78
  33. package/dist/internal/catalog.js.map +1 -1
  34. package/dist/internal/decide.js +25 -29
  35. package/dist/internal/decide.js.map +1 -1
  36. package/dist/internal/domain.js +19 -5
  37. package/dist/internal/domain.js.map +1 -1
  38. package/dist/internal/json.js +120 -76
  39. package/dist/internal/json.js.map +1 -1
  40. package/dist/internal/limits.d.ts +28 -0
  41. package/dist/internal/limits.d.ts.map +1 -1
  42. package/dist/internal/limits.js +75 -16
  43. package/dist/internal/limits.js.map +1 -1
  44. package/dist/internal/nodes.d.ts +7 -0
  45. package/dist/internal/nodes.d.ts.map +1 -1
  46. package/dist/internal/nodes.js +86 -47
  47. package/dist/internal/nodes.js.map +1 -1
  48. package/dist/internal/patterns.js +50 -15
  49. package/dist/internal/patterns.js.map +1 -1
  50. package/dist/internal/policy-json.js +169 -127
  51. package/dist/internal/policy-json.js.map +1 -1
  52. package/dist/internal/policy-nodes.d.ts.map +1 -1
  53. package/dist/internal/policy-nodes.js +17 -14
  54. package/dist/internal/policy-nodes.js.map +1 -1
  55. package/dist/internal/policy.d.ts.map +1 -1
  56. package/dist/internal/policy.js +38 -22
  57. package/dist/internal/policy.js.map +1 -1
  58. package/dist/internal/resource.js +1 -1
  59. package/dist/internal/rule.d.ts.map +1 -1
  60. package/dist/internal/rule.js +6 -5
  61. package/dist/internal/rule.js.map +1 -1
  62. package/dist/internal/search.d.ts.map +1 -1
  63. package/dist/internal/search.js +106 -92
  64. package/dist/internal/search.js.map +1 -1
  65. package/dist/internal/trampoline.js +89 -0
  66. package/dist/internal/trampoline.js.map +1 -0
  67. package/package.json +1 -1
  68. package/src/Authorizer.ts +30 -20
  69. package/src/Condition.ts +308 -83
  70. package/src/Domain.ts +9 -0
  71. package/src/Limits.ts +69 -29
  72. package/src/Policy.ts +131 -15
  73. package/src/internal/algebra.ts +218 -135
  74. package/src/internal/analysis.ts +154 -122
  75. package/src/internal/authorizer.ts +34 -16
  76. package/src/internal/bind.ts +279 -148
  77. package/src/internal/catalog.ts +307 -133
  78. package/src/internal/decide.ts +58 -49
  79. package/src/internal/domain.ts +39 -3
  80. package/src/internal/json.ts +212 -117
  81. package/src/internal/limits.ts +101 -31
  82. package/src/internal/nodes.ts +101 -59
  83. package/src/internal/patterns.ts +72 -26
  84. package/src/internal/policy-json.ts +266 -197
  85. package/src/internal/policy-nodes.ts +33 -24
  86. package/src/internal/policy.ts +63 -23
  87. package/src/internal/rule.ts +16 -3
  88. package/src/internal/search.ts +186 -136
  89. package/src/internal/trampoline.ts +194 -0
package/src/Limits.ts CHANGED
@@ -1,24 +1,56 @@
1
1
  /**
2
- * The limits of the library. Each one keeps a walk, a decoder or a search
3
- * small, so no input can fill the stack or take a very long time. An input
2
+ * The limits of the library. Each one keeps a decoder, a walk or a search
3
+ * small, so no input can take a very long time or very much memory. An input
4
4
  * past a limit is an error, with one form of message, as in "A condition has
5
- * 100001 nodes. The most is 100000.". In an `Authorizer`, such an error is
6
- * never a "yes".
5
+ * 1000001 nodes and values. The most is 1000000.". In an `Authorizer`, such
6
+ * an error is never a "yes".
7
+ *
8
+ * The constants here are the defaults. A domain may set its own limits, up
9
+ * to the {@link ceilings}: see `Domain.make`. A walk does not fill the stack
10
+ * at any depth, and the levels of an input are at most its nodes, so there is
11
+ * no limit of levels.
7
12
  *
8
13
  * @stability stable
9
14
  * @since 2.0.0
10
15
  */
11
- import * as Limits from "./internal/limits.ts"
16
+ import * as Internal from "./internal/limits.ts"
17
+
18
+ /**
19
+ * The values of all the limits.
20
+ *
21
+ * @stability stable
22
+ * @category models
23
+ * @since 2.1.0
24
+ */
25
+ export type Limits = Internal.Limits
26
+
27
+ /**
28
+ * The limits of a domain that sets none.
29
+ *
30
+ * @stability stable
31
+ * @category constants
32
+ * @since 2.1.0
33
+ */
34
+ export const defaults: Limits = Internal.defaults
35
+
36
+ /**
37
+ * The most that a domain may set for each limit.
38
+ *
39
+ * @stability stable
40
+ * @category constants
41
+ * @since 2.1.0
42
+ */
43
+ export const ceilings: Limits = Internal.ceilings
12
44
 
13
45
  /**
14
- * The most parts of a field path, as in `settings.model`. The types of the
15
- * field paths go to the same depth.
46
+ * The most parts of a field path, as in `settings.model`, by default. The
47
+ * types check a path that you write up to about 1000 parts.
16
48
  *
17
49
  * @stability stable
18
50
  * @category constants
19
51
  * @since 2.0.0
20
52
  */
21
- export const fieldPathParts: number = Limits.fieldPathParts
53
+ export const fieldPathParts: number = Internal.fieldPathParts
22
54
 
23
55
  /**
24
56
  * The most parts of a field pattern, as in `**.secret`.
@@ -27,26 +59,29 @@ export const fieldPathParts: number = Limits.fieldPathParts
27
59
  * @category constants
28
60
  * @since 2.0.0
29
61
  */
30
- export const patternParts: number = Limits.patternParts
62
+ export const patternParts: number = Internal.patternParts
31
63
 
32
64
  /**
33
- * The most nodes of one condition. A part that a condition uses in many
34
- * places counts for each place.
65
+ * The most nodes of one condition, with each value of an `In` or `Under`
66
+ * test. A part that a condition uses in many places counts for each place.
35
67
  *
36
68
  * @stability stable
37
69
  * @category constants
38
70
  * @since 2.0.0
39
71
  */
40
- export const conditionNodes: number = Limits.conditionNodes
72
+ export const conditionNodes: number = Internal.conditionNodes
41
73
 
42
74
  /**
43
- * The most levels of one condition.
75
+ * There is no limit of the levels of a condition: a walk does not fill the
76
+ * stack, and the levels are at most the nodes, which `conditionNodes`
77
+ * limits.
44
78
  *
45
- * @stability stable
79
+ * @deprecated Since 2.1.0, there is no such limit. The value is `Infinity`.
80
+ * @stability deprecated
46
81
  * @category constants
47
82
  * @since 2.0.0
48
83
  */
49
- export const conditionDepth: number = Limits.conditionDepth
84
+ export const conditionDepth: number = Number.POSITIVE_INFINITY
50
85
 
51
86
  /**
52
87
  * The most nodes of one residual condition.
@@ -55,26 +90,29 @@ export const conditionDepth: number = Limits.conditionDepth
55
90
  * @category constants
56
91
  * @since 2.0.0
57
92
  */
58
- export const residualNodes: number = Limits.residualNodes
93
+ export const residualNodes: number = Internal.residualNodes
59
94
 
60
95
  /**
61
- * The most levels of a policy tree.
96
+ * There is no limit of the levels of a policy tree: a walk does not fill the
97
+ * stack, and the levels are at most the nodes, which `policyNodes` limits.
62
98
  *
63
- * @stability stable
99
+ * @deprecated Since 2.1.0, there is no such limit. The value is `Infinity`.
100
+ * @stability deprecated
64
101
  * @category constants
65
102
  * @since 2.0.0
66
103
  */
67
- export const policyDepth: number = Limits.policyDepth
104
+ export const policyDepth: number = Number.POSITIVE_INFINITY
68
105
 
69
106
  /**
70
- * The most nodes of a policy tree, with its rules and the nodes of their
71
- * conditions. A part that the tree uses in many places counts for each place.
107
+ * The most nodes of a policy tree, with its rules and the nodes and values
108
+ * of their conditions. A part that the tree uses in many places counts for
109
+ * each place.
72
110
  *
73
111
  * @stability stable
74
112
  * @category constants
75
113
  * @since 2.0.0
76
114
  */
77
- export const policyNodes: number = Limits.policyNodes
115
+ export const policyNodes: number = Internal.policyNodes
78
116
 
79
117
  /**
80
118
  * The most objects and arrays in one input to the Schema of a policy or of a
@@ -84,16 +122,17 @@ export const policyNodes: number = Limits.policyNodes
84
122
  * @category constants
85
123
  * @since 2.0.0
86
124
  */
87
- export const jsonNodes: number = Limits.jsonNodes
125
+ export const jsonNodes: number = Internal.jsonNodes
88
126
 
89
127
  /**
90
- * The most fields in the catalog of one Schema.
128
+ * The most fields in the list of the fields of one Schema, as
129
+ * `Policy.permittedFields` reads it.
91
130
  *
92
131
  * @stability stable
93
132
  * @category constants
94
133
  * @since 2.0.0
95
134
  */
96
- export const catalogFields: number = Limits.catalogFields
135
+ export const catalogFields: number = Internal.catalogFields
97
136
 
98
137
  /**
99
138
  * The most steps of one search for a value, as in `Policy.coverage`. Past
@@ -105,7 +144,7 @@ export const catalogFields: number = Limits.catalogFields
105
144
  * @category constants
106
145
  * @since 2.0.0
107
146
  */
108
- export const searchSteps: number = Limits.searchSteps
147
+ export const searchSteps: number = Internal.searchSteps
109
148
 
110
149
  /**
111
150
  * The most fields that one search for a value may test. Past it, the answer
@@ -115,7 +154,7 @@ export const searchSteps: number = Limits.searchSteps
115
154
  * @category constants
116
155
  * @since 2.0.0
117
156
  */
118
- export const searchFields: number = Limits.searchFields
157
+ export const searchFields: number = Internal.searchFields
119
158
 
120
159
  /**
121
160
  * The error when an input goes past a limit.
@@ -124,11 +163,12 @@ export const searchFields: number = Limits.searchFields
124
163
  * @category errors
125
164
  * @since 2.0.0
126
165
  */
127
- export const LimitExceeded: typeof Limits.LimitExceeded = Limits.LimitExceeded
166
+ export const LimitExceeded: typeof Internal.LimitExceeded =
167
+ Internal.LimitExceeded
128
168
 
129
169
  /**
130
170
  * @stability stable
131
171
  * @category errors
132
172
  * @since 2.0.0
133
173
  */
134
- export type LimitExceeded = Limits.LimitExceeded
174
+ export type LimitExceeded = Internal.LimitExceeded
package/src/Policy.ts CHANGED
@@ -215,6 +215,66 @@ export type KindTarget<D> = {
215
215
  }
216
216
  }[Domain.Kind<D>]
217
217
 
218
+ /**
219
+ * A {@link Target} with a field of any length. A function that takes a
220
+ * target checks its field with {@link CheckField}, so a field of up to about
221
+ * 1000 parts is fine, and a field that is not a field of the kind is a type
222
+ * error.
223
+ *
224
+ * @stability stable
225
+ * @category models
226
+ * @since 2.1.0
227
+ */
228
+ export type DeepTarget<D> = {
229
+ [K in Domain.Kind<D>]: {
230
+ readonly action: Domain.ActionOf<D, K>
231
+ readonly resource: Domain.ValueOf<D, K>
232
+ readonly field?: string
233
+ readonly kind?: never
234
+ }
235
+ }[Domain.Kind<D>]
236
+
237
+ /**
238
+ * A {@link KindTarget} with a field of any length. See {@link DeepTarget}.
239
+ *
240
+ * @stability stable
241
+ * @category models
242
+ * @since 2.1.0
243
+ */
244
+ export type DeepKindTarget<D> = {
245
+ [K in Domain.Kind<D>]: {
246
+ readonly action: Domain.ActionOf<D, K>
247
+ readonly kind: K
248
+ readonly field?: string
249
+ readonly within?: Scope<D, K>
250
+ readonly resource?: never
251
+ }
252
+ }[Domain.Kind<D>]
253
+
254
+ /** The kind of a target: its `kind`, or the `_tag` of its resource value. */
255
+ type KindOfTarget<T> = T extends { readonly kind: infer K extends string }
256
+ ? K
257
+ : T extends { readonly resource: { readonly _tag: infer K extends string } }
258
+ ? K
259
+ : never
260
+
261
+ /**
262
+ * Checks the field of a target or a request: a field of its kind that a
263
+ * request may name, of any length up to about 1000 parts. See
264
+ * `Condition.SelectPath`. A target with no field needs no check.
265
+ *
266
+ * @stability stable
267
+ * @category utility types
268
+ * @since 2.1.0
269
+ */
270
+ export type CheckField<D, T> = T extends {
271
+ readonly field: infer F extends string
272
+ }
273
+ ? {
274
+ readonly field: Condition.SelectPath<Domain.DataOf<D, KindOfTarget<T>>, F>
275
+ }
276
+ : unknown
277
+
218
278
  /**
219
279
  * A request: a principal, an action, and a resource value.
220
280
  *
@@ -224,6 +284,17 @@ export type KindTarget<D> = {
224
284
  */
225
285
  export type Request<D> = Target<D> & { readonly principal: Domain.Principal<D> }
226
286
 
287
+ /**
288
+ * A {@link Request} with a field of any length. See {@link DeepTarget}.
289
+ *
290
+ * @stability stable
291
+ * @category models
292
+ * @since 2.1.0
293
+ */
294
+ export type DeepRequest<D> = DeepTarget<D> & {
295
+ readonly principal: Domain.Principal<D>
296
+ }
297
+
227
298
  /**
228
299
  * A {@link Request} with no field.
229
300
  *
@@ -246,6 +317,17 @@ export type KindRequest<D> = KindTarget<D> & {
246
317
  readonly principal: Domain.Principal<D>
247
318
  }
248
319
 
320
+ /**
321
+ * A {@link KindRequest} with a field of any length. See {@link DeepTarget}.
322
+ *
323
+ * @stability stable
324
+ * @category models
325
+ * @since 2.1.0
326
+ */
327
+ export type DeepKindRequest<D> = DeepKindTarget<D> & {
328
+ readonly principal: Domain.Principal<D>
329
+ }
330
+
249
331
  /**
250
332
  * A request with no types.
251
333
  *
@@ -449,13 +531,34 @@ export type ConditionFor<D, K> = Condition.Condition<
449
531
  >
450
532
 
451
533
  /**
452
- * The options of a rule.
534
+ * Checks each field of a rule: a field pattern with a star, or a name of the
535
+ * kinds `K`, of any length up to about 1000 parts. A name that is not a field
536
+ * gives the names that may come next, so the type error names them. See
537
+ * `Condition.NamePath`.
538
+ *
539
+ * @stability stable
540
+ * @category utility types
541
+ * @since 2.1.0
542
+ */
543
+ export type FieldsFor<D, K, Fields extends ReadonlyArray<string>> = {
544
+ readonly [I in keyof Fields]: Fields[I] extends `${string}*${string}`
545
+ ? Fields[I]
546
+ : Condition.NamePath<DataFor<D, K>, Fields[I] & string>
547
+ }
548
+
549
+ /**
550
+ * The options of a rule. `Fields` is the list of the fields of the rule.
453
551
  *
454
552
  * @stability stable
455
553
  * @category models
456
554
  * @since 0.3.0
457
555
  */
458
- export interface RuleOptions<D, K, C extends Condition.Any> {
556
+ export interface RuleOptions<
557
+ D,
558
+ K,
559
+ C extends Condition.Any,
560
+ Fields extends ReadonlyArray<string> = ReadonlyArray<FieldFor<D, K>>,
561
+ > {
459
562
  /** The condition. It gets a builder with the types of the resource. */
460
563
  readonly when?: (
461
564
  builder: Condition.Builder<
@@ -464,7 +567,7 @@ export interface RuleOptions<D, K, C extends Condition.Any> {
464
567
  Domain.Principal<D>
465
568
  >,
466
569
  ) => C
467
- readonly fields?: ReadonlyArray<FieldFor<D, K>>
570
+ readonly fields?: Fields & FieldsFor<D, K, Fields>
468
571
  readonly reason?: string
469
572
  readonly id?: string
470
573
  readonly meta?: Domain.Meta<D>
@@ -485,20 +588,22 @@ export interface Builder<D> {
485
588
  const A extends ActionForTargets<D, Res>,
486
589
  const C extends ConditionFor<D, KindOf<TargetOf<Res>>> =
487
590
  Condition.Condition,
591
+ const Fields extends ReadonlyArray<string> = readonly [],
488
592
  >(
489
593
  action: SingleOrArray<A>,
490
594
  resource: Res,
491
- options?: RuleOptions<D, KindOf<TargetOf<Res>>, C>,
595
+ options?: RuleOptions<D, KindOf<TargetOf<Res>>, C, Fields>,
492
596
  ) => Rule<D, Condition.ReadsOf<C>, Condition.OpaqueOf<C>>
493
597
  readonly deny: <
494
598
  const Res extends SingleOrArray<RuleTarget<D>>,
495
599
  const A extends ActionForTargets<D, Res>,
496
600
  const C extends ConditionFor<D, KindOf<TargetOf<Res>>> =
497
601
  Condition.Condition,
602
+ const Fields extends ReadonlyArray<string> = readonly [],
498
603
  >(
499
604
  action: SingleOrArray<A>,
500
605
  resource: Res,
501
- options?: RuleOptions<D, KindOf<TargetOf<Res>>, C>,
606
+ options?: RuleOptions<D, KindOf<TargetOf<Res>>, C, Fields>,
502
607
  ) => Rule<D, Condition.ReadsOf<C>, Condition.OpaqueOf<C>>
503
608
  }
504
609
 
@@ -688,9 +793,13 @@ export const provideContext: {
688
793
  * @category evaluation
689
794
  * @since 0.3.0
690
795
  */
691
- export const decide: <D extends Domain.Any, X>(
796
+ export const decide: <
797
+ D extends Domain.Any,
798
+ X,
799
+ const Req extends DeepRequest<NoInfer<D>>,
800
+ >(
692
801
  policy: Policy<D, X>,
693
- request: Request<NoInfer<D>>,
802
+ request: Req & CheckField<NoInfer<D>, Req>,
694
803
  ) => Effect.Effect<
695
804
  Decision.Decision,
696
805
  Condition.ErrorOf<X>,
@@ -704,9 +813,12 @@ export const decide: <D extends Domain.Any, X>(
704
813
  * @category evaluation
705
814
  * @since 0.3.0
706
815
  */
707
- export const decideSync: <D extends Domain.Any>(
816
+ export const decideSync: <
817
+ D extends Domain.Any,
818
+ const Req extends DeepRequest<NoInfer<D>>,
819
+ >(
708
820
  policy: Policy<D>,
709
- request: Request<NoInfer<D>>,
821
+ request: Req & CheckField<NoInfer<D>, Req>,
710
822
  ) => Decision.Decision = Decide.decideSync
711
823
 
712
824
  /**
@@ -719,9 +831,13 @@ export const decideSync: <D extends Domain.Any>(
719
831
  * @category evaluation
720
832
  * @since 0.3.0
721
833
  */
722
- export const explain: <D extends Domain.Any, X>(
834
+ export const explain: <
835
+ D extends Domain.Any,
836
+ X,
837
+ const Req extends DeepRequest<NoInfer<D>>,
838
+ >(
723
839
  policy: Policy<D, X>,
724
- request: Request<NoInfer<D>>,
840
+ request: Req & CheckField<NoInfer<D>, Req>,
725
841
  ) => Effect.Effect<Explanation, Condition.ErrorOf<X>, Condition.ServicesOf<X>> =
726
842
  Decide.explain as never
727
843
 
@@ -799,10 +915,10 @@ export type ResidualReads<D, K> = Condition.Reads<
799
915
  export const residual: <
800
916
  D extends Domain.Any,
801
917
  X,
802
- const Req extends KindRequest<NoInfer<D>>,
918
+ const Req extends DeepKindRequest<NoInfer<D>>,
803
919
  >(
804
920
  policy: Policy<D, X>,
805
- request: Req,
921
+ request: Req & CheckField<NoInfer<D>, Req>,
806
922
  ) => Condition.Condition<ResidualReads<D, Req["kind"]>, X> =
807
923
  Analysis.residual as never
808
924
 
@@ -861,10 +977,10 @@ export type Coverage<I = unknown, X = never> = Data.TaggedEnum<{
861
977
  export const coverage: <
862
978
  D extends Domain.Any,
863
979
  X,
864
- const Req extends KindRequest<NoInfer<D>>,
980
+ const Req extends DeepKindRequest<NoInfer<D>>,
865
981
  >(
866
982
  policy: Policy<D, X>,
867
- request: Req,
983
+ request: Req & CheckField<NoInfer<D>, Req>,
868
984
  ) => Coverage<ResidualReads<D, Req["kind"]>, X> = Analysis.coverageAny as never
869
985
 
870
986
  // Schema