/** * SQL Analyzer REST API client * Plain fetch() — no tRPC, no Node.js dependency. * Base URL is determined by the VITE_API_BASE env var (defaults to ""). * In production the React bundle is served by FastAPI itself, so "" works. * In dev, Vite proxies /api/* to http://localhost:7860. */ const BASE = (import.meta.env.VITE_API_BASE as string | undefined) ?? ""; async function post(path: string, body: unknown): Promise { const res = await fetch(`${BASE}${path}`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(body), }); if (!res.ok) { const text = await res.text().catch(() => ""); throw new Error(`API error ${res.status}: ${text.slice(0, 400)}`); } return res.json() as Promise; } async function get(path: string): Promise { const res = await fetch(`${BASE}${path}`); if (!res.ok) { const text = await res.text().catch(() => ""); throw new Error(`API error ${res.status}: ${text.slice(0, 400)}`); } return res.json() as Promise; } // ── Types ────────────────────────────────────────────────────────────────── export interface LintViolation { line_no: number; line_pos: number; code: string; description: string; name: string; warning: boolean; fixable: boolean; } export interface LintResult { dialect: string; violations: LintViolation[]; passed: boolean; stats: { total: number; errors: number; warnings: number; fixable: number }; } export interface AstNode { id: string; type: string; name: string; raw: string | null; start_line: number | null; start_pos: number | null; end_line: number | null; end_pos: number | null; is_leaf: boolean; children: AstNode[]; } export interface ParseResult { dialect: string; tree: AstNode; token_count: number; depth: number; } export interface FormatResult { dialect: string; original: string; formatted: string; changed: boolean; fixes_applied: number; } export interface InjectionPattern { pattern_id: string; risk_level: "critical" | "high" | "medium" | "low"; category: string; description: string; detail: string; offending_token: string | null; line_no: number | null; line_pos: number | null; recommendation: string; } export interface InjectionResult { dialect: string; safe: boolean; risk_score: number; patterns: InjectionPattern[]; summary: string; } export interface HealthResult { status: string; version: string; dialects: string[]; } // ── API calls ────────────────────────────────────────────────────────────── export const api = { health: () => get("/api/health"), lint: (sql: string, dialect: string) => post("/api/lint", { sql, dialect }), parse: (sql: string, dialect: string) => post("/api/parse", { sql, dialect }), format: (sql: string, dialect: string) => post("/api/format", { sql, dialect }), inject: (sql: string, dialect: string) => post("/api/inject", { sql, dialect }), };