Skip to content

Commit

Permalink
feat: export all types (#280)
Browse files Browse the repository at this point in the history
Co-authored-by: Pooya Parsa <[email protected]>
  • Loading branch information
bounoable and pi0 authored Oct 26, 2023
1 parent 3a73165 commit 3482a56
Show file tree
Hide file tree
Showing 5 changed files with 136 additions and 112 deletions.
18 changes: 1 addition & 17 deletions src/error.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,4 @@
import type {
FetchContext,
FetchOptions,
FetchRequest,
FetchResponse,
} from "./fetch";

export interface IFetchError<T = any> extends Error {
request?: FetchRequest;
options?: FetchOptions;
response?: FetchResponse<T>;
data?: T;
status?: number;
statusText?: string;
statusCode?: number;
statusMessage?: string;
}
import type { FetchContext, IFetchError } from "./types";

export class FetchError<T = any> extends Error implements IFetchError<T> {
constructor(message: string, opts?: { cause: unknown }) {
Expand Down
84 changes: 6 additions & 78 deletions src/fetch.ts
Original file line number Diff line number Diff line change
@@ -1,91 +1,19 @@
import type { Readable } from "node:stream";
import destr from "destr";
import { withBase, withQuery } from "ufo";
import type { Fetch, RequestInfo, RequestInit, Response } from "./types";
import { createFetchError } from "./error";
import {
isPayloadMethod,
isJSONSerializable,
detectResponseType,
ResponseType,
MappedType,
mergeFetchOptions,
} from "./utils";

export interface CreateFetchOptions {
// eslint-disable-next-line no-use-before-define
defaults?: FetchOptions;
fetch?: Fetch;
Headers?: typeof Headers;
AbortController?: typeof AbortController;
}

export type FetchRequest = RequestInfo;
export interface FetchResponse<T> extends Response {
_data?: T;
}
export interface SearchParameters {
[key: string]: any;
}

export interface FetchContext<T = any, R extends ResponseType = ResponseType> {
request: FetchRequest;
// eslint-disable-next-line no-use-before-define
options: FetchOptions<R>;
response?: FetchResponse<T>;
error?: Error;
}

export interface FetchOptions<R extends ResponseType = ResponseType>
extends Omit<RequestInit, "body"> {
baseURL?: string;
body?: RequestInit["body"] | Record<string, any>;
ignoreResponseError?: boolean;
params?: SearchParameters;
query?: SearchParameters;
parseResponse?: (responseText: string) => any;
responseType?: R;

/**
* @experimental Set to "half" to enable duplex streaming.
* Will be automatically set to "half" when using a ReadableStream as body.
* https://fetch.spec.whatwg.org/#enumdef-requestduplex
*/
duplex?: "half" | undefined;

/** timeout in milliseconds */
timeout?: number;

retry?: number | false;
/** Delay between retries in milliseconds. */
retryDelay?: number;
/** Default is [408, 409, 425, 429, 500, 502, 503, 504] */
retryStatusCodes?: number[];

onRequest?(context: FetchContext): Promise<void> | void;
onRequestError?(
context: FetchContext & { error: Error }
): Promise<void> | void;
onResponse?(
context: FetchContext & { response: FetchResponse<R> }
): Promise<void> | void;
onResponseError?(
context: FetchContext & { response: FetchResponse<R> }
): Promise<void> | void;
}

export interface $Fetch {
<T = any, R extends ResponseType = "json">(
request: FetchRequest,
options?: FetchOptions<R>
): Promise<MappedType<R, T>>;
raw<T = any, R extends ResponseType = "json">(
request: FetchRequest,
options?: FetchOptions<R>
): Promise<FetchResponse<MappedType<R, T>>>;
native: Fetch;
create(defaults: FetchOptions): $Fetch;
}
import type {
CreateFetchOptions,
FetchResponse,
FetchContext,
$Fetch,
} from "./types";

// https://developer.mozilla.org/en-US/docs/Web/HTTP/Status
const retryStatusCodes = new Set([
Expand Down
2 changes: 2 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import { createFetch } from "./base";

export * from "./base";

export type * from "./types";

// ref: https://github.com/tc39/proposal-global
const _globalThis = (function () {
if (typeof globalThis !== "undefined") {
Expand Down
129 changes: 126 additions & 3 deletions src/types.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,127 @@
// --------------------------
// $fetch API
// --------------------------

export interface $Fetch {
<T = any, R extends ResponseType = "json">(
request: FetchRequest,
options?: FetchOptions<R>
): Promise<MappedResponseType<R, T>>;
raw<T = any, R extends ResponseType = "json">(
request: FetchRequest,
options?: FetchOptions<R>
): Promise<FetchResponse<MappedResponseType<R, T>>>;
native: Fetch;
create(defaults: FetchOptions): $Fetch;
}

// --------------------------
// Context
// --------------------------

export interface FetchContext<T = any, R extends ResponseType = ResponseType> {
request: FetchRequest;
// eslint-disable-next-line no-use-before-define
options: FetchOptions<R>;
response?: FetchResponse<T>;
error?: Error;
}

// --------------------------
// Options
// --------------------------

export interface FetchOptions<R extends ResponseType = ResponseType>
extends Omit<RequestInit, "body"> {
baseURL?: string;
body?: RequestInit["body"] | Record<string, any>;
ignoreResponseError?: boolean;
params?: Record<string, any>;
query?: Record<string, any>;
parseResponse?: (responseText: string) => any;
responseType?: R;

/**
* @experimental Set to "half" to enable duplex streaming.
* Will be automatically set to "half" when using a ReadableStream as body.
* https://fetch.spec.whatwg.org/#enumdef-requestduplex
*/
duplex?: "half" | undefined;

/** timeout in milliseconds */
timeout?: number;

retry?: number | false;
/** Delay between retries in milliseconds. */
retryDelay?: number;
/** Default is [408, 409, 425, 429, 500, 502, 503, 504] */
retryStatusCodes?: number[];

onRequest?(context: FetchContext): Promise<void> | void;
onRequestError?(
context: FetchContext & { error: Error }
): Promise<void> | void;
onResponse?(
context: FetchContext & { response: FetchResponse<R> }
): Promise<void> | void;
onResponseError?(
context: FetchContext & { response: FetchResponse<R> }
): Promise<void> | void;
}

export interface CreateFetchOptions {
// eslint-disable-next-line no-use-before-define
defaults?: FetchOptions;
fetch?: Fetch;
Headers?: typeof Headers;
AbortController?: typeof AbortController;
}

// --------------------------
// Response Types
// --------------------------

export interface ResponseMap {
blob: Blob;
text: string;
arrayBuffer: ArrayBuffer;
stream: ReadableStream<Uint8Array>;
}

export type ResponseType = keyof ResponseMap | "json";

export type MappedResponseType<
R extends ResponseType,
JsonType = any,
> = R extends keyof ResponseMap ? ResponseMap[R] : JsonType;

export interface FetchResponse<T> extends Response {
_data?: T;
}

// --------------------------
// Error
// --------------------------

export interface IFetchError<T = any> extends Error {
request?: FetchRequest;
options?: FetchOptions;
response?: FetchResponse<T>;
data?: T;
status?: number;
statusText?: string;
statusCode?: number;
statusMessage?: string;
}

// --------------------------
// Other types
// --------------------------

export type Fetch = typeof globalThis.fetch;
export type RequestInfo = globalThis.RequestInfo;
export type RequestInit = globalThis.RequestInit;
export type Response = globalThis.Response;

export type FetchRequest = RequestInfo;

export interface SearchParameters {
[key: string]: any;
}
15 changes: 1 addition & 14 deletions src/utils.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { FetchOptions } from "./fetch";
import type { FetchOptions, ResponseType } from "./types";

const payloadMethods = new Set(
Object.freeze(["PATCH", "POST", "PUT", "DELETE"])
Expand Down Expand Up @@ -39,19 +39,6 @@ const textTypes = new Set([

const JSON_RE = /^application\/(?:[\w!#$%&*.^`~-]*\+)?json(;.+)?$/i;

export interface ResponseMap {
blob: Blob;
text: string;
arrayBuffer: ArrayBuffer;
stream: ReadableStream<Uint8Array>;
}

export type ResponseType = keyof ResponseMap | "json";
export type MappedType<
R extends ResponseType,
JsonType = any,
> = R extends keyof ResponseMap ? ResponseMap[R] : JsonType;

// This provides reasonable defaults for the correct parser based on Content-Type header.
export function detectResponseType(_contentType = ""): ResponseType {
if (!_contentType) {
Expand Down

0 comments on commit 3482a56

Please sign in to comment.