diff --git a/lib/rollup.config.js b/lib/rollup.config.js index 2e71cba..bda7e4f 100644 --- a/lib/rollup.config.js +++ b/lib/rollup.config.js @@ -6,12 +6,9 @@ import typescript from '@rollup/plugin-typescript'; import terser from '@rollup/plugin-terser'; import json from '@rollup/plugin-json'; import progress from 'rollup-plugin-progress'; +import { dts } from 'rollup-plugin-dts'; -const input = [ - './src/index.ts', - ...getFiles('./src/core'), - ...getFiles('./src/utils'), -]; +const input = ['./src/index.ts', ...getFiles('./src/core'), ...getFiles('./src/utils')]; const external = []; @@ -56,6 +53,11 @@ const options = [ /** * ES Modules and Types */ + { + input: './src/index.ts', + output: [{ file: 'types.d.ts', format: 'es' }], + plugins: [dts()], + }, { input, output: { diff --git a/lib/types.d.ts b/lib/types.d.ts new file mode 100644 index 0000000..41a3e4d --- /dev/null +++ b/lib/types.d.ts @@ -0,0 +1,263 @@ +declare const HttpMethods: readonly ["OPTIONS", "GET", "HEAD", "PUT", "POST", "DELETE", "PATCH"]; +declare const ResponseParsers: readonly ["ArrayBuffer", "Blob", "FormData", "JSON", "Text"]; + +type HttpMethod = (typeof HttpMethods)[number]; +type ResponseParser = (typeof ResponseParsers)[number]; +type SearchParamInput = string | URLSearchParams | [string, string | boolean | number][] | Record; +type FetchtasticOptions = Omit; +interface FetchOptions extends RequestInit { + method: HttpMethod; +} +type FetchRequestHeader = 'Accept' | 'Content-Type' | 'Authorization' | 'User-Agent' | 'Referer' | 'Cache-Control' | 'Accept-Encoding' | 'Origin' | 'Connection' | 'Cookie' | 'Pragma' | 'If-Modified-Since'; +type DataAssertionFn = (data: unknown) => T; + +/** + * Represents an error that occurs during an HTTP request made with Fetchtastic. + * It encapsulates information about the error, + * including the request URL, status code, response details, and error message. + * + * @extends Error + */ +declare class HttpError extends Error { + #private; + /** + * HTTP status code associated with the error. + */ + status: number; + /** + * Indicates the HTTP method used in the failed request. + */ + method: HttpMethod; + /** + * Refers to the `Response` object received from the failed request. + */ + response: Response; + /** + * Stores the URL of the failed request. + */ + url: string; + constructor(url: string, method: HttpMethod, response: Response, message?: string); +} + +type ErrorCatcher = (error: HttpError, config: Fetchtastic) => void | Promise; +/** + * Represents a configurable Fetchtastic instance that can be used to make HTTP requests. + * Implements the `ConfigurableFetch` interface. + * @preserve + */ +declare class Fetchtastic { + #private; + /** + * Gets the URL with search parameters. + */ + get URL(): string; + /** + * Gets the search parameters as a JSON object. + */ + get searchParamsJSON(): Record; + /** + * Gets the headers as a JSON object. + */ + get headersJSON(): Record; + /** + * Gets the HTTP method associated with this request. + */ + get method(): "OPTIONS" | "GET" | "HEAD" | "PUT" | "POST" | "DELETE" | "PATCH"; + /** + * Creates a new instance of Fetchtastic. + * @param baseUrl - The base URL for the requests. + * @param controller - An optional AbortController to control the request cancellation. + */ + constructor(baseUrl?: string | URL, controller?: AbortController); + /** + * Registers an abort controller, in order to cancel the request if needed. + * @param abortController - an `AbortController` instance + */ + controller(abortController: AbortController): Fetchtastic; + /** + * Sets the headers for the request. + * @param data - The headers data. + * @param replace - Specifies whether to replace the existing headers (default: false). + */ + headers(data?: HeadersInit, replace?: boolean): Fetchtastic; + /** + * Appends a header to the request. + * @param name - The name of the header. + * @param value - The value of the header. + */ + appendHeader(name: FetchRequestHeader, value: string): this; + appendHeader(name: string, value: string): this; + /** + * Deletes a header from the request. + * @param name - The name of the header to delete. + */ + deleteHeader(name: string): Fetchtastic; + /** + * Sets the URL for the request. + * @param url - The URL for the request. + * @param replace - Specifies whether to replace the existing URL (default: false). + */ + url(url: URL): this; + url(url: string, replace?: boolean): this; + /** + * Sets the search parameters for the request. + * @param data - The search parameters data. + * @param replace - Specifies whether to replace the existing search parameters (default: false). + */ + searchParams(data?: SearchParamInput, replace?: boolean): Fetchtastic; + /** + * Appends a search parameter to the request. + * @param name - The name of the search parameter. + * @param value - The value of the search parameter. + */ + appendSearchParam(name: string, value: string | number | boolean): Fetchtastic; + /** + * Deletes a search parameter from the request. + * @param name - The name of the search parameter to delete. + */ + deleteSearchParam(name: string): Fetchtastic; + /** + * Sets the body for the request. + * @param body - The body data. + */ + body(body: BodyInit | null | unknown): Fetchtastic; + /** + * Sets the options for the request. + * @param options - The options for the request. + * @param replace - Specifies whether to replace the existing options (default: false). + */ + setOptions(options: FetchtasticOptions, replace?: boolean): Fetchtastic; + /** + * Gets the options for the request. + * @param method - The HTTP method for the request. + */ + getOptions(method: HttpMethod): FetchOptions; + get(url?: string): Fetchtastic; + post(url?: string, body?: BodyInit | null | unknown): Fetchtastic; + put(url?: string, body?: BodyInit | null | unknown): Fetchtastic; + delete(url?: string, body?: BodyInit | null | unknown): Fetchtastic; + options(url?: string, body?: BodyInit | null | unknown): Fetchtastic; + patch(url?: string, body?: BodyInit | null | unknown): Fetchtastic; + head(url?: string): Fetchtastic; + /** + * Resolves the fetch request and returns the response. + * @returns A Promise that resolves to the fetch response. + * @throws FetchError if the fetch request fails. + */ + resolve(): Promise; + /** + * Send the fetch request and returns the response as JSON. + * @param assertData Optional. A function to assert and transform the response data. + * @returns A Promise that resolves to the JSON response. + */ + json(assertData?: DataAssertionFn): Promise; + /** + * Send the fetch request and returns the response as an ArrayBuffer. + * @returns A Promise that resolves to the ArrayBuffer response. + */ + arrayBuffer(): Promise; + /** + * Resolves the fetch request and returns the response as a Blob. + * @returns A Promise that resolves to the Blob response. + */ + blob(): Promise; + /** + * Resolves the fetch request and returns the response as a FormData. + * @returns A Promise that resolves to the FormData response. + */ + formData(): Promise; + /** + * Send the fetch request and resolve the response as plain text. + * @returns A promise that resolves to the text response. + */ + text(): Promise; + /** + * Registers an given error handler for a specific status code. + * @param status HTTP status code + * @param catcher on-error callback function + */ + onError(status: number, catcher: ErrorCatcher): this; + /** + * Handles 400 bad-request HTTP responses + */ + badRequest(catcher: ErrorCatcher): this; + /** + * Handles 401 unauthorized HTTP responses + */ + unauthorized(catcher: ErrorCatcher): this; + /** + * Handles 403 forbidden HTTP responses + */ + forbidden(catcher: ErrorCatcher): this; + /** + * Handles 404 not-found HTTP responses + */ + notFound(catcher: ErrorCatcher): this; + /** + * Handles 408 request-timeout HTTP responses + */ + timeout(catcher: ErrorCatcher): this; + /** + * Handles 500 internal-server-error HTTP responses + */ + serverError(catcher: ErrorCatcher): this; +} + +/** + * Pipes the value of an expression into a pipeline of functions. + * + * Performs left-to-right function composition, + * where the first argument is a value, + * and the remaining arguments must be unary functions. + * + * + * @example + * import { pipe } from 'fetchtastic/utils' + * + * const length = (s: string) => s.length + * const addOne = (n: number) => n + 1 + * const double = (n: number) => n * 2 + * + * // without pipe + * double(addOne(length('aaa'))); // 8 + * + * // with pipe + * pipe('aaa', length, addOne, double); // 8 + */ +declare function pipe(a: A): A; +declare function pipe(a: A, ab: (a: A) => B): B; +declare function pipe(a: A, ab: (a: A) => B, bc: (b: B) => C): C; +declare function pipe(a: A, ab: (a: A) => B, bc: (b: B) => C, cd: (c: C) => D): D; +declare function pipe(a: A, ab: (a: A) => B, bc: (b: B) => C, cd: (c: C) => D, de: (d: D) => E): E; +declare function pipe(a: A, ab: (a: A) => B, bc: (b: B) => C, cd: (c: C) => D, de: (d: D) => E, ef: (e: E) => F): F; +declare function pipe(a: A, ab: (a: A) => B, bc: (b: B) => C, cd: (c: C) => D, de: (d: D) => E, ef: (e: E) => F, fg: (f: F) => G): G; +declare function pipe(a: A, ab: (a: A) => B, bc: (b: B) => C, cd: (c: C) => D, de: (d: D) => E, ef: (e: E) => F, fg: (f: F) => G, gh: (g: G) => H): H; +declare function pipe(a: A, ab: (a: A) => B, bc: (b: B) => C, cd: (c: C) => D, de: (d: D) => E, ef: (e: E) => F, fg: (f: F) => G, gh: (g: G) => H, hi: (h: H) => I): I; +declare function pipe(a: A, ab: (a: A) => B, bc: (b: B) => C, cd: (c: C) => D, de: (d: D) => E, ef: (e: E) => F, fg: (f: F) => G, gh: (g: G) => H, hi: (h: H) => I, ij: (i: I) => J): J; +declare function pipe(a: A, ab: (a: A) => B, bc: (b: B) => C, cd: (c: C) => D, de: (d: D) => E, ef: (e: E) => F, fg: (f: F) => G, gh: (g: G) => H, hi: (h: H) => I, ij: (i: I) => J, jk: (j: J) => K): K; +declare function pipe(a: A, ab: (a: A) => B, bc: (b: B) => C, cd: (c: C) => D, de: (d: D) => E, ef: (e: E) => F, fg: (f: F) => G, gh: (g: G) => H, hi: (h: H) => I, ij: (i: I) => J, jk: (j: J) => K, kl: (k: K) => L): L; +declare function pipe(a: A, ab: (a: A) => B, bc: (b: B) => C, cd: (c: C) => D, de: (d: D) => E, ef: (e: E) => F, fg: (f: F) => G, gh: (g: G) => H, hi: (h: H) => I, ij: (i: I) => J, jk: (j: J) => K, kl: (k: K) => L, lm: (l: L) => M): M; +declare function pipe(a: A, ab: (a: A) => B, bc: (b: B) => C, cd: (c: C) => D, de: (d: D) => E, ef: (e: E) => F, fg: (f: F) => G, gh: (g: G) => H, hi: (h: H) => I, ij: (i: I) => J, jk: (j: J) => K, kl: (k: K) => L, lm: (l: L) => M, mn: (m: M) => N): N; +declare function pipe(a: A, ab: (a: A) => B, bc: (b: B) => C, cd: (c: C) => D, de: (d: D) => E, ef: (e: E) => F, fg: (f: F) => G, gh: (g: G) => H, hi: (h: H) => I, ij: (i: I) => J, jk: (j: J) => K, kl: (k: K) => L, lm: (l: L) => M, mn: (m: M) => N, no: (n: N) => O): O; +declare function pipe(a: A, ab: (a: A) => B, bc: (b: B) => C, cd: (c: C) => D, de: (d: D) => E, ef: (e: E) => F, fg: (f: F) => G, gh: (g: G) => H, hi: (h: H) => I, ij: (i: I) => J, jk: (j: J) => K, kl: (k: K) => L, lm: (l: L) => M, mn: (m: M) => N, no: (n: N) => O, op: (o: O) => P): P; +declare function pipe(a: A, ab: (a: A) => B, bc: (b: B) => C, cd: (c: C) => D, de: (d: D) => E, ef: (e: E) => F, fg: (f: F) => G, gh: (g: G) => H, hi: (h: H) => I, ij: (i: I) => J, jk: (j: J) => K, kl: (k: K) => L, lm: (l: L) => M, mn: (m: M) => N, no: (n: N) => O, op: (o: O) => P, pq: (p: P) => Q): Q; +declare function pipe(a: A, ab: (a: A) => B, bc: (b: B) => C, cd: (c: C) => D, de: (d: D) => E, ef: (e: E) => F, fg: (f: F) => G, gh: (g: G) => H, hi: (h: H) => I, ij: (i: I) => J, jk: (j: J) => K, kl: (k: K) => L, lm: (l: L) => M, mn: (m: M) => N, no: (n: N) => O, op: (o: O) => P, pq: (p: P) => Q, qr: (q: Q) => R): R; +declare function pipe(a: A, ab: (a: A) => B, bc: (b: B) => C, cd: (c: C) => D, de: (d: D) => E, ef: (e: E) => F, fg: (f: F) => G, gh: (g: G) => H, hi: (h: H) => I, ij: (i: I) => J, jk: (j: J) => K, kl: (k: K) => L, lm: (l: L) => M, mn: (m: M) => N, no: (n: N) => O, op: (o: O) => P, pq: (p: P) => Q, qr: (q: Q) => R, rs: (r: R) => S): S; +declare function pipe(a: A, ab: (a: A) => B, bc: (b: B) => C, cd: (c: C) => D, de: (d: D) => E, ef: (e: E) => F, fg: (f: F) => G, gh: (g: G) => H, hi: (h: H) => I, ij: (i: I) => J, jk: (j: J) => K, kl: (k: K) => L, lm: (l: L) => M, mn: (m: M) => N, no: (n: N) => O, op: (o: O) => P, pq: (p: P) => Q, qr: (q: Q) => R, rs: (r: R) => S, st: (s: S) => T): T; + +/** + * Identity, function that returns the same argument passed to it + */ +declare const identity: (data: T) => T; +/** + * No operation + */ +declare const noop: (..._any: unknown[]) => undefined; +/** + * Returns `true` if the given value is a valid `HttpMethod` + */ +declare function isHttpMethod(value: unknown): value is HttpMethod; + +declare const StatusCodes: Map; + +export { type DataAssertionFn, type ErrorCatcher, type FetchOptions, type FetchRequestHeader, Fetchtastic, type FetchtasticOptions, HttpError, type HttpMethod, type ResponseParser, type SearchParamInput, StatusCodes, identity, isHttpMethod, noop, pipe }; diff --git a/package.json b/package.json index bc7c047..aef5766 100644 --- a/package.json +++ b/package.json @@ -39,6 +39,7 @@ "@typescript-eslint/eslint-plugin": "^5.59.2", "eslint-config": "*", "prettier": "latest", + "rollup-plugin-dts": "^6.0.0", "size-limit": "^8.1.2", "ts-node": "^10.9.1", "turbo": "latest" diff --git a/yarn.lock b/yarn.lock index d3002ae..d63447c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -33,6 +33,16 @@ __metadata: languageName: node linkType: hard +"@babel/code-frame@npm:^7.22.10": + version: 7.22.13 + resolution: "@babel/code-frame@npm:7.22.13" + dependencies: + "@babel/highlight": ^7.22.13 + chalk: ^2.4.2 + checksum: 22e342c8077c8b77eeb11f554ecca2ba14153f707b85294fcf6070b6f6150aae88a7b7436dd88d8c9289970585f3fe5b9b941c5aa3aa26a6d5a8ef3f292da058 + languageName: node + linkType: hard + "@babel/compat-data@npm:^7.22.5": version: 7.22.5 resolution: "@babel/compat-data@npm:7.22.5" @@ -220,6 +230,17 @@ __metadata: languageName: node linkType: hard +"@babel/highlight@npm:^7.22.13": + version: 7.22.13 + resolution: "@babel/highlight@npm:7.22.13" + dependencies: + "@babel/helper-validator-identifier": ^7.22.5 + chalk: ^2.4.2 + js-tokens: ^4.0.0 + checksum: 7266d2bff8aa8fc78eb65b6e92a8211e12897a731126a282d2f9bb50d8fcaa4c1b02af2284f990ac7e3ab8d892d448a2cab8f5ed0ea8a90bce2c025b11ebe802 + languageName: node + linkType: hard + "@babel/parser@npm:^7.1.0, @babel/parser@npm:^7.14.7, @babel/parser@npm:^7.17.3, @babel/parser@npm:^7.20.5, @babel/parser@npm:^7.20.7, @babel/parser@npm:^7.22.5": version: 7.22.5 resolution: "@babel/parser@npm:7.22.5" @@ -1057,7 +1078,7 @@ __metadata: languageName: node linkType: hard -"@jridgewell/sourcemap-codec@npm:^1.4.10, @jridgewell/sourcemap-codec@npm:^1.4.13": +"@jridgewell/sourcemap-codec@npm:^1.4.10, @jridgewell/sourcemap-codec@npm:^1.4.13, @jridgewell/sourcemap-codec@npm:^1.4.15": version: 1.4.15 resolution: "@jridgewell/sourcemap-codec@npm:1.4.15" checksum: b881c7e503db3fc7f3c1f35a1dd2655a188cc51a3612d76efc8a6eb74728bef5606e6758ee77423e564092b4a518aba569bbb21c9bac5ab7a35b0c6ae7e344c8 @@ -4581,6 +4602,7 @@ __metadata: "@typescript-eslint/eslint-plugin": ^5.59.2 eslint-config: "*" prettier: latest + rollup-plugin-dts: ^6.0.0 size-limit: ^8.1.2 ts-node: ^10.9.1 turbo: latest @@ -6685,6 +6707,15 @@ __metadata: languageName: node linkType: hard +"magic-string@npm:^0.30.2": + version: 0.30.3 + resolution: "magic-string@npm:0.30.3" + dependencies: + "@jridgewell/sourcemap-codec": ^1.4.15 + checksum: a5a9ddf9bd3bf49a2de1048bf358464f1bda7b3cc1311550f4a0ba8f81a4070e25445d53a5ee28850161336f1bff3cf28aa3320c6b4aeff45ce3e689f300b2f3 + languageName: node + linkType: hard + "make-dir@npm:^3.0.0": version: 3.1.0 resolution: "make-dir@npm:3.1.0" @@ -8804,6 +8835,22 @@ __metadata: languageName: node linkType: hard +"rollup-plugin-dts@npm:^6.0.0": + version: 6.0.0 + resolution: "rollup-plugin-dts@npm:6.0.0" + dependencies: + "@babel/code-frame": ^7.22.10 + magic-string: ^0.30.2 + peerDependencies: + rollup: ^3.25.0 + typescript: ^4.5 || ^5.0 + dependenciesMeta: + "@babel/code-frame": + optional: true + checksum: cebe987d4711e85f113a2f4b095d743573420b9528b0eb158edd399a3111bc593637b41bf69d8dd94ce6f670ac6b238d1b1a8f23f495dbbd54728e3fb14a820e + languageName: node + linkType: hard + "rollup-plugin-progress@npm:^1.1.2": version: 1.1.2 resolution: "rollup-plugin-progress@npm:1.1.2"