UNPKG

@graphql-yoga/plugin-apq

Version:
56 lines (54 loc) 2.22 kB
import { createGraphQLError, createLRUCache } from "graphql-yoga"; import { handleMaybePromise } from "@whatwg-node/promise-helpers"; //#region src/index.ts function hashSHA256(text, api = globalThis) { const inputUint8Array = new api.TextEncoder().encode(text); return handleMaybePromise(() => api.crypto.subtle.digest({ name: "SHA-256" }, inputUint8Array), (arrayBuf) => { const outputUint8Array = new Uint8Array(arrayBuf); let hash = ""; for (const byte of outputUint8Array) { const hex = byte.toString(16); hash += "00".slice(0, Math.max(0, 2 - hex.length)) + hex; } return hash; }); } function createInMemoryAPQStore(options = {}) { return createLRUCache({ max: options.max ?? 1e3, ttl: options.ttl ?? 36e3 }); } function isAPQExtension(input) { return input != null && typeof input === "object" && "version" in input && input?.version === 1 && "sha256Hash" in input && typeof input?.sha256Hash === "string"; } function decodeAPQExtension(input) { if (isAPQExtension(input)) return input; return null; } function useAPQ(options = {}) { const { store = createInMemoryAPQStore(), hash = hashSHA256, responseConfig = {} } = options; return { onParams({ params, setParams, fetchAPI }) { const persistedQueryData = decodeAPQExtension(params.extensions?.["persistedQuery"]); if (persistedQueryData === null) return; if (params.query == null) return handleMaybePromise(() => store.get(persistedQueryData.sha256Hash), (persistedQuery) => { if (persistedQuery == null) throw createGraphQLError("PersistedQueryNotFound", { extensions: { http: { status: responseConfig.forceStatusCodeOk ? 200 : 404 }, code: "PERSISTED_QUERY_NOT_FOUND" } }); setParams({ ...params, query: persistedQuery }); }); return handleMaybePromise(() => hash(params.query, fetchAPI), (expectedHash) => { if (persistedQueryData.sha256Hash !== expectedHash) throw createGraphQLError("PersistedQueryMismatch", { extensions: { http: { status: responseConfig.forceStatusCodeOk ? 200 : 400 }, code: "PERSISTED_QUERY_MISMATCH" } }); return store.set(persistedQueryData.sha256Hash, params.query); }); } }; } //#endregion export { createInMemoryAPQStore, hashSHA256, useAPQ };