import { P as PublicEntitlement, C as CrossdeckOptions, I as IdentifyOptions, A as AliasResult, G as GroupTraits, E as EventProperties, a as PurchaseResult, H as HeartbeatResponse, D as Diagnostics, K as KeyValueStorage } from './types-B9sxUuKh.js';
export { b as AuditRail, c as AutoTrackOptions, d as EntitlementsListResponse, e as Environment, f as Platform } from './types-B9sxUuKh.js';

/**
 * Durable last-known-good cache of the customer's entitlements.
 *
 * This cache is NOT a second source of truth. Crossdeck remains the
 * only source; this is the SDK's local copy of what the server last
 * told us — a cache that doesn't forget during a network partition.
 *
 * Durability contract (the RevenueCat model):
 *   - Every successful server read is persisted to device storage
 *     (localStorage, via the SDK's storage adapter).
 *   - On SDK boot the cache hydrates from storage synchronously, so
 *     isEntitled() answers correctly from the very first call — there
 *     is no cold-start window where a returning Pro customer reads as
 *     free.
 *   - When the server is unreachable, the SDK keeps serving the last
 *     entitlements it successfully fetched. A failed refresh never
 *     reaches setFromList(), so it cannot clear the cache; only a
 *     SUCCESSFUL fetch replaces it. An outage can never fail a paying
 *     customer down to free.
 *   - Staleness alone never returns false. Each entitlement is honoured
 *     against its OWN validUntil instead — a time-based trial expiry
 *     still applies even mid-partition, a still-valid Pro entitlement
 *     rides the outage out.
 *   - Staleness is VISIBLE, not silent. validUntil covers time-based
 *     expiry; it does NOT cover an event-based revoke (chargeback,
 *     refund, fraud) — that has no validUntil, so the cache would keep
 *     serving a revoked customer through an outage. Serving them is the
 *     right trade (don't lock real payers out), but unbounded-and-
 *     invisible is the bug. So: once a refresh ATTEMPT fails (or the
 *     data ages past staleAfterMs) the cache is marked stale —
 *     isStale / freshness are surfaced in diagnostics(). It keeps
 *     serving last-known-good; the staleness is just no longer hidden.
 *
 * The cache is wiped only on reset() (logout) and on an identity switch
 * — never by a TTL.
 *
 * Reactive listener API
 * ---------------------
 * `subscribe(listener)` registers a callback fired every time the cache
 * mutates (setFromList or clear) — the foundation for the
 * `useEntitlement` React hook and other framework bindings. Semantics:
 *   - Fired AFTER the mutation, so the listener sees fresh state.
 *   - Fire-and-forget: a throwing listener is swallowed (and counted)
 *     so a buggy consumer can't crash the SDK or other listeners.
 *   - Unsubscribe is idempotent.
 *   - Listeners are NOT fired on subscribe — a caller that wants the
 *     initial state reads isEntitled() / list() synchronously, which
 *     work from boot thanks to hydration above.
 */

type EntitlementsListener = (entitlements: PublicEntitlement[]) => void;

/**
 * Consent gating — GDPR / CCPA-grade kill switches.
 *
 * Three independent dimensions, each defaulting to "granted" but
 * runtime-overridable:
 *
 *   analytics  — track(), identify(), heartbeat(), session/page auto-
 *                emissions. Off → events drop silently, no network
 *                calls fire.
 *   marketing  — paid-traffic click IDs (gclid/fbclid/etc) and
 *                acquisition referrer URL. Off → these get scrubbed
 *                before they ever land in the event bag.
 *   errors     — error / breadcrumb / Web Vitals capture. Off → no
 *                webvitals.* events emitted, no error reporting (when
 *                Phase 3 errors land).
 *
 * Why this granularity: real consent banners offer "Analytics",
 * "Marketing", "Functional" as separate boxes. The SDK has to match.
 *
 * Default state: every dimension is granted. The developer must
 * explicitly call `Crossdeck.consent({ analytics: false })` before
 * the first event to opt OUT — same convention as Google Tag Manager
 * Consent Mode. To start in deny mode, call `init(...)` then
 * immediately `consent({ analytics: false, marketing: false, errors:
 * false })` before any user activity.
 *
 * DNT (Do Not Track) browser header is checked once at init and
 * applied as an automatic deny across all dimensions when
 * `respectDnt: true` is set in CrossdeckOptions (default false because
 * the industry has effectively deprecated DNT — but opt-in support
 * is the polite default for privacy-first apps).
 */
interface ConsentState {
    analytics: boolean;
    marketing: boolean;
    errors: boolean;
}

/**
 * Breadcrumb ring buffer — context attached to every error report.
 *
 * Sentry / Datadog / Bugsnag all ship the same idea: keep a rolling
 * record of the last N "things the user did" (page views, clicks,
 * custom events, network calls, console logs). When an error fires,
 * attach the buffer so the engineer reading the error can see exactly
 * how the user got into the broken state. The single most powerful
 * debugging signal in error monitoring — without breadcrumbs, errors
 * are stack traces with no story.
 *
 * Implementation: a circular buffer with a fixed cap. Old entries are
 * evicted as new ones arrive. The default cap (50) is enough to cover
 * ~5 minutes of typical user activity without ballooning the error
 * payload — Sentry uses 100 by default but the SDK is more aggressive
 * about size since we ship breadcrumbs over the wire with every error,
 * not as a separate batch.
 *
 * Privacy: breadcrumbs auto-emit from the same auto-tracking sources
 * as analytics events (page.viewed, element.clicked). Those already
 * skip password fields, form inputs, and cd-noTrack subtrees. Custom
 * crumbs added via Crossdeck.addBreadcrumb() pass through the same
 * property sanitiser as track() events.
 */
