import { isDoddle, type MaybeDoddleAsync, type MaybePromise } from "../utils.js";
import type { Is_Any_Mixed, Is_Any_Pure_Async, Matches_Mixed_Value } from "./helpers.js";
export declare const ownerInstance: unique symbol;
/**
 * A TypeScript-first doddle evaluation primitive. An object that will only evaluate its initializer
 * function when the {@link pull} method is called.
 *
 * The initializer can return another {@link Doddle}, which will be chained like a promise.
 *
 * @category Use
 */
export declare class Doddle<T> {
    private _cacheName;
    /** The cached value or error, stored from a previous execution of the initializer. */
    private _cached?;
    private _info;
    /**
     * The initializer function that will be called to construct the value. It will be cleared after
     * the value is constructed.
     */
    private _init;
    /** @ignore */
    constructor(initializer: (...args: any[]) => any);
    /** Returns metadata about the current state of the Doddle. */
    get info(): Readonly<Doddle.Metadata>;
    /**
     * Returns a new {@link Doddle} based on this one. When pulled, it will pull `this` and yield the
     * same value. If an error occurs, the handler will be called with the error. The doddle then
     * yields whatever the handler returns.
     *
     * You can pass an async handler only if `this` is async too.
     *
     * @example
     *     const d = doddle(() => {
     *         throw new Error("Oops")
     *     })
     *     const handled = d.catch(error => {
     *         console.error(error)
     *         return 42
     *     })
     *     const pulled = handled.pull()
     *     console.log(pulled) // 42
     *
     *     // async Doddles allow an async handler:
     *     const d = doddle(async () => {
     *         throw new Error("Oops")
     *     })
     *     const handled = d.catch(async error => {
     *         console.error(error)
     *         return 42
     *     })
     *     const pulled = await handled.pull()
     *     console.log(pulled) // 42
     *
     *     // @ts-expect-error but passing an async handler for a sync doddle isn't allowed.
     *     const d = doddle(() => 1).catch(async () => 2)
     *
     * @param this The Doddle instance.
     * @param handler The error handler function.
     */
    catch<T, R>(this: DoddleAsync<T>, handler: (error: any) => Doddle.SomeAsync<R>): DoddleAsync<T | Awaited<R>>;
    catch<T, R>(this: DoddleAsync<T>, handler: (error: any) => R | Doddle<R> | Doddle.SomeAsync<R>): DoddleAsync<T | Awaited<R>>;
    catch<T, R>(this: Matches_Mixed_Value<T>, handler: (error: any) => Doddle.SomeAsync<R>): Doddle<T | Promise<R>>;
    catch<T, R>(this: Matches_Mixed_Value<T>, handler: (error: any) => R | Doddle<R>): Doddle<T | R>;
    catch<R>(handler: R extends PromiseLike<any> ? never : (error: any) => R | Doddle<R>): Doddle<R | T>;
    /**
     * Returns a new {@link Doddle} based on this one. When pulled, it will pull `this` and invoke
     * the given action function as a side-effect. It will then yield whatever `this` did.
     *
     * If the action returns a Promise, it will be awaited before yielding the result, making the
     * returned Doddle async.
     *
     * @param action The action to perform.
     */
    do<T>(this: Matches_Mixed_Value<T>, action: (value: Doddle.PulledAwaited<T>) => Doddle.SomeAsync<void>): DoddleAsync<Awaited<T>>;
    do<T>(this: DoddleAsync<T>, action: (value: Doddle.PulledAwaited<T>) => void | Doddle<void> | Doddle.SomeAsync<void>): DoddleAsync<T>;
    do<T>(this: Matches_Mixed_Value<T>, action: (value: Doddle.PulledAwaited<T>) => void | Doddle<void> | Doddle.SomeAsync<void>): Doddle<T>;
    do<T>(this: Doddle<T>, action: (value: Doddle.PulledAwaited<T>) => Doddle.SomeAsync<void>): DoddleAsync<T>;
    do<T, R>(this: Matches_Mixed_Value<R, Doddle<T>>, action: (value: Doddle.PulledAwaited<T>) => R | Doddle<R>): Doddle<T | Promise<T>>;
    do<T>(this: Doddle<T>, action: (value: Doddle.PulledAwaited<T>) => void | Doddle<void>): Doddle<T>;
    /**
     * Creates a new {@link Doddle} based on `this`. When pulled, it will pull `this` and project the
     * result using the given function.
     *
     * When `this` is async, the projection will be passed the awaited value, and the function will
     * return an async Doddle. It also happens if you pass an async projection.
     *
     * @example
     *     // Sync inputs:
     *     const d = doddle(() => 42)
     *     const mapped = d.map(x => x + 1)
     *     const pulled = mapped.pull()
     *     console.log(pulled) // 43
     *
     *     // async inputs:
     *     const d = doddle(async () => 42)
     *     const mapped = d.map(x => x + 1) // note that the awaited value is used
     *     const pulled = await mapped.pull()
     *     console.log(pulled) // 43
     *
     *     // async projection:
     *     const d = doddle(() => 42)
     *     const mapped = d.map(async x => x + 1)
     *     const pulled = await mapped.pull()
     *     console.log(pulled) // 43
     *
     * @param projection The function to apply to the pulled value.
     */
    map<T, R>(this: Doddle<T>, projection: (value: Doddle.PulledAwaited<T>) => Doddle.SomeAsync<R>): DoddleAsync<R>;
    map<T, R>(this: Matches_Mixed_Value<R, DoddleAsync<T>>, projection: (value: Doddle.PulledAwaited<T>) => R | Doddle<R>): DoddleAsync<Awaited<R>>;
    map<T, R>(this: DoddleAsync<T>, projection: (value: Doddle.PulledAwaited<T>) => Doddle<R> | R): DoddleAsync<R>;
    map<T, R>(this: Matches_Mixed_Value<T> & Matches_Mixed_Value<R, Doddle<T>>, projection: (value: Doddle.PulledAwaited<T>) => R | Doddle<R>): Doddle<R>;
    map<T, R>(this: Matches_Mixed_Value<T>, projection: (value: Doddle.PulledAwaited<T>) => R | Doddle<R>): Doddle<R | Promise<R>>;
    map<T, R>(this: Doddle<T>, projection: (value: Doddle.PulledAwaited<T>) => R | Doddle<R>): Doddle<R>;
    /**
     * Returns a memoized function, which acts like this Doddle while hiding its type.
     *
     * @returns A memoized function that pulls `this` and returns its result.
     */
    memoize(): () => T;
    /**
     * Evaluates this {@link Doddle} instance, flattening any nested {@link Doddle} or {@link Promise}
     * types and yielding its value.
     *
     * @returns The yielded value.
     * @throws The error thrown during initialization, if any.
     */
    pull(): Doddle.Pulled<T>;
    /** Returns a short description of the Doddle value and its state. */
    toString(): string;
    /**
     * Returns a new Doddle based on this one, together with the input Doddles. When pulled, it will
     * pull `this` and all `others`, yielding their results in an array.
     *
     * If either `this` or any of `others` is async, the resulting Doddle will also be async.
     *
     * @param others The other Doddles to zip with.
     */
    zip<const Others extends readonly [Doddle<any>, ...Doddle<any>[]]>(...others: Others): Is_Any_Pure_Async<[
        Doddle<T>,
        ...Others
    ], DoddleAsync<[
        Doddle.PulledAwaited<T>,
        ...{
            [K in keyof Others]: Doddle.PulledAwaited<Others[K]>;
        }
    ]>, Is_Any_Mixed<[
        Doddle<T>,
        ...Others
    ], Doddle<MaybePromise<[
        Doddle.PulledAwaited<T>,
        ...{
            [K in keyof Others]: Doddle.PulledAwaited<Others[K]>;
        }
    ]>>, Doddle<[
        Doddle.Pulled<T>,
        ...{
            [K in keyof Others]: Doddle.Pulled<Others[K]>;
        }
    ]>>>;
}
/**
 * Creates a {@link Doddle} lazy primitive around a given function. Supports both sync and async
 * initializers and flattens nested Doddle or {@link Promise} types.
 *
 * See examples for usage.
 *
 * @category Create
 * @example
 *     // Simple initializer:
 *     const regular = doddle(() => 1) satisfies Doddle<number>
 *
 *     // Initializer returning another lazily primitive is flattened:
 *     const lazyNested = doddle(() => doddle(() => 1)) satisfies Doddle<number>
 *
 *     // Async initializer gives a `DoddleAsync` instance:
 *     const lazyAsync = doddle(async () => 1) satisfies DoddleAsync<number>
 *
 *     // Async initializer returning another lazily primitive is flattened:
 *     const asyncDoddle = doddle(async () => doddle(() => 1)) satisfies DoddleAsync<number>
 *
 *     // Async initializer returning another lazily async primitive is flattened:
 *     const asyncDoddleAsync = doddle(async () =>
 *         doddle(async () => 1)
 *     ) satisfies DoddleAsync<number>
 *
 * @param initializer An initializer that will be called once to produce the value.
 */
