/**
 * @refraction-ui/analytics — type contracts.
 *
 * The library is a neutral Segment-spec collector/router. The app instruments
 * once via the canonical API; the router fans the canonical envelope out to
 * N pluggable sinks. There is NO privileged engine — every vendor is a sink.
 */
/** Canonical Segment event types. */
type AnalyticsEventType = 'track' | 'identify' | 'page' | 'screen' | 'group' | 'alias';
/** Schema version stamped onto every envelope and the wire contract path. */
declare const SCHEMA_VERSION = 1;
/** Arbitrary, JSON-serialisable bag of values. */
type AnalyticsProperties = Record<string, unknown>;
/**
 * The context block attached to every event. `library` identifies the
 * collector; `app`/`env` identify the product instance.
 */
interface AnalyticsContext {
    app: string;
    env: string;
    page?: {
        path?: string;
        url?: string;
        referrer?: string;
        title?: string;
        search?: string;
    };
    library: {
        name: string;
        version: string;
    };
    /** Free-form additions contributed by `with(context)` children. */
    [key: string]: unknown;
}
/**
 * The canonical Segment envelope. Every sink receives events in exactly this
 * shape; the built-in HTTP sink ships it verbatim over the wire contract.
 */
interface AnalyticsEvent {
    /** Segment call type. */
    type: AnalyticsEventType;
    /** Event name (track/screen) or page name (page). */
    event?: string;
    /** Idempotency key — backends MUST dedupe on this. */
    messageId: string;
    /** Persistent, non-PII, resettable client id. */
    anonymousId: string;
    /** Opaque app-supplied id (set after identify). */
    userId?: string;
    /** Group id (group calls). */
    groupId?: string;
    /** Previous id (alias calls). */
    previousId?: string;
    /** Analytics session id (UUIDv4, minted at session start). */
    sessionId: string;
    /** track/page/screen/group payload. */
    properties?: AnalyticsProperties;
    /** identify/group trait payload. */
    traits?: AnalyticsProperties;
    /** Collector + product context. */
    context: AnalyticsContext;
    /** ISO-8601 client timestamp. */
    timestamp: string;
    /** Envelope schema version. */
    schemaVersion: number;
}
/**
 * Sink SPI — implemented by adapter packages (GA4, Azure, PostHog) and by the
 * built-in HTTP sink. A sink declares the consent categories it requires; the
 * router will not deliver to a sink whose categories are not all granted.
 */