type BreadcrumbCategory = "navigation" | "ui.click" | "ui.input" | "http" | "console" | "custom" | "info";
type BreadcrumbLevel = "debug" | "info" | "warning" | "error";
interface Breadcrumb {
    /** epoch ms */
    timestamp: number;
    category: BreadcrumbCategory;
    level?: BreadcrumbLevel;
    /** Short human-readable description. */
    message?: string;
    /** Arbitrary key/value context for the crumb. */
    data?: Record<string, unknown>;
}

/**
 * Public, typed accessor for the bank-grade behavioural contracts
 * this SDK ships. The full architecture — schema, distribution,
 * audit loop, pillar taxonomy — lives in `contracts/README.md`
 * at the monorepo root.
 *
 * Why a typed surface (vs. plain JSON access): contract IDs and
 * pillar names are part of Crossdeck's public commitment to
 * customers. Reading them through `CrossdeckContracts` means the
 * compiler catches drift the moment a contract is renamed or
 * retired. Tools that consume contracts at runtime (dashboards,
 * AI assistants, customer integration tests) get the exact same
 * shape every SDK ships, with no parsing layer to drift.
 *
 * --- BINARY STABILITY ---
 * `Contract` is treated as an evolving — but back-compat — wire
 * shape. Fields may be added in any minor release. Existing
 * fields will not be removed or repurposed except in a major
 * version bump, even if all known contracts stop using them.
 * Customers can rely on `id`, `pillar`, `status`, `appliesTo`,
 * `codeRef`, `testRef`, `registeredAt`, `firstRegisteredIn`,
 * and `bundledIn` being present on every contract in every
 * future minor/patch release of this SDK.
 */
/**
 * Which bank-grade pillar a contract belongs to. The taxonomy is
 * deliberately small — every contract maps to exactly one. New
 * pillars require a Crossdeck major-version bump.
 */
type ContractPillar = "revenue" | "entitlements" | "analytics" | "webhooks" | "errors" | "lifecycle" | "identity";
/**
 * Lifecycle stage of a contract.
 * - `enforced`: live in this SDK and exercised by `testRef`.
 * - `proposed`: registered for an upcoming release; `testRef`
 *    may point to a not-yet-existing file.
 * - `retired`: kept for history only; the behaviour no longer
 *    ships. Filtered out of `CrossdeckContracts.all()` by default.
 */
type ContractStatus = "enforced" | "proposed" | "retired";
/** Which SDKs (and/or `backend`) a contract is binding on. */
type ContractAppliesTo = "web" | "node" | "react-native" | "swift" | "android" | "backend";
/**
 * Pointer to the test that exercises a contract clause. The
 * `name` is matched verbatim against the file's text by
 * `scripts/contract-audit.mjs`, so a rename without updating
 * the contract aborts CI.
 */
interface ContractTestRef {
    readonly file: string;
    readonly name: string;
}
/** One bank-grade behavioural guarantee — see `contracts/README.md`. */
interface Contract {
    readonly id: string;
    readonly pillar: ContractPillar;
    readonly status: ContractStatus;
    readonly claim: string;
    readonly appliesTo: readonly ContractAppliesTo[];
    readonly codeRef: readonly string[];
    readonly testRef: readonly ContractTestRef[];
    /** ISO-8601 date the contract was first registered. */
    readonly registeredAt: string;
    /** The release note / phase the contract first appeared in. Immutable. */
    readonly firstRegisteredIn: string;
    /** The SDK release this snapshot was bundled with, stamped at build time. */
    readonly bundledIn: string;
}
/**
 * Typed entry point to the bank-grade contracts bundled with this
 * SDK release. Stable, side-effect-free, tree-shakeable.
 *
 * @example Audit at app boot
 * ```ts
 * import { CrossdeckContracts } from "@cross-deck/web";
 *
 * for (const c of CrossdeckContracts.all()) {
 *   console.log(`[crossdeck] ${c.id} (${c.pillar})`);
 * }
 * ```
 *
 * @example Assert a specific clause is in force
 * ```ts
 * const isolation = CrossdeckContracts.byId("per-user-cache-isolation");
 * if (!isolation || isolation.status !== "enforced") {
 *   throw new Error("entitlement isolation contract is not enforced — refusing to start");
 * }
 * ```
 */
