UNPKG

@zhengxs/http

Version:

A lightweight cross-platform http request library

474 lines (404 loc) 18.7 kB
/// <reference types="node" /> import { Agent } from 'node:http'; import { Readable } from 'node:stream'; import { ReadStream } from 'node:fs'; export declare abstract class AbstractPage<Item> implements AsyncIterable<Item> { #private; protected options: FinalRequestOptions; protected response: Response; protected body: unknown; constructor(client: APIClient, response: Response, body: unknown, options: FinalRequestOptions); /** * @deprecated Use nextPageInfo instead */ abstract nextPageParams(): Partial<Record<string, unknown>> | null; abstract nextPageInfo(): PageInfo | null; abstract getPaginatedItems(): Item[]; hasNextPage(): boolean; getNextPage(): Promise<this>; iterPages(): AsyncGenerator<AbstractPage<Item>, void, unknown>; [Symbol.asyncIterator](): AsyncGenerator<Awaited<Item>, void, unknown>; } export declare class APIClient { baseURL: string; maxRetries: number; timeout: number; httpAgent: Agent | undefined; protected fetch: Fetch; protected idempotencyHeader?: string; constructor({ baseURL, maxRetries, timeout, // 10 minutes httpAgent, fetch: overrideFetch, }: APIClientOptions); /** * Override this to add your own auth headers. * * ```ts * { * Authorization: 'Bearer 123', * } * ``` */ protected authHeaders(_opts: FinalRequestOptions): PromiseOrValue<ReqHeaders>; /** * Override this to add your own default headers. */ protected defaultHeaders(opts: FinalRequestOptions): Promise<ReqHeaders>; protected defaultQuery(): DefaultQuery | undefined; /** * Override this to add your own headers validation: */ protected validateHeaders(_headers: ReqHeaders, _customHeaders: ReqHeaders): void; protected defaultIdempotencyKey(): string; get<Req extends NonNullable<unknown>, Rsp>(path: string, opts?: PromiseOrValue<RequestOptions<Req>>): APIPromise<Rsp>; post<Req extends NonNullable<unknown>, Rsp>(path: string, opts?: PromiseOrValue<RequestOptions<Req>>): APIPromise<Rsp>; patch<Req extends NonNullable<unknown>, Rsp>(path: string, opts?: PromiseOrValue<RequestOptions<Req>>): APIPromise<Rsp>; put<Req extends NonNullable<unknown>, Rsp>(path: string, opts?: PromiseOrValue<RequestOptions<Req>>): APIPromise<Rsp>; delete<Req extends NonNullable<unknown>, Rsp>(path: string, opts?: PromiseOrValue<RequestOptions<Req>>): APIPromise<Rsp>; private methodRequest; getAPIList<Item, PageClass extends AbstractPage<Item> = AbstractPage<Item>>(path: string, Page: new (...args: any[]) => PageClass, opts?: RequestOptions<any>): PagePromise<PageClass, Item>; private calculateContentLength; protected buildRequest<Req extends NonNullable<unknown>>(options: FinalRequestOptions<Req>): Promise<{ req: RequestInit_2; url: string; timeout: number; }>; /** * Used as a callback for mutating the given `RequestInit` object. * * This is useful for cases where you want to add certain headers based off of * the request properties, e.g. `method` or `url`. */ protected prepareRequest(_request: RequestInit_2, _config: { url: string; options: FinalRequestOptions; }): Promise<void>; protected parseHeaders(headers: HeadersInit_2 | null | undefined): Record<string, string>; protected makeStatusError(status: number | undefined, error: NonNullable<unknown> | undefined, message: string | undefined, headers: ReqHeaders | undefined): APIError; request<Req extends NonNullable<unknown>, Rsp>(options: PromiseOrValue<FinalRequestOptions<Req>>, remainingRetries?: number | null): APIPromise<Rsp>; protected makeRequest(optionsInput: PromiseOrValue<FinalRequestOptions>, retriesRemaining: number | null): Promise<APIResponseProps>; simple<Req extends NonNullable<unknown>, Rsp>(path: string, opts?: PromiseOrValue<RequestOptions<Req>>): APIPromise<Rsp>; protected makeSimpleRequest(optionsInput: PromiseOrValue<FinalRequestOptions>, retriesRemaining?: number | null): Promise<APIResponseProps>; requestAPIList<Item = unknown, PageClass extends AbstractPage<Item> = AbstractPage<Item>>(Page: new (...args: ConstructorParameters<typeof AbstractPage>) => PageClass, options: FinalRequestOptions): PagePromise<PageClass, Item>; buildURL<Req extends Record<string, unknown>>(path: string, query: Req | null | undefined): string; protected stringifyQuery(query: Record<string, unknown>): string; fetchWithTimeout(url: RequestInfo_2, init: RequestInit_2 | undefined, ms: number, controller: AbortController): Promise<Response>; protected getRequestClient(): RequestClient; private shouldRetry; private retryRequest; private calculateDefaultRetryTimeoutMillis; protected getUserAgent(): string; static create(baseURL: string, options?: Omit<APIClientOptions, 'baseURL'>): APIClient; } export declare interface APIClientOptions { baseURL: string; maxRetries?: number | undefined; timeout?: number | undefined; httpAgent?: Agent | undefined; fetch?: Fetch | undefined; } export declare class APIConnectionError extends APIError { readonly status: undefined; constructor({ message, cause, }: { message?: string; cause?: Error | undefined; }); } export declare class APIConnectionTimeoutError extends APIConnectionError { constructor({ message }?: { message?: string; }); } export declare class APIError extends HttpException { readonly status: number | undefined; readonly headers: ReqHeaders | undefined; readonly error: NonNullable<unknown> | undefined; readonly code: string | null | undefined; readonly param: string | null | undefined; readonly type: string | undefined; constructor(status: number | undefined, error: NonNullable<unknown> | undefined, message: string | undefined, headers: ReqHeaders | undefined); private static makeMessage; static generate(status: number | undefined, errorResponse: NonNullable<unknown> | undefined, message: string | undefined, headers: ReqHeaders | undefined): APIError; } /** * A subclass of `Promise` providing additional helper methods * for interacting with the SDK. */ export declare class APIPromise<T> extends Promise<T> { private responsePromise; private parseResponse; private parsedPromise; constructor(responsePromise: Promise<APIResponseProps>, parseResponse?: (props: APIResponseProps) => PromiseOrValue<T>); _thenUnwrap<U>(transform: (data: T) => U): APIPromise<U>; /** * Gets the raw `Response` instance instead of parsing the response * data. * * If you want to parse the response body but still get the `Response` * instance, you can use {@link withResponse()}. */ asResponse(): Promise<Response>; /** * Gets the parsed response data and the raw `Response` instance. * * If you just want to get the raw `Response` instance without parsing it, * you can use {@link asResponse()}. */ withResponse(): Promise<{ data: T; response: Response; }>; private parse; then<TResult1 = T, TResult2 = never>(onfulfilled?: ((value: T) => TResult1 | PromiseLike<TResult1>) | undefined | null, onrejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | undefined | null): Promise<TResult1 | TResult2>; catch<TResult = never>(onrejected?: ((reason: any) => TResult | PromiseLike<TResult>) | undefined | null): Promise<T | TResult>; finally(onfinally?: (() => void) | undefined | null): Promise<T>; } export declare type APIResponseProps = { response: Response; options: FinalRequestOptions; controller: AbortController; }; export declare class APIUserAbortError extends APIError { readonly status: undefined; constructor({ message }?: { message?: string; }); } export declare class AuthenticationError extends APIError { readonly status: 401; } export declare class BadRequestError extends APIError { readonly status: 400; } /** * Intended to match web.Blob, node.Blob, node-fetch.Blob, etc. */ export declare interface BlobLike { /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/Blob/size) */ readonly size: number; /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/Blob/type) */ readonly type: string; /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/Blob/text) */ text(): Promise<string>; /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/Blob/slice) */ slice(start?: number, end?: number): BlobLike; } declare type BlobLikePart = string | ArrayBuffer | ArrayBufferView | BlobLike | Uint8Array | DataView; declare type BlobPart_2 = string | ArrayBuffer | ArrayBufferView | Blob | Uint8Array | DataView; export { BlobPart_2 as BlobPart } export declare type Bytes = string | ArrayBuffer | Uint8Array | Buffer | null | undefined; export declare const castToError: (err: any) => Error; export declare class ConflictError extends APIError { readonly status: 409; } export declare const createForm: <T = Record<string, unknown>>(body: T | undefined) => Promise<FormData>; export declare const createResponseHeaders: (headers: Awaited<ReturnType<Fetch>>['headers']) => Record<string, string>; export declare function debug(action: string, ...args: any[]): void; export declare function defaultParseResponse<T>(props: APIResponseProps): Promise<T>; export declare type DefaultQuery = Record<string, string | undefined>; export declare type Fetch = (url: RequestInfo_2, init?: RequestInit_2) => Promise<Response>; /** * Intended to match web.File, node.File, node-fetch.File, etc. */ export declare interface FileLike extends BlobLike { /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/File/lastModified) */ readonly lastModified: number; /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/File/name) */ readonly name: string; } export declare type FinalRequestOptions<Req extends NonNullable<unknown> = Record<string, unknown> | Readable> = RequestOptions<Req> & { method: HTTPMethod; path: string; }; export declare function hasOwn(obj: NonNullable<unknown>, key: string): boolean; declare type HeadersInit_2 = globalThis.HeadersInit; export declare class HttpException extends Error { } export declare type HTTPMethod = 'get' | 'post' | 'put' | 'patch' | 'delete'; export declare class InternalServerError extends APIError { } export declare const isAbsoluteURL: (url: string) => boolean; /** * The BlobLike type omits arrayBuffer() because `@types/node-fetch@^2.6.4` lacks it; but this check * adds the arrayBuffer() method type because it is available and used at runtime */ export declare const isBlobLike: (value: any) => value is BlobLike & { arrayBuffer(): Promise<ArrayBuffer>; }; export declare function isEmptyObj(obj: NonNullable<unknown> | null | undefined): boolean; export declare const isFileLike: (value: any) => value is FileLike; export declare const isMultipartBody: (body: any) => body is MultipartBody; export declare const isRequestOptions: (obj: unknown) => obj is RequestOptions<Record<string, unknown> | Readable>; export declare const isResponseLike: (value: any) => value is ResponseLike; export declare const isUploadable: (value: any) => value is Uploadable; export declare type KeysEnum<T> = { [P in keyof Required<T>]: true; }; /** * A re-implementation of httpx's `LineDecoder` in Python that handles incrementally * reading lines from text. * * https://github.com/encode/httpx/blob/920333ea98118e9cf617f246905d7b202510941c/httpx/_decoders.py#L258 */ export declare class LineDecoder { static NEWLINE_CHARS: Set<string>; static NEWLINE_REGEXP: RegExp; buffer: string[]; trailingCR: boolean; textDecoder: any; constructor(); decode(chunk: Bytes): string[]; decodeText(bytes: Bytes): string; flush(): string[]; } /** * Returns a multipart/form-data request if any part of the given request body contains a File / Blob value. * Otherwise returns the request as is. */ export declare const maybeMultipartFormRequestOptions: <T extends {} = Record<string, unknown>>(opts: RequestOptions<T>) => Promise<RequestOptions<MultipartBody | T>>; /** * Disclaimer: modules in _shims aren't intended to be imported by SDK users. */ declare class MultipartBody { body: any; constructor(body: any); get [Symbol.toStringTag](): string; } export declare const multipartFormRequestOptions: <T extends {} = Record<string, unknown>>(opts: RequestOptions<T>) => Promise<RequestOptions<MultipartBody | T>>; export declare class NotFoundError extends APIError { readonly status: 404; } export declare type PageInfo = { url: URL; } | { params: Record<string, unknown> | null; }; /** * This subclass of Promise will resolve to an instantiated Page once the request completes. * * It also implements AsyncIterable to allow auto-paginating iteration on an unawaited list call, eg: * * ```ts * for await (const item of client.items.list()) { * console.log(item) * } * ``` */ export declare class PagePromise<PageClass extends AbstractPage<Item>, Item = ReturnType<PageClass['getPaginatedItems']>[number]> extends APIPromise<PageClass> implements AsyncIterable<Item> { constructor(client: APIClient, request: Promise<APIResponseProps>, Page: new (...args: ConstructorParameters<typeof AbstractPage>) => PageClass); /** * Allow auto-paginating iteration on an un awaited list call, eg: * * ```ts * for await (const item of client.items.list()) { * console.log(item) * } * ``` */ [Symbol.asyncIterator](): AsyncGenerator<Awaited<Item>, void, unknown>; } export declare class PermissionDeniedError extends APIError { readonly status: 403; } export declare type PromiseOrValue<T> = T | Promise<T>; export declare class RateLimitError extends APIError { readonly status: 429; } /** * Most browsers don't yet have async iterable support for ReadableStream, * and Node has a very different way of reading bytes from its "ReadableStream". * * This polyfill was pulled from https://github.com/MattiasBuelens/web-streams-polyfill/pull/122#issuecomment-1627354490 */ export declare function readableStreamAsyncIterable<T>(stream: any): AsyncIterableIterator<T>; export declare type ReqHeaders = Record<string, string | null | undefined>; export declare type RequestClient = { fetch: Fetch; }; declare type RequestInfo_2 = globalThis.RequestInfo; declare type RequestInit_2 = globalThis.RequestInit; export declare type RequestOptions<Req extends NonNullable<unknown> = Record<string, unknown> | Readable> = { method?: HTTPMethod; path?: string; query?: Req | undefined; body?: Req | undefined; headers?: ReqHeaders | undefined; maxRetries?: number; stream?: boolean | undefined; timeout?: number; httpAgent?: Agent; signal?: AbortSignal | undefined | null; idempotencyKey?: string; duplex?: 'half' | 'full' | undefined; __binaryResponse?: boolean | undefined; }; /** * Intended to match web.Response, node.Response, node-fetch.Response, etc. */ export declare interface ResponseLike { url: string; blob(): Promise<BlobLike>; } export declare const safeJSON: (text: string) => any; export declare type ServerSentEvent = { event: string | null; data: string; raw: string[]; }; export declare const sleep: (ms: number) => Promise<unknown>; export declare class SSEDecoder { private data; private event; private chunks; constructor(); decode(line: string): ServerSentEvent | null; } export declare class Stream<Item> implements AsyncIterable<Item> { private iterator; controller: AbortController; constructor(iterator: () => AsyncIterator<Item>, controller: AbortController); static fromSSEResponse<Item>(response: Response, controller: AbortController): Stream<Item>; /** * Generates a Stream from a newline-separated ReadableStream * where each item is a JSON value. */ static fromReadableStream<Item>(readableStream: globalThis.ReadableStream, controller: AbortController): Stream<Item>; [Symbol.asyncIterator](): AsyncIterator<Item>; /** * Splits the stream into two streams which can be * independently read from at different speeds. */ tee(): [Stream<Item>, Stream<Item>]; /** * Converts this stream to a newline-separated ReadableStream of * JSON stringified values in the stream * which can be turned back into a Stream with `Stream.fromReadableStream()`. */ toReadableStream(): globalThis.ReadableStream; } /** * Helper for creating a {@link File} to pass to an SDK upload method from a variety of different data formats * @param value - the raw content of the file. Can be an {@link Uploadable}, {@link BlobLikePart}, or {@link AsyncIterable} of {@link BlobLikePart}s * @param name - the name of the file. If omitted, toFile will try to determine a file name from bits if possible * @param options - additional properties * @returns a {@link File} with the given properties */ export declare function toFile(value: ToFileInput | PromiseLike<ToFileInput>, name?: string | null | undefined, options?: FilePropertyBag | undefined): Promise<FileLike>; export declare type ToFileInput = Uploadable | Exclude<BlobLikePart, string> | AsyncIterable<BlobLikePart>; export declare class UnprocessableEntityError extends APIError { readonly status: 422; } /** * Typically, this is a native "File" class. * * We provide the {@link toFile} utility to convert a variety of objects * into the File class. * * For convenience, you can also pass a fetch Response, or in Node, * the result of fs.createReadStream(). */ export declare type Uploadable = FileLike | ResponseLike | ReadStream; /** * https://stackoverflow.com/a/2117523 */ export declare const uuid4: () => string; export declare const validatePositiveInteger: (name: string, n: unknown) => number; export { }