@wtdlee/repomap 0.9.1 → 0.10.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -50,6 +50,7 @@ Powered by [SWC](https://swc.rs/) for blazing-fast AST parsing. Analyze large-sc
50
50
  - **Field details** - View all fields with types and arguments
51
51
  - **Usage tracking** - See where operations are used in components
52
52
  - **Component integration** - Track GraphQL usage through component dependencies
53
+ - **Custom wrapper hooks** - Configurable hook presets/patterns (Apollo/urql/Relay/custom)
53
54
 
54
55
  ### 📊 Data Flow
55
56
  - **Visual diagrams** - Mermaid-generated flowcharts
@@ -425,6 +426,7 @@ export default config;
425
426
  - Auto-generated markdown documentation
426
427
  - Navigation sidebar
427
428
  - Syntax-highlighted code blocks
429
+ - Coverage metrics (parse failures, codegen detection) to prevent silent omissions
428
430
 
429
431
  ## Supported Frameworks
430
432
 
@@ -466,6 +468,7 @@ interface AnalysisResult {
466
468
  timestamp: string;
467
469
  version: string;
468
470
  commitHash: string;
471
+ coverage?: CoverageMetrics;
469
472
  pages: PageInfo[];
470
473
  graphqlOperations: GraphQLOperation[];
471
474
  apiCalls: APICall[];
@@ -476,6 +479,15 @@ interface AnalysisResult {
476
479
  crossRepoLinks: CrossRepoLink[];
477
480
  }
478
481
 
482
+ interface CoverageMetrics {
483
+ tsFilesScanned: number;
484
+ tsParseFailures: number;
485
+ graphqlParseFailures: number;
486
+ codegenFilesDetected: number;
487
+ codegenFilesParsed: number;
488
+ codegenExportsFound: number;
489
+ }
490
+
479
491
  // Report
480
492
  interface DocumentationReport {
481
493
  generatedAt: string;
@@ -1,5 +1,5 @@
1
- import { B as BaseAnalyzer } from '../dataflow-analyzer-CJ2T0cGS.js';
2
- export { D as DataFlowAnalyzer, G as GraphQLAnalyzer, P as PagesAnalyzer } from '../dataflow-analyzer-CJ2T0cGS.js';
1
+ import { B as BaseAnalyzer } from '../dataflow-analyzer-DIUsRpvv.js';
2
+ export { D as DataFlowAnalyzer, G as GraphQLAnalyzer, P as PagesAnalyzer } from '../dataflow-analyzer-DIUsRpvv.js';
3
3
  import { Module, CallExpression } from '@swc/core';
4
4
  import { DataFetchingInfo, RepositoryConfig, AnalysisResult } from '../types.js';
5
5
 
@@ -33,7 +33,7 @@ declare const HOOK_TYPE_MAP: Record<string, DataFetchingInfo['type']>;
33
33
  /**
34
34
  * Keywords that indicate GraphQL usage in a file
35
35
  */
36
- declare const GRAPHQL_INDICATORS: readonly ["Document", "useQuery", "useMutation", "useLazyQuery", "useSuspenseQuery", "useBackgroundQuery", "useSubscription", "Query", "Mutation", "gql", "graphql", "GET_", "FETCH_", "SEARCH_", "CREATE_", "UPDATE_", "DELETE_", "SUBSCRIBE_", "@apollo", "ApolloClient"];
36
+ declare const GRAPHQL_INDICATORS: readonly ["Document", "useQuery", "useMutation", "useLazyQuery", "useSuspenseQuery", "useBackgroundQuery", "useSubscription", "Query", "Mutation", "gql", "graphql", "GET_", "FETCH_", "SEARCH_", "CREATE_", "UPDATE_", "DELETE_", "SUBSCRIBE_", "@apollo", "ApolloClient", "@urql", "urql", "react-relay", "Relay"];
37
37
  /**
38
38
  * Context extracted from a file for GraphQL operation resolution
39
39
  */
@@ -62,23 +62,23 @@ interface ExtractedGraphQLOperation {
62
62
  /**
63
63
  * Check if a hook name is a GraphQL query hook
64
64
  */
65
- declare function isQueryHook(hookName: string): boolean;
65
+ declare function isQueryHook(hookName: string, extraHookPatterns?: string[]): boolean;
66
66
  /**
67
67
  * Check if a hook name is a GraphQL mutation hook
68
68
  */
69
- declare function isMutationHook(hookName: string): boolean;
69
+ declare function isMutationHook(hookName: string, extraHookPatterns?: string[]): boolean;
70
70
  /**
71
71
  * Check if a hook name is a GraphQL subscription hook
72
72
  */
73
- declare function isSubscriptionHook(hookName: string): boolean;
73
+ declare function isSubscriptionHook(hookName: string, extraHookPatterns?: string[]): boolean;
74
74
  /**
75
75
  * Check if a hook name is any GraphQL hook
76
76
  */
77
- declare function isGraphQLHook(hookName: string): boolean;
77
+ declare function isGraphQLHook(hookName: string, extraHookPatterns?: string[]): boolean;
78
78
  /**
79
79
  * Get the data fetching type for a hook
80
80
  */
81
- declare function getHookType(hookName: string): DataFetchingInfo['type'];
81
+ declare function getHookType(hookName: string, extraHookPatterns?: string[]): DataFetchingInfo['type'];
82
82
  /**
83
83
  * Check if content has any GraphQL indicators
84
84
  */
@@ -1 +1 @@
1
- export{e as ALL_GRAPHQL_HOOKS,a as BaseAnalyzer,A as DataFlowAnalyzer,g as GRAPHQL_INDICATORS,c as GRAPHQL_MUTATION_HOOKS,d as GRAPHQL_OTHER_HOOKS,b as GRAPHQL_QUERY_HOOKS,z as GraphQLAnalyzer,f as HOOK_TYPE_MAP,y as PagesAnalyzer,B as RestApiAnalyzer,n as cleanOperationName,r as extractGraphQLContext,w as extractGraphQLOperationsFromFile,s as extractOperationNameFromGqlCall,t as extractOperationNameFromTemplate,p as getCalleeName,x as getHookInfoString,l as getHookType,u as hasGraphQLArgument,m as hasGraphQLIndicators,k as isGraphQLHook,i as isMutationHook,h as isQueryHook,j as isSubscriptionHook,o as parseToAst,v as resolveOperationName,q as traverseAst}from'../chunk-QDVE7MT3.js';
1
+ export{f as ALL_GRAPHQL_HOOKS,a as BaseAnalyzer,B as DataFlowAnalyzer,h as GRAPHQL_INDICATORS,d as GRAPHQL_MUTATION_HOOKS,e as GRAPHQL_OTHER_HOOKS,c as GRAPHQL_QUERY_HOOKS,A as GraphQLAnalyzer,g as HOOK_TYPE_MAP,z as PagesAnalyzer,C as RestApiAnalyzer,o as cleanOperationName,s as extractGraphQLContext,x as extractGraphQLOperationsFromFile,t as extractOperationNameFromGqlCall,u as extractOperationNameFromTemplate,q as getCalleeName,y as getHookInfoString,m as getHookType,v as hasGraphQLArgument,n as hasGraphQLIndicators,l as isGraphQLHook,j as isMutationHook,i as isQueryHook,k as isSubscriptionHook,p as parseToAst,w as resolveOperationName,r as traverseAst}from'../chunk-YO6EB4OI.js';
@@ -1,12 +1,12 @@
1
- import {a as a$2}from'./chunk-NQMJ3QRX.js';import {a as a$3}from'./chunk-VV3A3UE3.js';import {B,A,z,y}from'./chunk-QDVE7MT3.js';import {a,b}from'./chunk-2XZSFAJF.js';import {a as a$1}from'./chunk-WZAAA7DS.js';import {k}from'./chunk-H7VVRHQZ.js';import {simpleGit}from'simple-git';import*as T from'fs/promises';import*as R from'path';import {parseSync}from'@swc/core';import me from'express';import {Server}from'socket.io';import*as fe from'http';import {marked}from'marked';import*as ce from'net';var U=class{config;mermaidGenerator;markdownGenerator;constructor(t){this.config=t,this.mermaidGenerator=new a,this.markdownGenerator=new b;}async generate(){let t=[];for(let w of this.config.repositories)try{let g=await this.analyzeRepository(w);t.push(g);}catch(g){console.error(`\u274C ${w.name}: ${g.message}`);}let r=this.analyzeCrossRepo(t),e=t.map(w=>w.analysis),n=this.extractCrossRepoLinks(e),s=this.mermaidGenerator.generateAll(e,n),i={generatedAt:new Date().toISOString(),repositories:t,crossRepoAnalysis:r,diagrams:s};return await this.writeDocumentation(i),i}async analyzeRepository(t){let{version:r,commitHash:e}=await this.getRepoInfo(t),n=t.analyzers.map(y=>this.createAnalyzer(y,t)).filter(y=>y!==null),s=Date.now(),i=await Promise.all(n.map(y=>y.analyze())),w=((Date.now()-s)/1e3).toFixed(1);console.log(` Analyzed ${t.displayName} in ${w}s`);let g=this.mergeAnalysisResults(i,t.name,r,e);await this.enrichPagesWithHookGraphQL(g,t.path);let k={totalPages:g.pages.length,totalComponents:g.components.length,totalGraphQLOperations:g.graphqlOperations.length,totalDataFlows:g.dataFlows.length,authRequiredPages:g.pages.filter(y=>y.authentication.required).length,publicPages:g.pages.filter(y=>!y.authentication.required).length};return {name:t.name,displayName:t.displayName,version:r,commitHash:e,analysis:g,summary:k}}async getRepoInfo(t){try{let n=(await simpleGit(t.path).log({n:1})).latest?.hash||"unknown",s="unknown";try{let i=R.join(t.path,"package.json");s=JSON.parse(await T.readFile(i,"utf-8")).version||"unknown";}catch{}return {version:s,commitHash:n}}catch{return {version:"unknown",commitHash:"unknown"}}}createAnalyzer(t,r){switch(t){case "pages":if(r.type==="nextjs"||r.type==="rails"||r.type==="generic")return new y(r);break;case "graphql":return new z(r);case "dataflow":case "components":return new A(r);case "rest-api":case "api":return new B(r)}return null}mergeAnalysisResults(t,r,e,n){let s={repository:r,timestamp:new Date().toISOString(),version:e,commitHash:n,pages:[],graphqlOperations:[],apiCalls:[],components:[],dataFlows:[],apiEndpoints:[],models:[],crossRepoLinks:[]};for(let i of t)i.pages&&s.pages.push(...i.pages),i.graphqlOperations&&s.graphqlOperations.push(...i.graphqlOperations),i.apiCalls&&s.apiCalls.push(...i.apiCalls),i.components&&s.components.push(...i.components),i.dataFlows&&s.dataFlows.push(...i.dataFlows),i.apiEndpoints&&s.apiEndpoints.push(...i.apiEndpoints),i.models&&s.models.push(...i.models),i.crossRepoLinks&&s.crossRepoLinks.push(...i.crossRepoLinks);return s}analyzeCrossRepo(t){let r=[],e=[],n=[],s=[],i=new Map;for(let k of t)for(let y of k.analysis.graphqlOperations){let $=i.get(y.name)||[];$.push(k.name),i.set(y.name,$);}for(let[k,y]of i)y.length>1&&r.push(k);let w=t.filter(k=>k.analysis.pages.length>0),g=t.filter(k=>k.analysis.apiEndpoints.length>0);for(let k of w)for(let y of g)for(let $ of y.analysis.apiEndpoints)e.push({frontend:k.name,backend:y.name,endpoint:$.path,operations:k.analysis.graphqlOperations.filter(A=>A.usedIn.length>0).map(A=>A.name)});return {sharedTypes:r,apiConnections:e,navigationFlows:n,dataFlowAcrossRepos:s}}extractCrossRepoLinks(t){let r=[],e=new Map;for(let n of t)for(let s of n.graphqlOperations){let i=e.get(s.name)||[];i.push(n),e.set(s.name,i);}for(let[n,s]of e)s.length>1&&r.push({sourceRepo:s[0].repository,sourcePath:`graphql/${n}`,targetRepo:s[1].repository,targetPath:`graphql/${n}`,linkType:"graphql-operation",description:`Shared GraphQL operation: ${n}`});return r}async enrichPagesWithHookGraphQL(t,r){let e=t.graphqlOperations.filter(a=>a.type==="query"||a.type==="mutation"||a.type==="subscription"),n=/\.(ts|tsx|js|jsx)$/,s=a=>R.normalize(a).replace(/\\/g,"/"),i=(this.config.analysis?.include||["**/*.ts","**/*.tsx"]).map(String),w=(this.config.analysis?.exclude||[]).map(String),g=(await import('fast-glob')).default,k=await g(i,{cwd:r,ignore:["**/node_modules/**","**/.next/**","**/dist/**","**/build/**","**/coverage/**",...w],onlyFiles:true,unique:true,dot:false}),y=new Set(k.map(s)),$=new Map;for(let a of y){let o=a.replace(n,"");$.has(o)||$.set(o,a);}let A=new Set(["src/"]);for(let a of y){let o=a.indexOf("/src/");o!==-1&&A.add(a.slice(0,o+5));}let N=a=>{let o=s(a).replace(n,""),c=$.get(o);if(c)return c;let l=$.get(o+"/index");return l||null},he=async()=>{let a=["tsconfig.json","jsconfig.json"];for(let o of a)try{let c=await T.readFile(R.join(r,o),"utf-8"),p=JSON.parse(c)?.compilerOptions||{},f=typeof p.baseUrl=="string"?p.baseUrl:void 0,q=typeof p.paths=="object"&&p.paths?p.paths:void 0;return {baseUrl:f,paths:q}}catch{}return {}},{baseUrl:O,paths:X}=await he(),ge=a=>{if(!X)return [];let o=[];for(let[c,l]of Object.entries(X)){if(!c.includes("*")){a===c&&o.push(...l);continue}let[p,f]=c.split("*");if(!a.startsWith(p)||!a.endsWith(f))continue;let q=a.slice(p.length,a.length-f.length);for(let d of l)d.includes("*")?o.push(d.replace("*",q)):o.push(d);}return o},B=(a,o)=>{if(!o)return null;if(o.startsWith(".")){let l=R.dirname(a);return N(R.join(l,o))}if(o.startsWith("@/")){let l=o.replace("@/","");if(O){let p=N(R.join(O,l));if(p)return p}for(let p of A){let f=N(p+l);if(f)return f}return null}let c=ge(o);if(c.length>0)for(let l of c){let p=N(O?R.join(O,l):l);if(p)return p}if(O){let l=N(R.join(O,o));if(l)return l}return null},j=new Map,W=new Map,Z=async a=>{let o=s(a),c=W.get(o);if(c!==void 0)return c;try{let l=R.join(r,o),p=await T.readFile(l,"utf-8");return W.set(o,p),p}catch{return W.set(o,""),null}},ye=async a=>{let o=s(a),c=j.get(o);if(c)return c.map(m=>({spec:m,names:null}));let l=await Z(o);if(!l)return j.set(o,[]),[];let p;try{let m=o.endsWith(".ts")||o.endsWith(".tsx"),h=o.endsWith(".tsx")||o.endsWith(".jsx");p=parseSync(l,{syntax:m?"typescript":"ecmascript",tsx:h,jsx:h,comments:!1});}catch{return j.set(o,[]),[]}let f=new Set,q=[],d=(m,h)=>{typeof m!="string"||m.length===0||(f.add(m),q.push({spec:m,names:h}));},b=m=>{if(!m||typeof m!="object")return;let h=m,v=h;if(v.type==="ImportDeclaration"){if(!v.typeOnly){let x=v.source?.value,u=[],E=v.specifiers||[];for(let S of E){let C=S,I=C.type;if(I==="ImportDefaultSpecifier"||I==="ImportNamespaceSpecifier"){u=null;break}if(I==="ImportSpecifier"){let G=C.imported?.value,P=C.local?.value,z=G||P;z&&u.push(z);}}Array.isArray(u)&&u.length===0&&(u=null),d(x,u);}}else if(v.type==="ExportAllDeclaration")d(v.source?.value,null);else if(v.type==="ExportNamedDeclaration"){let x=v.source?.value,u=v.specifiers||[],E=[];for(let S of u){let C=S;if(C.type==="ExportSpecifier"){let G=C.exported?.value,P=C.orig?.value,z=G||P;z&&E.push(z);}}d(x,E.length>0?E:null);}else if(v.type==="CallExpression"){let x=v.callee||null;if(x?.type==="Identifier"&&x.value==="require"){let u=v.arguments?.[0]?.expression;u?.type==="StringLiteral"&&d(u.value,null);}if(x?.type==="Import"){let u=v.arguments?.[0]?.expression;u?.type==="StringLiteral"&&d(u.value,null);}}for(let x of Object.keys(h)){let u=h[x];if(Array.isArray(u))for(let E of u)b(E);else u&&typeof u=="object"&&b(u);}};b(p);let D=Array.from(f);return j.set(o,D),q.filter(m=>typeof m.spec=="string"&&m.spec.length>0)},Q=new Map,_=async a=>{let o=s(a),c=Q.get(o);if(c)return c;let l=await Z(o);if(!l){let m={named:new Map,stars:[],isBarrel:false};return Q.set(o,m),m}let p;try{let m=o.endsWith(".ts")||o.endsWith(".tsx"),h=o.endsWith(".tsx")||o.endsWith(".jsx");p=parseSync(l,{syntax:m?"typescript":"ecmascript",tsx:h,jsx:h,comments:!1});}catch{let m={named:new Map,stars:[],isBarrel:false};return Q.set(o,m),m}let f=new Map,q=[],d=true,b=p?.body;if(Array.isArray(b))for(let m of b){let h=m,v=h.type;if(v&&v!=="ImportDeclaration"){if(v==="ExportAllDeclaration"){let x=h.source?.value;typeof x=="string"&&q.push(x);continue}if(v==="ExportNamedDeclaration"){let x=h.source?.value,u=h.specifiers||[];if(typeof x=="string"&&Array.isArray(u))for(let E of u){let S=E;if(S.type!=="ExportSpecifier")continue;let I=S.exported?.value,G=S.orig?.value,P=I||G;P&&f.set(P,x);}continue}d=false;}}else d=false;let D={named:f,stars:q,isBarrel:d};return Q.set(o,D),D},V=async(a,o,c)=>{let l=s(a);if(c.has(l))return null;c.add(l);let p=await _(l),f=p.named.get(o);if(f)return B(l,f);for(let q of p.stars){let d=B(l,q);if(!d)continue;let b=await V(d,o,c);if(b)return b}return null},Y=new Map;for(let a of e){let o=new Set;a.filePath&&o.add(a.filePath);for(let c of a.usedIn||[])o.add(c);for(let c of o){let l=Y.get(c)||[];l.push({opName:a.name,opType:a.type}),Y.set(c,l);}}let ve=a=>{if(!a)return null;let o=[`src/pages/${a}`,`pages/${a}`,`src/app/${a}`,`app/${a}`,`frontend/src/pages/${a}`,`frontend/src/app/${a}`,`app/javascript/pages/${a}`,`app/javascript/app/${a}`];for(let p of o){if(y.has(p))return p;let f=N(p);if(f)return f}let c=[],l=[`/pages/${a}`,`/app/${a}`,`/${a}`];for(let p of y)for(let f of l)if(p.endsWith(f)&&!(f.startsWith("/pages/")&&!p.includes("/pages/"))&&!(f.startsWith("/app/")&&!p.includes("/app/"))){c.push(p);break}return c.length===0?null:(c.sort((p,f)=>p.length-f.length),c[0])},J=new Map,H=new Map;for(let a of t.pages){let o=ve(a.filePath);if(!o)continue;let c=new Map,l=new Set,p=[{f:o,depth:0}],f=30,q=2e4;for(;p.length>0;){let d=p.shift();if(!d)break;if(l.has(d.f))continue;if(l.add(d.f),l.size>=q)break;let b=Y.get(d.f);if(b)for(let m of b){let h=c.get(m.opName);(!h||d.depth<h.distance)&&c.set(m.opName,{opName:m.opName,opType:m.opType,sourceFile:d.f,distance:d.depth});}if(d.depth>=f)continue;let D=await ye(d.f);for(let m of D){let h=B(d.f,m.spec);if(h){if(l.has(h)||p.push({f:h,depth:d.depth+1}),Array.isArray(m.names)&&m.names.length>0){if((await _(h)).isBarrel)for(let x of m.names){let u=await V(h,x,new Set);u&&!l.has(u)&&p.push({f:u,depth:d.depth+2});}}else if(m.names===null){let v=await _(h);if(v.isBarrel){for(let x of v.stars){let u=B(h,x);u&&!l.has(u)&&p.push({f:u,depth:d.depth+2});}for(let x of v.named.values()){let u=B(h,x);u&&!l.has(u)&&p.push({f:u,depth:d.depth+2});}}}}}}J.set(a.path,{page:a,entryFile:o,bestByOp:c});for(let d of l)H.set(d,(H.get(d)||0)+1);}let we=J.size,xe=Array.from(H.values()).sort((a,o)=>a-o),be=((a,o)=>{if(a.length===0)return 0;let c=Math.min(a.length-1,Math.max(0,Math.floor(a.length*o)));return a[c]})(xe,.9),ke=Math.max(10,be),qe=Math.max(2,Math.floor(we*.05));for(let{page:a,bestByOp:o}of J.values()){let c=new Set((a.dataFetching||[]).map(l=>l.operationName?.replace(/^[→\->\s]+/,"")||""));for(let{opName:l,opType:p,sourceFile:f,distance:q}of o.values()){if(c.has(l))continue;c.add(l);let d=H.get(f)||0,b;q===0?b=void 0:d>=ke?b=`common:${f}`:q<=2||d<=qe?b=`close:${f}`:b=`indirect:${f}`,a.dataFetching.push({type:p==="mutation"?"useMutation":p==="subscription"?"useSubscription":"useQuery",operationName:l,source:b});}}}async writeDocumentation(t){let r=this.config.outputDir;await T.mkdir(r,{recursive:true});let e=this.markdownGenerator.generateDocumentation(t);for(let[s,i]of e){let w=R.join(r,s),g=R.dirname(w);await T.mkdir(g,{recursive:true}),await T.writeFile(w,i,"utf-8");}let n=R.join(r,"report.json");await T.writeFile(n,JSON.stringify(t,null,2),"utf-8");}};function Me(L){return new Promise(t=>{let r=ce.createServer();r.once("error",e=>{e.code,t(false);}),r.once("listening",()=>{r.close(),t(true);}),r.listen(L);})}async function de(L,t=10){for(let r=0;r<t;r++){let e=L+r;if(await Me(e))return e}throw new Error(`No available port found between ${L} and ${L+t-1}`)}var ue=class{config;port;app;server;io;engine;currentReport=null;envResult=null;railsAnalysis=null;constructor(t,r=3030){this.config=t,this.port=r,this.app=me(),this.server=fe.createServer(this.app),this.io=new Server(this.server),this.engine=new U(t),this.setupRoutes(),this.setupSocketIO();}setupRoutes(){this.app.use("/assets",me.static(R.join(this.config.outputDir,"assets"))),["common.css","page-map.css","docs.css","rails-map.css"].forEach(e=>{this.app.get(`/${e}`,async(n,s)=>{let i=[R.join(R.dirname(new URL(import.meta.url).pathname),"generators","assets",e),R.join(R.dirname(new URL(import.meta.url).pathname),"..","generators","assets",e),R.join(process.cwd(),"dist","generators","assets",e),R.join(process.cwd(),"src","generators","assets",e)];for(let w of i)try{let g=await T.readFile(w,"utf-8");s.type("text/css").send(g);return}catch{}s.status(404).send("CSS not found");});}),["favicon.ico","favicon.svg","favicon-96x96.png","apple-touch-icon.png","site.webmanifest","web-app-manifest-192x192.png","web-app-manifest-512x512.png"].forEach(e=>{(e==="favicon.ico"?[`/${e}`,`/favicon/${e}`]:[`/favicon/${e}`]).forEach(s=>{this.app.get(s,async(i,w)=>{let g=[R.join(R.dirname(new URL(import.meta.url).pathname),"generators","assets","favicon",e),R.join(R.dirname(new URL(import.meta.url).pathname),"..","generators","assets","favicon",e),R.join(process.cwd(),"dist","generators","assets","favicon",e),R.join(process.cwd(),"src","generators","assets","favicon",e)];for(let k of g)try{let y=await T.readFile(k),$=e.split(".").pop(),A={ico:"image/x-icon",svg:"image/svg+xml",png:"image/png",webmanifest:"application/manifest+json"};w.type(A[$||""]||"application/octet-stream").send(y);return}catch{}w.status(404).send("File not found");});});}),this.app.get("/",(e,n)=>{n.redirect("/page-map");}),this.app.get("/page-map",(e,n)=>{if(!this.currentReport){n.status(503).send("Documentation not ready yet");return}let s=new a$1;n.send(s.generatePageMapHtml(this.currentReport,{envResult:this.envResult,railsAnalysis:this.railsAnalysis}));}),this.app.get("/rails-map",(e,n)=>{if(!this.railsAnalysis){n.status(404).send("No Rails environment detected");return}let s=new a$2;n.send(s.generateFromResult(this.railsAnalysis));}),this.app.get("/docs",async(e,n)=>{n.send(await this.renderPage("index"));}),this.app.get("/docs/*path",async(e,n)=>{let s=e.params.path,i=Array.isArray(s)?s.join("/"):s||"index";n.send(await this.renderPage(i));}),this.app.get("/api/report",(e,n)=>{n.json(this.currentReport);}),this.app.get("/api/env",(e,n)=>{n.json(this.envResult);}),this.app.get("/api/rails",(e,n)=>{this.railsAnalysis?n.json(this.railsAnalysis):n.status(404).json({error:"No Rails analysis available"});}),this.app.get("/api/diagram/:name",(e,n)=>{let s=this.currentReport?.diagrams.find(i=>i.title.toLowerCase().replace(/\s+/g,"-")===e.params.name);s?n.json(s):n.status(404).json({error:"Diagram not found"});}),this.app.post("/api/regenerate",async(e,n)=>{try{await this.regenerate(),n.json({success:!0});}catch(s){n.status(500).json({error:s.message});}});}setupSocketIO(){this.io.on("connection",t=>{t.on("disconnect",()=>{});});}async renderPage(t){let r=t.replace(/\.md$/,""),e=R.join(this.config.outputDir,`${r}.md`),n="";try{let s=await T.readFile(e,"utf-8"),i=await marked.parse(s);i=i.replace(/<pre><code class="language-mermaid">([\s\S]*?)<\/code><\/pre>/g,'<div class="mermaid">$1</div>'),i=i.replace(/<table>/g,'<div class="table-wrapper"><table>'),i=i.replace(/<\/table>/g,"</table></div>"),n=i;}catch(s){let i=s;if(i.code==="ENOENT"){let w=this.currentReport?.repositories.map(g=>g.name)||[];n=`
1
+ import {a as a$2}from'./chunk-LHP2OKKA.js';import {a as a$3}from'./chunk-VV3A3UE3.js';import {C,B,A,z,b as b$1}from'./chunk-YO6EB4OI.js';import {a,b}from'./chunk-QBSB6BIU.js';import {a as a$1}from'./chunk-ATRSGO6O.js';import {k}from'./chunk-H7VVRHQZ.js';import {simpleGit}from'simple-git';import*as L from'fs/promises';import*as M from'path';import {parseSync}from'@swc/core';import fe from'express';import {Server}from'socket.io';import*as ge from'http';import {marked}from'marked';import*as me from'net';var W=class{config;mermaidGenerator;markdownGenerator;constructor(s){this.config=s,this.mermaidGenerator=new a,this.markdownGenerator=new b;}async generate(){let s=[];for(let k of this.config.repositories)try{let x=await this.analyzeRepository(k);s.push(x);}catch(x){console.error(`\u274C ${k.name}: ${x.message}`);}let r=this.analyzeCrossRepo(s),e=s.map(k=>k.analysis),a=this.extractCrossRepoLinks(e),o=this.mermaidGenerator.generateAll(e,a),i={generatedAt:new Date().toISOString(),repositories:s,crossRepoAnalysis:r,diagrams:o};return await this.writeDocumentation(i),i}async analyzeRepository(s){let{version:r,commitHash:e}=await this.getRepoInfo(s),a=s.analyzers.map(b=>this.createAnalyzer(b,s)).filter(b=>b!==null),o=Date.now(),i=await Promise.all(a.map(b=>b.analyze())),k=((Date.now()-o)/1e3).toFixed(1);console.log(` Analyzed ${s.displayName} in ${k}s`);let x=this.mergeAnalysisResults(i,s.name,r,e);await this.enrichPagesWithHookGraphQL(x,s.path);let $={totalPages:x.pages.length,totalComponents:x.components.length,totalGraphQLOperations:x.graphqlOperations.length,totalDataFlows:x.dataFlows.length,authRequiredPages:x.pages.filter(b=>b.authentication.required).length,publicPages:x.pages.filter(b=>!b.authentication.required).length};return {name:s.name,displayName:s.displayName,version:r,commitHash:e,analysis:x,summary:$}}async getRepoInfo(s){try{let a=(await simpleGit(s.path).log({n:1})).latest?.hash||"unknown",o="unknown";try{let i=M.join(s.path,"package.json");o=JSON.parse(await L.readFile(i,"utf-8")).version||"unknown";}catch{}return {version:o,commitHash:a}}catch{return {version:"unknown",commitHash:"unknown"}}}createAnalyzer(s,r){switch(s){case "pages":if(r.type==="nextjs"||r.type==="rails"||r.type==="generic")return new z(r);break;case "graphql":return new A(r);case "dataflow":case "components":return new B(r);case "rest-api":case "api":return new C(r)}return null}mergeAnalysisResults(s,r,e,a){let o={repository:r,timestamp:new Date().toISOString(),version:e,commitHash:a,coverage:{tsFilesScanned:0,tsParseFailures:0,graphqlParseFailures:0,codegenFilesDetected:0,codegenFilesParsed:0,codegenExportsFound:0},pages:[],graphqlOperations:[],apiCalls:[],components:[],dataFlows:[],apiEndpoints:[],models:[],crossRepoLinks:[]};for(let i of s)i.coverage&&o.coverage&&(o.coverage.tsFilesScanned+=i.coverage.tsFilesScanned||0,o.coverage.tsParseFailures+=i.coverage.tsParseFailures||0,o.coverage.graphqlParseFailures+=i.coverage.graphqlParseFailures||0,o.coverage.codegenFilesDetected+=i.coverage.codegenFilesDetected||0,o.coverage.codegenFilesParsed+=i.coverage.codegenFilesParsed||0,o.coverage.codegenExportsFound+=i.coverage.codegenExportsFound||0),i.pages&&o.pages.push(...i.pages),i.graphqlOperations&&o.graphqlOperations.push(...i.graphqlOperations),i.apiCalls&&o.apiCalls.push(...i.apiCalls),i.components&&o.components.push(...i.components),i.dataFlows&&o.dataFlows.push(...i.dataFlows),i.apiEndpoints&&o.apiEndpoints.push(...i.apiEndpoints),i.models&&o.models.push(...i.models),i.crossRepoLinks&&o.crossRepoLinks.push(...i.crossRepoLinks);return o}analyzeCrossRepo(s){let r=[],e=[],a=[],o=[],i=new Map;for(let $ of s)for(let b of $.analysis.graphqlOperations){let E=i.get(b.name)||[];E.push($.name),i.set(b.name,E);}for(let[$,b]of i)b.length>1&&r.push($);let k=s.filter($=>$.analysis.pages.length>0),x=s.filter($=>$.analysis.apiEndpoints.length>0);for(let $ of k)for(let b of x)for(let E of b.analysis.apiEndpoints)e.push({frontend:$.name,backend:b.name,endpoint:E.path,operations:$.analysis.graphqlOperations.filter(N=>N.usedIn.length>0).map(N=>N.name)});return {sharedTypes:r,apiConnections:e,navigationFlows:a,dataFlowAcrossRepos:o}}extractCrossRepoLinks(s){let r=[],e=new Map;for(let a of s)for(let o of a.graphqlOperations){let i=e.get(o.name)||[];i.push(a),e.set(o.name,i);}for(let[a,o]of e)o.length>1&&r.push({sourceRepo:o[0].repository,sourcePath:`graphql/${a}`,targetRepo:o[1].repository,targetPath:`graphql/${a}`,linkType:"graphql-operation",description:`Shared GraphQL operation: ${a}`});return r}async enrichPagesWithHookGraphQL(s,r){let e=s.graphqlOperations.filter(n=>n.type==="query"||n.type==="mutation"||n.type==="subscription"),a=/\.(ts|tsx|js|jsx)$/,o=n=>M.normalize(n).replace(/\\/g,"/"),i=(this.config.analysis?.include||["**/*.ts","**/*.tsx"]).map(String),k=(this.config.analysis?.exclude||[]).map(String),x=(await import('fast-glob')).default,$=await x(i,{cwd:r,ignore:["**/node_modules/**","**/.next/**","**/dist/**","**/build/**","**/coverage/**",...k],onlyFiles:true,unique:true,dot:false}),b=new Set($.map(o)),E=new Map;for(let n of b){let t=n.replace(a,"");E.has(t)||E.set(t,n);}let N=new b$1(r,b),Z=new Set(["src/"]);for(let n of b){let t=n.indexOf("/src/");t!==-1&&Z.add(n.slice(0,t+5));}let O=n=>{let t=o(n).replace(a,""),p=E.get(t);if(p)return p;let c=E.get(t+"/index");return c||null},ye=async()=>{let n=["tsconfig.json","jsconfig.json"];for(let t of n)try{let p=await L.readFile(M.join(r,t),"utf-8"),l=JSON.parse(p)?.compilerOptions||{},d=typeof l.baseUrl=="string"?l.baseUrl:void 0,R=typeof l.paths=="object"&&l.paths?l.paths:void 0;return {baseUrl:d,paths:R}}catch{}return {}},{baseUrl:F,paths:V}=await ye(),ve=n=>{if(!V)return [];let t=[];for(let[p,c]of Object.entries(V)){if(!p.includes("*")){n===p&&t.push(...c);continue}let[l,d]=p.split("*");if(!n.startsWith(l)||!n.endsWith(d))continue;let R=n.slice(l.length,n.length-d.length);for(let w of c)w.includes("*")?t.push(w.replace("*",R)):t.push(w);}return t},B=(n,t)=>{if(!t)return null;let p=N.resolve(n,t);if(p)return p.file;if(t.startsWith(".")){let l=M.dirname(n);return O(M.join(l,t))}if(t.startsWith("@/")){let l=t.replace("@/","");if(F){let d=O(M.join(F,l));if(d)return d}for(let d of Z){let R=O(d+l);if(R)return R}return null}let c=ve(t);if(c.length>0)for(let l of c){let d=O(F?M.join(F,l):l);if(d)return d}if(F){let l=O(M.join(F,t));if(l)return l}return null},j=new Map,Y=new Map,Q=async n=>{let t=o(n),p=Y.get(t);if(p!==void 0)return p;try{let c=M.join(r,t),l=await L.readFile(c,"utf-8");return Y.set(t,l),l}catch{return Y.set(t,""),null}},we=async n=>{let t=o(n),p=j.get(t);if(p)return p.map(h=>({spec:h,names:null}));let c=await Q(t);if(!c)return j.set(t,[]),[];let l;try{let h=t.endsWith(".ts")||t.endsWith(".tsx"),g=t.endsWith(".tsx")||t.endsWith(".jsx");l=parseSync(c,{syntax:h?"typescript":"ecmascript",tsx:g,jsx:g,comments:!1});}catch{return j.set(t,[]),[]}let d=new Set,R=[],w=(h,g,m)=>{typeof h!="string"||h.length===0||(d.add(h),R.push({spec:h,names:g,pos:m}));},f=h=>{if(!h||typeof h!="object")return;let g=h,m=g;if(m.type==="ImportDeclaration"){if(!m.typeOnly){let v=m.source?.value,u=[],y=m.specifiers||[];for(let C of y){let q=C,D=q.type;if(D==="ImportDefaultSpecifier"||D==="ImportNamespaceSpecifier"){u=null;break}if(D==="ImportSpecifier"){let G=q.imported?.value,P=q.local?.value,z=G||P;z&&u.push(z);}}Array.isArray(u)&&u.length===0&&(u=null),w(v,u,m.span?.start);}}else if(m.type==="ExportAllDeclaration")w(m.source?.value,null,m.span?.start);else if(m.type==="ExportNamedDeclaration"){let v=m.source?.value,u=m.specifiers||[],y=[];for(let C of u){let q=C;if(q.type==="ExportSpecifier"){let G=q.exported?.value,P=q.orig?.value,z=G||P;z&&y.push(z);}}w(v,y.length>0?y:null,m.span?.start);}else if(m.type==="CallExpression"){let v=m.callee||null;if(v?.type==="Identifier"&&v.value==="require"){let u=m.arguments?.[0]?.expression;u?.type==="StringLiteral"&&w(u.value,null,m.span?.start);}if(v?.type==="Import"){let u=m.arguments?.[0]?.expression;u?.type==="StringLiteral"&&w(u.value,null,m.span?.start);}}for(let v of Object.keys(g)){let u=g[v];if(Array.isArray(u))for(let y of u)f(y);else u&&typeof u=="object"&&f(u);}};f(l);let A=Array.from(d);return j.set(t,A),R.filter(h=>typeof h.spec=="string"&&h.spec.length>0)},xe=(n,t)=>{if(t===void 0||t<0)return;let p=1;for(let c=0;c<n.length&&c<t;c++)n.charCodeAt(c)===10&&p++;return p},be=(n,t)=>{if(t<0)return;let p=1;for(let c=0;c<n.length&&c<t;c++)n.charCodeAt(c)===10&&p++;return p},H=new Map,_=async n=>{let t=o(n),p=H.get(t);if(p)return p;let c=await Q(t);if(!c){let h={named:new Map,stars:[],isBarrel:false};return H.set(t,h),h}let l;try{let h=t.endsWith(".ts")||t.endsWith(".tsx"),g=t.endsWith(".tsx")||t.endsWith(".jsx");l=parseSync(c,{syntax:h?"typescript":"ecmascript",tsx:g,jsx:g,comments:!1});}catch{let h={named:new Map,stars:[],isBarrel:false};return H.set(t,h),h}let d=new Map,R=[],w=true,f=l?.body;if(Array.isArray(f))for(let h of f){let g=h,m=g.type;if(m&&m!=="ImportDeclaration"){if(m==="ExportAllDeclaration"){let v=g.source?.value;typeof v=="string"&&R.push(v);continue}if(m==="ExportNamedDeclaration"){let v=g.source?.value,u=g.specifiers||[];if(typeof v=="string"&&Array.isArray(u))for(let y of u){let C=y;if(C.type!=="ExportSpecifier")continue;let D=C.exported?.value,G=C.orig?.value,P=D||G;P&&d.set(P,v);}continue}w=false;}}else w=false;let A={named:d,stars:R,isBarrel:w};return H.set(t,A),A},ee=async(n,t,p)=>{let c=o(n);if(p.has(c))return null;p.add(c);let l=await _(c),d=l.named.get(t);if(d)return B(c,d);for(let R of l.stars){let w=B(c,R);if(!w)continue;let f=await ee(w,t,p);if(f)return f}return null},K=new Map;for(let n of e){let t=new Set;n.filePath&&t.add(n.filePath);for(let p of n.usedIn||[])t.add(p);for(let p of t){let c=K.get(p)||[];c.push({opName:n.name,opType:n.type}),K.set(p,c);}}let ke=n=>{if(!n)return null;let t=[`src/pages/${n}`,`pages/${n}`,`src/app/${n}`,`app/${n}`,`frontend/src/pages/${n}`,`frontend/src/app/${n}`,`app/javascript/pages/${n}`,`app/javascript/app/${n}`];for(let l of t){if(b.has(l))return l;let d=O(l);if(d)return d}let p=[],c=[`/pages/${n}`,`/app/${n}`,`/${n}`];for(let l of b)for(let d of c)if(l.endsWith(d)&&!(d.startsWith("/pages/")&&!l.includes("/pages/"))&&!(d.startsWith("/app/")&&!l.includes("/app/"))){p.push(l);break}return p.length===0?null:(p.sort((l,d)=>l.length-d.length),p[0])},J=new Map,U=new Map;for(let n of s.pages){let t=ke(n.filePath);if(!t)continue;let p=new Map,c=new Set,l=[{f:t,depth:0}],d=new Map,R=30,w=2e4;for(;l.length>0;){let f=l.shift();if(!f)break;if(c.has(f.f))continue;if(c.add(f.f),c.size>=w)break;let A=K.get(f.f);if(A)for(let g of A){let m=p.get(g.opName);(!m||f.depth<m.distance)&&p.set(g.opName,{opName:g.opName,opType:g.opType,sourceFile:f.f,distance:f.depth});}if(f.depth>=R)continue;let h=await we(f.f);for(let g of h){let m=B(f.f,g.spec);if(m){if(!c.has(m)&&(l.push({f:m,depth:f.depth+1}),!d.has(m))){let v=await Q(f.f),u=v?xe(v,g.pos):void 0,y=Array.isArray(g.names)&&g.names.length>0?`names:${g.names.join(",")}`:void 0;d.set(m,{from:f.f,spec:g.spec,line:u,detail:y});}if(Array.isArray(g.names)&&g.names.length>0){if((await _(m)).isBarrel)for(let u of g.names){let y=await ee(m,u,new Set);y&&!c.has(y)&&(l.push({f:y,depth:f.depth+2}),d.has(y)||d.set(y,{from:m,spec:`re-export:${u}`,line:void 0,detail:"barrel"}));}}else if(g.names===null){let v=await _(m);if(v.isBarrel){for(let u of v.stars){let y=B(m,u);y&&!c.has(y)&&l.push({f:y,depth:f.depth+2});}for(let u of v.named.values()){let y=B(m,u);y&&!c.has(y)&&l.push({f:y,depth:f.depth+2});}}}}}}J.set(n.path,{page:n,entryFile:t,parent:d,bestByOp:p});for(let f of c)U.set(f,(U.get(f)||0)+1);}let qe=J.size,Re=Array.from(U.values()).sort((n,t)=>n-t),$e=((n,t)=>{if(n.length===0)return 0;let p=Math.min(n.length-1,Math.max(0,Math.floor(n.length*t)));return n[p]})(Re,.9),Me=Math.max(10,$e),Te=Math.max(2,Math.floor(qe*.05));for(let{page:n,entryFile:t,parent:p,bestByOp:c}of J.values()){let l=new Set((n.dataFetching||[]).map(d=>d.operationName?.replace(/^[→\->\s]+/,"")||""));for(let{opName:d,opType:R,sourceFile:w,distance:f}of c.values()){if(l.has(d))continue;l.add(d);let A=U.get(w)||0,h;f===0?h=void 0:A>=Me?h=`common:${w}`:f<=2||A<=Te?h=`close:${w}`:h=`indirect:${w}`;let g=f===0||f<=2?"certain":h?.startsWith("common:")?"unknown":"likely",m=[];if(f>0&&w!==t){let u=[],y=w,C=new Set;for(;y!==t&&!C.has(y);){C.add(y);let q=p.get(y);if(!q)break;u.push({from:q.from,to:y,spec:q.spec,line:q.line,detail:q.detail}),y=q.from;}u.reverse();for(let q of u)m.push({kind:"import-edge",file:q.from,line:q.line,detail:`${q.spec} -> ${q.to}${q.detail?` (${q.detail})`:""}`});}let v=await Q(w);if(v){let u=v.indexOf(`${d}Document`)>=0?v.indexOf(`${d}Document`):v.indexOf(d),y=u>=0?be(v,u):void 0;m.push({kind:"operation-reference",file:w,line:y,detail:`ref:${d}`});}else m.push({kind:"operation-reference",file:w,detail:`ref:${d}`});n.dataFetching.push({type:R==="mutation"?"useMutation":R==="subscription"?"useSubscription":"useQuery",operationName:d,source:h,confidence:g,evidence:m.length>0?m:void 0});}}}async writeDocumentation(s){let r=this.config.outputDir;await L.mkdir(r,{recursive:true});let e=this.markdownGenerator.generateDocumentation(s);for(let[o,i]of e){let k=M.join(r,o),x=M.dirname(k);await L.mkdir(x,{recursive:true}),await L.writeFile(k,i,"utf-8");}let a=M.join(r,"report.json");await L.writeFile(a,JSON.stringify(s,null,2),"utf-8");}};function Ae(S){return new Promise(s=>{let r=me.createServer();r.once("error",e=>{e.code,s(false);}),r.once("listening",()=>{r.close(),s(true);}),r.listen(S);})}async function ue(S,s=10){for(let r=0;r<s;r++){let e=S+r;if(await Ae(e))return e}throw new Error(`No available port found between ${S} and ${S+s-1}`)}var he=class{config;port;app;server;io;engine;currentReport=null;envResult=null;railsAnalysis=null;constructor(s,r=3030){this.config=s,this.port=r,this.app=fe(),this.server=ge.createServer(this.app),this.io=new Server(this.server),this.engine=new W(s),this.setupRoutes(),this.setupSocketIO();}setupRoutes(){this.app.use("/assets",fe.static(M.join(this.config.outputDir,"assets"))),["common.css","page-map.css","docs.css","rails-map.css"].forEach(e=>{this.app.get(`/${e}`,async(a,o)=>{let i=[M.join(M.dirname(new URL(import.meta.url).pathname),"generators","assets",e),M.join(M.dirname(new URL(import.meta.url).pathname),"..","generators","assets",e),M.join(process.cwd(),"dist","generators","assets",e),M.join(process.cwd(),"src","generators","assets",e)];for(let k of i)try{let x=await L.readFile(k,"utf-8");o.type("text/css").send(x);return}catch{}o.status(404).send("CSS not found");});}),["favicon.ico","favicon.svg","favicon-96x96.png","apple-touch-icon.png","site.webmanifest","web-app-manifest-192x192.png","web-app-manifest-512x512.png"].forEach(e=>{(e==="favicon.ico"?[`/${e}`,`/favicon/${e}`]:[`/favicon/${e}`]).forEach(o=>{this.app.get(o,async(i,k)=>{let x=[M.join(M.dirname(new URL(import.meta.url).pathname),"generators","assets","favicon",e),M.join(M.dirname(new URL(import.meta.url).pathname),"..","generators","assets","favicon",e),M.join(process.cwd(),"dist","generators","assets","favicon",e),M.join(process.cwd(),"src","generators","assets","favicon",e)];for(let $ of x)try{let b=await L.readFile($),E=e.split(".").pop(),N={ico:"image/x-icon",svg:"image/svg+xml",png:"image/png",webmanifest:"application/manifest+json"};k.type(N[E||""]||"application/octet-stream").send(b);return}catch{}k.status(404).send("File not found");});});}),this.app.get("/",(e,a)=>{a.redirect("/page-map");}),this.app.get("/page-map",(e,a)=>{if(!this.currentReport){a.status(503).send("Documentation not ready yet");return}let o=new a$1;a.send(o.generatePageMapHtml(this.currentReport,{envResult:this.envResult,railsAnalysis:this.railsAnalysis}));}),this.app.get("/rails-map",(e,a)=>{if(!this.railsAnalysis){a.status(404).send("No Rails environment detected");return}let o=new a$2;a.send(o.generateFromResult(this.railsAnalysis));}),this.app.get("/docs",async(e,a)=>{a.send(await this.renderPage("index"));}),this.app.get("/docs/*path",async(e,a)=>{let o=e.params.path,i=Array.isArray(o)?o.join("/"):o||"index";a.send(await this.renderPage(i));}),this.app.get("/api/report",(e,a)=>{a.json(this.currentReport);}),this.app.get("/api/env",(e,a)=>{a.json(this.envResult);}),this.app.get("/api/rails",(e,a)=>{this.railsAnalysis?a.json(this.railsAnalysis):a.status(404).json({error:"No Rails analysis available"});}),this.app.get("/api/diagram/:name",(e,a)=>{let o=this.currentReport?.diagrams.find(i=>i.title.toLowerCase().replace(/\s+/g,"-")===e.params.name);o?a.json(o):a.status(404).json({error:"Diagram not found"});}),this.app.post("/api/regenerate",async(e,a)=>{try{await this.regenerate(),a.json({success:!0});}catch(o){a.status(500).json({error:o.message});}});}setupSocketIO(){this.io.on("connection",s=>{s.on("disconnect",()=>{});});}async renderPage(s){let r=s.replace(/\.md$/,""),e=M.join(this.config.outputDir,`${r}.md`),a="";try{let o=await L.readFile(e,"utf-8"),i=await marked.parse(o);i=i.replace(/<pre><code class="language-mermaid">([\s\S]*?)<\/code><\/pre>/g,'<div class="mermaid">$1</div>'),i=i.replace(/<table>/g,'<div class="table-wrapper"><table>'),i=i.replace(/<\/table>/g,"</table></div>"),a=i;}catch(o){let i=o;if(i.code==="ENOENT"){let k=this.currentReport?.repositories.map(x=>x.name)||[];a=`
2
2
  <h1>Page not found</h1>
3
3
  <p>The requested path <code>${r}</code> does not exist.</p>
4
- ${w.length>0?`
4
+ ${k.length>0?`
5
5
  <p>Available repositories:</p>
6
- <ul>${w.map(g=>`<li><a href="/docs/repos/${g}">${g}</a></li>`).join("")}</ul>
6
+ <ul>${k.map(x=>`<li><a href="/docs/repos/${x}">${x}</a></li>`).join("")}</ul>
7
7
  `:""}
8
8
  <p><a href="/">\u2190 Back to home</a></p>
9
- `;}else console.error(`\u26A0\uFE0F Error reading ${e}: ${i.message}`),n=`<h1>Error</h1><p>Failed to load page: ${i.message}</p>`;}return this.getHtmlTemplate(n)}getGraphQLData(){if(!this.currentReport)return "[]";let t=[];for(let r of this.currentReport.repositories)for(let e of r.analysis?.graphqlOperations||[])t.push({name:e.name,type:e.type,returnType:e.returnType,variables:e.variables,fields:e.fields,usedIn:e.usedIn});return JSON.stringify(t)}getApiCallsData(){if(!this.currentReport)return "[]";let t=[];for(let r of this.currentReport.repositories)for(let e of r.analysis?.apiCalls||[])t.push({id:e.id,method:e.method,url:e.url,callType:e.callType,filePath:e.filePath,line:e.line,containingFunction:e.containingFunction,requiresAuth:e.requiresAuth});return JSON.stringify(t)}getHtmlTemplate(t){let r=this.getGraphQLData(),e=this.getApiCallsData();return `<!DOCTYPE html>
9
+ `;}else console.error(`\u26A0\uFE0F Error reading ${e}: ${i.message}`),a=`<h1>Error</h1><p>Failed to load page: ${i.message}</p>`;}return this.getHtmlTemplate(a)}getGraphQLData(){if(!this.currentReport)return "[]";let s=[];for(let r of this.currentReport.repositories)for(let e of r.analysis?.graphqlOperations||[])s.push({name:e.name,type:e.type,returnType:e.returnType,variables:e.variables,fields:e.fields,usedIn:e.usedIn});return JSON.stringify(s)}getApiCallsData(){if(!this.currentReport)return "[]";let s=[];for(let r of this.currentReport.repositories)for(let e of r.analysis?.apiCalls||[])s.push({id:e.id,method:e.method,url:e.url,callType:e.callType,filePath:e.filePath,line:e.line,containingFunction:e.containingFunction,requiresAuth:e.requiresAuth});return JSON.stringify(s)}getHtmlTemplate(s){let r=this.getGraphQLData(),e=this.getApiCallsData();return `<!DOCTYPE html>
10
10
  <html lang="en">
11
11
  <head>
12
12
  <meta charset="UTF-8">
@@ -104,11 +104,11 @@ import {a as a$2}from'./chunk-NQMJ3QRX.js';import {a as a$3}from'./chunk-VV3A3UE
104
104
  <div class="nav-group">
105
105
  <span class="nav-group-title">Documentation</span>
106
106
  <div class="nav-subitems">
107
- ${this.config.repositories.map(n=>`
108
- <a href="/docs/repos/${n.name}/pages">Pages</a>
109
- <a href="/docs/repos/${n.name}/components">Components</a>
110
- <a href="/docs/repos/${n.name}/graphql">GraphQL</a>
111
- <a href="/docs/repos/${n.name}/dataflow">Data Flow</a>
107
+ ${this.config.repositories.map(a=>`
108
+ <a href="/docs/repos/${a.name}/pages">Pages</a>
109
+ <a href="/docs/repos/${a.name}/components">Components</a>
110
+ <a href="/docs/repos/${a.name}/graphql">GraphQL</a>
111
+ <a href="/docs/repos/${a.name}/dataflow">Data Flow</a>
112
112
  `).join("")}
113
113
  </div>
114
114
  </div>
@@ -124,7 +124,7 @@ import {a as a$2}from'./chunk-NQMJ3QRX.js';import {a as a$3}from'./chunk-VV3A3UE
124
124
  </aside>
125
125
  <div class="content-area">
126
126
  <div class="content">
127
- ${t}
127
+ ${s}
128
128
  </div>
129
129
  </div>
130
130
  </div>
@@ -469,7 +469,9 @@ import {a as a$2}from'./chunk-NQMJ3QRX.js';import {a as a$3}from'./chunk-VV3A3UE
469
469
 
470
470
  if (op.usedIn?.length) {
471
471
  html += '<div class="detail-section"><h4>Used In</h4><div style="max-height:100px;overflow-y:auto">';
472
- op.usedIn.forEach(f => { html += \`<p style="font-size:12px;color:#666;margin:2px 0">\${f}</p>\`; });
472
+ op.usedIn.forEach(f => {
473
+ html += \`<p><code>\${escapeHtml(f)}</code></p>\`;
474
+ });
473
475
  html += '</div></div>';
474
476
  }
475
477
  } else {
@@ -508,7 +510,9 @@ import {a as a$2}from'./chunk-NQMJ3QRX.js';import {a as a$3}from'./chunk-VV3A3UE
508
510
 
509
511
  if (partialMatch.usedIn?.length) {
510
512
  html += '<div class="detail-section"><h4>Used In</h4><div style="max-height:100px;overflow-y:auto">';
511
- partialMatch.usedIn.forEach(f => { html += \`<p style="font-size:12px;color:#666;margin:2px 0">\${f}</p>\`; });
513
+ partialMatch.usedIn.forEach(f => {
514
+ html += \`<p><code>\${escapeHtml(f)}</code></p>\`;
515
+ });
512
516
  html += '</div></div>';
513
517
  }
514
518
  } else {
@@ -590,6 +594,15 @@ import {a as a$2}from'./chunk-NQMJ3QRX.js';import {a as a$3}from'./chunk-VV3A3UE
590
594
  return badges[type] || badges.unknown;
591
595
  }
592
596
 
597
+ function escapeHtml(str) {
598
+ return String(str ?? '')
599
+ .replace(/&/g, '&amp;')
600
+ .replace(/</g, '&lt;')
601
+ .replace(/>/g, '&gt;')
602
+ .replace(/"/g, '&quot;')
603
+ .replace(/'/g, '&#39;');
604
+ }
605
+
593
606
  function closeModal() {
594
607
  document.getElementById('detailModal').classList.remove('open');
595
608
  modalHistory = [];
@@ -634,7 +647,8 @@ import {a as a$2}from'./chunk-NQMJ3QRX.js';import {a as a$3}from'./chunk-VV3A3UE
634
647
  el.addEventListener('click', (e) => {
635
648
  e.stopPropagation();
636
649
  const opName = el.dataset.op || el.textContent?.replace(/^[QM]:\\s*/, '') || '';
637
- if (opName) showGraphQLDetail(opName, el);
650
+ const confidence = el.dataset.confidence || '';
651
+ if (opName) showGraphQLDetail(opName, el, confidence);
638
652
  });
639
653
  });
640
654
 
@@ -668,7 +682,7 @@ import {a as a$2}from'./chunk-NQMJ3QRX.js';import {a as a$3}from'./chunk-VV3A3UE
668
682
  });
669
683
  });
670
684
 
671
- function showGraphQLDetail(name, el) {
685
+ function showGraphQLDetail(name, el, confidence) {
672
686
  const modal = document.getElementById('detailModal');
673
687
  const title = document.getElementById('modalTitle');
674
688
  const body = document.getElementById('modalBody');
@@ -685,9 +699,16 @@ import {a as a$2}from'./chunk-NQMJ3QRX.js';import {a as a$3}from'./chunk-VV3A3UE
685
699
 
686
700
  if (op) {
687
701
  titleText = op.name;
702
+ const confBadge =
703
+ confidence === 'likely'
704
+ ? '<span class="detail-badge confidence likely" title="Likely: reachable via the import graph, but indirect (3+ steps)">LIKELY</span>'
705
+ : '';
688
706
  html = \`<div class="detail-section">
689
707
  <h4>Type</h4>
690
- <p><span class="detail-badge \${op.type}">\${op.type.toUpperCase()}</span></p>
708
+ <div class="detail-badges">
709
+ <span class="detail-badge \${op.type}">\${op.type.toUpperCase()}</span>
710
+ \${confBadge}
711
+ </div>
691
712
  </div>\`;
692
713
 
693
714
  if (op.returnType) {
@@ -716,8 +737,10 @@ import {a as a$2}from'./chunk-NQMJ3QRX.js';import {a as a$3}from'./chunk-VV3A3UE
716
737
  }
717
738
 
718
739
  if (op.usedIn?.length) {
719
- html += '<div class="detail-section"><h4>Used In</h4><div style="font-size:12px;color:#666;max-height:100px;overflow-y:auto">';
720
- op.usedIn.forEach(f => { html += \`<div style="margin:2px 0">\${f}</div>\`; });
740
+ html += '<div class="detail-section"><h4>Used In</h4><div style="max-height:100px;overflow-y:auto">';
741
+ op.usedIn.forEach(f => {
742
+ html += \`<p><code>\${escapeHtml(f)}</code></p>\`;
743
+ });
721
744
  html += '</div></div>';
722
745
  }
723
746
  } else {
@@ -730,7 +753,9 @@ import {a as a$2}from'./chunk-NQMJ3QRX.js';import {a as a$3}from'./chunk-VV3A3UE
730
753
  html = \`
731
754
  <div class="detail-section">
732
755
  <h4>Type</h4>
733
- <p><span class="detail-badge \${type}">\${type.toUpperCase()}</span></p>
756
+ <div class="detail-badges">
757
+ <span class="detail-badge \${type}">\${type.toUpperCase()}</span>
758
+ </div>
734
759
  </div>
735
760
  <div class="detail-section">
736
761
  <h4>Operation Name</h4>
@@ -856,8 +881,10 @@ import {a as a$2}from'./chunk-NQMJ3QRX.js';import {a as a$3}from'./chunk-VV3A3UE
856
881
  }
857
882
 
858
883
  if (op.usedIn?.length) {
859
- html += '<div class="detail-section"><h4>Used In</h4><div style="font-size:12px;color:#666;max-height:100px;overflow-y:auto">';
860
- op.usedIn.forEach(f => { html += \`<div style="margin:2px 0">\${f}</div>\`; });
884
+ html += '<div class="detail-section"><h4>Used In</h4><div style="max-height:100px;overflow-y:auto">';
885
+ op.usedIn.forEach(f => {
886
+ html += \`<p><code>\${escapeHtml(f)}</code></p>\`;
887
+ });
861
888
  html += '</div></div>';
862
889
  }
863
890
 
@@ -1085,11 +1112,11 @@ import {a as a$2}from'./chunk-NQMJ3QRX.js';import {a as a$3}from'./chunk-VV3A3UE
1085
1112
  }
1086
1113
  </script>
1087
1114
  </body>
1088
- </html>`}async start(t=true){let r=this.config.repositories[0]?.path||process.cwd();this.envResult=await a$3(r),this.currentReport=await this.engine.generate(),console.log();for(let e of this.currentReport.repositories){let n=e.summary;console.log(` \u2705 ${e.displayName}`),console.log(` ${n.totalPages} pages \xB7 ${n.totalComponents} components \xB7 ${n.totalGraphQLOperations} GraphQL \xB7 ${n.totalDataFlows} data flows`);}if(this.envResult.hasRails){console.log(`
1089
- Rails...`);try{this.railsAnalysis=await k(r);let e=this.railsAnalysis.summary;console.log(` ${e.totalRoutes} routes \xB7 ${e.totalControllers} controllers \xB7 ${e.totalModels} models \xB7 ${e.totalGrpcServices} gRPC`);}catch(e){console.error(" \u26A0\uFE0F Rails analysis failed:",e.message);}}try{let e=await de(this.port);e!==this.port&&console.log(`
1115
+ </html>`}async start(s=true){let r=this.config.repositories[0]?.path||process.cwd();this.envResult=await a$3(r),this.currentReport=await this.engine.generate(),console.log();for(let e of this.currentReport.repositories){let a=e.summary;console.log(` \u2705 ${e.displayName}`),console.log(` ${a.totalPages} pages \xB7 ${a.totalComponents} components \xB7 ${a.totalGraphQLOperations} GraphQL \xB7 ${a.totalDataFlows} data flows`);}if(this.envResult.hasRails){console.log(`
1116
+ Rails...`);try{this.railsAnalysis=await k(r);let e=this.railsAnalysis.summary;console.log(` ${e.totalRoutes} routes \xB7 ${e.totalControllers} controllers \xB7 ${e.totalModels} models \xB7 ${e.totalGrpcServices} gRPC`);}catch(e){console.error(" \u26A0\uFE0F Rails analysis failed:",e.message);}}try{let e=await ue(this.port);e!==this.port&&console.log(`
1090
1117
  \u26A0\uFE0F Port ${this.port} is in use, using port ${e} instead`),this.port=e;}catch(e){console.error(`
1091
1118
  \u274C Failed to find available port: ${e.message}`),process.exit(1);}if(this.server.listen(this.port,()=>{console.log(`
1092
1119
  \u{1F310} Documentation server running at http://localhost:${this.port}`),this.envResult?.hasRails&&this.envResult?.hasNextjs&&console.log(" \u{1F4CA} Multiple environments detected - use tabs to switch views"),console.log(` Press Ctrl+C to stop
1093
- `);}),t){let e=(await import('open')).default;await e(`http://localhost:${this.port}`);}this.config.watch.enabled&&this.watchForChanges();}async regenerate(){if(console.log(`
1094
- \u{1F504} Regenerating...`),this.currentReport=await this.engine.generate(),this.envResult?.hasRails){let t=this.config.repositories[0]?.path||process.cwd();try{this.railsAnalysis=await k(t);}catch(r){console.error("\u26A0\uFE0F Rails re-analysis failed:",r.message);}}for(let t of this.currentReport.repositories)console.log(` ${t.displayName}: ${t.summary.totalPages} pages \xB7 ${t.summary.totalComponents} components \xB7 ${t.summary.totalGraphQLOperations} GraphQL`);this.io.emit("reload"),console.log("\u2705 Done");}async watchForChanges(){let t=this.config.repositories.map(e=>e.path),r=null;for(let e of t)try{let n=T.watch(e,{recursive:!0});(async()=>{for await(let s of n)s.filename&&(s.filename.endsWith(".ts")||s.filename.endsWith(".tsx"))&&(r&&clearTimeout(r),r=setTimeout(async()=>{await this.regenerate();},this.config.watch.debounce));})();}catch(n){console.warn(`Warning: Could not watch directory ${e}:`,n.message);}}stop(){this.server.close(),console.log(`
1095
- \u{1F44B} Server stopped`);}};export{U as a,ue as b};
1120
+ `);}),s){let e=(await import('open')).default;await e(`http://localhost:${this.port}`);}this.config.watch.enabled&&this.watchForChanges();}async regenerate(){if(console.log(`
1121
+ \u{1F504} Regenerating...`),this.currentReport=await this.engine.generate(),this.envResult?.hasRails){let s=this.config.repositories[0]?.path||process.cwd();try{this.railsAnalysis=await k(s);}catch(r){console.error("\u26A0\uFE0F Rails re-analysis failed:",r.message);}}for(let s of this.currentReport.repositories)console.log(` ${s.displayName}: ${s.summary.totalPages} pages \xB7 ${s.summary.totalComponents} components \xB7 ${s.summary.totalGraphQLOperations} GraphQL`);this.io.emit("reload"),console.log("\u2705 Done");}async watchForChanges(){let s=this.config.repositories.map(e=>e.path),r=null;for(let e of s)try{let a=L.watch(e,{recursive:!0});(async()=>{for await(let o of a)o.filename&&(o.filename.endsWith(".ts")||o.filename.endsWith(".tsx"))&&(r&&clearTimeout(r),r=setTimeout(async()=>{await this.regenerate();},this.config.watch.debounce));})();}catch(a){console.warn(`Warning: Could not watch directory ${e}:`,a.message);}}stop(){this.server.close(),console.log(`
1122
+ \u{1F44B} Server stopped`);}};export{W as a,he as b};