interface AnalyticsSink {
    /** Stable sink identifier. */
    name: string;
    /** Consent categories this sink requires (e.g. ['analytics']). */
    consentCategories?: string[];
    /** Called once before the first delivery. */
    init?(ctx: SinkInitContext): void | Promise<void>;
    /** Deliver a batch of canonical events. */
    deliver(batch: AnalyticsEvent[], ctx: SinkDeliverContext): void | Promise<void>;
    /** Flush any buffered state (best-effort). */
    flush?(): void | Promise<void>;
    /** Release resources on reset()/shutdown. */
    shutdown?(): void | Promise<void>;
}
/** Context handed to `sink.init`. */
interface SinkInitContext {
    app: string;
    env: string;
    endpoint?: string;
}
/** Context handed to every `sink.deliver`. */
interface SinkDeliverContext {
    /** True on the unload path — sinks should prefer sendBeacon. */
    unload: boolean;
}
/** Cross-tab persistence SPI. Defaults pick localStorage/cookie/memory. */
interface AnalyticsStorage {
    get(key: string): string | null;
    set(key: string, value: string): void;
    remove(key: string): void;
}
/** Session engine configuration. */
interface SessionConfig {
    /** Inactivity timeout in ms before a new session is minted. GA4 = 30min. */
    timeoutMs?: number;
    /** Reset the session when a campaign/utm change is detected. */
    resetOnCampaign?: boolean;
    /** Persistence backend; defaults to localStorage→cookie→memory. */
    storage?: AnalyticsStorage;
    /** Storage key namespace. */
    storageKey?: string;
}
/** Identity engine configuration. */
interface IdentityConfig {
    /** Persistence backend; defaults to localStorage→cookie→memory. */
    storage?: AnalyticsStorage;
    /** Storage key namespace. */
    storageKey?: string;
}
/** Consent gate configuration. */
interface ConsentConfig {
    /** Categories granted at boot. */
    granted?: string[];
    /**
     * If true, an event is dropped entirely when NO sink can receive it.
     * If false (default), events are still buffered until consent is granted.
     */
    strict?: boolean;
}
/** Consent gate runtime API. */
interface ConsentAPI {
    grant(...categories: string[]): void;
    revoke(...categories: string[]): void;
    granted(): string[];
    isGranted(category: string): boolean;
}
/** Session runtime API. */
interface SessionAPI {
    /** Current session id (mints one lazily if none active). */
    id(): string;
    /** Force-start a new session, returning the new id. */
    start(): string;
    /** End the current session. */
    end(): void;
    /** Attach arbitrary session-scoped properties. */
    set(props: AnalyticsProperties): void;
}
/** Built-in HTTP sink options (Segment HTTP Tracking API wire contract). */
interface HttpSinkOptions {
    /** Base endpoint; events POST to `{endpoint}/v{schemaVersion}/batch`. */
    endpoint: string;
    /** Write key — sent as `Authorization: Basic base64(writeKey:)`. */
    writeKey: string;
    /** Max retries for 429/5xx (exponential backoff). Default 3. */
    maxRetries?: number;
    /** Base backoff delay in ms. Default 500. */
    backoffBaseMs?: number;
    /** Injected fetch (defaults to global fetch). */
    fetchImpl?: typeof fetch;
    /** Injected sendBeacon (defaults to navigator.sendBeacon). */
    beaconImpl?: (url: string, body: string) => boolean;
    /** Consent categories this sink requires. Default ['analytics']. */
    consentCategories?: string[];
    /** Soft per-batch byte cap (Segment ≈ 500KB). Default 500_000. */
    maxBatchBytes?: number;
    /** Soft per-event byte cap (Segment ≈ 32KB). Default 32_000. */
    maxEventBytes?: number;
}
/** `createAnalytics` configuration. */
interface AnalyticsConfig {
    /** Product/app identifier (stamped into context.app). */
    app: string;
    /** Environment, e.g. 'development' | 'production'. Drives presets. */
    env: string;
    /** When set, a built-in HTTP sink is auto-registered to this endpoint. */
    endpoint?: string;
    /** Write key for the auto-registered HTTP sink. */
    writeKey?: string;
    /** Kill switch — when false, a tree-shakeable noop is returned. Default true. */
    enabled?: boolean;
    /** 0..1 sampling rate applied per top-level call. Default 1. */
    sampleRate?: number;
    /** Extra property/trait keys to redact (in addition to the PII deny-list). */
    redactKeys?: string[];
    /** Explicit sinks (in addition to / instead of the auto HTTP sink). */
    sinks?: AnalyticsSink[];
    /** Session engine config. */
    session?: SessionConfig;
    /** Identity engine config. */
    identity?: IdentityConfig;
    /** Consent gate config. */
    consent?: ConsentConfig;
    /**
     * Force a preset. Defaults: env==='production' → 'prod', else 'dev'.
     * dev = sync + console; prod = batch + sample + beacon flush on unload.
     */
    preset?: 'dev' | 'prod';
    /** Batch size before an automatic flush (prod). Default 20. */
    batchSize?: number;
    /** Auto-flush interval in ms (prod). Default 10_000. */
    flushIntervalMs?: number;
}
/** Options accepted by every top-level call. */
interface CallOptions {
    /** Per-call timestamp override (ISO-8601). */
    timestamp?: string;
    /** Per-call context overrides (shallow-merged). */
    context?: Partial<AnalyticsContext>;
}
/** The public analytics surface returned by `createAnalytics`. */
interface Analytics {
    track(event: string, properties?: AnalyticsProperties, opts?: CallOptions): void;
    identify(userId: string, traits?: AnalyticsProperties, opts?: CallOptions): void;
    page(name?: string, properties?: AnalyticsProperties, opts?: CallOptions): void;
    screen(name?: string, properties?: AnalyticsProperties, opts?: CallOptions): void;
    group(groupId: string, traits?: AnalyticsProperties, opts?: CallOptions): void;
    alias(userId: string, previousId?: string, opts?: CallOptions): void;
    /** Analytics-session API (NOT replay). */
    session: SessionAPI;
    /** Consent gate API. */
    consent: ConsentAPI;
    /** Persistent, non-PII anonymous id. */
    anonymousId(): string;
    /** Current opaque user id (if identified). */
    userId(): string | undefined;
    /** Derive a child that merges extra context into every event. */
    with(context: Partial<AnalyticsContext>): Analytics;
    /** Register an additional sink at runtime. */
    addSink(sink: AnalyticsSink): void;
    /** Remove a sink by name. */
    removeSink(name: string): void;
    /** Registered sink names. */
    readonly sinks: string[];
    /** Flush all sinks (and the internal batch). */
    flush(): Promise<void>;
    /**
     * Reset identity + session (privacy-safe logout). Mints a fresh
     * anonymousId and ends the session.
     */
    reset(): void;
    /** Whether this is a live collector (false for the noop). */
    readonly enabled: boolean;
}

