UNPKG

@hookform/resolvers

Version:

React Hook Form validation resolvers: Yup, Joi, Superstruct, Zod, Vest, Class Validator, io-ts, Nope, computed-types, TypeBox, arktype, Typanion, Effect-TS and VineJS

1 lines 16.9 kB
{"version":3,"file":"zod.modern.mjs","sources":["../src/zod.ts"],"sourcesContent":["import { toNestErrors, validateFieldsNatively } from '@hookform/resolvers';\nimport {\n FieldError,\n FieldValues,\n Resolver,\n ResolverError,\n ResolverSuccess,\n appendErrors,\n} from 'react-hook-form';\n// Type-only: these subpaths don't exist before zod@3.25.0, so importing them\n// as values would break resolution for anyone on an older zod 3.x. `import\n// type` is fully erased at build time, so the compiled output never\n// references these subpaths at runtime — see `isZod4Error` below for how the\n// one runtime check that used to need `z4.$ZodError` avoids that now.\nimport type * as z3 from 'zod/v3';\nimport type * as z4 from 'zod/v4/core';\n\nconst isZod3Error = (error: any): error is z3.ZodError => {\n return Array.isArray(error?.issues);\n};\nconst isZod3Schema = (schema: any): schema is z3.ZodSchema => {\n // Deliberately doesn't require `typeName` in `_def` — dynamically selected\n // schemas typed via the generic `z.ZodType<Output>` annotation (rather than\n // a concrete subclass like `ZodObject`) don't statically expose `typeName`,\n // even though they're genuine Zod 3 schema instances at runtime.\n return '_def' in schema && typeof schema._def === 'object';\n};\nconst isZod4Error = (error: any): error is z4.$ZodError => {\n // Replicates zod4's own `$ZodError`/`Symbol.hasInstance` check\n // (`inst?._zod?.traits?.has(name)`, see zod's `$constructor` in\n // `v4/core/core.ts`) by hand rather than importing `$ZodError` as a value,\n // since that import only exists in zod@3.25.0+ (see the `import type` note\n // above).\n return !!error?._zod?.traits?.has('$ZodError');\n};\nconst isZod4Schema = (schema: any): schema is z4.$ZodType => {\n return '_zod' in schema && typeof schema._zod === 'object';\n};\n\nfunction parseZod3Issues(\n zodErrors: z3.ZodIssue[],\n validateAllFieldCriteria: boolean,\n) {\n const errors: Record<string, FieldError> = {};\n for (; zodErrors.length; ) {\n const error = zodErrors[0];\n const { code, message, path } = error;\n const _path = path.join('.');\n\n if (!errors[_path]) {\n if ('unionErrors' in error) {\n // Surface the error from whichever union member came closest to\n // matching (fewest issues), rather than always the first member —\n // the first member isn't necessarily the one the input was meant for.\n const closestUnionError = error.unionErrors.reduce(\n (closest, current) =>\n current.errors.length < closest.errors.length ? current : closest,\n );\n // Fall back to the union issue's own message/code if the closest\n // member somehow has no issues of its own (shouldn't happen, but\n // avoids ever throwing on an unexpected/malformed union shape).\n const unionError = closestUnionError.errors[0];\n\n errors[_path] = {\n message: unionError?.message ?? message,\n type: unionError?.code ?? code,\n };\n } else {\n errors[_path] = { message, type: code };\n }\n }\n\n if ('unionErrors' in error) {\n error.unionErrors.forEach((unionError) =>\n unionError.errors.forEach((e) => zodErrors.push(e)),\n );\n }\n\n if (validateAllFieldCriteria) {\n const types = errors[_path].types;\n const messages = types && types[error.code];\n\n errors[_path] = appendErrors(\n _path,\n validateAllFieldCriteria,\n errors,\n code,\n messages\n ? ([] as string[]).concat(messages as string[], error.message)\n : error.message,\n ) as FieldError;\n }\n\n zodErrors.shift();\n }\n\n return errors;\n}\n\nfunction parseZod4Issues(\n zodErrors: z4.$ZodIssue[],\n validateAllFieldCriteria: boolean,\n) {\n const errors: Record<string, FieldError> = {};\n // const _zodErrors = zodErrors as z4.$ZodISsue; //\n for (; zodErrors.length; ) {\n const error = zodErrors[0];\n const { code, message, path } = error;\n const _path = path.join('.');\n\n if (!errors[_path]) {\n if (error.code === 'invalid_union' && error.errors.length > 0) {\n // Surface the error from whichever union member came closest to\n // matching (fewest issues), rather than always the first member —\n // the first member isn't necessarily the one the input was meant for.\n const closestBranch = error.errors.reduce((closest, branch) =>\n branch.length < closest.length ? branch : closest,\n );\n // Fall back to the union issue's own message/code if the closest\n // branch somehow has no issues of its own (shouldn't happen — a\n // 0-issue branch means that branch matched — but this guarantees we\n // never throw on an unexpected/malformed union shape, e.g. a \"no\n // matching discriminator\" issue nested in a way we haven't seen).\n const unionError = closestBranch[0];\n\n errors[_path] = {\n message: unionError?.message ?? message,\n type: unionError?.code ?? code,\n };\n } else {\n errors[_path] = { message, type: code };\n }\n }\n\n if (error.code === 'invalid_union') {\n error.errors.forEach((unionError) =>\n unionError.forEach((e) =>\n zodErrors.push({ ...e, path: [...error.path, ...e.path] }),\n ),\n );\n }\n\n if (validateAllFieldCriteria) {\n const types = errors[_path].types;\n const messages = types && types[error.code];\n\n errors[_path] = appendErrors(\n _path,\n validateAllFieldCriteria,\n errors,\n code,\n messages\n ? ([] as string[]).concat(messages as string[], error.message)\n : error.message,\n ) as FieldError;\n }\n\n zodErrors.shift();\n }\n\n return errors;\n}\n\ntype RawResolverOptions = {\n mode?: 'async' | 'sync';\n raw: true;\n};\ntype NonRawResolverOptions = {\n mode?: 'async' | 'sync';\n raw?: false;\n};\n\n// minimal interfaces to avoid asssignability issues between versions\ninterface Zod3Type<O = unknown, I = unknown> {\n _output: O;\n _input: I;\n _def: object;\n}\n\n// minimal interface to avoid assignability issues between zod v4 patch/minor\n// releases, which brand the full `$ZodType` shape with an exact version literal\n// (e.g. `_zod.version.minor`)\ninterface Zod4Type<Output = unknown, Input = unknown> {\n _zod: {\n output: Output;\n input: Input;\n };\n}\n\n// some type magic to make versions pre-3.25.0 still work\ntype IsUnresolved<T> = PropertyKey extends keyof T ? true : false;\ntype UnresolvedFallback<T, Fallback> = IsUnresolved<typeof z3> extends true\n ? Fallback\n : T;\ntype FallbackIssue = {\n code: string;\n message: string;\n path: (string | number)[];\n};\ntype Zod3ParseParams = UnresolvedFallback<\n z3.ParseParams,\n // fallback if user is on <3.25.0\n {\n path?: (string | number)[];\n errorMap?: (\n iss: FallbackIssue,\n ctx: {\n defaultError: string;\n data: any;\n },\n ) => { message: string };\n async?: boolean;\n }\n>;\ntype Zod4ParseParams = UnresolvedFallback<\n z4.ParseContext<z4.$ZodIssue>,\n // fallback if user is on <3.25.0\n {\n readonly error?: (\n iss: FallbackIssue,\n ) => null | undefined | string | { message: string };\n readonly reportInput?: boolean;\n readonly jitless?: boolean;\n }\n>;\n\nexport function zodResolver<Input extends FieldValues, Context, Output>(\n schema: Zod3Type<Output, Input>,\n schemaOptions?: Zod3ParseParams,\n resolverOptions?: NonRawResolverOptions,\n): Resolver<Input, Context, Output>;\nexport function zodResolver<Input extends FieldValues, Context, Output>(\n schema: Zod3Type<Output, Input>,\n schemaOptions: Zod3ParseParams | undefined,\n resolverOptions: RawResolverOptions,\n): Resolver<Input, Context, Input>;\n// the Zod 4 overloads need to be generic for complicated reasons\nexport function zodResolver<\n Input extends FieldValues,\n Context,\n Output,\n T extends Zod4Type<Output, Input> = Zod4Type<Output, Input>,\n>(\n schema: T,\n schemaOptions?: Zod4ParseParams, // already partial\n resolverOptions?: NonRawResolverOptions,\n): Resolver<z4.input<T>, Context, z4.output<T>>;\nexport function zodResolver<\n Input extends FieldValues,\n Context,\n Output,\n T extends Zod4Type<Output, Input> = Zod4Type<Output, Input>,\n>(\n schema: Zod4Type<Output, Input>,\n schemaOptions: Zod4ParseParams | undefined, // already partial\n resolverOptions: RawResolverOptions,\n): Resolver<z4.input<T>, Context, z4.input<T>>;\n/**\n * Creates a resolver function for react-hook-form that validates form data using a Zod schema\n * @param {z3.ZodSchema<Input>} schema - The Zod schema used to validate the form data\n * @param {Partial<z3.ParseParams>} [schemaOptions] - Optional configuration options for Zod parsing\n * @param {Object} [resolverOptions] - Optional resolver-specific configuration\n * @param {('async'|'sync')} [resolverOptions.mode='async'] - Validation mode. Use 'sync' for synchronous validation\n * @param {boolean} [resolverOptions.raw=false] - If true, returns the raw form values instead of the parsed data\n * @returns {Resolver<z3.output<typeof schema>>} A resolver function compatible with react-hook-form\n * @throws {Error} Throws if validation fails with a non-Zod error\n * @example\n * const schema = z3.object({\n * name: z3.string().min(2),\n * age: z3.number().min(18)\n * });\n *\n * useForm({\n * resolver: zodResolver(schema)\n * });\n */\nexport function zodResolver<Input extends FieldValues, Context, Output>(\n schema: object,\n schemaOptions?: object,\n resolverOptions: {\n mode?: 'async' | 'sync';\n raw?: boolean;\n } = {},\n): Resolver<Input, Context, Output | Input> {\n // Zod 4 is checked first: its `_zod` marker is unambiguous, whereas the\n // Zod 3 check below only requires an object-shaped `_def` (to support\n // dynamically-selected schemas typed via the generic `z.ZodType<Output>`\n // annotation, see `isZod3Schema`) — and Zod 4 schemas also have an\n // object-shaped `_def`, so they'd otherwise be misdetected as Zod 3.\n if (isZod4Schema(schema)) {\n return async (values: Input, _, options) => {\n try {\n // Call the schema's own bound `parse`/`parseAsync` method rather\n // than the standalone `z4.parse`/`z4.parseAsync` functions. Those\n // standalone functions read from this module's own imported\n // `zod/v4/core`, which — under bundlers that can resolve `zod` and\n // `zod/v4/core` to two distinct module instances (e.g. Metro with\n // package-exports resolution enabled) — may not be the same zod\n // instance the schema was created with, silently dropping any\n // global config (`z.config()`) or locale the app configured on its\n // own zod instance. The schema's own method is always bound to\n // whichever zod instance actually created it.\n const classicSchema = schema as unknown as {\n parse: (values: unknown, schemaOptions: unknown) => unknown;\n parseAsync: (\n values: unknown,\n schemaOptions: unknown,\n ) => Promise<unknown>;\n };\n const data: any =\n resolverOptions.mode === 'sync'\n ? classicSchema.parse(values, schemaOptions)\n : await classicSchema.parseAsync(values, schemaOptions);\n\n options.shouldUseNativeValidation &&\n validateFieldsNatively({}, options);\n\n return {\n errors: {},\n values: resolverOptions.raw ? Object.assign({}, values) : data,\n } satisfies ResolverSuccess<Output | Input>;\n } catch (error) {\n if (isZod4Error(error)) {\n return {\n values: {},\n errors: toNestErrors(\n parseZod4Issues(\n error.issues,\n !options.shouldUseNativeValidation &&\n options.criteriaMode === 'all',\n ),\n options,\n ),\n } satisfies ResolverError<Input>;\n }\n\n throw error;\n }\n };\n }\n\n if (isZod3Schema(schema)) {\n return async (values: Input, _, options) => {\n try {\n const data = await schema[\n resolverOptions.mode === 'sync' ? 'parse' : 'parseAsync'\n ](values, schemaOptions);\n\n options.shouldUseNativeValidation &&\n validateFieldsNatively({}, options);\n\n return {\n errors: {},\n values: resolverOptions.raw ? Object.assign({}, values) : data,\n } satisfies ResolverSuccess<Output | Input>;\n } catch (error) {\n if (isZod3Error(error)) {\n return {\n values: {},\n errors: toNestErrors(\n parseZod3Issues(\n error.errors,\n !options.shouldUseNativeValidation &&\n options.criteriaMode === 'all',\n ),\n options,\n ),\n } satisfies ResolverError<Input>;\n }\n\n throw error;\n }\n };\n }\n\n throw new Error('Invalid input: not a Zod schema');\n}\n"],"names":["parseZod3Issues","zodErrors","validateAllFieldCriteria","errors","length","error","code","message","path","_path","join","_unionError$message","_unionError$code","unionError","unionErrors","reduce","closest","current","type","forEach","e","push","types","messages","appendErrors","concat","shift","parseZod4Issues","_unionError$message2","_unionError$code2","branch","_extends","zodResolver","schema","schemaOptions","resolverOptions","_zod","isZod4Schema","values","_","options","classicSchema","data","mode","parse","parseAsync","shouldUseNativeValidation","validateFieldsNatively","raw","Object","assign","_error$_zod","traits","has","isZod4Error","toNestErrors","issues","criteriaMode","_def","isZod3Schema","async","Array","isArray","isZod3Error","Error"],"mappings":"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"}