@@ -94,13 +94,14 @@ export function defaultMergedResolver(
94
94
if (
95
95
fieldNodesByType ?. every ( ( fieldNode ) => {
96
96
const responseKey = fieldNode . alias ?. value ?? fieldNode . name . value ;
97
- if ( Object . prototype . hasOwnProperty . call ( parent , responseKey ) ) {
98
- return true ;
99
- }
100
- return false ;
97
+ return Object . prototype . hasOwnProperty . call ( parent , responseKey ) ;
101
98
} )
102
99
) {
103
100
handleResult ( parent , responseKey , context , info ) ;
101
+ } else {
102
+ // not all fields are present, we need to resolve more leftovers
103
+ // this can occur when there are circular @requires , see requires-circular audit test
104
+ handleLeftOver ( parent , context , info , leftOver ) ;
104
105
}
105
106
return deferred . promise ;
106
107
}
@@ -388,15 +389,48 @@ function handleDeferredResolverResult<TContext extends Record<string, any>>(
388
389
const deferredFields =
389
390
leftOver . missingFieldsParentDeferredMap . get ( leftOverParent ) ;
390
391
if ( deferredFields ) {
392
+ const stitchingInfo = info . schema . extensions ?. [
393
+ 'stitchingInfo'
394
+ ] as StitchingInfo ;
395
+ const parentTypeName = leftOverParent ?. __typename || info . parentType . name ;
396
+ const resolvedKeys = new Set < string > ( ) ;
391
397
for ( const [ responseKey , deferred ] of deferredFields ) {
392
- // If the deferred field is resolved, resolve the deferred field
393
- if ( Object . prototype . hasOwnProperty . call ( resolverResult , responseKey ) ) {
398
+ // after handleResolverResult, check if the field is now in the parent
399
+ if ( Object . prototype . hasOwnProperty . call ( leftOverParent , responseKey ) ) {
400
+ // field was added to parent by handleResolverResult
394
401
deferred . resolve (
395
402
handleResult ( leftOverParent , responseKey , context , info ) ,
396
403
) ;
404
+ resolvedKeys . add ( responseKey ) ;
405
+ } else {
406
+ // check if the required fields for this deferred field are now satisfied
407
+ const fieldNodesByType =
408
+ stitchingInfo ?. fieldNodesByField ?. [ parentTypeName ] ?. [ responseKey ] ;
409
+ if ( fieldNodesByType ) {
410
+ if (
411
+ fieldNodesByType . every ( ( fieldNode ) => {
412
+ const requiredKey =
413
+ fieldNode . alias ?. value ?? fieldNode . name . value ;
414
+ return Object . prototype . hasOwnProperty . call (
415
+ leftOverParent ,
416
+ requiredKey ,
417
+ ) ;
418
+ } )
419
+ ) {
420
+ // requirements are satisfied, trigger handleLeftOver to fetch this field
421
+ handleLeftOver ( leftOverParent , context , info , leftOver ) ;
422
+ }
423
+ }
397
424
}
398
425
}
399
- leftOver . missingFieldsParentDeferredMap . delete ( leftOverParent ) ;
426
+ // delete resolved deferred fields
427
+ for ( const key of resolvedKeys ) {
428
+ deferredFields . delete ( key ) ;
429
+ }
430
+ // and delete map if empty
431
+ if ( deferredFields . size === 0 ) {
432
+ leftOver . missingFieldsParentDeferredMap . delete ( leftOverParent ) ;
433
+ }
400
434
}
401
435
}
402
436
0 commit comments