import ts from 'typescript'; /** Classifier output — milestone m2+ (all `any` source kinds). */ type SourceKind = "explicit-any" | "as-any" | "untyped-import" | "untyped-return" | "catch-binding" | "implicit-param"; interface AnySource { /** Project-relative POSIX-style path (forward slashes). */ filePath: string; /** 1-based line for the reported name location. */ line: number; /** 1-based column (UTF-16 code units) for the reported name location. */ column: number; /** Best-effort symbol/declaration name for humans. */ name: string; sourceKind: SourceKind; } /** Classifier row + graph metrics (m4). */ interface SourceRanked extends AnySource { rank: number; blastRadius: number; graphNodeId: string; } /** One step of the greedy set-cover over infected graph nodes (m5). */ interface GreedyCoverPick { pick: number; graphNodeId: string; filePath: string; line: number; column: number; name: string; sourceKind: SourceKind; blastRadius: number; blastRank: number; newlyCoveredNodes: number; cumulativeCoveredNodes: number; /** Rounded percent of all infected nodes covered after this pick (0–100). */ cumulativeCoveragePct: number; } interface ScanSummary { sources: AnySource[]; fileCount: number; /** Graph nodes with at least one `any` infection tag after propagation. */ infectedNodeCount: number; /** Greedy set-cover order: each pick maximizes newly covered infected nodes. */ greedyCoverPicks: GreedyCoverPick[]; /** `any` sources with blast radius, sorted descending (project graph reachability). */ sourcesRankedByBlast: SourceRanked[]; /** Overlap / set-cover interpretability metrics (when graph scan ran). */ health?: ScanHealth; } type SetCoverDistinctiveness = "high" | "low"; interface ScanHealth { top3GreedyPct: number; medianPairwiseOverlap: number; setCoverDistinctiveness: SetCoverDistinctiveness; summaryLine: string; } interface BlastChangedSource { before: SourceRanked; after: SourceRanked; deltaBlastRadius: number; } interface DiffSummary { requestedBaseRef: string; requestedHeadRef: string; effectiveBaseRef: string; effectiveHeadRef: string; compareMode: "merge-base"; scope: "changed-files" | "full-project-fallback"; /** Scan-root-relative POSIX paths included in the git delta. */ changedFiles: string[]; before: ScanSummary; after: ScanSummary; /** Sources present only in `after`, sorted by blast/rank from `after`. */ addedSources: SourceRanked[]; /** Sources present only in `before`, sorted by blast/rank from `before`. */ removedSources: SourceRanked[]; /** Sources present in both scans whose blast radius changed. */ blastChangedSources: BlastChangedSource[]; } type GraphNodeKind = "variable" | "parameter" | "return" | "property" | "import-binding" | "export-binding"; type EdgeReason = "assignment" | "call-return" | "destructure" | "import" | "re-export" | "property-access" | "parameter-binding" | "class-member" | "spread" | "type-alias" | "index-access"; interface GraphEdge { from: string; to: string; reason: EdgeReason; } /** JSON-safe snapshot for `--dump-graph`. */ interface SerializedGraphNode { id: string; filePath: string; line: number; column: number; name: string; kind: GraphNodeKind; typeString: string; isSource: boolean; sourceKind?: SourceKind; infectedBy: string[]; } /** JSON-safe snapshot for `--dump-graph`. */ interface SerializedGraph { nodes: SerializedGraphNode[]; edges: GraphEdge[]; } interface TraceHop { nodeId: string; filePath: string; line: number; column: number; name: string; kind: GraphNodeKind; } interface TracePathSegment { from: TraceHop; to: TraceHop; reason: EdgeReason; } interface TracePathToSource { sourceId: string; filePath: string; line: number; column: number; name: string; sourceKind: SourceKind; /** Forward from source toward the traced symbol (first hop starts at source). */ segments: TracePathSegment[]; } interface TraceReport { targetPath: string; loc: string; target: TraceHop; paths: TracePathToSource[]; } declare class GraphBuilder { private readonly program; private readonly projectRootAbs; private readonly checker; private readonly nodes; private readonly edgeList; /** Type flows `from` → `to` (PLAN §5.2). */ private readonly outgoing; private readonly exportChainCache; constructor(program: ts.Program, projectRootAbs: string); private rel; private isUserSourceFile; private addEdge; private typeStringAt; private ensureNamedDecl; private ensurePropertyNamedNode; private declarationForSymbol; private propertySymbolForElementAccess; private reasonForValueRead; private ensureFromValueDeclaration; private ensureImportBinding; private ensureExportBinding; private ensureBindingElement; private symbolName; private resolveAliasedSymbol; private moduleSymbolForSpecifier; private moduleKey; private exportedSymbolByName; private sourceFileForModuleSymbol; private exportChainMatches; private buildExportChain; private returnAnchor; private ensureReturnNode; /** Value-flow sources: identifiers / simple references with a registered declaration. */ exprToNodeId(expr: ts.Expression): string | undefined; private visitImportDeclaration; private edgesFromCall; private edgesFromObjectLiteral; private visitExpressionStatement; private visitVariableDeclaration; private visitReturnStatement; private visitPropertyDeclaration; private visitFunctionLike; private visit; build(): void; applySources(sources: AnySource[]): void; /** * Forward propagation (PLAN §5.3): along edges `from` → `to`, each source id tags reachable nodes in `infectedBy`. */ propagate(): void; countInfectedBy(sourceId: string): number; /** Count of graph nodes with non-empty `infectedBy` after `propagate()`. */ getInfectedNodeCount(): number; getEdges(): GraphEdge[]; /** Infected graph node ids per `any` source id (post-`propagate()`). */ getInfectedNodeSetsPerSource(): Map>; /** * Greedy set-cover: repeatedly pick the `any` source that covers the most still-uncovered infected nodes. * Uses per-source infected sets so each pass is O(sources × min(|infected(s)|, |uncovered|)), not O(sources × |nodes|). */ greedySetCoverPicks(blastRanked: SourceRanked[]): GreedyCoverPick[]; /** * Exact match on project-relative path (forward slashes) and reported identifier position. */ getTraceHop(nodeId: string): TraceHop | undefined; /** Source ids in `targetId`'s infection set that are graph `any` origins. */ listInfectedSourceIds(targetId: string): string[]; getAnySourceRow(nodeId: string): { filePath: string; line: number; column: number; name: string; sourceKind: SourceKind; } | undefined; findNodeIdAtLocation(filePath: string, line: number, column: number): string | undefined; rankSourcesByBlast(): SourceRanked[]; serialize(): SerializedGraph; } declare function buildSerializedGraph(program: ts.Program, projectRootAbs: string, sources: AnySource[]): SerializedGraph; /** * Health metrics for interpreting blast vs greedy rankings on this repo. */ declare function computeScanHealth(builder: GraphBuilder, summary: ScanSummary): ScanHealth; declare function readScanCache(repoRoot: string, commit: string, scanRoot: string, sourceKinds?: string[], ignoreGlobs?: string[]): ScanSummary | undefined; declare function writeScanCache(repoRoot: string, commit: string, scanRoot: string, summary: ScanSummary, sourceKinds?: string[], ignoreGlobs?: string[]): void; declare function countProjectSourceFiles(program: ts.Program): number; /** * Resolve a user path to an absolute directory (file → its dirname). */ declare function resolveScanRoot(userPath: string): string; interface CreateProgramOptions { /** Cap the number of root source files (after tsconfig expansion). */ maxFiles?: number; } /** * Create a TypeScript program for the tsconfig that governs `rootDir`. */ declare function createProgramForDirectory(rootDir: string, opts?: CreateProgramOptions): ts.Program; interface SourceFilters { /** If set, only these classifier kinds are kept. */ sourceKinds?: SourceKind[]; /** Picomatch globs (POSIX paths); matching `filePath` rows are dropped. */ ignoreGlobs?: string[]; } declare function parseSourceKindsList(csv: string): SourceKind[]; declare function parseIgnoreGlobsList(csv: string): string[]; declare function filterSources(sources: AnySource[], filters: SourceFilters): AnySource[]; interface DiffScanOptions extends SourceFilters { targetPath: string; baseRef: string; headRef: string; top?: number; maxFiles?: number; /** Persist scan summaries under `.any-map-cache/` in the git repo root. */ useCache?: boolean; } declare function applyTopToDiffSummary(summary: DiffSummary, top?: number): DiffSummary; declare function diffScan(options: DiffScanOptions): DiffSummary; declare function summarizeDiffTotals(summary: DiffSummary): { beforeSourceCount: number; afterSourceCount: number; sourceDelta: number; beforeInfectedNodeCount: number; afterInfectedNodeCount: number; infectedNodeDelta: number; beforeTop3CoveragePct: number; afterTop3CoveragePct: number; top3CoverageDeltaPct: number; }; type ReportVersion = 1 | 2; interface ScanReportV1 extends ScanSummary { } interface ScanReportV2 { reportVersion: 2; health?: ScanSummary["health"]; summary: { sources: ScanSummary["sources"]; fileCount: number; infectedNodeCount: number; }; rankings: { greedyCoverPicks: ScanSummary["greedyCoverPicks"]; sourcesRankedByBlast: ScanSummary["sourcesRankedByBlast"]; }; } interface DiffReportV2 { reportVersion: 2; meta: Pick; health: { before?: ScanSummary["health"]; after?: ScanSummary["health"]; }; totals: ReturnType; rankings: { before: ReturnType; after: ReturnType; }; diff: { addedSources: DiffSummary["addedSources"]; removedSources: DiffSummary["removedSources"]; blastChangedSources: DiffSummary["blastChangedSources"]; }; } declare function scanRankings(summary: ScanSummary): { greedyCoverPicks: GreedyCoverPick[]; sourcesRankedByBlast: SourceRanked[]; }; declare function toScanReport(summary: ScanSummary, version: ReportVersion): ScanReportV1 | ScanReportV2; declare function toDiffReport(summary: DiffSummary, version: ReportVersion): DiffSummary | DiffReportV2; declare function setSarifToolVersion(version: string): void; declare function scanSummaryToSarif(summary: ScanSummary): Record; declare function diffSummaryToSarif(summary: DiffSummary): Record; interface DiffFailureOptions { failOnNewSources?: boolean; failOnInfectedIncrease?: boolean; failOnBlastIncrease?: boolean; maxNewSources?: number; } declare function evaluateDiffFailure(summary: DiffSummary, opts: DiffFailureOptions): { failed: boolean; message?: string; }; interface ScanOptions extends SourceFilters { targetPath: string; json?: boolean; dumpGraph?: boolean; /** Limit display / JSON list fields to N rows; full scan is computed first. */ top?: number; maxFiles?: number; } /** * Build `ScanOptions` for `exactOptionalPropertyTypes`: only set keys when arguments are defined * (avoid `{ sourceKinds: undefined }` object literals). */ declare function buildScanOptions(targetPath: string, sourceKinds?: SourceKind[], ignoreGlobs?: string[], top?: number, maxFiles?: number): ScanOptions; type ScanResult = ScanSummary | SerializedGraph; interface FullScanResult { summary: ScanSummary; serializedGraph: SerializedGraph; } /** * Limit table / JSON list views. Does not change totals (`sources`, `infectedNodeCount`). * CI `--fail-coverage` must run on the **untruncated** summary from `runFullScan`. */ declare function applyTopToScanSummary(summary: ScanSummary, top?: number): ScanSummary; /** * Single graph build: summary + serialized graph (for DOT / `--dump-graph` / CI). */ declare function runFullScan(options: ScanOptions): FullScanResult; /** * Classify `any` sources (m2), build the project flow graph + propagation + blast ranking (m4), optional graph JSON (m3). */ declare function classifyScan(options: ScanOptions & { dumpGraph: true; }): SerializedGraph; declare function classifyScan(options?: ScanOptions): ScanSummary; /** * Graphviz DOT for an intra-module type-flow snapshot (`--format dot`, `any-map graph`). */ declare function serializedGraphToDot(g: SerializedGraph, title?: string): string; /** * All six v1 source kinds (PLAN §4), merged and de-duplicated by report location + kind. */ declare function findAnySources(program: ts.Program, projectRootAbs: string): AnySource[]; /** * Collect every explicit `: any`-style annotation (including `any[]`, `Record`, etc.), * excluding `as any` / `` / `satisfies any` assertions (handled in m2). */ declare function findExplicitAnySources(program: ts.Program, projectRootAbs: string): AnySource[]; /** * Stable node id (PLAN §4): SHA-1 of path, position, name, and optional discriminator. */ declare function makeNodeId(filePath: string, line: number, column: number, name: string, discriminator?: string): string; /** * Parse `file:line` or `file:line:column` (column defaults to 1). * The file segment may be project-relative; drive letters on Windows use `C:\...` — use `path\file.ts:line:col` without extra colons in the path, or a relative path. */ declare function parseTraceLocation(loc: string): { filePath: string; line: number; column: number; }; interface TraceOptions { targetPath: string; /** `relative/file.ts:line:column` or `file.ts:line` */ loc: string; } declare function traceSymbol(options: TraceOptions): TraceReport; export { type AnySource, type BlastChangedSource, type CreateProgramOptions, type DiffFailureOptions, type DiffReportV2, type DiffScanOptions, type DiffSummary, type EdgeReason, type FullScanResult, GraphBuilder, type GraphEdge, type GraphNodeKind, type GreedyCoverPick, type ReportVersion, type ScanHealth, type ScanOptions, type ScanReportV2, type ScanResult, type ScanSummary, type SerializedGraph, type SerializedGraphNode, type SetCoverDistinctiveness, type SourceFilters, type SourceKind, type SourceRanked, type TraceHop, type TraceOptions, type TracePathSegment, type TracePathToSource, type TraceReport, applyTopToDiffSummary, applyTopToScanSummary, buildScanOptions, buildSerializedGraph, classifyScan, computeScanHealth, countProjectSourceFiles, createProgramForDirectory, diffScan, diffSummaryToSarif, evaluateDiffFailure, filterSources, findAnySources, findExplicitAnySources, makeNodeId, parseIgnoreGlobsList, parseSourceKindsList, parseTraceLocation, readScanCache, resolveScanRoot, runFullScan, scanSummaryToSarif, serializedGraphToDot, setSarifToolVersion, summarizeDiffTotals, toDiffReport, toScanReport, traceSymbol, writeScanCache };