UNPKG

@graphql-hive/yoga

Version:
179 lines (178 loc) • 8.73 kB
import { GraphQLError, Kind, parse } from 'graphql'; import { _createLRUCache } from 'graphql-yoga'; import { autoDisposeSymbol, createHive as createHiveClient, isAsyncIterable, isHiveClient, } from '@graphql-hive/core'; import { usePersistedOperations } from '@graphql-yoga/plugin-persisted-operations'; export { atLeastOnceSampler, createSchemaFetcher, createServicesFetcher, createSupergraphSDLFetcher, } from '@graphql-hive/core'; export function createHive(clientOrOptions) { return createHiveClient(Object.assign(Object.assign({}, clientOrOptions), { agent: Object.assign({ name: 'hive-client-yoga' }, clientOrOptions.agent) })); } export function useHive(clientOrOptions) { const parsedDocumentCache = _createLRUCache(); let latestSchema = null; const contextualCache = new WeakMap(); let hive; let yoga; return { onYogaInit(payload) { yoga = payload.yoga; }, onSchemaChange({ schema }) { hive.reportSchema({ schema }); latestSchema = schema; }, onParams(context) { // we set the params if there is either a query or documentId in the request if ((context.params.query || 'documentId' in context.params) && latestSchema) { contextualCache.set(context.context, { callback: hive.collectUsage(), paramsArgs: context.params, }); } }, // since response-cache modifies the executed GraphQL document, we need to extract it after parsing. onParse(parseCtx) { return ctx => { if (ctx.result.kind === Kind.DOCUMENT) { const record = contextualCache.get(ctx.context); if (record) { record.parsedDocument = ctx.result; parsedDocumentCache.set(parseCtx.params.source, ctx.result); } } }; }, onExecute() { return { onExecuteDone({ args, result }) { var _a; const record = contextualCache.get(args.contextValue); if (!record) { return; } record.executionArgs = args; if (!isAsyncIterable(result)) { args.contextValue.waitUntil(record.callback(Object.assign(Object.assign({}, record.executionArgs), { document: (_a = record.parsedDocument) !== null && _a !== void 0 ? _a : record.executionArgs.document }), result, record.experimental__documentId)); return; } const errors = []; return { onNext(ctx) { if (!ctx.result.errors) { return; } errors.push(...ctx.result.errors); }, onEnd() { args.contextValue.waitUntil(record.callback(args, errors.length ? { errors } : {}, record.experimental__documentId)); }, }; }, }; }, onSubscribe(context) { const record = contextualCache.get(context.args.contextValue); return { onSubscribeResult() { const experimental__persistedDocumentHash = record === null || record === void 0 ? void 0 : record.experimental__documentId; hive.collectSubscriptionUsage({ args: context.args, experimental__persistedDocumentHash, }); }, }; }, onResultProcess({ serverContext, result }) { const record = contextualCache.get(serverContext); if (!record || Array.isArray(result) || isAsyncIterable(result) || record.executionArgs) { return; } // Report if execution was skipped due to response cache ( Symbol.for('servedFromResponseCache') in context.result) if (record.paramsArgs.query && latestSchema && Symbol.for('servedFromResponseCache') in result) { try { let document = parsedDocumentCache.get(record.paramsArgs.query); if (document === undefined) { document = parse(record.paramsArgs.query); parsedDocumentCache.set(record.paramsArgs.query, document); } serverContext.waitUntil(record.callback({ document, schema: latestSchema, variableValues: record.paramsArgs.variables, operationName: record.paramsArgs.operationName, contextValue: serverContext, }, result, record.experimental__documentId)); } catch (err) { yoga.logger.error(err); } } }, onPluginInit({ addPlugin }) { hive = isHiveClient(clientOrOptions) ? clientOrOptions : createHive(Object.assign(Object.assign({}, clientOrOptions), { agent: clientOrOptions.agent ? Object.assign({ logger: { // Hive Plugin should respect the given Yoga logger error: (...args) => yoga.logger.error(...args), info: (...args) => yoga.logger.info(...args), }, // Hive Plugin should respect the given FetchAPI, note that this is not `yoga.fetch` fetch: (...args) => yoga.fetchAPI.fetch(...args) }, clientOrOptions.agent) : undefined })); void hive.info(); const experimentalPersistedDocs = hive.experimental__persistedDocuments; if (experimentalPersistedDocs) { addPlugin(usePersistedOperations({ extractPersistedOperationId(body, request) { if ('documentId' in body && typeof body.documentId === 'string') { return body.documentId; } const documentId = new URL(request.url).searchParams.get('documentId'); if (documentId) { return documentId; } return null; }, async getPersistedOperation(key, _request, context) { const document = await experimentalPersistedDocs.resolve(key); // after we resolve the document we need to update the cache record to contain the resolved document if (document) { const record = contextualCache.get(context); if (record) { record.experimental__documentId = key; record.paramsArgs = Object.assign(Object.assign({}, record.paramsArgs), { query: document }); } } return document; }, allowArbitraryOperations(request) { return experimentalPersistedDocs.allowArbitraryDocuments(request); }, customErrors: { keyNotFound() { return new GraphQLError('Persisted document not found.', { extensions: { code: 'PERSISTED_DOCUMENT_NOT_FOUND' }, }); }, notFound() { return new GraphQLError('Persisted document not found.', { extensions: { code: 'PERSISTED_DOCUMENT_NOT_FOUND' }, }); }, persistedQueryOnly() { return new GraphQLError('No persisted document provided.', { extensions: { code: 'PERSISTED_DOCUMENT_REQUIRED' }, }); }, }, })); } }, onDispose() { if (hive[autoDisposeSymbol]) { return hive.dispose(); } }, }; }