@@ -1104,13 +1104,68 @@ async function completeAsyncIteratorValue(
1104
1104
incrementalContext : IncrementalContext | undefined ,
1105
1105
deferMap : ReadonlyMap < DeferUsage , DeferredFragmentRecord > | undefined ,
1106
1106
) : Promise < ReadonlyArray < unknown > > {
1107
- const streamUsage = getStreamUsage ( exeContext , fieldGroup , path ) ;
1108
1107
let containsPromise = false ;
1109
1108
const completedResults : Array < unknown > = [ ] ;
1110
1109
let index = 0 ;
1111
1110
// eslint-disable-next-line no-constant-condition
1112
1111
while ( true ) {
1113
- if ( streamUsage && index >= streamUsage . initialCount ) {
1112
+ const itemPath = addPath ( path , index , undefined ) ;
1113
+ let iteration ;
1114
+ try {
1115
+ // eslint-disable-next-line no-await-in-loop
1116
+ iteration = await asyncIterator . next ( ) ;
1117
+ } catch ( rawError ) {
1118
+ throw locatedError ( rawError , toNodes ( fieldGroup ) , pathToArray ( path ) ) ;
1119
+ }
1120
+
1121
+ if ( iteration . done ) {
1122
+ break ;
1123
+ }
1124
+
1125
+ if (
1126
+ completeListItemValue (
1127
+ iteration . value ,
1128
+ completedResults ,
1129
+ exeContext ,
1130
+ itemType ,
1131
+ fieldGroup ,
1132
+ info ,
1133
+ itemPath ,
1134
+ incrementalContext ,
1135
+ deferMap ,
1136
+ )
1137
+ ) {
1138
+ containsPromise = true ;
1139
+ }
1140
+
1141
+ index ++ ;
1142
+ }
1143
+
1144
+ return containsPromise ? Promise . all ( completedResults ) : completedResults ;
1145
+ }
1146
+
1147
+ /**
1148
+ * Complete a async iterator value by completing the result and calling
1149
+ * recursively until all the results are completed.
1150
+ */
1151
+ async function completeAsyncIteratorValueWithPossibleStream (
1152
+ exeContext : ExecutionContext ,
1153
+ itemType : GraphQLOutputType ,
1154
+ fieldGroup : FieldGroup ,
1155
+ info : GraphQLResolveInfo ,
1156
+ path : Path ,
1157
+ asyncIterator : AsyncIterator < unknown > ,
1158
+ streamUsage : StreamUsage ,
1159
+ incrementalContext : IncrementalContext | undefined ,
1160
+ deferMap : ReadonlyMap < DeferUsage , DeferredFragmentRecord > | undefined ,
1161
+ ) : Promise < ReadonlyArray < unknown > > {
1162
+ let containsPromise = false ;
1163
+ const completedResults : Array < unknown > = [ ] ;
1164
+ let index = 0 ;
1165
+ const initialCount = streamUsage . initialCount ;
1166
+ // eslint-disable-next-line no-constant-condition
1167
+ while ( true ) {
1168
+ if ( index >= initialCount ) {
1114
1169
const streamRecord = new StreamRecord ( {
1115
1170
label : streamUsage . label ,
1116
1171
path,
@@ -1151,13 +1206,16 @@ async function completeAsyncIteratorValue(
1151
1206
try {
1152
1207
// eslint-disable-next-line no-await-in-loop
1153
1208
iteration = await asyncIterator . next ( ) ;
1154
- if ( iteration . done ) {
1155
- break ;
1156
- }
1157
1209
} catch ( rawError ) {
1158
1210
throw locatedError ( rawError , toNodes ( fieldGroup ) , pathToArray ( path ) ) ;
1159
1211
}
1160
1212
1213
+ // TODO: add test case for stream returning done before initialCount
1214
+ /* c8 ignore next 3 */
1215
+ if ( iteration . done ) {
1216
+ break ;
1217
+ }
1218
+
1161
1219
if (
1162
1220
completeListItemValue (
1163
1221
iteration . value ,
@@ -1169,14 +1227,17 @@ async function completeAsyncIteratorValue(
1169
1227
itemPath ,
1170
1228
incrementalContext ,
1171
1229
deferMap ,
1172
- )
1230
+ ) /* c8 ignore start */
1173
1231
) {
1232
+ // TODO: add test case for asyncIterator that yields promises
1174
1233
containsPromise = true ;
1175
- }
1176
- index += 1 ;
1234
+ } /* c8 ignore stop */
1235
+ index ++ ;
1177
1236
}
1178
1237
1179
- return containsPromise ? Promise . all ( completedResults ) : completedResults ;
1238
+ return containsPromise
1239
+ ? /* c8 ignore start */ Promise . all ( completedResults )
1240
+ : /* c8 ignore stop */ completedResults ;
1180
1241
}
1181
1242
1182
1243
function addIncrementalDataRecord (
@@ -1206,17 +1267,32 @@ function completeListValue(
1206
1267
deferMap : ReadonlyMap < DeferUsage , DeferredFragmentRecord > | undefined ,
1207
1268
) : PromiseOrValue < ReadonlyArray < unknown > > {
1208
1269
const itemType = returnType . ofType ;
1270
+ const streamUsage = getStreamUsage ( exeContext , fieldGroup , path ) ;
1209
1271
1210
1272
if ( isAsyncIterable ( result ) ) {
1211
1273
const asyncIterator = result [ Symbol . asyncIterator ] ( ) ;
1212
1274
1213
- return completeAsyncIteratorValue (
1275
+ if ( streamUsage === undefined ) {
1276
+ return completeAsyncIteratorValue (
1277
+ exeContext ,
1278
+ itemType ,
1279
+ fieldGroup ,
1280
+ info ,
1281
+ path ,
1282
+ asyncIterator ,
1283
+ incrementalContext ,
1284
+ deferMap ,
1285
+ ) ;
1286
+ }
1287
+
1288
+ return completeAsyncIteratorValueWithPossibleStream (
1214
1289
exeContext ,
1215
1290
itemType ,
1216
1291
fieldGroup ,
1217
1292
info ,
1218
1293
path ,
1219
1294
asyncIterator ,
1295
+ streamUsage ,
1220
1296
incrementalContext ,
1221
1297
deferMap ,
1222
1298
) ;
@@ -1228,19 +1304,97 @@ function completeListValue(
1228
1304
) ;
1229
1305
}
1230
1306
1231
- const streamUsage = getStreamUsage ( exeContext , fieldGroup , path ) ;
1307
+ if ( streamUsage === undefined ) {
1308
+ return completeIterableValue (
1309
+ exeContext ,
1310
+ itemType ,
1311
+ fieldGroup ,
1312
+ info ,
1313
+ path ,
1314
+ result ,
1315
+ incrementalContext ,
1316
+ deferMap ,
1317
+ ) ;
1318
+ }
1319
+
1320
+ return completeIterableValueWithPossibleStream (
1321
+ exeContext ,
1322
+ itemType ,
1323
+ fieldGroup ,
1324
+ info ,
1325
+ path ,
1326
+ result ,
1327
+ streamUsage ,
1328
+ incrementalContext ,
1329
+ deferMap ,
1330
+ ) ;
1331
+ }
1232
1332
1333
+ function completeIterableValue (
1334
+ exeContext : ExecutionContext ,
1335
+ itemType : GraphQLOutputType ,
1336
+ fieldGroup : FieldGroup ,
1337
+ info : GraphQLResolveInfo ,
1338
+ path : Path ,
1339
+ items : Iterable < unknown > ,
1340
+ incrementalContext : IncrementalContext | undefined ,
1341
+ deferMap : ReadonlyMap < DeferUsage , DeferredFragmentRecord > | undefined ,
1342
+ ) : PromiseOrValue < ReadonlyArray < unknown > > {
1343
+ let index = 0 ;
1233
1344
// This is specified as a simple map, however we're optimizing the path
1234
1345
// where the list contains no Promises by avoiding creating another Promise.
1235
1346
let containsPromise = false ;
1236
1347
const completedResults : Array < unknown > = [ ] ;
1348
+ for ( const item of items ) {
1349
+ // No need to modify the info object containing the path,
1350
+ // since from here on it is not ever accessed by resolver functions.
1351
+ const itemPath = addPath ( path , index , undefined ) ;
1352
+
1353
+ if (
1354
+ completeListItemValue (
1355
+ item ,
1356
+ completedResults ,
1357
+ exeContext ,
1358
+ itemType ,
1359
+ fieldGroup ,
1360
+ info ,
1361
+ itemPath ,
1362
+ incrementalContext ,
1363
+ deferMap ,
1364
+ )
1365
+ ) {
1366
+ containsPromise = true ;
1367
+ }
1368
+
1369
+ index ++ ;
1370
+ }
1371
+
1372
+ return containsPromise ? Promise . all ( completedResults ) : completedResults ;
1373
+ }
1374
+
1375
+ function completeIterableValueWithPossibleStream (
1376
+ exeContext : ExecutionContext ,
1377
+ itemType : GraphQLOutputType ,
1378
+ fieldGroup : FieldGroup ,
1379
+ info : GraphQLResolveInfo ,
1380
+ path : Path ,
1381
+ items : Iterable < unknown > ,
1382
+ streamUsage : StreamUsage ,
1383
+ incrementalContext : IncrementalContext | undefined ,
1384
+ deferMap : ReadonlyMap < DeferUsage , DeferredFragmentRecord > | undefined ,
1385
+ ) : PromiseOrValue < ReadonlyArray < unknown > > {
1237
1386
let index = 0 ;
1238
- const iterator = result [ Symbol . iterator ] ( ) ;
1387
+ // This is specified as a simple map, however we're optimizing the path
1388
+ // where the list contains no Promises by avoiding creating another Promise.
1389
+ let containsPromise = false ;
1390
+ const completedResults : Array < unknown > = [ ] ;
1391
+ const initialCount = streamUsage . initialCount ;
1392
+ const iterator = items [ Symbol . iterator ] ( ) ;
1239
1393
let iteration = iterator . next ( ) ;
1240
1394
while ( ! iteration . done ) {
1241
1395
const item = iteration . value ;
1242
1396
1243
- if ( streamUsage && index >= streamUsage . initialCount ) {
1397
+ if ( index >= initialCount ) {
1244
1398
const streamRecord = new StreamRecord ( {
1245
1399
label : streamUsage . label ,
1246
1400
path,
0 commit comments