{"version":3,"file":"FieldsOnCorrectTypeRule.js","sourceRoot":"","sources":["../../../src/validation/rules/FieldsOnCorrectTypeRule.ts"],"names":[],"mappings":"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","sourcesContent":["/** @category Validation Rules */\n\nimport { didYouMean } from '../../jsutils/didYouMean.ts';\nimport { naturalCompare } from '../../jsutils/naturalCompare.ts';\nimport { suggestionList } from '../../jsutils/suggestionList.ts';\n\nimport { GraphQLError } from '../../error/GraphQLError.ts';\n\nimport type { FieldNode } from '../../language/ast.ts';\nimport type { ASTVisitor } from '../../language/visitor.ts';\n\nimport type {\n  GraphQLInterfaceType,\n  GraphQLObjectType,\n  GraphQLOutputType,\n} from '../../type/definition.ts';\nimport {\n  isAbstractType,\n  isInterfaceType,\n  isObjectType,\n} from '../../type/definition.ts';\nimport type { GraphQLSchema } from '../../type/schema.ts';\n\nimport type { ValidationContext } from '../ValidationContext.ts';\n\n/**\n * Fields on correct type\n *\n * A GraphQL document is only valid if all fields selected are defined by the\n * parent type, or are an allowed meta field such as __typename.\n *\n * See https://spec.graphql.org/draft/#sec-Field-Selections\n * @param context - The validation context used while checking the document.\n * @returns A visitor that reports validation errors for this rule.\n * @example\n * ```ts\n * import { buildSchema, parse, validate } from 'graphql';\n * import { FieldsOnCorrectTypeRule } from 'graphql/validation';\n *\n * const schema = buildSchema(`\n *   type Query {\n *     name: String\n *   }\n * `);\n *\n * const invalidDocument = parse(`\n *   { missing }\n * `);\n * const invalidErrors = validate(schema, invalidDocument, [\n *   FieldsOnCorrectTypeRule,\n * ]);\n *\n * invalidErrors.length; // => 1\n *\n * const validDocument = parse(`\n *   { name }\n * `);\n * const validErrors = validate(schema, validDocument, [FieldsOnCorrectTypeRule]);\n *\n * validErrors; // => []\n * ```\n */\nexport function FieldsOnCorrectTypeRule(\n  context: ValidationContext,\n): ASTVisitor {\n  return {\n    Field(node: FieldNode) {\n      const type = context.getParentType();\n      if (type) {\n        const fieldDef = context.getFieldDef();\n        if (!fieldDef) {\n          // This field doesn't exist, lets look for suggestions.\n          const schema = context.getSchema();\n          const fieldName = node.name.value;\n\n          // First determine if there are any suggested types to condition on.\n          let suggestion = didYouMean(\n            'to use an inline fragment on',\n            context.hideSuggestions\n              ? []\n              : getSuggestedTypeNames(schema, type, fieldName),\n          );\n\n          // If there are no suggested types, then perhaps this was a typo?\n          if (suggestion === '') {\n            suggestion = didYouMean(\n              context.hideSuggestions\n                ? []\n                : getSuggestedFieldNames(type, fieldName),\n            );\n          }\n\n          // Report an error, including helpful suggestions.\n          context.reportError(\n            new GraphQLError(\n              `Cannot query field \"${fieldName}\" on type \"${type}\".` +\n                suggestion,\n              { nodes: node },\n            ),\n          );\n        }\n      }\n    },\n  };\n}\n\n/**\n * Go through all of the implementations of type, as well as the interfaces that\n * they implement. If any of those types include the provided field, suggest them,\n * sorted by how often the type is referenced.\n *\n * @internal\n */\nfunction getSuggestedTypeNames(\n  schema: GraphQLSchema,\n  type: GraphQLOutputType,\n  fieldName: string,\n): Array<string> {\n  if (!isAbstractType(type)) {\n    // Must be an Object type, which does not have possible fields.\n    return [];\n  }\n\n  const suggestedTypes = new Set<GraphQLObjectType | GraphQLInterfaceType>();\n  const usageCount = Object.create(null);\n  for (const possibleType of schema.getPossibleTypes(type)) {\n    if (possibleType.getFields()[fieldName] == null) {\n      continue;\n    }\n\n    // This object type defines this field.\n    suggestedTypes.add(possibleType);\n    usageCount[possibleType.name] = 1;\n\n    for (const possibleInterface of possibleType.getInterfaces()) {\n      if (possibleInterface.getFields()[fieldName] == null) {\n        continue;\n      }\n\n      // This interface type defines this field.\n      suggestedTypes.add(possibleInterface);\n      usageCount[possibleInterface.name] =\n        (usageCount[possibleInterface.name] ?? 0) + 1;\n    }\n  }\n\n  return [...suggestedTypes]\n    .sort((typeA, typeB) => {\n      // Suggest both interface and object types based on how common they are.\n      const usageCountDiff = usageCount[typeB.name] - usageCount[typeA.name];\n      if (usageCountDiff !== 0) {\n        return usageCountDiff;\n      }\n\n      // Suggest super types first followed by subtypes\n      if (isInterfaceType(typeA) && schema.isSubType(typeA, typeB)) {\n        return -1;\n      }\n      if (isInterfaceType(typeB) && schema.isSubType(typeB, typeA)) {\n        return 1;\n      }\n\n      return naturalCompare(typeA.name, typeB.name);\n    })\n    .map((x) => x.name);\n}\n\n/**\n * For the field name provided, determine if there are any similar field names\n * that may be the result of a typo.\n *\n * @internal\n */\nfunction getSuggestedFieldNames(\n  type: GraphQLOutputType,\n  fieldName: string,\n): Array<string> {\n  if (isObjectType(type) || isInterfaceType(type)) {\n    const possibleFieldNames = Object.keys(type.getFields());\n    return suggestionList(fieldName, possibleFieldNames);\n  }\n  // Otherwise, must be a Union type, which does not define fields.\n  return [];\n}\n"]}