type Method = 'get' | 'post' | 'put' | 'patch' | 'delete' | 'head' | 'options';
declare class HTTPClientError extends Error {
    readonly count: number;
    readonly request: Request;
    readonly response: Response;
    static readonly getErrorMessage: (count: number, response: Response) => string;
    constructor(count: number, request: Request, response: Response);
}
/**
 * Lifecycle describes a `.before()` and a `.failure()` function, which allow the developer
 * to write advanced behaviours like backoff retries, automatic authorization header injection,
 * easy rate limiting, etc
 */
interface Lifecycle {
    /**
     * Runs before every request, allows you to inject headers, modifiy the request, etc.
     * It is safe to mutate the request that is passed in, or return a new instance of a Request.
     *
     * Note: If you implement retries with `.failure`, then `.before` will NOT be called on each
     * retry. This is because it's possible to return the same request in the failure handler
     * which would be a request that has *already had* the `.before()` transform applied (and so)
     * `.before()` would be changing things that have already been changed.
     *
     * @param request The request that is about to be executed
     * @returns A modified OR entirely new request instance
     */
    before: (request: Request) => Promise<Request>;
    /**
     * Called when a request fails (dictated when response.ok = false)
     *
     * You can do one of three things inside of this function:
     * - 1. Return the existing instance, or a new instance of a Request, which will queue up a
     *      retry with the (and if it fails AGAIN, the count will be incremented for
     *      the next failure call). You can use this behaviour to implement retries with
     *      backoff strategies.
     *
     *      Be aware that returning a request here will NOT
     *      pass it through `.before()`, so make sure any transformations are applied beforehand.
     *
     *      Also, it is totally okay to wait inside of .failure in the case of a rate limit for example
     *
     * - 2. Return undefined, which will throw the default HTTPClientError with the
     *      request and response that failed. This won't trigger any retries and will cause the original
     *      call to fail
     *
     * - 3. Throw an error, which won't trigger any retries and will also cause the original call to fail
     *
     * @param count - The number of times the request has failed
     * @param request - The request that failed
     * @param response - The response from the failed request
     */
    failure: (count: number, request: Request, response: Response) => Promise<Request | void | undefined>;
}
type RequestTransformer<T> = (response: Response) => Promise<T>;
interface CompleteOptions<Transform> {
    base: string;
    transform: RequestTransformer<Transform>;
    lifecycle: Lifecycle;
}
type RequestConfig = Omit<RequestInit, 'method' | 'body'> & {
    body?: unknown;
};
type MethodHandler<Transform> = <T extends Transform = Transform>(path: string, config?: RequestConfig | undefined) => Promise<T>;
declare const defaultLifecycle: Lifecycle;
/**
 * This type is just what createHTTPClient returns, you can use this if you want to type
 * a class property or a variable or something, rather than doing `ReturnType<typeof createHTTPClient>`
 */
type HTTPClient<Transform> = Record<Method, MethodHandler<Transform>>;
declare function createHTTPClient<Transform = unknown>(rootOptions: {
    base: string;
    transform?: RequestTransformer<Transform>;
    lifecycle?: Partial<Lifecycle>;
}): HTTPClient<Transform>;

declare function join<Base extends string, Path extends string>(base: Base, path: Path): string;
declare function isBodyInit(body: unknown): body is BodyInit;
declare function streamToBody<T>(stream: ReadableStream<T>): Promise<string>;

export { type CompleteOptions, type HTTPClient, HTTPClientError, type Lifecycle, type Method, type MethodHandler, type RequestConfig, type RequestTransformer, createHTTPClient, defaultLifecycle, isBodyInit, join, streamToBody };
