@nxgt/graphql-validation 0.7.0 → 0.8.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 (49) hide show
  1. package/README.md +8 -3
  2. package/dist/chunks/{index-w9pw7fsk.js → index-5x1yd76p.js} +2 -2
  3. package/dist/chunks/{index-atwkcfg3.js → index-sq3exxg2.js} +25 -4
  4. package/dist/chunks/index-sq3exxg2.js.map +27 -0
  5. package/dist/cli.js +1 -1
  6. package/dist/codegen.js +2 -2
  7. package/dist/constraint-directive.d.ts.map +1 -1
  8. package/dist/index.js +102 -71
  9. package/dist/index.js.map +12 -9
  10. package/dist/limits/cost-context.d.ts +7 -7
  11. package/dist/limits/cost-context.d.ts.map +1 -1
  12. package/dist/limits/describe.d.ts +4 -0
  13. package/dist/limits/describe.d.ts.map +1 -1
  14. package/dist/limits/fragments.d.ts +19 -0
  15. package/dist/limits/fragments.d.ts.map +1 -0
  16. package/dist/limits/list-size.d.ts +2 -0
  17. package/dist/limits/list-size.d.ts.map +1 -1
  18. package/dist/limits/max-depth.d.ts.map +1 -1
  19. package/dist/limits/max-fields.d.ts.map +1 -1
  20. package/dist/limits/max-list-cost-options.d.ts +38 -0
  21. package/dist/limits/max-list-cost-options.d.ts.map +1 -0
  22. package/dist/limits/max-list-cost.d.ts +11 -29
  23. package/dist/limits/max-list-cost.d.ts.map +1 -1
  24. package/dist/limits/size-reading.d.ts +22 -0
  25. package/dist/limits/size-reading.d.ts.map +1 -0
  26. package/dist/rules/contains.d.ts.map +1 -1
  27. package/dist/rules/ends-with.d.ts.map +1 -1
  28. package/dist/rules/exclusive-max.d.ts.map +1 -1
  29. package/dist/rules/exclusive-min.d.ts.map +1 -1
  30. package/dist/rules/format.d.ts.map +1 -1
  31. package/dist/rules/max-items.d.ts.map +1 -1
  32. package/dist/rules/max-length.d.ts.map +1 -1
  33. package/dist/rules/max.d.ts.map +1 -1
  34. package/dist/rules/min-items.d.ts.map +1 -1
  35. package/dist/rules/min-length.d.ts.map +1 -1
  36. package/dist/rules/min.d.ts.map +1 -1
  37. package/dist/rules/multiple-of.d.ts.map +1 -1
  38. package/dist/rules/not-contains.d.ts.map +1 -1
  39. package/dist/rules/pattern.d.ts.map +1 -1
  40. package/dist/rules/rule.d.ts +5 -0
  41. package/dist/rules/rule.d.ts.map +1 -1
  42. package/dist/rules/starts-with.d.ts.map +1 -1
  43. package/docs/guide/query-limits.md +46 -13
  44. package/docs/roadmap.md +6 -0
  45. package/docs/troubleshooting.md +26 -9
  46. package/graphql/constraint.graphqls +48 -0
  47. package/package.json +1 -1
  48. package/dist/chunks/index-atwkcfg3.js.map +0 -27
  49. /package/dist/chunks/{index-w9pw7fsk.js.map → index-5x1yd76p.js.map} +0 -0
