import { type stop, type retry } from '../core/constants.js';
import type { Input, Options } from './options.js';
import type { ResponsePromise } from './ResponsePromise.js';
export type KyInstance = {
    /**
    Fetch the given `url`.

    Invalid options, an invalid `input`, and an error thrown by an `init` hook reject the returned promise instead of throwing, the same way `fetch()` reports them. They are not passed to `beforeError` hooks, because no request was made.

    @param url - `Request` object, `URL` object, or URL string.
    @returns A promise with `Body` method added.

    @example
    ```
    import ky from 'ky';

    const json = await ky('https://example.com', {json: {foo: true}}).json();

    console.log(json);
    //=> `{data: '🦄'}`
    ```
    */
    <T>(url: Input, options?: Options): ResponsePromise<T>;
    /**
    Fetch the given `url` using the option `{method: 'get'}`.

    @param url - `Request` object, `URL` object, or URL string.
    @returns A promise with `Body` methods added.
    */
    get: <T>(url: Input, options?: Options) => ResponsePromise<T>;
    /**
    Fetch the given `url` using the option `{method: 'post'}`.

    @param url - `Request` object, `URL` object, or URL string.
    @returns A promise with `Body` methods added.
    */
    post: <T>(url: Input, options?: Options) => ResponsePromise<T>;
    /**
    Fetch the given `url` using the option `{method: 'put'}`.

    @param url - `Request` object, `URL` object, or URL string.
    @returns A promise with `Body` methods added.
    */
    put: <T>(url: Input, options?: Options) => ResponsePromise<T>;
    /**
    Fetch the given `url` using the option `{method: 'delete'}`.

    @param url - `Request` object, `URL` object, or URL string.
    @returns A promise with `Body` methods added.
    */
    delete: <T>(url: Input, options?: Options) => ResponsePromise<T>;
    /**
    Fetch the given `url` using the option `{method: 'patch'}`.

    @param url - `Request` object, `URL` object, or URL string.
    @returns A promise with `Body` methods added.
    */
    patch: <T>(url: Input, options?: Options) => ResponsePromise<T>;
    /**
    Fetch the given `url` using the option `{method: 'head'}`.

    @param url - `Request` object, `URL` object, or URL string.
    @returns A promise with `Body` methods added.
    */
    head: (url: Input, options?: Options) => ResponsePromise;
    /**
    Fetch the given `url` using the option `{method: 'query'}`.

    @param url - `Request` object, `URL` object, or URL string.
    @returns A promise with `Body` methods added.
    */
    query: <T>(url: Input, options?: Options) => ResponsePromise<T>;
    /**
    Create a new Ky instance with complete new defaults, without inheriting from any parent instance.

    @returns A new Ky instance.
    */
    create: (defaultOptions?: Options) => KyInstance;
    /**
    Create a new Ky instance with some defaults overridden with your own.

    In contrast to `ky.create()`, `ky.extend()` inherits defaults from its parent.

    You can pass headers as a `Headers` instance or a plain object. Headers are normalized to a plain object with lowercase names when options are merged, so `init` hooks start with a plain object with lowercase keys. A header removed with `undefined` stays in that object with an `undefined` value.

    You can remove a header with `.extend()` by passing the header with an `undefined` value. Passing `undefined` as a string removes the header only if it comes from a `Headers` instance.

    Similarly, you can remove existing `hooks` entries by extending the hook with an explicit `undefined`.

    By default, `.extend()` deep-merges options: hooks are appended, headers are merged, and search parameters are accumulated. Use `replaceOption` when you want to fully replace a merged property instead.

    When extending an instance, signals are combined. Use `replaceOption(signal)` to replace inherited signals, or `signal: undefined` to remove them.

    You can also refer to parent defaults by providing a function to `.extend()`.

    @example
    ```
    import ky from 'ky';

    const api = ky.create({prefix: 'https://example.com/api'});

    const usersApi = api.extend((options) => ({prefix: `${options.prefix}/users`}));

    const response = await usersApi.get('123');
    //=> 'https://example.com/api/users/123'

    const response = await api.get('version');
    //=> 'https://example.com/api/version'
    ```

    @returns A new Ky instance.
    */
    extend: (defaultOptions: Options | ((parentOptions: Options) => Options)) => KyInstance;
    /**
    A `Symbol` that can be returned by a `beforeRetry` hook to stop the retry. This will also short circuit the remaining `beforeRetry` hooks.

    Note: Returning this symbol makes Ky abort and return with an `undefined` response. Be sure to check for a response before accessing any properties on it or use [optional chaining](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Optional_chaining). It is also incompatible with body methods, such as `.json()` or `.text()`, because there is no response to parse. Calling one throws a `TypeError` explaining this. In general, we recommend throwing an error instead of returning this symbol, as that will cause Ky to abort and then throw, which avoids these limitations.

    A valid use-case for `ky.stop` is to prevent retries when making requests for side effects, where the returned data is not important. For example, logging client activity to the server.

    @example
    ```
    import ky from 'ky';

    // Note that `response` will be `undefined` in case `ky.stop` is returned.
    const response = await ky.post('https://example.com', {
        hooks: {
            beforeRetry: [
                async ({request, options, error, retryCount}) => {
                    const shouldStopRetry = await ky('https://example.com/api');
                    if (shouldStopRetry) {
                        return ky.stop;
                    }
                }
            ]
        }
    });

    // Using `.text()` or other body methods is not supported, because there is no response to parse.
    ```
    */
    readonly stop: typeof stop;
    /**
    Force a retry from an `afterResponse` hook.

    This allows you to retry a request based on the response content, even if the response has a successful status code. The retry will respect the `retry.limit` option and skip the `shouldRetry` check. The forced retry is observable in `beforeRetry` hooks, where the error will be a `ForceRetryError`.

    A marker returned by a second copy of Ky, which happens with a duplicated dependency, is still recognised, the same way the Ky error guards accept branded errors.

    @example
    ```
    import ky, {isForceRetryError} from 'ky';

    const api = ky.extend({
        hooks: {
            afterResponse: [
                async ({response}) => {
                    // Retry based on response body content
                    if (response.status === 200) {
                        const data = await response.json();

                        // Simple retry with default delay
                        if (data.error?.code === 'TEMPORARY_ERROR') {
                            return ky.retry();
                        }

                        // Retry with custom delay from API response
                        if (data.error?.code === 'RATE_LIMIT') {
                            return ky.retry({
                                delay: data.error.retryAfter * 1000,
                                code: 'RATE_LIMIT'
                            });
                        }
                    }
                }
            ],
            beforeRetry: [
                ({error, retryCount}) => {
                    // Observable in beforeRetry hooks
                    if (isForceRetryError(error)) {
                        console.log(`Forced retry #${retryCount}: ${error.message}`);
                        // Example output: "Forced retry #1: Forced retry: RATE_LIMIT"
                    }
                }
            ]
        }
    });

    const response = await api.get('https://example.com/api');
    ```
    */
    readonly retry: typeof retry;
};
