@titan-design/code-graph 0.1.0

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.
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/store.ts","../src/schema.ts","../src/rows.ts","../src/indexer.ts","../src/parser/parser.ts","../src/parser/file-filter.ts","../src/extractors/ts-morph-extractor.ts","../src/extractors/file-nodes.ts","../src/extractors/ids.ts","../src/extractors/reference-weight.ts","../src/declared-names.ts","../src/extractors/symbol-signature.ts","../src/extractors/symbol-nodes.ts","../src/extractors/dynamic-imports.ts","../src/extractors/module-resolution.ts","../src/extractors/reexport-resolve.ts","../src/extractors/python-extractor.ts","../src/extractors/dispatch.ts","../src/git-renames.ts","../src/generated.ts","../src/roles.ts","../src/file-walk.ts","../src/barrel-resolve.ts","../src/incremental.ts","../src/cognitive-complexity.ts","../src/lcom.ts","../src/source-metrics.ts","../src/reconstruct.ts","../src/reuse-delta.ts","../src/metrics.ts","../src/index-metrics.ts","../src/merge.ts","../src/aliases.ts","../src/read.ts"],"sourcesContent":["import { openDatabase, runMigrations, type Db } from \"@titan-design/store-sqlite\";\nimport { MIGRATIONS } from \"./schema.js\";\nimport {\n rowToAlias,\n rowToEdge,\n rowToFingerprint,\n rowToMetric,\n rowToNode,\n rowToSnapshot,\n type AliasDbRow,\n type EdgeDbRow,\n type FingerprintDbRow,\n type MetricDbRow,\n type NodeDbRow,\n type SnapshotDbRow,\n} from \"./rows.js\";\nimport type {\n FileFingerprint,\n GraphEdge,\n GraphMetric,\n GraphNode,\n IdAlias,\n SnapshotRow,\n} from \"./types.js\";\n\nexport interface SnapshotInsert {\n ref: string;\n commitHash?: string;\n indexVersion: string;\n attrs?: Record<string, unknown>;\n}\n\nconst NODE_COLS = \"id, kind, name, parent_id, language, role, attrs\";\n\n/**\n * The code graph's query surface over store-sqlite's snapshot-scoped kit tables\n * plus this package's domain tables. Replaces codewatch's `GraphDatabase`: the\n * connection, pragmas, and migration runner are the kit's; what is left here is\n * the code-specific statements the indexer and its readers need.\n */\nexport class CodeGraphStore {\n private readonly statements;\n\n constructor(readonly db: Db) {\n this.statements = prepareStatements(db);\n }\n\n createSnapshot(input: SnapshotInsert): number {\n const result = this.statements.insertSnapshot.run({\n ref: input.ref,\n commitHash: input.commitHash ?? null,\n indexVersion: input.indexVersion,\n attrs: JSON.stringify(input.attrs ?? {}),\n });\n return Number(result.lastInsertRowid);\n }\n\n insertNodes(snapshotId: number, nodes: readonly GraphNode[]): void {\n const stmt = this.statements.insertNode;\n this.db.transaction(() => {\n for (const n of nodes) {\n stmt.run({\n snapshotId,\n id: n.id,\n kind: n.kind,\n name: n.name,\n parentId: n.parentId ?? null,\n language: n.language ?? null,\n role: n.role ?? null,\n attrs: JSON.stringify(n.attrs ?? {}),\n });\n }\n })();\n }\n\n insertEdges(snapshotId: number, edges: readonly GraphEdge[]): void {\n const stmt = this.statements.insertEdge;\n this.db.transaction(() => {\n for (const e of edges) {\n stmt.run({\n snapshotId,\n srcId: e.srcId,\n dstId: e.dstId,\n kind: e.kind,\n attrs: JSON.stringify(e.attrs ?? {}),\n });\n }\n })();\n }\n\n insertMetrics(snapshotId: number, metrics: readonly GraphMetric[]): void {\n const stmt = this.statements.insertMetric;\n this.db.transaction(() => {\n for (const m of metrics) {\n stmt.run({ snapshotId, nodeId: m.nodeId, name: m.name, value: m.value, unit: m.unit ?? null });\n }\n })();\n }\n\n insertAliases(snapshotId: number, aliases: readonly IdAlias[]): void {\n const stmt = this.statements.insertAlias;\n this.db.transaction(() => {\n for (const a of aliases) {\n stmt.run({ snapshotId, oldId: a.oldId, newId: a.newId, reason: a.reason });\n }\n })();\n }\n\n insertFingerprints(snapshotId: number, fingerprints: readonly FileFingerprint[]): void {\n const stmt = this.statements.insertFingerprint;\n this.db.transaction(() => {\n for (const f of fingerprints) {\n stmt.run({\n snapshotId,\n fileId: f.fileId,\n contentHash: f.contentHash,\n structuralHash: f.structuralHash ?? null,\n });\n }\n })();\n }\n\n getSnapshot(id: number): SnapshotRow | null {\n const row = this.statements.getSnapshot.get(id) as SnapshotDbRow | undefined;\n return row ? rowToSnapshot(row) : null;\n }\n\n listSnapshots(opts: { ref?: string; limit?: number } = {}): SnapshotRow[] {\n const limit = opts.limit ?? 50;\n const rows = (\n opts.ref\n ? this.statements.listSnapshotsByRef.all(opts.ref, limit)\n : this.statements.listSnapshots.all(limit)\n ) as SnapshotDbRow[];\n return rows.map(rowToSnapshot);\n }\n\n getLatestSnapshotByRef(ref: string): SnapshotRow | null {\n return this.listSnapshots({ ref, limit: 1 })[0] ?? null;\n }\n\n getNode(snapshotId: number, id: string): GraphNode | null {\n const row = this.statements.getNode.get(snapshotId, id) as NodeDbRow | undefined;\n return row ? rowToNode(row) : null;\n }\n\n /**\n * File-level structural graph by default: the per-symbol layer is excluded so\n * consumers that reason about module structure see the graph they expect. Pass\n * `includeSymbols` for the symbol layer (the reuse basis needs it).\n */\n listNodes(snapshotId: number, opts?: { includeSymbols?: boolean }): GraphNode[] {\n const nodes = (this.statements.listNodes.all(snapshotId) as NodeDbRow[]).map(rowToNode);\n return opts?.includeSymbols ? nodes : nodes.filter((n) => n.kind !== \"symbol\");\n }\n\n /** See {@link listNodes}: `references` edges are the symbol layer, excluded by default. */\n listEdges(snapshotId: number, opts?: { includeReferences?: boolean }): GraphEdge[] {\n const edges = (this.statements.listEdges.all(snapshotId) as EdgeDbRow[]).map(rowToEdge);\n return opts?.includeReferences ? edges : edges.filter((e) => e.kind !== \"references\");\n }\n\n listMetrics(snapshotId: number): GraphMetric[] {\n return (this.statements.listMetrics.all(snapshotId) as MetricDbRow[]).map(rowToMetric);\n }\n\n listAliases(snapshotId: number): IdAlias[] {\n return (this.statements.listAliases.all(snapshotId) as AliasDbRow[]).map(rowToAlias);\n }\n\n listFingerprints(snapshotId: number): FileFingerprint[] {\n return (this.statements.listFingerprints.all(snapshotId) as FingerprintDbRow[]).map(rowToFingerprint);\n }\n\n close(): void {\n this.db.close();\n }\n}\n\nfunction prepareStatements(db: Db) {\n return {\n insertSnapshot: db.prepare(\n `INSERT INTO snapshot (ref, commit_hash, index_version, attrs)\n VALUES (@ref, @commitHash, @indexVersion, @attrs)`,\n ),\n insertNode: db.prepare(\n `INSERT OR REPLACE INTO node (snapshot_id, id, kind, name, parent_id, language, role, attrs)\n VALUES (@snapshotId, @id, @kind, @name, @parentId, @language, @role, @attrs)`,\n ),\n insertEdge: db.prepare(\n `INSERT OR REPLACE INTO edge (snapshot_id, src_id, dst_id, kind, attrs)\n VALUES (@snapshotId, @srcId, @dstId, @kind, @attrs)`,\n ),\n insertMetric: db.prepare(\n `INSERT OR REPLACE INTO metric (snapshot_id, node_id, name, value, unit)\n VALUES (@snapshotId, @nodeId, @name, @value, @unit)`,\n ),\n insertAlias: db.prepare(\n `INSERT OR REPLACE INTO id_alias (snapshot_id, old_id, new_id, reason)\n VALUES (@snapshotId, @oldId, @newId, @reason)`,\n ),\n insertFingerprint: db.prepare(\n `INSERT OR REPLACE INTO file_fingerprint (snapshot_id, file_id, content_hash, structural_hash)\n VALUES (@snapshotId, @fileId, @contentHash, @structuralHash)`,\n ),\n getSnapshot: db.prepare(\"SELECT * FROM snapshot WHERE id = ?\"),\n listSnapshots: db.prepare(\"SELECT * FROM snapshot ORDER BY taken_at DESC, id DESC LIMIT ?\"),\n listSnapshotsByRef: db.prepare(\n \"SELECT * FROM snapshot WHERE ref = ? ORDER BY taken_at DESC, id DESC LIMIT ?\",\n ),\n getNode: db.prepare(`SELECT ${NODE_COLS} FROM node WHERE snapshot_id = ? AND id = ?`),\n listNodes: db.prepare(`SELECT ${NODE_COLS} FROM node WHERE snapshot_id = ?`),\n listEdges: db.prepare(\"SELECT src_id, dst_id, kind, attrs FROM edge WHERE snapshot_id = ?\"),\n listMetrics: db.prepare(\"SELECT node_id, name, value, unit FROM metric WHERE snapshot_id = ?\"),\n listAliases: db.prepare(\"SELECT old_id, new_id, reason FROM id_alias WHERE snapshot_id = ?\"),\n listFingerprints: db.prepare(\n \"SELECT file_id, content_hash, structural_hash FROM file_fingerprint WHERE snapshot_id = ?\",\n ),\n };\n}\n\n/** Open (creating if absent) a code graph database and bring it to the current schema. */\nexport function openCodeGraph(dbPath: string): CodeGraphStore {\n const db = openDatabase(dbPath);\n runMigrations(db, MIGRATIONS);\n return new CodeGraphStore(db);\n}\n","import { kitMigration, type Migration } from \"@titan-design/store-sqlite\";\n\n/**\n * Kit table names. The code graph is snapshot-scoped end to end: a whole repo is\n * re-indexed together, so `snapshotTableDdl` + `entitySnapTableDdl` are the right\n * time model and the interval `edgeTableDdl` is not — one unchanged import would\n * otherwise write a row per file per snapshot. The edge table below is therefore\n * the snapshot-scoped counterpart, owned here.\n */\nexport const KIT = { snapshot: \"snapshot\", entitySnap: \"node\", cacheBlob: \"blob_cache\" } as const;\n\n/**\n * Two columns the kit's `entity_snap` shape does not carry but every consumer of\n * a code graph reads directly, plus the snapshot provenance the reuse basis\n * selects on. Kept as real columns rather than folded into `attrs` so the\n * vocabularies stay queryable and stay codewatch-compatible.\n */\nconst KIT_EXTENSIONS = `\n ALTER TABLE node ADD COLUMN language TEXT;\n ALTER TABLE node ADD COLUMN role TEXT;\n ALTER TABLE snapshot ADD COLUMN commit_hash TEXT;\n ALTER TABLE snapshot ADD COLUMN index_version TEXT NOT NULL DEFAULT '';\n`;\n\n/**\n * Domain tables. `edge` is the snapshot-scoped edge shape (the kit's own edge\n * table is bi-temporal); `metric` holds index-time measurements keyed by node;\n * `id_alias` carries a node id across a rename so two snapshots line up; and\n * `file_fingerprint` is the reuse basis the incremental indexer diffs against.\n */\nexport const DOMAIN_DDL = `\n CREATE TABLE IF NOT EXISTS edge (\n snapshot_id INTEGER NOT NULL,\n src_id TEXT NOT NULL,\n dst_id TEXT NOT NULL,\n kind TEXT NOT NULL,\n attrs TEXT,\n PRIMARY KEY (snapshot_id, src_id, dst_id, kind)\n );\n CREATE INDEX IF NOT EXISTS idx_edge_dst ON edge(snapshot_id, dst_id);\n CREATE INDEX IF NOT EXISTS idx_edge_kind ON edge(snapshot_id, kind);\n\n CREATE TABLE IF NOT EXISTS metric (\n snapshot_id INTEGER NOT NULL,\n node_id TEXT NOT NULL,\n name TEXT NOT NULL,\n value REAL,\n unit TEXT,\n PRIMARY KEY (snapshot_id, node_id, name)\n );\n CREATE INDEX IF NOT EXISTS idx_metric_name ON metric(snapshot_id, name);\n\n CREATE TABLE IF NOT EXISTS id_alias (\n snapshot_id INTEGER NOT NULL,\n old_id TEXT NOT NULL,\n new_id TEXT NOT NULL,\n reason TEXT NOT NULL,\n PRIMARY KEY (snapshot_id, old_id, new_id)\n );\n\n CREATE TABLE IF NOT EXISTS file_fingerprint (\n snapshot_id INTEGER NOT NULL,\n file_id TEXT NOT NULL,\n content_hash TEXT NOT NULL,\n structural_hash TEXT,\n PRIMARY KEY (snapshot_id, file_id)\n );\n`;\n\nexport const MIGRATIONS: Migration[] = [\n kitMigration(1, { snapshot: KIT.snapshot, entitySnap: KIT.entitySnap, cacheBlob: KIT.cacheBlob }),\n { version: 2, name: \"code graph columns\", up: (db) => db.exec(KIT_EXTENSIONS) },\n { version: 3, name: \"code graph tables\", up: (db) => db.exec(DOMAIN_DDL) },\n];\n","import type {\n FileFingerprint,\n GraphEdge,\n GraphMetric,\n GraphNode,\n IdAlias,\n IdAliasReason,\n SnapshotRow,\n} from \"./types.js\";\n\n// Raw row shapes as stored in SQLite, paired with mappers to the domain types.\n// Kept separate from the connection/statement manager in database.ts.\n\nexport interface SnapshotDbRow {\n id: number;\n ref: string;\n commit_hash: string | null;\n taken_at: string;\n index_version: string;\n attrs: string;\n}\n\nexport interface NodeDbRow {\n id: string;\n kind: string;\n name: string;\n parent_id: string | null;\n language: string | null;\n role: string | null;\n attrs: string;\n}\n\nexport interface EdgeDbRow {\n src_id: string;\n dst_id: string;\n kind: string;\n attrs: string;\n}\n\nexport interface MetricDbRow {\n node_id: string;\n name: string;\n value: number | null;\n unit: string | null;\n}\n\nexport interface AliasDbRow {\n old_id: string;\n new_id: string;\n reason: string;\n}\n\nexport interface FingerprintDbRow {\n file_id: string;\n content_hash: string;\n structural_hash: string | null;\n}\n\nexport function rowToSnapshot(row: SnapshotDbRow): SnapshotRow {\n return {\n id: row.id,\n ref: row.ref,\n commitHash: row.commit_hash,\n takenAt: row.taken_at,\n indexVersion: row.index_version,\n attrs: JSON.parse(row.attrs) as Record<string, unknown>,\n };\n}\n\nexport function rowToNode(row: NodeDbRow): GraphNode {\n return {\n id: row.id,\n kind: row.kind as GraphNode[\"kind\"],\n name: row.name,\n parentId: row.parent_id ?? undefined,\n language: row.language ?? undefined,\n role: (row.role ?? undefined) as GraphNode[\"role\"],\n attrs: JSON.parse(row.attrs) as Record<string, unknown>,\n };\n}\n\nexport function rowToEdge(row: EdgeDbRow): GraphEdge {\n return {\n srcId: row.src_id,\n dstId: row.dst_id,\n kind: row.kind as GraphEdge[\"kind\"],\n attrs: JSON.parse(row.attrs) as Record<string, unknown>,\n };\n}\n\nexport function rowToMetric(row: MetricDbRow): GraphMetric {\n return {\n nodeId: row.node_id,\n name: row.name,\n value: row.value,\n unit: row.unit ?? undefined,\n };\n}\n\nexport function rowToAlias(row: AliasDbRow): IdAlias {\n return {\n oldId: row.old_id,\n newId: row.new_id,\n reason: row.reason as IdAliasReason,\n };\n}\n\nexport function rowToFingerprint(row: FingerprintDbRow): FileFingerprint {\n return {\n fileId: row.file_id,\n contentHash: row.content_hash,\n structuralHash: row.structural_hash ?? undefined,\n };\n}\n","import { realpathSync } from \"node:fs\";\nimport * as path from \"node:path\";\nimport { parseFile, type ParsedFile } from \"./parser/index.js\";\nimport type { CodeGraphStore } from \"./store.js\";\nimport { LanguageExtractor } from \"./extractors/dispatch.js\";\nimport {\n buildAliases,\n detectGitHead,\n detectGitToplevel,\n detectRenames,\n isInsideGitRepo,\n} from \"./git-renames.js\";\nimport { annotateRoles, computeRoleHints } from \"./roles.js\";\nimport { walkSourceFiles } from \"./file-walk.js\";\nimport { pruneDanglingReferences } from \"./barrel-resolve.js\";\nimport { fileId } from \"./extractors/ids.js\";\nimport {\n assembleFragments,\n buildFingerprints,\n classifyForReuse,\n classifyParsed,\n loadReuseBasis,\n readSourceFiles,\n} from \"./incremental.js\";\nimport { computeDeltaAffected } from \"./reuse-delta.js\";\nimport { buildIndexerMetrics } from \"./index-metrics.js\";\nimport { mergeFragments, type ExtractAccumulator } from \"./merge.js\";\nimport type { GraphMetric, IdAlias } from \"./types.js\";\n\n/**\n * Bumping this invalidates every reuse basis: a snapshot written by a different\n * index version is never reused, so a change to node/edge shape can never be\n * carried forward from an incompatible graph.\n */\nexport const INDEX_VERSION = \"0.11.0\";\n\n/** The languages walked and extracted. `typescript` covers `.ts` and `.tsx`. */\nconst LANGUAGES = [\"typescript\", \"python\"] as const;\n\nexport interface IndexOptions {\n /** Roots to walk. Node ids are still rooted at the git toplevel, so importers across roots share an id space. */\n paths: string[];\n ref?: string;\n commitHash?: string;\n tsConfig?: string;\n detectRenames?: boolean;\n computeMetrics?: boolean;\n /**\n * Reuse the prior snapshot for byte-identical files: skip their tree-sitter\n * parse + ts-morph extract and carry their nodes/edges/source metrics forward.\n * Defaults to `true`; the reuse path is equivalent to a full index, so the only\n * reason to disable it is to rebuild from scratch.\n */\n incremental?: boolean;\n}\n\nexport interface IndexResult {\n snapshotId: number;\n files: number;\n nodes: number;\n edges: number;\n aliases: number;\n metrics: number;\n /** Files whose parse + extract were skipped by reusing the prior snapshot. */\n reused: number;\n /** Files parsed + extracted this run (new, changed, or full index). */\n reparsed: number;\n /** Parsed files whose extract was skipped as cosmetic; a subset of `reparsed`. */\n cosmetic: number;\n nodesByKind: Record<string, number>;\n edgesByKind: Record<string, number>;\n}\n\nfunction canonicalizePath(p: string): string {\n try {\n return realpathSync(p);\n } catch {\n return p;\n }\n}\n\nfunction normalizePaths(paths: readonly string[]): string[] {\n if (paths.length === 0) throw new Error(\"indexPaths requires at least one path\");\n return [...new Set(paths.map((p) => path.resolve(p)))];\n}\n\nfunction countByKind<T extends { kind: string }>(items: Iterable<T>): Record<string, number> {\n const out: Record<string, number> = {};\n for (const item of items) out[item.kind] = (out[item.kind] ?? 0) + 1;\n return out;\n}\n\nfunction persist(\n store: CodeGraphStore,\n ref: string,\n commitHash: string | undefined,\n accumulator: ExtractAccumulator,\n aliases: readonly IdAlias[],\n metrics: readonly GraphMetric[],\n): number {\n const snapshotId = store.createSnapshot({ ref, commitHash, indexVersion: INDEX_VERSION });\n store.insertNodes(snapshotId, [...accumulator.nodes.values()]);\n store.insertEdges(snapshotId, [...accumulator.edges.values()]);\n if (aliases.length > 0) store.insertAliases(snapshotId, aliases);\n if (metrics.length > 0) store.insertMetrics(snapshotId, metrics);\n return snapshotId;\n}\n\nfunction findPriorCommit(store: CodeGraphStore): string | null {\n return store.listSnapshots({ limit: 50 }).find((s) => s.commitHash)?.commitHash ?? null;\n}\n\n/** Id aliases bridging a rename between the prior snapshot's commit and this one. */\nfunction resolveAliases(\n rootDir: string,\n idRoot: string,\n options: IndexOptions,\n store: CodeGraphStore,\n): IdAlias[] {\n if (options.detectRenames === false) return [];\n if (!isInsideGitRepo(rootDir)) return [];\n const priorCommit = findPriorCommit(store);\n if (!priorCommit) return [];\n const target = options.commitHash ?? detectGitHead(rootDir) ?? undefined;\n if (target === priorCommit) return [];\n return buildAliases(idRoot, detectRenames({ repoRoot: rootDir, fromCommit: priorCommit, toCommit: target }));\n}\n\n/**\n * Node ids are rooted at the git toplevel so a subtree index shares an id space\n * with a whole-repo one. Inside git, roots are canonicalized to match\n * `git rev-parse --show-toplevel`, which resolves symlinks (`/var` →\n * `/private/var` on macOS); outside git they are left as the caller wrote them.\n */\nfunction resolveRoots(paths: readonly string[]): { rootDirs: string[]; idRoot: string } {\n const raw = normalizePaths(paths);\n const gitToplevel = detectGitToplevel(raw[0]!);\n const rootDirs = gitToplevel !== null ? raw.map(canonicalizePath) : raw;\n return { rootDirs, idRoot: gitToplevel ?? rootDirs[0]! };\n}\n\nasync function parseAll(\n toParse: readonly { filePath: string; content: string; language: string }[],\n): Promise<Map<string, ParsedFile>> {\n const parsed = new Map<string, ParsedFile>();\n for (const rf of toParse) {\n parsed.set(rf.filePath, await parseFile(rf.content, rf.filePath, rf.language));\n }\n return parsed;\n}\n\n/**\n * Index one or more roots into a new snapshot: walk, read, parse what changed,\n * extract, annotate roles, compute metrics, and persist. Files unchanged since\n * the prior snapshot are carried forward rather than re-parsed, so the result\n * matches a full index regardless of how much was reused.\n */\nexport async function indexPaths(store: CodeGraphStore, options: IndexOptions): Promise<IndexResult> {\n const { rootDirs, idRoot } = resolveRoots(options.paths);\n const readFiles = await readSourceFiles(await walkSourceFiles(rootDirs, LANGUAGES));\n\n const reuse = options.incremental !== false ? loadReuseBasis(store, INDEX_VERSION) : null;\n const currentFileIds = new Set(readFiles.map((rf) => fileId(idRoot, rf.filePath)));\n // On a file-membership delta, reuse every unchanged file except those whose\n // imports the delta re-resolves; empty when membership is unchanged.\n const affected = reuse ? computeDeltaAffected(reuse, currentFileIds) : new Set<string>();\n const { toParse, reusedFileIds } = classifyForReuse(readFiles, idRoot, reuse, affected);\n\n const parsedByPath = await parseAll(toParse);\n const classified = classifyParsed(parsedByPath, idRoot, reuse, reusedFileIds, affected);\n const accumulator = mergeFragments(\n assembleFragments({\n readFiles,\n idRoot,\n parsedByPath,\n reuse,\n cosmeticFileIds: classified.cosmeticFileIds,\n extractor: new LanguageExtractor({ repoRoot: idRoot, tsConfigPath: options.tsConfig }),\n }),\n );\n const annotated = annotateRoles([...accumulator.nodes.values()], computeRoleHints(readFiles, idRoot, fileId));\n accumulator.nodes = new Map(annotated.map((n) => [n.id, n]));\n pruneDanglingReferences(accumulator.nodes, accumulator.edges);\n\n const metrics =\n options.computeMetrics === false\n ? []\n : buildIndexerMetrics({\n nodes: accumulator.nodes,\n edges: accumulator.edges,\n parsedFiles: [...parsedByPath.values()],\n reusedSourceMetrics: reuse\n ? reusedFileIds.flatMap((id) => reuse.sourceMetricsByFile.get(id) ?? [])\n : [],\n idRoot,\n });\n\n const rootDir = rootDirs[0]!;\n const aliases = resolveAliases(rootDir, idRoot, options, store);\n const commitHash = options.commitHash ?? detectGitHead(rootDir) ?? undefined;\n const snapshotId = persist(store, options.ref ?? \"wd\", commitHash, accumulator, aliases, metrics);\n store.insertFingerprints(snapshotId, buildFingerprints(readFiles, idRoot, classified.structuralByFileId));\n\n return {\n snapshotId,\n files: readFiles.length,\n nodes: accumulator.nodes.size,\n edges: accumulator.edges.size,\n aliases: aliases.length,\n metrics: metrics.length,\n reused: reusedFileIds.length,\n reparsed: toParse.length,\n cosmetic: classified.cosmeticFileIds.size,\n nodesByKind: countByKind(accumulator.nodes.values()),\n edgesByKind: countByKind(accumulator.edges.values()),\n };\n}\n","import { Parser, Language } from \"web-tree-sitter\";\nimport { createRequire } from \"node:module\";\nimport type { ParsedFile } from \"./types.js\";\n\nconst require = createRequire(import.meta.url);\n\nlet initialized = false;\nconst parsers = new Map<string, Parser>();\nconst languages = new Map<string, Language>();\n\nfunction resolveWasmPath(language: string): string {\n switch (language) {\n case \"typescript\":\n return require.resolve(\"tree-sitter-typescript/tree-sitter-typescript.wasm\");\n case \"tsx\":\n return require.resolve(\"tree-sitter-typescript/tree-sitter-tsx.wasm\");\n case \"python\":\n return require.resolve(\"tree-sitter-python/tree-sitter-python.wasm\");\n default:\n throw new Error(`Unsupported language: ${language}`);\n }\n}\n\nasync function ensureInitialized(): Promise<void> {\n if (initialized) return;\n await Parser.init();\n initialized = true;\n}\n\nasync function getLanguage(language: string): Promise<Language> {\n const cached = languages.get(language);\n if (cached) return cached;\n\n const wasmPath = resolveWasmPath(language);\n const lang = await Language.load(wasmPath);\n languages.set(language, lang);\n return lang;\n}\n\nasync function getParser(language: string): Promise<Parser> {\n const cached = parsers.get(language);\n if (cached) return cached;\n\n // Validate language before initializing WASM\n resolveWasmPath(language);\n\n await ensureInitialized();\n const lang = await getLanguage(language);\n const parser = new Parser();\n parser.setLanguage(lang);\n parsers.set(language, parser);\n return parser;\n}\n\nexport async function parseFile(\n content: string,\n filePath: string,\n language: string,\n): Promise<ParsedFile> {\n const parser = await getParser(language);\n const tree = parser.parse(content);\n if (!tree) {\n throw new Error(`Failed to parse ${filePath}`);\n }\n return { tree, content, filePath, language };\n}\n\nexport function getSupportedLanguages(): string[] {\n return [\"typescript\", \"tsx\", \"python\"];\n}\n","import * as path from \"node:path\";\n\nconst EXCLUDED_DIRS = [\n \"node_modules\",\n \"vendor\",\n \"dist\",\n \"build\",\n \".next\",\n \"__generated__\",\n \".git\",\n \".claude\",\n \"coverage\",\n];\n\nconst EXCLUDED_PATTERNS = [\n /\\.min\\.[jt]sx?$/,\n /\\.d\\.ts$/,\n /\\.map$/,\n /lock\\.(json|yaml)$/,\n /pnpm-lock\\.yaml$/,\n /package-lock\\.json$/,\n /yarn\\.lock$/,\n];\n\nconst LANGUAGE_EXTENSIONS: Record<string, string[]> = {\n typescript: [\".ts\", \".tsx\"],\n javascript: [\".js\", \".jsx\"],\n python: [\".py\"],\n};\n\n/**\n * True when a directory NAME should be pruned from a recursive source walk\n * (node_modules, dist, .git, …). File discovery already rejects files under\n * these via `shouldIncludeFile`, but a walker that recurses into them anyway\n * pays to `readdir` the entire tree (e.g. a multi-GB `node_modules`) only to\n * discard every file — so pruning at the directory level is a large speedup and\n * changes no output. Kept in sync with `EXCLUDED_DIRS`.\n */\nexport function isExcludedDir(dirName: string): boolean {\n return EXCLUDED_DIRS.includes(dirName);\n}\n\nexport function shouldIncludeFile(\n filePath: string,\n languages: string[],\n): boolean {\n const segments = filePath.split(\"/\");\n if (segments.some((s) => EXCLUDED_DIRS.includes(s))) {\n return false;\n }\n\n if (EXCLUDED_PATTERNS.some((p) => p.test(filePath))) {\n return false;\n }\n\n const ext = path.extname(filePath);\n const allowedExtensions = languages.flatMap(\n (lang) => LANGUAGE_EXTENSIONS[lang] ?? [],\n );\n\n return allowedExtensions.includes(ext);\n}\n\nexport function getLanguageFromPath(filePath: string): string | null {\n const ext = path.extname(filePath);\n for (const [language, extensions] of Object.entries(LANGUAGE_EXTENSIONS)) {\n if (extensions.includes(ext)) {\n return language;\n }\n }\n return null;\n}\n","import * as path from \"node:path\";\nimport { existsSync } from \"node:fs\";\nimport {\n Project,\n ScriptTarget,\n ModuleKind,\n ModuleResolutionKind,\n type ExportDeclaration,\n type ImportDeclaration,\n type SourceFile,\n} from \"ts-morph\";\nimport type { Extractor, ParsedFile } from \"../parser/index.js\";\nimport type { GraphEdge, GraphFragment, GraphNode } from \"../types.js\";\nimport { buildFileModuleNodes } from \"./file-nodes.js\";\nimport { externalId, fileId, symbolId } from \"./ids.js\";\nimport {\n addWeightedEdge,\n bindingWeight,\n buildLocalUsageCounts,\n importWeight,\n namedImportBindings,\n reExportWeight,\n} from \"./reference-weight.js\";\nimport { buildSymbolNodes } from \"./symbol-nodes.js\";\nimport {\n dynamicImportSpecifiers,\n recordDynamicSymbolRefEdges,\n} from \"./dynamic-imports.js\";\nimport { resolveReExportOrigin } from \"./reexport-resolve.js\";\nimport {\n inRepoFileId,\n isRelativeSpecifier,\n resolveRelativeAbs,\n} from \"./module-resolution.js\";\n\n/** Mutable accumulator threaded through one file's edge collection. */\ninterface EdgeCollector {\n srcAbs: string;\n srcFileId: string;\n usage: Map<string, number>;\n agg: Map<string, GraphEdge>;\n externalNodes: GraphNode[];\n seenExternals: Set<string>;\n}\n\nexport interface TsMorphGraphExtractorOptions {\n repoRoot: string;\n tsConfigPath?: string;\n project?: Project;\n}\n\nexport class TsMorphGraphExtractor implements Extractor<GraphFragment> {\n readonly name = \"ts-morph-graph\";\n private readonly repoRoot: string;\n private readonly tsConfigPath?: string;\n private project?: Project;\n\n constructor(options: TsMorphGraphExtractorOptions) {\n this.repoRoot = options.repoRoot;\n this.tsConfigPath = options.tsConfigPath;\n this.project = options.project;\n }\n\n extract(file: ParsedFile): GraphFragment[] {\n if (!isTypeScriptFile(file)) return [];\n\n const project = this.ensureProject();\n const sourceFile = this.loadSourceFile(project, file);\n const nodes = this.buildFileAndModuleNodes(sourceFile);\n const symbolNodes = buildSymbolNodes(this.repoRoot, sourceFile, file);\n const { edges, externalNodes } = this.collectEdges(sourceFile);\n return [{ nodes: [...nodes, ...symbolNodes, ...externalNodes], edges }];\n }\n\n private ensureProject(): Project {\n if (this.project) return this.project;\n this.project = this.tsConfigPath\n ? new Project({ tsConfigFilePath: this.tsConfigPath })\n : new Project({\n compilerOptions: {\n allowJs: true,\n target: ScriptTarget.ESNext,\n module: ModuleKind.ESNext,\n moduleResolution: ModuleResolutionKind.NodeNext,\n },\n });\n return this.project;\n }\n\n private loadSourceFile(project: Project, file: ParsedFile): SourceFile {\n const existing = project.getSourceFile(file.filePath);\n if (existing) {\n if (existing.getFullText() !== file.content) {\n existing.replaceWithText(file.content);\n }\n return existing;\n }\n return project.createSourceFile(file.filePath, file.content, {\n overwrite: true,\n });\n }\n\n private buildFileAndModuleNodes(sourceFile: SourceFile): GraphNode[] {\n return buildFileModuleNodes(this.repoRoot, sourceFile.getFilePath());\n }\n\n private collectEdges(sourceFile: SourceFile): {\n edges: GraphEdge[];\n externalNodes: GraphNode[];\n } {\n const c: EdgeCollector = {\n srcAbs: sourceFile.getFilePath(),\n srcFileId: fileId(this.repoRoot, sourceFile.getFilePath()),\n usage: buildLocalUsageCounts(sourceFile),\n agg: new Map(),\n externalNodes: [],\n seenExternals: new Set(),\n };\n for (const decl of sourceFile.getImportDeclarations()) {\n this.recordImportEdge(c, decl);\n }\n for (const decl of sourceFile.getExportDeclarations()) {\n this.recordReExportEdge(c, decl);\n }\n for (const specifier of dynamicImportSpecifiers(sourceFile)) {\n const dstId = this.resolveRelativeInternal(c.srcAbs, specifier);\n if (dstId) addWeightedEdge(c.agg, c.srcFileId, dstId, \"imports\", specifier, 1);\n }\n // C-68: destructured dynamic imports (`const { x } = await import(...)`) also\n // credit the target symbol; resolution is delegated so this stays out of the\n // (churn-hot, LOC-bound) extractor body.\n recordDynamicSymbolRefEdges(sourceFile, c.srcFileId, c.agg, (s) =>\n this.resolveRelativeInternal(c.srcAbs, s),\n );\n return { edges: [...c.agg.values()], externalNodes: c.externalNodes };\n }\n\n private recordImportEdge(c: EdgeCollector, decl: ImportDeclaration): void {\n const specifier = decl.getModuleSpecifierValue();\n const dstId = this.resolveTarget(c, specifier, decl);\n if (dstId) {\n addWeightedEdge(c.agg, c.srcFileId, dstId, \"imports\", specifier, importWeight(decl, c.usage));\n }\n this.recordSymbolReferences(c, decl);\n }\n\n /**\n * Split an import into per-export `references` edges targeting `symbol` nodes,\n * so per-symbol utilization (which exports are hot) falls out of the same\n * inbound-weight sum that powers file utilization (C-53). Extracted *forward*\n * from the importing file — source-local, exactly like the file-level import\n * weight — which sidesteps the reuse-breaking reverse `findReferences` query:\n * an unchanged file's outbound reference edges are carried forward verbatim.\n */\n private recordSymbolReferences(c: EdgeCollector, decl: ImportDeclaration): void {\n const specifier = decl.getModuleSpecifierValue();\n for (const binding of namedImportBindings(decl)) {\n const dstId = this.resolveSymbolTarget(c, decl, binding.importedName);\n if (!dstId) continue;\n addWeightedEdge(\n c.agg,\n c.srcFileId,\n dstId,\n \"references\",\n specifier,\n bindingWeight(binding, c.usage),\n );\n }\n }\n\n /**\n * Resolve an imported name to the id of the `symbol` node for the export that\n * actually *declares* it — following re-exports through barrels via ts-morph's\n * own `getExportedDeclarations`, so `import { x } from \"./barrel\"` credits the\n * origin file's `x`, not the barrel. When ts-morph can't resolve the specifier\n * (e.g. an extensionless relative import), falls back to a filesystem-based\n * re-export walk that still sees through barrel hops (C-70). Returns null for\n * external / unresolved targets.\n * A name that resolves to no local declaration (an aliased re-export the\n * origin doesn't export under this name) yields a dangling edge, pruned after\n * assembly (`pruneDanglingReferences`).\n */\n private resolveSymbolTarget(\n c: EdgeCollector,\n decl: ImportDeclaration,\n importedName: string,\n ): string | null {\n const targetSf = decl.getModuleSpecifierSourceFile();\n if (targetSf) {\n const decls = targetSf.getExportedDeclarations().get(importedName);\n const originSf =\n decls && decls.length > 0 ? decls[0]!.getSourceFile() : targetSf;\n const originId = this.inRepoFileId(remapDistToSrc(originSf.getFilePath()));\n return originId ? symbolId(originId, importedName) : null;\n }\n // ts-morph couldn't link the specifier (extensionless / out-of-project);\n // trace the target's re-export hops on disk so an import through an\n // extensionless barrel still credits the origin symbol (C-70).\n return resolveReExportOrigin(\n { project: this.ensureProject(), repoRoot: this.repoRoot },\n c.srcAbs,\n decl.getModuleSpecifierValue(),\n importedName,\n );\n }\n\n private recordReExportEdge(c: EdgeCollector, decl: ExportDeclaration): void {\n if (!decl.hasModuleSpecifier()) return;\n const specifier = decl.getModuleSpecifierValue();\n if (!specifier) return;\n const dstId = this.resolveTarget(c, specifier, decl);\n if (dstId) {\n addWeightedEdge(c.agg, c.srcFileId, dstId, \"re-exports\", specifier, reExportWeight(decl));\n }\n }\n\n /**\n * Resolve an import/export specifier to a graph node id (an in-repo file id or\n * an `external` node, creating the external node on first sight), or `null`\n * when it should not produce an edge.\n */\n private resolveTarget(\n c: EdgeCollector,\n specifier: string,\n decl: ImportDeclaration | ExportDeclaration,\n ): string | null {\n const internal =\n this.resolveInternal(decl.getModuleSpecifierSourceFile()) ??\n this.resolveRelativeInternal(c.srcAbs, specifier);\n if (internal) return internal;\n if (isRelativeSpecifier(specifier)) {\n // A relative import ts-morph could not resolve and that does not point at\n // a file on disk (e.g. an alias, or a genuinely missing target). Dropping\n // it is correct: bucketing it as an npm external produced `npm:..` junk\n // nodes and masked real internal edges for any out-of-project TS (C-44).\n return null;\n }\n const extId = externalId(specifier);\n if (!c.seenExternals.has(extId)) {\n c.seenExternals.add(extId);\n c.externalNodes.push({ id: extId, kind: \"external\", name: specifier });\n }\n return extId;\n }\n\n private resolveInternal(target: SourceFile | undefined): string | null {\n if (!target) return null;\n return this.inRepoFileId(remapDistToSrc(target.getFilePath()));\n }\n\n /**\n * Resolve a relative specifier ts-morph failed to link by walking the\n * filesystem from the importing file. ts-morph's NodeNext resolution only\n * links relative imports that carry an explicit `.js` extension, so\n * extensionless bundler-style imports (`../types`, common in code outside the\n * tsconfig project such as `dashboard/`) resolve to nothing and would\n * otherwise fall through to the `npm:` external bucket (C-44). Resolves\n * through the ts-morph filesystem host so it honours in-memory test fixtures.\n */\n private resolveRelativeInternal(\n srcAbs: string,\n specifier: string,\n ): string | null {\n const abs = resolveRelativeAbs(\n this.ensureProject().getFileSystem(),\n srcAbs,\n specifier,\n );\n return abs ? inRepoFileId(this.repoRoot, abs) : null;\n }\n\n private inRepoFileId(abs: string): string | null {\n return inRepoFileId(this.repoRoot, abs);\n }\n}\n\n// ts-morph resolves workspace imports like `@codewatch/analyzer` to the\n// package's `types` entry (`<pkg>/dist/index.d.ts`), but the indexer's file\n// walker excludes `dist/` and `.d.ts`. Without remapping, every cross-package\n// edge points to a nonexistent node and the rendered graph fails to construct.\nfunction remapDistToSrc(abs: string): string {\n const m = /^(.*)[\\\\/]dist[\\\\/](.+)\\.d\\.ts$/.exec(abs);\n if (!m) return abs;\n const base = m[1]!;\n const sub = m[2]!;\n for (const ext of [\".ts\", \".tsx\"]) {\n const candidate = path.join(base, \"src\", sub + ext);\n if (existsSync(candidate)) return candidate;\n }\n return abs;\n}\n\nfunction isTypeScriptFile(file: ParsedFile): boolean {\n return file.language === \"typescript\" || file.language === \"tsx\";\n}\n","import * as path from \"node:path\";\nimport { getLanguageFromPath } from \"../parser/index.js\";\nimport { fileId, moduleId, parentModuleId } from \"./ids.js\";\nimport type { GraphNode } from \"../types.js\";\n\n/**\n * Build the `file` + `module` nodes for a source file from its path alone.\n * Path-derived and deterministic, so the incremental indexer can reconstruct an\n * unchanged file's nodes without re-parsing it — they are byte-for-byte the same\n * nodes the extractor would emit. Keep this the single source of truth for\n * file/module node shape.\n */\nexport function buildFileModuleNodes(repoRoot: string, absPath: string): GraphNode[] {\n const fId = fileId(repoRoot, absPath);\n const mId = moduleId(repoRoot, absPath);\n const parentId = parentModuleId(mId) ?? undefined;\n const language = getLanguageFromPath(absPath) ?? \"typescript\";\n return [\n { id: fId, kind: \"file\", name: path.basename(fId), parentId: mId, language },\n { id: mId, kind: \"module\", name: path.basename(mId), parentId, language },\n ];\n}\n","import * as path from \"node:path\";\n\nconst MODULE_EXT_RE = /\\.(?:tsx|ts|jsx|js|mts|cts|mjs|cjs|py)$/;\n\nfunction toPosix(p: string): string {\n return p.split(path.sep).join(\"/\");\n}\n\nfunction repoRelative(repoRoot: string, absPath: string): string {\n const rel = path.relative(repoRoot, absPath);\n return toPosix(rel);\n}\n\nexport function fileId(repoRoot: string, absPath: string): string {\n return repoRelative(repoRoot, absPath);\n}\n\nexport function moduleId(repoRoot: string, absPath: string): string {\n const rel = repoRelative(repoRoot, absPath);\n return rel.replace(MODULE_EXT_RE, \"\");\n}\n\nexport function parentModuleId(id: string): string | null {\n const idx = id.lastIndexOf(\"/\");\n if (idx < 0) return null;\n return id.slice(0, idx);\n}\n\nexport function packageId(name: string): string {\n return name;\n}\n\n// Symbol nodes hang under their declaring file: `<fileId>#<exportName>` (C-53).\n// `#` never occurs in a posix path or a JS export identifier, so it can't\n// collide with a real file id — and it is printable, unlike the NUL separators\n// that made git treat files as binary (C-21).\nexport const SYMBOL_ID_SEP = \"#\";\n\nexport function symbolId(fileId: string, exportName: string): string {\n return `${fileId}${SYMBOL_ID_SEP}${exportName}`;\n}\n\n/**\n * Inverse of {@link symbolId}: split a `<fileId>#<name>` symbol id back into its\n * declaring file and export name. Returns null for an id with no separator (a\n * plain file id), so callers can filter the symbol layer cleanly. `#` is illegal\n * in both posix paths and JS identifiers, so the first occurrence is the split.\n */\nexport function parseSymbolId(\n id: string,\n): { fileId: string; name: string } | null {\n const idx = id.indexOf(SYMBOL_ID_SEP);\n if (idx < 0) return null;\n return { fileId: id.slice(0, idx), name: id.slice(idx + 1) };\n}\n\nexport function externalId(specifier: string): string {\n if (specifier.startsWith(\"node:\")) {\n return specifier;\n }\n return `npm:${bareName(specifier)}`;\n}\n\nfunction bareName(specifier: string): string {\n if (specifier.startsWith(\"@\")) {\n const parts = specifier.split(\"/\");\n return parts.slice(0, 2).join(\"/\");\n }\n const slash = specifier.indexOf(\"/\");\n return slash < 0 ? specifier : specifier.slice(0, slash);\n}\n","import {\n Node,\n SyntaxKind,\n type ExportDeclaration,\n type Identifier,\n type ImportDeclaration,\n type SourceFile,\n} from \"ts-morph\";\nimport type { EdgeKind, GraphEdge } from \"../types.js\";\n\n/**\n * Reference-count edge weighting (C-51). File-level import/re-export edges carry\n * a `weight` on `attrs` measuring how heavily the source file leans on the\n * target — not merely whether it imports from it. A file that calls one imported\n * helper 30× is more coupled than one that names 30 helpers once; a binary edge\n * cannot tell them apart. Everything here is source-local (it reads only the\n * importing file's text), so the weight stays sound under incremental reuse,\n * which reparses a file only when its own content changes.\n */\n\n/**\n * Fold a specifier into the aggregate, summing weights so parallel imports of\n * one module (e.g. a value import plus a type import) collapse to a single\n * edge whose weight is their combined reference count.\n */\nexport function addWeightedEdge(\n agg: Map<string, GraphEdge>,\n srcId: string,\n dstId: string,\n kind: EdgeKind,\n specifier: string,\n weight: number,\n): void {\n const key = JSON.stringify([kind, dstId]);\n const existing = agg.get(key);\n if (existing) {\n (existing.attrs as { weight: number }).weight += weight;\n return;\n }\n agg.set(key, { srcId, dstId, kind, attrs: { specifier, weight } });\n}\n\n/**\n * How many times the importing file actually uses the symbols it brings in,\n * floored at 1 so a live-but-unused import (or a `import \"./x\"` side-effect\n * import, which binds nothing) still reads as a real dependency.\n */\nexport function importWeight(\n decl: ImportDeclaration,\n usage: Map<string, number>,\n): number {\n const names = importBindingNames(decl);\n if (names.length === 0) return 1; // side-effect import: `import \"./x\"`\n const used = names.reduce((sum, n) => sum + (usage.get(n) ?? 0), 0);\n return Math.max(used, 1);\n}\n\n/**\n * One named or default import binding, pairing the name as *exported by the\n * target module* (what a symbol node is keyed on) with the *local* alias in\n * this file (what usage counts key on). Namespace imports (`import * as ns`)\n * are deliberately excluded: they reference the module as a whole, not any one\n * export, so per-symbol attribution isn't well-defined — the file-level\n * `imports` edge still accounts for them (C-53).\n */\nexport interface ImportBinding {\n importedName: string;\n localName: string;\n}\n\nexport function namedImportBindings(decl: ImportDeclaration): ImportBinding[] {\n const out: ImportBinding[] = [];\n const def = decl.getDefaultImport();\n if (def) out.push({ importedName: \"default\", localName: def.getText() });\n for (const spec of decl.getNamedImports()) {\n out.push({\n importedName: spec.getNameNode().getText(),\n localName: (spec.getAliasNode() ?? spec.getNameNode()).getText(),\n });\n }\n return out;\n}\n\n/**\n * Per-binding reference weight: how many times this file uses the imported\n * symbol, floored at 1 so a named-but-only-referenced-once import still reads\n * as a real dependency (naming a symbol is itself a use).\n */\nexport function bindingWeight(\n binding: ImportBinding,\n usage: Map<string, number>,\n): number {\n return Math.max(usage.get(binding.localName) ?? 0, 1);\n}\n\nfunction importBindingNames(decl: ImportDeclaration): string[] {\n const names: string[] = [];\n const def = decl.getDefaultImport();\n if (def) names.push(def.getText());\n const ns = decl.getNamespaceImport();\n if (ns) names.push(ns.getText());\n for (const spec of decl.getNamedImports()) {\n names.push((spec.getAliasNode() ?? spec.getNameNode()).getText());\n }\n return names;\n}\n\n/**\n * Re-export edges have no local usage to count — the names pass straight\n * through — so weight them by how many names cross the boundary: the count of\n * re-exported specifiers, or 1 for `export * from`.\n */\nexport function reExportWeight(decl: ExportDeclaration): number {\n if (decl.isNamespaceExport()) return 1;\n return Math.max(decl.getNamedExports().length, 1);\n}\n\n/**\n * Count identifier occurrences per name across the file in a single pass,\n * skipping the import/export declarations themselves and property *name*\n * positions (`obj.foo`, `{ foo: … }`) — those name a member, not a reference to\n * an imported binding. Binding names are unique per file (TS forbids\n * redeclaration), so a name's total attributes cleanly to one edge. Local\n * shadowing (a variable reusing an imported name) can over-count; acceptable\n * for a coupling-strength signal.\n */\nexport function buildLocalUsageCounts(\n sourceFile: SourceFile,\n): Map<string, number> {\n const counts = new Map<string, number>();\n for (const id of sourceFile.getDescendantsOfKind(SyntaxKind.Identifier)) {\n if (id.getFirstAncestorByKind(SyntaxKind.ImportDeclaration)) continue;\n if (id.getFirstAncestorByKind(SyntaxKind.ExportDeclaration)) continue;\n if (isPropertyNamePosition(id)) continue;\n const text = id.getText();\n counts.set(text, (counts.get(text) ?? 0) + 1);\n }\n return counts;\n}\n\nfunction isPropertyNamePosition(id: Identifier): boolean {\n const parent = id.getParent();\n if (Node.isPropertyAccessExpression(parent) && parent.getNameNode() === id) {\n return true;\n }\n if (Node.isPropertyAssignment(parent) && parent.getNameNode() === id) {\n return true;\n }\n return false;\n}\n","import type { ParsedFile } from \"./parser/index.js\";\nimport type { Node } from \"web-tree-sitter\";\n\nconst TS_DECL_TYPES = new Set([\n \"function_declaration\",\n \"method_definition\",\n \"class_declaration\",\n]);\n\nconst PY_DECL_TYPES = new Set([\"function_definition\", \"class_definition\"]);\n\n/** 1-based inclusive line span of a declaration, for coverage range-attribution (C-63). */\nexport interface LineSpan {\n startLine: number;\n endLine: number;\n}\n\n/**\n * Every function/method/class a file DECLARES, mapped to its 1-based line span\n * (the model-B symbol surface, C-64, now carrying spans for C-63 coverage\n * range-containment). A superset of the file's exports: internal helpers like\n * `mergeFragments` are included so they get a `symbol` node (and, by name match,\n * their complexity + coverage) even though nothing imports them. Names come from\n * the same tree-sitter walk that computes complexity, so they never drift.\n * Anonymous declarations (a default-exported arrow, inline callbacks) contribute\n * no name and are skipped. A name declared more than once keeps its last span\n * (overloads / same-named methods are rare; range lookup still resolves most).\n */\nexport function collectDeclaredSpans(file: ParsedFile): Map<string, LineSpan> {\n const declTypes = file.language === \"python\" ? PY_DECL_TYPES : TS_DECL_TYPES;\n const spans = new Map<string, LineSpan>();\n const visit = (node: Node): void => {\n const named = declaredNodeAt(node, declTypes);\n if (named) {\n spans.set(named.name, {\n startLine: named.node.startPosition.row + 1,\n endLine: named.node.endPosition.row + 1,\n });\n }\n for (const child of node.children) {\n if (child) visit(child);\n }\n };\n visit(file.tree.rootNode);\n return spans;\n}\n\n/** Declared names only — the Set view over {@link collectDeclaredSpans}. */\nexport function collectDeclaredNames(file: ParsedFile): Set<string> {\n return new Set(collectDeclaredSpans(file).keys());\n}\n\n/**\n * The declaration at this node — its name plus the node whose line span\n * represents it — or null. Mirrors source-metrics' `functionAt` handling of an\n * arrow / function-expression bound to a `const`/`let` (the span is the callable\n * body's node, so it contains the coverage `fnMap` loc), extended to classes.\n */\nfunction declaredNodeAt(\n node: Node,\n declTypes: ReadonlySet<string>,\n): { name: string; node: Node } | null {\n if (declTypes.has(node.type)) {\n const name = node.childForFieldName(\"name\")?.text;\n return name ? { name, node } : null;\n }\n if (\n (node.type === \"arrow_function\" || node.type === \"function_expression\") &&\n node.parent?.type === \"variable_declarator\"\n ) {\n const name = node.parent.childForFieldName(\"name\")?.text;\n return name ? { name, node } : null;\n }\n return null;\n}\n","import {\n Node,\n type ArrowFunction,\n type FunctionDeclaration,\n type FunctionExpression,\n type MethodDeclaration,\n type SourceFile,\n type VariableDeclaration,\n} from \"ts-morph\";\n\n/**\n * Persisted qualitative facts about a declaration (C-79): the one-line type\n * `signature` and the leading docstring `purpose`, so `graph context` can answer\n * \"what is this and how is it called\" without the reader opening the file — the\n * G1/G2 gap the C-74 spike found vs brain's codebase module.\n *\n * Both are pure syntactic projections of the declaration (params + explicit\n * return annotation + JSDoc), so an unchanged file's symbol nodes stay\n * byte-identical and the incremental indexer carries them forward. Inferred\n * types are only used when they carry no `import(\"…\")` module path — those embed\n * absolute paths that are non-deterministic and reuse-breaking.\n */\nexport interface SymbolText {\n signature?: string;\n purpose?: string;\n}\n\nconst MAX_SIGNATURE = 200;\nconst MAX_PURPOSE = 240;\n\ntype Callable =\n | FunctionDeclaration\n | MethodDeclaration\n | ArrowFunction\n | FunctionExpression;\n\n/** Signature + docstring for a ts-morph declaration node, best-effort. */\nexport function declarationText(decl: Node): SymbolText {\n const out: SymbolText = {};\n const signature = signatureOf(decl);\n if (signature) out.signature = signature;\n const purpose = purposeOf(decl);\n if (purpose) out.purpose = purpose;\n return out;\n}\n\n/**\n * The file-level declaration named `name`, for the non-exported symbol surface\n * (model B, C-64) where only the name + tree-sitter span is known. Class methods\n * are not addressable by file-level name, so they resolve to undefined (no\n * signature) — acceptable, the exported API is the primary target.\n */\nexport function lookupDeclaration(\n sf: SourceFile,\n name: string,\n): Node | undefined {\n return (\n sf.getFunction(name) ??\n sf.getClass(name) ??\n sf.getInterface(name) ??\n sf.getTypeAlias(name) ??\n sf.getEnum(name) ??\n sf.getVariableDeclaration(name)\n );\n}\n\nfunction signatureOf(decl: Node): string | undefined {\n if (Node.isFunctionDeclaration(decl) || Node.isMethodDeclaration(decl)) {\n return callableSignature(decl, decl.getName() ?? \"\");\n }\n if (Node.isVariableDeclaration(decl)) return variableSignature(decl);\n if (Node.isClassDeclaration(decl)) return clamp(`class ${decl.getName() ?? \"\"}`.trim());\n if (Node.isInterfaceDeclaration(decl)) return clamp(`interface ${decl.getName()}`);\n if (Node.isTypeAliasDeclaration(decl)) {\n return clamp(`type ${decl.getName()} = ${oneLine(decl.getTypeNode()?.getText() ?? \"\")}`);\n }\n if (Node.isEnumDeclaration(decl)) return clamp(`enum ${decl.getName()}`);\n return undefined;\n}\n\nfunction variableSignature(decl: VariableDeclaration): string | undefined {\n const init = decl.getInitializer();\n if (init && (Node.isArrowFunction(init) || Node.isFunctionExpression(init))) {\n return callableSignature(init, decl.getName());\n }\n const typeNode = decl.getTypeNode();\n const type = typeNode ? oneLine(typeNode.getText()) : cleanType(() => decl.getType().getText());\n return type ? clamp(`${decl.getName()}: ${type}`) : undefined;\n}\n\nfunction callableSignature(decl: Callable, name: string): string {\n const params = decl.getParameters().map((p) => oneLine(p.getText())).join(\", \");\n const ret = returnType(decl);\n return clamp(`${name}(${params})${ret ? `: ${ret}` : \"\"}`);\n}\n\nfunction returnType(decl: Callable): string | undefined {\n const node = decl.getReturnTypeNode();\n if (node) return oneLine(node.getText());\n return cleanType(() => decl.getReturnType().getText());\n}\n\n/**\n * Inferred type text, but only when it carries no `import(\"…\")` module path\n * (those embed absolute paths → non-deterministic + reuse-breaking). Returns\n * undefined rather than a dirty type, so the signature simply omits it.\n */\nfunction cleanType(getText: () => string): string | undefined {\n let text: string;\n try {\n text = getText();\n } catch {\n return undefined;\n }\n if (!text || text.includes(\"import(\")) return undefined;\n return oneLine(text);\n}\n\nfunction purposeOf(decl: Node): string | undefined {\n const docs = jsDocsFor(decl);\n if (!docs || docs.length === 0) return undefined;\n const desc = docs[docs.length - 1]!.getDescription().trim();\n return desc ? clamp(oneLine(desc), MAX_PURPOSE) : undefined;\n}\n\nfunction jsDocsFor(decl: Node) {\n if (Node.isJSDocable(decl)) return decl.getJsDocs();\n if (Node.isVariableDeclaration(decl)) return decl.getVariableStatement()?.getJsDocs();\n return undefined;\n}\n\nfunction oneLine(s: string): string {\n return s.replace(/\\s+/g, \" \").trim();\n}\n\nfunction clamp(s: string, max = MAX_SIGNATURE): string {\n return s.length > max ? `${s.slice(0, max - 1)}…` : s;\n}\n","import type { ParsedFile } from \"../parser/index.js\";\nimport type { SourceFile } from \"ts-morph\";\nimport { collectDeclaredSpans, type LineSpan } from \"../declared-names.js\";\nimport { fileId, symbolId } from \"./ids.js\";\nimport { declarationText, lookupDeclaration, type SymbolText } from \"./symbol-signature.js\";\nimport type { GraphNode } from \"../types.js\";\n\n/**\n * A `symbol` node for a declaration, tagging whether it is exported (C-64) and,\n * when it is a function/method/class, its 1-based line span (C-63) so coverage\n * can be attributed by range containment. A span-less symbol (an exported type or\n * const value) just carries `exported`. When ts-morph resolves the declaration it\n * also carries a one-line `signature` and docstring `purpose` (C-79) — both\n * omitted (not stored as null) when unavailable, so attrs stay byte-identical for\n * incremental reuse.\n */\nfunction makeSymbolNode(\n fileId: string,\n name: string,\n exported: boolean,\n span: LineSpan | undefined,\n text: SymbolText,\n): GraphNode {\n const attrs: Record<string, unknown> = { exported };\n if (span) {\n attrs.startLine = span.startLine;\n attrs.endLine = span.endLine;\n }\n if (text.signature) attrs.signature = text.signature;\n if (text.purpose) attrs.purpose = text.purpose;\n return {\n id: symbolId(fileId, name),\n kind: \"symbol\",\n name,\n parentId: fileId,\n language: \"typescript\",\n attrs,\n };\n}\n\n/**\n * A `symbol` node per declaration a file owns (model B, C-64). Two sources, both\n * source-local so an unchanged file's symbol nodes are byte-identical across runs\n * and the incremental indexer carries them forward without re-parsing:\n *\n * - **Exported declarations** (`exported: true`) — from ts-morph's\n * `getExportedDeclarations`, which barrel-resolves. Names this file merely\n * re-exports are skipped: they belong to the origin file's symbol nodes, so\n * `references` edges resolved through a barrel land on the file that does the\n * work, not the hub (C-53 / C-55). Covers functions, classes, types, and\n * exported const values alike — the reference-edge targets.\n * - **Non-exported functions / methods / classes** (`exported: false`) — from the\n * tree-sitter declared-name walk (same parser as complexity), so an internal\n * helper like `mergeFragments` gets a node and, by name match, its own\n * complexity, closing the C-59 Dossier gap. Internal *non-callable* bindings\n * (plain const values) are intentionally excluded — only functions/methods/\n * classes.\n */\nexport function buildSymbolNodes(\n repoRoot: string,\n sourceFile: SourceFile,\n file: ParsedFile,\n): GraphNode[] {\n const fId = fileId(repoRoot, sourceFile.getFilePath());\n const spans = collectDeclaredSpans(file);\n const out: GraphNode[] = [];\n const exported = new Set<string>();\n for (const [name, decls] of sourceFile.getExportedDeclarations()) {\n const own = decls.find((d) => d.getSourceFile() === sourceFile);\n if (!own) continue;\n exported.add(name);\n out.push(makeSymbolNode(fId, name, true, spans.get(name), declarationText(own)));\n }\n for (const [name, span] of spans) {\n if (exported.has(name)) continue;\n const decl = lookupDeclaration(sourceFile, name);\n out.push(makeSymbolNode(fId, name, false, span, decl ? declarationText(decl) : {}));\n }\n return out;\n}\n","import {\n Node,\n SyntaxKind,\n type ObjectBindingPattern,\n type SourceFile,\n} from \"ts-morph\";\nimport type { GraphEdge } from \"../types.js\";\nimport { symbolId } from \"./ids.js\";\nimport { addWeightedEdge } from \"./reference-weight.js\";\n\n/**\n * String-literal specifiers of dynamic `import(\"…\")` expressions in a file\n * (C-65). The static import graph only records `import`/`export` *declarations*,\n * so a module loaded lazily — e.g. `cli/src/index.ts` doing\n * `import(\"./commands/analyze.js\")` to register a command — otherwise has no\n * inbound edge and reads `fan_in` 0, falsely appearing dead. Capturing these\n * closes that blind spot for the common case.\n *\n * Only string-literal specifiers are returned; a computed specifier\n * (`import(variable)`) is genuinely unresolvable statically and is left out (a\n * documented blind spot, same class as DI/registry-string wiring).\n */\nexport function dynamicImportSpecifiers(sourceFile: SourceFile): string[] {\n const out: string[] = [];\n for (const call of sourceFile.getDescendantsOfKind(SyntaxKind.CallExpression)) {\n if (call.getExpression().getKind() !== SyntaxKind.ImportKeyword) continue;\n const arg = call.getArguments()[0];\n if (arg && arg.getKind() === SyntaxKind.StringLiteral) {\n out.push(arg.asKindOrThrow(SyntaxKind.StringLiteral).getLiteralText());\n }\n }\n return out;\n}\n\n/** One export pulled out of a destructured dynamic import. */\ninterface DynamicImportBinding {\n specifier: string;\n /** The name as exported by the target module (property name, not local alias). */\n importedName: string;\n}\n\n/**\n * Destructured bindings of a dynamic import — `const { runX } = await import(\"…\")`\n * — pairing each pulled-out export with its target module (C-68). This is the\n * lazy-command-loading pattern (the CLI registers a subcommand via\n * `const { runX } = await import(\"./x.js\")`); C-65 captured only the module\n * edge, so the target's export still read as unused despite being live. Emitting\n * a per-symbol `references` edge from these clears that dead-code false positive.\n *\n * Only object destructuring binds a specific export. `const ns = await import(…)`\n * (namespace) and `.then(m => m.x)` are excluded — the module `imports` edge\n * already accounts for the whole-module dependency, exactly as a static\n * namespace import is handled (C-53). The property name (not the local alias) is\n * returned, matching how a `symbol` node is keyed.\n */\nfunction dynamicImportBindings(\n sourceFile: SourceFile,\n): DynamicImportBinding[] {\n const out: DynamicImportBinding[] = [];\n for (const call of sourceFile.getDescendantsOfKind(SyntaxKind.CallExpression)) {\n if (call.getExpression().getKind() !== SyntaxKind.ImportKeyword) continue;\n const arg = call.getArguments()[0];\n if (!arg || arg.getKind() !== SyntaxKind.StringLiteral) continue;\n const pattern = destructuredTargetOf(call);\n if (!pattern) continue;\n const specifier = arg.asKindOrThrow(SyntaxKind.StringLiteral).getLiteralText();\n for (const importedName of destructuredNames(pattern)) {\n out.push({ specifier, importedName });\n }\n }\n return out;\n}\n\n/**\n * The object-binding pattern a dynamic `import()` call feeds, if the call is the\n * initializer of a `const { … } = (await) import(…)` declaration. Returns\n * undefined for a namespace binding, a `.then()` chain, or any non-initializer\n * position — those don't destructure a specific export.\n */\nfunction destructuredTargetOf(call: Node): ObjectBindingPattern | undefined {\n const parent = call.getParent();\n if (!parent) return undefined;\n const init = Node.isAwaitExpression(parent) ? parent : call;\n const decl = init.getParent();\n if (!decl || !Node.isVariableDeclaration(decl)) return undefined;\n if (decl.getInitializer() !== init) return undefined;\n const nameNode = decl.getNameNode();\n return Node.isObjectBindingPattern(nameNode) ? nameNode : undefined;\n}\n\n/**\n * Exported names bound by an object pattern: `{ runX }` → `runX`,\n * `{ runX: local }` → `runX` (the exported name, not the alias). A rest element\n * (`{ ...rest }`) binds no single export and is skipped.\n */\nfunction destructuredNames(pattern: ObjectBindingPattern): string[] {\n const names: string[] = [];\n for (const el of pattern.getElements()) {\n if (el.getDotDotDotToken()) continue;\n const nameNode = el.getPropertyNameNode() ?? el.getNameNode();\n names.push(nameNode.getText());\n }\n return names;\n}\n\n/**\n * Add a `references` edge (importer → target symbol) for each destructured\n * dynamic import in a file (C-68). Kept here rather than in the extractor class\n * so the churn-hot, LOC-bound extractor body stays minimal; specifier→file\n * resolution is delegated to the caller (it needs the ts-morph project's\n * filesystem host). A target the module doesn't export dangles and is pruned\n * after assembly (`pruneDanglingReferences`).\n */\nexport function recordDynamicSymbolRefEdges(\n sourceFile: SourceFile,\n srcFileId: string,\n agg: Map<string, GraphEdge>,\n resolveSpecifier: (specifier: string) => string | null,\n): void {\n for (const binding of dynamicImportBindings(sourceFile)) {\n const targetFileId = resolveSpecifier(binding.specifier);\n if (!targetFileId) continue;\n addWeightedEdge(\n agg,\n srcFileId,\n symbolId(targetFileId, binding.importedName),\n \"references\",\n binding.specifier,\n 1,\n );\n }\n}\n","import * as path from \"node:path\";\nimport type { FileSystemHost } from \"ts-morph\";\nimport { fileId } from \"./ids.js\";\n\n/** Source/compiled extensions a relative specifier may resolve to, in priority order. */\nexport const RESOLVABLE_EXTS = [\n \".ts\",\n \".tsx\",\n \".mts\",\n \".cts\",\n \".js\",\n \".jsx\",\n \".mjs\",\n \".cjs\",\n];\n\nexport function isRelativeSpecifier(specifier: string): boolean {\n return (\n specifier === \".\" ||\n specifier === \"..\" ||\n specifier.startsWith(\"./\") ||\n specifier.startsWith(\"../\")\n );\n}\n\n/**\n * Candidate on-disk paths for a resolved relative import base, in priority\n * order: an explicit extension already present, then the source extensions,\n * then a NodeNext `.js`→`.ts` remap, then `index.*` for directory imports.\n */\nexport function* relativeResolutionCandidates(base: string): Iterable<string> {\n yield base;\n for (const ext of RESOLVABLE_EXTS) yield base + ext;\n const jsExt = /\\.(?:jsx?|mjs|cjs)$/.exec(base);\n if (jsExt) {\n const stem = base.slice(0, base.length - jsExt[0].length);\n for (const ext of RESOLVABLE_EXTS) yield stem + ext;\n }\n for (const ext of RESOLVABLE_EXTS) yield path.join(base, \"index\" + ext);\n}\n\n/**\n * Resolve a relative specifier from a file to an on-disk absolute path via the\n * ts-morph filesystem host (honours in-memory test fixtures), or null. ts-morph's\n * NodeNext resolution only links relative imports carrying an explicit `.js`\n * extension, so extensionless bundler-style imports (`../types`, common outside\n * the tsconfig project) need this fallback (C-44).\n */\nexport function resolveRelativeAbs(\n fs: FileSystemHost,\n fromAbs: string,\n specifier: string,\n): string | null {\n if (!isRelativeSpecifier(specifier)) return null;\n const base = path.resolve(path.dirname(fromAbs), specifier);\n for (const candidate of relativeResolutionCandidates(base)) {\n if (fs.fileExistsSync(candidate)) return candidate;\n }\n return null;\n}\n\n/** Map an abs path to an in-repo file id, or null if out of tree / in node_modules. */\nexport function inRepoFileId(repoRoot: string, abs: string): string | null {\n const relative = path.relative(repoRoot, abs);\n if (relative.startsWith(\"..\") || path.isAbsolute(relative)) return null;\n if (relative.split(path.sep).includes(\"node_modules\")) return null;\n return fileId(repoRoot, abs);\n}\n","import type { ExportDeclaration, Project, SourceFile } from \"ts-morph\";\nimport { symbolId } from \"./ids.js\";\nimport { inRepoFileId, resolveRelativeAbs } from \"./module-resolution.js\";\n\n/** Context the re-export walk needs: the ts-morph project and the repo root. */\nexport interface ReExportCtx {\n project: Project;\n repoRoot: string;\n}\n\n/**\n * Trace an imported name to the id of the `symbol` node for the export that\n * physically DECLARES it, walking re-export hops on disk (C-70). ts-morph's own\n * `getExportedDeclarations` already sees through barrels whose specifiers it can\n * resolve; this covers the case it can't — extensionless / out-of-project\n * relative specifiers (the tRPC pattern) — by resolving `specifier` from\n * `importerAbs` and following `export { x } from './b'` and `export * from './b'`\n * hops by hand. Returns null (→ a dangling edge, pruned after assembly) when the\n * name is neither declared here nor forwarded onward.\n */\nexport function resolveReExportOrigin(\n ctx: ReExportCtx,\n importerAbs: string,\n specifier: string,\n name: string,\n): string | null {\n const fs = ctx.project.getFileSystem();\n const targetAbs = resolveRelativeAbs(fs, importerAbs, specifier);\n return targetAbs ? walkReExports(ctx, targetAbs, name, new Set()) : null;\n}\n\nfunction walkReExports(\n ctx: ReExportCtx,\n fileAbs: string,\n name: string,\n visited: Set<string>,\n): string | null {\n if (visited.has(fileAbs)) return null;\n visited.add(fileAbs);\n // The indexer adds files to the project one at a time as it extracts them, so\n // a barrel/origin file on this walk may not be loaded yet — pull it from the\n // filesystem host on demand (cached thereafter).\n const sf =\n ctx.project.getSourceFile(fileAbs) ??\n ctx.project.addSourceFileAtPathIfExists(fileAbs);\n if (!sf) return null;\n if (declaresExportLocally(sf, name)) {\n const id = inRepoFileId(ctx.repoRoot, fileAbs);\n return id ? symbolId(id, name) : null;\n }\n const fs = ctx.project.getFileSystem();\n for (const ed of sf.getExportDeclarations()) {\n const spec = ed.getModuleSpecifierValue();\n if (!spec) continue;\n const inward = reExportInwardName(ed, name);\n if (inward === null) continue;\n const targetAbs = resolveRelativeAbs(fs, fileAbs, spec);\n if (!targetAbs) continue;\n const origin = walkReExports(ctx, targetAbs, inward, visited);\n if (origin) return origin;\n }\n return null;\n}\n\n/**\n * Whether `sf` physically declares an export named `name` — the same test\n * `buildSymbolNodes` uses to decide a name is this file's own symbol, so a\n * re-export walk that stops here lands on a symbol node that actually exists.\n */\nfunction declaresExportLocally(sf: SourceFile, name: string): boolean {\n const decls = sf.getExportedDeclarations().get(name);\n return !!decls && decls.some((d) => d.getSourceFile() === sf);\n}\n\n/**\n * For a re-export `export ... from '...'`, the name to look up in the target\n * module when the outward name is `name`, or null if this declaration does not\n * forward `name`.\n * - `export { a as b } from '...'` forwards outward `b` ← inward `a`.\n * - `export * from '...'` forwards every name unchanged.\n * - `export * as ns from '...'` binds a namespace object, not member names, so\n * it never forwards an individual `name` (we don't attribute per-member).\n */\nfunction reExportInwardName(ed: ExportDeclaration, name: string): string | null {\n const named = ed.getNamedExports();\n if (named.length > 0) {\n for (const spec of named) {\n const outward = spec.getAliasNode()?.getText() ?? spec.getName();\n if (outward === name) return spec.getName();\n }\n return null;\n }\n if (ed.getNamespaceExport()) return null; // `export * as ns from`\n return name; // bare `export *`\n}\n","import * as path from \"node:path\";\nimport { existsSync } from \"node:fs\";\nimport type { Node } from \"web-tree-sitter\";\nimport type { Extractor, ParsedFile } from \"../parser/index.js\";\nimport { collectDeclaredSpans } from \"../declared-names.js\";\nimport { buildFileModuleNodes } from \"./file-nodes.js\";\nimport { externalId, fileId, symbolId } from \"./ids.js\";\nimport { inRepoFileId } from \"./module-resolution.js\";\nimport type { GraphEdge, GraphFragment, GraphNode } from \"../types.js\";\n\n/** `.py` candidates a dotted module path may resolve to, package `__init__` included. */\nfunction* candidates(base: string): Iterable<string> {\n yield `${base}.py`;\n yield path.join(base, \"__init__.py\");\n}\n\n/**\n * Tree-sitter Python extraction. Deliberately narrower than the ts-morph side:\n * no type checker, so imports resolve by dotted path against the tree rather\n * than by symbol resolution, and symbols are the declarations the same\n * tree-sitter walk already finds for complexity. Enough to place Python files in\n * the graph with their dependencies; a semantic Python layer is a follow-up.\n */\nexport class PythonGraphExtractor implements Extractor<GraphFragment> {\n readonly name = \"python-graph\";\n\n constructor(private readonly repoRoot: string) {}\n\n extract(file: ParsedFile): GraphFragment[] {\n if (file.language !== \"python\") return [];\n const fId = fileId(this.repoRoot, file.filePath);\n const nodes = buildFileModuleNodes(this.repoRoot, file.filePath);\n nodes.push(...symbolNodes(fId, file));\n const { edges, externals } = this.collectImports(fId, file);\n return [{ nodes: [...nodes, ...externals], edges }];\n }\n\n private collectImports(\n srcId: string,\n file: ParsedFile,\n ): { edges: GraphEdge[]; externals: GraphNode[] } {\n const edges = new Map<string, GraphEdge>();\n const externals: GraphNode[] = [];\n for (const specifier of importSpecifiers(file.tree.rootNode)) {\n const internal = this.resolveDotted(file.filePath, specifier);\n const dstId = internal ?? externalId(specifier);\n if (!internal && !edges.has(dstId)) {\n externals.push({ id: dstId, kind: \"external\", name: specifier });\n }\n edges.set(dstId, { srcId, dstId, kind: \"imports\", attrs: { specifier, weight: 1 } });\n }\n return { edges: [...edges.values()], externals };\n }\n\n /**\n * Resolve `a.b.c` (or a leading-dot relative import) to an in-repo file id by\n * walking the dotted path from the importing file's package and then from the\n * repo root, which covers both intra-package and root-relative layouts.\n */\n private resolveDotted(fromAbs: string, specifier: string): string | null {\n const leadingDots = /^\\.*/.exec(specifier)?.[0].length ?? 0;\n const parts = specifier.slice(leadingDots).split(\".\").filter(Boolean);\n const relativeBase = path.resolve(path.dirname(fromAbs), \"../\".repeat(Math.max(leadingDots - 1, 0)));\n const roots = leadingDots > 0 ? [relativeBase] : [path.dirname(fromAbs), this.repoRoot];\n for (const root of roots) {\n for (const candidate of candidates(path.join(root, ...parts))) {\n const id = inRepoFileId(this.repoRoot, candidate);\n if (id !== null && existsSync(candidate)) return id;\n }\n }\n return null;\n }\n}\n\nfunction symbolNodes(fId: string, file: ParsedFile): GraphNode[] {\n return [...collectDeclaredSpans(file)].map(([name, span]) => ({\n id: symbolId(fId, name),\n kind: \"symbol\" as const,\n name,\n parentId: fId,\n language: \"python\",\n attrs: { exported: !name.startsWith(\"_\"), startLine: span.startLine, endLine: span.endLine },\n }));\n}\n\n/** Every module named by an `import x` / `from x import y` statement, in tree order. */\nfunction importSpecifiers(root: Node): string[] {\n const out: string[] = [];\n const visit = (node: Node): void => {\n if (node.type === \"import_statement\" || node.type === \"import_from_statement\") {\n const name = node.childForFieldName(\"module_name\");\n if (name) out.push(name.text);\n else for (const child of node.namedChildren) if (child?.type === \"dotted_name\") out.push(child.text);\n }\n for (const child of node.namedChildren) if (child) visit(child);\n };\n visit(root);\n return out;\n}\n","import type { Extractor, ParsedFile } from \"../parser/index.js\";\nimport { TsMorphGraphExtractor } from \"./ts-morph-extractor.js\";\nimport { PythonGraphExtractor } from \"./python-extractor.js\";\nimport type { GraphFragment } from \"../types.js\";\n\nexport interface LanguageExtractorOptions {\n repoRoot: string;\n tsConfigPath?: string;\n}\n\n/**\n * One extractor over every supported language: each delegate already returns an\n * empty fragment list for files it does not own, so dispatch is a fold. Keeps\n * `assembleFragments` language-agnostic.\n */\nexport class LanguageExtractor implements Extractor<GraphFragment> {\n readonly name = \"language-graph\";\n private readonly delegates: Extractor<GraphFragment>[];\n\n constructor(options: LanguageExtractorOptions) {\n this.delegates = [\n new TsMorphGraphExtractor({ repoRoot: options.repoRoot, tsConfigPath: options.tsConfigPath }),\n new PythonGraphExtractor(options.repoRoot),\n ];\n }\n\n extract(file: ParsedFile): GraphFragment[] {\n return this.delegates.flatMap((delegate) => delegate.extract(file));\n }\n}\n","import { execFileSync } from \"node:child_process\";\nimport * as path from \"node:path\";\nimport { fileId, moduleId } from \"./extractors/ids.js\";\nimport type { IdAlias, IdAliasReason } from \"./types.js\";\n\nexport interface RenamePair {\n oldPath: string;\n newPath: string;\n similarity: number;\n}\n\nexport interface DetectRenamesOptions {\n repoRoot: string;\n fromCommit: string;\n toCommit?: string;\n similarityThreshold?: number;\n}\n\nexport function detectGitHead(repoRoot: string): string | null {\n return runGit(repoRoot, [\"rev-parse\", \"HEAD\"]);\n}\n\nexport function isInsideGitRepo(repoRoot: string): boolean {\n return runGit(repoRoot, [\"rev-parse\", \"--is-inside-work-tree\"]) === \"true\";\n}\n\nexport function detectGitToplevel(cwd: string): string | null {\n return runGit(cwd, [\"rev-parse\", \"--show-toplevel\"]);\n}\n\n/** Resolve a ref (branch, tag, sha) to its current commit sha, or null. */\nexport function resolveGitRef(repoRoot: string, ref: string): string | null {\n return runGit(repoRoot, [\"rev-parse\", ref]);\n}\n\nexport function detectRenames(\n options: DetectRenamesOptions,\n): RenamePair[] {\n const threshold = options.similarityThreshold ?? 80;\n const args = [\n \"diff\",\n `--find-renames=${threshold}%`,\n \"--name-status\",\n options.fromCommit,\n ];\n if (options.toCommit) args.push(options.toCommit);\n\n const out = runGit(options.repoRoot, args);\n if (out === null) return [];\n return parseRenameOutput(out);\n}\n\nexport function parseRenameOutput(text: string): RenamePair[] {\n const out: RenamePair[] = [];\n for (const rawLine of text.split(\"\\n\")) {\n const line = rawLine.trimEnd();\n if (!line) continue;\n const parts = line.split(\"\\t\");\n const status = parts[0] ?? \"\";\n if (!status.startsWith(\"R\") || parts.length < 3) continue;\n const similarity = parseInt(status.slice(1), 10);\n out.push({\n oldPath: parts[1]!,\n newPath: parts[2]!,\n similarity: Number.isFinite(similarity) ? similarity : 100,\n });\n }\n return out;\n}\n\nexport function classifyRename(\n oldPath: string,\n newPath: string,\n): IdAliasReason {\n return path.posix.dirname(oldPath) === path.posix.dirname(newPath)\n ? \"rename\"\n : \"move\";\n}\n\nexport function renameToAliases(\n repoRoot: string,\n pair: RenamePair,\n): IdAlias[] {\n const reason = classifyRename(pair.oldPath, pair.newPath);\n const oldAbs = path.join(repoRoot, pair.oldPath);\n const newAbs = path.join(repoRoot, pair.newPath);\n return [\n { oldId: fileId(repoRoot, oldAbs), newId: fileId(repoRoot, newAbs), reason },\n { oldId: moduleId(repoRoot, oldAbs), newId: moduleId(repoRoot, newAbs), reason },\n ];\n}\n\nexport function buildAliases(\n repoRoot: string,\n pairs: readonly RenamePair[],\n): IdAlias[] {\n const seen = new Set<string>();\n const out: IdAlias[] = [];\n for (const pair of pairs) {\n for (const alias of renameToAliases(repoRoot, pair)) {\n if (seen.has(alias.oldId)) continue;\n seen.add(alias.oldId);\n out.push(alias);\n }\n }\n return out;\n}\n\n/**\n * Strip the inherited GIT_DIR / GIT_INDEX_FILE / GIT_WORK_TREE vars when we\n * invoke git as a *repo-discovery* sub-tool. Those vars are set by git itself\n * when running hooks (and by some IDE integrations) to point the *parent* git\n * at a specific repo; if they leak into our subprocess, `git rev-parse\n * --show-toplevel` returns cwd rather than the real toplevel, and other\n * discovery commands misbehave similarly.\n */\nexport function discoveryEnv(): NodeJS.ProcessEnv {\n const env = { ...process.env };\n delete env.GIT_DIR;\n delete env.GIT_INDEX_FILE;\n delete env.GIT_WORK_TREE;\n return env;\n}\n\nfunction runGit(cwd: string, args: readonly string[]): string | null {\n try {\n return execFileSync(\"git\", [...args], {\n cwd,\n encoding: \"utf-8\",\n stdio: [\"ignore\", \"pipe\", \"ignore\"],\n env: discoveryEnv(),\n }).trim();\n } catch {\n return null;\n }\n}\n","import { readFileSync } from \"node:fs\";\nimport * as path from \"node:path\";\n\n/**\n * Generated-code detection. Generated files (codegen output, e.g. an OpenAPI\n * `client.gen.ts`) churn and re-export in bulk, so they dominate hotspots and\n * unused-exports with noise that no human maintains. This module supplies one\n * reusable predicate — {@link isGeneratedFile} — used to mark such files so the\n * hotspot / unused-export surfaces can exclude them at their shared boundary.\n *\n * Detection has two tiers: the authoritative `.gitattributes linguist-generated`\n * markers a repo already declares, and a filename/path heuristic fallback for\n * repos that declare nothing.\n */\n\n// Fallback heuristic: `*.gen.*` / `*.generated.*` basenames and any path with a\n// `generated/` segment. Anchored to a segment boundary so `regenerated/` and a\n// stray `foogen.ts` don't match.\nconst HEURISTIC_FILE_RE = /(?:^|\\/)[^/]*\\.(?:gen|generated)\\.[^/]*$/;\nconst HEURISTIC_DIR_RE = /(?:^|\\/)generated\\//;\n\n/** True when a path looks generated by filename/dir convention alone. */\nexport function isGeneratedByHeuristic(id: string): boolean {\n return HEURISTIC_FILE_RE.test(id) || HEURISTIC_DIR_RE.test(id);\n}\n\n/**\n * A file is generated when it matches a repo-declared `linguist-generated`\n * pattern or the filename/path heuristic. `patterns` come from\n * {@link parseGeneratedPatterns}; pass `[]` when no `.gitattributes` exists.\n */\nexport function isGeneratedFile(\n id: string,\n patterns: readonly RegExp[],\n): boolean {\n for (const rx of patterns) if (rx.test(id)) return true;\n return isGeneratedByHeuristic(id);\n}\n\n/** Read `<rootDir>/.gitattributes` and compile its linguist-generated patterns. */\nexport function loadGeneratedPatterns(rootDir: string): RegExp[] {\n try {\n const content = readFileSync(path.join(rootDir, \".gitattributes\"), \"utf-8\");\n return parseGeneratedPatterns(content);\n } catch {\n return [];\n }\n}\n\n/**\n * Parse `.gitattributes` text into regexes for every path pattern marked\n * `linguist-generated` (bare, or `=true`). A later `-linguist-generated` /\n * `=false` on the same line wins (git's last-attribute-wins rule), letting a\n * repo opt a path back out.\n */\nexport function parseGeneratedPatterns(content: string): RegExp[] {\n const out: RegExp[] = [];\n for (const raw of content.split(\"\\n\")) {\n const line = raw.trim();\n if (!line || line.startsWith(\"#\")) continue;\n const parsed = /^(\"(?:[^\"\\\\]|\\\\.)*\"|\\S+)\\s*(.*)$/.exec(line);\n if (!parsed) continue;\n const pattern = unquote(parsed[1]!);\n if (generatedAttr(parsed[2]!.split(/\\s+/).filter(Boolean)) === true) {\n out.push(gitattributesPatternToRegex(pattern));\n }\n }\n return out;\n}\n\n/** Resolve the effective linguist-generated value from a line's attribute tokens. */\nfunction generatedAttr(attrs: readonly string[]): boolean | undefined {\n let value: boolean | undefined;\n for (const a of attrs) {\n if (a === \"linguist-generated\" || a === \"linguist-generated=true\") value = true;\n else if (a === \"-linguist-generated\" || a === \"linguist-generated=false\") value = false;\n }\n return value;\n}\n\nfunction unquote(token: string): string {\n return token.startsWith('\"') && token.endsWith('\"')\n ? token.slice(1, -1).replace(/\\\\(.)/g, \"$1\")\n : token;\n}\n\n/**\n * Convert a gitignore-style `.gitattributes` path pattern to a regex matched\n * against a repo-relative posix id: a slash-free pattern matches at any depth\n * (`*.gen.ts`), a pattern with a slash anchors to the root, `**` crosses dirs,\n * `*`/`?` stay within a segment, and a trailing `/` matches a directory prefix.\n */\nexport function gitattributesPatternToRegex(pattern: string): RegExp {\n let p = pattern;\n const isDir = p.endsWith(\"/\");\n if (isDir) p = p.slice(0, -1);\n let body = \"\";\n for (let i = 0; i < p.length; i++) {\n const c = p[i]!;\n if (c === \"*\" && p[i + 1] === \"*\") {\n body += \".*\";\n i++;\n } else if (c === \"*\") body += \"[^/]*\";\n else if (c === \"?\") body += \"[^/]\";\n else body += c.replace(/[.+(){}|^$\\\\[\\]]/, \"\\\\$&\");\n }\n const prefix = p.includes(\"/\") ? \"^\" : \"(?:^|/)\";\n return new RegExp(prefix + body + (isDir ? \"(?:/|$)\" : \"$\"));\n}\n","import type { GraphNode, NodeRole } from \"./types.js\";\nimport { isGeneratedFile, loadGeneratedPatterns } from \"./generated.js\";\n\nconst TEST_RE = /(?:^|\\/)(?:__tests__\\/|tests?\\/)|\\.(?:test|spec)(?:\\.[a-z]+)?$/;\nconst FIXTURE_RE = /(?:^|\\/)fixtures(?:\\/|$)/;\n// One-off tooling under scripts/ or dormant archive/ dirs: report noise, not\n// product signal. Test-ness wins (checked first) since it's more meaningful.\nconst SCRIPT_RE = /(?:^|\\/)(?:scripts|archive)(?:\\/|$)/;\nconst BARREL_RE = /(?:^|\\/)index(?:\\.[a-z]+)?$/;\nconst TYPES_RE = /(?:^|\\/)(?:[a-z][\\w-]*\\.)?types(?:\\.[a-z]+)?$/i;\nconst CONFIG_RE = /\\.config(?:\\.[a-z]+)?$/;\n\nexport const ALL_ROLES: readonly NodeRole[] = [\n \"test\",\n \"fixture\",\n \"barrel\",\n \"types\",\n \"config\",\n \"script\",\n \"entry\",\n \"generated\",\n \"source\",\n];\n\nexport interface RoleHints {\n /** File begins with a `#!` shebang, i.e. it is an executable entry point. */\n hasShebang?: boolean;\n /** File is codegen output (`.gitattributes linguist-generated` or heuristic). */\n isGenerated?: boolean;\n}\n\nexport function classifyRole(id: string, hints?: RoleHints): NodeRole {\n // Generated wins outright: codegen output is noise for every human-facing\n // signal, so labeling it `generated` (not, say, `barrel` for a `.gen` index)\n // is what lets hotspots/unused-exports exclude it at one shared boundary.\n if (hints?.isGenerated) return \"generated\";\n if (TEST_RE.test(id)) return \"test\";\n if (FIXTURE_RE.test(id)) return \"fixture\";\n if (SCRIPT_RE.test(id)) return \"script\";\n // A shebang-prefixed file (e.g. a CLI's index.ts) is an executable entry\n // point, not re-export plumbing — check before the filename barrel heuristic\n // so it doesn't get mislabeled as `barrel`. `entry` seeds reachability (like\n // a barrel) and is exempt from the fan-out smell (an entry legitimately wires\n // up many command modules).\n if (hints?.hasShebang) return \"entry\";\n if (BARREL_RE.test(id)) return \"barrel\";\n if (TYPES_RE.test(id)) return \"types\";\n if (CONFIG_RE.test(id)) return \"config\";\n return \"source\";\n}\n\nexport interface AnnotateRolesOptions {\n /** Node ids whose source begins with a `#!` shebang. */\n shebangIds?: ReadonlySet<string>;\n /** Node ids detected as generated (codegen output). */\n generatedIds?: ReadonlySet<string>;\n}\n\ninterface ReadFileLike {\n filePath: string;\n content: string;\n}\n\n/**\n * Derive per-file role hints for a batch of read files in one pass: the shebang\n * set (executable entries) and the generated set (codegen output, detected via\n * `.gitattributes linguist-generated` under `idRoot` plus filename/path\n * heuristics). Lives here so the indexer stays at its file-size ceiling.\n */\nexport function computeRoleHints(\n readFiles: readonly ReadFileLike[],\n idRoot: string,\n toId: (root: string, filePath: string) => string,\n): AnnotateRolesOptions {\n const patterns = loadGeneratedPatterns(idRoot);\n const shebangIds = new Set<string>();\n const generatedIds = new Set<string>();\n for (const rf of readFiles) {\n const id = toId(idRoot, rf.filePath);\n if (rf.content.startsWith(\"#!\")) shebangIds.add(id);\n if (isGeneratedFile(id, patterns)) generatedIds.add(id);\n }\n return { shebangIds, generatedIds };\n}\n\nexport function annotateRoles(\n nodes: readonly GraphNode[],\n options?: AnnotateRolesOptions,\n): GraphNode[] {\n return nodes.map((n) =>\n n.kind === \"file\" || n.kind === \"module\"\n ? {\n ...n,\n role:\n n.role ??\n classifyRole(n.id, {\n hasShebang: options?.shebangIds?.has(n.id) ?? false,\n isGenerated: options?.generatedIds?.has(n.id) ?? false,\n }),\n }\n : n,\n );\n}\n","import * as fs from \"node:fs/promises\";\nimport * as path from \"node:path\";\nimport { isExcludedDir, shouldIncludeFile } from \"./parser/index.js\";\n\n/**\n * Recursively collect the source files under `rootDirs` that pass the ingest\n * filter for `languages`, deduped by absolute path across roots (walk order\n * preserved). Extracted from the indexer (C-61): as a function nested inside the\n * per-root loop, the recursive walker carried a cognitive-complexity nesting\n * bonus that made it the indexer's single most complex function (cx 19, above\n * the exported entry point). Hoisting it here — one recursion helper, one\n * directory branch — drops it well under budget and makes the walker unit-\n * testable in isolation.\n */\nexport async function walkSourceFiles(\n rootDirs: readonly string[],\n languages: readonly string[],\n): Promise<string[]> {\n const seen = new Set<string>();\n const out: string[] = [];\n for (const rootDir of rootDirs) {\n await collectFiles(rootDir, rootDir, languages, seen, out);\n }\n return out;\n}\n\nasync function collectFiles(\n dir: string,\n rootDir: string,\n languages: readonly string[],\n seen: Set<string>,\n out: string[],\n): Promise<void> {\n const entries = await fs.readdir(dir, { withFileTypes: true });\n for (const entry of entries) {\n const full = path.join(dir, entry.name);\n // Prune excluded directories (node_modules, dist, .git, …) instead of\n // recursing into them: `shouldIncludeFile` already rejects every file\n // beneath them, so descending only pays to `readdir` a potentially multi-GB\n // tree and discard all of it. Pruning here changes no output but makes\n // `graph index` on a repo with installed dependencies orders of magnitude\n // faster (found dogfooding on a pnpm monorepo — a whole-repo index hung in\n // node_modules for minutes).\n if (entry.isDirectory()) {\n if (isExcludedDir(entry.name)) continue;\n await collectFiles(full, rootDir, languages, seen, out);\n } else if (entry.isFile() && !seen.has(full)) {\n const relative = path.relative(rootDir, full);\n if (shouldIncludeFile(relative, [...languages])) {\n seen.add(full);\n out.push(full);\n }\n }\n }\n}\n","import type { GraphEdge, GraphNode } from \"./types.js\";\n\n/** Edge weight (C-51 reference count), floored at 1 for unweighted edges. */\nexport function edgeWeight(e: GraphEdge): number {\n const w = (e.attrs as { weight?: number } | undefined)?.weight;\n return typeof w === \"number\" && w > 0 ? w : 1;\n}\n\n/**\n * Drop `references` edges (C-53) whose target `symbol` node doesn't exist — an\n * aliased re-export chain, or a barrel that changed under a reused file, can\n * resolve a name to an origin that doesn't declare it. Only `references` can\n * dangle (imports/re-exports resolve to always-emitted file/external nodes);\n * metrics already guard unknown ids, so this just keeps the persisted edge set\n * clean. Mutates `edges` in place.\n */\nexport function pruneDanglingReferences(\n nodes: ReadonlyMap<string, GraphNode>,\n edges: Map<string, GraphEdge>,\n): void {\n for (const [key, edge] of edges) {\n if (edge.kind === \"references\" && !nodes.has(edge.dstId)) edges.delete(key);\n }\n}\n\n/**\n * Resolve edges that land on a barrel (`role=\"barrel\"` — a bare `index.*`\n * re-export file) onto the files the barrel actually re-exports from, so\n * downstream signals measure the real dependency surface instead of the\n * re-export plumbing. A cross-package `import … from \"@codewatch/graph\"`\n * resolves to the barrel; without this every such import piles onto the barrel\n * as an artificial hub while the module that truly does the work is\n * under-credited.\n *\n * An inbound edge `F → B` (weight w) is split across B's outbound re-export\n * targets `t_i` in proportion to each target's re-export weight `r_i` (C-51:\n * the count of names B forwards from `t_i`). This CONSERVES w — no magnitude\n * inflation, unlike a uniform fan-out that turns one import into N edges — and\n * attributes it by how much of the barrel each target supplies. Resolution\n * recurses through barrel chains (a barrel re-exporting a barrel) with a\n * visited guard against re-export cycles; a barrel with no resolvable\n * re-exports is left as its own target so the dependency is never dropped.\n *\n * Pure over the assembled node/edge set, so it is deterministic regardless of\n * how much of an incremental index was reused.\n */\nexport function resolveBarrelEdges(\n nodes: readonly GraphNode[],\n edges: readonly GraphEdge[],\n): GraphEdge[] {\n const barrels = new Set<string>();\n for (const n of nodes) if (n.role === \"barrel\") barrels.add(n.id);\n if (barrels.size === 0) return [...edges];\n\n // Each barrel's outbound forwarding edges (re-exports, and any import it then\n // re-exports), keyed by barrel id, as {target, weight} to split inbound by.\n // A `references` edge is per-symbol *usage*, not re-export plumbing (a pure\n // barrel emits none — it only `export … from`s), so it's excluded from the\n // forwarding basis and kept as a real edge below (C-68).\n const forwards = new Map<string, { dst: string; weight: number }[]>();\n for (const e of edges) {\n if (!barrels.has(e.srcId) || e.kind === \"references\") continue;\n const arr = forwards.get(e.srcId) ?? [];\n arr.push({ dst: e.dstId, weight: edgeWeight(e) });\n forwards.set(e.srcId, arr);\n }\n\n const cache = new Map<string, Map<string, number>>();\n /** Distribution of a target over real (non-barrel) files: realId → fraction (sums to 1). */\n function resolve(id: string, seen: ReadonlySet<string>): Map<string, number> {\n if (!barrels.has(id)) return new Map([[id, 1]]);\n const cached = cache.get(id);\n if (cached) return cached;\n const targets = forwards.get(id);\n const total = targets?.reduce((s, t) => s + t.weight, 0) ?? 0;\n // Dead-end barrel (forwards nothing resolvable) or a cycle: keep as itself.\n if (!targets || total === 0 || seen.has(id)) return new Map([[id, 1]]);\n const out = new Map<string, number>();\n const nextSeen = new Set(seen).add(id);\n for (const t of targets) {\n const share = t.weight / total;\n for (const [real, frac] of resolve(t.dst, nextSeen)) {\n out.set(real, (out.get(real) ?? 0) + share * frac);\n }\n }\n cache.set(id, out);\n return out;\n }\n\n const resolved: GraphEdge[] = [];\n for (const e of edges) {\n // A barrel's own outbound import/re-export edges are plumbing — captured by\n // resolving its inbound edges to targets — so drop them to avoid\n // double-crediting. A `references` edge is genuine usage (e.g. a composition\n // root that the role classifier labels `barrel` but which actually consumes\n // a symbol via `const { runX } = await import(...)`, C-68), so keep it.\n if (barrels.has(e.srcId) && e.kind !== \"references\") continue;\n if (!barrels.has(e.dstId)) {\n resolved.push(e);\n continue;\n }\n const w = edgeWeight(e);\n for (const [real, frac] of resolve(e.dstId, new Set())) {\n if (real === e.srcId) continue; // a barrel that circles back to the importer\n resolved.push({\n ...e,\n dstId: real,\n attrs: { ...(e.attrs as object), weight: w * frac },\n });\n }\n }\n return resolved;\n}\n","import { createHash } from \"node:crypto\";\nimport * as fs from \"node:fs/promises\";\nimport type { Node } from \"web-tree-sitter\";\nimport { getLanguageFromPath, type ParsedFile } from \"./parser/index.js\";\nimport type { CodeGraphStore } from \"./store.js\";\nimport { fileId } from \"./extractors/ids.js\";\nimport { SOURCE_METRIC_NAMES } from \"./source-metrics.js\";\nimport { reconstructCosmetic, reconstructFragment } from \"./reconstruct.js\";\nimport type { Extractor } from \"./parser/index.js\";\nimport type {\n FileFingerprint,\n GraphEdge,\n GraphFragment,\n GraphMetric,\n GraphNode,\n} from \"./types.js\";\n\n/** SHA-256 of file content. The fingerprint that gates per-file reuse. */\nexport function hashContent(content: string): string {\n return createHash(\"sha256\").update(content, \"utf8\").digest(\"hex\");\n}\n\n/**\n * Comment/whitespace-insensitive hash of a file's parse structure (C-18). Walks\n * the tree-sitter tree emitting each node's type plus leaf text, skipping comment\n * nodes; whitespace and positions are absent from the tree, so a pure reformat or\n * comment edit yields the SAME signature while any token change yields a new one.\n * Two files with an equal signature therefore produce identical edges and AST\n * metrics and differ only in line spans (and loc) — the COSMETIC reuse class.\n */\nexport function structuralSignature(file: ParsedFile): string {\n const parts: string[] = [];\n const visit = (node: Node): void => {\n if (node.type === \"comment\") return;\n parts.push(node.type);\n if (node.childCount === 0) parts.push(node.text);\n for (const child of node.children) if (child) visit(child);\n };\n visit(file.tree.rootNode);\n return hashContent(parts.join(\"\u001f\"));\n}\n\n/** The languages that have an extractor; anything else is skipped by the walk. */\nexport type SourceLanguage = \"typescript\" | \"python\";\n\nexport interface ReadFile {\n filePath: string;\n language: SourceLanguage;\n content: string;\n hash: string;\n}\n\nfunction sourceLanguage(filePath: string): SourceLanguage | null {\n const language = getLanguageFromPath(filePath);\n return language === \"typescript\" || language === \"python\" ? language : null;\n}\n\n/** Read + hash every source file. Cheap I/O; the parse/extract it gates is not. */\nexport async function readSourceFiles(\n filePaths: readonly string[],\n): Promise<ReadFile[]> {\n const out: ReadFile[] = [];\n for (const filePath of filePaths) {\n const language = sourceLanguage(filePath);\n if (language === null) continue;\n const content = await fs.readFile(filePath, \"utf-8\");\n out.push({ filePath, language, content, hash: hashContent(content) });\n }\n return out;\n}\n\n/**\n * Split files into those to (re)parse and those reusable from the prior snapshot.\n * A file is reused when its content hash matches AND it is not in `affected` — the\n * set (C-20) whose edges a file-membership delta changed, which must re-extract\n * despite being byte-identical. `affected` is empty when membership is unchanged.\n */\nexport function classifyForReuse(\n readFiles: readonly ReadFile[],\n idRoot: string,\n reuse: ReuseBasis | null,\n affected: ReadonlySet<string> = new Set(),\n): { toParse: ReadFile[]; reusedFileIds: string[] } {\n const toParse: ReadFile[] = [];\n const reusedFileIds: string[] = [];\n for (const rf of readFiles) {\n const id = fileId(idRoot, rf.filePath);\n if (reuse && reuse.fingerprints.get(id) === rf.hash && !affected.has(id)) {\n reusedFileIds.push(id);\n } else {\n toParse.push(rf);\n }\n }\n return { toParse, reusedFileIds };\n}\n\n/** COSMETIC/structural split of the freshly-parsed files, plus every parsed and\n * reused file's structural signature (to persist for the next run). */\nexport interface ParsedClassification {\n /** Parsed files whose structure matches the basis — reuse edges, skip extract. */\n cosmeticFileIds: Set<string>;\n /** fileId → structural signature, for every parsed file and every reused file. */\n structuralByFileId: Map<string, string>;\n}\n\n/**\n * Split freshly-parsed files into COSMETIC (structure unchanged vs the basis —\n * only comments/whitespace moved) and STRUCTURAL (a real token change), by\n * comparing each file's structural signature against the basis (C-18). Also\n * carries reused (NONE-tier) files' signatures forward so they survive across\n * generations rather than decaying to NULL after their first reuse.\n */\nexport function classifyParsed(\n parsedByPath: Map<string, ParsedFile>,\n idRoot: string,\n reuse: ReuseBasis | null,\n reusedFileIds: readonly string[],\n affected: ReadonlySet<string> = new Set(),\n): ParsedClassification {\n const cosmeticFileIds = new Set<string>();\n const structuralByFileId = new Map<string, string>();\n for (const [filePath, parsed] of parsedByPath) {\n const id = fileId(idRoot, filePath);\n const sig = structuralSignature(parsed);\n structuralByFileId.set(id, sig);\n // A file affected by a membership delta (C-20) has a stale basis edge set even\n // though its structure is unchanged, so it must NOT take the cosmetic path.\n if (reuse && !affected.has(id) && reuse.structuralHashes.get(id) === sig) {\n cosmeticFileIds.add(id);\n }\n }\n if (reuse) {\n for (const id of reusedFileIds) {\n const sig = reuse.structuralHashes.get(id);\n if (sig !== undefined) structuralByFileId.set(id, sig);\n }\n }\n return { cosmeticFileIds, structuralByFileId };\n}\n\n/**\n * Build fragments for all files in walk order — extracting STRUCTURAL freshly-\n * parsed files, reconstructing NONE-tier reused ones, and (C-18) reconstructing\n * COSMETIC files from the basis edges while refreshing their symbol line spans\n * from the fresh parse, skipping the expensive ts-morph extract. Walk order is\n * preserved so the merge dedup resolves shared nodes identically to a full index.\n */\nexport function assembleFragments(input: {\n readFiles: readonly ReadFile[];\n idRoot: string;\n parsedByPath: Map<string, ParsedFile>;\n reuse: ReuseBasis | null;\n cosmeticFileIds: ReadonlySet<string>;\n extractor: Extractor<GraphFragment>;\n}): GraphFragment[] {\n const fragments: GraphFragment[] = [];\n for (const rf of input.readFiles) {\n const parsed = input.parsedByPath.get(rf.filePath);\n const id = fileId(input.idRoot, rf.filePath);\n if (parsed && input.reuse && input.cosmeticFileIds.has(id)) {\n fragments.push(\n reconstructCosmetic(input.idRoot, rf.filePath, id, input.reuse, parsed),\n );\n } else if (parsed) {\n fragments.push(...input.extractor.extract(parsed));\n } else if (input.reuse) {\n fragments.push(\n reconstructFragment(input.idRoot, rf.filePath, id, input.reuse),\n );\n }\n }\n return fragments;\n}\n\n/**\n * One fingerprint per file (content hash + C-18 structural signature), to persist\n * for the next run to diff against. The structural signature is looked up per\n * file; a file with none (e.g. a non-TS file, or a reuse carried from a pre-C-18\n * basis) stores NULL and simply can't take the cosmetic path next time.\n */\nexport function buildFingerprints(\n readFiles: readonly ReadFile[],\n idRoot: string,\n structuralByFileId: ReadonlyMap<string, string>,\n): FileFingerprint[] {\n return readFiles.map((rf) => {\n const id = fileId(idRoot, rf.filePath);\n return {\n fileId: id,\n contentHash: rf.hash,\n structuralHash: structuralByFileId.get(id),\n };\n });\n}\n\n/**\n * Everything from a prior snapshot needed to rebuild an unchanged file's\n * contribution to the graph without re-parsing it: its content fingerprint,\n * the node table (to look up external-node names), outbound edges grouped by\n * source, and the source-content metrics (loc, complexity, lcom4, ...).\n */\nexport interface ReuseBasis {\n snapshotId: number;\n fingerprints: Map<string, string>;\n /** Per-file structural signature (C-18); empty for a pre-C-18 basis. */\n structuralHashes: Map<string, string>;\n nodesById: Map<string, GraphNode>;\n edgesBySrc: Map<string, GraphEdge[]>;\n sourceMetricsByFile: Map<string, GraphMetric[]>;\n /** Symbol nodes grouped by their declaring file id, to carry forward for reused files (C-53). */\n symbolsByFile: Map<string, GraphNode[]>;\n}\n\n/**\n * Pick the most recent snapshot usable as a reuse basis: same index version and\n * carrying file fingerprints. Older snapshots predate the fingerprint table and\n * can't be diffed, so we return null and the caller does a full index.\n */\nexport function loadReuseBasis(\n db: CodeGraphStore,\n indexVersion: string,\n): ReuseBasis | null {\n for (const snap of db.listSnapshots({ limit: 50 })) {\n if (snap.indexVersion !== indexVersion) continue;\n const fingerprints = db.listFingerprints(snap.id);\n if (fingerprints.length === 0) continue;\n\n const nodesById = new Map<string, GraphNode>();\n const symbolsByFile = new Map<string, GraphNode[]>();\n // Opt into the symbol layer: reuse must carry symbol nodes and their inbound\n // reference edges forward for unchanged files (C-53).\n for (const n of db.listNodes(snap.id, { includeSymbols: true })) {\n nodesById.set(n.id, n);\n if (n.kind === \"symbol\" && n.parentId) {\n const bucket = symbolsByFile.get(n.parentId);\n if (bucket) bucket.push(n);\n else symbolsByFile.set(n.parentId, [n]);\n }\n }\n\n const edgesBySrc = new Map<string, GraphEdge[]>();\n for (const e of db.listEdges(snap.id, { includeReferences: true })) {\n const bucket = edgesBySrc.get(e.srcId);\n if (bucket) bucket.push(e);\n else edgesBySrc.set(e.srcId, [e]);\n }\n\n const sourceMetricsByFile = new Map<string, GraphMetric[]>();\n for (const m of db.listMetrics(snap.id)) {\n // Source-content metrics are pure functions of a file's bytes, so they\n // carry forward verbatim for an unchanged file.\n if (!SOURCE_METRIC_NAMES.has(m.name)) continue;\n // Per-symbol metrics (C-58) live on `<fileId>#<name>` nodes; bucket them\n // under their parent file so an unchanged file carries its symbol\n // complexity forward alongside its file-level source metrics.\n const node = nodesById.get(m.nodeId);\n const fileKey =\n node?.kind === \"symbol\" && node.parentId ? node.parentId : m.nodeId;\n const bucket = sourceMetricsByFile.get(fileKey);\n if (bucket) bucket.push(m);\n else sourceMetricsByFile.set(fileKey, [m]);\n }\n\n const structuralHashes = new Map<string, string>();\n for (const f of fingerprints) {\n if (f.structuralHash) structuralHashes.set(f.fileId, f.structuralHash);\n }\n\n return {\n snapshotId: snap.id,\n fingerprints: new Map(fingerprints.map((f) => [f.fileId, f.contentHash])),\n structuralHashes,\n nodesById,\n edgesBySrc,\n sourceMetricsByFile,\n symbolsByFile,\n };\n }\n return null;\n}\n","import type { Node } from \"web-tree-sitter\";\n\n/**\n * Cognitive complexity per Sonarsource (\"Cognitive Complexity: A new way of\n * measuring understandability\"). Compared to cyclomatic, this:\n *\n * - Doesn't grow per `case` in a switch — the switch counts once.\n * - Adds a nesting bonus: nested control structures cost more than flat ones.\n * - Treats `else if` chains as one increment each, not as nested ifs.\n *\n * Returns the score for a function body. Caller drives the per-function loop.\n */\nexport function cognitiveComplexityOf(body: Node, language: string): number {\n const handler = language === \"python\" ? PY_HANDLER : TS_HANDLER;\n return scoreNode(body, 0, handler);\n}\n\ninterface LanguageHandler {\n /** Returns the nesting-charged increment (+1 + nesting), or 0 if the node\n * isn't a complexity contributor. */\n structuralIncrement: (node: Node, nesting: number) => number;\n /** True if this node increases nesting depth for its descendants. */\n isNesting: (node: Node) => boolean;\n /** Flat +1 increments (no nesting bonus): logical operators, `else`, etc. */\n flatIncrement: (node: Node) => number;\n}\n\nconst TS_STRUCTURAL = new Set([\n \"if_statement\",\n \"for_statement\",\n \"for_in_statement\",\n \"while_statement\",\n \"do_statement\",\n \"switch_statement\",\n \"catch_clause\",\n \"ternary_expression\",\n]);\n\nconst PY_STRUCTURAL = new Set([\n \"if_statement\",\n \"for_statement\",\n \"while_statement\",\n \"except_clause\",\n \"conditional_expression\",\n // elif_clause is intentionally NOT here — it's a flat continuation of an\n // existing if (counted in flatIncrement). Including it would charge both\n // a structural and a flat +1.\n]);\n\nconst TS_NESTING = new Set([\n \"if_statement\",\n \"for_statement\",\n \"for_in_statement\",\n \"while_statement\",\n \"do_statement\",\n \"switch_statement\",\n \"catch_clause\",\n \"function_declaration\",\n \"method_definition\",\n \"arrow_function\",\n]);\n\nconst PY_NESTING = new Set([\n \"if_statement\",\n \"for_statement\",\n \"while_statement\",\n \"except_clause\",\n \"function_definition\",\n \"lambda\",\n]);\n\nconst TS_HANDLER: LanguageHandler = {\n structuralIncrement(node, nesting) {\n return TS_STRUCTURAL.has(node.type) ? 1 + nesting : 0;\n },\n isNesting(node) {\n return TS_NESTING.has(node.type);\n },\n flatIncrement(node) {\n if (node.type === \"else_clause\") return 1;\n if (\n node.type === \"binary_expression\" &&\n isLogicalOp(node) &&\n !isInnerOfSameOpChain(node, \"binary_expression\")\n ) {\n return 1;\n }\n return 0;\n },\n};\n\nconst PY_HANDLER: LanguageHandler = {\n structuralIncrement(node, nesting) {\n return PY_STRUCTURAL.has(node.type) ? 1 + nesting : 0;\n },\n isNesting(node) {\n return PY_NESTING.has(node.type);\n },\n flatIncrement(node) {\n if (node.type === \"elif_clause\" || node.type === \"else_clause\") return 1;\n if (\n node.type === \"boolean_operator\" &&\n !isInnerOfSameOpChain(node, \"boolean_operator\")\n ) {\n return 1;\n }\n return 0;\n },\n};\n\nfunction isLogicalOp(node: Node): boolean {\n const op = node.childForFieldName(\"operator\");\n return !!op && (op.text === \"&&\" || op.text === \"||\");\n}\n\n/**\n * Per Sonarsource: a sequence of like binary logical operators counts once,\n * not per occurrence. `a && b && c` is one chain (+1), not two (+2). Skip\n * the increment when this node's operator matches its parent's operator —\n * the outer node already counted the chain.\n */\nfunction isInnerOfSameOpChain(node: Node, parentType: string): boolean {\n const parent = node.parent;\n if (!parent || parent.type !== parentType) return false;\n const op = node.childForFieldName(\"operator\")?.text;\n const parentOp = parent.childForFieldName(\"operator\")?.text;\n return !!op && op === parentOp;\n}\n\n/**\n * A nested if under an else_clause is part of an `else if` chain — the\n * else_clause already counted +1, and treating it as a nested if would\n * double-charge AND inflate the nesting bonus. Skip both.\n */\nfunction isElseIf(node: Node): boolean {\n if (node.type !== \"if_statement\" && node.type !== \"elif_clause\") return false;\n const parent = node.parent;\n return !!parent && parent.type === \"else_clause\";\n}\n\nfunction scoreNode(\n node: Node,\n nesting: number,\n handler: LanguageHandler,\n): number {\n const elseIf = isElseIf(node);\n const structural = elseIf ? 0 : handler.structuralIncrement(node, nesting);\n const flat = handler.flatIncrement(node);\n const childNesting =\n !elseIf && handler.isNesting(node) ? nesting + 1 : nesting;\n\n let score = structural + flat;\n for (const child of node.namedChildren) {\n if (child) score += scoreNode(child, childNesting, handler);\n }\n return score;\n}\n","import type { ParsedFile } from \"./parser/index.js\";\nimport type { Node } from \"web-tree-sitter\";\nimport type { GraphMetric } from \"./types.js\";\n\n/**\n * Lack of Cohesion of Methods, LCOM4 variant: count connected components\n * in the graph where each method is a node and two methods are linked when\n * they share at least one `this.<x>` (or `self.<x>`) reference or one calls\n * the other. LCOM4=1 → cohesive class; LCOM4>1 → the class could be split.\n *\n * Static methods and constructors are excluded (they don't bind to `this`).\n * Files with no classes emit no metrics. Files with classes emit\n * `lcom4_max` (max LCOM4 across classes in the file) and `class_count`.\n */\nexport function computeLcomMetrics(\n file: ParsedFile,\n fileId: string,\n): GraphMetric[] {\n const classes = collectClasses(file);\n if (classes.length === 0) return [];\n const out: GraphMetric[] = [\n {\n nodeId: fileId,\n name: \"class_count\",\n value: classes.length,\n unit: \"count\",\n },\n ];\n const scores = classes\n .map((c) => lcom4OfClass(c, file.language))\n .filter((s): s is number => s !== null);\n if (scores.length > 0) {\n out.push({\n nodeId: fileId,\n name: \"lcom4_max\",\n value: Math.max(...scores),\n unit: \"count\",\n });\n }\n return out;\n}\n\nfunction collectClasses(file: ParsedFile): Node[] {\n const isClass =\n file.language === \"python\"\n ? (n: Node) => n.type === \"class_definition\"\n : (n: Node) =>\n n.type === \"class_declaration\" || n.type === \"class_expression\";\n const out: Node[] = [];\n const visit = (node: Node): void => {\n if (isClass(node)) out.push(node);\n for (const child of node.namedChildren) {\n if (child) visit(child);\n }\n };\n visit(file.tree.rootNode);\n return out;\n}\n\ninterface MethodInfo {\n name: string;\n usedNames: Set<string>;\n}\n\nfunction lcom4OfClass(classNode: Node, language: string): number | null {\n const methods = collectMethods(classNode, language);\n if (methods.length === 0) return null;\n if (methods.length === 1) return 1;\n return countComponents(methods);\n}\n\nfunction collectMethods(classNode: Node, language: string): MethodInfo[] {\n return language === \"python\"\n ? collectPythonMethods(classNode)\n : collectTsMethods(classNode);\n}\n\nfunction collectTsMethods(classNode: Node): MethodInfo[] {\n const body = classNode.childForFieldName(\"body\");\n if (!body) return [];\n const out: MethodInfo[] = [];\n for (const child of body.namedChildren) {\n if (!child || child.type !== \"method_definition\") continue;\n if (hasStaticModifier(child)) continue;\n const name = methodName(child);\n if (!name || name === \"constructor\") continue;\n out.push({ name, usedNames: thisRefs(child, \"this\") });\n }\n return out;\n}\n\nfunction collectPythonMethods(classNode: Node): MethodInfo[] {\n const body = classNode.childForFieldName(\"body\");\n if (!body) return [];\n const out: MethodInfo[] = [];\n for (const child of body.namedChildren) {\n if (!child || child.type !== \"function_definition\") continue;\n const name = child.childForFieldName(\"name\")?.text;\n if (!name || name === \"__init__\") continue;\n if (isPythonStaticOrClassMethod(child)) continue;\n out.push({ name, usedNames: thisRefs(child, \"self\") });\n }\n return out;\n}\n\nfunction methodName(methodNode: Node): string | null {\n const nameNode = methodNode.childForFieldName(\"name\");\n return nameNode?.text ?? null;\n}\n\nfunction hasStaticModifier(methodNode: Node): boolean {\n for (const child of methodNode.children) {\n if (child?.type === \"static\") return true;\n }\n return false;\n}\n\nfunction isPythonStaticOrClassMethod(funcNode: Node): boolean {\n let prev = funcNode.previousNamedSibling;\n while (prev && prev.type === \"decorator\") {\n const text = prev.text;\n if (text.includes(\"@staticmethod\") || text.includes(\"@classmethod\")) {\n return true;\n }\n prev = prev.previousNamedSibling;\n }\n return false;\n}\n\nfunction thisRefs(methodNode: Node, selfKeyword: string): Set<string> {\n const out = new Set<string>();\n const visit = (n: Node): void => {\n collectThisRef(n, selfKeyword, out);\n for (const child of n.namedChildren) {\n if (child) visit(child);\n }\n };\n visit(methodNode);\n return out;\n}\n\nfunction collectThisRef(\n n: Node,\n selfKeyword: string,\n out: Set<string>,\n): void {\n if (n.type === \"member_expression\" || n.type === \"attribute\") {\n const obj = n.childForFieldName(\"object\");\n if (!obj) return;\n if (matchesSelf(obj, selfKeyword)) {\n const prop =\n n.childForFieldName(\"property\") ?? n.childForFieldName(\"attribute\");\n if (prop) out.add(prop.text);\n }\n }\n}\n\nfunction matchesSelf(node: Node, selfKeyword: string): boolean {\n if (selfKeyword === \"this\") return node.type === \"this\";\n return node.type === \"identifier\" && node.text === selfKeyword;\n}\n\nfunction countComponents(methods: readonly MethodInfo[]): number {\n const n = methods.length;\n const parent = Array.from({ length: n }, (_, i) => i);\n const find = (i: number): number => {\n while (parent[i]! !== i) {\n parent[i] = parent[parent[i]!]!;\n i = parent[i]!;\n }\n return i;\n };\n const union = (a: number, b: number): void => {\n const ra = find(a);\n const rb = find(b);\n if (ra !== rb) parent[ra] = rb;\n };\n for (let i = 0; i < n; i++) {\n for (let j = i + 1; j < n; j++) {\n if (areLinked(methods[i]!, methods[j]!)) union(i, j);\n }\n }\n const roots = new Set<number>();\n for (let i = 0; i < n; i++) roots.add(find(i));\n return roots.size;\n}\n\nfunction areLinked(a: MethodInfo, b: MethodInfo): boolean {\n if (a.usedNames.has(b.name)) return true;\n if (b.usedNames.has(a.name)) return true;\n for (const name of a.usedNames) {\n if (b.usedNames.has(name)) return true;\n }\n return false;\n}\n","import type { ParsedFile } from \"./parser/index.js\";\nimport type { Node } from \"web-tree-sitter\";\nimport { cognitiveComplexityOf } from \"./cognitive-complexity.js\";\nimport { computeLcomMetrics } from \"./lcom.js\";\nimport { symbolId } from \"./extractors/ids.js\";\nimport type { GraphMetric } from \"./types.js\";\n\nconst TS_FUNCTION_TYPES = new Set([\n \"function_declaration\",\n \"method_definition\",\n]);\n\nconst PY_FUNCTION_TYPES = new Set([\"function_definition\"]);\n\nconst TS_NESTING_TYPES = new Set([\n \"if_statement\",\n \"for_statement\",\n \"for_in_statement\",\n \"while_statement\",\n \"do_statement\",\n \"switch_statement\",\n \"try_statement\",\n]);\n\nconst PY_NESTING_TYPES = new Set([\n \"if_statement\",\n \"for_statement\",\n \"while_statement\",\n \"try_statement\",\n]);\n\nconst TS_BRANCH_TYPES = new Set([\n \"if_statement\",\n \"for_statement\",\n \"for_in_statement\",\n \"while_statement\",\n \"do_statement\",\n \"switch_case\",\n \"catch_clause\",\n \"ternary_expression\",\n]);\n\nconst PY_BRANCH_TYPES = new Set([\n \"if_statement\",\n \"elif_clause\",\n \"for_statement\",\n \"while_statement\",\n \"except_clause\",\n \"conditional_expression\",\n]);\n\ninterface FunctionStats {\n /** Declared name, or null for an anonymous function (e.g. `export default () => {}`). */\n name: string | null;\n cyclomatic: number;\n cognitive: number;\n nestingDepth: number;\n}\n\n/**\n * Names of every metric `computeSourceMetrics` can emit. These are pure\n * functions of a file's content, so the incremental indexer can carry them\n * forward unchanged for a byte-identical file instead of re-parsing it. If a\n * new source metric is added above, add its name here — the incremental\n * round-trip test will fail loudly if this set drifts out of sync.\n */\nexport const SOURCE_METRIC_NAMES: ReadonlySet<string> = new Set([\n \"loc\",\n \"function_count\",\n \"cyclomatic_max\",\n \"cyclomatic_sum\",\n \"cognitive_max\",\n \"cognitive_sum\",\n \"max_nesting_depth\",\n \"class_count\",\n \"lcom4_max\",\n // Per-symbol complexity (C-58; C-64 extends it to non-exported symbols), keyed\n // to `symbol` node ids. Source-local like the file-level metrics above, so an\n // unchanged file carries them forward — but their nodeId is `<fileId>#<name>`,\n // so the reuse basis buckets them under the symbol's parent file (incremental.ts).\n \"symbol_cognitive\",\n \"symbol_cyclomatic\",\n]);\n\nconst EMPTY_NAMES: ReadonlySet<string> = new Set();\n\n/**\n * Per-file source metrics. `symbolNamesByFile` maps a file id to the names of\n * the `symbol` nodes it declares; when supplied, per-function complexity is\n * emitted on those symbol nodes (C-58; all declared functions since C-64). It\n * defaults to empty, so existing callers/tests that don't thread the symbol\n * layer keep emitting only file-level metrics.\n */\nexport function computeSourceMetrics(\n files: readonly ParsedFile[],\n fileIdOf: (filePath: string) => string,\n symbolNamesByFile: ReadonlyMap<string, ReadonlySet<string>> = new Map(),\n): GraphMetric[] {\n const out: GraphMetric[] = [];\n for (const file of files) {\n const id = fileIdOf(file.filePath);\n out.push(...metricsForFile(id, file, symbolNamesByFile.get(id) ?? EMPTY_NAMES));\n }\n return out;\n}\n\nfunction metricsForFile(\n nodeId: string,\n file: ParsedFile,\n symbolNames: ReadonlySet<string>,\n): GraphMetric[] {\n const out: GraphMetric[] = [];\n const loc = countLoc(file.content);\n out.push({ nodeId, name: \"loc\", value: loc, unit: \"lines\" });\n\n const stats = analyzeFunctions(file);\n out.push({\n nodeId,\n name: \"function_count\",\n value: stats.length,\n unit: \"count\",\n });\n\n if (stats.length > 0) {\n out.push({\n nodeId,\n name: \"cyclomatic_max\",\n value: Math.max(...stats.map((s) => s.cyclomatic)),\n unit: \"count\",\n });\n out.push({\n nodeId,\n name: \"cyclomatic_sum\",\n value: stats.reduce((acc, s) => acc + s.cyclomatic, 0),\n unit: \"count\",\n });\n out.push({\n nodeId,\n name: \"cognitive_max\",\n value: Math.max(...stats.map((s) => s.cognitive)),\n unit: \"count\",\n });\n out.push({\n nodeId,\n name: \"cognitive_sum\",\n value: stats.reduce((acc, s) => acc + s.cognitive, 0),\n unit: \"count\",\n });\n out.push({\n nodeId,\n name: \"max_nesting_depth\",\n value: Math.max(...stats.map((s) => s.nestingDepth)),\n unit: \"count\",\n });\n }\n out.push(...symbolComplexityMetrics(nodeId, stats, symbolNames));\n out.push(...computeLcomMetrics(file, nodeId));\n return out;\n}\n\n/**\n * Per-symbol complexity (C-58, C-64): for each named function whose name has a\n * `symbol` node on this file, emit `symbol_cognitive`/`symbol_cyclomatic` on that\n * node (`<fileId>#<name>`). Model B (C-64) gives non-exported helpers a node too,\n * so internal functions now get their own complexity here, not just exports. A\n * name shared by several functions takes the max (the file-level `_max` framing);\n * a declared name with no complexity (a bare class) matches no stat, emits nothing.\n */\nfunction symbolComplexityMetrics(\n fileId: string,\n stats: readonly FunctionStats[],\n symbolNames: ReadonlySet<string>,\n): GraphMetric[] {\n if (symbolNames.size === 0) return [];\n const byName = new Map<string, { cognitive: number; cyclomatic: number }>();\n for (const s of stats) {\n if (!s.name || !symbolNames.has(s.name)) continue;\n const prev = byName.get(s.name);\n if (!prev) byName.set(s.name, { cognitive: s.cognitive, cyclomatic: s.cyclomatic });\n else {\n prev.cognitive = Math.max(prev.cognitive, s.cognitive);\n prev.cyclomatic = Math.max(prev.cyclomatic, s.cyclomatic);\n }\n }\n const out: GraphMetric[] = [];\n for (const [name, m] of byName) {\n const sid = symbolId(fileId, name);\n out.push({ nodeId: sid, name: \"symbol_cognitive\", value: m.cognitive, unit: \"count\" });\n out.push({ nodeId: sid, name: \"symbol_cyclomatic\", value: m.cyclomatic, unit: \"count\" });\n }\n return out;\n}\n\nfunction countLoc(content: string): number {\n return content.split(\"\\n\").filter((l) => l.trim() !== \"\").length;\n}\n\nfunction analyzeFunctions(file: ParsedFile): FunctionStats[] {\n const stats: FunctionStats[] = [];\n const fnTypes =\n file.language === \"python\" ? PY_FUNCTION_TYPES : TS_FUNCTION_TYPES;\n const visit = (node: Node): void => {\n const fn = functionAt(node, fnTypes);\n if (fn) {\n stats.push({\n name: fn.name,\n cyclomatic: cyclomaticOf(fn.body, file.language),\n cognitive: cognitiveComplexityOf(fn.body, file.language),\n nestingDepth: nestingDepthOf(fn.body, file.language, 0),\n });\n }\n for (const child of node.children) {\n if (child) visit(child);\n }\n };\n visit(file.tree.rootNode);\n return stats;\n}\n\n/**\n * A named, standalone function at this node, with its body and declared name —\n * or null. Covers declarations/methods (name on the node) and, crucially,\n * arrow / function-expression bound to a `const`/`let` (`export const foo =\n * () => {}`), where the name lives on the enclosing variable_declarator. Those\n * bindings were previously invisible to the analyzer (C-58) — a real complexity\n * under-count in an arrow-heavy codebase. Anonymous inline callbacks (parent is\n * a call, not a declarator) are intentionally excluded: their control flow\n * already rolls into the enclosing function's cognitive score.\n */\nfunction functionAt(\n node: Node,\n fnTypes: ReadonlySet<string>,\n): { name: string | null; body: Node } | null {\n if (fnTypes.has(node.type)) {\n const body = node.childForFieldName(\"body\");\n return body ? { name: node.childForFieldName(\"name\")?.text ?? null, body } : null;\n }\n if (\n (node.type === \"arrow_function\" || node.type === \"function_expression\") &&\n node.parent?.type === \"variable_declarator\"\n ) {\n const body = node.childForFieldName(\"body\");\n if (!body) return null;\n return { name: node.parent.childForFieldName(\"name\")?.text ?? null, body };\n }\n return null;\n}\n\nfunction nestingDepthOf(node: Node, language: string, depth: number): number {\n const nestingTypes =\n language === \"python\" ? PY_NESTING_TYPES : TS_NESTING_TYPES;\n let maxDepth = depth;\n for (const child of node.namedChildren) {\n if (!child) continue;\n const next = nestingTypes.has(child.type) ? depth + 1 : depth;\n const childMax = nestingDepthOf(child, language, next);\n if (childMax > maxDepth) maxDepth = childMax;\n }\n return maxDepth;\n}\n\nfunction cyclomaticOf(body: Node, language: string): number {\n const branchTypes =\n language === \"python\" ? PY_BRANCH_TYPES : TS_BRANCH_TYPES;\n let complexity = 1;\n const visit = (node: Node): void => {\n if (branchTypes.has(node.type)) complexity++;\n if (node.type === \"binary_expression\") {\n const op = node.childForFieldName(\"operator\");\n if (op && (op.text === \"&&\" || op.text === \"||\")) complexity++;\n }\n if (language === \"python\" && node.type === \"boolean_operator\") {\n complexity++;\n }\n for (const child of node.namedChildren) {\n if (child) visit(child);\n }\n };\n visit(body);\n return complexity;\n}\n","import type { ParsedFile } from \"./parser/index.js\";\nimport { buildFileModuleNodes } from \"./extractors/file-nodes.js\";\nimport { collectDeclaredSpans, type LineSpan } from \"./declared-names.js\";\nimport type { ReuseBasis } from \"./incremental.js\";\nimport type { GraphFragment, GraphNode } from \"./types.js\";\n\n/**\n * Rebuild an unchanged file's graph fragment from the prior snapshot. Produces\n * byte-for-byte the same nodes and edges the extractor would emit: file +\n * module nodes (path-derived), the symbol nodes it declares (content-derived,\n * so carried forward from the basis rather than rebuilt — C-53), the external\n * nodes its imports reference, and the outbound edges carried forward verbatim.\n */\nexport function reconstructFragment(\n repoRoot: string,\n absPath: string,\n fileId: string,\n basis: ReuseBasis,\n): GraphFragment {\n const nodes = buildFileModuleNodes(repoRoot, absPath);\n nodes.push(...(basis.symbolsByFile.get(fileId) ?? []));\n const edges = basis.edgesBySrc.get(fileId) ?? [];\n const seenExternals = new Set<string>();\n for (const edge of edges) {\n const dst = basis.nodesById.get(edge.dstId);\n if (dst?.kind === \"external\" && !seenExternals.has(dst.id)) {\n seenExternals.add(dst.id);\n nodes.push({ id: dst.id, kind: \"external\", name: dst.name });\n }\n }\n return { nodes, edges };\n}\n\n/**\n * Reconstruct a COSMETIC file's fragment (C-18): its edges + file/module/external\n * nodes are structure-invariant so they come from the basis verbatim, but its\n * symbol line spans shifted with the moved comments/whitespace, so those are\n * refreshed from the fresh parse. Produces byte-for-byte what a full extract\n * would — same names, same `exported`, same edges — with up-to-date spans, while\n * skipping the ts-morph extract entirely. Its loc + AST metrics are recomputed\n * from the same parse by the normal metrics path.\n */\nexport function reconstructCosmetic(\n repoRoot: string,\n absPath: string,\n fileId: string,\n basis: ReuseBasis,\n parsed: ParsedFile,\n): GraphFragment {\n const base = reconstructFragment(repoRoot, absPath, fileId, basis);\n const spans = collectDeclaredSpans(parsed);\n const nodes = base.nodes.map((n) =>\n n.kind === \"symbol\" ? withRefreshedSpan(n, spans) : n,\n );\n return { nodes, edges: base.edges };\n}\n\n/**\n * A symbol node with its line span refreshed from a fresh parse. Span-less\n * symbols (exported types/consts, which never carry startLine) are returned\n * unchanged; a function/method/class symbol takes the freshly-parsed span for\n * its name, matching exactly what the extractor would emit.\n */\nfunction withRefreshedSpan(\n node: GraphNode,\n spans: ReadonlyMap<string, LineSpan>,\n): GraphNode {\n const attrs = node.attrs as\n | { exported?: boolean; startLine?: number; endLine?: number }\n | undefined;\n if (!attrs || attrs.startLine === undefined) return node;\n const span = spans.get(node.name);\n if (!span) return node;\n return {\n ...node,\n attrs: { ...attrs, startLine: span.startLine, endLine: span.endLine },\n };\n}\n","import { RESOLVABLE_EXTS } from \"./extractors/module-resolution.js\";\nimport { parseSymbolId } from \"./extractors/ids.js\";\nimport type { ReuseBasis } from \"./incremental.js\";\nimport type { GraphEdge } from \"./types.js\";\n\n/**\n * Partial reuse across a file-membership delta (C-20). PR #28 fell back to a full\n * index whenever any file was added or removed, because a byte-identical file's\n * import edges resolve against the *global* file set and can change when a\n * referenced path appears or disappears. This narrows that: only files whose\n * edges are actually affected by the delta re-extract; every other unchanged\n * file is still reused.\n *\n * A stored edge carries its original `specifier`, so an unchanged file's relative\n * imports can be re-resolved over the new file-id set — no filesystem, no\n * ts-morph — to detect two effects: a target that was removed, and a target now\n * shadowed by an added file at a higher-priority resolution path.\n *\n * Known limitation (documented, sound-for-valid-code): a previously *unresolved*\n * relative import (which left no edge — a broken import in a file that didn't\n * compile) that an added file now satisfies is not detected, because there is no\n * stored specifier to re-resolve. Such a file is reused until it next changes.\n */\n\n/** Posix dirname of a repo-relative file id (`a/b/c.ts` → `a/b`). */\nfunction dirOf(id: string): string {\n const i = id.lastIndexOf(\"/\");\n return i < 0 ? \"\" : id.slice(0, i);\n}\n\n/** Normalize a posix path, collapsing `.` and `..` segments. */\nfunction normalizePosix(p: string): string {\n const out: string[] = [];\n for (const seg of p.split(\"/\")) {\n if (seg === \"\" || seg === \".\") continue;\n if (seg === \"..\") out.pop();\n else out.push(seg);\n }\n return out.join(\"/\");\n}\n\nconst JS_EXT_RE = /\\.(?:jsx?|mjs|cjs)$/;\n\n/** Candidate file ids a relative import base could resolve to, in priority order\n * — mirrors the extractor's `relativeResolutionCandidates` in id space. */\nfunction* candidateIds(base: string): Iterable<string> {\n yield base;\n for (const ext of RESOLVABLE_EXTS) yield base + ext;\n const jsExt = JS_EXT_RE.exec(base);\n if (jsExt) {\n const stem = base.slice(0, base.length - jsExt[0].length);\n for (const ext of RESOLVABLE_EXTS) yield stem + ext;\n }\n for (const ext of RESOLVABLE_EXTS) yield `${base}/index${ext}`;\n}\n\nfunction isRelative(specifier: string): boolean {\n return specifier === \".\" || specifier === \"..\" ||\n specifier.startsWith(\"./\") || specifier.startsWith(\"../\");\n}\n\n/**\n * Resolve a relative specifier from a source file id to a target file id in the\n * current file set, or null when nothing matches — the id-space equivalent of the\n * extractor's filesystem walk, so it yields the same target for the same inputs.\n */\nexport function resolveRelativeId(\n srcFileId: string,\n specifier: string,\n fileIds: ReadonlySet<string>,\n): string | null {\n const base = normalizePosix(`${dirOf(srcFileId)}/${specifier}`);\n for (const candidate of candidateIds(base)) {\n if (fileIds.has(candidate)) return candidate;\n }\n return null;\n}\n\n/** The file id an edge's target belongs to (a symbol target `f#x` → `f`). */\nfunction targetFileId(edge: GraphEdge): string {\n return parseSymbolId(edge.dstId)?.fileId ?? edge.dstId;\n}\n\n/**\n * File ids that must re-extract because the membership delta changed how their\n * imports resolve: an edge target was removed, or a relative specifier now\n * resolves to a different file (e.g. an added file shadowing the old target).\n * Empty when membership is unchanged — the caller then reuses exactly as before.\n */\nexport function computeDeltaAffected(\n basis: ReuseBasis,\n currentFileIds: ReadonlySet<string>,\n): Set<string> {\n const affected = new Set<string>();\n const removed = new Set<string>();\n for (const id of basis.fingerprints.keys()) {\n if (!currentFileIds.has(id)) removed.add(id);\n }\n const added = currentFileIds.size !== basis.fingerprints.size || removed.size > 0;\n if (!added) return affected; // membership unchanged → nothing forced\n\n for (const [srcId, edges] of basis.edgesBySrc) {\n if (!currentFileIds.has(srcId)) continue; // src itself gone\n for (const edge of edges) {\n if (isEdgeAffected(edge, srcId, removed, currentFileIds)) {\n affected.add(srcId);\n break;\n }\n }\n }\n return affected;\n}\n\nfunction isEdgeAffected(\n edge: GraphEdge,\n srcId: string,\n removed: ReadonlySet<string>,\n currentFileIds: ReadonlySet<string>,\n): boolean {\n if (removed.has(targetFileId(edge))) return true;\n const specifier = (edge.attrs as { specifier?: string } | undefined)?.specifier;\n if (!specifier || !isRelative(specifier)) return false;\n // Re-resolve the relative import; if it now lands on a different file (added\n // shadow, or the old target vanished), the file's edges must be recomputed.\n return resolveRelativeId(srcId, specifier, currentFileIds) !== targetFileId(edge);\n}\n","import type { GraphEdge, GraphMetric, GraphNode } from \"./types.js\";\nimport { edgeWeight, resolveBarrelEdges } from \"./barrel-resolve.js\";\n\ninterface DegreeCounts {\n fanIn: Map<string, number>;\n fanOut: Map<string, number>;\n}\n\n/**\n * fan_in / fan_out are file/module *structural* coupling — how many modules a\n * file depends on, and vice versa. They count only `imports`/`re-exports`\n * edges; the per-symbol `references` layer (C-53) is a finer granularity and\n * must not inflate module degree (one references edge per imported *symbol*\n * would turn \"depends on 3 modules\" into \"names 30 symbols\").\n */\nfunction isStructuralEdge(e: GraphEdge): boolean {\n return e.kind === \"imports\" || e.kind === \"re-exports\";\n}\n\nfunction countDegrees(\n nodes: readonly GraphNode[],\n edges: readonly GraphEdge[],\n): DegreeCounts {\n const fanIn = new Map<string, number>();\n const fanOut = new Map<string, number>();\n for (const n of nodes) {\n fanIn.set(n.id, 0);\n fanOut.set(n.id, 0);\n }\n for (const e of edges) {\n if (!isStructuralEdge(e)) continue;\n if (fanOut.has(e.srcId)) {\n fanOut.set(e.srcId, fanOut.get(e.srcId)! + 1);\n }\n if (fanIn.has(e.dstId)) {\n fanIn.set(e.dstId, fanIn.get(e.dstId)! + 1);\n }\n }\n return { fanIn, fanOut };\n}\n\n/**\n * Utilization: inbound edges weighted by their reference count (C-51's\n * `attrs.weight`), so it measures how heavily a file's exports are actually\n * *used*, not merely how many files name it. Computed over the\n * BARREL-RESOLVED edge set (C-53), so re-export plumbing is credited to the\n * files that do the work rather than to the `index.ts` hub everything routes\n * through. A pure barrel therefore reads utilization ~0 (fan_in still counts\n * its raw importers) — a clean re-export-hub signature. Deterministic over the\n * assembled edge set, so it stays correct under incremental reuse.\n */\nfunction weightedInbound(\n nodes: readonly GraphNode[],\n edges: readonly GraphEdge[],\n): Map<string, number> {\n const util = new Map<string, number>();\n for (const n of nodes) util.set(n.id, 0);\n for (const e of resolveBarrelEdges(nodes, edges)) {\n if (util.has(e.dstId)) util.set(e.dstId, util.get(e.dstId)! + edgeWeight(e));\n }\n return util;\n}\n\nexport function computeMetrics(\n nodes: readonly GraphNode[],\n edges: readonly GraphEdge[],\n): GraphMetric[] {\n const { fanIn, fanOut } = countDegrees(nodes, edges);\n const utilization = weightedInbound(nodes, edges);\n const out: GraphMetric[] = [];\n for (const n of nodes) {\n // Symbol nodes carry only utilization (how heavily the export is used).\n // fan_in/fan_out/instability are module-structural and don't apply — and\n // skipping them keeps the per-export layer from tripling the metric table.\n if (n.kind === \"symbol\") {\n out.push({ nodeId: n.id, name: \"utilization\", value: utilization.get(n.id) ?? 0, unit: \"count\" });\n continue;\n }\n const ci = fanIn.get(n.id) ?? 0;\n const co = fanOut.get(n.id) ?? 0;\n out.push({ nodeId: n.id, name: \"fan_in\", value: ci, unit: \"count\" });\n out.push({ nodeId: n.id, name: \"fan_out\", value: co, unit: \"count\" });\n out.push({ nodeId: n.id, name: \"utilization\", value: utilization.get(n.id) ?? 0, unit: \"count\" });\n if (ci + co > 0) {\n out.push({\n nodeId: n.id,\n name: \"instability\",\n value: co / (ci + co),\n unit: \"ratio\",\n });\n }\n }\n return out;\n}\n","import type { ParsedFile } from \"./parser/index.js\";\nimport { computeMetrics } from \"./metrics.js\";\nimport { computeSourceMetrics } from \"./source-metrics.js\";\nimport { fileId } from \"./extractors/ids.js\";\nimport type { GraphEdge, GraphMetric, GraphNode } from \"./types.js\";\n\nexport interface IndexerMetricsInput {\n nodes: Map<string, GraphNode>;\n edges: Map<string, GraphEdge>;\n /** Files (re)parsed this run — source metrics are computed fresh for these. */\n parsedFiles: ParsedFile[];\n /** Source metrics carried forward verbatim for reused (unchanged) files. */\n reusedSourceMetrics: GraphMetric[];\n idRoot: string;\n}\n\n/**\n * Map each file id to the names of the `symbol` nodes it declares, so\n * `computeSourceMetrics` can attach per-function complexity. Reflects the\n * assembled node set (parsed + reused symbol nodes alike), so per-symbol\n * complexity is emitted for exactly the names that have a symbol node.\n */\nfunction symbolNamesByFile(nodes: Iterable<GraphNode>): Map<string, Set<string>> {\n const out = new Map<string, Set<string>>();\n for (const n of nodes) {\n if (n.kind !== \"symbol\" || !n.parentId) continue;\n const bucket = out.get(n.parentId);\n if (bucket) bucket.add(n.name);\n else out.set(n.parentId, new Set([n.name]));\n }\n return out;\n}\n\n/**\n * Assemble the metric set for a snapshot: graph-wide degree metrics over the\n * full node/edge set, freshly-computed source metrics for (re)parsed files, and\n * reused source metrics carried forward for unchanged files. Everything but the\n * reused source metrics is recomputed over the full set, so the result matches a\n * full index regardless of how much was reused.\n *\n * Git-history metrics (churn, ownership, change coupling, test coverage) are\n * deliberately absent here; they are a separate analysis layer, deferred with\n * the rest of codewatch's analyses.\n */\nexport function buildIndexerMetrics(input: IndexerMetricsInput): GraphMetric[] {\n const nodeList = [...input.nodes.values()];\n return [\n ...computeMetrics(nodeList, [...input.edges.values()]),\n ...computeSourceMetrics(\n input.parsedFiles,\n (p) => fileId(input.idRoot, p),\n symbolNamesByFile(nodeList),\n ),\n ...input.reusedSourceMetrics,\n ];\n}\n","import type { GraphEdge, GraphFragment, GraphNode } from \"./types.js\";\n\nexport interface ExtractAccumulator {\n nodes: Map<string, GraphNode>;\n edges: Map<string, GraphEdge>;\n}\n\n/**\n * JSON-encoded so ids containing the separator can't collide (paths may hold\n * spaces). Mirrors the `(snapshot_id, src_id, dst_id, kind)` edge primary key.\n */\nfunction edgeKey(edge: GraphEdge): string {\n return JSON.stringify([edge.srcId, edge.dstId, edge.kind]);\n}\n\n/** First fragment wins per id, so walk order decides shared nodes exactly as a full index would. */\nexport function mergeFragments(fragments: readonly GraphFragment[]): ExtractAccumulator {\n const nodes = new Map<string, GraphNode>();\n const edges = new Map<string, GraphEdge>();\n for (const fragment of fragments) {\n for (const node of fragment.nodes) if (!nodes.has(node.id)) nodes.set(node.id, node);\n for (const edge of fragment.edges) {\n const key = edgeKey(edge);\n if (!edges.has(key)) edges.set(key, edge);\n }\n }\n return { nodes, edges };\n}\n","import type { EdgeKind, NodeRole } from \"./types.js\";\n\n/**\n * Schema-healing aliases: deprecated or synonymous spellings mapped to the one\n * canonical name codewatch emits today. Two jobs:\n *\n * 1. Rename tolerance on snapshot diff — a metric/edge-kind renamed between an\n * old snapshot and a new one is reconciled to its canonical name so it\n * lines up instead of surfacing as a spurious add/remove.\n * 2. Config tolerance — an external `.codewatch/check.json` that spells a\n * metric or role with a separator/plural variant loads with a deprecation\n * warning and the canonical name, not a hard error.\n *\n * Seeds are conservative: only separator (`-`/`_`), singular/plural, and\n * unambiguous synonym variants of names codewatch actually produces. Add a real\n * historical rename here when one lands; nothing maps a name to a *different*\n * concept.\n */\n\nconst METRIC_ALIASES: ReadonlyMap<string, string> = new Map([\n [\"lines\", \"loc\"],\n [\"lines_of_code\", \"loc\"],\n [\"fan-in\", \"fan_in\"],\n [\"fanin\", \"fan_in\"],\n [\"fan-out\", \"fan_out\"],\n [\"fanout\", \"fan_out\"],\n [\"nesting_depth\", \"max_nesting_depth\"],\n [\"max-nesting-depth\", \"max_nesting_depth\"],\n [\"lcom4\", \"lcom4_max\"],\n]);\n\nconst ROLE_ALIASES: ReadonlyMap<string, string> = new Map([\n [\"tests\", \"test\"],\n [\"spec\", \"test\"],\n [\"specs\", \"test\"],\n [\"fixtures\", \"fixture\"],\n [\"barrels\", \"barrel\"],\n]);\n\nconst EDGE_KIND_ALIASES: ReadonlyMap<string, string> = new Map([\n [\"import\", \"imports\"],\n [\"call\", \"calls\"],\n [\"reference\", \"references\"],\n [\"re_exports\", \"re-exports\"],\n [\"reexports\", \"re-exports\"],\n [\"re-export\", \"re-exports\"],\n [\"depends_on\", \"depends-on\"],\n [\"dependson\", \"depends-on\"],\n]);\n\n/** Canonical name for a deprecated alias, or `undefined` if `name` is not aliased. */\nexport function deprecatedTarget(\n map: ReadonlyMap<string, string>,\n name: string,\n): string | undefined {\n return map.get(name);\n}\n\nexport function canonicalMetricName(name: string): string {\n return METRIC_ALIASES.get(name) ?? name;\n}\n\nexport function canonicalRole(name: string): NodeRole {\n return (ROLE_ALIASES.get(name) ?? name) as NodeRole;\n}\n\nexport function canonicalEdgeKind(name: string): EdgeKind {\n return (EDGE_KIND_ALIASES.get(name) ?? name) as EdgeKind;\n}\n\n/** Canonical metric name for a deprecated alias, or `undefined`. */\nexport function metricAliasTarget(name: string): string | undefined {\n return deprecatedTarget(METRIC_ALIASES, name);\n}\n\n/** Canonical role for a deprecated alias, or `undefined`. */\nexport function roleAliasTarget(name: string): NodeRole | undefined {\n return deprecatedTarget(ROLE_ALIASES, name) as NodeRole | undefined;\n}\n","import type { CodeGraphStore } from \"./store.js\";\nimport type { GraphEdge, GraphMetric, GraphNode } from \"./types.js\";\n\n/**\n * The free-function reading surface named in the package API. Each delegates to\n * the store's method of the same name, so a caller that only reads a snapshot\n * never has to hold on to the store's shape.\n */\nexport function listNodes(\n store: CodeGraphStore,\n snapshotId: number,\n opts?: { includeSymbols?: boolean },\n): GraphNode[] {\n return store.listNodes(snapshotId, opts);\n}\n\nexport function listEdges(\n store: CodeGraphStore,\n snapshotId: number,\n opts?: { includeReferences?: boolean },\n): GraphEdge[] {\n return store.listEdges(snapshotId, opts);\n}\n\nexport function listMetrics(store: CodeGraphStore, snapshotId: number): GraphMetric[] {\n return store.listMetrics(snapshotId);\n}\n"],"mappings":"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","names":["path","require","languages","path","path","path","fileId","Node","fileId","Node","SyntaxKind","SyntaxKind","Node","targetFileId","path","fs","relative","fs","symbolNodes","path","existsSync","existsSync","path","path","path","languages","relative","resolve","fs","fileId","symbolNamesByFile","fileId","fileId"]}
package/package.json ADDED
@@ -0,0 +1,45 @@
1
+ {
2
+ "name": "@titan-design/code-graph",
3
+ "version": "0.1.0",
4
+ "description": "TypeScript/Python code graph: ts-morph + tree-sitter extraction with incremental reuse, on the store kit",
5
+ "type": "module",
6
+ "main": "./dist/index.js",
7
+ "types": "./dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/index.d.ts",
11
+ "import": "./dist/index.js"
12
+ }
13
+ },
14
+ "files": [
15
+ "dist",
16
+ "README.md"
17
+ ],
18
+ "author": "Henry Jewkes",
19
+ "license": "MIT",
20
+ "homepage": "https://github.com/HJewkes/titan-platform#readme",
21
+ "repository": {
22
+ "type": "git",
23
+ "url": "git+https://github.com/HJewkes/titan-platform.git",
24
+ "directory": "packages/code-graph"
25
+ },
26
+ "bugs": "https://github.com/HJewkes/titan-platform/issues",
27
+ "engines": {
28
+ "node": ">=20"
29
+ },
30
+ "publishConfig": {
31
+ "access": "public"
32
+ },
33
+ "dependencies": {
34
+ "ts-morph": "^25.0.0",
35
+ "tree-sitter-python": "^0.25.0",
36
+ "tree-sitter-typescript": "^0.23.2",
37
+ "web-tree-sitter": "^0.26.6",
38
+ "@titan-design/store-sqlite": "^0.1.0"
39
+ },
40
+ "scripts": {
41
+ "build": "tsup",
42
+ "typecheck": "tsc --noEmit",
43
+ "lint": "eslint src"
44
+ }
45
+ }