/// <reference types="node" />
/// <reference types="node" />
import type RestOptions from "./RestOptions";
import RestRequest from "./RestRequest";
import type RestResponse from "./RestResponse";
/**
 * A callback which returns access token, possibly after refreshing it, and also
 * possibly before a retry on "invalid token" condition. I.e. it can be called
 * once or twice (the 2nd time after the previous request error, and that error
 * will be passed as a parameter).
 */
export interface TokenGetter<TData = string> {
    (prevError: Error | null): Promise<TData>;
}
/**
 * RestClient is an immutable object which allows to:
 * 1. Send remote requests in different formats, in a caller-friendly manner.
 * 2. Create a new RestClient objects deriving the current set of options and
 *    adding new ones.
 */
export default class RestClient {
    private readonly _options;
    constructor(options?: Partial<RestOptions>);
    /**
     * Returns a new RestClient with some options updated with the passed ones.
     */
    withOptions(options: Partial<RestOptions>): RestClient;
    /**
     * Returns a new RestClient with added middleware.
     */
    withMiddleware(middleware: RestOptions["middlewares"][0], method?: "unshift" | "push"): RestClient;
    /**
     * Returns a new RestClient with the base URL which will be prepended to all
     * relative paths in get(), writeForm() etc. Allows to defer resolution of
     * this base URL to the very late per-request moment. The complicated piece
     * here is that, if we want base URL to be resolved asynchronously, we often
     * times want to reuse the same RestClient object (to e.g. fetch some part of
     * the base URL using already authenticated client). And a re-enterable call
     * appears here which we must protect against in the code below.
     */
    withBase(base: string | (() => Promise<string>)): RestClient;
    /**
     * Returns a new RestClient with a custom header.
     */
    withHeader(name: string, value: string | (() => Promise<string>)): RestClient;
    /**
     * Returns a new RestClient with a bearer token authentication workflow.
     * - RestClient supports interception of options.isTokenInvalid() signal and
     *   conversion it into RestTokenInvalidError exception.
     * - If a token() is a lambda with 1 argument, it may be called the 2nd time
     *   when we get an isTokenInvalid() signal. In this case, the request is
     *   retried.
     * - If token() is a lambda with 0 arguments, that means it doesn't want to
     *   watch for the isTokenInvalid() signal, so there is no sense in retrying
     *   the request either.
     *
     * From the first sight, it looks like options.isTokenInvalid() signal is
     * coupled to setBearer() auth method only. But it's not true:
     * isTokenInvalid() makes sense for ALL authentication methods actually (even
     * for basic auth), and setBearer() is just one of "clients" which implements
     * refreshing/retries on top of isTokenInvalid().
     *
     * Passing the token as lambda allows the caller to implement some complex
     * logic, e.g.:
     * - oauth2 tokens refreshing
     * - marking the token as "revoked" in the database in case the refresh fails
     * - marking the token as "revoked" after a failed request if refresh-token is
     *   not supported
     */
    withBearer(token: TokenGetter, bearerPrefix?: string): RestClient;
    /**
     * Returns a new RestClient with oauth1 authentication workflow.
     * - In case we get an options.isTokenInvalid() signal, the token() lambda is
     *   called the 2nd time with the error object, then the request is retries.
     *   This gives the lambda a chance to recover or update something in the
     *   database.
     *
     * We use a separate and small oauth-1.0a node library here, because the more
     * popular one (https://www.npmjs.com/package/oauth) doesn't support signing
     * of arbitrary requests, it can only send its own requests.
     */
    withOAuth1(consumer: {
        consumerKey: string;
        consumerSecret: string;
    }, token: TokenGetter<{
        token: string;
        tokenSecret: string;
    }>): RestClient;
    /**
     * Returns a new RestClient with basic authorization workflow.
     */
    withBasic(token: TokenGetter<{
        name: string;
        password: string;
    }>): RestClient;
    /**
     * Sends a plain GET request without body.
     *
     * NOTE, all args will be passed through `encodeURIComponent`.
     */
    get(path: string, args?: Partial<Record<string, string | number | string[]>>, accept?: string): RestRequest<any>;
    /**
     * Writes some raw string, buffer or a stream.
     */
    writeRaw(path: string, body: string | Buffer | NodeJS.ReadableStream, contentType: string, method?: "POST" | "PUT" | "PATCH", accept?: string): RestRequest<any>;
    /**
     * A shortcut method to write JSON body.
     */
    writeJson(path: string, body: any, method?: "POST" | "PUT" | "PATCH" | "DELETE", accept?: string): RestRequest<any>;
    /**
     * A shortcut method to write "application/x-www-form-urlencoded" data.
     */
    writeForm(path: string, body: Partial<Record<string, string>> | string, method?: "POST" | "PUT" | "PATCH", accept?: string): RestRequest<any>;
    /**
     * A shortcut method to write DELETE request.
     */
    writeDelete(path: string, args?: Partial<Record<string, string>>, accept?: string): RestRequest<any>;
    /**
     * Returns a RestRequest prepared for sending GraphQL operation.
     * - Expects the response to contain no errors; throws otherwise.
     * - In case of success, returns just the content of `data` field (this is
     *   different with writeGraphQLNullable() which returns `data` as a separate
     *   fields along with `error` and `errors`).
     */
    writeGraphQLX(query: string, variables?: any): RestRequest<any>;
    /**
     * Same as writeGraphQLX(), but doesn't throw if GraphQL response contains
     * non-empty `error` or `errors` fields and instead returns the full response.
     * I.e. allows the caller to process these errors.
     */
    writeGraphQLNullable(query: string, variables?: any): RestRequest<{
        data?: any;
        error?: any;
        errors?: any[] | undefined;
    } | null | undefined>;
    /**
     * Performs a series of Content-Range requests with content from a sequence of
     * Buffers.
     */
    rangeUpload(path: string, mimeType: string, stream: AsyncIterable<Buffer>, method: "POST" | "PUT" | undefined, chunkSize: number): Promise<string | null>;
    private _writeGraphQLImpl;
    /**
     * Sends a plain request (with no body, like GET or DELETE).
     */
    private _noBodyRequest;
}
/**
 * @ignore
 * Calls token(null), then runs body() passing the result there. If we get a
 * RestTokenInvalidError exception, call token() with this error as a parameter
 * and then passes the response to body() again (kinda retry with a new token).
 */
export declare function tokenRetryStrategy<TData>(token: TokenGetter<TData>, body: (tokenData: TData) => Promise<RestResponse>): Promise<RestResponse>;
//# sourceMappingURL=RestClient.d.ts.map