import type { ObjMap } from '../../jsutils/ObjMap' ; import { GraphQLError } from '../../error/GraphQLError' ; import type { FragmentDefinitionNode , OperationDefinitionNode , } from '../../language/ast' ; import { Kind } from '../../language/kinds' ; import type { ASTVisitor } from '../../language/visitor' ; import { collectFields } from '../../execution/collectFields' ; import type { ValidationContext } from '../ValidationContext' ; /**
 * Subscriptions must only include a non-introspection field.
 *
 * A GraphQL subscription is valid only if it contains a single root field and
 * that root field is not an introspection field.
 *
 * See https://spec.graphql.org/draft/#sec-Single-root-field
 */ export function SingleFieldSubscriptionsRule ( context ) { return { OperationDefinition ( node ) { if ( node . operation === 'subscription' ) { const schema = context . getSchema ( ) ; const subscriptionType = schema . getSubscriptionType ( ) ; if ( subscriptionType ) { const operationName = node . name ? node . name . value : null ; const variableValues = Object . create ( null ) ; const document = context . getDocument ( ) ; const fragments = Object . create ( null ) ; for ( const definition of document . definitions ) { if ( definition . kind === Kind . FRAGMENT_DEFINITION ) { fragments [ definition . name . value ] = definition ; } } const fields = collectFields ( schema , fragments , variableValues , subscriptionType , node . selectionSet ) ; if ( fields . size > 1 ) { const fieldSelectionLists = [ ... fields . values ( ) ] ; const extraFieldSelectionLists = fieldSelectionLists . slice ( 1 ) ; const extraFieldSelections = extraFieldSelectionLists . flat ( ) ; context . reportError ( new GraphQLError ( operationName != null ? `Subscription "${ operationName }" must select only one top level field.` : 'Anonymous Subscription must select only one top level field.' , extraFieldSelections ) ) ; } for ( const fieldNodes of fields . values ( ) ) { const field = fieldNodes [ 0 ] ; const fieldName = field . name . value ; if ( fieldName . startsWith ( '__' ) ) { context . reportError ( new GraphQLError ( operationName != null ? `Subscription "${ operationName }" must not select an introspection top level field.` : 'Anonymous Subscription must not select an introspection top level field.' , fieldNodes ) ) ; } } } } } , } ; }