UNPKG

openapi-hooks

Version:

Magical fetch inference for OpenAPI with React Query support

248 lines (245 loc) 8.12 kB
type HTTPStatusCode = /** CONTINUE */ 100 /** SWITCHING_PROTOCOLS */ | 101 /** PROCESSING */ | 102 /** EARLY_HINTS */ | 103 /** OK */ | 200 /** CREATED */ | 201 /** ACCEPTED */ | 202 /** NON_AUTHORITATIVE_INFORMATION */ | 203 /** NO_CONTENT */ | 204 /** RESET_CONTENT */ | 205 /** PARTIAL_CONTENT */ | 206 /** MULTI_STATUS */ | 207 /** MULTIPLE_CHOICES */ | 300 /** MOVED_PERMANENTLY */ | 301 /** MOVED_TEMPORARILY */ | 302 /** SEE_OTHER */ | 303 /** NOT_MODIFIED */ | 304 /** USE_PROXY */ | 305 /** TEMPORARY_REDIRECT */ | 307 /** PERMANENT_REDIRECT */ | 308 /** BAD_REQUEST */ | 400 /** UNAUTHORIZED */ | 401 /** PAYMENT_REQUIRED */ | 402 /** FORBIDDEN */ | 403 /** NOT_FOUND */ | 404 /** METHOD_NOT_ALLOWED */ | 405 /** NOT_ACCEPTABLE */ | 406 /** PROXY_AUTHENTICATION_REQUIRED */ | 407 /** REQUEST_TIMEOUT */ | 408 /** CONFLICT */ | 409 /** GONE */ | 410 /** LENGTH_REQUIRED */ | 411 /** PRECONDITION_FAILED */ | 412 /** REQUEST_TOO_LONG */ | 413 /** REQUEST_URI_TOO_LONG */ | 414 /** UNSUPPORTED_MEDIA_TYPE */ | 415 /** REQUESTED_RANGE_NOT_SATISFIABLE */ | 416 /** EXPECTATION_FAILED */ | 417 /** IM_A_TEAPOT */ | 418 /** INSUFFICIENT_SPACE_ON_RESOURCE */ | 419 /** METHOD_FAILURE */ | 420 /** MISDIRECTED_REQUEST */ | 421 /** UNPROCESSABLE_ENTITY */ | 422 /** LOCKED */ | 423 /** FAILED_DEPENDENCY */ | 424 /** UPGRADE_REQUIRED */ | 426 /** PRECONDITION_REQUIRED */ | 428 /** TOO_MANY_REQUESTS */ | 429 /** REQUEST_HEADER_FIELDS_TOO_LARGE */ | 431 /** UNAVAILABLE_FOR_LEGAL_REASONS */ | 451 /** INTERNAL_SERVER_ERROR */ | 500 /** NOT_IMPLEMENTED */ | 501 /** BAD_GATEWAY */ | 502 /** SERVICE_UNAVAILABLE */ | 503 /** GATEWAY_TIMEOUT */ | 504 /** HTTP_VERSION_NOT_SUPPORTED */ | 505 /** INSUFFICIENT_STORAGE */ | 507 /** NETWORK_AUTHENTICATION_REQUIRED */ | 511; type HTTPMethod = "get" | "put" | "post" | "delete" | "options" | "head" | "patch" | "trace"; type RouteFor<TPaths, TPath extends keyof TPaths, TMethod extends HTTPMethod> = TMethod extends keyof TPaths[TPath] ? Extract<NonNullable<TPaths[TPath][TMethod]>, AnyRoute> : never; type EmptyParameters = { query?: never; header?: never; path?: never; cookie?: never; }; type RouteParameters<TParameters> = [Extract<TParameters, AnyParameters>] extends [ never ] ? EmptyParameters : Extract<TParameters, AnyParameters>; type PathMethods<TPaths, TPath extends keyof TPaths> = { [TMethod in HTTPMethod]: [RouteFor<TPaths, TPath, TMethod>] extends [never] ? never : TMethod; }[HTTPMethod]; type AnyRequestBody = { content: Record<string, any>; }; type AnyResponses = Record<number, { content?: Record<string, any>; headers?: Record<string, any>; }>; type AnyParameters = { query?: Record<string, any>; header?: Record<string, any>; path?: Record<string, any>; cookie?: Record<string, any>; }; type AnyRoute = { responses: AnyResponses; requestBody?: AnyRequestBody; parameters?: AnyParameters; }; type Prettify<T> = { [K in keyof T]: T[K]; } & {}; /** * ApiResponse is a utility type that infers the possible response shapes for an OpenAPI route. * * Given a set of possible HTTP responses (TResponses), it produces a union type representing * all possible response objects, including status, content type, data, and headers. * * For each status code (TStatus) in TResponses: * - If TResponses[TStatus]['content'] is undefined (i.e., no content for this status): * - The response object has: * - status: the status code * - contentType: never * - data: never * - headers: either a Map of header keys/values (if headers are a record), or the headers as-is * - If TResponses[TStatus]['content'] is defined: * - For each content type K in the content object: * - The response object has: * - status: the status code * - contentType: K (the MIME type) * - data: the data for that content type * - headers: either a Map of header keys/values (if headers are a record), or the headers as-is * * The final type is a union of all possible response objects for all status codes and content types. */ type ApiResponse<TResponses extends AnyResponses> = { [TStatus in keyof TResponses]: TStatus extends number ? TResponses[TStatus]["content"] extends undefined ? { status: TStatus; contentType: never; data: never; headers: TResponses[TStatus]["headers"] extends Record<string, unknown> ? Map<keyof TResponses[TStatus]["headers"], TResponses[TStatus]["headers"][keyof TResponses[TStatus]["headers"]]> : TResponses[TStatus]["headers"]; } : { [K in keyof TResponses[TStatus]["content"]]: { status: TStatus; contentType: K; data: TResponses[TStatus]["content"][K]; headers: TResponses[TStatus]["headers"] extends Record<string, unknown> ? Map<keyof TResponses[TStatus]["headers"], TResponses[TStatus]["headers"][keyof TResponses[TStatus]["headers"]]> : TResponses[TStatus]["headers"]; }; }[keyof TResponses[TStatus]["content"]] : never; }[keyof TResponses] | UnknownApiResponse<keyof TResponses>; type ExcludedStatusCodes<TStatus> = Exclude<HTTPStatusCode, TStatus> & {}; type UnknownApiResponse<TStatus> = { status: ExcludedStatusCodes<TStatus>; contentType?: string; data?: unknown; headers?: Headers; }; type AnyApiResponse = { status: number; contentType?: string; data?: unknown; headers?: Headers; }; type NoRequestBody = { contentType?: undefined; data?: undefined; }; type RequestBodyContentType<TBody extends AnyRequestBody | undefined> = Extract<keyof Extract<TBody, AnyRequestBody>["content"], string>; type ApiRequestBody<TBody extends AnyRequestBody | undefined> = [ Extract<TBody, AnyRequestBody> ] extends [never] ? NoRequestBody : { [K in RequestBodyContentType<TBody>]: { contentType: K; data: Extract<TBody, AnyRequestBody>["content"][K]; }; }[RequestBodyContentType<TBody>]; type HeaderObject = Record<string, string>; type HeaderPredicate = () => PromiseLike<HeaderObject>; type OpenApiHookOptions = { baseUrl: URL | string; headers?: HeaderObject | HeaderPredicate; onError?: (error: ApiError) => void; fetch?: typeof fetch; decodeResponse?: (response: Response, responseContentType: string | null) => Promise<AnyApiResponse>; encodeBody?: (data: any, contentType: string | undefined) => BodyInit | undefined; }; /** * FetchOptions is a type that represents the options for the fetch function. * It can be a RequestInit object or a function that takes a base RequestInit object and returns a RequestInit object. * The function is useful for adding additional headers or other properties to the request or modifying properties. */ type FetchOptions = RequestInit | ((base: Pick<RequestInit, "body" | "headers" | "method" | "mode">) => RequestInit); declare class ApiError extends Error { status: number; response?: Response | undefined; data?: unknown | undefined; constructor(message: string, status: number, response?: Response | undefined, data?: unknown | undefined); static fromResponse(response: Response, data?: unknown): ApiError; static fromNetworkError(error: Error): ApiError; } type OptionsFor<TRoute extends AnyRoute> = RouteParameters<TRoute["parameters"]> & ApiRequestBody<TRoute["requestBody"]> & { fetchOptions?: FetchOptions; }; declare const createFetch: <paths extends object>(options?: OpenApiHookOptions) => <TPath extends keyof paths & string, TMethod extends PathMethods<paths, TPath>, TRoute extends AnyRoute = RouteFor<paths, TPath, TMethod>, TOptions extends OptionsFor<TRoute> = OptionsFor<TRoute>>(path: TPath, method: TMethod, options: TOptions) => Promise<Prettify<ApiResponse<TRoute["responses"]>>>; export { type AnyApiResponse, ApiError, type ApiRequestBody, type ApiResponse, type FetchOptions, type HeaderObject, type HeaderPredicate, type OpenApiHookOptions, type OptionsFor, type PathMethods, createFetch };