Files
Magent/frontend/app/lib/api-client.ts
T
Assclaw f852e7c941
Magent CI/CD / verify (push) Failing after 9m34s
Magent CI/CD / deploy-beta (push) Skipped
chore: standardize security and quality foundations
2026-09-17 20:03:47 +12:00

53 lines
1.4 KiB
TypeScript

import { authFetchOrThrow, getApiBase } from "./auth";
export type ApiTransport = (input: RequestInfo | URL, init?: RequestInit) => Promise<Response>;
export class ApiClientError extends Error {
readonly status: number;
constructor(status: number, message: string) {
super(message);
this.name = "ApiClientError";
this.status = status;
}
}
const errorMessage = (payload: unknown, fallback: string) => {
if (!payload || typeof payload !== "object") return fallback;
const record = payload as Record<string, unknown>;
for (const key of ["detail", "error", "message"]) {
const value = record[key];
if (typeof value === "string" && value.trim()) return value.trim();
}
return fallback;
};
export const apiUrl = (path: string) => {
const normalizedPath = path.startsWith("/") ? path : `/${path}`;
return `${getApiBase()}${normalizedPath}`;
};
export async function requestJson<T>(
path: string,
init?: RequestInit,
transport: ApiTransport = authFetchOrThrow,
): Promise<T> {
const response = await transport(apiUrl(path), init);
if (response.status === 204) return undefined as T;
const text = await response.text();
let payload: unknown = null;
if (text) {
try {
payload = JSON.parse(text);
} catch {
payload = text;
}
}
if (!response.ok) {
throw new ApiClientError(response.status, errorMessage(payload, text || `Request failed: ${response.status}`));
}
return payload as T;
}