fix(ci): unblock UI job β anchor gitignore, include ui/src/libLast CI's Cannot find module @/lib/api (and @/lib/samples) turned outto be a gitignore over-match, not a tsconfig issue. .gitignore had barelib/ and lib64/ from its Python section β those aren't anchored, sothey silently matched ui/src/lib/ too, and neither api.ts nor samples.tsever made it into a commit. CI's fresh clone had an empty ui/src/lib/,tsc -b failed on the missing modules, docker-build gated behind and skipped.- .gitignore: anchor lib/ and lib64/ to repo root as /lib/, /lib64/- Force-add ui/src/lib/api.ts + ui/src/lib/samples.ts- Cascading fix: 'Parameter s implicitly has any' in Dropzone was because samples.ts couldn't resolve, so SampleDoc type didn't flow into the SAMPLE_DOCS.map callback. Present-again = inferred-again.
8c75a7a | /** | |
| * Thin fetch client for the FastAPI backend. | |
| * | |
| * Dev: Vite proxies /api/* to http://localhost:8000. See vite.config.ts. | |
| * Prod: set VITE_API_BASE to your deployed API's origin. | |
| */ | |
| import type { APIErrorEnvelope, DocType, ExtractResponse } from "@/types"; | |
| const API_BASE = (import.meta.env.VITE_API_BASE as string | undefined) ?? "/api"; | |
| export class APIError extends Error { | |
| code: string; | |
| requestId?: string | null; | |
| details?: Record<string, unknown>; | |
| constructor(envelope: APIErrorEnvelope["error"]) { | |
| super(envelope.message); | |
| this.code = envelope.code; | |
| this.requestId = envelope.request_id; | |
| this.details = envelope.details; | |
| } | |
| } | |
| async function parseError(res: Response): Promise<never> { | |
| let envelope: APIErrorEnvelope | null = null; | |
| try { | |
| envelope = (await res.json()) as APIErrorEnvelope; | |
| } catch { | |
| /* body wasn't JSON */ | |
| } | |
| if (envelope?.error) throw new APIError(envelope.error); | |
| throw new APIError({ code: `http_${res.status}`, message: res.statusText }); | |
| } | |
| export interface ExtractArgs { | |
| file: File; | |
| docType: DocType; | |
| model?: string; | |
| } | |
| export async function extract({ file, docType, model }: ExtractArgs): Promise<ExtractResponse> { | |
| const form = new FormData(); | |
| form.append("file", file, file.name); | |
| form.append("doc_type", docType); | |
| if (model) form.append("model", model); | |
| const res = await fetch(`${API_BASE}/extract`, { | |
| method: "POST", | |
| body: form, | |
| }); | |
| if (!res.ok) await parseError(res); | |
| return (await res.json()) as ExtractResponse; | |
| } | |
| export async function listSchemas(): Promise<{ doc_types: string[] }> { | |
| const res = await fetch(`${API_BASE}/schemas`); | |
| if (!res.ok) await parseError(res); | |
| return await res.json(); | |
| } | |
| export async function health(): Promise<{ status: string }> { | |
| const res = await fetch(`${API_BASE}/health`); | |
| if (!res.ok) await parseError(res); | |
| return await res.json(); | |
| } | |