UNPKG

@zimic/http

Version:

Next-gen TypeScript-first HTTP utilities

173 lines (156 loc) 7.56 kB
import { Default, DefaultNoExclude, IfNever, Replace, JSONValue } from '@zimic/utils/types'; import { HttpMethodSchema, HttpRequestSchema, HttpResponseSchema, HttpStatusCode } from '@/types/schema'; import HttpFormData from '../formData/HttpFormData'; import { HttpFormDataSchema } from '../formData/types'; import HttpHeaders from '../headers/HttpHeaders'; import { HttpHeadersSchema } from '../headers/types'; import HttpSearchParams from '../searchParams/HttpSearchParams'; import { HttpSearchParamsSchema } from '../searchParams/types'; /** The body type for HTTP requests and responses. */ // eslint-disable-next-line @typescript-eslint/no-explicit-any export type HttpBody = JSONValue | HttpFormData<any> | HttpSearchParams<any> | Blob | ArrayBuffer | ReadableStream; export namespace HttpBody { /** A loose version of the HTTP body type. JSON values are not strictly typed. */ export type Loose = Replace<HttpBody, JSONValue, JSONValue.Loose>; } /** * An HTTP headers object with a strictly-typed schema. Fully compatible with the built-in * {@link https://developer.mozilla.org/docs/Web/API/Headers `Headers`} class. */ export type StrictHeaders<Schema extends HttpHeadersSchema.Loose = HttpHeadersSchema.Loose> = Pick< HttpHeaders<Schema>, keyof Headers >; /** * An HTTP search params object with a strictly-typed schema. Fully compatible with the built-in * {@link https://developer.mozilla.org/docs/Web/API/URLSearchParams `URLSearchParams`} class. */ export type StrictURLSearchParams<Schema extends HttpSearchParamsSchema.Loose = HttpSearchParamsSchema.Loose> = Pick< HttpSearchParams<Schema>, keyof URLSearchParams >; /** * An HTTP form data object with a strictly-typed schema. Fully compatible with the built-in * {@link https://developer.mozilla.org/docs/Web/API/FormData `FormData`} class. */ export type StrictFormData<Schema extends HttpFormDataSchema.Loose = HttpFormDataSchema.Loose> = Pick< HttpFormData<Schema>, keyof FormData >; /** * An HTTP request with a strictly-typed JSON body. Fully compatible with the built-in * {@link https://developer.mozilla.org/docs/Web/API/Request `Request`} class. */ export interface HttpRequest< StrictBody extends HttpBody.Loose = HttpBody.Loose, StrictHeadersSchema extends HttpHeadersSchema.Loose = HttpHeadersSchema.Loose, > extends Request { headers: StrictHeaders<StrictHeadersSchema>; text: () => Promise<StrictBody extends string ? StrictBody : string>; json: () => Promise< StrictBody extends string | Exclude<HttpBody, JSONValue> ? never : Replace<StrictBody, null | undefined, never> >; formData: () => Promise< StrictBody extends HttpFormData<infer HttpFormDataSchema> ? StrictFormData<HttpFormDataSchema> : StrictBody extends HttpSearchParams<infer HttpSearchParamsSchema> ? StrictFormData<HttpSearchParamsSchema> : StrictBody extends null | undefined ? never : FormData >; clone: () => this; } /** * An HTTP response with a strictly-typed JSON body and status code. Fully compatible with the built-in * {@link https://developer.mozilla.org/docs/Web/API/Response `Response`} class. */ export interface HttpResponse< StrictBody extends HttpBody.Loose = HttpBody.Loose, StrictHeadersSchema extends HttpHeadersSchema.Loose = HttpHeadersSchema.Loose, StatusCode extends number = number, > extends Response { ok: StatusCode extends HttpStatusCode.Information | HttpStatusCode.Success | HttpStatusCode.Redirection ? true : false; status: StatusCode; headers: StrictHeaders<StrictHeadersSchema>; text: () => Promise<StrictBody extends string ? StrictBody : string>; json: () => Promise< StrictBody extends string | Exclude<HttpBody, JSONValue> ? never : Replace<StrictBody, null | undefined, never> >; formData: () => Promise< StrictBody extends HttpFormData<infer HttpFormDataSchema> ? StrictFormData<HttpFormDataSchema> : StrictBody extends HttpSearchParams<infer HttpSearchParamsSchema> ? StrictFormData<HttpSearchParamsSchema> : StrictBody extends null | undefined ? never : FormData >; clone: () => this; } type HttpRequestHeadersSchemaFromBody< RequestSchema extends HttpRequestSchema, DefaultHeadersSchema, > = 'body' extends keyof RequestSchema ? [RequestSchema['body']] extends [never] ? DefaultHeadersSchema : [Extract<RequestSchema['body'], BodyInit | HttpFormData | HttpSearchParams>] extends [never] ? 'headers' extends keyof RequestSchema ? [RequestSchema['headers']] extends [never] ? DefaultHeadersSchema : 'content-type' extends keyof Default<RequestSchema['headers']> ? DefaultHeadersSchema : { 'content-type': 'application/json' } : { 'content-type': 'application/json' } : DefaultHeadersSchema : DefaultHeadersSchema; export type HttpRequestHeadersSchema<MethodSchema extends HttpMethodSchema> = 'headers' extends keyof MethodSchema['request'] ? [MethodSchema['request']['headers']] extends [never] ? HttpRequestHeadersSchemaFromBody<Default<MethodSchema['request']>, never> : | (MethodSchema['request']['headers'] & HttpRequestHeadersSchemaFromBody<Default<MethodSchema['request']>, {}>) | Extract<MethodSchema['request']['headers'], undefined> : HttpRequestHeadersSchemaFromBody<Default<MethodSchema['request']>, never>; export type HttpRequestSearchParamsSchema<MethodSchema extends HttpMethodSchema> = 'searchParams' extends keyof MethodSchema['request'] ? Default<MethodSchema['request']>['searchParams'] : never; export type HttpRequestBodySchema<MethodSchema extends HttpMethodSchema> = Replace< IfNever<DefaultNoExclude<Default<MethodSchema['request']>['body']>, null>, undefined, null >; type HttpResponseHeadersSchemaFromBody< ResponseSchema extends HttpResponseSchema, DefaultHeadersSchema, > = 'body' extends keyof ResponseSchema ? [ResponseSchema['body']] extends [never] ? DefaultHeadersSchema : [Extract<ResponseSchema['body'], BodyInit | HttpSearchParams | HttpFormData>] extends [never] ? 'headers' extends keyof ResponseSchema ? [ResponseSchema['headers']] extends [never] ? DefaultHeadersSchema : 'content-type' extends keyof Default<ResponseSchema['headers']> ? DefaultHeadersSchema : { 'content-type': 'application/json' } : { 'content-type': 'application/json' } : DefaultHeadersSchema : DefaultHeadersSchema; export type HttpResponseHeadersSchema< MethodSchema extends HttpMethodSchema, StatusCode extends HttpStatusCode, > = 'headers' extends keyof Default<MethodSchema['response']>[StatusCode] ? [Default<MethodSchema['response']>[StatusCode]] extends [never] ? HttpResponseHeadersSchemaFromBody<Default<Default<MethodSchema['response']>[StatusCode]>, never> : | (Default<Default<MethodSchema['response']>[StatusCode]>['headers'] & HttpResponseHeadersSchemaFromBody<Default<Default<MethodSchema['response']>[StatusCode]>, {}>) | Extract<Default<Default<MethodSchema['response']>[StatusCode]>['headers'], undefined> : HttpResponseHeadersSchemaFromBody<Default<Default<MethodSchema['response']>[StatusCode]>, never>; export type HttpResponseBodySchema<MethodSchema extends HttpMethodSchema, StatusCode extends HttpStatusCode> = Replace< IfNever<DefaultNoExclude<Default<Default<MethodSchema['response']>[StatusCode]>['body']>, null>, undefined, null >;