Skip to content

Commit e8167d8

Browse files
committed
Fix GraphQLResolveInfo for graphql@v17
1 parent 0abffb1 commit e8167d8

2 files changed

Lines changed: 44 additions & 5 deletions

File tree

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import { isPromise } from '@graphql-tools/utils';
2+
3+
export class AsyncWorkTracker {
4+
private pendingAsyncWork = new Set<Promise<void>>();
5+
6+
add(promiseLike: PromiseLike<unknown>): void {
7+
const pendingAsyncWork = this.pendingAsyncWork;
8+
const promiseToSettle = Promise.resolve(promiseLike).then(
9+
() => {
10+
pendingAsyncWork.delete(promiseToSettle);
11+
},
12+
() => {
13+
pendingAsyncWork.delete(promiseToSettle);
14+
},
15+
);
16+
pendingAsyncWork.add(promiseToSettle);
17+
}
18+
19+
addValues(values: ReadonlyArray<unknown>): void {
20+
for (const value of values) {
21+
if (isPromise(value)) {
22+
this.add(value);
23+
}
24+
}
25+
}
26+
27+
promiseAllTrackOnReject<T>(values: ReadonlyArray<PromiseLike<T> | T>): Promise<Array<T>> {
28+
const promise = Promise.all(values);
29+
promise.then(undefined, () => {
30+
this.addValues(values);
31+
});
32+
return promise;
33+
}
34+
}

packages/executor/src/execution/execute.ts

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ import {
5555
import { TypedDocumentNode } from '@graphql-typed-document-node/core';
5656
import { DisposableSymbols } from '@whatwg-node/disposablestack';
5757
import { createDeferredPromise, handleMaybePromise } from '@whatwg-node/promise-helpers';
58+
import { AsyncWorkTracker } from './AsyncWorkTracker.js';
5859
import { coerceError } from './coerceError.js';
5960
import { flattenAsyncIterable } from './flattenAsyncIterable.js';
6061
import { invariant } from './invariant.js';
@@ -124,6 +125,7 @@ export interface ExecutionContext<TVariables = any, TContext = any> {
124125
subscribeFieldResolver: GraphQLFieldResolver<any, TContext>;
125126
errors: Array<GraphQLError>;
126127
subsequentPayloads: Set<AsyncPayloadRecord>;
128+
asyncWorkTracker: AsyncWorkTracker;
127129
signal?: AbortSignal;
128130
onSignalAbort?(handler: () => void): void;
129131
signalPromise?: Promise<never>;
@@ -530,6 +532,7 @@ export function buildExecutionContext<TData = any, TVariables = any, TContext =
530532
typeResolver: typeResolver ?? defaultTypeResolver,
531533
subscribeFieldResolver: subscribeFieldResolver ?? defaultFieldResolver,
532534
subsequentPayloads: new Set(),
535+
asyncWorkTracker: new AsyncWorkTracker(),
533536
errors: [],
534537
signal,
535538
onSignalAbort,
@@ -546,6 +549,7 @@ function buildPerEventExecutionContext(
546549
...exeContext,
547550
rootValue: payload,
548551
subsequentPayloads: new Set(),
552+
asyncWorkTracker: new AsyncWorkTracker(),
549553
errors: [],
550554
};
551555
}
@@ -805,10 +809,6 @@ function executeField(
805809

806810
/**
807811
* TODO: consider no longer exporting this function
808-
*
809-
* FIXME: `any` is used as ReturnType to silence graphql@17
810-
* but it's missing `getAbortSignal` and `getAsyncHelpers`
811-
*
812812
* @internal
813813
*/
814814
export function buildResolveInfo(
@@ -817,7 +817,7 @@ export function buildResolveInfo(
817817
fieldNodes: Array<FieldNode>,
818818
parentType: GraphQLObjectType,
819819
path: Path,
820-
): any {
820+
): GraphQLResolveInfo {
821821
// The resolve function's optional fourth argument is a collection of
822822
// information about the current execution state.
823823
return {
@@ -832,6 +832,11 @@ export function buildResolveInfo(
832832
operation: exeContext.operation,
833833
variableValues: exeContext.variableValues,
834834
signal: exeContext.signal,
835+
getAbortSignal: () => exeContext.signal,
836+
getAsyncHelpers: () => ({
837+
promiseAll: values => exeContext.asyncWorkTracker.promiseAllTrackOnReject(values),
838+
track: maybePromises => exeContext.asyncWorkTracker.addValues(maybePromises),
839+
}),
835840
};
836841
}
837842

0 commit comments

Comments
 (0)