/**
 * AutoRefreshCoordinator — automatic token refresh with in-flight request queuing.
 *
 * Prevents user-visible 401s by:
 *   1. Pre-flight leeway check: if the token expires within refreshLeewayMs,
 *      trigger an automatic refresh before the request is dispatched.
 *   2. Reactive 401 retry: if a request returns 401 and the token wasn't
 *      already known expired, trigger a refresh then retry the request once.
 *   3. In-flight queuing: while a refresh is in progress, subsequent refresh
 *      attempts queue on the same promise — one refresh call for all waiters.
 *
 * When no refresh endpoint is available (the coordinator has no `refresh`
 * function), the pre-flight check is a graceful no-op and the reactive path
 * triggers a token re-read (which may succeed if the store was updated externally).
 *
 * Error messages use this three-part format:
 *   [what happened] · [why] · [what to do]
 */
import type { GoodVibesTokenStore } from './types.js';
import type { SDKObserver } from '../observer/index.js';
export interface AutoRefreshOptions {
    /**
     * Enable or disable automatic token refresh. Default: `true`.
     * When `false`, 401 responses propagate immediately without retry.
     */
    readonly autoRefresh?: boolean | undefined;
    /**
     * Milliseconds before token expiry to trigger an automatic refresh.
     * Default: 60_000 (1 minute).
     */
    readonly refreshLeewayMs?: number | undefined;
    /**
     * Consumer-provided callback invoked to obtain a new token when the current
     * token is near expiry (pre-flight leeway check) or a 401 is received
     * (reactive retry path).
     *
     * The callback must return the new token string and, optionally, its expiry
     * timestamp in Unix milliseconds. When provided, the coordinator calls this
     * to perform the actual token refresh and persists the result via
     * `setTokenEntry` (or `setToken` on stores that don't implement
     * `setTokenEntry`).
     *
     * When absent, the coordinator's pre-flight check is a graceful no-op and
     * the reactive 401 path re-reads the token store without making a network
     * call (useful when an external party updates the store).
     *
     * @example
     * const store = createMemoryTokenStore(initialToken);
     * const sdk = createGoodVibesSdk({
     *   baseUrl: 'https://daemon.example.com',
     *   tokenStore: store,
     *   autoRefresh: {
     *     refresh: async () => {
     *       const res = await fetch('/api/auth/refresh', { method: 'POST' });
     *       const { token, expiresAt } = await res.json();
     *       return { token, expiresAt };
     *     },
     *   },
     * });
     */
    readonly refresh?: (() => Promise<{
        token: string;
        expiresAt?: number;
    }>) | undefined;
}
export interface AutoRefreshCoordinatorOptions {
    readonly tokenStore: GoodVibesTokenStore;
    readonly autoRefresh: boolean;
    readonly refreshLeewayMs: number;
    /**
     * Optional refresh function. Called to acquire a new token when the current
     * one is near expiry or a 401 was received.
     *
     * If undefined, the coordinator performs a graceful no-op (does not
     * error) — in-flight queuing and leeway checks are still respected, but
     * no network call is made. Reactive 401 retry still re-reads the token
     * store in case an external party updated it.
     */
    readonly refresh?: (() => Promise<{
        token: string;
        expiresAt?: number | undefined;
    }>) | undefined;
    readonly observer?: SDKObserver | undefined;
}
export declare class AutoRefreshCoordinator {
    #private;
    constructor(options: AutoRefreshCoordinatorOptions);
    /**
     * Call before dispatching a request. If the token is near expiry (within
     * `refreshLeewayMs`), refreshes before the request goes out.
     *
     * If `autoRefresh` is disabled, this is a no-op.
     */
    ensureFreshToken(): Promise<void>;
    /**
     * Execute `fn` and retry once on 401 after triggering a refresh.
     *
     * If `autoRefresh` is false, the first 401 is rethrown immediately.
     * If the retry also returns 401, throws `GoodVibesSdkError{kind:'auth'}`.
     *
     * @param fn - The request function to execute. Must be side-effect-safe to
     *   call twice (called at most twice).
     */
    withRetryOn401<T>(fn: () => Promise<T>): Promise<T>;
    /**
     * Refresh the token immediately and execute `fn` exactly once as the retry.
     *
     * Unlike `withRetryOn401`, this method does NOT call `fn` before refreshing —
     * it assumes the caller already received a 401 on the initial attempt. It
     * refreshes the token (serialised via the shared promise, as with
     * `withRetryOn401`) and then calls `fn` a single time.
     *
     * If `fn` throws a 401 on retry, a terminal `GoodVibesSdkError{kind:'auth'}`
     * is thrown with the standard three-part message format.
     *
     * Used by `createAutoRefreshMiddleware` to avoid making an extra HTTP call
     * when the middleware already observed the initial 401 from `next()`.
     *
     * @param fn - The retry request to execute after the refresh completes.
     */
    refreshAndRetryOnce<T>(fn: () => Promise<T>): Promise<T>;
}
export declare function is401Error(error: unknown): boolean;
//# sourceMappingURL=auto-refresh.d.ts.map