{"version":3,"sources":["../../../src/core/handlers/GraphQLHandler.ts"],"sourcesContent":["import type { DocumentNode, GraphQLError, OperationTypeNode } from 'graphql'\nimport {\n  DefaultBodyType,\n  RequestHandler,\n  RequestHandlerDefaultInfo,\n  RequestHandlerOptions,\n  ResponseResolver,\n} from './RequestHandler'\nimport { getTimestamp } from '../utils/logging/getTimestamp'\nimport { getStatusCodeColor } from '../utils/logging/getStatusCodeColor'\nimport { serializeRequest } from '../utils/logging/serializeRequest'\nimport { serializeResponse } from '../utils/logging/serializeResponse'\nimport { Match, matchRequestUrl, Path } from '../utils/matching/matchRequestUrl'\nimport {\n  ParsedGraphQLRequest,\n  GraphQLMultipartRequestBody,\n  parseGraphQLRequest,\n  parseDocumentNode,\n} from '../utils/internal/parseGraphQLRequest'\nimport { toPublicUrl } from '../utils/request/toPublicUrl'\nimport { devUtils } from '../utils/internal/devUtils'\nimport { getAllRequestCookies } from '../utils/request/getRequestCookies'\n\nexport type ExpectedOperationTypeNode = OperationTypeNode | 'all'\nexport type GraphQLHandlerNameSelector = DocumentNode | RegExp | string\n\nexport type GraphQLQuery = Record<string, any>\nexport type GraphQLVariables = Record<string, any>\n\nexport interface GraphQLHandlerInfo extends RequestHandlerDefaultInfo {\n  operationType: ExpectedOperationTypeNode\n  operationName: GraphQLHandlerNameSelector\n}\n\nexport type GraphQLRequestParsedResult = {\n  match: Match\n  cookies: Record<string, string>\n} & (\n  | ParsedGraphQLRequest<GraphQLVariables>\n  /**\n   * An empty version of the ParsedGraphQLRequest\n   * which simplifies the return type of the resolver\n   * when the request is to a non-matching endpoint\n   */\n  | {\n      operationType?: undefined\n      operationName?: undefined\n      query?: undefined\n      variables?: undefined\n    }\n)\n\nexport type GraphQLResolverExtras<Variables extends GraphQLVariables> = {\n  query: string\n  operationName: string\n  variables: Variables\n  cookies: Record<string, string>\n}\n\nexport type GraphQLRequestBody<VariablesType extends GraphQLVariables> =\n  | GraphQLJsonRequestBody<VariablesType>\n  | GraphQLMultipartRequestBody\n  | Record<string, any>\n  | undefined\n\nexport interface GraphQLJsonRequestBody<Variables extends GraphQLVariables> {\n  query: string\n  variables?: Variables\n}\n\nexport type GraphQLResponseBody<BodyType extends DefaultBodyType> =\n  | {\n      data?: BodyType | null\n      errors?: readonly Partial<GraphQLError>[] | null\n    }\n  | null\n  | undefined\n\nexport function isDocumentNode(\n  value: DocumentNode | any,\n): value is DocumentNode {\n  if (value == null) {\n    return false\n  }\n\n  return typeof value === 'object' && 'kind' in value && 'definitions' in value\n}\n\nexport class GraphQLHandler extends RequestHandler<\n  GraphQLHandlerInfo,\n  GraphQLRequestParsedResult,\n  GraphQLResolverExtras<any>\n> {\n  private endpoint: Path\n\n  static parsedRequestCache = new WeakMap<\n    Request,\n    ParsedGraphQLRequest<GraphQLVariables>\n  >()\n\n  constructor(\n    operationType: ExpectedOperationTypeNode,\n    operationName: GraphQLHandlerNameSelector,\n    endpoint: Path,\n    resolver: ResponseResolver<GraphQLResolverExtras<any>, any, any>,\n    options?: RequestHandlerOptions,\n  ) {\n    let resolvedOperationName = operationName\n\n    if (isDocumentNode(operationName)) {\n      const parsedNode = parseDocumentNode(operationName)\n\n      if (parsedNode.operationType !== operationType) {\n        throw new Error(\n          `Failed to create a GraphQL handler: provided a DocumentNode with a mismatched operation type (expected \"${operationType}\", but got \"${parsedNode.operationType}\").`,\n        )\n      }\n\n      if (!parsedNode.operationName) {\n        throw new Error(\n          `Failed to create a GraphQL handler: provided a DocumentNode with no operation name.`,\n        )\n      }\n\n      resolvedOperationName = parsedNode.operationName\n    }\n\n    const header =\n      operationType === 'all'\n        ? `${operationType} (origin: ${endpoint.toString()})`\n        : `${operationType} ${resolvedOperationName} (origin: ${endpoint.toString()})`\n\n    super({\n      info: {\n        header,\n        operationType,\n        operationName: resolvedOperationName,\n      },\n      resolver,\n      options,\n    })\n\n    this.endpoint = endpoint\n  }\n\n  /**\n   * Parses the request body, once per request, cached across all\n   * GraphQL handlers. This is done to avoid multiple parsing of the\n   * request body, which each requires a clone of the request.\n   */\n  async parseGraphQLRequestOrGetFromCache(\n    request: Request,\n  ): Promise<ParsedGraphQLRequest<GraphQLVariables>> {\n    if (!GraphQLHandler.parsedRequestCache.has(request)) {\n      GraphQLHandler.parsedRequestCache.set(\n        request,\n        await parseGraphQLRequest(request).catch((error) => {\n          // eslint-disable-next-line no-console\n          console.error(error)\n          return undefined\n        }),\n      )\n    }\n\n    return GraphQLHandler.parsedRequestCache.get(request)\n  }\n\n  async parse(args: { request: Request }): Promise<GraphQLRequestParsedResult> {\n    /**\n     * If the request doesn't match a specified endpoint, there's no\n     * need to parse it since there's no case where we would handle this\n     */\n    const match = matchRequestUrl(new URL(args.request.url), this.endpoint)\n    const cookies = getAllRequestCookies(args.request)\n\n    if (!match.matches) {\n      return { match, cookies }\n    }\n\n    const parsedResult = await this.parseGraphQLRequestOrGetFromCache(\n      args.request,\n    )\n\n    if (typeof parsedResult === 'undefined') {\n      return { match, cookies }\n    }\n\n    return {\n      match,\n      cookies,\n      query: parsedResult.query,\n      operationType: parsedResult.operationType,\n      operationName: parsedResult.operationName,\n      variables: parsedResult.variables,\n    }\n  }\n\n  predicate(args: {\n    request: Request\n    parsedResult: GraphQLRequestParsedResult\n  }) {\n    if (args.parsedResult.operationType === undefined) {\n      return false\n    }\n\n    if (!args.parsedResult.operationName && this.info.operationType !== 'all') {\n      const publicUrl = toPublicUrl(args.request.url)\n\n      devUtils.warn(`\\\nFailed to intercept a GraphQL request at \"${args.request.method} ${publicUrl}\": anonymous GraphQL operations are not supported.\n\nConsider naming this operation or using \"graphql.operation()\" request handler to intercept GraphQL requests regardless of their operation name/type. Read more: https://mswjs.io/docs/api/graphql/#graphqloperationresolver`)\n      return false\n    }\n\n    const hasMatchingOperationType =\n      this.info.operationType === 'all' ||\n      args.parsedResult.operationType === this.info.operationType\n\n    const hasMatchingOperationName =\n      this.info.operationName instanceof RegExp\n        ? this.info.operationName.test(args.parsedResult.operationName || '')\n        : args.parsedResult.operationName === this.info.operationName\n\n    return (\n      args.parsedResult.match.matches &&\n      hasMatchingOperationType &&\n      hasMatchingOperationName\n    )\n  }\n\n  protected extendResolverArgs(args: {\n    request: Request\n    parsedResult: GraphQLRequestParsedResult\n  }) {\n    return {\n      query: args.parsedResult.query || '',\n      operationName: args.parsedResult.operationName || '',\n      variables: args.parsedResult.variables || {},\n      cookies: args.parsedResult.cookies,\n    }\n  }\n\n  async log(args: {\n    request: Request\n    response: Response\n    parsedResult: GraphQLRequestParsedResult\n  }) {\n    const loggedRequest = await serializeRequest(args.request)\n    const loggedResponse = await serializeResponse(args.response)\n    const statusColor = getStatusCodeColor(loggedResponse.status)\n    const requestInfo = args.parsedResult.operationName\n      ? `${args.parsedResult.operationType} ${args.parsedResult.operationName}`\n      : `anonymous ${args.parsedResult.operationType}`\n\n    // eslint-disable-next-line no-console\n    console.groupCollapsed(\n      devUtils.formatMessage(\n        `${getTimestamp()} ${requestInfo} (%c${loggedResponse.status} ${\n          loggedResponse.statusText\n        }%c)`,\n      ),\n      `color:${statusColor}`,\n      'color:inherit',\n    )\n    // eslint-disable-next-line no-console\n    console.log('Request:', loggedRequest)\n    // eslint-disable-next-line no-console\n    console.log('Handler:', this)\n    // eslint-disable-next-line no-console\n    console.log('Response:', loggedResponse)\n    // eslint-disable-next-line no-console\n    console.groupEnd()\n  }\n}\n"],"mappings":"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","names":[]}