demo 20-4-2026
This commit is contained in:
57
api/http.ts
57
api/http.ts
@@ -1,24 +1,43 @@
|
||||
export class ApiError extends Error {
|
||||
status: number;
|
||||
body: string;
|
||||
errors: unknown[];
|
||||
|
||||
constructor(message: string, status: number, body: string) {
|
||||
constructor(message: string, status: number, body: string, errors: unknown[] = []) {
|
||||
super(message);
|
||||
this.name = "ApiError";
|
||||
this.status = status;
|
||||
this.body = body;
|
||||
this.errors = errors;
|
||||
}
|
||||
}
|
||||
|
||||
type ApiEnvelope<T> = {
|
||||
status: "success" | "error" | string;
|
||||
data: T;
|
||||
message: string;
|
||||
errors: unknown[];
|
||||
};
|
||||
|
||||
export async function requestJson<T>(input: RequestInfo | URL, init?: RequestInit): Promise<T> {
|
||||
const res = await fetch(input, init);
|
||||
const payload = await parseJsonResponse(res);
|
||||
const envelope = isApiEnvelope<T>(payload) ? payload : null;
|
||||
|
||||
if (!res.ok) {
|
||||
const text = await res.text();
|
||||
throw new ApiError(`Request failed with status ${res.status}`, res.status, text);
|
||||
const message = envelope?.message || `Request failed with status ${res.status}`;
|
||||
const body = envelope ? message : stringifyPayload(payload);
|
||||
throw new ApiError(message, res.status, body, envelope?.errors || []);
|
||||
}
|
||||
|
||||
return (await res.json()) as T;
|
||||
if (envelope) {
|
||||
if (envelope.status === "error") {
|
||||
throw new ApiError(envelope.message || "Request failed", res.status, JSON.stringify(envelope), envelope.errors);
|
||||
}
|
||||
return envelope.data;
|
||||
}
|
||||
|
||||
return payload as T;
|
||||
}
|
||||
|
||||
export function jsonRequestInit(method: string, body: unknown): RequestInit {
|
||||
@@ -28,3 +47,33 @@ export function jsonRequestInit(method: string, body: unknown): RequestInit {
|
||||
body: JSON.stringify(body),
|
||||
};
|
||||
}
|
||||
|
||||
async function parseJsonResponse(res: Response): Promise<unknown> {
|
||||
const text = await res.text();
|
||||
if (!text.length) return null;
|
||||
try {
|
||||
return JSON.parse(text);
|
||||
} catch {
|
||||
return text;
|
||||
}
|
||||
}
|
||||
|
||||
function isApiEnvelope<T>(value: unknown): value is ApiEnvelope<T> {
|
||||
if (!value || typeof value !== "object" || Array.isArray(value)) return false;
|
||||
const source = value as Record<string, unknown>;
|
||||
return (
|
||||
"status" in source &&
|
||||
"data" in source &&
|
||||
"message" in source &&
|
||||
"errors" in source
|
||||
);
|
||||
}
|
||||
|
||||
function stringifyPayload(payload: unknown): string {
|
||||
if (typeof payload === "string") return payload;
|
||||
try {
|
||||
return JSON.stringify(payload);
|
||||
} catch {
|
||||
return String(payload);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -144,7 +144,7 @@ export async function restoreSectionCommit(
|
||||
|
||||
export async function submitSection(
|
||||
sectionId: string,
|
||||
input: { commit_id?: string; submitted_by?: string; user_id?: string }
|
||||
input: { submitted_by?: string; user_id?: string }
|
||||
): Promise<SectionSubmission> {
|
||||
return requestJson<SectionSubmission>(sectionUrl(sectionId, "submit"), jsonRequestInit("POST", input));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user