@graphql-yoga/plugin-apq
Version:
APQ plugin for GraphQL Yoga.
58 lines (56 loc) • 2.43 kB
JavaScript
let graphql_yoga = require("graphql-yoga");
let _whatwg_node_promise_helpers = require("@whatwg-node/promise-helpers");
//#region src/index.ts
function hashSHA256(text, api = globalThis) {
const inputUint8Array = new api.TextEncoder().encode(text);
return (0, _whatwg_node_promise_helpers.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 (0, graphql_yoga.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 (0, _whatwg_node_promise_helpers.handleMaybePromise)(() => store.get(persistedQueryData.sha256Hash), (persistedQuery) => {
if (persistedQuery == null) throw (0, graphql_yoga.createGraphQLError)("PersistedQueryNotFound", { extensions: {
http: { status: responseConfig.forceStatusCodeOk ? 200 : 404 },
code: "PERSISTED_QUERY_NOT_FOUND"
} });
setParams({
...params,
query: persistedQuery
});
});
return (0, _whatwg_node_promise_helpers.handleMaybePromise)(() => hash(params.query, fetchAPI), (expectedHash) => {
if (persistedQueryData.sha256Hash !== expectedHash) throw (0, graphql_yoga.createGraphQLError)("PersistedQueryMismatch", { extensions: {
http: { status: responseConfig.forceStatusCodeOk ? 200 : 400 },
code: "PERSISTED_QUERY_MISMATCH"
} });
return store.set(persistedQueryData.sha256Hash, params.query);
});
} };
}
//#endregion
exports.createInMemoryAPQStore = createInMemoryAPQStore;
exports.hashSHA256 = hashSHA256;
exports.useAPQ = useAPQ;