import { inspect } from '../../jsutils/inspect' ; import type { Maybe } from '../../jsutils/Maybe' ; import { GraphQLError } from '../../error/GraphQLError' ; import type { ValueNode } from '../../language/ast' ; import { Kind } from '../../language/kinds' ; import type { ASTVisitor } from '../../language/visitor' ; import type { GraphQLType } from '../../type/definition' ; import { isNonNullType } from '../../type/definition' ; import type { GraphQLSchema } from '../../type/schema' ; import { isTypeSubTypeOf } from '../../utilities/typeComparators' ; import { typeFromAST } from '../../utilities/typeFromAST' ; import type { ValidationContext } from '../ValidationContext' ; /**
 * Variables in allowed position
 *
 * Variable usages must be compatible with the arguments they are passed to.
 *
 * See https://spec.graphql.org/draft/#sec-All-Variable-Usages-are-Allowed
 */ export function VariablesInAllowedPositionRule ( context ) { let varDefMap = Object . create ( null ) ; return { OperationDefinition : { enter ( ) { varDefMap = Object . create ( null ) ; } , leave ( operation ) { const usages = context . getRecursiveVariableUsages ( operation ) ; for ( const { node , type , defaultValue } of usages ) { const varName = node . name . value ; const varDef = varDefMap [ varName ] ; if ( varDef && type ) { const schema = context . getSchema ( ) ; const varType = typeFromAST ( schema , varDef . type ) ; if ( varType && ! allowedVariableUsage ( schema , varType , varDef . defaultValue , type , defaultValue ) ) { const varTypeStr = inspect ( varType ) ; const typeStr = inspect ( type ) ; context . reportError ( new GraphQLError ( `Variable "$${ varName }" of type "${ varTypeStr }" used in position expecting type "${ typeStr }".` , [ varDef , node ] ) ) ; } } } } , } , VariableDefinition ( node ) { varDefMap [ node . variable . name . value ] = node ; } , } ; } /**
 * Returns true if the variable is allowed in the location it was found,
 * which includes considering if default values exist for either the variable
 * or the location at which it is located.
 */ function allowedVariableUsage ( schema , varType , varDefaultValue , locationType , locationDefaultValue ) { if ( isNonNullType ( locationType ) && ! isNonNullType ( varType ) ) { const hasNonNullVariableDefaultValue = varDefaultValue != null && varDefaultValue . kind !== Kind . NULL ; const hasLocationDefaultValue = locationDefaultValue !== undefined ; if ( ! hasNonNullVariableDefaultValue && ! hasLocationDefaultValue ) { return false ; } const nullableLocationType = locationType . ofType ; return isTypeSubTypeOf ( schema , varType , nullableLocationType ) ; } return isTypeSubTypeOf ( schema , varType , locationType ) ; }