@@ -701,7 +701,6 @@ export async function generateSessionEvents(schemaPath?: string): Promise<void>
701701// RPC TYPES
702702// ══════════════════════════════════════════════════════════════════════════════
703703
704- let emittedRpcClasses = new Set < string > ( ) ;
705704let emittedRpcClassSchemas = new Map < string , string > ( ) ;
706705let experimentalRpcTypes = new Set < string > ( ) ;
707706let rpcKnownTypes = new Map < string , string > ( ) ;
@@ -734,20 +733,6 @@ function stableStringify(value: unknown): string {
734733 return JSON . stringify ( value ) ;
735734}
736735
737- function chooseRpcClassName ( preferredName : string , fallbackName : string , schema : JSONSchema7 ) : string {
738- const schemaKey = stableStringify ( schema ) ;
739- const existingPreferred = emittedRpcClassSchemas . get ( preferredName ) ;
740- if ( ! existingPreferred || existingPreferred === schemaKey ) return preferredName ;
741-
742- let candidate = fallbackName ;
743- let suffix = 2 ;
744- while ( true ) {
745- const existing = emittedRpcClassSchemas . get ( candidate ) ;
746- if ( ! existing || existing === schemaKey ) return candidate ;
747- candidate = `${ fallbackName } ${ suffix ++ } ` ;
748- }
749- }
750-
751736function resolveRpcType ( schema : JSONSchema7 , isRequired : boolean , parentClassName : string , propName : string , classes : string [ ] ) : string {
752737 // Handle anyOf: [T, null] → T? (nullable typed property)
753738 if ( schema . anyOf ) {
@@ -770,32 +755,39 @@ function resolveRpcType(schema: JSONSchema7, isRequired: boolean, parentClassNam
770755 if ( schema . type === "array" && schema . items ) {
771756 const items = schema . items as JSONSchema7 ;
772757 if ( items . type === "object" && items . properties ) {
773- const defaultName = ( items . title as string ) ?? singularPascal ( propName ) ;
774- const contextualName = `${ parentClassName } ${ defaultName } ` ;
775- const itemClass = chooseRpcClassName ( defaultName , contextualName , items ) ;
776- if ( ! emittedRpcClasses . has ( itemClass ) ) classes . push ( emitRpcClass ( itemClass , items , "public" , classes ) ) ;
777- return isRequired ? `IList<${ itemClass } >` : `IList<${ itemClass } >?` ;
758+ const itemClass = ( items . title as string ) ?? singularPascal ( propName ) ;
759+ classes . push ( emitRpcClass ( itemClass , items , "public" , classes ) ) ;
760+ return isRequired ? `List<${ itemClass } >` : `List<${ itemClass } >?` ;
778761 }
779762 const itemType = schemaTypeToCSharp ( items , true , rpcKnownTypes ) ;
780- return isRequired ? `IList <${ itemType } >` : `IList <${ itemType } >?` ;
763+ return isRequired ? `List <${ itemType } >` : `List <${ itemType } >?` ;
781764 }
782765 if ( schema . type === "object" && schema . additionalProperties && typeof schema . additionalProperties === "object" ) {
783766 const vs = schema . additionalProperties as JSONSchema7 ;
784767 if ( vs . type === "object" && vs . properties ) {
785768 const valClass = `${ parentClassName } ${ propName } Value` ;
786769 classes . push ( emitRpcClass ( valClass , vs , "public" , classes ) ) ;
787- return isRequired ? `IDictionary <string, ${ valClass } >` : `IDictionary <string, ${ valClass } >?` ;
770+ return isRequired ? `Dictionary <string, ${ valClass } >` : `Dictionary <string, ${ valClass } >?` ;
788771 }
789772 const valueType = schemaTypeToCSharp ( vs , true , rpcKnownTypes ) ;
790- return isRequired ? `IDictionary <string, ${ valueType } >` : `IDictionary <string, ${ valueType } >?` ;
773+ return isRequired ? `Dictionary <string, ${ valueType } >` : `Dictionary <string, ${ valueType } >?` ;
791774 }
792775 return schemaTypeToCSharp ( schema , isRequired , rpcKnownTypes ) ;
793776}
794777
795778function emitRpcClass ( className : string , schema : JSONSchema7 , visibility : "public" | "internal" , extraClasses : string [ ] ) : string {
796- if ( emittedRpcClasses . has ( className ) ) return "" ;
797- emittedRpcClasses . add ( className ) ;
798- emittedRpcClassSchemas . set ( className , stableStringify ( schema ) ) ;
779+ const schemaKey = stableStringify ( schema ) ;
780+ const existingSchema = emittedRpcClassSchemas . get ( className ) ;
781+ if ( existingSchema ) {
782+ if ( existingSchema !== schemaKey ) {
783+ throw new Error (
784+ `Conflicting RPC class name "${ className } " for different schemas. Add a schema title/withTypeName to disambiguate.`
785+ ) ;
786+ }
787+ return "" ;
788+ }
789+
790+ emittedRpcClassSchemas . set ( className , schemaKey ) ;
799791
800792 const requiredSet = new Set ( schema . required || [ ] ) ;
801793 const lines : string [ ] = [ ] ;
@@ -824,12 +816,9 @@ function emitRpcClass(className: string, schema: JSONSchema7, visibility: "publi
824816 if ( isReq && ! csharpType . endsWith ( "?" ) ) {
825817 if ( csharpType === "string" ) defaultVal = " = string.Empty;" ;
826818 else if ( csharpType === "object" ) defaultVal = " = null!;" ;
827- else if ( csharpType . startsWith ( "IList <" ) ) {
819+ else if ( csharpType . startsWith ( "List<" ) || csharpType . startsWith ( "Dictionary <") ) {
828820 propAccessors = "{ get => field ??= []; set; }" ;
829- } else if ( csharpType . startsWith ( "IDictionary<" ) ) {
830- const concreteType = csharpType . replace ( "IDictionary<" , "Dictionary<" ) ;
831- propAccessors = `{ get => field ??= new ${ concreteType } (); set; }` ;
832- } else if ( emittedRpcClasses . has ( csharpType ) ) {
821+ } else if ( emittedRpcClassSchemas . has ( csharpType ) ) {
833822 propAccessors = "{ get => field ??= new(); set; }" ;
834823 }
835824 }
@@ -1200,7 +1189,6 @@ function emitClientSessionApiRegistration(clientSchema: Record<string, unknown>,
12001189}
12011190
12021191function generateRpcCode ( schema : ApiSchema ) : string {
1203- emittedRpcClasses . clear ( ) ;
12041192 emittedRpcClassSchemas . clear ( ) ;
12051193 experimentalRpcTypes . clear ( ) ;
12061194 rpcKnownTypes . clear ( ) ;
@@ -1246,7 +1234,7 @@ internal static class Diagnostics
12461234 if ( clientSessionParts . length > 0 ) lines . push ( ...clientSessionParts , "" ) ;
12471235
12481236 // Add JsonSerializerContext for AOT/trimming support
1249- const typeNames = [ ...emittedRpcClasses ] . sort ( ) ;
1237+ const typeNames = [ ...emittedRpcClassSchemas . keys ( ) ] . sort ( ) ;
12501238 if ( typeNames . length > 0 ) {
12511239 lines . push ( `[JsonSourceGenerationOptions(` ) ;
12521240 lines . push ( ` JsonSerializerDefaults.Web,` ) ;
0 commit comments