@nmnmcc/ability 2.0.7 → 2.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (87) hide show
  1. package/README.md +52 -29
  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 +121 -19
  6. package/dist/Condition.d.ts.map +1 -1
  7. package/dist/Condition.js +32 -21
  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 +102 -18
  18. package/dist/Policy.d.ts.map +1 -1
  19. package/dist/Policy.js +7 -6
  20. package/dist/Policy.js.map +1 -1
  21. package/dist/index.d.ts +1 -1
  22. package/dist/internal/algebra.js +2 -2
  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 +11 -7
  27. package/dist/internal/authorizer.js.map +1 -1
  28. package/dist/internal/bind.js +86 -64
  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 +170 -77
  33. package/dist/internal/catalog.js.map +1 -1
  34. package/dist/internal/decide.js +27 -31
  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.map +1 -1
  45. package/dist/internal/nodes.js +53 -44
  46. package/dist/internal/nodes.js.map +1 -1
  47. package/dist/internal/patterns.js +50 -15
  48. package/dist/internal/patterns.js.map +1 -1
  49. package/dist/internal/policy-json.js +169 -127
  50. package/dist/internal/policy-json.js.map +1 -1
  51. package/dist/internal/policy-nodes.d.ts.map +1 -1
  52. package/dist/internal/policy-nodes.js +16 -13
  53. package/dist/internal/policy-nodes.js.map +1 -1
  54. package/dist/internal/policy.d.ts.map +1 -1
  55. package/dist/internal/policy.js +38 -22
  56. package/dist/internal/policy.js.map +1 -1
  57. package/dist/internal/resource.js +1 -1
  58. package/dist/internal/rule.d.ts.map +1 -1
  59. package/dist/internal/rule.js +6 -5
  60. package/dist/internal/rule.js.map +1 -1
  61. package/dist/internal/search.d.ts.map +1 -1
  62. package/dist/internal/search.js +106 -92
  63. package/dist/internal/search.js.map +1 -1
  64. package/dist/internal/trampoline.js +19 -5
  65. package/dist/internal/trampoline.js.map +1 -1
  66. package/package.json +1 -1
  67. package/src/Authorizer.ts +30 -20
  68. package/src/Condition.ts +317 -86
  69. package/src/Domain.ts +9 -0
  70. package/src/Limits.ts +69 -29
  71. package/src/Policy.ts +143 -21
  72. package/src/internal/analysis.ts +154 -124
  73. package/src/internal/authorizer.ts +29 -14
  74. package/src/internal/bind.ts +193 -136
  75. package/src/internal/catalog.ts +320 -118
  76. package/src/internal/decide.ts +61 -50
  77. package/src/internal/domain.ts +39 -3
  78. package/src/internal/json.ts +212 -117
  79. package/src/internal/limits.ts +101 -31
  80. package/src/internal/nodes.ts +65 -47
  81. package/src/internal/patterns.ts +79 -25
  82. package/src/internal/policy-json.ts +266 -197
  83. package/src/internal/policy-nodes.ts +31 -23
  84. package/src/internal/policy.ts +63 -23
  85. package/src/internal/rule.ts +16 -3
  86. package/src/internal/search.ts +186 -136
  87. package/src/internal/trampoline.ts +51 -4
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,71 @@ 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 { readonly field?: infer F }
271
+ ? [Exclude<F, undefined>] extends [never]
272
+ ? unknown
273
+ : {
274
+ // A field that the target may leave out is checked too, when it is
275
+ // there.
276
+ readonly field?: Condition.SelectPath<
277
+ Domain.DataOf<D, KindOfTarget<T>>,
278
+ Exclude<F, undefined> & string
279
+ >
280
+ }
281
+ : unknown
282
+
218
283
  /**
219
284
  * A request: a principal, an action, and a resource value.
220
285
  *
@@ -224,6 +289,17 @@ export type KindTarget<D> = {
224
289
  */
225
290
  export type Request<D> = Target<D> & { readonly principal: Domain.Principal<D> }
226
291
 
