diff --git a/src/execution/IncrementalPublisher.ts b/src/execution/IncrementalPublisher.ts index 0722da1ed1..0504238eae 100644 --- a/src/execution/IncrementalPublisher.ts +++ b/src/execution/IncrementalPublisher.ts @@ -1,174 +1,35 @@ import { invariant } from '../jsutils/invariant.js'; import { isPromise } from '../jsutils/isPromise.js'; import type { ObjMap } from '../jsutils/ObjMap.js'; -import type { Path } from '../jsutils/Path.js'; import { pathToArray } from '../jsutils/Path.js'; -import type { PromiseOrValue } from '../jsutils/PromiseOrValue.js'; import { promiseWithResolvers } from '../jsutils/promiseWithResolvers.js'; -import type { - GraphQLError, - GraphQLFormattedError, -} from '../error/GraphQLError.js'; - -/** - * The result of GraphQL execution. - * - * - `errors` is included when any errors occurred as a non-empty array. - * - `data` is the result of a successful execution of the query. - * - `hasNext` is true if a future payload is expected. - * - `extensions` is reserved for adding non-standard properties. - * - `incremental` is a list of the results from defer/stream directives. - */ -export interface ExecutionResult< - TData = ObjMap, - TExtensions = ObjMap, -> { - errors?: ReadonlyArray; - data?: TData | null; - extensions?: TExtensions; -} - -export interface FormattedExecutionResult< - TData = ObjMap, - TExtensions = ObjMap, -> { - errors?: ReadonlyArray; - data?: TData | null; - extensions?: TExtensions; -} - -export interface ExperimentalIncrementalExecutionResults< - TData = unknown, - TExtensions = ObjMap, -> { - initialResult: InitialIncrementalExecutionResult; - subsequentResults: AsyncGenerator< - SubsequentIncrementalExecutionResult, - void, - void - >; -} - -export interface InitialIncrementalExecutionResult< - TData = ObjMap, - TExtensions = ObjMap, -> extends ExecutionResult { - data: TData; - pending: ReadonlyArray; - hasNext: true; - extensions?: TExtensions; -} - -export interface FormattedInitialIncrementalExecutionResult< - TData = ObjMap, - TExtensions = ObjMap, -> extends FormattedExecutionResult { - data: TData; - pending: ReadonlyArray; - hasNext: boolean; - extensions?: TExtensions; -} - -export interface SubsequentIncrementalExecutionResult< - TData = unknown, - TExtensions = ObjMap, -> { - pending?: ReadonlyArray; - incremental?: ReadonlyArray>; - completed?: ReadonlyArray; - hasNext: boolean; - extensions?: TExtensions; -} - -export interface FormattedSubsequentIncrementalExecutionResult< - TData = unknown, - TExtensions = ObjMap, -> { - hasNext: boolean; - pending?: ReadonlyArray; - incremental?: ReadonlyArray>; - completed?: ReadonlyArray; - extensions?: TExtensions; -} - -interface BareDeferredGroupedFieldSetResult> { - errors?: ReadonlyArray; - data: TData; -} - -export interface IncrementalDeferResult< - TData = ObjMap, - TExtensions = ObjMap, -> extends BareDeferredGroupedFieldSetResult { - id: string; - subPath?: ReadonlyArray; - extensions?: TExtensions; -} +import type { GraphQLError } from '../error/GraphQLError.js'; -export interface FormattedIncrementalDeferResult< - TData = ObjMap, - TExtensions = ObjMap, -> { - errors?: ReadonlyArray; - data: TData; - id: string; - subPath?: ReadonlyArray; - extensions?: TExtensions; -} - -interface BareStreamItemsResult> { - errors?: ReadonlyArray; - items: TData; -} - -export interface IncrementalStreamResult< - TData = ReadonlyArray, - TExtensions = ObjMap, -> extends BareStreamItemsResult { - id: string; - subPath?: ReadonlyArray; - extensions?: TExtensions; -} - -export interface FormattedIncrementalStreamResult< - TData = Array, - TExtensions = ObjMap, -> { - errors?: ReadonlyArray; - items: TData; - id: string; - subPath?: ReadonlyArray; - extensions?: TExtensions; -} - -export type IncrementalResult> = - | IncrementalDeferResult - | IncrementalStreamResult; - -export type FormattedIncrementalResult< - TData = unknown, - TExtensions = ObjMap, -> = - | FormattedIncrementalDeferResult - | FormattedIncrementalStreamResult; - -export interface PendingResult { - id: string; - path: ReadonlyArray; - label?: string; -} - -export interface CompletedResult { - id: string; - errors?: ReadonlyArray; -} - -export interface FormattedCompletedResult { - path: ReadonlyArray; - label?: string; - errors?: ReadonlyArray; -} +import type { + CancellableStreamRecord, + CompletedResult, + DeferredFragmentRecord, + DeferredGroupedFieldSetResult, + ExperimentalIncrementalExecutionResults, + IncrementalDataRecord, + IncrementalDataRecordResult, + IncrementalDeferResult, + IncrementalResult, + IncrementalStreamResult, + InitialIncrementalExecutionResult, + PendingResult, + StreamItemsResult, + SubsequentIncrementalExecutionResult, + SubsequentResultRecord, +} from './types.js'; +import { + isCancellableStreamRecord, + isDeferredFragmentRecord, + isDeferredGroupedFieldSetRecord, + isDeferredGroupedFieldSetResult, + isNonReconcilableDeferredGroupedFieldSetResult, +} from './types.js'; export function buildIncrementalResponse( context: IncrementalPublisherContext, @@ -657,138 +518,3 @@ class IncrementalPublisher { }; } } - -function isDeferredFragmentRecord( - subsequentResultRecord: SubsequentResultRecord, -): subsequentResultRecord is DeferredFragmentRecord { - return 'parent' in subsequentResultRecord; -} - -function isDeferredGroupedFieldSetRecord( - incrementalDataRecord: IncrementalDataRecord, -): incrementalDataRecord is DeferredGroupedFieldSetRecord { - return 'deferredFragmentRecords' in incrementalDataRecord; -} - -export type DeferredGroupedFieldSetResult = - | ReconcilableDeferredGroupedFieldSetResult - | NonReconcilableDeferredGroupedFieldSetResult; - -function isDeferredGroupedFieldSetResult( - subsequentResult: DeferredGroupedFieldSetResult | StreamItemsResult, -): subsequentResult is DeferredGroupedFieldSetResult { - return 'deferredFragmentRecords' in subsequentResult; -} - -interface ReconcilableDeferredGroupedFieldSetResult { - deferredFragmentRecords: ReadonlyArray; - path: Array; - result: BareDeferredGroupedFieldSetResult; - incrementalDataRecords: ReadonlyArray | undefined; - sent?: true | undefined; - errors?: never; -} - -interface NonReconcilableDeferredGroupedFieldSetResult { - errors: ReadonlyArray; - deferredFragmentRecords: ReadonlyArray; - path: Array; - result?: never; -} - -function isNonReconcilableDeferredGroupedFieldSetResult( - deferredGroupedFieldSetResult: DeferredGroupedFieldSetResult, -): deferredGroupedFieldSetResult is NonReconcilableDeferredGroupedFieldSetResult { - return deferredGroupedFieldSetResult.errors !== undefined; -} - -export interface DeferredGroupedFieldSetRecord { - deferredFragmentRecords: ReadonlyArray; - result: PromiseOrValue; -} - -export interface SubsequentResultRecord { - path: Path | undefined; - label: string | undefined; - id?: string | undefined; -} - -/** @internal */ -export class DeferredFragmentRecord implements SubsequentResultRecord { - path: Path | undefined; - label: string | undefined; - id?: string | undefined; - parent: DeferredFragmentRecord | undefined; - expectedReconcilableResults: number; - results: Array; - reconcilableResults: Array; - children: Set; - - constructor(opts: { - path: Path | undefined; - label: string | undefined; - parent: DeferredFragmentRecord | undefined; - }) { - this.path = opts.path; - this.label = opts.label; - this.parent = opts.parent; - this.expectedReconcilableResults = 0; - this.results = []; - this.reconcilableResults = []; - this.children = new Set(); - } -} - -export interface CancellableStreamRecord extends SubsequentResultRecord { - earlyReturn: () => Promise; -} - -function isCancellableStreamRecord( - subsequentResultRecord: SubsequentResultRecord, -): subsequentResultRecord is CancellableStreamRecord { - return 'earlyReturn' in subsequentResultRecord; -} - -interface ReconcilableStreamItemsResult { - streamRecord: SubsequentResultRecord; - result: BareStreamItemsResult; - incrementalDataRecords: ReadonlyArray | undefined; - errors?: never; -} - -export function isReconcilableStreamItemsResult( - streamItemsResult: StreamItemsResult, -): streamItemsResult is ReconcilableStreamItemsResult { - return streamItemsResult.result !== undefined; -} - -interface TerminatingStreamItemsResult { - streamRecord: SubsequentResultRecord; - result?: never; - incrementalDataRecords?: never; - errors?: never; -} - -interface NonReconcilableStreamItemsResult { - streamRecord: SubsequentResultRecord; - errors: ReadonlyArray; - result?: never; -} - -export type StreamItemsResult = - | ReconcilableStreamItemsResult - | TerminatingStreamItemsResult - | NonReconcilableStreamItemsResult; - -export interface StreamItemsRecord { - streamRecord: SubsequentResultRecord; - result: PromiseOrValue; -} - -export type IncrementalDataRecord = - | DeferredGroupedFieldSetRecord - | StreamItemsRecord; - -export type IncrementalDataRecordResult = - | DeferredGroupedFieldSetResult - | StreamItemsResult; diff --git a/src/execution/__tests__/defer-test.ts b/src/execution/__tests__/defer-test.ts index 31529c078d..71d86862f4 100644 --- a/src/execution/__tests__/defer-test.ts +++ b/src/execution/__tests__/defer-test.ts @@ -20,7 +20,7 @@ import { execute, experimentalExecuteIncrementally } from '../execute.js'; import type { InitialIncrementalExecutionResult, SubsequentIncrementalExecutionResult, -} from '../IncrementalPublisher.js'; +} from '../types.js'; const friendType = new GraphQLObjectType({ fields: { diff --git a/src/execution/__tests__/lists-test.ts b/src/execution/__tests__/lists-test.ts index 167d580ef5..5d646e1770 100644 --- a/src/execution/__tests__/lists-test.ts +++ b/src/execution/__tests__/lists-test.ts @@ -19,7 +19,7 @@ import { GraphQLSchema } from '../../type/schema.js'; import { buildSchema } from '../../utilities/buildASTSchema.js'; import { execute, executeSync } from '../execute.js'; -import type { ExecutionResult } from '../IncrementalPublisher.js'; +import type { ExecutionResult } from '../types.js'; describe('Execute: Accepts any iterable as list value', () => { function complete(rootValue: unknown) { diff --git a/src/execution/__tests__/nonnull-test.ts b/src/execution/__tests__/nonnull-test.ts index 12b223a622..9ad78a09cd 100644 --- a/src/execution/__tests__/nonnull-test.ts +++ b/src/execution/__tests__/nonnull-test.ts @@ -14,7 +14,7 @@ import { GraphQLSchema } from '../../type/schema.js'; import { buildSchema } from '../../utilities/buildASTSchema.js'; import { execute, executeSync } from '../execute.js'; -import type { ExecutionResult } from '../IncrementalPublisher.js'; +import type { ExecutionResult } from '../types.js'; const syncError = new Error('sync'); const syncNonNullError = new Error('syncNonNull'); diff --git a/src/execution/__tests__/oneof-test.ts b/src/execution/__tests__/oneof-test.ts index af0e0580ab..f4a11f8997 100644 --- a/src/execution/__tests__/oneof-test.ts +++ b/src/execution/__tests__/oneof-test.ts @@ -7,7 +7,7 @@ import { parse } from '../../language/parser.js'; import { buildSchema } from '../../utilities/buildASTSchema.js'; import { execute } from '../execute.js'; -import type { ExecutionResult } from '../IncrementalPublisher.js'; +import type { ExecutionResult } from '../types.js'; const schema = buildSchema(` type Query { diff --git a/src/execution/__tests__/stream-test.ts b/src/execution/__tests__/stream-test.ts index 522b82f3d4..905b00be9e 100644 --- a/src/execution/__tests__/stream-test.ts +++ b/src/execution/__tests__/stream-test.ts @@ -22,7 +22,7 @@ import { experimentalExecuteIncrementally } from '../execute.js'; import type { InitialIncrementalExecutionResult, SubsequentIncrementalExecutionResult, -} from '../IncrementalPublisher.js'; +} from '../types.js'; const friendType = new GraphQLObjectType({ fields: { diff --git a/src/execution/__tests__/subscribe-test.ts b/src/execution/__tests__/subscribe-test.ts index eff5032811..e6faca31e5 100644 --- a/src/execution/__tests__/subscribe-test.ts +++ b/src/execution/__tests__/subscribe-test.ts @@ -22,7 +22,7 @@ import { GraphQLSchema } from '../../type/schema.js'; import type { ExecutionArgs } from '../execute.js'; import { createSourceEventStream, subscribe } from '../execute.js'; -import type { ExecutionResult } from '../IncrementalPublisher.js'; +import type { ExecutionResult } from '../types.js'; import { SimplePubSub } from './simplePubSub.js'; diff --git a/src/execution/execute.ts b/src/execution/execute.ts index e5e220dd66..dfb7f36074 100644 --- a/src/execution/execute.ts +++ b/src/execution/execute.ts @@ -58,6 +58,8 @@ import { collectFields, collectSubfields as _collectSubfields, } from './collectFields.js'; +import { buildIncrementalResponse } from './IncrementalPublisher.js'; +import { mapAsyncIterable } from './mapAsyncIterable.js'; import type { CancellableStreamRecord, DeferredGroupedFieldSetRecord, @@ -68,13 +70,11 @@ import type { StreamItemsRecord, StreamItemsResult, SubsequentResultRecord, -} from './IncrementalPublisher.js'; +} from './types.js'; import { - buildIncrementalResponse, DeferredFragmentRecord, isReconcilableStreamItemsResult, -} from './IncrementalPublisher.js'; -import { mapAsyncIterable } from './mapAsyncIterable.js'; +} from './types.js'; import { getArgumentValues, getDirectiveValues, diff --git a/src/execution/index.ts b/src/execution/index.ts index 9d481ea6af..adf4b109f5 100644 --- a/src/execution/index.ts +++ b/src/execution/index.ts @@ -26,7 +26,7 @@ export type { FormattedIncrementalDeferResult, FormattedIncrementalStreamResult, FormattedIncrementalResult, -} from './IncrementalPublisher.js'; +} from './types.js'; export { getArgumentValues, diff --git a/src/execution/types.ts b/src/execution/types.ts new file mode 100644 index 0000000000..d2fd84827b --- /dev/null +++ b/src/execution/types.ts @@ -0,0 +1,302 @@ +import type { ObjMap } from '../jsutils/ObjMap.js'; +import type { Path } from '../jsutils/Path.js'; +import type { PromiseOrValue } from '../jsutils/PromiseOrValue.js'; + +import type { + GraphQLError, + GraphQLFormattedError, +} from '../error/GraphQLError.js'; + +/** + * The result of GraphQL execution. + * + * - `errors` is included when any errors occurred as a non-empty array. + * - `data` is the result of a successful execution of the query. + * - `hasNext` is true if a future payload is expected. + * - `extensions` is reserved for adding non-standard properties. + * - `incremental` is a list of the results from defer/stream directives. + */ +export interface ExecutionResult< + TData = ObjMap, + TExtensions = ObjMap, +> { + errors?: ReadonlyArray; + data?: TData | null; + extensions?: TExtensions; +} + +export interface FormattedExecutionResult< + TData = ObjMap, + TExtensions = ObjMap, +> { + errors?: ReadonlyArray; + data?: TData | null; + extensions?: TExtensions; +} + +export interface ExperimentalIncrementalExecutionResults< + TData = unknown, + TExtensions = ObjMap, +> { + initialResult: InitialIncrementalExecutionResult; + subsequentResults: AsyncGenerator< + SubsequentIncrementalExecutionResult, + void, + void + >; +} + +export interface InitialIncrementalExecutionResult< + TData = ObjMap, + TExtensions = ObjMap, +> extends ExecutionResult { + data: TData; + pending: ReadonlyArray; + hasNext: true; + extensions?: TExtensions; +} + +export interface FormattedInitialIncrementalExecutionResult< + TData = ObjMap, + TExtensions = ObjMap, +> extends FormattedExecutionResult { + data: TData; + pending: ReadonlyArray; + hasNext: boolean; + extensions?: TExtensions; +} + +export interface SubsequentIncrementalExecutionResult< + TData = unknown, + TExtensions = ObjMap, +> { + pending?: ReadonlyArray; + incremental?: ReadonlyArray>; + completed?: ReadonlyArray; + hasNext: boolean; + extensions?: TExtensions; +} + +export interface FormattedSubsequentIncrementalExecutionResult< + TData = unknown, + TExtensions = ObjMap, +> { + hasNext: boolean; + pending?: ReadonlyArray; + incremental?: ReadonlyArray>; + completed?: ReadonlyArray; + extensions?: TExtensions; +} + +interface BareDeferredGroupedFieldSetResult> { + errors?: ReadonlyArray; + data: TData; +} + +export interface IncrementalDeferResult< + TData = ObjMap, + TExtensions = ObjMap, +> extends BareDeferredGroupedFieldSetResult { + id: string; + subPath?: ReadonlyArray; + extensions?: TExtensions; +} + +export interface FormattedIncrementalDeferResult< + TData = ObjMap, + TExtensions = ObjMap, +> { + errors?: ReadonlyArray; + data: TData; + id: string; + subPath?: ReadonlyArray; + extensions?: TExtensions; +} + +interface BareStreamItemsResult> { + errors?: ReadonlyArray; + items: TData; +} + +export interface IncrementalStreamResult< + TData = ReadonlyArray, + TExtensions = ObjMap, +> extends BareStreamItemsResult { + id: string; + subPath?: ReadonlyArray; + extensions?: TExtensions; +} + +export interface FormattedIncrementalStreamResult< + TData = Array, + TExtensions = ObjMap, +> { + errors?: ReadonlyArray; + items: TData; + id: string; + subPath?: ReadonlyArray; + extensions?: TExtensions; +} + +export type IncrementalResult> = + | IncrementalDeferResult + | IncrementalStreamResult; + +export type FormattedIncrementalResult< + TData = unknown, + TExtensions = ObjMap, +> = + | FormattedIncrementalDeferResult + | FormattedIncrementalStreamResult; + +export interface PendingResult { + id: string; + path: ReadonlyArray; + label?: string; +} + +export interface CompletedResult { + id: string; + errors?: ReadonlyArray; +} + +export interface FormattedCompletedResult { + path: ReadonlyArray; + label?: string; + errors?: ReadonlyArray; +} + +export function isDeferredFragmentRecord( + subsequentResultRecord: SubsequentResultRecord, +): subsequentResultRecord is DeferredFragmentRecord { + return 'parent' in subsequentResultRecord; +} + +export function isDeferredGroupedFieldSetRecord( + incrementalDataRecord: IncrementalDataRecord, +): incrementalDataRecord is DeferredGroupedFieldSetRecord { + return 'deferredFragmentRecords' in incrementalDataRecord; +} + +export type DeferredGroupedFieldSetResult = + | ReconcilableDeferredGroupedFieldSetResult + | NonReconcilableDeferredGroupedFieldSetResult; + +export function isDeferredGroupedFieldSetResult( + subsequentResult: DeferredGroupedFieldSetResult | StreamItemsResult, +): subsequentResult is DeferredGroupedFieldSetResult { + return 'deferredFragmentRecords' in subsequentResult; +} + +interface ReconcilableDeferredGroupedFieldSetResult { + deferredFragmentRecords: ReadonlyArray; + path: Array; + result: BareDeferredGroupedFieldSetResult; + incrementalDataRecords: ReadonlyArray | undefined; + sent?: true | undefined; + errors?: never; +} + +interface NonReconcilableDeferredGroupedFieldSetResult { + errors: ReadonlyArray; + deferredFragmentRecords: ReadonlyArray; + path: Array; + result?: never; +} + +export function isNonReconcilableDeferredGroupedFieldSetResult( + deferredGroupedFieldSetResult: DeferredGroupedFieldSetResult, +): deferredGroupedFieldSetResult is NonReconcilableDeferredGroupedFieldSetResult { + return deferredGroupedFieldSetResult.errors !== undefined; +} + +export interface DeferredGroupedFieldSetRecord { + deferredFragmentRecords: ReadonlyArray; + result: PromiseOrValue; +} + +export interface SubsequentResultRecord { + path: Path | undefined; + label: string | undefined; + id?: string | undefined; +} + +/** @internal */ +export class DeferredFragmentRecord implements SubsequentResultRecord { + path: Path | undefined; + label: string | undefined; + id?: string | undefined; + parent: DeferredFragmentRecord | undefined; + expectedReconcilableResults: number; + results: Array; + reconcilableResults: Array; + children: Set; + + constructor(opts: { + path: Path | undefined; + label: string | undefined; + parent: DeferredFragmentRecord | undefined; + }) { + this.path = opts.path; + this.label = opts.label; + this.parent = opts.parent; + this.expectedReconcilableResults = 0; + this.results = []; + this.reconcilableResults = []; + this.children = new Set(); + } +} + +export interface CancellableStreamRecord extends SubsequentResultRecord { + earlyReturn: () => Promise; +} + +export function isCancellableStreamRecord( + subsequentResultRecord: SubsequentResultRecord, +): subsequentResultRecord is CancellableStreamRecord { + return 'earlyReturn' in subsequentResultRecord; +} + +interface ReconcilableStreamItemsResult { + streamRecord: SubsequentResultRecord; + result: BareStreamItemsResult; + incrementalDataRecords: ReadonlyArray | undefined; + errors?: never; +} + +export function isReconcilableStreamItemsResult( + streamItemsResult: StreamItemsResult, +): streamItemsResult is ReconcilableStreamItemsResult { + return streamItemsResult.result !== undefined; +} + +interface TerminatingStreamItemsResult { + streamRecord: SubsequentResultRecord; + result?: never; + incrementalDataRecords?: never; + errors?: never; +} + +interface NonReconcilableStreamItemsResult { + streamRecord: SubsequentResultRecord; + errors: ReadonlyArray; + result?: never; +} + +export type StreamItemsResult = + | ReconcilableStreamItemsResult + | TerminatingStreamItemsResult + | NonReconcilableStreamItemsResult; + +export interface StreamItemsRecord { + streamRecord: SubsequentResultRecord; + result: PromiseOrValue; +} + +export type IncrementalDataRecord = + | DeferredGroupedFieldSetRecord + | StreamItemsRecord; + +export type IncrementalDataRecordResult = + | DeferredGroupedFieldSetResult + | StreamItemsResult; diff --git a/src/graphql.ts b/src/graphql.ts index 0c8187ae0e..7596cf524f 100644 --- a/src/graphql.ts +++ b/src/graphql.ts @@ -15,7 +15,7 @@ import { validateSchema } from './type/validate.js'; import { validate } from './validation/validate.js'; import { execute } from './execution/execute.js'; -import type { ExecutionResult } from './execution/IncrementalPublisher.js'; +import type { ExecutionResult } from './execution/types.js'; /** * This is the primary entry point function for fulfilling GraphQL operations