@@ -1088,13 +1088,68 @@ async function completeAsyncIteratorValue(
1088
1088
incrementalContext : IncrementalContext | undefined ,
1089
1089
deferMap : ReadonlyMap < DeferUsage , DeferredFragmentRecord > | undefined ,
1090
1090
) : Promise < ReadonlyArray < unknown > > {
1091
- const streamUsage = getStreamUsage ( exeContext , fieldGroup , path ) ;
1092
1091
let containsPromise = false ;
1093
1092
const completedResults : Array < unknown > = [ ] ;
1094
1093
let index = 0 ;
1095
1094
// eslint-disable-next-line no-constant-condition
1096
1095
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
+ if ( iteration . done ) {
1106
+ break ;
1107
+ }
1108
+
1109
+ if (
1110
+ completeListItemValue (
1111
+ iteration . value ,
1112
+ completedResults ,
1113
+ exeContext ,
1114
+ itemType ,
1115
+ fieldGroup ,
1116
+ info ,
1117
+ itemPath ,
1118
+ incrementalContext ,
1119
+ deferMap ,
1120
+ )
1121
+ ) {
1122
+ containsPromise = true ;
1123
+ }
1124
+
1125
+ index ++ ;
1126
+ }
1127
+
1128
+ return containsPromise ? Promise . all ( completedResults ) : completedResults ;
1129
+ }
1130
+
1131
+ /**
1132
+ * Complete a async iterator value by completing the result and calling
1133
+ * recursively until all the results are completed.
1134
+ */
1135
+ async function completeAsyncIteratorValueWithPossibleStream (
1136
+ exeContext : ExecutionContext ,
1137
+ itemType : GraphQLOutputType ,
1138
+ fieldGroup : FieldGroup ,
1139
+ info : GraphQLResolveInfo ,
1140
+ path : Path ,
1141
+ asyncIterator : AsyncIterator < unknown > ,
1142
+ streamUsage : StreamUsage ,
1143
+ incrementalContext : IncrementalContext | undefined ,
1144
+ deferMap : ReadonlyMap < DeferUsage , DeferredFragmentRecord > | undefined ,
1145
+ ) : Promise < ReadonlyArray < unknown > > {
1146
+ let containsPromise = false ;
1147
+ const completedResults : Array < unknown > = [ ] ;
1148
+ let index = 0 ;
1149
+ const initialCount = streamUsage . initialCount ;
1150
+ // eslint-disable-next-line no-constant-condition
1151
+ while ( true ) {
1152
+ if ( index >= initialCount ) {
1098
1153
const streamRecord = new StreamRecord ( {
1099
1154
label : streamUsage . label ,
1100
1155
path,
@@ -1135,13 +1190,16 @@ async function completeAsyncIteratorValue(
1135
1190
try {
1136
1191
// eslint-disable-next-line no-await-in-loop
1137
1192
iteration = await asyncIterator . next ( ) ;
1138
- if ( iteration . done ) {
1139
- break ;
1140
- }
1141
1193
} catch ( rawError ) {
1142
1194
throw locatedError ( rawError , toNodes ( fieldGroup ) , pathToArray ( path ) ) ;
1143
1195
}
1144
1196
1197
+ // TODO: add test case for stream returning done before initialCount
1198
+ /* c8 ignore next 3 */
1199
+ if ( iteration . done ) {
1200
+ break ;
1201
+ }
1202
+
1145
1203
if (
1146
1204
completeListItemValue (
1147
1205
iteration . value ,
@@ -1153,14 +1211,17 @@ async function completeAsyncIteratorValue(
1153
1211
itemPath ,
1154
1212
incrementalContext ,
1155
1213
deferMap ,
1156
- )
1214
+ ) /* c8 ignore start */
1157
1215
) {
1216
+ // TODO: add test case for asyncIterator that yields promises
1158
1217
containsPromise = true ;
1159
- }
1160
- index += 1 ;
1218
+ } /* c8 ignore stop */
1219
+ index ++ ;
1161
1220
}
1162
1221
1163
- return containsPromise ? Promise . all ( completedResults ) : completedResults ;
1222
+ return containsPromise
1223
+ ? /* c8 ignore start */ Promise . all ( completedResults )
1224
+ : /* c8 ignore stop */ completedResults ;
1164
1225
}
1165
1226
1166
1227
function addIncrementalDataRecord (
@@ -1190,17 +1251,32 @@ function completeListValue(
1190
1251
deferMap : ReadonlyMap < DeferUsage , DeferredFragmentRecord > | undefined ,
1191
1252
) : PromiseOrValue < ReadonlyArray < unknown > > {
1192
1253
const itemType = returnType . ofType ;
1254
+ const streamUsage = getStreamUsage ( exeContext , fieldGroup , path ) ;
1193
1255
1194
1256
if ( isAsyncIterable ( result ) ) {
1195
1257
const asyncIterator = result [ Symbol . asyncIterator ] ( ) ;
1196
1258
1197
- return completeAsyncIteratorValue (
1259
+ if ( streamUsage === undefined ) {
1260
+ return completeAsyncIteratorValue (
1261
+ exeContext ,
1262
+ itemType ,
1263
+ fieldGroup ,
1264
+ info ,
1265
+ path ,
1266
+ asyncIterator ,
1267
+ incrementalContext ,
1268
+ deferMap ,
1269
+ ) ;
1270
+ }
1271
+
1272
+ return completeAsyncIteratorValueWithPossibleStream (
1198
1273
exeContext ,
1199
1274
itemType ,
1200
1275
fieldGroup ,
1201
1276
info ,
1202
1277
path ,
1203
1278
asyncIterator ,
1279
+ streamUsage ,
1204
1280
incrementalContext ,
1205
1281
deferMap ,
1206
1282
) ;
@@ -1212,19 +1288,97 @@ function completeListValue(
1212
1288
) ;
1213
1289
}
1214
1290
1215
- const streamUsage = getStreamUsage ( exeContext , fieldGroup , path ) ;
1291
+ if ( streamUsage === undefined ) {
1292
+ return completeIterableValue (
1293
+ exeContext ,
1294
+ itemType ,
1295
+ fieldGroup ,
1296
+ info ,
1297
+ path ,
1298
+ result ,
1299
+ incrementalContext ,
1300
+ deferMap ,
1301
+ ) ;
1302
+ }
1303
+
1304
+ return completeIterableValueWithPossibleStream (
1305
+ exeContext ,
1306
+ itemType ,
1307
+ fieldGroup ,
1308
+ info ,
1309
+ path ,
1310
+ result ,
1311
+ streamUsage ,
1312
+ incrementalContext ,
1313
+ deferMap ,
1314
+ ) ;
1315
+ }
1216
1316
1317
+ function completeIterableValue (
1318
+ exeContext : ExecutionContext ,
1319
+ itemType : GraphQLOutputType ,
1320
+ fieldGroup : FieldGroup ,
1321
+ info : GraphQLResolveInfo ,
1322
+ path : Path ,
1323
+ items : Iterable < unknown > ,
1324
+ incrementalContext : IncrementalContext | undefined ,
1325
+ deferMap : ReadonlyMap < DeferUsage , DeferredFragmentRecord > | undefined ,
1326
+ ) : PromiseOrValue < ReadonlyArray < unknown > > {
1327
+ let index = 0 ;
1217
1328
// This is specified as a simple map, however we're optimizing the path
1218
1329
// where the list contains no Promises by avoiding creating another Promise.
1219
1330
let containsPromise = false ;
1220
1331
const completedResults : Array < unknown > = [ ] ;
1332
+ for ( const item of items ) {
1333
+ // No need to modify the info object containing the path,
1334
+ // since from here on it is not ever accessed by resolver functions.
1335
+ const itemPath = addPath ( path , index , undefined ) ;
1336
+
1337
+ if (
1338
+ completeListItemValue (
1339
+ item ,
1340
+ completedResults ,
1341
+ exeContext ,
1342
+ itemType ,
1343
+ fieldGroup ,
1344
+ info ,
1345
+ itemPath ,
1346
+ incrementalContext ,
1347
+ deferMap ,
1348
+ )
1349
+ ) {
1350
+ containsPromise = true ;
1351
+ }
1352
+
1353
+ index ++ ;
1354
+ }
1355
+
1356
+ return containsPromise ? Promise . all ( completedResults ) : completedResults ;
1357
+ }
1358
+
1359
+ function completeIterableValueWithPossibleStream (
1360
+ exeContext : ExecutionContext ,
1361
+ itemType : GraphQLOutputType ,
1362
+ fieldGroup : FieldGroup ,
1363
+ info : GraphQLResolveInfo ,
1364
+ path : Path ,
1365
+ items : Iterable < unknown > ,
1366
+ streamUsage : StreamUsage ,
1367
+ incrementalContext : IncrementalContext | undefined ,
1368
+ deferMap : ReadonlyMap < DeferUsage , DeferredFragmentRecord > | undefined ,
1369
+ ) : PromiseOrValue < ReadonlyArray < unknown > > {
1221
1370
let index = 0 ;
1222
- const iterator = result [ Symbol . iterator ] ( ) ;
1371
+ // This is specified as a simple map, however we're optimizing the path
1372
+ // where the list contains no Promises by avoiding creating another Promise.
1373
+ let containsPromise = false ;
1374
+ const completedResults : Array < unknown > = [ ] ;
1375
+ const initialCount = streamUsage . initialCount ;
1376
+ const iterator = items [ Symbol . iterator ] ( ) ;
1223
1377
let iteration = iterator . next ( ) ;
1224
1378
while ( ! iteration . done ) {
1225
1379
const item = iteration . value ;
1226
1380
1227
- if ( streamUsage && index >= streamUsage . initialCount ) {
1381
+ if ( index >= initialCount ) {
1228
1382
const streamRecord = new StreamRecord ( {
1229
1383
label : streamUsage . label ,
1230
1384
path,
0 commit comments