91 lines
2.6 KiB
TypeScript
91 lines
2.6 KiB
TypeScript
// Thin fetch wrapper. Throws on non-2xx with a structured ApiError.
|
|
|
|
export class ApiError extends Error {
|
|
status: number;
|
|
field?: string;
|
|
constructor(status: number, message: string, field?: string) {
|
|
super(message);
|
|
this.status = status;
|
|
this.field = field;
|
|
}
|
|
}
|
|
|
|
const INGEST_BASE = import.meta.env.VITE_API_BASE_URL ?? '/api/ingest';
|
|
const ROTOR_BASE = import.meta.env.VITE_ROTOR_BASE_URL ?? '/api/rotor';
|
|
const BULKER_BASE = import.meta.env.VITE_BULKER_BASE_URL ?? '/api/bulker';
|
|
|
|
async function request<T>(base: string, path: string, init?: RequestInit): Promise<T> {
|
|
const res = await fetch(`${base}${path}`, {
|
|
...init,
|
|
headers: {
|
|
'content-type': 'application/json',
|
|
...(init?.headers ?? {}),
|
|
},
|
|
});
|
|
const text = await res.text();
|
|
const data = text ? safeJSON(text) : undefined;
|
|
if (!res.ok) {
|
|
const msg = (data as { error?: string })?.error ?? res.statusText;
|
|
const field = (data as { field?: string })?.field;
|
|
throw new ApiError(res.status, msg, field);
|
|
}
|
|
return data as T;
|
|
}
|
|
|
|
function safeJSON(text: string): unknown {
|
|
try {
|
|
return JSON.parse(text);
|
|
} catch {
|
|
return text;
|
|
}
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Ingest API
|
|
// ---------------------------------------------------------------------------
|
|
|
|
export const ingest = {
|
|
health: () => request<{ status: string }>(INGEST_BASE, '/health'),
|
|
ready: () => request<{ status: string }>(INGEST_BASE, '/ready'),
|
|
|
|
track: (writeKey: string, body: Record<string, unknown>) =>
|
|
request<{ ok: boolean }>(INGEST_BASE, '/track', {
|
|
method: 'POST',
|
|
headers: { Authorization: `Bearer ${writeKey}` },
|
|
body: JSON.stringify(body),
|
|
}),
|
|
};
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Rotor API
|
|
// ---------------------------------------------------------------------------
|
|
|
|
export interface RunRequest {
|
|
code: string;
|
|
event: Record<string, unknown>;
|
|
}
|
|
export interface RunResponse {
|
|
result: unknown;
|
|
}
|
|
|
|
export const rotor = {
|
|
run: (body: RunRequest) =>
|
|
request<RunResponse>(ROTOR_BASE, '/v1/run', {
|
|
method: 'POST',
|
|
body: JSON.stringify(body),
|
|
}),
|
|
upsert: (body: { workspace_id: string; slug: string; code: string }) =>
|
|
request<{ ok: boolean }>(ROTOR_BASE, '/v1/functions', {
|
|
method: 'POST',
|
|
body: JSON.stringify(body),
|
|
}),
|
|
};
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Bulker API
|
|
// ---------------------------------------------------------------------------
|
|
|
|
export const bulker = {
|
|
health: () => request<{ status: string }>(BULKER_BASE, '/health'),
|
|
};
|