@@ -445,19 +445,34 @@ function executeOperation(
445445
446446 switch ( operation . operation ) {
447447 case OperationTypeNode . QUERY :
448- return executeFields ( exeContext , rootType , rootValue , path , rootFields ) ;
448+ return executeFields (
449+ exeContext ,
450+ rootType ,
451+ rootValue ,
452+ path ,
453+ rootFields ,
454+ 1 ,
455+ ) ;
449456 case OperationTypeNode . MUTATION :
450457 return executeFieldsSerially (
451458 exeContext ,
452459 rootType ,
453460 rootValue ,
454461 path ,
455462 rootFields ,
463+ 1 ,
456464 ) ;
457465 case OperationTypeNode . SUBSCRIPTION :
458466 // TODO: deprecate `subscribe` and move all logic here
459467 // Temporary solution until we finish merging execute and subscribe together
460- return executeFields ( exeContext , rootType , rootValue , path , rootFields ) ;
468+ return executeFields (
469+ exeContext ,
470+ rootType ,
471+ rootValue ,
472+ path ,
473+ rootFields ,
474+ 1 ,
475+ ) ;
461476 }
462477}
463478
@@ -471,6 +486,7 @@ function executeFieldsSerially(
471486 sourceValue : unknown ,
472487 path : Path | undefined ,
473488 fields : Map < string , ReadonlyArray < FieldNode > > ,
489+ depth : number ,
474490) : PromiseOrValue < ObjMap < unknown > > {
475491 return promiseReduce (
476492 fields . entries ( ) ,
@@ -482,6 +498,7 @@ function executeFieldsSerially(
482498 sourceValue ,
483499 fieldNodes ,
484500 fieldPath ,
501+ depth ,
485502 ) ;
486503 if ( result === undefined ) {
487504 return results ;
@@ -509,6 +526,7 @@ function executeFields(
509526 sourceValue : unknown ,
510527 path : Path | undefined ,
511528 fields : Map < string , ReadonlyArray < FieldNode > > ,
529+ depth : number ,
512530) : PromiseOrValue < ObjMap < unknown > > {
513531 const results = Object . create ( null ) ;
514532 let containsPromise = false ;
@@ -522,6 +540,7 @@ function executeFields(
522540 sourceValue ,
523541 fieldNodes ,
524542 fieldPath ,
543+ depth ,
525544 ) ;
526545
527546 if ( result !== undefined ) {
@@ -578,22 +597,6 @@ function checkAliasCount(
578597 }
579598}
580599
581- /**
582- * Counts the field depth represented by a Path. Only string keys are counted
583- * (numeric keys represent list indices and should not increase the depth).
584- */
585- function pathDepth ( path : Path | undefined ) : number {
586- let depth = 0 ;
587- let current = path ;
588- while ( current !== undefined ) {
589- if ( typeof current . key === 'string' ) {
590- depth ++ ;
591- }
592- current = current . prev ;
593- }
594- return depth ;
595- }
596-
597600/**
598601 * Implements the "Executing fields" section of the spec
599602 * In particular, this function figures out the value that the field returns by
@@ -606,16 +609,14 @@ function executeField(
606609 source : unknown ,
607610 fieldNodes : ReadonlyArray < FieldNode > ,
608611 path : Path ,
612+ depth : number ,
609613) : PromiseOrValue < unknown > {
610614 // Check depth limit before resolving the field.
611- if ( exeContext . maxDepth !== undefined ) {
612- const depth = pathDepth ( path ) ;
613- if ( depth > exeContext . maxDepth ) {
614- throw new GraphQLError (
615- `Query depth limit of ${ exeContext . maxDepth } exceeded, found depth: ${ depth } .` ,
616- { nodes : fieldNodes } ,
617- ) ;
618- }
615+ if ( exeContext . maxDepth !== undefined && depth > exeContext . maxDepth ) {
616+ throw new GraphQLError (
617+ `Query depth limit of ${ exeContext . maxDepth } exceeded, found depth: ${ depth } .` ,
618+ { nodes : fieldNodes } ,
619+ ) ;
619620 }
620621
621622 const fieldDef = getFieldDef ( exeContext . schema , parentType , fieldNodes [ 0 ] ) ;
@@ -655,7 +656,15 @@ function executeField(
655656 let completed ;
656657 if ( isPromise ( result ) ) {
657658 completed = result . then ( ( resolved ) =>
658- completeValue ( exeContext , returnType , fieldNodes , info , path , resolved ) ,
659+ completeValue (
660+ exeContext ,
661+ returnType ,
662+ fieldNodes ,
663+ info ,
664+ path ,
665+ resolved ,
666+ depth ,
667+ ) ,
659668 ) ;
660669 } else {
661670 completed = completeValue (
@@ -665,6 +674,7 @@ function executeField(
665674 info ,
666675 path ,
667676 result ,
677+ depth ,
668678 ) ;
669679 }
670680
@@ -755,6 +765,7 @@ function completeValue(
755765 info : GraphQLResolveInfo ,
756766 path : Path ,
757767 result : unknown ,
768+ depth : number ,
758769) : PromiseOrValue < unknown > {
759770 // If result is an Error, throw a located error.
760771 if ( result instanceof Error ) {
@@ -771,6 +782,7 @@ function completeValue(
771782 info ,
772783 path ,
773784 result ,
785+ depth ,
774786 ) ;
775787 if ( completed === null ) {
776788 throw new Error (
@@ -794,6 +806,7 @@ function completeValue(
794806 info ,
795807 path ,
796808 result ,
809+ depth ,
797810 ) ;
798811 }
799812
@@ -813,6 +826,7 @@ function completeValue(
813826 info ,
814827 path ,
815828 result ,
829+ depth ,
816830 ) ;
817831 }
818832
@@ -825,6 +839,7 @@ function completeValue(
825839 info ,
826840 path ,
827841 result ,
842+ depth ,
828843 ) ;
829844 }
830845 /* c8 ignore next 6 */
@@ -846,6 +861,7 @@ function completeListValue(
846861 info : GraphQLResolveInfo ,
847862 path : Path ,
848863 result : unknown ,
864+ depth : number ,
849865) : PromiseOrValue < ReadonlyArray < unknown > > {
850866 if ( ! isIterableObject ( result ) ) {
851867 throw new GraphQLError (
@@ -872,6 +888,7 @@ function completeListValue(
872888 info ,
873889 itemPath ,
874890 resolved ,
891+ depth ,
875892 ) ,
876893 ) ;
877894 } else {
@@ -882,6 +899,7 @@ function completeListValue(
882899 info ,
883900 itemPath ,
884901 item ,
902+ depth ,
885903 ) ;
886904 }
887905
@@ -937,6 +955,7 @@ function completeAbstractValue(
937955 info : GraphQLResolveInfo ,
938956 path : Path ,
939957 result : unknown ,
958+ depth : number ,
940959) : PromiseOrValue < ObjMap < unknown > > {
941960 const resolveTypeFn = returnType . resolveType ?? exeContext . typeResolver ;
942961 const contextValue = exeContext . contextValue ;
@@ -958,6 +977,7 @@ function completeAbstractValue(
958977 info ,
959978 path ,
960979 result ,
980+ depth ,
961981 ) ,
962982 ) ;
963983 }
@@ -976,6 +996,7 @@ function completeAbstractValue(
976996 info ,
977997 path ,
978998 result ,
999+ depth ,
9791000 ) ;
9801001}
9811002
@@ -1044,12 +1065,15 @@ function completeObjectValue(
10441065 info : GraphQLResolveInfo ,
10451066 path : Path ,
10461067 result : unknown ,
1068+ depth : number ,
10471069) : PromiseOrValue < ObjMap < unknown > > {
10481070 // Collect sub-fields to execute to complete this value.
10491071 const subFieldNodes = collectSubfields ( exeContext , returnType , fieldNodes ) ;
10501072
10511073 checkAliasCount ( exeContext , subFieldNodes ) ;
10521074
1075+ const subDepth = depth + 1 ;
1076+
10531077 // If there is an isTypeOf predicate function, call it with the
10541078 // current result. If isTypeOf returns false, then raise an error rather
10551079 // than continuing execution.
@@ -1067,6 +1091,7 @@ function completeObjectValue(
10671091 result ,
10681092 path ,
10691093 subFieldNodes ,
1094+ subDepth ,
10701095 ) ;
10711096 } ) ;
10721097 }
@@ -1076,7 +1101,14 @@ function completeObjectValue(
10761101 }
10771102 }
10781103
1079- return executeFields ( exeContext , returnType , result , path , subFieldNodes ) ;
1104+ return executeFields (
1105+ exeContext ,
1106+ returnType ,
1107+ result ,
1108+ path ,
1109+ subFieldNodes ,
1110+ subDepth ,
1111+ ) ;
10801112}
10811113
10821114function invalidReturnTypeError (
0 commit comments