import { DependencyList } from "react";
/**
 * Represents an integer timestamp (e.g. milliseconds since epoch or any monotonic count).
 *
 * @public
 */
export type TimeStamp = number;
/**
 * Returns the current time as a `TimeStamp`.
 *
 * @remarks
 * By default, this is just `Date.now()`. You can replace this with a custom
 * monotonic clock or high-resolution timer if desired.
 *
 * @returns The current time in milliseconds.
 *
 * @example
 * ```ts
 * const now = currentTimestamp()
 * console.log("The time is", now)
 * ```
 *
 * @public
 */
export declare function currentTimestamp(): TimeStamp;
/**
 * Provides a stable function that, when called, creates (or re-creates)
 * an `AbortController` and returns its `signal`.
 *
 * @remarks
 * Each render, the same function reference is returned. Calling it effectively
 * aborts any in-flight request and starts a fresh `AbortController`.
 *
 * @returns A function which, when called, returns a fresh `AbortSignal`.
 *
 * @example
 * ```ts
 * function MyComponent() {
 *   const createAbortSignal = useAbort()
 *
 *   useEffect(() => {
 *     const signal = createAbortSignal()
 *     fetch('/api', { signal }).catch(e => { ... })
 *   }, [])
 *   ...
 * }
 * ```
 *
 * @public
 */
export declare function useAbort(): () => AbortSignal;
/**
 * A class-based token to represent a unique "loading" state instance.
 *
 * @remarks
 * Using a `LoadingToken` instead of the default `loading` symbol allows you
 * to store additional metadata—e.g., timestamps, request IDs, etc. This can
 * facilitate debugging or concurrency strategies that rely on distinct tokens.
 *
 * @example
 * ```ts
 * import { LoadingToken } from "./useLoadable"
 *
 * const token = new LoadingToken()
 * console.log("Loading started at:", token.startTime)
 * ```
 *
 * @public
 */
export declare class LoadingToken {
    readonly startTime: TimeStamp;
    /**
     * Creates a new `LoadingToken`.
     *
     * @param startTime - When this token was created. Defaults to currentTimestamp().
     */
    constructor(startTime?: TimeStamp);
}
/**
 * A unique symbol representing a "loading" state.
 *
 * @remarks
 * This symbol is used by default in loadable data when an async request is in-flight.
 * Using a symbol is a simple approach for representing loading without additional metadata.
 *
 * @public
 */
export declare const loading: unique symbol;
/**
 * A union type that can be either the default `loading` symbol or a class-based `LoadingToken`.
 *
 * @public
 */
export type Loading = typeof loading | LoadingToken;
/**
 * Checks if the given value represents a "loading" state.
 *
 * @param value - The value to check.
 * @returns True if it’s either `loading` (symbol) or an instance of `LoadingToken`.
 *
 * @example
 * ```ts
 * if (isLoadingValue(loadable)) {
 *   return <Spinner />
 * }
 * ```
 *
 * @public
 */
export declare function isLoadingValue(value: unknown): value is Loading;
/**
 * Represents an error that occurred while loading or fetching data.
 *
 * @remarks
 * Wraps the original `cause` and optionally overrides the error message.
 *
 * @example
 * ```ts
 * // If a fetch fails, we might return a LoadError instead of a generic Error.
 * throw new LoadError(err, "Failed to load user info")
 * ```
 *
 * @public
 */
export declare class LoadError extends Error {
    readonly cause: unknown;
    /**
     * Creates a new `LoadError`.
     *
     * @param cause - The underlying reason for the load failure (e.g., an Error object).
     * @param message - An optional descriptive message. Defaults to the cause’s message.
     */
    constructor(cause: unknown, message?: string);
}
/**
 * A union type that can be either a "start" (e.g., `loading`) or a "result" (success or failure).
 *
 * @remarks
 * - `Start` usually represents a `Loading` state.
 * - `Result` can be the successful data type `T` or `LoadError`.
 *
 * @public
 */
export type Reaction<Start, Result> = Start | Result;
/**
 * A `Loadable<T>` can be:
 * - `loading` or `LoadingToken` (in-flight),
 * - a loaded value of type `T`, or
 * - a `LoadError` (failed).
 *
 * @public
 */
export type Loadable<T> = Reaction<Loading, T | LoadError>;
/**
 * Extracts the loaded type from a `Loadable<T>`, excluding `loading` or `LoadError`.
 *
 * @public
 */
export type Loaded<T> = Exclude<T, Loading | LoadError>;
/**
 * Checks if a `Loadable<T>` has fully loaded (i.e., is neither loading nor an error).
 *
 * @param loadable - The loadable value to check.
 * @returns True if it’s the successful data of type `T`.
 *
 * @public
 */