292
+ /**
293
+ * A {@link Request} with a field of any length. See {@link DeepTarget}.
294
+ *
295
+ * @stability stable
296
+ * @category models
297
+ * @since 2.1.0
298
+ */
299
+ export type DeepRequest<D> = DeepTarget<D> & {
300
+ readonly principal: Domain.Principal<D>
301
+ }
302
+
227
303
  /**
228
304
  * A {@link Request} with no field.
229
305
  *
@@ -246,6 +322,17 @@ export type KindRequest<D> = KindTarget<D> & {
246
322
  readonly principal: Domain.Principal<D>
247
323
  }
248
324
 
325
+ /**
326
+ * A {@link KindRequest} with a field of any length. See {@link DeepTarget}.
327
+ *
328
+ * @stability stable
329
+ * @category models
330
+ * @since 2.1.0
331
+ */
332
+ export type DeepKindRequest<D> = DeepKindTarget<D> & {
333
+ readonly principal: Domain.Principal<D>
334
+ }
335
+
249
336
  /**
250
337
  * A request with no types.
251
338
  *
@@ -449,13 +536,34 @@ export type ConditionFor<D, K> = Condition.Condition<
449
536
  >
450
537
 
451
538
  /**
452
- * The options of a rule.
539
+ * Checks each field of a rule: a field pattern with a star, or a name of the
540
+ * kinds `K`, of any length up to about 1000 parts. A name that is not a field
541
+ * gives the names that may come next, so the type error names them. See
542
+ * `Condition.NamePath`.
543
+ *
544
+ * @stability stable
545
+ * @category utility types
546
+ * @since 2.1.0
547
+ */
548
+ export type FieldsFor<D, K, Fields extends ReadonlyArray<string>> = {
549
+ readonly [I in keyof Fields]: Fields[I] extends `${string}*${string}`
550
+ ? Fields[I]
551
+ : Condition.NamePath<DataFor<D, K>, Fields[I] & string>
552
+ }
553
+
554
+ /**
555
+ * The options of a rule. `Fields` is the list of the fields of the rule.
453
556
  *
454
557
  * @stability stable
455
558
  * @category models
456
559
  * @since 0.3.0
457
560
  */
