demo 20-4-2026

This commit is contained in:
taDuc
2026-04-19 23:43:31 +07:00
parent 57a7843d80
commit 2508172489
10 changed files with 1443 additions and 98 deletions

View File

@@ -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);
}
}