text: import { parse, isNonNullType, GraphQLError, Kind, valueFromAST, print, isObjectType, isScalarType, isSpecifiedScalarType, isIntrospectionType, printType, specifiedRules, validate, buildSchema, buildClientSchema, Source, isListType, getNamedType, isUnionType, isInterfaceType, GraphQLString, GraphQLNonNull, GraphQLList, isInputObjectType, GraphQLID, GraphQLBoolean, GraphQLFloat, GraphQLInt, GraphQLObjectType, GraphQLInterfaceType, GraphQLInputObjectType, GraphQLDirective, GraphQLUnionType, isEnumType, GraphQLEnumType, GraphQLScalarType, isNamedType, getNullableType, isLeafType, GraphQLSchema, isSpecifiedDirective, isSchema, isInputType, valueFromASTUntyped, isDirective, getDirectiveValues as getDirectiveValues$1, GraphQLSkipDirective, GraphQLIncludeDirective, typeFromAST, isAbstractType, isCompositeType, doTypesOverlap } from 'graphql';
  import AggregateError from 'aggregate-error';
  import { camelCase } from 'camel-case';
  import { mapSchema as mapSchema$1, MapperKind as MapperKind$1 } from '@graphql-tools/utils';
  
  const asArray = (fns) => (Array.isArray(fns) ? fns : fns ? [fns] : []);
  function isEqual(a, b) {
      if (Array.isArray(a) && Array.isArray(b)) {
          if (a.length !== b.length) {
              return false;
          }
          for (let index = 0; index < a.length; index++) {
              if (a[index] !== b[index]) {
                  return false;
              }
          }
          return true;
      }
      return a === b || (!a && !b);
  }
  function isNotEqual(a, b) {
      return !isEqual(a, b);
  }
  function isDocumentString(str) {
      // XXX: is-valid-path or is-glob treat SDL as a valid path
      // (`scalar Date` for example)
      // this why checking the extension is fast enough
      // and prevent from parsing the string in order to find out
      // if the string is a SDL
      if (/\.[a-z0-9]+$/i.test(str)) {
          return false;
      }
      try {
          parse(str);
          return true;
      }
      catch (e) { }
      return false;
  }
  const invalidPathRegex = /[‘“!$%&^<=>`]/;
  function isValidPath(str) {
      return typeof str === 'string' && !invalidPathRegex.test(str);
  }
  function compareStrings(a, b) {
      if (a.toString() < b.toString()) {
          return -1;
      }
      if (a.toString() > b.toString()) {
          return 1;
      }
      return 0;
  }
  function nodeToString(a) {
      if ('alias' in a) {
          return a.alias.value;
      }
      if ('name' in a) {
          return a.name.value;
      }
      return a.kind;
  }
  function compareNodes(a, b, customFn) {
      const aStr = nodeToString(a);
      const bStr = nodeToString(b);
      if (typeof customFn === 'function') {
          return customFn(aStr, bStr);
      }
      return compareStrings(aStr, bStr);
  }
  
  function debugLog(...args) {
      if (process && process.env && process.env.DEBUG && !process.env.GQL_tools_NODEBUG) {
          // tslint:disable-next-line: no-console
          console.log(...args);
      }
  }
  
  const fixWindowsPath = (path) => path.replace(/\\/g, '/');
  
  const flattenArray = (arr) => arr.reduce((acc, next) => acc.concat(Array.isArray(next) ? flattenArray(next) : next), []);
  
  const MAX_ARRAY_LENGTH = 10;
  const MAX_RECURSIVE_DEPTH = 2;
  /**
   * Used to print values in error messages.
   */
  function inspect(value) {
      return formatValue(value, []);
  }
  function formatValue(value, seenValues) {
      switch (typeof value) {
          case 'string':
              return JSON.stringify(value);
          case 'function':
              return value.name ? `[function ${value.name}]` : '[function]';
          case 'object':
              if (value === null) {
                  return 'null';
              }
              return formatObjectValue(value, seenValues);
          default:
              return String(value);
      }
  }
  function formatObjectValue(value, previouslySeenValues) {
      if (previouslySeenValues.indexOf(value) !== -1) {
          return '[Circular]';
      }
      const seenValues = [...previouslySeenValues, value];
      const customInspectFn = getCustomFn(value);
      if (customInspectFn !== undefined) {
          const customValue = customInspectFn.call(value);
          // check for infinite recursion
          if (customValue !== value) {
              return typeof customValue === 'string' ? customValue : formatValue(customValue, seenValues);
          }
      }
      else if (Array.isArray(value)) {
          return formatArray(value, seenValues);
      }
      return formatObject(value, seenValues);
  }
  function formatObject(object, seenValues) {
      const keys = Object.keys(object);
      if (keys.length === 0) {
          return '{}';
      }
      if (seenValues.length > MAX_RECURSIVE_DEPTH) {
          return '[' + getObjectTag(object) + ']';
      }
      const properties = keys.map(key => {
          const value = formatValue(object[key], seenValues);
          return key + ': ' + value;
      });
      return '{ ' + properties.join(', ') + ' }';
  }
  function formatArray(array, seenValues) {
      if (array.length === 0) {
          return '[]';
      }
      if (seenValues.length > MAX_RECURSIVE_DEPTH) {
          return '[Array]';
      }
      const len = Math.min(MAX_ARRAY_LENGTH, array.length);
      const remaining = array.length - len;
      const items = [];
      for (let i = 0; i < len; ++i) {
          items.push(formatValue(array[i], seenValues));
      }
      if (remaining === 1) {
          items.push('... 1 more item');
      }
      else if (remaining > 1) {
          items.push(`... ${remaining.toString(10)} more items`);
      }
      return '[' + items.join(', ') + ']';
  }
  function getCustomFn(obj) {
      if (typeof obj.inspect === 'function') {
          return obj.inspect;
      }
  }
  function getObjectTag(obj) {
      const tag = Object.prototype.toString
          .call(obj)
          .replace(/^\[object /, '')
          .replace(/]$/, '');
      if (tag === 'Object' && typeof obj.constructor === 'function') {
          const name = obj.constructor.name;
          if (typeof name === 'string' && name !== '') {
              return name;
          }
      }
      return tag;
  }
  
  /**
   * Prepares an object map of argument values given a list of argument
   * definitions and list of argument AST nodes.
   *
   * Note: The returned value is a plain Object with a prototype, since it is
   * exposed to user code. Care should be taken to not pull values from the
   * Object prototype.
   */
  function getArgumentValues(def, node, variableValues = {}) {
      var _a;
      const variableMap = Object.entries(variableValues).reduce((prev, [key, value]) => ({
          ...prev,
          [key]: value,
      }), {});
      const coercedValues = {};
      // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
      const argumentNodes = (_a = node.arguments) !== null && _a !== void 0 ? _a : [];
      const argNodeMap = argumentNodes.reduce((prev, arg) => ({
          ...prev,
          [arg.name.value]: arg,
      }), {});
      for (const argDef of def.args) {
          const name = argDef.name;
          const argType = argDef.type;
          const argumentNode = argNodeMap[name];
          if (!argumentNode) {
              if (argDef.defaultValue !== undefined) {
                  coercedValues[name] = argDef.defaultValue;
              }
              else if (isNonNullType(argType)) {
                  throw new GraphQLError(`Argument "${name}" of required type "${inspect(argType)}" ` + 'was not provided.', node);
              }
              continue;
          }
          const valueNode = argumentNode.value;
          let isNull = valueNode.kind === Kind.NULL;
          if (valueNode.kind === Kind.VARIABLE) {
              const variableName = valueNode.name.value;
              if (variableValues == null || !(variableName in variableMap)) {
                  if (argDef.defaultValue !== undefined) {
                      coercedValues[name] = argDef.defaultValue;
                  }
                  else if (isNonNullType(argType)) {
                      throw new GraphQLError(`Argument "${name}" of required type "${inspect(argType)}" ` +
                          `was provided the variable "$${variableName}" which was not provided a runtime value.`, valueNode);
                  }
                  continue;
              }
              isNull = variableValues[variableName] == null;
          }
          if (isNull && isNonNullType(argType)) {
              throw new GraphQLError(`Argument "${name}" of non-null type "${inspect(argType)}" ` + 'must not be null.', valueNode);
          }
          const coercedValue = valueFromAST(valueNode, argType, variableValues);
          if (coercedValue === undefined) {
              // Note: ValuesOfCorrectTypeRule validation should catch this before
              // execution. This is a runtime check to ensure execution does not
              // continue with an invalid argument value.
              throw new GraphQLError(`Argument "${name}" has invalid value ${print(valueNode)}.`, valueNode);
          }
          coercedValues[name] = coercedValue;
      }
      return coercedValues;
  }
  
  function getDirectives(schema, node) {
      const schemaDirectives = schema && schema.getDirectives ? schema.getDirectives() : [];
      const schemaDirectiveMap = schemaDirectives.reduce((schemaDirectiveMap, schemaDirective) => {
          schemaDirectiveMap[schemaDirective.name] = schemaDirective;
          return schemaDirectiveMap;
      }, {});
      let astNodes = [];
      if (node.astNode) {
          astNodes.push(node.astNode);
      }
      if ('extensionASTNodes' in node && node.extensionASTNodes) {
          astNodes = [...astNodes, ...node.extensionASTNodes];
      }
      const result = {};
      astNodes.forEach(astNode => {
          if (astNode.directives) {
              astNode.directives.forEach(directive => {
                  const schemaDirective = schemaDirectiveMap[directive.name.value];
                  if (schemaDirective) {
                      const directiveValue = getDirectiveValues(schemaDirective, astNode);
                      if (schemaDirective.isRepeatable) {
                          if (result[schemaDirective.name]) {
                              result[schemaDirective.name] = result[schemaDirective.name].concat([directiveValue]);
                          }
                          else {
                              result[schemaDirective.name] = [directiveValue];
                          }
                      }
                      else {
                          result[schemaDirective.name] = directiveValue;
                      }
                  }
              });
          }
      });
      return result;
  }
  // graphql-js getDirectiveValues does not handle repeatable directives
  function getDirectiveValues(directiveDef, node) {
      if (node.directives) {
          if (directiveDef.isRepeatable) {
              const directiveNodes = node.directives.filter(directive => directive.name.value === directiveDef.name);
              return directiveNodes.map(directiveNode => getArgumentValues(directiveDef, directiveNode));
          }
          const directiveNode = node.directives.find(directive => directive.name.value === directiveDef.name);
          return getArgumentValues(directiveDef, directiveNode);
      }
  }
  
  function isObjectTypeDefinitionOrExtension(obj) {
      return obj && (obj.kind === 'ObjectTypeDefinition' || obj.kind === 'ObjectTypeExtension');
  }
  function parseDirectiveValue(value) {
      switch (value.kind) {
          case Kind.INT:
              return parseInt(value.value);
          case Kind.FLOAT:
              return parseFloat(value.value);
          case Kind.BOOLEAN:
              return Boolean(value.value);
          case Kind.STRING:
          case Kind.ENUM:
              return value.value;
          case Kind.LIST:
              return value.values.map(v => parseDirectiveValue(v));
          case Kind.OBJECT:
              return value.fields.reduce((prev, v) => ({ ...prev, [v.name.value]: parseDirectiveValue(v.value) }), {});
          case Kind.NULL:
              return null;
          default:
              return null;
      }
  }
  function getFieldsWithDirectives(documentNode) {
      const result = {};
      const allTypes = documentNode.definitions.filter(isObjectTypeDefinitionOrExtension);
      for (const type of allTypes) {
          const typeName = type.name.value;
          for (const field of type.fields) {
              if (field.directives && field.directives.length > 0) {
                  const fieldName = field.name.value;
                  const key = `${typeName}.${fieldName}`;
                  const directives = field.directives.map(d => ({
                      name: d.name.value,
                      args: (d.arguments || []).reduce((prev, arg) => ({ ...prev, [arg.name.value]: parseDirectiveValue(arg.value) }), {}),
                  }));
                  result[key] = directives;
              }
          }
      }
      return result;
  }
  
  function getImplementingTypes(interfaceName, schema) {
      const allTypesMap = schema.getTypeMap();
      const result = [];
      for (const graphqlTypeName in allTypesMap) {
          const graphqlType = allTypesMap[graphqlTypeName];
          if (isObjectType(graphqlType)) {
              const allInterfaces = graphqlType.getInterfaces();
              if (allInterfaces.find(int => int.name === interfaceName)) {
                  result.push(graphqlType.name);
              }
          }
      }
      return result;
  }
  
  function createSchemaDefinition(def, config) {
      const schemaRoot = {};
      if (def.query) {
          schemaRoot.query = def.query.toString();
      }
      if (def.mutation) {
          schemaRoot.mutation = def.mutation.toString();
      }
      if (def.subscription) {
          schemaRoot.subscription = def.subscription.toString();
      }
      const fields = Object.keys(schemaRoot)
          .map(rootType => (schemaRoot[rootType] ? `${rootType}: ${schemaRoot[rootType]}` : null))
          .filter(a => a);
      if (fields.length) {
          return `schema { ${fields.join('\n')} }`;
      }
      if (config && config.force) {
          return ` schema { query: Query } `;
      }
      return undefined;
  }
  
  function printSchemaWithDirectives(schema, _options = {}) {
      var _a;
      const typesMap = schema.getTypeMap();
      const result = [getSchemaDefinition(schema)];
      for (const typeName in typesMap) {
          const type = typesMap[typeName];
          const isPredefinedScalar = isScalarType(type) && isSpecifiedScalarType(type);
          const isIntrospection = isIntrospectionType(type);
          if (isPredefinedScalar || isIntrospection) {
              continue;
          }
          // KAMIL: we might want to turn on descriptions in future
          result.push(print((_a = correctType(typeName, typesMap)) === null || _a === void 0 ? void 0 : _a.astNode));
      }
      const directives = schema.getDirectives();
      for (const directive of directives) {
          if (directive.astNode) {
              result.push(print(directive.astNode));
          }
      }
      return result.join('\n');
  }
  function extendDefinition(type) {
      switch (type.astNode.kind) {
          case Kind.OBJECT_TYPE_DEFINITION:
              return {
                  ...type.astNode,
                  fields: type.astNode.fields.concat(type.extensionASTNodes.reduce((fields, node) => fields.concat(node.fields), [])),
              };
          case Kind.INPUT_OBJECT_TYPE_DEFINITION:
              return {
                  ...type.astNode,
                  fields: type.astNode.fields.concat(type.extensionASTNodes.reduce((fields, node) => fields.concat(node.fields), [])),
              };
          default:
              return type.astNode;
      }
  }
  function correctType(typeName, typesMap) {
      var _a;
      const type = typesMap[typeName];
      type.name = typeName.toString();
      if (type.astNode && type.extensionASTNodes) {
          type.astNode = type.extensionASTNodes ? extendDefinition(type) : type.astNode;
      }
      const doc = parse(printType(type));
      const fixedAstNode = doc.definitions[0];
      const originalAstNode = type === null || type === void 0 ? void 0 : type.astNode;
      if (originalAstNode) {
          fixedAstNode.directives = originalAstNode === null || originalAstNode === void 0 ? void 0 : originalAstNode.directives;
          if (fixedAstNode && 'fields' in fixedAstNode && originalAstNode && 'fields' in originalAstNode) {
              for (const fieldDefinitionNode of fixedAstNode.fields) {
                  const originalFieldDefinitionNode = originalAstNode.fields.find(field => field.name.value === fieldDefinitionNode.name.value);
                  fieldDefinitionNode.directives = originalFieldDefinitionNode === null || originalFieldDefinitionNode === void 0 ? void 0 : originalFieldDefinitionNode.directives;
                  if (fieldDefinitionNode &&
                      'arguments' in fieldDefinitionNode &&
                      originalFieldDefinitionNode &&
                      'arguments' in originalFieldDefinitionNode) {
                      for (const argument of fieldDefinitionNode.arguments) {
                          const originalArgumentNode = (_a = originalFieldDefinitionNode.arguments) === null || _a === void 0 ? void 0 : _a.find(arg => arg.name.value === argument.name.value);
                          argument.directives = originalArgumentNode.directives;
                      }
                  }
              }
          }
          else if (fixedAstNode && 'values' in fixedAstNode && originalAstNode && 'values' in originalAstNode) {
              for (const valueDefinitionNode of fixedAstNode.values) {
                  const originalValueDefinitionNode = originalAstNode.values.find(valueNode => valueNode.name.value === valueDefinitionNode.name.value);
                  valueDefinitionNode.directives = originalValueDefinitionNode === null || originalValueDefinitionNode === void 0 ? void 0 : originalValueDefinitionNode.directives;
              }
          }
      }
      type.astNode = fixedAstNode;
      return type;
  }
  function getSchemaDefinition(schema) {
      if (!Object.getOwnPropertyDescriptor(schema, 'astNode').get && schema.astNode) {
          return print(schema.astNode);
      }
      else {
          return createSchemaDefinition({
              query: schema.getQueryType(),
              mutation: schema.getMutationType(),
              subscription: schema.getSubscriptionType(),
          });
      }
  }
  
  const DEFAULT_EFFECTIVE_RULES = createDefaultRules();
  async function validateGraphQlDocuments(schema, documentFiles, effectiveRules = DEFAULT_EFFECTIVE_RULES) {
      const allFragments = [];
      documentFiles.forEach(documentFile => {
          if (documentFile.document) {
              for (const definitionNode of documentFile.document.definitions) {
                  if (definitionNode.kind === Kind.FRAGMENT_DEFINITION) {
                      allFragments.push(definitionNode);
                  }
              }
          }
      });
      const allErrors = [];
      await Promise.all(documentFiles.map(async (documentFile) => {
          const documentToValidate = {
              kind: Kind.DOCUMENT,
              definitions: [...allFragments, ...documentFile.document.definitions].filter((definition, index, list) => {
                  if (definition.kind === Kind.FRAGMENT_DEFINITION) {
                      const firstIndex = list.findIndex(def => def.kind === Kind.FRAGMENT_DEFINITION && def.name.value === definition.name.value);
                      const isDuplicated = firstIndex !== index;
                      if (isDuplicated) {
                          return false;
                      }
                  }
                  return true;
              }),
          };
          const errors = validate(schema, documentToValidate, effectiveRules);
          if (errors.length > 0) {
              allErrors.push({
                  filePath: documentFile.location,
                  errors,
              });
          }
      }));
      return allErrors;
  }
  function checkValidationErrors(loadDocumentErrors) {
      if (loadDocumentErrors.length > 0) {
          const errors = [];
          for (const loadDocumentError of loadDocumentErrors) {
              for (const graphQLError of loadDocumentError.errors) {
                  const error = new Error();
                  error.name = 'GraphQLDocumentError';
                  error.message = `${error.name}: ${graphQLError.message}`;
                  error.stack = error.message;
                  graphQLError.locations.forEach(location => (error.stack += `\n    at ${loadDocumentError.filePath}:${location.line}:${location.column}`));
                  errors.push(error);
              }
          }
          throw new AggregateError(errors);
      }
  }
  function createDefaultRules() {
      const ignored = ['NoUnusedFragmentsRule', 'NoUnusedVariablesRule', 'KnownDirectivesRule'];
      // GraphQL v14 has no Rule suffix in function names
      // Adding `*Rule` makes validation backwards compatible
      ignored.forEach(rule => {
          ignored.push(rule.replace(/Rule$/, ''));
      });
      return specifiedRules.filter((f) => !ignored.includes(f.name));
  }
  
  function buildFixedSchema(schema, options) {
      return buildSchema(printSchemaWithDirectives(schema, options), {
          noLocation: true,
          ...(options || {}),
      });
  }
  function fixSchemaAst(schema, options) {
      let schemaWithValidAst;
      if (!schema.astNode) {
          Object.defineProperty(schema, 'astNode', {
              get() {
                  if (!schemaWithValidAst) {
                      schemaWithValidAst = buildFixedSchema(schema, options);
                  }
                  return schemaWithValidAst.astNode;
              },
          });
      }
      if (!schema.extensionASTNodes) {
          Object.defineProperty(schema, 'extensionASTNodes', {
              get() {
                  if (!schemaWithValidAst) {
                      schemaWithValidAst = buildFixedSchema(schema, options);
                  }
                  return schemaWithValidAst.extensionASTNodes;
              },
          });
      }
      return schema;
  }
  
  function stripBOM(content) {
      content = content.toString();
      // Remove byte order marker. This catches EF BB BF (the UTF-8 BOM)
      // because the buffer-to-string conversion in `fs.readFileSync()`
      // translates it to FEFF, the UTF-16 BOM.
      if (content.charCodeAt(0) === 0xfeff) {
          content = content.slice(1);
      }
      return content;
  }
  function parseBOM(content) {
      return JSON.parse(stripBOM(content));
  }
  function parseGraphQLJSON(location, jsonContent, options) {
      let parsedJson = parseBOM(jsonContent);
      if (parsedJson.data) {
          parsedJson = parsedJson.data;
      }
      if (parsedJson.kind === 'Document') {
          const document = parsedJson;
          return {
              location,
              document,
          };
      }
      else if (parsedJson.__schema) {
          const schema = buildClientSchema(parsedJson, options);
          const rawSDL = printSchemaWithDirectives(schema, options);
          return {
              location,
              document: parse(rawSDL, options),
              rawSDL,
              schema,
          };
      }
      throw new Error(`Not valid JSON content`);
  }
  
  function parseGraphQLSDL(location, rawSDL, options) {
      let document;
      try {
          document = parse(new Source(rawSDL, location), options);
      }
      catch (e) {
          if (e.message.includes('EOF')) {
              document = {
                  kind: Kind.DOCUMENT,
                  definitions: [],
              };
          }
          else {
              throw e;
          }
      }
      return {
          location,
          document,
          rawSDL,
      };
  }
  
  /**
   * Get all GraphQL types from schema without:
   *
   * - Query, Mutation, Subscription objects
   * - Internal scalars added by parser
   *
   * @param schema
   */
  function getUserTypesFromSchema(schema) {
      const allTypesMap = schema.getTypeMap();
      // tslint:disable-next-line: no-unnecessary-local-variable
      const modelTypes = Object.values(allTypesMap).filter((graphqlType) => {
          if (isObjectType(graphqlType)) {
              // Filter out private types
              if (graphqlType.name.startsWith('__')) {
                  return false;
              }
              if (schema.getMutationType() && graphqlType.name === schema.getMutationType().name) {
                  return false;
              }
              if (schema.getQueryType() && graphqlType.name === schema.getQueryType().name) {
                  return false;
              }
              if (schema.getSubscriptionType() && graphqlType.name === schema.getSubscriptionType().name) {
                  return false;
              }
              return true;
          }
          return false;
      });
      return modelTypes;
  }
  
  let operationVariables = [];
  let fieldTypeMap = new Map();
  function addOperationVariable(variable) {
      operationVariables.push(variable);
  }
  function resetOperationVariables() {
      operationVariables = [];
  }
  function resetFieldMap() {
      fieldTypeMap = new Map();
  }
  function buildOperationName(name) {
      return camelCase(name);
  }
  function buildOperationNodeForField({ schema, kind, field, models, ignore, depthLimit, circularReferenceDepth, argNames, selectedFields = true, }) {
      resetOperationVariables();
      resetFieldMap();
      const operationNode = buildOperationAndCollectVariables({
          schema,
          fieldName: field,
          kind,
          models: models || [],
          ignore: ignore || [],
          depthLimit: depthLimit || Infinity,
          circularReferenceDepth: circularReferenceDepth || 1,
          argNames,
          selectedFields,
      });
      // attach variables
      operationNode.variableDefinitions = [...operationVariables];
      resetOperationVariables();
      resetFieldMap();
      return operationNode;
  }
  function buildOperationAndCollectVariables({ schema, fieldName, kind, models, ignore, depthLimit, circularReferenceDepth, argNames, selectedFields, }) {
      const typeMap = {
          query: schema.getQueryType(),
          mutation: schema.getMutationType(),
          subscription: schema.getSubscriptionType(),
      };
      const type = typeMap[kind];
      const field = type.getFields()[fieldName];
      const operationName = buildOperationName(`${fieldName}_${kind}`);
      if (field.args) {
          field.args.forEach(arg => {
              const argName = arg.name;
              if (!argNames || argNames.includes(argName)) {
                  addOperationVariable(resolveVariable(arg, argName));
              }
          });
      }
      return {
          kind: Kind.OPERATION_DEFINITION,
          operation: kind,
          name: {
              kind: 'Name',
              value: operationName,
          },
          variableDefinitions: [],
          selectionSet: {
              kind: Kind.SELECTION_SET,
              selections: [
                  resolveField({
                      type,
                      field,
                      models,
                      firstCall: true,
                      path: [],
                      ancestors: [],
                      ignore,
                      depthLimit,
                      circularReferenceDepth,
                      schema,
                      depth: 0,
                      argNames,
                      selectedFields,
                  }),
              ],
          },
      };
  }
  function resolveSelectionSet({ parent, type, models, firstCall, path, ancestors, ignore, depthLimit, circularReferenceDepth, schema, depth, argNames, selectedFields, }) {
      if (typeof selectedFields === 'boolean' && depth > depthLimit) {
          return;
      }
      if (isUnionType(type)) {
          const types = type.getTypes();
          return {
              kind: Kind.SELECTION_SET,
              selections: types
                  .filter(t => !hasCircularRef([...ancestors, t], {
                  depth: circularReferenceDepth,
              }))
                  .map(t => {
                  return {
                      kind: Kind.INLINE_FRAGMENT,
                      typeCondition: {
                          kind: Kind.NAMED_TYPE,
                          name: {
                              kind: Kind.NAME,
                              value: t.name,
                          },
                      },
                      selectionSet: resolveSelectionSet({
                          parent: type,
                          type: t,
                          models,
                          path,
                          ancestors,
                          ignore,
                          depthLimit,
                          circularReferenceDepth,
                          schema,
                          depth,
                          argNames,
                          selectedFields,
                      }),
                  };
              })
                  .filter(fragmentNode => { var _a, _b; return ((_b = (_a = fragmentNode === null || fragmentNode === void 0 ? void 0 : fragmentNode.selectionSet) === null || _a === void 0 ? void 0 : _a.selections) === null || _b === void 0 ? void 0 : _b.length) > 0; }),
          };
      }
      if (isInterfaceType(type)) {
          const types = Object.values(schema.getTypeMap()).filter((t) => isObjectType(t) && t.getInterfaces().includes(type));
          return {
              kind: Kind.SELECTION_SET,
              selections: types
                  .filter(t => !hasCircularRef([...ancestors, t], {
                  depth: circularReferenceDepth,
              }))
                  .map(t => {
                  return {
                      kind: Kind.INLINE_FRAGMENT,
                      typeCondition: {
                          kind: Kind.NAMED_TYPE,
                          name: {
                              kind: Kind.NAME,
                              value: t.name,
                          },
                      },
                      selectionSet: resolveSelectionSet({
                          parent: type,
                          type: t,
                          models,
                          path,
                          ancestors,
                          ignore,
                          depthLimit,
                          circularReferenceDepth,
                          schema,
                          depth,
                          argNames,
                          selectedFields,
                      }),
                  };
              })
                  .filter(fragmentNode => { var _a, _b; return ((_b = (_a = fragmentNode === null || fragmentNode === void 0 ? void 0 : fragmentNode.selectionSet) === null || _a === void 0 ? void 0 : _a.selections) === null || _b === void 0 ? void 0 : _b.length) > 0; }),
          };
      }
      if (isObjectType(type)) {
          const isIgnored = ignore.includes(type.name) || ignore.includes(`${parent.name}.${path[path.length - 1]}`);
          const isModel = models.includes(type.name);
          if (!firstCall && isModel && !isIgnored) {
              return {
                  kind: Kind.SELECTION_SET,
                  selections: [
                      {
                          kind: Kind.FIELD,
                          name: {
                              kind: Kind.NAME,
                              value: 'id',
                          },
                      },
                  ],
              };
          }
          const fields = type.getFields();
          return {
              kind: Kind.SELECTION_SET,
              selections: Object.keys(fields)
                  .filter(fieldName => {
                  return !hasCircularRef([...ancestors, getNamedType(fields[fieldName].type)], {
                      depth: circularReferenceDepth,
                  });
              })
                  .map(fieldName => {
                  const selectedSubFields = typeof selectedFields === 'object' ? selectedFields[fieldName] : true;
                  if (selectedSubFields) {
                      return resolveField({
                          type: type,
                          field: fields[fieldName],
                          models,
                          path: [...path, fieldName],
                          ancestors,
                          ignore,
                          depthLimit,
                          circularReferenceDepth,
                          schema,
                          depth,
                          argNames,
                          selectedFields: selectedSubFields,
                      });
                  }
              })
                  .filter(f => {
                  var _a, _b;
                  if (f) {
                      if ('selectionSet' in f) {
                          return (_b = (_a = f.selectionSet) === null || _a === void 0 ? void 0 : _a.selections) === null || _b === void 0 ? void 0 : _b.length;
                      }
                      else {
                          return true;
                      }
                  }
                  return false;
              }),
          };
      }
  }
  function resolveVariable(arg, name) {
      function resolveVariableType(type) {
          if (isListType(type)) {
              return {
                  kind: Kind.LIST_TYPE,
                  type: resolveVariableType(type.ofType),
              };
          }
          if (isNonNullType(type)) {
              return {
                  kind: Kind.NON_NULL_TYPE,
                  type: resolveVariableType(type.ofType),
              };
          }
          return {
              kind: Kind.NAMED_TYPE,
              name: {
                  kind: Kind.NAME,
                  value: type.name,
              },
          };
      }
      return {
          kind: Kind.VARIABLE_DEFINITION,
          variable: {
              kind: Kind.VARIABLE,
              name: {
                  kind: Kind.NAME,
                  value: name || arg.name,
              },
          },
          type: resolveVariableType(arg.type),
      };
  }
  function getArgumentName(name, path) {
      return camelCase([...path, name].join('_'));
  }
  function resolveField({ type, field, models, firstCall, path, ancestors, ignore, depthLimit, circularReferenceDepth, schema, depth, argNames, selectedFields, }) {
      const namedType = getNamedType(field.type);
      let args = [];
      let removeField = false;
      if (field.args && field.args.length) {
          args = field.args
              .map(arg => {
              const argumentName = getArgumentName(arg.name, path);
              if (argNames && !argNames.includes(argumentName)) {
                  if (isNonNullType(arg.type)) {
                      removeField = true;
                  }
                  return null;
              }
              if (!firstCall) {
                  addOperationVariable(resolveVariable(arg, argumentName));
              }
              return {
                  kind: Kind.ARGUMENT,
                  name: {
                      kind: Kind.NAME,
                      value: arg.name,
                  },
                  value: {
                      kind: Kind.VARIABLE,
                      name: {
                          kind: Kind.NAME,
                          value: getArgumentName(arg.name, path),
                      },
                  },
              };
          })
              .filter(Boolean);
      }
      if (removeField) {
          return null;
      }
      let fieldName = field.name;
      if (fieldTypeMap.has(fieldName) && fieldTypeMap.get(fieldName) !== field.type.toString()) {
          fieldName += field.type.toString().replace('!', 'NonNull');
      }
      fieldTypeMap.set(fieldName, field.type.toString());
      if (!isScalarType(namedType)) {
          return {
              kind: Kind.FIELD,
              name: {
                  kind: Kind.NAME,
                  value: field.name,
              },
              ...(fieldName !== field.name && { alias: { kind: Kind.NAME, value: fieldName } }),
              selectionSet: resolveSelectionSet({
                  parent: type,
                  type: namedType,
                  models,
                  firstCall,
                  path: [...path, field.name],
                  ancestors: [...ancestors, type],
                  ignore,
                  depthLimit,
                  circularReferenceDepth,
                  schema,
                  depth: depth + 1,
                  argNames,
                  selectedFields,
              }) || undefined,
              arguments: args,
          };
      }
      return {
          kind: Kind.FIELD,
          name: {
              kind: Kind.NAME,
              value: field.name,
          },
          ...(fieldName !== field.name && { alias: { kind: Kind.NAME, value: fieldName } }),
          arguments: args,
      };
  }
  function hasCircularRef(types, config = {
      depth: 1,
  }) {
      const type = types[types.length - 1];
      if (isScalarType(type)) {
          return false;
      }
      const size = types.filter(t => t.name === type.name).length;
      return size > config.depth;
  }
  
  var VisitSchemaKind;
  (function (VisitSchemaKind) {
      VisitSchemaKind["TYPE"] = "VisitSchemaKind.TYPE";
      VisitSchemaKind["SCALAR_TYPE"] = "VisitSchemaKind.SCALAR_TYPE";
      VisitSchemaKind["ENUM_TYPE"] = "VisitSchemaKind.ENUM_TYPE";
      VisitSchemaKind["COMPOSITE_TYPE"] = "VisitSchemaKind.COMPOSITE_TYPE";
      VisitSchemaKind["OBJECT_TYPE"] = "VisitSchemaKind.OBJECT_TYPE";
      VisitSchemaKind["INPUT_OBJECT_TYPE"] = "VisitSchemaKind.INPUT_OBJECT_TYPE";
      VisitSchemaKind["ABSTRACT_TYPE"] = "VisitSchemaKind.ABSTRACT_TYPE";
      VisitSchemaKind["UNION_TYPE"] = "VisitSchemaKind.UNION_TYPE";
      VisitSchemaKind["INTERFACE_TYPE"] = "VisitSchemaKind.INTERFACE_TYPE";
      VisitSchemaKind["ROOT_OBJECT"] = "VisitSchemaKind.ROOT_OBJECT";
      VisitSchemaKind["QUERY"] = "VisitSchemaKind.QUERY";
      VisitSchemaKind["MUTATION"] = "VisitSchemaKind.MUTATION";
      VisitSchemaKind["SUBSCRIPTION"] = "VisitSchemaKind.SUBSCRIPTION";
  })(VisitSchemaKind || (VisitSchemaKind = {}));
  var MapperKind;
  (function (MapperKind) {
      MapperKind["TYPE"] = "MapperKind.TYPE";
      MapperKind["SCALAR_TYPE"] = "MapperKind.SCALAR_TYPE";
      MapperKind["ENUM_TYPE"] = "MapperKind.ENUM_TYPE";
      MapperKind["COMPOSITE_TYPE"] = "MapperKind.COMPOSITE_TYPE";
      MapperKind["OBJECT_TYPE"] = "MapperKind.OBJECT_TYPE";
      MapperKind["INPUT_OBJECT_TYPE"] = "MapperKind.INPUT_OBJECT_TYPE";
      MapperKind["ABSTRACT_TYPE"] = "MapperKind.ABSTRACT_TYPE";
      MapperKind["UNION_TYPE"] = "MapperKind.UNION_TYPE";
      MapperKind["INTERFACE_TYPE"] = "MapperKind.INTERFACE_TYPE";
      MapperKind["ROOT_OBJECT"] = "MapperKind.ROOT_OBJECT";
      MapperKind["QUERY"] = "MapperKind.QUERY";
      MapperKind["MUTATION"] = "MapperKind.MUTATION";
      MapperKind["SUBSCRIPTION"] = "MapperKind.SUBSCRIPTION";
      MapperKind["DIRECTIVE"] = "MapperKind.DIRECTIVE";
      MapperKind["FIELD"] = "MapperKind.FIELD";
      MapperKind["COMPOSITE_FIELD"] = "MapperKind.COMPOSITE_FIELD";
      MapperKind["OBJECT_FIELD"] = "MapperKind.OBJECT_FIELD";
      MapperKind["ROOT_FIELD"] = "MapperKind.ROOT_FIELD";
      MapperKind["QUERY_ROOT_FIELD"] = "MapperKind.QUERY_ROOT_FIELD";
      MapperKind["MUTATION_ROOT_FIELD"] = "MapperKind.MUTATION_ROOT_FIELD";
      MapperKind["SUBSCRIPTION_ROOT_FIELD"] = "MapperKind.SUBSCRIPTION_ROOT_FIELD";
      MapperKind["INTERFACE_FIELD"] = "MapperKind.INTERFACE_FIELD";
      MapperKind["INPUT_OBJECT_FIELD"] = "MapperKind.INPUT_OBJECT_FIELD";
      MapperKind["ARGUMENT"] = "MapperKind.ARGUMENT";
      MapperKind["ENUM_VALUE"] = "MapperKind.ENUM_VALUE";
  })(MapperKind || (MapperKind = {}));
  
  function createNamedStub(name, type) {
      let constructor;
      if (type === 'object') {
          constructor = GraphQLObjectType;
      }
      else if (type === 'interface') {
          constructor = GraphQLInterfaceType;
      }
      else {
          constructor = GraphQLInputObjectType;
      }
      return new constructor({
          name,
          fields: {
              __fake: {
                  type: GraphQLString,
              },
          },
      });
  }
  function createStub(node, type) {
      switch (node.kind) {
          case Kind.LIST_TYPE:
              return new GraphQLList(createStub(node.type, type));
          case Kind.NON_NULL_TYPE:
              return new GraphQLNonNull(createStub(node.type, type));
          default:
              if (type === 'output') {
                  return createNamedStub(node.name.value, 'object');
              }
              return createNamedStub(node.name.value, 'input');
      }
  }
  function isNamedStub(type) {
      if (isObjectType(type) || isInterfaceType(type) || isInputObjectType(type)) {
          const fields = type.getFields();
          const fieldNames = Object.keys(fields);
          return fieldNames.length === 1 && fields[fieldNames[0]].name === '__fake';
      }
      return false;
  }
  function getBuiltInForStub(type) {
      switch (type.name) {
          case GraphQLInt.name:
              return GraphQLInt;
          case GraphQLFloat.name:
              return GraphQLFloat;
          case GraphQLString.name:
              return GraphQLString;
          case GraphQLBoolean.name:
              return GraphQLBoolean;
          case GraphQLID.name:
              return GraphQLID;
          default:
              return type;
      }
  }
  
  function rewireTypes(originalTypeMap, directives, options = {
      skipPruning: false,
  }) {
      const newTypeMap = Object.create(null);
      Object.keys(originalTypeMap).forEach(typeName => {
          const namedType = originalTypeMap[typeName];
          if (namedType == null || typeName.startsWith('__')) {
              return;
          }
          const newName = namedType.name;
          if (newName.startsWith('__')) {
              return;
          }
          if (newTypeMap[newName] != null) {
              throw new Error(`Duplicate schema type name ${newName}`);
          }
          newTypeMap[newName] = namedType;
      });
      Object.keys(newTypeMap).forEach(typeName => {
          newTypeMap[typeName] = rewireNamedType(newTypeMap[typeName]);
      });
      const newDirectives = directives.map(directive => rewireDirective(directive));
      // TODO:
      // consider removing the default level of pruning in v7,
      // see comments below on the pruneTypes function.
      return options.skipPruning
          ? {
              typeMap: newTypeMap,
              directives: newDirectives,
          }
          : pruneTypes(newTypeMap, newDirectives);
      function rewireDirective(directive) {
          const directiveConfig = directive.toConfig();
          directiveConfig.args = rewireArgs(directiveConfig.args);
          return new GraphQLDirective(directiveConfig);
      }
      function rewireArgs(args) {
          const rewiredArgs = {};
          Object.keys(args).forEach(argName => {
              const arg = args[argName];
              const rewiredArgType = rewireType(arg.type);
              if (rewiredArgType != null) {
                  arg.type = rewiredArgType;
                  rewiredArgs[argName] = arg;
              }
          });
          return rewiredArgs;
      }
      function rewireNamedType(type) {
          if (isObjectType(type)) {
              const config = type.toConfig();
              const newConfig = {
                  ...config,
                  fields: () => rewireFields(config.fields),
                  interfaces: () => rewireNamedTypes(config.interfaces),
              };
              return new GraphQLObjectType(newConfig);
          }
          else if (isInterfaceType(type)) {
              const config = type.toConfig();
              const newConfig = {
                  ...config,
                  fields: () => rewireFields(config.fields),
              };
              if ('interfaces' in newConfig) {
                  newConfig.interfaces = () => rewireNamedTypes(config.interfaces);
              }
              return new GraphQLInterfaceType(newConfig);
          }
          else if (isUnionType(type)) {
              const config = type.toConfig();
              const newConfig = {
                  ...config,
                  types: () => rewireNamedTypes(config.types),
              };
              return new GraphQLUnionType(newConfig);
          }
          else if (isInputObjectType(type)) {
              const config = type.toConfig();
              const newConfig = {
                  ...config,
                  fields: () => rewireInputFields(config.fields),
              };
              return new GraphQLInputObjectType(newConfig);
          }
          else if (isEnumType(type)) {
              const enumConfig = type.toConfig();
              return new GraphQLEnumType(enumConfig);
          }
          else if (isScalarType(type)) {
              if (isSpecifiedScalarType(type)) {
                  return type;
              }
              const scalarConfig = type.toConfig();
              return new GraphQLScalarType(scalarConfig);
          }
          throw new Error(`Unexpected schema type: ${type}`);
      }
      function rewireFields(fields) {
          const rewiredFields = {};
          Object.keys(fields).forEach(fieldName => {
              const field = fields[fieldName];
              const rewiredFieldType = rewireType(field.type);
              if (rewiredFieldType != null) {
                  field.type = rewiredFieldType;
                  field.args = rewireArgs(field.args);
                  rewiredFields[fieldName] = field;
              }
          });
          return rewiredFields;
      }
      function rewireInputFields(fields) {
          const rewiredFields = {};
          Object.keys(fields).forEach(fieldName => {
              const field = fields[fieldName];
              const rewiredFieldType = rewireType(field.type);
              if (rewiredFieldType != null) {
                  field.type = rewiredFieldType;
                  rewiredFields[fieldName] = field;
              }
          });
          return rewiredFields;
      }
      function rewireNamedTypes(namedTypes) {
          const rewiredTypes = [];
          namedTypes.forEach(namedType => {
              const rewiredType = rewireType(namedType);
              if (rewiredType != null) {
                  rewiredTypes.push(rewiredType);
              }
          });
          return rewiredTypes;
      }
      function rewireType(type) {
          if (isListType(type)) {
              const rewiredType = rewireType(type.ofType);
              return rewiredType != null ? new GraphQLList(rewiredType) : null;
          }
          else if (isNonNullType(type)) {
              const rewiredType = rewireType(type.ofType);
              return rewiredType != null ? new GraphQLNonNull(rewiredType) : null;
          }
          else if (isNamedType(type)) {
              let rewiredType = originalTypeMap[type.name];
              if (rewiredType === undefined) {
                  rewiredType = isNamedStub(type) ? getBuiltInForStub(type) : type;
                  newTypeMap[rewiredType.name] = rewiredType;
              }
              return rewiredType != null ? newTypeMap[rewiredType.name] : null;
          }
          return null;
      }
  }
  // TODO:
  // consider removing the default level of pruning in v7
  //
  // Pruning during mapSchema limits the ability to create an unpruned schema, which may be of use
  // to some library users. pruning is now recommended via the dedicated pruneSchema function
  // which does not force pruning on library users and gives granular control in terms of pruning
  // types.
  function pruneTypes(typeMap, directives) {
      const newTypeMap = {};
      const implementedInterfaces = {};
      Object.keys(typeMap).forEach(typeName => {
          const namedType = typeMap[typeName];
          if ('getInterfaces' in namedType) {
              namedType.getInterfaces().forEach(iface => {
                  implementedInterfaces[iface.name] = true;
              });
          }
      });
      let prunedTypeMap = false;
      const typeNames = Object.keys(typeMap);
      for (let i = 0; i < typeNames.length; i++) {
          const typeName = typeNames[i];
          const type = typeMap[typeName];
          if (isObjectType(type) || isInputObjectType(type)) {
              // prune types with no fields
              if (Object.keys(type.getFields()).length) {
                  newTypeMap[typeName] = type;
              }
              else {
                  prunedTypeMap = true;
              }
          }
          else if (isUnionType(type)) {
              // prune unions without underlying types
              if (type.getTypes().length) {
                  newTypeMap[typeName] = type;
              }
              else {
                  prunedTypeMap = true;
              }
          }
          else if (isInterfaceType(type)) {
              // prune interfaces without fields or without implementations
              if (Object.keys(type.getFields()).length && implementedInterfaces[type.name]) {
                  newTypeMap[typeName] = type;
              }
              else {
                  prunedTypeMap = true;
              }
          }
          else {
              newTypeMap[typeName] = type;
          }
      }
      // every prune requires another round of healing
      return prunedTypeMap ? rewireTypes(newTypeMap, directives) : { typeMap, directives };
  }
  
  function transformInputValue(type, value, transformer) {
      if (value == null) {
          return value;
      }
      const nullableType = getNullableType(type);
      if (isLeafType(nullableType)) {
          return transformer(nullableType, value);
      }
      else if (isListType(nullableType)) {
          return value.map((listMember) => transformInputValue(nullableType.ofType, listMember, transformer));
      }
      else if (isInputObjectType(nullableType)) {
          const fields = nullableType.getFields();
          const newValue = {};
          Object.keys(value).forEach(key => {
              newValue[key] = transformInputValue(fields[key].type, value[key], transformer);
          });
          return newValue;
      }
      // unreachable, no other possible return value
  }
  function serializeInputValue(type, value) {
      return transformInputValue(type, value, (t, v) => t.serialize(v));
  }
  function parseInputValue(type, value) {
      return transformInputValue(type, value, (t, v) => t.parseValue(v));
  }
  function parseInputValueLiteral(type, value) {
      return transformInputValue(type, value, (t, v) => t.parseLiteral(v, {}));
  }
  
  function mapSchema(schema, schemaMapper = {}) {
      const originalTypeMap = schema.getTypeMap();
      let newTypeMap = mapDefaultValues(originalTypeMap, schema, serializeInputValue);
      newTypeMap = mapTypes(newTypeMap, schema, schemaMapper, type => isLeafType(type));
      newTypeMap = mapEnumValues(newTypeMap, schema, schemaMapper);
      newTypeMap = mapDefaultValues(newTypeMap, schema, parseInputValue);
      newTypeMap = mapTypes(newTypeMap, schema, schemaMapper, type => !isLeafType(type));
      newTypeMap = mapFields(newTypeMap, schema, schemaMapper);
      newTypeMap = mapArguments(newTypeMap, schema, schemaMapper);
      const originalDirectives = schema.getDirectives();
      const newDirectives = mapDirectives(originalDirectives, schema, schemaMapper);
      const queryType = schema.getQueryType();
      const mutationType = schema.getMutationType();
      const subscriptionType = schema.getSubscriptionType();
      const newQueryTypeName = queryType != null ? (newTypeMap[queryType.name] != null ? newTypeMap[queryType.name].name : undefined) : undefined;
      const newMutationTypeName = mutationType != null
          ? newTypeMap[mutationType.name] != null
              ? newTypeMap[mutationType.name].name
              : undefined
          : undefined;
      const newSubscriptionTypeName = subscriptionType != null
          ? newTypeMap[subscriptionType.name] != null
              ? newTypeMap[subscriptionType.name].name
              : undefined
          : undefined;
      const { typeMap, directives } = rewireTypes(newTypeMap, newDirectives);
      return new GraphQLSchema({
          ...schema.toConfig(),
          query: newQueryTypeName ? typeMap[newQueryTypeName] : undefined,
          mutation: newMutationTypeName ? typeMap[newMutationTypeName] : undefined,
          subscription: newSubscriptionTypeName != null ? typeMap[newSubscriptionTypeName] : undefined,
          types: Object.keys(typeMap).map(typeName => typeMap[typeName]),
          directives,
      });
  }
  function mapTypes(originalTypeMap, schema, schemaMapper, testFn = () => true) {
      const newTypeMap = {};
      Object.keys(originalTypeMap).forEach(typeName => {
          if (!typeName.startsWith('__')) {
              const originalType = originalTypeMap[typeName];
              if (originalType == null || !testFn(originalType)) {
                  newTypeMap[typeName] = originalType;
                  return;
              }
              const typeMapper = getTypeMapper(schema, schemaMapper, typeName);
              if (typeMapper == null) {
                  newTypeMap[typeName] = originalType;
                  return;
              }
              const maybeNewType = typeMapper(originalType, schema);
              if (maybeNewType === undefined) {
                  newTypeMap[typeName] = originalType;
                  return;
              }
              newTypeMap[typeName] = maybeNewType;
          }
      });
      return newTypeMap;
  }
  function mapEnumValues(originalTypeMap, schema, schemaMapper) {
      const enumValueMapper = getEnumValueMapper(schemaMapper);
      if (!enumValueMapper) {
          return originalTypeMap;
      }
      return mapTypes(originalTypeMap, schema, {
          [MapperKind.ENUM_TYPE]: type => {
              const config = type.toConfig();
              const originalEnumValueConfigMap = config.values;
              const newEnumValueConfigMap = {};
              Object.keys(originalEnumValueConfigMap).forEach(enumValueName => {
                  const originalEnumValueConfig = originalEnumValueConfigMap[enumValueName];
                  const mappedEnumValue = enumValueMapper(originalEnumValueConfig, type.name, schema);
                  if (mappedEnumValue === undefined) {
                      newEnumValueConfigMap[enumValueName] = originalEnumValueConfig;
                  }
                  else if (Array.isArray(mappedEnumValue)) {
                      const [newEnumValueName, newEnumValueConfig] = mappedEnumValue;
                      newEnumValueConfigMap[newEnumValueName] = newEnumValueConfig;
                  }
                  else if (mappedEnumValue !== null) {
                      newEnumValueConfigMap[enumValueName] = mappedEnumValue;
                  }
              });
              return new GraphQLEnumType({
                  ...config,
                  values: newEnumValueConfigMap,
              });
          },
      }, type => isEnumType(type));
  }
  function mapDefaultValues(originalTypeMap, schema, fn) {
      const newTypeMap = mapArguments(originalTypeMap, schema, {
          [MapperKind.ARGUMENT]: argumentConfig => {
              if (argumentConfig.defaultValue === undefined) {
                  return argumentConfig;
              }
              const maybeNewType = getNewType(originalTypeMap, argumentConfig.type);
              if (maybeNewType != null) {
                  return {
                      ...argumentConfig,
                      defaultValue: fn(maybeNewType, argumentConfig.defaultValue),
                  };
              }
          },
      });
      return mapFields(newTypeMap, schema, {
          [MapperKind.INPUT_OBJECT_FIELD]: inputFieldConfig => {
              if (inputFieldConfig.defaultValue === undefined) {
                  return inputFieldConfig;
              }
              const maybeNewType = getNewType(newTypeMap, inputFieldConfig.type);
              if (maybeNewType != null) {
                  return {
                      ...inputFieldConfig,
                      defaultValue: fn(maybeNewType, inputFieldConfig.defaultValue),
                  };
              }
          },
      });
  }
  function getNewType(newTypeMap, type) {
      if (isListType(type)) {
          const newType = getNewType(newTypeMap, type.ofType);
          return newType != null ? new GraphQLList(newType) : null;
      }
      else if (isNonNullType(type)) {
          const newType = getNewType(newTypeMap, type.ofType);
          return newType != null ? new GraphQLNonNull(newType) : null;
      }
      else if (isNamedType(type)) {
          const newType = newTypeMap[type.name];
          return newType != null ? newType : null;
      }
      return null;
  }
  function mapFields(originalTypeMap, schema, schemaMapper) {
      const newTypeMap = {};
      Object.keys(originalTypeMap).forEach(typeName => {
          if (!typeName.startsWith('__')) {
              const originalType = originalTypeMap[typeName];
              if (!isObjectType(originalType) && !isInterfaceType(originalType) && !isInputObjectType(originalType)) {
                  newTypeMap[typeName] = originalType;
                  return;
              }
              const fieldMapper = getFieldMapper(schema, schemaMapper, typeName);
              if (fieldMapper == null) {
                  newTypeMap[typeName] = originalType;
                  return;
              }
              const config = originalType.toConfig();
              const originalFieldConfigMap = config.fields;
              const newFieldConfigMap = {};
              Object.keys(originalFieldConfigMap).forEach(fieldName => {
                  const originalFieldConfig = originalFieldConfigMap[fieldName];
                  const mappedField = fieldMapper(originalFieldConfig, fieldName, typeName, schema);
                  if (mappedField === undefined) {
                      newFieldConfigMap[fieldName] = originalFieldConfig;
                  }
                  else if (Array.isArray(mappedField)) {
                      const [newFieldName, newFieldConfig] = mappedField;
                      newFieldConfigMap[newFieldName] = newFieldConfig;
                  }
                  else if (mappedField !== null) {
                      newFieldConfigMap[fieldName] = mappedField;
                  }
              });
              if (isObjectType(originalType)) {
                  newTypeMap[typeName] = new GraphQLObjectType({
                      ...config,
                      fields: newFieldConfigMap,
                  });
              }
              else if (isInterfaceType(originalType)) {
                  newTypeMap[typeName] = new GraphQLInterfaceType({
                      ...config,
                      fields: newFieldConfigMap,
                  });
              }
              else {
                  newTypeMap[typeName] = new GraphQLInputObjectType({
                      ...config,
                      fields: newFieldConfigMap,
                  });
              }
          }
      });
      return newTypeMap;
  }
  function mapArguments(originalTypeMap, schema, schemaMapper) {
      const newTypeMap = {};
      Object.keys(originalTypeMap).forEach(typeName => {
          if (!typeName.startsWith('__')) {
              const originalType = originalTypeMap[typeName];
              if (!isObjectType(originalType) && !isInterfaceType(originalType)) {
                  newTypeMap[typeName] = originalType;
                  return;
              }
              const argumentMapper = getArgumentMapper(schemaMapper);
              if (argumentMapper == null) {
                  newTypeMap[typeName] = originalType;
                  return;
              }
              const config = originalType.toConfig();
              const originalFieldConfigMap = config.fields;
              const newFieldConfigMap = {};
              Object.keys(originalFieldConfigMap).forEach(fieldName => {
                  const originalFieldConfig = originalFieldConfigMap[fieldName];
                  const originalArgumentConfigMap = originalFieldConfig.args;
                  if (originalArgumentConfigMap == null) {
                      newFieldConfigMap[fieldName] = originalFieldConfig;
                      return;
                  }
                  const argumentNames = Object.keys(originalArgumentConfigMap);
                  if (!argumentNames.length) {
                      newFieldConfigMap[fieldName] = originalFieldConfig;
                      return;
                  }
                  const newArgumentConfigMap = {};
                  argumentNames.forEach(argumentName => {
                      const originalArgumentConfig = originalArgumentConfigMap[argumentName];
                      const mappedArgument = argumentMapper(originalArgumentConfig, fieldName, typeName, schema);
                      if (mappedArgument === undefined) {
                          newArgumentConfigMap[argumentName] = originalArgumentConfig;
                      }
                      else if (Array.isArray(mappedArgument)) {
                          const [newArgumentName, newArgumentConfig] = mappedArgument;
                          newArgumentConfigMap[newArgumentName] = newArgumentConfig;
                      }
                      else if (mappedArgument !== null) {
                          newArgumentConfigMap[argumentName] = mappedArgument;
                      }
                  });
                  newFieldConfigMap[fieldName] = {
                      ...originalFieldConfig,
                      args: newArgumentConfigMap,
                  };
              });
              if (isObjectType(originalType)) {
                  newTypeMap[typeName] = new GraphQLObjectType({
                      ...config,
                      fields: newFieldConfigMap,
                  });
              }
              else if (isInterfaceType(originalType)) {
                  newTypeMap[typeName] = new GraphQLInterfaceType({
                      ...config,
                      fields: newFieldConfigMap,
                  });
              }
              else {
                  newTypeMap[typeName] = new GraphQLInputObjectType({
                      ...config,
                      fields: newFieldConfigMap,
                  });
              }
          }
      });
      return newTypeMap;
  }
  function mapDirectives(originalDirectives, schema, schemaMapper) {
      const directiveMapper = getDirectiveMapper(schemaMapper);
      if (directiveMapper == null) {
          return originalDirectives.slice();
      }
      const newDirectives = [];
      originalDirectives.forEach(directive => {
          const mappedDirective = directiveMapper(directive, schema);
          if (mappedDirective === undefined) {
              newDirectives.push(directive);
          }
          else if (mappedDirective !== null) {
              newDirectives.push(mappedDirective);
          }
      });
      return newDirectives;
  }
  function getTypeSpecifiers(schema, typeName) {
      const type = schema.getType(typeName);
      const specifiers = [MapperKind.TYPE];
      if (isObjectType(type)) {
          specifiers.push(MapperKind.COMPOSITE_TYPE, MapperKind.OBJECT_TYPE);
          const query = schema.getQueryType();
          const mutation = schema.getMutationType();
          const subscription = schema.getSubscriptionType();
          if (query != null && typeName === query.name) {
              specifiers.push(MapperKind.ROOT_OBJECT, MapperKind.QUERY);
          }
          else if (mutation != null && typeName === mutation.name) {
              specifiers.push(MapperKind.ROOT_OBJECT, MapperKind.MUTATION);
          }
          else if (subscription != null && typeName === subscription.name) {
              specifiers.push(MapperKind.ROOT_OBJECT, MapperKind.SUBSCRIPTION);
          }
      }
      else if (isInputObjectType(type)) {
          specifiers.push(MapperKind.INPUT_OBJECT_TYPE);
      }
      else if (isInterfaceType(type)) {
          specifiers.push(MapperKind.COMPOSITE_TYPE, MapperKind.ABSTRACT_TYPE, MapperKind.INTERFACE_TYPE);
      }
      else if (isUnionType(type)) {
          specifiers.push(MapperKind.COMPOSITE_TYPE, MapperKind.ABSTRACT_TYPE, MapperKind.UNION_TYPE);
      }
      else if (isEnumType(type)) {
          specifiers.push(MapperKind.ENUM_TYPE);
      }
      else if (isScalarType(type)) {
          specifiers.push(MapperKind.SCALAR_TYPE);
      }
      return specifiers;
  }
  function getTypeMapper(schema, schemaMapper, typeName) {
      const specifiers = getTypeSpecifiers(schema, typeName);
      let typeMapper;
      const stack = [...specifiers];
      while (!typeMapper && stack.length > 0) {
          const next = stack.pop();
          typeMapper = schemaMapper[next];
      }
      return typeMapper != null ? typeMapper : null;
  }
  function getFieldSpecifiers(schema, typeName) {
      const type = schema.getType(typeName);
      const specifiers = [MapperKind.FIELD];
      if (isObjectType(type)) {
          specifiers.push(MapperKind.COMPOSITE_FIELD, MapperKind.OBJECT_FIELD);
          const query = schema.getQueryType();
          const mutation = schema.getMutationType();
          const subscription = schema.getSubscriptionType();
          if (query != null && typeName === query.name) {
              specifiers.push(MapperKind.ROOT_FIELD, MapperKind.QUERY_ROOT_FIELD);
          }
          else if (mutation != null && typeName === mutation.name) {
              specifiers.push(MapperKind.ROOT_FIELD, MapperKind.MUTATION_ROOT_FIELD);
          }
          else if (subscription != null && typeName === subscription.name) {
              specifiers.push(MapperKind.ROOT_FIELD, MapperKind.SUBSCRIPTION_ROOT_FIELD);
          }
      }
      else if (isInterfaceType(type)) {
          specifiers.push(MapperKind.COMPOSITE_FIELD, MapperKind.INTERFACE_FIELD);
      }
      else if (isInputObjectType(type)) {
          specifiers.push(MapperKind.INPUT_OBJECT_FIELD);
      }
      return specifiers;
  }
  function getFieldMapper(schema, schemaMapper, typeName) {
      const specifiers = getFieldSpecifiers(schema, typeName);
      let fieldMapper;
      const stack = [...specifiers];
      while (!fieldMapper && stack.length > 0) {
          const next = stack.pop();
          fieldMapper = schemaMapper[next];
      }
      return fieldMapper != null ? fieldMapper : null;
  }
  function getArgumentMapper(schemaMapper) {
      const argumentMapper = schemaMapper[MapperKind.ARGUMENT];
      return argumentMapper != null ? argumentMapper : null;
  }
  function getDirectiveMapper(schemaMapper) {
      const directiveMapper = schemaMapper[MapperKind.DIRECTIVE];
      return directiveMapper != null ? directiveMapper : null;
  }
  function getEnumValueMapper(schemaMapper) {
      const enumValueMapper = schemaMapper[MapperKind.ENUM_VALUE];
      return enumValueMapper != null ? enumValueMapper : null;
  }
  
  function filterSchema({ schema, rootFieldFilter = () => true, typeFilter = () => true, fieldFilter = () => true, }) {
      const filteredSchema = mapSchema(schema, {
          [MapperKind.QUERY]: (type) => filterRootFields(type, 'Query', rootFieldFilter),
          [MapperKind.MUTATION]: (type) => filterRootFields(type, 'Mutation', rootFieldFilter),
          [MapperKind.SUBSCRIPTION]: (type) => filterRootFields(type, 'Subscription', rootFieldFilter),
          [MapperKind.OBJECT_TYPE]: (type) => typeFilter(type.name, type) ? filterObjectFields(type, fieldFilter) : null,
          [MapperKind.INTERFACE_TYPE]: (type) => (typeFilter(type.name, type) ? undefined : null),
          [MapperKind.UNION_TYPE]: (type) => (typeFilter(type.name, type) ? undefined : null),
          [MapperKind.INPUT_OBJECT_TYPE]: (type) => (typeFilter(type.name, type) ? undefined : null),
          [MapperKind.ENUM_TYPE]: (type) => (typeFilter(type.name, type) ? undefined : null),
          [MapperKind.SCALAR_TYPE]: (type) => (typeFilter(type.name, type) ? undefined : null),
      });
      return filteredSchema;
  }
  function filterRootFields(type, operation, rootFieldFilter) {
      const config = type.toConfig();
      Object.keys(config.fields).forEach(fieldName => {
          if (!rootFieldFilter(operation, fieldName, config.fields[fieldName])) {
              delete config.fields[fieldName];
          }
      });
      return new GraphQLObjectType(config);
  }
  function filterObjectFields(type, fieldFilter) {
      const config = type.toConfig();
      Object.keys(config.fields).forEach(fieldName => {
          if (!fieldFilter(type.name, fieldName, config.fields[fieldName])) {
              delete config.fields[fieldName];
          }
      });
      return new GraphQLObjectType(config);
  }
  
  function cloneDirective(directive) {
      return isSpecifiedDirective(directive) ? directive : new GraphQLDirective(directive.toConfig());
  }
  function cloneType(type) {
      if (isObjectType(type)) {
          const config = type.toConfig();
          return new GraphQLObjectType({
              ...config,
              interfaces: typeof config.interfaces === 'function' ? config.interfaces : config.interfaces.slice(),
          });
      }
      else if (isInterfaceType(type)) {
          const config = type.toConfig();
          const newConfig = {
              ...config,
              interfaces: [...((typeof config.interfaces === 'function' ? config.interfaces() : config.interfaces) || [])],
          };
          return new GraphQLInterfaceType(newConfig);
      }
      else if (isUnionType(type)) {
          const config = type.toConfig();
          return new GraphQLUnionType({
              ...config,
              types: config.types.slice(),
          });
      }
      else if (isInputObjectType(type)) {
          return new GraphQLInputObjectType(type.toConfig());
      }
      else if (isEnumType(type)) {
          return new GraphQLEnumType(type.toConfig());
      }
      else if (isScalarType(type)) {
          return isSpecifiedScalarType(type) ? type : new GraphQLScalarType(type.toConfig());
      }
      throw new Error(`Invalid type ${type}`);
  }
  function cloneSchema(schema) {
      return mapSchema(schema);
  }
  
  // Update any references to named schema types that disagree with the named
  // types found in schema.getTypeMap().
  //
  // healSchema and its callers (visitSchema/visitSchemaDirectives) all modify the schema in place.
  // Therefore, private variables (such as the stored implementation map and the proper root types)
  // are not updated.
  //
  // If this causes issues, the schema could be more aggressively healed as follows:
  //
  // healSchema(schema);
  // const config = schema.toConfig()
  // const healedSchema = new GraphQLSchema({
  //   ...config,
  //   query: schema.getType('<desired new root query type name>'),
  //   mutation: schema.getType('<desired new root mutation type name>'),
  //   subscription: schema.getType('<desired new root subscription type name>'),
  // });
  //
  // One can then also -- if necessary --  assign the correct private variables to the initial schema
  // as follows:
  // Object.assign(schema, healedSchema);
  //
  // These steps are not taken automatically to preserve backwards compatibility with graphql-tools v4.
  // See https://github.com/ardatan/graphql-tools/issues/1462
  //
  // They were briefly taken in v5, but can now be phased out as they were only required when other
  // areas of the codebase were using healSchema and visitSchema more extensively.
  //
  function healSchema(schema) {
      healTypes(schema.getTypeMap(), schema.getDirectives());
      return schema;
  }
  function healTypes(originalTypeMap, directives, config = {
      skipPruning: false,
  }) {
      const actualNamedTypeMap = Object.create(null);
      // If any of the .name properties of the GraphQLNamedType objects in
      // schema.getTypeMap() have changed, the keys of the type map need to
      // be updated accordingly.
      Object.entries(originalTypeMap).forEach(([typeName, namedType]) => {
          if (namedType == null || typeName.startsWith('__')) {
              return;
          }
          const actualName = namedType.name;
          if (actualName.startsWith('__')) {
              return;
          }
          if (actualName in actualNamedTypeMap) {
              throw new Error(`Duplicate schema type name ${actualName}`);
          }
          actualNamedTypeMap[actualName] = namedType;
          // Note: we are deliberately leaving namedType in the schema by its
          // original name (which might be different from actualName), so that
          // references by that name can be healed.
      });
      // Now add back every named type by its actual name.
      Object.entries(actualNamedTypeMap).forEach(([typeName, namedType]) => {
          originalTypeMap[typeName] = namedType;
      });
      // Directive declaration argument types can refer to named types.
      directives.forEach((decl) => {
          decl.args = decl.args.filter(arg => {
              arg.type = healType(arg.type);
              return arg.type !== null;
          });
      });
      Object.entries(originalTypeMap).forEach(([typeName, namedType]) => {
          // Heal all named types, except for dangling references, kept only to redirect.
          if (!typeName.startsWith('__') && typeName in actualNamedTypeMap) {
              if (namedType != null) {
                  healNamedType(namedType);
              }
          }
      });
      for (const typeName of Object.keys(originalTypeMap)) {
          if (!typeName.startsWith('__') && !(typeName in actualNamedTypeMap)) {
              delete originalTypeMap[typeName];
          }
      }
      if (!config.skipPruning) {
          // TODO:
          // consider removing the default level of pruning in v7,
          // see comments below on the pruneTypes function.
          pruneTypes$1(originalTypeMap, directives);
      }
      function healNamedType(type) {
          if (isObjectType(type)) {
              healFields(type);
              healInterfaces(type);
              return;
          }
          else if (isInterfaceType(type)) {
              healFields(type);
              if ('getInterfaces' in type) {
                  healInterfaces(type);
              }
              return;
          }
          else if (isUnionType(type)) {
              healUnderlyingTypes(type);
              return;
          }
          else if (isInputObjectType(type)) {
              healInputFields(type);
              return;
          }
          else if (isLeafType(type)) {
              return;
          }
          throw new Error(`Unexpected schema type: ${type}`);
      }
      function healFields(type) {
          const fieldMap = type.getFields();
          for (const [key, field] of Object.entries(fieldMap)) {
              field.args
                  .map(arg => {
                  arg.type = healType(arg.type);
                  return arg.type === null ? null : arg;
              })
                  .filter(Boolean);
              field.type = healType(field.type);
              if (field.type === null) {
                  delete fieldMap[key];
              }
          }
      }
      function healInterfaces(type) {
          if ('getInterfaces' in type) {
              const interfaces = type.getInterfaces();
              interfaces.push(...interfaces
                  .splice(0)
                  .map(iface => healType(iface))
                  .filter(Boolean));
          }
      }
      function healInputFields(type) {
          const fieldMap = type.getFields();
          for (const [key, field] of Object.entries(fieldMap)) {
              field.type = healType(field.type);
              if (field.type === null) {
                  delete fieldMap[key];
              }
          }
      }
      function healUnderlyingTypes(type) {
          const types = type.getTypes();
          types.push(...types
              .splice(0)
              .map(t => healType(t))
              .filter(Boolean));
      }
      function healType(type) {
          // Unwrap the two known wrapper types
          if (isListType(type)) {
              const healedType = healType(type.ofType);
              return healedType != null ? new GraphQLList(healedType) : null;
          }
          else if (isNonNullType(type)) {
              const healedType = healType(type.ofType);
              return healedType != null ? new GraphQLNonNull(healedType) : null;
          }
          else if (isNamedType(type)) {
              // If a type annotation on a field or an argument or a union member is
              // any `GraphQLNamedType` with a `name`, then it must end up identical
              // to `schema.getType(name)`, since `schema.getTypeMap()` is the source
              // of truth for all named schema types.
              // Note that new types can still be simply added by adding a field, as
              // the official type will be undefined, not null.
              const officialType = originalTypeMap[type.name];
              if (officialType && type !== officialType) {
                  return officialType;
              }
          }
          return type;
      }
  }
  // TODO:
  // consider removing the default level of pruning in v7
  //
  // Pruning was introduced into healSchema in v5, so legacy schema directives relying on pruning
  // during healing are likely to be rare. pruning is now recommended via the dedicated pruneSchema
  // function which does not force pruning on library users and gives granular control in terms of
  // pruning types. pruneSchema does recreate the schema -- a parallel version that prunes in place
  // could be considered.
  function pruneTypes$1(typeMap, directives) {
      const implementedInterfaces = {};
      Object.values(typeMap).forEach(namedType => {
          if ('getInterfaces' in namedType) {
              namedType.getInterfaces().forEach(iface => {
                  implementedInterfaces[iface.name] = true;
              });
          }
      });
      let prunedTypeMap = false;
      const typeNames = Object.keys(typeMap);
      for (let i = 0; i < typeNames.length; i++) {
          const typeName = typeNames[i];
          const type = typeMap[typeName];
          if (isObjectType(type) || isInputObjectType(type)) {
              // prune types with no fields
              if (!Object.keys(type.getFields()).length) {
                  typeMap[typeName] = null;
                  prunedTypeMap = true;
              }
          }
          else if (isUnionType(type)) {
              // prune unions without underlying types
              if (!type.getTypes().length) {
                  typeMap[typeName] = null;
                  prunedTypeMap = true;
              }
          }
          else if (isInterfaceType(type)) {
              // prune interfaces without fields or without implementations
              if (!Object.keys(type.getFields()).length || !(type.name in implementedInterfaces)) {
                  typeMap[typeName] = null;
                  prunedTypeMap = true;
              }
          }
      }
      // every prune requires another round of healing
      if (prunedTypeMap) {
          healTypes(typeMap, directives);
      }
  }
  
  // Abstract base class of any visitor implementation, defining the available
  // visitor methods along with their parameter types, and providing a static
  // helper function for determining whether a subclass implements a given
  // visitor method, as opposed to inheriting one of the stubs defined here.
  class SchemaVisitor {
      // Determine if this SchemaVisitor (sub)class implements a particular
      // visitor method.
      static implementsVisitorMethod(methodName) {
          if (!methodName.startsWith('visit')) {
              return false;
          }
          const method = this.prototype[methodName];
          if (typeof method !== 'function') {
              return false;
          }
          if (this.name === 'SchemaVisitor') {
              // The SchemaVisitor class implements every visitor method.
              return true;
          }
          const stub = SchemaVisitor.prototype[methodName];
          if (method === stub) {
              // If this.prototype[methodName] was just inherited from SchemaVisitor,
              // then this class does not really implement the method.
              return false;
          }
          return true;
      }
      // Concrete subclasses of SchemaVisitor should override one or more of these
      // visitor methods, in order to express their interest in handling certain
      // schema types/locations. Each method may return null to remove the given
      // type from the schema, a non-null value of the same type to update the
      // type in the schema, or nothing to leave the type as it was.
      // eslint-disable-next-line @typescript-eslint/no-empty-function
      visitSchema(_schema) { }
      visitScalar(_scalar
      // eslint-disable-next-line @typescript-eslint/no-empty-function
      ) { }
      visitObject(_object
      // eslint-disable-next-line @typescript-eslint/no-empty-function
      ) { }
      visitFieldDefinition(_field, _details
      // eslint-disable-next-line @typescript-eslint/no-empty-function
      ) { }
      visitArgumentDefinition(_argument, _details
      // eslint-disable-next-line @typescript-eslint/no-empty-function
      ) { }
      visitInterface(_iface
      // eslint-disable-next-line @typescript-eslint/no-empty-function
      ) { }
      // eslint-disable-next-line @typescript-eslint/no-empty-function
      visitUnion(_union) { }
      // eslint-disable-next-line @typescript-eslint/no-empty-function
      visitEnum(_type) { }
      visitEnumValue(_value, _details
      // eslint-disable-next-line @typescript-eslint/no-empty-function
      ) { }
      visitInputObject(_object
      // eslint-disable-next-line @typescript-eslint/no-empty-function
      ) { }
      visitInputFieldDefinition(_field, _details
      // eslint-disable-next-line @typescript-eslint/no-empty-function
      ) { }
  }
  
  function isSchemaVisitor(obj) {
      if ('schema' in obj && isSchema(obj.schema)) {
          if ('visitSchema' in obj && typeof obj.visitSchema === 'function') {
              return true;
          }
      }
      return false;
  }
  // Generic function for visiting GraphQLSchema objects.
  function visitSchema(schema, 
  // To accommodate as many different visitor patterns as possible, the
  // visitSchema function does not simply accept a single instance of the
  // SchemaVisitor class, but instead accepts a function that takes the
  // current VisitableSchemaType object and the name of a visitor method and
  // returns an array of SchemaVisitor instances that implement the visitor
  // method and have an interest in handling the given VisitableSchemaType
  // object. In the simplest case, this function can always return an array
  // containing a single visitor object, without even looking at the type or
  // methodName parameters. In other cases, this function might sometimes
  // return an empty array to indicate there are no visitors that should be
  // applied to the given VisitableSchemaType object. For an example of a
  // visitor pattern that benefits from this abstraction, see the
  // SchemaDirectiveVisitor class below.
  visitorOrVisitorSelector) {
      const visitorSelector = typeof visitorOrVisitorSelector === 'function' ? visitorOrVisitorSelector : () => visitorOrVisitorSelector;
      // Helper function that calls visitorSelector and applies the resulting
      // visitors to the given type, with arguments [type, ...args].
      function callMethod(methodName, type, ...args) {
          let visitors = visitorSelector(type, methodName);
          visitors = Array.isArray(visitors) ? visitors : [visitors];
          let finalType = type;
          visitors.every(visitorOrVisitorDef => {
              let newType;
              if (isSchemaVisitor(visitorOrVisitorDef)) {
                  newType = visitorOrVisitorDef[methodName](finalType, ...args);
              }
              else if (isNamedType(finalType) &&
                  (methodName === 'visitScalar' ||
                      methodName === 'visitEnum' ||
                      methodName === 'visitObject' ||
                      methodName === 'visitInputObject' ||
                      methodName === 'visitUnion' ||
                      methodName === 'visitInterface')) {
                  const specifiers = getTypeSpecifiers$1(finalType, schema);
                  const typeVisitor = getVisitor(visitorOrVisitorDef, specifiers);
                  newType = typeVisitor != null ? typeVisitor(finalType, schema) : undefined;
              }
              if (typeof newType === 'undefined') {
                  // Keep going without modifying type.
                  return true;
              }
              if (methodName === 'visitSchema' || isSchema(finalType)) {
                  throw new Error(`Method ${methodName} cannot replace schema with ${newType}`);
              }
              if (newType === null) {
                  // Stop the loop and return null form callMethod, which will cause
                  // the type to be removed from the schema.
                  finalType = null;
                  return false;
              }
              // Update type to the new type returned by the visitor method, so that
              // later directives will see the new type, and callMethod will return
              // the final type.
              finalType = newType;
              return true;
          });
          // If there were no directives for this type object, or if all visitor
          // methods returned nothing, type will be returned unmodified.
          return finalType;
      }
      // Recursive helper function that calls any appropriate visitor methods for
      // each object in the schema, then traverses the object's children (if any).
      function visit(type) {
          if (isSchema(type)) {
              // Unlike the other types, the root GraphQLSchema object cannot be
              // replaced by visitor methods, because that would make life very hard
              // for SchemaVisitor subclasses that rely on the original schema object.
              callMethod('visitSchema', type);
              const typeMap = type.getTypeMap();
              Object.entries(typeMap).forEach(([typeName, namedType]) => {
                  if (!typeName.startsWith('__') && namedType != null) {
                      // Call visit recursively to let it determine which concrete
                      // subclass of GraphQLNamedType we found in the type map.
                      // We do not use updateEachKey because we want to preserve
                      // deleted types in the typeMap so that other types that reference
                      // the deleted types can be healed.
                      typeMap[typeName] = visit(namedType);
                  }
              });
              return type;
          }
          if (isObjectType(type)) {
              // Note that callMethod('visitObject', type) may not actually call any
              // methods, if there are no @directive annotations associated with this
              // type, or if this SchemaDirectiveVisitor subclass does not override
              // the visitObject method.
              const newObject = callMethod('visitObject', type);
              if (newObject != null) {
                  visitFields(newObject);
              }
              return newObject;
          }
          if (isInterfaceType(type)) {
              const newInterface = callMethod('visitInterface', type);
              if (newInterface != null) {
                  visitFields(newInterface);
              }
              return newInterface;
          }
          if (isInputObjectType(type)) {
              const newInputObject = callMethod('visitInputObject', type);
              if (newInputObject != null) {
                  const fieldMap = newInputObject.getFields();
                  for (const key of Object.keys(fieldMap)) {
                      fieldMap[key] = callMethod('visitInputFieldDefinition', fieldMap[key], {
                          // Since we call a different method for input object fields, we
                          // can't reuse the visitFields function here.
                          objectType: newInputObject,
                      });
                      if (!fieldMap[key]) {
                          delete fieldMap[key];
                      }
                  }
              }
              return newInputObject;
          }
          if (isScalarType(type)) {
              return callMethod('visitScalar', type);
          }
          if (isUnionType(type)) {
              return callMethod('visitUnion', type);
          }
          if (isEnumType(type)) {
              let newEnum = callMethod('visitEnum', type);
              if (newEnum != null) {
                  const newValues = newEnum
                      .getValues()
                      .map(value => callMethod('visitEnumValue', value, {
                      enumType: newEnum,
                  }))
                      .filter(Boolean);
                  // Recreate the enum type if any of the values changed
                  const valuesUpdated = newValues.some((value, index) => value !== newEnum.getValues()[index]);
                  if (valuesUpdated) {
                      newEnum = new GraphQLEnumType({
                          ...newEnum.toConfig(),
                          values: newValues.reduce((prev, value) => ({
                              ...prev,
                              [value.name]: {
                                  value: value.value,
                                  deprecationReason: value.deprecationReason,
                                  description: value.description,
                                  astNode: value.astNode,
                              },
                          }), {}),
                      });
                  }
              }
              return newEnum;
          }
          throw new Error(`Unexpected schema type: ${type}`);
      }
      function visitFields(type) {
          const fieldMap = type.getFields();
          for (const [key, field] of Object.entries(fieldMap)) {
              // It would be nice if we could call visit(field) recursively here, but
              // GraphQLField is merely a type, not a value that can be detected using
              // an instanceof check, so we have to visit the fields in this lexical
              // context, so that TypeScript can validate the call to
              // visitFieldDefinition.
              const newField = callMethod('visitFieldDefinition', field, {
                  // While any field visitor needs a reference to the field object, some
                  // field visitors may also need to know the enclosing (parent) type,
                  // perhaps to determine if the parent is a GraphQLObjectType or a
                  // GraphQLInterfaceType. To obtain a reference to the parent, a
                  // visitor method can have a second parameter, which will be an object
                  // with an .objectType property referring to the parent.
                  objectType: type,
              });
              if (newField.args != null) {
                  newField.args = newField.args
                      .map(arg => callMethod('visitArgumentDefinition', arg, {
                      // Like visitFieldDefinition, visitArgumentDefinition takes a
                      // second parameter that provides additional context, namely the
                      // parent .field and grandparent .objectType. Remember that the
                      // current GraphQLSchema is always available via this.schema.
                      field: newField,
                      objectType: type,
                  }))
                      .filter(Boolean);
              }
              // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
              if (newField) {
                  fieldMap[key] = newField;
              }
              else {
                  delete fieldMap[key];
              }
          }
      }
      visit(schema);
      // Automatically update any references to named schema types replaced
      // during the traversal, so implementors don't have to worry about that.
      healSchema(schema);
      // Return schema for convenience, even though schema parameter has all updated types.
      return schema;
  }
  function getTypeSpecifiers$1(type, schema) {
      const specifiers = [VisitSchemaKind.TYPE];
      if (isObjectType(type)) {
          specifiers.push(VisitSchemaKind.COMPOSITE_TYPE, VisitSchemaKind.OBJECT_TYPE);
          const query = schema.getQueryType();
          const mutation = schema.getMutationType();
          const subscription = schema.getSubscriptionType();
          if (type === query) {
              specifiers.push(VisitSchemaKind.ROOT_OBJECT, VisitSchemaKind.QUERY);
          }
          else if (type === mutation) {
              specifiers.push(VisitSchemaKind.ROOT_OBJECT, VisitSchemaKind.MUTATION);
          }
          else if (type === subscription) {
              specifiers.push(VisitSchemaKind.ROOT_OBJECT, VisitSchemaKind.SUBSCRIPTION);
          }
      }
      else if (isInputType(type)) {
          specifiers.push(VisitSchemaKind.INPUT_OBJECT_TYPE);
      }
      else if (isInterfaceType(type)) {
          specifiers.push(VisitSchemaKind.COMPOSITE_TYPE, VisitSchemaKind.ABSTRACT_TYPE, VisitSchemaKind.INTERFACE_TYPE);
      }
      else if (isUnionType(type)) {
          specifiers.push(VisitSchemaKind.COMPOSITE_TYPE, VisitSchemaKind.ABSTRACT_TYPE, VisitSchemaKind.UNION_TYPE);
      }
      else if (isEnumType(type)) {
          specifiers.push(VisitSchemaKind.ENUM_TYPE);
      }
      else if (isScalarType(type)) {
          specifiers.push(VisitSchemaKind.SCALAR_TYPE);
      }
      return specifiers;
  }
  function getVisitor(visitorDef, specifiers) {
      let typeVisitor;
      const stack = [...specifiers];
      while (!typeVisitor && stack.length > 0) {
          const next = stack.pop();
          typeVisitor = visitorDef[next];
      }
      return typeVisitor != null ? typeVisitor : null;
  }
  
  // This class represents a reusable implementation of a @directive that may
  // appear in a GraphQL schema written in Schema Definition Language.
  //
  // By overriding one or more visit{Object,Union,...} methods, a subclass
  // registers interest in certain schema types, such as GraphQLObjectType,
  // GraphQLUnionType, etc. When SchemaDirectiveVisitor.visitSchemaDirectives is
  // called with a GraphQLSchema object and a map of visitor subclasses, the
  // overidden methods of those subclasses allow the visitors to obtain
  // references to any type objects that have @directives attached to them,
  // enabling visitors to inspect or modify the schema as appropriate.
  //
  // For example, if a directive called @rest(url: "...") appears after a field
  // definition, a SchemaDirectiveVisitor subclass could provide meaning to that
  // directive by overriding the visitFieldDefinition method (which receives a
  // GraphQLField parameter), and then the body of that visitor method could
  // manipulate the field's resolver function to fetch data from a REST endpoint
  // described by the url argument passed to the @rest directive:
  //
  //   const typeDefs = `
  //   type Query {
  //     people: [Person] @rest(url: "/api/v1/people")
  //   }`;
  //
  //   const schema = makeExecutableSchema({ typeDefs });
  //
  //   SchemaDirectiveVisitor.visitSchemaDirectives(schema, {
  //     rest: class extends SchemaDirectiveVisitor {
  //       public visitFieldDefinition(field: GraphQLField<any, any>) {
  //         const { url } = this.args;
  //         field.resolve = () => fetch(url);
  //       }
  //     }
  //   });
  //
  // The subclass in this example is defined as an anonymous class expression,
  // for brevity. A truly reusable SchemaDirectiveVisitor would most likely be
  // defined in a library using a named class declaration, and then exported for
  // consumption by other modules and packages.
  //
  // See below for a complete list of overridable visitor methods, their
  // parameter types, and more details about the properties exposed by instances
  // of the SchemaDirectiveVisitor class.
  class SchemaDirectiveVisitor extends SchemaVisitor {
      // Mark the constructor protected to enforce passing SchemaDirectiveVisitor
      // subclasses (not instances) to visitSchemaDirectives.
      constructor(config) {
          super();
          this.name = config.name;
          this.args = config.args;
          this.visitedType = config.visitedType;
          this.schema = config.schema;
          this.context = config.context;
      }
      // Override this method to return a custom GraphQLDirective (or modify one
      // already present in the schema) to enforce argument types, provide default
      // argument values, or specify schema locations where this @directive may
      // appear. By default, any declaration found in the schema will be returned.
      static getDirectiveDeclaration(directiveName, schema) {
          return schema.getDirective(directiveName);
      }
      // Call SchemaDirectiveVisitor.visitSchemaDirectives to visit every
      // @directive in the schema and create an appropriate SchemaDirectiveVisitor
      // instance to visit the object decorated by the @directive.
      static visitSchemaDirectives(schema, 
      // The keys of this object correspond to directive names as they appear
      // in the schema, and the values should be subclasses (not instances!)
      // of the SchemaDirectiveVisitor class. This distinction is important
      // because a new SchemaDirectiveVisitor instance will be created each
      // time a matching directive is found in the schema AST, with arguments
      // and other metadata specific to that occurrence. To help prevent the
      // mistake of passing instances, the SchemaDirectiveVisitor constructor
      // method is marked as protected.
      directiveVisitors, 
      // Optional context object that will be available to all visitor instances
      // via this.context. Defaults to an empty null-prototype object.
      context = Object.create(null)
      // The visitSchemaDirectives method returns a map from directive names to
      // lists of SchemaDirectiveVisitors created while visiting the schema.
      ) {
          // If the schema declares any directives for public consumption, record
          // them here so that we can properly coerce arguments when/if we encounter
          // an occurrence of the directive while walking the schema below.
          const declaredDirectives = this.getDeclaredDirectives(schema, directiveVisitors);
          // Map from directive names to lists of SchemaDirectiveVisitor instances
          // created while visiting the schema.
          const createdVisitors = Object.keys(directiveVisitors).reduce((prev, item) => ({
              ...prev,
              [item]: [],
          }), {});
          const directiveVisitorMap = Object.entries(directiveVisitors).reduce((prev, [key, value]) => ({
              ...prev,
              [key]: value,
          }), {});
          function visitorSelector(type, methodName) {
              var _a, _b;
              let directiveNodes = (_b = (_a = type === null || type === void 0 ? void 0 : type.astNode) === null || _a === void 0 ? void 0 : _a.directives) !== null && _b !== void 0 ? _b : [];
              const extensionASTNodes = type.extensionASTNodes;
              if (extensionASTNodes != null) {
                  extensionASTNodes.forEach(extensionASTNode => {
                      if (extensionASTNode.directives != null) {
                          directiveNodes = directiveNodes.concat(extensionASTNode.directives);
                      }
                  });
              }
              const visitors = [];
              directiveNodes.forEach(directiveNode => {
                  const directiveName = directiveNode.name.value;
                  if (!(directiveName in directiveVisitorMap)) {
                      return;
                  }
                  const VisitorClass = directiveVisitorMap[directiveName];
                  // Avoid creating visitor objects if visitorClass does not override
                  // the visitor method named by methodName.
                  if (!VisitorClass.implementsVisitorMethod(methodName)) {
                      return;
                  }
                  const decl = declaredDirectives[directiveName];
                  let args;
                  if (decl != null) {
                      // If this directive was explicitly declared, use the declared
                      // argument types (and any default values) to check, coerce, and/or
                      // supply default values for the given arguments.
                      args = getArgumentValues(decl, directiveNode);
                  }
                  else {
                      // If this directive was not explicitly declared, just convert the
                      // argument nodes to their corresponding JavaScript values.
                      args = Object.create(null);
                      if (directiveNode.arguments != null) {
                          directiveNode.arguments.forEach(arg => {
                              args[arg.name.value] = valueFromASTUntyped(arg.value);
                          });
                      }
                  }
                  // As foretold in comments near the top of the visitSchemaDirectives
                  // method, this is where instances of the SchemaDirectiveVisitor class
                  // get created and assigned names. While subclasses could override the
                  // constructor method, the constructor is marked as protected, so
                  // these are the only arguments that will ever be passed.
                  visitors.push(new VisitorClass({
                      name: directiveName,
                      args,
                      visitedType: type,
                      schema,
                      context,
                  }));
              });
              if (visitors.length > 0) {
                  visitors.forEach(visitor => {
                      createdVisitors[visitor.name].push(visitor);
                  });
              }
              return visitors;
          }
          visitSchema(schema, visitorSelector);
          return createdVisitors;
      }
      static getDeclaredDirectives(schema, directiveVisitors) {
          const declaredDirectives = schema.getDirectives().reduce((prev, curr) => ({
              ...prev,
              [curr.name]: curr,
          }), {});
          // If the visitor subclass overrides getDirectiveDeclaration, and it
          // returns a non-null GraphQLDirective, use that instead of any directive
          // declared in the schema itself. Reasoning: if a SchemaDirectiveVisitor
          // goes to the trouble of implementing getDirectiveDeclaration, it should
          // be able to rely on that implementation.
          Object.entries(directiveVisitors).forEach(([directiveName, visitorClass]) => {
              const decl = visitorClass.getDirectiveDeclaration(directiveName, schema);
              if (decl != null) {
                  declaredDirectives[directiveName] = decl;
              }
          });
          Object.entries(declaredDirectives).forEach(([name, decl]) => {
              if (!(name in directiveVisitors)) {
                  // SchemaDirectiveVisitors.visitSchemaDirectives might be called
                  // multiple times with partial directiveVisitors maps, so it's not
                  // necessarily an error for directiveVisitors to be missing an
                  // implementation of a directive that was declared in the schema.
                  return;
              }
              const visitorClass = directiveVisitors[name];
              decl.locations.forEach(loc => {
                  const visitorMethodName = directiveLocationToVisitorMethodName(loc);
                  if (SchemaVisitor.implementsVisitorMethod(visitorMethodName) &&
                      !visitorClass.implementsVisitorMethod(visitorMethodName)) {
                      // While visitor subclasses may implement extra visitor methods,
                      // it's definitely a mistake if the GraphQLDirective declares itself
                      // applicable to certain schema locations, and the visitor subclass
                      // does not implement all the corresponding methods.
                      throw new Error(`SchemaDirectiveVisitor for @${name} must implement ${visitorMethodName} method`);
                  }
              });
          });
          return declaredDirectives;
      }
  }
  // Convert a string like "FIELD_DEFINITION" to "visitFieldDefinition".
  function directiveLocationToVisitorMethodName(loc) {
      return ('visit' +
          loc.replace(/([^_]*)_?/g, (_wholeMatch, part) => part.charAt(0).toUpperCase() + part.slice(1).toLowerCase()));
  }
  
  function getResolversFromSchema(schema) {
      const resolvers = Object.create({});
      const typeMap = schema.getTypeMap();
      Object.keys(typeMap).forEach(typeName => {
          const type = typeMap[typeName];
          if (isScalarType(type)) {
              if (!isSpecifiedScalarType(type)) {
                  resolvers[typeName] = cloneType(type);
              }
          }
          else if (isEnumType(type)) {
              resolvers[typeName] = {};
              const values = type.getValues();
              values.forEach(value => {
                  resolvers[typeName][value.name] = value.value;
              });
          }
          else if (isInterfaceType(type)) {
              if (type.resolveType != null) {
                  resolvers[typeName] = {
                      __resolveType: type.resolveType,
                  };
              }
          }
          else if (isUnionType(type)) {
              if (type.resolveType != null) {
                  resolvers[typeName] = {
                      __resolveType: type.resolveType,
                  };
              }
          }
          else if (isObjectType(type)) {
              resolvers[typeName] = {};
              if (type.isTypeOf != null) {
                  resolvers[typeName].__isTypeOf = type.isTypeOf;
              }
              const fields = type.getFields();
              Object.keys(fields).forEach(fieldName => {
                  const field = fields[fieldName];
                  resolvers[typeName][fieldName] = {
                      resolve: field.resolve,
                      subscribe: field.subscribe,
                  };
              });
          }
      });
      return resolvers;
  }
  
  function forEachField(schema, fn) {
      const typeMap = schema.getTypeMap();
      Object.keys(typeMap).forEach(typeName => {
          const type = typeMap[typeName];
          // TODO: maybe have an option to include these?
          if (!getNamedType(type).name.startsWith('__') && isObjectType(type)) {
              const fields = type.getFields();
              Object.keys(fields).forEach(fieldName => {
                  const field = fields[fieldName];
                  fn(field, typeName, fieldName);
              });
          }
      });
  }
  
  function forEachDefaultValue(schema, fn) {
      const typeMap = schema.getTypeMap();
      Object.keys(typeMap).forEach(typeName => {
          const type = typeMap[typeName];
          if (!getNamedType(type).name.startsWith('__')) {
              if (isObjectType(type)) {
                  const fields = type.getFields();
                  Object.keys(fields).forEach(fieldName => {
                      const field = fields[fieldName];
                      field.args.forEach(arg => {
                          arg.defaultValue = fn(arg.type, arg.defaultValue);
                      });
                  });
              }
              else if (isInputObjectType(type)) {
                  const fields = type.getFields();
                  Object.keys(fields).forEach(fieldName => {
                      const field = fields[fieldName];
                      field.defaultValue = fn(field.type, field.defaultValue);
                  });
              }
          }
      });
  }
  
  // addTypes uses toConfig to create a new schema with a new or replaced
  function addTypes(schema, newTypesOrDirectives) {
      const queryType = schema.getQueryType();
      const mutationType = schema.getMutationType();
      const subscriptionType = schema.getSubscriptionType();
      const queryTypeName = queryType != null ? queryType.name : undefined;
      const mutationTypeName = mutationType != null ? mutationType.name : undefined;
      const subscriptionTypeName = subscriptionType != null ? subscriptionType.name : undefined;
      const config = schema.toConfig();
      const originalTypeMap = {};
      config.types.forEach(type => {
          originalTypeMap[type.name] = type;
      });
      const originalDirectiveMap = {};
      config.directives.forEach(directive => {
          originalDirectiveMap[directive.name] = directive;
      });
      newTypesOrDirectives.forEach(newTypeOrDirective => {
          if (isNamedType(newTypeOrDirective)) {
              originalTypeMap[newTypeOrDirective.name] = newTypeOrDirective;
          }
          else if (isDirective(newTypeOrDirective)) {
              originalDirectiveMap[newTypeOrDirective.name] = newTypeOrDirective;
          }
      });
      const { typeMap, directives } = rewireTypes(originalTypeMap, Object.keys(originalDirectiveMap).map(directiveName => originalDirectiveMap[directiveName]));
      return new GraphQLSchema({
          ...config,
          query: queryTypeName ? typeMap[queryTypeName] : undefined,
          mutation: mutationTypeName ? typeMap[mutationTypeName] : undefined,
          subscription: subscriptionTypeName != null ? typeMap[subscriptionTypeName] : undefined,
          types: Object.keys(typeMap).map(typeName => typeMap[typeName]),
          directives,
      });
  }
  
  function pruneSchema(schema, options = {}) {
      const pruningContext = {
          schema,
          unusedTypes: Object.create(null),
          implementations: Object.create(null),
      };
      Object.keys(schema.getTypeMap()).forEach(typeName => {
          const type = schema.getType(typeName);
          if ('getInterfaces' in type) {
              type.getInterfaces().forEach(iface => {
                  if (pruningContext.implementations[iface.name] == null) {
                      pruningContext.implementations[iface.name] = Object.create(null);
                  }
                  pruningContext.implementations[iface.name][type.name] = true;
              });
          }
      });
      visitTypes(pruningContext, schema);
      return mapSchema$1(schema, {
          [MapperKind$1.TYPE]: (type) => {
              if (isObjectType(type) || isInputObjectType(type)) {
                  if ((!Object.keys(type.getFields()).length && !options.skipEmptyCompositeTypePruning) ||
                      (pruningContext.unusedTypes[type.name] && !options.skipUnusedTypesPruning)) {
                      return null;
                  }
              }
              else if (isUnionType(type)) {
                  if ((!type.getTypes().length && !options.skipEmptyUnionPruning) ||
                      (pruningContext.unusedTypes[type.name] && !options.skipUnusedTypesPruning)) {
                      return null;
                  }
              }
              else if (isInterfaceType(type)) {
                  if ((!Object.keys(type.getFields()).length && !options.skipEmptyCompositeTypePruning) ||
                      (!Object.keys(pruningContext.implementations[type.name]).length &&
                          !options.skipUnimplementedInterfacesPruning) ||
                      (pruningContext.unusedTypes[type.name] && !options.skipUnusedTypesPruning)) {
                      return null;
                  }
              }
              else {
                  if (pruningContext.unusedTypes[type.name] && !options.skipUnusedTypesPruning) {
                      return null;
                  }
              }
          },
      });
  }
  function visitOutputType(visitedTypes, pruningContext, type) {
      if (visitedTypes[type.name]) {
          return;
      }
      visitedTypes[type.name] = true;
      pruningContext.unusedTypes[type.name] = false;
      if (isObjectType(type) || isInterfaceType(type)) {
          const fields = type.getFields();
          Object.keys(fields).forEach(fieldName => {
              const field = fields[fieldName];
              const namedType = getNamedType(field.type);
              visitOutputType(visitedTypes, pruningContext, namedType);
              const args = field.args;
              args.forEach(arg => {
                  const type = getNamedType(arg.type);
                  visitInputType(visitedTypes, pruningContext, type);
              });
          });
          if (isInterfaceType(type)) {
              Object.keys(pruningContext.implementations[type.name]).forEach(typeName => {
                  visitOutputType(visitedTypes, pruningContext, pruningContext.schema.getType(typeName));
              });
          }
          if ('getInterfaces' in type) {
              type.getInterfaces().forEach(type => {
                  visitOutputType(visitedTypes, pruningContext, type);
              });
          }
      }
      else if (isUnionType(type)) {
          const types = type.getTypes();
          types.forEach(type => visitOutputType(visitedTypes, pruningContext, type));
      }
  }
  function visitInputType(visitedTypes, pruningContext, type) {
      if (visitedTypes[type.name]) {
          return;
      }
      pruningContext.unusedTypes[type.name] = false;
      if (isInputObjectType(type)) {
          const fields = type.getFields();
          Object.keys(fields).forEach(fieldName => {
              const field = fields[fieldName];
              const namedType = getNamedType(field.type);
              visitInputType(visitedTypes, pruningContext, namedType);
          });
      }
      visitedTypes[type.name] = true;
  }
  function visitTypes(pruningContext, schema) {
      Object.keys(schema.getTypeMap()).forEach(typeName => {
          if (!typeName.startsWith('__')) {
              pruningContext.unusedTypes[typeName] = true;
          }
      });
      const visitedTypes = Object.create(null);
      const rootTypes = [schema.getQueryType(), schema.getMutationType(), schema.getSubscriptionType()].filter(type => type != null);
      rootTypes.forEach(rootType => visitOutputType(visitedTypes, pruningContext, rootType));
      schema.getDirectives().forEach(directive => {
          directive.args.forEach(arg => {
              const type = getNamedType(arg.type);
              visitInputType(visitedTypes, pruningContext, type);
          });
      });
  }
  
  /* eslint-disable @typescript-eslint/explicit-module-boundary-types */
  function mergeDeep(target, ...sources) {
      if (isScalarType(target)) {
          return target;
      }
      const output = {
          ...target,
      };
      for (const source of sources) {
          if (isObject(target) && isObject(source)) {
              for (const key in source) {
                  if (isObject(source[key])) {
                      if (!(key in target)) {
                          Object.assign(output, { [key]: source[key] });
                      }
                      else {
                          output[key] = mergeDeep(target[key], source[key]);
                      }
                  }
                  else {
                      Object.assign(output, { [key]: source[key] });
                  }
              }
          }
      }
      return output;
  }
  function isObject(item) {
      return item && typeof item === 'object' && !Array.isArray(item);
  }
  
  function renameFieldNode(fieldNode, name) {
      return {
          ...fieldNode,
          alias: {
              kind: Kind.NAME,
              value: fieldNode.alias != null ? fieldNode.alias.value : fieldNode.name.value,
          },
          name: {
              kind: Kind.NAME,
              value: name,
          },
      };
  }
  function preAliasFieldNode(fieldNode, str) {
      return {
          ...fieldNode,
          alias: {
              kind: Kind.NAME,
              value: `${str}${fieldNode.alias != null ? fieldNode.alias.value : fieldNode.name.value}`,
          },
      };
  }
  function wrapFieldNode(fieldNode, path) {
      let newFieldNode = fieldNode;
      path.forEach(fieldName => {
          newFieldNode = {
              kind: Kind.FIELD,
              name: {
                  kind: Kind.NAME,
                  value: fieldName,
              },
              selectionSet: {
                  kind: Kind.SELECTION_SET,
                  selections: [fieldNode],
              },
          };
      });
      return newFieldNode;
  }
  function collectFields(selectionSet, fragments, fields = [], visitedFragmentNames = {}) {
      if (selectionSet != null) {
          selectionSet.selections.forEach(selection => {
              switch (selection.kind) {
                  case Kind.FIELD:
                      fields.push(selection);
                      break;
                  case Kind.INLINE_FRAGMENT:
                      collectFields(selection.selectionSet, fragments, fields, visitedFragmentNames);
                      break;
                  case Kind.FRAGMENT_SPREAD: {
                      const fragmentName = selection.name.value;
                      if (!visitedFragmentNames[fragmentName]) {
                          visitedFragmentNames[fragmentName] = true;
                          collectFields(fragments[fragmentName].selectionSet, fragments, fields, visitedFragmentNames);
                      }
                      break;
                  }
              }
          });
      }
      return fields;
  }
  function hoistFieldNodes({ fieldNode, fieldNames, path = [], delimeter = '__gqltf__', fragments, }) {
      const alias = fieldNode.alias != null ? fieldNode.alias.value : fieldNode.name.value;
      let newFieldNodes = [];
      if (path.length) {
          const remainingPathSegments = path.slice();
          const initialPathSegment = remainingPathSegments.shift();
          collectFields(fieldNode.selectionSet, fragments).forEach((possibleFieldNode) => {
              if (possibleFieldNode.name.value === initialPathSegment) {
                  newFieldNodes = newFieldNodes.concat(hoistFieldNodes({
                      fieldNode: preAliasFieldNode(possibleFieldNode, `${alias}${delimeter}`),
                      fieldNames,
                      path: remainingPathSegments,
                      delimeter,
                      fragments,
                  }));
              }
          });
      }
      else {
          collectFields(fieldNode.selectionSet, fragments).forEach((possibleFieldNode) => {
              if (!fieldNames || fieldNames.includes(possibleFieldNode.name.value)) {
                  newFieldNodes.push(preAliasFieldNode(possibleFieldNode, `${alias}${delimeter}`));
              }
          });
      }
      return newFieldNodes;
  }
  
  function concatInlineFragments(type, fragments) {
      const fragmentSelections = fragments.reduce((selections, fragment) => selections.concat(fragment.selectionSet.selections), []);
      const deduplicatedFragmentSelection = deduplicateSelection(fragmentSelections);
      return {
          kind: Kind.INLINE_FRAGMENT,
          typeCondition: {
              kind: Kind.NAMED_TYPE,
              name: {
                  kind: Kind.NAME,
                  value: type,
              },
          },
          selectionSet: {
              kind: Kind.SELECTION_SET,
              selections: deduplicatedFragmentSelection,
          },
      };
  }
  function deduplicateSelection(nodes) {
      const selectionMap = nodes.reduce((map, node) => {
          switch (node.kind) {
              case 'Field': {
                  if (node.alias != null) {
                      if (node.alias.value in map) {
                          return map;
                      }
                      return {
                          ...map,
                          [node.alias.value]: node,
                      };
                  }
                  if (node.name.value in map) {
                      return map;
                  }
                  return {
                      ...map,
                      [node.name.value]: node,
                  };
              }
              case 'FragmentSpread': {
                  if (node.name.value in map) {
                      return map;
                  }
                  return {
                      ...map,
                      [node.name.value]: node,
                  };
              }
              case 'InlineFragment': {
                  if (map.__fragment != null) {
                      const fragment = map.__fragment;
                      return {
                          ...map,
                          __fragment: concatInlineFragments(fragment.typeCondition.name.value, [fragment, node]),
                      };
                  }
                  return {
                      ...map,
                      __fragment: node,
                  };
              }
              default: {
                  return map;
              }
          }
      }, Object.create(null));
      const selection = Object.keys(selectionMap).reduce((selectionList, node) => selectionList.concat(selectionMap[node]), []);
      return selection;
  }
  function parseFragmentToInlineFragment(definitions) {
      if (definitions.trim().startsWith('fragment')) {
          const document = parse(definitions);
          for (const definition of document.definitions) {
              if (definition.kind === Kind.FRAGMENT_DEFINITION) {
                  return {
                      kind: Kind.INLINE_FRAGMENT,
                      typeCondition: definition.typeCondition,
                      selectionSet: definition.selectionSet,
                  };
              }
          }
      }
      const query = parse(`{${definitions}}`).definitions[0];
      for (const selection of query.selectionSet.selections) {
          if (selection.kind === Kind.INLINE_FRAGMENT) {
              return selection;
          }
      }
      throw new Error('Could not parse fragment');
  }
  
  function parseSelectionSet(selectionSet) {
      const query = parse(selectionSet).definitions[0];
      return query.selectionSet;
  }
  function typeContainsSelectionSet(type, selectionSet) {
      const fields = type.getFields();
      for (const selection of selectionSet.selections) {
          if (selection.kind === Kind.FIELD) {
              const field = fields[selection.name.value];
              if (field == null) {
                  return false;
              }
              if (selection.selectionSet != null) {
                  return typeContainsSelectionSet(getNamedType(field.type), selection.selectionSet);
              }
          }
          else if (selection.kind === Kind.INLINE_FRAGMENT) {
              const containsSelectionSet = typeContainsSelectionSet(type, selection.selectionSet);
              if (!containsSelectionSet) {
                  return false;
              }
          }
      }
      return true;
  }
  
  /**
   * Get the key under which the result of this resolver will be placed in the response JSON. Basically, just
   * resolves aliases.
   * @param info The info argument to the resolver.
   */
  function getResponseKeyFromInfo(info) {
      return info.fieldNodes[0].alias != null ? info.fieldNodes[0].alias.value : info.fieldName;
  }
  
  function applySchemaTransforms(originalSchema, transforms) {
      return transforms.reduce((schema, transform) => transform.transformSchema != null ? transform.transformSchema(cloneSchema(schema)) : schema, originalSchema);
  }
  function applyRequestTransforms(originalRequest, transforms) {
      return transforms.reduce((request, transform) => transform.transformRequest != null ? transform.transformRequest(request) : request, originalRequest);
  }
  function applyResultTransforms(originalResult, transforms) {
      return transforms.reduceRight((result, transform) => transform.transformResult != null ? transform.transformResult(result) : result, originalResult);
  }
  
  function appendObjectFields(schema, typeName, additionalFields) {
      if (schema.getType(typeName) == null) {
          return addTypes(schema, [
              new GraphQLObjectType({
                  name: typeName,
                  fields: additionalFields,
              }),
          ]);
      }
      return mapSchema(schema, {
          [MapperKind.OBJECT_TYPE]: type => {
              if (type.name === typeName) {
                  const config = type.toConfig();
                  const originalFieldConfigMap = config.fields;
                  const newFieldConfigMap = {};
                  Object.keys(originalFieldConfigMap).forEach(fieldName => {
                      newFieldConfigMap[fieldName] = originalFieldConfigMap[fieldName];
                  });
                  Object.keys(additionalFields).forEach(fieldName => {
                      newFieldConfigMap[fieldName] = additionalFields[fieldName];
                  });
                  return new GraphQLObjectType({
                      ...config,
                      fields: newFieldConfigMap,
                  });
              }
          },
      });
  }
  function removeObjectFields(schema, typeName, testFn) {
      const removedFields = {};
      const newSchema = mapSchema(schema, {
          [MapperKind.OBJECT_TYPE]: type => {
              if (type.name === typeName) {
                  const config = type.toConfig();
                  const originalFieldConfigMap = config.fields;
                  const newFieldConfigMap = {};
                  Object.keys(originalFieldConfigMap).forEach(fieldName => {
                      const originalFieldConfig = originalFieldConfigMap[fieldName];
                      if (testFn(fieldName, originalFieldConfig)) {
                          removedFields[fieldName] = originalFieldConfig;
                      }
                      else {
                          newFieldConfigMap[fieldName] = originalFieldConfig;
                      }
                  });
                  return new GraphQLObjectType({
                      ...config,
                      fields: newFieldConfigMap,
                  });
              }
          },
      });
      return [newSchema, removedFields];
  }
  function selectObjectFields(schema, typeName, testFn) {
      const selectedFields = {};
      mapSchema(schema, {
          [MapperKind.OBJECT_TYPE]: type => {
              if (type.name === typeName) {
                  const config = type.toConfig();
                  const originalFieldConfigMap = config.fields;
                  Object.keys(originalFieldConfigMap).forEach(fieldName => {
                      const originalFieldConfig = originalFieldConfigMap[fieldName];
                      if (testFn(fieldName, originalFieldConfig)) {
                          selectedFields[fieldName] = originalFieldConfig;
                      }
                  });
              }
              return undefined;
          },
      });
      return selectedFields;
  }
  function modifyObjectFields(schema, typeName, testFn, newFields) {
      const removedFields = {};
      const newSchema = mapSchema(schema, {
          [MapperKind.OBJECT_TYPE]: type => {
              if (type.name === typeName) {
                  const config = type.toConfig();
                  const originalFieldConfigMap = config.fields;
                  const newFieldConfigMap = {};
                  Object.keys(originalFieldConfigMap).forEach(fieldName => {
                      const originalFieldConfig = originalFieldConfigMap[fieldName];
                      if (testFn(fieldName, originalFieldConfig)) {
                          removedFields[fieldName] = originalFieldConfig;
                      }
                      else {
                          newFieldConfigMap[fieldName] = originalFieldConfig;
                      }
                  });
                  Object.keys(newFields).forEach(fieldName => {
                      const fieldConfig = newFields[fieldName];
                      newFieldConfigMap[fieldName] = fieldConfig;
                  });
                  return new GraphQLObjectType({
                      ...config,
                      fields: newFieldConfigMap,
                  });
              }
          },
      });
      return [newSchema, removedFields];
  }
  
  /**
   * Given a selectionSet, adds all of the fields in that selection to
   * the passed in map of fields, and returns it at the end.
   *
   * CollectFields requires the "runtime type" of an object. For a field which
   * returns an Interface or Union type, the "runtime type" will be the actual
   * Object type returned by that field.
   *
   * @internal
   */
  function collectFields$1(exeContext, runtimeType, selectionSet, fields, visitedFragmentNames) {
      for (const selection of selectionSet.selections) {
          switch (selection.kind) {
              case Kind.FIELD: {
                  if (!shouldIncludeNode(exeContext, selection)) {
                      continue;
                  }
                  const name = getFieldEntryKey(selection);
                  if (!(name in fields)) {
                      fields[name] = [];
                  }
                  fields[name].push(selection);
                  break;
              }
              case Kind.INLINE_FRAGMENT: {
                  if (!shouldIncludeNode(exeContext, selection) ||
                      !doesFragmentConditionMatch(exeContext, selection, runtimeType)) {
                      continue;
                  }
                  collectFields$1(exeContext, runtimeType, selection.selectionSet, fields, visitedFragmentNames);
                  break;
              }
              case Kind.FRAGMENT_SPREAD: {
                  const fragName = selection.name.value;
                  if (visitedFragmentNames[fragName] || !shouldIncludeNode(exeContext, selection)) {
                      continue;
                  }
                  visitedFragmentNames[fragName] = true;
                  const fragment = exeContext.fragments[fragName];
                  if (!fragment || !doesFragmentConditionMatch(exeContext, fragment, runtimeType)) {
                      continue;
                  }
                  collectFields$1(exeContext, runtimeType, fragment.selectionSet, fields, visitedFragmentNames);
                  break;
              }
          }
      }
      return fields;
  }
  /**
   * Determines if a field should be included based on the @include and @skip
   * directives, where @skip has higher precedence than @include.
   */
  function shouldIncludeNode(exeContext, node) {
      const skip = getDirectiveValues$1(GraphQLSkipDirective, node, exeContext.variableValues);
      if ((skip === null || skip === void 0 ? void 0 : skip.if) === true) {
          return false;
      }
      const include = getDirectiveValues$1(GraphQLIncludeDirective, node, exeContext.variableValues);
      if ((include === null || include === void 0 ? void 0 : include.if) === false) {
          return false;
      }
      return true;
  }
  /**
   * Determines if a fragment is applicable to the given type.
   */
  function doesFragmentConditionMatch(exeContext, fragment, type) {
      const typeConditionNode = fragment.typeCondition;
      if (!typeConditionNode) {
          return true;
      }
      const conditionalType = typeFromAST(exeContext.schema, typeConditionNode);
      if (conditionalType === type) {
          return true;
      }
      if (isAbstractType(conditionalType)) {
          return exeContext.schema.isPossibleType(conditionalType, type);
      }
      return false;
  }
  /**
   * Implements the logic to compute the key of a given field's entry
   */
  function getFieldEntryKey(node) {
      // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
      return node.alias ? node.alias.value : node.name.value;
  }
  
  /**
   * Given an AsyncIterable and a callback function, return an AsyncIterator
   * which produces values mapped via calling the callback function.
   */
  function mapAsyncIterator(iterator, callback, rejectCallback) {
      let $return;
      let abruptClose;
      if (typeof iterator.return === 'function') {
          $return = iterator.return;
          abruptClose = (error) => {
              const rethrow = () => Promise.reject(error);
              return $return.call(iterator).then(rethrow, rethrow);
          };
      }
      function mapResult(result) {
          return result.done ? result : asyncMapValue(result.value, callback).then(iteratorResult, abruptClose);
      }
      let mapReject;
      if (rejectCallback) {
          // Capture rejectCallback to ensure it cannot be null.
          const reject = rejectCallback;
          mapReject = (error) => asyncMapValue(error, reject).then(iteratorResult, abruptClose);
      }
      return {
          next() {
              return iterator.next().then(mapResult, mapReject);
          },
          return() {
              return $return
                  ? $return.call(iterator).then(mapResult, mapReject)
                  : Promise.resolve({ value: undefined, done: true });
          },
          throw(error) {
              if (typeof iterator.throw === 'function') {
                  return iterator.throw(error).then(mapResult, mapReject);
              }
              return Promise.reject(error).catch(abruptClose);
          },
          [Symbol.asyncIterator]() {
              return this;
          },
      };
  }
  function asyncMapValue(value, callback) {
      return new Promise(resolve => resolve(callback(value)));
  }
  function iteratorResult(value) {
      return { value, done: false };
  }
  
  function astFromType(type) {
      if (isNonNullType(type)) {
          const innerType = astFromType(type.ofType);
          if (innerType.kind === Kind.NON_NULL_TYPE) {
              throw new Error(`Invalid type node ${JSON.stringify(type)}. Inner type of non-null type cannot be a non-null type.`);
          }
          return {
              kind: Kind.NON_NULL_TYPE,
              type: innerType,
          };
      }
      else if (isListType(type)) {
          return {
              kind: Kind.LIST_TYPE,
              type: astFromType(type.ofType),
          };
      }
      return {
          kind: Kind.NAMED_TYPE,
          name: {
              kind: Kind.NAME,
              value: type.name,
          },
      };
  }
  
  function updateArgument(argName, argType, argumentNodes, variableDefinitionsMap, variableValues, newArg) {
      let varName;
      let numGeneratedVariables = 0;
      do {
          varName = `_v${(numGeneratedVariables++).toString()}_${argName}`;
      } while (varName in variableDefinitionsMap);
      argumentNodes[argName] = {
          kind: Kind.ARGUMENT,
          name: {
              kind: Kind.NAME,
              value: argName,
          },
          value: {
              kind: Kind.VARIABLE,
              name: {
                  kind: Kind.NAME,
                  value: varName,
              },
          },
      };
      variableDefinitionsMap[varName] = {
          kind: Kind.VARIABLE_DEFINITION,
          variable: {
              kind: Kind.VARIABLE,
              name: {
                  kind: Kind.NAME,
                  value: varName,
              },
          },
          type: astFromType(argType),
      };
      if (newArg === undefined) {
          delete variableValues[varName];
      }
      else {
          variableValues[varName] = newArg;
      }
  }
  
  function implementsAbstractType(schema, typeA, typeB) {
      if (typeA === typeB) {
          return true;
      }
      else if (isCompositeType(typeA) && isCompositeType(typeB)) {
          return doTypesOverlap(schema, typeA, typeB);
      }
      return false;
  }
  
  const ERROR_SYMBOL = Symbol('subschemaErrors');
  function relocatedError(originalError, path) {
      return new GraphQLError(originalError.message, originalError.nodes, originalError.source, originalError.positions, path === null ? undefined : path === undefined ? originalError.path : path, originalError.originalError, originalError.extensions);
  }
  function slicedError(originalError) {
      return relocatedError(originalError, originalError.path != null ? originalError.path.slice(1) : undefined);
  }
  function getErrorsByPathSegment(errors) {
      const record = Object.create(null);
      errors.forEach(error => {
          if (!error.path || error.path.length < 2) {
              return;
          }
          const pathSegment = error.path[1];
          const current = pathSegment in record ? record[pathSegment] : [];
          current.push(slicedError(error));
          record[pathSegment] = current;
      });
      return record;
  }
  function setErrors(result, errors) {
      result[ERROR_SYMBOL] = errors;
  }
  function getErrors(result, pathSegment) {
      const errors = result != null ? result[ERROR_SYMBOL] : result;
      if (!Array.isArray(errors)) {
          return null;
      }
      const fieldErrors = [];
      for (const error of errors) {
          if (!error.path || error.path[0] === pathSegment) {
              fieldErrors.push(error);
          }
      }
      return fieldErrors;
  }
  
  function inputFieldToFieldConfig(field) {
      return {
          description: field.description,
          type: field.type,
          defaultValue: field.defaultValue,
          extensions: field.extensions,
          astNode: field.astNode,
      };
  }
  function fieldToFieldConfig(field) {
      return {
          description: field.description,
          type: field.type,
          args: argsToFieldConfigArgumentMap(field.args),
          resolve: field.resolve,
          subscribe: field.subscribe,
          deprecationReason: field.deprecationReason,
          extensions: field.extensions,
          astNode: field.astNode,
      };
  }
  function argsToFieldConfigArgumentMap(args) {
      const newArguments = {};
      args.forEach(arg => {
          newArguments[arg.name] = argumentToArgumentConfig(arg);
      });
      return newArguments;
  }
  function argumentToArgumentConfig(arg) {
      return {
          description: arg.description,
          type: arg.type,
          defaultValue: arg.defaultValue,
          extensions: arg.extensions,
          astNode: arg.astNode,
      };
  }
  
  function observableToAsyncIterable(observable) {
      const pullQueue = [];
      const pushQueue = [];
      let listening = true;
      const pushValue = (value) => {
          if (pullQueue.length !== 0) {
              pullQueue.shift()({ value, done: false });
          }
          else {
              pushQueue.push({ value });
          }
      };
      const pushError = (error) => {
          if (pullQueue.length !== 0) {
              pullQueue.shift()({ value: { errors: [error] }, done: false });
          }
          else {
              pushQueue.push({ value: { errors: [error] } });
          }
      };
      const pullValue = () => new Promise(resolve => {
          if (pushQueue.length !== 0) {
              const element = pushQueue.shift();
              // either {value: {errors: [...]}} or {value: ...}
              resolve({
                  ...element,
                  done: false,
              });
          }
          else {
              pullQueue.push(resolve);
          }
      });
      const subscription = observable.subscribe({
          next(value) {
              pushValue(value);
          },
          error(err) {
              pushError(err);
          },
      });
      const emptyQueue = () => {
          if (listening) {
              listening = false;
              subscription.unsubscribe();
              pullQueue.forEach(resolve => resolve({ value: undefined, done: true }));
              pullQueue.length = 0;
              pushQueue.length = 0;
          }
      };
      return {
          next() {
              return listening ? pullValue() : this.return();
          },
          return() {
              emptyQueue();
              return Promise.resolve({ value: undefined, done: true });
          },
          throw(error) {
              emptyQueue();
              return Promise.reject(error);
          },
          [Symbol.asyncIterator]() {
              return this;
          },
      };
  }
  
  export { ERROR_SYMBOL, MapperKind, SchemaDirectiveVisitor, SchemaVisitor, VisitSchemaKind, addTypes, appendObjectFields, applyRequestTransforms, applyResultTransforms, applySchemaTransforms, argsToFieldConfigArgumentMap, argumentToArgumentConfig, asArray, buildOperationNodeForField, checkValidationErrors, cloneDirective, cloneSchema, cloneType, collectFields$1 as collectFields, compareNodes, compareStrings, concatInlineFragments, createNamedStub, createSchemaDefinition, createStub, debugLog, fieldToFieldConfig, filterSchema, fixSchemaAst, fixWindowsPath, flattenArray, forEachDefaultValue, forEachField, getBuiltInForStub, getDirectives, getErrors, getErrorsByPathSegment, getFieldsWithDirectives, getImplementingTypes, getResolversFromSchema, getResponseKeyFromInfo, getUserTypesFromSchema, healSchema, healTypes, hoistFieldNodes, implementsAbstractType, inputFieldToFieldConfig, isDocumentString, isEqual, isNamedStub, isNotEqual, isValidPath, mapAsyncIterator, mapSchema, mergeDeep, modifyObjectFields, nodeToString, observableToAsyncIterable, parseFragmentToInlineFragment, parseGraphQLJSON, parseGraphQLSDL, parseInputValue, parseInputValueLiteral, parseSelectionSet, preAliasFieldNode, printSchemaWithDirectives, pruneSchema, relocatedError, removeObjectFields, renameFieldNode, rewireTypes, selectObjectFields, serializeInputValue, setErrors, slicedError, transformInputValue, typeContainsSelectionSet, updateArgument, validateGraphQlDocuments, visitSchema, wrapFieldNode };
  //# sourceMappingURL=index.esm.js.map
  