declare const CrossdeckContracts: {
    /** Every contract that applies to this SDK and is currently enforced. */
    readonly all: () => readonly Contract[];
    /**
     * Every contract bundled with this SDK release, including
     * `proposed` and `retired` entries. Use `all()` for the
     * enforced-only view.
     */
    readonly allIncludingHistorical: () => readonly Contract[];
    /** Look up a contract by its stable `id`. */
    readonly byId: (id: string) => Contract | undefined;
    /** Every enforced contract within a pillar. */
    readonly byPillar: (pillar: ContractPillar) => readonly Contract[];
    /** Filter by lifecycle status. */
    readonly withStatus: (status: ContractStatus) => readonly Contract[];
    /** Semver of the SDK release these contracts were bundled with. */
    readonly sdkVersion: "1.5.3";
    /** Fully-qualified bundle identifier — e.g. `@cross-deck/web@1.4.2`. */
    readonly bundledIn: "@cross-deck/web@1.5.3";
    /**
     * Resolve a failing test back to the contract it exercises.
     * Used by test-framework hooks (Vitest `afterEach`, XCTest
     * observation, JUnit `TestWatcher`) to find the contract id of
     * a failed contract test so `reportContractFailure(...)` can
     * stamp the right `contract_id` on the emitted event.
     *
     * Match is on `testRef.name` (case-sensitive, exact). Returns
     * the first contract whose `testRef` list contains a matching
     * entry, regardless of pillar or status.
     */
    readonly findByTestName: (name: string) => Contract | undefined;
};
/**
 * Input to {@link Crossdeck.reportContractFailure}. Lets a test
 * harness / dogfood app / customer integration report a contract
 * violation back to Crossdeck on the dedicated reliability channel —
 * single-fire, never visible in the customer's dashboard.
 *
 * SCHEMA-LOCK: this interface's field set is exhaustively named. No
 * free-form `extra: Record<string, unknown>` — the schema-lock
 * contract at
 * `contracts/diagnostics/contract-failed-payload-schema-lock.json`
 * forbids unbounded fields. Adding a field requires a PR that
 * amends the contract first, then the public interface.
 *
 * `sdk_version` and `sdk_platform` are auto-stamped by the SDK so
 * every emitted event carries them correctly without the caller
 * needing to read them out of `CrossdeckContracts.sdkVersion`.
 */
interface ContractFailureInput {
    /** Stable contract id (`per-user-cache-isolation` etc.). */
    contractId: string;
    /**
     * Short categorical-ish label — the SDK convention is to keep this
     * under 128 chars and stable across runs (so dashboards can group).
     * Never an end-user-supplied string.
     */
    failureReason: string;
    /**
     * Where the failure was observed:
     *   - `ci`            — the SDK's own test suite on CI
     *   - `dogfood`       — Crossdeck's internal dogfood project
     *   - `customer-app`  — a customer's app verifying contracts
     */
    runContext: "ci" | "dogfood" | "customer-app";
    /**
     * Stable identifier for this verification run. CI: `GITHUB_RUN_ID`
     * or equivalent. Dogfood: per-launch UUID. Customer app: any
     * stable handle the customer chooses to group fires by run.
     */
    runId: string;
    /**
     * Optional pointer back to the failing test, for triage. The SDK
     * sends both `test_file` and `test_name` on the wire when set.
     */
    testRef?: {
        file: string;
        name: string;
    };
    /**
     * Optional coarse device class, e.g. "desktop", "mobile-web",
     * "ssr". A categorical bucket, not a device identifier.
     */
    deviceClass?: string;
}

/**
 * Stack-trace parser — normalises Chrome / Firefox / Safari / Edge
 * stack strings into a common frame shape.
 *
 * Why hand-rolled, not stack-trace-js or error-stack-parser libraries:
 * those weigh 5–15 KB after minification and we'd be pulling in their
 * full feature matrix just for the parser. The patterns below cover
 * the four shapes any modern browser emits, totalling ~80 lines.
 *
 * The output frame shape mirrors what Sentry's `mechanism: { type:
 * 'generic' }` events ship, so future source-map symbolication on the
 * Crossdeck backend has a stable input to work against.
 *
 * Defensive: never throws. An unparseable line becomes a `raw` frame
 * with just the literal text. Engineers reading errors still get the
 * raw stack as fallback.
 */
interface StackFrame {
    /** Function name, or "?" if anonymous / unparseable. */
    function: string;
    /** Source file URL the frame ran in. Empty when unknown. */
    filename: string;
    /** 1-indexed line number, or 0 when unknown. */
    lineno: number;
    /** 1-indexed column number, or 0 when unknown. */
    colno: number;
    /**
     * True when the frame is in the app's own code (best-effort:
     * detected by URL not starting with chrome-extension://, etc.).
     * Helps the dashboard's "your code vs library code" view.
     */
    in_app: boolean;
    /** Raw line from the stack string for debugging when parse fails. */
    raw: string;
}

