156 lines
4.7 KiB
TypeScript
156 lines
4.7 KiB
TypeScript
import type Database from "better-sqlite3";
|
|
|
|
export interface SymbolRef {
|
|
caller_name: string;
|
|
caller_path: string;
|
|
caller_line: number;
|
|
is_server_action: boolean;
|
|
depth: number;
|
|
path_chain: string;
|
|
}
|
|
|
|
export function querySymbolRefs(
|
|
db: Database.Database,
|
|
symbolName: string,
|
|
maxDepth = 10
|
|
): SymbolRef[] {
|
|
const sql = `
|
|
WITH RECURSIVE upstream(caller_id, caller_name, caller_path, caller_line, is_sa, depth, path_chain) AS (
|
|
SELECT c.caller_id, s.name, f.path, c.call_line, s.is_server_action, 0, s.name
|
|
FROM calls c
|
|
JOIN symbols s ON c.caller_id = s.id
|
|
JOIN files f ON s.file_id = f.id
|
|
WHERE c.callee_id = (SELECT id FROM symbols WHERE name = ? LIMIT 1)
|
|
UNION
|
|
SELECT c.caller_id, s.name, f.path, c.call_line, s.is_server_action, u.depth + 1,
|
|
u.path_chain || ' → ' || s.name
|
|
FROM upstream u
|
|
JOIN calls c ON c.callee_id = u.caller_id
|
|
JOIN symbols s ON c.caller_id = s.id
|
|
JOIN files f ON s.file_id = f.id
|
|
WHERE u.depth < ?
|
|
)
|
|
SELECT caller_name, caller_path, caller_line, is_sa as is_server_action, depth, path_chain
|
|
FROM upstream ORDER BY depth, caller_path`;
|
|
return db.prepare(sql).all(symbolName, maxDepth) as SymbolRef[];
|
|
}
|
|
|
|
export interface SymbolCall {
|
|
callee_name: string;
|
|
callee_path: string;
|
|
callee_line: number;
|
|
depth: number;
|
|
path_chain: string;
|
|
}
|
|
|
|
export function querySymbolForwardCalls(
|
|
db: Database.Database,
|
|
symbolName: string,
|
|
maxDepth = 10
|
|
): SymbolCall[] {
|
|
const sql = `
|
|
WITH RECURSIVE downstream(callee_id, callee_name, callee_path, callee_line, depth, path_chain) AS (
|
|
SELECT c.callee_id, s.name, f.path, c.call_line, 0, ?
|
|
FROM calls c
|
|
JOIN symbols s ON c.callee_id = s.id
|
|
JOIN files f ON s.file_id = f.id
|
|
WHERE c.caller_id = (SELECT id FROM symbols WHERE name = ? LIMIT 1)
|
|
UNION
|
|
SELECT c.callee_id, s.name, f.path, c.call_line, d.depth + 1,
|
|
d.path_chain || ' → ' || s.name
|
|
FROM downstream d
|
|
JOIN calls c ON c.caller_id = d.callee_id
|
|
JOIN symbols s ON c.callee_id = s.id
|
|
JOIN files f ON s.file_id = f.id
|
|
WHERE d.depth < ?
|
|
)
|
|
SELECT callee_name, callee_path, callee_line, depth, path_chain
|
|
FROM downstream ORDER BY depth, callee_path`;
|
|
return db.prepare(sql).all(symbolName, symbolName, maxDepth) as SymbolCall[];
|
|
}
|
|
|
|
export interface ModuleDep {
|
|
target_module: string;
|
|
dep_type: string;
|
|
}
|
|
|
|
export function queryModuleDeps(
|
|
db: Database.Database,
|
|
moduleName: string,
|
|
_maxDepth = 10
|
|
): ModuleDep[] {
|
|
const sql = `
|
|
SELECT DISTINCT m2.name AS target_module, md.dep_type
|
|
FROM module_deps md
|
|
JOIN modules m ON md.source_module_id = m.id
|
|
JOIN modules m2 ON md.target_module_id = m2.id
|
|
WHERE m.name = ?`;
|
|
return db.prepare(sql).all(moduleName) as ModuleDep[];
|
|
}
|
|
|
|
export interface ModuleReverseDep {
|
|
source_module: string;
|
|
dep_type: string;
|
|
}
|
|
|
|
export function queryModuleReverseDeps(
|
|
db: Database.Database,
|
|
moduleName: string,
|
|
_maxDepth = 10
|
|
): ModuleReverseDep[] {
|
|
const sql = `
|
|
SELECT DISTINCT m.name AS source_module, md.dep_type
|
|
FROM module_deps md
|
|
JOIN modules m ON md.source_module_id = m.id
|
|
JOIN modules m2 ON md.target_module_id = m2.id
|
|
WHERE m2.name = ?`;
|
|
return db.prepare(sql).all(moduleName) as ModuleReverseDep[];
|
|
}
|
|
|
|
export interface TechUsage {
|
|
symbol_name: string;
|
|
file_path: string;
|
|
tag_name: string;
|
|
}
|
|
|
|
export function queryTechUsage(
|
|
db: Database.Database,
|
|
tagName: string
|
|
): TechUsage[] {
|
|
const sql = `
|
|
SELECT s.name AS symbol_name, f.path AS file_path, t.name AS tag_name
|
|
FROM symbols s
|
|
JOIN symbol_tech_tags stt ON s.id = stt.symbol_id
|
|
JOIN tech_tags t ON stt.tag_id = t.id
|
|
JOIN files f ON s.file_id = f.id
|
|
WHERE t.name = ?`;
|
|
return db.prepare(sql).all(tagName) as TechUsage[];
|
|
}
|
|
|
|
export interface Violations {
|
|
long_files: { path: string; lines: number }[];
|
|
server_actions_without_permission: { name: string; path: string }[];
|
|
}
|
|
|
|
export function queryViolations(db: Database.Database): Violations {
|
|
const longFiles = db
|
|
.prepare("SELECT path, lines FROM files WHERE lines > 800 ORDER BY lines DESC")
|
|
.all() as { path: string; lines: number }[];
|
|
const serverActionsWithoutPerm = db
|
|
.prepare(
|
|
`SELECT s.name, f.path FROM symbols s
|
|
JOIN files f ON s.file_id = f.id
|
|
WHERE s.is_server_action = 1
|
|
AND NOT EXISTS (
|
|
SELECT 1 FROM calls c
|
|
JOIN symbols cs ON c.callee_id = cs.id
|
|
WHERE c.caller_id = s.id AND cs.name = 'requirePermission'
|
|
)`
|
|
)
|
|
.all() as { name: string; path: string }[];
|
|
return {
|
|
long_files: longFiles,
|
|
server_actions_without_permission: serverActionsWithoutPerm,
|
|
};
|
|
}
|