@@ -596,6 +596,7 @@ export async function generateSessionEvents(schemaPath?: string): Promise<void>
596596// ══════════════════════════════════════════════════════════════════════════════
597597
598598let emittedRpcClasses = new Set < string > ( ) ;
599+ let emittedRpcClassSchemas = new Map < string , string > ( ) ;
599600let experimentalRpcTypes = new Set < string > ( ) ;
600601let rpcKnownTypes = new Map < string , string > ( ) ;
601602let rpcEnumOutput : string [ ] = [ ] ;
@@ -616,6 +617,31 @@ function paramsTypeName(rpcMethod: string): string {
616617 return `${ typeToClassName ( rpcMethod ) } Params` ;
617618}
618619
620+ function stableStringify ( value : unknown ) : string {
621+ if ( Array . isArray ( value ) ) {
622+ return `[${ value . map ( ( item ) => stableStringify ( item ) ) . join ( "," ) } ]` ;
623+ }
624+ if ( value && typeof value === "object" ) {
625+ const entries = Object . entries ( value as Record < string , unknown > ) . sort ( ( [ a ] , [ b ] ) => a . localeCompare ( b ) ) ;
626+ return `{${ entries . map ( ( [ key , entryValue ] ) => `${ JSON . stringify ( key ) } :${ stableStringify ( entryValue ) } ` ) . join ( "," ) } }` ;
627+ }
628+ return JSON . stringify ( value ) ;
629+ }
630+
631+ function chooseRpcClassName ( preferredName : string , fallbackName : string , schema : JSONSchema7 ) : string {
632+ const schemaKey = stableStringify ( schema ) ;
633+ const existingPreferred = emittedRpcClassSchemas . get ( preferredName ) ;
634+ if ( ! existingPreferred || existingPreferred === schemaKey ) return preferredName ;
635+
636+ let candidate = fallbackName ;
637+ let suffix = 2 ;
638+ while ( true ) {
639+ const existing = emittedRpcClassSchemas . get ( candidate ) ;
640+ if ( ! existing || existing === schemaKey ) return candidate ;
641+ candidate = `${ fallbackName } ${ suffix ++ } ` ;
642+ }
643+ }
644+
619645function resolveRpcType ( schema : JSONSchema7 , isRequired : boolean , parentClassName : string , propName : string , classes : string [ ] ) : string {
620646 // Handle anyOf: [T, null] → T? (nullable typed property)
621647 if ( schema . anyOf ) {
@@ -638,7 +664,9 @@ function resolveRpcType(schema: JSONSchema7, isRequired: boolean, parentClassNam
638664 if ( schema . type === "array" && schema . items ) {
639665 const items = schema . items as JSONSchema7 ;
640666 if ( items . type === "object" && items . properties ) {
641- const itemClass = singularPascal ( propName ) ;
667+ const defaultName = ( items . title as string ) ?? singularPascal ( propName ) ;
668+ const contextualName = `${ parentClassName } ${ defaultName } ` ;
669+ const itemClass = chooseRpcClassName ( defaultName , contextualName , items ) ;
642670 if ( ! emittedRpcClasses . has ( itemClass ) ) classes . push ( emitRpcClass ( itemClass , items , "public" , classes ) ) ;
643671 return isRequired ? `List<${ itemClass } >` : `List<${ itemClass } >?` ;
644672 }
@@ -661,6 +689,7 @@ function resolveRpcType(schema: JSONSchema7, isRequired: boolean, parentClassNam
661689function emitRpcClass ( className : string , schema : JSONSchema7 , visibility : "public" | "internal" , extraClasses : string [ ] ) : string {
662690 if ( emittedRpcClasses . has ( className ) ) return "" ;
663691 emittedRpcClasses . add ( className ) ;
692+ emittedRpcClassSchemas . set ( className , stableStringify ( schema ) ) ;
664693
665694 const requiredSet = new Set ( schema . required || [ ] ) ;
666695 const lines : string [ ] = [ ] ;
@@ -1061,6 +1090,7 @@ function emitClientSessionApiRegistration(clientSchema: Record<string, unknown>,
10611090
10621091function generateRpcCode ( schema : ApiSchema ) : string {
10631092 emittedRpcClasses . clear ( ) ;
1093+ emittedRpcClassSchemas . clear ( ) ;
10641094 experimentalRpcTypes . clear ( ) ;
10651095 rpcKnownTypes . clear ( ) ;
10661096 rpcEnumOutput = [ ] ;
0 commit comments