interface UserInput {
    userId?: string;
    email?: string;
}
interface User {
    userId: string;
    email?: string;
}

type Logger = (message: string, payload?: unknown) => void;
type ErrorCallback = (error: unknown) => void;
interface ConfigInput {
    apiKey: string;
    baseUrl?: string;
    user?: UserInput;
    /** Milliseconds between background flushes. */
    flushInterval?: number;
    logger?: Logger;
    errorCallback?: ErrorCallback;
}
interface Config {
    apiKey: string;
    baseUrl: string;
    flushIntervalMs: number;
    logger: Logger;
    errorCallback?: ErrorCallback;
}

interface ExposureRecord {
    parameter_id: number;
    space_id: number;
    resolved_value: unknown;
    user_id: string;
    exposable_type: 'Experiment';
    exposable_id: number;
    audience_id: number;
    resolved_at: string;
}
/**
 * Initialize the SDK singleton. Cache-then-network: hydrates synchronously
 * from localStorage when a cached map exists, then refreshes in the background
 * (await ready() to know the refresh settled). Throws on misconfiguration —
 * a bad apiKey must be loud, not error-safe.
 */
declare function configure(input: ConfigInput): void;
/** Resolves when the background assignments refresh has settled (fetched or failed). */
declare function ready(): Promise<void>;
/**
 * Resolved value for this user, or undefined when unknown/unconfigured. Queues
 * an exposure lazily — only experiment resolutions carry exposure metadata,
 * and repeats inside the dedup window are not re-queued.
 */
declare function resolveParameter(slug: string): unknown;
/** The exposure record resolveParameter would submit, or null — without queueing anything. */
declare function getExposure(slug: string): ExposureRecord | null;
/**
 * Queue an event for the configured user.
 *
 * Deliberately takes no user id, unlike the server-side SDKs: there one
 * process serves every user, so each call must say who it is for, while a
 * page has exactly the one user configure() established. An override would
 * only ever detach the event — results attribute events to a visitor by
 * matching the id their exposure was recorded under, so an event under any
 * other id is stored, counted, and never joined.
 */
declare function trackEvent(eventSlug: string, customFields?: Record<string, unknown>): void;
/** Drain the queue now (e.g. on SPA route changes). */
declare function flush(): Promise<void>;
/**
 * Drain fully and tear down timers/listeners; configure() again to restart.
 * force drops the queue instead of draining it.
 */
declare function reset(options?: {
    force?: boolean;
}): Promise<void>;

declare const VERSION = "0.2.2";

interface ExposureMetadata {
    exposable_type: 'Experiment';
    exposable_id: number;
    audience_id: number;
}
interface Assignment {
    value: unknown;
    parameter_id: number;
    space_id: number;
    /** null for feature-flag and default resolutions — never report those as exposures. */
    exposure: ExposureMetadata | null;
}
interface AssignmentsPayload {
    user: {
        user_id: string;
        email?: string | null;
    };
    assignments: Record<string, Assignment>;
}

interface ErrorBody {
    error?: unknown;
    details?: {
        failures?: unknown[];
        invalid_count?: number;
    };
}
declare class ApiError extends Error {
    readonly status: number;
    readonly details: ErrorBody['details'];
    constructor(status: number, body: unknown);
    get retryable(): boolean;
    get partialFailure(): boolean;
}

export { ApiError, type Assignment, type AssignmentsPayload, type Config, type ConfigInput, type ErrorCallback, type ExposureMetadata, type ExposureRecord, type Logger, type User, type UserInput, VERSION, configure, flush, getExposure, ready, reset, resolveParameter, trackEvent };
