codemap-ai 0.2.0 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,730 @@
1
+ // src/flow/storage.ts
2
+ import Database from "better-sqlite3";
3
+ var FlowStorage = class {
4
+ db;
5
+ // Public for builder to access
6
+ constructor(dbPath) {
7
+ this.db = new Database(dbPath);
8
+ this.initSchema();
9
+ }
10
+ initSchema() {
11
+ this.db.exec(`
12
+ -- Nodes table (enhanced with hash)
13
+ CREATE TABLE IF NOT EXISTS nodes (
14
+ id TEXT PRIMARY KEY,
15
+ type TEXT NOT NULL,
16
+ name TEXT NOT NULL,
17
+ file_path TEXT NOT NULL,
18
+ start_line INTEGER NOT NULL,
19
+ end_line INTEGER NOT NULL,
20
+ language TEXT NOT NULL,
21
+ hash TEXT,
22
+ metadata TEXT
23
+ );
24
+
25
+ -- Edges table (calls and related edges)
26
+ CREATE TABLE IF NOT EXISTS edges (
27
+ id TEXT PRIMARY KEY,
28
+ type TEXT NOT NULL,
29
+ source_id TEXT NOT NULL,
30
+ target_id TEXT NOT NULL,
31
+ metadata TEXT
32
+ );
33
+
34
+ -- Files table
35
+ CREATE TABLE IF NOT EXISTS files (
36
+ path TEXT PRIMARY KEY,
37
+ language TEXT NOT NULL,
38
+ hash TEXT NOT NULL,
39
+ analyzed_at TEXT NOT NULL
40
+ );
41
+
42
+ -- Exports: symbols exported by each file
43
+ CREATE TABLE IF NOT EXISTS exports (
44
+ file_path TEXT NOT NULL,
45
+ symbol_name TEXT NOT NULL,
46
+ node_id TEXT NOT NULL,
47
+ PRIMARY KEY (file_path, symbol_name)
48
+ );
49
+
50
+ -- Imports: what each file imports and from where
51
+ CREATE TABLE IF NOT EXISTS imports (
52
+ file_path TEXT NOT NULL,
53
+ imported_symbol TEXT NOT NULL,
54
+ from_module TEXT NOT NULL,
55
+ resolved_node_id TEXT,
56
+ line INTEGER NOT NULL
57
+ );
58
+
59
+ -- Containers (Docker services)
60
+ CREATE TABLE IF NOT EXISTS containers (
61
+ id TEXT PRIMARY KEY,
62
+ name TEXT NOT NULL,
63
+ image TEXT,
64
+ ports TEXT,
65
+ environment TEXT,
66
+ depends_on TEXT,
67
+ networks TEXT,
68
+ metadata TEXT
69
+ );
70
+
71
+ -- HTTP Endpoints (API routes)
72
+ CREATE TABLE IF NOT EXISTS http_endpoints (
73
+ id TEXT PRIMARY KEY,
74
+ method TEXT NOT NULL,
75
+ path TEXT NOT NULL,
76
+ handler_node_id TEXT,
77
+ container_id TEXT,
78
+ middleware TEXT,
79
+ metadata TEXT
80
+ );
81
+
82
+ -- HTTP Calls (client requests)
83
+ CREATE TABLE IF NOT EXISTS http_calls (
84
+ id TEXT PRIMARY KEY,
85
+ source_node_id TEXT NOT NULL,
86
+ target_endpoint_id TEXT,
87
+ url TEXT NOT NULL,
88
+ method TEXT NOT NULL,
89
+ line INTEGER,
90
+ metadata TEXT
91
+ );
92
+
93
+ -- Framework Dependencies (FastAPI Depends, etc.)
94
+ CREATE TABLE IF NOT EXISTS framework_dependencies (
95
+ id TEXT PRIMARY KEY,
96
+ source_node_id TEXT NOT NULL,
97
+ target_node_id TEXT,
98
+ framework TEXT NOT NULL,
99
+ pattern TEXT NOT NULL,
100
+ parameter_name TEXT,
101
+ line INTEGER,
102
+ unresolved_name TEXT,
103
+ metadata TEXT
104
+ );
105
+
106
+ -- Database Operations
107
+ CREATE TABLE IF NOT EXISTS database_operations (
108
+ id TEXT PRIMARY KEY,
109
+ node_id TEXT NOT NULL,
110
+ database_type TEXT NOT NULL,
111
+ operation TEXT NOT NULL,
112
+ collection TEXT,
113
+ line INTEGER,
114
+ metadata TEXT
115
+ );
116
+
117
+ -- Message Queue Operations
118
+ CREATE TABLE IF NOT EXISTS message_queue_operations (
119
+ id TEXT PRIMARY KEY,
120
+ node_id TEXT NOT NULL,
121
+ operation TEXT NOT NULL,
122
+ exchange TEXT,
123
+ routing_key TEXT,
124
+ queue TEXT,
125
+ line INTEGER,
126
+ metadata TEXT
127
+ );
128
+
129
+ -- Variable Type Tracking
130
+ CREATE TABLE IF NOT EXISTS variable_types (
131
+ id TEXT PRIMARY KEY,
132
+ variable_name TEXT NOT NULL,
133
+ type_name TEXT NOT NULL,
134
+ scope_node_id TEXT NOT NULL,
135
+ file_path TEXT NOT NULL,
136
+ line INTEGER NOT NULL,
137
+ is_parameter BOOLEAN DEFAULT 0,
138
+ metadata TEXT
139
+ );
140
+
141
+ -- Indexes for fast queries
142
+ CREATE INDEX IF NOT EXISTS idx_nodes_file ON nodes(file_path);
143
+ CREATE INDEX IF NOT EXISTS idx_nodes_name ON nodes(name);
144
+ CREATE INDEX IF NOT EXISTS idx_nodes_hash ON nodes(hash);
145
+ CREATE INDEX IF NOT EXISTS idx_edges_source ON edges(source_id);
146
+ CREATE INDEX IF NOT EXISTS idx_edges_target ON edges(target_id);
147
+ CREATE INDEX IF NOT EXISTS idx_edges_resolved ON edges(target_id) WHERE target_id NOT LIKE 'ref:%';
148
+ CREATE INDEX IF NOT EXISTS idx_exports_file ON exports(file_path);
149
+ CREATE INDEX IF NOT EXISTS idx_imports_file ON imports(file_path);
150
+ CREATE INDEX IF NOT EXISTS idx_http_endpoints_path ON http_endpoints(method, path);
151
+ CREATE INDEX IF NOT EXISTS idx_http_calls_url ON http_calls(url);
152
+ CREATE INDEX IF NOT EXISTS idx_framework_deps_source ON framework_dependencies(source_node_id);
153
+ CREATE INDEX IF NOT EXISTS idx_framework_deps_target ON framework_dependencies(target_node_id);
154
+ CREATE INDEX IF NOT EXISTS idx_variable_types_scope ON variable_types(scope_node_id, variable_name);
155
+ CREATE INDEX IF NOT EXISTS idx_variable_types_file ON variable_types(file_path, variable_name);
156
+ `);
157
+ }
158
+ // ============ Node Operations ============
159
+ insertNode(node) {
160
+ const stmt = this.db.prepare(`
161
+ INSERT OR REPLACE INTO nodes (id, type, name, file_path, start_line, end_line, language, hash, metadata)
162
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
163
+ `);
164
+ stmt.run(
165
+ node.id,
166
+ node.type,
167
+ node.name,
168
+ node.filePath,
169
+ node.startLine,
170
+ node.endLine,
171
+ node.language,
172
+ node.hash || null,
173
+ node.metadata ? JSON.stringify(node.metadata) : null
174
+ );
175
+ }
176
+ getNode(id) {
177
+ const stmt = this.db.prepare("SELECT * FROM nodes WHERE id = ?");
178
+ const row = stmt.get(id);
179
+ if (!row) return null;
180
+ return this.rowToNode(row);
181
+ }
182
+ searchNodesByName(name) {
183
+ const stmt = this.db.prepare("SELECT * FROM nodes WHERE name = ? OR name LIKE ?");
184
+ const rows = stmt.all(name, `%${name}%`);
185
+ return rows.map((r) => this.rowToNode(r));
186
+ }
187
+ getNodesByFile(filePath) {
188
+ const stmt = this.db.prepare("SELECT * FROM nodes WHERE file_path = ?");
189
+ const rows = stmt.all(filePath);
190
+ return rows.map((r) => this.rowToNode(r));
191
+ }
192
+ rowToNode(row) {
193
+ return {
194
+ id: row.id,
195
+ type: row.type,
196
+ name: row.name,
197
+ filePath: row.file_path,
198
+ startLine: row.start_line,
199
+ endLine: row.end_line,
200
+ language: row.language,
201
+ metadata: row.metadata ? JSON.parse(row.metadata) : void 0
202
+ };
203
+ }
204
+ // ============ Edge Operations ============
205
+ insertEdge(edge) {
206
+ const stmt = this.db.prepare(`
207
+ INSERT OR REPLACE INTO edges (id, type, source_id, target_id, metadata)
208
+ VALUES (?, ?, ?, ?, ?)
209
+ `);
210
+ stmt.run(
211
+ edge.id,
212
+ edge.type,
213
+ edge.sourceId,
214
+ edge.targetId,
215
+ edge.metadata ? JSON.stringify(edge.metadata) : null
216
+ );
217
+ }
218
+ // ============ Call Graph Queries ============
219
+ /**
220
+ * Get all resolved callees (functions this node calls)
221
+ */
222
+ getResolvedCallees(nodeId) {
223
+ const stmt = this.db.prepare(`
224
+ SELECT n.* FROM nodes n
225
+ JOIN edges e ON n.id = e.target_id
226
+ WHERE e.source_id = ? AND e.type = 'calls' AND e.target_id NOT LIKE 'ref:%'
227
+ `);
228
+ const rows = stmt.all(nodeId);
229
+ return rows.map((r) => this.rowToNode(r));
230
+ }
231
+ /**
232
+ * Get all resolved callers (functions that call this node)
233
+ */
234
+ getResolvedCallers(nodeId) {
235
+ const stmt = this.db.prepare(`
236
+ SELECT n.* FROM nodes n
237
+ JOIN edges e ON n.id = e.source_id
238
+ WHERE e.target_id = ? AND e.type = 'calls'
239
+ `);
240
+ const rows = stmt.all(nodeId);
241
+ return rows.map((r) => this.rowToNode(r));
242
+ }
243
+ /**
244
+ * Get impact: all nodes affected by changes to this node (BFS traversal)
245
+ */
246
+ getImpactedNodes(nodeId, maxDepth = 3) {
247
+ const visited = /* @__PURE__ */ new Set();
248
+ const result = [];
249
+ const queue = [{ id: nodeId, depth: 0 }];
250
+ while (queue.length > 0) {
251
+ const { id, depth } = queue.shift();
252
+ if (visited.has(id) || depth > maxDepth) continue;
253
+ visited.add(id);
254
+ const callers = this.getResolvedCallers(id);
255
+ for (const caller of callers) {
256
+ if (!visited.has(caller.id)) {
257
+ result.push(caller);
258
+ queue.push({ id: caller.id, depth: depth + 1 });
259
+ }
260
+ }
261
+ }
262
+ return result;
263
+ }
264
+ /**
265
+ * Trace call path from source to target using BFS
266
+ */
267
+ traceCallPath(fromId, toId) {
268
+ const visited = /* @__PURE__ */ new Set();
269
+ const parent = /* @__PURE__ */ new Map();
270
+ const queue = [fromId];
271
+ while (queue.length > 0) {
272
+ const current = queue.shift();
273
+ if (current === toId) {
274
+ const path = [];
275
+ let node = toId;
276
+ while (node !== fromId) {
277
+ path.unshift(node);
278
+ node = parent.get(node).nodeId;
279
+ }
280
+ path.unshift(fromId);
281
+ const nodes = path.map((id) => {
282
+ const n = this.getNode(id);
283
+ return {
284
+ id: n.id,
285
+ name: n.name,
286
+ file: n.filePath,
287
+ line: n.startLine
288
+ };
289
+ });
290
+ const edges = [];
291
+ for (let i = 0; i < path.length - 1; i++) {
292
+ edges.push({
293
+ from: path[i],
294
+ to: path[i + 1],
295
+ type: "calls"
296
+ });
297
+ }
298
+ return { nodes, edges };
299
+ }
300
+ if (visited.has(current)) continue;
301
+ visited.add(current);
302
+ const callees = this.getResolvedCallees(current);
303
+ for (const callee of callees) {
304
+ if (!visited.has(callee.id)) {
305
+ parent.set(callee.id, { nodeId: current, edgeType: "calls" });
306
+ queue.push(callee.id);
307
+ }
308
+ }
309
+ }
310
+ return null;
311
+ }
312
+ // ============ Export/Import Tracking ============
313
+ registerExport(filePath, symbolName, nodeId) {
314
+ const stmt = this.db.prepare(`
315
+ INSERT OR REPLACE INTO exports (file_path, symbol_name, node_id)
316
+ VALUES (?, ?, ?)
317
+ `);
318
+ stmt.run(filePath, symbolName, nodeId);
319
+ }
320
+ registerImport(filePath, symbol, fromModule, line) {
321
+ const stmt = this.db.prepare(`
322
+ INSERT OR REPLACE INTO imports (file_path, imported_symbol, from_module, line)
323
+ VALUES (?, ?, ?, ?)
324
+ `);
325
+ stmt.run(filePath, symbol, fromModule, line);
326
+ }
327
+ resolveImport(filePath, symbol) {
328
+ const importStmt = this.db.prepare(`
329
+ SELECT from_module FROM imports WHERE file_path = ? AND imported_symbol = ?
330
+ `).get(filePath, symbol);
331
+ if (!importStmt) return null;
332
+ const modulePath = importStmt.from_module;
333
+ const possiblePaths = this.resolvePythonModulePath(filePath, modulePath);
334
+ for (const targetPath of possiblePaths) {
335
+ const exportStmt = this.db.prepare(`
336
+ SELECT node_id FROM exports WHERE file_path = ? AND symbol_name = ?
337
+ `).get(targetPath, symbol);
338
+ if (exportStmt) {
339
+ return exportStmt.node_id;
340
+ }
341
+ }
342
+ return null;
343
+ }
344
+ /**
345
+ * Resolve a Python module path to possible file paths
346
+ * Handles: absolute imports, relative imports, __init__.py
347
+ */
348
+ resolvePythonModulePath(currentFile, modulePath) {
349
+ const possiblePaths = [];
350
+ if (modulePath.startsWith(".")) {
351
+ const currentDir = currentFile.substring(0, currentFile.lastIndexOf("/"));
352
+ const depth = modulePath.match(/^\.+/)?.[0].length || 0;
353
+ const moduleName = modulePath.replace(/^\.+/, "");
354
+ let targetDir = currentDir;
355
+ for (let i = 1; i < depth; i++) {
356
+ const lastSlash = targetDir.lastIndexOf("/");
357
+ if (lastSlash > 0) {
358
+ targetDir = targetDir.substring(0, lastSlash);
359
+ }
360
+ }
361
+ if (moduleName) {
362
+ const relativePath = moduleName.replace(/\./g, "/");
363
+ possiblePaths.push(`${targetDir}/${relativePath}.py`);
364
+ possiblePaths.push(`${targetDir}/${relativePath}/__init__.py`);
365
+ } else {
366
+ possiblePaths.push(`${targetDir}/__init__.py`);
367
+ }
368
+ } else {
369
+ const pathComponents = modulePath.split(".");
370
+ const filePath = pathComponents.join("/");
371
+ possiblePaths.push(`${filePath}.py`);
372
+ possiblePaths.push(`${filePath}/__init__.py`);
373
+ possiblePaths.push(`src/${filePath}.py`);
374
+ possiblePaths.push(`src/${filePath}/__init__.py`);
375
+ const lastComponent = pathComponents[pathComponents.length - 1];
376
+ const matchingFiles = this.db.prepare(`
377
+ SELECT DISTINCT file_path FROM exports
378
+ WHERE file_path LIKE ?
379
+ `).all(`%/${lastComponent}.py`);
380
+ for (const file of matchingFiles) {
381
+ const normalizedFilePath = file.file_path.replace(/\\/g, "/");
382
+ if (normalizedFilePath.includes(filePath)) {
383
+ possiblePaths.push(normalizedFilePath);
384
+ }
385
+ }
386
+ }
387
+ return possiblePaths;
388
+ }
389
+ /**
390
+ * Resolve a class method call like ClassName.method_name
391
+ * Handles: static methods, class methods, imported classes
392
+ */
393
+ resolveClassMethod(className, methodName, callerFilePath) {
394
+ const classNodeId = this.resolveImport(callerFilePath, className);
395
+ if (classNodeId) {
396
+ const methods = this.db.prepare(`
397
+ SELECT target_id FROM edges
398
+ WHERE source_id = ? AND type = 'contains'
399
+ `).all(classNodeId);
400
+ for (const edge of methods) {
401
+ const methodNode = this.getNode(edge.target_id);
402
+ if (methodNode && methodNode.name === methodName) {
403
+ return methodNode.id;
404
+ }
405
+ }
406
+ }
407
+ const classNode = this.db.prepare(`
408
+ SELECT id FROM nodes
409
+ WHERE file_path = ? AND name = ? AND type = 'class'
410
+ `).get(callerFilePath, className);
411
+ if (classNode) {
412
+ const methodNode = this.db.prepare(`
413
+ SELECT n.id FROM nodes n
414
+ JOIN edges e ON e.target_id = n.id
415
+ WHERE e.source_id = ? AND e.type = 'contains' AND n.name = ?
416
+ `).get(classNode.id, methodName);
417
+ if (methodNode) {
418
+ return methodNode.id;
419
+ }
420
+ }
421
+ const allClasses = this.db.prepare(`
422
+ SELECT id FROM nodes WHERE name = ? AND type = 'class'
423
+ `).all(className);
424
+ for (const cls of allClasses) {
425
+ const methodNode = this.db.prepare(`
426
+ SELECT n.id FROM nodes n
427
+ JOIN edges e ON e.target_id = n.id
428
+ WHERE e.source_id = ? AND e.type = 'contains' AND n.name = ?
429
+ `).get(cls.id, methodName);
430
+ if (methodNode) {
431
+ return methodNode.id;
432
+ }
433
+ }
434
+ return null;
435
+ }
436
+ // ============ Variable Type Tracking ============
437
+ /**
438
+ * Register a variable type (from assignment or parameter)
439
+ */
440
+ registerVariableType(variableName, typeName, scopeNodeId, filePath, line, isParameter = false) {
441
+ const id = `${scopeNodeId}:var:${variableName}`;
442
+ const stmt = this.db.prepare(`
443
+ INSERT OR REPLACE INTO variable_types
444
+ (id, variable_name, type_name, scope_node_id, file_path, line, is_parameter)
445
+ VALUES (?, ?, ?, ?, ?, ?, ?)
446
+ `);
447
+ stmt.run(id, variableName, typeName, scopeNodeId, filePath, line, isParameter ? 1 : 0);
448
+ }
449
+ /**
450
+ * Resolve variable.method to actual method node
451
+ * Example: user.get_name() where user is of type User
452
+ */
453
+ resolveVariableMethod(variableName, methodName, scopeNodeId) {
454
+ if (variableName === "self") {
455
+ return this.resolveSelfMethod(methodName, scopeNodeId);
456
+ }
457
+ const varType = this.db.prepare(`
458
+ SELECT type_name FROM variable_types
459
+ WHERE scope_node_id = ? AND variable_name = ?
460
+ `).get(scopeNodeId, variableName);
461
+ if (!varType) {
462
+ return null;
463
+ }
464
+ return this.resolveClassMethod(varType.type_name, methodName, "");
465
+ }
466
+ /**
467
+ * Resolve self.method() calls to methods in the same class
468
+ */
469
+ resolveSelfMethod(methodName, scopeNodeId) {
470
+ const parentClass = this.db.prepare(`
471
+ SELECT source_id FROM edges
472
+ WHERE target_id = ? AND type = 'contains'
473
+ `).get(scopeNodeId);
474
+ if (!parentClass) {
475
+ return null;
476
+ }
477
+ const method = this.db.prepare(`
478
+ SELECT n.id FROM nodes n
479
+ JOIN edges e ON e.target_id = n.id
480
+ WHERE e.source_id = ? AND e.type = 'contains' AND n.name = ?
481
+ `).get(parentClass.source_id, methodName);
482
+ return method?.id || null;
483
+ }
484
+ /**
485
+ * Resolve super().method() calls to parent class methods
486
+ */
487
+ resolveSuperMethod(methodName, scopeNodeId) {
488
+ const parentClass = this.db.prepare(`
489
+ SELECT source_id FROM edges
490
+ WHERE target_id = ? AND type = 'contains'
491
+ `).get(scopeNodeId);
492
+ if (!parentClass) {
493
+ return null;
494
+ }
495
+ const classNode = this.getNode(parentClass.source_id);
496
+ if (!classNode || classNode.type !== "class") {
497
+ return null;
498
+ }
499
+ const baseClass = this.db.prepare(`
500
+ SELECT target_id FROM edges
501
+ WHERE source_id = ? AND type = 'extends'
502
+ `).get(classNode.id);
503
+ if (!baseClass) {
504
+ return null;
505
+ }
506
+ let baseClassId = baseClass.target_id;
507
+ if (baseClassId.startsWith("ref:")) {
508
+ const baseClassName = baseClassId.replace("ref:", "");
509
+ const resolvedClass = this.db.prepare(`
510
+ SELECT id FROM nodes WHERE name = ? AND type = 'class'
511
+ `).get(baseClassName);
512
+ if (!resolvedClass) {
513
+ return null;
514
+ }
515
+ baseClassId = resolvedClass.id;
516
+ }
517
+ const method = this.db.prepare(`
518
+ SELECT n.id FROM nodes n
519
+ JOIN edges e ON e.target_id = n.id
520
+ WHERE e.source_id = ? AND e.type = 'contains' AND n.name = ?
521
+ `).get(baseClassId, methodName);
522
+ return method?.id || null;
523
+ }
524
+ // ============ File Operations ============
525
+ upsertFile(filePath, language, hash) {
526
+ const stmt = this.db.prepare(`
527
+ INSERT OR REPLACE INTO files (path, language, hash, analyzed_at)
528
+ VALUES (?, ?, ?, ?)
529
+ `);
530
+ stmt.run(filePath, language, hash, (/* @__PURE__ */ new Date()).toISOString());
531
+ }
532
+ getFileHash(filePath) {
533
+ const stmt = this.db.prepare("SELECT hash FROM files WHERE path = ?");
534
+ const row = stmt.get(filePath);
535
+ return row?.hash || null;
536
+ }
537
+ deleteFileData(filePath) {
538
+ this.db.prepare("DELETE FROM nodes WHERE file_path = ?").run(filePath);
539
+ this.db.prepare("DELETE FROM exports WHERE file_path = ?").run(filePath);
540
+ this.db.prepare("DELETE FROM imports WHERE file_path = ?").run(filePath);
541
+ this.db.prepare("DELETE FROM files WHERE path = ?").run(filePath);
542
+ }
543
+ // ============ Stats ============
544
+ getStats() {
545
+ const totalFiles = this.db.prepare("SELECT COUNT(*) as count FROM files").get().count;
546
+ const totalNodes = this.db.prepare("SELECT COUNT(*) as count FROM nodes").get().count;
547
+ const totalEdges = this.db.prepare("SELECT COUNT(*) as count FROM edges WHERE type = 'calls'").get().count;
548
+ const resolvedCalls = this.db.prepare("SELECT COUNT(*) as count FROM edges WHERE type = 'calls' AND target_id NOT LIKE 'ref:%'").get().count;
549
+ const unresolvedCalls = totalEdges - resolvedCalls;
550
+ const httpEndpoints = this.db.prepare("SELECT COUNT(*) as count FROM http_endpoints").get().count;
551
+ const frameworkDependencies = this.db.prepare("SELECT COUNT(*) as count FROM framework_dependencies").get().count;
552
+ const resolvedDependencies = this.db.prepare("SELECT COUNT(*) as count FROM framework_dependencies WHERE target_node_id IS NOT NULL").get().count;
553
+ const containers = this.db.prepare("SELECT COUNT(*) as count FROM containers").get().count;
554
+ return {
555
+ totalFiles,
556
+ totalNodes,
557
+ totalEdges,
558
+ resolvedCalls,
559
+ unresolvedCalls,
560
+ httpEndpoints,
561
+ frameworkDependencies,
562
+ resolvedDependencies,
563
+ containers
564
+ };
565
+ }
566
+ // ============ Container Operations ============
567
+ insertContainer(container) {
568
+ const stmt = this.db.prepare(`
569
+ INSERT OR REPLACE INTO containers (id, name, image, ports, environment, depends_on, networks, metadata)
570
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?)
571
+ `);
572
+ stmt.run(
573
+ container.id,
574
+ container.name,
575
+ container.image || null,
576
+ container.ports ? JSON.stringify(container.ports) : null,
577
+ container.environment ? JSON.stringify(container.environment) : null,
578
+ container.depends_on ? JSON.stringify(container.depends_on) : null,
579
+ container.networks ? JSON.stringify(container.networks) : null,
580
+ container.metadata ? JSON.stringify(container.metadata) : null
581
+ );
582
+ }
583
+ getContainer(id) {
584
+ const stmt = this.db.prepare("SELECT * FROM containers WHERE id = ?");
585
+ const row = stmt.get(id);
586
+ if (!row) return null;
587
+ return {
588
+ id: row.id,
589
+ name: row.name,
590
+ image: row.image,
591
+ ports: row.ports ? JSON.parse(row.ports) : [],
592
+ environment: row.environment ? JSON.parse(row.environment) : {},
593
+ depends_on: row.depends_on ? JSON.parse(row.depends_on) : [],
594
+ networks: row.networks ? JSON.parse(row.networks) : [],
595
+ metadata: row.metadata ? JSON.parse(row.metadata) : {}
596
+ };
597
+ }
598
+ getAllContainers() {
599
+ const stmt = this.db.prepare("SELECT * FROM containers");
600
+ const rows = stmt.all();
601
+ return rows.map((row) => ({
602
+ id: row.id,
603
+ name: row.name,
604
+ image: row.image,
605
+ ports: row.ports ? JSON.parse(row.ports) : [],
606
+ environment: row.environment ? JSON.parse(row.environment) : {},
607
+ depends_on: row.depends_on ? JSON.parse(row.depends_on) : [],
608
+ networks: row.networks ? JSON.parse(row.networks) : [],
609
+ metadata: row.metadata ? JSON.parse(row.metadata) : {}
610
+ }));
611
+ }
612
+ // ============ HTTP Endpoint Operations ============
613
+ insertHttpEndpoint(endpoint) {
614
+ const stmt = this.db.prepare(`
615
+ INSERT OR REPLACE INTO http_endpoints (id, method, path, handler_node_id, container_id, middleware, metadata)
616
+ VALUES (?, ?, ?, ?, ?, ?, ?)
617
+ `);
618
+ stmt.run(
619
+ endpoint.id,
620
+ endpoint.method,
621
+ endpoint.path,
622
+ endpoint.handler_node_id || null,
623
+ endpoint.container_id || null,
624
+ endpoint.middleware ? JSON.stringify(endpoint.middleware) : null,
625
+ endpoint.metadata ? JSON.stringify(endpoint.metadata) : null
626
+ );
627
+ }
628
+ insertDatabaseOperation(dbOp) {
629
+ const stmt = this.db.prepare(`
630
+ INSERT OR REPLACE INTO database_operations (id, node_id, database_type, operation, collection, line, metadata)
631
+ VALUES (?, ?, ?, ?, ?, ?, ?)
632
+ `);
633
+ stmt.run(
634
+ dbOp.id,
635
+ dbOp.node_id,
636
+ dbOp.database_type,
637
+ dbOp.operation,
638
+ dbOp.collection || null,
639
+ dbOp.line || null,
640
+ dbOp.metadata ? JSON.stringify(dbOp.metadata) : null
641
+ );
642
+ }
643
+ findHttpEndpoint(method, path) {
644
+ const stmt = this.db.prepare("SELECT * FROM http_endpoints WHERE method = ? AND path = ?");
645
+ const row = stmt.get(method, path);
646
+ if (!row) return null;
647
+ return {
648
+ id: row.id,
649
+ method: row.method,
650
+ path: row.path,
651
+ handler_node_id: row.handler_node_id,
652
+ container_id: row.container_id,
653
+ middleware: row.middleware ? JSON.parse(row.middleware) : [],
654
+ metadata: row.metadata ? JSON.parse(row.metadata) : {}
655
+ };
656
+ }
657
+ // ============ Framework Dependency Operations ============
658
+ insertFrameworkDependency(dep) {
659
+ const stmt = this.db.prepare(`
660
+ INSERT OR REPLACE INTO framework_dependencies
661
+ (id, source_node_id, target_node_id, framework, pattern, parameter_name, line, unresolved_name, metadata)
662
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
663
+ `);
664
+ stmt.run(
665
+ dep.id,
666
+ dep.source_node_id,
667
+ dep.target_node_id || null,
668
+ dep.framework,
669
+ dep.pattern,
670
+ dep.parameter_name || null,
671
+ dep.line || null,
672
+ dep.unresolved_name || null,
673
+ dep.metadata ? JSON.stringify(dep.metadata) : null
674
+ );
675
+ }
676
+ getFrameworkDependencies(sourceNodeId) {
677
+ const stmt = this.db.prepare("SELECT * FROM framework_dependencies WHERE source_node_id = ?");
678
+ const rows = stmt.all(sourceNodeId);
679
+ return rows.map((row) => ({
680
+ id: row.id,
681
+ source_node_id: row.source_node_id,
682
+ target_node_id: row.target_node_id,
683
+ framework: row.framework,
684
+ pattern: row.pattern,
685
+ parameter_name: row.parameter_name,
686
+ line: row.line,
687
+ unresolved_name: row.unresolved_name,
688
+ metadata: row.metadata ? JSON.parse(row.metadata) : {}
689
+ }));
690
+ }
691
+ getAllUnresolvedDependencies() {
692
+ const stmt = this.db.prepare("SELECT * FROM framework_dependencies WHERE target_node_id IS NULL");
693
+ const rows = stmt.all();
694
+ return rows.map((row) => ({
695
+ id: row.id,
696
+ source_node_id: row.source_node_id,
697
+ framework: row.framework,
698
+ pattern: row.pattern,
699
+ unresolved_name: row.unresolved_name,
700
+ line: row.line
701
+ }));
702
+ }
703
+ updateFrameworkDependencyTarget(id, targetNodeId) {
704
+ const stmt = this.db.prepare("UPDATE framework_dependencies SET target_node_id = ? WHERE id = ?");
705
+ stmt.run(targetNodeId, id);
706
+ }
707
+ clear() {
708
+ this.db.exec(`
709
+ DELETE FROM nodes;
710
+ DELETE FROM edges;
711
+ DELETE FROM files;
712
+ DELETE FROM exports;
713
+ DELETE FROM imports;
714
+ DELETE FROM containers;
715
+ DELETE FROM http_endpoints;
716
+ DELETE FROM http_calls;
717
+ DELETE FROM framework_dependencies;
718
+ DELETE FROM database_operations;
719
+ DELETE FROM message_queue_operations;
720
+ `);
721
+ }
722
+ close() {
723
+ this.db.close();
724
+ }
725
+ };
726
+
727
+ export {
728
+ FlowStorage
729
+ };
730
+ //# sourceMappingURL=chunk-GX7ZMBC2.js.map