UNPKG

@graphql-tools/executor

Version:

Fork of GraphQL.js' execute function

273 lines (272 loc) • 14.3 kB
import { defaultFieldResolver, defaultTypeResolver, DocumentNode, FieldNode, FragmentDefinitionNode, GraphQLError, GraphQLField, GraphQLFieldResolver, GraphQLFormattedError, GraphQLObjectType, GraphQLSchema, GraphQLTypeResolver, OperationDefinitionNode } from 'graphql'; import { GraphQLResolveInfo, Maybe, Path, VariableValues, type IncrementalSpec } from '@graphql-tools/utils'; import { TypedDocumentNode } from '@graphql-typed-document-node/core'; import { type MaybePromise } from '@whatwg-node/promise-helpers'; import { type AsyncPayloadRecord, type CompletedResult, type IncrementalPublisherState, type PendingResult } from './incremental.cjs'; import { collectIncrementalSubfields, type DeferUsage, type DeferUsageSet } from './incrementalCollect.cjs'; export { defaultFieldResolver, defaultTypeResolver }; export type { CompletedResult, PendingResult } from './incremental.cjs'; export interface SingularExecutionResult<TData = any, TExtensions = any> { errors?: ReadonlyArray<GraphQLError>; data?: TData | null; extensions?: TExtensions; } /** * Terminology * * "Definitions" are the generic name for top-level statements in the document. * Examples of this include: * 1) Operations (such as a query) * 2) Fragments * * "Operations" are a generic name for requests in the document. * Examples of this include: * 1) query, * 2) mutation * * "Selections" are the definitions that can appear legally and at * single level of the query. These include: * 1) field references e.g `a` * 2) fragment "spreads" e.g. `...c` * 3) inline fragment "spreads" e.g. `...on Type { a }` */ /** * Data that must be available at all points during query execution. * * Namely, schema of the type system that is currently executing, * and the fragments defined in the query document */ export interface ExecutionContext<TVariables = any, TContext = any> { schema: GraphQLSchema; fragments: Record<string, FragmentDefinitionNode>; rootValue: unknown; contextValue: TContext; operation: OperationDefinitionNode; variableValues: VariableValues<TVariables>; /** Precomputed for GraphQLResolveInfo (GQL16 coerced map vs GQL17 VariableValues). */ resolveInfoVariableValues: GraphQLResolveInfo['variableValues']; fieldResolver: GraphQLFieldResolver<any, TContext>; typeResolver: GraphQLTypeResolver<any, TContext>; subscribeFieldResolver: GraphQLFieldResolver<any, TContext>; errors: Array<GraphQLError>; subsequentPayloads: Set<AsyncPayloadRecord>; incrementalSpec: IncrementalSpec; publisher?: IncrementalPublisherState; /** * Allocated only when the operation actually uses @defer/@stream so * default execution does not pay for null-bubble bookkeeping. */ nulledPaths?: Array<ReadonlyArray<string | number>>; /** * Per-execution FieldNode → DeferUsage. Document AST nodes are reused * across requests, so this must not live at module scope. */ deferUsageByFieldNode?: WeakMap<FieldNode, DeferUsage | undefined>; /** * Field-node arrays from a planned group → that group's DeferUsageSet. * List items share the same array identity, so overlapping sibling * usages stay stable across async item completion. */ deferUsagesForFieldNodes?: WeakMap<ReadonlyArray<FieldNode>, DeferUsageSet>; /** * Memoize incremental subfield collect per fieldNodes array and * concrete return type so list items of the same type reuse DeferUsage * objects without sharing type-conditioned selections across a * heterogeneous interface or union list. */ incrementalSubfieldCache?: WeakMap<ReadonlyArray<FieldNode>, WeakMap<GraphQLObjectType, ReturnType<typeof collectIncrementalSubfields>>>; signal?: AbortSignal; getAbortSignal: () => AbortSignal | undefined; getAsyncHelpers: () => { promiseAll: typeof Promise.all; track: (maybePromises: ReadonlyArray<unknown>) => void; }; onSignalAbort?(handler: () => void): void; signalPromise?: Promise<never>; schemaCoordinateInErrors?: boolean; } export interface FormattedExecutionResult<TData = Record<string, unknown>, TExtensions = Record<string, unknown>> { errors?: ReadonlyArray<GraphQLFormattedError>; data?: TData | null; extensions?: TExtensions; } export interface IncrementalExecutionResults<TData = Record<string, unknown>, TExtensions = Record<string, unknown>> { initialResult: InitialIncrementalExecutionResult<TData, TExtensions>; subsequentResults: AsyncGenerator<SubsequentIncrementalExecutionResult<TData, TExtensions>, void, void>; } export interface InitialIncrementalExecutionResult<TData = Record<string, unknown>, TExtensions = Record<string, unknown>> extends SingularExecutionResult<TData, TExtensions> { hasNext: boolean; pending?: ReadonlyArray<PendingResult>; incremental?: ReadonlyArray<IncrementalResult<TData, TExtensions>>; completed?: ReadonlyArray<CompletedResult>; extensions?: TExtensions; } export interface FormattedInitialIncrementalExecutionResult<TData = Record<string, unknown>, TExtensions = Record<string, unknown>> extends FormattedExecutionResult<TData, TExtensions> { hasNext: boolean; pending?: ReadonlyArray<PendingResult>; incremental?: ReadonlyArray<FormattedIncrementalResult<TData, TExtensions>>; completed?: ReadonlyArray<CompletedResult>; extensions?: TExtensions; } export interface SubsequentIncrementalExecutionResult<TData = Record<string, unknown>, TExtensions = Record<string, unknown>> { hasNext: boolean; pending?: ReadonlyArray<PendingResult>; incremental?: ReadonlyArray<IncrementalResult<TData, TExtensions>>; completed?: ReadonlyArray<CompletedResult>; extensions?: TExtensions; } export interface FormattedSubsequentIncrementalExecutionResult<TData = Record<string, unknown>, TExtensions = Record<string, unknown>> { hasNext: boolean; pending?: ReadonlyArray<PendingResult>; incremental?: ReadonlyArray<FormattedIncrementalResult<TData, TExtensions>>; completed?: ReadonlyArray<CompletedResult>; extensions?: TExtensions; } export interface IncrementalDeferResult<TData = Record<string, unknown>, TExtensions = Record<string, unknown>> extends SingularExecutionResult<TData, TExtensions> { path?: ReadonlyArray<string | number>; label?: string; id?: string; subPath?: ReadonlyArray<string | number>; } export interface FormattedIncrementalDeferResult<TData = Record<string, unknown>, TExtensions = Record<string, unknown>> extends FormattedExecutionResult<TData, TExtensions> { path?: ReadonlyArray<string | number>; label?: string; id?: string; subPath?: ReadonlyArray<string | number>; } export interface IncrementalStreamResult<TData = Array<unknown>, TExtensions = Record<string, unknown>> { errors?: ReadonlyArray<GraphQLError>; items?: TData | null; path?: ReadonlyArray<string | number>; label?: string; id?: string; extensions?: TExtensions; } export interface FormattedIncrementalStreamResult<TData = Array<unknown>, TExtensions = Record<string, unknown>> { errors?: ReadonlyArray<GraphQLFormattedError>; items?: TData | null; path?: ReadonlyArray<string | number>; label?: string; id?: string; extensions?: TExtensions; } export type IncrementalResult<TData = Record<string, unknown>, TExtensions = Record<string, unknown>> = IncrementalDeferResult<TData, TExtensions> | IncrementalStreamResult<TData, TExtensions>; export type FormattedIncrementalResult<TData = Record<string, unknown>, TExtensions = Record<string, unknown>> = FormattedIncrementalDeferResult<TData, TExtensions> | FormattedIncrementalStreamResult<TData, TExtensions>; export interface ExecutionArgs<TData = any, TVariables = any, TContext = any> { schema: GraphQLSchema; document: TypedDocumentNode<TData, TVariables>; rootValue?: unknown; contextValue?: TContext; variableValues?: TVariables; operationName?: Maybe<string>; fieldResolver?: Maybe<GraphQLFieldResolver<any, TContext>>; typeResolver?: Maybe<GraphQLTypeResolver<any, TContext>>; subscribeFieldResolver?: Maybe<GraphQLFieldResolver<any, TContext>>; signal?: AbortSignal; schemaCoordinateInErrors?: boolean; /** * When true, omit "Did you mean ...?" suggestion text from variable coercion * errors with GraphQL 17. GraphQL 16 ignores this option. * Matches `graphql@17`'s `ExecutionArgs.hideSuggestions`. */ hideSuggestions?: Maybe<boolean>; /** * Incremental delivery payload format. * * HTTP mapping lives in `INCREMENTAL_DELIVERY_SPECS` from `@graphql-tools/utils`. * `'v0.1'` is the default. */ incrementalSpec?: IncrementalSpec; } /** * Implements the "Executing requests" section of the GraphQL specification, * including `@defer` and `@stream` as proposed in * https://github.com/graphql/graphql-spec/pull/742 * * This function returns a Promise of an IncrementalExecutionResults * object. This object either consists of a single ExecutionResult, or an * object containing an `initialResult` and a stream of `subsequentResults`. * * If the arguments to this function do not result in a legal execution context, * a GraphQLError will be thrown immediately explaining the invalid input. */ export declare function execute<TData = any, TVariables = any, TContext = any>(args: ExecutionArgs<TData, TVariables, TContext>): MaybePromise<SingularExecutionResult<TData> | IncrementalExecutionResults<TData>>; /** * Also implements the "Executing requests" section of the GraphQL specification. * However, it guarantees to complete synchronously (or throw an error) assuming * that all field resolvers are also synchronous. */ export declare function executeSync(args: ExecutionArgs): SingularExecutionResult; /** * Essential assertions before executing to provide developer feedback for * improper use of the GraphQL library. * * @internal */ export declare function assertValidExecutionArguments<TVariables>(schema: GraphQLSchema, document: TypedDocumentNode<any, TVariables>, rawVariableValues: Maybe<TVariables>): void; export declare function getFragmentsFromDocument(document: DocumentNode): Record<string, FragmentDefinitionNode>; /** * Constructs a ExecutionContext object from the arguments passed to * execute, which we will pass throughout the other execution methods. * * Throws a GraphQLError if a valid execution context cannot be created. * * TODO: consider no longer exporting this function * @internal */ export declare function buildExecutionContext<TData = any, TVariables = any, TContext = any>(args: ExecutionArgs<TData, TVariables, TContext>): ReadonlyArray<GraphQLError> | ExecutionContext; /** * TODO: consider no longer exporting this function * @internal */ export declare function buildResolveInfo(exeContext: ExecutionContext, fieldDef: GraphQLField<unknown, unknown>, fieldNodes: Array<FieldNode>, parentType: GraphQLObjectType, path: Path): GraphQLResolveInfo; export declare const CRITICAL_ERROR: 'CRITICAL_ERROR'; /** * Implements the "Subscribe" algorithm described in the GraphQL specification, * including `@defer` and `@stream` as proposed in * https://github.com/graphql/graphql-spec/pull/742 * * Returns a Promise which resolves to either an AsyncIterator (if successful) * or an ExecutionResult (error). The promise will be rejected if the schema or * other arguments to this function are invalid, or if the resolved event stream * is not an async iterable. * * If the client-provided arguments to this function do not result in a * compliant subscription, a GraphQL Response (ExecutionResult) with descriptive * errors and no data will be returned. * * If the source stream could not be created due to faulty subscription resolver * logic or underlying systems, the promise will resolve to a single * ExecutionResult containing `errors` and no `data`. * * If the operation succeeded, the promise resolves to an AsyncIterator, which * yields a stream of result representing the response stream. * * Each result may be an ExecutionResult with no `hasNext` (if executing the * event did not use `@defer` or `@stream`), or an * `InitialIncrementalExecutionResult` or `SubsequentIncrementalExecutionResult` * (if executing the event used `@defer` or `@stream`). In the case of * incremental execution results, each event produces a single * `InitialIncrementalExecutionResult` followed by one or more * `SubsequentIncrementalExecutionResult`s; all but the last have `hasNext: true`, * and the last has `hasNext: false`. There is no interleaving between results * generated from the same original event. * * Accepts an object with named arguments. */ export declare function subscribe<TData = any, TVariables = any, TContext = any>(args: ExecutionArgs<TData, TVariables, TContext>): MaybePromise<AsyncGenerator<SingularExecutionResult<TData> | InitialIncrementalExecutionResult<TData> | SubsequentIncrementalExecutionResult<TData>, void, void> | SingularExecutionResult<TData>>; export declare function isIncrementalResults<TData>(results: any): results is IncrementalExecutionResults<TData>; export declare function flattenIncrementalResults<TData>(incrementalResults: IncrementalExecutionResults<TData>): AsyncGenerator<SubsequentIncrementalExecutionResult<TData, Record<string, unknown>>, void, void>; /** * This method looks up the field on the given type definition. * It has special casing for the three introspection fields, * __schema, __type and __typename. __typename is special because * it can always be queried as a field, even in situations where no * other fields are allowed, like on a Union. __schema and __type * could get automatically added to the query type, but that would * require mutating type definitions, which would cause issues. * * @internal */ export declare function getFieldDef(schema: GraphQLSchema, parentType: GraphQLObjectType, fieldNode: FieldNode): Maybe<GraphQLField<unknown, unknown>>; export declare function isIncrementalResult<TData>(result: SingularExecutionResult<TData> | IncrementalExecutionResults<TData>): result is IncrementalExecutionResults<TData>;