/**
 * Error capture — the third Crossdeck USP.
 *
 * Catches every error source the browser can hand us and ships them as
 * Crossdeck events. The pipeline reuses the analytics queue:
 *   - Same durable persistence (errors survive crashes / hard closes)
 *   - Same exponential backoff (a flapping server doesn't flood
 *     errors past the rate limit)
 *   - Same Idempotency-Key (duplicate batches dedup server-side)
 *   - Same consent gate (consent.errors)
 *   - Same PII scrub on properties before they leave
 *
 * Error sources captured (each toggleable):
 *   1. window.onerror — uncaught synchronous errors
 *   2. window.onunhandledrejection — unhandled promise rejections
 *   3. fetch() wrap — HTTP errors the app code didn't catch
 *   4. XMLHttpRequest wrap — same, for legacy XHR consumers
 *   5. Crossdeck.captureError(err) — manual API for try/catch blocks
 *   6. Crossdeck.captureMessage(msg) — non-error events you want to
 *      surface as issues (e.g. "we hit the soft-deprecated path")
 *
 * Defensive design rules:
 *   - The error handler must NEVER throw — if our own code crashes
 *     while reporting an error, we'd take down the host app's error
 *     handler too. Every callback is wrapped in try/swallow.
 *   - Recursion guard: a `_reporting` flag prevents the SDK from
 *     reporting its own errors recursively forever.
 *   - Rate limited per-fingerprint: max N reports per second to defend
 *     against runaway loops (e.g. an error in setInterval).
 *   - Browser-extension noise is filtered by default — those errors
 *     aren't the developer's fault and would otherwise drown the
 *     signal.
 */

type ErrorLevel = "error" | "warning" | "info";
interface CapturedError {
    /** When the error fired (epoch ms). */
    timestamp: number;
    /** error.unhandled, error.unhandledrejection, error.handled, error.message, error.http */
    kind: "error.unhandled" | "error.unhandledrejection" | "error.handled" | "error.message" | "error.http";
    level: ErrorLevel;
    message: string;
    /** The error class name when we have it (TypeError, ReferenceError, etc.) */
    errorType: string | null;
    /** Parsed stack frames, empty when unavailable. */
    frames: StackFrame[];
    /** Raw stack string for fallback display. */
    rawStack: string | null;
    /** Origin URL when available (window.onerror's `source` arg). */
    filename: string | null;
    lineno: number | null;
    colno: number | null;
    /** djb2 hash of message + top frames — group identical errors. */
    fingerprint: string;
    /** Snapshot of the breadcrumb buffer at the moment the error fired. */
    breadcrumbs: Breadcrumb[];
    /** Free-form context attached via Crossdeck.setContext(). */
    context: Record<string, unknown>;
    /** Free-form tags attached via Crossdeck.setTag(). */
    tags: Record<string, string>;
    /** "TypeError: x is not a function" → "TypeError" + "x is not a function". */
    /** Whether the error happened during a fetch / XHR. */
    http?: {
        url: string;
        method: string;
        status: number;
        statusText?: string;
    };
}

/**
 * Public API surface for @cross-deck/web.
 *
 * Usage (browser):
 *
 *   import { Crossdeck } from "@cross-deck/web";
 *
 *   Crossdeck.init({
 *     appId: "app_web_xxx",
 *     publicKey: "cd_pub_live_…",
 *     environment: "production",
 *   });
 *
 *   await Crossdeck.identify("user_847");
 *   const ents = await Crossdeck.getEntitlements();
 *   if (Crossdeck.isEntitled("pro")) {
 *     showPro();
 *   }
 *   Crossdeck.track("paywall_shown", { variant: "v3" });
 *
 *
 * Usage (Node):
 *
 *   import { Crossdeck, MemoryStorage } from "@cross-deck/web";
 *
 *   Crossdeck.init({
 *     appId: "app_node_xxx",
 *     publicKey: "cd_pub_test_…",
 *     environment: "sandbox",
 *     storage: new MemoryStorage(),  // session-only persistence
 *     autoHeartbeat: false,           // skip the boot ping in scripts
 *   });
 */

