diff --git a/frontend/src/api.ts b/frontend/src/api.ts index eea8113..eacfc8a 100644 --- a/frontend/src/api.ts +++ b/frontend/src/api.ts @@ -3,6 +3,8 @@ const API_URL = import.meta.env.VITE_API_URL || ""; const TIMEOUT_MS = 120_000; +const CACHE_TTL_MS = 5 * 60 * 1000; +const cache = new Map(); export interface Violation { rule: string; @@ -36,7 +38,29 @@ export interface AnalysisResult { metrics: Metrics; } +function cacheKey(repoUrl: string, branch: string): string { + return `${repoUrl.trim().toLowerCase()}@${branch.trim()}` +} + +function getCached(repoUrl: string, branch: string): AnalysisResult | null { + const key = cacheKey(repoUrl, branch) + const entry = cache.get(key) + if (entry && Date.now() - entry.timestamp < CACHE_TTL_MS) { + return entry.data + } + cache.delete(key) + return null +} + +function setCache(repoUrl: string, branch: string, data: AnalysisResult): void { + const key = cacheKey(repoUrl, branch) + cache.set(key, { data, timestamp: Date.now() }) +} + export async function analyze(repoUrl: string, branch: string): Promise { + const cached = getCached(repoUrl, branch) + if (cached) return cached + const controller = new AbortController(); const timeout = setTimeout(() => controller.abort(), TIMEOUT_MS); @@ -51,7 +75,9 @@ export async function analyze(repoUrl: string, branch: string): Promise ({ detail: resp.statusText })); throw new Error(error.detail || `HTTP ${resp.status}`); } - return resp.json(); + const data: AnalysisResult = await resp.json() + setCache(repoUrl, branch, data) + return data } catch (err) { if (err instanceof DOMException && err.name === "AbortError") { throw new Error(`Request timed out after ${TIMEOUT_MS / 1000}s — the repository may be too large or unreachable`);