const BASE =
typeof window !== 'undefined' && window.__ANALYZER_BASE__
? window.__ANALYZER_BASE__
: '';
async function request(path, opts = {}) {
const res = await fetch(BASE + path, {
headers: { 'Content-Type': 'application/json', ...(opts.headers || {}) },
...opts
});
if (!res.ok) {
let detail = '';
try {
detail = await res.text();
} catch {
}
throw new Error(`${res.status} ${res.statusText}: ${detail}`);
}
if (res.status === 204) return null;
const ct = res.headers.get('content-type') || '';
if (ct.includes('application/json')) return res.json();
return res.text();
}
export const api = {
health: () => request('/health'),
indexes: () => request('/indexes'),
complexityHotspots: (id, topK = 20) =>
request(`/indexes/${encodeURIComponent(id)}/complexity_hotspots?top_k=${topK}`),
smells: (id, category) =>
request(
`/indexes/${encodeURIComponent(id)}/smells${category ? '?category=' + encodeURIComponent(category) : ''}`
),
quality: (id) => request(`/indexes/${encodeURIComponent(id)}/quality`),
refactorSuggestions: (id, { minSeverity = 'low', topK = 20 } = {}) => {
const qs = new URLSearchParams({
min_severity: minSeverity,
top_k: String(topK)
});
return request(`/indexes/${encodeURIComponent(id)}/refactor-suggestions?${qs}`);
},
clusters: (id, { k = 8, method = 'bow' } = {}) =>
request(`/indexes/${encodeURIComponent(id)}/clusters?k=${k}&method=${method}`),
listFacts: (subject, predicate) => {
const qs = new URLSearchParams();
if (subject) qs.set('subject', subject);
if (predicate) qs.set('predicate', predicate);
const tail = qs.toString();
return request(`/facts${tail ? '?' + tail : ''}`);
},
upsertFact: (fact) =>
request('/facts', { method: 'POST', body: JSON.stringify(fact) }),
deleteFact: (id) =>
request(`/facts/${encodeURIComponent(id)}`, { method: 'DELETE' })
};