declare class CrossdeckClient {
    private state;
    private verifiers;
    private verifierReporter;
    private verifierCtx;
    /**
     * Boot the SDK. Idempotent — calling init twice with the same options
     * is a no-op; calling with different options replaces the previous
     * configuration.
     *
     * NorthStar §11.1: signature is `Crossdeck.init({ appId, publicKey,
     * environment })`. The trio is validated up-front so a typo'd key or a
     * mismatched env fails fast at boot rather than at first event-flush.
     */
    init(options: CrossdeckOptions): void;
    /**
     * Fetch /v1/config from the backend and apply any per-app verifier
     * overrides set in the dashboard, then fire the boot self-test
     * once with the FINAL resolved flags.
     *
     * Precedence (also documented at the call site in init()):
     *   code option > dashboard remote config > DEBUG/RELEASE default
     *
     * Code wins so engineers retain ultimate control; dashboard is the
     * no-deploy operational lever for QA / staging soaks.
     *
     * Never throws — a /v1/config failure surfaces as "stick with the
     * synchronous defaults that init() already applied" and the boot
     * self-test runs only if code > default resolves true.
     */
    private bootstrapVerifierLayerRemote;
    /**
     * @deprecated Use `init()` instead. NorthStar §4 standardised the
     * lifecycle method name across SDKs as `init` (formerly `start` /
     * `configure`). `start` will be removed in a future major version.
     */
    start(options: CrossdeckOptions): void;
    /**
     * Link the anonymous device to a developer-supplied user ID. Cache
     * the resolved Crossdeck customer for follow-up calls.
     *
     * v0.9.0+ accepts an optional `traits` bag — profile data (name,
     * plan, signupDate, role) persisted on the Crossdeck customer record
     * and queryable from dashboards. Traits are sanitised through the
     * same validator that gates `track()` properties, so a `{ avatar:
     * <File>, onSave: () => {} }` payload can't corrupt the alias call.
     *
     *   Crossdeck.identify("user_847", {
     *     email: "wes@pinet.co.za",
     *     traits: { name: "Wes", plan: "pro", signedUpAt: "2026-05-11" },
     *   });
     */
    identify(userId: string, options?: IdentifyOptions): Promise<AliasResult>;
    /**
     * Register super-properties — Mixpanel pattern. Once set, every
     * subsequent event of THIS SDK instance carries these keys on its
     * properties bag automatically.
     *
     *   Crossdeck.register({ plan: "pro", releaseChannel: "beta" });
     *   Crossdeck.track("paywall_shown");  // includes plan + releaseChannel
     *
     * Values that are `null` are deleted (the explicit "stop tracking
     * this key" idiom). Returns the resulting bag.
     *
     * Sanitised through `validateEventProperties` so a `{ avatar: File }`
     * payload can't poison the queue at flush time.
     */
    register(properties: Record<string, unknown>): Record<string, unknown>;
    /** Remove a single super-property key. Idempotent. */
    unregister(key: string): void;
    /** Snapshot of the current super-property bag. */
    getSuperProperties(): Record<string, unknown>;
    /**
     * Associate the current user with a group (org, team, account, etc.).
     * Mixpanel / Segment "Group Analytics" pattern.
     *
     *   Crossdeck.group("org", "acme_inc");
     *   Crossdeck.group("team", "design", { headcount: 12 });
     *
     * Once set, every subsequent event carries `$groups.<type>: id` on
     * its properties bag, enabling B2B dashboards ("how is Acme using
     * the product"). Pass `id: null` to clear a group membership.
     */
    group(type: string, id: string | null, traits?: GroupTraits): void;
    /** Snapshot of the current groups map keyed by type. */
    getGroups(): Record<string, {
        id: string;
        traits?: Record<string, unknown>;
    }>;
    /**
     * Update consent state. Three independent dimensions:
     *
     *   analytics  — track() + identify() + auto-emissions
     *   marketing  — paid-traffic click IDs + referrer URL on events
     *   errors     — Web Vitals + (future) error reporting
     *
     * Each defaults to `true` (granted). Pass partial state — only the
     * keys you provide are changed.
     *
     *   Crossdeck.consent({ analytics: false });
     *   Crossdeck.consent({ marketing: true, errors: true });
     *
     * DNT-derived denies cannot be flipped back on; if the browser said
     * "don't track" we don't track even if the developer code disagrees.
     */
    consent(state: Partial<ConsentState>): ConsentState;
    /** Snapshot of the current consent state. */
    consentStatus(): ConsentState;
    /**
     * Manually capture an error from a try/catch block.
     *
     *   try { …risky… } catch (err) {
     *     Crossdeck.captureError(err, { context: { plan: "pro" } });
     *   }
     *
     * The error is shipped through the same event queue as analytics
     * (durable, retried, rate-limited per fingerprint). Sends are gated
     * by `consent.errors`. Returns silently — never throws, even if the
     * SDK isn't initialised yet.
     */
    captureError(error: unknown, options?: {
        context?: Record<string, unknown>;
        tags?: Record<string, string>;
        level?: ErrorLevel;
    }): void;
    /**
     * Capture a non-error event you want to surface as an issue
     * ("deprecated path hit", "we entered the slow code path"). Sentry
     * captureMessage pattern. Returns silently if not initialised.
     */
    captureMessage(message: string, level?: ErrorLevel): void;
    /**
     * Attach a tag to every subsequent error report. Tags are key/value
     * strings (Sentry pattern): `setTag("flow", "checkout")` → every
     * error from this point on carries `tags.flow === "checkout"`.
     */
    setTag(key: string, value: string): void;
    /** Bulk-set tags. Merges with existing tags. */
    setTags(tags: Record<string, string>): void;
    /**
     * Attach a structured context blob to every subsequent error report.
     * Unlike tags (flat key/value), context is a named bag of arbitrary
     * data: `setContext("cart", { items: 3, total: 42.99 })`.
     */
    setContext(name: string, data: Record<string, unknown>): void;
    /**
     * Add a custom breadcrumb to the rolling buffer. Useful for marking
     * domain-meaningful moments ("user opened paywall") that aren't
     * already auto-captured. The buffer caps at 50 entries; old ones
     * evict.
     */
    addBreadcrumb(crumb: Breadcrumb): void;
    /**
     * Install a pre-send hook for errors. Return null to drop, or a
     * modified CapturedError to scrub / rewrite. Sentry's beforeSend
     * pattern — the only way to redact app-specific PII (auth tokens
     * in URLs, etc.) before the report leaves the browser.
     */
    setErrorBeforeSend(hook: ((err: CapturedError) => CapturedError | null) | null): void;
    /**
     * Internal: turn a CapturedError into a Crossdeck event and enqueue
     * it. Goes through the same queue / persistence / consent / scrub
     * pipeline as analytics events.
     */
    private reportError;
    /**
     * GDPR/CCPA "right to be forgotten" — calls the backend's
     * /v1/identity/forget endpoint to schedule a server-side deletion of
     * the customer's events and profile, then wipes all local state
     * (identity, entitlements, queue, super-props, persistent stores).
     *
     * Idempotent. Safe to call when no identity has been established
     * (it just wipes the empty local state).
     *
     * After forget() resolves, the SDK is in the same shape as if the
     * developer had called `Crossdeck.reset()` — a fresh anonymousId is
     * minted and the next session is a brand new identity-graph entry.
     */
    forget(): Promise<void>;
    /**
     * Read the current customer's active entitlements from the server.
     * Updates the local cache so subsequent isEntitled() calls answer
     * synchronously.
     */
    getEntitlements(): Promise<PublicEntitlement[]>;
    /**
     * Synchronous read from the durable local cache — answers from
     * last-known-good. The cache hydrates from device storage on boot and
     * survives a Crossdeck outage, so a returning paying customer reads
     * true even before the session's first network round-trip. Returns
     * false only for a genuinely new install that has never completed a
     * getEntitlements(), or for an entitlement past its own validUntil.
     */
    isEntitled(key: string): boolean;
    /** Snapshot of the local entitlement cache. */
    listEntitlements(): PublicEntitlement[];
    /**
     * Subscribe to entitlement-cache changes. Returns an unsubscribe fn.
     *
     * The listener is invoked AFTER the cache mutates — once after a
     * successful `getEntitlements()` warms it, again after `syncPurchases()`
     * delivers fresh entitlements, and once on `reset()` to fire the
     * empty-cache state for logout flows.
     *
     * It is NOT invoked synchronously on subscribe. Callers that need
     * the current state should read it via `isEntitled()` / `listEntitlements()`
     * inline; the listener fires only on FUTURE changes.
     *
     * This is the foundation of the `useEntitlement` React hook in
     * `@cross-deck/web/react` — without it, React (or SwiftUI / Compose
     * / Vue) would have no way to re-render when entitlements arrive
     * asynchronously after init. The naive pattern of calling
     * `Crossdeck.isEntitled("pro")` directly inside a render path
     * shows the empty-cache result forever; binding the result to
     * component state via `onEntitlementsChange` is the correct
     * pattern.
     *
     * Idempotent unsubscribe — calling the returned function multiple
     * times is safe.
     *
     * Listener errors are swallowed (a buggy listener can't crash the
     * SDK or other listeners).
     */
    onEntitlementsChange(listener: EntitlementsListener): () => void;
    /**
     * Queue a telemetry event. Returns immediately — the network round-
     * trip happens in the background. To flush before the page unloads,
     * call flush().
     */
    /**
     * Emit `crossdeck.contract_failed` to the Crossdeck reliability
     * endpoint — single-fire, one-way, never visible in the customer's
     * dashboard. Goes over a dedicated HTTP path with the reliability
     * publishable key embedded at build time; the customer's track()
     * pipeline never carries `crossdeck.*` events. This is the
     * independent-controller flow described in Privacy Policy §6
     * ("Flow B"). The wire shape is fixed by the schema-lock contract
     * at `contracts/diagnostics/contract-failed-payload-schema-lock.json`.
     *
     * Wire the call from a test hook, dogfood failure path, or
     * customer contract-verification harness; see
     * `contracts/README.md` for the per-test-framework hook recipes.
     */
    reportContractFailure(input: ContractFailureInput): void;
    track(name: string, properties?: EventProperties): void;
    /**
     * Force-flush queued events. Useful to call from page-unload handlers.
     *
     * Pass `{ keepalive: true }` from terminal handlers (pagehide /
     * visibilitychange→hidden / beforeunload). The browser keeps the
     * request alive after the page tears down, so the final batch
     * actually lands instead of being cancelled with the unload.
     *
     * NorthStar §4: standard method name across all Crossdeck SDKs.
     */
    flush(options?: {
        keepalive?: boolean;
    }): Promise<void>;
    /** @deprecated Use `flush()` instead. NorthStar §4 standardised the name. */
    flushEvents(): Promise<void>;
    /**
     * Forward purchase evidence to the backend for verification + entitlement
     * projection. NorthStar §4 + §13 canonical name.
     *
     * Today the web SDK only supports Apple StoreKit 2 forwarding (web apps
     * that sit alongside an iOS app). Stripe doesn't need this method —
     * Stripe webhooks deliver evidence server-side without a client round-trip.
     */
    syncPurchases(input: {
        rail?: "apple";
        signedTransactionInfo: string;
        signedRenewalInfo?: string;
        appAccountToken?: string;
    }): Promise<PurchaseResult>;
    /** @deprecated Use `syncPurchases()` instead. NorthStar §4 standardised the name. */
    purchaseApple(input: {
        signedTransactionInfo: string;
        signedRenewalInfo?: string;
        appAccountToken?: string;
    }): Promise<PurchaseResult>;
    /**
     * Toggle verbose diagnostic logging — NorthStar §16. When enabled, the
     * SDK emits a fixed vocabulary of debug signals to console.info that the
     * dashboard's onboarding checklist can also surface as live events.
     */
    setDebugMode(enabled: boolean): void;
    /**
     * Send the boot heartbeat. Called automatically by start() unless
     * autoHeartbeat:false. Safe to call manually as a "we're still here" ping.
     */
    heartbeat(): Promise<HeartbeatResponse>;
    /**
     * Wipe persisted identity + entitlement cache. Use on logout. The
     * next pre-login session generates a fresh anonymousId and starts a
     * new identity-graph entry.
     */
    reset(): void;
    /**
     * Diagnostic: current state + queue stats. Useful for the dashboard's
     * heartbeat row and debugging in dev.
     *
     * Returns a stable shape regardless of whether start() has been called —
     * callers don't need to narrow on `started` to access `events` or
     * `entitlements`. Pre-start values are sensible empties.
     */
    diagnostics(): Diagnostics;
    private requireStarted;
    /**
     * Build the identity query for /v1/entitlements. Priority:
     *   crossdeckCustomerId > developerUserId > anonymousId
     * — matches the resolveCrossdeckCustomerId precedence on the server.
     */
    private identityQueryParams;
    /**
     * Embed every known identity axis on the event. Earlier this returned
     * just the highest-priority hint (cdcust → developerUserId → anonymousId)
     * to keep payloads small, but that leaked into analytics: once a user
     * was logged in, every subsequent page.viewed shipped without
     * anonymousId, and `uniqExact(anonymous_id)` on the warehouse side
     * counted 0 visitors for the entire authenticated app.
     *
     * Bank-grade rule: the server is the single source of truth on
     * dedup. Send everything we know; let CH count by whichever axis
     * matches the question. Each field is at most 32 bytes — sending
     * three on every event costs ~80 bytes per request, which is
     * trivial compared to the analytics correctness it buys.
     */
    private identityHintForEvent;
    private mintEventId;
}
/**
 * Default singleton — most consumers want one SDK instance per app.
 * Creating extra instances is fine; just `new CrossdeckClient()`.
 */