/**
 * createAnalytics — the neutral Segment-spec collector/router.
 *
 * Mirrors `createAI`: a single factory returns the entire public surface and
 * fans the canonical envelope out to registered sinks. There is NO privileged
 * engine — the built-in HTTP sink and every vendor adapter are equal sinks.
 *
 * Presets:
 *   dev  — synchronous delivery + a console sink (see exactly what ships).
 *   prod — batching + sampling + beacon flush on pagehide/visibilitychange.
 *
 * When `enabled: false`, a tree-shakeable noop is returned and none of the
 * live collector / sink code is reachable.
 */
declare function createAnalytics(config: AnalyticsConfig): Analytics;

/**
 * The noop collector returned when `enabled: false`.
 *
 * Every method is an empty function so calls are free and the surrounding
 * vendor/sink code can be tree-shaken out of a production bundle when
 * analytics is compiled off. Kept in its own module so bundlers can drop the
 * live collector entirely on the `enabled:false` path.
 */
declare function createNoopAnalytics(): Analytics;

/** Create the built-in Segment-spec HTTP sink. */
declare function createHttpSink(options: HttpSinkOptions): AnalyticsSink;

interface ConsoleSinkOptions {
    /** Injected logger (defaults to globalThis.console). */
    logger?: Pick<Console, 'log' | 'groupCollapsed' | 'groupEnd'>;
    /** Consent categories this sink requires. Default: none (always allowed). */
    consentCategories?: string[];
}
/**
 * Built-in `console` sink — the dev preset's default. Prints each canonical
 * envelope so engineers can see exactly what would ship over the wire.
 */
declare function createConsoleSink(options?: ConsoleSinkOptions): AnalyticsSink;

/** A mock sink that records every call — used for testing the router. */
interface MockSink extends AnalyticsSink {
    /** All events received across all deliver() calls (flattened). */
    events: AnalyticsEvent[];
    /** Each deliver() call's batch + context. */
    deliveries: Array<{
        batch: AnalyticsEvent[];
        ctx: SinkDeliverContext;
    }>;
    /** init() call contexts. */
    initCalls: SinkInitContext[];
    /** flush() invocation count. */
    flushCalls: number;
    /** shutdown() invocation count. */
    shutdownCalls: number;
}
interface CreateMockSinkOptions {
    name?: string;
    consentCategories?: string[];
}
/**
 * createMockSink — an `AnalyticsSink` that captures everything for assertion.
 * Mirrors the testing ergonomics of `@refraction-ui/ai`'s mock providers.
 */
declare function createMockSink(options?: CreateMockSinkOptions): MockSink;

/** GA4 parity: 30 minutes of inactivity ends a session. */
declare const DEFAULT_SESSION_TIMEOUT_MS: number;
/**
 * Derive a stable campaign fingerprint from a URL's query string. A change in
 * this fingerprint (a *new* campaign, not its absence) forces a new session,
 * matching GA4's "campaign change resets the session" behaviour.
 */
declare function campaignFingerprint(search?: string): string | undefined;
/**
 * Session engine.
 *
 * A session is a span of continuous activity. It ends after `timeoutMs` of
 * inactivity (GA4 parity, default 30 min) or when a *new* campaign is
 * detected. State is persisted cross-tab so multiple tabs share one session.
 */
declare function createSession(config?: SessionConfig, now?: () => number): {
    /** Get the current session id, rotating if expired. */
    id(campaign?: string): string;
    /** Force a brand-new session. */
    start(campaign?: string): string;
    /** End the current session (next id() mints a fresh one). */
    end(): void;
    /** Touch activity so the inactivity window slides forward. */
    touch(campaign?: string): string;
    /** Attach/merge session-scoped properties. */
    set(props: AnalyticsProperties): void;
    /** Read session-scoped properties (undefined when none). */
    props(): AnalyticsProperties | undefined;
};

/**
 * Identity engine.
 *
 * - `anonymousId`: persistent, non-PII, resettable UUIDv4 stored cross-tab.
 * - `userId`: opaque, app-supplied; never persisted by the library (the app
 *   owns the user record) — kept only in memory for the active page.
 * - `alias`: records a previous→current stitch for the wire envelope.
 */
declare function createIdentity(config?: IdentityConfig): {
    anonymousId(): string;
    userId(): string | undefined;
    /** identify(): bind an opaque app user id (no validation, no persistence). */
    setUserId(id: string): void;
    /**
     * alias(): returns the stitch pair for the envelope. `previousId`
     * defaults to the current user or anonymous id.
     */
    alias(nextUserId: string, previousId?: string): {
        userId: string;
        previousId: string;
    };
    /**
     * reset(): privacy-safe logout. Drops the user binding and mints a brand
     * new anonymousId so the next visitor is not stitched to the old one.
     */
    reset(): string;
};

