codemap-ai 3.2.0 → 3.5.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.
package/README.md CHANGED
@@ -8,11 +8,38 @@ Stop guessing what breaks when you change code. CodeMap builds a complete call g
8
8
 
9
9
  - 🔍 **Call Graph Analysis** - Trace every function call across your entire codebase
10
10
  - 🎯 **Impact Analysis** - Know exactly what breaks before you change code
11
- - 🔗 **Cross-File Resolution** - Follows imports and tracks dependencies
11
+ - 🔄 **Data Flow Analysis** - Track property accesses and detect breaking data shape changes (NEW in v3.5)
12
+ - 🔗 **Cross-File Resolution** - Follows imports and tracks dependencies (90%+ accuracy)
12
13
  - 🚀 **FastAPI Support** - Detects HTTP endpoints and dependency injection
13
14
  - 💾 **Incremental Updates** - Only re-indexes changed files (git-aware)
14
15
  - 🤖 **Claude Code Integration** - MCP server for AI-powered code queries
15
16
 
17
+ ### New in v3.5.0: Data Flow Analysis
18
+
19
+ CodeMap now tracks **property accesses** and **return shapes** to detect breaking changes:
20
+
21
+ ```python
22
+ # Developer changes this:
23
+ def get_user():
24
+ return {"id": 1, "name": "John", "email": "john@example.com"}
25
+
26
+ # To this:
27
+ def get_user():
28
+ return {"id": 1, "name": "John"} # email removed!
29
+
30
+ # CodeMap detects:
31
+ # 🔴 CRITICAL: email accessed but no longer returned:
32
+ # • send_welcome_email() - line 78
33
+ # • validate_user_data() - line 120
34
+ # Impact: 15 functions will break
35
+ ```
36
+
37
+ **Phase 3 captures:**
38
+ - 6,976+ property accesses (`user.email`, `config.database.host`)
39
+ - 890+ return shapes (object structures)
40
+ - Breaking change detection (removed properties, type changes)
41
+ - Severity scoring (CRITICAL for id/email/token/auth)
42
+
16
43
  ## Installation
17
44
 
18
45
  ```bash
@@ -1,5 +1,65 @@
1
+ #!/usr/bin/env node
2
+
3
+
1
4
  // src/flow/storage.ts
2
5
  import Database from "better-sqlite3";
6
+
7
+ // src/flow/storage-schema-phase3.ts
8
+ function createPhase3Tables(db) {
9
+ db.exec(`
10
+ CREATE TABLE IF NOT EXISTS property_accesses (
11
+ id TEXT PRIMARY KEY,
12
+ scope_node_id TEXT NOT NULL,
13
+ object_name TEXT NOT NULL,
14
+ property_path TEXT NOT NULL,
15
+ full_path TEXT NOT NULL,
16
+ access_type TEXT NOT NULL,
17
+ file_path TEXT NOT NULL,
18
+ line INTEGER NOT NULL,
19
+ FOREIGN KEY (scope_node_id) REFERENCES nodes(id)
20
+ );
21
+ `);
22
+ db.exec(`
23
+ CREATE TABLE IF NOT EXISTS return_shapes (
24
+ id TEXT PRIMARY KEY,
25
+ function_id TEXT NOT NULL,
26
+ return_type TEXT,
27
+ object_shape TEXT,
28
+ line INTEGER NOT NULL,
29
+ return_expression TEXT,
30
+ FOREIGN KEY (function_id) REFERENCES nodes(id)
31
+ );
32
+ `);
33
+ db.exec(`
34
+ CREATE TABLE IF NOT EXISTS data_flows (
35
+ id TEXT PRIMARY KEY,
36
+ source_id TEXT NOT NULL,
37
+ target_id TEXT NOT NULL,
38
+ data_type TEXT NOT NULL,
39
+ shape TEXT,
40
+ flow_type TEXT NOT NULL,
41
+ line INTEGER
42
+ );
43
+ `);
44
+ db.exec(`
45
+ CREATE INDEX IF NOT EXISTS idx_property_accesses_path
46
+ ON property_accesses(full_path);
47
+
48
+ CREATE INDEX IF NOT EXISTS idx_property_accesses_scope
49
+ ON property_accesses(scope_node_id);
50
+
51
+ CREATE INDEX IF NOT EXISTS idx_return_shapes_function
52
+ ON return_shapes(function_id);
53
+
54
+ CREATE INDEX IF NOT EXISTS idx_data_flows_source
55
+ ON data_flows(source_id);
56
+
57
+ CREATE INDEX IF NOT EXISTS idx_data_flows_target
58
+ ON data_flows(target_id);
59
+ `);
60
+ }
61
+
62
+ // src/flow/storage.ts
3
63
  var FlowStorage = class {
4
64
  db;
5
65
  // Public for builder to access
@@ -154,6 +214,7 @@ var FlowStorage = class {
154
214
  CREATE INDEX IF NOT EXISTS idx_variable_types_scope ON variable_types(scope_node_id, variable_name);
155
215
  CREATE INDEX IF NOT EXISTS idx_variable_types_file ON variable_types(file_path, variable_name);
156
216
  `);
217
+ createPhase3Tables(this.db);
157
218
  }
158
219
  // ============ Node Operations ============