declare const Crossdeck: CrossdeckClient;

/**
 * Stripe-style error wrapper for @cross-deck/web.
 *
 * Mirrors the wire shape returned by the v1 backend (see
 * backend/src/api/v1-errors.ts) so SDK consumers can `catch`
 * with consistent fields:
 *
 *   try {
 *     await crossdeck.identify("user_847");
 *   } catch (err) {
 *     if (err instanceof CrossdeckError && err.code === "invalid_api_key") {
 *       // ...
 *     }
 *   }
 */
type CrossdeckErrorType = "authentication_error" | "permission_error" | "invalid_request_error" | "rate_limit_error" | "internal_error" | "network_error" | "configuration_error";
interface CrossdeckErrorPayload {
    type: CrossdeckErrorType;
    code: string;
    message: string;
    /** Server-issued request ID. Echoed in support tickets. */
    requestId?: string;
    /** HTTP status code if the error came from an API response. */
    status?: number;
    /**
     * Server-suggested wait (in milliseconds) before retrying. Populated
     * from the `Retry-After` response header on 429 / 503. The header
     * spec allows either delta-seconds or an HTTP-date; the parser below
     * normalises both to milliseconds. Consumers MUST honour this — the
     * server is telling you the safe rate.
     */
    retryAfterMs?: number;
}
declare class CrossdeckError extends Error {
    readonly type: CrossdeckErrorType;
    readonly code: string;
    readonly requestId?: string;
    readonly status?: number;
    readonly retryAfterMs?: number;
    constructor(payload: CrossdeckErrorPayload);
}

