UNPKG

@benzene/core

Version:

Fast, minimal, agnostic GraphQL Libraries

199 lines (191 loc) 6.78 kB
'use strict'; Object.defineProperty(exports, '__esModule', { value: true }); var graphql = require('graphql'); var lru = require('tiny-lru'); function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; } var lru__default = /*#__PURE__*/_interopDefaultLegacy(lru); function isAsyncIterator(val) { return typeof Object(val)[Symbol.asyncIterator] === "function"; } /** * Create a compileQuery function using graphql-js * @returns CompileQuery */ function makeCompileQuery() { return function compileQuery(schema) { return { execute(args) { return graphql.execute({ ...args, schema }); }, subscribe(args) { return graphql.subscribe({ ...args, schema }); }, }; }; } function isExecutionResult(val) { return (typeof val === "object" && val !== null && (Array.isArray(val.errors) || (typeof val.data === "object" && !Array.isArray(val.data)))); } /** * Validate whether an operation does not exist * or operationName is missing. Even though * execution will realize this, we need to provide * this hint mainly for handlers to avoid execution * @param operation * @param operationName */ function validateOperationName(operation, operationName) { if (operation) return []; if (!operationName) { return [ new graphql.GraphQLError("Must provide operation name if query contains multiple operations."), ]; } return [new graphql.GraphQLError(`Unknown operation named "${operationName}".`)]; } /** * Given a GraphQLError, format it according to the rules described by the * Response Format, Errors section of the GraphQL Specification. */ function formatError(error) { const formattedError = { message: error.message, }; if (error.locations != null) { formattedError.locations = error.locations; } if (error.path != null) { formattedError.path = error.path; } if (error.extensions != null && Object.keys(error.extensions).length > 0) { formattedError.extensions = error.extensions; } return formattedError; } class Benzene { constructor(options) { if (!options) throw new TypeError("GQL must be initialized with options"); this.validateFn = options.validateFn || graphql.validate; this.validationRules = options.validationRules; this.formatErrorFn = options.formatErrorFn || formatError; this.contextFn = options.contextFn; // build cache this.lru = lru__default["default"](1024); // construct schema and validate if (!(options.schema instanceof graphql.GraphQLSchema)) { throw new Error(`Expected ${options.schema} to be a GraphQL schema.`); } const schemaValidationErrors = graphql.validateSchema(options.schema); if (schemaValidationErrors.length > 0) { throw schemaValidationErrors; } this.schema = options.schema; this.compileQuery = options.compileQuery || makeCompileQuery(); } compile(query, operationName) { let document; if (typeof query === "object") { // query is DocumentNode document = query; query = graphql.print(document); } const key = query + (operationName ? `:${operationName}` : ""); let cached = this.lru.get(key); if (cached) { return cached; } else { if (!document) { if (!query) throw new Error("Must provide document."); try { document = graphql.parse(query); } catch (syntaxErr) { return { errors: [syntaxErr], }; } } const validationErrors = this.validateFn(this.schema, document, this.validationRules); if (validationErrors.length > 0) { return { errors: validationErrors, }; } const compiled = this.compileQuery(this.schema, document, operationName); // Compilation is a failure since its result is ExecutionResult if (isExecutionResult(compiled)) return compiled; cached = compiled; cached.document = document; const operation = graphql.getOperationAST(document, operationName)?.operation; if (operation) { // If we could not determine the operation, it is unsafe to cache cached.operation = operation; this.lru.set(key, cached); } return cached; } } formatExecutionResult(result) { const o = {}; if (result.data) o.data = result.data; if (result.errors) o.errors = result.errors.map(this.formatErrorFn); return o; } async graphql({ source, contextValue, variableValues, operationName, rootValue, }) { const cachedOrResult = this.compile(source, operationName); if (isExecutionResult(cachedOrResult)) return cachedOrResult; return this.execute({ document: cachedOrResult.document, contextValue, variableValues, rootValue, operationName, compiled: cachedOrResult, }); } execute(args) { if (!args.compiled) { if (!args.document) throw new Error("Must provide document."); const compiledOrResult = this.compile(args.document); if (isExecutionResult(compiledOrResult)) return compiledOrResult; args.compiled = compiledOrResult; } else { args.document = args.compiled.document; } return args.compiled.execute(args); } async subscribe(args) { if (!args.compiled) { if (!args.document) throw new Error("Must provide document."); const compiledOrResult = this.compile(args.document); if (isExecutionResult(compiledOrResult)) return compiledOrResult; args.compiled = compiledOrResult; } else { args.document = args.compiled.document; } return args.compiled.subscribe(args); } } exports.Benzene = Benzene; exports.isAsyncIterator = isAsyncIterator; exports.isExecutionResult = isExecutionResult; exports.makeCompileQuery = makeCompileQuery; exports.validateOperationName = validateOperationName;