export declare function doddle<X>(initializer: () => PromiseLike<DoddleAsync<X>>): DoddleAsync<X>;
export declare function doddle<X>(initializer: () => PromiseLike<Doddle<X>>): DoddleAsync<X>;
export declare function doddle<X>(initializer: () => PromiseLike<X>): DoddleAsync<X>;
export declare function doddle<T>(initializer: () => Doddle<T>): Doddle<T>;
export declare function doddle<T>(initializer: () => T | Doddle<T>): Doddle<T>;
/**
 * Doddle utility functions.
 *
 * @category Create
 */
export declare namespace doddle {
    const is: typeof isDoddle;
}
/**
 * Doddle utility types.
 *
 * @category Types
 */
export declare namespace Doddle {
    /** An metadata object describing the state of a {@link Doddle} instance. */
    interface Metadata {
        /** A human-readable representation of the Doddle's state. */
        readonly desc: string;
        /** Whether the Doddle has already been pulled. */
        readonly isReady: boolean;
        /** The current stage of the Doddle's execution. */
        readonly stage: string;
        /** Whether the Doddle is sync or async (or whether it's unknown). */
        readonly syncness: string;
    }
    /**
     * Recursively pulls the result type of a {@link Doddle}, cutting thoruhg any {@link PromiseLike}
     * types. Returns an async representation if the input as async.
     */
    type Pulled<T> = T extends PromiseLike<infer X> ? Promise<PulledAwaited<X>> : T extends Doddle<infer X> ? Pulled<X> : T;
    /** Recursively pulls the result type of a {@link Doddle}, cutting through any {@link PromiseLike} */
    type PulledAwaited<T> = T extends Doddle<infer R> ? PulledAwaited<R> : T extends PromiseLike<infer R> ? PulledAwaited<R> : T;
    /** An async value or a {@link Doddle} that can be pulled to get an async value. */
    type SomeAsync<T> = PromiseLike<T> | DoddleAsync<T> | PromiseLike<Doddle<T>> | PromiseLike<DoddleAsync<T>>;
    /** A value, a promise, a doddle, an async doddle, or similar nestings. */
    type MaybePromised<T> = MaybePromise<DoddleAsync<T> | MaybeDoddleAsync<T>>;
}
/**
 * An async {@link Doddle}, which is just a `Doddle<Promise<T>>`.
 *
 * @category Use
 */
export type DoddleAsync<T> = Doddle<Promise<T>>;
/**
 * Similar to `await`. Pulls a value from a {@link Doddle}, which may be async. The same as calling
 * {@link Doddle.pull} on the input.
 *
 * @category Use
 * @param input
 */
export declare function pull<T>(input: 1 extends 0 & T ? T : never): any;
/**
 * Similar to `await`. Pulls a value from a {@link Doddle}, which may be async. The same as calling
 * {@link Doddle.pull} on the input.
 *
 * @param input
 */
export declare function pull<T>(input: T): Doddle.Pulled<T>;
//# sourceMappingURL=index.d.ts.map