/**
 * Storage adapters for SDK-persisted state.
 *
 * Three flavours:
 *   - browser localStorage (default in browsers)
 *   - 1st-party document.cookie (redundancy for cleared localStorage)
 *   - in-memory (default in Node, or as an explicit fallback)
 *
 * Detection is at construction time, not at every call — picking the
 * adapter once means we don't hit `typeof window` checks on hot paths.
 *
 * ----- Bank-grade identity continuity -----
 *
 * Plain localStorage is not enough. ITP, private browsing, "clear site
 * data" actions, and aggressive privacy extensions all wipe it. When
 * that happens, the SDK mints a fresh anonymousId on next page load
 * and the customer's analytics see one human as multiple "new
 * visitors" — a credibility hit on every dashboard chart that depends
 * on visitor uniqueness (new vs returning, retention, funnels).
 *
 * The fix is redundancy: we write the same identity to BOTH
 * localStorage AND a 1st-party cookie. On boot we read both; whichever
 * survived wins. On set, we write to both stores so a future clear of
 * either doesn't lose the user.
 *
 * Caveats (documented honestly):
 *   1. Safari ITP caps client-set 1st-party cookies at 7 days. Cookie
 *      redundancy protects against localStorage clears WITHIN that
 *      7-day window, not beyond it. The full ITP-bypass story (server-
 *      set cookies via a customer-CNAMEd subdomain) is a Phase 2
 *      follow-up that requires customer DNS configuration.
 *   2. We never write fingerprintable data — only the same anonymousId
 *      already in localStorage. Privacy posture is unchanged from
 *      single-store identity.
 *   3. `persistIdentity: false` disables BOTH stores so customers
 *      running strict consent flows can defer cookie writes until the
 *      user opts in.
 */

/**
 * In-memory storage. Cleared on process exit. Useful for Node runtimes
 * where you want session-scoped identity that doesn't persist to disk.
 */
