export interface FetchOptions extends RequestInit { params?: Record timeout?: number } export interface FetchResponse { data: T status: number statusText: string ok: boolean error?: string } export class FetchError extends Error { constructor( public message: string, public status?: number, public response?: any ) { super(message) this.name = 'FetchError' } } /** * Fetch wrapper with timeout support and axios-like interface */ async function fetchWithTimeout( url: string, options: FetchOptions = {} ): Promise { const { timeout = 30000, ...fetchOptions } = options const controller = new AbortController() const timeoutId = setTimeout(() => controller.abort(), timeout) try { const response = await fetch(url, { ...fetchOptions, signal: controller.signal, }) clearTimeout(timeoutId) return response } catch (error) { clearTimeout(timeoutId) throw error } } /** * GET request */ export async function get( url: string, options?: FetchOptions ): Promise> { try { const response = await fetchWithTimeout(url, { ...options, method: 'GET', headers: { 'Content-Type': 'application/json', ...options?.headers, }, }) const data = await response.json() return { data, status: response.status, statusText: response.statusText, ok: response.ok, error: response.ok ? undefined : data?.message || data?.error || response.statusText, } } catch (error) { throw new FetchError( error instanceof Error ? error.message : 'Network request failed', undefined, error ) } } /** * POST request */ export async function post( url: string, data?: any, options?: FetchOptions ): Promise> { try { const response = await fetchWithTimeout(url, { ...options, method: 'POST', headers: { 'Content-Type': 'application/json', ...options?.headers, }, body: JSON.stringify(data), }) const responseData = await response.json() return { data: responseData, status: response.status, statusText: response.statusText, ok: response.ok, error: response.ok ? undefined : responseData?.message || responseData?.error || response.statusText, } } catch (error) { throw new FetchError( error instanceof Error ? error.message : 'Network request failed', undefined, error ) } } /** * DELETE request */ export async function del( url: string, options?: FetchOptions ): Promise> { try { const response = await fetchWithTimeout(url, { ...options, method: 'DELETE', headers: { 'Content-Type': 'application/json', ...options?.headers, }, }) const data = await response.json().catch(() => ({})) return { data, status: response.status, statusText: response.statusText, ok: response.ok, error: response.ok ? undefined : data?.message || data?.error || response.statusText, } } catch (error) { throw new FetchError( error instanceof Error ? error.message : 'Network request failed', undefined, error ) } } export const fetchClient = { get, post, delete: del, }