/** * Call Graph Analyzer * * Performs static analysis of function calls across source files using tree-sitter. * Supports TypeScript/JavaScript, Python, Go, Rust, Ruby, Java, Swift — no LLM, pure AST. * * Produces: * - FunctionNode[] — all identified functions/methods * - CallEdge[] — resolved function→function call relationships * - Hub functions — high-fanIn nodes (called by many others) * - Entry points — functions with no internal callers * - Layer violations — cross-layer calls in the wrong direction */ import type { ImportMap } from './import-resolver-bridge.js'; import { type FunctionCfg } from './cfg.js'; export type EdgeConfidence = 'self_cls' | 'type_inference' | 'import' | 'http_endpoint' | 'same_file' | 'name_only' | 'type_name' | 'synthesized' | 'external'; /** Broad relationship kind */ export type EdgeKind = 'calls' | 'overrides' | 'tested_by' | 'references' | 'depends_on' | 'affects' | 'authored_by' | 'changed_in_pr'; /** Semantic nature of the call at the call site */ export type CallType = 'direct' | 'method' | 'awaited' | 'constructor'; export interface FunctionNode { /** Unique ID: "filepath::ClassName.methodName" or "filepath::functionName" */ id: string; name: string; filePath: string; className?: string; isAsync: boolean; language: string; /** Byte offset range in source (for call attribution) */ startIndex: number; endIndex: number; fanIn: number; fanOut: number; /** First meaningful line of the doc comment / docstring, extracted from AST positions */ docstring?: string; /** Declaration line(s) up to opening brace/colon, whitespace-normalized */ signature?: string; /** True for synthetic nodes representing unresolved external/stdlib calls (e.g. fetch, https.request) */ isExternal?: boolean; /** Classification of external node — used to filter stdlib noise from views */ externalKind?: ExternalKind; /** True for nodes whose source file is a test file (*.test.ts, *_test.py, etc.) */ isTest?: boolean; /** 1-based line number of the function start (computed from startIndex at build time) */ startLine?: number; /** 1-based line number of the function end (computed from endIndex at build time) */ endLine?: number; /** Label-propagation community ID (canonical node id of the community representative) */ communityId?: string; /** Human-readable community label (name of the hub function in the community) */ communityLabel?: string; /** McCabe cyclomatic complexity computed from AST body slice (1 = linear, ≥10 = complex) */ cyclomaticComplexity?: number; /** * Content-addressed, location-independent stable identity (change: * add-content-addressed-stable-symbol-ids). Derived from the qualified name + * signature shape, excluding the file path — so it survives a rename/move. * Additive: the path-based `id` remains the canonical key. Absent for * anonymous/synthetic symbols with no derivable descriptor. */ stableId?: string; } /** Broad category of an external (unresolved) call */ export type ExternalKind = 'http' | 'database' | 'filesystem' | 'stdlib' | 'unknown'; export interface CallEdge { callerId: string; /** Resolved callee ID */ calleeId: string; /** Raw name as it appears in source */ calleeName: string; line?: number; confidence: EdgeConfidence; /** Broad relationship kind — omitted on legacy/pre-existing edges, treated as 'calls' */ kind?: EdgeKind; /** Semantic call type; only set when kind === 'calls' */ callType?: CallType; /** * Name of the synthesis rule that produced this edge (e.g. 'event-channel', * 'route-handler'). Set only when `confidence === 'synthesized'`; absent on * directly-resolved edges. Lets every consumer and agent see which conclusions * lean on a heuristic and which rest on direct name resolution. */ synthesizedBy?: string; } /** * Deterministic call-distance cost per edge resolution confidence. A lower cost * means a structurally *nearer* (more strongly resolved) edge. Used by * {@link callDistance} and the weighted traversal that scopes context by nearest * neighbour instead of by a fixed neighbour count. * * `external` is `Infinity`: external nodes are synthetic stdlib/HTTP leaves and * are never traversed *through* for internal scoping (see `weightedBfs`). */ export declare const CALL_DISTANCE_COSTS: Record; /** * Deterministic distance cost for a single call edge, derived solely from its * resolution confidence — a pure function of static analysis, no learned or * stochastic component. The switch is exhaustive over {@link EdgeConfidence} * (the `never` assignment fails compilation if a member is added without a * cost); the runtime `default` defends against malformed/legacy edge data. */ export declare function callDistance(edge: CallEdge): number; export interface LayerViolation { callerId: string; calleeId: string; callerLayer: string; calleeLayer: string; reason: string; } /** * The layer a file belongs to, by the first matching prefix in declared order. * Shared by the call-graph layer detector and the architecture guardrail (spec-23) * so both agree on one layering convention. */ export declare function layerOf(filePath: string, layers: Record): string | undefined; /** * Classify a single directed edge (from → to) against a layer ordering. Declared * key order is top → bottom; a lower layer depending on an upper layer is a * violation. Returns the offending layer pair, or null when the edge is legal, * unclassified, or intra-layer. The canonical layer-direction primitive — reused * by `detectLayerViolations` (call edges) and the spec-23 architecture checker * (file dependency edges). */ export declare function classifyLayerEdge(fromFile: string, toFile: string, layers: Record): { fromLayer: string; toLayer: string; } | null; /** * A class or interface as a structural unit, grouping its methods. * Derived from FunctionNode.className after the call graph is built. */ export interface ClassNode { /** Unique ID: first filePath where the class is seen + "::" + className */ id: string; name: string; filePath: string; language: string; /** Direct parent class names (from `extends` / Python base / C++ base) */ parentClasses: string[]; /** Implemented interfaces (TypeScript `implements`, Java `implements`) */ interfaces: string[]; /** IDs of FunctionNode members that belong to this class */ methodIds: string[]; /** Sum of method fanIn values */ fanIn: number; /** Sum of method fanOut values */ fanOut: number; /** True for synthetic file-level module nodes (free functions grouped by file) */ isModule?: boolean; /** * Content-addressed, location-independent stable identity (change: * add-content-addressed-stable-symbol-ids); the escaped class name, excluding * the file path. Absent for synthetic module groupings. Additive — `id` * remains canonical. */ stableId?: string; } /** * An inheritance or implementation edge between two ClassNodes in the graph. */ export interface InheritanceEdge { id: string; /** ClassNode id of the parent / base / interface */ parentId: string; /** ClassNode id of the child / derived / implementor */ childId: string; kind: 'extends' | 'implements' | 'embeds' | 'overrides'; } export interface CallGraphResult { nodes: Map; edges: CallEdge[]; /** * Per-function intra-procedural control-flow + reaching-definitions overlay * (spec: add-intraprocedural-cfg-dataflow-overlay), keyed by function id. * Transient build-time data: persisted to the disposable SQLite store but * deliberately NOT carried into {@link SerializedCallGraph}/the resident graph, * so in-memory footprint is unchanged. Absent for unsupported languages. */ cfgs?: Map; /** Class-level structural nodes, derived from FunctionNode.className grouping */ classes: ClassNode[]; /** Inheritance / implementation edges between ClassNodes */ inheritanceEdges: InheritanceEdge[]; /** Functions with fanIn >= HUB_THRESHOLD */ hubFunctions: FunctionNode[]; /** Functions with no internal callers (fanIn === 0) */ entryPoints: FunctionNode[]; layerViolations: LayerViolation[]; stats: { totalNodes: number; totalEdges: number; avgFanIn: number; avgFanOut: number; }; } /** Serializable version (Maps replaced by arrays) for JSON storage */ export interface SerializedCallGraph { nodes: FunctionNode[]; edges: CallEdge[]; classes: ClassNode[]; inheritanceEdges: InheritanceEdge[]; hubFunctions: FunctionNode[]; entryPoints: FunctionNode[]; layerViolations: LayerViolation[]; stats: CallGraphResult['stats']; } /** Reset loader caches — test-only hook for the graceful-degradation test. */ export declare function __resetGrammarCacheForTests(): void; /** * McCabe cyclomatic complexity via regex over function body. * CC = 1 + decision points (if, while, for, case, catch, &&, ||). * Approximate (regex, not AST), suitable for triage/ranking. */ export declare function computeCyclomaticComplexity(body: string, language: string): number; /** Per-channel handler fan-out cap. Over-cap channels are DROPPED, never guessed. */ export declare const EVENT_CHANNEL_FANOUT_CAP = 8; /** Resolve a referenced simple name to a single internal function node, or undefined * when unknown or ambiguous (never guesses). Prefers a match in `preferFile`. */ type HandlerResolver = (name: string, preferFile: string) => FunctionNode | undefined; export declare function synthesizeDynamicDispatchEdges(files: Array<{ path: string; content: string; language: string; }>, allNodes: Map, resolveHandler: HandlerResolver): Promise; export declare class CallGraphBuilder { /** * Build a call graph from a list of source files. * * @param files Source files with path, content, and language * @param layers Optional layer map { layerName: [path prefix, ...] } * @param importMap Optional per-file import map (from ImportResolverBridge) * @param resolutionNodes Optional pre-existing nodes used only to seed the * call-resolution trie (not added to the returned nodes/edges). An * incremental subset rebuild passes the full set of known nodes so calls * into files outside the re-parsed subset resolve to their real node * instead of degrading to a synthetic `external::` leaf. */ build(files: Array<{ path: string; content: string; language: string; }>, layers?: Record, importMap?: ImportMap, resolutionNodes?: FunctionNode[]): Promise; private detectLayerViolations; } export declare function serializeCallGraph(result: CallGraphResult): SerializedCallGraph; export {}; //# sourceMappingURL=call-graph.d.ts.map