@@ -1 +1 @@
1
- {"version":3,"file":"exclusive-min.d.ts","sourceRoot":"","sources":["../../src/rules/exclusive-min.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,gBAAgB,0DAU3B,CAAC"}
1
+ {"version":3,"file":"exclusive-min.d.ts","sourceRoot":"","sources":["../../src/rules/exclusive-min.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,gBAAgB,0DAY3B,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"format.d.ts","sourceRoot":"","sources":["../../src/rules/format.ts"],"names":[],"mappings":"AAKA,eAAO,MAAM,UAAU,qDAmBrB,CAAC"}
1
+ {"version":3,"file":"format.d.ts","sourceRoot":"","sources":["../../src/rules/format.ts"],"names":[],"mappings":"AAKA,eAAO,MAAM,UAAU,qDAqBrB,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"max-items.d.ts","sourceRoot":"","sources":["../../src/rules/max-items.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,YAAY,kDAOvB,CAAC"}
1
+ {"version":3,"file":"max-items.d.ts","sourceRoot":"","sources":["../../src/rules/max-items.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,YAAY,kDASvB,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"max-length.d.ts","sourceRoot":"","sources":["../../src/rules/max-length.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,aAAa,qDAOxB,CAAC"}
1
+ {"version":3,"file":"max-length.d.ts","sourceRoot":"","sources":["../../src/rules/max-length.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,aAAa,qDASxB,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"max.d.ts","sourceRoot":"","sources":["../../src/rules/max.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,OAAO,iDAUlB,CAAC"}
1
+ {"version":3,"file":"max.d.ts","sourceRoot":"","sources":["../../src/rules/max.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,OAAO,iDAYlB,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"min-items.d.ts","sourceRoot":"","sources":["../../src/rules/min-items.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,YAAY,kDAOvB,CAAC"}
1
+ {"version":3,"file":"min-items.d.ts","sourceRoot":"","sources":["../../src/rules/min-items.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,YAAY,kDASvB,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"min-length.d.ts","sourceRoot":"","sources":["../../src/rules/min-length.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,aAAa,qDAOxB,CAAC"}
1
+ {"version":3,"file":"min-length.d.ts","sourceRoot":"","sources":["../../src/rules/min-length.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,aAAa,qDASxB,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"min.d.ts","sourceRoot":"","sources":["../../src/rules/min.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,OAAO,iDAUlB,CAAC"}
1
+ {"version":3,"file":"min.d.ts","sourceRoot":"","sources":["../../src/rules/min.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,OAAO,iDAYlB,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"multiple-of.d.ts","sourceRoot":"","sources":["../../src/rules/multiple-of.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,cAAc,wDAOzB,CAAC"}
1
+ {"version":3,"file":"multiple-of.d.ts","sourceRoot":"","sources":["../../src/rules/multiple-of.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,cAAc,wDASzB,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"not-contains.d.ts","sourceRoot":"","sources":["../../src/rules/not-contains.ts"],"names":[],"mappings":"AAIA,eAAO,MAAM,eAAe,0DAU1B,CAAC"}
1
+ {"version":3,"file":"not-contains.d.ts","sourceRoot":"","sources":["../../src/rules/not-contains.ts"],"names":[],"mappings":"AAIA,eAAO,MAAM,eAAe,0DAY1B,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"pattern.d.ts","sourceRoot":"","sources":["../../src/rules/pattern.ts"],"names":[],"mappings":"AAaA,eAAO,MAAM,WAAW,sDAQtB,CAAC"}
1
+ {"version":3,"file":"pattern.d.ts","sourceRoot":"","sources":["../../src/rules/pattern.ts"],"names":[],"mappings":"AAaA,eAAO,MAAM,WAAW,sDAUtB,CAAC"}
@@ -36,6 +36,11 @@ export interface Rule<A extends string = string, T extends keyof ValueOf = keyof
36
36
  readonly argument: A;
37
37
  readonly type: T;
38
38
  readonly target: K;
39
+ /**
40
+ * The argument's description in the shipped SDL: what it enforces, its
41
+ * bounds, the values it applies to and how a refusal reaches the client.
42
+ */
43
+ readonly description: string;
39
44
  readonly base?: true;
40
45
  readonly toZod: (schema: Targets[K], value: ValueOf[T], context: RuleContext) => Targets[K];
41
46
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"rule.d.ts","sourceRoot":"","sources":["../../src/rules/rule.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAC7B,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACtD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAE1D,4CAA4C;AAC5C,MAAM,WAAW,OAAO;IACvB,MAAM,EAAE,YAAY,CAAC;IACrB,MAAM,EAAE,CAAC,CAAC,SAAS,CAAC;IACpB,IAAI,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC;CAC5B;AAED,MAAM,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC;AAEnC;;;;GAIG;AACH,MAAM,WAAW,WAAW;IAC3B,QAAQ,CAAC,OAAO,EAAE,cAAc,CAAC;IACjC,QAAQ,CAAC,UAAU,CAAC,EAAE,CAAC,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAC,GAAG,SAAS,CAAC;CAC7D;AAED,oDAAoD;AACpD,UAAU,OAAO;IAChB,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;CACf;AAED;;;;;;;;GAQG;AACH,MAAM,WAAW,IAAI,CACpB,CAAC,SAAS,MAAM,GAAG,MAAM,EACzB,CAAC,SAAS,MAAM,OAAO,GAAG,MAAM,OAAO,EACvC,CAAC,SAAS,MAAM,GAAG,MAAM;IAEzB,QAAQ,CAAC,QAAQ,EAAE,CAAC,CAAC;IACrB,QAAQ,CAAC,IAAI,EAAE,CAAC,CAAC;IACjB,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC;IACnB,QAAQ,CAAC,IAAI,CAAC,EAAE,IAAI,CAAC;IACrB,QAAQ,CAAC,KAAK,EAAE,CACf,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,EAClB,KAAK,EAAE,OAAO,CAAC,CAAC,CAAC,EACjB,OAAO,EAAE,WAAW,KAChB,OAAO,CAAC,CAAC,CAAC,CAAC;IAChB;;;OAGG;IACH,QAAQ,CAAC,MAAM,EAAE,CAChB,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,OAAO,CAAC,CAAC,CAAC,EACjB,OAAO,EAAE,WAAW,KAChB,MAAM,CAAC;IACZ;;;;OAIG;IACH,QAAQ,CAAC,IAAI,EAAE,CAAC,KAAK,EAAE,WAAW,KAAK,OAAO,CAAC;CAC/C;AAED,kEAAkE;AAClE,MAAM,WAAW,WAAW;IAC3B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,SAAS,CAAC,EAAE,OAAO,CAAC;IAC7B,QAAQ,CAAC,MAAM,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;CACpD;AAED;;;;GAIG;AACH,eAAO,MAAM,UAAU,mBAAmB,CAAC;AAE3C,4DAA4D;AAC5D,wBAAgB,UAAU,CACzB,KAAK,CAAC,CAAC,SAAS,MAAM,EACtB,CAAC,SAAS,MAAM,OAAO,EACvB,CAAC,SAAS,MAAM,EACf,IAAI,EAAE,IAAI,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAEpC;AAED,+DAA+D;AAC/D,eAAO,MAAM,OAAO,GAAI,OAAO,MAAM,KAAG,MAA+B,CAAC"}
1
+ {"version":3,"file":"rule.d.ts","sourceRoot":"","sources":["../../src/rules/rule.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAC7B,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACtD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAE1D,4CAA4C;AAC5C,MAAM,WAAW,OAAO;IACvB,MAAM,EAAE,YAAY,CAAC;IACrB,MAAM,EAAE,CAAC,CAAC,SAAS,CAAC;IACpB,IAAI,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC;CAC5B;AAED,MAAM,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC;AAEnC;;;;GAIG;AACH,MAAM,WAAW,WAAW;IAC3B,QAAQ,CAAC,OAAO,EAAE,cAAc,CAAC;IACjC,QAAQ,CAAC,UAAU,CAAC,EAAE,CAAC,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAC,GAAG,SAAS,CAAC;CAC7D;AAED,oDAAoD;AACpD,UAAU,OAAO;IAChB,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;CACf;AAED;;;;;;;;GAQG;AACH,MAAM,WAAW,IAAI,CACpB,CAAC,SAAS,MAAM,GAAG,MAAM,EACzB,CAAC,SAAS,MAAM,OAAO,GAAG,MAAM,OAAO,EACvC,CAAC,SAAS,MAAM,GAAG,MAAM;IAEzB,QAAQ,CAAC,QAAQ,EAAE,CAAC,CAAC;IACrB,QAAQ,CAAC,IAAI,EAAE,CAAC,CAAC;IACjB,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC;IACnB;;;OAGG;IACH,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,IAAI,CAAC,EAAE,IAAI,CAAC;IACrB,QAAQ,CAAC,KAAK,EAAE,CACf,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,EAClB,KAAK,EAAE,OAAO,CAAC,CAAC,CAAC,EACjB,OAAO,EAAE,WAAW,KAChB,OAAO,CAAC,CAAC,CAAC,CAAC;IAChB;;;OAGG;IACH,QAAQ,CAAC,MAAM,EAAE,CAChB,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,OAAO,CAAC,CAAC,CAAC,EACjB,OAAO,EAAE,WAAW,KAChB,MAAM,CAAC;IACZ;;;;OAIG;IACH,QAAQ,CAAC,IAAI,EAAE,CAAC,KAAK,EAAE,WAAW,KAAK,OAAO,CAAC;CAC/C;AAED,kEAAkE;AAClE,MAAM,WAAW,WAAW;IAC3B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,SAAS,CAAC,EAAE,OAAO,CAAC;IAC7B,QAAQ,CAAC,MAAM,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;CACpD;AAED;;;;GAIG;AACH,eAAO,MAAM,UAAU,mBAAmB,CAAC;AAE3C,4DAA4D;AAC5D,wBAAgB,UAAU,CACzB,KAAK,CAAC,CAAC,SAAS,MAAM,EACtB,CAAC,SAAS,MAAM,OAAO,EACvB,CAAC,SAAS,MAAM,EACf,IAAI,EAAE,IAAI,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAEpC;AAED,+DAA+D;AAC/D,eAAO,MAAM,OAAO,GAAI,OAAO,MAAM,KAAG,MAA+B,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"starts-with.d.ts","sourceRoot":"","sources":["../../src/rules/starts-with.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,cAAc,yDAOzB,CAAC"}
1
+ {"version":3,"file":"starts-with.d.ts","sourceRoot":"","sources":["../../src/rules/starts-with.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,cAAc,yDASzB,CAAC"}
@@ -102,7 +102,7 @@ The refusal:
102
102
  `depth` is the operation's real depth. An anonymous operation reads
103
103
  `Operation is 9 levels deep, ...`.
104
104
 
105
- ## `maxListCost({ budget, defaultFirst, sizeArguments?, ignoreIntrospection? })`
105
+ ## `maxListCost({ budget, defaultFirst, sizeArguments?, ignoreIntrospection?, variableSize? })`
106
106
 
107
107
  ### How cost is counted
108
108
 
@@ -149,31 +149,59 @@ argument says:
149
149
  | a bigint (an SDL default held by a code-first schema, or after graphql-tools' `mapSchema`) | that number |
150
150
  | an integral Float literal (`first: 1000.0`, `first: 1e3`) | that number; `1.5` and `-1000.0` are unusable |
151
151
  | a negative literal (`first: -1000`), another string | as if unusable, see below |
152
- | a variable (`first: $n`) | as if unusable: validation has no variable values |
152
+ | a variable (`first: $n`) | as if unusable, see below: validation has no variable values |
153
153
  | not given | its SDL default, when it has one; else nothing |
154
154
 
155
155
  An SDL default is read the same way: `limit: String = "1000"` and `first:
156
156
  Float = 1000.0` are sizes of 1000. Every size is held at most
157
157
  `Number.MAX_SAFE_INTEGER`.
158
158
 
159
- An argument given but unusable counts for its SDL default when it has one in
160
- any accepted form (`items(first: Int = 100)`), and `defaultFirst` otherwise. On
161
- an abstract parent that is the largest default, or `defaultFirst` when it is
162
- larger and some possible type has no default. A
163
- field where no size argument says anything is sized `defaultFirst`.
159
+ An argument given but unusable (a negative number, a variable) counts for the
160
+ largest of its SDL default when it has one in any accepted form (`items(first:
161
+ Int = 100)`, else `defaultFirst`), its declared bound (`@constraint(max)` or
162
+ `exclusiveMax`) and, where some definition declares no bound, `variableSize`:
163
+ see [Variables](#variables). A field where no size argument says anything is
164
+ sized `defaultFirst`.
164
165
 
165
166
  On an interface or a union, the default is the **largest** over the interface's
166
167
  field and the field of every possible type (`Node.kids(first: Int = 1)` next to
167
168
  `Folder.kids(first: Int = 100000)` is sized 100,000), since the query cannot
168
- say which object comes back.
169
+ say which object comes back; `defaultFirst` counts too when it is larger and
170
+ some possible type has no default.
169
171
 
170
172
  A negative number is therefore never taken as a size: no subtree costs less than
171
173
  nothing, and `items(first: -1000)` next to an expensive sibling cannot cancel
172
174
  the sibling's cost.
173
175
 
174
- Pick `defaultFirst` knowing a variable counts for it: a client using
175
- `first: $n` is charged the default whatever it sends. If your server caps a size
176
- argument, set `defaultFirst` (or the SDL default) at that cap.
176
+ ### Variables
177
+
178
+ The client can send any value the argument accepts, so a size given as a
179
+ variable (or as an unusable literal) counts as the **largest** of:
180
+
181
+ - its SDL default, else `defaultFirst`: never less than that;
182
+ - the bound of the argument's `@constraint` (the directive `withValidation`
183
+ enforces), read from the schema's SDL: `first: Int = 20 @constraint(max: 100)`
184
+ counts 100 for `first: $n`. `exclusiveMax` is read the same way (the value
185
+ itself, a slight over-count), a fractional bound is rounded up (`max: 100.5`
186
+ counts 101), and the largest of `max` and `exclusiveMax` counts. On an
187
+ interface it is the largest over the interface's field and each possible
188
+ type's field;
189
+ - `variableSize`, where some definition declares no bound:
190
+ `maxListCost({ budget, defaultFirst, variableSize: 100 })` for a server that
191
+ caps every page at 100 in its resolvers. It applies to every size argument
192
+ (`first`, `last`, `limit`, your own `sizeArguments`) and to unusable
193
+ literals. A declared bound wins over it only when every definition (the
194
+ interface's field and each possible type's) declares one; if any lacks one,
195
+ `variableSize` applies too and the largest wins.
196
+
197
+ ```graphql
198
+ query ($n: Int) { wards(first: $n) { edges { node { rooms(first: $n) { id } } } } }
199
+ ```
200
+
201
+ With `first: Int = 20 @constraint(max: 100)` on both fields, that query is
202
+ costed as the same one written with `first: 100`, not with the defaults. The
203
+ operation's own variable default (`query ($n: Int = 5)`) is ignored: the client
204
+ can override it. An argument not given at all keeps its SDL default.
177
205
 
178
206
  ### Relay connections
179
207
 
@@ -209,7 +237,7 @@ selected **directly on its return type**:
209
237
  - A connection field that states nothing (no size argument given, none declared
210
238
  with a default) hands down nothing: its lists are sized `defaultFirst`, as
211
239
  before. A variable, a negative number or an unusable value on it counts as for
212
- a list: its SDL default, else `defaultFirst`.
240
+ a list, see [Variables](#variables).
213
241
  - The rule is structural: any non-list field stating a size sizes the lists
214
242
  declaring no size argument directly on its return type, whether or not it is
215
243
  a connection. `user(limit: 0) { friends { … } }` sizes `friends` 0. So an
@@ -243,7 +271,7 @@ never underestimated.
243
271
  }
244
272
  ```
245
273
 
246
- `budget` and `defaultFirst` must be integers below `Number.MAX_SAFE_INTEGER`
274
+ `budget`, `defaultFirst` and `variableSize` must be integers below `Number.MAX_SAFE_INTEGER`
247
275
  (a cost never exceeds it, so a budget at or over it would refuse nothing); the
248
276
  rule throws a `RangeError` otherwise.
249
277
 
@@ -301,6 +329,11 @@ two: `maxDepth` bounds nesting, `maxListCost` bounds list multiplication, and
301
329
 
302
330
  ## Not covered
303
331
 
332
+ - A code-first argument has no SDL node, hence no declared bound: only
333
+ `variableSize` applies to a variable on it.
334
+ - A size bounded only by a custom scalar (`first: PageSize`) or by `maxItems`
335
+ or `maxLength` counts as having no bound: declare `@constraint(max)` on the
336
+ argument.
304
337
  - A size carried inside an input object (`items(page: { first: 1000 })`) is not
305
338
  read: the field has no size argument, so it is sized `defaultFirst`. Name the
306
339
  size as an argument, or raise `defaultFirst`.
package/docs/roadmap.md CHANGED
@@ -21,6 +21,12 @@ Nothing planned yet.
21
21
  - **Relay connection sizing** — `maxListCost` sizes the `edges` and `nodes` of
22
22
  a connection from the connection field's size argument (`items(first: 100)
23
23
  { edges { … } }`), not `defaultFirst`.
24
+ - **Variable sizes counted at their bound** — `maxListCost` counts a size sent
25
+ as a variable (or an unusable literal) at the argument's declared bound
26
+ (`@constraint(max)` or `exclusiveMax`) and the new `variableSize` option. The
27
+ bound wins only when every definition declares one, and never counts below the
28
+ SDL default or `defaultFirst`, so a query cannot hide a large page behind a
29
+ variable.
24
30
  - **Query depth, cost and breadth limits** — `maxDepth`, `maxListCost` and
25
31
  `maxFields` are graphql-js validation rules (also for graphql-yoga's
26
32
  `addValidationRule`) that refuse a query nested too deep, too costly for a
@@ -425,14 +425,18 @@ reads `Operation is 9 levels deep, ...`.
425
425
  **When:** an operation is validated with `maxListCost({ budget: 1000, ... })`.
426
426
  **Why:** `extensions` is `{ code: 'QUERY_TOO_COSTLY', cost: 5002, limit: 1000 }`.
427
427
  Each field costs 1, times the sizes of the lists enclosing it. A list without a
428
- usable `first` (a variable, a negative number, nothing) counts for its SDL
429
- default, else `defaultFirst`, so a query that is cheap with the variable's real
430
- value can still be refused. A size argument on a field returning an object (a
431
- connection's `first`) now sizes its `edges` and `nodes`, and any non-list field
432
- stating a size sizes the lists declaring none directly under it, see
428
+ usable `first` counts for its SDL default, else `defaultFirst`; a variable or
429
+ a negative number counts for the largest of that, the declared bound and
430
+ `variableSize`, so a query that is cheap with the variable's real value can
431
+ still be refused, see [Variables](guide/query-limits.md#variables). A size
432
+ argument on a field returning an object (a connection's `first`) sizes its
433
+ `edges` and `nodes`, and any non-list field stating a size sizes the lists
434
+ declaring none directly under it, see
433
435
  [Relay connections](guide/query-limits.md#relay-connections).
434
- **Fix:** ask for fewer items with a literal `first`, or raise `budget`. A query
435
- on an interface or a union is overestimated (every branch is summed).
436
+
437
+ **Fix:** ask for fewer items with a literal `first`, lower the `@constraint(max)`
438
+ of a size argument, or raise `budget`. A query on an interface or a union is
439
+ overestimated (every branch is summed).
436
440
 
437
441
  ### `Operation "Name" selects 4000 fields, more than the 500 allowed`
438
442
 
@@ -468,6 +472,17 @@ budget at or over it would refuse nothing.
468
472
  **When:** `defaultFirst` is negative, fractional or too large.
469
473
  **Fix:** pass the size to assume for a list with no usable size argument.
470
474
 
475
+ ### `maxListCost: variableSize must be an integer from 0 to 9007199254740990.`
476
+
477
+ **When:** `variableSize` is negative, fractional or too large.
478
+ **Why:** it is the size assumed for a variable or unusable size argument, and
479
+ a size is an integer from 0 up to a bound a cost cannot overflow.
480
+ **Fix:** pass an integer in range, such as the page cap of your resolvers:
481
+
482
+ ```ts
483
+ maxListCost({ budget: 10_000, defaultFirst: 50, variableSize: 100 });
484
+ ```
485
+
471
486
  ### A query refused although its `first` is small, or an introspection query refused
472
487
 
473
488
  **When:** `QUERY_TOO_COSTLY`, `QUERY_TOO_DEEP` or `QUERY_TOO_WIDE` for GraphiQL's schema fetch or
@@ -475,8 +490,10 @@ a codegen's introspection query.
475
490
  **Why:** `__schema` is counted: the standard introspection query is 15 levels
476
491
  deep, selects 220 fields and costs on the order of 200,000 on a one-field schema.
477
492
  **Fix:** `ignoreIntrospection: true` on the rule where introspection is
478
- disabled or the client is trusted. A `first: $n` variable counts for the SDL
479
- default, not for the value sent.
493
+ disabled or the client is trusted. A `first: $n` variable counts for its
494
+ declared bound (`@constraint(max)` or `exclusiveMax`, when every definition
495
+ declares one) and `variableSize` otherwise, never below the SDL default or
496
+ `defaultFirst`, not for the value sent.
480
497
 
481
498
  ## The bin
482
499
 
@@ -1,20 +1,68 @@
1
1
  # The @constraint directive of @nxgt/graphql-validation, for your IDE.
2
2
  # Generated by `nxgt-graphql-validation typedefs`: do not edit.
3
3
 
4
+ """
5
+ Validates an argument or an input field when the request arrives, before the resolver runs. A value that breaks a constraint is refused with a BAD_USER_INPUT error whose extensions list each issue with its path and the constraint that refused it; null and omitted values are not checked. Each argument below is one constraint, and they combine on one field.
6
+ """
4
7
  directive @constraint(
8
+ """
9
+ The string must contain this text (case-sensitive). Refused otherwise with a BAD_USER_INPUT error naming the constraint. Applies to String and ID values.
10
+ """
5
11
  contains: String
12
+ """
13
+ The string must end with this text (case-sensitive). Refused otherwise with a BAD_USER_INPUT error naming the constraint. Applies to String and ID values.
14
+ """
6
15
  endsWith: String
16
+ """
17
+ The number must be strictly less than this value (the value itself is refused). Refused otherwise with a BAD_USER_INPUT error naming the constraint. Applies to Int and Float values.
18
+ """
7
19
  exclusiveMax: Float
20
+ """
21
+ The number must be strictly greater than this value (the value itself is refused). Refused otherwise with a BAD_USER_INPUT error naming the constraint. Applies to Int and Float values.
22
+ """
8
23
  exclusiveMin: Float
24
+ """
25
+ The string must match this named format: byte, date, date-time, email, ipv4, ipv6, uri or uuid, or a format the server registered. An unknown name fails at server startup. Refused otherwise with a BAD_USER_INPUT error naming the constraint. Applies to String and ID values.
26
+ """
9
27
  format: String
28
+ """
29
+ The list must hold at most this many items (inclusive). Refused otherwise with a BAD_USER_INPUT error naming the constraint. Applies to list values.
30
+ """
10
31
  maxItems: Int
32
+ """
33
+ The string must be at most this many characters long (UTF-16 code units, inclusive). Refused otherwise with a BAD_USER_INPUT error naming the constraint. Applies to String and ID values.
34
+ """
11
35
  maxLength: Int
36
+ """
37
+ The number must be less than or equal to this value (inclusive). Refused otherwise with a BAD_USER_INPUT error naming the constraint. Applies to Int and Float values.
38
+ """
12
39
  max: Float
40
+ """
41
+ The list must hold at least this many items (inclusive). Refused otherwise with a BAD_USER_INPUT error naming the constraint. Applies to list values.
42
+ """
13
43
  minItems: Int
44
+ """
45
+ The string must be at least this many characters long (UTF-16 code units, inclusive). Refused otherwise with a BAD_USER_INPUT error naming the constraint. Applies to String and ID values.
46
+ """
14
47
  minLength: Int
48
+ """
49
+ The number must be greater than or equal to this value (inclusive). Refused otherwise with a BAD_USER_INPUT error naming the constraint. Applies to Int and Float values.
50
+ """
15
51
  min: Float
52
+ """
53
+ The number must be a whole multiple of this value (for example 5 accepts 0, 5 and 10). Refused otherwise with a BAD_USER_INPUT error naming the constraint. Applies to Int and Float values.
54
+ """
16
55
  multipleOf: Float
56
+ """
57
+ The string must not contain this text (case-sensitive). Refused otherwise with a BAD_USER_INPUT error naming the constraint. Applies to String and ID values.
58
+ """
17
59
  notContains: String
60
+ """
61
+ The string must match this regular expression (JavaScript syntax, not anchored unless it uses ^ and $). An invalid expression fails at server startup. Refused otherwise with a BAD_USER_INPUT error naming the constraint. Applies to String and ID values.
62
+ """
18
63
  pattern: String
64
+ """
65
+ The string must start with this text (case-sensitive). Refused otherwise with a BAD_USER_INPUT error naming the constraint. Applies to String and ID values.
66
+ """
19
67
  startsWith: String
20
68
  ) on ARGUMENT_DEFINITION | INPUT_FIELD_DEFINITION
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nxgt/graphql-validation",
3
- "version": "0.7.0",
3
+ "version": "0.8.1",
4
4
  "description": "Validate GraphQL arguments and input fields with @constraint directives, checked by Zod schemas, for graphql 16 and 17",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -1,27 +0,0 @@
1
- {
2
- "version": 3,
3
- "sources": ["../src/constraint-directive.ts", "../src/rules/rule.ts", "../src/rules/contains.ts", "../src/rules/ends-with.ts", "../src/rules/exclusive-max.ts", "../src/rules/exclusive-min.ts", "../src/rules/format.ts", "../src/rules/max.ts", "../src/rules/max-items.ts", "../src/rules/max-length.ts", "../src/rules/min.ts", "../src/rules/min-items.ts", "../src/rules/min-length.ts", "../src/rules/multiple-of.ts", "../src/rules/not-contains.ts", "../src/rules/pattern.ts", "../src/rules/starts-with.ts", "../src/rules/index.ts"],
4
- "sourcesContent": [
5
- "import { DirectiveLocation, type GraphQLDirective } from 'graphql';\nimport { rules } from './rules';\n\n/**\n * The SDL of `@constraint`, to add to a schema-first server's `typeDefs`,\n * written from the rules: an argument is declared once its rule exists.\n *\n * Its arguments are graphql-constraint-directive's, minus `uniqueTypeName`\n * (a detail of that package's scalar wrapping). It is allowed on arguments\n * and input fields only, where that package also allows output fields: a\n * resolver's result is the output scalars' job, and a directive that checked\n * nothing there would be a promise broken in silence.\n */\nexport const constraintTypeDefs: string = `directive @constraint(\n${Object.values(rules)\n\t.map((rule) => `\\t${rule.argument}: ${rule.type}`)\n\t.join('\\n')}\n) on ARGUMENT_DEFINITION | INPUT_FIELD_DEFINITION\n`;\n\nconst allowed = new Set<string>([\n\tDirectiveLocation.ARGUMENT_DEFINITION,\n\tDirectiveLocation.INPUT_FIELD_DEFINITION,\n]);\n\n/**\n * Fails when the schema's `@constraint` is not the one\n * {@link constraintTypeDefs} declares — graphql-constraint-directive's, say,\n * kept from a migration. Its extra locations would be accepted and checked\n * by nothing, its extra arguments mean nothing here, and an argument of\n * another type would reach a rule as the wrong kind of value.\n */\nexport function assertOwnConstraint(directive: GraphQLDirective): void {\n\tconst problems = [\n\t\t...(directive.isRepeatable\n\t\t\t? ['repeatable, and only its first use is read']\n\t\t\t: []),\n\t\t...directive.locations\n\t\t\t.filter((location) => !allowed.has(location))\n\t\t\t.map((location) => `allowed on ${location}`),\n\t\t...directive.args.flatMap((arg) => {\n\t\t\tconst rule = (rules as Record<string, { type: string } | undefined>)[\n\t\t\t\targ.name\n\t\t\t];\n\t\t\tif (!rule) return [`declares ${arg.name}, which no rule reads`];\n\t\t\tconst type = String(arg.type);\n\t\t\treturn type === rule.type\n\t\t\t\t? []\n\t\t\t\t: [`declares ${arg.name}: ${type}, not ${rule.type}`];\n\t\t}),\n\t];\n\tif (problems.length > 0) {\n\t\tthrow new Error(\n\t\t\t`This schema's @constraint is not constraintTypeDefs': it is ${problems.join('; ')}. Declare it with constraintTypeDefs.`,\n\t\t);\n\t}\n}\n",
6
- "import type { z } from 'zod';\nimport type { StringSchema } from '../formats/format';\nimport type { FormatRegistry } from '../formats/registry';\n\n/** The schema each kind of rule narrows. */\nexport interface Targets {\n\tstring: StringSchema;\n\tnumber: z.ZodNumber;\n\tlist: z.ZodArray<z.ZodType>;\n}\n\nexport type Target = keyof Targets;\n\n/**\n * What a rule may read besides its value: the formats `format` resolves\n * against, the built-in ones and the application's own, and, when writing\n * source, how to write one of the application's (`InputCodeOptions.format`).\n */\nexport interface RuleContext {\n\treadonly formats: FormatRegistry;\n\treadonly formatCode?: ((name: string) => string) | undefined;\n}\n\n/** What a `@constraint` argument's value can be. */\ninterface ValueOf {\n\tInt: number;\n\tFloat: number;\n\tString: string;\n}\n\n/**\n * One `@constraint` argument: its GraphQL type in the directive's SDL, the\n * kind of value it narrows, and the same narrowing twice — `toZod` applied to\n * a schema at startup, `toCode` written as source by a code generator. The\n * spec beside each rule proves the two accept and refuse the same values.\n *\n * A `base` rule (only `format`) replaces the schema instead of narrowing it,\n * so a builder applies it before every other rule of its target.\n */\nexport interface Rule<\n\tA extends string = string,\n\tT extends keyof ValueOf = keyof ValueOf,\n\tK extends Target = Target,\n> {\n\treadonly argument: A;\n\treadonly type: T;\n\treadonly target: K;\n\treadonly base?: true;\n\treadonly toZod: (\n\t\tschema: Targets[K],\n\t\tvalue: ValueOf[T],\n\t\tcontext: RuleContext,\n\t) => Targets[K];\n\t/**\n\t * `schema` is the source of the schema this rule narrows. The source names\n\t * zod as a free `z`: the generated file imports it.\n\t */\n\treadonly toCode: (\n\t\tschema: string,\n\t\tvalue: ValueOf[T],\n\t\tcontext: RuleContext,\n\t) => string;\n\t/**\n\t * Whether a Zod issue is this rule's refusal, so the error a client gets\n\t * names the rule (`constraint: 'minLength'`), which does not change with\n\t * Zod's own issue codes.\n\t */\n\treadonly owns: (issue: IssueFields) => boolean;\n}\n\n/** The fields of a Zod issue a rule recognises its refusal by. */\nexport interface IssueFields {\n\treadonly code: string;\n\treadonly origin?: string;\n\treadonly format?: string;\n\treadonly inclusive?: boolean;\n\treadonly params?: Readonly<Record<string, unknown>>;\n}\n\n/**\n * The `params` key that marks an issue as one argument's whatever its code:\n * an application format's issues carry `format` there, so a `.regex()` or a\n * `.refine()` inside one is not read as `pattern` or `notContains`.\n */\nexport const RULE_PARAM = 'nxgtConstraint';\n\n/** Declares a rule, with its argument name kept literal. */\nexport function defineRule<\n\tconst A extends string,\n\tT extends keyof ValueOf,\n\tK extends Target,\n>(rule: Rule<A, T, K>): Rule<A, T, K> {\n\treturn rule;\n}\n\n/** A string literal as source: quotes and escapes included. */\nexport const literal = (value: string): string => JSON.stringify(value);\n",
7
- "import { defineRule, literal } from './rule';\n\nexport const containsRule = defineRule({\n\targument: 'contains',\n\ttype: 'String',\n\ttarget: 'string',\n\ttoZod: (schema, value) => schema.includes(value),\n\ttoCode: (schema, value) => `${schema}.includes(${literal(value)})`,\n\towns: (issue) => issue.format === 'includes',\n});\n",
8
- "import { defineRule, literal } from './rule';\n\nexport const endsWithRule = defineRule({\n\targument: 'endsWith',\n\ttype: 'String',\n\ttarget: 'string',\n\ttoZod: (schema, value) => schema.endsWith(value),\n\ttoCode: (schema, value) => `${schema}.endsWith(${literal(value)})`,\n\towns: (issue) => issue.format === 'ends_with',\n});\n",
9
- "import { defineRule } from './rule';\n\nexport const exclusiveMaxRule = defineRule({\n\targument: 'exclusiveMax',\n\ttype: 'Float',\n\ttarget: 'number',\n\ttoZod: (schema, value) => schema.lt(value),\n\ttoCode: (schema, value) => `${schema}.lt(${value})`,\n\towns: (issue) =>\n\t\tissue.code === 'too_big' &&\n\t\tissue.origin === 'number' &&\n\t\tissue.inclusive === false,\n});\n",
10
- "import { defineRule } from './rule';\n\nexport const exclusiveMinRule = defineRule({\n\targument: 'exclusiveMin',\n\ttype: 'Float',\n\ttarget: 'number',\n\ttoZod: (schema, value) => schema.gt(value),\n\ttoCode: (schema, value) => `${schema}.gt(${value})`,\n\towns: (issue) =>\n\t\tissue.code === 'too_small' &&\n\t\tissue.origin === 'number' &&\n\t\tissue.inclusive === false,\n});\n",
11
- "import { defineRule, RULE_PARAM } from './rule';\n\n// The string rules' own formats; every other invalid_format is a format's.\nconst NARROWING = new Set(['starts_with', 'ends_with', 'includes', 'regex']);\n\nexport const formatRule = defineRule({\n\targument: 'format',\n\ttype: 'String',\n\ttarget: 'string',\n\tbase: true,\n\ttoZod: (_schema, value, { formats }) => formats.named(value).toZod(),\n\ttoCode: (_schema, value, { formats, formatCode }) => {\n\t\tconst format = formats.named(value);\n\t\tif (format.toCode) return format.toCode();\n\t\tif (formatCode) return formatCode(value);\n\t\tthrow new Error(\n\t\t\t`@constraint(format: \"${value}\") is one of the application's formats: its source is the code generator's to write, through inputCode's format option.`,\n\t\t);\n\t},\n\t// An application format's issues are marked, whatever their code\n\t// (`custom` for a `.refine()`): see FormatRegistry.\n\towns: (issue) =>\n\t\tissue.params?.[RULE_PARAM] === 'format' ||\n\t\t(issue.code === 'invalid_format' && !NARROWING.has(issue.format ?? '')),\n});\n",
12
- "import { defineRule } from './rule';\n\nexport const maxRule = defineRule({\n\targument: 'max',\n\ttype: 'Float',\n\ttarget: 'number',\n\ttoZod: (schema, value) => schema.lte(value),\n\ttoCode: (schema, value) => `${schema}.lte(${value})`,\n\towns: (issue) =>\n\t\tissue.code === 'too_big' &&\n\t\tissue.origin === 'number' &&\n\t\tissue.inclusive === true,\n});\n",
13
- "import { defineRule } from './rule';\n\nexport const maxItemsRule = defineRule({\n\targument: 'maxItems',\n\ttype: 'Int',\n\ttarget: 'list',\n\ttoZod: (schema, value) => schema.max(value),\n\ttoCode: (schema, value) => `${schema}.max(${value})`,\n\towns: (issue) => issue.code === 'too_big' && issue.origin === 'array',\n});\n",
14
- "import { defineRule } from './rule';\n\nexport const maxLengthRule = defineRule({\n\targument: 'maxLength',\n\ttype: 'Int',\n\ttarget: 'string',\n\ttoZod: (schema, value) => schema.max(value),\n\ttoCode: (schema, value) => `${schema}.max(${value})`,\n\towns: (issue) => issue.code === 'too_big' && issue.origin === 'string',\n});\n",
15
- "import { defineRule } from './rule';\n\nexport const minRule = defineRule({\n\targument: 'min',\n\ttype: 'Float',\n\ttarget: 'number',\n\ttoZod: (schema, value) => schema.gte(value),\n\ttoCode: (schema, value) => `${schema}.gte(${value})`,\n\towns: (issue) =>\n\t\tissue.code === 'too_small' &&\n\t\tissue.origin === 'number' &&\n\t\tissue.inclusive === true,\n});\n",
16
- "import { defineRule } from './rule';\n\nexport const minItemsRule = defineRule({\n\targument: 'minItems',\n\ttype: 'Int',\n\ttarget: 'list',\n\ttoZod: (schema, value) => schema.min(value),\n\ttoCode: (schema, value) => `${schema}.min(${value})`,\n\towns: (issue) => issue.code === 'too_small' && issue.origin === 'array',\n});\n",
17
- "import { defineRule } from './rule';\n\nexport const minLengthRule = defineRule({\n\targument: 'minLength',\n\ttype: 'Int',\n\ttarget: 'string',\n\ttoZod: (schema, value) => schema.min(value),\n\ttoCode: (schema, value) => `${schema}.min(${value})`,\n\towns: (issue) => issue.code === 'too_small' && issue.origin === 'string',\n});\n",
18
- "import { defineRule } from './rule';\n\nexport const multipleOfRule = defineRule({\n\targument: 'multipleOf',\n\ttype: 'Float',\n\ttarget: 'number',\n\ttoZod: (schema, value) => schema.multipleOf(value),\n\ttoCode: (schema, value) => `${schema}.multipleOf(${value})`,\n\towns: (issue) => issue.code === 'not_multiple_of',\n});\n",
19
- "import { defineRule, literal } from './rule';\n\nconst message = (value: string) => `Must not contain ${literal(value)}`;\n\nexport const notContainsRule = defineRule({\n\targument: 'notContains',\n\ttype: 'String',\n\ttarget: 'string',\n\t// `refine` keeps the schema's own type, so later rules still chain.\n\ttoZod: (schema, value) =>\n\t\tschema.refine((input) => !input.includes(value), message(value)),\n\ttoCode: (schema, value) =>\n\t\t`${schema}.refine((input) => !input.includes(${literal(value)}), ${literal(message(value))})`,\n\towns: (issue) => issue.code === 'custom',\n});\n",
20
- "import { defineRule, literal } from './rule';\n\n/** The pattern as a RegExp, or an error that names the argument. */\nfunction compile(value: string): RegExp {\n\ttry {\n\t\treturn new RegExp(value);\n\t} catch (error) {\n\t\tthrow new Error(\n\t\t\t`Invalid @constraint pattern ${literal(value)}: ${(error as Error).message}`,\n\t\t);\n\t}\n}\n\nexport const patternRule = defineRule({\n\targument: 'pattern',\n\ttype: 'String',\n\ttarget: 'string',\n\ttoZod: (schema, value) => schema.regex(compile(value)),\n\ttoCode: (schema, value) =>\n\t\t`${schema}.regex(new RegExp(${literal(compile(value).source)}))`,\n\towns: (issue) => issue.format === 'regex',\n});\n",
21
- "import { defineRule, literal } from './rule';\n\nexport const startsWithRule = defineRule({\n\targument: 'startsWith',\n\ttype: 'String',\n\ttarget: 'string',\n\ttoZod: (schema, value) => schema.startsWith(value),\n\ttoCode: (schema, value) => `${schema}.startsWith(${literal(value)})`,\n\towns: (issue) => issue.format === 'starts_with',\n});\n",
22
- "import type { z } from 'zod';\nimport * as all from './all';\nimport { type IssueFields, RULE_PARAM, type RuleContext } from './rule';\n\n/**\n * One of the rules, as its own type: a builder narrows it on `target`, so a\n * number schema never reaches a string rule.\n */\nexport type RegisteredRule = (typeof all)[keyof typeof all];\n\n/** An argument `@constraint` takes. */\nexport type ConstraintArgument = RegisteredRule['argument'];\n\n/**\n * Every rule, keyed by its `@constraint` argument. Derived from what\n * `./all` exports, so a new rule is one file and one line there, and the\n * directive's SDL follows.\n */\nexport const rules = Object.fromEntries(\n\tObject.values(all).map((rule) => [rule.argument, rule]),\n) as {\n\treadonly [A in ConstraintArgument]: Extract<RegisteredRule, { argument: A }>;\n};\n\n/**\n * Applies a rule to a schema its caller has already matched to the rule's\n * target: the one place the rule's typed `toZod` meets a schema built from\n * GraphQL types, which the compiler cannot relate.\n */\nexport function applyRule(\n\trule: RegisteredRule,\n\tschema: z.ZodType,\n\tvalue: string | number,\n\tcontext: RuleContext,\n): z.ZodType {\n\tconst toZod = rule.toZod as unknown as (\n\t\tschema: z.ZodType,\n\t\tvalue: string | number,\n\t\tcontext: RuleContext,\n\t) => z.ZodType;\n\treturn toZod(schema, value, context);\n}\n\n/** `applyRule` for source: the rule's typed `toCode` on a schema's source. */\nexport function applyRuleCode(\n\trule: RegisteredRule,\n\tsource: string,\n\tvalue: string | number,\n\tcontext: RuleContext,\n): string {\n\tconst toCode = rule.toCode as (\n\t\tschema: string,\n\t\tvalue: string | number,\n\t\tcontext: RuleContext,\n\t) => string;\n\treturn toCode(source, value, context);\n}\n\n/**\n * The `@constraint` argument whose rule refused with this issue, if any: the\n * one the issue is marked with (an application format's), else the one rule\n * that owns its shape.\n */\nexport function constraintOf(\n\tissue: z.core.$ZodIssue,\n): ConstraintArgument | undefined {\n\tconst fields = issue as unknown as IssueFields;\n\tconst marked = fields.params?.[RULE_PARAM];\n\tif (typeof marked === 'string' && Object.hasOwn(rules, marked))\n\t\treturn marked as ConstraintArgument;\n\treturn Object.values(rules).find((rule) => rule.owns(fields))?.argument;\n}\n"
23
- ],
24
- "mappings": "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",
25
- "debugId": "02497C6067AC4D4564756E2164756E21",
26
- "names": []
27
- }