@nxgt/graphql-validation 0.0.0-stage → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +235 -2
- package/dist/bad-user-input.d.ts +35 -0
- package/dist/bad-user-input.d.ts.map +1 -0
- package/dist/builder/args-schema.d.ts +17 -0
- package/dist/builder/args-schema.d.ts.map +1 -0
- package/dist/builder/check-constraints.d.ts +15 -0
- package/dist/builder/check-constraints.d.ts.map +1 -0
- package/dist/builder/constraints.d.ts +16 -0
- package/dist/builder/constraints.d.ts.map +1 -0
- package/dist/builder/input-code.d.ts +29 -0
- package/dist/builder/input-code.d.ts.map +1 -0
- package/dist/builder/input-schema.d.ts +31 -0
- package/dist/builder/input-schema.d.ts.map +1 -0
- package/dist/builder/leaf.d.ts +24 -0
- package/dist/builder/leaf.d.ts.map +1 -0
- package/dist/chunks/index-rbxe5m3y.js +319 -0
- package/dist/chunks/index-rbxe5m3y.js.map +37 -0
- package/dist/chunks/index-s1kkmg35.js +266 -0
- package/dist/chunks/index-s1kkmg35.js.map +14 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +69 -0
- package/dist/cli.js.map +11 -0
- package/dist/codegen.d.ts +4 -0
- package/dist/codegen.d.ts.map +1 -0
- package/dist/codegen.js +56 -0
- package/dist/codegen.js.map +10 -0
- package/dist/constraint-directive.d.ts +21 -0
- package/dist/constraint-directive.d.ts.map +1 -0
- package/dist/formats/all.d.ts +9 -0
- package/dist/formats/all.d.ts.map +1 -0
- package/dist/formats/byte.d.ts +2 -0
- package/dist/formats/byte.d.ts.map +1 -0
- package/dist/formats/date-time.d.ts +2 -0
- package/dist/formats/date-time.d.ts.map +1 -0
- package/dist/formats/date.d.ts +2 -0
- package/dist/formats/date.d.ts.map +1 -0
- package/dist/formats/email.d.ts +2 -0
- package/dist/formats/email.d.ts.map +1 -0
- package/dist/formats/format.d.ts +15 -0
- package/dist/formats/format.d.ts.map +1 -0
- package/dist/formats/index.d.ts +13 -0
- package/dist/formats/index.d.ts.map +1 -0
- package/dist/formats/ipv4.d.ts +2 -0
- package/dist/formats/ipv4.d.ts.map +1 -0
- package/dist/formats/ipv6.d.ts +2 -0
- package/dist/formats/ipv6.d.ts.map +1 -0
- package/dist/formats/uri.d.ts +2 -0
- package/dist/formats/uri.d.ts.map +1 -0
- package/dist/formats/uuid.d.ts +2 -0
- package/dist/formats/uuid.d.ts.map +1 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +84 -0
- package/dist/index.js.map +12 -0
- package/dist/rules/all.d.ts +16 -0
- package/dist/rules/all.d.ts.map +1 -0
- package/dist/rules/contains.d.ts +2 -0
- package/dist/rules/contains.d.ts.map +1 -0
- package/dist/rules/ends-with.d.ts +2 -0
- package/dist/rules/ends-with.d.ts.map +1 -0
- package/dist/rules/exclusive-max.d.ts +2 -0
- package/dist/rules/exclusive-max.d.ts.map +1 -0
- package/dist/rules/exclusive-min.d.ts +2 -0
- package/dist/rules/exclusive-min.d.ts.map +1 -0
- package/dist/rules/format.d.ts +2 -0
- package/dist/rules/format.d.ts.map +1 -0
- package/dist/rules/index.d.ts +28 -0
- package/dist/rules/index.d.ts.map +1 -0
- package/dist/rules/max-items.d.ts +2 -0
- package/dist/rules/max-items.d.ts.map +1 -0
- package/dist/rules/max-length.d.ts +2 -0
- package/dist/rules/max-length.d.ts.map +1 -0
- package/dist/rules/max.d.ts +2 -0
- package/dist/rules/max.d.ts.map +1 -0
- package/dist/rules/min-items.d.ts +2 -0
- package/dist/rules/min-items.d.ts.map +1 -0
- package/dist/rules/min-length.d.ts +2 -0
- package/dist/rules/min-length.d.ts.map +1 -0
- package/dist/rules/min.d.ts +2 -0
- package/dist/rules/min.d.ts.map +1 -0
- package/dist/rules/multiple-of.d.ts +2 -0
- package/dist/rules/multiple-of.d.ts.map +1 -0
- package/dist/rules/not-contains.d.ts +2 -0
- package/dist/rules/not-contains.d.ts.map +1 -0
- package/dist/rules/pattern.d.ts +2 -0
- package/dist/rules/pattern.d.ts.map +1 -0
- package/dist/rules/rule.d.ts +55 -0
- package/dist/rules/rule.d.ts.map +1 -0
- package/dist/rules/starts-with.d.ts +2 -0
- package/dist/rules/starts-with.d.ts.map +1 -0
- package/dist/typedefs-command.d.ts +10 -0
- package/dist/typedefs-command.d.ts.map +1 -0
- package/dist/validated.d.ts +22 -0
- package/dist/validated.d.ts.map +1 -0
- package/dist/with-validation.d.ts +26 -0
- package/dist/with-validation.d.ts.map +1 -0
- package/docs/README.md +15 -0
- package/docs/guide/constraints.md +291 -0
- package/docs/guide/errors.md +194 -0
- package/docs/roadmap.md +31 -0
- package/docs/troubleshooting.md +260 -0
- package/graphql/constraint.graphqls +20 -0
- package/package.json +73 -5
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/builder/constraints.ts", "../src/builder/check-constraints.ts", "../src/builder/args-schema.ts", "../src/builder/input-schema.ts", "../src/builder/leaf.ts"],
|
|
4
|
+
"sourcesContent": [
|
|
5
|
+
"import {\n\ttype DirectiveNode,\n\ttype GraphQLDirective,\n\tgetDirectiveValues,\n} from 'graphql';\nimport { type RegisteredRule, rules } from '../rules';\n\n/** One `@constraint` argument written on an argument or an input field. */\nexport interface Constraint {\n\treadonly rule: RegisteredRule;\n\treadonly value: string | number;\n}\n\n/**\n * The constraints written on a node, base rules (`format`) first so the\n * others narrow the schema they produce. Empty when the schema declares no\n * `@constraint` or the node carries none.\n */\nexport function constraintsOn(\n\tdirective: GraphQLDirective | undefined,\n\tnode:\n\t\t| { readonly directives?: ReadonlyArray<DirectiveNode> | undefined }\n\t\t| null\n\t\t| undefined,\n): Constraint[] {\n\tif (!directive || !node) return [];\n\tconst values =\n\t\tgetDirectiveValues(directive, { directives: node.directives ?? [] }) ?? {};\n\treturn Object.entries(values)\n\t\t.filter(([, value]) => value !== undefined && value !== null)\n\t\t.map(([argument, value]) => ({\n\t\t\trule: rules[argument as keyof typeof rules],\n\t\t\tvalue: value as string | number,\n\t\t}))\n\t\t.sort(\n\t\t\t(a, b) => Number(Boolean(b.rule.base)) - Number(Boolean(a.rule.base)),\n\t\t);\n}\n",
|
|
6
|
+
"import {\n\ttype GraphQLDirective,\n\ttype GraphQLField,\n\ttype GraphQLObjectType,\n\ttype GraphQLSchema,\n\tisInterfaceType,\n\tisObjectType,\n} from 'graphql';\nimport type { z } from 'zod';\nimport { assertOwnConstraint } from '../constraint-directive';\nimport { argsSchemaOf, assertInterfaceConstraintsKept } from './args-schema';\nimport { constraintsOn } from './constraints';\nimport { InputSchemas } from './input-schema';\n\n/**\n * Runs every check `withValidation` makes at startup, so a code generator\n * refuses the same schemas: the declaration is `constraintTypeDefs`', every\n * constraint applies where it is written (in every input type), none sits on\n * a directive's argument, objects keep their interfaces' constraints, and no\n * default value breaks its own. `onArgs` receives each object field's\n * arguments schema, for a field with something to check.\n *\n * Returns the `@constraint` directive, or `undefined` when the schema\n * declares none (and so carries no constraint).\n */\nexport function checkConstraints(\n\tschema: GraphQLSchema,\n\tonArgs?: (\n\t\ttype: GraphQLObjectType,\n\t\tfield: GraphQLField<unknown, unknown>,\n\t\targs: z.ZodType,\n\t) => void,\n): GraphQLDirective | undefined {\n\tconst inputs = new InputSchemas(schema);\n\tif (!inputs.directive) return undefined;\n\tassertOwnConstraint(inputs.directive);\n\tinputs.buildAll(schema);\n\tassertNoDirectiveArgumentConstrained(inputs, schema);\n\tfor (const type of Object.values(schema.getTypeMap())) {\n\t\tif (type.name.startsWith('__')) continue;\n\t\t// An interface's fields resolve on its objects: built for their errors only.\n\t\tif (isInterfaceType(type)) {\n\t\t\tfor (const field of Object.values(type.getFields()))\n\t\t\t\targsSchemaOf(inputs, type.name, field);\n\t\t}\n\t\tif (!isObjectType(type)) continue;\n\t\tassertInterfaceConstraintsKept(inputs, type);\n\t\tfor (const field of Object.values(type.getFields()) as GraphQLField<\n\t\t\tunknown,\n\t\t\tunknown\n\t\t>[]) {\n\t\t\tconst args = argsSchemaOf(inputs, type.name, field);\n\t\t\tif (args) onArgs?.(type, field, args);\n\t\t}\n\t}\n\tinputs.checkDefaults();\n\treturn inputs.directive;\n}\n\n/**\n * Fails on a `@constraint` written on a directive's argument: graphql allows\n * it there (ARGUMENT_DEFINITION), but no resolver receives that argument, so\n * it would check nothing.\n */\nfunction assertNoDirectiveArgumentConstrained(\n\tinputs: InputSchemas,\n\tschema: GraphQLSchema,\n): void {\n\tfor (const directive of schema.getDirectives()) {\n\t\tfor (const arg of directive.args) {\n\t\t\tif (constraintsOn(inputs.directive, arg.astNode).length > 0) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`@constraint on @${directive.name}(${arg.name}:) checks nothing: a directive's argument reaches no resolver. Remove it.`,\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t}\n}\n",
|
|
7
|
+
"import type { GraphQLArgument, GraphQLField, GraphQLObjectType } from 'graphql';\nimport { z } from 'zod';\nimport { constraintsOn } from './constraints';\nimport type { InputSchemas } from './input-schema';\n\n/**\n * The schema of a field's arguments, or `undefined` when none of them can\n * break a constraint, so a field with nothing to check is left untouched.\n * `parent` is the type's name, for errors: `Mutation.signUp(input:)`.\n */\nexport function argsSchemaOf(\n\tinputs: InputSchemas,\n\tparent: string,\n\tfield: GraphQLField<unknown, unknown>,\n): z.ZodType | undefined {\n\tif (!field.args.some((arg) => inputs.needsCheck(arg))) return undefined;\n\treturn z.object(\n\t\tObject.fromEntries(\n\t\t\tfield.args.map((arg) => [\n\t\t\t\targ.name,\n\t\t\t\tinputs.of(arg, `${parent}.${field.name}(${arg.name}:)`),\n\t\t\t]),\n\t\t),\n\t);\n}\n\n/** The constraints of an argument as written, sorted: `maxLength: 5, min: 1`. */\nfunction signature(inputs: InputSchemas, arg: GraphQLArgument): string {\n\treturn constraintsOn(inputs.directive, arg.astNode)\n\t\t.map(({ rule, value }) => `${rule.argument}: ${JSON.stringify(value)}`)\n\t\t.sort()\n\t\t.join(', ');\n}\n\n/** A signature as it reads in an error. */\nconst written = (signature: string) =>\n\tsignature ? `@constraint(${signature})` : 'no @constraint';\n\n/**\n * Fails when an object type's field does not repeat the `@constraint` its\n * interface writes on an argument. The object's field is the one that\n * resolves, and graphql lets it redeclare the argument bare: the interface's\n * constraint would then check nothing, in silence.\n */\nexport function assertInterfaceConstraintsKept(\n\tinputs: InputSchemas,\n\ttype: GraphQLObjectType,\n): void {\n\tfor (const contract of type.getInterfaces()) {\n\t\tfor (const [name, declared] of Object.entries(contract.getFields())) {\n\t\t\tconst field = type.getFields()[name];\n\t\t\tfor (const arg of declared.args) {\n\t\t\t\tconst wanted = signature(inputs, arg);\n\t\t\t\tif (!wanted) continue;\n\t\t\t\tconst own = field?.args.find(\n\t\t\t\t\t({ name: argName }) => argName === arg.name,\n\t\t\t\t);\n\t\t\t\tconst found = own ? signature(inputs, own) : wanted;\n\t\t\t\tif (found !== wanted) {\n\t\t\t\t\tconst verb = found ? 'differs on' : 'is not repeated on';\n\t\t\t\t\tthrow new Error(\n\t\t\t\t\t\t`@constraint on ${contract.name}.${name}(${arg.name}:) ${verb} ${type.name}.${name}(${arg.name}:), which resolves it (${written(wanted)} there, ${written(found)} here). Write the same @constraint on both.`,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n",
|
|
8
|
+
"import {\n\ttype ConstValueNode,\n\ttype GraphQLArgument,\n\ttype GraphQLDirective,\n\ttype GraphQLInputField,\n\ttype GraphQLInputObjectType,\n\ttype GraphQLInputType,\n\ttype GraphQLSchema,\n\tgetNamedType,\n\tisInputObjectType,\n\tisListType,\n\tisNonNullType,\n\tvalueFromAST,\n} from 'graphql';\nimport { z } from 'zod';\nimport { applyRule } from '../rules';\nimport { type Constraint, constraintsOn } from './constraints';\nimport { assertObjectTargets, leafSchema } from './leaf';\n\n/**\n * Builds the schemas of the arguments and input fields of one GraphQL\n * schema, once each: an input type is walked a single time however many\n * fields use it, and a recursive one (`input Filter { and: [Filter!] }`) is\n * reached through `z.lazy`.\n */\nexport class InputSchemas {\n\treadonly directive: GraphQLDirective | undefined;\n\treadonly #objects = new Map<string, z.ZodType>();\n\treadonly #defaults: {\n\t\tschema: z.ZodType;\n\t\tliteral: ConstValueNode;\n\t\ttype: GraphQLInputType;\n\t\twhere: string;\n\t}[] = [];\n\t/** The input types a value can break a constraint inside. */\n\treadonly #constrained = new Set<string>();\n\n\tconstructor(schema: GraphQLSchema) {\n\t\tthis.directive = schema.getDirective('constraint') ?? undefined;\n\t\tconst objects = Object.values(schema.getTypeMap()).filter(\n\t\t\tisInputObjectType,\n\t\t);\n\t\t// A fixpoint, not a memoised walk: in a cycle, a type visited while it\n\t\t// is still being decided would be cached as unconstrained for good.\n\t\tfor (let changed = true; changed; ) {\n\t\t\tchanged = false;\n\t\t\tfor (const type of objects) {\n\t\t\t\tif (this.#constrained.has(type.name)) continue;\n\t\t\t\tconst constrained = Object.values(type.getFields()).some(\n\t\t\t\t\t(field) =>\n\t\t\t\t\t\tconstraintsOn(this.directive, field.astNode).length > 0 ||\n\t\t\t\t\t\tthis.isConstrained(field.type),\n\t\t\t\t);\n\t\t\t\tif (constrained) {\n\t\t\t\t\tthis.#constrained.add(type.name);\n\t\t\t\t\tchanged = true;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t/** Whether a value of this type can break a constraint anywhere inside. */\n\tisConstrained(type: GraphQLInputType): boolean {\n\t\tconst named = getNamedType(type);\n\t\treturn isInputObjectType(named) && this.#constrained.has(named.name);\n\t}\n\n\t/**\n\t * Builds every constrained input type, reached by an argument or not, so\n\t * a constraint that cannot apply fails at startup wherever it is written.\n\t */\n\tbuildAll(schema: GraphQLSchema): void {\n\t\tfor (const type of Object.values(schema.getTypeMap())) {\n\t\t\tif (isInputObjectType(type)) this.#object(type);\n\t\t}\n\t}\n\n\t/** Whether an argument or input field needs checking at all. */\n\tneedsCheck(input: GraphQLArgument | GraphQLInputField): boolean {\n\t\treturn (\n\t\t\tconstraintsOn(this.directive, input.astNode).length > 0 ||\n\t\t\tthis.isConstrained(input.type)\n\t\t);\n\t}\n\n\t/** The schema of one argument or input field, `where` naming it in errors. */\n\tof(input: GraphQLArgument | GraphQLInputField, where: string): z.ZodType {\n\t\tconst schema = this.#typed(\n\t\t\tinput.type,\n\t\t\tconstraintsOn(this.directive, input.astNode),\n\t\t\twhere,\n\t\t);\n\t\tconst literal = input.astNode?.defaultValue;\n\t\tif (literal)\n\t\t\tthis.#defaults.push({ schema, literal, type: input.type, where });\n\t\treturn schema;\n\t}\n\n\t/**\n\t * Fails when a default value breaks its own constraint: every request that\n\t * leaves the value out would be refused for something the client never\n\t * sent. Run once every schema is built, so a recursive type is complete.\n\t */\n\tcheckDefaults(): void {\n\t\tfor (const { schema, literal, type, where } of this.#defaults.splice(0)) {\n\t\t\t// Coerced as graphql coerces it: a single value for a list, an input\n\t\t\t// object's own field defaults. One graphql refuses is its to report.\n\t\t\tconst value = valueFromAST(literal, type);\n\t\t\tif (value === undefined) continue;\n\t\t\tconst result = schema.safeParse(value);\n\t\t\tif (!result.success) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`The default value of ${where} breaks its @constraint: ${result.error.issues[0]?.message}`,\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t}\n\n\t#typed(\n\t\ttype: GraphQLInputType,\n\t\tconstraints: readonly Constraint[],\n\t\twhere: string,\n\t): z.ZodType {\n\t\tif (isNonNullType(type))\n\t\t\treturn this.#required(type.ofType, constraints, where);\n\t\treturn this.#required(type, constraints, where).nullish();\n\t}\n\n\t#required(\n\t\ttype: GraphQLInputType,\n\t\tconstraints: readonly Constraint[],\n\t\twhere: string,\n\t): z.ZodType {\n\t\tif (isNonNullType(type))\n\t\t\treturn this.#required(type.ofType, constraints, where);\n\t\tif (isListType(type)) {\n\t\t\t// minItems and maxItems are the list's; every other rule is its items'.\n\t\t\tconst own = constraints.filter(({ rule }) => rule.target === 'list');\n\t\t\tconst items = constraints.filter(({ rule }) => rule.target !== 'list');\n\t\t\tlet list: z.ZodType = z.array(this.#typed(type.ofType, items, where));\n\t\t\tfor (const { rule, value } of own) list = applyRule(rule, list, value);\n\t\t\treturn list;\n\t\t}\n\t\tif (isInputObjectType(type)) {\n\t\t\tassertObjectTargets(type.name, constraints, where);\n\t\t\treturn this.#object(type);\n\t\t}\n\t\treturn leafSchema(type, constraints, where);\n\t}\n\n\t#object(type: GraphQLInputObjectType): z.ZodType {\n\t\tif (!this.isConstrained(type)) return z.unknown();\n\t\tconst built = this.#objects.get(type.name);\n\t\tif (built) return built;\n\t\t// Registered before the walk, so a field that comes back here ends.\n\t\tlet schema: z.ZodType | undefined;\n\t\tthis.#objects.set(\n\t\t\ttype.name,\n\t\t\tz.lazy(() => schema as z.ZodType),\n\t\t);\n\t\tschema = z.object(\n\t\t\tObject.fromEntries(\n\t\t\t\tObject.values(type.getFields()).map((field) => [\n\t\t\t\t\tfield.name,\n\t\t\t\t\tthis.of(field, `${type.name}.${field.name}`),\n\t\t\t\t]),\n\t\t\t),\n\t\t);\n\t\tthis.#objects.set(type.name, schema);\n\t\treturn schema;\n\t}\n}\n",
|
|
9
|
+
"import {\n\tGraphQLBoolean,\n\tGraphQLFloat,\n\tGraphQLID,\n\tGraphQLInt,\n\ttype GraphQLLeafType,\n\tGraphQLString,\n} from 'graphql';\nimport { z } from 'zod';\nimport { applyRule } from '../rules';\nimport type { Target } from '../rules/rule';\nimport type { Constraint } from './constraints';\n\n/** The kind of value each built-in scalar holds once graphql parsed it. */\nconst kinds: Record<string, Exclude<Target, 'list'>> = {\n\t[GraphQLString.name]: 'string',\n\t[GraphQLID.name]: 'string',\n\t[GraphQLInt.name]: 'number',\n\t[GraphQLFloat.name]: 'number',\n};\n\nfunction baseOf(type: GraphQLLeafType): z.ZodType {\n\tif (type.name === GraphQLInt.name) return z.number().int();\n\treturn kinds[type.name] === 'number' ? z.number() : z.string();\n}\n\n/**\n * Fails unless every constraint applies to a value of `type`: `minLength` on\n * an `Int`, anything on a custom scalar or an enum, fails, naming `where`.\n * The runtime and the code generator refuse the same schemas with the same\n * message.\n */\nexport function assertLeafTargets(\n\ttype: GraphQLLeafType,\n\tconstraints: readonly Constraint[],\n\twhere: string,\n): void {\n\tconst misplaced = constraints.find(({ rule }) => rule.target === 'list');\n\tif (misplaced) throw misplacedOn(misplaced, where, type.name);\n\tconst kind = kinds[type.name];\n\tconst wrong = constraints.find(({ rule }) => rule.target !== kind);\n\tif (wrong) throw misplacedOn(wrong, where, type.name);\n}\n\n/** Fails when an input object carries a constraint: none applies to it. */\nexport function assertObjectTargets(\n\ttypeName: string,\n\tconstraints: readonly Constraint[],\n\twhere: string,\n): void {\n\tconst first =\n\t\tconstraints.find(({ rule }) => rule.target === 'list') ?? constraints[0];\n\tif (first) throw misplacedOn(first, where, typeName);\n}\n\nfunction misplacedOn(\n\t{ rule }: Constraint,\n\twhere: string,\n\ttypeName: string,\n): Error {\n\treturn new Error(\n\t\t`@constraint(${rule.argument}) on ${where} needs ${describeTarget(rule.target)}, not ${typeName}.`,\n\t);\n}\n\n/**\n * The schema of a scalar or enum value with its constraints applied. A leaf\n * without any is left as graphql parsed it. A constraint that cannot apply\n * fails here, at startup.\n */\nexport function leafSchema(\n\ttype: GraphQLLeafType,\n\tconstraints: readonly Constraint[],\n\twhere: string,\n): z.ZodType {\n\tif (constraints.length === 0) return z.unknown();\n\tassertLeafTargets(type, constraints, where);\n\tlet schema = baseOf(type);\n\tfor (const { rule, value } of constraints) {\n\t\tschema = applyRule(rule, schema, value);\n\t}\n\treturn schema;\n}\n\n/** The source of a built-in scalar's schema, before its constraints. */\nexport function baseCode(type: GraphQLLeafType): string | undefined {\n\t// graphql's Int is 32-bit: the client refuses what it refuses.\n\tif (type.name === GraphQLInt.name) return 'z.int32()';\n\tif (type.name === GraphQLFloat.name) return 'z.number()';\n\tif (type.name === GraphQLBoolean.name) return 'z.boolean()';\n\treturn kinds[type.name] === 'string' ? 'z.string()' : undefined;\n}\n\n/** What a rule's target reads as in an error. */\nexport function describeTarget(target: Target): string {\n\tif (target === 'string') return 'a String or an ID';\n\tif (target === 'number') return 'an Int or a Float';\n\treturn 'a list';\n}\n"
|
|
10
|
+
],
|
|
11
|
+
"mappings": "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",
|
|
12
|
+
"debugId": "7C317F3D691975BE64756E2164756E21",
|
|
13
|
+
"names": []
|
|
14
|
+
}
|
package/dist/cli.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":""}
|
package/dist/cli.js
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
constraintTypeDefs2
|
|
4
|
+
} from "./chunks/index-rbxe5m3y.js";
|
|
5
|
+
|
|
6
|
+
// src/typedefs-command.ts
|
|
7
|
+
import { mkdir, writeFile } from "node:fs/promises";
|
|
8
|
+
import { dirname } from "node:path";
|
|
9
|
+
var DEFAULT_OUT = "generated/graphql/constraint.graphqls";
|
|
10
|
+
var usage = `Usage: nxgt-graphql-validation typedefs [--out [<file>]]
|
|
11
|
+
|
|
12
|
+
typedefs print the SDL of @constraint
|
|
13
|
+
--out [<file>] write it to <file> instead, creating its folder
|
|
14
|
+
(default: ${DEFAULT_OUT})
|
|
15
|
+
--help, -h show this help
|
|
16
|
+
`;
|
|
17
|
+
function graphqls() {
|
|
18
|
+
return `# The @constraint directive of @nxgt/graphql-validation, for your IDE.
|
|
19
|
+
# Generated by \`nxgt-graphql-validation typedefs\`: do not edit.
|
|
20
|
+
|
|
21
|
+
${constraintTypeDefs2}`;
|
|
22
|
+
}
|
|
23
|
+
function refuse(message) {
|
|
24
|
+
process.stderr.write(`${message}
|
|
25
|
+
|
|
26
|
+
${usage}`);
|
|
27
|
+
return 2;
|
|
28
|
+
}
|
|
29
|
+
async function main(argv) {
|
|
30
|
+
if (argv.includes("--help") || argv.includes("-h")) {
|
|
31
|
+
process.stdout.write(usage);
|
|
32
|
+
return 0;
|
|
33
|
+
}
|
|
34
|
+
const [command, ...rest] = argv;
|
|
35
|
+
if (command === undefined)
|
|
36
|
+
return refuse("No command.");
|
|
37
|
+
if (command !== "typedefs")
|
|
38
|
+
return refuse(`Unknown command "${command}".`);
|
|
39
|
+
if (rest.length === 0) {
|
|
40
|
+
process.stdout.write(graphqls());
|
|
41
|
+
return 0;
|
|
42
|
+
}
|
|
43
|
+
const [flag, file, ...extra] = rest;
|
|
44
|
+
if (flag !== "--out" || extra.length > 0 || file === "" || file?.startsWith("-")) {
|
|
45
|
+
return refuse(`Unexpected arguments: ${rest.join(" ")}`);
|
|
46
|
+
}
|
|
47
|
+
const out = file ?? DEFAULT_OUT;
|
|
48
|
+
try {
|
|
49
|
+
await mkdir(dirname(out), { recursive: true });
|
|
50
|
+
await writeFile(out, graphqls());
|
|
51
|
+
} catch (error) {
|
|
52
|
+
process.stderr.write(`typedefs failed: ${error.message}
|
|
53
|
+
`);
|
|
54
|
+
return 1;
|
|
55
|
+
}
|
|
56
|
+
process.stdout.write(`Wrote ${out}
|
|
57
|
+
`);
|
|
58
|
+
return 0;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// src/cli.ts
|
|
62
|
+
process.stdout.on("error", (error) => {
|
|
63
|
+
if (error.code !== "EPIPE")
|
|
64
|
+
throw error;
|
|
65
|
+
});
|
|
66
|
+
process.exitCode = await main(process.argv.slice(2));
|
|
67
|
+
|
|
68
|
+
//# debugId=675FAE0D80D1DEF264756E2164756E21
|
|
69
|
+
//# sourceMappingURL=cli.js.map
|
package/dist/cli.js.map
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/typedefs-command.ts", "../src/cli.ts"],
|
|
4
|
+
"sourcesContent": [
|
|
5
|
+
"// The `typedefs` command of the bin, apart from `cli.ts` so a spec can run\n// it without spawning a process. Its flags read as `nxgt-graphql-scalars\n// typedefs`' do, so both bins are used the same way.\nimport { mkdir, writeFile } from 'node:fs/promises';\nimport { dirname } from 'node:path';\nimport { constraintTypeDefs } from './constraint-directive';\n\n/** Where `--out` writes when it is given no file. */\nexport const DEFAULT_OUT = 'generated/graphql/constraint.graphqls';\n\nconst usage = `Usage: nxgt-graphql-validation typedefs [--out [<file>]]\n\n typedefs print the SDL of @constraint\n --out [<file>] write it to <file> instead, creating its folder\n (default: ${DEFAULT_OUT})\n --help, -h show this help\n`;\n\n/** The `.graphqls` file's content: a header, then the SDL. */\nexport function graphqls(): string {\n\treturn `# The @constraint directive of @nxgt/graphql-validation, for your IDE.\\n# Generated by \\`nxgt-graphql-validation typedefs\\`: do not edit.\\n\\n${constraintTypeDefs}`;\n}\n\nfunction refuse(message: string): number {\n\tprocess.stderr.write(`${message}\\n\\n${usage}`);\n\treturn 2;\n}\n\n/**\n * Runs the bin on `argv`: 0 done, 1 the file could not be written, 2 a usage\n * error (nxgt-mongo-backup's convention). `--help` anywhere wins.\n */\nexport async function main(argv: readonly string[]): Promise<number> {\n\tif (argv.includes('--help') || argv.includes('-h')) {\n\t\tprocess.stdout.write(usage);\n\t\treturn 0;\n\t}\n\tconst [command, ...rest] = argv;\n\tif (command === undefined) return refuse('No command.');\n\tif (command !== 'typedefs') return refuse(`Unknown command \"${command}\".`);\n\tif (rest.length === 0) {\n\t\tprocess.stdout.write(graphqls());\n\t\treturn 0;\n\t}\n\tconst [flag, file, ...extra] = rest;\n\tif (\n\t\tflag !== '--out' ||\n\t\textra.length > 0 ||\n\t\tfile === '' ||\n\t\tfile?.startsWith('-')\n\t) {\n\t\treturn refuse(`Unexpected arguments: ${rest.join(' ')}`);\n\t}\n\tconst out = file ?? DEFAULT_OUT;\n\ttry {\n\t\tawait mkdir(dirname(out), { recursive: true });\n\t\tawait writeFile(out, graphqls());\n\t} catch (error) {\n\t\tprocess.stderr.write(`typedefs failed: ${(error as Error).message}\\n`);\n\t\treturn 1;\n\t}\n\tprocess.stdout.write(`Wrote ${out}\\n`);\n\treturn 0;\n}\n",
|
|
6
|
+
"#!/usr/bin/env node\n/**\n * The `nxgt-graphql-validation` bin. One command, `typedefs`: the SDL of\n * `@constraint` — what `constraintTypeDefs` holds — on standard output or\n * into a file, so an IDE's GraphQL plugin knows the directive without a\n * running server. It reads nothing and writes only the file it is given.\n */\nimport { main } from './typedefs-command';\n\n// A reader that closed the pipe (`| head`) wants no more: not an error.\nprocess.stdout.on('error', (error: NodeJS.ErrnoException) => {\n\tif (error.code !== 'EPIPE') throw error;\n});\n\nprocess.exitCode = await main(process.argv.slice(2));\n"
|
|
7
|
+
],
|
|
8
|
+
"mappings": ";;;;;;AAGA;AACA;AAIO,IAAM,cAAc;AAE3B,IAAM,QAAQ;AAAA;AAAA;AAAA;AAAA,iCAImB;AAAA;AAAA;AAK1B,SAAS,QAAQ,GAAW;AAAA,EAClC,OAAO;AAAA;AAAA;AAAA,EAAgJ;AAAA;AAGxJ,SAAS,MAAM,CAAC,SAAyB;AAAA,EACxC,QAAQ,OAAO,MAAM,GAAG;AAAA;AAAA,EAAc,OAAO;AAAA,EAC7C,OAAO;AAAA;AAOR,eAAsB,IAAI,CAAC,MAA0C;AAAA,EACpE,IAAI,KAAK,SAAS,QAAQ,KAAK,KAAK,SAAS,IAAI,GAAG;AAAA,IACnD,QAAQ,OAAO,MAAM,KAAK;AAAA,IAC1B,OAAO;AAAA,EACR;AAAA,EACA,OAAO,YAAY,QAAQ;AAAA,EAC3B,IAAI,YAAY;AAAA,IAAW,OAAO,OAAO,aAAa;AAAA,EACtD,IAAI,YAAY;AAAA,IAAY,OAAO,OAAO,oBAAoB,WAAW;AAAA,EACzE,IAAI,KAAK,WAAW,GAAG;AAAA,IACtB,QAAQ,OAAO,MAAM,SAAS,CAAC;AAAA,IAC/B,OAAO;AAAA,EACR;AAAA,EACA,OAAO,MAAM,SAAS,SAAS;AAAA,EAC/B,IACC,SAAS,WACT,MAAM,SAAS,KACf,SAAS,MACT,MAAM,WAAW,GAAG,GACnB;AAAA,IACD,OAAO,OAAO,yBAAyB,KAAK,KAAK,GAAG,GAAG;AAAA,EACxD;AAAA,EACA,MAAM,MAAM,QAAQ;AAAA,EACpB,IAAI;AAAA,IACH,MAAM,MAAM,QAAQ,GAAG,GAAG,EAAE,WAAW,KAAK,CAAC;AAAA,IAC7C,MAAM,UAAU,KAAK,SAAS,CAAC;AAAA,IAC9B,OAAO,OAAO;AAAA,IACf,QAAQ,OAAO,MAAM,oBAAqB,MAAgB;AAAA,CAAW;AAAA,IACrE,OAAO;AAAA;AAAA,EAER,QAAQ,OAAO,MAAM,SAAS;AAAA,CAAO;AAAA,EACrC,OAAO;AAAA;;;ACpDR,QAAQ,OAAO,GAAG,SAAS,CAAC,UAAiC;AAAA,EAC5D,IAAI,MAAM,SAAS;AAAA,IAAS,MAAM;AAAA,CAClC;AAED,QAAQ,WAAW,MAAM,KAAK,QAAQ,KAAK,MAAM,CAAC,CAAC;",
|
|
9
|
+
"debugId": "675FAE0D80D1DEF264756E2164756E21",
|
|
10
|
+
"names": []
|
|
11
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"codegen.d.ts","sourceRoot":"","sources":["../src/codegen.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,gBAAgB,EAAE,MAAM,6BAA6B,CAAC;AAC/D,OAAO,EAAE,KAAK,UAAU,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AACvE,OAAO,EAAE,KAAK,gBAAgB,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC"}
|
package/dist/codegen.js
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import {
|
|
2
|
+
applyRuleCode
|
|
3
|
+
} from "./chunks/index-rbxe5m3y.js";
|
|
4
|
+
import {
|
|
5
|
+
constraintsOn2,
|
|
6
|
+
assertLeafTargets,
|
|
7
|
+
assertObjectTargets,
|
|
8
|
+
baseCode,
|
|
9
|
+
checkConstraints2
|
|
10
|
+
} from "./chunks/index-s1kkmg35.js";
|
|
11
|
+
// src/builder/input-code.ts
|
|
12
|
+
import {
|
|
13
|
+
getNamedType,
|
|
14
|
+
isInputObjectType,
|
|
15
|
+
isListType,
|
|
16
|
+
isNonNullType
|
|
17
|
+
} from "graphql";
|
|
18
|
+
function inputCode(type, constraints, where, named, options = {}) {
|
|
19
|
+
const write = { where, named, options };
|
|
20
|
+
return typedCode(write, type, constraints);
|
|
21
|
+
}
|
|
22
|
+
function typedCode(write, type, constraints) {
|
|
23
|
+
if (isNonNullType(type))
|
|
24
|
+
return requiredCode(write, type.ofType, constraints);
|
|
25
|
+
return `${requiredCode(write, type, constraints)}.nullish()`;
|
|
26
|
+
}
|
|
27
|
+
function requiredCode(write, type, constraints) {
|
|
28
|
+
if (isNonNullType(type))
|
|
29
|
+
return requiredCode(write, type.ofType, constraints);
|
|
30
|
+
if (isListType(type)) {
|
|
31
|
+
const own = constraints.filter(({ rule }) => rule.target === "list");
|
|
32
|
+
const items = constraints.filter(({ rule }) => rule.target !== "list");
|
|
33
|
+
const code = applyCode(`z.array(${typedCode(write, type.ofType, items)})`, own);
|
|
34
|
+
if (!write.options.list)
|
|
35
|
+
return code;
|
|
36
|
+
const single = requiredCode(write, getNamedType(type), []);
|
|
37
|
+
return write.options.list({ type, code, single });
|
|
38
|
+
}
|
|
39
|
+
if (isInputObjectType(type)) {
|
|
40
|
+
assertObjectTargets(type.name, constraints, write.where);
|
|
41
|
+
return write.named(type);
|
|
42
|
+
}
|
|
43
|
+
assertLeafTargets(type, constraints, write.where);
|
|
44
|
+
return applyCode(baseCode(type) ?? write.named(type), constraints);
|
|
45
|
+
}
|
|
46
|
+
function applyCode(source, constraints) {
|
|
47
|
+
return constraints.reduce((code, { rule, value }) => applyRuleCode(rule, code, value), source);
|
|
48
|
+
}
|
|
49
|
+
export {
|
|
50
|
+
checkConstraints2 as checkConstraints,
|
|
51
|
+
constraintsOn2 as constraintsOn,
|
|
52
|
+
inputCode
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
//# debugId=CF7DECB6FA09613D64756E2164756E21
|
|
56
|
+
//# sourceMappingURL=codegen.js.map
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/builder/input-code.ts"],
|
|
4
|
+
"sourcesContent": [
|
|
5
|
+
"import {\n\ttype GraphQLInputType,\n\ttype GraphQLList,\n\ttype GraphQLNamedInputType,\n\tgetNamedType,\n\tisInputObjectType,\n\tisListType,\n\tisNonNullType,\n} from 'graphql';\nimport { applyRuleCode } from '../rules';\nimport type { Constraint } from './constraints';\nimport { assertLeafTargets, assertObjectTargets, baseCode } from './leaf';\n\n/**\n * The source of the schema of one argument or input field: what\n * `InputSchemas` builds at startup, written for a code generator. The\n * source names zod as a free `z`.\n *\n * The runtime leaves what graphql already checked to graphql; generated\n * code checks every value, so each named type comes from `named`: an input\n * object's or an enum's schema by name, a custom scalar's from a mapping.\n * The built-in scalars are written here, with their constraints. A\n * constraint that cannot apply fails as it does at startup.\n */\nexport function inputCode(\n\ttype: GraphQLInputType,\n\tconstraints: readonly Constraint[],\n\twhere: string,\n\tnamed: (type: GraphQLNamedInputType) => string,\n\toptions: InputCodeOptions = {},\n): string {\n\tconst write: Write = { where, named, options };\n\treturn typedCode(write, type, constraints);\n}\n\n/** How `inputCode` writes what a generator may want written otherwise. */\nexport interface InputCodeOptions {\n\t/**\n\t * A list's schema, given its `type`, `code` (the array with every rule)\n\t * and `single` (the list's named type, non-null, without rules): to\n\t * take a single value for a list, as graphql does, `single` wrapped then\n\t * piped into `code`. Default: `code`.\n\t */\n\treadonly list?: (list: {\n\t\treadonly type: GraphQLList<GraphQLInputType>;\n\t\treadonly code: string;\n\t\treadonly single: string;\n\t}) => string;\n}\n\ninterface Write {\n\treadonly where: string;\n\treadonly named: (type: GraphQLNamedInputType) => string;\n\treadonly options: InputCodeOptions;\n}\n\nfunction typedCode(\n\twrite: Write,\n\ttype: GraphQLInputType,\n\tconstraints: readonly Constraint[],\n): string {\n\tif (isNonNullType(type)) return requiredCode(write, type.ofType, constraints);\n\treturn `${requiredCode(write, type, constraints)}.nullish()`;\n}\n\nfunction requiredCode(\n\twrite: Write,\n\ttype: GraphQLInputType,\n\tconstraints: readonly Constraint[],\n): string {\n\tif (isNonNullType(type)) return requiredCode(write, type.ofType, constraints);\n\tif (isListType(type)) {\n\t\t// minItems and maxItems are the list's; every other rule is its items'.\n\t\tconst own = constraints.filter(({ rule }) => rule.target === 'list');\n\t\tconst items = constraints.filter(({ rule }) => rule.target !== 'list');\n\t\tconst code = applyCode(\n\t\t\t`z.array(${typedCode(write, type.ofType, items)})`,\n\t\t\town,\n\t\t);\n\t\tif (!write.options.list) return code;\n\t\tconst single = requiredCode(write, getNamedType(type), []);\n\t\treturn write.options.list({ type, code, single });\n\t}\n\tif (isInputObjectType(type)) {\n\t\tassertObjectTargets(type.name, constraints, write.where);\n\t\treturn write.named(type);\n\t}\n\tassertLeafTargets(type, constraints, write.where);\n\treturn applyCode(baseCode(type) ?? write.named(type), constraints);\n}\n\nfunction applyCode(source: string, constraints: readonly Constraint[]): string {\n\treturn constraints.reduce(\n\t\t(code, { rule, value }) => applyRuleCode(rule, code, value),\n\t\tsource,\n\t);\n}\n"
|
|
6
|
+
],
|
|
7
|
+
"mappings": ";;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAwBO,SAAS,SAAS,CACxB,MACA,aACA,OACA,OACA,UAA4B,CAAC,GACpB;AAAA,EACT,MAAM,QAAe,EAAE,OAAO,OAAO,QAAQ;AAAA,EAC7C,OAAO,UAAU,OAAO,MAAM,WAAW;AAAA;AAwB1C,SAAS,SAAS,CACjB,OACA,MACA,aACS;AAAA,EACT,IAAI,cAAc,IAAI;AAAA,IAAG,OAAO,aAAa,OAAO,KAAK,QAAQ,WAAW;AAAA,EAC5E,OAAO,GAAG,aAAa,OAAO,MAAM,WAAW;AAAA;AAGhD,SAAS,YAAY,CACpB,OACA,MACA,aACS;AAAA,EACT,IAAI,cAAc,IAAI;AAAA,IAAG,OAAO,aAAa,OAAO,KAAK,QAAQ,WAAW;AAAA,EAC5E,IAAI,WAAW,IAAI,GAAG;AAAA,IAErB,MAAM,MAAM,YAAY,OAAO,GAAG,WAAW,KAAK,WAAW,MAAM;AAAA,IACnE,MAAM,QAAQ,YAAY,OAAO,GAAG,WAAW,KAAK,WAAW,MAAM;AAAA,IACrE,MAAM,OAAO,UACZ,WAAW,UAAU,OAAO,KAAK,QAAQ,KAAK,MAC9C,GACD;AAAA,IACA,IAAI,CAAC,MAAM,QAAQ;AAAA,MAAM,OAAO;AAAA,IAChC,MAAM,SAAS,aAAa,OAAO,aAAa,IAAI,GAAG,CAAC,CAAC;AAAA,IACzD,OAAO,MAAM,QAAQ,KAAK,EAAE,MAAM,MAAM,OAAO,CAAC;AAAA,EACjD;AAAA,EACA,IAAI,kBAAkB,IAAI,GAAG;AAAA,IAC5B,oBAAoB,KAAK,MAAM,aAAa,MAAM,KAAK;AAAA,IACvD,OAAO,MAAM,MAAM,IAAI;AAAA,EACxB;AAAA,EACA,kBAAkB,MAAM,aAAa,MAAM,KAAK;AAAA,EAChD,OAAO,UAAU,SAAS,IAAI,KAAK,MAAM,MAAM,IAAI,GAAG,WAAW;AAAA;AAGlE,SAAS,SAAS,CAAC,QAAgB,aAA4C;AAAA,EAC9E,OAAO,YAAY,OAClB,CAAC,QAAQ,MAAM,YAAY,cAAc,MAAM,MAAM,KAAK,GAC1D,MACD;AAAA;",
|
|
8
|
+
"debugId": "CF7DECB6FA09613D64756E2164756E21",
|
|
9
|
+
"names": []
|
|
10
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { type GraphQLDirective } from 'graphql';
|
|
2
|
+
/**
|
|
3
|
+
* The SDL of `@constraint`, to add to a schema-first server's `typeDefs`,
|
|
4
|
+
* written from the rules: an argument is declared once its rule exists.
|
|
5
|
+
*
|
|
6
|
+
* Its arguments are graphql-constraint-directive's, minus `uniqueTypeName`
|
|
7
|
+
* (a detail of that package's scalar wrapping). It is allowed on arguments
|
|
8
|
+
* and input fields only, where that package also allows output fields: a
|
|
9
|
+
* resolver's result is the output scalars' job, and a directive that checked
|
|
10
|
+
* nothing there would be a promise broken in silence.
|
|
11
|
+
*/
|
|
12
|
+
export declare const constraintTypeDefs: string;
|
|
13
|
+
/**
|
|
14
|
+
* Fails when the schema's `@constraint` is not the one
|
|
15
|
+
* {@link constraintTypeDefs} declares — graphql-constraint-directive's, say,
|
|
16
|
+
* kept from a migration. Its extra locations would be accepted and checked
|
|
17
|
+
* by nothing, its extra arguments mean nothing here, and an argument of
|
|
18
|
+
* another type would reach a rule as the wrong kind of value.
|
|
19
|
+
*/
|
|
20
|
+
export declare function assertOwnConstraint(directive: GraphQLDirective): void;
|
|
21
|
+
//# sourceMappingURL=constraint-directive.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"constraint-directive.d.ts","sourceRoot":"","sources":["../src/constraint-directive.ts"],"names":[],"mappings":"AAAA,OAAO,EAAqB,KAAK,gBAAgB,EAAE,MAAM,SAAS,CAAC;AAGnE;;;;;;;;;GASG;AACH,eAAO,MAAM,kBAAkB,EAAE,MAKhC,CAAC;AAOF;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CAAC,SAAS,EAAE,gBAAgB,GAAG,IAAI,CAwBrE"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"all.d.ts","sourceRoot":"","sources":["../../src/formats/all.ts"],"names":[],"mappings":"AAAA,cAAc,QAAQ,CAAC;AACvB,cAAc,QAAQ,CAAC;AACvB,cAAc,aAAa,CAAC;AAC5B,cAAc,SAAS,CAAC;AACxB,cAAc,QAAQ,CAAC;AACvB,cAAc,QAAQ,CAAC;AACvB,cAAc,OAAO,CAAC;AACtB,cAAc,QAAQ,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"byte.d.ts","sourceRoot":"","sources":["../../src/formats/byte.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,UAAU,mCAIrB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"date-time.d.ts","sourceRoot":"","sources":["../../src/formats/date-time.ts"],"names":[],"mappings":"AAMA,eAAO,MAAM,cAAc,wCAIzB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"date.d.ts","sourceRoot":"","sources":["../../src/formats/date.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,UAAU,mCAIrB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"email.d.ts","sourceRoot":"","sources":["../../src/formats/email.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,WAAW,oCAItB,CAAC"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { z } from 'zod';
|
|
2
|
+
/** A string schema, plain or a format (`z.email()` is not a `z.ZodString`). */
|
|
3
|
+
export type StringSchema = z.ZodString | z.ZodStringFormat;
|
|
4
|
+
/**
|
|
5
|
+
* One value of `@constraint(format: "...")`: the schema it stands for, as a
|
|
6
|
+
* schema at startup and as source for a code generator.
|
|
7
|
+
*/
|
|
8
|
+
export interface Format<N extends string = string> {
|
|
9
|
+
readonly name: N;
|
|
10
|
+
readonly toZod: () => StringSchema;
|
|
11
|
+
readonly toCode: () => string;
|
|
12
|
+
}
|
|
13
|
+
/** Declares a format, with its name kept literal. */
|
|
14
|
+
export declare function defineFormat<const N extends string>(format: Format<N>): Format<N>;
|
|
15
|
+
//# sourceMappingURL=format.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"format.d.ts","sourceRoot":"","sources":["../../src/formats/format.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAE7B,+EAA+E;AAC/E,MAAM,MAAM,YAAY,GAAG,CAAC,CAAC,SAAS,GAAG,CAAC,CAAC,eAAe,CAAC;AAE3D;;;GAGG;AACH,MAAM,WAAW,MAAM,CAAC,CAAC,SAAS,MAAM,GAAG,MAAM;IAChD,QAAQ,CAAC,IAAI,EAAE,CAAC,CAAC;IACjB,QAAQ,CAAC,KAAK,EAAE,MAAM,YAAY,CAAC;IACnC,QAAQ,CAAC,MAAM,EAAE,MAAM,MAAM,CAAC;CAC9B;AAED,qDAAqD;AACrD,wBAAgB,YAAY,CAAC,KAAK,CAAC,CAAC,SAAS,MAAM,EAClD,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC,GACf,MAAM,CAAC,CAAC,CAAC,CAEX"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import * as all from './all';
|
|
2
|
+
import type { Format } from './format';
|
|
3
|
+
/**
|
|
4
|
+
* Every format, keyed by the value `@constraint(format: "...")` takes.
|
|
5
|
+
* Derived from what `./all` exports, so a new format is one file and one
|
|
6
|
+
* line there.
|
|
7
|
+
*/
|
|
8
|
+
export declare const formats: { readonly [N in FormatName]: Format<N>; };
|
|
9
|
+
/** A value `@constraint(format: "...")` takes. */
|
|
10
|
+
export type FormatName = (typeof all)[keyof typeof all]['name'];
|
|
11
|
+
/** The format named `name`, or an error that lists the known ones. */
|
|
12
|
+
export declare function formatNamed(name: string): Format;
|
|
13
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/formats/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,GAAG,MAAM,OAAO,CAAC;AAC7B,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAEvC;;;;GAIG;AACH,eAAO,MAAM,OAAO,EAEf,EAAE,QAAQ,EAAE,CAAC,IAAI,UAAU,GAAG,MAAM,CAAC,CAAC,CAAC,GAAE,CAAC;AAE/C,kDAAkD;AAClD,MAAM,MAAM,UAAU,GAAG,CAAC,OAAO,GAAG,CAAC,CAAC,MAAM,OAAO,GAAG,CAAC,CAAC,MAAM,CAAC,CAAC;AAEhE,sEAAsE;AACtE,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAKhD"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ipv4.d.ts","sourceRoot":"","sources":["../../src/formats/ipv4.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,UAAU,mCAIrB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ipv6.d.ts","sourceRoot":"","sources":["../../src/formats/ipv6.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,UAAU,mCAIrB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"uri.d.ts","sourceRoot":"","sources":["../../src/formats/uri.ts"],"names":[],"mappings":"AAMA,eAAO,MAAM,SAAS,kCAIpB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"uuid.d.ts","sourceRoot":"","sources":["../../src/formats/uuid.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,UAAU,mCAIrB,CAAC"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export { type BadUserInputExtensions, badUserInput, type ValidationIssue, } from './bad-user-input';
|
|
2
|
+
export { constraintTypeDefs } from './constraint-directive';
|
|
3
|
+
export type { ConstraintArgument } from './rules';
|
|
4
|
+
export { type ArgsSchema, type SchemaOf, validated } from './validated';
|
|
5
|
+
export { withValidation } from './with-validation';
|
|
6
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACN,KAAK,sBAAsB,EAC3B,YAAY,EACZ,KAAK,eAAe,GACpB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;AAC5D,YAAY,EAAE,kBAAkB,EAAE,MAAM,SAAS,CAAC;AAClD,OAAO,EAAE,KAAK,UAAU,EAAE,KAAK,QAAQ,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxE,OAAO,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import {
|
|
2
|
+
constraintOf,
|
|
3
|
+
constraintTypeDefs2
|
|
4
|
+
} from "./chunks/index-rbxe5m3y.js";
|
|
5
|
+
import {
|
|
6
|
+
checkConstraints2
|
|
7
|
+
} from "./chunks/index-s1kkmg35.js";
|
|
8
|
+
|
|
9
|
+
// src/bad-user-input.ts
|
|
10
|
+
import { GraphQLError } from "graphql";
|
|
11
|
+
function issuesOf(error, constraintOf) {
|
|
12
|
+
return error.issues.map((issue) => {
|
|
13
|
+
const path = issue.path.map((key) => typeof key === "number" ? key : String(key));
|
|
14
|
+
const constraint = constraintOf?.(issue);
|
|
15
|
+
return constraint ? { path, message: issue.message, code: issue.code, constraint } : { path, message: issue.message, code: issue.code };
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
function errorOf(where, issues) {
|
|
19
|
+
const [first] = issues;
|
|
20
|
+
const reason = first ? `${first.path.join(".") || "arguments"}: ${first.message}` : "refused";
|
|
21
|
+
const extensions = { code: "BAD_USER_INPUT", issues };
|
|
22
|
+
return new GraphQLError(`Invalid arguments for ${where}. ${reason}`, {
|
|
23
|
+
extensions
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
function badUserInput(where, error) {
|
|
27
|
+
return errorOf(where, issuesOf(error));
|
|
28
|
+
}
|
|
29
|
+
async function parseArgs(schema, args, where, constraintOf) {
|
|
30
|
+
const result = await schema.safeParseAsync(args);
|
|
31
|
+
if (!result.success)
|
|
32
|
+
throw errorOf(where, issuesOf(result.error, constraintOf));
|
|
33
|
+
return result.data;
|
|
34
|
+
}
|
|
35
|
+
// src/validated.ts
|
|
36
|
+
import { z } from "zod";
|
|
37
|
+
function validated(schema, resolve) {
|
|
38
|
+
const checked = "_zod" in schema ? schema : z.object(schema);
|
|
39
|
+
return async (source, args, context, info) => {
|
|
40
|
+
const where = `${info.parentType.name}.${info.fieldName}`;
|
|
41
|
+
const parsed = await parseArgs(checked, args, where);
|
|
42
|
+
return await resolve(source, parsed, context, info);
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
// src/with-validation.ts
|
|
46
|
+
import {
|
|
47
|
+
defaultFieldResolver
|
|
48
|
+
} from "graphql";
|
|
49
|
+
var wrapped = Symbol.for("@nxgt/graphql-validation/wrapped");
|
|
50
|
+
function checking(schema, where, resolve) {
|
|
51
|
+
const check = async (source, args, context, info) => await resolve(source, await parseArgs(schema, args, where, constraintOf), context, info);
|
|
52
|
+
check[wrapped] = true;
|
|
53
|
+
return check;
|
|
54
|
+
}
|
|
55
|
+
function wrap(field, args, where, subscription) {
|
|
56
|
+
const subscribe = field.subscribe ?? (subscription ? defaultFieldResolver : undefined);
|
|
57
|
+
if (subscribe) {
|
|
58
|
+
if (!subscribe[wrapped])
|
|
59
|
+
field.subscribe = checking(args, where, subscribe);
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
const resolve = field.resolve ?? defaultFieldResolver;
|
|
63
|
+
if (!resolve[wrapped])
|
|
64
|
+
field.resolve = checking(args, where, resolve);
|
|
65
|
+
}
|
|
66
|
+
function withValidation(schema) {
|
|
67
|
+
const directive = checkConstraints2(schema, (type, field, args) => {
|
|
68
|
+
const subscription = type === schema.getSubscriptionType();
|
|
69
|
+
wrap(field, args, `${type.name}.${field.name}`, subscription);
|
|
70
|
+
});
|
|
71
|
+
if (!directive) {
|
|
72
|
+
throw new Error("withValidation: this schema declares no @constraint directive. Add constraintTypeDefs to its type definitions.");
|
|
73
|
+
}
|
|
74
|
+
return schema;
|
|
75
|
+
}
|
|
76
|
+
export {
|
|
77
|
+
badUserInput,
|
|
78
|
+
constraintTypeDefs2 as constraintTypeDefs,
|
|
79
|
+
validated,
|
|
80
|
+
withValidation
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
//# debugId=EAADEEA4CBC8F7A764756E2164756E21
|
|
84
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/bad-user-input.ts", "../src/validated.ts", "../src/with-validation.ts"],
|
|
4
|
+
"sourcesContent": [
|
|
5
|
+
"import { GraphQLError } from 'graphql';\nimport type { z } from 'zod';\nimport type { ConstraintArgument } from './rules';\n\n/** One reason an input was refused, at its path inside the arguments. */\nexport interface ValidationIssue {\n\t/** `['input', 'address', 'zip']`: the argument, then the input fields. */\n\treadonly path: readonly (string | number)[];\n\treadonly message: string;\n\t/** Zod's issue code: `too_small`, `invalid_format`, `custom`, ... */\n\treadonly code: string;\n\t/**\n\t * The `@constraint` argument that refused (`minLength`, `format`), when a\n\t * directive did: stable across Zod versions, unlike `code`. Absent for a\n\t * refusal of `validated()`'s own schema.\n\t */\n\treadonly constraint?: ConstraintArgument;\n}\n\n/** The `extensions` of the error an invalid input becomes. */\nexport type BadUserInputExtensions = {\n\treadonly code: 'BAD_USER_INPUT';\n\treadonly issues: readonly ValidationIssue[];\n};\n\n/** Names the `@constraint` argument behind an issue, when there is one. */\nexport type ConstraintOf = (\n\tissue: z.core.$ZodIssue,\n) => ConstraintArgument | undefined;\n\nfunction issuesOf(\n\terror: z.ZodError,\n\tconstraintOf?: ConstraintOf,\n): ValidationIssue[] {\n\treturn error.issues.map((issue) => {\n\t\tconst path = issue.path.map((key) =>\n\t\t\ttypeof key === 'number' ? key : String(key),\n\t\t);\n\t\tconst constraint = constraintOf?.(issue);\n\t\treturn constraint\n\t\t\t? { path, message: issue.message, code: issue.code, constraint }\n\t\t\t: { path, message: issue.message, code: issue.code };\n\t});\n}\n\nfunction errorOf(where: string, issues: ValidationIssue[]): GraphQLError {\n\tconst [first] = issues;\n\tconst reason = first\n\t\t? `${first.path.join('.') || 'arguments'}: ${first.message}`\n\t\t: 'refused';\n\tconst extensions: BadUserInputExtensions = { code: 'BAD_USER_INPUT', issues };\n\treturn new GraphQLError(`Invalid arguments for ${where}. ${reason}`, {\n\t\textensions,\n\t});\n}\n\n/**\n * The one error an invalid input becomes: `extensions.code` is\n * `BAD_USER_INPUT`, the code Apollo and most clients already read, and\n * `extensions.issues` lists every reason with its path, so a form can show\n * each one beside its field. The message names the field and the first\n * reason, for a log line.\n */\nexport function badUserInput(where: string, error: z.ZodError): GraphQLError {\n\treturn errorOf(where, issuesOf(error));\n}\n\n/** The parsed arguments, or the error above, its issues named by `constraintOf`. */\nexport async function parseArgs(\n\tschema: z.ZodType,\n\targs: unknown,\n\twhere: string,\n\tconstraintOf?: ConstraintOf,\n): Promise<unknown> {\n\tconst result = await schema.safeParseAsync(args);\n\tif (!result.success)\n\t\tthrow errorOf(where, issuesOf(result.error, constraintOf));\n\treturn result.data;\n}\n",
|
|
6
|
+
"import type { GraphQLFieldResolver, GraphQLResolveInfo } from 'graphql';\nimport { z } from 'zod';\nimport { parseArgs } from './bad-user-input';\n\n/** An object schema of the arguments, or its shape. */\nexport type ArgsSchema = z.ZodType<Record<string, unknown>> | z.ZodRawShape;\n\n/** The schema `ArgsSchema` stands for. */\nexport type SchemaOf<S extends ArgsSchema> = S extends z.ZodType\n\t? S\n\t: z.ZodObject<S & z.ZodRawShape>;\n\n/**\n * A resolver whose arguments are checked by `schema` before it runs, for what\n * a `@constraint` cannot say: a rule across arguments, a business refinement,\n * an async check. It receives the parsed arguments (`z.output`), defaults and\n * transforms applied; an invalid input is the same `BAD_USER_INPUT` error as\n * the directives'.\n *\n * ```ts\n * signUp: validated(\n * z.object({ input: signUpSchema }).refine(({ input }) => input.password !== input.email),\n * (_, { input }) => users.create(input),\n * )\n * ```\n */\nexport function validated<\n\tS extends ArgsSchema,\n\tTSource = unknown,\n\tTContext = unknown,\n\tTResult = unknown,\n>(\n\tschema: S,\n\tresolve: (\n\t\tsource: TSource,\n\t\targs: z.output<SchemaOf<S>>,\n\t\tcontext: TContext,\n\t\tinfo: GraphQLResolveInfo,\n\t) => TResult,\n): GraphQLFieldResolver<\n\tTSource,\n\tTContext,\n\tz.input<SchemaOf<S>>,\n\tPromise<Awaited<TResult>>\n> {\n\t// `_zod`, not `instanceof`: a schema from another copy of zod is still one.\n\tconst checked = (\n\t\t'_zod' in schema ? schema : z.object(schema as z.ZodRawShape)\n\t) as z.ZodType;\n\treturn async (source, args, context, info): Promise<Awaited<TResult>> => {\n\t\tconst where = `${info.parentType.name}.${info.fieldName}`;\n\t\tconst parsed = (await parseArgs(checked, args, where)) as z.output<\n\t\t\tSchemaOf<S>\n\t\t>;\n\t\treturn await resolve(source, parsed, context, info);\n\t};\n}\n",
|
|
7
|
+
"import {\n\tdefaultFieldResolver,\n\ttype GraphQLField,\n\ttype GraphQLFieldResolver,\n\ttype GraphQLSchema,\n} from 'graphql';\nimport type { z } from 'zod';\nimport { parseArgs } from './bad-user-input';\nimport { checkConstraints } from './builder/check-constraints';\nimport { constraintOf } from './rules';\n\n// Symbol.for: two copies of the package in one tree still wrap a field once.\nconst wrapped = Symbol.for('@nxgt/graphql-validation/wrapped');\n\ntype Resolver = GraphQLFieldResolver<unknown, unknown> & { [wrapped]?: true };\n\nfunction checking(\n\tschema: z.ZodType,\n\twhere: string,\n\tresolve: Resolver,\n): Resolver {\n\tconst check: Resolver = async (source, args, context, info) =>\n\t\tawait resolve(\n\t\t\tsource,\n\t\t\tawait parseArgs(schema, args, where, constraintOf),\n\t\t\tcontext,\n\t\t\tinfo,\n\t\t);\n\tcheck[wrapped] = true;\n\treturn check;\n}\n\n/**\n * Wraps a field once. A subscription field is checked in `subscribe`, which\n * runs once per subscription — graphql's default one when the field has none\n * — and its `resolve` runs once per event, with the same arguments, already\n * checked.\n */\nfunction wrap(\n\tfield: GraphQLField<unknown, unknown>,\n\targs: z.ZodType,\n\twhere: string,\n\tsubscription: boolean,\n): void {\n\tconst subscribe = (field.subscribe ??\n\t\t(subscription ? defaultFieldResolver : undefined)) as Resolver | undefined;\n\tif (subscribe) {\n\t\tif (!subscribe[wrapped]) field.subscribe = checking(args, where, subscribe);\n\t\treturn;\n\t}\n\tconst resolve = (field.resolve ?? defaultFieldResolver) as Resolver;\n\tif (!resolve[wrapped]) field.resolve = checking(args, where, resolve);\n}\n\n/**\n * Checks every `@constraint` of `schema` before the resolvers run. Each\n * field with a constrained argument — directly, or anywhere inside an input\n * type it takes — gets its resolver (and its `subscribe`) wrapped: the\n * arguments are parsed by the Zod schema the directives describe, the\n * resolver receives the parsed value, and an invalid input becomes one\n * `BAD_USER_INPUT` error. Fields with nothing to check are left untouched.\n *\n * The schema is built here, once: a constraint that cannot apply (a\n * `minLength` on an `Int`, an unknown format, a bad pattern) throws now, not\n * on the first request. The resolvers are wrapped in place and the same\n * schema is returned, so it goes wherever it went before; calling it twice\n * wraps nothing twice. Call it last, once every resolver is attached: a\n * resolver set on a field afterwards replaces the check.\n *\n * A `@constraint` on an interface's argument must be repeated on each\n * object's field, which is the one that resolves; a field that drops it\n * fails here.\n *\n * The directives are read from the SDL (`astNode`), so a schema built from\n * type definitions — `buildSchema`, `makeExecutableSchema`, Yoga, Apollo —\n * is checked; a code-first schema with no SDL has no `@constraint` to read.\n */\nexport function withValidation<S extends GraphQLSchema>(schema: S): S {\n\tconst directive = checkConstraints(schema, (type, field, args) => {\n\t\tconst subscription = type === schema.getSubscriptionType();\n\t\twrap(field, args, `${type.name}.${field.name}`, subscription);\n\t});\n\tif (!directive) {\n\t\tthrow new Error(\n\t\t\t'withValidation: this schema declares no @constraint directive. Add constraintTypeDefs to its type definitions.',\n\t\t);\n\t}\n\treturn schema;\n}\n"
|
|
8
|
+
],
|
|
9
|
+
"mappings": ";;;;;;;;;AAAA;AA8BA,SAAS,QAAQ,CAChB,OACA,cACoB;AAAA,EACpB,OAAO,MAAM,OAAO,IAAI,CAAC,UAAU;AAAA,IAClC,MAAM,OAAO,MAAM,KAAK,IAAI,CAAC,QAC5B,OAAO,QAAQ,WAAW,MAAM,OAAO,GAAG,CAC3C;AAAA,IACA,MAAM,aAAa,eAAe,KAAK;AAAA,IACvC,OAAO,aACJ,EAAE,MAAM,SAAS,MAAM,SAAS,MAAM,MAAM,MAAM,WAAW,IAC7D,EAAE,MAAM,SAAS,MAAM,SAAS,MAAM,MAAM,KAAK;AAAA,GACpD;AAAA;AAGF,SAAS,OAAO,CAAC,OAAe,QAAyC;AAAA,EACxE,OAAO,SAAS;AAAA,EAChB,MAAM,SAAS,QACZ,GAAG,MAAM,KAAK,KAAK,GAAG,KAAK,gBAAgB,MAAM,YACjD;AAAA,EACH,MAAM,aAAqC,EAAE,MAAM,kBAAkB,OAAO;AAAA,EAC5E,OAAO,IAAI,aAAa,yBAAyB,UAAU,UAAU;AAAA,IACpE;AAAA,EACD,CAAC;AAAA;AAUK,SAAS,YAAY,CAAC,OAAe,OAAiC;AAAA,EAC5E,OAAO,QAAQ,OAAO,SAAS,KAAK,CAAC;AAAA;AAItC,eAAsB,SAAS,CAC9B,QACA,MACA,OACA,cACmB;AAAA,EACnB,MAAM,SAAS,MAAM,OAAO,eAAe,IAAI;AAAA,EAC/C,IAAI,CAAC,OAAO;AAAA,IACX,MAAM,QAAQ,OAAO,SAAS,OAAO,OAAO,YAAY,CAAC;AAAA,EAC1D,OAAO,OAAO;AAAA;;AC5Ef;AAyBO,SAAS,SAKf,CACA,QACA,SAWC;AAAA,EAED,MAAM,UACL,UAAU,SAAS,SAAS,EAAE,OAAO,MAAuB;AAAA,EAE7D,OAAO,OAAO,QAAQ,MAAM,SAAS,SAAoC;AAAA,IACxE,MAAM,QAAQ,GAAG,KAAK,WAAW,QAAQ,KAAK;AAAA,IAC9C,MAAM,SAAU,MAAM,UAAU,SAAS,MAAM,KAAK;AAAA,IAGpD,OAAO,MAAM,QAAQ,QAAQ,QAAQ,SAAS,IAAI;AAAA;AAAA;;ACtDpD;AAAA;AAAA;AAYA,IAAM,UAAU,OAAO,IAAI,kCAAkC;AAI7D,SAAS,QAAQ,CAChB,QACA,OACA,SACW;AAAA,EACX,MAAM,QAAkB,OAAO,QAAQ,MAAM,SAAS,SACrD,MAAM,QACL,QACA,MAAM,UAAU,QAAQ,MAAM,OAAO,YAAY,GACjD,SACA,IACD;AAAA,EACD,MAAM,WAAW;AAAA,EACjB,OAAO;AAAA;AASR,SAAS,IAAI,CACZ,OACA,MACA,OACA,cACO;AAAA,EACP,MAAM,YAAa,MAAM,cACvB,eAAe,uBAAuB;AAAA,EACxC,IAAI,WAAW;AAAA,IACd,IAAI,CAAC,UAAU;AAAA,MAAU,MAAM,YAAY,SAAS,MAAM,OAAO,SAAS;AAAA,IAC1E;AAAA,EACD;AAAA,EACA,MAAM,UAAW,MAAM,WAAW;AAAA,EAClC,IAAI,CAAC,QAAQ;AAAA,IAAU,MAAM,UAAU,SAAS,MAAM,OAAO,OAAO;AAAA;AA0B9D,SAAS,cAAuC,CAAC,QAAc;AAAA,EACrE,MAAM,YAAY,kBAAiB,QAAQ,CAAC,MAAM,OAAO,SAAS;AAAA,IACjE,MAAM,eAAe,SAAS,OAAO,oBAAoB;AAAA,IACzD,KAAK,OAAO,MAAM,GAAG,KAAK,QAAQ,MAAM,QAAQ,YAAY;AAAA,GAC5D;AAAA,EACD,IAAI,CAAC,WAAW;AAAA,IACf,MAAM,IAAI,MACT,gHACD;AAAA,EACD;AAAA,EACA,OAAO;AAAA;",
|
|
10
|
+
"debugId": "EAADEEA4CBC8F7A764756E2164756E21",
|
|
11
|
+
"names": []
|
|
12
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export * from './contains';
|
|
2
|
+
export * from './ends-with';
|
|
3
|
+
export * from './exclusive-max';
|
|
4
|
+
export * from './exclusive-min';
|
|
5
|
+
export * from './format';
|
|
6
|
+
export * from './max';
|
|
7
|
+
export * from './max-items';
|
|
8
|
+
export * from './max-length';
|
|
9
|
+
export * from './min';
|
|
10
|
+
export * from './min-items';
|
|
11
|
+
export * from './min-length';
|
|
12
|
+
export * from './multiple-of';
|
|
13
|
+
export * from './not-contains';
|
|
14
|
+
export * from './pattern';
|
|
15
|
+
export * from './starts-with';
|
|
16
|
+
//# sourceMappingURL=all.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"all.d.ts","sourceRoot":"","sources":["../../src/rules/all.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAC;AAC3B,cAAc,aAAa,CAAC;AAC5B,cAAc,iBAAiB,CAAC;AAChC,cAAc,iBAAiB,CAAC;AAChC,cAAc,UAAU,CAAC;AACzB,cAAc,OAAO,CAAC;AACtB,cAAc,aAAa,CAAC;AAC5B,cAAc,cAAc,CAAC;AAC7B,cAAc,OAAO,CAAC;AACtB,cAAc,aAAa,CAAC;AAC5B,cAAc,cAAc,CAAC;AAC7B,cAAc,eAAe,CAAC;AAC9B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,WAAW,CAAC;AAC1B,cAAc,eAAe,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"contains.d.ts","sourceRoot":"","sources":["../../src/rules/contains.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,YAAY,uDAOvB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ends-with.d.ts","sourceRoot":"","sources":["../../src/rules/ends-with.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,YAAY,uDAOvB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"exclusive-max.d.ts","sourceRoot":"","sources":["../../src/rules/exclusive-max.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,gBAAgB,0DAU3B,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"exclusive-min.d.ts","sourceRoot":"","sources":["../../src/rules/exclusive-min.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,gBAAgB,0DAU3B,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"format.d.ts","sourceRoot":"","sources":["../../src/rules/format.ts"],"names":[],"mappings":"AAMA,eAAO,MAAM,UAAU,qDASrB,CAAC"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { z } from 'zod';
|
|
2
|
+
import * as all from './all';
|
|
3
|
+
/**
|
|
4
|
+
* One of the rules, as its own type: a builder narrows it on `target`, so a
|
|
5
|
+
* number schema never reaches a string rule.
|
|
6
|
+
*/
|
|
7
|
+
export type RegisteredRule = (typeof all)[keyof typeof all];
|
|
8
|
+
/** An argument `@constraint` takes. */
|
|
9
|
+
export type ConstraintArgument = RegisteredRule['argument'];
|
|
10
|
+
/**
|
|
11
|
+
* Every rule, keyed by its `@constraint` argument. Derived from what
|
|
12
|
+
* `./all` exports, so a new rule is one file and one line there, and the
|
|
13
|
+
* directive's SDL follows.
|
|
14
|
+
*/
|
|
15
|
+
export declare const rules: { readonly [A in ConstraintArgument]: Extract<RegisteredRule, {
|
|
16
|
+
argument: A;
|
|
17
|
+
}>; };
|
|
18
|
+
/**
|
|
19
|
+
* Applies a rule to a schema its caller has already matched to the rule's
|
|
20
|
+
* target: the one place the rule's typed `toZod` meets a schema built from
|
|
21
|
+
* GraphQL types, which the compiler cannot relate.
|
|
22
|
+
*/
|
|
23
|
+
export declare function applyRule(rule: RegisteredRule, schema: z.ZodType, value: string | number): z.ZodType;
|
|
24
|
+
/** `applyRule` for source: the rule's typed `toCode` on a schema's source. */
|
|
25
|
+
export declare function applyRuleCode(rule: RegisteredRule, source: string, value: string | number): string;
|
|
26
|
+
/** The `@constraint` argument whose rule refused with this issue, if any. */
|
|
27
|
+
export declare function constraintOf(issue: z.core.$ZodIssue): ConstraintArgument | undefined;
|
|
28
|
+
//# sourceMappingURL=index.d.ts.map
|