@@ -1052,10 +1052,97 @@ async function completeAsyncIteratorValue(
10521052 [ ] ,
10531053 ] ;
10541054 let index = 0 ;
1055- const streamUsage = getStreamUsage ( exeContext , fieldGroup , path ) ;
10561055 // eslint-disable-next-line no-constant-condition
10571056 while ( true ) {
1058- if ( streamUsage && index >= streamUsage . initialCount ) {
1057+ const itemPath = addPath ( path , index , undefined ) ;
1058+ let iteration ;
1059+ try {
1060+ // eslint-disable-next-line no-await-in-loop
1061+ iteration = await asyncIterator . next ( ) ;
1062+ } catch ( rawError ) {
1063+ throw locatedError ( rawError , toNodes ( fieldGroup ) , pathToArray ( path ) ) ;
1064+ }
1065+
1066+ // TODO: add test case for stream returning done before initialCount
1067+ /* c8 ignore next 3 */
1068+ if ( iteration . done ) {
1069+ break ;
1070+ }
1071+
1072+ const item = iteration . value ;
1073+ // TODO: add tests for stream backed by asyncIterator that returns a promise
1074+ /* c8 ignore start */
1075+ if ( isPromise ( item ) ) {
1076+ completedResults . push (
1077+ completePromisedListItemValue (
1078+ item ,
1079+ graphqlWrappedResult ,
1080+ exeContext ,
1081+ itemType ,
1082+ fieldGroup ,
1083+ info ,
1084+ itemPath ,
1085+ incrementalContext ,
1086+ deferMap ,
1087+ ) ,
1088+ ) ;
1089+ containsPromise = true ;
1090+ } else if (
1091+ /* c8 ignore stop */
1092+ completeListItemValue (
1093+ item ,
1094+ completedResults ,
1095+ graphqlWrappedResult ,
1096+ exeContext ,
1097+ itemType ,
1098+ fieldGroup ,
1099+ info ,
1100+ itemPath ,
1101+ incrementalContext ,
1102+ deferMap ,
1103+ )
1104+ // TODO: add tests for stream backed by asyncIterator that completes to a promise
1105+ /* c8 ignore start */
1106+ ) {
1107+ containsPromise = true ;
1108+ }
1109+ /* c8 ignore stop */
1110+ index ++ ;
1111+ }
1112+
1113+ return containsPromise
1114+ ? /* c8 ignore start */ Promise . all ( completedResults ) . then ( ( resolved ) => [
1115+ resolved ,
1116+ graphqlWrappedResult [ 1 ] ,
1117+ ] )
1118+ : /* c8 ignore stop */ graphqlWrappedResult ;
1119+ }
1120+
1121+ /**
1122+ * Complete a async iterator value by completing the result and calling
1123+ * recursively until all the results are completed.
1124+ */
1125+ async function completeAsyncIteratorValueWithPossibleStream (
1126+ exeContext : ExecutionContext ,
1127+ itemType : GraphQLOutputType ,
1128+ fieldGroup : FieldGroup ,
1129+ info : GraphQLResolveInfo ,
1130+ path : Path ,
1131+ asyncIterator : AsyncIterator < unknown > ,
1132+ streamUsage : StreamUsage ,
1133+ incrementalContext : IncrementalContext | undefined ,
1134+ deferMap : ReadonlyMap < DeferUsage , DeferredFragmentRecord > | undefined ,
1135+ ) : Promise < GraphQLWrappedResult < ReadonlyArray < unknown > > > {
1136+ let containsPromise = false ;
1137+ const completedResults : Array < unknown > = [ ] ;
1138+ const graphqlWrappedResult : GraphQLWrappedResult < Array < unknown > > = [
1139+ completedResults ,
1140+ [ ] ,
1141+ ] ;
1142+ let index = 0 ;
1143+ // eslint-disable-next-line no-constant-condition
1144+ while ( true ) {
1145+ if ( index >= streamUsage . initialCount ) {
10591146 const returnFn = asyncIterator . return ;
10601147 let streamRecord : SubsequentResultRecord | CancellableStreamRecord ;
10611148 if ( returnFn === undefined ) {
@@ -1166,17 +1253,32 @@ function completeListValue(
11661253 deferMap : ReadonlyMap < DeferUsage , DeferredFragmentRecord > | undefined ,
11671254) : PromiseOrValue < GraphQLWrappedResult < ReadonlyArray < unknown > > > {
11681255 const itemType = returnType . ofType ;
1256+ const streamUsage = getStreamUsage ( exeContext , fieldGroup , path ) ;
11691257
11701258 if ( isAsyncIterable ( result ) ) {
11711259 const asyncIterator = result [ Symbol . asyncIterator ] ( ) ;
11721260
1173- return completeAsyncIteratorValue (
1261+ if ( streamUsage === undefined ) {
1262+ return completeAsyncIteratorValue (
1263+ exeContext ,
1264+ itemType ,
1265+ fieldGroup ,
1266+ info ,
1267+ path ,
1268+ asyncIterator ,
1269+ incrementalContext ,
1270+ deferMap ,
1271+ ) ;
1272+ }
1273+
1274+ return completeAsyncIteratorValueWithPossibleStream (
11741275 exeContext ,
11751276 itemType ,
11761277 fieldGroup ,
11771278 info ,
11781279 path ,
11791280 asyncIterator ,
1281+ streamUsage ,
11801282 incrementalContext ,
11811283 deferMap ,
11821284 ) ;
@@ -1188,13 +1290,27 @@ function completeListValue(
11881290 ) ;
11891291 }
11901292
1191- return completeIterableValue (
1293+ if ( streamUsage === undefined ) {
1294+ return completeIterableValue (
1295+ exeContext ,
1296+ itemType ,
1297+ fieldGroup ,
1298+ info ,
1299+ path ,
1300+ result ,
1301+ incrementalContext ,
1302+ deferMap ,
1303+ ) ;
1304+ }
1305+
1306+ return completeIterableValueWithPossibleStream (
11921307 exeContext ,
11931308 itemType ,
11941309 fieldGroup ,
11951310 info ,
11961311 path ,
11971312 result ,
1313+ streamUsage ,
11981314 incrementalContext ,
11991315 deferMap ,
12001316 ) ;
@@ -1219,13 +1335,79 @@ function completeIterableValue(
12191335 [ ] ,
12201336 ] ;
12211337 let index = 0 ;
1222- const streamUsage = getStreamUsage ( exeContext , fieldGroup , path ) ;
1338+ for ( const item of items ) {
1339+ // No need to modify the info object containing the path,
1340+ // since from here on it is not ever accessed by resolver functions.
1341+ const itemPath = addPath ( path , index , undefined ) ;
1342+
1343+ if ( isPromise ( item ) ) {
1344+ completedResults . push (
1345+ completePromisedListItemValue (
1346+ item ,
1347+ graphqlWrappedResult ,
1348+ exeContext ,
1349+ itemType ,
1350+ fieldGroup ,
1351+ info ,
1352+ itemPath ,
1353+ incrementalContext ,
1354+ deferMap ,
1355+ ) ,
1356+ ) ;
1357+ containsPromise = true ;
1358+ } else if (
1359+ completeListItemValue (
1360+ item ,
1361+ completedResults ,
1362+ graphqlWrappedResult ,
1363+ exeContext ,
1364+ itemType ,
1365+ fieldGroup ,
1366+ info ,
1367+ itemPath ,
1368+ incrementalContext ,
1369+ deferMap ,
1370+ )
1371+ ) {
1372+ containsPromise = true ;
1373+ }
1374+ index ++ ;
1375+ }
1376+
1377+ return containsPromise
1378+ ? Promise . all ( completedResults ) . then ( ( resolved ) => [
1379+ resolved ,
1380+ graphqlWrappedResult [ 1 ] ,
1381+ ] )
1382+ : graphqlWrappedResult ;
1383+ }
1384+
1385+ function completeIterableValueWithPossibleStream (
1386+ exeContext : ExecutionContext ,
1387+ itemType : GraphQLOutputType ,
1388+ fieldGroup : FieldGroup ,
1389+ info : GraphQLResolveInfo ,
1390+ path : Path ,
1391+ items : Iterable < unknown > ,
1392+ streamUsage : StreamUsage ,
1393+ incrementalContext : IncrementalContext | undefined ,
1394+ deferMap : ReadonlyMap < DeferUsage , DeferredFragmentRecord > | undefined ,
1395+ ) : PromiseOrValue < GraphQLWrappedResult < ReadonlyArray < unknown > > > {
1396+ // This is specified as a simple map, however we're optimizing the path
1397+ // where the list contains no Promises by avoiding creating another Promise.
1398+ let containsPromise = false ;
1399+ const completedResults : Array < unknown > = [ ] ;
1400+ const graphqlWrappedResult : GraphQLWrappedResult < Array < unknown > > = [
1401+ completedResults ,
1402+ [ ] ,
1403+ ] ;
1404+ let index = 0 ;
12231405 const iterator = items [ Symbol . iterator ] ( ) ;
12241406 let iteration = iterator . next ( ) ;
12251407 while ( ! iteration . done ) {
12261408 const item = iteration . value ;
12271409
1228- if ( streamUsage && index >= streamUsage . initialCount ) {
1410+ if ( index >= streamUsage . initialCount ) {
12291411 const streamRecord : SubsequentResultRecord = {
12301412 label : streamUsage . label ,
12311413 path,
0 commit comments