@@ -126,6 +126,7 @@ export interface ExecutionContext {
126
126
errors : Array < GraphQLError > ;
127
127
subsequentPayloads : Set < AsyncPayloadRecord > ;
128
128
branches : WeakMap < GroupedFieldSet , Set < Path | undefined > > ;
129
+ leaves : WeakMap < GroupedFieldSet , Set < Path | undefined > > ;
129
130
}
130
131
131
132
/**
@@ -506,6 +507,7 @@ export function buildExecutionContext(
506
507
addPath : createPathFactory ( ) ,
507
508
subsequentPayloads : new Set ( ) ,
508
509
branches : new WeakMap ( ) ,
510
+ leaves : new WeakMap ( ) ,
509
511
errors : [ ] ,
510
512
} ;
511
513
}
@@ -520,6 +522,7 @@ function buildPerEventExecutionContext(
520
522
addPath : createPathFactory ( ) ,
521
523
subsequentPayloads : new Set ( ) ,
522
524
branches : new WeakMap ( ) ,
525
+ leaves : new WeakMap ( ) ,
523
526
errors : [ ] ,
524
527
} ;
525
528
}
@@ -639,7 +642,9 @@ function executeFieldsSerially(
639
642
640
643
const returnType = fieldDef . type ;
641
644
642
- if ( ! shouldExecute ( fieldGroup , returnType ) ) {
645
+ const isLeaf = isLeafType ( getNamedType ( returnType ) ) ;
646
+
647
+ if ( ! shouldExecute ( fieldGroup , isLeaf ) ) {
643
648
return results ;
644
649
}
645
650
const result = executeField (
@@ -666,10 +671,10 @@ function executeFieldsSerially(
666
671
667
672
function shouldExecute (
668
673
fieldGroup : FieldGroup ,
669
- returnType : GraphQLOutputType ,
674
+ isLeaf : boolean ,
670
675
deferDepth ?: number | undefined ,
671
676
) : boolean {
672
- if ( deferDepth === undefined || ! isLeafType ( getNamedType ( returnType ) ) ) {
677
+ if ( deferDepth === undefined || ! isLeaf ) {
673
678
return fieldGroup . some (
674
679
( { deferDepth : fieldDeferDepth } ) => fieldDeferDepth === deferDepth ,
675
680
) ;
@@ -702,6 +707,8 @@ function executeFields(
702
707
const results = Object . create ( null ) ;
703
708
let containsPromise = false ;
704
709
710
+ const shouldMask = Object . create ( null ) ;
711
+
705
712
try {
706
713
for ( const [ responseName , fieldGroup ] of groupedFieldSet ) {
707
714
const fieldPath = exeContext . addPath ( path , responseName , parentType . name ) ;
@@ -714,9 +721,27 @@ function executeFields(
714
721
715
722
const returnType = fieldDef . type ;
716
723
717
- if (
718
- shouldExecute ( fieldGroup , returnType , asyncPayloadRecord ?. deferDepth )
719
- ) {
724
+ const isLeaf = isLeafType ( getNamedType ( returnType ) ) ;
725
+
726
+ if ( shouldExecute ( fieldGroup , isLeaf , asyncPayloadRecord ?. deferDepth ) ) {
727
+ if (
728
+ asyncPayloadRecord !== undefined &&
729
+ isLeafType ( getNamedType ( returnType ) )
730
+ ) {
731
+ shouldMask [ responseName ] = ( ) => {
732
+ const set = exeContext . leaves . get ( groupedFieldSet ) ;
733
+ if ( set === undefined ) {
734
+ exeContext . leaves . set ( groupedFieldSet , new Set ( [ fieldPath ] ) ) ;
735
+ return false ;
736
+ }
737
+ if ( set . has ( fieldPath ) ) {
738
+ return true ;
739
+ }
740
+ set . add ( fieldPath ) ;
741
+ return false ;
742
+ } ;
743
+ }
744
+
720
745
const result = executeField (
721
746
exeContext ,
722
747
parentType ,
@@ -748,13 +773,27 @@ function executeFields(
748
773
749
774
// If there are no promises, we can just return the object
750
775
if ( ! containsPromise ) {
751
- return results ;
776
+ return asyncPayloadRecord === undefined
777
+ ? results
778
+ : new Proxy ( results , {
779
+ ownKeys : ( target ) =>
780
+ Reflect . ownKeys ( target ) . filter ( ( key ) => ! shouldMask [ key ] ?.( ) ) ,
781
+ } ) ;
752
782
}
753
783
754
784
// Otherwise, results is a map from field name to the result of resolving that
755
785
// field, which is possibly a promise. Return a promise that will return this
756
786
// same map, but with any promises replaced with the values they resolved to.
757
- return promiseForObject ( results ) ;
787
+ const promisedResult = promiseForObject ( results ) ;
788
+ return asyncPayloadRecord === undefined
789
+ ? promisedResult
790
+ : promisedResult . then (
791
+ ( resolved ) =>
792
+ new Proxy ( resolved , {
793
+ ownKeys : ( target ) =>
794
+ Reflect . ownKeys ( target ) . filter ( ( key ) => ! shouldMask [ key ] ?.( ) ) ,
795
+ } ) ,
796
+ ) ;
758
797
}
759
798
760
799
function toNodes ( fieldGroup : FieldGroup ) : ReadonlyArray < FieldNode > {
0 commit comments