import type { IfNever, Simplify } from 'type-fest'; import type { HttpClient } from '../index.js'; import { request } from '../request.js'; import type { GraphqlRequest, GraphqlRequestData, GraphqlResponse, GraphqlResponseData } from './interfaces.js'; import { compress } from './compress.js'; import { checkError, GraphqlError, wrapError } from './error.js'; import { encode, encodeDev } from './encode.js'; import { template } from './template/build.js'; import type { GqlTemplateValue } from './template/index.js'; /** 分析 Response 类型 */ type ExtractResponse = K extends string ? GraphqlResponse>> : K extends object ? IfNever, never> : never; /** 发送 gql 请求 */ export interface Gql { /** * 发送 gql 请求 * * 可预期的异常包括 `RequestError` 和 `GraphqlError` */ ( request: GraphqlRequestData | string, init?: RequestInit, ): Promise>; /** * 发送 gql query 请求,可使用插值输入变量 * * 可预期的异常包括 `RequestError` 和 `GraphqlError` */ query( query: TemplateStringsArray, ...args: GqlTemplateValue[] ): Promise>; /** * 发送 gql mutation 请求,可使用插值输入变量 * * 可预期的异常包括 `RequestError` 和 `GraphqlError` */ mutation( query: TemplateStringsArray, ...args: GqlTemplateValue[] ): Promise>; } /** * 发送 gql query 请求 */ async function query( this: Gql, query: TemplateStringsArray, ...args: GqlTemplateValue[] ): Promise> { const req = template('query', query, args); return this(req); } /** * 发送 gql mutation 请求 */ async function mutation( this: Gql, query: TemplateStringsArray, ...args: GqlTemplateValue[] ): Promise> { const req = template('mutation', query, args); return this(req); } /** 创建 GQL 请求处理 */ export function createGql(client: HttpClient): Gql { const gql = (async ( reqData: GraphqlRequestData | string, init?: RequestInit, ): Promise> => { const { config } = client; const { fetcher: { Headers }, } = config; if (typeof reqData == 'string') { reqData = { query: reqData }; } const req: GraphqlRequest = { query: compress(reqData.query).trim(), variables: reqData.variables ?? {}, operationName: reqData.operationName, url: config.gqlUrl, }; let res; try { const payload = await (config.dev ? encodeDev : encode)({ query: req.query, variables: reqData.variables, operationName: reqData.operationName, }); const headers = new Headers(init?.headers); const reqInit: RequestInit = { method: 'POST', ...init, headers, body: payload.data }; headers.set('Content-Type', payload.contentType); if (config.dev) { headers.set('Accept', 'application/json'); headers.set('X-Raw-Size', String(payload.rawSize)); headers.set('X-Compressed-Size', String(payload.compressedSize)); } else { headers.set('Accept', 'application/ubjson, application/json;q=0.9'); } res = await request(config, req.url, reqInit); const resData = res.data as GraphqlResponseData; const errors = checkError(req, res, resData); return { ...res, errors, data: resData.data, extensions: resData.extensions, request: req, }; } catch (ex) { if (ex instanceof GraphqlError) throw ex; throw wrapError(ex as Error, req, res); } }) as Gql; Object.defineProperties(gql, { query: { value: query }, mutation: { value: mutation }, }); return gql; }