diff --git a/src/execution/execute.ts b/src/execution/execute.ts index beef2fbb80..7c06624414 100644 --- a/src/execution/execute.ts +++ b/src/execution/execute.ts @@ -197,7 +197,10 @@ export interface StreamUsage { fieldDetailsList: FieldDetailsList; } -type GraphQLWrappedResult = [T, Array | undefined]; +interface GraphQLWrappedResult { + rawResult: T; + incrementalDataRecords: Array | undefined; +} const UNEXPECTED_EXPERIMENTAL_DIRECTIVES = 'The provided schema unexpectedly contains experimental directives (@defer or @stream). These directives may only be utilized if experimental execution features are explicitly enabled.'; @@ -360,18 +363,14 @@ export function experimentalExecuteQueryOrMutationOrSubscriptionEvent( if (isPromise(graphqlWrappedResult)) { return graphqlWrappedResult.then( - (resolved) => buildDataResponse(exeContext, resolved[0], resolved[1]), + (resolved) => buildDataResponse(exeContext, resolved), (error: unknown) => ({ data: null, errors: withError(exeContext.errors, error as GraphQLError), }), ); } - return buildDataResponse( - exeContext, - graphqlWrappedResult[0], - graphqlWrappedResult[1], - ); + return buildDataResponse(exeContext, graphqlWrappedResult); } catch (error) { return { data: null, errors: withError(exeContext.errors, error) }; } @@ -442,10 +441,10 @@ function addIncrementalDataRecords( if (incrementalDataRecords === undefined) { return; } - if (graphqlWrappedResult[1] === undefined) { - graphqlWrappedResult[1] = [...incrementalDataRecords]; + if (graphqlWrappedResult.incrementalDataRecords === undefined) { + graphqlWrappedResult.incrementalDataRecords = [...incrementalDataRecords]; } else { - graphqlWrappedResult[1].push(...incrementalDataRecords); + graphqlWrappedResult.incrementalDataRecords.push(...incrementalDataRecords); } } @@ -458,9 +457,9 @@ function withError( function buildDataResponse( exeContext: ExecutionContext, - data: ObjMap, - incrementalDataRecords: ReadonlyArray | undefined, + graphqlWrappedResult: GraphQLWrappedResult>, ): ExecutionResult | ExperimentalIncrementalExecutionResults { + const { rawResult: data, incrementalDataRecords } = graphqlWrappedResult; const errors = exeContext.errors; if (incrementalDataRecords === undefined) { return errors !== undefined ? { errors, data } : { data }; @@ -675,7 +674,7 @@ function executeFieldsSerially( fieldPath, incrementalContext, ); - graphqlWrappedResult[0][responseName] = null; + graphqlWrappedResult.rawResult[responseName] = null; return graphqlWrappedResult; } @@ -693,16 +692,25 @@ function executeFieldsSerially( } if (isPromise(result)) { return result.then((resolved) => { - graphqlWrappedResult[0][responseName] = resolved[0]; - addIncrementalDataRecords(graphqlWrappedResult, resolved[1]); + graphqlWrappedResult.rawResult[responseName] = resolved.rawResult; + addIncrementalDataRecords( + graphqlWrappedResult, + resolved.incrementalDataRecords, + ); return graphqlWrappedResult; }); } - graphqlWrappedResult[0][responseName] = result[0]; - addIncrementalDataRecords(graphqlWrappedResult, result[1]); + graphqlWrappedResult.rawResult[responseName] = result.rawResult; + addIncrementalDataRecords( + graphqlWrappedResult, + result.incrementalDataRecords, + ); return graphqlWrappedResult; }, - [Object.create(null), undefined] as GraphQLWrappedResult>, + { + rawResult: Object.create(null), + incrementalDataRecords: undefined, + }, ); } @@ -720,10 +728,10 @@ function executeFields( deferMap: ReadonlyMap | undefined, ): PromiseOrValue>> { const results = Object.create(null); - const graphqlWrappedResult: GraphQLWrappedResult> = [ - results, - undefined, - ]; + const graphqlWrappedResult: GraphQLWrappedResult> = { + rawResult: results, + incrementalDataRecords: undefined, + }; let containsPromise = false; try { @@ -742,13 +750,19 @@ function executeFields( if (result !== undefined) { if (isPromise(result)) { results[responseName] = result.then((resolved) => { - addIncrementalDataRecords(graphqlWrappedResult, resolved[1]); - return resolved[0]; + addIncrementalDataRecords( + graphqlWrappedResult, + resolved.incrementalDataRecords, + ); + return resolved.rawResult; }); containsPromise = true; } else { - results[responseName] = result[0]; - addIncrementalDataRecords(graphqlWrappedResult, result[1]); + results[responseName] = result.rawResult; + addIncrementalDataRecords( + graphqlWrappedResult, + result.incrementalDataRecords, + ); } } } @@ -772,10 +786,10 @@ function executeFields( // Otherwise, results is a map from field name to the result of resolving that // field, which is possibly a promise. Return a promise that will return this // same map, but with any promises replaced with the values they resolved to. - return promiseForObject(results, (resolved) => [ - resolved, - graphqlWrappedResult[1], - ]); + return promiseForObject(results, (resolved) => ({ + rawResult: resolved, + incrementalDataRecords: graphqlWrappedResult.incrementalDataRecords, + })); } function toNodes(fieldDetailsList: FieldDetailsList): ReadonlyArray { @@ -871,7 +885,7 @@ function executeField( path, incrementalContext, ); - return [null, undefined]; + return { rawResult: null, incrementalDataRecords: undefined }; }); } return completed; @@ -884,7 +898,7 @@ function executeField( path, incrementalContext, ); - return [null, undefined]; + return { rawResult: null, incrementalDataRecords: undefined }; } } @@ -997,7 +1011,7 @@ function completeValue( incrementalContext, deferMap, ); - if ((completed as GraphQLWrappedResult)[0] === null) { + if ((completed as GraphQLWrappedResult).rawResult === null) { throw new Error( `Cannot return null for non-nullable field ${info.parentType}.${info.fieldName}.`, ); @@ -1007,7 +1021,7 @@ function completeValue( // If result value is null or undefined then return null. if (result == null) { - return [null, undefined]; + return { rawResult: null, incrementalDataRecords: undefined }; } // If field type is List, complete each item in the list with the inner type @@ -1027,7 +1041,10 @@ function completeValue( // If field type is a leaf type, Scalar or Enum, coerce to a valid value, // returning null if coercion is not possible. if (isLeafType(returnType)) { - return [completeLeafValue(returnType, result), undefined]; + return { + rawResult: completeLeafValue(returnType, result), + incrementalDataRecords: undefined, + }; } // If field type is an abstract type, Interface or Union, determine the @@ -1103,7 +1120,7 @@ async function completePromisedValue( path, incrementalContext, ); - return [null, undefined]; + return { rawResult: null, incrementalDataRecords: undefined }; } } @@ -1201,10 +1218,10 @@ async function completeAsyncIteratorValue( ): Promise>> { let containsPromise = false; const completedResults: Array = []; - const graphqlWrappedResult: GraphQLWrappedResult> = [ - completedResults, - undefined, - ]; + const graphqlWrappedResult: GraphQLWrappedResult> = { + rawResult: completedResults, + incrementalDataRecords: undefined, + }; let index = 0; const streamUsage = getStreamUsage( exeContext.validatedExecutionArgs, @@ -1323,10 +1340,10 @@ async function completeAsyncIteratorValue( } return containsPromise - ? /* c8 ignore start */ Promise.all(completedResults).then((resolved) => [ - resolved, - graphqlWrappedResult[1], - ]) + ? /* c8 ignore start */ Promise.all(completedResults).then((resolved) => ({ + rawResult: resolved, + incrementalDataRecords: graphqlWrappedResult.incrementalDataRecords, + })) : /* c8 ignore stop */ graphqlWrappedResult; } @@ -1393,10 +1410,10 @@ function completeIterableValue( // where the list contains no Promises by avoiding creating another Promise. let containsPromise = false; const completedResults: Array = []; - const graphqlWrappedResult: GraphQLWrappedResult> = [ - completedResults, - undefined, - ]; + const graphqlWrappedResult: GraphQLWrappedResult> = { + rawResult: completedResults, + incrementalDataRecords: undefined, + }; let index = 0; const streamUsage = getStreamUsage( exeContext.validatedExecutionArgs, @@ -1469,10 +1486,10 @@ function completeIterableValue( } return containsPromise - ? Promise.all(completedResults).then((resolved) => [ - resolved, - graphqlWrappedResult[1], - ]) + ? Promise.all(completedResults).then((resolved) => ({ + rawResult: resolved, + incrementalDataRecords: graphqlWrappedResult.incrementalDataRecords, + })) : graphqlWrappedResult; } @@ -1511,8 +1528,8 @@ function completeListItemValue( completedResults.push( completedItem.then( (resolved) => { - addIncrementalDataRecords(parent, resolved[1]); - return resolved[0]; + addIncrementalDataRecords(parent, resolved.incrementalDataRecords); + return resolved.rawResult; }, (rawError: unknown) => { handleFieldError( @@ -1530,8 +1547,8 @@ function completeListItemValue( return true; } - completedResults.push(completedItem[0]); - addIncrementalDataRecords(parent, completedItem[1]); + completedResults.push(completedItem.rawResult); + addIncrementalDataRecords(parent, completedItem.incrementalDataRecords); } catch (rawError) { handleFieldError( rawError, @@ -1572,8 +1589,8 @@ async function completePromisedListItemValue( if (isPromise(completed)) { completed = await completed; } - addIncrementalDataRecords(parent, completed[1]); - return completed[0]; + addIncrementalDataRecords(parent, completed.incrementalDataRecords); + return completed.rawResult; } catch (rawError) { handleFieldError( rawError, @@ -2343,12 +2360,12 @@ function buildCompletedExecutionGroup( path: Path | undefined, result: GraphQLWrappedResult>, ): CompletedExecutionGroup { + const { rawResult: data, incrementalDataRecords } = result; return { pendingExecutionGroup, path: pathToArray(path), - result: - errors === undefined ? { data: result[0] } : { data: result[0], errors }, - incrementalDataRecords: result[1], + result: errors === undefined ? { data } : { data, errors }, + incrementalDataRecords, }; } @@ -2583,7 +2600,7 @@ function completeStreamItem( itemPath, incrementalContext, ); - result = [null, undefined]; + result = { rawResult: null, incrementalDataRecords: undefined }; } } catch (error) { return { @@ -2602,7 +2619,7 @@ function completeStreamItem( itemPath, incrementalContext, ); - return [null, undefined] as GraphQLWrappedResult; + return { rawResult: null, incrementalDataRecords: undefined }; }) .then( (resolvedItem) => @@ -2620,9 +2637,10 @@ function buildStreamItemResult( errors: ReadonlyArray | undefined, result: GraphQLWrappedResult, ): StreamItemResult { + const { rawResult: item, incrementalDataRecords } = result; return { - item: result[0], + item, errors, - incrementalDataRecords: result[1], + incrementalDataRecords, }; }