@@ -1091,10 +1091,97 @@ async function completeAsyncIteratorValue(
10911091 undefined ,
10921092 ] ;
10931093 let index = 0 ;
1094- const streamUsage = getStreamUsage ( exeContext , fieldGroup , path ) ;
10951094 // eslint-disable-next-line no-constant-condition
10961095 while ( true ) {
1097- if ( streamUsage && index >= streamUsage . initialCount ) {
1096+ const itemPath = addPath ( path , index , undefined ) ;
1097+ let iteration ;
1098+ try {
1099+ // eslint-disable-next-line no-await-in-loop
1100+ iteration = await asyncIterator . next ( ) ;
1101+ } catch ( rawError ) {
1102+ throw locatedError ( rawError , toNodes ( fieldGroup ) , pathToArray ( path ) ) ;
1103+ }
1104+
1105+ // TODO: add test case for stream returning done before initialCount
1106+ /* c8 ignore next 3 */
1107+ if ( iteration . done ) {
1108+ break ;
1109+ }
1110+
1111+ const item = iteration . value ;
1112+ // TODO: add tests for stream backed by asyncIterator that returns a promise
1113+ /* c8 ignore start */
1114+ if ( isPromise ( item ) ) {
1115+ completedResults . push (
1116+ completePromisedListItemValue (
1117+ item ,
1118+ graphqlWrappedResult ,
1119+ exeContext ,
1120+ itemType ,
1121+ fieldGroup ,
1122+ info ,
1123+ itemPath ,
1124+ incrementalContext ,
1125+ deferMap ,
1126+ ) ,
1127+ ) ;
1128+ containsPromise = true ;
1129+ } else if (
1130+ /* c8 ignore stop */
1131+ completeListItemValue (
1132+ item ,
1133+ completedResults ,
1134+ graphqlWrappedResult ,
1135+ exeContext ,
1136+ itemType ,
1137+ fieldGroup ,
1138+ info ,
1139+ itemPath ,
1140+ incrementalContext ,
1141+ deferMap ,
1142+ )
1143+ // TODO: add tests for stream backed by asyncIterator that completes to a promise
1144+ /* c8 ignore start */
1145+ ) {
1146+ containsPromise = true ;
1147+ }
1148+ /* c8 ignore stop */
1149+ index ++ ;
1150+ }
1151+
1152+ return containsPromise
1153+ ? /* c8 ignore start */ Promise . all ( completedResults ) . then ( ( resolved ) => [
1154+ resolved ,
1155+ graphqlWrappedResult [ 1 ] ,
1156+ ] )
1157+ : /* c8 ignore stop */ graphqlWrappedResult ;
1158+ }
1159+
1160+ /**
1161+ * Complete a async iterator value by completing the result and calling
1162+ * recursively until all the results are completed.
1163+ */
1164+ async function completeAsyncIteratorValueWithPossibleStream (
1165+ exeContext : ExecutionContext ,
1166+ itemType : GraphQLOutputType ,
1167+ fieldGroup : FieldGroup ,
1168+ info : GraphQLResolveInfo ,
1169+ path : Path ,
1170+ asyncIterator : AsyncIterator < unknown > ,
1171+ streamUsage : StreamUsage ,
1172+ incrementalContext : IncrementalContext | undefined ,
1173+ deferMap : ReadonlyMap < DeferUsage , DeferredFragmentRecord > | undefined ,
1174+ ) : Promise < GraphQLWrappedResult < ReadonlyArray < unknown > > > {
1175+ let containsPromise = false ;
1176+ const completedResults : Array < unknown > = [ ] ;
1177+ const graphqlWrappedResult : GraphQLWrappedResult < Array < unknown > > = [
1178+ completedResults ,
1179+ [ ] ,
1180+ ] ;
1181+ let index = 0 ;
1182+ // eslint-disable-next-line no-constant-condition
1183+ while ( true ) {
1184+ if ( index >= streamUsage . initialCount ) {
10981185 const returnFn = asyncIterator . return ;
10991186 let streamRecord : SubsequentResultRecord | CancellableStreamRecord ;
11001187 if ( returnFn === undefined ) {
@@ -1208,17 +1295,32 @@ function completeListValue(
12081295 deferMap : ReadonlyMap < DeferUsage , DeferredFragmentRecord > | undefined ,
12091296) : PromiseOrValue < GraphQLWrappedResult < ReadonlyArray < unknown > > > {
12101297 const itemType = returnType . ofType ;
1298+ const streamUsage = getStreamUsage ( exeContext , fieldGroup , path ) ;
12111299
12121300 if ( isAsyncIterable ( result ) ) {
12131301 const asyncIterator = result [ Symbol . asyncIterator ] ( ) ;
12141302
1215- return completeAsyncIteratorValue (
1303+ if ( streamUsage === undefined ) {
1304+ return completeAsyncIteratorValue (
1305+ exeContext ,
1306+ itemType ,
1307+ fieldGroup ,
1308+ info ,
1309+ path ,
1310+ asyncIterator ,
1311+ incrementalContext ,
1312+ deferMap ,
1313+ ) ;
1314+ }
1315+
1316+ return completeAsyncIteratorValueWithPossibleStream (
12161317 exeContext ,
12171318 itemType ,
12181319 fieldGroup ,
12191320 info ,
12201321 path ,
12211322 asyncIterator ,
1323+ streamUsage ,
12221324 incrementalContext ,
12231325 deferMap ,
12241326 ) ;
@@ -1230,13 +1332,27 @@ function completeListValue(
12301332 ) ;
12311333 }
12321334
1233- return completeIterableValue (
1335+ if ( streamUsage === undefined ) {
1336+ return completeIterableValue (
1337+ exeContext ,
1338+ itemType ,
1339+ fieldGroup ,
1340+ info ,
1341+ path ,
1342+ result ,
1343+ incrementalContext ,
1344+ deferMap ,
1345+ ) ;
1346+ }
1347+
1348+ return completeIterableValueWithPossibleStream (
12341349 exeContext ,
12351350 itemType ,
12361351 fieldGroup ,
12371352 info ,
12381353 path ,
12391354 result ,
1355+ streamUsage ,
12401356 incrementalContext ,
12411357 deferMap ,
12421358 ) ;
@@ -1261,13 +1377,79 @@ function completeIterableValue(
12611377 undefined ,
12621378 ] ;
12631379 let index = 0 ;
1264- const streamUsage = getStreamUsage ( exeContext , fieldGroup , path ) ;
1380+ for ( const item of items ) {
1381+ // No need to modify the info object containing the path,
1382+ // since from here on it is not ever accessed by resolver functions.
1383+ const itemPath = addPath ( path , index , undefined ) ;
1384+
1385+ if ( isPromise ( item ) ) {
1386+ completedResults . push (
1387+ completePromisedListItemValue (
1388+ item ,
1389+ graphqlWrappedResult ,
1390+ exeContext ,
1391+ itemType ,
1392+ fieldGroup ,
1393+ info ,
1394+ itemPath ,
1395+ incrementalContext ,
1396+ deferMap ,
1397+ ) ,
1398+ ) ;
1399+ containsPromise = true ;
1400+ } else if (
1401+ completeListItemValue (
1402+ item ,
1403+ completedResults ,
1404+ graphqlWrappedResult ,
1405+ exeContext ,
1406+ itemType ,
1407+ fieldGroup ,
1408+ info ,
1409+ itemPath ,
1410+ incrementalContext ,
1411+ deferMap ,
1412+ )
1413+ ) {
1414+ containsPromise = true ;
1415+ }
1416+ index ++ ;
1417+ }
1418+
1419+ return containsPromise
1420+ ? Promise . all ( completedResults ) . then ( ( resolved ) => [
1421+ resolved ,
1422+ graphqlWrappedResult [ 1 ] ,
1423+ ] )
1424+ : graphqlWrappedResult ;
1425+ }
1426+
1427+ function completeIterableValueWithPossibleStream (
1428+ exeContext : ExecutionContext ,
1429+ itemType : GraphQLOutputType ,
1430+ fieldGroup : FieldGroup ,
1431+ info : GraphQLResolveInfo ,
1432+ path : Path ,
1433+ items : Iterable < unknown > ,
1434+ streamUsage : StreamUsage ,
1435+ incrementalContext : IncrementalContext | undefined ,
1436+ deferMap : ReadonlyMap < DeferUsage , DeferredFragmentRecord > | undefined ,
1437+ ) : PromiseOrValue < GraphQLWrappedResult < ReadonlyArray < unknown > > > {
1438+ // This is specified as a simple map, however we're optimizing the path
1439+ // where the list contains no Promises by avoiding creating another Promise.
1440+ let containsPromise = false ;
1441+ const completedResults : Array < unknown > = [ ] ;
1442+ const graphqlWrappedResult : GraphQLWrappedResult < Array < unknown > > = [
1443+ completedResults ,
1444+ [ ] ,
1445+ ] ;
1446+ let index = 0 ;
12651447 const iterator = items [ Symbol . iterator ] ( ) ;
12661448 let iteration = iterator . next ( ) ;
12671449 while ( ! iteration . done ) {
12681450 const item = iteration . value ;
12691451
1270- if ( streamUsage && index >= streamUsage . initialCount ) {
1452+ if ( index >= streamUsage . initialCount ) {
12711453 const streamRecord : SubsequentResultRecord = {
12721454 label : streamUsage . label ,
12731455 path,
0 commit comments