import { ApiResponse } from "./runtime"; /** * Type to access a response's data property for a given status. */ type DataType = T extends { status: S } ? T["data"] : never; /** * Object with methods to handle possible status codes of an ApiResponse. */ type ResponseHandler = { [P in T["status"]]: (res: DataType) => any; } & { default?: (status: number, data: any) => any; }; /** * Utility function to handle different status codes. * * Example: * * const userId = await handle(api.register({ email, password }), { * 200: (user: User) => user.id, * 400: (err: string) => console.log(err), * }) **/ export async function handle< T extends ApiResponse, H extends ResponseHandler >(promise: Promise, handler: H): Promise> { const { status, data } = await promise; const statusHandler = (handler as any)[status]; if (statusHandler) return statusHandler(data); if (handler.default) return handler.default(status, data); throw new HttpError(status, data); } const SUCCESS_CODES = [200, 201, 202, 204] as const; type SuccessCodes = typeof SUCCESS_CODES[number]; type SuccessResponse = DataType; /** * Utility function to directly return any successful response * and throw a HttpError otherwise. * * Example: * * try { * const userId = await ok(api.register({ email, password })); * } * catch (err) { * console.log(err.status) * } */ export async function ok( promise: Promise ): Promise> { const res = await promise; if (SUCCESS_CODES.some((s) => s == res.status)) return res.data; throw new HttpError(res.status, res.data); } export type Args = T extends (...args: infer U) => any ? U : any; export type ApiFunction = (...args: any[]) => Promise; export type AsyncReturnType = T extends (...args: any[]) => Promise ? V : never; export type OkResponse = SuccessResponse< AsyncReturnType >; export type Okify = ( ...args: Args ) => Promise>; /** * Utility function to wrap an API function with `ok(...)`. */ export function okify(fn: T): Okify { return (...args: Args) => ok(fn(...args)); } type OptimisticApi = { [K in keyof T]: T[K] extends ApiFunction ? Okify : T[K]; }; /** * Utility to `okify` each function of an API. */ export function optimistic>( api: T ): OptimisticApi { const okApi: any = {}; Object.entries(api).forEach(([key, value]) => { okApi[key] = typeof value === "function" ? okify(value as any) : value; }); return okApi; } export class HttpError extends Error { status: number; data?: any; constructor(status: number, data: any) { super(`Error: ${status}`); this.status = status; this.data = data; } }