export declare function hasLoaded<T>(loadable: Loadable<T>): loadable is Loaded<T>;
/**
 * Checks if a `Loadable<T>` is a load failure (`LoadError`).
 *
 * @param loadable - The loadable value to check.
 * @returns True if it’s a `LoadError`.
 *
 * @public
 */
export declare function loadFailed<T>(loadable: Loadable<T>): loadable is LoadError;
/**
 * Applies a mapper function to a loadable if it’s successfully loaded, returning a new loadable.
 *
 * @remarks
 * If `loadable` is an error or loading, it’s returned unchanged.
 *
 * @param loadable - The original loadable.
 * @param mapper - A function that transforms the loaded data `T` into `R`.
 * @returns A new loadable with data of type `R`, or the same loading/error state.
 *
 * @public
 */
export declare function map<T, R>(loadable: Loadable<T>, mapper: (loaded: T) => R): Loadable<R>;
/**
 * Combines multiple loadables into one. If any are still loading or have failed, returns `loading`.
 *
 * @remarks
 * In reality, `all()` returns `loading` if ANY have not loaded. If all are loaded, it returns an array
 * of their loaded values (typed to match each item in `loadables`).
 *
 * @param loadables - The loadable values to combine.
 * @returns A single loadable that is `loading` if any item is not loaded, else an array of loaded items.
 *
 * @example
 * ```ts
 * const combined = all(userLoadable, postsLoadable, statsLoadable)
 * if (!hasLoaded(combined)) {
 *   return <Spinner />
 * }
 * const [user, posts, stats] = combined
 * ```
 *
 * @public
 */
export declare function all<T extends Loadable<unknown>[]>(...loadables: T): Loadable<{
    [K in keyof T]: Loaded<T[K]>;
}>;
/**
 * Converts a loadable to `undefined` if not fully loaded, or the loaded value otherwise.
 *
 * @param loadable - The loadable value to unwrap.
 * @returns `T` if loaded, otherwise `undefined`.
 *
 * @public
 */
export declare function toOptional<T>(loadable: Loadable<T>): T | undefined;
/**
 * Returns the loaded value if `loadable` is fully loaded, otherwise `defaultValue`.
 *
 * @param loadable - The loadable value to unwrap.
 * @param defaultValue - The fallback if loadable is not loaded.
 * @returns The loaded `T` or the provided `defaultValue`.
 *
 * @public
 */
export declare function orElse<T, R>(loadable: Loadable<T>, defaultValue: R): T | R;
/**
 * Checks if a loadable is fully loaded AND not null/undefined.
 *
 * @param loadable - A loadable that could be `null` or `undefined` once loaded.
 * @returns True if the loadable is successfully loaded and non-nullish.
 *
 * @public
 */
export declare function isUsable<T>(loadable: Loadable<T | null | undefined>): loadable is T;
/**
 * A function type that fetches data and returns a promise, using an `AbortSignal`.
 *
 * @param signal - The `AbortSignal` to handle cancellations.
 * @returns A promise resolving to the fetched data of type `T`.
 *
 * @public
 */
export type Fetcher<T> = (signal: AbortSignal) => Promise<T>;
/**
 * Defines the shape of a cache option with a key and an optional store.
 *
 * @public
 */
export interface CacheOption {
    /**
     * The key to store in the cache (e.g., "myUserData").
     */
    key: string;
    /**
     * The store used for caching. Defaults to `"localStorage"`.
     */
    store?: "memory" | "localStorage" | "indexedDB";
}
/**
 * The options object for `useLoadable`.
 *
 * @typeParam T - The data type we expect to load.
 *
 * @public
 */
export interface UseLoadableOptions<T = any> {
    /**
     * A prefetched loadable value, if available (used instead of calling the fetcher).
     */
    prefetched?: Loadable<T>;
    /**
     * An optional callback for load errors. Called with the raw error object.
     */
    onError?: (error: unknown) => void;
    /**
     * If true, once we have a loaded value, do **not** revert to `loading`
     * on subsequent fetches; instead, keep the old value until the new fetch
     * finishes or fails.
     */
    hideReload?: boolean;
    /**
     * Caching configuration. Can be:
     * - A string: used as the cache key (store defaults to `"localStorage"`).
     * - An object: `{ key: string, store?: "memory" | "localStorage" | "indexedDB" }`.
     */
    cache?: string | CacheOption;
}
/**
 * A hook that manages a piece of state (`T`) alongside a timestamp, allowing you
 * to ignore stale updates with older timestamps.
 *
 * @remarks
 * Internally, it stores the current `value` plus a `loadStart` timestamp. Each time
 * you set a new value, you can provide an optional new timestamp. If that timestamp
 * is older than the current state's `loadStart`, the update is ignored.
 *
 * @param initial - The initial state value.
 * @returns A tuple: `[value, setValue, loadStart]`.
 *
 * @example
 * ```ts
 * const [myValue, setMyValue, lastUpdated] = useLatestState(0)
 *
 * function handleUpdate(newVal: number) {
 *   // We'll pass a timestamp
 *   setMyValue(newVal, performance.now())
 * }
 * ```
 *
 * @public
 */
