@@ -34,9 +34,10 @@ import type {
3434 z ,
3535 AnyZodObject ,
3636 ZodEffects ,
37+ ZodArray ,
38+ ZodAny ,
3739 ZodTypeAny ,
38- SafeParseReturnType ,
39- ZodArray
40+ SafeParseReturnType
4041} from 'zod' ;
4142import { stringify } from 'devalue' ;
4243import type { FormFields } from '../index.js' ;
@@ -47,7 +48,8 @@ import {
4748 traversePathsAsync ,
4849 comparePaths ,
4950 setPaths ,
50- pathExists
51+ pathExists ,
52+ type ZodTypeInfo
5153} from '../entity.js' ;
5254import { fieldProxy } from './proxies.js' ;
5355import { clone } from '../utils.js' ;
@@ -920,9 +922,68 @@ async function validateField<T extends AnyZodObject, M>(
920922 return defaultValidate ( ) ;
921923 }
922924
925+ // Remove numeric indices, they're not used for validators.
926+ const validationPath = path . filter ( ( p ) => isNaN ( parseInt ( p ) ) ) ;
927+
928+ function extractValidator (
929+ data : ZodTypeInfo ,
930+ key : string
931+ ) : ZodTypeAny | undefined {
932+ if ( data . effects ) return undefined ;
933+
934+ // No effects, check if ZodObject or ZodArray, which are the
935+ // "allowed" objects in the path above the leaf.
936+ const type = data . zodType ;
937+
938+ if ( type . _def . typeName == 'ZodObject' ) {
939+ const nextType = ( type as AnyZodObject ) . _def . shape ( ) [ key ] ;
940+ const unwrapped = unwrapZodType ( nextType ) ;
941+ return unwrapped . effects ? undefined : unwrapped . zodType ;
942+ } else if ( type . _def . typeName == 'ZodArray' ) {
943+ const array = type as ZodArray < ZodAny > ;
944+ const unwrapped = unwrapZodType ( array . element ) ;
945+ if ( unwrapped . effects ) return undefined ;
946+ return extractValidator ( unwrapped , key ) ;
947+ } else {
948+ throw new SuperFormError ( 'Invalid validator' ) ;
949+ }
950+ }
951+
923952 if ( 'safeParseAsync' in validators ) {
924953 // Zod validator
925- const result = await ( validators as AnyZodObject ) . safeParseAsync (
954+ // Check if any effects exist for the path, then parse the entire schema.
955+ const leaf = traversePath (
956+ validators ,
957+ validationPath as FieldPath < typeof validators > ,
958+ ( pathData ) => {
959+ return extractValidator (
960+ unwrapZodType ( pathData . parent ) ,
961+ pathData . key
962+ ) ;
963+ }
964+ ) ;
965+
966+ if ( leaf ) {
967+ const validator = extractValidator (
968+ unwrapZodType ( leaf . parent ) ,
969+ leaf . key
970+ ) ;
971+ if ( validator ) {
972+ console . log ( '🚀 ~ file: index.ts:972 ~ no effects:' , validator ) ;
973+ const result = await validator . safeParseAsync ( value ) ;
974+ if ( ! result . success ) {
975+ const errors = result . error . format ( ) ;
976+ return setError ( errors . _errors ) ;
977+ } else {
978+ return setError ( undefined ) ;
979+ }
980+ }
981+ }
982+
983+ console . log ( '🚀 ~ file: index.ts:983 ~ Effects found, validating all' ) ;
984+
985+ // Effects are found, validate entire data, unfortunately
986+ const result = await ( validators as ZodTypeAny ) . safeParseAsync (
926987 get ( data )
927988 ) ;
928989
@@ -936,9 +997,6 @@ async function validateField<T extends AnyZodObject, M>(
936997 } else {
937998 // SuperForms validator
938999
939- // Remove numeric indices, they're not used for validators.
940- const validationPath = path . filter ( ( p ) => isNaN ( parseInt ( p ) ) ) ;
941-
9421000 const validator = traversePath (
9431001 validators as Validators < UnwrapEffects < T > > ,
9441002 validationPath as FieldPath < typeof validators >
0 commit comments