458
- export interface RuleOptions<D, K, C extends Condition.Any> {
561
+ export interface RuleOptions<
562
+ D,
563
+ K,
564
+ C extends Condition.Any,
565
+ Fields extends ReadonlyArray<string> = ReadonlyArray<FieldFor<D, K>>,
566
+ > {
459
567
  /** The condition. It gets a builder with the types of the resource. */
460
568
  readonly when?: (
461
569
  builder: Condition.Builder<
@@ -464,7 +572,7 @@ export interface RuleOptions<D, K, C extends Condition.Any> {
464
572
  Domain.Principal<D>
465
573
  >,
466
574
  ) => C
467
- readonly fields?: ReadonlyArray<FieldFor<D, K>>
575
+ readonly fields?: Fields & FieldsFor<D, K, Fields>
468
576
  readonly reason?: string
469
577
  readonly id?: string
470
578
  readonly meta?: Domain.Meta<D>
@@ -485,20 +593,22 @@ export interface Builder<D> {
485
593
  const A extends ActionForTargets<D, Res>,
486
594
  const C extends ConditionFor<D, KindOf<TargetOf<Res>>> =
487
595
  Condition.Condition,
596
+ const Fields extends ReadonlyArray<string> = readonly [],
488
597
  >(
489
598
  action: SingleOrArray<A>,
490
599
  resource: Res,
491
- options?: RuleOptions<D, KindOf<TargetOf<Res>>, C>,
600
+ options?: RuleOptions<D, KindOf<TargetOf<Res>>, C, Fields>,
492
601
  ) => Rule<D, Condition.ReadsOf<C>, Condition.OpaqueOf<C>>
493
602
  readonly deny: <
494
603
  const Res extends SingleOrArray<RuleTarget<D>>,
495
604
  const A extends ActionForTargets<D, Res>,
496
605
  const C extends ConditionFor<D, KindOf<TargetOf<Res>>> =
497
606
  Condition.Condition,
607
+ const Fields extends ReadonlyArray<string> = readonly [],
498
608
  >(
499
609
  action: SingleOrArray<A>,
500
610
  resource: Res,
501
- options?: RuleOptions<D, KindOf<TargetOf<Res>>, C>,
611
+ options?: RuleOptions<D, KindOf<TargetOf<Res>>, C, Fields>,
502
612
  ) => Rule<D, Condition.ReadsOf<C>, Condition.OpaqueOf<C>>
503
613
  }
504
614
 
@@ -688,9 +798,13 @@ export const provideContext: {
688
798
  * @category evaluation
689
799
  * @since 0.3.0
690
800
  */
691
- export const decide: <D extends Domain.Any, X>(
801
+ export const decide: <
802
+ D extends Domain.Any,
803
+ X,
804
+ const Req extends DeepRequest<NoInfer<D>>,
805
+ >(
692
806
  policy: Policy<D, X>,
693
- request: Request<NoInfer<D>>,
807
+ request: Req & CheckField<NoInfer<D>, Req>,
694
808
  ) => Effect.Effect<
695
809
  Decision.Decision,
696
810
  Condition.ErrorOf<X>,
@@ -704,9 +818,12 @@ export const decide: <D extends Domain.Any, X>(
704
818
  * @category evaluation
705
819
  * @since 0.3.0
706
820
  */
707
- export const decideSync: <D extends Domain.Any>(
821
+ export const decideSync: <
822
+ D extends Domain.Any,
823
+ const Req extends DeepRequest<NoInfer<D>>,
824
+ >(
708
825
  policy: Policy<D>,
709
- request: Request<NoInfer<D>>,
826
+ request: Req & CheckField<NoInfer<D>, Req>,
710
827
  ) => Decision.Decision = Decide.decideSync
711
828
 
712
829
  /**
@@ -719,9 +836,13 @@ export const decideSync: <D extends Domain.Any>(
719
836
  * @category evaluation
720
837
  * @since 0.3.0
721
838
  */
722
- export const explain: <D extends Domain.Any, X>(
839
+ export const explain: <
840
+ D extends Domain.Any,
841
+ X,
842
+ const Req extends DeepRequest<NoInfer<D>>,
843
+ >(
723
844
  policy: Policy<D, X>,
724
- request: Request<NoInfer<D>>,
845
+ request: Req & CheckField<NoInfer<D>, Req>,
725
846
  ) => Effect.Effect<Explanation, Condition.ErrorOf<X>, Condition.ServicesOf<X>> =
726
847
  Decide.explain as never
727
848
 
@@ -789,8 +910,8 @@ export type ResidualReads<D, K> = Condition.Reads<
789
910
  *
790
911
  * A policy tree may use the condition of a child two times, so a small policy
791
912
  * may give a very large condition. When the condition would have more than
792
- * 10000 nodes, this function throws an error. In an `Authorizer`, the error
793
- * is a defect, so the check fails.
913
+ * the `residualNodes` of the domain, 100000 by default, this function throws
914
+ * an error. In an `Authorizer`, the error is a defect, so the check fails.
794
915
  *
795
916
  * @stability stable
796
917
  * @category evaluation
@@ -799,10 +920,10 @@ export type ResidualReads<D, K> = Condition.Reads<
799
920
  export const residual: <
800
921
  D extends Domain.Any,
801
922
  X,
802
- const Req extends KindRequest<NoInfer<D>>,
923
+ const Req extends DeepKindRequest<NoInfer<D>>,
803
924
  >(
804
925
  policy: Policy<D, X>,
805
- request: Req,
926
+ request: Req & CheckField<NoInfer<D>, Req>,
806
927
  ) => Condition.Condition<ResidualReads<D, Req["kind"]>, X> =
807
928
  Analysis.residual as never
808
929
 
@@ -861,10 +982,10 @@ export type Coverage<I = unknown, X = never> = Data.TaggedEnum<{
861
982
  export const coverage: <
862
983
  D extends Domain.Any,
863
984
  X,
864
- const Req extends KindRequest<NoInfer<D>>,
985
+ const Req extends DeepKindRequest<NoInfer<D>>,
865
986
  >(
866
987
  policy: Policy<D, X>,
867
- request: Req,
988
+ request: Req & CheckField<NoInfer<D>, Req>,
868
989
  ) => Coverage<ResidualReads<D, Req["kind"]>, X> = Analysis.coverageAny as never
869
990
 
870
991
  // Schema
@@ -915,10 +1036,11 @@ export type EncodedNode =
915
1036
  * every kind, action, field and principal field against the domain, and the
916
1037
  * `meta` of each rule against the `meta` Schema of the domain.
917
1038
  *
918
- * The decoder takes at most 256 levels of policies and 500 levels of
919
- * conditions, and at most 1000000 objects and array items, with a shared
920
- * object counted for each place. It reads only the own data fields of the
921
- * input.
1039
+ * The decoder takes the limits of the domain: at most `policyNodes` nodes
1040
+ * of the tree, `conditionNodes` nodes of each condition, and `jsonNodes`
1041
+ * objects and array items, with a shared object counted for each place. It
1042
+ * stops at the first one past a limit. It takes any number of levels, and it
1043
+ * reads only the own data fields of the input.
922
1044
  *
923
1045
  * @stability stable
924
1046
  * @category schemas