@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.
- package/README.md +8 -3
- package/dist/chunks/{index-w9pw7fsk.js → index-5x1yd76p.js} +2 -2
- package/dist/chunks/{index-atwkcfg3.js → index-sq3exxg2.js} +25 -4
- package/dist/chunks/index-sq3exxg2.js.map +27 -0
- package/dist/cli.js +1 -1
- package/dist/codegen.js +2 -2
- package/dist/constraint-directive.d.ts.map +1 -1
- package/dist/index.js +102 -71
- package/dist/index.js.map +12 -9
- package/dist/limits/cost-context.d.ts +7 -7
- package/dist/limits/cost-context.d.ts.map +1 -1
- package/dist/limits/describe.d.ts +4 -0
- package/dist/limits/describe.d.ts.map +1 -1
- package/dist/limits/fragments.d.ts +19 -0
- package/dist/limits/fragments.d.ts.map +1 -0
- package/dist/limits/list-size.d.ts +2 -0
- package/dist/limits/list-size.d.ts.map +1 -1
- package/dist/limits/max-depth.d.ts.map +1 -1
- package/dist/limits/max-fields.d.ts.map +1 -1
- package/dist/limits/max-list-cost-options.d.ts +38 -0
- package/dist/limits/max-list-cost-options.d.ts.map +1 -0
- package/dist/limits/max-list-cost.d.ts +11 -29
- package/dist/limits/max-list-cost.d.ts.map +1 -1
- package/dist/limits/size-reading.d.ts +22 -0
- package/dist/limits/size-reading.d.ts.map +1 -0
- package/dist/rules/contains.d.ts.map +1 -1
- package/dist/rules/ends-with.d.ts.map +1 -1
- package/dist/rules/exclusive-max.d.ts.map +1 -1
- package/dist/rules/exclusive-min.d.ts.map +1 -1
- package/dist/rules/format.d.ts.map +1 -1
- package/dist/rules/max-items.d.ts.map +1 -1
- package/dist/rules/max-length.d.ts.map +1 -1
- package/dist/rules/max.d.ts.map +1 -1
- package/dist/rules/min-items.d.ts.map +1 -1
- package/dist/rules/min-length.d.ts.map +1 -1
- package/dist/rules/min.d.ts.map +1 -1
- package/dist/rules/multiple-of.d.ts.map +1 -1
- package/dist/rules/not-contains.d.ts.map +1 -1
- package/dist/rules/pattern.d.ts.map +1 -1
- package/dist/rules/rule.d.ts +5 -0
- package/dist/rules/rule.d.ts.map +1 -1
- package/dist/rules/starts-with.d.ts.map +1 -1
- package/docs/guide/query-limits.md +46 -13
- package/docs/roadmap.md +6 -0
- package/docs/troubleshooting.md +26 -9
- package/graphql/constraint.graphqls +48 -0
- package/package.json +1 -1
- package/dist/chunks/index-atwkcfg3.js.map +0 -27
- /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,
|
|
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,
|
|
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,
|
|
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,
|
|
1
|
+
{"version":3,"file":"max-length.d.ts","sourceRoot":"","sources":["../../src/rules/max-length.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,aAAa,qDASxB,CAAC"}
|
package/dist/rules/max.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"max.d.ts","sourceRoot":"","sources":["../../src/rules/max.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,OAAO,
|
|
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,
|
|
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,
|
|
1
|
+
{"version":3,"file":"min-length.d.ts","sourceRoot":"","sources":["../../src/rules/min-length.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,aAAa,qDASxB,CAAC"}
|
package/dist/rules/min.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"min.d.ts","sourceRoot":"","sources":["../../src/rules/min.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,OAAO,
|
|
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,
|
|
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,
|
|
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,
|
|
1
|
+
{"version":3,"file":"pattern.d.ts","sourceRoot":"","sources":["../../src/rules/pattern.ts"],"names":[],"mappings":"AAaA,eAAO,MAAM,WAAW,sDAUtB,CAAC"}
|
package/dist/rules/rule.d.ts
CHANGED
|
@@ -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
|
/**
|
package/dist/rules/rule.d.ts.map
CHANGED
|
@@ -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,
|
|
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
|
|
160
|
-
any accepted form (`items(first:
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
field where no size argument says anything is
|
|
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
|
-
|
|
175
|
-
|
|
176
|
-
|
|
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
|
|
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 `
|
|
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
|
package/docs/troubleshooting.md
CHANGED
|
@@ -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`
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
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
|
-
|
|
435
|
-
|
|
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
|
|
479
|
-
|
|
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,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
|
-
}
|
|
File without changes
|