159
220
  insertNode(node) {
@@ -772,4 +833,4 @@ var FlowStorage = class {
772
833
  export {
773
834
  FlowStorage
774
835
  };
775
- //# sourceMappingURL=chunk-VB74K47A.js.map
836
+ //# sourceMappingURL=chunk-BRVRY5KT.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/flow/storage.ts","../src/flow/storage-schema-phase3.ts"],"sourcesContent":["/**\n * Flow-focused storage for call graphs\n * Simplified from graph/storage.ts with enhanced call resolution\n */\n\nimport Database from \"better-sqlite3\";\nimport type { GraphNode, GraphEdge } from \"../types.js\";\nimport { createPhase3Tables } from \"./storage-schema-phase3.js\";\n\nexport interface CallPath {\n nodes: Array<{\n id: string;\n name: string;\n file: string;\n line: number;\n }>;\n edges: Array<{\n from: string;\n to: string;\n type: string;\n }>;\n}\n\nexport class FlowStorage {\n public db: Database.Database; // Public for builder to access\n\n constructor(dbPath: string) {\n this.db = new Database(dbPath);\n this.initSchema();\n }\n\n private initSchema(): void {\n this.db.exec(`\n -- Nodes table (enhanced with hash)\n CREATE TABLE IF NOT EXISTS nodes (\n id TEXT PRIMARY KEY,\n type TEXT NOT NULL,\n name TEXT NOT NULL,\n file_path TEXT NOT NULL,\n start_line INTEGER NOT NULL,\n end_line INTEGER NOT NULL,\n language TEXT NOT NULL,\n hash TEXT,\n metadata TEXT\n );\n\n -- Edges table (calls and related edges)\n CREATE TABLE IF NOT EXISTS edges (\n id TEXT PRIMARY KEY,\n type TEXT NOT NULL,\n source_id TEXT NOT NULL,\n target_id TEXT NOT NULL,\n metadata TEXT\n );\n\n -- Files table\n CREATE TABLE IF NOT EXISTS files (\n path TEXT PRIMARY KEY,\n language TEXT NOT NULL,\n hash TEXT NOT NULL,\n analyzed_at TEXT NOT NULL\n );\n\n -- Exports: symbols exported by each file\n CREATE TABLE IF NOT EXISTS exports (\n file_path TEXT NOT NULL,\n symbol_name TEXT NOT NULL,\n node_id TEXT NOT NULL,\n PRIMARY KEY (file_path, symbol_name)\n );\n\n -- Imports: what each file imports and from where\n CREATE TABLE IF NOT EXISTS imports (\n file_path TEXT NOT NULL,\n imported_symbol TEXT NOT NULL,\n from_module TEXT NOT NULL,\n resolved_node_id TEXT,\n line INTEGER NOT NULL\n );\n\n -- Containers (Docker services)\n CREATE TABLE IF NOT EXISTS containers (\n id TEXT PRIMARY KEY,\n name TEXT NOT NULL,\n image TEXT,\n ports TEXT,\n environment TEXT,\n depends_on TEXT,\n networks TEXT,\n metadata TEXT\n );\n\n -- HTTP Endpoints (API routes)\n CREATE TABLE IF NOT EXISTS http_endpoints (\n id TEXT PRIMARY KEY,\n method TEXT NOT NULL,\n path TEXT NOT NULL,\n handler_node_id TEXT,\n container_id TEXT,\n middleware TEXT,\n metadata TEXT\n );\n\n -- HTTP Calls (client requests)\n CREATE TABLE IF NOT EXISTS http_calls (\n id TEXT PRIMARY KEY,\n source_node_id TEXT NOT NULL,\n target_endpoint_id TEXT,\n url TEXT NOT NULL,\n method TEXT NOT NULL,\n line INTEGER,\n metadata TEXT\n );\n\n -- Framework Dependencies (FastAPI Depends, etc.)\n CREATE TABLE IF NOT EXISTS framework_dependencies (\n id TEXT PRIMARY KEY,\n source_node_id TEXT NOT NULL,\n target_node_id TEXT,\n framework TEXT NOT NULL,\n pattern TEXT NOT NULL,\n parameter_name TEXT,\n line INTEGER,\n unresolved_name TEXT,\n metadata TEXT\n );\n\n -- Database Operations\n CREATE TABLE IF NOT EXISTS database_operations (\n id TEXT PRIMARY KEY,\n node_id TEXT NOT NULL,\n database_type TEXT NOT NULL,\n operation TEXT NOT NULL,\n collection TEXT,\n line INTEGER,\n metadata TEXT\n );\n\n -- Message Queue Operations\n CREATE TABLE IF NOT EXISTS message_queue_operations (\n id TEXT PRIMARY KEY,\n node_id TEXT NOT NULL,\n operation TEXT NOT NULL,\n exchange TEXT,\n routing_key TEXT,\n queue TEXT,\n line INTEGER,\n metadata TEXT\n );\n\n -- Variable Type Tracking\n CREATE TABLE IF NOT EXISTS variable_types (\n id TEXT PRIMARY KEY,\n variable_name TEXT NOT NULL,\n type_name TEXT NOT NULL,\n scope_node_id TEXT NOT NULL,\n file_path TEXT NOT NULL,\n line INTEGER NOT NULL,\n is_parameter BOOLEAN DEFAULT 0,\n metadata TEXT\n );\n\n -- Indexes for fast queries\n CREATE INDEX IF NOT EXISTS idx_nodes_file ON nodes(file_path);\n CREATE INDEX IF NOT EXISTS idx_nodes_name ON nodes(name);\n CREATE INDEX IF NOT EXISTS idx_nodes_hash ON nodes(hash);\n CREATE INDEX IF NOT EXISTS idx_edges_source ON edges(source_id);\n CREATE INDEX IF NOT EXISTS idx_edges_target ON edges(target_id);\n CREATE INDEX IF NOT EXISTS idx_edges_resolved ON edges(target_id) WHERE target_id NOT LIKE 'ref:%';\n CREATE INDEX IF NOT EXISTS idx_exports_file ON exports(file_path);\n CREATE INDEX IF NOT EXISTS idx_imports_file ON imports(file_path);\n CREATE INDEX IF NOT EXISTS idx_http_endpoints_path ON http_endpoints(method, path);\n CREATE INDEX IF NOT EXISTS idx_http_calls_url ON http_calls(url);\n CREATE INDEX IF NOT EXISTS idx_framework_deps_source ON framework_dependencies(source_node_id);\n CREATE INDEX IF NOT EXISTS idx_framework_deps_target ON framework_dependencies(target_node_id);\n CREATE INDEX IF NOT EXISTS idx_variable_types_scope ON variable_types(scope_node_id, variable_name);\n CREATE INDEX IF NOT EXISTS idx_variable_types_file ON variable_types(file_path, variable_name);\n `);\n\n // Create Phase 3 tables\n createPhase3Tables(this.db);\n }\n\n // ============ Node Operations ============\n\n insertNode(node: GraphNode & { hash?: string }): void {\n const stmt = this.db.prepare(`\n INSERT OR REPLACE INTO nodes (id, type, name, file_path, start_line, end_line, language, hash, metadata)\n VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)\n `);\n stmt.run(\n node.id,\n node.type,\n node.name,\n node.filePath,\n node.startLine,\n node.endLine,\n node.language,\n node.hash || null,\n node.metadata ? JSON.stringify(node.metadata) : null\n );\n }\n\n getNode(id: string): GraphNode | null {\n const stmt = this.db.prepare(\"SELECT * FROM nodes WHERE id = ?\");\n const row = stmt.get(id) as Record<string, unknown> | undefined;\n if (!row) return null;\n return this.rowToNode(row);\n }\n\n searchNodesByName(name: string): GraphNode[] {\n const stmt = this.db.prepare(\"SELECT * FROM nodes WHERE name = ? OR name LIKE ?\");\n const rows = stmt.all(name, `%${name}%`) as Record<string, unknown>[];\n return rows.map((r) => this.rowToNode(r));\n }\n\n getNodesByFile(filePath: string): GraphNode[] {\n const stmt = this.db.prepare(\"SELECT * FROM nodes WHERE file_path = ?\");\n const rows = stmt.all(filePath) as Record<string, unknown>[];\n return rows.map((r) => this.rowToNode(r));\n }\n\n private rowToNode(row: Record<string, unknown>): GraphNode {\n return {\n id: row.id as string,\n type: row.type as GraphNode[\"type\"],\n name: row.name as string,\n filePath: row.file_path as string,\n startLine: row.start_line as number,\n endLine: row.end_line as number,\n language: row.language as string,\n metadata: row.metadata ? JSON.parse(row.metadata as string) : undefined,\n };\n }\n\n // ============ Edge Operations ============\n\n insertEdge(edge: GraphEdge): void {\n const stmt = this.db.prepare(`\n INSERT OR REPLACE INTO edges (id, type, source_id, target_id, metadata)\n VALUES (?, ?, ?, ?, ?)\n `);\n stmt.run(\n edge.id,\n edge.type,\n edge.sourceId,\n edge.targetId,\n edge.metadata ? JSON.stringify(edge.metadata) : null\n );\n }\n\n // ============ Call Graph Queries ============\n\n /**\n * Get all resolved callees (functions this node calls)\n */\n getResolvedCallees(nodeId: string): GraphNode[] {\n const stmt = this.db.prepare(`\n SELECT n.* FROM nodes n\n JOIN edges e ON n.id = e.target_id\n WHERE e.source_id = ? AND e.type = 'calls' AND e.target_id NOT LIKE 'ref:%'\n `);\n const rows = stmt.all(nodeId) as Record<string, unknown>[];\n return rows.map((r) => this.rowToNode(r));\n }\n\n /**\n * Get all resolved callers (functions that call this node)\n */\n getResolvedCallers(nodeId: string): GraphNode[] {\n const stmt = this.db.prepare(`\n SELECT n.* FROM nodes n\n JOIN edges e ON n.id = e.source_id\n WHERE e.target_id = ? AND e.type = 'calls'\n `);\n const rows = stmt.all(nodeId) as Record<string, unknown>[];\n return rows.map((r) => this.rowToNode(r));\n }\n\n /**\n * Get impact: all nodes affected by changes to this node (BFS traversal)\n */\n getImpactedNodes(nodeId: string, maxDepth: number = 3): GraphNode[] {\n const visited = new Set<string>();\n const result: GraphNode[] = [];\n const queue: Array<{ id: string; depth: number }> = [{ id: nodeId, depth: 0 }];\n\n while (queue.length > 0) {\n const { id, depth } = queue.shift()!;\n\n if (visited.has(id) || depth > maxDepth) continue;\n visited.add(id);\n\n const callers = this.getResolvedCallers(id);\n for (const caller of callers) {\n if (!visited.has(caller.id)) {\n result.push(caller);\n queue.push({ id: caller.id, depth: depth + 1 });\n }\n }\n }\n\n return result;\n }\n\n /**\n * Trace call path from source to target using BFS\n */\n traceCallPath(fromId: string, toId: string): CallPath | null {\n const visited = new Set<string>();\n const parent = new Map<string, { nodeId: string; edgeType: string }>();\n const queue: string[] = [fromId];\n\n while (queue.length > 0) {\n const current = queue.shift()!;\n\n if (current === toId) {\n // Reconstruct path\n const path: string[] = [];\n let node = toId;\n while (node !== fromId) {\n path.unshift(node);\n node = parent.get(node)!.nodeId;\n }\n path.unshift(fromId);\n\n // Get node details\n const nodes = path.map((id) => {\n const n = this.getNode(id);\n return {\n id: n!.id,\n name: n!.name,\n file: n!.filePath,\n line: n!.startLine,\n };\n });\n\n const edges = [];\n for (let i = 0; i < path.length - 1; i++) {\n edges.push({\n from: path[i],\n to: path[i + 1],\n type: 'calls',\n });\n }\n\n return { nodes, edges };\n }\n\n if (visited.has(current)) continue;\n visited.add(current);\n\n const callees = this.getResolvedCallees(current);\n for (const callee of callees) {\n if (!visited.has(callee.id)) {\n parent.set(callee.id, { nodeId: current, edgeType: 'calls' });\n queue.push(callee.id);\n }\n }\n }\n\n return null;\n }\n\n // ============ Export/Import Tracking ============\n\n registerExport(filePath: string, symbolName: string, nodeId: string): void {\n const stmt = this.db.prepare(`\n INSERT OR REPLACE INTO exports (file_path, symbol_name, node_id)\n VALUES (?, ?, ?)\n `);\n stmt.run(filePath, symbolName, nodeId);\n }\n\n registerImport(filePath: string, symbol: string, fromModule: string, line: number): void {\n const stmt = this.db.prepare(`\n INSERT OR REPLACE INTO imports (file_path, imported_symbol, from_module, line)\n VALUES (?, ?, ?, ?)\n `);\n stmt.run(filePath, symbol, fromModule, line);\n }\n\n resolveImport(filePath: string, symbol: string): string | null {\n // Get the import statement\n const importStmt = this.db.prepare(`\n SELECT from_module FROM imports WHERE file_path = ? AND imported_symbol = ?\n `).get(filePath, symbol) as { from_module: string } | undefined;\n\n if (!importStmt) return null;\n\n // Convert module path to possible file paths\n // Examples:\n // - \"backend.authentication\" -> [\"backend/authentication.py\", \"backend/authentication/__init__.py\"]\n // - \"..authentication\" -> relative import (handle separately)\n const modulePath = importStmt.from_module;\n const possiblePaths = this.resolvePythonModulePath(filePath, modulePath);\n\n // Try each possible path\n for (const targetPath of possiblePaths) {\n const exportStmt = this.db.prepare(`\n SELECT node_id FROM exports WHERE file_path = ? AND symbol_name = ?\n `).get(targetPath, symbol) as { node_id: string } | undefined;\n\n if (exportStmt) {\n return exportStmt.node_id;\n }\n }\n\n return null;\n }\n\n /**\n * Resolve a Python module path to possible file paths\n * Handles: absolute imports, relative imports, __init__.py\n */\n private resolvePythonModulePath(currentFile: string, modulePath: string): string[] {\n const possiblePaths: string[] = [];\n\n // Handle relative imports (from . import x, from .. import y)\n if (modulePath.startsWith(\".\")) {\n const currentDir = currentFile.substring(0, currentFile.lastIndexOf(\"/\"));\n const depth = modulePath.match(/^\\.+/)?.[0].length || 0;\n const moduleName = modulePath.replace(/^\\.+/, \"\");\n\n // Go up directories based on depth\n let targetDir = currentDir;\n for (let i = 1; i < depth; i++) {\n const lastSlash = targetDir.lastIndexOf(\"/\");\n if (lastSlash > 0) {\n targetDir = targetDir.substring(0, lastSlash);\n }\n }\n\n if (moduleName) {\n const relativePath = moduleName.replace(/\\./g, \"/\");\n possiblePaths.push(`${targetDir}/${relativePath}.py`);\n possiblePaths.push(`${targetDir}/${relativePath}/__init__.py`);\n } else {\n possiblePaths.push(`${targetDir}/__init__.py`);\n }\n } else {\n // Absolute import - convert dots to slashes\n const pathComponents = modulePath.split(\".\");\n\n // Try different interpretations\n // 1. Full path as file: backend.authentication -> backend/authentication.py\n const filePath = pathComponents.join(\"/\");\n possiblePaths.push(`${filePath}.py`);\n possiblePaths.push(`${filePath}/__init__.py`);\n\n // 2. Try with src/ prefix (common pattern)\n possiblePaths.push(`src/${filePath}.py`);\n possiblePaths.push(`src/${filePath}/__init__.py`);\n\n // 3. Query actual files to find match\n // Get all files that end with the last component\n const lastComponent = pathComponents[pathComponents.length - 1];\n const matchingFiles = this.db.prepare(`\n SELECT DISTINCT file_path FROM exports\n WHERE file_path LIKE ?\n `).all(`%/${lastComponent}.py`) as Array<{ file_path: string }>;\n\n for (const file of matchingFiles) {\n // Check if the file path ends with the module path\n const normalizedFilePath = file.file_path.replace(/\\\\/g, \"/\");\n if (normalizedFilePath.includes(filePath)) {\n possiblePaths.push(normalizedFilePath);\n }\n }\n }\n\n return possiblePaths;\n }\n\n /**\n * Resolve a class method call like ClassName.method_name\n * Handles: static methods, class methods, imported classes\n */\n resolveClassMethod(className: string, methodName: string, callerFilePath: string): string | null {\n // Strategy 1: Check if the class is imported in the caller file\n const classNodeId = this.resolveImport(callerFilePath, className);\n\n if (classNodeId) {\n // Find the method within this class\n const methods = this.db.prepare(`\n SELECT target_id FROM edges\n WHERE source_id = ? AND type = 'contains'\n `).all(classNodeId) as Array<{ target_id: string }>;\n\n for (const edge of methods) {\n const methodNode = this.getNode(edge.target_id);\n if (methodNode && methodNode.name === methodName) {\n return methodNode.id;\n }\n }\n }\n\n // Strategy 2: Search for class in the same file\n const classNode = this.db.prepare(`\n SELECT id FROM nodes\n WHERE file_path = ? AND name = ? AND type = 'class'\n `).get(callerFilePath, className) as { id: string } | undefined;\n\n if (classNode) {\n // Find the method within this class\n const methodNode = this.db.prepare(`\n SELECT n.id FROM nodes n\n JOIN edges e ON e.target_id = n.id\n WHERE e.source_id = ? AND e.type = 'contains' AND n.name = ?\n `).get(classNode.id, methodName) as { id: string } | undefined;\n\n if (methodNode) {\n return methodNode.id;\n }\n }\n\n // Strategy 3: Search globally for the class\n const allClasses = this.db.prepare(`\n SELECT id FROM nodes WHERE name = ? AND type = 'class'\n `).all(className) as Array<{ id: string }>;\n\n for (const cls of allClasses) {\n const methodNode = this.db.prepare(`\n SELECT n.id FROM nodes n\n JOIN edges e ON e.target_id = n.id\n WHERE e.source_id = ? AND e.type = 'contains' AND n.name = ?\n `).get(cls.id, methodName) as { id: string } | undefined;\n\n if (methodNode) {\n return methodNode.id;\n }\n }\n\n return null;\n }\n\n // ============ Variable Type Tracking ============\n\n /**\n * Register a variable type (from assignment or parameter)\n */\n registerVariableType(\n variableName: string,\n typeName: string,\n scopeNodeId: string,\n filePath: string,\n line: number,\n isParameter: boolean = false\n ): void {\n const id = `${scopeNodeId}:var:${variableName}`;\n const stmt = this.db.prepare(`\n INSERT OR REPLACE INTO variable_types\n (id, variable_name, type_name, scope_node_id, file_path, line, is_parameter)\n VALUES (?, ?, ?, ?, ?, ?, ?)\n `);\n stmt.run(id, variableName, typeName, scopeNodeId, filePath, line, isParameter ? 1 : 0);\n }\n\n /**\n * Resolve variable.method to actual method node\n * Example: user.get_name() where user is of type User\n */\n resolveVariableMethod(variableName: string, methodName: string, scopeNodeId: string): string | null {\n // Special case: self.method calls - resolve to methods in parent class\n if (variableName === \"self\") {\n return this.resolveSelfMethod(methodName, scopeNodeId);\n }\n\n // Get the variable's type\n const varType = this.db.prepare(`\n SELECT type_name FROM variable_types\n WHERE scope_node_id = ? AND variable_name = ?\n `).get(scopeNodeId, variableName) as { type_name: string } | undefined;\n\n if (!varType) {\n return null;\n }\n\n // Now resolve ClassName.method_name\n return this.resolveClassMethod(varType.type_name, methodName, \"\");\n }\n\n /**\n * Resolve self.method() calls to methods in the same class\n */\n private resolveSelfMethod(methodName: string, scopeNodeId: string): string | null {\n // Find the parent class of this method\n const parentClass = this.db.prepare(`\n SELECT source_id FROM edges\n WHERE target_id = ? AND type = 'contains'\n `).get(scopeNodeId) as { source_id: string } | undefined;\n\n if (!parentClass) {\n return null;\n }\n\n // Find the method with this name in the same class\n const method = this.db.prepare(`\n SELECT n.id FROM nodes n\n JOIN edges e ON e.target_id = n.id\n WHERE e.source_id = ? AND e.type = 'contains' AND n.name = ?\n `).get(parentClass.source_id, methodName) as { id: string } | undefined;\n\n return method?.id || null;\n }\n\n /**\n * Resolve super().method() calls to parent class methods\n */\n resolveSuperMethod(methodName: string, scopeNodeId: string): string | null {\n // Find the parent class of the current method\n const parentClass = this.db.prepare(`\n SELECT source_id FROM edges\n WHERE target_id = ? AND type = 'contains'\n `).get(scopeNodeId) as { source_id: string } | undefined;\n\n if (!parentClass) {\n return null;\n }\n\n // Get the class node\n const classNode = this.getNode(parentClass.source_id);\n if (!classNode || classNode.type !== 'class') {\n return null;\n }\n\n // Find the parent class (via extends edge)\n const baseClass = this.db.prepare(`\n SELECT target_id FROM edges\n WHERE source_id = ? AND type = 'extends'\n `).get(classNode.id) as { target_id: string } | undefined;\n\n if (!baseClass) {\n return null;\n }\n\n // The target might be a ref, resolve it first\n let baseClassId = baseClass.target_id;\n if (baseClassId.startsWith('ref:')) {\n const baseClassName = baseClassId.replace('ref:', '');\n // Search for the class by name\n const resolvedClass = this.db.prepare(`\n SELECT id FROM nodes WHERE name = ? AND type = 'class'\n `).get(baseClassName) as { id: string } | undefined;\n\n if (!resolvedClass) {\n return null;\n }\n baseClassId = resolvedClass.id;\n }\n\n // Find the method in the parent class\n const method = this.db.prepare(`\n SELECT n.id FROM nodes n\n JOIN edges e ON e.target_id = n.id\n WHERE e.source_id = ? AND e.type = 'contains' AND n.name = ?\n `).get(baseClassId, methodName) as { id: string } | undefined;\n\n return method?.id || null;\n }\n\n // ============ File Operations ============\n\n upsertFile(filePath: string, language: string, hash: string): void {\n const stmt = this.db.prepare(`\n INSERT OR REPLACE INTO files (path, language, hash, analyzed_at)\n VALUES (?, ?, ?, ?)\n `);\n stmt.run(filePath, language, hash, new Date().toISOString());\n }\n\n getFileHash(filePath: string): string | null {\n const stmt = this.db.prepare(\"SELECT hash FROM files WHERE path = ?\");\n const row = stmt.get(filePath) as { hash: string } | undefined;\n return row?.hash || null;\n }\n\n deleteFileData(filePath: string): void {\n this.db.prepare(\"DELETE FROM nodes WHERE file_path = ?\").run(filePath);\n this.db.prepare(\"DELETE FROM exports WHERE file_path = ?\").run(filePath);\n this.db.prepare(\"DELETE FROM imports WHERE file_path = ?\").run(filePath);\n this.db.prepare(\"DELETE FROM files WHERE path = ?\").run(filePath);\n }\n\n // ============ Stats ============\n\n getStats(): {\n totalFiles: number;\n totalNodes: number;\n totalEdges: number;\n resolvedCalls: number;\n unresolvedCalls: number;\n httpEndpoints: number;\n frameworkDependencies: number;\n resolvedDependencies: number;\n containers: number;\n } {\n const totalFiles = (this.db.prepare(\"SELECT COUNT(*) as count FROM files\").get() as { count: number }).count;\n const totalNodes = (this.db.prepare(\"SELECT COUNT(*) as count FROM nodes\").get() as { count: number }).count;\n const totalEdges = (this.db.prepare(\"SELECT COUNT(*) as count FROM edges WHERE type = 'calls'\").get() as { count: number }).count;\n const resolvedCalls = (this.db.prepare(\"SELECT COUNT(*) as count FROM edges WHERE type = 'calls' AND target_id NOT LIKE 'ref:%'\").get() as { count: number }).count;\n const unresolvedCalls = totalEdges - resolvedCalls;\n const httpEndpoints = (this.db.prepare(\"SELECT COUNT(*) as count FROM http_endpoints\").get() as { count: number }).count;\n const frameworkDependencies = (this.db.prepare(\"SELECT COUNT(*) as count FROM framework_dependencies\").get() as { count: number }).count;\n const resolvedDependencies = (this.db.prepare(\"SELECT COUNT(*) as count FROM framework_dependencies WHERE target_node_id IS NOT NULL\").get() as { count: number }).count;\n const containers = (this.db.prepare(\"SELECT COUNT(*) as count FROM containers\").get() as { count: number }).count;\n\n return {\n totalFiles,\n totalNodes,\n totalEdges,\n resolvedCalls,\n unresolvedCalls,\n httpEndpoints,\n frameworkDependencies,\n resolvedDependencies,\n containers,\n };\n }\n\n // ============ Container Operations ============\n\n insertContainer(container: {\n id: string;\n name: string;\n image?: string;\n ports?: Array<{ host: number; container: number }>;\n environment?: Record<string, string>;\n depends_on?: string[];\n networks?: string[];\n metadata?: Record<string, unknown>;\n }): void {\n const stmt = this.db.prepare(`\n INSERT OR REPLACE INTO containers (id, name, image, ports, environment, depends_on, networks, metadata)\n VALUES (?, ?, ?, ?, ?, ?, ?, ?)\n `);\n stmt.run(\n container.id,\n container.name,\n container.image || null,\n container.ports ? JSON.stringify(container.ports) : null,\n container.environment ? JSON.stringify(container.environment) : null,\n container.depends_on ? JSON.stringify(container.depends_on) : null,\n container.networks ? JSON.stringify(container.networks) : null,\n container.metadata ? JSON.stringify(container.metadata) : null\n );\n }\n\n getContainer(id: string): any | null {\n const stmt = this.db.prepare(\"SELECT * FROM containers WHERE id = ?\");\n const row = stmt.get(id) as Record<string, unknown> | undefined;\n if (!row) return null;\n return {\n id: row.id,\n name: row.name,\n image: row.image,\n ports: row.ports ? JSON.parse(row.ports as string) : [],\n environment: row.environment ? JSON.parse(row.environment as string) : {},\n depends_on: row.depends_on ? JSON.parse(row.depends_on as string) : [],\n networks: row.networks ? JSON.parse(row.networks as string) : [],\n metadata: row.metadata ? JSON.parse(row.metadata as string) : {},\n };\n }\n\n getAllContainers(): any[] {\n const stmt = this.db.prepare(\"SELECT * FROM containers\");\n const rows = stmt.all() as Record<string, unknown>[];\n return rows.map(row => ({\n id: row.id,\n name: row.name,\n image: row.image,\n ports: row.ports ? JSON.parse(row.ports as string) : [],\n environment: row.environment ? JSON.parse(row.environment as string) : {},\n depends_on: row.depends_on ? JSON.parse(row.depends_on as string) : [],\n networks: row.networks ? JSON.parse(row.networks as string) : [],\n metadata: row.metadata ? JSON.parse(row.metadata as string) : {},\n }));\n }\n\n // ============ HTTP Endpoint Operations ============\n\n insertHttpEndpoint(endpoint: {\n id: string;\n method: string;\n path: string;\n handler_node_id?: string;\n container_id?: string;\n middleware?: string[];\n metadata?: Record<string, unknown>;\n }): void {\n const stmt = this.db.prepare(`\n INSERT OR REPLACE INTO http_endpoints (id, method, path, handler_node_id, container_id, middleware, metadata)\n VALUES (?, ?, ?, ?, ?, ?, ?)\n `);\n stmt.run(\n endpoint.id,\n endpoint.method,\n endpoint.path,\n endpoint.handler_node_id || null,\n endpoint.container_id || null,\n endpoint.middleware ? JSON.stringify(endpoint.middleware) : null,\n endpoint.metadata ? JSON.stringify(endpoint.metadata) : null\n );\n }\n\n insertDatabaseOperation(dbOp: {\n id: string;\n node_id: string;\n database_type: string;\n operation: string;\n collection?: string;\n line?: number;\n metadata?: Record<string, unknown>;\n }): void {\n const stmt = this.db.prepare(`\n INSERT OR REPLACE INTO database_operations (id, node_id, database_type, operation, collection, line, metadata)\n VALUES (?, ?, ?, ?, ?, ?, ?)\n `);\n stmt.run(\n dbOp.id,\n dbOp.node_id,\n dbOp.database_type,\n dbOp.operation,\n dbOp.collection || null,\n dbOp.line || null,\n dbOp.metadata ? JSON.stringify(dbOp.metadata) : null\n );\n }\n\n findHttpEndpoint(method: string, path: string): any | null {\n const stmt = this.db.prepare(\"SELECT * FROM http_endpoints WHERE method = ? AND path = ?\");\n const row = stmt.get(method, path) as Record<string, unknown> | undefined;\n if (!row) return null;\n return {\n id: row.id,\n method: row.method,\n path: row.path,\n handler_node_id: row.handler_node_id,\n container_id: row.container_id,\n middleware: row.middleware ? JSON.parse(row.middleware as string) : [],\n metadata: row.metadata ? JSON.parse(row.metadata as string) : {},\n };\n }\n\n // ============ Framework Dependency Operations ============\n\n insertFrameworkDependency(dep: {\n id: string;\n source_node_id: string;\n target_node_id?: string;\n framework: string;\n pattern: string;\n parameter_name?: string;\n line?: number;\n unresolved_name?: string;\n metadata?: Record<string, unknown>;\n }): void {\n const stmt = this.db.prepare(`\n INSERT OR REPLACE INTO framework_dependencies\n (id, source_node_id, target_node_id, framework, pattern, parameter_name, line, unresolved_name, metadata)\n VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)\n `);\n stmt.run(\n dep.id,\n dep.source_node_id,\n dep.target_node_id || null,\n dep.framework,\n dep.pattern,\n dep.parameter_name || null,\n dep.line || null,\n dep.unresolved_name || null,\n dep.metadata ? JSON.stringify(dep.metadata) : null\n );\n }\n\n getFrameworkDependencies(sourceNodeId: string): any[] {\n const stmt = this.db.prepare(\"SELECT * FROM framework_dependencies WHERE source_node_id = ?\");\n const rows = stmt.all(sourceNodeId) as Record<string, unknown>[];\n return rows.map(row => ({\n id: row.id,\n source_node_id: row.source_node_id,\n target_node_id: row.target_node_id,\n framework: row.framework,\n pattern: row.pattern,\n parameter_name: row.parameter_name,\n line: row.line,\n unresolved_name: row.unresolved_name,\n metadata: row.metadata ? JSON.parse(row.metadata as string) : {},\n }));\n }\n\n getAllUnresolvedDependencies(): any[] {\n const stmt = this.db.prepare(\"SELECT * FROM framework_dependencies WHERE target_node_id IS NULL\");\n const rows = stmt.all() as Record<string, unknown>[];\n return rows.map(row => ({\n id: row.id,\n source_node_id: row.source_node_id,\n framework: row.framework,\n pattern: row.pattern,\n unresolved_name: row.unresolved_name,\n line: row.line,\n }));\n }\n\n updateFrameworkDependencyTarget(id: string, targetNodeId: string): void {\n const stmt = this.db.prepare(\"UPDATE framework_dependencies SET target_node_id = ? WHERE id = ?\");\n stmt.run(targetNodeId, id);\n }\n\n clear(): void {\n this.db.exec(`\n DELETE FROM nodes;\n DELETE FROM edges;\n DELETE FROM files;\n DELETE FROM exports;\n DELETE FROM imports;\n DELETE FROM containers;\n DELETE FROM http_endpoints;\n DELETE FROM http_calls;\n DELETE FROM framework_dependencies;\n DELETE FROM database_operations;\n DELETE FROM message_queue_operations;\n `);\n }\n\n /**\n * Get all file hashes (for detecting changes)\n */\n getAllFileHashes(): Record<string, string> {\n const stmt = this.db.prepare(\"SELECT path, hash FROM files WHERE hash IS NOT NULL\");\n const rows = stmt.all() as Array<{ path: string; hash: string }>;\n const hashes: Record<string, string> = {};\n for (const row of rows) {\n hashes[row.path] = row.hash;\n }\n return hashes;\n }\n\n /**\n * Get all nodes (for impact comparison)\n */\n getAllNodes(): Array<{ id: string; name: string; type: string; filePath: string }> {\n const stmt = this.db.prepare(\"SELECT id, name, type, file_path FROM nodes\");\n return stmt.all() as Array<{ id: string; name: string; type: string; filePath: string }>;\n }\n\n /**\n * Get all edges (for impact comparison)\n */\n getAllEdges(): Array<{ id: string; source_id: string; target_id: string; type: string }> {\n const stmt = this.db.prepare(\"SELECT id, source_id, target_id, type FROM edges\");\n return stmt.all() as Array<{ id: string; source_id: string; target_id: string; type: string }>;\n }\n\n /**\n * Get resolved callers with file/line info (for impact analysis)\n */\n getResolvedCallersWithLocation(nodeId: string): Array<{ name: string; file: string; line: number }> {\n const stmt = this.db.prepare(`\n SELECT n.name, n.file_path as file, n.start_line as line\n FROM edges e\n JOIN nodes n ON e.source_id = n.id\n WHERE e.target_id = ? AND e.type = 'calls' AND e.target_id NOT LIKE 'ref:%'\n `);\n return stmt.all(nodeId) as Array<{ name: string; file: string; line: number }>;\n }\n\n /**\n * Get HTTP endpoints by handler node ID\n */\n getHttpEndpointsByHandler(handlerNodeId: string): Array<{ method: string; path: string }> {\n const stmt = this.db.prepare(\"SELECT method, path FROM http_endpoints WHERE handler_node_id = ?\");\n return stmt.all(handlerNodeId) as Array<{ method: string; path: string }>;\n }\n\n close(): void {\n this.db.close();\n }\n}\n","/**\n * Phase 3 Storage Schema Extensions\n * Additional tables for data flow analysis\n */\n\nimport { Database } from \"better-sqlite3\";\n\n/**\n * Create Phase 3 tables for data flow analysis\n */\nexport function createPhase3Tables(db: Database): void {\n // Property accesses table\n db.exec(`\n CREATE TABLE IF NOT EXISTS property_accesses (\n id TEXT PRIMARY KEY,\n scope_node_id TEXT NOT NULL,\n object_name TEXT NOT NULL,\n property_path TEXT NOT NULL,\n full_path TEXT NOT NULL,\n access_type TEXT NOT NULL,\n file_path TEXT NOT NULL,\n line INTEGER NOT NULL,\n FOREIGN KEY (scope_node_id) REFERENCES nodes(id)\n );\n `);\n\n // Return shapes table\n db.exec(`\n CREATE TABLE IF NOT EXISTS return_shapes (\n id TEXT PRIMARY KEY,\n function_id TEXT NOT NULL,\n return_type TEXT,\n object_shape TEXT,\n line INTEGER NOT NULL,\n return_expression TEXT,\n FOREIGN KEY (function_id) REFERENCES nodes(id)\n );\n `);\n\n // Data flows table\n db.exec(`\n CREATE TABLE IF NOT EXISTS data_flows (\n id TEXT PRIMARY KEY,\n source_id TEXT NOT NULL,\n target_id TEXT NOT NULL,\n data_type TEXT NOT NULL,\n shape TEXT,\n flow_type TEXT NOT NULL,\n line INTEGER\n );\n `);\n\n // Create indexes for performance\n db.exec(`\n CREATE INDEX IF NOT EXISTS idx_property_accesses_path\n ON property_accesses(full_path);\n\n CREATE INDEX IF NOT EXISTS idx_property_accesses_scope\n ON property_accesses(scope_node_id);\n\n CREATE INDEX IF NOT EXISTS idx_return_shapes_function\n ON return_shapes(function_id);\n\n CREATE INDEX IF NOT EXISTS idx_data_flows_source\n ON data_flows(source_id);\n\n CREATE INDEX IF NOT EXISTS idx_data_flows_target\n ON data_flows(target_id);\n `);\n}\n\n/**\n * Drop Phase 3 tables (for testing/cleanup)\n */\nexport function dropPhase3Tables(db: Database): void {\n db.exec(`\n DROP TABLE IF EXISTS data_flows;\n DROP TABLE IF EXISTS return_shapes;\n DROP TABLE IF EXISTS property_accesses;\n `);\n}\n"],"mappings":"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","names":[]}
@@ -1,8 +1,62 @@
1
- #!/usr/bin/env node
1
+ // src/flow/storage.ts
2
+ import Database from "better-sqlite3";
3
+
4
+ // src/flow/storage-schema-phase3.ts
5
+ function createPhase3Tables(db) {
6
+ db.exec(`
7
+ CREATE TABLE IF NOT EXISTS property_accesses (
8
+ id TEXT PRIMARY KEY,
9
+ scope_node_id TEXT NOT NULL,
10
+ object_name TEXT NOT NULL,
11
+ property_path TEXT NOT NULL,
12
+ full_path TEXT NOT NULL,
13
+ access_type TEXT NOT NULL,
14
+ file_path TEXT NOT NULL,
15
+ line INTEGER NOT NULL,
16
+ FOREIGN KEY (scope_node_id) REFERENCES nodes(id)
17
+ );
18
+ `);
19
+ db.exec(`
20
+ CREATE TABLE IF NOT EXISTS return_shapes (
21
+ id TEXT PRIMARY KEY,
22
+ function_id TEXT NOT NULL,
23
+ return_type TEXT,
24
+ object_shape TEXT,
25
+ line INTEGER NOT NULL,
26
+ return_expression TEXT,
27
+ FOREIGN KEY (function_id) REFERENCES nodes(id)
28
+ );
29
+ `);
30
+ db.exec(`
31
+ CREATE TABLE IF NOT EXISTS data_flows (
32
+ id TEXT PRIMARY KEY,
33
+ source_id TEXT NOT NULL,
34
+ target_id TEXT NOT NULL,
35
+ data_type TEXT NOT NULL,
36
+ shape TEXT,
37
+ flow_type TEXT NOT NULL,
38
+ line INTEGER
39
+ );
40
+ `);
41
+ db.exec(`
42
+ CREATE INDEX IF NOT EXISTS idx_property_accesses_path
43
+ ON property_accesses(full_path);
44
+
45
+ CREATE INDEX IF NOT EXISTS idx_property_accesses_scope
46
+ ON property_accesses(scope_node_id);
47
+
48
+ CREATE INDEX IF NOT EXISTS idx_return_shapes_function
49
+ ON return_shapes(function_id);
2
50
 
51
+ CREATE INDEX IF NOT EXISTS idx_data_flows_source
52
+ ON data_flows(source_id);
53
+
54
+ CREATE INDEX IF NOT EXISTS idx_data_flows_target
55
+ ON data_flows(target_id);
56
+ `);
57
+ }
3
58
 
4
59
  // src/flow/storage.ts
5
- import Database from "better-sqlite3";
6
60
  var FlowStorage = class {
7
61
  db;
8
62
  // Public for builder to access
@@ -157,6 +211,7 @@ var FlowStorage = class {
157
211
  CREATE INDEX IF NOT EXISTS idx_variable_types_scope ON variable_types(scope_node_id, variable_name);
158
212
  CREATE INDEX IF NOT EXISTS idx_variable_types_file ON variable_types(file_path, variable_name);
159
213
  `);
214
+ createPhase3Tables(this.db);
160
215
  }
161
216
  // ============ Node Operations ============
162
217
  insertNode(node) {
@@ -775,4 +830,4 @@ var FlowStorage = class {
775
830
  export {
776
831
  FlowStorage
777
832
  };
778
- //# sourceMappingURL=chunk-LXZ73T7X.js.map
833
+ //# sourceMappingURL=chunk-EEMILSZ4.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/flow/storage.ts","../src/flow/storage-schema-phase3.ts"],"sourcesContent":["/**\n * Flow-focused storage for call graphs\n * Simplified from graph/storage.ts with enhanced call resolution\n */\n\nimport Database from \"better-sqlite3\";\nimport type { GraphNode, GraphEdge } from \"../types.js\";\nimport { createPhase3Tables } from \"./storage-schema-phase3.js\";\n\nexport interface CallPath {\n nodes: Array<{\n id: string;\n name: string;\n file: string;\n line: number;\n }>;\n edges: Array<{\n from: string;\n to: string;\n type: string;\n }>;\n}\n\nexport class FlowStorage {\n public db: Database.Database; // Public for builder to access\n\n constructor(dbPath: string) {\n this.db = new Database(dbPath);\n this.initSchema();\n }\n\n private initSchema(): void {\n this.db.exec(`\n -- Nodes table (enhanced with hash)\n CREATE TABLE IF NOT EXISTS nodes (\n id TEXT PRIMARY KEY,\n type TEXT NOT NULL,\n name TEXT NOT NULL,\n file_path TEXT NOT NULL,\n start_line INTEGER NOT NULL,\n end_line INTEGER NOT NULL,\n language TEXT NOT NULL,\n hash TEXT,\n metadata TEXT\n );\n\n -- Edges table (calls and related edges)\n CREATE TABLE IF NOT EXISTS edges (\n id TEXT PRIMARY KEY,\n type TEXT NOT NULL,\n source_id TEXT NOT NULL,\n target_id TEXT NOT NULL,\n metadata TEXT\n );\n\n -- Files table\n CREATE TABLE IF NOT EXISTS files (\n path TEXT PRIMARY KEY,\n language TEXT NOT NULL,\n hash TEXT NOT NULL,\n analyzed_at TEXT NOT NULL\n );\n\n -- Exports: symbols exported by each file\n CREATE TABLE IF NOT EXISTS exports (\n file_path TEXT NOT NULL,\n symbol_name TEXT NOT NULL,\n node_id TEXT NOT NULL,\n PRIMARY KEY (file_path, symbol_name)\n );\n\n -- Imports: what each file imports and from where\n CREATE TABLE IF NOT EXISTS imports (\n file_path TEXT NOT NULL,\n imported_symbol TEXT NOT NULL,\n from_module TEXT NOT NULL,\n resolved_node_id TEXT,\n line INTEGER NOT NULL\n );\n\n -- Containers (Docker services)\n CREATE TABLE IF NOT EXISTS containers (\n id TEXT PRIMARY KEY,\n name TEXT NOT NULL,\n image TEXT,\n ports TEXT,\n environment TEXT,\n depends_on TEXT,\n networks TEXT,\n metadata TEXT\n );\n\n -- HTTP Endpoints (API routes)\n CREATE TABLE IF NOT EXISTS http_endpoints (\n id TEXT PRIMARY KEY,\n method TEXT NOT NULL,\n path TEXT NOT NULL,\n handler_node_id TEXT,\n container_id TEXT,\n middleware TEXT,\n metadata TEXT\n );\n\n -- HTTP Calls (client requests)\n CREATE TABLE IF NOT EXISTS http_calls (\n id TEXT PRIMARY KEY,\n source_node_id TEXT NOT NULL,\n target_endpoint_id TEXT,\n url TEXT NOT NULL,\n method TEXT NOT NULL,\n line INTEGER,\n metadata TEXT\n );\n\n -- Framework Dependencies (FastAPI Depends, etc.)\n CREATE TABLE IF NOT EXISTS framework_dependencies (\n id TEXT PRIMARY KEY,\n source_node_id TEXT NOT NULL,\n target_node_id TEXT,\n framework TEXT NOT NULL,\n pattern TEXT NOT NULL,\n parameter_name TEXT,\n line INTEGER,\n unresolved_name TEXT,\n metadata TEXT\n );\n\n -- Database Operations\n CREATE TABLE IF NOT EXISTS database_operations (\n id TEXT PRIMARY KEY,\n node_id TEXT NOT NULL,\n database_type TEXT NOT NULL,\n operation TEXT NOT NULL,\n collection TEXT,\n line INTEGER,\n metadata TEXT\n );\n\n -- Message Queue Operations\n CREATE TABLE IF NOT EXISTS message_queue_operations (\n id TEXT PRIMARY KEY,\n node_id TEXT NOT NULL,\n operation TEXT NOT NULL,\n exchange TEXT,\n routing_key TEXT,\n queue TEXT,\n line INTEGER,\n metadata TEXT\n );\n\n -- Variable Type Tracking\n CREATE TABLE IF NOT EXISTS variable_types (\n id TEXT PRIMARY KEY,\n variable_name TEXT NOT NULL,\n type_name TEXT NOT NULL,\n scope_node_id TEXT NOT NULL,\n file_path TEXT NOT NULL,\n line INTEGER NOT NULL,\n is_parameter BOOLEAN DEFAULT 0,\n metadata TEXT\n );\n\n -- Indexes for fast queries\n CREATE INDEX IF NOT EXISTS idx_nodes_file ON nodes(file_path);\n CREATE INDEX IF NOT EXISTS idx_nodes_name ON nodes(name);\n CREATE INDEX IF NOT EXISTS idx_nodes_hash ON nodes(hash);\n CREATE INDEX IF NOT EXISTS idx_edges_source ON edges(source_id);\n CREATE INDEX IF NOT EXISTS idx_edges_target ON edges(target_id);\n CREATE INDEX IF NOT EXISTS idx_edges_resolved ON edges(target_id) WHERE target_id NOT LIKE 'ref:%';\n CREATE INDEX IF NOT EXISTS idx_exports_file ON exports(file_path);\n CREATE INDEX IF NOT EXISTS idx_imports_file ON imports(file_path);\n CREATE INDEX IF NOT EXISTS idx_http_endpoints_path ON http_endpoints(method, path);\n CREATE INDEX IF NOT EXISTS idx_http_calls_url ON http_calls(url);\n CREATE INDEX IF NOT EXISTS idx_framework_deps_source ON framework_dependencies(source_node_id);\n CREATE INDEX IF NOT EXISTS idx_framework_deps_target ON framework_dependencies(target_node_id);\n CREATE INDEX IF NOT EXISTS idx_variable_types_scope ON variable_types(scope_node_id, variable_name);\n CREATE INDEX IF NOT EXISTS idx_variable_types_file ON variable_types(file_path, variable_name);\n `);\n\n // Create Phase 3 tables\n createPhase3Tables(this.db);\n }\n\n // ============ Node Operations ============\n\n insertNode(node: GraphNode & { hash?: string }): void {\n const stmt = this.db.prepare(`\n INSERT OR REPLACE INTO nodes (id, type, name, file_path, start_line, end_line, language, hash, metadata)\n VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)\n `);\n stmt.run(\n node.id,\n node.type,\n node.name,\n node.filePath,\n node.startLine,\n node.endLine,\n node.language,\n node.hash || null,\n node.metadata ? JSON.stringify(node.metadata) : null\n );\n }\n\n getNode(id: string): GraphNode | null {\n const stmt = this.db.prepare(\"SELECT * FROM nodes WHERE id = ?\");\n const row = stmt.get(id) as Record<string, unknown> | undefined;\n if (!row) return null;\n return this.rowToNode(row);\n }\n\n searchNodesByName(name: string): GraphNode[] {\n const stmt = this.db.prepare(\"SELECT * FROM nodes WHERE name = ? OR name LIKE ?\");\n const rows = stmt.all(name, `%${name}%`) as Record<string, unknown>[];\n return rows.map((r) => this.rowToNode(r));\n }\n\n getNodesByFile(filePath: string): GraphNode[] {\n const stmt = this.db.prepare(\"SELECT * FROM nodes WHERE file_path = ?\");\n const rows = stmt.all(filePath) as Record<string, unknown>[];\n return rows.map((r) => this.rowToNode(r));\n }\n\n private rowToNode(row: Record<string, unknown>): GraphNode {\n return {\n id: row.id as string,\n type: row.type as GraphNode[\"type\"],\n name: row.name as string,\n filePath: row.file_path as string,\n startLine: row.start_line as number,\n endLine: row.end_line as number,\n language: row.language as string,\n metadata: row.metadata ? JSON.parse(row.metadata as string) : undefined,\n };\n }\n\n // ============ Edge Operations ============\n\n insertEdge(edge: GraphEdge): void {\n const stmt = this.db.prepare(`\n INSERT OR REPLACE INTO edges (id, type, source_id, target_id, metadata)\n VALUES (?, ?, ?, ?, ?)\n `);\n stmt.run(\n edge.id,\n edge.type,\n edge.sourceId,\n edge.targetId,\n edge.metadata ? JSON.stringify(edge.metadata) : null\n );\n }\n\n // ============ Call Graph Queries ============\n\n /**\n * Get all resolved callees (functions this node calls)\n */\n getResolvedCallees(nodeId: string): GraphNode[] {\n const stmt = this.db.prepare(`\n SELECT n.* FROM nodes n\n JOIN edges e ON n.id = e.target_id\n WHERE e.source_id = ? AND e.type = 'calls' AND e.target_id NOT LIKE 'ref:%'\n `);\n const rows = stmt.all(nodeId) as Record<string, unknown>[];\n return rows.map((r) => this.rowToNode(r));\n }\n\n /**\n * Get all resolved callers (functions that call this node)\n */\n getResolvedCallers(nodeId: string): GraphNode[] {\n const stmt = this.db.prepare(`\n SELECT n.* FROM nodes n\n JOIN edges e ON n.id = e.source_id\n WHERE e.target_id = ? AND e.type = 'calls'\n `);\n const rows = stmt.all(nodeId) as Record<string, unknown>[];\n return rows.map((r) => this.rowToNode(r));\n }\n\n /**\n * Get impact: all nodes affected by changes to this node (BFS traversal)\n */\n getImpactedNodes(nodeId: string, maxDepth: number = 3): GraphNode[] {\n const visited = new Set<string>();\n const result: GraphNode[] = [];\n const queue: Array<{ id: string; depth: number }> = [{ id: nodeId, depth: 0 }];\n\n while (queue.length > 0) {\n const { id, depth } = queue.shift()!;\n\n if (visited.has(id) || depth > maxDepth) continue;\n visited.add(id);\n\n const callers = this.getResolvedCallers(id);\n for (const caller of callers) {\n if (!visited.has(caller.id)) {\n result.push(caller);\n queue.push({ id: caller.id, depth: depth + 1 });\n }\n }\n }\n\n return result;\n }\n\n /**\n * Trace call path from source to target using BFS\n */\n traceCallPath(fromId: string, toId: string): CallPath | null {\n const visited = new Set<string>();\n const parent = new Map<string, { nodeId: string; edgeType: string }>();\n const queue: string[] = [fromId];\n\n while (queue.length > 0) {\n const current = queue.shift()!;\n\n if (current === toId) {\n // Reconstruct path\n const path: string[] = [];\n let node = toId;\n while (node !== fromId) {\n path.unshift(node);\n node = parent.get(node)!.nodeId;\n }\n path.unshift(fromId);\n\n // Get node details\n const nodes = path.map((id) => {\n const n = this.getNode(id);\n return {\n id: n!.id,\n name: n!.name,\n file: n!.filePath,\n line: n!.startLine,\n };\n });\n\n const edges = [];\n for (let i = 0; i < path.length - 1; i++) {\n edges.push({\n from: path[i],\n to: path[i + 1],\n type: 'calls',\n });\n }\n\n return { nodes, edges };\n }\n\n if (visited.has(current)) continue;\n visited.add(current);\n\n const callees = this.getResolvedCallees(current);\n for (const callee of callees) {\n if (!visited.has(callee.id)) {\n parent.set(callee.id, { nodeId: current, edgeType: 'calls' });\n queue.push(callee.id);\n }\n }\n }\n\n return null;\n }\n\n // ============ Export/Import Tracking ============\n\n registerExport(filePath: string, symbolName: string, nodeId: string): void {\n const stmt = this.db.prepare(`\n INSERT OR REPLACE INTO exports (file_path, symbol_name, node_id)\n VALUES (?, ?, ?)\n `);\n stmt.run(filePath, symbolName, nodeId);\n }\n\n registerImport(filePath: string, symbol: string, fromModule: string, line: number): void {\n const stmt = this.db.prepare(`\n INSERT OR REPLACE INTO imports (file_path, imported_symbol, from_module, line)\n VALUES (?, ?, ?, ?)\n `);\n stmt.run(filePath, symbol, fromModule, line);\n }\n\n resolveImport(filePath: string, symbol: string): string | null {\n // Get the import statement\n const importStmt = this.db.prepare(`\n SELECT from_module FROM imports WHERE file_path = ? AND imported_symbol = ?\n `).get(filePath, symbol) as { from_module: string } | undefined;\n\n if (!importStmt) return null;\n\n // Convert module path to possible file paths\n // Examples:\n // - \"backend.authentication\" -> [\"backend/authentication.py\", \"backend/authentication/__init__.py\"]\n // - \"..authentication\" -> relative import (handle separately)\n const modulePath = importStmt.from_module;\n const possiblePaths = this.resolvePythonModulePath(filePath, modulePath);\n\n // Try each possible path\n for (const targetPath of possiblePaths) {\n const exportStmt = this.db.prepare(`\n SELECT node_id FROM exports WHERE file_path = ? AND symbol_name = ?\n `).get(targetPath, symbol) as { node_id: string } | undefined;\n\n if (exportStmt) {\n return exportStmt.node_id;\n }\n }\n\n return null;\n }\n\n /**\n * Resolve a Python module path to possible file paths\n * Handles: absolute imports, relative imports, __init__.py\n */\n private resolvePythonModulePath(currentFile: string, modulePath: string): string[] {\n const possiblePaths: string[] = [];\n\n // Handle relative imports (from . import x, from .. import y)\n if (modulePath.startsWith(\".\")) {\n const currentDir = currentFile.substring(0, currentFile.lastIndexOf(\"/\"));\n const depth = modulePath.match(/^\\.+/)?.[0].length || 0;\n const moduleName = modulePath.replace(/^\\.+/, \"\");\n\n // Go up directories based on depth\n let targetDir = currentDir;\n for (let i = 1; i < depth; i++) {\n const lastSlash = targetDir.lastIndexOf(\"/\");\n if (lastSlash > 0) {\n targetDir = targetDir.substring(0, lastSlash);\n }\n }\n\n if (moduleName) {\n const relativePath = moduleName.replace(/\\./g, \"/\");\n possiblePaths.push(`${targetDir}/${relativePath}.py`);\n possiblePaths.push(`${targetDir}/${relativePath}/__init__.py`);\n } else {\n possiblePaths.push(`${targetDir}/__init__.py`);\n }\n } else {\n // Absolute import - convert dots to slashes\n const pathComponents = modulePath.split(\".\");\n\n // Try different interpretations\n // 1. Full path as file: backend.authentication -> backend/authentication.py\n const filePath = pathComponents.join(\"/\");\n possiblePaths.push(`${filePath}.py`);\n possiblePaths.push(`${filePath}/__init__.py`);\n\n // 2. Try with src/ prefix (common pattern)\n possiblePaths.push(`src/${filePath}.py`);\n possiblePaths.push(`src/${filePath}/__init__.py`);\n\n // 3. Query actual files to find match\n // Get all files that end with the last component\n const lastComponent = pathComponents[pathComponents.length - 1];\n const matchingFiles = this.db.prepare(`\n SELECT DISTINCT file_path FROM exports\n WHERE file_path LIKE ?\n `).all(`%/${lastComponent}.py`) as Array<{ file_path: string }>;\n\n for (const file of matchingFiles) {\n // Check if the file path ends with the module path\n const normalizedFilePath = file.file_path.replace(/\\\\/g, \"/\");\n if (normalizedFilePath.includes(filePath)) {\n possiblePaths.push(normalizedFilePath);\n }\n }\n }\n\n return possiblePaths;\n }\n\n /**\n * Resolve a class method call like ClassName.method_name\n * Handles: static methods, class methods, imported classes\n */\n resolveClassMethod(className: string, methodName: string, callerFilePath: string): string | null {\n // Strategy 1: Check if the class is imported in the caller file\n const classNodeId = this.resolveImport(callerFilePath, className);\n\n if (classNodeId) {\n // Find the method within this class\n const methods = this.db.prepare(`\n SELECT target_id FROM edges\n WHERE source_id = ? AND type = 'contains'\n `).all(classNodeId) as Array<{ target_id: string }>;\n\n for (const edge of methods) {\n const methodNode = this.getNode(edge.target_id);\n if (methodNode && methodNode.name === methodName) {\n return methodNode.id;\n }\n }\n }\n\n // Strategy 2: Search for class in the same file\n const classNode = this.db.prepare(`\n SELECT id FROM nodes\n WHERE file_path = ? AND name = ? AND type = 'class'\n `).get(callerFilePath, className) as { id: string } | undefined;\n\n if (classNode) {\n // Find the method within this class\n const methodNode = this.db.prepare(`\n SELECT n.id FROM nodes n\n JOIN edges e ON e.target_id = n.id\n WHERE e.source_id = ? AND e.type = 'contains' AND n.name = ?\n `).get(classNode.id, methodName) as { id: string } | undefined;\n\n if (methodNode) {\n return methodNode.id;\n }\n }\n\n // Strategy 3: Search globally for the class\n const allClasses = this.db.prepare(`\n SELECT id FROM nodes WHERE name = ? AND type = 'class'\n `).all(className) as Array<{ id: string }>;\n\n for (const cls of allClasses) {\n const methodNode = this.db.prepare(`\n SELECT n.id FROM nodes n\n JOIN edges e ON e.target_id = n.id\n WHERE e.source_id = ? AND e.type = 'contains' AND n.name = ?\n `).get(cls.id, methodName) as { id: string } | undefined;\n\n if (methodNode) {\n return methodNode.id;\n }\n }\n\n return null;\n }\n\n // ============ Variable Type Tracking ============\n\n /**\n * Register a variable type (from assignment or parameter)\n */\n registerVariableType(\n variableName: string,\n typeName: string,\n scopeNodeId: string,\n filePath: string,\n line: number,\n isParameter: boolean = false\n ): void {\n const id = `${scopeNodeId}:var:${variableName}`;\n const stmt = this.db.prepare(`\n INSERT OR REPLACE INTO variable_types\n (id, variable_name, type_name, scope_node_id, file_path, line, is_parameter)\n VALUES (?, ?, ?, ?, ?, ?, ?)\n `);\n stmt.run(id, variableName, typeName, scopeNodeId, filePath, line, isParameter ? 1 : 0);\n }\n\n /**\n * Resolve variable.method to actual method node\n * Example: user.get_name() where user is of type User\n */\n resolveVariableMethod(variableName: string, methodName: string, scopeNodeId: string): string | null {\n // Special case: self.method calls - resolve to methods in parent class\n if (variableName === \"self\") {\n return this.resolveSelfMethod(methodName, scopeNodeId);\n }\n\n // Get the variable's type\n const varType = this.db.prepare(`\n SELECT type_name FROM variable_types\n WHERE scope_node_id = ? AND variable_name = ?\n `).get(scopeNodeId, variableName) as { type_name: string } | undefined;\n\n if (!varType) {\n return null;\n }\n\n // Now resolve ClassName.method_name\n return this.resolveClassMethod(varType.type_name, methodName, \"\");\n }\n\n /**\n * Resolve self.method() calls to methods in the same class\n */\n private resolveSelfMethod(methodName: string, scopeNodeId: string): string | null {\n // Find the parent class of this method\n const parentClass = this.db.prepare(`\n SELECT source_id FROM edges\n WHERE target_id = ? AND type = 'contains'\n `).get(scopeNodeId) as { source_id: string } | undefined;\n\n if (!parentClass) {\n return null;\n }\n\n // Find the method with this name in the same class\n const method = this.db.prepare(`\n SELECT n.id FROM nodes n\n JOIN edges e ON e.target_id = n.id\n WHERE e.source_id = ? AND e.type = 'contains' AND n.name = ?\n `).get(parentClass.source_id, methodName) as { id: string } | undefined;\n\n return method?.id || null;\n }\n\n /**\n * Resolve super().method() calls to parent class methods\n */\n resolveSuperMethod(methodName: string, scopeNodeId: string): string | null {\n // Find the parent class of the current method\n const parentClass = this.db.prepare(`\n SELECT source_id FROM edges\n WHERE target_id = ? AND type = 'contains'\n `).get(scopeNodeId) as { source_id: string } | undefined;\n\n if (!parentClass) {\n return null;\n }\n\n // Get the class node\n const classNode = this.getNode(parentClass.source_id);\n if (!classNode || classNode.type !== 'class') {\n return null;\n }\n\n // Find the parent class (via extends edge)\n const baseClass = this.db.prepare(`\n SELECT target_id FROM edges\n WHERE source_id = ? AND type = 'extends'\n `).get(classNode.id) as { target_id: string } | undefined;\n\n if (!baseClass) {\n return null;\n }\n\n // The target might be a ref, resolve it first\n let baseClassId = baseClass.target_id;\n if (baseClassId.startsWith('ref:')) {\n const baseClassName = baseClassId.replace('ref:', '');\n // Search for the class by name\n const resolvedClass = this.db.prepare(`\n SELECT id FROM nodes WHERE name = ? AND type = 'class'\n `).get(baseClassName) as { id: string } | undefined;\n\n if (!resolvedClass) {\n return null;\n }\n baseClassId = resolvedClass.id;\n }\n\n // Find the method in the parent class\n const method = this.db.prepare(`\n SELECT n.id FROM nodes n\n JOIN edges e ON e.target_id = n.id\n WHERE e.source_id = ? AND e.type = 'contains' AND n.name = ?\n `).get(baseClassId, methodName) as { id: string } | undefined;\n\n return method?.id || null;\n }\n\n // ============ File Operations ============\n\n upsertFile(filePath: string, language: string, hash: string): void {\n const stmt = this.db.prepare(`\n INSERT OR REPLACE INTO files (path, language, hash, analyzed_at)\n VALUES (?, ?, ?, ?)\n `);\n stmt.run(filePath, language, hash, new Date().toISOString());\n }\n\n getFileHash(filePath: string): string | null {\n const stmt = this.db.prepare(\"SELECT hash FROM files WHERE path = ?\");\n const row = stmt.get(filePath) as { hash: string } | undefined;\n return row?.hash || null;\n }\n\n deleteFileData(filePath: string): void {\n this.db.prepare(\"DELETE FROM nodes WHERE file_path = ?\").run(filePath);\n this.db.prepare(\"DELETE FROM exports WHERE file_path = ?\").run(filePath);\n this.db.prepare(\"DELETE FROM imports WHERE file_path = ?\").run(filePath);\n this.db.prepare(\"DELETE FROM files WHERE path = ?\").run(filePath);\n }\n\n // ============ Stats ============\n\n getStats(): {\n totalFiles: number;\n totalNodes: number;\n totalEdges: number;\n resolvedCalls: number;\n unresolvedCalls: number;\n httpEndpoints: number;\n frameworkDependencies: number;\n resolvedDependencies: number;\n containers: number;\n } {\n const totalFiles = (this.db.prepare(\"SELECT COUNT(*) as count FROM files\").get() as { count: number }).count;\n const totalNodes = (this.db.prepare(\"SELECT COUNT(*) as count FROM nodes\").get() as { count: number }).count;\n const totalEdges = (this.db.prepare(\"SELECT COUNT(*) as count FROM edges WHERE type = 'calls'\").get() as { count: number }).count;\n const resolvedCalls = (this.db.prepare(\"SELECT COUNT(*) as count FROM edges WHERE type = 'calls' AND target_id NOT LIKE 'ref:%'\").get() as { count: number }).count;\n const unresolvedCalls = totalEdges - resolvedCalls;\n const httpEndpoints = (this.db.prepare(\"SELECT COUNT(*) as count FROM http_endpoints\").get() as { count: number }).count;\n const frameworkDependencies = (this.db.prepare(\"SELECT COUNT(*) as count FROM framework_dependencies\").get() as { count: number }).count;\n const resolvedDependencies = (this.db.prepare(\"SELECT COUNT(*) as count FROM framework_dependencies WHERE target_node_id IS NOT NULL\").get() as { count: number }).count;\n const containers = (this.db.prepare(\"SELECT COUNT(*) as count FROM containers\").get() as { count: number }).count;\n\n return {\n totalFiles,\n totalNodes,\n totalEdges,\n resolvedCalls,\n unresolvedCalls,\n httpEndpoints,\n frameworkDependencies,\n resolvedDependencies,\n containers,\n };\n }\n\n // ============ Container Operations ============\n\n insertContainer(container: {\n id: string;\n name: string;\n image?: string;\n ports?: Array<{ host: number; container: number }>;\n environment?: Record<string, string>;\n depends_on?: string[];\n networks?: string[];\n metadata?: Record<string, unknown>;\n }): void {\n const stmt = this.db.prepare(`\n INSERT OR REPLACE INTO containers (id, name, image, ports, environment, depends_on, networks, metadata)\n VALUES (?, ?, ?, ?, ?, ?, ?, ?)\n `);\n stmt.run(\n container.id,\n container.name,\n container.image || null,\n container.ports ? JSON.stringify(container.ports) : null,\n container.environment ? JSON.stringify(container.environment) : null,\n container.depends_on ? JSON.stringify(container.depends_on) : null,\n container.networks ? JSON.stringify(container.networks) : null,\n container.metadata ? JSON.stringify(container.metadata) : null\n );\n }\n\n getContainer(id: string): any | null {\n const stmt = this.db.prepare(\"SELECT * FROM containers WHERE id = ?\");\n const row = stmt.get(id) as Record<string, unknown> | undefined;\n if (!row) return null;\n return {\n id: row.id,\n name: row.name,\n image: row.image,\n ports: row.ports ? JSON.parse(row.ports as string) : [],\n environment: row.environment ? JSON.parse(row.environment as string) : {},\n depends_on: row.depends_on ? JSON.parse(row.depends_on as string) : [],\n networks: row.networks ? JSON.parse(row.networks as string) : [],\n metadata: row.metadata ? JSON.parse(row.metadata as string) : {},\n };\n }\n\n getAllContainers(): any[] {\n const stmt = this.db.prepare(\"SELECT * FROM containers\");\n const rows = stmt.all() as Record<string, unknown>[];\n return rows.map(row => ({\n id: row.id,\n name: row.name,\n image: row.image,\n ports: row.ports ? JSON.parse(row.ports as string) : [],\n environment: row.environment ? JSON.parse(row.environment as string) : {},\n depends_on: row.depends_on ? JSON.parse(row.depends_on as string) : [],\n networks: row.networks ? JSON.parse(row.networks as string) : [],\n metadata: row.metadata ? JSON.parse(row.metadata as string) : {},\n }));\n }\n\n // ============ HTTP Endpoint Operations ============\n\n insertHttpEndpoint(endpoint: {\n id: string;\n method: string;\n path: string;\n handler_node_id?: string;\n container_id?: string;\n middleware?: string[];\n metadata?: Record<string, unknown>;\n }): void {\n const stmt = this.db.prepare(`\n INSERT OR REPLACE INTO http_endpoints (id, method, path, handler_node_id, container_id, middleware, metadata)\n VALUES (?, ?, ?, ?, ?, ?, ?)\n `);\n stmt.run(\n endpoint.id,\n endpoint.method,\n endpoint.path,\n endpoint.handler_node_id || null,\n endpoint.container_id || null,\n endpoint.middleware ? JSON.stringify(endpoint.middleware) : null,\n endpoint.metadata ? JSON.stringify(endpoint.metadata) : null\n );\n }\n\n insertDatabaseOperation(dbOp: {\n id: string;\n node_id: string;\n database_type: string;\n operation: string;\n collection?: string;\n line?: number;\n metadata?: Record<string, unknown>;\n }): void {\n const stmt = this.db.prepare(`\n INSERT OR REPLACE INTO database_operations (id, node_id, database_type, operation, collection, line, metadata)\n VALUES (?, ?, ?, ?, ?, ?, ?)\n `);\n stmt.run(\n dbOp.id,\n dbOp.node_id,\n dbOp.database_type,\n dbOp.operation,\n dbOp.collection || null,\n dbOp.line || null,\n dbOp.metadata ? JSON.stringify(dbOp.metadata) : null\n );\n }\n\n findHttpEndpoint(method: string, path: string): any | null {\n const stmt = this.db.prepare(\"SELECT * FROM http_endpoints WHERE method = ? AND path = ?\");\n const row = stmt.get(method, path) as Record<string, unknown> | undefined;\n if (!row) return null;\n return {\n id: row.id,\n method: row.method,\n path: row.path,\n handler_node_id: row.handler_node_id,\n container_id: row.container_id,\n middleware: row.middleware ? JSON.parse(row.middleware as string) : [],\n metadata: row.metadata ? JSON.parse(row.metadata as string) : {},\n };\n }\n\n // ============ Framework Dependency Operations ============\n\n insertFrameworkDependency(dep: {\n id: string;\n source_node_id: string;\n target_node_id?: string;\n framework: string;\n pattern: string;\n parameter_name?: string;\n line?: number;\n unresolved_name?: string;\n metadata?: Record<string, unknown>;\n }): void {\n const stmt = this.db.prepare(`\n INSERT OR REPLACE INTO framework_dependencies\n (id, source_node_id, target_node_id, framework, pattern, parameter_name, line, unresolved_name, metadata)\n VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)\n `);\n stmt.run(\n dep.id,\n dep.source_node_id,\n dep.target_node_id || null,\n dep.framework,\n dep.pattern,\n dep.parameter_name || null,\n dep.line || null,\n dep.unresolved_name || null,\n dep.metadata ? JSON.stringify(dep.metadata) : null\n );\n }\n\n getFrameworkDependencies(sourceNodeId: string): any[] {\n const stmt = this.db.prepare(\"SELECT * FROM framework_dependencies WHERE source_node_id = ?\");\n const rows = stmt.all(sourceNodeId) as Record<string, unknown>[];\n return rows.map(row => ({\n id: row.id,\n source_node_id: row.source_node_id,\n target_node_id: row.target_node_id,\n framework: row.framework,\n pattern: row.pattern,\n parameter_name: row.parameter_name,\n line: row.line,\n unresolved_name: row.unresolved_name,\n metadata: row.metadata ? JSON.parse(row.metadata as string) : {},\n }));\n }\n\n getAllUnresolvedDependencies(): any[] {\n const stmt = this.db.prepare(\"SELECT * FROM framework_dependencies WHERE target_node_id IS NULL\");\n const rows = stmt.all() as Record<string, unknown>[];\n return rows.map(row => ({\n id: row.id,\n source_node_id: row.source_node_id,\n framework: row.framework,\n pattern: row.pattern,\n unresolved_name: row.unresolved_name,\n line: row.line,\n }));\n }\n\n updateFrameworkDependencyTarget(id: string, targetNodeId: string): void {\n const stmt = this.db.prepare(\"UPDATE framework_dependencies SET target_node_id = ? WHERE id = ?\");\n stmt.run(targetNodeId, id);\n }\n\n clear(): void {\n this.db.exec(`\n DELETE FROM nodes;\n DELETE FROM edges;\n DELETE FROM files;\n DELETE FROM exports;\n DELETE FROM imports;\n DELETE FROM containers;\n DELETE FROM http_endpoints;\n DELETE FROM http_calls;\n DELETE FROM framework_dependencies;\n DELETE FROM database_operations;\n DELETE FROM message_queue_operations;\n `);\n }\n\n /**\n * Get all file hashes (for detecting changes)\n */\n getAllFileHashes(): Record<string, string> {\n const stmt = this.db.prepare(\"SELECT path, hash FROM files WHERE hash IS NOT NULL\");\n const rows = stmt.all() as Array<{ path: string; hash: string }>;\n const hashes: Record<string, string> = {};\n for (const row of rows) {\n hashes[row.path] = row.hash;\n }\n return hashes;\n }\n\n /**\n * Get all nodes (for impact comparison)\n */\n getAllNodes(): Array<{ id: string; name: string; type: string; filePath: string }> {\n const stmt = this.db.prepare(\"SELECT id, name, type, file_path FROM nodes\");\n return stmt.all() as Array<{ id: string; name: string; type: string; filePath: string }>;\n }\n\n /**\n * Get all edges (for impact comparison)\n */\n getAllEdges(): Array<{ id: string; source_id: string; target_id: string; type: string }> {\n const stmt = this.db.prepare(\"SELECT id, source_id, target_id, type FROM edges\");\n return stmt.all() as Array<{ id: string; source_id: string; target_id: string; type: string }>;\n }\n\n /**\n * Get resolved callers with file/line info (for impact analysis)\n */\n getResolvedCallersWithLocation(nodeId: string): Array<{ name: string; file: string; line: number }> {\n const stmt = this.db.prepare(`\n SELECT n.name, n.file_path as file, n.start_line as line\n FROM edges e\n JOIN nodes n ON e.source_id = n.id\n WHERE e.target_id = ? AND e.type = 'calls' AND e.target_id NOT LIKE 'ref:%'\n `);\n return stmt.all(nodeId) as Array<{ name: string; file: string; line: number }>;\n }\n\n /**\n * Get HTTP endpoints by handler node ID\n */\n getHttpEndpointsByHandler(handlerNodeId: string): Array<{ method: string; path: string }> {\n const stmt = this.db.prepare(\"SELECT method, path FROM http_endpoints WHERE handler_node_id = ?\");\n return stmt.all(handlerNodeId) as Array<{ method: string; path: string }>;\n }\n\n close(): void {\n this.db.close();\n }\n}\n","/**\n * Phase 3 Storage Schema Extensions\n * Additional tables for data flow analysis\n */\n\nimport { Database } from \"better-sqlite3\";\n\n/**\n * Create Phase 3 tables for data flow analysis\n */\nexport function createPhase3Tables(db: Database): void {\n // Property accesses table\n db.exec(`\n CREATE TABLE IF NOT EXISTS property_accesses (\n id TEXT PRIMARY KEY,\n scope_node_id TEXT NOT NULL,\n object_name TEXT NOT NULL,\n property_path TEXT NOT NULL,\n full_path TEXT NOT NULL,\n access_type TEXT NOT NULL,\n file_path TEXT NOT NULL,\n line INTEGER NOT NULL,\n FOREIGN KEY (scope_node_id) REFERENCES nodes(id)\n );\n `);\n\n // Return shapes table\n db.exec(`\n CREATE TABLE IF NOT EXISTS return_shapes (\n id TEXT PRIMARY KEY,\n function_id TEXT NOT NULL,\n return_type TEXT,\n object_shape TEXT,\n line INTEGER NOT NULL,\n return_expression TEXT,\n FOREIGN KEY (function_id) REFERENCES nodes(id)\n );\n `);\n\n // Data flows table\n db.exec(`\n CREATE TABLE IF NOT EXISTS data_flows (\n id TEXT PRIMARY KEY,\n source_id TEXT NOT NULL,\n target_id TEXT NOT NULL,\n data_type TEXT NOT NULL,\n shape TEXT,\n flow_type TEXT NOT NULL,\n line INTEGER\n );\n `);\n\n // Create indexes for performance\n db.exec(`\n CREATE INDEX IF NOT EXISTS idx_property_accesses_path\n ON property_accesses(full_path);\n\n CREATE INDEX IF NOT EXISTS idx_property_accesses_scope\n ON property_accesses(scope_node_id);\n\n CREATE INDEX IF NOT EXISTS idx_return_shapes_function\n ON return_shapes(function_id);\n\n CREATE INDEX IF NOT EXISTS idx_data_flows_source\n ON data_flows(source_id);\n\n CREATE INDEX IF NOT EXISTS idx_data_flows_target\n ON data_flows(target_id);\n `);\n}\n\n/**\n * Drop Phase 3 tables (for testing/cleanup)\n */\nexport function dropPhase3Tables(db: Database): void {\n db.exec(`\n DROP TABLE IF EXISTS data_flows;\n DROP TABLE IF EXISTS return_shapes;\n DROP TABLE IF EXISTS property_accesses;\n `);\n}\n"],"mappings":"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","names":[]}