@@ -1473,38 +1473,39 @@ function completeListValue(
1473
1473
// This is specified as a simple map, however we're optimizing the path
1474
1474
// where the list contains no Promises by avoiding creating another Promise.
1475
1475
let containsPromise = false ;
1476
- let currentParents = parentRecords ;
1477
1476
const completedResults : Array < unknown > = [ ] ;
1478
1477
let index = 0 ;
1479
1478
let streamContext : StreamContext | undefined ;
1480
- for ( const item of result ) {
1481
- // No need to modify the info object containing the path,
1482
- // since from here on it is not ever accessed by resolver functions.
1483
- const itemPath = addPath ( path , index , fieldGroup ) ;
1484
-
1479
+ const iterator = result [ Symbol . iterator ] ( ) ;
1480
+ // eslint-disable-next-line no-constant-condition
1481
+ while ( true ) {
1485
1482
if ( streamUsage && index >= streamUsage . initialCount ) {
1486
- if ( streamContext === undefined ) {
1487
- streamContext = {
1488
- label : streamUsage . label ,
1489
- path : pathToArray ( path ) ,
1490
- } ;
1491
- }
1492
- currentParents = executeStreamField (
1493
- path ,
1494
- itemPath ,
1495
- item ,
1483
+ streamContext = {
1484
+ label : streamUsage . label ,
1485
+ path : pathToArray ( path ) ,
1486
+ } ;
1487
+ executeStreamIterator (
1488
+ index ,
1489
+ iterator ,
1496
1490
exeContext ,
1491
+ itemType ,
1497
1492
getStreamedFieldGroup ( fieldGroup , streamUsage ) ,
1498
1493
info ,
1499
- itemType ,
1500
- streamContext ,
1494
+ path ,
1501
1495
deferMap ,
1502
- currentParents ,
1496
+ streamContext ,
1497
+ parentRecords ,
1503
1498
) ;
1504
- index ++ ;
1505
- continue ;
1499
+ break ;
1500
+ }
1501
+
1502
+ const iteration = iterator . next ( ) ;
1503
+ if ( iteration . done ) {
1504
+ break ;
1506
1505
}
1507
1506
1507
+ const item = iteration . value ;
1508
+ const itemPath = addPath ( path , index , fieldGroup ) ;
1508
1509
addPendingDeferredField ( fieldGroup , itemPath , deferMap ) ;
1509
1510
if (
1510
1511
completeListItemValue (
@@ -2171,86 +2172,71 @@ function assertEventStream(result: unknown): AsyncIterable<unknown> {
2171
2172
return result ;
2172
2173
}
2173
2174
2174
- function executeStreamField (
2175
- path : Path < FieldGroup > ,
2176
- itemPath : Path < FieldGroup > ,
2177
- item : PromiseOrValue < unknown > ,
2175
+ function executeStreamIterator (
2176
+ initialIndex : number ,
2177
+ iterator : Iterator < unknown > ,
2178
2178
exeContext : ExecutionContext ,
2179
+ itemType : GraphQLOutputType ,
2179
2180
fieldGroup : FieldGroup ,
2180
2181
info : GraphQLResolveInfo ,
2181
- itemType : GraphQLOutputType ,
2182
- streamContext : StreamContext ,
2182
+ path : Path < FieldGroup > ,
2183
2183
deferMap : Map < DeferUsage , DeferredFragmentRecord > ,
2184
- parents ?: Array < AsyncPayloadRecord > | undefined ,
2185
- ) : Array < StreamRecord > {
2186
- const streamRecord = new StreamRecord ( {
2187
- streamContext,
2188
- path : itemPath ,
2189
- parents,
2190
- exeContext,
2191
- } ) ;
2192
- const currentParents = [ streamRecord ] ;
2193
- if ( isPromise ( item ) ) {
2194
- const completedItems = completePromisedValue (
2195
- exeContext ,
2196
- itemType ,
2197
- fieldGroup ,
2198
- info ,
2199
- itemPath ,
2200
- item ,
2201
- deferMap ,
2202
- streamRecord ,
2203
- currentParents ,
2204
- ) . then (
2205
- ( value ) => [ value ] ,
2206
- ( error ) => {
2207
- streamRecord . errors . push ( error ) ;
2208
- filterSubsequentPayloads ( exeContext , path , currentParents ) ;
2209
- return null ;
2210
- } ,
2211
- ) ;
2184
+ streamContext : StreamContext ,
2185
+ parents : Array < AsyncPayloadRecord > | undefined ,
2186
+ ) : void {
2187
+ let index = initialIndex ;
2188
+ let currentParents = parents ;
2189
+ // eslint-disable-next-line no-constant-condition
2190
+ while ( true ) {
2191
+ const iteration = iterator . next ( ) ;
2192
+ if ( iteration . done ) {
2193
+ break ;
2194
+ }
2212
2195
2213
- streamRecord . addItems ( completedItems ) ;
2214
- return currentParents ;
2215
- }
2196
+ const item = iteration . value ;
2197
+ const itemPath = addPath ( path , index , fieldGroup ) ;
2198
+ const streamRecord = new StreamRecord ( {
2199
+ streamContext,
2200
+ path : itemPath ,
2201
+ parents : currentParents ,
2202
+ exeContext,
2203
+ } ) ;
2216
2204
2217
- let completedItem : PromiseOrValue < unknown > ;
2218
- try {
2219
- try {
2220
- completedItem = completeValue (
2205
+ currentParents = [ streamRecord ] ;
2206
+ if ( isPromise ( item ) ) {
2207
+ // eslint-disable-next-line @typescript-eslint/no-floating-promises
2208
+ handlePromisedItemForStream (
2221
2209
exeContext ,
2222
2210
itemType ,
2223
2211
fieldGroup ,
2224
2212
info ,
2213
+ path ,
2225
2214
itemPath ,
2226
2215
item ,
2227
2216
deferMap ,
2228
2217
streamRecord ,
2229
2218
currentParents ,
2230
2219
) ;
2231
- } catch ( rawError ) {
2232
- handleFieldError (
2233
- rawError ,
2234
- exeContext ,
2235
- itemType ,
2236
- fieldGroup ,
2237
- itemPath ,
2238
- deferMap ,
2239
- streamRecord ,
2240
- ) ;
2241
- completedItem = null ;
2242
- filterSubsequentPayloads ( exeContext , itemPath , currentParents ) ;
2220
+
2221
+ index ++ ;
2222
+ continue ;
2243
2223
}
2244
- } catch ( error ) {
2245
- streamRecord . errors . push ( error ) ;
2246
- filterSubsequentPayloads ( exeContext , path , currentParents ) ;
2247
- streamRecord . addItems ( null ) ;
2248
- return currentParents ;
2249
- }
2250
2224
2251
- if ( isPromise ( completedItem ) ) {
2252
- const completedItems = completedItem
2253
- . then ( undefined , ( rawError ) => {
2225
+ let completedItem : PromiseOrValue < unknown > ;
2226
+ try {
2227
+ try {
2228
+ completedItem = completeValue (
2229
+ exeContext ,
2230
+ itemType ,
2231
+ fieldGroup ,
2232
+ info ,
2233
+ itemPath ,
2234
+ item ,
2235
+ deferMap ,
2236
+ streamRecord ,
2237
+ currentParents ,
2238
+ ) ;
2239
+ } catch ( rawError ) {
2254
2240
handleFieldError (
2255
2241
rawError ,
2256
2242
exeContext ,
@@ -2261,23 +2247,91 @@ function executeStreamField(
2261
2247
streamRecord ,
2262
2248
) ;
2263
2249
filterSubsequentPayloads ( exeContext , itemPath , currentParents ) ;
2264
- return null ;
2265
- } )
2266
- . then (
2267
- ( value ) => [ value ] ,
2268
- ( error ) => {
2269
- streamRecord . errors . push ( error ) ;
2270
- filterSubsequentPayloads ( exeContext , path , currentParents ) ;
2271
- return null ;
2272
- } ,
2250
+ completedItem = null ;
2251
+ }
2252
+ } catch ( error ) {
2253
+ if ( fieldGroup . inInitialResult ) {
2254
+ streamRecord . errors . push ( error ) ;
2255
+ }
2256
+ returnStreamIteratorIgnoringError ( streamContext ) ;
2257
+ filterSubsequentPayloads ( exeContext , path , currentParents ) ;
2258
+ streamRecord . addItems ( null ) ;
2259
+ break ;
2260
+ }
2261
+
2262
+ if ( isPromise ( completedItem ) ) {
2263
+ // eslint-disable-next-line @typescript-eslint/no-floating-promises
2264
+ handlePromisedStreamResult (
2265
+ completedItem ,
2266
+ streamRecord ,
2267
+ exeContext ,
2268
+ itemType ,
2269
+ fieldGroup ,
2270
+ path ,
2271
+ itemPath ,
2272
+ deferMap ,
2273
+ streamContext ,
2273
2274
) ;
2275
+ } else {
2276
+ streamRecord . addItems ( [ completedItem ] ) ;
2277
+ }
2274
2278
2275
- streamRecord . addItems ( completedItems ) ;
2276
- return currentParents ;
2279
+ index ++ ;
2277
2280
}
2281
+ }
2278
2282
2279
- streamRecord . addItems ( [ completedItem ] ) ;
2280
- return currentParents ;
2283
+ async function handlePromisedItemForStream (
2284
+ exeContext : ExecutionContext ,
2285
+ returnType : GraphQLOutputType ,
2286
+ fieldGroup : FieldGroup ,
2287
+ info : GraphQLResolveInfo ,
2288
+ path : Path < FieldGroup > ,
2289
+ itemPath : Path < FieldGroup > ,
2290
+ item : Promise < unknown > ,
2291
+ deferMap : Map < DeferUsage , DeferredFragmentRecord > ,
2292
+ streamRecord : StreamRecord ,
2293
+ parentRecords : Array < AsyncPayloadRecord > | undefined ,
2294
+ ) : Promise < void > {
2295
+ try {
2296
+ try {
2297
+ const resolvedItem = await item ;
2298
+ let completedItem = completeValue (
2299
+ exeContext ,
2300
+ returnType ,
2301
+ fieldGroup ,
2302
+ info ,
2303
+ itemPath ,
2304
+ resolvedItem ,
2305
+ deferMap ,
2306
+ streamRecord ,
2307
+ parentRecords ,
2308
+ ) ;
2309
+ // TODO: add test for this
2310
+ /* c8 ignore next 3 */
2311
+ if ( isPromise ( completedItem ) ) {
2312
+ completedItem = await completedItem ;
2313
+ }
2314
+ streamRecord . addItems ( [ completedItem ] ) ;
2315
+ } catch ( rawError ) {
2316
+ handleFieldError (
2317
+ rawError ,
2318
+ exeContext ,
2319
+ returnType ,
2320
+ fieldGroup ,
2321
+ itemPath ,
2322
+ deferMap ,
2323
+ streamRecord ,
2324
+ ) ;
2325
+ filterSubsequentPayloads ( exeContext , itemPath , [ streamRecord ] ) ;
2326
+ streamRecord . addItems ( [ null ] ) ;
2327
+ }
2328
+ } catch ( error ) {
2329
+ if ( fieldGroup . inInitialResult ) {
2330
+ streamRecord . errors . push ( error ) ;
2331
+ }
2332
+ filterSubsequentPayloads ( exeContext , path , [ streamRecord ] ) ;
2333
+ streamRecord . addItems ( null ) ;
2334
+ }
2281
2335
}
2282
2336
2283
2337
async function executeStreamAsyncIterator (
@@ -2765,7 +2819,7 @@ class StreamRecord {
2765
2819
} ) ;
2766
2820
}
2767
2821
2768
- addItems ( items : PromiseOrValue < Array < unknown > | null > ) {
2822
+ addItems ( items : Array < unknown > | null ) {
2769
2823
if ( this . parents !== undefined ) {
2770
2824
const parentPromises = this . parents . map ( ( parent ) => parent . promise ) ;
2771
2825
this . _resolve ?.( Promise . any ( parentPromises ) . then ( ( ) => items ) ) ;
0 commit comments