arela
Version:
AI-powered CTO with multi-agent orchestration, code summarization, visual testing (web + mobile) for blazing fast development.
262 lines • 9.72 kB
JavaScript
/**
* Load graph from Graph DB
*/
import { GraphDB } from "../ingest/storage.js";
/**
* Load the complete graph from the Graph DB
*/
export function loadGraph(dbPath) {
const db = new GraphDB(dbPath);
try {
// Load all files as nodes
const fileRows = db.query(`SELECT id, path, type FROM files ORDER BY id`);
// Create node map with degree counting
const nodes = [];
const nodeMap = new Map();
const pathToIdMap = createPathToIdMap(fileRows);
// Initialize nodes
for (const row of fileRows) {
const node = {
id: row.id,
path: row.path,
type: row.type,
degree: 0, // Will be updated below
};
nodes.push(node);
nodeMap.set(row.id, node);
}
// Load all imports - both internal and resolvable
const importRows = db.query(`SELECT from_file_id, to_file_id, to_module, COUNT(*) as weight
FROM imports
GROUP BY from_file_id, COALESCE(to_file_id, to_module)`);
const edges = [];
// Calculate degrees and create edges from explicit imports
for (const row of importRows) {
const fromNode = nodeMap.get(row.from_file_id);
if (!fromNode)
continue;
let toId = row.to_file_id;
// Try to resolve to_module to a file ID
if (!toId && row.to_module) {
toId = resolveModuleToFileId(row.to_module, pathToIdMap);
}
if (toId) {
const toNode = nodeMap.get(toId);
if (toNode) {
edges.push({
from: row.from_file_id,
to: toId,
weight: row.weight,
});
// Update degrees
fromNode.degree += row.weight;
toNode.degree += row.weight;
}
}
}
// Add implicit edges between files in the same directory
// This reflects package-level coupling in languages like Go, Python, Java
const directoryGroups = new Map();
// Group files by directory
for (const node of nodes) {
const dir = node.path.includes('/')
? node.path.substring(0, node.path.lastIndexOf('/'))
: '.';
if (!directoryGroups.has(dir)) {
directoryGroups.set(dir, []);
}
directoryGroups.get(dir).push(node.id);
}
// Create edges between files in the same directory
for (const [dir, fileIds] of directoryGroups.entries()) {
if (fileIds.length > 1) {
// Create bidirectional edges between all pairs of files in the directory
for (let i = 0; i < fileIds.length; i++) {
for (let j = i + 1; j < fileIds.length; j++) {
// Add edge in both directions for stronger coupling
edges.push({
from: fileIds[i],
to: fileIds[j],
weight: 1.0, // Same weight as explicit imports
});
edges.push({
from: fileIds[j],
to: fileIds[i],
weight: 1.0,
});
// Update degrees
const fromNode = nodeMap.get(fileIds[i]);
const toNode = nodeMap.get(fileIds[j]);
if (fromNode && toNode) {
fromNode.degree += 1.0;
toNode.degree += 1.0;
}
}
}
}
}
return {
nodes,
edges,
};
}
finally {
db.close();
}
}
/**
* Create a map of file paths to IDs for quick lookup
*/
function createPathToIdMap(fileRows) {
const map = new Map();
for (const row of fileRows) {
// Store all variations: .ts, .js, without extension
const basePath = row.path.replace(/\.(ts|js)$/, "");
map.set(basePath, row.id);
map.set(row.path, row.id);
map.set(basePath + ".ts", row.id);
map.set(basePath + ".js", row.id);
}
return map;
}
/**
* Try to resolve a module path to a file ID
*/
function resolveModuleToFileId(modulePath, pathToIdMap) {
// Remove leading ./ and handle relative imports
let normalized = modulePath.replace(/^\.\//, "");
// Try exact match
if (pathToIdMap.has(normalized)) {
return pathToIdMap.get(normalized) || null;
}
// Try with .ts extension
if (pathToIdMap.has(normalized + ".ts")) {
return pathToIdMap.get(normalized + ".ts") || null;
}
// Try with .js extension
if (pathToIdMap.has(normalized + ".js")) {
return pathToIdMap.get(normalized + ".js") || null;
}
// Try treating as directory with index.ts
if (pathToIdMap.has(normalized + "/index.ts")) {
return pathToIdMap.get(normalized + "/index.ts") || null;
}
// Try treating as directory with index.js
if (pathToIdMap.has(normalized + "/index.js")) {
return pathToIdMap.get(normalized + "/index.js") || null;
}
return null;
}
/**
* Load graph for multiple repositories
*/
export function loadMultiRepoGraph(dbPath, repos) {
const db = new GraphDB(dbPath);
try {
// Load files, optionally filtered by repo
let query = `SELECT id, path, type FROM files`;
const params = [];
if (repos && repos.length > 0) {
query += ` WHERE repo IN (${repos.map(() => "?").join(",")})`;
params.push(...repos);
}
query += ` ORDER BY id`;
const fileRows = db.query(query, params);
const nodes = [];
const nodeMap = new Map();
const nodeIds = new Set();
const pathToIdMap = createPathToIdMap(fileRows);
// Initialize nodes
for (const row of fileRows) {
nodeIds.add(row.id);
const node = {
id: row.id,
path: row.path,
type: row.type,
degree: 0,
};
nodes.push(node);
nodeMap.set(row.id, node);
}
// Load all imports - both internal and resolvable
const importRows = db.query(`SELECT from_file_id, to_file_id, to_module, COUNT(*) as weight
FROM imports
GROUP BY from_file_id, COALESCE(to_file_id, to_module)`);
const edges = [];
for (const row of importRows) {
// Only include imports from files in our set
if (!nodeIds.has(row.from_file_id))
continue;
const fromNode = nodeMap.get(row.from_file_id);
if (!fromNode)
continue;
let toId = row.to_file_id;
// Try to resolve to_module to a file ID
if (!toId && row.to_module) {
toId = resolveModuleToFileId(row.to_module, pathToIdMap);
}
// Only include edges between files in our set
if (toId && nodeIds.has(toId)) {
const toNode = nodeMap.get(toId);
if (toNode) {
edges.push({
from: row.from_file_id,
to: toId,
weight: row.weight,
});
fromNode.degree += row.weight;
toNode.degree += row.weight;
}
}
}
// Add implicit edges between files in the same directory
// This reflects package-level coupling in languages like Go, Python, Java
const directoryGroups = new Map();
// Group files by directory
for (const node of nodes) {
const dir = node.path.includes('/')
? node.path.substring(0, node.path.lastIndexOf('/'))
: '.';
if (!directoryGroups.has(dir)) {
directoryGroups.set(dir, []);
}
directoryGroups.get(dir).push(node.id);
}
// Create edges between all pairs of files in the same directory
for (const [dir, fileIds] of directoryGroups.entries()) {
if (fileIds.length > 1) {
// Create bidirectional edges between all pairs
for (let i = 0; i < fileIds.length; i++) {
for (let j = i + 1; j < fileIds.length; j++) {
// Add edge in both directions for stronger coupling
edges.push({
from: fileIds[i],
to: fileIds[j],
weight: 1.0,
});
edges.push({
from: fileIds[j],
to: fileIds[i],
weight: 1.0,
});
// Update degrees
const fromNode = nodeMap.get(fileIds[i]);
const toNode = nodeMap.get(fileIds[j]);
if (fromNode && toNode) {
fromNode.degree += 1.0;
toNode.degree += 1.0;
}
}
}
}
}
return {
nodes,
edges,
};
}
finally {
db.close();
}
}
//# sourceMappingURL=graph-loader.js.map