@@ -1014,7 +1014,6 @@ function getStreamUsage(
1014
1014
1015
1015
return streamUsage ;
1016
1016
}
1017
-
1018
1017
/**
1019
1018
* Complete a async iterator value by completing the result and calling
1020
1019
* recursively until all the results are completed.
@@ -1033,10 +1032,87 @@ async function completeAsyncIteratorValue(
1033
1032
const completedResults : Array < unknown > = [ ] ;
1034
1033
const acc : GraphQLResult < Array < unknown > > = [ completedResults , [ ] ] ;
1035
1034
let index = 0 ;
1036
- const streamUsage = getStreamUsage ( exeContext , fieldGroup , path ) ;
1037
1035
// eslint-disable-next-line no-constant-condition
1038
1036
while ( true ) {
1039
- if ( streamUsage && index >= streamUsage . initialCount ) {
1037
+ const itemPath = addPath ( path , index , undefined ) ;
1038
+ let iteration ;
1039
+ try {
1040
+ // eslint-disable-next-line no-await-in-loop
1041
+ iteration = await asyncIterator . next ( ) ;
1042
+ } catch ( rawError ) {
1043
+ throw locatedError ( rawError , toNodes ( fieldGroup ) , pathToArray ( path ) ) ;
1044
+ }
1045
+
1046
+ if ( iteration . done ) {
1047
+ break ;
1048
+ }
1049
+
1050
+ const item = iteration . value ;
1051
+ // TODO: add test case for asyncIterator returning a promise
1052
+ /* c8 ignore start */
1053
+ if ( isPromise ( item ) ) {
1054
+ completedResults . push (
1055
+ completePromisedListItemValue (
1056
+ item ,
1057
+ acc ,
1058
+ exeContext ,
1059
+ itemType ,
1060
+ fieldGroup ,
1061
+ info ,
1062
+ itemPath ,
1063
+ incrementalContext ,
1064
+ deferMap ,
1065
+ ) ,
1066
+ ) ;
1067
+ containsPromise = true ;
1068
+ } else if (
1069
+ /* c8 ignore stop */
1070
+ completeListItemValue (
1071
+ item ,
1072
+ completedResults ,
1073
+ acc ,
1074
+ exeContext ,
1075
+ itemType ,
1076
+ fieldGroup ,
1077
+ info ,
1078
+ itemPath ,
1079
+ incrementalContext ,
1080
+ deferMap ,
1081
+ )
1082
+ ) {
1083
+ containsPromise = true ;
1084
+ }
1085
+ index ++ ;
1086
+ }
1087
+
1088
+ return containsPromise
1089
+ ? Promise . all ( completedResults ) . then ( ( resolved ) => [ resolved , acc [ 1 ] ] )
1090
+ : acc ;
1091
+ }
1092
+
1093
+ /**
1094
+ * Complete a async iterator value by completing the result and calling
1095
+ * recursively until all the results are completed.
1096
+ */
1097
+ async function completeAsyncIteratorValueWithPossibleStream (
1098
+ exeContext : ExecutionContext ,
1099
+ itemType : GraphQLOutputType ,
1100
+ fieldGroup : FieldGroup ,
1101
+ info : GraphQLResolveInfo ,
1102
+ path : Path ,
1103
+ asyncIterator : AsyncIterator < unknown > ,
1104
+ streamUsage : StreamUsage ,
1105
+ incrementalContext : IncrementalContext | undefined ,
1106
+ deferMap : ReadonlyMap < DeferUsage , DeferredFragmentRecord > | undefined ,
1107
+ ) : Promise < GraphQLResult < ReadonlyArray < unknown > > > {
1108
+ let containsPromise = false ;
1109
+ const completedResults : Array < unknown > = [ ] ;
1110
+ const acc : GraphQLResult < Array < unknown > > = [ completedResults , [ ] ] ;
1111
+ let index = 0 ;
1112
+ const initialCount = streamUsage . initialCount ;
1113
+ // eslint-disable-next-line no-constant-condition
1114
+ while ( true ) {
1115
+ if ( index >= initialCount ) {
1040
1116
const streamRecord = new StreamRecord ( {
1041
1117
label : streamUsage . label ,
1042
1118
path,
@@ -1147,17 +1223,32 @@ function completeListValue(
1147
1223
deferMap : ReadonlyMap < DeferUsage , DeferredFragmentRecord > | undefined ,
1148
1224
) : PromiseOrValue < GraphQLResult < ReadonlyArray < unknown > > > {
1149
1225
const itemType = returnType . ofType ;
1226
+ const streamUsage = getStreamUsage ( exeContext , fieldGroup , path ) ;
1150
1227
1151
1228
if ( isAsyncIterable ( result ) ) {
1152
1229
const asyncIterator = result [ Symbol . asyncIterator ] ( ) ;
1153
1230
1154
- return completeAsyncIteratorValue (
1231
+ if ( streamUsage === undefined ) {
1232
+ return completeAsyncIteratorValue (
1233
+ exeContext ,
1234
+ itemType ,
1235
+ fieldGroup ,
1236
+ info ,
1237
+ path ,
1238
+ asyncIterator ,
1239
+ incrementalContext ,
1240
+ deferMap ,
1241
+ ) ;
1242
+ }
1243
+
1244
+ return completeAsyncIteratorValueWithPossibleStream (
1155
1245
exeContext ,
1156
1246
itemType ,
1157
1247
fieldGroup ,
1158
1248
info ,
1159
1249
path ,
1160
1250
asyncIterator ,
1251
+ streamUsage ,
1161
1252
incrementalContext ,
1162
1253
deferMap ,
1163
1254
) ;
@@ -1169,13 +1260,27 @@ function completeListValue(
1169
1260
) ;
1170
1261
}
1171
1262
1172
- return completeIterableValue (
1263
+ if ( streamUsage === undefined ) {
1264
+ return completeIterableValue (
1265
+ exeContext ,
1266
+ itemType ,
1267
+ fieldGroup ,
1268
+ info ,
1269
+ path ,
1270
+ result ,
1271
+ incrementalContext ,
1272
+ deferMap ,
1273
+ ) ;
1274
+ }
1275
+
1276
+ return completeIterableValueWithPossibleStream (
1173
1277
exeContext ,
1174
1278
itemType ,
1175
1279
fieldGroup ,
1176
1280
info ,
1177
1281
path ,
1178
1282
result ,
1283
+ streamUsage ,
1179
1284
incrementalContext ,
1180
1285
deferMap ,
1181
1286
) ;
@@ -1197,13 +1302,74 @@ function completeIterableValue(
1197
1302
const completedResults : Array < unknown > = [ ] ;
1198
1303
const acc : GraphQLResult < Array < unknown > > = [ completedResults , [ ] ] ;
1199
1304
let index = 0 ;
1200
- const streamUsage = getStreamUsage ( exeContext , fieldGroup , path ) ;
1305
+ for ( const item of items ) {
1306
+ // No need to modify the info object containing the path,
1307
+ // since from here on it is not ever accessed by resolver functions.
1308
+ const itemPath = addPath ( path , index , undefined ) ;
1309
+
1310
+ if ( isPromise ( item ) ) {
1311
+ completedResults . push (
1312
+ completePromisedListItemValue (
1313
+ item ,
1314
+ acc ,
1315
+ exeContext ,
1316
+ itemType ,
1317
+ fieldGroup ,
1318
+ info ,
1319
+ itemPath ,
1320
+ incrementalContext ,
1321
+ deferMap ,
1322
+ ) ,
1323
+ ) ;
1324
+ containsPromise = true ;
1325
+ } else if (
1326
+ completeListItemValue (
1327
+ item ,
1328
+ completedResults ,
1329
+ acc ,
1330
+ exeContext ,
1331
+ itemType ,
1332
+ fieldGroup ,
1333
+ info ,
1334
+ itemPath ,
1335
+ incrementalContext ,
1336
+ deferMap ,
1337
+ )
1338
+ ) {
1339
+ containsPromise = true ;
1340
+ }
1341
+ index ++ ;
1342
+ }
1343
+
1344
+ return containsPromise
1345
+ ? Promise . all ( completedResults ) . then ( ( resolved ) => [ resolved , acc [ 1 ] ] )
1346
+ : acc ;
1347
+ }
1348
+
1349
+ function completeIterableValueWithPossibleStream (
1350
+ exeContext : ExecutionContext ,
1351
+ itemType : GraphQLOutputType ,
1352
+ fieldGroup : FieldGroup ,
1353
+ info : GraphQLResolveInfo ,
1354
+ path : Path ,
1355
+ items : Iterable < unknown > ,
1356
+ streamUsage : StreamUsage ,
1357
+ incrementalContext : IncrementalContext | undefined ,
1358
+ deferMap : ReadonlyMap < DeferUsage , DeferredFragmentRecord > | undefined ,
1359
+ ) : PromiseOrValue < GraphQLResult < ReadonlyArray < unknown > > > {
1360
+ // This is specified as a simple map, however we're optimizing the path
1361
+ // where the list contains no Promises by avoiding creating another Promise.
1362
+ let containsPromise = false ;
1363
+ const completedResults : Array < unknown > = [ ] ;
1364
+ const acc : GraphQLResult < Array < unknown > > = [ completedResults , [ ] ] ;
1365
+ let index = 0 ;
1366
+ const initialCount = streamUsage . initialCount ;
1201
1367
const iterator = items [ Symbol . iterator ] ( ) ;
1202
1368
let iteration = iterator . next ( ) ;
1203
1369
while ( ! iteration . done ) {
1204
1370
const item = iteration . value ;
1205
1371
1206
- if ( streamUsage && index >= streamUsage . initialCount ) {
1372
+ if ( index >= initialCount ) {
1207
1373
const streamRecord = new StreamRecord ( {
1208
1374
label : streamUsage . label ,
1209
1375
path,
0 commit comments