text: /**
   * Copyright (c) 2015-present, Facebook, Inc.
   *
   * This source code is licensed under the MIT license found in the
   * LICENSE file in the root directory of this source tree.
   *
   * @flow strict
   */
  
  import isInvalid from '../jsutils/isInvalid';
  import objectValues from '../jsutils/objectValues';
  import { astFromValue } from '../utilities/astFromValue';
  import { print } from '../language/printer';
  import {
    GraphQLObjectType,
    GraphQLEnumType,
    GraphQLList,
    GraphQLNonNull,
    isScalarType,
    isObjectType,
    isInterfaceType,
    isUnionType,
    isEnumType,
    isInputObjectType,
    isListType,
    isNonNullType,
    isAbstractType,
    isNamedType,
  } from './definition';
  import { GraphQLString, GraphQLBoolean } from './scalars';
  import { DirectiveLocation } from '../language/directiveLocation';
  import type { GraphQLField } from './definition';
  
  export const __Schema = new GraphQLObjectType({
    name: '__Schema',
    isIntrospection: true,
    description:
      'A GraphQL Schema defines the capabilities of a GraphQL server. It ' +
      'exposes all available types and directives on the server, as well as ' +
      'the entry points for query, mutation, and subscription operations.',
    fields: () => ({
      types: {
        description: 'A list of all types supported by this server.',
        type: GraphQLNonNull(GraphQLList(GraphQLNonNull(__Type))),
        resolve(schema) {
          return objectValues(schema.getTypeMap());
        },
      },
      queryType: {
        description: 'The type that query operations will be rooted at.',
        type: GraphQLNonNull(__Type),
        resolve: schema => schema.getQueryType(),
      },
      mutationType: {
        description:
          'If this server supports mutation, the type that ' +
          'mutation operations will be rooted at.',
        type: __Type,
        resolve: schema => schema.getMutationType(),
      },
      subscriptionType: {
        description:
          'If this server support subscription, the type that ' +
          'subscription operations will be rooted at.',
        type: __Type,
        resolve: schema => schema.getSubscriptionType(),
      },
      directives: {
        description: 'A list of all directives supported by this server.',
        type: GraphQLNonNull(GraphQLList(GraphQLNonNull(__Directive))),
        resolve: schema => schema.getDirectives(),
      },
    }),
  });
  
  export const __Directive = new GraphQLObjectType({
    name: '__Directive',
    isIntrospection: true,
    description:
      'A Directive provides a way to describe alternate runtime execution and ' +
      'type validation behavior in a GraphQL document.' +
      "\n\nIn some cases, you need to provide options to alter GraphQL's " +
      'execution behavior in ways field arguments will not suffice, such as ' +
      'conditionally including or skipping a field. Directives provide this by ' +
      'describing additional information to the executor.',
    fields: () => ({
      name: {
        type: GraphQLNonNull(GraphQLString),
        resolve: obj => obj.name,
      },
      description: {
        type: GraphQLString,
        resolve: obj => obj.description,
      },
      locations: {
        type: GraphQLNonNull(GraphQLList(GraphQLNonNull(__DirectiveLocation))),
        resolve: obj => obj.locations,
      },
      args: {
        type: GraphQLNonNull(GraphQLList(GraphQLNonNull(__InputValue))),
        resolve: directive => directive.args || [],
      },
      // NOTE: the following three fields are deprecated and are no longer part
      // of the GraphQL specification.
      onOperation: {
        deprecationReason: 'Use `locations`.',
        type: GraphQLNonNull(GraphQLBoolean),
        resolve: d =>
          d.locations.indexOf(DirectiveLocation.QUERY) !== -1 ||
          d.locations.indexOf(DirectiveLocation.MUTATION) !== -1 ||
          d.locations.indexOf(DirectiveLocation.SUBSCRIPTION) !== -1,
      },
      onFragment: {
        deprecationReason: 'Use `locations`.',
        type: GraphQLNonNull(GraphQLBoolean),
        resolve: d =>
          d.locations.indexOf(DirectiveLocation.FRAGMENT_SPREAD) !== -1 ||
          d.locations.indexOf(DirectiveLocation.INLINE_FRAGMENT) !== -1 ||
          d.locations.indexOf(DirectiveLocation.FRAGMENT_DEFINITION) !== -1,
      },
      onField: {
        deprecationReason: 'Use `locations`.',
        type: GraphQLNonNull(GraphQLBoolean),
        resolve: d => d.locations.indexOf(DirectiveLocation.FIELD) !== -1,
      },
    }),
  });
  
  export const __DirectiveLocation = new GraphQLEnumType({
    name: '__DirectiveLocation',
    isIntrospection: true,
    description:
      'A Directive can be adjacent to many parts of the GraphQL language, a ' +
      '__DirectiveLocation describes one such possible adjacencies.',
    values: {
      QUERY: {
        value: DirectiveLocation.QUERY,
        description: 'Location adjacent to a query operation.',
      },
      MUTATION: {
        value: DirectiveLocation.MUTATION,
        description: 'Location adjacent to a mutation operation.',
      },
      SUBSCRIPTION: {
        value: DirectiveLocation.SUBSCRIPTION,
        description: 'Location adjacent to a subscription operation.',
      },
      FIELD: {
        value: DirectiveLocation.FIELD,
        description: 'Location adjacent to a field.',
      },
      FRAGMENT_DEFINITION: {
        value: DirectiveLocation.FRAGMENT_DEFINITION,
        description: 'Location adjacent to a fragment definition.',
      },
      FRAGMENT_SPREAD: {
        value: DirectiveLocation.FRAGMENT_SPREAD,
        description: 'Location adjacent to a fragment spread.',
      },
      INLINE_FRAGMENT: {
        value: DirectiveLocation.INLINE_FRAGMENT,
        description: 'Location adjacent to an inline fragment.',
      },
      SCHEMA: {
        value: DirectiveLocation.SCHEMA,
        description: 'Location adjacent to a schema definition.',
      },
      SCALAR: {
        value: DirectiveLocation.SCALAR,
        description: 'Location adjacent to a scalar definition.',
      },
      OBJECT: {
        value: DirectiveLocation.OBJECT,
        description: 'Location adjacent to an object type definition.',
      },
      FIELD_DEFINITION: {
        value: DirectiveLocation.FIELD_DEFINITION,
        description: 'Location adjacent to a field definition.',
      },
      ARGUMENT_DEFINITION: {
        value: DirectiveLocation.ARGUMENT_DEFINITION,
        description: 'Location adjacent to an argument definition.',
      },
      INTERFACE: {
        value: DirectiveLocation.INTERFACE,
        description: 'Location adjacent to an interface definition.',
      },
      UNION: {
        value: DirectiveLocation.UNION,
        description: 'Location adjacent to a union definition.',
      },
      ENUM: {
        value: DirectiveLocation.ENUM,
        description: 'Location adjacent to an enum definition.',
      },
      ENUM_VALUE: {
        value: DirectiveLocation.ENUM_VALUE,
        description: 'Location adjacent to an enum value definition.',
      },
      INPUT_OBJECT: {
        value: DirectiveLocation.INPUT_OBJECT,
        description: 'Location adjacent to an input object type definition.',
      },
      INPUT_FIELD_DEFINITION: {
        value: DirectiveLocation.INPUT_FIELD_DEFINITION,
        description: 'Location adjacent to an input object field definition.',
      },
    },
  });
  
  export const __Type = new GraphQLObjectType({
    name: '__Type',
    isIntrospection: true,
    description:
      'The fundamental unit of any GraphQL Schema is the type. There are ' +
      'many kinds of types in GraphQL as represented by the `__TypeKind` enum.' +
      '\n\nDepending on the kind of a type, certain fields describe ' +
      'information about that type. Scalar types provide no information ' +
      'beyond a name and description, while Enum types provide their values. ' +
      'Object and Interface types provide the fields they describe. Abstract ' +
      'types, Union and Interface, provide the Object types possible ' +
      'at runtime. List and NonNull types compose other types.',
    fields: () => ({
      kind: {
        type: GraphQLNonNull(__TypeKind),
        resolve(type) {
          if (isScalarType(type)) {
            return TypeKind.SCALAR;
          } else if (isObjectType(type)) {
            return TypeKind.OBJECT;
          } else if (isInterfaceType(type)) {
            return TypeKind.INTERFACE;
          } else if (isUnionType(type)) {
            return TypeKind.UNION;
          } else if (isEnumType(type)) {
            return TypeKind.ENUM;
          } else if (isInputObjectType(type)) {
            return TypeKind.INPUT_OBJECT;
          } else if (isListType(type)) {
            return TypeKind.LIST;
          } else if (isNonNullType(type)) {
            return TypeKind.NON_NULL;
          }
          throw new Error('Unknown kind of type: ' + type);
        },
      },
      name: {
        type: GraphQLString,
        resolve: obj => obj.name,
      },
      description: {
        type: GraphQLString,
        resolve: obj => obj.description,
      },
      fields: {
        type: GraphQLList(GraphQLNonNull(__Field)),
        args: {
          includeDeprecated: { type: GraphQLBoolean, defaultValue: false },
        },
        resolve(type, { includeDeprecated }) {
          if (isObjectType(type) || isInterfaceType(type)) {
            let fields = objectValues(type.getFields());
            if (!includeDeprecated) {
              fields = fields.filter(field => !field.deprecationReason);
            }
            return fields;
          }
          return null;
        },
      },
      interfaces: {
        type: GraphQLList(GraphQLNonNull(__Type)),
        resolve(type) {
          if (isObjectType(type)) {
            return type.getInterfaces();
          }
        },
      },
      possibleTypes: {
        type: GraphQLList(GraphQLNonNull(__Type)),
        resolve(type, args, context, { schema }) {
          if (isAbstractType(type)) {
            return schema.getPossibleTypes(type);
          }
        },
      },
      enumValues: {
        type: GraphQLList(GraphQLNonNull(__EnumValue)),
        args: {
          includeDeprecated: { type: GraphQLBoolean, defaultValue: false },
        },
        resolve(type, { includeDeprecated }) {
          if (isEnumType(type)) {
            let values = type.getValues();
            if (!includeDeprecated) {
              values = values.filter(value => !value.deprecationReason);
            }
            return values;
          }
        },
      },
      inputFields: {
        type: GraphQLList(GraphQLNonNull(__InputValue)),
        resolve(type) {
          if (isInputObjectType(type)) {
            return objectValues(type.getFields());
          }
        },
      },
      ofType: {
        type: __Type,
        resolve: obj => obj.ofType,
      },
    }),
  });
  
  export const __Field = new GraphQLObjectType({
    name: '__Field',
    isIntrospection: true,
    description:
      'Object and Interface types are described by a list of Fields, each of ' +
      'which has a name, potentially a list of arguments, and a return type.',
    fields: () => ({
      name: {
        type: GraphQLNonNull(GraphQLString),
        resolve: obj => obj.name,
      },
      description: {
        type: GraphQLString,
        resolve: obj => obj.description,
      },
      args: {
        type: GraphQLNonNull(GraphQLList(GraphQLNonNull(__InputValue))),
        resolve: field => field.args || [],
      },
      type: {
        type: GraphQLNonNull(__Type),
        resolve: obj => obj.type,
      },
      isDeprecated: {
        type: GraphQLNonNull(GraphQLBoolean),
        resolve: obj => obj.isDeprecated,
      },
      deprecationReason: {
        type: GraphQLString,
        resolve: obj => obj.deprecationReason,
      },
    }),
  });
  
  export const __InputValue = new GraphQLObjectType({
    name: '__InputValue',
    isIntrospection: true,
    description:
      'Arguments provided to Fields or Directives and the input fields of an ' +
      'InputObject are represented as Input Values which describe their type ' +
      'and optionally a default value.',
    fields: () => ({
      name: {
        type: GraphQLNonNull(GraphQLString),
        resolve: obj => obj.name,
      },
      description: {
        type: GraphQLString,
        resolve: obj => obj.description,
      },
      type: {
        type: GraphQLNonNull(__Type),
        resolve: obj => obj.type,
      },
      defaultValue: {
        type: GraphQLString,
        description:
          'A GraphQL-formatted string representing the default value for this ' +
          'input value.',
        resolve: inputVal =>
          isInvalid(inputVal.defaultValue)
            ? null
            : print(astFromValue(inputVal.defaultValue, inputVal.type)),
      },
    }),
  });
  
  export const __EnumValue = new GraphQLObjectType({
    name: '__EnumValue',
    isIntrospection: true,
    description:
      'One possible value for a given Enum. Enum values are unique values, not ' +
      'a placeholder for a string or numeric value. However an Enum value is ' +
      'returned in a JSON response as a string.',
    fields: () => ({
      name: {
        type: GraphQLNonNull(GraphQLString),
        resolve: obj => obj.name,
      },
      description: {
        type: GraphQLString,
        resolve: obj => obj.description,
      },
      isDeprecated: {
        type: GraphQLNonNull(GraphQLBoolean),
        resolve: obj => obj.isDeprecated,
      },
      deprecationReason: {
        type: GraphQLString,
        resolve: obj => obj.deprecationReason,
      },
    }),
  });
  
  export const TypeKind = {
    SCALAR: 'SCALAR',
    OBJECT: 'OBJECT',
    INTERFACE: 'INTERFACE',
    UNION: 'UNION',
    ENUM: 'ENUM',
    INPUT_OBJECT: 'INPUT_OBJECT',
    LIST: 'LIST',
    NON_NULL: 'NON_NULL',
  };
  
  export const __TypeKind = new GraphQLEnumType({
    name: '__TypeKind',
    isIntrospection: true,
    description: 'An enum describing what kind of type a given `__Type` is.',
    values: {
      SCALAR: {
        value: TypeKind.SCALAR,
        description: 'Indicates this type is a scalar.',
      },
      OBJECT: {
        value: TypeKind.OBJECT,
        description:
          'Indicates this type is an object. ' +
          '`fields` and `interfaces` are valid fields.',
      },
      INTERFACE: {
        value: TypeKind.INTERFACE,
        description:
          'Indicates this type is an interface. ' +
          '`fields` and `possibleTypes` are valid fields.',
      },
      UNION: {
        value: TypeKind.UNION,
        description:
          'Indicates this type is a union. ' +
          '`possibleTypes` is a valid field.',
      },
      ENUM: {
        value: TypeKind.ENUM,
        description:
          'Indicates this type is an enum. ' + '`enumValues` is a valid field.',
      },
      INPUT_OBJECT: {
        value: TypeKind.INPUT_OBJECT,
        description:
          'Indicates this type is an input object. ' +
          '`inputFields` is a valid field.',
      },
      LIST: {
        value: TypeKind.LIST,
        description:
          'Indicates this type is a list. ' + '`ofType` is a valid field.',
      },
      NON_NULL: {
        value: TypeKind.NON_NULL,
        description:
          'Indicates this type is a non-null. ' + '`ofType` is a valid field.',
      },
    },
  });
  
  /**
   * Note that these are GraphQLField and not GraphQLFieldConfig,
   * so the format for args is different.
   */
  
  export const SchemaMetaFieldDef: GraphQLField<*, *> = {
    name: '__schema',
    type: GraphQLNonNull(__Schema),
    description: 'Access the current type schema of this server.',
    args: [],
    resolve: (source, args, context, { schema }) => schema,
  };
  
  export const TypeMetaFieldDef: GraphQLField<*, *> = {
    name: '__type',
    type: __Type,
    description: 'Request the type information of a single type.',
    args: [{ name: 'name', type: GraphQLNonNull(GraphQLString) }],
    resolve: (source, { name }, context, { schema }) => schema.getType(name),
  };
  
  export const TypeNameMetaFieldDef: GraphQLField<*, *> = {
    name: '__typename',
    type: GraphQLNonNull(GraphQLString),
    description: 'The name of the current Object type at runtime.',
    args: [],
    resolve: (source, args, context, { parentType }) => parentType.name,
  };
  
  export const introspectionTypes: $ReadOnlyArray<*> = [
    __Schema,
    __Directive,
    __DirectiveLocation,
    __Type,
    __Field,
    __InputValue,
    __EnumValue,
    __TypeKind,
  ];
  
  export function isIntrospectionType(type: mixed): boolean %checks {
    return (
      isNamedType(type) &&
      // Would prefer to use introspectionTypes.some(), however %checks needs
      // a simple expression.
      (type.name === __Schema.name ||
        type.name === __Directive.name ||
        type.name === __DirectiveLocation.name ||
        type.name === __Type.name ||
        type.name === __Field.name ||
        type.name === __InputValue.name ||
        type.name === __EnumValue.name ||
        type.name === __TypeKind.name)
    );
  }
  