/**
 * Consent gate.
 *
 * Holds the set of granted consent categories. The router asks the gate, per
 * sink, whether *all* of that sink's required categories are granted before
 * delivering. A sink with no declared categories is always allowed (it is the
 * sink author's responsibility to declare what it needs).
 */
declare function createConsent(config?: ConsentConfig): ConsentAPI & {
    /** True when every required category is granted (empty list ⇒ allowed). */
    allows(required?: string[]): boolean;
    /** True when at least one sink could receive (used by strict mode). */
    strict: boolean;
};

/**
 * PII redaction.
 *
 * A built-in deny-list of well-known PII key names (email/phone/name and
 * common variants) plus any caller-supplied `redactKeys`. Matching is
 * case-insensitive and substring-based so `userEmail`, `email_address`,
 * `phoneNumber`, `fullName`, etc. are all caught. Redaction recurses into
 * nested objects and arrays so PII cannot hide one level down.
 */
/**
 * Built-in PII deny-list (substring, case-insensitive, separator-insensitive).
 * These match anywhere in a key, so `userEmail`, `email_address`,
 * `phoneNumber`, `firstName`, etc. are all caught.
 */
declare const PII_DENY_LIST: readonly string[];
/**
 * Keys that are PII only as an *exact* (normalised) match. `name` belongs
 * here so genuine name fields redact while `username`, `eventName`,
 * `fileName`, `firstName`-style compounds (handled by the deny-list) do not
 * over-redact every key that merely contains "name".
 */
declare const PII_EXACT_KEYS: readonly string[];
/** Replacement token written in place of a redacted value. */
declare const REDACTED = "[REDACTED]";
/**
 * Build a key matcher from the deny-list + extra keys. `extra` entries match
 * exactly (case-insensitive, normalised); deny-list entries match as
 * substrings.
 */
declare function createRedactor(extraKeys?: string[]): {
    shouldRedact: (key: string) => boolean;
    /** Redact a properties/traits bag (returns a new object). */
    redact(props?: AnalyticsProperties): AnalyticsProperties | undefined;
};
type Redactor = ReturnType<typeof createRedactor>;

/**
 * Storage adapters.
 *
 * Order of preference for the cross-tab default: localStorage → cookie →
 * in-memory. The package is environment-agnostic; consumers can inject any
 * `AnalyticsStorage` (e.g. an RN AsyncStorage shim) via config.
 */
/** Volatile per-process store (SSR / Node / no-DOM fallback). */
declare function createMemoryStorage(): AnalyticsStorage;
/** `window.localStorage` adapter (cross-tab via the storage event). */
declare function createLocalStorageAdapter(ls: Storage): AnalyticsStorage;
interface CookieDoc {
    cookie: string;
}
/** `document.cookie` adapter (cross-tab + cross-subdomain capable). */
declare function createCookieAdapter(doc: CookieDoc, maxAgeSeconds?: number): AnalyticsStorage;
/**
 * Resolve the default storage for the current environment. A caller-supplied
 * storage always wins. Browser → localStorage (falls back to cookie if
 * localStorage throws), otherwise in-memory.
 */
declare function resolveStorage(override?: AnalyticsStorage): AnalyticsStorage;

/**
 * uuidv4 — RFC 4122 version 4 UUID.
 *
 * Prefers `crypto.randomUUID` / `crypto.getRandomValues` when available
 * (browser, modern Node) and degrades to `Math.random` only as a last
 * resort. No external dependency by design (this is the neutral router).
 */
declare function uuidv4(): string;
/** RFC 4122 v4 shape matcher (case-insensitive). */
declare const UUID_V4_RE: RegExp;
/** True when `value` is a well-formed v4 UUID. */
declare function isUuidV4(value: unknown): value is string;

export { type Analytics, type AnalyticsConfig, type AnalyticsContext, type AnalyticsEvent, type AnalyticsEventType, type AnalyticsProperties, type AnalyticsSink, type AnalyticsStorage, type CallOptions, type ConsentAPI, type ConsentConfig, type ConsoleSinkOptions, type CreateMockSinkOptions, DEFAULT_SESSION_TIMEOUT_MS, type HttpSinkOptions, type IdentityConfig, type MockSink, PII_DENY_LIST, PII_EXACT_KEYS, REDACTED, type Redactor, SCHEMA_VERSION, type SessionAPI, type SessionConfig, type SinkDeliverContext, type SinkInitContext, UUID_V4_RE, campaignFingerprint, createAnalytics, createConsent, createConsoleSink, createCookieAdapter, createHttpSink, createIdentity, createLocalStorageAdapter, createMemoryStorage, createMockSink, createNoopAnalytics, createRedactor, createSession, isUuidV4, resolveStorage, uuidv4 };
