shelving
Version:
Toolkit for using data in JavaScript.
186 lines (185 loc) • 10.3 kB
TypeScript
import { type Data } from "./data.js";
import type { ImmutableDictionary } from "./dictionary.js";
import type { AnyCaller, Arguments } from "./function.js";
import { type Nullish } from "./null.js";
import { type PossibleURIParams } from "./uri.js";
import { type PossibleURL } from "./url.js";
/** A handler function takes a `Request` and optional extra arguments and returns a `Response` (possibly asynchronously). */
export type RequestHandler<A extends Arguments = []> = (request: Request, ...args: A) => Response | Promise<Response>;
/** An optional request handler that may return `undefined` to indicate no match. */
export type OptionalRequestHandler<A extends Arguments = []> = (request: Request, ...args: A) => Response | Promise<Response> | undefined;
/** A list of optional request handlers. */
export type OptionalRequestHandlers<A extends Arguments = []> = Iterable<OptionalRequestHandler<A>>;
/**
* Parse the body content of an HTTP `Request` based on its content type, or throw `RequestError` if the content could not be parsed.
*
* @returns undefined If the request method is `GET` or `HEAD` (these request methods have no body).
* @returns unknown If content type is `application/json` and has valid JSON (including `undefined` if the content is empty).
* @returns unknown If content type is `multipart/form-data` then convert it to a simple `Data` object.
* @returns string If content type is `text/plain` or anything else (including `""` empty string if it's empty).
*
* @throws RequestError if the content is not `text/plain`, or `application/json` with valid JSON.
*/
export declare function parseRequestBody(request: Request, caller?: AnyCaller): Promise<unknown>;
/**
* Parse JSON from an HTTP `Request`, or return `undefined` when the request has no body.
*
* @throws RequestError If the request body is not valid JSON.
*/
export declare function parseRequestJSON(request: Request, caller?: AnyCaller): Promise<unknown>;
/**
* Parse `FormData` from an HTTP `Request`, or return `undefined` when the request has no body.
*
* @throws RequestError If the request body is not valid multipart form-data.
*/
export declare function parseRequestFormData(request: Request, caller?: AnyCaller): Promise<FormData | undefined>;
/**
* Parse the body content of an HTTP `Response` based on its content type, or throw `ResponseError` if the content could not be parsed.
*
* @returns unknown If content type is `application/json` and has valid JSON (including `undefined` if the content is empty).
* @returns unknown If content type is `multipart/form-data` then convert it to a simple `Data` object.
* @returns string If content type is `text/plain` or anything else (including `""` empty string if it's empty).
*
* @throws ResponseError if the content is not `text/plain` or `application/json` with valid JSON.
*/
export declare function parseResponseBody(response: Response, caller?: AnyCaller): Promise<unknown>;
/**
* Parse JSON from an HTTP `Response`, or return `undefined` when the response has no body.
*
* @throws ResponseError If the response body is not valid JSON.
*/
export declare function parseResponseJSON(response: Response, caller?: AnyCaller): Promise<unknown>;
/**
* Parse `FormData` from an HTTP `Response`, or return `undefined` when the response has no body.
*
* @throws ResponseError If the response body is not valid multipart form-data.
*/
export declare function parseResponseFormData(response: Response, caller?: AnyCaller): Promise<FormData>;
/**
* Get an HTTP `Response` for an unknown value.
*
* @param value The value to convert to a `Response`.
* @returns A `Response` with a 2xx status, and response body as JSON (if it was set), or no body if `value` is `undefined`
*/
export declare function getResponse(value: unknown): Response;
/**
* Get an HTTP `Response` for an unknown error value.
*
* Returns the correct `Response` based on the type of error thrown:
* - If `reason` is a `Response` instance, return it directly.
* - If `reason` is a string, return a 422 response with the string message, e.g. `"Invalid input"`
* - If `reason` is an `RequestError` instance, return a response with the error's message and code (but only if `debug` is true so we don't leak error details to the client).
* - If `reason` is an `Error` instance, return a 500 response with the error's message (but only if `debug` is true so we don't leak error details to the client).
* - Anything else returns a 500 response.
*
* @param reason The error value to convert to a `Response`.
* @param debug If `true` include the error message in the response (for debugging), or `false` to return generic error codes (for security).
*/
export declare function getErrorResponse(reason: unknown, debug?: boolean): Response;
/** HTTP request methods that have no body. */
export type RequestHeadMethod = "HEAD" | "GET";
/** HTTP request methods that have a body. */
export type RequestBodyMethod = "POST" | "PUT" | "PATCH" | "DELETE";
/** HTTP request methods. */
export type RequestMethod = RequestHeadMethod | RequestBodyMethod;
/** Check whether an HTTP Request method string is a supported request methods. */
export declare function isRequestMethod(method: string): method is RequestMethod;
/** Check whether an HTTP Request method string is a supported request method that never sends a body. */
export declare function isRequestHeadMethod(method: string): method is RequestHeadMethod;
/** Params in requests are a dictionary of strings. */
export type RequestParams = ImmutableDictionary<string>;
/** Configurable options for endpoint requests. */
export type RequestOptions = Pick<RequestInit, "cache" | "credentials" | "headers" | "integrity" | "keepalive" | "mode" | "redirect" | "referrer" | "referrerPolicy" | "signal">;
/**
* Merge provider-level and call-level request options.
* - Scalar options from `b` override `a`.
* - Header dictionaries are merged so call-level headers override default headers by key.
* - Abort signals are merged, so either abort signal will cancel the request.
*/
export declare function mergeRequestOptions({ headers: aHeaders, signal: aSignal, ...a }?: RequestOptions, { headers: bHeaders, signal: bSignal, ...b }?: RequestOptions): RequestOptions;
/**
* Create a body-less `Request`.
* - `HEAD` and `GET` requests never send a body.
*
* @param method The HTTP method.
* @param url The target URL.
* @param params `?query` params to encode into the URL.
* @param options Additional request options.
* @returns A `Request` with no body content.
*
* @example createHeadRequest("POST", "https://api.example.com/items", { name: "abc" })
*/
export declare function createHeadRequest(method: RequestHeadMethod, url: PossibleURL, params: Nullish<PossibleURIParams>, options?: RequestOptions, caller?: AnyCaller): Request;
/**
* Create a plain-text `Request`.
*
* - `HEAD` and `GET` requests never send a body.
*
* @param method The HTTP method.
* @param url The target URL.
* @param body The plain-text request body.
* @param options Additional request options.
* @returns A `Request` with `text/plain` content type.
*
* @example createTextRequest("POST", "https://api.example.com/items", "hello")
*/
export declare function createTextRequest(method: RequestMethod, url: PossibleURL, body: string, options?: RequestOptions, caller?: AnyCaller): Request;
/**
* Create a JSON `Request`.
* - `HEAD` and `GET` requests never send a body.
* - If the JSON body is a data object for `HEAD` or `GET`, it is appended as `?query` params instead.
*
* @param method The HTTP method.
* @param url The target URL.
* @param body The value to JSON-encode.
* @param options Additional request options.
* @returns A `Request` with `application/json` content type.
*
* @example createJSONRequest("POST", "https://api.example.com/items", { name: "abc" })
*/
export declare function createJSONRequest(method: RequestBodyMethod, url: PossibleURL, body: unknown, options?: RequestOptions, caller?: AnyCaller): Request;
/**
* Create a multipart form-data `Request`.
* - `HEAD` and `GET` requests never send a body.
*
* @param method The HTTP method.
* @param url The target URL.
* @param body The `FormData` payload.
* @param options Additional request options.
* @returns A `Request` with a multipart body.
*
* @example createFormDataRequest("POST", "https://api.example.com/upload", new FormData())
*/
export declare function createFormDataRequest(method: RequestBodyMethod, url: PossibleURL, body: FormData, options?: RequestOptions, caller?: AnyCaller): Request;
/**
* Create an XML `Request`.
* - `HEAD` and `GET` requests never send a body.
* - For `HEAD` and `GET`, the data object is appended as `?query` params instead.
*
* @param method The HTTP method.
* @param url The target URL.
* @param data The data object to serialize as XML.
* @param options Additional request options.
* @returns A `Request` with `application/xml` content type.
*
* @throws {RequiredError} If the XML data contains invalid element names or values.
*
* @example createXMLRequest("POST", "https://api.example.com/items", { item: { name: "abc" } })
*/
export declare function createXMLRequest(method: RequestBodyMethod, url: PossibleURL, data: Data, options?: RequestOptions, caller?: AnyCaller): Request;
/**
* Create a `Request` instance with a valid content type based on the body.
* - `undefined` or `null` are sent with no body.
* - `FormData` is sent with `multipart/formdata`
* - `string` is sent with `text/plain` header.
* - Anything else is sent as `application/json`
* - Expects a fully valid URL (any `{placeholders}` in the URL are not considered).
* - As per the HTTP spec, `GET` and `HEAD` requests cannot contain a body
*
* @returns Request object.
*
* @throws {RequiredError} if this is a `HEAD` or `GET` request but `body` is not a data object.
*/
export declare function createRequest(method: RequestMethod, url: PossibleURL, payload: unknown, options?: RequestOptions, caller?: AnyCaller): Request;
/** Assert that the payload for a HEAD or GET method is a data object, null, or undefined. */
export declare function assertRequestHeadPayload(payload: unknown, method: RequestHeadMethod, caller?: AnyCaller): asserts payload is Nullish<Data>;