UNPKG

@pothos/plugin-zod

Version:

A Pothos plugin for adding argument validation

216 lines (215 loc) 7.63 kB
import { PothosSchemaError } from '@pothos/core'; import * as zod from 'zod'; const baseValidations = [ "refine", "schema" ]; const numberValidations = [ ...baseValidations, "int", "max", "min", "negative", "nonnegative", "nonpositive", "positive", "type" ]; const bigIntValidations = [ ...baseValidations, "type" ]; const booleanValidations = [ ...baseValidations, "type" ]; const dateValidations = [ ...baseValidations, "type" ]; const stringValidations = [ ...baseValidations, "email", "length", "maxLength", "minLength", "regex", "type", "url", "uuid" ]; const arrayValidations = [ ...baseValidations, "items", "length", "maxLength", "minLength", "type" ]; const objectValidations = [ ...baseValidations, "type" ]; // biome-ignore lint/suspicious/noExplicitAny: this is fine function validatorCreator(type, validationNames, create) { function check(options) { if (typeof options !== "object" || options.type && options.type !== type) { return false; } const validations = Object.keys(options); return validations.every((validation) => validationNames.includes(validation)); } return (options) => { if (check(options)) { return create(options); } return null; }; } export function refine(originalValidator, options) { var _options_refine; if (!options) { return originalValidator; } if (typeof options === "function") { return originalValidator.refine(options); } if (Array.isArray(options)) { return refine(originalValidator, { refine: options }); } let validator = originalValidator; if (options.schema) { validator = options.schema.pipe(originalValidator); } if (!options.refine) { return validator; } if (typeof options.refine === "function") { return validator.refine(options.refine); } if (typeof ((_options_refine = options.refine) === null || _options_refine === void 0 ? void 0 : _options_refine[0]) === "function") { return validator.refine(...options.refine); } const refinements = options.refine; return refinements.reduce((prev, [refineFn, opts]) => prev.refine(refineFn, opts), validator); } export const createNumberValidator = validatorCreator("number", numberValidations, (options) => { let validator = zod.number(); if (options.min) { validator = Array.isArray(options.min) ? validator.min(Number(options.min[0]), options.min[1]) : validator.min(Number(options.min)); } if (options.max) { validator = Array.isArray(options.max) ? validator.max(Number(options.max[0]), options.max[1]) : validator.max(Number(options.max)); } const booleanConstraints = [ "int", "negative", "nonnegative", "positive", "nonpositive" ]; for (const constraint of booleanConstraints) { if (options[constraint]) { const value = options[constraint]; validator = validator[constraint](Array.isArray(value) ? value[1] : {}); } } return refine(validator, options); }); export const createBigintValidator = validatorCreator("bigint", bigIntValidations, (options) => refine(zod.bigint(), options)); export const createBooleanValidator = validatorCreator("boolean", booleanValidations, (options) => refine(zod.boolean(), options)); export const createDateValidator = validatorCreator("date", dateValidations, (options) => refine(zod.date(), options)); export const createStringValidator = validatorCreator("string", stringValidations, (options) => { let validator = zod.string(); if (options.length !== undefined) { validator = Array.isArray(options.length) ? validator.length(options.length[0], options.length[1]) : validator.length(options.length); } if (options.minLength) { validator = Array.isArray(options.minLength) ? validator.min(options.minLength[0], options.minLength[1]) : validator.min(options.minLength); } if (options.maxLength) { validator = Array.isArray(options.maxLength) ? validator.max(options.maxLength[0], options.maxLength[1]) : validator.max(options.maxLength); } if (options.regex) { validator = Array.isArray(options.regex) ? validator.regex(options.regex[0], options.regex[1]) : validator.regex(options.regex); } const booleanConstraints = [ "email", "url", "uuid" ]; for (const constraint of booleanConstraints) { if (options[constraint]) { const value = options[constraint]; validator = validator[constraint](Array.isArray(value) ? value[1] : {}); } } return refine(validator, options); }); export function isArrayValidator(options) { if (typeof options !== "object" || options.type && options.type !== "array") { return false; } const validations = Object.keys(options); return validations.every((validation) => arrayValidations.includes(validation)); } export function createArrayValidator(options, items) { let validator = items.array(); if (options.length !== undefined) { validator = Array.isArray(options.length) ? validator.length(options.length[0], options.length[1].message) : validator.length(options.length); } if (options.minLength) { validator = Array.isArray(options.minLength) ? validator.min(options.minLength[0], options.minLength[1]) : validator.min(options.minLength); } if (options.maxLength) { validator = Array.isArray(options.maxLength) ? validator.max(options.maxLength[0], options.maxLength[1]) : validator.max(options.maxLength); } return refine(validator, options); } export const createObjectValidator = validatorCreator("object", objectValidations, (options) => refine(zod.looseObject({}), options)); const validationCreators = [ createNumberValidator, createBigintValidator, createBooleanValidator, createDateValidator, createStringValidator, createObjectValidator ]; export function isBaseValidator(options) { if (typeof options === "function") { return true; } const validations = Object.keys(options); return validations.every((validation) => baseValidations.includes(validation)); } export function combine(validators, required) { const union = validators.length > 1 ? zod.union(validators) : validators[0]; return required ? union : union.optional().nullable(); } export default function createZodSchema(optionsOrConstraint, required = false) { const options = Array.isArray(optionsOrConstraint) || typeof optionsOrConstraint === "function" ? { refine: optionsOrConstraint } : optionsOrConstraint; if (!options) { return zod.unknown(); } if (isBaseValidator(options)) { return combine([ refine(zod.unknown(), options) ], required); } const typeValidators = validationCreators.map((create) => create(options)).filter(Boolean); if (isArrayValidator(options)) { const items = options.items ? createZodSchema(options.items) : zod.unknown(); typeValidators.push(createArrayValidator(options, items)); } if (typeValidators.length === 0) { throw new PothosSchemaError(`No type validator can implement every constraint in (${Object.keys(options)})`); } return combine([ ...typeValidators ], required); } //# sourceMappingURL=createZodSchema.js.map