declare class MemoryStorage implements KeyValueStorage {
    private store;
    getItem(key: string): string | null;
    setItem(key: string, value: string): void;
    removeItem(key: string): void;
}

/**
 * SDK version constant — generated by `scripts/sync-sdk-versions.mjs`.
 *
 * Single source of truth: the `version` field in this package's
 * package.json. The sync script writes this file so that
 * `SDK_VERSION` is a plain TypeScript literal at runtime — no
 * runtime JSON-import gotcha (Node ESM requires
 * `with { type: "json" }` to import JSON as ESM, and the published
 * dist file would otherwise fail to load).
 *
 * Drift protection: `node scripts/sync-sdk-versions.mjs --check` (the
 * CI gate) flags this file when it falls out of sync with package.json.
 * Bumping `package.json` without re-running the sync script fails CI.
 *
 * Do NOT edit by hand — `node scripts/sync-sdk-versions.mjs`.
 */
declare const SDK_VERSION = "1.5.3";
declare const SDK_NAME = "@cross-deck/web";

/**
 * HTTP transport for the SDK. Single fetch wrapper used by every endpoint
 * call. Adds the Bearer token and SDK version header, parses responses,
 * normalises errors to CrossdeckError.
 *
 * Uses platform-native fetch (browser + Node 18+). No axios, no isomorphic-
 * fetch shim, no transitive deps.
 */

declare const DEFAULT_BASE_URL = "https://api.cross-deck.com/v1";

/**
 * Machine-readable index of every error code the SDK can throw, with
 * a short description and a hint on what action to take. Published
 * verbatim as `crossdeck-error-codes.json` in the npm tarball so AI
 * integration assistants, error-aggregator dashboards (Sentry,
 * DataDog), and the Crossdeck dashboard can render human-friendly
 * messages without parsing freeform `message` strings.
 *
 * Stripe publishes the same surface at stripe.com/docs/error-codes;
 * developers love it because every code has a canonical "what does
 * this mean / what should I do" answer.
 *
 * Adding a new error code:
 *   1. Add the code string to the union in `errors.ts` (where used).
 *   2. Add an entry here.
 *   3. The next `npm run build` regenerates the JSON sidecar.
 *
 * Keep entries terse — the consumer surfaces this in tooltips and
 * automated tickets, not in long-form docs.
 */
interface ErrorCodeEntry {
    /** The string thrown as CrossdeckError.code. */
    code: string;
    /** CrossdeckError.type — broad category. */
    type: "authentication_error" | "permission_error" | "invalid_request_error" | "rate_limit_error" | "internal_error" | "network_error" | "configuration_error";
    /** One-sentence description. Surfaced verbatim in dashboards. */
    description: string;
    /** What the developer should do. Imperative phrasing. */
    resolution: string;
    /** True for codes the SDK can auto-recover from (no developer action). */
    retryable: boolean;
}
declare const CROSSDECK_ERROR_CODES: readonly ErrorCodeEntry[];
/** Lookup helper — returns the entry matching a CrossdeckError.code, or undefined. */
declare function getErrorCode(code: string): ErrorCodeEntry | undefined;

/**
 * Device + environment enrichment.
 *
 * Auto-attached to every event the SDK emits when `autoTrack.deviceInfo` is
 * enabled (default). Caller-supplied event properties always override
 * auto-detected ones (so a developer can manually set `app.version` per
 * event if they want to A/B between builds).
 *
 * Privacy posture:
 *   - No fingerprinting (no canvas hashes, no font enumeration).
 *   - No precise geolocation (only timezone + locale, both of which the
 *     browser exposes to every page anyway).
 *   - No IP collection — the backend logs the request IP for rate-limit
 *     purposes; it isn't stored on the event document.
 *   - All fields are typed enums or short strings; we never echo back
 *     full User-Agent strings to avoid surfacing fingerprintable detail
 *     in dashboards.
 */
interface DeviceInfo {
    os?: string;
    osVersion?: string;
    browser?: string;
    browserVersion?: string;
    locale?: string;
    timezone?: string;
    screenWidth?: number;
    screenHeight?: number;
    viewportWidth?: number;
    viewportHeight?: number;
    devicePixelRatio?: number;
    /** Caller-supplied. Set via Crossdeck.start({ appVersion: "1.2.3" }). */
    appVersion?: string;
}

export { AliasResult, type Breadcrumb, type BreadcrumbCategory, type BreadcrumbLevel, CROSSDECK_ERROR_CODES, type CapturedError, type ConsentState, type Contract, type ContractAppliesTo, type ContractFailureInput, type ContractPillar, type ContractStatus, type ContractTestRef, Crossdeck, CrossdeckClient, CrossdeckContracts, CrossdeckError, type CrossdeckErrorPayload, type CrossdeckErrorType, CrossdeckOptions, DEFAULT_BASE_URL, type DeviceInfo, Diagnostics, type ErrorCodeEntry, type ErrorLevel, EventProperties, GroupTraits, HeartbeatResponse, IdentifyOptions, KeyValueStorage, MemoryStorage, PublicEntitlement, PurchaseResult, SDK_NAME, SDK_VERSION, type StackFrame, getErrorCode };
