@@ -105,6 +105,11 @@ export class GraphQLConverter {
105105 return true ;
106106 }
107107
108+ // NOTE: This heuristic treats a type as a namespace (operation-grouping type) when ALL
109+ // of its fields accept arguments. This can produce a false positive for regular data
110+ // types where every field happens to be parameterized. If that becomes a problem,
111+ // introduce an explicit config option (e.g. namespacedRootTypes: [...]) rather than
112+ // relying on field-arg counts alone.
108113 private isNamespaceType ( type : GraphQLObjectType ) : boolean {
109114 const fields = Object . values ( type . getFields ( ) ) ;
110115 if ( fields . length === 0 ) {
@@ -113,6 +118,44 @@ export class GraphQLConverter {
113118 return fields . every ( ( f ) => f . args . length > 0 ) ;
114119 }
115120
121+ // Returns the names of object types that will be consumed as namespace groupings by
122+ // convertOperations — i.e. types that appear as the return type of a zero-arg root
123+ // field whose own fields all accept arguments. These types must be excluded from the
124+ // type registry in collectTypeDefinitions to prevent their fields from showing up
125+ // both as object properties and as standalone operations.
126+ private collectNamespaceTypeNames ( ) : Set < string > {
127+ if ( ! this . schema ) {
128+ return new Set ( ) ;
129+ }
130+ const namespaceTypeNames = new Set < string > ( ) ;
131+ const rootTypes : GraphQLObjectType [ ] = [ ] ;
132+ const queryType = this . schema . getQueryType ( ) ;
133+ if ( queryType ) {
134+ rootTypes . push ( queryType ) ;
135+ }
136+ const mutationType = this . schema . getMutationType ( ) ;
137+ if ( mutationType ) {
138+ rootTypes . push ( mutationType ) ;
139+ }
140+ const subscriptionType = this . schema . getSubscriptionType ( ) ;
141+ if ( subscriptionType && this . isActualSubscriptionRootType ( subscriptionType ) ) {
142+ rootTypes . push ( subscriptionType ) ;
143+ }
144+ for ( const rootType of rootTypes ) {
145+ for ( const field of Object . values ( rootType . getFields ( ) ) ) {
146+ const returnRawType = this . unwrapNonNull ( field . type ) ;
147+ if (
148+ returnRawType instanceof GraphQLObjectType &&
149+ field . args . length === 0 &&
150+ this . isNamespaceType ( returnRawType )
151+ ) {
152+ namespaceTypeNames . add ( returnRawType . name ) ;
153+ }
154+ }
155+ }
156+ return namespaceTypeNames ;
157+ }
158+
116159 public async convert ( ) : Promise < GraphQLConverterResult > {
117160 const sdlContent = await readFile ( this . filePath , "utf-8" ) ;
118161 this . schema = buildSchema ( sdlContent ) ;
@@ -144,6 +187,12 @@ export class GraphQLConverter {
144187 return ;
145188 }
146189
190+ // Identify namespace types up-front so we can exclude them below. Namespace types
191+ // have their fields registered as standalone operations via convertNamespaceOperations;
192+ // registering them as type definitions too would make their fields appear in both
193+ // places, causing redundant/confusing output.
194+ const namespaceTypeNames = this . collectNamespaceTypeNames ( ) ;
195+
147196 const typeMap = this . schema . getTypeMap ( ) ;
148197 for ( const [ typeName , type ] of Object . entries ( typeMap ) ) {
149198 // Skip built-in types
@@ -163,6 +212,11 @@ export class GraphQLConverter {
163212 continue ;
164213 }
165214
215+ // Skip namespace types — their fields are promoted to top-level operations.
216+ if ( type instanceof GraphQLObjectType && namespaceTypeNames . has ( typeName ) ) {
217+ continue ;
218+ }
219+
166220 if ( type instanceof GraphQLScalarType && this . isBuiltInScalar ( typeName ) ) {
167221 continue ;
168222 }
@@ -519,13 +573,14 @@ export class GraphQLConverter {
519573
520574 private convertObjectTypeDefinition ( type : GraphQLObjectType ) : FdrAPI . api . v1 . register . TypeShape {
521575 const fields = type . getFields ( ) ;
522- const properties = Object . entries ( fields ) . map (
523- ( [ fieldName , field ] ) : FdrAPI . api . v1 . register . ObjectProperty => ( {
576+ const properties : FdrAPI . api . v1 . register . ObjectProperty [ ] = Object . entries ( fields ) . map (
577+ ( [ fieldName , field ] ) => ( {
524578 key : FdrAPI . PropertyKey ( fieldName ) ,
525579 valueType : this . convertOutputType ( field . type ) ,
526580 description : field . description ?? undefined ,
527581 availability : undefined ,
528- propertyAccess : undefined
582+ propertyAccess : undefined ,
583+ arguments : field . args . length > 0 ? field . args . map ( ( arg ) => this . convertArgument ( arg ) ) : undefined
529584 } )
530585 ) ;
531586
@@ -581,13 +636,14 @@ export class GraphQLConverter {
581636
582637 private convertInterfaceAsObject ( type : GraphQLInterfaceType ) : FdrAPI . api . v1 . register . TypeShape {
583638 const fields = type . getFields ( ) ;
584- const properties = Object . entries ( fields ) . map (
585- ( [ fieldName , field ] ) : FdrAPI . api . v1 . register . ObjectProperty => ( {
639+ const properties : FdrAPI . api . v1 . register . ObjectProperty [ ] = Object . entries ( fields ) . map (
640+ ( [ fieldName , field ] ) => ( {
586641 key : FdrAPI . PropertyKey ( fieldName ) ,
587642 valueType : this . convertOutputType ( field . type ) ,
588643 description : field . description ?? undefined ,
589644 availability : undefined ,
590- propertyAccess : undefined
645+ propertyAccess : undefined ,
646+ arguments : field . args . length > 0 ? field . args . map ( ( arg ) => this . convertArgument ( arg ) ) : undefined
591647 } )
592648 ) ;
593649
@@ -601,8 +657,8 @@ export class GraphQLConverter {
601657
602658 private convertInputObjectTypeDefinition ( type : GraphQLInputObjectType ) : FdrAPI . api . v1 . register . TypeShape {
603659 const fields = type . getFields ( ) ;
604- const properties = Object . entries ( fields ) . map (
605- ( [ fieldName , field ] ) : FdrAPI . api . v1 . register . ObjectProperty => ( {
660+ const properties : FdrAPI . api . v1 . register . ObjectProperty [ ] = Object . entries ( fields ) . map (
661+ ( [ fieldName , field ] ) => ( {
606662 key : FdrAPI . PropertyKey ( fieldName ) ,
607663 valueType : this . convertInputType ( field . type ) ,
608664 description : field . description ?? undefined ,
0 commit comments