fets
Version:
TypeScript HTTP Framework focusing on e2e type-safety, easy setup, performance & great developer experience
220 lines (219 loc) • 13.2 kB
TypeScript
import { StatusCodeMap } from './types';
export type OkStatusCode = 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 226;
type RedirectStatusCode = 300 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | 308;
type ClientErrorStatusCode = 400 | 401 | 402 | 403 | 404 | 405 | 406 | 407 | 408 | 409 | 410 | 411 | 412 | 413 | 414 | 415 | 416 | 417 | 418 | 421 | 422 | 423 | 424 | 425 | 426 | 428 | 429 | 431 | 451;
type ServerErrorStatusCode = 500 | 501 | 502 | 503 | 504 | 505 | 506 | 507 | 508 | 510 | 511;
export type StatusCode = OkStatusCode | RedirectStatusCode | ClientErrorStatusCode | ServerErrorStatusCode;
export type NotOkStatusCode = Exclude<StatusCode, OkStatusCode>;
export type TypedBody<TJSON, TFormData extends Record<string, FormDataEntryValue | undefined>, THeaders extends Record<string, string | undefined>> = Omit<Body, 'json' | 'formData' | 'headers'> & {
/**
* The `json()` method takes the stream and reads it to completion.
* It returns a promise which resolves with the result of parsing the body text as JSON.
*
* Note that despite the method being named `json()`, the result is not JSON but is instead the result of taking JSON as input and parsing it to produce a JavaScript object.
*
* @see https://developer.mozilla.org/en-US/docs/Web/API/Response/json
*/
json(): Promise<TJSON>;
/**
* The formData() method takes the stream and reads it to completion. It returns a promise that resolves with a `FormData` object.
*
* @see https://developer.mozilla.org/en-US/docs/Web/API/Response/formData
*/
formData(): Promise<TypedFormData<TFormData>>;
/**
* The headers read-only property of the Response interface contains the Headers object associated with the response.
*
* @see https://developer.mozilla.org/en-US/docs/Web/API/Response/headers
*/
headers: TypedHeaders<THeaders>;
};
type DefaultHTTPHeaders = 'accept' | 'accept-language' | 'content-language' | 'content-type' | 'content-length';
type Maybe = undefined | null;
type UndefinedToNull<T> = T extends undefined ? Exclude<T, undefined> | null : T;
export interface TypedHeaders<TMap extends Record<string, string | undefined>> {
append<TName extends DefaultHTTPHeaders | keyof TMap>(name: TName, value: TName extends keyof TMap ? TMap[TName] : string): void;
delete<TName extends DefaultHTTPHeaders | keyof TMap>(name: TName): void;
get<TName extends DefaultHTTPHeaders | keyof TMap>(name: TName): TName extends keyof TMap ? UndefinedToNull<TMap[TName]> : TName extends DefaultHTTPHeaders ? string | null : never;
has<TName extends DefaultHTTPHeaders | keyof TMap>(name: TName): TName extends DefaultHTTPHeaders ? boolean : TName extends keyof TMap ? TMap[TName] extends Maybe ? boolean : true : never;
set<TName extends DefaultHTTPHeaders | keyof TMap>(name: TName, value: TName extends keyof TMap ? TMap[TName] : string): void;
forEach(callbackfn: <TName extends keyof TMap>(value: TMap[TName], key: TName, parent: TypedHeaders<TMap>) => void, thisArg?: any): void;
entries(): IterableIterator<[keyof TMap, TMap[keyof TMap]]>;
keys(): IterableIterator<keyof TMap>;
values(): IterableIterator<TMap[keyof TMap]>;
[Symbol.iterator](): IterableIterator<[keyof TMap, TMap[keyof TMap]]>;
getSetCookie(): string[];
}
export type TypedHeadersCtor = new <TMap extends Record<string, string>>(init?: TMap) => TypedHeaders<TMap>;
export type TypedResponseInit<TStatusCode extends StatusCode = 200> = Omit<ResponseInit, 'status' | 'statusText'> & {
/**
* This is the status code that will be set in the response.
* For example, 200 for success, 404 if the resource could not be found.
*
* @see https://developer.mozilla.org/en-US/docs/Web/API/Response/status
*/
status: TStatusCode;
/**
* This is the status message that will be set in the response.
* You don't need to set this value if it's a standard message.
* For example, this would be OK for a status code 200, Continue for 100, Not Found for 404.
*
* @see https://developer.mozilla.org/en-US/docs/Web/API/Response/statusText
*/
statusText?: TStatusCode extends keyof StatusTextMap ? StatusTextMap[TStatusCode] : string;
};
export type StatusTextMap = {
100: 'Continue';
101: 'Switching Protocols';
102: 'Processing';
103: 'Early Hints';
200: 'OK';
201: 'Created';
202: 'Accepted';
203: 'Non-Authoritative Information';
204: 'No Content';
205: 'Reset Content';
206: 'Partial Content';
207: 'Multi-Status';
208: 'Already Reported';
226: 'IM Used';
300: 'Multiple Choices';
301: 'Moved Permanently';
302: 'Found';
303: 'See Other';
304: 'Not Modified';
305: 'Use Proxy';
307: 'Temporary Redirect';
308: 'Permanent Redirect';
400: 'Bad Request';
401: 'Unauthorized';
402: 'Payment Required';
403: 'Forbidden';
404: 'Not Found';
405: 'Method Not Allowed';
406: 'Not Acceptable';
407: 'Proxy Authentication Required';
408: 'Request Timeout';
409: 'Conflict';
410: 'Gone';
411: 'Length Required';
412: 'Precondition Failed';
413: 'Payload Too Large';
414: 'URI Too Long';
415: 'Unsupported Media Type';
416: 'Range Not Satisfiable';
417: 'Expectation Failed';
418: "I'm a Teapot";
421: 'Misdirected Request';
422: 'Unprocessable Entity';
423: 'Locked';
424: 'Failed Dependency';
425: 'Too Early';
426: 'Upgrade Required';
428: 'Precondition Required';
429: 'Too Many Requests';
431: 'Request Header Fields Too Large';
451: 'Unavailable For Legal Reasons';
500: 'Internal Server Error';
501: 'Not Implemented';
502: 'Bad Gateway';
503: 'Service Unavailable';
504: 'Gateway Timeout';
505: 'HTTP Version Not Supported';
506: 'Variant Also Negotiates';
507: 'Insufficient Storage';
508: 'Loop Detected';
509: 'Bandwidth Limit Exceeded';
510: 'Not Extended';
511: 'Network Authentication Required';
};
export type TypedResponse<TJSON = unknown, THeaders extends Record<string, string> = Record<string, string>, TStatusCode extends StatusCode = StatusCode> = Omit<Response, 'json' | 'status' | 'ok'> & Omit<TypedBody<TJSON, any, THeaders>, 'formData'> & {
/**
* The status read-only property of the Response interface contains the HTTP status codes of the response.
* @see https://developer.mozilla.org/en-US/docs/Web/API/Response/status
*
* You can use `Response.ok` to check if the status is in the range 200-299.
*/
status: TStatusCode;
/**
* The statusText read-only property of the Response interface contains the status message corresponding to the HTTP status code in Response.status.
* For example, this would be OK for a status code 200, Continue for 100, Not Found for 404.
* @see https://developer.mozilla.org/en-US/docs/Web/API/Response/statusText
*/
statusText: TStatusCode extends keyof StatusTextMap ? StatusTextMap[TStatusCode] : string;
} & {
/**
* The ok read-only property of the Response interface contains a Boolean stating whether the response was successful (status in the range 200-299) or not.
*/
ok: TStatusCode extends OkStatusCode ? true : false;
};
export type JSONofResponse<TResponse extends TypedResponse> = TResponse extends TypedResponse<infer TJSON> ? TJSON : never;
export type TypedResponseCtor<TTypedResponse extends TypedResponse = TypedResponse> = Omit<typeof Response, 'json'> & {
new <TStatusCode extends TTypedResponse['status'] = 200>(body: BodyInit | null | undefined, init: (TypedResponseInit<TStatusCode> & {
status: TStatusCode;
}) | undefined): TypedResponse<any, Record<string, string>, TStatusCode>;
new (body: BodyInit | null | undefined, init: ResponseInit | undefined): TypedResponse<any, Record<string, string>, 200>;
new (body?: BodyInit | null | undefined): TypedResponse<any, Record<string, string>, 200>;
/**
* The `json()` static method of the `Response` interface returns a `Response` that contains the provided JSON data as body,
* and a `Content-Type` header which is set to `application/json`. The response status, status message, and additional headers can also be set.
* @param data The JSON data to be used as the response body.
* @param options An options object containing settings for the response, including the status code, status text, and headers. This is the same as the options parameter of the `Response()` constructor.
*
* @see https://developer.mozilla.org/en-US/docs/Web/API/Response/json_static
*/
json<TJSON extends JSONofResponse<TTypedResponse>, TStatusCode extends TTypedResponse['status']>(data: TJSON, options: TypedResponseInit<TStatusCode>): TypedResponse<TJSON, Record<string, string>, TStatusCode>;
/**
* The `json()` static method of the `Response` interface returns a `Response` that contains the provided JSON data as body,
* and a `Content-Type` header which is set to `application/json`.
* @param data The JSON data to be used as the response body.
*
* @see https://developer.mozilla.org/en-US/docs/Web/API/Response/json_static
*/
json<TJSON extends JSONofResponse<TTypedResponse>>(data: TJSON): TypedResponse<TJSON, Record<string, string>, 200>;
/**
* The redirect() static method of the Response interface returns a Response resulting in a redirect to the specified URL.
* @param url The URL that the new response is to originate from.
* @param status An optional status code for the response (e.g., 302.)
*
* @see https://developer.mozilla.org/en-US/docs/Web/API/Response/redirect_static
*/
redirect<TStatusCode extends TTypedResponse['status']>(url: string | URL, status: TStatusCode): TypedResponse<any, Record<string, string>, TStatusCode>;
/**
* The redirect() static method of the Response interface returns a Response resulting in a redirect to the specified URL.
* @param url The URL that the new response is to originate from.
*
* @see https://developer.mozilla.org/en-US/docs/Web/API/Response/redirect_static
*/
redirect(url: string | URL): TypedResponse<any, Record<string, string>, 302>;
};
export type TypedResponseWithJSONStatusMap<TResponseJSONStatusMap extends StatusCodeMap<any>> = {
[TStatusCode in keyof TResponseJSONStatusMap]: TStatusCode extends StatusCode ? TypedResponse<TResponseJSONStatusMap[TStatusCode], Record<string, string>, TStatusCode> : never;
}[keyof TResponseJSONStatusMap];
export type HTTPMethod = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE' | 'HEAD' | 'OPTIONS' | 'CONNECT' | 'TRACE';
export type TypedRequestInit<THeaders extends Record<string, string | undefined>, TMethod extends HTTPMethod, TFormData extends Record<string, FormDataEntryValue | undefined>> = Omit<RequestInit, 'method' | 'headers' | 'body'> & {
method: TMethod;
headers: TypedHeaders<THeaders>;
body?: Exclude<BodyInit, FormData> | TFormData;
};
export type TypedRequest<TJSON = any, TFormData extends Record<string, FormDataEntryValue | undefined> = Record<string, FormDataEntryValue | undefined>, THeaders extends Record<string, string | undefined> = Record<string, string | undefined>, TMethod extends HTTPMethod = HTTPMethod, TQueryParams = any, TPathParams extends Record<string, any> = Record<string, any>> = Omit<Request, 'json' | 'method' | 'headers' | 'formData'> & TypedBody<TJSON, TFormData, THeaders> & {
parsedUrl: URL;
method: TMethod;
params: TPathParams;
query: TQueryParams;
};
export type TypedRequestCtor = new <THeaders extends Record<string, string | undefined>, TMethod extends HTTPMethod, TFormData extends Record<string, FormDataEntryValue | undefined>>(input: string | URL, init?: TypedRequestInit<THeaders, TMethod, TFormData>) => TypedRequest<any, TFormData, THeaders, TMethod, any, any>;
export interface TypedFormData<TMap extends Record<string, FormDataEntryValue | undefined> = Record<string, FormDataEntryValue | undefined>> {
append<TName extends keyof TMap>(name: TName, value: TMap[TName] extends any[] ? TMap[TName][0] : TMap[TName], fileName?: string): void;
delete(name: keyof TMap): void;
get<TName extends keyof TMap>(name: TName): TMap[TName] extends any[] ? TMap[TName][0] : TMap[TName];
getAll<TName extends keyof TMap>(name: TName): TMap[TName] extends any[] ? TMap[TName] : TMap[TName][];
has<TName extends string>(name: TName): TName extends keyof TMap ? (TMap[TName] extends Maybe ? boolean : true) : false;
set<TName extends keyof TMap>(name: TName, value: TMap[TName] extends any[] ? TMap[TName][0] : TMap[TName], fileName?: string): void;
forEach(callbackfn: <TName extends keyof TMap>(value: TMap[TName], key: TName, parent: this) => void, thisArg?: any): void;
entries(): IterableIterator<[keyof TMap, TMap[keyof TMap]]>;
keys(): IterableIterator<keyof TMap>;
values(): IterableIterator<TMap[keyof TMap]>;
[Symbol.iterator](): IterableIterator<[keyof TMap, TMap[keyof TMap]]>;
}
export {};