codemap-ai 3.6.0 → 3.8.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/LICENSE +1 -1
- package/README.md +13 -13
- package/dist/{chunk-EEMILSZ4.js → chunk-4BHPAOQQ.js} +2 -2
- package/dist/{chunk-EEMILSZ4.js.map → chunk-4BHPAOQQ.js.map} +1 -1
- package/dist/{chunk-6TAWVGMT.js → chunk-HKSMTZWU.js} +2 -2
- package/dist/{chunk-BRVRY5KT.js → chunk-YEC7G3GL.js} +2 -2
- package/dist/{chunk-BRVRY5KT.js.map → chunk-YEC7G3GL.js.map} +1 -1
- package/dist/cli.js +4 -4
- package/dist/{flow-server-U3IPHQ3N.js → flow-server-LS5KWRA7.js} +2 -2
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -1
- package/dist/mcp-server.js +1 -1
- package/dist/{visualize-LB2A4KEA.js → visualize-Q66C3T4R.js} +24 -22
- package/dist/visualize-Q66C3T4R.js.map +1 -0
- package/package.json +11 -6
- package/web-dist/assets/index-D8Y8ugGt.js +161 -0
- package/web-dist/assets/index-DGTC-M9T.css +1 -0
- package/web-dist/index.html +2 -2
- package/dist/visualize-LB2A4KEA.js.map +0 -1
- package/web-dist/assets/index-AyyZ2Kpr.js +0 -96
- package/web-dist/assets/index-CeA2-_I-.css +0 -1
- /package/dist/{chunk-6TAWVGMT.js.map → chunk-HKSMTZWU.js.map} +0 -0
- /package/dist/{flow-server-U3IPHQ3N.js.map → flow-server-LS5KWRA7.js.map} +0 -0
package/LICENSE
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
MIT License
|
|
2
2
|
|
|
3
|
-
Copyright (c) 2025 Sahan Nishshanka
|
|
3
|
+
Copyright (c) 2025 Sahan Nishshanka
|
|
4
4
|
|
|
5
5
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
6
|
of this software and associated documentation files (the "Software"), to deal
|
package/README.md
CHANGED
|
@@ -149,9 +149,9 @@ Risk Assessment:
|
|
|
149
149
|
|
|
150
150
|
---
|
|
151
151
|
|
|
152
|
-
### `codemap visualize` - Interactive Impact Visualization ✨ NEW
|
|
152
|
+
### `codemap visualize` - Interactive Impact Visualization ✨ NEW in v3.7
|
|
153
153
|
|
|
154
|
-
Visualize impact analysis in
|
|
154
|
+
Visualize impact analysis in a modern, interactive web interface:
|
|
155
155
|
|
|
156
156
|
```bash
|
|
157
157
|
npx codemap-ai visualize
|
|
@@ -164,19 +164,21 @@ npx codemap-ai visualize
|
|
|
164
164
|
4. Shows call chains, severity levels, and affected functions
|
|
165
165
|
|
|
166
166
|
**Features:**
|
|
167
|
-
- 🎨
|
|
168
|
-
-
|
|
169
|
-
-
|
|
170
|
-
-
|
|
171
|
-
- 📝 Detailed
|
|
167
|
+
- 🎨 **Modern Dark UI** - Professional dark theme with shadcn components
|
|
168
|
+
- 📊 **Interactive Graph** - Powered by React Flow with smooth animations
|
|
169
|
+
- 🔍 **Zoom & Pan** - Explore complex call graphs intuitively
|
|
170
|
+
- 💎 **Visual Severity Levels** - Color-coded nodes (Red=CRITICAL, Orange=HIGH, Blue=MEDIUM/LOW)
|
|
171
|
+
- 📝 **Detailed Popups** - Click any node for caller/callee information in a clean modal
|
|
172
|
+
- 🌳 **Smart Layout** - Hierarchical top-to-bottom flow using Dagre algorithm
|
|
173
|
+
- 📂 **Organized Sidebar** - Browse affected files and functions with severity indicators
|
|
174
|
+
- 🎯 **Changed Functions** - Modified functions clearly marked with badges
|
|
172
175
|
|
|
173
176
|
**Perfect for:**
|
|
174
177
|
- Understanding complex impact chains
|
|
175
178
|
- PR reviews and demos
|
|
176
|
-
- Identifying critical paths
|
|
179
|
+
- Identifying critical paths before refactoring
|
|
177
180
|
- Sharing impact visualizations with team
|
|
178
|
-
|
|
179
|
-
See [Visualization Guide](./docs/VISUALIZATION.md) for details.
|
|
181
|
+
- Visualizing execution flows at a glance
|
|
180
182
|
|
|
181
183
|
---
|
|
182
184
|
|
|
@@ -566,12 +568,10 @@ MIT License - see [LICENSE](LICENSE) file
|
|
|
566
568
|
|
|
567
569
|
## Author
|
|
568
570
|
|
|
569
|
-
Sahan Nishshanka
|
|
571
|
+
Sahan Nishshanka
|
|
570
572
|
|
|
571
573
|
## Links
|
|
572
574
|
|
|
573
|
-
- Repository: https://github.com/justaddai/codemap-ai
|
|
574
|
-
- Issues: https://github.com/justaddai/codemap-ai/issues
|
|
575
575
|
- NPM: https://www.npmjs.com/package/codemap-ai
|
|
576
576
|
|
|
577
577
|
---
|
|
@@ -808,7 +808,7 @@ var FlowStorage = class {
|
|
|
808
808
|
*/
|
|
809
809
|
getResolvedCallersWithLocation(nodeId) {
|
|
810
810
|
const stmt = this.db.prepare(`
|
|
811
|
-
SELECT n.name, n.file_path as file, n.start_line as line
|
|
811
|
+
SELECT n.id, n.name, n.file_path as file, n.start_line as line
|
|
812
812
|
FROM edges e
|
|
813
813
|
JOIN nodes n ON e.source_id = n.id
|
|
814
814
|
WHERE e.target_id = ? AND e.type = 'calls' AND e.target_id NOT LIKE 'ref:%'
|
|
@@ -830,4 +830,4 @@ var FlowStorage = class {
|
|
|
830
830
|
export {
|
|
831
831
|
FlowStorage
|
|
832
832
|
};
|
|
833
|
-
//# sourceMappingURL=chunk-
|
|
833
|
+
//# sourceMappingURL=chunk-4BHPAOQQ.js.map
|
|
@@ -1 +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
|
+
{"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<{ id: string; name: string; file: string; line: number }> {\n const stmt = this.db.prepare(`\n SELECT n.id, 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<{ id: string; 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":[]}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import {
|
|
4
4
|
FlowStorage
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-YEC7G3GL.js";
|
|
6
6
|
|
|
7
7
|
// src/parsers/base.ts
|
|
8
8
|
var BaseParser = class {
|
|
@@ -3536,4 +3536,4 @@ export {
|
|
|
3536
3536
|
DEFAULT_CONFIG,
|
|
3537
3537
|
analyzeImpact
|
|
3538
3538
|
};
|
|
3539
|
-
//# sourceMappingURL=chunk-
|
|
3539
|
+
//# sourceMappingURL=chunk-HKSMTZWU.js.map
|
|
@@ -811,7 +811,7 @@ var FlowStorage = class {
|
|
|
811
811
|
*/
|
|
812
812
|
getResolvedCallersWithLocation(nodeId) {
|
|
813
813
|
const stmt = this.db.prepare(`
|
|
814
|
-
SELECT n.name, n.file_path as file, n.start_line as line
|
|
814
|
+
SELECT n.id, n.name, n.file_path as file, n.start_line as line
|
|
815
815
|
FROM edges e
|
|
816
816
|
JOIN nodes n ON e.source_id = n.id
|
|
817
817
|
WHERE e.target_id = ? AND e.type = 'calls' AND e.target_id NOT LIKE 'ref:%'
|
|
@@ -833,4 +833,4 @@ var FlowStorage = class {
|
|
|
833
833
|
export {
|
|
834
834
|
FlowStorage
|
|
835
835
|
};
|
|
836
|
-
//# sourceMappingURL=chunk-
|
|
836
|
+
//# sourceMappingURL=chunk-YEC7G3GL.js.map
|
|
@@ -1 +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
|
+
{"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<{ id: string; name: string; file: string; line: number }> {\n const stmt = this.db.prepare(`\n SELECT n.id, 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<{ id: string; 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":";;;;AAKA,OAAO,cAAc;;;ACKd,SAAS,mBAAmB,IAAoB;AAErD,KAAG,KAAK;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,GAYP;AAGD,KAAG,KAAK;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,GAUP;AAGD,KAAG,KAAK;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,GAUP;AAGD,KAAG,KAAK;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,GAeP;AACH;;;AD9CO,IAAM,cAAN,MAAkB;AAAA,EAChB;AAAA;AAAA,EAEP,YAAY,QAAgB;AAC1B,SAAK,KAAK,IAAI,SAAS,MAAM;AAC7B,SAAK,WAAW;AAAA,EAClB;AAAA,EAEQ,aAAmB;AACzB,SAAK,GAAG,KAAK;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,KAiJZ;AAGD,uBAAmB,KAAK,EAAE;AAAA,EAC5B;AAAA;AAAA,EAIA,WAAW,MAA2C;AACpD,UAAM,OAAO,KAAK,GAAG,QAAQ;AAAA;AAAA;AAAA,KAG5B;AACD,SAAK;AAAA,MACH,KAAK;AAAA,MACL,KAAK;AAAA,MACL,KAAK;AAAA,MACL,KAAK;AAAA,MACL,KAAK;AAAA,MACL,KAAK;AAAA,MACL,KAAK;AAAA,MACL,KAAK,QAAQ;AAAA,MACb,KAAK,WAAW,KAAK,UAAU,KAAK,QAAQ,IAAI;AAAA,IAClD;AAAA,EACF;AAAA,EAEA,QAAQ,IAA8B;AACpC,UAAM,OAAO,KAAK,GAAG,QAAQ,kCAAkC;AAC/D,UAAM,MAAM,KAAK,IAAI,EAAE;AACvB,QAAI,CAAC,IAAK,QAAO;AACjB,WAAO,KAAK,UAAU,GAAG;AAAA,EAC3B;AAAA,EAEA,kBAAkB,MAA2B;AAC3C,UAAM,OAAO,KAAK,GAAG,QAAQ,mDAAmD;AAChF,UAAM,OAAO,KAAK,IAAI,MAAM,IAAI,IAAI,GAAG;AACvC,WAAO,KAAK,IAAI,CAAC,MAAM,KAAK,UAAU,CAAC,CAAC;AAAA,EAC1C;AAAA,EAEA,eAAe,UAA+B;AAC5C,UAAM,OAAO,KAAK,GAAG,QAAQ,yCAAyC;AACtE,UAAM,OAAO,KAAK,IAAI,QAAQ;AAC9B,WAAO,KAAK,IAAI,CAAC,MAAM,KAAK,UAAU,CAAC,CAAC;AAAA,EAC1C;AAAA,EAEQ,UAAU,KAAyC;AACzD,WAAO;AAAA,MACL,IAAI,IAAI;AAAA,MACR,MAAM,IAAI;AAAA,MACV,MAAM,IAAI;AAAA,MACV,UAAU,IAAI;AAAA,MACd,WAAW,IAAI;AAAA,MACf,SAAS,IAAI;AAAA,MACb,UAAU,IAAI;AAAA,MACd,UAAU,IAAI,WAAW,KAAK,MAAM,IAAI,QAAkB,IAAI;AAAA,IAChE;AAAA,EACF;AAAA;AAAA,EAIA,WAAW,MAAuB;AAChC,UAAM,OAAO,KAAK,GAAG,QAAQ;AAAA;AAAA;AAAA,KAG5B;AACD,SAAK;AAAA,MACH,KAAK;AAAA,MACL,KAAK;AAAA,MACL,KAAK;AAAA,MACL,KAAK;AAAA,MACL,KAAK,WAAW,KAAK,UAAU,KAAK,QAAQ,IAAI;AAAA,IAClD;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,mBAAmB,QAA6B;AAC9C,UAAM,OAAO,KAAK,GAAG,QAAQ;AAAA;AAAA;AAAA;AAAA,KAI5B;AACD,UAAM,OAAO,KAAK,IAAI,MAAM;AAC5B,WAAO,KAAK,IAAI,CAAC,MAAM,KAAK,UAAU,CAAC,CAAC;AAAA,EAC1C;AAAA;AAAA;AAAA;AAAA,EAKA,mBAAmB,QAA6B;AAC9C,UAAM,OAAO,KAAK,GAAG,QAAQ;AAAA;AAAA;AAAA;AAAA,KAI5B;AACD,UAAM,OAAO,KAAK,IAAI,MAAM;AAC5B,WAAO,KAAK,IAAI,CAAC,MAAM,KAAK,UAAU,CAAC,CAAC;AAAA,EAC1C;AAAA;AAAA;AAAA;AAAA,EAKA,iBAAiB,QAAgB,WAAmB,GAAgB;AAClE,UAAM,UAAU,oBAAI,IAAY;AAChC,UAAM,SAAsB,CAAC;AAC7B,UAAM,QAA8C,CAAC,EAAE,IAAI,QAAQ,OAAO,EAAE,CAAC;AAE7E,WAAO,MAAM,SAAS,GAAG;AACvB,YAAM,EAAE,IAAI,MAAM,IAAI,MAAM,MAAM;AAElC,UAAI,QAAQ,IAAI,EAAE,KAAK,QAAQ,SAAU;AACzC,cAAQ,IAAI,EAAE;AAEd,YAAM,UAAU,KAAK,mBAAmB,EAAE;AAC1C,iBAAW,UAAU,SAAS;AAC5B,YAAI,CAAC,QAAQ,IAAI,OAAO,EAAE,GAAG;AAC3B,iBAAO,KAAK,MAAM;AAClB,gBAAM,KAAK,EAAE,IAAI,OAAO,IAAI,OAAO,QAAQ,EAAE,CAAC;AAAA,QAChD;AAAA,MACF;AAAA,IACF;AAEA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA,EAKA,cAAc,QAAgB,MAA+B;AAC3D,UAAM,UAAU,oBAAI,IAAY;AAChC,UAAM,SAAS,oBAAI,IAAkD;AACrE,UAAM,QAAkB,CAAC,MAAM;AAE/B,WAAO,MAAM,SAAS,GAAG;AACvB,YAAM,UAAU,MAAM,MAAM;AAE5B,UAAI,YAAY,MAAM;AAEpB,cAAM,OAAiB,CAAC;AACxB,YAAI,OAAO;AACX,eAAO,SAAS,QAAQ;AACtB,eAAK,QAAQ,IAAI;AACjB,iBAAO,OAAO,IAAI,IAAI,EAAG;AAAA,QAC3B;AACA,aAAK,QAAQ,MAAM;AAGnB,cAAM,QAAQ,KAAK,IAAI,CAAC,OAAO;AAC7B,gBAAM,IAAI,KAAK,QAAQ,EAAE;AACzB,iBAAO;AAAA,YACL,IAAI,EAAG;AAAA,YACP,MAAM,EAAG;AAAA,YACT,MAAM,EAAG;AAAA,YACT,MAAM,EAAG;AAAA,UACX;AAAA,QACF,CAAC;AAED,cAAM,QAAQ,CAAC;AACf,iBAAS,IAAI,GAAG,IAAI,KAAK,SAAS,GAAG,KAAK;AACxC,gBAAM,KAAK;AAAA,YACT,MAAM,KAAK,CAAC;AAAA,YACZ,IAAI,KAAK,IAAI,CAAC;AAAA,YACd,MAAM;AAAA,UACR,CAAC;AAAA,QACH;AAEA,eAAO,EAAE,OAAO,MAAM;AAAA,MACxB;AAEA,UAAI,QAAQ,IAAI,OAAO,EAAG;AAC1B,cAAQ,IAAI,OAAO;AAEnB,YAAM,UAAU,KAAK,mBAAmB,OAAO;AAC/C,iBAAW,UAAU,SAAS;AAC5B,YAAI,CAAC,QAAQ,IAAI,OAAO,EAAE,GAAG;AAC3B,iBAAO,IAAI,OAAO,IAAI,EAAE,QAAQ,SAAS,UAAU,QAAQ,CAAC;AAC5D,gBAAM,KAAK,OAAO,EAAE;AAAA,QACtB;AAAA,MACF;AAAA,IACF;AAEA,WAAO;AAAA,EACT;AAAA;AAAA,EAIA,eAAe,UAAkB,YAAoB,QAAsB;AACzE,UAAM,OAAO,KAAK,GAAG,QAAQ;AAAA;AAAA;AAAA,KAG5B;AACD,SAAK,IAAI,UAAU,YAAY,MAAM;AAAA,EACvC;AAAA,EAEA,eAAe,UAAkB,QAAgB,YAAoB,MAAoB;AACvF,UAAM,OAAO,KAAK,GAAG,QAAQ;AAAA;AAAA;AAAA,KAG5B;AACD,SAAK,IAAI,UAAU,QAAQ,YAAY,IAAI;AAAA,EAC7C;AAAA,EAEA,cAAc,UAAkB,QAA+B;AAE7D,UAAM,aAAa,KAAK,GAAG,QAAQ;AAAA;AAAA,KAElC,EAAE,IAAI,UAAU,MAAM;AAEvB,QAAI,CAAC,WAAY,QAAO;AAMxB,UAAM,aAAa,WAAW;AAC9B,UAAM,gBAAgB,KAAK,wBAAwB,UAAU,UAAU;AAGvE,eAAW,cAAc,eAAe;AACtC,YAAM,aAAa,KAAK,GAAG,QAAQ;AAAA;AAAA,OAElC,EAAE,IAAI,YAAY,MAAM;AAEzB,UAAI,YAAY;AACd,eAAO,WAAW;AAAA,MACpB;AAAA,IACF;AAEA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA,EAMQ,wBAAwB,aAAqB,YAA8B;AACjF,UAAM,gBAA0B,CAAC;AAGjC,QAAI,WAAW,WAAW,GAAG,GAAG;AAC9B,YAAM,aAAa,YAAY,UAAU,GAAG,YAAY,YAAY,GAAG,CAAC;AACxE,YAAM,QAAQ,WAAW,MAAM,MAAM,IAAI,CAAC,EAAE,UAAU;AACtD,YAAM,aAAa,WAAW,QAAQ,QAAQ,EAAE;AAGhD,UAAI,YAAY;AAChB,eAAS,IAAI,GAAG,IAAI,OAAO,KAAK;AAC9B,cAAM,YAAY,UAAU,YAAY,GAAG;AAC3C,YAAI,YAAY,GAAG;AACjB,sBAAY,UAAU,UAAU,GAAG,SAAS;AAAA,QAC9C;AAAA,MACF;AAEA,UAAI,YAAY;AACd,cAAM,eAAe,WAAW,QAAQ,OAAO,GAAG;AAClD,sBAAc,KAAK,GAAG,SAAS,IAAI,YAAY,KAAK;AACpD,sBAAc,KAAK,GAAG,SAAS,IAAI,YAAY,cAAc;AAAA,MAC/D,OAAO;AACL,sBAAc,KAAK,GAAG,SAAS,cAAc;AAAA,MAC/C;AAAA,IACF,OAAO;AAEL,YAAM,iBAAiB,WAAW,MAAM,GAAG;AAI3C,YAAM,WAAW,eAAe,KAAK,GAAG;AACxC,oBAAc,KAAK,GAAG,QAAQ,KAAK;AACnC,oBAAc,KAAK,GAAG,QAAQ,cAAc;AAG5C,oBAAc,KAAK,OAAO,QAAQ,KAAK;AACvC,oBAAc,KAAK,OAAO,QAAQ,cAAc;AAIhD,YAAM,gBAAgB,eAAe,eAAe,SAAS,CAAC;AAC9D,YAAM,gBAAgB,KAAK,GAAG,QAAQ;AAAA;AAAA;AAAA,OAGrC,EAAE,IAAI,KAAK,aAAa,KAAK;AAE9B,iBAAW,QAAQ,eAAe;AAEhC,cAAM,qBAAqB,KAAK,UAAU,QAAQ,OAAO,GAAG;AAC5D,YAAI,mBAAmB,SAAS,QAAQ,GAAG;AACzC,wBAAc,KAAK,kBAAkB;AAAA,QACvC;AAAA,MACF;AAAA,IACF;AAEA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,mBAAmB,WAAmB,YAAoB,gBAAuC;AAE/F,UAAM,cAAc,KAAK,cAAc,gBAAgB,SAAS;AAEhE,QAAI,aAAa;AAEf,YAAM,UAAU,KAAK,GAAG,QAAQ;AAAA;AAAA;AAAA,OAG/B,EAAE,IAAI,WAAW;AAElB,iBAAW,QAAQ,SAAS;AAC1B,cAAM,aAAa,KAAK,QAAQ,KAAK,SAAS;AAC9C,YAAI,cAAc,WAAW,SAAS,YAAY;AAChD,iBAAO,WAAW;AAAA,QACpB;AAAA,MACF;AAAA,IACF;AAGA,UAAM,YAAY,KAAK,GAAG,QAAQ;AAAA;AAAA;AAAA,KAGjC,EAAE,IAAI,gBAAgB,SAAS;AAEhC,QAAI,WAAW;AAEb,YAAM,aAAa,KAAK,GAAG,QAAQ;AAAA;AAAA;AAAA;AAAA,OAIlC,EAAE,IAAI,UAAU,IAAI,UAAU;AAE/B,UAAI,YAAY;AACd,eAAO,WAAW;AAAA,MACpB;AAAA,IACF;AAGA,UAAM,aAAa,KAAK,GAAG,QAAQ;AAAA;AAAA,KAElC,EAAE,IAAI,SAAS;AAEhB,eAAW,OAAO,YAAY;AAC5B,YAAM,aAAa,KAAK,GAAG,QAAQ;AAAA;AAAA;AAAA;AAAA,OAIlC,EAAE,IAAI,IAAI,IAAI,UAAU;AAEzB,UAAI,YAAY;AACd,eAAO,WAAW;AAAA,MACpB;AAAA,IACF;AAEA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,qBACE,cACA,UACA,aACA,UACA,MACA,cAAuB,OACjB;AACN,UAAM,KAAK,GAAG,WAAW,QAAQ,YAAY;AAC7C,UAAM,OAAO,KAAK,GAAG,QAAQ;AAAA;AAAA;AAAA;AAAA,KAI5B;AACD,SAAK,IAAI,IAAI,cAAc,UAAU,aAAa,UAAU,MAAM,cAAc,IAAI,CAAC;AAAA,EACvF;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,sBAAsB,cAAsB,YAAoB,aAAoC;AAElG,QAAI,iBAAiB,QAAQ;AAC3B,aAAO,KAAK,kBAAkB,YAAY,WAAW;AAAA,IACvD;AAGA,UAAM,UAAU,KAAK,GAAG,QAAQ;AAAA;AAAA;AAAA,KAG/B,EAAE,IAAI,aAAa,YAAY;AAEhC,QAAI,CAAC,SAAS;AACZ,aAAO;AAAA,IACT;AAGA,WAAO,KAAK,mBAAmB,QAAQ,WAAW,YAAY,EAAE;AAAA,EAClE;AAAA;AAAA;AAAA;AAAA,EAKQ,kBAAkB,YAAoB,aAAoC;AAEhF,UAAM,cAAc,KAAK,GAAG,QAAQ;AAAA;AAAA;AAAA,KAGnC,EAAE,IAAI,WAAW;AAElB,QAAI,CAAC,aAAa;AAChB,aAAO;AAAA,IACT;AAGA,UAAM,SAAS,KAAK,GAAG,QAAQ;AAAA;AAAA;AAAA;AAAA,KAI9B,EAAE,IAAI,YAAY,WAAW,UAAU;AAExC,WAAO,QAAQ,MAAM;AAAA,EACvB;AAAA;AAAA;AAAA;AAAA,EAKA,mBAAmB,YAAoB,aAAoC;AAEzE,UAAM,cAAc,KAAK,GAAG,QAAQ;AAAA;AAAA;AAAA,KAGnC,EAAE,IAAI,WAAW;AAElB,QAAI,CAAC,aAAa;AAChB,aAAO;AAAA,IACT;AAGA,UAAM,YAAY,KAAK,QAAQ,YAAY,SAAS;AACpD,QAAI,CAAC,aAAa,UAAU,SAAS,SAAS;AAC5C,aAAO;AAAA,IACT;AAGA,UAAM,YAAY,KAAK,GAAG,QAAQ;AAAA;AAAA;AAAA,KAGjC,EAAE,IAAI,UAAU,EAAE;AAEnB,QAAI,CAAC,WAAW;AACd,aAAO;AAAA,IACT;AAGA,QAAI,cAAc,UAAU;AAC5B,QAAI,YAAY,WAAW,MAAM,GAAG;AAClC,YAAM,gBAAgB,YAAY,QAAQ,QAAQ,EAAE;AAEpD,YAAM,gBAAgB,KAAK,GAAG,QAAQ;AAAA;AAAA,OAErC,EAAE,IAAI,aAAa;AAEpB,UAAI,CAAC,eAAe;AAClB,eAAO;AAAA,MACT;AACA,oBAAc,cAAc;AAAA,IAC9B;AAGA,UAAM,SAAS,KAAK,GAAG,QAAQ;AAAA;AAAA;AAAA;AAAA,KAI9B,EAAE,IAAI,aAAa,UAAU;AAE9B,WAAO,QAAQ,MAAM;AAAA,EACvB;AAAA;AAAA,EAIA,WAAW,UAAkB,UAAkB,MAAoB;AACjE,UAAM,OAAO,KAAK,GAAG,QAAQ;AAAA;AAAA;AAAA,KAG5B;AACD,SAAK,IAAI,UAAU,UAAU,OAAM,oBAAI,KAAK,GAAE,YAAY,CAAC;AAAA,EAC7D;AAAA,EAEA,YAAY,UAAiC;AAC3C,UAAM,OAAO,KAAK,GAAG,QAAQ,uCAAuC;AACpE,UAAM,MAAM,KAAK,IAAI,QAAQ;AAC7B,WAAO,KAAK,QAAQ;AAAA,EACtB;AAAA,EAEA,eAAe,UAAwB;AACrC,SAAK,GAAG,QAAQ,uCAAuC,EAAE,IAAI,QAAQ;AACrE,SAAK,GAAG,QAAQ,yCAAyC,EAAE,IAAI,QAAQ;AACvE,SAAK,GAAG,QAAQ,yCAAyC,EAAE,IAAI,QAAQ;AACvE,SAAK,GAAG,QAAQ,kCAAkC,EAAE,IAAI,QAAQ;AAAA,EAClE;AAAA;AAAA,EAIA,WAUE;AACA,UAAM,aAAc,KAAK,GAAG,QAAQ,qCAAqC,EAAE,IAAI,EAAwB;AACvG,UAAM,aAAc,KAAK,GAAG,QAAQ,qCAAqC,EAAE,IAAI,EAAwB;AACvG,UAAM,aAAc,KAAK,GAAG,QAAQ,0DAA0D,EAAE,IAAI,EAAwB;AAC5H,UAAM,gBAAiB,KAAK,GAAG,QAAQ,yFAAyF,EAAE,IAAI,EAAwB;AAC9J,UAAM,kBAAkB,aAAa;AACrC,UAAM,gBAAiB,KAAK,GAAG,QAAQ,8CAA8C,EAAE,IAAI,EAAwB;AACnH,UAAM,wBAAyB,KAAK,GAAG,QAAQ,sDAAsD,EAAE,IAAI,EAAwB;AACnI,UAAM,uBAAwB,KAAK,GAAG,QAAQ,uFAAuF,EAAE,IAAI,EAAwB;AACnK,UAAM,aAAc,KAAK,GAAG,QAAQ,0CAA0C,EAAE,IAAI,EAAwB;AAE5G,WAAO;AAAA,MACL;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,EACF;AAAA;AAAA,EAIA,gBAAgB,WASP;AACP,UAAM,OAAO,KAAK,GAAG,QAAQ;AAAA;AAAA;AAAA,KAG5B;AACD,SAAK;AAAA,MACH,UAAU;AAAA,MACV,UAAU;AAAA,MACV,UAAU,SAAS;AAAA,MACnB,UAAU,QAAQ,KAAK,UAAU,UAAU,KAAK,IAAI;AAAA,MACpD,UAAU,cAAc,KAAK,UAAU,UAAU,WAAW,IAAI;AAAA,MAChE,UAAU,aAAa,KAAK,UAAU,UAAU,UAAU,IAAI;AAAA,MAC9D,UAAU,WAAW,KAAK,UAAU,UAAU,QAAQ,IAAI;AAAA,MAC1D,UAAU,WAAW,KAAK,UAAU,UAAU,QAAQ,IAAI;AAAA,IAC5D;AAAA,EACF;AAAA,EAEA,aAAa,IAAwB;AACnC,UAAM,OAAO,KAAK,GAAG,QAAQ,uCAAuC;AACpE,UAAM,MAAM,KAAK,IAAI,EAAE;AACvB,QAAI,CAAC,IAAK,QAAO;AACjB,WAAO;AAAA,MACL,IAAI,IAAI;AAAA,MACR,MAAM,IAAI;AAAA,MACV,OAAO,IAAI;AAAA,MACX,OAAO,IAAI,QAAQ,KAAK,MAAM,IAAI,KAAe,IAAI,CAAC;AAAA,MACtD,aAAa,IAAI,cAAc,KAAK,MAAM,IAAI,WAAqB,IAAI,CAAC;AAAA,MACxE,YAAY,IAAI,aAAa,KAAK,MAAM,IAAI,UAAoB,IAAI,CAAC;AAAA,MACrE,UAAU,IAAI,WAAW,KAAK,MAAM,IAAI,QAAkB,IAAI,CAAC;AAAA,MAC/D,UAAU,IAAI,WAAW,KAAK,MAAM,IAAI,QAAkB,IAAI,CAAC;AAAA,IACjE;AAAA,EACF;AAAA,EAEA,mBAA0B;AACxB,UAAM,OAAO,KAAK,GAAG,QAAQ,0BAA0B;AACvD,UAAM,OAAO,KAAK,IAAI;AACtB,WAAO,KAAK,IAAI,UAAQ;AAAA,MACtB,IAAI,IAAI;AAAA,MACR,MAAM,IAAI;AAAA,MACV,OAAO,IAAI;AAAA,MACX,OAAO,IAAI,QAAQ,KAAK,MAAM,IAAI,KAAe,IAAI,CAAC;AAAA,MACtD,aAAa,IAAI,cAAc,KAAK,MAAM,IAAI,WAAqB,IAAI,CAAC;AAAA,MACxE,YAAY,IAAI,aAAa,KAAK,MAAM,IAAI,UAAoB,IAAI,CAAC;AAAA,MACrE,UAAU,IAAI,WAAW,KAAK,MAAM,IAAI,QAAkB,IAAI,CAAC;AAAA,MAC/D,UAAU,IAAI,WAAW,KAAK,MAAM,IAAI,QAAkB,IAAI,CAAC;AAAA,IACjE,EAAE;AAAA,EACJ;AAAA;AAAA,EAIA,mBAAmB,UAQV;AACP,UAAM,OAAO,KAAK,GAAG,QAAQ;AAAA;AAAA;AAAA,KAG5B;AACD,SAAK;AAAA,MACH,SAAS;AAAA,MACT,SAAS;AAAA,MACT,SAAS;AAAA,MACT,SAAS,mBAAmB;AAAA,MAC5B,SAAS,gBAAgB;AAAA,MACzB,SAAS,aAAa,KAAK,UAAU,SAAS,UAAU,IAAI;AAAA,MAC5D,SAAS,WAAW,KAAK,UAAU,SAAS,QAAQ,IAAI;AAAA,IAC1D;AAAA,EACF;AAAA,EAEA,wBAAwB,MAQf;AACP,UAAM,OAAO,KAAK,GAAG,QAAQ;AAAA;AAAA;AAAA,KAG5B;AACD,SAAK;AAAA,MACH,KAAK;AAAA,MACL,KAAK;AAAA,MACL,KAAK;AAAA,MACL,KAAK;AAAA,MACL,KAAK,cAAc;AAAA,MACnB,KAAK,QAAQ;AAAA,MACb,KAAK,WAAW,KAAK,UAAU,KAAK,QAAQ,IAAI;AAAA,IAClD;AAAA,EACF;AAAA,EAEA,iBAAiB,QAAgB,MAA0B;AACzD,UAAM,OAAO,KAAK,GAAG,QAAQ,4DAA4D;AACzF,UAAM,MAAM,KAAK,IAAI,QAAQ,IAAI;AACjC,QAAI,CAAC,IAAK,QAAO;AACjB,WAAO;AAAA,MACL,IAAI,IAAI;AAAA,MACR,QAAQ,IAAI;AAAA,MACZ,MAAM,IAAI;AAAA,MACV,iBAAiB,IAAI;AAAA,MACrB,cAAc,IAAI;AAAA,MAClB,YAAY,IAAI,aAAa,KAAK,MAAM,IAAI,UAAoB,IAAI,CAAC;AAAA,MACrE,UAAU,IAAI,WAAW,KAAK,MAAM,IAAI,QAAkB,IAAI,CAAC;AAAA,IACjE;AAAA,EACF;AAAA;AAAA,EAIA,0BAA0B,KAUjB;AACP,UAAM,OAAO,KAAK,GAAG,QAAQ;AAAA;AAAA;AAAA;AAAA,KAI5B;AACD,SAAK;AAAA,MACH,IAAI;AAAA,MACJ,IAAI;AAAA,MACJ,IAAI,kBAAkB;AAAA,MACtB,IAAI;AAAA,MACJ,IAAI;AAAA,MACJ,IAAI,kBAAkB;AAAA,MACtB,IAAI,QAAQ;AAAA,MACZ,IAAI,mBAAmB;AAAA,MACvB,IAAI,WAAW,KAAK,UAAU,IAAI,QAAQ,IAAI;AAAA,IAChD;AAAA,EACF;AAAA,EAEA,yBAAyB,cAA6B;AACpD,UAAM,OAAO,KAAK,GAAG,QAAQ,+DAA+D;AAC5F,UAAM,OAAO,KAAK,IAAI,YAAY;AAClC,WAAO,KAAK,IAAI,UAAQ;AAAA,MACtB,IAAI,IAAI;AAAA,MACR,gBAAgB,IAAI;AAAA,MACpB,gBAAgB,IAAI;AAAA,MACpB,WAAW,IAAI;AAAA,MACf,SAAS,IAAI;AAAA,MACb,gBAAgB,IAAI;AAAA,MACpB,MAAM,IAAI;AAAA,MACV,iBAAiB,IAAI;AAAA,MACrB,UAAU,IAAI,WAAW,KAAK,MAAM,IAAI,QAAkB,IAAI,CAAC;AAAA,IACjE,EAAE;AAAA,EACJ;AAAA,EAEA,+BAAsC;AACpC,UAAM,OAAO,KAAK,GAAG,QAAQ,mEAAmE;AAChG,UAAM,OAAO,KAAK,IAAI;AACtB,WAAO,KAAK,IAAI,UAAQ;AAAA,MACtB,IAAI,IAAI;AAAA,MACR,gBAAgB,IAAI;AAAA,MACpB,WAAW,IAAI;AAAA,MACf,SAAS,IAAI;AAAA,MACb,iBAAiB,IAAI;AAAA,MACrB,MAAM,IAAI;AAAA,IACZ,EAAE;AAAA,EACJ;AAAA,EAEA,gCAAgC,IAAY,cAA4B;AACtE,UAAM,OAAO,KAAK,GAAG,QAAQ,mEAAmE;AAChG,SAAK,IAAI,cAAc,EAAE;AAAA,EAC3B;AAAA,EAEA,QAAc;AACZ,SAAK,GAAG,KAAK;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,KAYZ;AAAA,EACH;AAAA;AAAA;AAAA;AAAA,EAKA,mBAA2C;AACzC,UAAM,OAAO,KAAK,GAAG,QAAQ,qDAAqD;AAClF,UAAM,OAAO,KAAK,IAAI;AACtB,UAAM,SAAiC,CAAC;AACxC,eAAW,OAAO,MAAM;AACtB,aAAO,IAAI,IAAI,IAAI,IAAI;AAAA,IACzB;AACA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA,EAKA,cAAmF;AACjF,UAAM,OAAO,KAAK,GAAG,QAAQ,6CAA6C;AAC1E,WAAO,KAAK,IAAI;AAAA,EAClB;AAAA;AAAA;AAAA;AAAA,EAKA,cAAyF;AACvF,UAAM,OAAO,KAAK,GAAG,QAAQ,kDAAkD;AAC/E,WAAO,KAAK,IAAI;AAAA,EAClB;AAAA;AAAA;AAAA;AAAA,EAKA,+BAA+B,QAAiF;AAC9G,UAAM,OAAO,KAAK,GAAG,QAAQ;AAAA;AAAA;AAAA;AAAA;AAAA,KAK5B;AACD,WAAO,KAAK,IAAI,MAAM;AAAA,EACxB;AAAA;AAAA;AAAA;AAAA,EAKA,0BAA0B,eAAgE;AACxF,UAAM,OAAO,KAAK,GAAG,QAAQ,mEAAmE;AAChG,WAAO,KAAK,IAAI,aAAa;AAAA,EAC/B;AAAA,EAEA,QAAc;AACZ,SAAK,GAAG,MAAM;AAAA,EAChB;AACF;","names":[]}
|