UNPKG

graphql-request

Version:

Minimal GraphQL client supporting Node and browsers for scripts or simple apps.

112 lines (102 loc) 3.73 kB
import type { Errors } from '../errors/__.js' import { ContextualError } from '../errors/ContextualError.js' import { casesExhausted, createDeferred, debug } from '../prelude.js' import { defaultFunctionName } from './lib.js' import type { Core, Extension, ResultEnvelop } from './main.js' import { createResultEnvelope } from './main.js' import type { HookResult, HookResultErrorAsync } from './runHook.js' import { runHook } from './runHook.js' export const runPipeline = async ( { core, hookNamesOrderedBySequence, originalInput, extensionsStack, asyncErrorDeferred }: { core: Core hookNamesOrderedBySequence: readonly string[] originalInput: unknown extensionsStack: readonly Extension[] asyncErrorDeferred: HookResultErrorAsync }, ): Promise<ResultEnvelop | Errors.ContextualError> => { const [hookName, ...hookNamesRest] = hookNamesOrderedBySequence if (!hookName) { debug(`pipeline: ending`) const result = await runPipelineEnd({ extensionsStack, result: originalInput }) debug(`pipeline: returning`) return createResultEnvelope(result) } debug(`hook ${hookName}: start`) const done = createDeferred<HookResult>({ strict: false }) void runHook({ core, name: hookName, done: done.resolve, originalInput, extensionsStack, asyncErrorDeferred, nextExtensionsStack: [], }) const signal = await Promise.race( [done.promise, asyncErrorDeferred.promise], ) switch (signal.type) { case `completed`: { const { result, nextExtensionsStack } = signal return await runPipeline({ core, hookNamesOrderedBySequence: hookNamesRest, originalInput: result, extensionsStack: nextExtensionsStack, asyncErrorDeferred, }) } case `shortCircuited`: { debug(`signal: shortCircuited`) const { result } = signal return createResultEnvelope(result) } case `error`: { debug(`signal: error`) const wasAsync = asyncErrorDeferred.isResolved() // todo type test for this possible return value switch (signal.source) { case `extension`: { // todo test these 2 branches explicitly const nameTip = signal.extensionName === defaultFunctionName ? ` (use named functions to improve this error message)` : `` const message = wasAsync ? `There was an error in the extension "${signal.extensionName}"${nameTip}.` : `There was an error in the extension "${signal.extensionName}"${nameTip} while running hook "${signal.hookName}".` return new ContextualError(message, { hookName: signal.hookName, source: signal.source, extensionName: signal.extensionName, }, signal.error) } case `implementation`: { const message = `There was an error in the core implementation of hook "${signal.hookName}".` return new ContextualError(message, { hookName: signal.hookName, source: signal.source }, signal.error) } case `user`: { return signal.error } default: throw casesExhausted(signal) } } default: throw casesExhausted(signal) } } const runPipelineEnd = async ({ extensionsStack, result, }: { result: unknown; extensionsStack: readonly Extension[] }): Promise<unknown> => { const [extension, ...extensionsRest] = extensionsStack if (!extension) return result debug(`extension ${extension.name}: end`) extension.currentChunk.resolve(result as any) const nextResult = await extension.body.promise return await runPipelineEnd({ extensionsStack: extensionsRest, result: nextResult, }) }