@@ -162,9 +162,9 @@ function isGeneratedOrVendor(filePath: string): boolean {
162162}
163163
164164function inferTestRelations ( fileRecords : Array < { path : string ; imports : string [ ] } > , edges : Array < { from : string ; resolved ?: string } > ) {
165- const sourceFiles = new Set ( fileRecords . map ( ( record ) => record . path ) . filter ( ( filePath ) => ! isTestPath ( filePath ) ) ) ;
165+ const sourceFiles = new Set ( fileRecords . map ( ( record ) => record . path ) . filter ( ( filePath ) => ! isTestPath ( filePath ) && ! isInitFile ( filePath ) ) ) ;
166166 const relations : Array < { test : string ; source : string ; reason : string } > = [ ] ;
167- for ( const test of fileRecords . filter ( ( record ) => isTestPath ( record . path ) ) ) {
167+ for ( const test of fileRecords . filter ( ( record ) => isTestPath ( record . path ) && ! isInitFile ( record . path ) ) ) {
168168 for ( const edge of edges . filter ( ( item ) => item . from === test . path && item . resolved && sourceFiles . has ( item . resolved ) ) ) {
169169 relations . push ( { test : test . path , source : edge . resolved ! , reason : 'import' } ) ;
170170 }
@@ -187,6 +187,10 @@ function isTestPath(filePath: string): boolean {
187187 return / ( ^ | \/ ) ( t e s t | t e s t s | s p e c | _ _ t e s t s _ _ ) ( \/ | $ ) | \. ( t e s t | s p e c ) \. | ( ^ | \/ ) t e s t _ [ ^ / ] + \. p y $ | ( ^ | \/ ) [ ^ / ] + _ t e s t \. p y $ / . test ( filePath ) ;
188188}
189189
190+ function isInitFile ( filePath : string ) : boolean {
191+ return / ( ^ | \/ ) _ _ i n i t _ _ \. p y $ / . test ( filePath ) ;
192+ }
193+
190194function buildArchitectureSummary ( fileRecords : Array < { path : string ; imports : string [ ] ; symbols : ReturnType < typeof flattenSymbols > } > , edges : Array < { from : string ; source : string ; resolved ?: string } > , testRelations : Array < { test : string ; source : string ; reason : string } > ) {
191195 const files = fileRecords . map ( ( record ) => ( {
192196 path : record . path ,
@@ -199,20 +203,26 @@ function buildArchitectureSummary(fileRecords: Array<{ path: string; imports: st
199203 const handler = record . symbols
200204 . filter ( ( symbol ) => [ 'function' , 'method' ] . includes ( symbol . kind ) && symbol . startLine > route . startLine )
201205 . sort ( ( a , b ) => a . startLine - b . startLine ) [ 0 ] ;
202- return { file : record . path , route : route . name , line : route . startLine , handler : handler ?. qualifiedName } ;
206+ return { file : record . path , route : route . name , line : route . startLine , handler : route . source ?? handler ?. qualifiedName } ;
203207 } ) ) ;
204208
205- const dependencies = uniqueBy ( fileRecords . flatMap ( ( record ) => record . symbols . filter ( ( symbol ) => symbol . kind === 'dependency' ) . map ( ( symbol ) => ( { file : record . path , key : symbol . name , line : symbol . startLine } ) ) ) , ( item ) => ` ${ item . file } : ${ item . key } : ${ item . line } ` ) ;
209+ const dependencies = buildAppDependencyGraph ( fileRecords ) ;
206210 const tables = uniqueBy ( fileRecords . flatMap ( ( record ) => record . symbols . filter ( ( symbol ) => symbol . kind === 'table' ) . map ( ( symbol ) => ( { file : record . path , name : symbol . name , line : symbol . startLine } ) ) ) , ( item ) => `${ item . file } :${ item . name } ` ) ;
207211 const localImports = edges . filter ( ( edge ) => edge . resolved ) . map ( ( edge ) => ( { from : edge . from , to : edge . resolved ! , source : edge . source } ) ) ;
208212 const serviceEdges = localImports . filter ( ( edge ) => / s e r v i c e | a p i | r o u t e | h a n d l e r | v i e w | d a o | t a s k | w o r k e r | c r e a t o r / i. test ( `${ edge . from } ${ edge . to } ${ edge . source } ` ) ) ;
209- return { files, routes, dependencies, tables, serviceEdges : serviceEdges . slice ( 0 , 30 ) , testRelations } ;
213+ const rpc = buildRpcSummary ( fileRecords , dependencies ) ;
214+ return { files, routes, rpc, dependencies, tables, serviceEdges : serviceEdges . slice ( 0 , 20 ) , testRelations } ;
210215}
211216
212217function renderArchitectureSummary ( summary : ReturnType < typeof buildArchitectureSummary > ) : string {
213218 const sections : string [ ] = [ 'Architecture summary' ] ;
214219 if ( summary . routes . length ) sections . push ( `Routes -> handlers:\n${ summary . routes . map ( ( item ) => ` ${ item . route } -> ${ item . handler ?? 'unknown' } (${ item . file } :${ item . line } )` ) . join ( '\n' ) } ` ) ;
215- if ( summary . dependencies . length ) sections . push ( `App dependencies:\n${ summary . dependencies . map ( ( item ) => ` app["${ item . key } "] (${ item . file } :${ item . line } )` ) . join ( '\n' ) } ` ) ;
220+ if ( summary . rpc . length ) sections . push ( `RPC summary:\n${ summary . rpc . map ( ( item ) => ` ${ item . name } (${ item . file } :${ item . line } )${ item . dependencies . length ? ` uses app[${ item . dependencies . map ( ( key ) => `"${ key } "` ) . join ( ', ' ) } ]` : '' } ` ) . join ( '\n' ) } ` ) ;
221+ if ( summary . dependencies . length ) sections . push ( `App dependency graph:\n${ summary . dependencies . map ( ( item ) => {
222+ const writes = item . writes . length ? ` created: ${ item . writes . map ( ( usage ) => `${ usage . file } :${ usage . line } ` ) . join ( ', ' ) } ` : '' ;
223+ const reads = item . reads . length ? ` read: ${ item . reads . slice ( 0 , 6 ) . map ( ( usage ) => `${ usage . file } :${ usage . line } ` ) . join ( ', ' ) } ` : '' ;
224+ return ` app["${ item . key } "]${ writes } ${ reads } ` ;
225+ } ) . join ( '\n' ) } `) ;
216226 if ( summary . tables . length ) sections . push ( `SQLAlchemy tables:\n${ summary . tables . map ( ( item ) => ` ${ item . name } (${ item . file } :${ item . line } )` ) . join ( '\n' ) } ` ) ;
217227 if ( summary . serviceEdges . length ) sections . push ( `Local dependency flow:\n${ summary . serviceEdges . map ( ( item ) => ` ${ item . from } -> ${ item . to } ` ) . join ( '\n' ) } ` ) ;
218228 if ( summary . files . length ) sections . push ( `Key files:\n${ summary . files . slice ( 0 , 30 ) . map ( ( item ) => {
@@ -227,6 +237,30 @@ function renderArchitectureSummary(summary: ReturnType<typeof buildArchitectureS
227237 return sections . join ( '\n\n' ) ;
228238}
229239
240+ function buildAppDependencyGraph ( fileRecords : Array < { path : string ; symbols : ReturnType < typeof flattenSymbols > } > ) {
241+ const byKey = new Map < string , { key : string ; writes : Array < { file : string ; line : number } > ; reads : Array < { file : string ; line : number } > } > ( ) ;
242+ for ( const record of fileRecords ) {
243+ for ( const symbol of record . symbols . filter ( ( item ) => item . kind === 'dependency' ) ) {
244+ const entry = byKey . get ( symbol . name ) ?? { key : symbol . name , writes : [ ] , reads : [ ] } ;
245+ const usage = { file : record . path , line : symbol . startLine } ;
246+ if ( / \b (?: a p p | r e q u e s t \. a p p ) \[ [ ' " ] [ ^ ' " ] + [ ' " ] \] \s * = / . test ( symbol . signature ) ) entry . writes . push ( usage ) ;
247+ else entry . reads . push ( usage ) ;
248+ byKey . set ( symbol . name , entry ) ;
249+ }
250+ }
251+ return [ ...byKey . values ( ) ] . sort ( ( a , b ) => Number ( b . writes . length > 0 ) - Number ( a . writes . length > 0 ) || a . key . localeCompare ( b . key ) ) ;
252+ }
253+
254+ function buildRpcSummary ( fileRecords : Array < { path : string ; symbols : ReturnType < typeof flattenSymbols > } > , dependencies : ReturnType < typeof buildAppDependencyGraph > ) {
255+ const dependencyKeys = new Set ( dependencies . map ( ( dependency ) => dependency . key ) ) ;
256+ return fileRecords . flatMap ( ( record ) => record . symbols . filter ( ( symbol ) => [ 'function' , 'method' ] . includes ( symbol . kind ) && symbol . name . startsWith ( 'rpc_' ) ) . map ( ( symbol ) => {
257+ const deps = record . symbols
258+ . filter ( ( item ) => item . kind === 'dependency' && item . startLine >= symbol . startLine && item . endLine <= symbol . endLine && dependencyKeys . has ( item . name ) )
259+ . map ( ( item ) => item . name ) ;
260+ return { file : record . path , name : symbol . qualifiedName , line : symbol . startLine , dependencies : Array . from ( new Set ( deps ) ) } ;
261+ } ) ) ;
262+ }
263+
230264function uniqueBy < T > ( items : T [ ] , key : ( item : T ) => string ) : T [ ] {
231265 const seen = new Set < string > ( ) ;
232266 return items . filter ( ( item ) => {
0 commit comments