export declare function useLatestState<T>(initial: T): [T, (value: T | ((current: T) => T), loadStart?: TimeStamp) => void, TimeStamp];
/**
 * Overload: `useLoadable(waitable, readyCondition, fetcher, dependencies, optionsOrOnError?)`
 */
export declare function useLoadable<W, R>(waitable: W, readyCondition: (loaded: W) => boolean, fetcher: (loaded: W, abort: AbortSignal) => Promise<R>, dependencies: DependencyList, optionsOrOnError?: ((e: unknown) => void) | UseLoadableOptions<R>): Loadable<R>;
/**
 * Overload: `useLoadable(fetcher, deps, options?)`
 */
export declare function useLoadable<T>(fetcher: Fetcher<T>, deps: DependencyList, options?: UseLoadableOptions<T>): Loadable<T>;
/**
 * A hook that waits for a `loadable` to finish, then calls another async `fetcher`.
 *
 * @remarks
 * If `loadable` is still loading or has failed, this hook returns the same `loadable` state.
 * Otherwise, if `loadable` is loaded, it calls `fetcher(loadedValue)` and returns the result
 * as a new `Loadable<R>`.
 *
 * @param loadable - The initial loadable value.
 * @param fetcher - A function that takes the successfully loaded data plus an abort signal, returning a promise.
 * @param dependencies - An optional list of dependencies to trigger re-runs. Defaults to `[hasLoaded(loadable)]`.
 * @param options - Optional `UseLoadableOptions` for error handling, caching, etc.
 * @returns A `Loadable<R>` that is `loading` until the chained fetch finishes, or a `LoadError` if it fails.
 *
 * @example
 * ```ts
 * const user = useLoadable(() => fetchUser(userId), [userId])
 * const posts = useThen(user, (u) => fetchPostsForUser(u.id))
 * ```
 *
 * @public
 */
export declare function useThen<T, R>(loadable: Loadable<T>, fetcher: (loaded: T, abort: AbortSignal) => Promise<R>, dependencies?: DependencyList, options?: UseLoadableOptions<R>): Loadable<R>;
/** @internal */
type UnwrapLoadable<T> = T extends Loadable<infer U> ? U : never;
/** @internal */
type LoadableParameters<T extends Loadable<any>[]> = {
    [K in keyof T]: UnwrapLoadable<T[K]>;
};
/**
 * A hook that waits for multiple loadables to finish, then calls a `fetcher` using all their loaded values.
 *
 * @remarks
 * Internally, it calls `all(...loadables)`. If any loadable is still loading or fails, the combined is `loading`.
 * Once all are loaded, calls `fetcher(...loadedValues, signal)` and returns a `Loadable<R>`.
 *
 * @param loadables - An array (spread) of loadable values, e.g. `[user, stats, posts]`.
 * @param fetcher - A function that takes each loaded value plus an `AbortSignal`.
 * @param dependencies - An optional list of dependencies to re-run the effect. Defaults to the loadables array.
 * @param options - Optional config for error handling, caching, etc.
 * @returns A loadable result of type `R`.
 *
 * @example
 * ```ts
 * const user = useLoadable(fetchUser, [])
 * const stats = useLoadable(fetchStats, [])
 *
 * const combined = useAllThen(
 *   [user, stats],
 *   (u, s, signal) => fetchDashboard(u, s, signal),
 *   []
 * )
 * ```
 *
 * @public
 */
export declare function useAllThen<T extends Loadable<any>[], R>(loadables: [...T], fetcher: (...args: [...LoadableParameters<T>, AbortSignal]) => Promise<R>, dependencies?: DependencyList, options?: UseLoadableOptions<R>): Loadable<R>;
/**
 * Overload: `useLoadableWithCleanup(waitable, readyCondition, fetcher, deps, optionsOrOnError?)`.
 */
export declare function useLoadableWithCleanup<W, R>(waitable: W, readyCondition: (loaded: W) => boolean, fetcher: (loaded: W, abort: AbortSignal) => Promise<R>, dependencies: DependencyList, optionsOrOnError?: ((e: unknown) => void) | UseLoadableOptions<R>): [Loadable<R>, () => void];
/**
 * Overload: `useLoadableWithCleanup(fetcher, deps, options?)`.
 */
export declare function useLoadableWithCleanup<T>(fetcher: Fetcher<T>, deps: DependencyList, options?: UseLoadableOptions<T>): [Loadable<T>, () => void];
export {};
