{"version":3,"sources":["../src/consent-banner.ts","../src/consent-coexistence.ts","../src/index.ts","../src/errors.ts","../src/_version.ts","../src/http.ts","../src/identity.ts","../src/trust.ts","../src/hash.ts","../src/entitlement-cache.ts","../src/idempotency-key.ts","../src/retry-policy.ts","../src/event-queue.ts","../src/event-storage.ts","../src/storage.ts","../src/read-cost-bridge.ts","../src/device-info.ts","../src/auto-track.ts","../src/debug.ts","../src/event-validation.ts","../src/super-properties.ts","../src/internal-opt-out.ts","../src/web-vitals.ts","../src/consent.ts","../src/breadcrumbs.ts","../src/_diagnostic-telemetry.ts","../src/error-codes.ts","../src/_contract-verifiers.ts","../src/stack-parser.ts","../src/error-capture.ts","../src/crossdeck.ts","../src/singleton.ts","../src/_contracts-bundled.ts","../src/contracts.ts"],"sourcesContent":["/**\n * Crossdeck Consent — the branded, Stripe-premium consent surface.\n *\n * This is the CONSENT-MODE GUEST PRIMITIVE (Path B, CD-175 / CD-185): the\n * consent widget bundled inside the injected SDK on marketplace installs\n * (Webflow, Wix, Framer, WordPress, WooCommerce, Squarespace, Bubble) and the\n * direct install. It renders the signed-off compact bottom-left arrival banner\n * (Accept all / Manage / Reject all) and a four-switch Manage pane — Strictly\n * necessary (locked) · Analytics (anonymous) · Recognize me (identity opt-in) ·\n * Marketing — in a style-isolated Shadow DOM, and records a withdrawable,\n * auditable consent choice that gates all downstream autocapture.\n *\n * Relationship to the rest of the SDK:\n *  - It WIRES to the existing {@link ../consent ConsentManager} (analytics /\n *    marketing dimensions) — it never forks a parallel consent state. The\n *    NEW \"Recognize me\" switch drives an `identityOptIn` boolean that this\n *    widget only RECORDS and EMITS; another module gates `identify()` on it.\n *  - The FULL-CAPABILITY / Path-A direct build is UNAFFECTED by this file.\n *    Error tracking, host-global monkey-patching and the Trust iframe are\n *    deliberately absent here — this guest build is the leaner, review-safe\n *    consent set only (CD-175 #6 / #7, CD-185 Part 2A).\n *\n * Non-negotiables baked in (Stripe / bank grade, `trust-bank-grade-no-cleverness`):\n *  - CONSENT-FIRST — everything defaults OFF except Strictly necessary; nothing\n *    is auto-on. No capture is implied until the visitor makes a choice.\n *  - GENUINE CHOICE, NEVER A DARK PATTERN — refusing (Reject all) is exactly as\n *    prominent and one-click as accepting. No pre-ticked boxes, no nag pill.\n *  - STYLE-ISOLATED — rendered in a Shadow DOM: the host page's CSS cannot leak\n *    in, and our CSS cannot leak out. In-page, NOT an iframe.\n *  - AUDITABLE + WITHDRAWABLE — every choice is persisted with proof\n *    (categories, method, timestamp, policyVersion, id) and re-openable at any\n *    time via `[data-crossdeck-consent]` or `handle.open()`. A policyVersion\n *    bump re-prompts (re-consent).\n *  - POLICY-URL REQUIRED — in managed mode the widget will NOT render without\n *    the site owner's real privacy-policy URL; it surfaces a clear diagnostic.\n *  - DIAGNOSTIC, NEVER SILENT — risky DOM work is wrapped in `safe()`, which\n *    logs a concise `[crossdeck] consent: …` warning rather than swallowing\n *    the failure (Webflow finding #11).\n */\n\nimport type { ConsentManager } from \"./consent\";\n\n/** Informational \"what is Crossdeck Consent\" page — the attribution/acquisition link. */\nexport const CONSENT_INFO_URL = \"https://cross-deck.com/consent\";\n\n/** localStorage key for the auditable consent record. Origin-scoped by the browser. */\nexport const CONSENT_STORAGE_KEY = \"crossdeck.consent.v1\";\n\n/**\n * The consent choice this widget records and emits. Strictly-necessary is always\n * on and is NOT represented here (it is not a toggleable dimension).\n *\n *  - `analytics`      → maps to ConsentManager `analytics`.\n *  - `marketing`      → maps to ConsentManager `marketing`.\n *  - `identityOptIn`  → NEW. The \"Recognize me\" switch. NOT a ConsentManager\n *                       dimension — the site's `identify()` is gated on it by a\n *                       separate module; here it is only recorded + emitted.\n */\nexport interface ConsentBannerState {\n  analytics: boolean;\n  marketing: boolean;\n  identityOptIn: boolean;\n}\n\n/** How the visitor arrived at a choice — part of the auditable record. */\nexport type ConsentMethod = \"accept_all\" | \"reject_all\" | \"custom\";\n\n/**\n * The auditable, withdrawable consent record. Persisted to localStorage so the\n * site owner (the data controller) can demonstrate consent if challenged.\n */\nexport interface ConsentRecord {\n  /** The per-category choice. Strictly-necessary is implicit (always on). */\n  categories: ConsentBannerState;\n  /** Which control produced the choice. */\n  method: ConsentMethod;\n  /** Epoch ms at which the choice was recorded. */\n  timestamp: number;\n  /** The site's policy version in effect when consent was given. */\n  policyVersion: string;\n  /** Opaque unique id for this consent event. */\n  id: string;\n}\n\n/**\n * Which consent regime this install runs. `managed` = Crossdeck renders the\n * banner and REQUIRES the owner's `policyUrl`. Deferral to a host CMP is handled\n * upstream (CD-185 Part 1) — a deferred install simply does not mount this widget.\n */\nexport type ConsentBannerMode = \"managed\";\n\n/** Copy + tag for one category row in the Manage pane. Config is per-build. */\nexport interface ConsentCategoryConfig {\n  /** Row title, e.g. \"Analytics\". */\n  title: string;\n  /** Enumerated body copy — MUST state exactly what is captured and its identity status. */\n  description: string;\n  /** Optional pill next to the title, e.g. { text: \"Anonymous\", kind: \"anon\" }. */\n  tag?: { text: string; kind: \"anon\" | \"locked\" | \"optin\" };\n}\n\n/** The four-category copy set. Defaults match the signed-off Webflow build. */\nexport interface ConsentCategoriesConfig {\n  necessary: ConsentCategoryConfig;\n  analytics: ConsentCategoryConfig;\n  identity: ConsentCategoryConfig;\n  marketing: ConsentCategoryConfig;\n}\n\nexport interface ConsentBannerOptions {\n  /** Where to attach the widget host. Element or selector. Defaults to `document.body`. */\n  target?: HTMLElement | string;\n  /**\n   * The site owner's real privacy/cookie-policy URL. REQUIRED in managed mode —\n   * no dead `#`, no Crossdeck-hosted stand-in. The widget refuses to render\n   * without it and logs a diagnostic (CD-185 Part 2).\n   */\n  policyUrl?: string;\n  /** Per-category copy overrides. Omitted categories fall back to the signed-off defaults. */\n  categories?: Partial<ConsentCategoriesConfig>;\n  /** Called on every recorded choice with the resulting state. */\n  onChange?: (state: ConsentBannerState) => void;\n  /** Consent regime. Only `managed` renders a banner. Default `managed`. */\n  mode?: ConsentBannerMode;\n  /**\n   * A previously known choice (e.g. from the site owner's server). Used to seed\n   * the widget when no localStorage record is present. If it satisfies the\n   * current `policyVersion`, the banner stays hidden and the choice is applied.\n   */\n  existingConsent?: Partial<ConsentBannerState>;\n  /**\n   * The site's current policy version. When it differs from the stored record's\n   * version, the widget re-prompts (re-consent). Defaults to `\"1\"`.\n   */\n  policyVersion?: string;\n  /**\n   * The SDK's {@link ConsentManager} (or anything with a compatible `set`). The\n   * widget calls `.set({ analytics, marketing })` on every choice so autocapture\n   * follows consent live. `identityOptIn` is emitted separately via `onChange`.\n   */\n  consent?: Pick<ConsentManager, \"set\">;\n  /** Override the info/attribution link target (tests / self-host). */\n  infoUrl?: string;\n}\n\nexport interface ConsentBannerHandle {\n  /** The host element carrying the shadow root — for layout only; never reach inside. */\n  readonly host: HTMLElement | null;\n  /** Re-open the Manage pane (the withdrawal affordance). Idempotent, safe post-choice. */\n  open(): void;\n  /** The current persisted consent record, or `null` if none has been recorded. */\n  getRecord(): ConsentRecord | null;\n  /** The current in-memory state (defaults to all-off until a choice is recorded). */\n  getState(): ConsentBannerState;\n  /** Tear down: remove the host, delegated listener, and media subscription. Idempotent. */\n  destroy(): void;\n}\n\n/** The `Crossdeck.consent` widget namespace surfaced on the SDK client. */\nexport interface CrossdeckConsentNamespace {\n  /** Mount the branded consent banner. See {@link mountConsentBanner}. */\n  banner(opts: ConsentBannerOptions): ConsentBannerHandle;\n}\n\n// The gradient-cross brand mark, rendered inline. Uses the shadow-local `#cd`\n// gradient defined once in the shadow root (isolation keeps the id collision-free).\nconst MARK = `<svg class=\"mark\" viewBox=\"0 0 48 48\" aria-hidden=\"true\"><path fill=\"url(#cd)\" d=\"M13.9 8.2a4 4 0 0 0-5.7 5.6L18.3 24 8.2 34.2a4 4 0 1 0 5.7 5.6L24 29.7l10.1 10.1a4 4 0 0 0 5.7-5.6L29.7 24l10.1-10.2a4 4 0 1 0-5.7-5.6L24 18.3 13.9 8.2Z\"/></svg>`;\n\nconst DEFAULT_CATEGORIES: ConsentCategoriesConfig = {\n  necessary: {\n    title: \"Strictly necessary\",\n    description:\n      \"The site's own essential cookies — sign-in, session, core function. Crossdeck sets nothing here.\",\n    tag: { text: \"On\", kind: \"locked\" },\n  },\n  analytics: {\n    title: \"Analytics\",\n    description:\n      'Page views, sessions, product usage — anonymous. Identified only if you turn on \"Recognize me\", never automatically.',\n    tag: { text: \"Anonymous\", kind: \"anon\" },\n  },\n  identity: {\n    title: \"Recognize me\",\n    description:\n      \"Link activity to your account across visits and devices. On = your explicit identity opt-in; off = anonymous even when signed in.\",\n    tag: { text: \"Identity opt-in\", kind: \"optin\" },\n  },\n  marketing: {\n    title: \"Marketing\",\n    description:\n      \"Referrer + ad-click IDs (gclid, fbclid…) for attribution, and a HubSpot arrival signal.\",\n  },\n};\n\nconst DEFAULT_STATE: ConsentBannerState = {\n  analytics: false,\n  marketing: false,\n  identityOptIn: false,\n};\n\nconst STYLE = `\n:host{ all: initial; }\n.cd-root{\n  --stage:#efe9dd; --surface:#ffffff; --surface-2:#faf7f0;\n  --text:#17130f; --muted:#6b6259; --faint:#9a9088;\n  --border:rgba(23,19,17,.11); --hair:rgba(23,19,17,.07);\n  --accent:#ee4f25; --accent-strong:#c23a15;\n  --brand-a:#FF3D2E; --brand-b:#FF9A3D;\n  --track:rgba(23,19,17,.15); --lock:#efe9de;\n  --shadow:0 16px 40px -14px rgba(23,19,17,.30), 0 2px 6px rgba(23,19,17,.05);\n  font-family:'Inter',-apple-system,system-ui,sans-serif;\n  color:var(--text); -webkit-font-smoothing:antialiased;\n}\n.cd-root.dark{\n  --stage:#141110; --surface:#1c1917; --surface-2:#232019;\n  --text:#f4efe8; --muted:#a89f95; --faint:#7c7267;\n  --border:rgba(244,239,232,.13); --hair:rgba(244,239,232,.08);\n  --accent:#ff6a42; --accent-strong:#ff8862;\n  --track:rgba(244,239,232,.17); --lock:#2a2521;\n  --shadow:0 20px 46px -14px rgba(0,0,0,.72), 0 2px 6px rgba(0,0,0,.4);\n}\n.cd-root *{box-sizing:border-box}\n.hidden{display:none!important}\n.mark{flex-shrink:0}\n\n.dock{position:fixed;left:20px;bottom:20px;z-index:2147483000}\n\n/* ---- ARRIVAL — slim compact banner ---- */\n.arr{width:330px;background:var(--surface);border:1px solid var(--border);border-radius:15px;box-shadow:var(--shadow);\n  padding:15px 16px 14px}\n.arr-brand{display:flex;align-items:center;gap:7px;margin-bottom:8px}\n.arr-brand .mark{width:16px;height:16px}\n.arr-brand b{font-size:12.5px;font-weight:640}\n.arr-brand .by{margin-left:auto;font-size:9.5px;font-weight:600;color:var(--faint);display:inline-flex;align-items:center;gap:4px;text-decoration:none}\n.arr-brand .by .mark{width:10px;height:10px}\n.arr-brand .by:hover{color:var(--muted)}\n.arr p{font-size:12px;line-height:1.5;color:var(--muted);margin:0 0 12px}\n.arr p b{color:var(--text);font-weight:600}\n.arr p a{color:var(--accent-strong);font-weight:600;text-decoration:none}\n.arr-btns{display:flex;align-items:center;gap:7px}\n.arr-btns .btn{flex:1}\n\n/* buttons */\n.btn{font:inherit;font-family:inherit;font-size:12px;font-weight:640;padding:8px 12px;border-radius:9px;cursor:pointer;\n  border:1px solid var(--border);background:var(--surface);color:var(--text);transition:transform .1s,border-color .12s}\n.btn:hover{transform:translateY(-1px);border-color:var(--accent)}\n.btn:focus-visible{outline:2px solid var(--accent);outline-offset:2px}\n.btn.primary{border:none;color:#fff;background:linear-gradient(120deg,var(--brand-a),var(--brand-b));box-shadow:0 4px 12px -3px rgba(255,61,46,.5)}\n.btn.primary:hover{border:none}\n\n/* ---- MANAGE — compact, bottom-left, corner-anchored ---- */\n.mg{width:360px;background:var(--surface);border:1px solid var(--border);border-radius:15px;box-shadow:var(--shadow);overflow:hidden}\n.mg-head{padding:15px 16px 12px;border-bottom:1px solid var(--hair)}\n.mg-brand{display:flex;align-items:center;gap:7px;margin-bottom:8px}\n.mg-brand .mark{width:16px;height:16px}.mg-brand b{font-size:12.5px;font-weight:640}\n.mg-brand .env{margin-left:auto;font-size:9.5px;font-weight:600;color:var(--faint)}\n.mg-head strong{font-size:13.5px;font-weight:670;display:block}\n.cats{padding:2px 16px;max-height:250px;overflow-y:auto}\n.cat{padding:11px 0;border-bottom:1px solid var(--hair)}\n.cat:last-child{border-bottom:0}\n.cat-top{display:flex;align-items:flex-start;gap:10px}\n.cat-t{display:flex;flex-direction:column;gap:2px;min-width:0}\n.cat-t strong{font-size:12px;font-weight:640;display:flex;align-items:center;gap:6px;flex-wrap:wrap}\n.tag{font-size:8.5px;font-weight:700;letter-spacing:.03em;text-transform:uppercase;padding:1.5px 5px;border-radius:4px}\n.tag.anon{background:rgba(31,157,85,.12);color:#1f9d55}\n.tag.locked{background:var(--surface-2);color:var(--faint);border:1px solid var(--hair)}\n.tag.optin{background:rgba(238,79,37,.12);color:var(--accent-strong)}\n.cd-root.dark .tag.optin{background:rgba(255,106,66,.16)}\n.cat-t span{font-size:10.5px;line-height:1.45;color:var(--muted)}\n.cat-t span b{color:var(--text)}\n.cat-t span code{font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:10px}\n.sw{margin-left:auto;margin-top:2px;flex-shrink:0;position:relative;width:36px;height:21px;border-radius:99px;background:var(--track);border:none;cursor:pointer;transition:background .18s;padding:0}\n.sw::after{content:\"\";position:absolute;top:2.5px;left:2.5px;width:16px;height:16px;border-radius:50%;background:#fff;box-shadow:0 1px 2px rgba(0,0,0,.28);transition:transform .18s}\n.sw.on{background:linear-gradient(120deg,var(--brand-a),var(--brand-b))}\n.sw.on::after{transform:translateX(15px)}\n.sw:focus-visible{outline:2px solid var(--accent);outline-offset:2px}\n.sw.locked{background:var(--lock);cursor:not-allowed}\n.sw.locked::after{background:linear-gradient(120deg,var(--brand-a),var(--brand-b));transform:translateX(15px);box-shadow:none}\n.mg-actions{padding:12px 16px;display:flex;gap:7px;background:var(--surface-2);border-top:1px solid var(--hair)}\n.mg-actions .btn{flex:1}\n.mg-foot{padding:9px 16px;display:flex;align-items:center;justify-content:space-between;background:var(--surface-2);border-top:1px solid var(--hair)}\n.mg-foot a{font-size:10px;font-weight:600;color:var(--faint);display:inline-flex;align-items:center;gap:5px;text-decoration:none}\n.mg-foot a .mark{width:11px;height:11px}\n.mg-foot a:hover{color:var(--muted)}\n.mg-foot span{font-size:9px;color:var(--faint)}\n`;\n\n/**\n * Mount the Crossdeck Consent banner. Framework-agnostic and guaranteed not to\n * throw — any construction failure is logged and an inert handle returned so the\n * host app is never destabilised by our consent surface.\n */\nexport function mountConsentBanner(\n  opts: ConsentBannerOptions,\n): ConsentBannerHandle {\n  const mode: ConsentBannerMode = opts.mode || \"managed\";\n  const policyUrl = typeof opts.policyUrl === \"string\" ? opts.policyUrl.trim() : \"\";\n  const policyVersion =\n    typeof opts.policyVersion === \"string\" && opts.policyVersion\n      ? opts.policyVersion\n      : \"1\";\n  const infoUrl = normalizeUrl(opts.infoUrl) || CONSENT_INFO_URL;\n  const cats = mergeCategories(opts.categories);\n\n  // Mutable widget state — the single source of truth for switch positions.\n  const state: ConsentBannerState = { ...DEFAULT_STATE };\n\n  let hostEl: HTMLElement | null = null;\n  let root: ShadowRoot | null = null;\n  let rootEl: HTMLElement | null = null;\n  let arrEl: HTMLElement | null = null;\n  let mgEl: HTMLElement | null = null;\n  let mql: MediaQueryList | null = null;\n  let onScheme: ((e: MediaQueryListEvent) => void) | null = null;\n  let onDelegatedClick: ((e: Event) => void) | null = null;\n  let record: ConsentRecord | null = null;\n  let destroyed = false;\n\n  // --- managed mode requires the owner's real policy URL (CD-185 Part 2) ---\n  if (mode === \"managed\" && !policyUrl) {\n    warn(\n      \"policyUrl is required in managed mode — refusing to render a policy-less banner. Pass opts.policyUrl (the site owner's real privacy-policy URL).\",\n    );\n    return makeInertHandle();\n  }\n\n  if (typeof window === \"undefined\" || typeof document === \"undefined\") {\n    warn(\"no DOM available (SSR / non-browser) — consent banner not mounted.\");\n    return makeInertHandle();\n  }\n\n  // --- resolve prior consent: stored record wins, else existingConsent seed ---\n  record = readRecord();\n  const stale = record !== null && record.policyVersion !== policyVersion;\n  let alreadyConsented = false;\n\n  if (record && !stale) {\n    Object.assign(state, record.categories);\n    alreadyConsented = true;\n  } else if (!record && opts.existingConsent) {\n    // Seed from an integrator-supplied prior choice; treat as current-policy consent.\n    Object.assign(state, sanitizeState(opts.existingConsent));\n    alreadyConsented = true;\n  }\n\n  // Build the shadow-isolated UI up front so `open()` works whether or not the\n  // arrival banner is shown on load.\n  safe(() => build(), \"failed to build widget\");\n\n  if (alreadyConsented) {\n    // Consent already on file for this policy — apply it, keep the surface hidden,\n    // but keep the reopen affordance live so withdrawal stays as easy as granting.\n    applyToManager();\n    emit();\n    hideAll();\n  } else {\n    // Fresh visitor, or the policy changed (re-consent) — prompt.\n    showArrival();\n  }\n\n  bindDelegatedReopen();\n  subscribeScheme();\n\n  return {\n    get host() {\n      return hostEl;\n    },\n    open: openManage,\n    getRecord() {\n      return record ? { ...record, categories: { ...record.categories } } : null;\n    },\n    getState() {\n      return { ...state };\n    },\n    destroy,\n  };\n\n  // ---------------------------------------------------------------- build ----\n\n  function build(): void {\n    hostEl = document.createElement(\"div\");\n    hostEl.setAttribute(\"data-crossdeck-consent-host\", \"\");\n    // Keep the host out of the host page's layout/flow; the dock is fixed.\n    hostEl.style.cssText = \"position:static;all:initial;\";\n\n    root = hostEl.attachShadow({ mode: \"open\" });\n\n    const style = document.createElement(\"style\");\n    style.textContent = STYLE;\n    root.appendChild(style);\n\n    rootEl = document.createElement(\"div\");\n    rootEl.className = \"cd-root\";\n    rootEl.innerHTML = template();\n    root.appendChild(rootEl);\n\n    arrEl = rootEl.querySelector(\".arr\");\n    mgEl = rootEl.querySelector(\".mg\");\n\n    // Fill in owner-configured URLs programmatically (never via innerHTML) so a\n    // hostile URL can't break out of the attribute context.\n    setHref(rootEl.querySelector(\".arr .policy-link\"), policyUrl);\n    setHref(rootEl.querySelector(\".by\"), infoUrl);\n    setHref(rootEl.querySelector(\".mg-foot a\"), infoUrl);\n\n    wireControls();\n    syncSwitches();\n\n    const parent = resolveTarget(opts.target) || document.body;\n    if (!parent) {\n      warn(\"no mount target and no document.body — consent banner not attached.\");\n      return;\n    }\n    parent.appendChild(hostEl);\n  }\n\n  function template(): string {\n    const gradient = `<svg width=\"0\" height=\"0\" style=\"position:absolute\" aria-hidden=\"true\"><defs><linearGradient id=\"cd\" x1=\"8\" y1=\"6\" x2=\"40\" y2=\"42\" gradientUnits=\"userSpaceOnUse\"><stop offset=\"0\" stop-color=\"#FF3D2E\"/><stop offset=\"1\" stop-color=\"#FF9A3D\"/></linearGradient></defs></svg>`;\n    return (\n      gradient +\n      `<div class=\"dock\">` +\n      arrivalMarkup() +\n      manageMarkup() +\n      `</div>`\n    );\n  }\n\n  function arrivalMarkup(): string {\n    return (\n      `<div class=\"arr\" role=\"dialog\" aria-label=\"Your privacy\" aria-modal=\"false\">` +\n      `<div class=\"arr-brand\">${MARK}<b>Your privacy</b>` +\n      `<a class=\"by\" target=\"_blank\" rel=\"noopener\">${MARK}by Crossdeck</a></div>` +\n      `<p><b>You choose what's collected.</b> Nothing but essentials runs until you say so. ` +\n      `<a class=\"policy-link\" target=\"_blank\" rel=\"noopener\">Privacy policy</a>.</p>` +\n      `<div class=\"arr-btns\">` +\n      `<button type=\"button\" class=\"btn primary\" data-act=\"accept_all\">Accept all</button>` +\n      `<button type=\"button\" class=\"btn\" data-act=\"manage\">Manage</button>` +\n      `<button type=\"button\" class=\"btn\" data-act=\"reject_all\">Reject all</button>` +\n      `</div></div>`\n    );\n  }\n\n  function manageMarkup(): string {\n    return (\n      `<div class=\"mg hidden\" role=\"dialog\" aria-label=\"Manage privacy preferences\" aria-modal=\"false\">` +\n      `<div class=\"mg-head\"><div class=\"mg-brand\">${MARK}<b>Your privacy</b>` +\n      `<span class=\"env\">This site uses Crossdeck</span></div>` +\n      `<strong>Manage preferences</strong></div>` +\n      `<div class=\"cats\">` +\n      catRow(cats.necessary, null, true) +\n      catRow(cats.analytics, \"analytics\", false) +\n      catRow(cats.identity, \"identityOptIn\", false) +\n      catRow(cats.marketing, \"marketing\", false) +\n      `</div>` +\n      `<div class=\"mg-actions\">` +\n      `<button type=\"button\" class=\"btn\" data-act=\"reject_all\">Reject all</button>` +\n      `<button type=\"button\" class=\"btn\" data-act=\"accept_all\">Accept all</button>` +\n      `<button type=\"button\" class=\"btn primary\" data-act=\"save\">Save</button>` +\n      `</div>` +\n      `<div class=\"mg-foot\"><a target=\"_blank\" rel=\"noopener\">${MARK}Consent by Crossdeck</a>` +\n      `<span>processes data on this site's behalf</span></div>` +\n      `</div>`\n    );\n  }\n\n  function catRow(\n    cfg: ConsentCategoryConfig,\n    key: keyof ConsentBannerState | null,\n    locked: boolean,\n  ): string {\n    const tag = cfg.tag\n      ? `<span class=\"tag ${cfg.tag.kind}\">${escapeHtml(cfg.tag.text)}</span>`\n      : \"\";\n    const sw = locked\n      ? `<button type=\"button\" class=\"sw locked\" disabled aria-label=\"${escapeHtml(cfg.title)} (always on)\"></button>`\n      : `<button type=\"button\" class=\"sw\" role=\"switch\" aria-checked=\"false\" data-cat=\"${String(key)}\" aria-label=\"${escapeHtml(cfg.title)}\"></button>`;\n    return (\n      `<div class=\"cat\"><div class=\"cat-top\"><div class=\"cat-t\">` +\n      `<strong>${escapeHtml(cfg.title)} ${tag}</strong>` +\n      `<span>${escapeHtml(cfg.description)}</span></div>${sw}</div></div>`\n    );\n  }\n\n  // ------------------------------------------------------------- wiring ----\n\n  function wireControls(): void {\n    if (!rootEl) return;\n    const buttons = rootEl.querySelectorAll<HTMLButtonElement>(\"[data-act]\");\n    buttons.forEach((btn) => {\n      btn.addEventListener(\"click\", () => {\n        const act = btn.getAttribute(\"data-act\");\n        if (act === \"manage\") showManage();\n        else if (act === \"accept_all\") choose(\"accept_all\");\n        else if (act === \"reject_all\") choose(\"reject_all\");\n        else if (act === \"save\") choose(\"custom\");\n      });\n    });\n    const switches = rootEl.querySelectorAll<HTMLButtonElement>(\".sw[data-cat]\");\n    switches.forEach((el) => {\n      el.addEventListener(\"click\", () => {\n        const key = el.getAttribute(\"data-cat\") as keyof ConsentBannerState | null;\n        if (!key) return;\n        state[key] = !state[key];\n        syncSwitches();\n      });\n    });\n  }\n\n  /** Reflect `state` onto the switch DOM (class + aria). */\n  function syncSwitches(): void {\n    if (!rootEl) return;\n    rootEl.querySelectorAll<HTMLButtonElement>(\".sw[data-cat]\").forEach((el) => {\n      const key = el.getAttribute(\"data-cat\") as keyof ConsentBannerState | null;\n      if (!key) return;\n      const on = state[key] === true;\n      el.classList.toggle(\"on\", on);\n      el.setAttribute(\"aria-checked\", on ? \"true\" : \"false\");\n    });\n  }\n\n  // ------------------------------------------------------------ choices ----\n\n  function choose(method: ConsentMethod): void {\n    if (method === \"accept_all\") {\n      state.analytics = true;\n      state.marketing = true;\n      state.identityOptIn = true;\n    } else if (method === \"reject_all\") {\n      state.analytics = false;\n      state.marketing = false;\n      state.identityOptIn = false;\n    }\n    // `custom` keeps the current per-switch state as-is.\n    syncSwitches();\n    persist(method);\n    applyToManager();\n    emit();\n    hideAll();\n  }\n\n  function persist(method: ConsentMethod): void {\n    record = {\n      categories: { ...state },\n      method,\n      timestamp: Date.now(),\n      policyVersion,\n      id: makeId(),\n    };\n    safe(() => {\n      window.localStorage.setItem(CONSENT_STORAGE_KEY, JSON.stringify(record));\n    }, \"could not persist consent record to localStorage\");\n  }\n\n  function applyToManager(): void {\n    if (!opts.consent) return;\n    safe(() => {\n      opts.consent?.set({ analytics: state.analytics, marketing: state.marketing });\n    }, \"could not apply consent to the ConsentManager\");\n  }\n\n  function emit(): void {\n    if (!opts.onChange) return;\n    safe(() => opts.onChange?.({ ...state }), \"onChange callback threw\");\n  }\n\n  // -------------------------------------------------------- visibility ----\n\n  function showArrival(): void {\n    toggle(arrEl, true);\n    toggle(mgEl, false);\n  }\n\n  function showManage(): void {\n    syncSwitches();\n    toggle(arrEl, false);\n    toggle(mgEl, true);\n  }\n\n  function hideAll(): void {\n    // After a choice the widget DISAPPEARS COMPLETELY — no floating pill.\n    toggle(arrEl, false);\n    toggle(mgEl, false);\n  }\n\n  function openManage(): void {\n    if (destroyed) return;\n    safe(() => showManage(), \"could not reopen the Manage pane\");\n  }\n\n  function toggle(el: HTMLElement | null, visible: boolean): void {\n    if (el) el.classList.toggle(\"hidden\", !visible);\n  }\n\n  // ----------------------------------------------------- reopen / theme ----\n\n  function bindDelegatedReopen(): void {\n    onDelegatedClick = (e: Event) => {\n      const path = e.composedPath ? e.composedPath() : [];\n      for (const node of path) {\n        if (node instanceof Element && node.closest(\"[data-crossdeck-consent]\")) {\n          e.preventDefault();\n          openManage();\n          return;\n        }\n      }\n      // Fallback for environments without composedPath.\n      const t = e.target;\n      if (t instanceof Element && t.closest(\"[data-crossdeck-consent]\")) {\n        e.preventDefault();\n        openManage();\n      }\n    };\n    safe(\n      () => document.addEventListener(\"click\", onDelegatedClick as EventListener),\n      \"could not bind [data-crossdeck-consent] reopen listener\",\n    );\n  }\n\n  function subscribeScheme(): void {\n    safe(() => {\n      mql = window.matchMedia(\"(prefers-color-scheme: dark)\");\n      applyScheme(mql.matches);\n      onScheme = (e: MediaQueryListEvent) => applyScheme(e.matches);\n      // addEventListener is the modern API; older Safari only has addListener.\n      if (typeof mql.addEventListener === \"function\") {\n        mql.addEventListener(\"change\", onScheme);\n      } else if (typeof mql.addListener === \"function\") {\n        mql.addListener(onScheme);\n      }\n    }, \"could not subscribe to prefers-color-scheme\");\n  }\n\n  function applyScheme(dark: boolean): void {\n    if (rootEl) rootEl.classList.toggle(\"dark\", dark);\n  }\n\n  // -------------------------------------------------------- lifecycle ----\n\n  function destroy(): void {\n    if (destroyed) return;\n    destroyed = true;\n    if (onDelegatedClick) {\n      safe(\n        () => document.removeEventListener(\"click\", onDelegatedClick as EventListener),\n        \"could not remove reopen listener\",\n      );\n    }\n    if (mql && onScheme) {\n      safe(() => {\n        if (typeof mql?.removeEventListener === \"function\") {\n          mql.removeEventListener(\"change\", onScheme as (e: MediaQueryListEvent) => void);\n        } else if (typeof mql?.removeListener === \"function\") {\n          mql.removeListener(onScheme as (e: MediaQueryListEvent) => void);\n        }\n      }, \"could not remove scheme listener\");\n    }\n    safe(() => hostEl?.parentNode?.removeChild(hostEl as HTMLElement), \"could not remove host node\");\n  }\n\n  // ------------------------------------------------------------ helpers ----\n\n  function readRecord(): ConsentRecord | null {\n    let raw: string | null = null;\n    safe(() => {\n      raw = window.localStorage.getItem(CONSENT_STORAGE_KEY);\n    }, \"could not read consent record from localStorage\");\n    if (!raw) return null;\n    try {\n      const parsed = JSON.parse(raw) as Partial<ConsentRecord>;\n      if (!parsed || typeof parsed !== \"object\" || !parsed.categories) return null;\n      return {\n        categories: sanitizeState(parsed.categories),\n        method: (parsed.method as ConsentMethod) || \"custom\",\n        timestamp: typeof parsed.timestamp === \"number\" ? parsed.timestamp : 0,\n        policyVersion:\n          typeof parsed.policyVersion === \"string\" ? parsed.policyVersion : \"1\",\n        id: typeof parsed.id === \"string\" ? parsed.id : makeId(),\n      };\n    } catch (err) {\n      warn(\"stored consent record was not valid JSON — ignoring it.\", err);\n      return null;\n    }\n  }\n\n  function makeInertHandle(): ConsentBannerHandle {\n    return {\n      host: null,\n      open() {\n        warn(\"open() called but the banner did not mount (see prior warning).\");\n      },\n      getRecord() {\n        return null;\n      },\n      getState() {\n        return { ...DEFAULT_STATE };\n      },\n      destroy() {\n        /* nothing was mounted */\n      },\n    };\n  }\n}\n\n// ============================================================\n// Module-level pure helpers\n// ============================================================\n\nfunction mergeCategories(\n  overrides?: Partial<ConsentCategoriesConfig>,\n): ConsentCategoriesConfig {\n  if (!overrides) return DEFAULT_CATEGORIES;\n  return {\n    necessary: overrides.necessary || DEFAULT_CATEGORIES.necessary,\n    analytics: overrides.analytics || DEFAULT_CATEGORIES.analytics,\n    identity: overrides.identity || DEFAULT_CATEGORIES.identity,\n    marketing: overrides.marketing || DEFAULT_CATEGORIES.marketing,\n  };\n}\n\nfunction sanitizeState(partial: Partial<ConsentBannerState>): ConsentBannerState {\n  return {\n    analytics: partial.analytics === true,\n    marketing: partial.marketing === true,\n    identityOptIn: partial.identityOptIn === true,\n  };\n}\n\nfunction resolveTarget(target?: HTMLElement | string): HTMLElement | null {\n  try {\n    if (!target) return null;\n    if (typeof target === \"string\") return document.querySelector(target);\n    return target;\n  } catch (err) {\n    warn(\"could not resolve mount target\", err);\n    return null;\n  }\n}\n\nfunction setHref(el: Element | null, url: string): void {\n  if (!el) return;\n  const safeUrl = normalizeUrl(url);\n  if (safeUrl) {\n    el.setAttribute(\"href\", safeUrl);\n  } else if (url) {\n    // A non-http(s) or malformed URL was supplied — do not render it as a link.\n    el.removeAttribute(\"href\");\n    warn(\"refusing to set a non-http(s) URL as a link: \" + String(url));\n  }\n}\n\n/** Only http(s) URLs are allowed as rendered links (blocks javascript:/data: etc). */\nfunction normalizeUrl(url?: string): string | null {\n  if (!url || typeof url !== \"string\") return null;\n  try {\n    const u = new URL(url, typeof location !== \"undefined\" ? location.href : undefined);\n    return u.protocol === \"http:\" || u.protocol === \"https:\" ? u.href : null;\n  } catch {\n    return null;\n  }\n}\n\nfunction makeId(): string {\n  try {\n    const c = (globalThis as { crypto?: Crypto }).crypto;\n    if (c && typeof c.randomUUID === \"function\") return c.randomUUID();\n  } catch (err) {\n    warn(\"crypto.randomUUID unavailable — falling back to a random id\", err);\n  }\n  return \"cdc_\" + Math.random().toString(36).slice(2) + Date.now().toString(36);\n}\n\nfunction escapeHtml(s: string): string {\n  return String(s)\n    .replace(/&/g, \"&amp;\")\n    .replace(/</g, \"&lt;\")\n    .replace(/>/g, \"&gt;\")\n    .replace(/\"/g, \"&quot;\")\n    .replace(/'/g, \"&#39;\");\n}\n\n/**\n * Run risky DOM work, surfacing (never swallowing) any failure as a concise\n * `[crossdeck] consent: …` warning. Webflow finding #11: no empty catch blocks.\n */\nfunction safe(fn: () => void, context: string): void {\n  try {\n    fn();\n  } catch (err) {\n    warn(context, err);\n  }\n}\n\nfunction warn(message: string, err?: unknown): void {\n  try {\n    const prefix = \"[crossdeck] consent: \" + message;\n    if (typeof console !== \"undefined\" && typeof console.warn === \"function\") {\n      if (err !== undefined) console.warn(prefix, err);\n      else console.warn(prefix);\n    }\n  } catch {\n    // console itself is unavailable — nothing further we can safely do.\n  }\n}\n","/**\n * Consent co-existence — \"defer, never double up\" (CD-185, PART 1).\n *\n * Crossdeck Consent is a branded banner bundled into the injected SDK. It must\n * NEVER render a second consent banner on a site that already manages consent —\n * two banners is a poor-experience marketplace auto-reject. On init we detect any\n * existing consent mechanism; if one is present we do NOT render our banner and\n * instead READ its signal and gate autocapture off it.\n *\n * Detection order (first match wins → defer & read):\n *   1. Explicit site-owner config  — \"I use my own CMP\" → read the configured source.\n *   2. GPC  — navigator.globalPrivacyControl; a legally-binding opt-out in several\n *             US states → ALWAYS honour (force marketing + analytics OFF).\n *   3. IAB TCF v2.2  — window.__tcfapi → subscribe, map purpose consents.\n *   4. IAB GPP  — window.__gpp (supersedes US Privacy __uspapi) → read US signals.\n *   5. Google Consent Mode v2  — analytics_storage / ad_storage / ad_user_data in\n *             the dataLayer → read.\n *   6. Known CMP plugins  — Cookiebot, OneTrust, CookieYes, Osano, Termly.\n *   7. Platform-native  — Webflow / Wix / Squarespace cookie banners.\n *\n * DESIGN CONTRACT (mirrors trust.ts):\n *  - ISOLATED — every global / DOM / postMessage touch is wrapped in `safe()`; a\n *    stumble in a detector can never throw into the host page or the rest of the SDK.\n *  - CONSERVATIVE — external purposes map to our {analytics, marketing} categories\n *    \"when in doubt, deny.\" A detected-but-unreadable CMP defers (no banner) AND\n *    keeps capture OFF until we can positively read a grant.\n *  - SUBSCRIBE, DON'T SNAPSHOT — where the source emits changes (TCF / GPP /\n *    Consent Mode / Cookiebot / OneTrust) we subscribe and update capture live when\n *    the visitor changes their choice in the OTHER tool. Snapshot-only sources\n *    return a no-op unsubscribe.\n *\n * API-VERIFICATION LEDGER (\"pack your bags\" — confirmed against current docs):\n *  - GPC              CONFIRMED  — https://globalprivacycontrol.github.io/gpc-spec/\n *  - TCF v2.2         CONFIRMED  — IAB Tech Lab CMP API v2 (__tcfapi signature,\n *                                  addEventListener/removeEventListener, eventStatus,\n *                                  purpose.consents):\n *                                  https://github.com/InteractiveAdvertisingBureau/GDPR-Transparency-and-Consent-Framework/blob/master/TCFv2/IAB%20Tech%20Lab%20-%20CMP%20API%20v2.md\n *                                  Purpose IDs 1-10: https://support.didomi.io/iab-tcf-v2.2-purposes/features-summary\n *  - Cookiebot        CONFIRMED  — Cookiebot.consent.{statistics,marketing} +\n *                                  CookiebotOnConsentReady/OnAccept/OnDecline events:\n *                                  https://www.cookiebot.com/en/developer/\n *  - OneTrust         CONFIRMED  — window.OnetrustActiveGroups (',C0002,'/',C0004,')\n *                                  + OneTrust.OnConsentChanged:\n *                                  https://developer.onetrust.com/onetrust/docs/javascript-api\n *  - IAB GPP          NEEDS-FINAL-VERIFICATION — __gpp(command,cb,parameter) signature +\n *                                  ping/addEventListener/getSection confirmed\n *                                  (https://github.com/InteractiveAdvertisingBureau/Global-Privacy-Platform/blob/main/Core/CMP%20API%20Specification.md),\n *                                  but the per-section US-state FIELD layouts (uspv1 /\n *                                  usnat / usca) vary — confirm field names before GA.\n *  - Google Consent Mode  NEEDS-FINAL-VERIFICATION — gtag('consent','default'|'update',{...})\n *                                  and the storage keys are confirmed\n *                                  (https://developers.google.com/tag-platform/security/guides/consent),\n *                                  BUT Google publishes NO official read/subscribe API\n *                                  for current consent state. We scan `dataLayer` and\n *                                  chain `dataLayer.push` (non-destructive, reversible).\n *                                  Re-confirm against the gtag reference before GA.\n *  - Osano / CookieYes / Termly  NEEDS-FINAL-VERIFICATION — presence-detected only; we do\n *                                  NOT fabricate a read API, so a detected instance defers\n *                                  and stays conservative-deny until a verified reader lands.\n *  - Webflow / Wix / Squarespace native  NEEDS-FINAL-VERIFICATION — presence-detected only,\n *                                  same conservative-deny handling as above.\n */\n\nimport type { ConsentState } from \"./consent\";\n\n// ============================================================\n// Public types\n// ============================================================\n\n/** The consent mechanisms we can detect and defer to. */\nexport type ConsentMechanism =\n  | \"site-config\"\n  | \"gpc\"\n  | \"tcf-v2\"\n  | \"gpp\"\n  | \"google-consent-mode\"\n  | \"cookiebot\"\n  | \"onetrust\"\n  | \"cookieyes\"\n  | \"osano\"\n  | \"termly\"\n  | \"webflow-native\"\n  | \"wix-native\"\n  | \"squarespace-native\";\n\n/** How confident we are in the read/subscribe wiring for a mechanism. */\nexport type VerificationStatus = \"confirmed\" | \"needs-verification\";\n\n/**\n * A detected existing consent mechanism. If `detectExistingConsent` returns one of\n * these, Crossdeck Consent MUST NOT render its own banner — read `read()` for the\n * current mapped grant and (if `emitsChanges`) call `subscribeToExternalConsent` to\n * follow live changes.\n */\nexport interface ExistingConsentSource {\n  /** Which mechanism was detected. */\n  readonly mechanism: ConsentMechanism;\n  /** Human-readable label for logs / the \"deferring to X\" disclosure. */\n  readonly source: string;\n  /**\n   * Snapshot read of the source, mapped conservatively to our categories. Only the\n   * keys the source speaks to are present; absent keys leave our defaults untouched.\n   * GPC returns { analytics:false, marketing:false }.\n   */\n  read(): Partial<ConsentState>;\n  /** True iff `subscribeToExternalConsent` can deliver live updates for this source. */\n  readonly emitsChanges: boolean;\n  /**\n   * Categories this source FORCES off regardless of anything else (GPC → analytics +\n   * marketing). Enforced on top of `read()` — a legally-binding opt-out cannot be\n   * flipped back on by any later signal.\n   */\n  readonly forcesDeny?: ReadonlyArray<keyof ConsentState>;\n  /** API-confirmation status for this mechanism (see the file-top ledger). */\n  readonly verification: VerificationStatus;\n}\n\n/** Options for {@link detectExistingConsent}. */\nexport interface DetectExistingConsentOptions {\n  /**\n   * The site owner explicitly declared (at install) that they run their own consent\n   * tool (\"I use my own CMP\"). Highest priority — we defer unconditionally.\n   */\n  ownConsentTool?: boolean;\n  /**\n   * Optional reader for the owner's configured source, used only when `ownConsentTool`\n   * is set. Returns their current grant mapped to our categories. If omitted we defer\n   * (render nothing) and stay conservative-deny until the owner wires a reader.\n   */\n  readOwnConsent?: () => Partial<ConsentState>;\n  /**\n   * Global scope to probe. Defaults to the real window. Injectable for tests and for\n   * SSR guards (pass a plain object to probe nothing).\n   */\n  scope?: ConsentGlobals;\n}\n\n// ============================================================\n// Typed views over the third-party globals we probe\n// ============================================================\n\n/** IAB TCF v2.2 CMP API. Signature: __tcfapi(command, version, callback, parameter?). */\ntype TcfApi = (\n  command: string,\n  version: number,\n  callback: (data: unknown, success: boolean) => void,\n  parameter?: unknown,\n) => void;\n\n/** The subset of the TCF `TCData` object we read. */\ninterface TcData {\n  eventStatus?: string;\n  gdprApplies?: boolean;\n  listenerId?: number;\n  purpose?: { consents?: Record<string, boolean> };\n}\n\n/** IAB GPP CMP API. Signature: __gpp(command, callback, parameter?, version?). */\ntype GppApi = (\n  command: string,\n  callback: (data: unknown, success: boolean) => void,\n  parameter?: unknown,\n  version?: number,\n) => void;\n\n/** Cookiebot's window.Cookiebot object (the fields we read). */\ninterface CookiebotGlobal {\n  consent?: {\n    necessary?: boolean;\n    preferences?: boolean;\n    statistics?: boolean;\n    marketing?: boolean;\n  };\n  consented?: boolean;\n}\n\n/** OneTrust's window.OneTrust object (the change hook we use). */\ninterface OneTrustGlobal {\n  OnConsentChanged?: (callback: (event: unknown) => void) => void;\n}\n\n/** A dataLayer that is a real array with a reassignable push (Google Consent Mode). */\ntype DataLayerArray = unknown[] & {\n  push: (...items: unknown[]) => number;\n};\n\n/**\n * The globals we probe. Everything is optional — presence is the detection signal.\n * Kept as one interface so `scope` is fully typed with no `any`.\n */\nexport interface ConsentGlobals {\n  navigator?: Navigator & { globalPrivacyControl?: boolean };\n  __tcfapi?: TcfApi;\n  __gpp?: GppApi;\n  dataLayer?: unknown[];\n  Cookiebot?: CookiebotGlobal;\n  OneTrust?: OneTrustGlobal;\n  OnetrustActiveGroups?: string;\n  OptanonActiveGroups?: string;\n  Osano?: unknown;\n  CookieYes?: unknown;\n  cookieyes?: unknown;\n  getCkyConsent?: unknown;\n  Termly?: unknown;\n  Webflow?: unknown;\n  consentPolicyManager?: unknown; // Wix\n  Static?: unknown; // Squarespace exposes window.Static\n  addEventListener?: Window[\"addEventListener\"];\n  removeEventListener?: Window[\"removeEventListener\"];\n}\n\n// ============================================================\n// Detection — first match wins\n// ============================================================\n\n/**\n * Detect the first existing consent mechanism on the page, in the CD-185 priority\n * order. Returns the source to defer to, or `null` if nothing is present (in which\n * case Crossdeck Consent may render its own banner, subject to Part 3's \"who owns\n * consent\" choice).\n *\n * Guaranteed not to throw — any probe failure is swallowed and treated as \"absent\".\n */\nexport function detectExistingConsent(\n  opts?: DetectExistingConsentOptions,\n): ExistingConsentSource | null {\n  const scope = resolveScope(opts?.scope);\n  if (!scope) return null;\n\n  // 1. Explicit site-owner config — highest priority.\n  if (opts?.ownConsentTool) {\n    const reader = opts.readOwnConsent;\n    return {\n      mechanism: \"site-config\",\n      source: \"Site owner's own consent tool\",\n      emitsChanges: false,\n      verification: \"confirmed\",\n      read: () =>\n        // Their reader wins; without one we defer AND stay conservative-deny so we\n        // never capture on a site whose consent we cannot read.\n        safeRead(() => reader?.(), undefined) ??\n        ({ analytics: false, marketing: false } as Partial<ConsentState>),\n    };\n  }\n\n  // 2. GPC — legally binding, always honoured. Checked before every CMP so it can\n  //    never be overridden by a downstream signal.\n  //    Spec: https://globalprivacycontrol.github.io/gpc-spec/\n  if (safeRead(() => scope.navigator?.globalPrivacyControl === true, false)) {\n    return {\n      mechanism: \"gpc\",\n      source: \"Global Privacy Control (navigator.globalPrivacyControl)\",\n      emitsChanges: false, // GPC is a static per-request signal; no change event.\n      forcesDeny: [\"analytics\", \"marketing\"],\n      verification: \"confirmed\",\n      read: () => ({ analytics: false, marketing: false }),\n    };\n  }\n\n  // 3. IAB TCF v2.2.\n  if (typeof scope.__tcfapi === \"function\") {\n    return {\n      mechanism: \"tcf-v2\",\n      source: \"IAB TCF v2.2 (__tcfapi)\",\n      emitsChanges: true,\n      verification: \"confirmed\",\n      read: () => readTcfSnapshot(scope),\n    };\n  }\n\n  // 4. IAB GPP (supersedes the deprecated US Privacy __uspapi).\n  if (typeof scope.__gpp === \"function\") {\n    return {\n      mechanism: \"gpp\",\n      source: \"IAB GPP (__gpp)\",\n      emitsChanges: true,\n      verification: \"needs-verification\",\n      read: () => readGppSnapshot(scope),\n    };\n  }\n\n  // 5. Google Consent Mode v2 — only if the dataLayer actually carries consent\n  //    signals (a bare GTM dataLayer is NOT a consent mechanism).\n  if (hasConsentModeSignal(scope.dataLayer)) {\n    return {\n      mechanism: \"google-consent-mode\",\n      source: \"Google Consent Mode v2 (dataLayer)\",\n      emitsChanges: true,\n      verification: \"needs-verification\",\n      read: () => readConsentMode(scope.dataLayer),\n    };\n  }\n\n  // 6. Known CMP plugins.\n  if (isObject(scope.Cookiebot)) {\n    return {\n      mechanism: \"cookiebot\",\n      source: \"Cookiebot\",\n      emitsChanges: true,\n      verification: \"confirmed\",\n      read: () => readCookiebot(scope),\n    };\n  }\n  if (\n    isObject(scope.OneTrust) ||\n    typeof scope.OnetrustActiveGroups === \"string\" ||\n    typeof scope.OptanonActiveGroups === \"string\"\n  ) {\n    return {\n      mechanism: \"onetrust\",\n      source: \"OneTrust\",\n      emitsChanges: true,\n      verification: \"confirmed\",\n      read: () => readOneTrust(scope),\n    };\n  }\n  // CookieYes / Osano / Termly — presence-detected only. We do NOT invent a read API,\n  // so we defer (no banner) and stay conservative-deny until a verified reader ships.\n  if (scope.CookieYes != null || scope.cookieyes != null || scope.getCkyConsent != null) {\n    return deferUnread(\"cookieyes\", \"CookieYes\");\n  }\n  if (scope.Osano != null) {\n    return deferUnread(\"osano\", \"Osano\");\n  }\n  if (scope.Termly != null) {\n    return deferUnread(\"termly\", \"Termly\");\n  }\n\n  // 7. Platform-native banners — presence-detected only, same conservative handling.\n  //    (Best-effort heuristics; confirm exact globals per platform before GA.)\n  if (isWebflowCookieConsent(scope)) {\n    return deferUnread(\"webflow-native\", \"Webflow cookie consent\");\n  }\n  if (scope.consentPolicyManager != null) {\n    return deferUnread(\"wix-native\", \"Wix cookie banner\");\n  }\n  if (isSquarespaceCookieBanner(scope)) {\n    return deferUnread(\"squarespace-native\", \"Squarespace cookie banner\");\n  }\n\n  return null;\n}\n\n/**\n * A detected-but-unreadable source: we defer (never double up) and keep every\n * non-essential category OFF until a verified reader for it exists. Conservative by\n * construction — \"when in doubt, deny.\"\n */\nfunction deferUnread(\n  mechanism: ConsentMechanism,\n  label: string,\n): ExistingConsentSource {\n  return {\n    mechanism,\n    source: label + \" (detected; read not yet verified)\",\n    emitsChanges: false,\n    verification: \"needs-verification\",\n    read: () => ({ analytics: false, marketing: false }),\n  };\n}\n\n// ============================================================\n// Subscription — follow live changes in the other tool\n// ============================================================\n\n/**\n * Subscribe to a source that emits changes and forward each new mapped grant to `cb`.\n * Returns an unsubscribe function. Snapshot-only sources (GPC, site-config, and any\n * presence-only `deferUnread` source) return a no-op unsubscribe.\n *\n * Guaranteed not to throw — wiring failures degrade to a no-op unsubscribe.\n */\nexport function subscribeToExternalConsent(\n  source: ExistingConsentSource,\n  cb: (state: Partial<ConsentState>) => void,\n  opts?: { scope?: ConsentGlobals },\n): () => void {\n  const scope = resolveScope(opts?.scope);\n  if (!scope || !source.emitsChanges) return noop;\n\n  switch (source.mechanism) {\n    case \"tcf-v2\":\n      return subscribeTcf(scope, cb);\n    case \"gpp\":\n      return subscribeGpp(scope, cb);\n    case \"google-consent-mode\":\n      return subscribeConsentMode(scope, cb);\n    case \"cookiebot\":\n      return subscribeCookiebot(scope, cb);\n    case \"onetrust\":\n      return subscribeOneTrust(scope, cb);\n    default:\n      return noop;\n  }\n}\n\n// ------------------------------------------------------------\n// TCF v2.2\n// ------------------------------------------------------------\n\n/**\n * Purpose → category mapping (conservative, \"when in doubt deny\").\n *   analytics ← Purpose 1 (store/access info on device) AND Purpose 8 (measure\n *               content performance) — the two purposes first-party product analytics\n *               depends on. Purposes 9/10 (cross-source audience building / product\n *               improvement) are broader and NOT required to grant analytics.\n *   marketing ← Purpose 1 AND Purpose 3 (create a personalised-ads profile) AND\n *               Purpose 4 (select personalised ads) — the core advertising consents.\n * Any required purpose missing → that category is denied.\n * IDs per https://support.didomi.io/iab-tcf-v2.2-purposes/features-summary\n */\nfunction mapTcfPurposes(d: TcData): Partial<ConsentState> {\n  // gdprApplies === false: GDPR does not apply to this user, so TCF imposes no\n  // restriction — do not manufacture a denial.\n  if (d.gdprApplies === false) return { analytics: true, marketing: true };\n  const c = d.purpose?.consents ?? {};\n  const has = (id: number): boolean => c[String(id)] === true;\n  return {\n    analytics: has(1) && has(8),\n    marketing: has(1) && has(3) && has(4),\n  };\n}\n\nfunction readTcfSnapshot(scope: ConsentGlobals): Partial<ConsentState> {\n  const api = scope.__tcfapi;\n  if (typeof api !== \"function\") return {};\n  let out: Partial<ConsentState> = {};\n  // getTCData resolves synchronously when the CMP is loaded; if it hasn't, the\n  // callback simply doesn't fire and we keep {} (defaults untouched) until the\n  // subscription delivers the first `tcloaded`.\n  safe(() =>\n    api(\"getTCData\", 2, (data: unknown, success: boolean) => {\n      if (success && isObject(data)) out = mapTcfPurposes(data as TcData);\n    }),\n  );\n  return out;\n}\n\nfunction subscribeTcf(\n  scope: ConsentGlobals,\n  cb: (state: Partial<ConsentState>) => void,\n): () => void {\n  const api = scope.__tcfapi;\n  if (typeof api !== \"function\") return noop;\n  let listenerId: number | null = null;\n\n  const handler = (data: unknown, success: boolean): void => {\n    if (!success || !isObject(data)) return;\n    const d = data as TcData;\n    if (typeof d.listenerId === \"number\") listenerId = d.listenerId;\n    // Map only on states that carry a settled consent snapshot.\n    if (d.eventStatus === \"tcloaded\" || d.eventStatus === \"useractioncomplete\") {\n      safe(() => cb(mapTcfPurposes(d)));\n    }\n  };\n\n  safe(() => api(\"addEventListener\", 2, handler));\n\n  return () => {\n    if (listenerId == null) return;\n    const id = listenerId;\n    safe(() =>\n      api(\n        \"removeEventListener\",\n        2,\n        () => {\n          /* removal ack — nothing to do */\n        },\n        id,\n      ),\n    );\n  };\n}\n\n// ------------------------------------------------------------\n// IAB GPP  (NEEDS-FINAL-VERIFICATION: per-section US-state field layouts)\n// ------------------------------------------------------------\n\n/** The subset of GPP `PingReturn` we read. */\ninterface GppPing {\n  signalStatus?: string;\n  applicableSections?: unknown;\n  parsedSections?: Record<string, unknown>;\n}\n\n/** GPP addEventListener event object. */\ninterface GppEvent {\n  eventName?: string;\n  data?: unknown;\n  pingData?: GppPing;\n}\n\n/**\n * Map GPP US-state signals conservatively. We read the (legacy but widely present)\n * US Privacy section `uspv1`, whose string's 3rd char = \"opt-out of sale\": 'Y' → the\n * user opted out → deny marketing. Analytics is not gated by US Privacy, so we leave\n * it untouched (no key emitted). The newer usnat/usca sections carry richer opt-outs\n * (targeted advertising, sale, sharing) but their FIELD names vary by version — hence\n * this mechanism is flagged needs-verification; wire those fields once confirmed.\n */\nfunction mapGppPing(ping: GppPing): Partial<ConsentState> {\n  const parsed = ping.parsedSections;\n  if (!isObject(parsed)) return {};\n  const usp = (parsed as Record<string, unknown>)[\"uspv1\"];\n  const uspString = extractUspString(usp);\n  if (uspString && uspString.length >= 3) {\n    // char[2]: 'Y' opted out of sale, 'N' did not, '-' not applicable.\n    if (uspString.charAt(2) === \"Y\") return { marketing: false };\n    if (uspString.charAt(2) === \"N\") return { marketing: true };\n  }\n  return {};\n}\n\n/** uspv1 may be parsed to a string or an object carrying the raw string. */\nfunction extractUspString(usp: unknown): string | null {\n  if (typeof usp === \"string\") return usp;\n  if (isObject(usp)) {\n    const rec = usp as Record<string, unknown>;\n    for (const key of [\"Value\", \"value\", \"uspString\", \"String\"]) {\n      if (typeof rec[key] === \"string\") return rec[key] as string;\n    }\n  }\n  return null;\n}\n\nfunction readGppSnapshot(scope: ConsentGlobals): Partial<ConsentState> {\n  const api = scope.__gpp;\n  if (typeof api !== \"function\") return {};\n  let out: Partial<ConsentState> = {};\n  safe(() =>\n    api(\"ping\", (data: unknown, success: boolean) => {\n      if (success && isObject(data)) out = mapGppPing(data as GppPing);\n    }),\n  );\n  return out;\n}\n\nfunction subscribeGpp(\n  scope: ConsentGlobals,\n  cb: (state: Partial<ConsentState>) => void,\n): () => void {\n  const api = scope.__gpp;\n  if (typeof api !== \"function\") return noop;\n  let listenerId: number | null = null;\n\n  const handler = (evt: unknown, success: boolean): void => {\n    if (!success || !isObject(evt)) return;\n    const e = evt as GppEvent & { listenerId?: number };\n    if (typeof e.listenerId === \"number\") listenerId = e.listenerId;\n    const ping = e.pingData;\n    if (isObject(ping) && (ping as GppPing).signalStatus === \"ready\") {\n      safe(() => cb(mapGppPing(ping as GppPing)));\n    }\n  };\n\n  safe(() => api(\"addEventListener\", handler));\n\n  return () => {\n    if (listenerId == null) return;\n    const id = listenerId;\n    safe(() =>\n      api(\n        \"removeEventListener\",\n        () => {\n          /* removal ack */\n        },\n        id,\n      ),\n    );\n  };\n}\n\n// ------------------------------------------------------------\n// Google Consent Mode v2  (NEEDS-FINAL-VERIFICATION: no official read/subscribe API)\n// ------------------------------------------------------------\n\n/** True iff the dataLayer carries at least one gtag('consent', …) entry. */\nfunction hasConsentModeSignal(dataLayer: unknown): boolean {\n  if (!Array.isArray(dataLayer)) return false;\n  for (const entry of dataLayer) {\n    const e = asIndexed(entry);\n    if (e && e[0] === \"consent\") return true;\n  }\n  return false;\n}\n\n/**\n * Fold every consent 'default'/'update' entry in dataLayer, last-write-wins, into our\n * categories. gtag pushes an arguments-like object `{0:'consent',1:'update',2:{…}}`;\n * we also accept plain arrays. Values are 'granted' | 'denied'; anything else → denied.\n *   analytics ← analytics_storage === 'granted'\n *   marketing ← ad_storage === 'granted'  (require the ad-cookie grant; ad_user_data /\n *               ad_personalization only narrow it further, never widen it)\n */\nfunction readConsentMode(dataLayer: unknown): Partial<ConsentState> {\n  if (!Array.isArray(dataLayer)) return {};\n  let analyticsStorage: string | undefined;\n  let adStorage: string | undefined;\n  for (const entry of dataLayer) {\n    const e = asIndexed(entry);\n    if (!e || e[0] !== \"consent\") continue;\n    const mode = e[1];\n    if (mode !== \"default\" && mode !== \"update\") continue;\n    const vals = e[2];\n    if (!isObject(vals)) continue;\n    const v = vals as Record<string, unknown>;\n    if (typeof v.analytics_storage === \"string\") analyticsStorage = v.analytics_storage;\n    if (typeof v.ad_storage === \"string\") adStorage = v.ad_storage;\n  }\n  const out: Partial<ConsentState> = {};\n  if (analyticsStorage !== undefined) out.analytics = analyticsStorage === \"granted\";\n  if (adStorage !== undefined) out.marketing = adStorage === \"granted\";\n  return out;\n}\n\n/**\n * Subscribe by CHAINING dataLayer.push. Google exposes no consent read/subscribe API,\n * so this is the pragmatic industry approach: wrap `push` to observe new consent\n * entries, always calling the original first (non-destructive), and restore the\n * original on unsubscribe (fully reversible). This wraps ONLY the customer's own\n * dataLayer array — it is not host-global fetch/XHR/history monkey-patching.\n * NEEDS-FINAL-VERIFICATION against the current gtag reference before GA.\n */\nfunction subscribeConsentMode(\n  scope: ConsentGlobals,\n  cb: (state: Partial<ConsentState>) => void,\n): () => void {\n  const dl = scope.dataLayer;\n  if (!Array.isArray(dl)) return noop;\n  const arr = dl as DataLayerArray;\n  const original = arr.push;\n  if (typeof original !== \"function\") return noop;\n  const originalBound = original.bind(arr) as DataLayerArray[\"push\"];\n  let active = true;\n\n  const wrapped: DataLayerArray[\"push\"] = (...items: unknown[]): number => {\n    const result = originalBound(...items);\n    if (active) {\n      for (const item of items) {\n        const e = asIndexed(item);\n        if (e && e[0] === \"consent\" && (e[1] === \"default\" || e[1] === \"update\")) {\n          safe(() => cb(readConsentMode(arr)));\n          break;\n        }\n      }\n    }\n    return result;\n  };\n\n  const installed = safeRead(() => {\n    arr.push = wrapped;\n    return true;\n  }, false);\n  if (!installed) return noop;\n\n  return () => {\n    active = false;\n    // Only restore if nothing else re-wrapped push after us.\n    safe(() => {\n      if (arr.push === wrapped) arr.push = original;\n    });\n  };\n}\n\n// ------------------------------------------------------------\n// Cookiebot  (CONFIRMED)\n// ------------------------------------------------------------\n\n/** Cookiebot.consent.statistics → analytics; .marketing → marketing. */\nfunction readCookiebot(scope: ConsentGlobals): Partial<ConsentState> {\n  const c = scope.Cookiebot?.consent;\n  if (!isObject(c)) return {};\n  const out: Partial<ConsentState> = {};\n  if (typeof c.statistics === \"boolean\") out.analytics = c.statistics;\n  if (typeof c.marketing === \"boolean\") out.marketing = c.marketing;\n  return out;\n}\n\n/** Confirmed events: CookiebotOnConsentReady / CookiebotOnAccept / CookiebotOnDecline. */\nconst COOKIEBOT_EVENTS = [\n  \"CookiebotOnConsentReady\",\n  \"CookiebotOnAccept\",\n  \"CookiebotOnDecline\",\n] as const;\n\nfunction subscribeCookiebot(\n  scope: ConsentGlobals,\n  cb: (state: Partial<ConsentState>) => void,\n): () => void {\n  const add = scope.addEventListener;\n  const remove = scope.removeEventListener;\n  if (typeof add !== \"function\") return noop;\n  const handler = (): void => safe(() => cb(readCookiebot(scope)));\n  for (const name of COOKIEBOT_EVENTS) {\n    safe(() => add.call(scope, name, handler as EventListener, false));\n  }\n  return () => {\n    if (typeof remove !== \"function\") return;\n    for (const name of COOKIEBOT_EVENTS) {\n      safe(() => remove.call(scope, name, handler as EventListener, false));\n    }\n  };\n}\n\n// ------------------------------------------------------------\n// OneTrust  (CONFIRMED)\n// ------------------------------------------------------------\n\n/**\n * OneTrust exposes active category IDs as a comma-wrapped string in\n * window.OnetrustActiveGroups, e.g. ',C0001,C0002,C0004,'. Default taxonomy:\n *   C0001 strictly necessary · C0002 performance/analytics · C0003 functional ·\n *   C0004 targeting/advertising · C0005 social media.\n *   analytics ← contains ',C0002,'   marketing ← contains ',C0004,'\n */\nfunction readOneTrust(scope: ConsentGlobals): Partial<ConsentState> {\n  const groups =\n    (typeof scope.OnetrustActiveGroups === \"string\" && scope.OnetrustActiveGroups) ||\n    (typeof scope.OptanonActiveGroups === \"string\" && scope.OptanonActiveGroups) ||\n    \"\";\n  if (!groups) return {};\n  return {\n    analytics: groups.includes(\",C0002,\"),\n    marketing: groups.includes(\",C0004,\"),\n  };\n}\n\nfunction subscribeOneTrust(\n  scope: ConsentGlobals,\n  cb: (state: Partial<ConsentState>) => void,\n): () => void {\n  const ot = scope.OneTrust;\n  if (!isObject(ot) || typeof ot.OnConsentChanged !== \"function\") return noop;\n  // Guarded by the typeof check above; cast to the checked call signature.\n  const onConsentChanged = ot.OnConsentChanged as (cb: () => void) => void;\n  // OnConsentChanged re-reads the (freshly updated) global string; the event detail\n  // format varies across OneTrust versions, so we always re-read OnetrustActiveGroups\n  // rather than trusting the event payload.\n  safe(() => onConsentChanged(() => safe(() => cb(readOneTrust(scope)))));\n  // OneTrust's OnConsentChanged provides no documented de-registration handle; the\n  // callback simply forwards reads. There is nothing to tear down.\n  return noop;\n}\n\n// ------------------------------------------------------------\n// Platform-native best-effort presence heuristics\n// ------------------------------------------------------------\n\n/** Webflow sites expose window.Webflow; a cookie-consent module lives under it. */\nfunction isWebflowCookieConsent(scope: ConsentGlobals): boolean {\n  if (!isObject(scope.Webflow)) return false;\n  // Presence of the cc/cookie-consent require entry is the signal; exact API varies,\n  // so we only detect (then defer conservative-deny). Confirm the read before GA.\n  return safeRead(() => {\n    const wf = scope.Webflow as { require?: (name: string) => unknown };\n    return typeof wf.require === \"function\";\n  }, false);\n}\n\n/** Squarespace exposes window.Static.SQUARESPACE_CONTEXT; the cookie banner is site-config driven. */\nfunction isSquarespaceCookieBanner(scope: ConsentGlobals): boolean {\n  if (!isObject(scope.Static)) return false;\n  return safeRead(() => {\n    const s = scope.Static as { SQUARESPACE_CONTEXT?: unknown };\n    return s.SQUARESPACE_CONTEXT != null;\n  }, false);\n}\n\n// ============================================================\n// Shared helpers\n// ============================================================\n\nconst noop = (): void => {\n  /* snapshot-only source, or nothing to tear down */\n};\n\n/**\n * Resolve the global scope to probe. Returns null in non-browser contexts (SSR) when\n * no explicit scope is supplied, so every detector short-circuits to \"absent\".\n */\nfunction resolveScope(explicit?: ConsentGlobals): ConsentGlobals | null {\n  if (explicit) return explicit;\n  const g = globalThis as unknown as { window?: ConsentGlobals };\n  if (typeof g.window !== \"undefined\" && g.window) return g.window;\n  return null;\n}\n\n/** Index an arguments-like or array entry (gtag pushes arguments objects). */\nfunction asIndexed(entry: unknown): { [k: number]: unknown } | null {\n  if (entry != null && typeof entry === \"object\") {\n    return entry as { [k: number]: unknown };\n  }\n  return null;\n}\n\n/** Narrow to a non-null plain-ish object. */\nfunction isObject(v: unknown): v is Record<string, unknown> {\n  return v != null && typeof v === \"object\";\n}\n\n/**\n * Run `fn` for side effects; swallow any throw. Isolation guarantee — a stumble in a\n * third-party global probe must never escape into the host app or the SDK.\n */\nfunction safe(fn: () => void): void {\n  try {\n    fn();\n  } catch {\n    /* isolation: probing foreign globals must never throw into the host */\n  }\n}\n\n/** Run `fn` and return its value, or `fallback` if it throws or returns undefined. */\nfunction safeRead<T>(fn: () => T, fallback: T): T {\n  try {\n    const v = fn();\n    return v === undefined ? fallback : v;\n  } catch {\n    return fallback;\n  }\n}\n","/**\n * @cross-deck/web — public entry point.\n *\n * The default export is a singleton `Crossdeck` instance. Most apps want\n * exactly one client; instantiate `CrossdeckClient` directly if you need\n * isolated instances (e.g. one per tenant in a multi-tenant SaaS shell).\n */\n\nexport { Crossdeck } from \"./singleton\";\nexport { CrossdeckClient } from \"./crossdeck\";\nexport { CrossdeckError } from \"./errors\";\n// Crossdeck Trust — the human-proof panel. `Crossdeck.trust.panel(...)` is the\n// usual entry; `mountTrustPanel` is the framework-agnostic core for non-SDK use.\nexport { mountTrustPanel, TRUST_PANEL_ORIGIN } from \"./trust\";\nexport type {\n  TrustToken,\n  TrustUnavailable,\n  TrustResult,\n  TrustTokenStatus,\n  TrustPanelHandle,\n  TrustPanelInput,\n  MountTrustPanelOptions,\n  CrossdeckTrustNamespace,\n} from \"./trust\";\n// Crossdeck Consent — OPT-IN (CD-185).\n//\n// Consent ENFORCEMENT is core and always shipped: the ConsentManager, the\n// public `Crossdeck.consent()` socket that ANY external CMP plugs into, and\n// GPC. That is the legal floor — a customer's own banner can always tell us\n// \"stop tracking\", and nobody has to opt in to that.\n//\n// Consent PRESENTATION — the branded Crossdeck banner, the CMP auto-detect\n// adapters, and the guest boot profile — is a FEATURE you switch on, never a\n// tax on every install. Two ways to turn it on:\n//\n//   Crossdeck.init({ …, consentBanner: true })            ← the light switch\n//   import { mountConsentBanner } from \"@cross-deck/web/consent\"\n//\n// The widget is code-split, so its bytes are fetched only when enabled. Types\n// are erased at build time, so re-exporting them here costs zero bytes.\nexport type {\n  ConsentBannerOptions,\n  ConsentBannerHandle,\n  ConsentBannerState,\n  ConsentRecord,\n  ConsentMethod,\n  ConsentBannerMode,\n  ConsentCategoryConfig,\n  ConsentCategoriesConfig,\n  CrossdeckConsentNamespace,\n} from \"./consent-banner\";\nexport type {\n  ConsentModeOptions,\n  ConsentModeHandle,\n  ConsentOwner,\n} from \"./consent-mode\";\nexport type {\n  ExistingConsentSource,\n  ConsentMechanism,\n  VerificationStatus,\n  DetectExistingConsentOptions,\n  ConsentGlobals,\n} from \"./consent-coexistence\";\nexport { MemoryStorage } from \"./storage\";\nexport { SDK_NAME, SDK_VERSION, DEFAULT_BASE_URL } from \"./http\";\nexport { CROSSDECK_ERROR_CODES, getErrorCode } from \"./error-codes\";\nexport { CrossdeckContracts } from \"./contracts\";\nexport type {\n  Contract,\n  ContractPillar,\n  ContractStatus,\n  ContractAppliesTo,\n  ContractTestRef,\n  ContractFailureInput,\n} from \"./contracts\";\n\nexport type {\n  CrossdeckOptions,\n  IdentifyOptions,\n  GroupTraits,\n  EventProperties,\n  KeyValueStorage,\n  PublicEntitlement,\n  EntitlementsListResponse,\n  AliasResult,\n  PurchaseResult,\n  HeartbeatResponse,\n  Diagnostics,\n  Environment,\n  Platform,\n  AuditRail,\n  AutoTrackOptions,\n} from \"./types\";\nexport type { ConsentState } from \"./consent\";\nexport type { DeviceInfo } from \"./device-info\";\nexport type { CrossdeckErrorType, CrossdeckErrorPayload } from \"./errors\";\nexport type { ErrorCodeEntry } from \"./error-codes\";\nexport type { Breadcrumb, BreadcrumbCategory, BreadcrumbLevel } from \"./breadcrumbs\";\nexport type { CapturedError, ErrorLevel } from \"./error-capture\";\nexport type { StackFrame } from \"./stack-parser\";\n","/**\n * Stripe-style error wrapper for @cross-deck/web.\n *\n * Mirrors the wire shape returned by the v1 backend (see\n * backend/src/api/v1-errors.ts) so SDK consumers can `catch`\n * with consistent fields:\n *\n *   try {\n *     await crossdeck.identify(\"user_847\");\n *   } catch (err) {\n *     if (err instanceof CrossdeckError && err.code === \"invalid_api_key\") {\n *       // ...\n *     }\n *   }\n */\n\nexport type CrossdeckErrorType =\n  | \"authentication_error\"\n  | \"permission_error\"\n  | \"invalid_request_error\"\n  | \"rate_limit_error\"\n  // version_error → HTTP 426: the SDK's wire format is too old. The queue\n  // PARKS on it (holds events, resumes on upgrade), distinct from a drop.\n  | \"version_error\"\n  | \"internal_error\"\n  | \"network_error\"\n  | \"configuration_error\";\n\nexport interface CrossdeckErrorPayload {\n  type: CrossdeckErrorType;\n  code: string;\n  message: string;\n  /** Server-issued request ID. Echoed in support tickets. */\n  requestId?: string;\n  /** HTTP status code if the error came from an API response. */\n  status?: number;\n  /**\n   * Server-suggested wait (in milliseconds) before retrying. Populated\n   * from the `Retry-After` response header on 429 / 503. The header\n   * spec allows either delta-seconds or an HTTP-date; the parser below\n   * normalises both to milliseconds. Consumers MUST honour this — the\n   * server is telling you the safe rate.\n   */\n  retryAfterMs?: number;\n  /**\n   * Required SDK version floor — populated only on a `426 Upgrade Required`\n   * / `sdk_version_unsupported` response. The queue surfaces it in the\n   * \"update to >= X\" PARK message so the cure is exact.\n   */\n  minVersion?: string;\n  /** SDK surface the rejection applies to (web/node/swift/…), on a 426. */\n  surface?: string;\n}\n\nexport class CrossdeckError extends Error {\n  public readonly type: CrossdeckErrorType;\n  public readonly code: string;\n  public readonly requestId?: string;\n  public readonly status?: number;\n  public readonly retryAfterMs?: number;\n  public readonly minVersion?: string;\n  public readonly surface?: string;\n\n  constructor(payload: CrossdeckErrorPayload) {\n    super(payload.message);\n    this.name = \"CrossdeckError\";\n    this.type = payload.type;\n    this.code = payload.code;\n    this.requestId = payload.requestId;\n    this.status = payload.status;\n    this.retryAfterMs = payload.retryAfterMs;\n    this.minVersion = payload.minVersion;\n    this.surface = payload.surface;\n    // Restore prototype chain — needed when targeting ES5.\n    Object.setPrototypeOf(this, CrossdeckError.prototype);\n  }\n}\n\n/**\n * Build a CrossdeckError from a non-OK fetch Response. Reads the\n * Stripe-style envelope { error: { type, code, message, request_id } }.\n * Falls back to a generic shape if the body isn't valid JSON.\n */\nexport async function crossdeckErrorFromResponse(res: Response): Promise<CrossdeckError> {\n  const requestId = res.headers.get(\"x-request-id\") ?? undefined;\n  const retryAfterMs = parseRetryAfterHeader(res.headers.get(\"retry-after\"));\n  let body: unknown;\n  try {\n    body = await res.json();\n  } catch {\n    body = null;\n  }\n  const envelope = (body as { error?: Partial<CrossdeckErrorPayload> & { request_id?: string } })?.error;\n  if (envelope && typeof envelope.type === \"string\" && typeof envelope.code === \"string\") {\n    return new CrossdeckError({\n      type: envelope.type as CrossdeckErrorType,\n      code: envelope.code,\n      message: envelope.message ?? `HTTP ${res.status}`,\n      requestId: envelope.request_id ?? requestId,\n      status: res.status,\n      retryAfterMs,\n      // PARK metadata, present only on a 426 / sdk_version_unsupported body.\n      minVersion: typeof envelope.minVersion === \"string\" ? envelope.minVersion : undefined,\n      surface: typeof envelope.surface === \"string\" ? envelope.surface : undefined,\n    });\n  }\n  return new CrossdeckError({\n    type: typeMapForStatus(res.status),\n    code: `http_${res.status}`,\n    message: `HTTP ${res.status} ${res.statusText || \"\"}`.trim(),\n    requestId,\n    status: res.status,\n    retryAfterMs,\n  });\n}\n\n/**\n * Parse the `Retry-After` header per RFC 7231 §7.1.3. Two forms:\n *   - delta-seconds: \"Retry-After: 120\"  → 120_000 ms\n *   - HTTP-date:     \"Retry-After: Wed, 21 Oct 2026 07:28:00 GMT\"\n *                    → max(0, target - now) ms\n *\n * Returns undefined when the header is missing, malformed, or in the past.\n * Exported for unit testing.\n */\nexport function parseRetryAfterHeader(value: string | null): number | undefined {\n  if (!value) return undefined;\n  const trimmed = value.trim();\n  if (!trimmed) return undefined;\n  // delta-seconds form (non-negative integer or decimal).\n  if (/^\\d+(\\.\\d+)?$/.test(trimmed)) {\n    const secs = Number(trimmed);\n    if (!Number.isFinite(secs) || secs < 0) return undefined;\n    return Math.round(secs * 1000);\n  }\n  // HTTP-date form. Only attempt Date.parse if the value looks like a\n  // date (has a comma, slash, or alphabetic character) — otherwise\n  // garbage like \"-5\" or \"abc\" gets coerced by Date.parse into weird\n  // values and we'd silently return 0.\n  if (!/[a-zA-Z,/:]/.test(trimmed)) return undefined;\n  const target = Date.parse(trimmed);\n  if (!Number.isFinite(target)) return undefined;\n  const delta = target - Date.now();\n  return delta > 0 ? delta : 0;\n}\n\nfunction typeMapForStatus(status: number): CrossdeckErrorType {\n  if (status === 401) return \"authentication_error\";\n  if (status === 403) return \"permission_error\";\n  if (status === 429) return \"rate_limit_error\";\n  if (status >= 400 && status < 500) return \"invalid_request_error\";\n  return \"internal_error\";\n}\n","/**\n * SDK version constant — generated by `scripts/sync-sdk-versions.mjs`.\n *\n * Single source of truth: the `version` field in this package's\n * package.json. The sync script writes this file so that\n * `SDK_VERSION` is a plain TypeScript literal at runtime — no\n * runtime JSON-import gotcha (Node ESM requires\n * `with { type: \"json\" }` to import JSON as ESM, and the published\n * dist file would otherwise fail to load).\n *\n * Drift protection: `node scripts/sync-sdk-versions.mjs --check` (the\n * CI gate) flags this file when it falls out of sync with package.json.\n * Bumping `package.json` without re-running the sync script fails CI.\n *\n * Do NOT edit by hand — `node scripts/sync-sdk-versions.mjs`.\n */\nexport const SDK_VERSION = \"1.14.2\";\nexport const SDK_NAME = \"@cross-deck/web\";\n","/**\n * HTTP transport for the SDK. Single fetch wrapper used by every endpoint\n * call. Adds the Bearer token and SDK version header, parses responses,\n * normalises errors to CrossdeckError.\n *\n * Uses platform-native fetch (browser + Node 18+). No axios, no isomorphic-\n * fetch shim, no transitive deps.\n */\n\nimport { CrossdeckError, crossdeckErrorFromResponse } from \"./errors\";\n// Single source of truth — `_version.ts` is generated from\n// package.json by `scripts/sync-sdk-versions.mjs`. A plain TypeScript\n// re-export here means the runtime `Crossdeck-Sdk-Version` header\n// always matches the published bundle, with zero Node-ESM JSON-import\n// gotchas (Node requires `with { type: \"json\" }` to load JSON as ESM\n// from a .mjs file — that bit dist-loading on 1.3.0 RC).\n//\n// Pre-fix this was a hardcoded literal that drifted from package.json:\n// the published 1.2.0 web bundle reported `@cross-deck/web@1.1.0` on\n// the wire because nobody bumped the constant. The generated\n// `_version.ts` closes that loop permanently — `--check` mode of the\n// sync script fails CI if anything goes out of sync.\nimport { SDK_NAME, SDK_VERSION } from \"./_version\";\nexport { SDK_NAME, SDK_VERSION };\n\nexport const DEFAULT_BASE_URL = \"https://api.cross-deck.com/v1\";\n\nexport interface HttpClientConfig {\n  publicKey: string;\n  baseUrl: string;\n  sdkVersion: string;\n  /**\n   * Localhost auto-detection short-circuit. When true, every request\n   * resolves to a fabricated 2xx-shaped response — no network call\n   * goes out. Set by Crossdeck.init() when the SDK boots on a local\n   * dev hostname. Confidence-first design: we never let a developer's\n   * laptop pollute their live analytics by accident.\n   */\n  localDevMode?: boolean;\n  /**\n   * Default request timeout in ms. Per-call `options.timeoutMs` overrides.\n   * Caller's `options.timeoutMs: 0` disables the timeout entirely (useful\n   * for tests that intentionally hang).\n   *\n   * Stripe-grade default: 15s. Long enough that a slow-3G mobile keeps\n   * the request alive; short enough that a captive portal or a hung\n   * connection doesn't sit forever. Without this, fetch() inherits the\n   * browser's default (which on Chrome can be 5+ minutes) and a single\n   * bad network can lock up the entire event queue.\n   */\n  timeoutMs?: number;\n  /**\n   * Contract verifier hook — fired AFTER `crossdeckErrorFromResponse`\n   * has built the typed error from a non-OK wire envelope, just BEFORE\n   * the throw. Lets the SDK's verifier layer observe the parsed envelope\n   * shape (type / code / request_id / http status) and assert the\n   * `error-envelope-shape` contract on every real failure, not just at\n   * boot. Synchronous + best-effort: any throw from the hook is swallowed\n   * so a buggy verifier can NEVER replace the customer's real error with\n   * a verifier-internal one. See sdks/web/src/_contract-verifiers.ts\n   * runOnErrorParse for the consumer end of this wire.\n   */\n  onErrorParsed?: (err: CrossdeckError) => void;\n}\n\nexport const DEFAULT_TIMEOUT_MS = 15_000;\n\nexport interface HttpRequestOptions {\n  body?: unknown;\n  query?: Record<string, string | undefined>;\n  /**\n   * Mark the request as `keepalive` so the browser keeps it in flight\n   * even after the page begins unloading. Critical for terminal flushes\n   * fired from `pagehide` / `visibilitychange` — without this, the queued\n   * page.viewed / session.ended events get cancelled the moment the user\n   * navigates away.\n   *\n   * Spec: https://developer.mozilla.org/docs/Web/API/Fetch_API/Using_Fetch#sending_a_request_with_keepalive\n   * Body cap: 64 KB total across all keepalive requests in flight.\n   */\n  keepalive?: boolean;\n  /**\n   * Per-request timeout override (ms). Defaults to the client's\n   * `timeoutMs` (15s). Pass 0 to disable the timeout entirely — only\n   * sensible for tests or long-poll endpoints we don't have today.\n   */\n  timeoutMs?: number;\n  /**\n   * Stripe-style idempotency key. When set, the SDK adds\n   * `Idempotency-Key: <value>` to the request. Reuses the SAME key\n   * across retries of the SAME logical operation so the server can\n   * short-circuit duplicate work without per-event dedup.\n   *\n   * The SDK supplies this for every batch flush — see `event-queue.ts`.\n   * Callers can pass it for ad-hoc retried POSTs too.\n   */\n  idempotencyKey?: string;\n}\n\nexport class HttpClient {\n  constructor(private readonly config: HttpClientConfig) {}\n\n  /**\n   * Issue a request. `path` is relative to the configured baseUrl\n   * (\"/entitlements\", \"/identity/alias\", etc.).\n   *\n   * Throws CrossdeckError on:\n   *   - Network failure (`type: \"network_error\"`)\n   *   - Non-2xx response (typed from the body envelope)\n   *   - JSON parse failure on a 2xx (treated as `internal_error`)\n   */\n  async request<T>(\n    method: \"GET\" | \"POST\",\n    path: string,\n    options: HttpRequestOptions = {}\n  ): Promise<T> {\n    // Localhost short-circuit. Every request returns a synthetic\n    // success shape so the SDK methods that depend on a response\n    // (heartbeat, alias, getEntitlements) don't break — but no\n    // packet leaves the browser. The shape is path-aware so common\n    // callers get sensible empties.\n    if (this.config.localDevMode) {\n      return synthesizeLocalDevResponse<T>(path);\n    }\n\n    const url = this.buildUrl(path, options.query);\n\n    const headers: Record<string, string> = {\n      Authorization: `Bearer ${this.config.publicKey}`,\n      \"Crossdeck-Sdk-Version\": `${SDK_NAME}@${this.config.sdkVersion}`,\n      Accept: \"application/json\",\n    };\n    if (options.idempotencyKey) {\n      // Stripe pattern: same key on retries → server can short-circuit\n      // duplicate work without inspecting the body.\n      headers[\"Idempotency-Key\"] = options.idempotencyKey;\n    }\n    let bodyInit: BodyInit | undefined;\n    if (options.body !== undefined) {\n      headers[\"Content-Type\"] = \"application/json\";\n      bodyInit = JSON.stringify(options.body);\n    }\n\n    // ----- Abort timeout -----\n    // Wire up an AbortController so a stuck connection (captive portal,\n    // satellite link, DNS hang) doesn't lock the queue forever. Per-call\n    // `timeoutMs: 0` disables, otherwise fall back to client default\n    // (15s). The controller is scoped to this request only — clearing\n    // the timer in finally prevents a stale abort from firing after we\n    // already got a response. `AbortSignal.timeout(ms)` would be cleaner\n    // but isn't supported in Safari < 16.4; this hand-rolled pattern is\n    // portable to every browser fetch() supports.\n    const effectiveTimeout = options.timeoutMs ?? this.config.timeoutMs ?? DEFAULT_TIMEOUT_MS;\n    const controller =\n      typeof AbortController !== \"undefined\" && effectiveTimeout > 0\n        ? new AbortController()\n        : null;\n    let timeoutHandle: ReturnType<typeof setTimeout> | null = null;\n    if (controller && effectiveTimeout > 0) {\n      timeoutHandle = setTimeout(() => controller.abort(), effectiveTimeout);\n    }\n\n    let response: Response;\n    try {\n      response = await fetch(url, {\n        method,\n        headers,\n        body: bodyInit,\n        keepalive: options.keepalive === true,\n        signal: controller?.signal,\n      });\n    } catch (err) {\n      const aborted = controller?.signal?.aborted === true;\n      throw new CrossdeckError({\n        type: \"network_error\",\n        code: aborted ? \"request_timeout\" : \"fetch_failed\",\n        message: aborted\n          ? `Request to ${path} aborted after ${effectiveTimeout}ms`\n          : err instanceof Error\n            ? err.message\n            : \"fetch failed\",\n      });\n    } finally {\n      if (timeoutHandle !== null) clearTimeout(timeoutHandle);\n    }\n\n    if (!response.ok) {\n      const parsed = await crossdeckErrorFromResponse(response);\n      // Fire the contract-verifier hook BEFORE the throw so the\n      // verifier sees every real wire envelope, not a sanitised\n      // replay. Wrapped in try/catch so a buggy verifier can never\n      // replace the customer's authentic error with one of our own.\n      // The verifier layer is observability, not control flow.\n      if (this.config.onErrorParsed) {\n        try { this.config.onErrorParsed(parsed); } catch { /* swallow */ }\n      }\n      throw parsed;\n    }\n\n    // 204 No Content / OPTIONS-like — return undefined cast as T (callers\n    // that don't expect a body shouldn't read it).\n    if (response.status === 204) return undefined as T;\n\n    try {\n      return (await response.json()) as T;\n    } catch (err) {\n      throw new CrossdeckError({\n        type: \"internal_error\",\n        code: \"invalid_json_response\",\n        message: \"Server returned a 2xx with an unparseable body.\",\n        requestId: response.headers.get(\"x-request-id\") ?? undefined,\n        status: response.status,\n      });\n    }\n  }\n\n  /**\n   * Whether this client is in localhost dev-mode short-circuit. Used\n   * by other SDK pieces (event-queue) to skip network-bound work\n   * entirely rather than going through synthesizeLocalDevResponse.\n   */\n  get isLocalDevMode(): boolean {\n    return this.config.localDevMode === true;\n  }\n\n  private buildUrl(path: string, query?: Record<string, string | undefined>): string {\n    const base = this.config.baseUrl.replace(/\\/+$/, \"\");\n    const cleanPath = path.startsWith(\"/\") ? path : `/${path}`;\n    let url = base + cleanPath;\n    if (query) {\n      const params = new URLSearchParams();\n      for (const [k, v] of Object.entries(query)) {\n        if (typeof v === \"string\" && v.length > 0) params.append(k, v);\n      }\n      const qs = params.toString();\n      if (qs) url += (url.includes(\"?\") ? \"&\" : \"?\") + qs;\n    }\n    return url;\n  }\n}\n\n/**\n * Build a synthetic response for localhost dev mode. Path-aware so\n * heartbeat / alias / entitlements / events callers each get a\n * sensible empty shape that won't crash downstream code.\n *\n * Heartbeat returns ok=true so the SDK considers itself \"started.\"\n * Alias returns a stub `cdcust_local_*` so identify() resolves to a\n * stable ID across calls (we mint a per-tab one via crypto.randomUUID\n * once and remember it). Entitlements returns an empty list — the\n * dev should grant entitlements in their own dashboard before relying\n * on isEntitled() locally.\n */\nlet cachedLocalCdcust: string | null = null;\nfunction synthesizeLocalDevResponse<T>(path: string): T {\n  if (path.startsWith(\"/sdk/heartbeat\")) {\n    return {\n      object: \"heartbeat\",\n      ok: true,\n      projectId: \"proj_local_dev\",\n      appId: \"app_local_dev\",\n      platform: \"web\",\n      env: \"sandbox\",\n      serverTime: Date.now(),\n    } as unknown as T;\n  }\n  if (path.startsWith(\"/identity/alias\")) {\n    if (!cachedLocalCdcust) {\n      const tail =\n        typeof crypto !== \"undefined\" && \"randomUUID\" in crypto\n          ? crypto.randomUUID().replace(/-/g, \"\").slice(0, 16)\n          : Math.random().toString(36).slice(2, 18);\n      cachedLocalCdcust = `cdcust_local_${tail}`;\n    }\n    return {\n      object: \"alias_result\",\n      crossdeckCustomerId: cachedLocalCdcust,\n      linked: [],\n      mergePending: false,\n      env: \"sandbox\",\n    } as unknown as T;\n  }\n  if (path.startsWith(\"/entitlements\")) {\n    return {\n      object: \"list\",\n      data: [],\n      crossdeckCustomerId: cachedLocalCdcust ?? \"\",\n      env: \"sandbox\",\n    } as unknown as T;\n  }\n  if (path.startsWith(\"/events\")) {\n    return {\n      object: \"list\",\n      received: 0,\n      env: \"sandbox\",\n    } as unknown as T;\n  }\n  // Generic fallback — empty success shape.\n  return {} as T;\n}\n","/**\n * Identity persistence for the SDK.\n *\n * Two values are tracked:\n *   anonymousId          — generated on first boot. Persists for the\n *                          install lifetime so pre-login events stay\n *                          attached to the same identity graph entry.\n *   crossdeckCustomerId  — populated after the first identify() or\n *                          getEntitlements() that resolves a customer.\n *                          Persisted so subsequent boots can read\n *                          entitlements directly without an alias call.\n *\n * ----- Bank-grade identity continuity (v0.6.0+) -----\n *\n * In a browser context, the SDK reads/writes BOTH localStorage and a\n * 1st-party cookie. This is the redundancy that keeps \"10k unique\n * visitors\" actually meaning 10k humans even when one store is wiped:\n *\n *   - Read on boot: take whichever value exists. If both differ\n *     (impossible in normal operation — would mean one store was\n *     restored from a stale backup), localStorage wins because it's\n *     the higher-fidelity store and what we wrote most recently.\n *   - Write on every change: write to BOTH. Future clears of either\n *     don't lose identity continuity.\n *   - Reset: clear BOTH stores so logout actually wipes the device.\n *\n * Outside browsers (Node, Workers) the redundant cookie store is\n * absent and behaviour collapses to the single-store original — no\n * code path changes for non-web SDK consumers.\n */\n\nimport type { KeyValueStorage } from \"./types\";\n\nconst KEY_ANON = \"anon_id\";\nconst KEY_CDCUST = \"cdcust_id\";\n\nexport interface IdentityState {\n  anonymousId: string;\n  crossdeckCustomerId: string | null;\n}\n\nexport class IdentityStore {\n  private state: IdentityState;\n  /**\n   * Optional secondary store written/read alongside the primary. When\n   * present, every setItem fans out to both stores and getItem prefers\n   * primary but falls back to secondary if primary returned null. This\n   * is what gives us localStorage + cookie redundancy in browsers.\n   */\n  private readonly secondary: KeyValueStorage | null;\n\n  constructor(\n    private readonly primary: KeyValueStorage,\n    private readonly prefix: string,\n    secondary?: KeyValueStorage,\n  ) {\n    this.secondary = secondary ?? null;\n    const anonFromPrimary = primary.getItem(prefix + KEY_ANON);\n    const cdcustFromPrimary = primary.getItem(prefix + KEY_CDCUST);\n    const anonFromSecondary = this.secondary?.getItem(prefix + KEY_ANON) ?? null;\n    const cdcustFromSecondary = this.secondary?.getItem(prefix + KEY_CDCUST) ?? null;\n\n    // Prefer the primary store's value; fall back to secondary on miss.\n    // The \"both populated, values differ\" case never happens in normal\n    // operation — every write goes to both stores in lockstep — so we\n    // don't bother with conflict resolution beyond \"trust primary.\"\n    const anon = anonFromPrimary ?? anonFromSecondary;\n    const cdcust = cdcustFromPrimary ?? cdcustFromSecondary;\n\n    this.state = {\n      anonymousId: anon ?? this.mintAnonymousId(),\n      crossdeckCustomerId: cdcust,\n    };\n\n    // If we just minted a new anonymousId, write it to both stores so\n    // either store can answer \"what's our id\" on subsequent boots.\n    // If we read it from one store but not the other, write it to the\n    // missing store too — that's the resync that catches a recovering\n    // ITP-cleared cookie or a freshly-minted private-tab localStorage.\n    if (!anonFromPrimary || !anonFromSecondary) {\n      this.writeBoth(prefix + KEY_ANON, this.state.anonymousId);\n    }\n    if (cdcust && (!cdcustFromPrimary || !cdcustFromSecondary)) {\n      this.writeBoth(prefix + KEY_CDCUST, cdcust);\n    }\n  }\n\n  /** Return the persisted anonymous device ID (always set). */\n  get anonymousId(): string {\n    return this.state.anonymousId;\n  }\n\n  /** Return the resolved cross­deckCustomerId once we have one, else null. */\n  get crossdeckCustomerId(): string | null {\n    return this.state.crossdeckCustomerId;\n  }\n\n  /** Persist a newly-resolved Crossdeck customer ID. */\n  setCrossdeckCustomerId(value: string): void {\n    this.state.crossdeckCustomerId = value;\n    this.writeBoth(this.prefix + KEY_CDCUST, value);\n  }\n\n  /**\n   * Wipe persisted identity. Called by reset() — used when an end-user\n   * logs out. After reset the SDK mints a new anonymousId so the next\n   * pre-login session is a fresh customer in the identity graph.\n   */\n  reset(): void {\n    this.deleteBoth(this.prefix + KEY_ANON);\n    this.deleteBoth(this.prefix + KEY_CDCUST);\n    this.state = {\n      anonymousId: this.mintAnonymousId(),\n      crossdeckCustomerId: null,\n    };\n    this.writeBoth(this.prefix + KEY_ANON, this.state.anonymousId);\n  }\n\n  /**\n   * Generate an anonymousId. Crockford-ish base32 timestamp + random\n   * suffix. Same shape Stripe / Segment / others use — sortable, log-\n   * friendly, no PII.\n   */\n  private mintAnonymousId(): string {\n    const ts = Date.now().toString(36);\n    const rand = randomChars(10);\n    return `anon_${ts}${rand}`;\n  }\n\n  private writeBoth(key: string, value: string): void {\n    try { this.primary.setItem(key, value); } catch { /* see storage.ts probe */ }\n    if (this.secondary) {\n      try { this.secondary.setItem(key, value); } catch { /* swallow per-store */ }\n    }\n  }\n\n  private deleteBoth(key: string): void {\n    try { this.primary.removeItem(key); } catch { /* swallow */ }\n    if (this.secondary) {\n      try { this.secondary.removeItem(key); } catch { /* swallow */ }\n    }\n  }\n}\n\n/**\n * Generate a cryptographically-random short string. Uses\n * crypto.getRandomValues when available (browser + Node 18+ via webcrypto),\n * else falls back to Math.random — that fallback is safe here because\n * anonymousId entropy doesn't need to resist offline brute force; it\n * needs to be unique-with-overwhelming-probability across one device's\n * lifetime.\n *\n * Exported for unit testing (alphabet round-trip).\n */\nexport function randomChars(count: number): string {\n  const alphabet = \"0123456789abcdefghijklmnopqrstuvwxyz\";\n  const out: string[] = [];\n  const cryptoApi = (globalThis as { crypto?: { getRandomValues?: (a: Uint8Array) => Uint8Array } }).crypto;\n  if (cryptoApi?.getRandomValues) {\n    const buf = new Uint8Array(count);\n    cryptoApi.getRandomValues(buf);\n    for (let i = 0; i < count; i++) {\n      out.push(alphabet[buf[i]! % alphabet.length] ?? \"0\");\n    }\n  } else {\n    for (let i = 0; i < count; i++) {\n      out.push(alphabet[Math.floor(Math.random() * alphabet.length)] ?? \"0\");\n    }\n  }\n  return out.join(\"\");\n}\n","/**\n * Crossdeck Trust — the human-proof panel, native to the SDK.\n *\n * This is the browser half of Crossdeck Trust wired THROUGH the SDK the customer\n * already runs, not bolted on beside it. It renders the SAME cross-origin iframe\n * served from trust.cross-deck.com — the un-restylable, browser-enforced brand\n * panel — and hands the minted attestation back PROGRAMMATICALLY (a Promise + an\n * `onToken` callback) instead of the hidden-field/DOM handshake the raw\n * `embed.js` loader uses. The iframe is the bouncer; the SDK is the premium front\n * door to it. You pass the token to your server, which verifies it at the gate\n * (`crossdeck.trust.gate(...)` in @cross-deck/node, or a raw POST /v1/trust/gate).\n *\n * Non-negotiables baked in (Stripe / Cloudflare / bank grade):\n *  - ISOLATED — every DOM / postMessage touch is wrapped; a stumble here can never\n *    throw into the host app, the rest of the SDK, or the customer's signup form.\n *  - FAIL-OPEN, never fail-allow — if the panel can't mint (adblocker, our outage,\n *    offline, timeout), `ready` resolves with NO token. The signup proceeds; the\n *    SERVER gate scores the ABSENCE. We never wave a tokenless request through, and\n *    we never block the form because our panel had a bad day. Failing CLOSED would\n *    hand an attacker a site-wide-outage weapon — so we never do.\n *  - ORIGIN-LOCKED — we only trust messages from OUR panel origin AND from this\n *    exact iframe's window. Nothing on the host page can forge a token.\n *  - The token is minted INSIDE our origin; the host page (even an XSS on it) never\n *    participates in the mint, it only receives the finished token.\n */\n\n/** The origin that serves the branded, un-restylable Trust panel. */\nexport const TRUST_PANEL_ORIGIN = \"https://trust.cross-deck.com\";\n\n/** Size clamps — identical to embed.js so the panel is the same on every install. */\nconst MAX_WIDTH_PX = 360;\nconst INITIAL_HEIGHT_PX = 132;\nconst MIN_HEIGHT_PX = 96;\nconst MAX_HEIGHT_PX = 220;\nconst DEFAULT_TIMEOUT_MS = 15000;\n\n/** A minted human-proof attestation. Pass `token` to your gate call. */\nexport interface TrustToken {\n  /** The single-use, project-bound attestation to send to your server as `token`. */\n  token: string;\n  /** Epoch ms at which the token expires (mint fresh per signup attempt), or null. */\n  expiresAt: number | null;\n}\n\n/** Resolution of a panel that failed open — a token never minted. Not an error. */\nexport interface TrustUnavailable {\n  token: null;\n  expiresAt: null;\n  /** Why no token was minted (for your logs) — e.g. \"timeout\", \"blocked\", \"offline\". */\n  reason: string;\n}\n\nexport type TrustResult = TrustToken | TrustUnavailable;\n\n/** Lifecycle of a panel driven by a framework binding: pending → ready | unavailable. */\nexport type TrustTokenStatus = \"pending\" | \"ready\" | \"unavailable\";\n\nexport interface MountTrustPanelOptions {\n  /** The project's publishable key (cd_pub_…). The SDK client supplies this for you. */\n  publicKey: string;\n  /** Where to render the panel — an element or a selector resolved at mount time. */\n  target: HTMLElement | string;\n  /** Called once when a token is minted. Optional — you can await `ready` instead. */\n  onToken?: (t: TrustToken) => void;\n  /**\n   * Called if the panel could not mint (blocked, offline, timeout, our outage).\n   * INFORMATIONAL for your logs — NOT an error you must handle: `ready` still\n   * resolves and your signup proceeds. Fail-open is the contract.\n   */\n  onUnavailable?: (reason: string) => void;\n  /** Override the panel origin (tests / self-host). Defaults to production. */\n  origin?: string;\n  /** Max wait for a mint before failing open, in ms. Default 15000. */\n  timeoutMs?: number;\n}\n\nexport interface TrustPanelHandle {\n  /** The iframe we mounted — for layout/measurement only; never reach inside it. */\n  readonly frame: HTMLIFrameElement | null;\n  /**\n   * Resolves EXACTLY once: the token on success, or a {@link TrustUnavailable} if\n   * the panel failed open. NEVER rejects — a rejection would tempt callers to block\n   * the signup, which is precisely what fail-open forbids.\n   */\n  readonly ready: Promise<TrustResult>;\n  /** Tear down: remove the iframe + listeners. Idempotent. */\n  destroy(): void;\n}\n\n/**\n * Options for `Crossdeck.trust.panel(...)` — the same as {@link MountTrustPanelOptions}\n * but the SDK client injects `publicKey` from your `init()`, so you omit it.\n */\nexport type TrustPanelInput = Omit<MountTrustPanelOptions, \"publicKey\"> & {\n  /**\n   * The project's publishable key (cd_pub_…). Pass it **explicitly** — the robust,\n   * Stripe (`loadStripe(pk)`) / Cloudflare Turnstile (`sitekey`) pattern — and the\n   * panel needs no `Crossdeck.init()` at all. Omit it and the SDK falls back to the\n   * key from `init()`. Explicit always wins.\n   */\n  publicKey?: string;\n};\n\n/** The `Crossdeck.trust` namespace surfaced on the SDK client. */\nexport interface CrossdeckTrustNamespace {\n  /** Render the branded Trust panel and mint an attestation. See {@link mountTrustPanel}. */\n  panel(input: TrustPanelInput): TrustPanelHandle;\n}\n\n/**\n * Mount the Crossdeck Trust panel and mint an attestation. Framework-agnostic and\n * guaranteed not to throw — any construction failure resolves `ready` fail-open.\n */\nexport function mountTrustPanel(opts: MountTrustPanelOptions): TrustPanelHandle {\n  const origin = normalizeOrigin(opts.origin) || TRUST_PANEL_ORIGIN;\n  const timeoutMs = clampInt(opts.timeoutMs, DEFAULT_TIMEOUT_MS, 1000, 120000);\n\n  let settled = false;\n  let onMessage: ((ev: MessageEvent) => void) | null = null;\n  let timer: ReturnType<typeof setTimeout> | null = null;\n  let frame: HTMLIFrameElement | null = null;\n\n  let resolveReady!: (v: TrustResult) => void;\n  const ready = new Promise<TrustResult>((res) => {\n    resolveReady = res;\n  });\n\n  /** Single, idempotent exit — success AND fail-open both come through here. */\n  function settle(result: TrustToken | null, reason?: string): void {\n    if (settled) return;\n    settled = true;\n    if (timer) safe(() => clearTimeout(timer as ReturnType<typeof setTimeout>));\n    if (onMessage) safe(() => window.removeEventListener(\"message\", onMessage as EventListener));\n    if (result) {\n      safe(() => opts.onToken?.(result));\n      resolveReady(result);\n    } else {\n      const why = reason || \"unavailable\";\n      safe(() => opts.onUnavailable?.(why));\n      resolveReady({ token: null, expiresAt: null, reason: why });\n    }\n  }\n\n  try {\n    if (typeof window === \"undefined\" || typeof document === \"undefined\") {\n      // SSR / non-browser — nothing to mount. Fail open on the next tick.\n      queueMicrotask(() => settle(null, \"no_dom\"));\n      return makeHandle();\n    }\n\n    const host = resolveTarget(opts.target);\n    frame = createFrame(origin, opts.publicKey);\n\n    if (!host) {\n      queueMicrotask(() => settle(null, \"no_mount_target\"));\n      return makeHandle();\n    }\n\n    onMessage = (ev: MessageEvent) => {\n      // ORIGIN LOCK #1 — only our panel origin.\n      if (ev.origin !== origin) return;\n      // ORIGIN LOCK #2 — only THIS iframe's window (blocks any other frame/tab).\n      if (!ev.data || !frame || ev.source !== frame.contentWindow) return;\n      const d = ev.data as { type?: string; height?: number; token?: string; expMs?: number };\n      if (d.type === \"cd-trust-size\" && typeof d.height === \"number\") {\n        safe(() => {\n          if (frame) frame.style.height = clampInt(d.height, INITIAL_HEIGHT_PX, MIN_HEIGHT_PX, MAX_HEIGHT_PX) + \"px\";\n        });\n      } else if (d.type === \"cd-trust-token\" && typeof d.token === \"string\") {\n        settle({ token: d.token, expiresAt: typeof d.expMs === \"number\" ? d.expMs : null });\n      }\n    };\n    window.addEventListener(\"message\", onMessage);\n    host.appendChild(frame);\n\n    // Fail-open guard: if no mint arrives in time, proceed tokenless.\n    timer = setTimeout(() => settle(null, \"timeout\"), timeoutMs);\n  } catch (err) {\n    // ANY construction failure fails open. The host never sees a throw.\n    queueMicrotask(() => settle(null, \"mount_error:\" + safeMessage(err)));\n  }\n\n  return makeHandle();\n\n  function makeHandle(): TrustPanelHandle {\n    return {\n      frame,\n      ready,\n      destroy() {\n        if (onMessage) safe(() => window.removeEventListener(\"message\", onMessage as EventListener));\n        if (timer) safe(() => clearTimeout(timer as ReturnType<typeof setTimeout>));\n        safe(() => frame?.parentNode?.removeChild(frame as HTMLIFrameElement));\n        // If torn down before minting, release any awaiting caller (fail-open).\n        settle(null, \"destroyed\");\n      },\n    };\n  }\n}\n\n/** Build the branded iframe — same fixed, capped footprint as embed.js. */\nfunction createFrame(origin: string, publicKey: string): HTMLIFrameElement {\n  const frame = document.createElement(\"iframe\");\n  frame.title = \"Protected by Crossdeck Trust\";\n  frame.setAttribute(\"aria-label\", \"Protected by Crossdeck Trust\");\n  frame.setAttribute(\"scrolling\", \"no\");\n  frame.setAttribute(\"allowtransparency\", \"true\");\n  frame.style.cssText =\n    \"border:0;display:block;width:100%;max-width:\" +\n    MAX_WIDTH_PX +\n    \"px;height:\" +\n    INITIAL_HEIGHT_PX +\n    \"px;margin:0;background:transparent;color-scheme:light;\";\n  frame.src = origin + \"/panel?k=\" + encodeURIComponent(publicKey || \"\");\n  return frame;\n}\n\nfunction resolveTarget(target: HTMLElement | string): HTMLElement | null {\n  try {\n    if (typeof target === \"string\") return document.querySelector(target);\n    return target || null;\n  } catch {\n    return null;\n  }\n}\n\nfunction normalizeOrigin(o?: string): string | null {\n  if (!o || typeof o !== \"string\") return null;\n  try {\n    return new URL(o).origin;\n  } catch {\n    return null;\n  }\n}\n\nfunction clampInt(v: unknown, fallback: number, min: number, max: number): number {\n  const n = typeof v === \"number\" && isFinite(v) ? v : fallback;\n  return Math.min(Math.max(n, min), max);\n}\n\nfunction safe(fn: () => void): void {\n  try {\n    fn();\n  } catch {\n    /* isolation: a stumble here must never escape into the host app */\n  }\n}\n\nfunction safeMessage(err: unknown): string {\n  try {\n    return err instanceof Error ? err.message : String(err);\n  } catch {\n    return \"unknown\";\n  }\n}\n","/**\n * Minimal synchronous SHA-256 implementation (FIPS 180-4).\n *\n * Used to derive a per-user storage suffix for the entitlement\n * cache so each developerUserId's data lives under a physically\n * separate localStorage key. Bank-grade isolation contract: even\n * a botched identify() that skips the in-memory clear cannot\n * cross-read a different user's cached entitlements.\n *\n * Why not SubtleCrypto: SubtleCrypto.digest is async, and the\n * entitlement-cache hot path (setFromList, clear, hydrate) is\n * synchronous. An async hash would force every cache operation\n * to become Promise<>-returning, cascading through identify(),\n * getEntitlements(), and the React `useEntitlement` hook. A\n * pure-JS sync impl keeps the API shape unchanged.\n *\n * Why not a smaller hash (FNV-1a etc): SHA-256 is the contract\n * documented in the SDK README. A non-crypto hash would\n * collision-resist for normal user populations but the public\n * promise is SHA-256.\n *\n * Implementation notes:\n *   - Input: UTF-8 bytes from `String → encodeURIComponent`-style\n *     percent-encoding. Pure ASCII is identity-mapped; multi-byte\n *     runes are expanded to UTF-8 octets.\n *   - Output: 64-character lowercase hex string.\n *   - Independent reference vectors checked: SHA256(\"\") =\n *     e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855,\n *     SHA256(\"abc\") =\n *     ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad.\n */\n\nconst K = new Uint32Array([\n  0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1,\n  0x923f82a4, 0xab1c5ed5, 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3,\n  0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, 0xe49b69c1, 0xefbe4786,\n  0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,\n  0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147,\n  0x06ca6351, 0x14292967, 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13,\n  0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, 0xa2bfe8a1, 0xa81a664b,\n  0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,\n  0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a,\n  0x5b9cca4f, 0x682e6ff3, 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208,\n  0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2,\n]);\n\nfunction utf8Bytes(input: string): Uint8Array {\n  // TextEncoder is universal in browsers + Node 11+ + RN (Hermes/JSC).\n  // Constructed lazily so the module load doesn't depend on the global.\n  if (typeof TextEncoder !== \"undefined\") {\n    return new TextEncoder().encode(input);\n  }\n  // Hand-rolled UTF-8 encode for environments without TextEncoder.\n  const out: number[] = [];\n  for (let i = 0; i < input.length; i++) {\n    let codePoint = input.charCodeAt(i);\n    if (codePoint >= 0xd800 && codePoint <= 0xdbff && i + 1 < input.length) {\n      const next = input.charCodeAt(i + 1);\n      if (next >= 0xdc00 && next <= 0xdfff) {\n        codePoint = 0x10000 + ((codePoint - 0xd800) << 10) + (next - 0xdc00);\n        i++;\n      }\n    }\n    if (codePoint < 0x80) {\n      out.push(codePoint);\n    } else if (codePoint < 0x800) {\n      out.push(0xc0 | (codePoint >> 6));\n      out.push(0x80 | (codePoint & 0x3f));\n    } else if (codePoint < 0x10000) {\n      out.push(0xe0 | (codePoint >> 12));\n      out.push(0x80 | ((codePoint >> 6) & 0x3f));\n      out.push(0x80 | (codePoint & 0x3f));\n    } else {\n      out.push(0xf0 | (codePoint >> 18));\n      out.push(0x80 | ((codePoint >> 12) & 0x3f));\n      out.push(0x80 | ((codePoint >> 6) & 0x3f));\n      out.push(0x80 | (codePoint & 0x3f));\n    }\n  }\n  return new Uint8Array(out);\n}\n\n/**\n * Compute SHA-256 of `input` as a 64-character lowercase hex\n * string. Synchronous; safe to call on the hot path.\n */\nexport function sha256Hex(input: string): string {\n  const bytes = utf8Bytes(input);\n  const bitLength = bytes.length * 8;\n  // Pad: append 0x80, then zeros, then 64-bit big-endian length to\n  // reach a multiple of 512 bits (64 bytes).\n  const blockCount = Math.floor((bytes.length + 9 + 63) / 64);\n  const padded = new Uint8Array(blockCount * 64);\n  padded.set(bytes);\n  padded[bytes.length] = 0x80;\n  // Length in bits as 64-bit big-endian. JS bitwise ops are 32-bit,\n  // so split high / low halves.\n  const high = Math.floor(bitLength / 0x100000000);\n  const low = bitLength >>> 0;\n  const lenOffset = padded.length - 8;\n  padded[lenOffset + 0] = (high >>> 24) & 0xff;\n  padded[lenOffset + 1] = (high >>> 16) & 0xff;\n  padded[lenOffset + 2] = (high >>> 8) & 0xff;\n  padded[lenOffset + 3] = high & 0xff;\n  padded[lenOffset + 4] = (low >>> 24) & 0xff;\n  padded[lenOffset + 5] = (low >>> 16) & 0xff;\n  padded[lenOffset + 6] = (low >>> 8) & 0xff;\n  padded[lenOffset + 7] = low & 0xff;\n\n  const H = new Uint32Array([\n    0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c,\n    0x1f83d9ab, 0x5be0cd19,\n  ]);\n  const W = new Uint32Array(64);\n\n  // Non-null assertions throughout: the loop bounds + Uint8Array /\n  // Uint32Array allocations guarantee every indexed access is\n  // in-bounds. TypeScript's noUncheckedIndexedAccess flags the\n  // `T | undefined` shape regardless; the `!` here is correctness-\n  // preserving suppression, not a hope-and-pray.\n  for (let block = 0; block < blockCount; block++) {\n    const offset = block * 64;\n    for (let t = 0; t < 16; t++) {\n      W[t] =\n        ((padded[offset + t * 4]! << 24) |\n          (padded[offset + t * 4 + 1]! << 16) |\n          (padded[offset + t * 4 + 2]! << 8) |\n          padded[offset + t * 4 + 3]!) >>>\n        0;\n    }\n    for (let t = 16; t < 64; t++) {\n      const w15 = W[t - 15]!;\n      const w2 = W[t - 2]!;\n      const s0 = ((w15 >>> 7) | (w15 << 25)) ^ ((w15 >>> 18) | (w15 << 14)) ^ (w15 >>> 3);\n      const s1 = ((w2 >>> 17) | (w2 << 15)) ^ ((w2 >>> 19) | (w2 << 13)) ^ (w2 >>> 10);\n      W[t] = (W[t - 16]! + s0 + W[t - 7]! + s1) >>> 0;\n    }\n\n    let a = H[0]!, b = H[1]!, c = H[2]!, d = H[3]!;\n    let e = H[4]!, f = H[5]!, g = H[6]!, h = H[7]!;\n\n    for (let t = 0; t < 64; t++) {\n      const S1 = ((e >>> 6) | (e << 26)) ^ ((e >>> 11) | (e << 21)) ^ ((e >>> 25) | (e << 7));\n      const ch = (e & f) ^ (~e & g);\n      const temp1 = (h + S1 + ch + K[t]! + W[t]!) >>> 0;\n      const S0 = ((a >>> 2) | (a << 30)) ^ ((a >>> 13) | (a << 19)) ^ ((a >>> 22) | (a << 10));\n      const maj = (a & b) ^ (a & c) ^ (b & c);\n      const temp2 = (S0 + maj) >>> 0;\n      h = g;\n      g = f;\n      f = e;\n      e = (d + temp1) >>> 0;\n      d = c;\n      c = b;\n      b = a;\n      a = (temp1 + temp2) >>> 0;\n    }\n\n    H[0] = (H[0]! + a) >>> 0;\n    H[1] = (H[1]! + b) >>> 0;\n    H[2] = (H[2]! + c) >>> 0;\n    H[3] = (H[3]! + d) >>> 0;\n    H[4] = (H[4]! + e) >>> 0;\n    H[5] = (H[5]! + f) >>> 0;\n    H[6] = (H[6]! + g) >>> 0;\n    H[7] = (H[7]! + h) >>> 0;\n  }\n\n  let hex = \"\";\n  for (let i = 0; i < 8; i++) {\n    hex += H[i]!.toString(16).padStart(8, \"0\");\n  }\n  return hex;\n}\n","/**\n * Durable last-known-good cache of the customer's entitlements.\n *\n * This cache is NOT a second source of truth. Crossdeck remains the\n * only source; this is the SDK's local copy of what the server last\n * told us — a cache that doesn't forget during a network partition.\n *\n * Durability contract (the RevenueCat model):\n *   - Every successful server read is persisted to device storage\n *     (localStorage, via the SDK's storage adapter).\n *   - On SDK boot the cache hydrates from storage synchronously, so\n *     isEntitled() answers correctly from the very first call — there\n *     is no cold-start window where a returning Pro customer reads as\n *     free.\n *   - When the server is unreachable, the SDK keeps serving the last\n *     entitlements it successfully fetched. A failed refresh never\n *     reaches setFromList(), so it cannot clear the cache; only a\n *     SUCCESSFUL fetch replaces it. An outage can never fail a paying\n *     customer down to free.\n *   - Staleness alone never returns false. Each entitlement is honoured\n *     against its OWN validUntil instead — a time-based trial expiry\n *     still applies even mid-partition, a still-valid Pro entitlement\n *     rides the outage out.\n *   - Staleness is VISIBLE, not silent. validUntil covers time-based\n *     expiry; it does NOT cover an event-based revoke (chargeback,\n *     refund, fraud) — that has no validUntil, so the cache would keep\n *     serving a revoked customer through an outage. Serving them is the\n *     right trade (don't lock real payers out), but unbounded-and-\n *     invisible is the bug. So: once a refresh ATTEMPT fails (or the\n *     data ages past staleAfterMs) the cache is marked stale —\n *     isStale / freshness are surfaced in diagnostics(). It keeps\n *     serving last-known-good; the staleness is just no longer hidden.\n *\n * The cache is wiped only on reset() (logout) and on an identity switch\n * — never by a TTL.\n *\n * Reactive listener API\n * ---------------------\n * `subscribe(listener)` registers a callback fired every time the cache\n * mutates (setFromList or clear) — the foundation for the\n * `useEntitlement` React hook and other framework bindings. Semantics:\n *   - Fired AFTER the mutation, so the listener sees fresh state.\n *   - Fire-and-forget: a throwing listener is swallowed (and counted)\n *     so a buggy consumer can't crash the SDK or other listeners.\n *   - Unsubscribe is idempotent.\n *   - Listeners are NOT fired on subscribe — a caller that wants the\n *     initial state reads isEntitled() / list() synchronously, which\n *     work from boot thanks to hydration above.\n */\n\nimport { sha256Hex } from \"./hash\";\nimport type { KeyValueStorage, PublicEntitlement } from \"./types\";\n\nexport type EntitlementsListener = (entitlements: PublicEntitlement[]) => void;\n\n/** Shape of the blob persisted to device storage. Versioned for forward-compat. */\ninterface PersistedCache {\n  v: 1;\n  entitlements: PublicEntitlement[];\n  lastUpdated: number;\n}\n\n/** Default staleness window — data older than this is flagged even with no failed refresh. */\nconst DEFAULT_STALE_AFTER_MS = 24 * 60 * 60 * 1000; // 24h\n\n/** Anonymous suffix used before identify() has been called. Stable\n * across launches so an anonymous session's cache survives a reload\n * — but physically separate from any identified user's cache. */\nconst ANON_SUFFIX = \"_anon\";\n\n/** Suffix for the index entry that tracks every per-user key we've\n * written. Used by clearAll() to scope a logout-wipe to ONLY\n * Crossdeck keys, never the host app's own localStorage. */\nconst INDEX_SUFFIX = \"_index\";\n\nexport class EntitlementCache {\n  private all: PublicEntitlement[] = [];\n  private lastUpdated = 0;\n  private lastRefreshFailedAt = 0;\n  private listeners = new Set<EntitlementsListener>();\n  private listenerErrorCount = 0;\n  private readonly storage?: KeyValueStorage;\n  private readonly storageKeyPrefix: string;\n  private readonly staleAfterMs: number;\n  private currentSuffix: string = ANON_SUFFIX;\n  private _generation = 0;\n\n  /**\n   * Monotonic identity generation. Bumped on every setUserKey() (identity\n   * switch OR same-id re-identify). An async getEntitlements() captures this\n   * before its await and drops its write if the generation moved while the\n   * request was in flight — so a stale fetch (e.g. the anonymous boot fetch,\n   * or one started under a prior slot) can never clobber the authoritative\n   * answer for the user who is identified NOW. Last-writer-wins is the enemy;\n   * this makes the write conditional on \"still the same identity\".\n   */\n  get generation(): number {\n    return this._generation;\n  }\n\n  /**\n   * @param storage          Device storage adapter. When omitted (tests) or\n   *                         a MemoryStorage (strict-consent / no-persistence\n   *                         mode) the cache is session-only — durability is\n   *                         simply absent, never wrong.\n   * @param storageKeyPrefix Prefix used to derive per-user storage keys\n   *                         (`<prefix>:<sha256(userId)>`). Default\n   *                         `crossdeck:entitlements`. The trailing user\n   *                         suffix is filled at identify() / reset()\n   *                         time — see [[setUserKey]].\n   * @param staleAfterMs     Age past which last-known-good is flagged stale\n   *                         even without a failed refresh. Default 24h.\n   */\n  constructor(\n    storage?: KeyValueStorage,\n    storageKeyPrefix = \"crossdeck:entitlements\",\n    staleAfterMs = DEFAULT_STALE_AFTER_MS,\n  ) {\n    this.storage = storage;\n    this.storageKeyPrefix = storageKeyPrefix;\n    this.staleAfterMs = staleAfterMs;\n    this.hydrate();\n  }\n\n  /** The full storage key the current-user blob is persisted under. */\n  private get storageKey(): string {\n    return `${this.storageKeyPrefix}:${this.currentSuffix}`;\n  }\n\n  /** Key of the index blob — a JSON array of every suffix we've\n   * written. Used by clearAll() to scope a logout-wipe. */\n  private get indexKey(): string {\n    return `${this.storageKeyPrefix}:${INDEX_SUFFIX}`;\n  }\n\n  /** Derive a stable suffix for a developerUserId via SHA-256. The\n   * raw userId never lands in the storage key — protects against\n   * accidentally leaking email-style identifiers through DevTools\n   * inspection. Pass `null` to switch back to the anonymous slot. */\n  static suffixForUserId(userId: string | null): string {\n    if (userId == null || userId === \"\") return ANON_SUFFIX;\n    return sha256Hex(userId);\n  }\n\n  /**\n   * Switch the cache to a different user's storage slot. Bank-grade\n   * three-layer isolation:\n   *   (a) Physical key separation — `<prefix>:<sha256(userId)>` so\n   *       a user-switch can't physically read prior user's data\n   *       even if the in-memory clear was skipped.\n   *   (b) Unconditional in-memory clear — invoked whenever the\n   *       active suffix changes, even on same-id re-identify.\n   *   (c) Re-hydrate from the new slot — a returning user observes\n   *       their last-known-good cache from storage immediately.\n   *\n   * Caller (identify() / reset()) MUST invoke this BEFORE the next\n   * setFromList() so the write lands under the right key.\n   */\n  setUserKey(userId: string | null): void {\n    // ONE code path for both the different-user switch AND the same-id\n    // re-identify (React/Vue providers call identify() 2–3 times as auth\n    // traits populate in stages: uid, then email, then profile name).\n    //\n    // Order is load-bearing and was the bug: hydrate() BEFORE notify().\n    // The previous same-id branch did notify() FIRST (emitting an EMPTY\n    // snapshot) then hydrate() SILENTLY — so every onEntitlementsChange\n    // subscriber (useEntitlement / useEntitlements / the Vue composable /\n    // any hand-rolled listener) latched `[]` → rendered the paywall → and\n    // was never told to re-read, because the restore didn't notify. The\n    // in-memory `all` ended up correct (which is why synchronous\n    // isEntitled() and the server-side gate were never wrong, and why the\n    // per-user-cache-isolation self-check stayed quiet), but the EMITTED\n    // stream was corrupted. Restore first, then emit the SETTLED state\n    // exactly once: subscribers never observe an empty flash.\n    this.currentSuffix = EntitlementCache.suffixForUserId(userId); // no-op if unchanged\n    this._generation++; // invalidate any getEntitlements() in flight under the old identity\n    this.all = []; // unconditional in-memory clear (founder contract) — transient, no notify between\n    this.lastUpdated = 0;\n    this.lastRefreshFailedAt = 0;\n    this.hydrate(); // restore THIS slot's last-known-good first\n    this.notify(); // emit the settled snapshot once\n  }\n\n  /**\n   * Sync read — true iff the entitlement is currently granting access.\n   *\n   * Served from last-known-good: a stale cache (server unreachable since\n   * the last successful fetch) still answers true for a still-valid\n   * entitlement. The ONLY thing that turns it false is the entitlement's\n   * own expiry (validUntil) — never overall cache staleness.\n   */\n  isEntitled(key: string): boolean {\n    const nowSec = Date.now() / 1000;\n    return this.all.some(\n      (e) =>\n        e.key === key &&\n        e.isActive &&\n        (e.validUntil == null || e.validUntil > nowSec),\n    );\n  }\n\n  /** Full snapshot for callers that need source / validUntil details. */\n  list(): PublicEntitlement[] {\n    return this.all.slice();\n  }\n\n  /**\n   * True iff the in-memory snapshot is exactly what the CURRENT slot holds in\n   * storage — i.e., it was hydrated from THIS slot, never leaked from another.\n   *\n   * This is the real per-user isolation invariant after a slot rotation: the\n   * snapshot is sourced from the new slot's storage — empty for a first-time\n   * user, or that user's OWN last-known-good for a returning one (the\n   * intentional rehydrate, layer (c) of setUserKey). It is NEVER the prior\n   * user's data. Asserting \"empty after rotation\" is too strict — it false-\n   * fails a returning user legitimately re-reading their own cache.\n   * Session-only (no storage) can't diverge, so it is trivially isolated.\n   */\n  snapshotMatchesCurrentSlot(): boolean {\n    if (!this.storage) return true;\n    let persisted: PublicEntitlement[] = [];\n    try {\n      const raw = this.storage.getItem(this.storageKey);\n      if (raw) {\n        const parsed = JSON.parse(raw) as PersistedCache;\n        if (parsed && parsed.v === 1 && Array.isArray(parsed.entitlements)) {\n          persisted = parsed.entitlements;\n        }\n      }\n    } catch {\n      return false;\n    }\n    const norm = (l: PublicEntitlement[]) =>\n      l.map((e) => `${e.key}|${e.isActive}|${e.validUntil}`).sort().join(\",\");\n    return norm(this.all) === norm(persisted);\n  }\n\n  /** When the cache was last refreshed from the server. 0 means \"never\". */\n  get freshness(): number {\n    return this.lastUpdated;\n  }\n\n  /**\n   * Whether the cache is knowingly serving older-than-trustworthy data.\n   *\n   * True when the most recent refresh ATTEMPT failed (Crossdeck\n   * unreachable since the last success — the outage case, distinct from\n   * a benign idle tab that simply hasn't re-fetched), OR when\n   * last-known-good has aged past staleAfterMs.\n   *\n   * isStale never changes what isEntitled() returns — the cache still\n   * serves last-known-good. It exists so the staleness is observable\n   * (diagnostics()) instead of an unbounded silent window where a\n   * revoked customer holds access with nobody able to see it.\n   */\n  get isStale(): boolean {\n    if (this.lastRefreshFailedAt > this.lastUpdated) return true;\n    return (\n      this.lastUpdated > 0 &&\n      Date.now() - this.lastUpdated > this.staleAfterMs\n    );\n  }\n\n  /** Epoch ms of the last failed refresh attempt. 0 if none since the last success. */\n  get refreshFailedAt(): number {\n    return this.lastRefreshFailedAt;\n  }\n\n  get listenerErrors(): number {\n    return this.listenerErrorCount;\n  }\n\n  /**\n   * Record that a refresh attempt failed (Crossdeck unreachable / a\n   * transient error). The SDK's getEntitlements() calls this in its\n   * catch path. It does NOT touch the cached entitlements — last-known-\n   * good keeps serving — it only flips isStale so the staleness shows\n   * up in diagnostics() rather than being silent.\n   */\n  markRefreshFailed(): void {\n    this.lastRefreshFailedAt = Date.now();\n  }\n\n  /**\n   * Replace the cache with a fresh server response and persist it to\n   * device storage so it survives a reload / app restart.\n   *\n   * Called ONLY after a successful server read — a failed fetch throws\n   * before it reaches here, so last-known-good is preserved through an\n   * outage. A success also clears the stale flag.\n   */\n  setFromList(entitlements: PublicEntitlement[]): void {\n    this.all = entitlements.slice();\n    this.lastUpdated = Date.now();\n    this.lastRefreshFailedAt = 0;\n    this.persist();\n    this.recordSuffixInIndex(this.currentSuffix);\n    this.notify();\n  }\n\n  /**\n   * Wipe the CURRENT user's slot. Used internally when a single\n   * user's cache needs to be invalidated without affecting other\n   * persisted slots. The full-logout path is [[clearAll]].\n   */\n  clear(): void {\n    this.all = [];\n    this.lastUpdated = 0;\n    this.lastRefreshFailedAt = 0;\n    if (this.storage) {\n      try {\n        this.storage.removeItem(this.storageKey);\n      } catch {\n        // Private mode / quota — best-effort, same posture as storage.ts.\n      }\n    }\n    this.removeSuffixFromIndex(this.currentSuffix);\n    this.notify();\n  }\n\n  /**\n   * Logout-grade wipe — bank-grade contract: removes EVERY per-user\n   * entitlement slot the SDK has ever written on this device, then\n   * clears the index. Used by `Crossdeck.reset()` so a logout on a\n   * shared device can never leave another user's entitlements\n   * readable (layer (c) of the v1.4.0 isolation fix).\n   *\n   * After clearAll(), the cache is back to anonymous + empty.\n   */\n  clearAll(): void {\n    this.all = [];\n    this.lastUpdated = 0;\n    this.lastRefreshFailedAt = 0;\n    this.currentSuffix = ANON_SUFFIX;\n    if (this.storage) {\n      const suffixes = this.readIndex();\n      for (const suffix of suffixes) {\n        try {\n          this.storage.removeItem(`${this.storageKeyPrefix}:${suffix}`);\n        } catch {\n          // best-effort\n        }\n      }\n      // Also remove the anonymous slot explicitly — it may not have\n      // been indexed if the cache was wiped before its first write.\n      try {\n        this.storage.removeItem(`${this.storageKeyPrefix}:${ANON_SUFFIX}`);\n      } catch {\n        // best-effort\n      }\n      try {\n        this.storage.removeItem(this.indexKey);\n      } catch {\n        // best-effort\n      }\n    }\n    this.notify();\n  }\n\n  /**\n   * Subscribe to cache mutations. Returns an idempotent unsubscribe fn.\n   * The listener fires AFTER setFromList() or clear() with the current\n   * snapshot. Used by `@cross-deck/web/react`'s `useEntitlement` hook.\n   */\n  subscribe(listener: EntitlementsListener): () => void {\n    this.listeners.add(listener);\n    let unsubscribed = false;\n    return () => {\n      if (unsubscribed) return;\n      unsubscribed = true;\n      this.listeners.delete(listener);\n    };\n  }\n\n  // ----- Durable persistence -----\n\n  /**\n   * Load last-known-good from device storage. Runs once in the\n   * constructor, synchronously, so isEntitled() is correct from boot.\n   * Any corrupt / unparseable blob degrades silently to an empty cache —\n   * boot must never throw.\n   */\n  private hydrate(): void {\n    if (!this.storage) return;\n    try {\n      const raw = this.storage.getItem(this.storageKey);\n      if (!raw) return;\n      const parsed = JSON.parse(raw) as PersistedCache;\n      if (parsed && parsed.v === 1 && Array.isArray(parsed.entitlements)) {\n        this.all = parsed.entitlements;\n        this.lastUpdated =\n          typeof parsed.lastUpdated === \"number\" ? parsed.lastUpdated : 0;\n      }\n    } catch {\n      // Corrupt / unparseable blob → start empty. Never throw on boot.\n    }\n  }\n\n  /** Write last-known-good to device storage. Best-effort. */\n  private persist(): void {\n    if (!this.storage) return;\n    try {\n      const blob: PersistedCache = {\n        v: 1,\n        entitlements: this.all,\n        lastUpdated: this.lastUpdated,\n      };\n      this.storage.setItem(this.storageKey, JSON.stringify(blob));\n    } catch {\n      // Quota exceeded / private mode — the in-memory cache still works\n      // for this session; we just lose cross-reload durability.\n    }\n  }\n\n  /** Read the index of all per-user suffixes the SDK has written. */\n  private readIndex(): string[] {\n    if (!this.storage) return [];\n    try {\n      const raw = this.storage.getItem(this.indexKey);\n      if (!raw) return [];\n      const parsed = JSON.parse(raw);\n      if (Array.isArray(parsed)) {\n        return parsed.filter((x): x is string => typeof x === \"string\");\n      }\n      return [];\n    } catch {\n      return [];\n    }\n  }\n\n  /** Add a suffix to the persisted index. Idempotent. */\n  private recordSuffixInIndex(suffix: string): void {\n    if (!this.storage) return;\n    const existing = this.readIndex();\n    if (existing.includes(suffix)) return;\n    existing.push(suffix);\n    try {\n      this.storage.setItem(this.indexKey, JSON.stringify(existing));\n    } catch {\n      // best-effort\n    }\n  }\n\n  /** Remove a suffix from the persisted index. No-op if absent. */\n  private removeSuffixFromIndex(suffix: string): void {\n    if (!this.storage) return;\n    const existing = this.readIndex();\n    const next = existing.filter((s) => s !== suffix);\n    if (next.length === existing.length) return;\n    try {\n      if (next.length === 0) {\n        this.storage.removeItem(this.indexKey);\n      } else {\n        this.storage.setItem(this.indexKey, JSON.stringify(next));\n      }\n    } catch {\n      // best-effort\n    }\n  }\n\n  private notify(): void {\n    if (this.listeners.size === 0) return;\n    const snapshot = this.all.slice();\n    // Iterate a snapshot of the listener set so a listener that\n    // unsubscribes itself (or registers a new one) during dispatch\n    // doesn't break the iteration.\n    const listenersSnapshot = [...this.listeners];\n    for (const listener of listenersSnapshot) {\n      try {\n        listener(snapshot);\n      } catch {\n        // Swallow listener errors — a buggy consumer shouldn't break the\n        // SDK or other listeners. Counted for diagnostics().\n        this.listenerErrorCount += 1;\n      }\n    }\n  }\n}\n","/**\n * Deterministic Idempotency-Key derivation for /purchases/sync.\n *\n * Phase 2.2 of bank-grade reconciliation v1.4.0. Pre-v1.4.0 each\n * call minted a fresh random UUID — two retries of the same\n * purchase (network blip, app crash mid-flight, deliberate caller\n * retry) got DIFFERENT keys, so server-side idempotency could not\n * collapse them. The bank-grade contract every Stripe-grade API\n * ships: same input → same key → same response.\n *\n * Algorithm:\n *   1. Extract a stable identifier from the request body:\n *      - Apple: the signed JWS string (uniquely identifies the\n *        transaction by Apple's signature).\n *      - Google: the purchaseToken.\n *   2. SHA-256 the identifier.\n *   3. Format the first 32 hex chars of the digest as a UUID-shaped\n *      string (8-4-4-4-12). Backend treats the key as opaque so\n *      RFC 4122 version/variant bits are unnecessary — what matters\n *      is determinism.\n *\n * The resulting key is identical across retries of the same\n * transaction, so the backend's idempotency cache short-circuits\n * with `idempotent_replay: true` in the response.\n */\n\nimport { sha256Hex } from \"./hash\";\n\nexport interface PurchaseSyncIdentity {\n  rail: \"apple\" | \"google\" | \"stripe\" | string;\n  signedTransactionInfo?: string;\n  purchaseToken?: string;\n}\n\n/**\n * Format a hex string as `8-4-4-4-12` UUID shape using its first\n * 32 hex chars. Used for the idempotency-key derivation — the\n * shape matches what backend logs / dashboards already pattern-\n * match against; treating it as a UUID at the wire makes\n * inspection familiar even though it isn't RFC 4122 versioned.\n */\nexport function formatAsUuid(hex: string): string {\n  return [\n    hex.slice(0, 8),\n    hex.slice(8, 12),\n    hex.slice(12, 16),\n    hex.slice(16, 20),\n    hex.slice(20, 32),\n  ].join(\"-\");\n}\n\n/**\n * Deterministic Idempotency-Key for a purchase sync. Returns the\n * canonical UUID-shaped string the SDK sends as the\n * `Idempotency-Key` header.\n *\n * Same input → same key → backend returns the cached response\n * with `idempotent_replay: true` instead of double-processing.\n *\n * Throws on bodies that have no stable identifier — never silently\n * fall back to a fresh random key, since that would defeat the\n * very contract this helper exists to enforce.\n */\nexport function deriveIdempotencyKeyForPurchase(body: PurchaseSyncIdentity): string {\n  let identifier: string;\n  if (body.rail === \"apple\") {\n    identifier = body.signedTransactionInfo ?? \"\";\n  } else if (body.rail === \"google\") {\n    identifier = body.purchaseToken ?? \"\";\n  } else {\n    identifier = \"\";\n  }\n  if (!identifier) {\n    throw new Error(\n      `deriveIdempotencyKeyForPurchase: no stable identifier in body ` +\n        `(rail=${body.rail}). Apple needs signedTransactionInfo; ` +\n        `Google needs purchaseToken.`,\n    );\n  }\n  // Namespace the digest so the same JWS used by /purchases/sync\n  // and a hypothetical future /purchases/verify produce DIFFERENT\n  // keys — prevents cross-endpoint idempotency collisions.\n  const namespaced = `crossdeck:purchases/sync:${body.rail}:${identifier}`;\n  return formatAsUuid(sha256Hex(namespaced));\n}\n","/**\n * Retry policy for the event-queue flush.\n *\n * After a failed flush, the queue must wait some time before trying\n * again — otherwise a flapping backend causes a hot loop, and a 429\n * \"slow down\" goes ignored.\n *\n * Policy:\n *   - Exponential backoff: `base * 2^attempts`, capped at `maxMs`.\n *   - Full jitter: result is multiplied by Math.random() so 100 SDK\n *     instances retrying the same downed endpoint don't all hammer at\n *     the same instant. Spread the storm.\n *   - 429 / 503 `Retry-After`: ALWAYS honour the server-supplied delay\n *     when it's larger than our computed backoff. The server knows its\n *     own capacity better than we do; ignoring it is what gets your IP\n *     blocked.\n *   - Reset on success.\n *\n * The policy is a pure object — no state, no timers. The EventQueue\n * owns the timer, the policy owns the math.\n *\n * Default values match Stripe-JS-style retry windows:\n *   - baseMs: 1000  (first retry ~1s out)\n *   - maxMs: 60000  (never wait longer than 60s)\n *   - factor: 2     (1s, 2s, 4s, 8s, 16s, 32s, 60s, 60s, …)\n *\n * After `maxConsecutiveFailures` (default 8) without a success, the\n * caller is expected to surface that as a `lastError` for the\n * developer to see in diagnostics. We never stop retrying — events\n * matter and a transient outage can take hours — but we report it\n * clearly so the dev knows their data is queued, not lost.\n */\n\nexport interface RetryPolicyOptions {\n  baseMs?: number;\n  maxMs?: number;\n  factor?: number;\n  /** Number of consecutive failures before flagging diagnostics. Default 8. */\n  failuresBeforeWarn?: number;\n}\n\nconst DEFAULT_BASE = 1000;\nconst DEFAULT_MAX = 60_000;\nconst DEFAULT_FACTOR = 2;\nconst DEFAULT_WARN = 8;\n\n/**\n * Compute the next retry delay (ms) given the consecutive-failure\n * count and an optional server-supplied Retry-After (ms).\n *\n *   computeNextDelay(0, undefined)              → ~500ms (jittered 0-1000)\n *   computeNextDelay(3, undefined)              → ~4s    (jittered 0-8000)\n *   computeNextDelay(0, 30_000)                 → 30s    (server wins)\n *   computeNextDelay(8, undefined)              → 60s    (capped)\n *\n * Pure function — exported for testing. Real callers should go through\n * `RetryPolicy.nextDelay` so option defaults stay co-located.\n */\nexport function computeNextDelay(\n  attempts: number,\n  retryAfterMs: number | undefined,\n  options: RetryPolicyOptions = {},\n  random: () => number = Math.random,\n): number {\n  const base = options.baseMs ?? DEFAULT_BASE;\n  const max = options.maxMs ?? DEFAULT_MAX;\n  const factor = options.factor ?? DEFAULT_FACTOR;\n\n  // Cap attempts so 2^attempts doesn't overflow into Infinity.\n  const safeAttempts = Math.min(attempts, 30);\n  const ceiling = Math.min(max, base * Math.pow(factor, safeAttempts));\n  // Full jitter: random across [0, ceiling]. Caller can substitute a\n  // deterministic RNG for testing.\n  const jittered = ceiling * random();\n  // Honour server's Retry-After when bigger than our window — the\n  // server's the authority on its own pressure. Pre-fix this was\n  // clamped to `maxMs` (60s default), which meant a `Retry-After: 120`\n  // got truncated to 60s and we hammered an already-rate-limited\n  // endpoint twice as fast as it asked. Honour the server delay\n  // as-is, but cap at 24h as a final sanity guard against an absurd\n  // value (server bug / clock skew on an HTTP-date form) that would\n  // otherwise wedge the queue for years. RFC 7231 doesn't require\n  // honouring beyond that.\n  if (retryAfterMs !== undefined) {\n    const ABSOLUTE_MAX_MS = 24 * 60 * 60 * 1000; // 24h\n    const honoured = Math.min(ABSOLUTE_MAX_MS, retryAfterMs);\n    if (honoured > jittered) return honoured;\n  }\n  return Math.max(0, Math.round(jittered));\n}\n\nexport class RetryPolicy {\n  private attempts = 0;\n  constructor(private readonly options: RetryPolicyOptions = {}) {}\n\n  /** How many consecutive failures since the last success. */\n  get consecutiveFailures(): number {\n    return this.attempts;\n  }\n\n  /** Whether we've crossed the failuresBeforeWarn threshold. */\n  get isWarning(): boolean {\n    return this.attempts >= (this.options.failuresBeforeWarn ?? DEFAULT_WARN);\n  }\n\n  /** Schedule-time delay for the NEXT retry. Increments the counter. */\n  nextDelay(retryAfterMs?: number, random: () => number = Math.random): number {\n    const delay = computeNextDelay(this.attempts, retryAfterMs, this.options, random);\n    this.attempts += 1;\n    return delay;\n  }\n\n  /** Mark a successful flush — reset the counter. */\n  recordSuccess(): void {\n    this.attempts = 0;\n  }\n}\n","/**\n * Local event queue + batched flush.\n *\n * Why a queue: track() is called from hot paths (button clicks, screen\n * views) and shouldn't block the UI on a network round-trip. Events go\n * into a local buffer, flushed in bursts.\n *\n * Flush triggers:\n *   - Buffer reaches batchSize (default 20) → flush immediately.\n *   - intervalMs of inactivity (default 1500ms) → flush idle batch.\n *   - flush() called explicitly (e.g. before page unload).\n *\n * Wave 1 hardening (v0.8.0+, the \"bank-grade plumbing\" pass):\n *\n *   - Exponential backoff with full jitter on flush failures. Respects\n *     server `Retry-After` headers (parsed onto CrossdeckError by the\n *     HTTP layer). Replaces the prior policy of \"retry on the next\n *     idle window\" which hot-looped against a flapping endpoint.\n *\n *   - Durable persistence. Events are written through to a\n *     `PersistentEventStore` (localStorage by default) so a hard\n *     browser crash / power loss / keepalive cap exceedance doesn't\n *     drop data. The next SDK boot replays the persisted queue.\n *\n *   - Per-batch `Idempotency-Key`. Same key is reused across retries\n *     of the SAME batch so the server can short-circuit duplicate work\n *     without inspecting bodies. The backend ALSO dedupes individual\n *     events via CH ReplacingMergeTree on `eventId`, so this is belt-\n *     and-suspenders.\n *\n *   - Property validation runs upstream in crossdeck.ts:track() — by\n *     the time an event lands in this queue, it's known to be safe\n *     to JSON.stringify.\n *\n * On a permanent network outage we keep retrying with bounded backoff;\n * we never drop events because of network failures alone. The only\n * drop path is the hard buffer cap (1000 events): once exceeded we\n * evict the OLDEST events and increment `dropped` so the developer\n * can see the loss in `diagnostics()`.\n */\n\nimport type { HttpClient } from \"./http\";\nimport type { EventProperties, IngestResponse } from \"./types\";\nimport type { CrossdeckError } from \"./errors\";\nimport { RetryPolicy, type RetryPolicyOptions } from \"./retry-policy\";\nimport { PersistentEventStore } from \"./event-storage\";\nimport { randomChars } from \"./identity\";\n\nconst HARD_BUFFER_CAP = 1000;\n\nexport interface QueuedEvent {\n  eventId: string;\n  name: string;\n  timestamp: number;\n  /**\n   * Event Envelope v1 §3 — per-session monotonic sequence number.\n   * Assigned synchronously at track() time from the session-scoped counter\n   * (AutoTracker.nextSeq()), reset to 0 at session.started. The\n   * deterministic tiebreak for events sharing a timestamp. Persisted on\n   * disk alongside the event so a delayed flush across a\n   * background/foreground cycle keeps its original seq.\n   */\n  seq: number;\n  /**\n   * Event Envelope v1 §4 — standardized device/platform context,\n   * promoted OUT of `properties` into one named object. Keys: os,\n   * osVersion, appVersion, sdkName, sdkVersion, locale, timezone,\n   * browser, browserVersion. App-supplied properties stay in `properties`.\n   */\n  context: Record<string, string>;\n  properties: EventProperties;\n  // identity hint — at least anonymousId is always set\n  developerUserId?: string;\n  anonymousId?: string;\n  crossdeckCustomerId?: string;\n}\n\nexport interface BatchEnvelope {\n  appId: string;\n  environment: \"production\" | \"sandbox\";\n  sdk: { name: string; version: string };\n}\n\nexport interface EventQueueConfig {\n  http: HttpClient;\n  batchSize: number;\n  intervalMs: number;\n  /**\n   * Returns the NorthStar §13.1 envelope to attach to each batch POST.\n   * It's a function (not a value) so a future call to setDebugMode or a\n   * config swap can update the envelope without re-instantiating the\n   * queue.\n   */\n  envelope: () => BatchEnvelope;\n  /** Schedule a function to run after `ms` ms. Default: setTimeout. Override for tests. */\n  scheduler?: (fn: () => void, ms: number) => () => void;\n  /** Called when the SDK drops events because the buffer is full. */\n  onDrop?: (dropped: number) => void;\n  /** Called once after the first successful flush — drives the §16 \"First event sent\" signal. */\n  onFirstFlushSuccess?: () => void;\n  /**\n   * Durable persistence. When supplied, every buffer mutation is\n   * written through to the store; on construction, persisted events\n   * are loaded back into the buffer. Omitting this is fine for tests\n   * and Node consumers — the queue falls back to in-memory only.\n   */\n  persistentStore?: PersistentEventStore;\n  /** Retry policy overrides for failed flushes. */\n  retry?: RetryPolicyOptions;\n  /**\n   * Called whenever an item is added to the buffer or removed by a\n   * successful flush. Exposed so the host SDK can surface live queue\n   * stats via diagnostics() without polling.\n   */\n  onBufferChange?: (size: number) => void;\n  /**\n   * Surface for the SDK's debug logger to record retry scheduling +\n   * persistence events. Fired async — never throws.\n   */\n  onRetryScheduled?: (info: {\n    delayMs: number;\n    consecutiveFailures: number;\n    retryAfterMs?: number;\n    lastError: string;\n  }) => void;\n  /**\n   * Fired when the queue DROPS a batch because the server returned a\n   * permanent 4xx (anything except 408 Request Timeout / 429 Too Many\n   * Requests). The host SDK should surface this loudly — pre-fix the\n   * queue retried 4xx errors forever with the same Idempotency-Key,\n   * silently growing the backlog while the customer thought events\n   * were landing. Common causes:\n   *   - 401: publishable key revoked / rotated\n   *   - 403: lacking permission for the project\n   *   - 400/422: malformed batch (schema mismatch, oversized event)\n   *   - 404: endpoint doesn't exist (typo'd baseUrl)\n   */\n  onPermanentFailure?: (info: {\n    status: number;\n    droppedCount: number;\n    lastError: string;\n  }) => void;\n  /**\n   * Fired when the server PARKS this SDK with HTTP 426 Upgrade Required\n   * (code `sdk_version_unsupported`) — the SDK's wire dialect is too old\n   * for the server to accept. This is the third outcome, distinct from\n   * retry (transient) and drop (invalid): the data is GOOD, only the\n   * format is stale. The queue holds the events durably, stops flushing\n   * (the same payload will fail until the app upgrades the SDK), and on\n   * the next boot AFTER upgrade the rehydrated queue delivers them — so\n   * \"paused, not lost — held on-device, resumes on upgrade\" is literally\n   * true. The host SDK surfaces this to the developer's own console once\n   * and to the dashboard (via the heartbeat) so both channels carry the\n   * same cure. `minVersion` is the server's required floor when supplied.\n   */\n  onParked?: (info: { minVersion?: string; surface?: string }) => void;\n}\n\nexport interface EventQueueStats {\n  buffered: number;\n  dropped: number;\n  inFlight: number;\n  lastFlushAt: number;\n  lastError: string | null;\n  /** Consecutive flush failures since the last success. */\n  consecutiveFailures: number;\n  /** Set when the next flush is scheduled by the retry policy. */\n  nextRetryAt: number | null;\n}\n\nexport class EventQueue {\n  private buffer: QueuedEvent[] = [];\n  /**\n   * In-flight events that have been spliced from `buffer` for the\n   * current batch but haven't yet been confirmed (success or permanent\n   * failure). On a retry-driven re-flush we re-use this slot alongside\n   * `pendingBatchId` so the Stripe-style Idempotency-Key is preserved\n   * across retries of the SAME logical batch.\n   *\n   * Pre-fix the splice cleared the buffer AND we immediately\n   * `persistent.save(empty)` BEFORE awaiting the network call — a\n   * crash mid-flight wiped the persisted blob and the batch was lost\n   * with no replay on next boot. Now the persisted blob always carries\n   * `[...pendingBatch, ...buffer]` so the in-flight events survive\n   * until the server confirms them.\n   *\n   * Pre-fix every retry attempt also minted a NEW batchId via\n   * `splice + mintBatchId`, defeating the backend's\n   * `Idempotency-Key` dedup. Reuse via this slot brings web in\n   * lockstep with node (which already had the field).\n   */\n  private pendingBatch: QueuedEvent[] | null = null;\n  private pendingBatchId: string | null = null;\n  private dropped = 0;\n  private inFlight = 0;\n  private lastFlushAt = 0;\n  private lastError: string | null = null;\n  private cancelTimer: (() => void) | null = null;\n  private firstFlushFired = false;\n  private nextRetryAt: number | null = null;\n  /**\n   * PARK state (HTTP 426 / `sdk_version_unsupported`). Once parked, the\n   * queue stops flushing — the server has told us our wire dialect is too\n   * old, and retrying the same payload only burns the user's battery and\n   * bandwidth and drips pointless rejects into the server logs until the\n   * app ships an upgraded SDK. The held events stay durable (persisted)\n   * and are delivered by the next boot's rehydrate, post-upgrade. Parked\n   * is per-instance: a fresh boot (the upgraded build) starts unparked.\n   */\n  private parked = false;\n  /** One developer-facing console warning per instance — never per-event spam. */\n  private parkWarned = false;\n  private readonly retry: RetryPolicy;\n  private readonly persistent: PersistentEventStore | null;\n\n  constructor(private readonly cfg: EventQueueConfig) {\n    this.retry = new RetryPolicy(cfg.retry ?? {});\n    this.persistent = cfg.persistentStore ?? null;\n\n    // Rehydrate any events left over from a prior session (crash, hard\n    // close, keepalive cap exceeded). Eventid-based dedup at the server\n    // means re-sending an event that may have already landed is safe.\n    // Rehydrated events land in `buffer` — the \"was in-flight\" axis\n    // doesn't survive a crash, so they're treated like fresh enqueues.\n    if (this.persistent) {\n      const restored = this.persistent.load();\n      if (restored.length > 0) {\n        // Apply the same hard cap on rehydrate — defends against a\n        // malicious / corrupted blob with a million entries.\n        if (restored.length > HARD_BUFFER_CAP) {\n          this.dropped += restored.length - HARD_BUFFER_CAP;\n          this.buffer = restored.slice(restored.length - HARD_BUFFER_CAP);\n        } else {\n          this.buffer = restored;\n        }\n        this.cfg.onBufferChange?.(this.buffer.length);\n        // Schedule an immediate idle flush so rehydrated events land\n        // on the next tick — even if no new track() call comes in.\n        this.scheduleIdleFlush();\n      }\n    }\n  }\n\n  enqueue(event: QueuedEvent): void {\n    this.buffer.push(event);\n    if (this.buffer.length > HARD_BUFFER_CAP) {\n      const overflow = this.buffer.length - HARD_BUFFER_CAP;\n      this.buffer.splice(0, overflow);\n      this.dropped += overflow;\n      this.cfg.onDrop?.(overflow);\n    }\n    this.cfg.onBufferChange?.(this.buffer.length);\n    this.persistAll();\n    if (this.buffer.length >= this.cfg.batchSize) {\n      void this.flush();\n    } else {\n      this.scheduleIdleFlush();\n    }\n  }\n\n  /**\n   * Flush the buffer to /v1/events. Resolves when the network call\n   * completes (success or failure). On a retryable failure the batch\n   * stays in `pendingBatch` for the next scheduled retry — the SAME\n   * batch with the SAME `Idempotency-Key` is re-sent (Stripe pattern).\n   *\n   * Three terminal states from one call:\n   *   - 2xx success: pendingBatch cleared, persisted state collapses to\n   *     just `buffer` (any new events that arrived during in-flight).\n   *   - 4xx permanent (except 408/429): pendingBatch DROPPED, `dropped`\n   *     counter increments, a `permanent_failure` signal fires. The\n   *     server is telling us our request is malformed / our key is\n   *     revoked / we lack permission — retrying with the same key\n   *     forever just grows the queue while the customer thinks events\n   *     are landing.\n   *   - 5xx / network / 408 / 429: pendingBatch + batchId stay; backoff\n   *     schedules a retry; the next `flush()` re-uses both.\n   *\n   * `options.keepalive` marks the underlying fetch as keepalive so the\n   * browser keeps the request alive past page unload. Use for terminal\n   * flushes (pagehide / visibilitychange→hidden / beforeunload).\n   */\n  async flush(options: { keepalive?: boolean } = {}): Promise<IngestResponse | null> {\n    // PARK hush: once the server has rejected our wire dialect as too old\n    // (426), every flush of the same-format payload will fail identically.\n    // Hold — don't flush — until the next boot on an upgraded SDK. The\n    // events stay buffered + persisted, so they deliver on that boot.\n    if (this.parked) return null;\n    // Resume an in-flight batch retry path: if we already have a\n    // pending batch (prior flush failed, retry timer / caller is\n    // re-invoking), re-attempt with the SAME batchId. Stripe\n    // Idempotency-Key reuse contract.\n    let batch: QueuedEvent[];\n    let batchId: string;\n    if (this.pendingBatch !== null && this.pendingBatchId !== null) {\n      batch = this.pendingBatch;\n      batchId = this.pendingBatchId;\n    } else {\n      if (this.buffer.length === 0) return null;\n      batch = this.buffer.splice(0);\n      batchId = this.mintBatchId();\n      this.pendingBatch = batch;\n      this.pendingBatchId = batchId;\n      this.inFlight += batch.length;\n      this.cfg.onBufferChange?.(this.buffer.length);\n      // Persisted state continues to include this batch via persistAll()\n      // until the server confirms it — that's the durability fix.\n      this.persistAll();\n    }\n    this.cancelTimerIfSet();\n    this.nextRetryAt = null;\n\n    try {\n      const env = this.cfg.envelope();\n      const result = await this.cfg.http.request<IngestResponse>(\"POST\", \"/events\", {\n        body: {\n          // Event Envelope v1 batch envelope (backend/docs/\n          // event-envelope-spec-v1.md §1). `envelopeVersion` is the\n          // schema/wire version the server parses against (\"can I parse\n          // this?\") and is DISTINCT from `sdk.version` (\"which build is in\n          // the wild?\") — two questions, two fields, never conflated. The\n          // backend refuses payloads with a missing/unknown envelopeVersion.\n          envelopeVersion: 1,\n          appId: env.appId,\n          environment: env.environment,\n          sdk: env.sdk,\n          events: batch,\n        },\n        keepalive: options.keepalive === true,\n        idempotencyKey: batchId,\n      });\n      this.lastFlushAt = Date.now();\n      this.lastError = null;\n      this.inFlight -= batch.length;\n      this.pendingBatch = null;\n      this.pendingBatchId = null;\n      this.retry.recordSuccess();\n      // Persisted blob collapses to just `buffer` (which may include\n      // new enqueues that arrived while this batch was in flight).\n      this.persistAll();\n      if (!this.firstFlushFired) {\n        this.firstFlushFired = true;\n        this.cfg.onFirstFlushSuccess?.();\n      }\n      return result;\n    } catch (err) {\n      const message = err instanceof Error ? err.message : String(err);\n      this.lastError = message;\n\n      // PARK (HTTP 426 / `sdk_version_unsupported`) — the THIRD outcome.\n      // Checked BEFORE the permanent-4xx drop so a version-rejection never\n      // falls into the drop path. The data is good; only the dialect is\n      // old. Keep every held event — fold the in-flight batch back to the\n      // FRONT of the buffer (oldest-first) so it persists and the next\n      // (upgraded) boot's rehydrate delivers it — then hush (no retry\n      // scheduled; the flush guard above blocks further attempts).\n      if (isVersionRejected(err)) {\n        this.parked = true;\n        this.buffer = [...batch, ...this.buffer];\n        if (this.buffer.length > HARD_BUFFER_CAP) {\n          const overflow = this.buffer.length - HARD_BUFFER_CAP;\n          this.buffer.splice(0, overflow); // FIFO: evict oldest, keep newest 1000\n          this.dropped += overflow;\n        }\n        this.pendingBatch = null;\n        this.pendingBatchId = null;\n        this.inFlight -= batch.length;\n        this.persistAll();\n        this.cfg.onBufferChange?.(this.buffer.length);\n        const minVersion = versionRejectionFloor(err);\n        if (!this.parkWarned) {\n          this.parkWarned = true;\n          // ONE developer-facing console line — the terminal-side cure that\n          // reaches them mid-debug, paired with the dashboard banner.\n          console.warn(\n            \"[Crossdeck] SDK outdated — the server is no longer accepting this \" +\n              \"version's event format. Your events are PARKED on-device (held, \" +\n              \"not lost) and will deliver automatically once you update \" +\n              `@cross-deck/web${minVersion ? ` to >= ${minVersion}` : \"\"} and redeploy.`,\n          );\n        }\n        this.cfg.onParked?.({ minVersion, surface: versionRejectionSurface(err) });\n        return null;\n      }\n\n      // Permanent failures (4xx except 408/429) are NOT retryable. The\n      // server is telling us our request is malformed (400/422), our\n      // key is revoked (401), we lack permission (403), or the endpoint\n      // doesn't exist (404). Retrying with the same Idempotency-Key\n      // forever just grows the queue silently while the customer\n      // thinks events are landing. Drop the batch loudly.\n      if (isPermanent4xx(err)) {\n        const droppedCount = batch.length;\n        this.pendingBatch = null;\n        this.pendingBatchId = null;\n        this.inFlight -= droppedCount;\n        this.dropped += droppedCount;\n        this.persistAll();\n        this.cfg.onDrop?.(droppedCount);\n        this.cfg.onPermanentFailure?.({\n          status: (err as { status?: number }).status ?? 0,\n          droppedCount,\n          lastError: message,\n        });\n        return null;\n      }\n\n      // Retryable failure (5xx / network / 408 / 429). pendingBatch +\n      // pendingBatchId stay set; the next scheduler-driven flush re-uses\n      // both. Persisted state is unchanged from the entry path — it\n      // still includes `[...pendingBatch, ...buffer]`.\n      const retryAfterMs = extractRetryAfterMs(err);\n      const delay = this.retry.nextDelay(retryAfterMs);\n      this.scheduleRetry(delay);\n      this.cfg.onRetryScheduled?.({\n        delayMs: delay,\n        consecutiveFailures: this.retry.consecutiveFailures,\n        retryAfterMs,\n        lastError: message,\n      });\n      return null;\n    }\n  }\n\n  /** Cancel any pending timer and clear in-memory state. Wipes durable store too. */\n  reset(): void {\n    this.cancelTimerIfSet();\n    this.nextRetryAt = null;\n    this.buffer = [];\n    this.pendingBatch = null;\n    this.pendingBatchId = null;\n    this.dropped = 0;\n    this.inFlight = 0;\n    this.lastError = null;\n    this.retry.recordSuccess();\n    this.persistent?.clear();\n    this.cfg.onBufferChange?.(0);\n    // Note: we deliberately do NOT reset firstFlushFired — the\n    // \"First event sent\" signal is a one-time onboarding moment per\n    // SDK instance lifetime, not per-identity.\n  }\n\n  getStats(): EventQueueStats {\n    return {\n      // `buffered` counts events waiting for their FIRST flush. The\n      // in-flight pendingBatch (retrying) is tracked separately via\n      // `inFlight` — surfacing both lets diagnostics show \"we have\n      // events stuck retrying\" distinct from \"new events arriving\".\n      buffered: this.buffer.length,\n      dropped: this.dropped,\n      inFlight: this.inFlight,\n      lastFlushAt: this.lastFlushAt,\n      lastError: this.lastError,\n      consecutiveFailures: this.retry.consecutiveFailures,\n      nextRetryAt: this.nextRetryAt,\n    };\n  }\n\n  /**\n   * The Idempotency-Key of the in-flight pending batch (if any).\n   * Exposed for testing the Stripe-style retry-reuse contract.\n   * Production callers don't need this.\n   */\n  get pendingIdempotencyKey(): string | null {\n    return this.pendingBatchId;\n  }\n\n  /**\n   * Persist `[...pendingBatch, ...buffer]` — the full set of\n   * not-yet-confirmed events. The next boot rehydrates this exact set\n   * into `buffer` and replays. The server dedups via eventId\n   * (ReplacingMergeTree on the warehouse side), so re-sending an event\n   * that may have already landed is safe.\n   */\n  private persistAll(): void {\n    if (!this.persistent) return;\n    if (this.pendingBatch === null) {\n      this.persistent.save(this.buffer);\n      return;\n    }\n    this.persistent.save([...this.pendingBatch, ...this.buffer]);\n  }\n\n  // ---------- internal scheduling ----------\n\n  private scheduleIdleFlush(): void {\n    this.cancelTimerIfSet();\n    const sched = this.cfg.scheduler ?? defaultScheduler;\n    this.cancelTimer = sched(() => {\n      void this.flush();\n    }, this.cfg.intervalMs);\n  }\n\n  private scheduleRetry(delayMs: number): void {\n    this.cancelTimerIfSet();\n    this.nextRetryAt = Date.now() + delayMs;\n    const sched = this.cfg.scheduler ?? defaultScheduler;\n    this.cancelTimer = sched(() => {\n      void this.flush();\n    }, delayMs);\n  }\n\n  private cancelTimerIfSet(): void {\n    if (this.cancelTimer) {\n      this.cancelTimer();\n      this.cancelTimer = null;\n    }\n  }\n\n  private mintBatchId(): string {\n    return `batch_${Date.now().toString(36)}${randomChars(10)}`;\n  }\n}\n\nfunction extractRetryAfterMs(err: unknown): number | undefined {\n  if (err && typeof err === \"object\" && \"retryAfterMs\" in err) {\n    const v = (err as CrossdeckError).retryAfterMs;\n    return typeof v === \"number\" && Number.isFinite(v) && v >= 0 ? v : undefined;\n  }\n  return undefined;\n}\n\n/**\n * True when the error represents a permanent 4xx response that\n * SHOULDN'T be retried. Excludes 408 Request Timeout and 429 Too Many\n * Requests — both indicate transient state where the SAME request\n * (with the SAME Idempotency-Key) can succeed on a retry.\n *\n * Anything that isn't a CrossdeckError-shaped object with a numeric\n * status field returns false (network errors / fetch failures fall\n * here — those ARE retryable). Conservative default: only flag as\n * permanent when we have strong evidence from the server.\n */\nfunction isPermanent4xx(err: unknown): boolean {\n  if (!err || typeof err !== \"object\") return false;\n  const status = (err as { status?: unknown }).status;\n  if (typeof status !== \"number\" || !Number.isFinite(status)) return false;\n  if (status < 400 || status >= 500) return false;\n  if (status === 408 || status === 429) return false;\n  // 426 is PARK, never drop — handled by isVersionRejected before this is\n  // ever reached, but excluded here too so no path can drop a parkable batch.\n  if (status === 426) return false;\n  return true;\n}\n\n/**\n * True when the server PARKED this SDK: HTTP 426 Upgrade Required, the\n * status code invented for exactly this — \"your client is too old.\"\n * Unambiguous (no proxy or middleware emits 426 spuriously, unlike\n * 400/422), and corroborated by the machine code `sdk_version_unsupported`\n * so a status-rewriting layer can't hide it. Distinct from a permanent\n * 4xx drop: the data is good, only the wire dialect is stale.\n */\nfunction isVersionRejected(err: unknown): boolean {\n  if (!err || typeof err !== \"object\") return false;\n  if ((err as { status?: unknown }).status === 426) return true;\n  return (err as { code?: unknown }).code === \"sdk_version_unsupported\";\n}\n\n/** Server-supplied required version floor from the 426 body, if present. */\nfunction versionRejectionFloor(err: unknown): string | undefined {\n  const v = (err as { minVersion?: unknown } | null)?.minVersion;\n  return typeof v === \"string\" && v.length > 0 ? v : undefined;\n}\n\n/** Server-supplied surface id from the 426 body, if present. */\nfunction versionRejectionSurface(err: unknown): string | undefined {\n  const v = (err as { surface?: unknown } | null)?.surface;\n  return typeof v === \"string\" && v.length > 0 ? v : undefined;\n}\n\nfunction defaultScheduler(fn: () => void, ms: number): () => void {\n  // Use unref()-style behaviour where supported so a pending flush doesn't\n  // block Node from exiting. setTimeout in browsers ignores .unref() —\n  // that's fine.\n  const id = setTimeout(fn, ms);\n  if (typeof (id as unknown as { unref?: () => void }).unref === \"function\") {\n    try {\n      (id as unknown as { unref: () => void }).unref();\n    } catch {\n      // ignore — unref is best-effort\n    }\n  }\n  return () => clearTimeout(id);\n}\n","/**\n * Durable event-queue persistence.\n *\n * Why this exists: the in-memory event-queue is fragile. Three failure\n * modes lose data today:\n *\n *   1. Page unload with a failed terminal flush. `keepalive: true`\n *      survives the unload, but only up to 64 KB total across all\n *      keepalive requests. A large batch on a slow network drops past\n *      that cap.\n *   2. Hard browser crash / power loss. The in-memory buffer goes with\n *      the process.\n *   3. Network down for longer than the user's session. Events that\n *      stay in the in-memory buffer disappear when the tab closes.\n *\n * Stripe / Segment / PostHog all persist queued events to a durable\n * store (localStorage for browsers, IndexedDB for very large queues,\n * AsyncStorage on RN) and replay them on the next boot. We do the\n * same here with localStorage as the default backing store.\n *\n * Failure modes handled gracefully:\n *   - Storage throws (quota exceeded, private mode, sandboxed iframe)\n *     → silent degrade to in-memory only. The SDK keeps working; the\n *     durability guarantee is best-effort.\n *   - Persisted blob unparseable on next boot (manual corruption,\n *     schema drift) → drop silently, fresh empty queue. Don't crash\n *     the consumer app on a bad localStorage value.\n *   - Storage write contention from another tab → last-writer-wins is\n *     fine because every queued event has an `eventId` and the\n *     backend dedupes via ReplacingMergeTree. Cross-tab coordination\n *     via BroadcastChannel is a Phase 2 follow-up.\n *\n * The storage key is `${prefix}queue.v1` to leave room for future\n * format migrations.\n */\n\nimport type { KeyValueStorage } from \"./types\";\nimport type { QueuedEvent } from \"./event-queue\";\n\nexport interface PersistentEventStoreOptions {\n  storage: KeyValueStorage;\n  prefix: string;\n}\n\n/**\n * Wire format for persisted batches. Versioned so a future change to\n * QueuedEvent shape can be detected + ignored cleanly.\n */\ninterface PersistedQueue {\n  version: 1;\n  events: QueuedEvent[];\n}\n\nexport class PersistentEventStore {\n  private readonly key: string;\n  private writeScheduled = false;\n  // Pending events captured on the most recent write request. We keep\n  // the latest snapshot ref so a debounced write always picks up the\n  // freshest buffer state.\n  private pendingSnapshot: QueuedEvent[] | null = null;\n\n  constructor(private readonly options: PersistentEventStoreOptions) {\n    this.key = `${options.prefix}queue.v1`;\n  }\n\n  /**\n   * Read the persisted queue on boot. Returns an empty array (with no\n   * warning) when nothing is stored, the blob is malformed, or storage\n   * is unavailable. Caller is responsible for treating duplicates from\n   * the persisted queue as the SAME events (eventId-based dedup).\n   */\n  load(): QueuedEvent[] {\n    let raw: string | null;\n    try {\n      raw = this.options.storage.getItem(this.key);\n    } catch {\n      return [];\n    }\n    if (!raw) return [];\n    try {\n      const parsed = JSON.parse(raw) as PersistedQueue;\n      if (!parsed || parsed.version !== 1 || !Array.isArray(parsed.events)) {\n        return [];\n      }\n      return parsed.events;\n    } catch {\n      // Corrupt blob — drop silently. Next save() overwrites.\n      return [];\n    }\n  }\n\n  /**\n   * Schedule a write of the current buffer. Debounced via microtask so\n   * a burst of enqueue() calls coalesces into one persistence write.\n   * Writes are best-effort: if storage throws (quota, private mode),\n   * we swallow and rely on the in-memory buffer.\n   */\n  save(snapshot: readonly QueuedEvent[]): void {\n    // Defensive copy so a later mutation of the buffer doesn't change\n    // what we're about to persist.\n    this.pendingSnapshot = snapshot.slice();\n    if (this.writeScheduled) return;\n    this.writeScheduled = true;\n    queueMicrotask(() => this.flushWrite());\n  }\n\n  /** Synchronous variant for terminal flushes (pagehide / beforeunload). */\n  saveSync(snapshot: readonly QueuedEvent[]): void {\n    this.pendingSnapshot = snapshot.slice();\n    this.flushWrite();\n  }\n\n  /** Wipe the persisted blob. Used by reset() (logout). */\n  clear(): void {\n    this.pendingSnapshot = null;\n    this.writeScheduled = false;\n    try {\n      this.options.storage.removeItem(this.key);\n    } catch {\n      // ignore\n    }\n  }\n\n  private flushWrite(): void {\n    this.writeScheduled = false;\n    const snapshot = this.pendingSnapshot;\n    this.pendingSnapshot = null;\n    if (snapshot === null) return;\n\n    if (snapshot.length === 0) {\n      try {\n        this.options.storage.removeItem(this.key);\n      } catch {\n        // ignore\n      }\n      return;\n    }\n\n    const blob: PersistedQueue = { version: 1, events: snapshot };\n    try {\n      this.options.storage.setItem(this.key, JSON.stringify(blob));\n    } catch {\n      // Quota exceeded / private mode / etc. — silent degrade. The\n      // in-memory buffer is still authoritative; we just lose\n      // crash-safety for this batch.\n    }\n  }\n}\n","/**\n * Storage adapters for SDK-persisted state.\n *\n * Three flavours:\n *   - browser localStorage (default in browsers)\n *   - 1st-party document.cookie (redundancy for cleared localStorage)\n *   - in-memory (default in Node, or as an explicit fallback)\n *\n * Detection is at construction time, not at every call — picking the\n * adapter once means we don't hit `typeof window` checks on hot paths.\n *\n * ----- Bank-grade identity continuity -----\n *\n * Plain localStorage is not enough. ITP, private browsing, \"clear site\n * data\" actions, and aggressive privacy extensions all wipe it. When\n * that happens, the SDK mints a fresh anonymousId on next page load\n * and the customer's analytics see one human as multiple \"new\n * visitors\" — a credibility hit on every dashboard chart that depends\n * on visitor uniqueness (new vs returning, retention, funnels).\n *\n * The fix is redundancy: we write the same identity to BOTH\n * localStorage AND a 1st-party cookie. On boot we read both; whichever\n * survived wins. On set, we write to both stores so a future clear of\n * either doesn't lose the user.\n *\n * Caveats (documented honestly):\n *   1. Safari ITP caps client-set 1st-party cookies at 7 days. Cookie\n *      redundancy protects against localStorage clears WITHIN that\n *      7-day window, not beyond it. The full ITP-bypass story (server-\n *      set cookies via a customer-CNAMEd subdomain) is a Phase 2\n *      follow-up that requires customer DNS configuration.\n *   2. We never write fingerprintable data — only the same anonymousId\n *      already in localStorage. Privacy posture is unchanged from\n *      single-store identity.\n *   3. `persistIdentity: false` disables BOTH stores so customers\n *      running strict consent flows can defer cookie writes until the\n *      user opts in.\n */\n\nimport type { KeyValueStorage } from \"./types\";\n\n/**\n * In-memory storage. Cleared on process exit. Useful for Node runtimes\n * where you want session-scoped identity that doesn't persist to disk.\n */\nexport class MemoryStorage implements KeyValueStorage {\n  private store = new Map<string, string>();\n  getItem(key: string): string | null {\n    return this.store.get(key) ?? null;\n  }\n  setItem(key: string, value: string): void {\n    this.store.set(key, value);\n  }\n  removeItem(key: string): void {\n    this.store.delete(key);\n  }\n}\n\n/**\n * 1st-party cookie storage. All writes set:\n *   - Path=/                — visible site-wide so SPA route changes\n *                             keep the same identity\n *   - Max-Age=63072000      — 2 years (clamped to 7 days by Safari ITP\n *                             but written long anyway for non-ITP UAs)\n *   - SameSite=Lax          — standard for 1st-party identity, blocks\n *                             cross-site request abuse but allows\n *                             top-level navigation reads\n *   - Secure                — only set when page is served over HTTPS;\n *                             omitted on http://localhost so dev still\n *                             works without a TLS cert\n *\n * We do NOT set HttpOnly because the SDK itself needs to read the\n * cookie via document.cookie to honour the redundancy contract. That\n * means malicious JS on the same origin could read the anonymousId,\n * which is the same security posture as localStorage — anything that\n * can read localStorage can read this cookie. Stripe, Segment, and\n * PostHog ship with the same trade-off for the same reason.\n *\n * Empty / unparseable cookie strings degrade silently to null. We never\n * throw — a broken cookie should look identical to \"no cookie set.\"\n */\nexport class CookieStorage implements KeyValueStorage {\n  private readonly maxAgeSec: number;\n  private readonly secure: boolean;\n  private readonly sameSite: \"Lax\" | \"Strict\" | \"None\";\n  /**\n   * Cookie `Domain=` (e.g. `.cross-deck.com`). When set, the cookie is shared\n   * across every SUBDOMAIN of that registrable domain — so a visitor who lands\n   * on the marketing site (`cross-deck.com`) and then the app (`app.cross-deck.com`)\n   * is ONE anonymous person, not two. Empty → host-only (the previous behaviour).\n   */\n  private readonly domain: string | undefined;\n\n  constructor(options?: {\n    maxAgeSec?: number;\n    secure?: boolean;\n    sameSite?: \"Lax\" | \"Strict\" | \"None\";\n    domain?: string;\n  }) {\n    this.maxAgeSec = options?.maxAgeSec ?? 63_072_000; // 2 years\n    this.secure = options?.secure ?? defaultSecure();\n    this.sameSite = options?.sameSite ?? \"Lax\";\n    this.domain = options?.domain || undefined;\n  }\n\n  getItem(key: string): string | null {\n    if (!hasDocument()) return null;\n    const doc = (globalThis as { document: Document }).document;\n    const cookies = doc.cookie ? doc.cookie.split(/;\\s*/) : [];\n    const prefix = encodeURIComponent(key) + \"=\";\n    for (const c of cookies) {\n      if (c.startsWith(prefix)) {\n        try {\n          return decodeURIComponent(c.slice(prefix.length));\n        } catch {\n          return null;\n        }\n      }\n    }\n    return null;\n  }\n\n  setItem(key: string, value: string): void {\n    if (!hasDocument()) return;\n    const doc = (globalThis as { document: Document }).document;\n    const parts = [\n      `${encodeURIComponent(key)}=${encodeURIComponent(value)}`,\n      \"Path=/\",\n      `Max-Age=${this.maxAgeSec}`,\n      `SameSite=${this.sameSite}`,\n    ];\n    if (this.domain) parts.push(`Domain=${this.domain}`);\n    if (this.secure) parts.push(\"Secure\");\n    try {\n      doc.cookie = parts.join(\"; \");\n    } catch {\n      // Some embedded webviews block document.cookie writes — swallow.\n      // localStorage redundancy still gives us identity continuity.\n    }\n  }\n\n  removeItem(key: string): void {\n    if (!hasDocument()) return;\n    const doc = (globalThis as { document: Document }).document;\n    // Negative Max-Age + matching path expires the cookie immediately.\n    // We keep the same SameSite/Secure attributes so browsers actually\n    // accept the deletion request as targeting the same cookie.\n    const parts = [\n      `${encodeURIComponent(key)}=`,\n      \"Path=/\",\n      \"Max-Age=0\",\n      `SameSite=${this.sameSite}`,\n    ];\n    if (this.domain) parts.push(`Domain=${this.domain}`);\n    if (this.secure) parts.push(\"Secure\");\n    try {\n      doc.cookie = parts.join(\"; \");\n    } catch {\n      // Same reasoning as setItem — swallow.\n    }\n  }\n}\n\n/**\n * Resolve the `cookieDomain` option into a concrete `Domain=` value (or undefined\n * for host-only). This is what makes marketing↔app one identity across subdomains.\n *\n *   - a concrete string (\"cross-deck.com\" / \".cross-deck.com\") → normalised to a\n *     leading-dot domain and used as-is (the customer told us their domain).\n *   - \"auto\" (the default) → the registrable domain (eTLD+1), found the GA4 way:\n *     walk from the broadest 2-label candidate up, set a throwaway test cookie at\n *     each, and take the BROADEST the browser actually accepts. A public suffix\n *     (`.co.za`, `.com`) is rejected by the browser, so the walk naturally skips\n *     it and lands on the true registrable domain — no bundled public-suffix list.\n *   - undefined / \"none\" / localhost / a bare IP → host-only (no cross-subdomain).\n */\nexport function resolveCookieDomain(config?: string): string | undefined {\n  if (config === undefined || config === \"none\" || config === \"\") return undefined;\n  if (config !== \"auto\") {\n    const d = config.trim().toLowerCase();\n    return d.startsWith(\".\") ? d : `.${d}`;\n  }\n  // \"auto\"\n  if (!hasDocument()) return undefined;\n  const doc = (globalThis as { document: Document }).document;\n  const loc = (globalThis as { location?: { hostname?: string } }).location;\n  const host = (loc?.hostname ?? \"\").toLowerCase();\n  if (!host || host === \"localhost\" || /^\\d{1,3}(\\.\\d{1,3}){3}$/.test(host) || host.indexOf(\".\") === -1) {\n    return undefined; // localhost / IP / single-label → no domain cookie possible\n  }\n  const labels = host.split(\".\");\n  for (let i = labels.length - 2; i >= 0; i--) {\n    const candidate = \".\" + labels.slice(i).join(\".\");\n    const testKey = \"__cd_domain_probe\";\n    try {\n      doc.cookie = `${testKey}=1; Domain=${candidate}; Path=/; SameSite=Lax`;\n      const accepted = doc.cookie.indexOf(`${testKey}=1`) !== -1;\n      // Always clean up the probe.\n      doc.cookie = `${testKey}=; Domain=${candidate}; Path=/; Max-Age=0; SameSite=Lax`;\n      if (accepted) return candidate; // broadest domain the browser allowed\n    } catch {\n      // document.cookie blocked (sandboxed) → give up, host-only.\n      return undefined;\n    }\n  }\n  return undefined;\n}\n\n/**\n * Pick the best available storage. Browser → localStorage if accessible,\n * else MemoryStorage. Node → MemoryStorage. Caller can override via\n * Crossdeck.start({ storage: ... }) for custom adapters (RN AsyncStorage,\n * Cookies, encrypted vaults, etc.).\n *\n * We probe localStorage with a try/catch because some environments\n * (private mode Safari, embedded webviews) define `localStorage` but\n * throw on every call — falling back to memory keeps us correct.\n */\nexport function detectDefaultStorage(): KeyValueStorage {\n  try {\n    const ls = (globalThis as { localStorage?: KeyValueStorage }).localStorage;\n    if (ls) {\n      // Probe with a no-op write to confirm we can actually use it.\n      const probe = \"__crossdeck_probe__\";\n      ls.setItem(probe, \"1\");\n      ls.removeItem(probe);\n      return ls;\n    }\n  } catch {\n    // Private mode / sandboxed iframe / quota exceeded — fall through.\n  }\n  return new MemoryStorage();\n}\n\n/**\n * Detect whether the current page is served over HTTPS so we can set\n * the Secure cookie attribute. Defensive against environments where\n * `location` is missing (Workers, server-rendered pre-hydration).\n */\nfunction defaultSecure(): boolean {\n  try {\n    const loc = (globalThis as { location?: Location }).location;\n    return loc?.protocol === \"https:\";\n  } catch {\n    return false;\n  }\n}\n\nfunction hasDocument(): boolean {\n  return typeof (globalThis as { document?: unknown }).document !== \"undefined\";\n}\n","/**\n * Read-cost bridge — the one-way seam from the Crossdeck SDK to the Buckets OSS\n * collector (`@cross-deck/buckets`), without either package depending on the other.\n *\n * Buckets, at `init()`, registers a setter on a well-known global key. This module\n * looks that setter up and calls it. If Buckets isn't installed, the global is\n * absent and every call here is a silent no-op — the SDK never requires Buckets,\n * and Buckets never requires the SDK.\n *\n * What it carries is the cross-match input: WHO (the identified user behind a\n * request) and WHAT (the route / operation). Buckets stamps those onto every\n * database read that happens inside the request's async context, so a heavy read\n * attributes to \"this user's this operation\" instead of an anonymous collection.\n *\n * The global key and context shape MUST match `@cross-deck/buckets`'\n * `actor-bridge.ts` (`BUCKETS_BRIDGE_KEY`, `RequestContext`). They are a wire\n * contract between two independently-published packages — keep them in lockstep.\n */\n\n/** Must equal `BUCKETS_BRIDGE_KEY` in @cross-deck/buckets. */\nconst BUCKETS_BRIDGE_KEY = \"__crossdeckBucketsBridge__\";\n\n/** The cross-match context. Only the provided fields are applied. */\nexport interface ReadCostContext {\n  /** WHO — the identified user behind this request (the developer's own id). */\n  actor?: string;\n  /** WHAT — the operation/feature that spent the reads. */\n  feature?: string;\n  /** WHAT (fallback) — the matched route pattern, e.g. `/users/:id`. */\n  route?: string;\n}\n\ntype BridgeSetter = (ctx: ReadCostContext) => void;\n\n/**\n * Push the cross-match context into Buckets for the current request's async\n * context. No-op (and never throws) when the Buckets collector isn't installed.\n */\nexport function bridgeReadCost(ctx: ReadCostContext): void {\n  try {\n    const setter = (globalThis as Record<string, unknown>)[BUCKETS_BRIDGE_KEY] as\n      | BridgeSetter\n      | undefined;\n    if (typeof setter === \"function\") setter(ctx);\n  } catch {\n    /* metering is best-effort — never disturb the host application */\n  }\n}\n","/**\n * Device + environment enrichment.\n *\n * Auto-attached to every event the SDK emits when `autoTrack.deviceInfo` is\n * enabled (default). Caller-supplied event properties always override\n * auto-detected ones (so a developer can manually set `app.version` per\n * event if they want to A/B between builds).\n *\n * Privacy posture:\n *   - No fingerprinting (no canvas hashes, no font enumeration).\n *   - No precise geolocation (only timezone + locale, both of which the\n *     browser exposes to every page anyway).\n *   - No IP collection — the backend logs the request IP for rate-limit\n *     purposes; it isn't stored on the event document.\n *   - All fields are typed enums or short strings; we never echo back\n *     full User-Agent strings to avoid surfacing fingerprintable detail\n *     in dashboards.\n */\n\nexport interface DeviceInfo {\n  os?: string;\n  osVersion?: string;\n  browser?: string;\n  browserVersion?: string;\n  locale?: string;\n  timezone?: string;\n  screenWidth?: number;\n  screenHeight?: number;\n  viewportWidth?: number;\n  viewportHeight?: number;\n  devicePixelRatio?: number;\n  /** Caller-supplied. Set via Crossdeck.start({ appVersion: \"1.2.3\" }). */\n  appVersion?: string;\n}\n\n/**\n * Are we in a browser context? Pure function; no side effects.\n *\n * Detects: globalThis.window AND globalThis.document AND globalThis.navigator.\n * All three must be present — Workers / Service Workers have window but\n * no document, and Node 18+ now has navigator but no window.\n */\nexport function isBrowser(): boolean {\n  return (\n    typeof (globalThis as { window?: unknown }).window !== \"undefined\" &&\n    typeof (globalThis as { document?: unknown }).document !== \"undefined\" &&\n    typeof (globalThis as { navigator?: unknown }).navigator !== \"undefined\"\n  );\n}\n\n/**\n * Collect every safe-to-attach environment field. Returns an empty object\n * outside browsers (Node, Workers) — caller can pass appVersion via the\n * `extra` argument for non-browser runtimes.\n */\nexport function collectDeviceInfo(extra?: { appVersion?: string }): DeviceInfo {\n  const info: DeviceInfo = {};\n  if (extra?.appVersion) info.appVersion = extra.appVersion;\n\n  if (!isBrowser()) return info;\n\n  const w = (globalThis as { window: Window }).window;\n  const nav = (globalThis as { navigator: Navigator }).navigator;\n  const doc = (globalThis as { document: Document }).document;\n\n  // ----- Locale + timezone -----\n  try {\n    if (typeof nav.language === \"string\") info.locale = nav.language;\n  } catch {}\n  try {\n    info.timezone = Intl.DateTimeFormat().resolvedOptions().timeZone;\n  } catch {}\n\n  // ----- Screen + viewport -----\n  try {\n    if (w.screen) {\n      info.screenWidth = w.screen.width;\n      info.screenHeight = w.screen.height;\n    }\n    info.viewportWidth = w.innerWidth;\n    info.viewportHeight = w.innerHeight;\n    info.devicePixelRatio = w.devicePixelRatio;\n  } catch {}\n\n  // ----- Browser + OS from User-Agent -----\n  try {\n    const ua = nav.userAgent ?? \"\";\n    const parsed = parseUserAgent(ua);\n    Object.assign(info, parsed);\n  } catch {}\n\n  // ua-ch hints (Chromium browsers expose these properly without UA-string parsing)\n  try {\n    const uaData = (nav as Navigator & {\n      userAgentData?: { platform?: string; brands?: Array<{ brand: string; version: string }> };\n    }).userAgentData;\n    if (uaData?.platform && !info.os) info.os = uaData.platform;\n    if (uaData?.brands && !info.browser) {\n      // Pick the most-specific non-\"Not.A;Brand\" entry\n      const real = uaData.brands.find(\n        (b) => !/Not[ .;A]*Brand/i.test(b.brand) && !/Chromium/i.test(b.brand),\n      );\n      if (real) {\n        info.browser = real.brand;\n        info.browserVersion = real.version;\n      }\n    }\n  } catch {}\n\n  // Suppress empties (a doc not yet hydrated could leave fields undefined)\n  void doc; // referenced only for the isBrowser narrowing\n  return info;\n}\n\n/**\n * Tiny User-Agent parser — extracts os, osVersion, browser, browserVersion.\n *\n * Doesn't try to be a full UA database (Bowser, ua-parser-js are large\n * deps). Covers ~95% of real-world traffic by recognising the major\n * browsers and operating systems. Unknown UAs fall through silently.\n *\n * Exported for unit testing.\n */\nexport function parseUserAgent(ua: string): Partial<DeviceInfo> {\n  const out: Partial<DeviceInfo> = {};\n\n  // ----- Operating system -----\n  // Order matters: iPad/iPhone before Mac (iPadOS 13+ UAs claim \"Macintosh\"),\n  // Android before Linux (Android UAs contain \"Linux\").\n  if (/iPad|iPhone|iPod/.test(ua)) {\n    out.os = \"iOS\";\n    const m = ua.match(/OS (\\d+[._]\\d+(?:[._]\\d+)?)/);\n    if (m?.[1]) out.osVersion = m[1].replace(/_/g, \".\");\n  } else if (/Android/.test(ua)) {\n    out.os = \"Android\";\n    const m = ua.match(/Android (\\d+(?:\\.\\d+)*)/);\n    if (m?.[1]) out.osVersion = m[1];\n  } else if (/Windows/.test(ua)) {\n    out.os = \"Windows\";\n    const m = ua.match(/Windows NT (\\d+\\.\\d+)/);\n    if (m?.[1]) out.osVersion = m[1];\n  } else if (/Mac OS X|Macintosh/.test(ua)) {\n    out.os = \"macOS\";\n    const m = ua.match(/Mac OS X (\\d+[._]\\d+(?:[._]\\d+)?)/);\n    if (m?.[1]) out.osVersion = m[1].replace(/_/g, \".\");\n  } else if (/Linux/.test(ua)) {\n    out.os = \"Linux\";\n  }\n\n  // ----- Browser -----\n  // Order matters: Edge before Chrome (Edge UA contains \"Chrome\"),\n  // Chrome before Safari (Chrome UA contains \"Safari\").\n  if (/Edg\\/(\\d+(?:\\.\\d+)*)/.test(ua)) {\n    out.browser = \"Edge\";\n    out.browserVersion = ua.match(/Edg\\/(\\d+(?:\\.\\d+)*)/)?.[1];\n  } else if (/Firefox\\/(\\d+(?:\\.\\d+)*)/.test(ua)) {\n    out.browser = \"Firefox\";\n    out.browserVersion = ua.match(/Firefox\\/(\\d+(?:\\.\\d+)*)/)?.[1];\n  } else if (/OPR\\/(\\d+(?:\\.\\d+)*)/.test(ua)) {\n    out.browser = \"Opera\";\n    out.browserVersion = ua.match(/OPR\\/(\\d+(?:\\.\\d+)*)/)?.[1];\n  } else if (/Chrome\\/(\\d+(?:\\.\\d+)*)/.test(ua)) {\n    out.browser = \"Chrome\";\n    out.browserVersion = ua.match(/Chrome\\/(\\d+(?:\\.\\d+)*)/)?.[1];\n  } else if (/Version\\/(\\d+(?:\\.\\d+)*).*Safari/.test(ua)) {\n    out.browser = \"Safari\";\n    out.browserVersion = ua.match(/Version\\/(\\d+(?:\\.\\d+)*)/)?.[1];\n  }\n\n  return out;\n}\n","/**\n * Auto-tracking — sessions and SPA page views, emitted via the same\n * track() pipeline as developer-instrumented events. No-op outside\n * browsers (Node, Workers).\n *\n * Sessions:\n *   - One sessionId per \"alive in foreground\" window.\n *   - `session.started` fires on install (after start()).\n *   - `session.ended` fires on visibilitychange→hidden, beforeunload,\n *     and pagehide. Multiple end-triggers are deduplicated so we don't\n *     emit two `session.ended` events for one tab close.\n *   - `session.duration_ms` attached on end.\n *   - If the tab returns to foreground after >30 minutes idle, the next\n *     visibilitychange→visible mints a NEW sessionId — matches the\n *     30-min session-window convention used by GA4 / Mixpanel / etc.\n *\n * Page views:\n *   - `page.viewed` fires on initial install.\n *   - Hooks into `history.pushState` / `history.replaceState` (monkey-\n *     patched in a non-destructive way so other libraries that hook\n *     them still see their events) and `popstate` for SPA navigation.\n *   - Properties: path, url, title, referrer, search, hash.\n *\n * Privacy: this module emits names + properties only. The Crossdeck\n * client adds device info on top via track(). Nothing here collects\n * PII beyond the URL itself, and we don't even log query strings\n * separately from the path (developer can post-process if needed).\n */\n\nimport { randomChars } from \"./identity\";\nimport type { KeyValueStorage } from \"./types\";\n\nexport interface AutoTrackConfig {\n  sessions: boolean;\n  pageViews: boolean;\n  /** Whether to enrich every event with device info. Lives on the client, not here, but documented together. */\n  deviceInfo: boolean;\n  /**\n   * Click autocapture. When true, the SDK installs a global click\n   * listener that fires `element.clicked` for every interactive\n   * click. Captures the target element's selector, text content,\n   * tag, href, data-* attributes, and viewport coordinates — enough\n   * to power funnel attribution (\"clicked X then converted\") and\n   * heatmap visualisation. Mixpanel / Amplitude default. Privacy\n   * guardrails baked in (input/password/sensitive-class skips).\n   *\n   * Default ON because behavioural attribution is Crossdeck's USP.\n   * Set to false to disable autocapture entirely (developer adds\n   * track() calls manually).\n   */\n  clicks: boolean;\n  /** Capture Web Vitals (LCP/INP/CLS/FCP/TTFB). Default true (browser only). */\n  webVitals: boolean;\n  /** Capture uncaught errors + unhandled rejections + 5xx fetch/XHR. Default true (browser only). */\n  errors: boolean;\n  /**\n   * Strip query strings + URL fragments (`#hash`) and reduce the referrer to\n   * its origin before a page-view event leaves the SDK. Default false (full\n   * URLs, unchanged). The consent-mode guest build sets this true — marketplace\n   * reviewers (Webflow finding #3) require query/fragment/full-referrer stripped\n   * from analytics events.\n   */\n  stripUrlParams: boolean;\n  /**\n   * Wrap `history.pushState`/`replaceState` so SPA navigations register as new\n   * page views. Default true. Set false to capture only the initial page view\n   * per load and NEVER monkey-patch host globals — required for the consent-mode\n   * guest build (Webflow finding #7 forbids history wrapping). Fine for Webflow's\n   * mostly-static pages, which reload on navigation anyway.\n   */\n  wrapHistory: boolean;\n}\n\nexport const DEFAULT_AUTO_TRACK: AutoTrackConfig = {\n  sessions: true,\n  pageViews: true,\n  deviceInfo: true,\n  clicks: true,\n  webVitals: true,\n  errors: true,\n  stripUrlParams: false,\n  wrapHistory: true,\n};\n\n/**\n * Reopen as a NEW session once activity has been idle this long — the\n * 30-min rolling-inactivity window GA4 / Mixpanel use. Applies both\n * in-page (tab hidden → returns >30min later) AND across page loads (the\n * SDK re-installs on every navigation of a multi-page site; if the stored\n * session's last activity is within this window we RESUME it instead of\n * minting a new one). Without the cross-page-load half, every navigation\n * ended one session and started another at the same instant.\n */\nconst SESSION_RESUME_THRESHOLD_MS = 30 * 60 * 1000;\n\n/** Default storage key for the persisted session continuity record. */\nconst SESSION_STORAGE_KEY = \"crossdeck:session\";\n\n/**\n * Throttle for flushing the rolling last-activity time to storage. The\n * in-memory value stays exact; we just avoid a storage write on every\n * single event. pagehide does a final forced flush so the last activity\n * before a navigation is never lost.\n */\nconst ACTIVITY_PERSIST_THROTTLE_MS = 5_000;\n\ntype TrackFn = (name: string, properties?: Record<string, unknown>) => void;\n\n/**\n * The slice of session state we persist across page loads. Kept minimal:\n * enough to RESUME the same visit (id + first-touch acquisition) and to\n * decide whether the resume window is still open (lastActivityAt).\n */\ninterface StoredSession {\n  id: string;\n  startedAt: number;\n  lastActivityAt: number;\n  acquisition: SessionAcquisition;\n}\n\ninterface SessionState {\n  sessionId: string;\n  startedAt: number;\n  /** Rolling timestamp of the last tracked event — drives the 30-min window. */\n  lastActivityAt: number;\n  hiddenAt: number | null;\n  endedSent: boolean;\n  /**\n   * Acquisition context captured once at session start. GA4 calls\n   * this \"first-touch attribution within the session.\" We attach\n   * these to every event of the session so dashboards can answer\n   * \"what was the source of users who triggered paywall_shown\" — a\n   * per-event lookup against the captured-once state, not a re-parse\n   * of the URL on every track().\n   *\n   * Empty strings (not undefined) so JSON envelope serialisation\n   * stays uniform — backend's extractAcquisition handles \"\" the\n   * same as missing.\n   */\n  acquisition: SessionAcquisition;\n}\n\nexport interface SessionAcquisition {\n  utm_source: string;\n  utm_medium: string;\n  utm_campaign: string;\n  utm_content: string;\n  utm_term: string;\n  referrer: string;\n  // ---------- paid-traffic click IDs (v0.9.0+) ----------\n  // UTM parameters are a documentation convention — anyone writing\n  // ads can forget to add them, and many platforms (Performance Max,\n  // Display & Video 360, automated bidding) emit ONLY a click-id.\n  // Capturing these alongside UTMs catches the ~40% of paid traffic\n  // that UTMs miss. Each is the platform's stable click identifier\n  // that flows from ad-click → landing-page URL → conversion event.\n  /** Google Ads click identifier. */\n  gclid: string;\n  /** Facebook / Meta Ads click identifier. */\n  fbclid: string;\n  /** Microsoft Advertising (Bing) click identifier. */\n  msclkid: string;\n  /** TikTok Ads click identifier. */\n  ttclid: string;\n  /** LinkedIn Ads click identifier. */\n  li_fat_id: string;\n  /** Twitter / X Ads click identifier. */\n  twclid: string;\n}\n\nconst EMPTY_ACQUISITION: SessionAcquisition = {\n  utm_source: \"\",\n  utm_medium: \"\",\n  utm_campaign: \"\",\n  utm_content: \"\",\n  utm_term: \"\",\n  referrer: \"\",\n  gclid: \"\",\n  fbclid: \"\",\n  msclkid: \"\",\n  ttclid: \"\",\n  li_fat_id: \"\",\n  twclid: \"\",\n};\n\nexport class AutoTracker {\n  private session: SessionState | null = null;\n  private cleanups: Array<() => void> = [];\n  /**\n   * Persistent storage for session continuity across page loads. Uses\n   * the SAME adapter as the rest of the SDK (localStorage by default,\n   * MemoryStorage when identity persistence is disabled for consent) so\n   * session persistence honours the same privacy posture as identity.\n   * Null only if no adapter was supplied → session is in-memory only\n   * (per-page), the pre-fix behaviour.\n   */\n  private readonly storage: KeyValueStorage | null;\n  private readonly sessionKey: string;\n  /** Last time we flushed lastActivityAt to storage (throttle gate). */\n  private lastPersistAt = 0;\n  /**\n   * Event Envelope v1 §3 — per-session monotonic sequence counter.\n   * The single owner of the seq counter lives here, alongside all other\n   * session state. Incremented atomically at track() time (via nextSeq()),\n   * reset to 0 whenever a new session boundary is crossed (startNewSession\n   * path + resetSession). Persists across background/foreground within a\n   * session — only a real session boundary (not a tab hide/show) resets it.\n   */\n  private _sessionSeq = 0;\n  /**\n   * Stable per-page-view identifier. Minted at every `page.viewed`\n   * emission and attached to every subsequent event until the next\n   * `page.viewed`. Lets dashboards correlate \"user clicked X\" to\n   * \"user viewed page Y\" without timestamp arithmetic — the canonical\n   * Mixpanel `$current_url` / Segment `pageId` pattern.\n   *\n   * Null until the first `page.viewed` fires (which happens at SDK\n   * install if `autoTrack.pageViews !== false`).\n   */\n  private pageviewId: string | null = null;\n\n  /**\n   * Secondary (cookie) store, scoped to the registrable domain — the SAME\n   * instance identity uses. First-touch origin is written to BOTH stores so it\n   * survives the marketing→app subdomain hop: localStorage is per-origin, so a\n   * visitor acquired on `example.com` would otherwise land on `app.example.com`\n   * with no memory of the campaign that won them, and convert as \"direct\".\n   * Null outside browsers or when identity persistence is off for consent.\n   */\n  private readonly cookieStorage: KeyValueStorage | null;\n\n  constructor(\n    private readonly cfg: AutoTrackConfig,\n    private readonly track: TrackFn,\n    opts?: { storage?: KeyValueStorage; storageKey?: string; cookieStorage?: KeyValueStorage },\n  ) {\n    this.storage = opts?.storage ?? null;\n    this.sessionKey = opts?.storageKey ?? SESSION_STORAGE_KEY;\n    this.cookieStorage = opts?.cookieStorage ?? null;\n  }\n\n  install(): void {\n    if (!isBrowserSafe()) return;\n    if (this.cfg.sessions) this.installSessionTracking();\n    if (this.cfg.pageViews) this.installPageViewTracking();\n    if (this.cfg.clicks) this.installClickTracking();\n  }\n\n  uninstall(): void {\n    while (this.cleanups.length) {\n      const fn = this.cleanups.pop();\n      try { fn?.(); } catch { /* ignore */ }\n    }\n    if (this.session && !this.session.endedSent) {\n      this.emitSessionEnd();\n    }\n    // Explicit teardown is a real end (unlike a navigation's pagehide) —\n    // clear the persisted session so the next start() begins fresh\n    // rather than resuming a session the host deliberately stopped.\n    this.clearStoredSession();\n    this.session = null;\n  }\n\n  /** Exposed for tests + consumers that want to reset the session manually. */\n  resetSession(): void {\n    if (this.session && !this.session.endedSent) this.emitSessionEnd();\n    // Null pageviewId on session boundary so any event fired between\n    // session reset and the next page.viewed doesn't ship the previous\n    // session's pageview attribution. Audit P1 #16: pre-fix the\n    // pageviewId survived 30-min idle resets and silently corrupted\n    // post-resume event → pageview correlation. The next page.viewed\n    // mints a fresh id (see installPageViewTracking's `fire()`).\n    this.pageviewId = null;\n    this.session = this.startNewSession();\n    this.persistSession();\n    this.emitSessionStart();\n  }\n\n  /**\n   * Keep the rolling session window alive. Called by the host on EVERY\n   * tracked event (auto or custom) so any activity — not just the\n   * pageviews/clicks AutoTracker emits itself — pushes the 30-min idle\n   * boundary forward. In-memory time is updated exactly; the storage\n   * flush is throttled (pagehide forces a final flush).\n   */\n  markActivity(): void {\n    if (!this.session) return;\n    const now = Date.now();\n    // If this activity lands AFTER the resume window has lapsed, it belongs to\n    // a NEW visit — roll the session BEFORE the event records, so a single\n    // stored session can never span a >30-min gap (the contract's\n    // session_gap_within_window invariant). This covers the case the\n    // visibility/page-load resume checks miss entirely: a tab left open and\n    // idle, then interacted with again, with no visibility transition. We do\n    // NOT back-date a session.ended onto the prior session — that would itself\n    // open the gap; its end is inferred from its last event (same as the\n    // page-load resume path). session.started is emitted here, before the\n    // triggering event is stamped (timestamp is set later in track()), so it\n    // is the earliest event of the new session.\n    if (now - this.session.lastActivityAt >= SESSION_RESUME_THRESHOLD_MS) {\n      this.pageviewId = null;\n      this.session = this.startNewSession();\n      this.persistSession();\n      this.emitSessionStart();\n      return;\n    }\n    this.session.lastActivityAt = now;\n    if (now - this.lastPersistAt >= ACTIVITY_PERSIST_THROTTLE_MS) {\n      this.persistSession();\n    }\n  }\n\n  /** Exposed for inspection/tests — returns the current sessionId (or null if not in a session). */\n  get currentSessionId(): string | null {\n    return this.session?.sessionId ?? null;\n  }\n\n  /** Stable per-page-view ID. Null before the first page.viewed has fired. */\n  get currentPageviewId(): string | null {\n    return this.pageviewId;\n  }\n\n  /**\n   * Per-session acquisition context — utm_* + referrer, captured once\n   * at session start. Returns empty strings when there's no session\n   * (Node, before init, after uninstall) so callers can spread without\n   * conditional logic. Bank-grade rule: capture once, attach to every\n   * event of the session, don't re-read on every track() (the URL\n   * changes via SPA pushState; the source-of-record is the URL we\n   * landed on).\n   */\n  get currentAcquisition(): SessionAcquisition {\n    return this.session?.acquisition ?? EMPTY_ACQUISITION;\n  }\n\n  /**\n   * Event Envelope v1 §3 — return the next seq value for the current session\n   * and advance the counter. Called SYNCHRONOUSLY at track() time, before any\n   * async dispatch, so the seq assignment order is deterministic (call order,\n   * not scheduler luck). The counter is reset to 0 at every session boundary;\n   * it is NOT reset by tab hide/show (spec §3 clause 1: backgrounding does not\n   * reset seq). When no session is active (Node, before init, after uninstall),\n   * returns 0 and leaves the counter unchanged — fallback, not an error.\n   */\n  nextSeq(): number {\n    // Increment FIRST, then return. Pre-increment means the first event of a\n    // session gets seq=0 (counter starts at 0 after reset, returns 0 pre-bump,\n    // but we return-then-increment to match Swift's pattern: 0-based first).\n    // We return the current value and then increment so seq 0 = first event.\n    const seq = this._sessionSeq;\n    this._sessionSeq += 1;\n    return seq;\n  }\n\n  // ---------- sessions ----------\n  private installSessionTracking(): void {\n    const now = Date.now();\n    const stored = this.readStoredSession();\n    if (stored && now - stored.lastActivityAt < SESSION_RESUME_THRESHOLD_MS) {\n      // RESUME the in-flight visit across a full page load. A multi-page\n      // site re-installs the SDK on every navigation; minting a new\n      // session here (the old behaviour) split one visit into one session\n      // per page, each ended at the same instant the next began — the\n      // \"session ends and the next begins at 05:18:11\" bug. Reuse the id\n      // + first-touch acquisition; do NOT emit a second session.started.\n      this.session = {\n        sessionId: stored.id,\n        startedAt: stored.startedAt,\n        lastActivityAt: now,\n        hiddenAt: null,\n        endedSent: false,\n        acquisition: stored.acquisition,\n      };\n      this.persistSession();\n    } else {\n      // Genuinely new visit (no stored session, or the 30-min window\n      // lapsed). A stale stored session is simply superseded — we do NOT\n      // emit a back-dated session.ended for it, which would land at the\n      // new session's timestamp and corrupt the old session's duration;\n      // the dashboard infers the prior session's end from its last event.\n      this.session = this.startNewSession();\n      this.persistSession();\n      this.emitSessionStart();\n    }\n\n    const onVisChange = (): void => {\n      try {\n      if (!this.session) return;\n      const doc = (globalThis as { document: Document }).document;\n      if (doc.visibilityState === \"hidden\") {\n        // Quick tab switches and Cmd-Tabs land here, but the page is\n        // still alive. Record the time and flush, but do NOT emit\n        // session.ended — returning seconds later must continue the same\n        // session (the 30-min window intent). The flush keeps the\n        // last-activity time accurate for a next-visit resume if the tab\n        // is closed rather than returned to.\n        this.session.hiddenAt = Date.now();\n        this.persistSession();\n      } else if (doc.visibilityState === \"visible\") {\n        // Decide on real inactivity, not just time-hidden: lastActivityAt\n        // is the last tracked event, so this is the true idle gap.\n        const idleFor = Date.now() - this.session.lastActivityAt;\n        if (idleFor >= SESSION_RESUME_THRESHOLD_MS) {\n          // Long idle → the previous session genuinely ended. Open a fresh\n          // one. Do NOT back-date a session.ended onto the prior session: it\n          // would land >30 min after that session's last real event and open\n          // an intra-session gap (session_gap_within_window). Its end is\n          // inferred from its last event — consistent with the page-load\n          // resume path. Null pageviewId on the boundary (audit P1 #16) so any\n          // event before the next page.viewed doesn't ship stale attribution.\n          this.pageviewId = null;\n          this.session = this.startNewSession();\n          this.persistSession();\n          this.emitSessionStart();\n        } else {\n          // Quick return — same session continues.\n          this.session.hiddenAt = null;\n        }\n      }\n      } catch {\n        // Unload-time defense. This handler runs during page teardown, where\n        // in-app browsers (Instagram/Facebook's `iabjs://`, TikTok, …) hook\n        // browser APIs and throw from their OWN teardown (\"Java object is\n        // gone\"). A throw here would propagate to window.onerror and the SDK\n        // would self-report the host shell's noise as the developer's error.\n        // The page is going away; nothing here can be meaningfully handled.\n      }\n    };\n\n    // A page unload is NOT a session end — on a multi-page site it's a\n    // navigation, and the next page resumes this same session. Flush the\n    // final activity time so the next load's resume-window check is\n    // accurate. The session ends only on real 30-min inactivity or an\n    // explicit uninstall(). (This is what stopped the per-navigation\n    // session.ended / session.started churn.)\n    // try/catch for the same unload-teardown reason as onVisChange above.\n    const onPageHide = (): void => {\n      try { this.persistSession(); } catch { /* unload teardown — swallow */ }\n    };\n\n    const w = (globalThis as { window: Window }).window;\n    const doc = (globalThis as { document: Document }).document;\n    doc.addEventListener(\"visibilitychange\", onVisChange);\n    w.addEventListener(\"pagehide\", onPageHide);\n    // beforeunload is unreliable on mobile; pagehide is the modern equivalent.\n    // We listen to both for desktop-vs-mobile coverage.\n    w.addEventListener(\"beforeunload\", onPageHide);\n\n    this.cleanups.push(() => {\n      doc.removeEventListener(\"visibilitychange\", onVisChange);\n      w.removeEventListener(\"pagehide\", onPageHide);\n      w.removeEventListener(\"beforeunload\", onPageHide);\n    });\n  }\n\n  private startNewSession(): SessionState {\n    const now = Date.now();\n    // Envelope v1 §3: reset the seq counter at every session boundary. The\n    // counter belongs to the session — a new session always starts at seq 0.\n    this._sessionSeq = 0;\n    return {\n      sessionId: mintSessionId(),\n      startedAt: now,\n      lastActivityAt: now,\n      hiddenAt: null,\n      endedSent: false,\n      acquisition: this.resolveAcquisition(),\n    };\n  }\n\n  /**\n   * Session acquisition, with FIRST-TOUCH survival across sessions.\n   *\n   * WHY: `captureAcquisition()` reads the CURRENT page. That is correct for a\n   * fresh arrival, but a session boundary (30-min idle, or a return visit next\n   * week) starts a new session — and a visitor returning directly has no utm_*\n   * and no click id, so the acquisition that actually WON them was silently\n   * replaced with empty. Someone who arrives from LinkedIn on Monday, returns\n   * direct on Tuesday and signs up on Wednesday converted with no source at all.\n   *\n   * That is exactly the question the product exists to answer — \"where did this\n   * PAYING CUSTOMER come from\" — so origin has to outlive the session that\n   * captured it. We persist the first touch that carried a real signal and,\n   * whenever a later session arrives with nothing, fall back to it.\n   *\n   * Deliberately NOT last-touch-wins: a real new campaign click overwrites\n   * nothing, it simply records a newer touch for that session while the stored\n   * first touch stays put. Write-once by design.\n   */\n  private resolveAcquisition(): SessionAcquisition {\n    const current = captureAcquisition();\n    if (!this.storage && !this.cookieStorage) return current;\n\n    const key = `${this.sessionKey}_origin_first`;\n\n    // Read prefers localStorage (higher fidelity, survives cookie caps) and\n    // falls back to the shared-domain cookie — which is the leg that carries\n    // the origin ACROSS SUBDOMAINS. Same precedence as IdentityStore.\n    const readFirstTouch = (): string | null => {\n      try { const v = this.storage?.getItem(key); if (v) return v; } catch { /* private mode */ }\n      try { return this.cookieStorage?.getItem(key) ?? null; } catch { return null; }\n    };\n    // Write fans out to BOTH so a later clear of either doesn't lose the touch,\n    // and so the app subdomain can read what the marketing site captured.\n    const writeFirstTouch = (v: string): void => {\n      try { this.storage?.setItem(key, v); } catch { /* quota / private mode */ }\n      try { this.cookieStorage?.setItem(key, v); } catch { /* cookie blocked */ }\n    };\n\n    // A \"real\" signal is any campaign param or click id. `referrer` alone does\n    // not count — every visit has one, so treating it as a touch would freeze\n    // the first referrer forever and never let a genuine campaign register.\n    const hasSignal = (a: SessionAcquisition): boolean =>\n      Boolean(a.utm_source || a.utm_medium || a.utm_campaign || a.utm_content || a.utm_term\n        || a.gclid || a.fbclid || a.msclkid || a.ttclid || a.li_fat_id || a.twclid);\n\n    try {\n      if (hasSignal(current)) {\n        // First real touch wins and is never overwritten.\n        if (readFirstTouch() === null) writeFirstTouch(JSON.stringify(current));\n        return current;\n      }\n\n      // No signal on this page — restore the touch that won them, so the\n      // conversion still carries its source.\n      const raw = readFirstTouch();\n      if (raw) {\n        const stored = JSON.parse(raw) as Partial<SessionAcquisition>;\n        return {\n          ...EMPTY_ACQUISITION,\n          ...stored,\n          // Referrer always describes THIS visit, never the remembered one.\n          referrer: current.referrer,\n        };\n      }\n    } catch {\n      // Quota, private mode, malformed JSON — degrade to the current page.\n    }\n    return current;\n  }\n\n  /**\n   * Read the persisted session continuity record. Returns null on no\n   * storage, no record, malformed JSON, or a record missing its required\n   * fields — every failure degrades to \"no session to resume\", which is\n   * the safe (start-fresh) path.\n   */\n  private readStoredSession(): StoredSession | null {\n    if (!this.storage) return null;\n    try {\n      const raw = this.storage.getItem(this.sessionKey);\n      if (!raw) return null;\n      const p = JSON.parse(raw) as Partial<StoredSession>;\n      if (\n        !p ||\n        typeof p.id !== \"string\" ||\n        typeof p.startedAt !== \"number\" ||\n        typeof p.lastActivityAt !== \"number\"\n      ) {\n        return null;\n      }\n      return {\n        id: p.id,\n        startedAt: p.startedAt,\n        lastActivityAt: p.lastActivityAt,\n        acquisition:\n          p.acquisition && typeof p.acquisition === \"object\"\n            ? (p.acquisition as SessionAcquisition)\n            : EMPTY_ACQUISITION,\n      };\n    } catch {\n      return null;\n    }\n  }\n\n  /** Flush the current session to storage. No-op without storage/session. */\n  private persistSession(): void {\n    if (!this.storage || !this.session) return;\n    this.lastPersistAt = Date.now();\n    try {\n      const rec: StoredSession = {\n        id: this.session.sessionId,\n        startedAt: this.session.startedAt,\n        lastActivityAt: this.session.lastActivityAt,\n        acquisition: this.session.acquisition,\n      };\n      this.storage.setItem(this.sessionKey, JSON.stringify(rec));\n    } catch {\n      // Quota / blocked storage — session degrades to in-memory only.\n    }\n  }\n\n  private clearStoredSession(): void {\n    if (!this.storage) return;\n    try {\n      this.storage.removeItem(this.sessionKey);\n    } catch {\n      // ignore\n    }\n  }\n\n  private emitSessionStart(): void {\n    if (!this.session) return;\n    this.track(\"session.started\", { sessionId: this.session.sessionId });\n  }\n\n  private emitSessionEnd(): void {\n    if (!this.session || this.session.endedSent) return;\n    const duration = Date.now() - this.session.startedAt;\n    this.track(\"session.ended\", {\n      sessionId: this.session.sessionId,\n      durationMs: duration,\n    });\n    this.session.endedSent = true;\n  }\n\n  // ---------- page views ----------\n  private installPageViewTracking(): void {\n    const w = (globalThis as { window: Window }).window;\n    const doc = (globalThis as { document: Document }).document;\n\n    // PwC M-5: dedup. SPA frameworks (Next.js, React Router, Vue\n    // Router) routinely fire pushState() back-to-back during a single\n    // navigation — animation enter, then the destination's settle.\n    // Without a guard, we'd send 2-3 page.viewed events per click,\n    // inflating pageview / session counts. Dedup window: 250ms,\n    // keyed by URL. Identical URL within window = drop.\n    //\n    // EXCEPTION: popstate (user back/forward) is always a real\n    // navigation, even if it lands on a URL we've recently seen.\n    // Force-fire on popstate so back-button traffic is never dropped.\n    let lastFiredAt = 0;\n    let lastFiredUrl = \"\";\n    const DEDUP_WINDOW_MS = 250;\n\n    const fire = (force = false): void => {\n      const loc = w.location;\n      // Consent-mode guest build: strip query + fragment from the URL and drop\n      // the referrer entirely (Webflow finding #3). Default false → full URL.\n      const strip = this.cfg.stripUrlParams;\n      const url = strip ? `${loc.origin}${loc.pathname}` : loc.href;\n      const now = Date.now();\n      if (!force && url === lastFiredUrl && now - lastFiredAt < DEDUP_WINDOW_MS) return;\n      lastFiredAt = now;\n      lastFiredUrl = url;\n\n      // Mint a fresh pageviewId BEFORE emitting the event so this\n      // page.viewed itself carries it, and every subsequent event up\n      // to the next page.viewed inherits it via the auto-attached\n      // enrichment in crossdeck.ts:track().\n      this.pageviewId = `pv_${Date.now().toString(36)}${randomChars(10)}`;\n\n      this.track(\"page.viewed\", {\n        pageviewId: this.pageviewId,\n        path: loc.pathname,\n        url,\n        search: strip ? undefined : loc.search || undefined,\n        hash: strip ? undefined : loc.hash || undefined,\n        title: doc.title,\n        // referrer only on the first hit of the session — afterward it's\n        // always our previous URL, which isn't useful. Dropped entirely when\n        // stripUrlParams is on (consent-mode); it also gates on marketing consent.\n        referrer: strip ? undefined : doc.referrer || undefined,\n      });\n    };\n\n    // Initial page view\n    fire();\n\n    // SPA navigation is tracked by wrapping history.pushState/replaceState.\n    // The consent-mode guest build sets wrapHistory:false so we NEVER\n    // monkey-patch host globals (Webflow finding #7) — only the initial page\n    // view above fires per load. Fine for mostly-static Webflow pages.\n    if (!this.cfg.wrapHistory) return;\n\n    // SPA navigation: monkey-patch pushState / replaceState. Capture the\n    // BARE function references (not bound) so uninstall restores exactly\n    // what was there. Bind chains accumulate without limit if every\n    // install/uninstall cycle wraps with .bind() — over many cycles\n    // pushState becomes [bound bound bound … pushState] and tests that\n    // assert \"pushState restored to its previous value\" break.\n    //\n    // We use `function (this: History, ...args)` so JS's normal method-call\n    // semantics bind `this` to history when our wrapper is invoked as\n    // history.pushState(...). Then we forward via .apply(this, args) — no\n    // pre-binding needed, no chain growth.\n    type HistoryFn = (data: unknown, unused: string, url?: string | null) => void;\n    const origPush = w.history.pushState as HistoryFn;\n    const origReplace = w.history.replaceState as HistoryFn;\n\n    function patchedPush(this: History, data: unknown, unused: string, url?: string | null): void {\n      origPush.apply(this, [data, unused, url]);\n      queueMicrotask(fire);\n    }\n    function patchedReplace(this: History, data: unknown, unused: string, url?: string | null): void {\n      origReplace.apply(this, [data, unused, url]);\n      queueMicrotask(fire);\n    }\n\n    (w.history.pushState as HistoryFn) = patchedPush;\n    (w.history.replaceState as HistoryFn) = patchedReplace;\n\n    // popstate fires on user back/forward — bypass the dedup window\n    // because user navigation is always a real event, not a framework\n    // double-fire artefact.\n    const onPopState = (): void => fire(true);\n    w.addEventListener(\"popstate\", onPopState);\n\n    this.cleanups.push(() => {\n      // Only restore if WE'RE still the active wrapper. If another tracker\n      // installed on top of ours, blindly setting pushState back would\n      // unwind their patch too. Conservative: only restore our slot.\n      if (w.history.pushState === patchedPush) {\n        (w.history.pushState as HistoryFn) = origPush;\n      }\n      if (w.history.replaceState === patchedReplace) {\n        (w.history.replaceState as HistoryFn) = origReplace;\n      }\n      w.removeEventListener(\"popstate\", onPopState);\n    });\n  }\n\n  // ---------- click autocapture ----------\n  /**\n   * Global click tracking — Mixpanel / Amplitude style autocapture.\n   * Fires `element.clicked` for every interactive click with the\n   * target element's selector path, text content, tag, href, data-*\n   * attributes, and viewport coordinates. Powers the funnel /\n   * attribution USP: \"users who clicked X then converted within\n   * 7 days.\" Default ON because behavioural attribution is the\n   * core product promise.\n   *\n   * Privacy guardrails:\n   *   - Skip clicks ON inputs / textareas / selects (form interaction\n   *     isn't button telemetry; the dev should track form submits\n   *     deliberately via track('form_submitted'))\n   *   - Skip clicks INSIDE [type=\"password\"] and password-class\n   *     elements\n   *   - Skip clicks inside elements opted out via class=\"cd-noTrack\"\n   *     or data-cd-noTrack attribute (Mixpanel's exact opt-out\n   *     idiom — most devs already know it)\n   *   - Capture text content but cap at 64 chars and trim — never\n   *     more than what you'd see on a button label\n   *\n   * Volume guardrails:\n   *   - Coalesce double-clicks within 100ms (React's synthetic click\n   *     pattern + browser's native dblclick can fire twice)\n   *   - Listen on document at capture phase so we see the click\n   *     before any framework's own handlers stop propagation\n   */\n  private installClickTracking(): void {\n    const w = (globalThis as { window: Window }).window;\n    const doc = (globalThis as { document: Document }).document;\n\n    let lastFiredAt = 0;\n    let lastFiredTarget: EventTarget | null = null;\n    const COALESCE_MS = 100;\n    const TEXT_CAP = 64;\n\n    const onClick = (ev: MouseEvent): void => {\n      const target = ev.target as Element | null;\n      if (!target || !(target instanceof Element)) return;\n\n      // De-dupe rapid double-fires on the same target (React synthetic\n      // click + browser native click can land in the same tick).\n      const now = Date.now();\n      if (target === lastFiredTarget && now - lastFiredAt < COALESCE_MS) return;\n      lastFiredAt = now;\n      lastFiredTarget = target;\n\n      // Walk up to the nearest \"actionable\" ancestor. A click inside\n      // <button><span>Sign up</span></button> should fire as a click\n      // on the BUTTON, not the inner span. We climb up to a button /\n      // a / [role=\"button\"] / [data-cd-event] / [onclick] — whichever\n      // is closer.\n      const actionable = closestActionable(target);\n      const clicked: Element = actionable || target;\n\n      // Privacy: skip form-input clicks, password fields, opted-out\n      // subtrees. PII risk is too high to capture text from these.\n      if (isFormInput(clicked)) return;\n      if (isInOptedOut(clicked)) return;\n      if (isInsidePasswordField(clicked)) return;\n\n      // Build the event properties.\n      const tag = clicked.tagName.toLowerCase();\n      const text = trimText(extractText(clicked), TEXT_CAP);\n      const href = (clicked as HTMLAnchorElement).href || undefined;\n      const linkTarget = (clicked as HTMLAnchorElement).target || undefined;\n      const elementId = clicked.id || undefined;\n      const role = clicked.getAttribute(\"role\") || undefined;\n      const ariaLabel = clicked.getAttribute(\"aria-label\") || undefined;\n      const selector = buildSelector(clicked);\n      const dataAttrs = collectDataAttrs(clicked);\n      const isLink = tag === \"a\" && !!href;\n\n      // Optional explicit override: if the dev tagged the element\n      // with data-cd-event=\"custom_name\", we use THAT as the event\n      // name and stash the auto-properties as `meta` rather than\n      // firing as element.clicked. Devs who want named events get\n      // them; everyone else gets the auto.\n      const explicitName = clicked.getAttribute(\"data-cd-event\");\n\n      const props: Record<string, unknown> = {\n        selector,\n        tag,\n        text,\n        elementId,\n        role,\n        ariaLabel,\n        href,\n        isLink,\n        linkTarget,\n        viewportX: ev.clientX,\n        viewportY: ev.clientY,\n        pageX: ev.pageX,\n        pageY: ev.pageY,\n        ...dataAttrs,\n      };\n      // Drop empties so the event property bag isn't full of nulls.\n      for (const k of Object.keys(props)) {\n        if (props[k] === undefined || props[k] === null || props[k] === \"\") delete props[k];\n      }\n\n      this.track(explicitName || \"element.clicked\", props);\n    };\n\n    doc.addEventListener(\"click\", onClick, { capture: true, passive: true });\n    this.cleanups.push(() => {\n      doc.removeEventListener(\"click\", onClick, { capture: true } as AddEventListenerOptions);\n    });\n  }\n}\n\n// ---------- click-tracking helpers ----------\n\nfunction closestActionable(el: Element): Element | null {\n  // Climb up to the nearest interactive ancestor. The order matters —\n  // [data-cd-event] wins because it's an explicit dev-supplied tag.\n  return (\n    el.closest(\"[data-cd-event]\") ||\n    el.closest(\"[data-cd-noTrack]\") ||\n    el.closest(\"button, a, [role='button'], [role='link'], input[type='button'], input[type='submit']\") ||\n    null\n  );\n}\n\nfunction isFormInput(el: Element): boolean {\n  if (!(el instanceof HTMLElement)) return false;\n  const tag = el.tagName.toLowerCase();\n  if (tag === \"textarea\" || tag === \"select\") return true;\n  if (tag === \"input\") {\n    const type = ((el as HTMLInputElement).type || \"\").toLowerCase();\n    // Buttons are inputs but they ARE click targets — track them.\n    return type !== \"button\" && type !== \"submit\" && type !== \"image\" && type !== \"reset\";\n  }\n  return false;\n}\n\nfunction isInOptedOut(el: Element): boolean {\n  // Mixpanel's idiom: class=\"cd-noTrack\" or [data-cd-noTrack] on any\n  // ancestor opts the entire subtree out of autocapture.\n  if (el.closest('[data-cd-noTrack], [data-cd-no-track], .cd-noTrack, .cd-no-track')) return true;\n  return false;\n}\n\nfunction isInsidePasswordField(el: Element): boolean {\n  // Defensive: never capture clicks on / near password fields.\n  if (el.closest('input[type=\"password\"]')) return true;\n  return false;\n}\n\n// Tags whose text IS a control's own caption when they sit directly\n// inside it — buttons/links routinely wrap their label in one of these.\nconst INLINE_LABEL_TAGS = new Set([\n  \"span\", \"b\", \"strong\", \"em\", \"i\", \"small\", \"mark\", \"u\", \"label\",\n  \"abbr\", \"time\", \"bdi\", \"cite\", \"code\", \"kbd\", \"q\", \"sub\", \"sup\",\n]);\n\n// Subtrees whose text is decorative (icon internals / styling) and must\n// never leak into a label.\nconst NON_LABEL_SUBTREES = new Set([\"svg\", \"style\", \"script\", \"noscript\"]);\n\n// A clicked control that CONTAINS one of these is a wrapper — around\n// other controls (a card whose whole body is one big <a>, holding three\n// sign-in buttons) or around a content block (a hero <a> wrapping a\n// heading + paragraph). Its full textContent is a mash, not a label.\n// Headings count because a heading inside a clickable is the signature of\n// \"this is a content block, not a button\".\nconst CONTAINER_MARKERS =\n  \"a[href], button, input, select, textarea, [role='button'], [role='link'], h1, h2, h3, h4, h5, h6\";\n\nconst HEADING_SELECTOR = \"h1, h2, h3, h4, h5, h6\";\n\n/** Collapse whitespace runs and trim. \" Sign\\n up \" → \"Sign up\". */\nfunction collapseWs(s: string): string {\n  return s.replace(/\\s+/g, \" \").trim();\n}\n\n/**\n * Text of an element with WORD BOUNDARIES preserved across nested\n * elements. `el.textContent` concatenates text nodes with nothing between\n * them, so `<h1>…só link</h1><p>Portfolio…` fuses into \"só linkPortfolio\"\n * and `<button>Log in</button><button>Continue…` into \"Log inContinue…\".\n * We insert a space at every element boundary, skip decorative subtrees\n * (icons), then collapse — so the label reads as written, never fused.\n */\nfunction boundaryText(el: Element): string {\n  let out = \"\";\n  const walk = (node: Node): void => {\n    const kids = node.childNodes;\n    for (let i = 0; i < kids.length; i++) {\n      const child = kids[i];\n      if (!child) continue;\n      if (child.nodeType === 3 /* TEXT_NODE */) {\n        out += child.textContent || \"\";\n      } else if (child.nodeType === 1 /* ELEMENT_NODE */) {\n        if (NON_LABEL_SUBTREES.has((child as Element).tagName.toLowerCase())) continue;\n        out += \" \";\n        walk(child);\n        out += \" \";\n      }\n    }\n  };\n  walk(el);\n  return collapseWs(out);\n}\n\n/**\n * The element's OWN label — immediate text nodes plus the text of inline\n * label wrappers (<span> etc.) directly inside it. Deliberately does NOT\n * descend into block or interactive children, so a wrapper's nested\n * controls/headings never leak in. \"\" when the element carries no label\n * of its own.\n */\nfunction directLabel(el: Element): string {\n  let out = \"\";\n  const kids = el.childNodes;\n  for (let i = 0; i < kids.length; i++) {\n    const child = kids[i];\n    if (!child) continue;\n    if (child.nodeType === 3 /* TEXT_NODE */) {\n      out += \" \" + (child.textContent || \"\");\n    } else if (\n      child.nodeType === 1 /* ELEMENT_NODE */ &&\n      INLINE_LABEL_TAGS.has((child as Element).tagName.toLowerCase())\n    ) {\n      out += \" \" + boundaryText(child as Element);\n    }\n  }\n  return collapseWs(out);\n}\n\n/**\n * Resolve the visible label for a clicked control WITHOUT mashing nested\n * controls or content blocks together.\n *\n * - A \"simple\" control — no nested interactive element, no nested heading\n *   — yields its boundary-spaced text. Covers the everyday case:\n *   `<button>Sign up</button>`, `<a>Pricing</a>`,\n *   `<button><svg/><span>Save</span></button>` → \"Save\".\n * - A \"container\" — a control wrapping OTHER controls or a content block —\n *   would otherwise collapse to \"Log inContinue with GoogleContinue with\n *   Apple…\" or \"Tudo que você é,em um só link.Portfolio…\". For these we\n *   use, in order: the element's own direct label → the first heading\n *   inside it (the human-recognisable name of the block) → \"\". Returning\n *   \"\" lets extractText fall through to title / img alt / svg title, and\n *   finally to the selector — honest, not garbage.\n */\nfunction visibleLabel(el: Element): string {\n  const isContainer = el.querySelector(CONTAINER_MARKERS) !== null;\n  if (!isContainer) return boundaryText(el);\n\n  const direct = directLabel(el);\n  if (direct) return direct;\n\n  const heading = el.querySelector(HEADING_SELECTOR);\n  if (heading) {\n    const h = boundaryText(heading);\n    if (h) return h;\n  }\n  return \"\";\n}\n\n/**\n * Compute the best human-readable label for a clicked element.\n *\n * Precedence (highest → lowest):\n *   1. data-cd-track / data-track / data-testid — explicit dev label\n *   2. aria-label — accessible name (most icon-only buttons set this)\n *   3. aria-labelledby — references another element by ID\n *   4. <input value=\"…\"> — for submit/button inputs\n *   5. visible textContent — the most common case\n *   6. title attribute — tooltip fallback\n *   7. <img alt=\"…\"> inside the element — for image-only buttons\n *   8. <svg><title>…</title></svg> inside the element — for SVG icons\n *      that include an accessible name\n *\n * Returns \"\" only when the element is truly anonymous (no label, no\n * text, no inner image/icon with a name). The journey UI falls back\n * to the selector when this returns empty, so any source we can lift\n * a name from beats the CSS-homework display.\n *\n * Whitespace is always collapsed — \"  Sign\\n  up  \" → \"Sign up\".\n */\nfunction extractText(el: Element): string {\n  const clean = (s: string): string => s.replace(/\\s+/g, \" \").trim();\n\n  // 1. Explicit dev-supplied labels. data-cd-track is the Crossdeck-\n  //    canonical attribute; data-track and data-testid are the\n  //    common community standards we accept as synonyms.\n  const explicit =\n    el.getAttribute(\"data-cd-track\") ||\n    el.getAttribute(\"data-track\") ||\n    el.getAttribute(\"data-testid\");\n  if (explicit) {\n    const t = clean(explicit);\n    if (t) return t;\n  }\n\n  // 2. Aria-label — the ARIA accessible-name primary source.\n  const aria = el.getAttribute(\"aria-label\");\n  if (aria) {\n    const t = clean(aria);\n    if (t) return t;\n  }\n\n  // 3. Aria-labelledby — resolve referenced element's text. The ID\n  //    list can have multiple tokens; concatenate referents (ARIA spec).\n  const labelledBy = el.getAttribute(\"aria-labelledby\");\n  if (labelledBy && typeof el.ownerDocument?.getElementById === \"function\") {\n    const parts: string[] = [];\n    for (const id of labelledBy.split(/\\s+/)) {\n      const ref = el.ownerDocument.getElementById(id);\n      const t = ref?.textContent ? clean(ref.textContent) : \"\";\n      if (t) parts.push(t);\n    }\n    if (parts.length > 0) return parts.join(\" \");\n  }\n\n  // 4. Input value (submit/button inputs render their label here).\n  if (el instanceof HTMLInputElement && el.value) {\n    const t = clean(el.value);\n    if (t) return t;\n  }\n\n  // 5. Visible text — the element's OWN label, never a mash of nested\n  //    controls or content blocks. extractText used to return\n  //    clean(el.textContent) here, which on a control that WRAPS other\n  //    things collapses the whole subtree into one string:\n  //    \"Log inContinue with GoogleContinue with Apple…\" (an auth card\n  //    around three buttons) or \"Tudo que você é,em um só link.Portfolio\n  //    …\" (a hero <a> around an <h1>+<p>+<ul>). visibleLabel() resolves a\n  //    faithful single-control label instead. See its doc comment.\n  const text = visibleLabel(el);\n  if (text) return text;\n\n  // 6. Title attribute (tooltip-style accessible name).\n  const title = el.getAttribute(\"title\");\n  if (title) {\n    const t = clean(title);\n    if (t) return t;\n  }\n\n  // 7. <img alt=\"…\"> within — common for image-only buttons. Prefer\n  //    a non-empty alt; treat alt=\"\" (decorative) as no signal.\n  const img = el.querySelector(\"img[alt]\");\n  if (img) {\n    const alt = img.getAttribute(\"alt\") ?? \"\";\n    const t = clean(alt);\n    if (t) return t;\n  }\n\n  // 8. SVG <title> child — the conventional way to ship an accessible\n  //    name on inline icons.\n  const svgTitle = el.querySelector(\"svg title\");\n  if (svgTitle?.textContent) {\n    const t = clean(svgTitle.textContent);\n    if (t) return t;\n  }\n\n  return \"\";\n}\n\nfunction trimText(s: string, cap: number): string {\n  if (s.length <= cap) return s;\n  return s.slice(0, cap - 1) + \"…\";\n}\n\n/**\n * Build a stable CSS selector path for the element. Used for\n * server-side dedup (\"clicked the same button on the same page\")\n * and for replay / heatmap reconstruction. Walks up to the body or\n * an element with an id, whichever comes first. Caps the depth at\n * 5 to keep selectors short and human-readable.\n */\nfunction buildSelector(el: Element): string {\n  const parts: string[] = [];\n  let cur: Element | null = el;\n  let depth = 0;\n  while (cur && cur.nodeName.toLowerCase() !== \"body\" && depth < 5) {\n    let part = cur.nodeName.toLowerCase();\n    if (cur.id) {\n      parts.unshift(`${part}#${cur.id}`);\n      break; // ID is unique-enough — stop walking up\n    }\n    if (cur.classList.length > 0) {\n      const cls = Array.from(cur.classList)\n        .filter((c) => !c.startsWith(\"cd-\")) // skip our own marker classes\n        .slice(0, 2)\n        .join(\".\");\n      if (cls) part += `.${cls}`;\n    }\n    parts.unshift(part);\n    cur = cur.parentElement;\n    depth++;\n  }\n  return parts.join(\" > \");\n}\n\nfunction collectDataAttrs(el: Element): Record<string, string> {\n  // Pull every data-* attribute off the element. Devs use these\n  // for explicit tagging — `data-cd-prop-plan=\"pro\"` becomes a\n  // property on the event so you can filter conversions by plan.\n  const out: Record<string, string> = {};\n  if (!(el instanceof HTMLElement)) return out;\n  for (const name of el.getAttributeNames()) {\n    if (!name.startsWith(\"data-\")) continue;\n    if (name === \"data-cd-noTrack\" || name === \"data-cd-no-track\") continue;\n    if (name === \"data-cd-event\") continue; // used as event name, not prop\n    const value = el.getAttribute(name) || \"\";\n    // Normalise data-cd-prop-plan → \"plan\" key on properties\n    const key = name.replace(/^data-cd-prop-/, \"\").replace(/^data-/, \"\");\n    out[key] = value;\n  }\n  return out;\n}\n\n/**\n * Browser detection identical to device-info.ts isBrowser. Inlined here\n * so this module has zero internal imports — easier to tree-shake\n * out of Node-only consumers, and the function body is trivial.\n */\nfunction isBrowserSafe(): boolean {\n  return (\n    typeof (globalThis as { window?: unknown }).window !== \"undefined\" &&\n    typeof (globalThis as { document?: unknown }).document !== \"undefined\"\n  );\n}\n\nfunction mintSessionId(): string {\n  // Inline the same shape used elsewhere — `<prefix>_<base32-ts><10-char-rand>`.\n  const ts = Date.now().toString(36);\n  return `sess_${ts}${randomChars(10)}`;\n}\n\n/**\n * Read first-touch acquisition signals off the current page. Captures:\n *   - utm_source, utm_medium, utm_campaign, utm_content, utm_term\n *     (the standard Google Analytics campaign params)\n *   - referrer (full URL — backend extracts hostname for grouping)\n *\n * Returns empty strings outside a browser, before navigation, or when\n * the page has none of these signals. Never throws — a malformed URL\n * or an iframe with no document.referrer falls through to empty.\n *\n * Pure function. Exported for unit testing acquisition extraction.\n */\nexport function captureAcquisition(): SessionAcquisition {\n  if (!isBrowserSafe()) return { ...EMPTY_ACQUISITION };\n\n  const result: SessionAcquisition = { ...EMPTY_ACQUISITION };\n\n  try {\n    const w = (globalThis as { window: Window }).window;\n    const params = new URLSearchParams(w.location.search ?? \"\");\n    result.utm_source = params.get(\"utm_source\") ?? \"\";\n    result.utm_medium = params.get(\"utm_medium\") ?? \"\";\n    result.utm_campaign = params.get(\"utm_campaign\") ?? \"\";\n    result.utm_content = params.get(\"utm_content\") ?? \"\";\n    result.utm_term = params.get(\"utm_term\") ?? \"\";\n    // Paid-traffic click IDs — captured alongside UTMs because many\n    // ad platforms (Performance Max, automated bidding) ship ONLY\n    // a click-id, no utm_*.\n    result.gclid = params.get(\"gclid\") ?? \"\";\n    result.fbclid = params.get(\"fbclid\") ?? \"\";\n    result.msclkid = params.get(\"msclkid\") ?? \"\";\n    result.ttclid = params.get(\"ttclid\") ?? \"\";\n    result.li_fat_id = params.get(\"li_fat_id\") ?? \"\";\n    result.twclid = params.get(\"twclid\") ?? \"\";\n  } catch {\n    // window.location can throw in sandboxed iframes / data: URLs\n  }\n\n  try {\n    const doc = (globalThis as { document: Document }).document;\n    if (typeof doc.referrer === \"string\") result.referrer = doc.referrer;\n  } catch {\n    // document.referrer is well-supported but defensive in case\n  }\n\n  return result;\n}\n","/**\n * Debug signal vocabulary per NorthStar §16.\n *\n * The SDK speaks a small fixed vocabulary of signals so the dashboard's\n * onboarding checklist can show \"we saw your first event\" without having\n * to parse free-form console output. When debug mode is enabled the\n * signals are also logged to the console so a developer doing\n * copy-paste integration sees actionable feedback live.\n *\n * Signal names are STABLE — adding new ones is fine, renaming is a\n * breaking change because the dashboard onboarding step keys off them.\n */\n\nexport type DebugSignal =\n  | \"sdk.configured\"\n  | \"sdk.first_event_sent\"\n  | \"sdk.invalid_key\"\n  | \"sdk.no_identity\"\n  | \"sdk.entitlement_cache_used\"\n  | \"sdk.purchase_evidence_sent\"\n  | \"sdk.environment_mismatch\"\n  | \"sdk.sensitive_property_warning\"\n  | \"sdk.property_coerced\"\n  | \"sdk.queue_persisted\"\n  | \"sdk.queue_restored\"\n  | \"sdk.flush_retry_scheduled\"\n  // Emitted when the queue drops a batch because the server returned\n  // a permanent 4xx (key revoked, malformed batch, etc.). Always loud,\n  // regardless of debug mode — see the console.error in crossdeck.ts.\n  | \"sdk.flush_permanent_failure\"\n  // Emitted when the server PARKS the SDK (HTTP 426 / sdk_version_unsupported):\n  // the wire dialect is too old. Distinct from flush_permanent_failure —\n  // events are HELD on-device (not dropped) and deliver on upgrade. The\n  // second signal channel (paired with the queue's one console line); the\n  // dashboard reads it to render the amber \"update to resume\" advisory.\n  | \"sdk.parked\"\n  | \"sdk.consent_changed\"\n  | \"sdk.consent_denied\"\n  | \"sdk.consent_dnt_applied\"\n  | \"sdk.pii_scrubbed\";\n\nexport interface DebugContext {\n  /** Free-form details surfaced under the signal — appId, key prefix, etc. */\n  [key: string]: unknown;\n}\n\n/**\n * Names that almost always indicate PII or secret data. Used by track()\n * to warn the developer when a property key looks dangerous. Per\n * NorthStar §15 these are reject/warn-on-sight values; we warn rather\n * than reject because the developer might genuinely want a property\n * called e.g. \"tokens_remaining\".\n */\nconst SENSITIVE_KEY_PATTERNS: readonly RegExp[] = [\n  /^email$/i,\n  /^password$/i,\n  /^token$/i,\n  /^secret$/i,\n  /^card$/i,\n  /^phone$/i,\n  /password/i,\n  /credit_?card/i,\n];\n\nexport function findSensitivePropertyKeys(\n  properties: Record<string, unknown> | undefined,\n): string[] {\n  if (!properties) return [];\n  const hits: string[] = [];\n  for (const k of Object.keys(properties)) {\n    if (SENSITIVE_KEY_PATTERNS.some((re) => re.test(k))) hits.push(k);\n  }\n  return hits;\n}\n\nexport interface DebugLogger {\n  enabled: boolean;\n  emit(signal: DebugSignal, message: string, context?: DebugContext): void;\n}\n\nexport class ConsoleDebugLogger implements DebugLogger {\n  enabled = false;\n  private seen = new Set<DebugSignal>();\n\n  emit(signal: DebugSignal, message: string, context?: DebugContext): void {\n    if (!this.enabled) return;\n    // For one-shot signals (sdk.configured, sdk.first_event_sent,\n    // sdk.environment_mismatch) suppress duplicates within a session\n    // so a chatty app doesn't spam the console with the same message.\n    if (ONCE_SIGNALS.has(signal)) {\n      if (this.seen.has(signal)) return;\n      this.seen.add(signal);\n    }\n    const ctx = context ? ` ${safeJson(context)}` : \"\";\n    // eslint-disable-next-line no-console\n    console.info(`[crossdeck:${signal}] ${message}${ctx}`);\n  }\n}\n\nconst ONCE_SIGNALS = new Set<DebugSignal>([\n  \"sdk.configured\",\n  \"sdk.first_event_sent\",\n  \"sdk.environment_mismatch\",\n]);\n\nfunction safeJson(obj: unknown): string {\n  try {\n    return JSON.stringify(obj);\n  } catch {\n    return \"[unserialisable context]\";\n  }\n}\n","/**\n * Property validation + coercion for `track()` events.\n *\n * Why this exists: the public `EventProperties` type is\n * `Record<string, unknown>` — developers can (and will) put anything\n * in there. Without a sanitiser, JSON.stringify at flush time will\n * throw on a function, a BigInt, a circular reference, or a Map, and\n * the WHOLE BATCH gets re-buffered every flush attempt until the\n * offending event is manually purged. Stripe-grade SDKs sanitise at\n * the call site so one bad property can't poison the queue.\n *\n * Contract:\n *   - Drop functions / symbols / undefined values (with a warning).\n *   - Coerce Date → ISO string, BigInt → string, Error → { name, message, stack }.\n *   - Truncate string values longer than `maxStringLength` (default 1024).\n *   - Replace circular refs with `\"[circular]\"`.\n *   - Cap total serialised size at `maxBatchPropertyBytes` (default 8192).\n *     Past the cap, properties are dropped (newest first) and a\n *     `__truncated` marker is added.\n *   - Reserved keys (`sessionId`, `referrer`, `utm_*`, anything with\n *     `__` prefix) are NOT validated specially — they're treated like\n *     any other property. The SDK's own enrichment goes through this\n *     same path so it gets the same safety guarantees.\n *\n * Pure function — no I/O, no console calls. Caller decides how to\n * surface warnings (debug log, telemetry counter, etc.).\n */\n\nimport type { EventProperties } from \"./types\";\n\nexport interface ValidationOptions {\n  maxStringLength?: number;\n  maxBatchPropertyBytes?: number;\n  /**\n   * Hard cap on depth of object/array nesting. Anything deeper is\n   * coerced to \"[depth-exceeded]\". Defaults to 5 — covers most real\n   * shapes (e.g. nested API responses) without letting a circular\n   * structure consume the call stack via recursion.\n   */\n  maxDepth?: number;\n}\n\nexport interface ValidationWarning {\n  kind:\n    | \"dropped_function\"\n    | \"dropped_symbol\"\n    | \"dropped_undefined\"\n    | \"coerced_date\"\n    | \"coerced_bigint\"\n    | \"coerced_error\"\n    | \"coerced_map\"\n    | \"coerced_set\"\n    | \"truncated_string\"\n    | \"circular_reference\"\n    | \"depth_exceeded\"\n    | \"non_serialisable\"\n    | \"size_cap_exceeded\";\n  key: string;\n}\n\nexport interface ValidationResult {\n  /** Sanitised properties safe to JSON.stringify. */\n  properties: EventProperties;\n  /** Per-issue warnings — surface in debug mode or diagnostics. */\n  warnings: ValidationWarning[];\n}\n\nconst DEFAULT_MAX_STRING = 1024;\nconst DEFAULT_MAX_BYTES = 8 * 1024;\nconst DEFAULT_MAX_DEPTH = 5;\n\n/**\n * Validate + coerce a property bag. Always returns a NEW object — the\n * caller's input is never mutated.\n */\nexport function validateEventProperties(\n  input: EventProperties | undefined,\n  options: ValidationOptions = {},\n): ValidationResult {\n  const warnings: ValidationWarning[] = [];\n  if (!input) return { properties: {}, warnings };\n\n  const maxStringLength = options.maxStringLength ?? DEFAULT_MAX_STRING;\n  const maxBatchPropertyBytes = options.maxBatchPropertyBytes ?? DEFAULT_MAX_BYTES;\n  const maxDepth = options.maxDepth ?? DEFAULT_MAX_DEPTH;\n\n  // Ancestor-stack circular detection: add the object/array to `seen`\n  // before recursing into its children, REMOVE it after. A re-encounter\n  // while a value is still in the set means it's an ancestor of the\n  // current node (a real cycle). Sibling sharing — two properties\n  // pointing at the same sub-object (a legitimate DAG, e.g. an enriched\n  // event with `{ user: shared, owner: shared }`) — is NOT a cycle and\n  // must NOT be flagged. Pre-fix the WeakSet was add-on-visit /\n  // never-delete, so the second sibling visit always tripped the\n  // `[circular]` branch and silently dropped real data with a\n  // misleading warning. Audit P1 #18.\n  const seen = new Set<object>();\n\n  const visit = (\n    value: unknown,\n    key: string,\n    depth: number,\n  ): { keep: boolean; value: unknown } => {\n    if (depth > maxDepth) {\n      warnings.push({ kind: \"depth_exceeded\", key });\n      return { keep: true, value: \"[depth-exceeded]\" };\n    }\n    if (value === null) return { keep: true, value: null };\n    const t = typeof value;\n    if (t === \"string\") {\n      const s = value as string;\n      if (s.length > maxStringLength) {\n        warnings.push({ kind: \"truncated_string\", key });\n        return { keep: true, value: s.slice(0, maxStringLength - 1) + \"…\" };\n      }\n      return { keep: true, value: s };\n    }\n    if (t === \"number\") {\n      // Strip NaN / ±Infinity — they JSON-stringify as null which is\n      // surprising. Convert to null explicitly with a warning.\n      if (!Number.isFinite(value as number)) {\n        warnings.push({ kind: \"non_serialisable\", key });\n        return { keep: true, value: null };\n      }\n      return { keep: true, value };\n    }\n    if (t === \"boolean\") return { keep: true, value };\n    if (t === \"bigint\") {\n      warnings.push({ kind: \"coerced_bigint\", key });\n      return { keep: true, value: (value as bigint).toString() };\n    }\n    if (t === \"function\") {\n      warnings.push({ kind: \"dropped_function\", key });\n      return { keep: false, value: undefined };\n    }\n    if (t === \"symbol\") {\n      warnings.push({ kind: \"dropped_symbol\", key });\n      return { keep: false, value: undefined };\n    }\n    if (t === \"undefined\") {\n      warnings.push({ kind: \"dropped_undefined\", key });\n      return { keep: false, value: undefined };\n    }\n\n    // Objects from here on.\n    if (value instanceof Date) {\n      warnings.push({ kind: \"coerced_date\", key });\n      const iso = Number.isFinite(value.getTime()) ? value.toISOString() : null;\n      return { keep: true, value: iso };\n    }\n    if (value instanceof Error) {\n      warnings.push({ kind: \"coerced_error\", key });\n      return {\n        keep: true,\n        value: {\n          name: value.name,\n          message: value.message,\n          stack: typeof value.stack === \"string\" ? value.stack.slice(0, maxStringLength) : undefined,\n        },\n      };\n    }\n    if (value instanceof Map) {\n      warnings.push({ kind: \"coerced_map\", key });\n      const obj: Record<string, unknown> = {};\n      for (const [k, v] of value.entries()) {\n        const subKey = typeof k === \"string\" ? k : String(k);\n        const result = visit(v, `${key}.${subKey}`, depth + 1);\n        if (result.keep) obj[subKey] = result.value;\n      }\n      return { keep: true, value: obj };\n    }\n    if (value instanceof Set) {\n      warnings.push({ kind: \"coerced_set\", key });\n      const arr: unknown[] = [];\n      let i = 0;\n      for (const v of value.values()) {\n        const result = visit(v, `${key}[${i}]`, depth + 1);\n        if (result.keep) arr.push(result.value);\n        i++;\n      }\n      return { keep: true, value: arr };\n    }\n\n    if (Array.isArray(value)) {\n      if (seen.has(value)) {\n        warnings.push({ kind: \"circular_reference\", key });\n        return { keep: true, value: \"[circular]\" };\n      }\n      seen.add(value);\n      const out: unknown[] = [];\n      for (let i = 0; i < value.length; i++) {\n        const result = visit(value[i], `${key}[${i}]`, depth + 1);\n        if (result.keep) out.push(result.value);\n      }\n      // Delete on exit — the array is no longer an ancestor of any\n      // sibling visit. Sibling DAG sharing is fine; only true cycles\n      // (parent re-encountered via a descendant) should flag.\n      seen.delete(value);\n      return { keep: true, value: out };\n    }\n\n    if (t === \"object\") {\n      const obj = value as Record<string, unknown>;\n      if (seen.has(obj)) {\n        warnings.push({ kind: \"circular_reference\", key });\n        return { keep: true, value: \"[circular]\" };\n      }\n      seen.add(obj);\n      const out: Record<string, unknown> = {};\n      for (const k of Object.keys(obj)) {\n        const result = visit(obj[k], `${key}.${k}`, depth + 1);\n        if (result.keep) out[k] = result.value;\n      }\n      // Delete on exit — see the array branch above for rationale.\n      seen.delete(obj);\n      return { keep: true, value: out };\n    }\n\n    // Unknown exotic type — try string coercion.\n    warnings.push({ kind: \"non_serialisable\", key });\n    try {\n      return { keep: true, value: String(value) };\n    } catch {\n      return { keep: false, value: undefined };\n    }\n  };\n\n  const cleaned: Record<string, unknown> = {};\n  for (const k of Object.keys(input)) {\n    const result = visit(input[k], k, 0);\n    if (result.keep) cleaned[k] = result.value;\n  }\n\n  // Final pass: enforce overall byte cap. JSON.stringify the cleaned\n  // bag; if too large, drop properties (largest-first) until under.\n  // We use byteLength via UTF-8 since the wire is JSON-over-HTTPS.\n  const serialised = safeStringify(cleaned);\n  if (serialised && byteLength(serialised) > maxBatchPropertyBytes) {\n    warnings.push({ kind: \"size_cap_exceeded\", key: \"*\" });\n    const sizes = Object.keys(cleaned)\n      .map((k) => ({ k, size: byteLength(safeStringify(cleaned[k]) ?? \"\") }))\n      .sort((a, b) => b.size - a.size);\n    let currentSize = byteLength(serialised);\n    for (const { k } of sizes) {\n      if (currentSize <= maxBatchPropertyBytes) break;\n      currentSize -= sizes.find((s) => s.k === k)!.size;\n      delete cleaned[k];\n    }\n    cleaned.__truncated = true;\n  }\n\n  return { properties: cleaned, warnings };\n}\n\nfunction safeStringify(v: unknown): string | null {\n  try {\n    return JSON.stringify(v) ?? null;\n  } catch {\n    return null;\n  }\n}\n\nfunction byteLength(s: string): number {\n  if (typeof TextEncoder !== \"undefined\") {\n    return new TextEncoder().encode(s).length;\n  }\n  // Fallback for runtimes without TextEncoder (very old): assume\n  // worst-case 4 bytes/char to stay conservative.\n  return s.length * 4;\n}\n","/**\n * Super properties + group analytics — Mixpanel pattern.\n *\n * **Super properties** are key/value pairs the developer registers ONCE\n * via `Crossdeck.register({ plan: \"pro\" })` that get attached to every\n * subsequent event of that SDK instance. They're the single most-used\n * feature in Mixpanel-style analytics: \"every event from this user\n * should have `plan` and `appVersion` on it\" instead of remembering to\n * pass them on every track() call.\n *\n * **Groups** are organisational identifiers: a customer might belong to\n * an `org` (\"acme\"), a `team` (\"design\"), and a `plan` (\"enterprise\").\n * Each event carries `$groups.{type}: id` so B2B dashboards can pivot:\n * \"Acme's team:design has fired 142 paywall_shown events this week\".\n *\n * Both surfaces live in this module because they share two traits:\n *   - They're set once, attached to every event automatically.\n *   - They persist across reloads via the same storage layer the SDK\n *     uses for identity (localStorage + cookie redundancy doesn't make\n *     sense here — these are larger and live longer; localStorage only\n *     is fine).\n *\n * The store is reset on `Crossdeck.reset()` (logout) — both super\n * properties and groups are cleared because their lifetime is tied\n * to the identified user, not the SDK instance.\n */\n\nimport type { KeyValueStorage } from \"./types\";\n\nconst KEY_SUPER = \"super_props\";\nconst KEY_GROUPS = \"groups\";\n\nexport class SuperPropertyStore {\n  private superProps: Record<string, unknown> = {};\n  private groups: Record<string, { id: string; traits?: Record<string, unknown> }> = {};\n\n  constructor(\n    private readonly storage: KeyValueStorage,\n    private readonly prefix: string,\n  ) {\n    this.superProps = readJson(storage, prefix + KEY_SUPER) ?? {};\n    this.groups = readJson(storage, prefix + KEY_GROUPS) ?? {};\n  }\n\n  // ---------- super properties ----------\n\n  /**\n   * Merge new keys into the super-property bag. Returns a snapshot of\n   * the resulting bag. Values that are `null` are deleted (Mixpanel\n   * semantics — explicit null = \"stop tracking this key\").\n   */\n  register(props: Record<string, unknown>): Record<string, unknown> {\n    for (const [k, v] of Object.entries(props)) {\n      if (v === null) {\n        delete this.superProps[k];\n      } else if (v !== undefined) {\n        this.superProps[k] = v;\n      }\n    }\n    writeJson(this.storage, this.prefix + KEY_SUPER, this.superProps);\n    return { ...this.superProps };\n  }\n\n  /** Remove a single super-property key. Idempotent. */\n  unregister(key: string): void {\n    if (key in this.superProps) {\n      delete this.superProps[key];\n      writeJson(this.storage, this.prefix + KEY_SUPER, this.superProps);\n    }\n  }\n\n  /** Snapshot of the current super-property bag. */\n  getSuperProperties(): Record<string, unknown> {\n    return { ...this.superProps };\n  }\n\n  // ---------- groups ----------\n\n  /**\n   * Set a group membership. Passing `id: null` clears the membership\n   * for that group type — the SDK stops attaching it to events.\n   */\n  setGroup(type: string, id: string | null, traits?: Record<string, unknown>): void {\n    if (id === null) {\n      delete this.groups[type];\n    } else {\n      this.groups[type] = traits !== undefined ? { id, traits } : { id };\n    }\n    writeJson(this.storage, this.prefix + KEY_GROUPS, this.groups);\n  }\n\n  /**\n   * Snapshot of the current groups map, keyed by group type. Returned\n   * shape mirrors what the SDK attaches to every event as\n   * `$groups.{type}`. The `traits` sub-object is the most-recent\n   * traits payload passed to `setGroup` for that type; null when none.\n   */\n  getGroups(): Record<string, { id: string; traits?: Record<string, unknown> }> {\n    return JSON.parse(JSON.stringify(this.groups));\n  }\n\n  /**\n   * The flat `{ type: id }` projection used for event-attachment. Stable\n   * for fast every-event merge — we don't want to JSON-clone on each\n   * track() call.\n   */\n  getGroupIds(): Record<string, string> {\n    const out: Record<string, string> = {};\n    for (const [type, info] of Object.entries(this.groups)) {\n      out[type] = info.id;\n    }\n    return out;\n  }\n\n  /** Wipe both bags. Called by Crossdeck.reset() (logout). */\n  clear(): void {\n    this.superProps = {};\n    this.groups = {};\n    try {\n      this.storage.removeItem(this.prefix + KEY_SUPER);\n    } catch {\n      // ignore\n    }\n    try {\n      this.storage.removeItem(this.prefix + KEY_GROUPS);\n    } catch {\n      // ignore\n    }\n  }\n}\n\nfunction readJson<T>(storage: KeyValueStorage, key: string): T | null {\n  let raw: string | null;\n  try {\n    raw = storage.getItem(key);\n  } catch {\n    return null;\n  }\n  if (!raw) return null;\n  try {\n    return JSON.parse(raw) as T;\n  } catch {\n    return null;\n  }\n}\n\nfunction writeJson(storage: KeyValueStorage, key: string, value: unknown): void {\n  try {\n    storage.setItem(key, JSON.stringify(value));\n  } catch {\n    // Quota / private mode — silent degrade. In-memory still holds\n    // the current state; cross-tab sync just loses fidelity.\n  }\n}\n","/**\n * Internal-traffic browser opt-out.\n *\n * Visiting any tracked page with `?crossdeck_internal=1` persists a flag in\n * localStorage; every event from this browser is then tagged with the\n * reserved `$crossdeck_internal` property, which ingest reads to classify\n * the event as internal. `?crossdeck_internal=0` clears it.\n *\n * This covers the cases identity- and IP-based rules miss: a dynamic home\n * IP, or browsing your own product logged out. It's per-browser and\n * self-service — no dashboard change required to set or clear it.\n *\n * Design: the URL is parsed ONCE at init (processInternalOptOutUrl) to\n * set/clear the persisted flag; each event then does a cheap localStorage\n * read (isInternalOptOut) so clearing takes effect immediately. Everything\n * is wrapped — SSR / Node / private-mode / disabled-storage all degrade to\n * \"not opted out\", never a throw.\n */\n\n/**\n * The reserved property key stamped on every event while opted out. Must\n * match the backend contract (backend/src/api/lib/actor-type.ts:\n * INTERNAL_OPT_OUT_PROPERTY). `$`-prefixed so it can't collide with a\n * developer's own property names.\n */\nexport const INTERNAL_OPT_OUT_PROPERTY = \"$crossdeck_internal\";\n\nconst STORAGE_KEY = \"crossdeck.internalOptOut\";\n\nfunction localStore(): Storage | null {\n  try {\n    return typeof localStorage !== \"undefined\" ? localStorage : null;\n  } catch {\n    return null;\n  }\n}\n\n/**\n * Run once at init. A `?crossdeck_internal=1` (or `=true`) in the current\n * URL persists the opt-out flag; `=0` (or `=false`) clears it. No param →\n * leaves the existing persisted state untouched.\n */\nexport function processInternalOptOutUrl(): void {\n  try {\n    const search = typeof location !== \"undefined\" ? location.search : \"\";\n    const params = new URLSearchParams(search || \"\");\n    if (!params.has(\"crossdeck_internal\")) return;\n    const store = localStore();\n    if (!store) return;\n    const v = params.get(\"crossdeck_internal\");\n    if (v === \"1\" || v === \"true\") {\n      store.setItem(STORAGE_KEY, \"1\");\n    } else if (v === \"0\" || v === \"false\") {\n      store.removeItem(STORAGE_KEY);\n    }\n  } catch {\n    /* no-op — never let opt-out handling break init */\n  }\n}\n\n/** Cheap per-event check: is this browser currently opted out? */\nexport function isInternalOptOut(): boolean {\n  try {\n    return localStore()?.getItem(STORAGE_KEY) === \"1\";\n  } catch {\n    return false;\n  }\n}\n","/**\n * Web Vitals capture — LCP, INP, CLS, FCP, TTFB.\n *\n * Why hand-rolled, not the `web-vitals` library: that library is ~6 KB\n * gz and pulls in handlers for every metric ever published by Google,\n * many of which (FID, soft-navigation, etc.) are deprecated or\n * superseded. The five metrics below are the ones every dashboard\n * actually renders — LCP for perceived load, INP for responsiveness,\n * CLS for visual stability, FCP for first paint, TTFB for backend\n * speed. Total here is ~80 lines, zero runtime deps.\n *\n * Each metric fires as a Crossdeck event:\n *   `webvitals.lcp`  → properties: { valueMs }\n *   `webvitals.inp`  → properties: { valueMs }\n *   `webvitals.cls`  → properties: { value }      // unitless score\n *   `webvitals.fcp`  → properties: { valueMs }\n *   `webvitals.ttfb` → properties: { valueMs }\n *\n * Capture timing:\n *   - FCP, TTFB fire once after the page settles (typically <1s).\n *   - LCP, CLS fire at page hidden (visibilitychange→hidden) — the\n *     final value is only known when the user stops interacting\n *     with the page.\n *   - INP samples interactions and fires at page hidden.\n *\n * No-op outside browsers (PerformanceObserver missing) or when the\n * `autoTrack.webVitals` flag is false.\n */\n\ntype Reporter = (name: string, properties: Record<string, unknown>) => void;\n\nexport interface WebVitalsConfig {\n  enabled: boolean;\n  /**\n   * Cap on the number of metric events emitted per page. Defaults to\n   * one per metric type (so 5 max). Defends against a rogue browser\n   * firing 100 PerformanceObserver entries.\n   */\n  maxEventsPerMetric?: number;\n}\n\nexport class WebVitalsTracker {\n  private observers: PerformanceObserver[] = [];\n  private flushed = new Set<string>();\n  private cls = 0;\n  private clsEntries: PerformanceEntry[] = [];\n  private inp = 0;\n  private cleanups: Array<() => void> = [];\n\n  constructor(\n    private readonly cfg: WebVitalsConfig,\n    private readonly report: Reporter,\n  ) {}\n\n  install(): void {\n    if (!this.cfg.enabled) return;\n    if (typeof PerformanceObserver === \"undefined\") return;\n    if (typeof globalThis === \"undefined\" || !(\"document\" in globalThis)) return;\n\n    const doc = (globalThis as { document: Document }).document;\n\n    // TTFB / FCP — fire as soon as we have data.\n    try {\n      const navObserver = new PerformanceObserver((list) => {\n        for (const entry of list.getEntries()) {\n          const e = entry as PerformanceNavigationTiming;\n          if (e.responseStart > 0 && !this.flushed.has(\"ttfb\")) {\n            this.flushed.add(\"ttfb\");\n            this.report(\"webvitals.ttfb\", { valueMs: Math.round(e.responseStart - e.startTime) });\n          }\n        }\n      });\n      navObserver.observe({ type: \"navigation\", buffered: true });\n      this.observers.push(navObserver);\n    } catch {\n      // not supported — fall through\n    }\n\n    try {\n      const paintObserver = new PerformanceObserver((list) => {\n        for (const entry of list.getEntries()) {\n          if (entry.name === \"first-contentful-paint\" && !this.flushed.has(\"fcp\")) {\n            this.flushed.add(\"fcp\");\n            this.report(\"webvitals.fcp\", { valueMs: Math.round(entry.startTime) });\n          }\n        }\n      });\n      paintObserver.observe({ type: \"paint\", buffered: true });\n      this.observers.push(paintObserver);\n    } catch {\n      // not supported\n    }\n\n    // LCP — track the LATEST entry; flush at page hidden.\n    let lcpValue = 0;\n    try {\n      const lcpObserver = new PerformanceObserver((list) => {\n        const entries = list.getEntries();\n        const last = entries[entries.length - 1];\n        if (last) lcpValue = last.startTime;\n      });\n      lcpObserver.observe({ type: \"largest-contentful-paint\", buffered: true });\n      this.observers.push(lcpObserver);\n    } catch {\n      // not supported\n    }\n\n    // CLS — accumulate per-session layout-shift score.\n    try {\n      const clsObserver = new PerformanceObserver((list) => {\n        for (const entry of list.getEntries()) {\n          // Layout-shift entries have a `value` field (cast loosely\n          // since the typed DOM lib doesn't always expose it).\n          const e = entry as PerformanceEntry & { value?: number; hadRecentInput?: boolean };\n          if (typeof e.value === \"number\" && !e.hadRecentInput) {\n            this.cls += e.value;\n            this.clsEntries.push(entry);\n          }\n        }\n      });\n      clsObserver.observe({ type: \"layout-shift\", buffered: true });\n      this.observers.push(clsObserver);\n    } catch {\n      // not supported\n    }\n\n    // INP — find the worst-case interaction duration.\n    try {\n      const eventObserver = new PerformanceObserver((list) => {\n        for (const entry of list.getEntries()) {\n          const e = entry as PerformanceEntry & { duration: number; interactionId?: number };\n          if (e.interactionId && e.duration > this.inp) {\n            this.inp = e.duration;\n          }\n        }\n      });\n      // The `event` type is the modern way; `first-input` is a fallback.\n      // Wrapped in try because Safari < 16.4 throws on `event` type.\n      try {\n        eventObserver.observe({ type: \"event\", buffered: true, durationThreshold: 16 } as PerformanceObserverInit);\n      } catch {\n        eventObserver.observe({ type: \"first-input\", buffered: true });\n      }\n      this.observers.push(eventObserver);\n    } catch {\n      // not supported\n    }\n\n    // Flush LCP / CLS / INP at page-hidden — the final values are only\n    // known after the user stops interacting.\n    const flush = (): void => {\n      // Unload-time defense: this fires on pagehide / visibilitychange→hidden,\n      // and `report` enqueues (and can flush) through browser transport APIs\n      // that in-app browsers (Instagram/Facebook's `iabjs://`, …) hook and\n      // throw from during their own teardown. Swallow so the host shell's\n      // teardown noise never propagates to window.onerror and gets\n      // self-reported as the developer's error. The page is going away.\n      try {\n        if (lcpValue > 0 && !this.flushed.has(\"lcp\")) {\n          this.flushed.add(\"lcp\");\n          this.report(\"webvitals.lcp\", { valueMs: Math.round(lcpValue) });\n        }\n        if (this.cls > 0 && !this.flushed.has(\"cls\")) {\n          this.flushed.add(\"cls\");\n          this.report(\"webvitals.cls\", { value: Math.round(this.cls * 1000) / 1000 });\n        }\n        if (this.inp > 0 && !this.flushed.has(\"inp\")) {\n          this.flushed.add(\"inp\");\n          this.report(\"webvitals.inp\", { valueMs: Math.round(this.inp) });\n        }\n      } catch {\n        /* unload teardown — swallow */\n      }\n    };\n    const onHidden = (): void => {\n      if (doc.visibilityState === \"hidden\") flush();\n    };\n    doc.addEventListener(\"visibilitychange\", onHidden);\n    (globalThis as { window: Window }).window.addEventListener(\"pagehide\", flush);\n    this.cleanups.push(() => {\n      doc.removeEventListener(\"visibilitychange\", onHidden);\n      (globalThis as { window: Window }).window.removeEventListener(\"pagehide\", flush);\n    });\n  }\n\n  uninstall(): void {\n    for (const o of this.observers) {\n      try {\n        o.disconnect();\n      } catch {\n        // ignore\n      }\n    }\n    this.observers = [];\n    for (const fn of this.cleanups.splice(0)) {\n      try {\n        fn();\n      } catch {\n        // ignore\n      }\n    }\n  }\n}\n","/**\n * Consent gating — GDPR / CCPA-grade kill switches.\n *\n * Three independent dimensions, each defaulting to \"granted\" but\n * runtime-overridable:\n *\n *   analytics  — track(), identify(), heartbeat(), session/page auto-\n *                emissions. Off → events drop silently, no network\n *                calls fire.\n *   marketing  — paid-traffic click IDs (gclid/fbclid/etc) and\n *                acquisition referrer URL. Off → these get scrubbed\n *                before they ever land in the event bag.\n *   errors     — error / breadcrumb / Web Vitals capture. Off → no\n *                webvitals.* events emitted, no error reporting (when\n *                Phase 3 errors land).\n *\n * Why this granularity: real consent banners offer \"Analytics\",\n * \"Marketing\", \"Functional\" as separate boxes. The SDK has to match.\n *\n * Default state: every dimension is granted. The developer must\n * explicitly call `Crossdeck.consent({ analytics: false })` before\n * the first event to opt OUT — same convention as Google Tag Manager\n * Consent Mode. To start in deny mode, call `init(...)` then\n * immediately `consent({ analytics: false, marketing: false, errors:\n * false })` before any user activity.\n *\n * DNT (Do Not Track) browser header is checked once at init and\n * applied as an automatic deny across all dimensions when\n * `respectDnt: true` is set in CrossdeckOptions (default false because\n * the industry has effectively deprecated DNT — but opt-in support\n * is the polite default for privacy-first apps).\n */\n\nexport interface ConsentState {\n  analytics: boolean;\n  marketing: boolean;\n  errors: boolean;\n}\n\nconst ALL_GRANTED: ConsentState = {\n  analytics: true,\n  marketing: true,\n  errors: true,\n};\n\nexport class ConsentManager {\n  private state: ConsentState = { ...ALL_GRANTED };\n  private dntDenied = false;\n  /** GPC (navigator.globalPrivacyControl) — a legally binding opt-out in\n   *  several jurisdictions, so it is honoured ALWAYS: no flag, no opt-in.\n   *  Core enforcement, never a feature. (DNT stays behind `respectDnt`\n   *  because it is advisory and widely spoofed.) */\n  private gpcDenied = false;\n  // Explicit identity opt-in (the consent widget's \"Recognize me\" switch).\n  // Default TRUE so the CORE SDK identifies exactly as it does today — this is\n  // additive. The consent-mode guest build (Path B) sets it false at boot and\n  // flips it true only when the visitor opts in. `identify()` checks it.\n  private identityOptInState = true;\n\n  constructor(options?: { respectDnt?: boolean }) {\n    if (options?.respectDnt && this.detectDnt()) {\n      this.dntDenied = true;\n      this.state = { analytics: false, marketing: false, errors: false };\n      this.identityOptInState = false;\n    }\n  }\n\n  /**\n   * Set the explicit identity opt-in. DNT forces it off and locks it — once the\n   * browser says \"don't track\", we don't recognise, even if code disagrees.\n   */\n  setIdentityOptIn(v: boolean): void {\n    if (this.signalDenied) {\n      this.identityOptInState = false;\n      return;\n    }\n    this.identityOptInState = v;\n  }\n\n  /** Whether the visitor has explicitly opted in to being recognised. */\n  get identityOptIn(): boolean {\n    return this.signalDenied ? false : this.identityOptInState;\n  }\n\n  /**\n   * Merge new dimensions onto the current state. Returns the resulting\n   * snapshot. DNT-derived denies cannot be flipped back on by a `set`\n   * call — once the browser says \"don't track\", we don't track even if\n   * the developer code disagrees. That's the contract.\n   */\n  set(partial: Partial<ConsentState>): ConsentState {\n    if (this.signalDenied) return { ...this.state };\n    for (const k of Object.keys(partial) as Array<keyof ConsentState>) {\n      const v = partial[k];\n      if (typeof v === \"boolean\") this.state[k] = v;\n    }\n    return { ...this.state };\n  }\n\n  /** Snapshot of the current state. */\n  get(): ConsentState {\n    return { ...this.state };\n  }\n\n  /** Convenience getters for hot paths. */\n  get analytics(): boolean {\n    return this.state.analytics;\n  }\n  get marketing(): boolean {\n    return this.state.marketing;\n  }\n  get errors(): boolean {\n    return this.state.errors;\n  }\n\n  /** True iff the constructor detected and applied DNT. */\n  get isDntDenied(): boolean {\n    return this.signalDenied;\n  }\n\n  /** Either binding signal says no. */\n  private get signalDenied(): boolean {\n    return this.dntDenied || this.gpcDenied;\n  }\n\n  /** GPC — read defensively; a hostile/absent navigator must never throw. */\n  private detectGpc(): boolean {\n    try {\n      return (\n        typeof navigator !== \"undefined\" &&\n        (navigator as Navigator & { globalPrivacyControl?: boolean })\n          .globalPrivacyControl === true\n      );\n    } catch {\n      return false;\n    }\n  }\n  private detectDnt(): boolean {\n    try {\n      const nav = (globalThis as { navigator?: Navigator }).navigator;\n      if (!nav) return false;\n      // Three historical spellings: navigator.doNotTrack (standard),\n      // navigator.msDoNotTrack (IE), window.doNotTrack (Safari).\n      // All return \"1\" / \"yes\" when the user has DNT enabled.\n      const sources = [\n        (nav as Navigator & { doNotTrack?: string }).doNotTrack,\n        (nav as Navigator & { msDoNotTrack?: string }).msDoNotTrack,\n        (globalThis as { doNotTrack?: string }).doNotTrack,\n      ];\n      return sources.some((v) => v === \"1\" || v === \"yes\");\n    } catch {\n      return false;\n    }\n  }\n}\n\n// ============================================================\n// PII scrubbing — URL + property values\n// ============================================================\n\n/**\n * Email-shaped pattern. Reasonably restrictive — matches RFC 5322's\n * \"obs-local-part\" common case (the practical 99% of emails). We\n * deliberately don't try to match every legal email; the goal is\n * \"if it looks like an email, scrub it\" without false positives.\n */\nconst EMAIL_PATTERN =\n  /[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}/g;\n\n/**\n * Card-number shaped pattern. Matches sequences of 13-19 digits that\n * could be split by space or hyphen — the format every payment form\n * accepts. We don't validate Luhn; this is best-effort scrubbing,\n * not card-data tokenisation. If you're handling actual PAN data\n * you should not be passing it through analytics in the first place.\n */\n// Anchor on a digit at both ends so trailing separators (space / hyphen)\n// aren't pulled into the match — otherwise \"4242 4242 4242 4242 today\"\n// scrubs as \"<card>today\" instead of \"<card> today\".\nconst CARD_PATTERN = /\\b\\d(?:[ -]?\\d){12,18}\\b/g;\n\n// Sentinel tokens — aligned with backend/src/api/lib/scrub.ts which uses\n// <email>, <card>, <uuid>, <cdcust>, <crossdeck_secret_key>, <aws_access_key>.\n// Mismatched tokens between the SDK scrub and the backend's defence-in-\n// depth scrub would split dashboard aggregation (the same event arriving\n// from two paths would carry two different sentinels).\nconst REPLACEMENT_EMAIL = \"<email>\";\nconst REPLACEMENT_CARD = \"<card>\";\n\n/**\n * Scrub a single string value: replace email-shaped substrings with\n * `<email>` and card-number-shaped substrings with `<card>`. Returns\n * the original string when nothing matched.\n *\n * Implementation note: we call `.replace()` unconditionally rather than\n * gating on `.test()`. The /g regexes are module-level so `.test()`\n * carries `lastIndex` state between calls — a prior match leaves\n * `lastIndex` mid-string and the next `.test()` can falsely return\n * false on a string that DOES match. `.replace(/g)` always scans the\n * full string regardless of `lastIndex`, so dropping the test-guard\n * removes the sharp edge at zero cost (when nothing matches, replace\n * returns the same `(===)` string).\n */\nexport function scrubPii(value: string): string {\n  if (!value) return value;\n  return value\n    .replace(EMAIL_PATTERN, REPLACEMENT_EMAIL)\n    .replace(CARD_PATTERN, REPLACEMENT_CARD);\n}\n\n/**\n * Walk an event's properties and replace PII-shaped strings in place.\n * Returns a new object with strings scrubbed; non-string values pass\n * through unchanged.\n *\n * Defensive copy — the input is never altered. Caller can pass the\n * result straight to the queue.\n *\n * Recursive: nested plain objects + arrays are walked. Without this,\n * an event like `{ user: { email: \"wes@…\" } }` would ship the email\n * unscrubbed because the top-level value is an object, not a string.\n * Pre-fix this was the SDK's #1 PII leak vector — every captured-error\n * report ships nested `frames[]` / `breadcrumbs[]` / `context{}` /\n * `http{}` shapes through here. Date / Map / Set / Error / class\n * instances pass through untouched (those are the\n * `validateEventProperties` sanitiser's job — this is the PII regex\n * pass only).\n */\nexport function scrubPiiFromProperties(\n  properties: Record<string, unknown>,\n): Record<string, unknown> {\n  const out: Record<string, unknown> = {};\n  for (const k of Object.keys(properties)) {\n    out[k] = scrubValue(properties[k]);\n  }\n  return out;\n}\n\nfunction scrubValue(v: unknown): unknown {\n  if (typeof v === \"string\") return scrubPii(v);\n  if (Array.isArray(v)) return v.map(scrubValue);\n  if (v && typeof v === \"object\" && (v as object).constructor === Object) {\n    // Plain objects only — Date, Map, Set, Error, RegExp, class\n    // instances are left untouched so we don't accidentally mutate\n    // an Error's `message` and confuse the downstream error reporter.\n    return scrubPiiFromProperties(v as Record<string, unknown>);\n  }\n  return v;\n}\n","/**\n * Breadcrumb ring buffer — context attached to every error report.\n *\n * Sentry / Datadog / Bugsnag all ship the same idea: keep a rolling\n * record of the last N \"things the user did\" (page views, clicks,\n * custom events, network calls, console logs). When an error fires,\n * attach the buffer so the engineer reading the error can see exactly\n * how the user got into the broken state. The single most powerful\n * debugging signal in error monitoring — without breadcrumbs, errors\n * are stack traces with no story.\n *\n * Implementation: a circular buffer with a fixed cap. Old entries are\n * evicted as new ones arrive. The default cap (50) is enough to cover\n * ~5 minutes of typical user activity without ballooning the error\n * payload — Sentry uses 100 by default but the SDK is more aggressive\n * about size since we ship breadcrumbs over the wire with every error,\n * not as a separate batch.\n *\n * Privacy: breadcrumbs auto-emit from the same auto-tracking sources\n * as analytics events (page.viewed, element.clicked). Those already\n * skip password fields, form inputs, and cd-noTrack subtrees. Custom\n * crumbs added via Crossdeck.addBreadcrumb() pass through the same\n * property sanitiser as track() events.\n */\n\nexport type BreadcrumbCategory =\n  | \"navigation\"\n  | \"ui.click\"\n  | \"ui.input\"\n  | \"http\"\n  | \"console\"\n  | \"custom\"\n  | \"info\";\n\nexport type BreadcrumbLevel = \"debug\" | \"info\" | \"warning\" | \"error\";\n\nexport interface Breadcrumb {\n  /** epoch ms */\n  timestamp: number;\n  category: BreadcrumbCategory;\n  level?: BreadcrumbLevel;\n  /** Short human-readable description. */\n  message?: string;\n  /** Arbitrary key/value context for the crumb. */\n  data?: Record<string, unknown>;\n}\n\nexport class BreadcrumbBuffer {\n  private items: Breadcrumb[] = [];\n  constructor(private readonly maxSize: number = 50) {}\n\n  add(crumb: Breadcrumb): void {\n    this.items.push(crumb);\n    if (this.items.length > this.maxSize) {\n      this.items.shift();\n    }\n  }\n\n  /** Defensive copy — caller can read freely without mutating buffer state. */\n  snapshot(): Breadcrumb[] {\n    return this.items.slice();\n  }\n\n  clear(): void {\n    this.items = [];\n  }\n\n  get size(): number {\n    return this.items.length;\n  }\n}\n","/**\n * _diagnostic-telemetry.ts\n *\n * Single-fire reliability telemetry for the SDK. Carries the\n * `crossdeck.contract_failed` event ONE WAY to the Crossdeck\n * reliability endpoint — NEVER the customer's appId, NEVER the\n * customer's track() pipeline, NEVER visible in the customer's\n * dashboard.\n *\n * Why this exists\n * ───────────────────────────────────────────────────────────────────\n * Crossdeck is an independent controller for SDK Diagnostic\n * Telemetry (Privacy Policy §6, \"Flow B\"). The legitimate-interest\n * basis depends on the payload remaining diagnostic-only: no\n * end-user identifiers, no free-form text, no stack frames. The\n * schema-lock contract at\n * `contracts/diagnostics/contract-failed-payload-schema-lock.json`\n * fixes the wire shape; this module is the call site that has to\n * honour it.\n *\n * Why bypass the existing HttpClient\n * ───────────────────────────────────────────────────────────────────\n * The HttpClient is configured for the customer's project (their\n * API key, their endpoint). Routing reliability telemetry through\n * it would (a) bill against the customer's event quota and (b)\n * show individual contract failures in their dashboard, which is\n * neither the customer's nor Crossdeck's intent. A separate one-way\n * path is the structural guarantee.\n *\n * PROVISIONING NOTE\n * ───────────────────────────────────────────────────────────────────\n * The reliability endpoint URL + publishable key below are LITERAL\n * CONSTANTS shipped in the SDK. Until the reliability project is\n * minted, the placeholder values disable telemetry — the function\n * returns early without making a request. After provisioning, swap\n * the placeholders for the real values; the same values go into the\n * backend at backend/src/api/v1-sdk-diagnostic.ts.\n */\n\nimport { SDK_NAME, SDK_VERSION } from \"./_version\";\n\n/**\n * Reliability endpoint URL — `/v1/events`, the SAME endpoint customer\n * `cd.track()` calls use. Contract failures land as ordinary\n * `crossdeck.contract_failed` events in the Crossdeck reliability\n * project's events warehouse (resolved via the hardcoded reliability\n * publishable key below). They surface in the Crossdeck team's\n * events-explorer alongside any other custom event on that project\n * — no separate dashboard surface required.\n *\n * Pre-2026-05-28 this pointed at `/v1/sdk/diagnostic`, a Crossdeck-\n * specific endpoint that wrote to a top-level `sdkDiagnostics`\n * collection. That endpoint has no dashboard renderer; failures\n * piled up invisibly. Routing through `/v1/events` reuses the\n * generic custom-event pipeline + UI any customer would have for\n * their own track() calls.\n */\nexport const DIAGNOSTIC_TELEMETRY_ENDPOINT =\n  \"https://api.cross-deck.com/v1/events\";\n\n/** Reliability project's publishable key. Hardcoded constant.\n *  Provisioned 2026-05-27 — Crossdeck reliability workspace\n *  (app_web_ea3dc71f61af4b). Every customer SDK's contract_failed\n *  events route here for Crossdeck-on-Crossdeck observability. */\nexport const DIAGNOSTIC_TELEMETRY_PUBLISHABLE_KEY =\n  \"cd_pub_live_eea2f84dbb834a45b7\";\n\n/**\n * Whether the telemetry is enabled. Disabled while the reliability\n * project is unprovisioned (placeholder key in place).\n */\nexport function isDiagnosticTelemetryEnabled(): boolean {\n  return !DIAGNOSTIC_TELEMETRY_PUBLISHABLE_KEY.startsWith(\n    \"cd_pub_RELIABILITY_PLACEHOLDER\",\n  );\n}\n\n/**\n * The exhaustive set of fields the payload may contain — mirrors the\n * schema-lock contract. Anything outside this set is dropped at the\n * call site so a future caller can't accidentally widen the wire\n * shape.\n */\nexport const DIAGNOSTIC_TELEMETRY_ALLOWED_KEYS: ReadonlySet<string> = new Set([\n  \"contract_id\",\n  \"sdk_version\",\n  \"sdk_platform\",\n  \"failure_reason\",\n  \"run_context\",\n  \"run_id\",\n  \"test_file\",\n  \"test_name\",\n  \"device_class\",\n  // verification_phase is set by the runtime contract verifier layer\n  // (sdks/web/src/_contract-verifiers.ts) — values `boot` / `hot_path`.\n  // Absent for failures emitted by external test harnesses\n  // (XCTestObservation, Vitest hooks, JUnit watchers) which carry\n  // test_file + test_name instead. See contracts/diagnostics/\n  // contract-failed-payload-schema-lock.json.\n  \"verification_phase\",\n]);\n\n/**\n * Whitelist filter — even if a caller threads a forbidden key\n * (anonymousId, ip, etc.) through, it never hits the wire. The\n * backend would reject it anyway; this is defence in depth.\n *\n * Exported so unit tests can verify the schema-lock without needing\n * to monkey-patch fetch or wait for the reliability endpoint to be\n * provisioned.\n */\nexport function filterDiagnosticPayload(\n  payload: Record<string, string>,\n): Record<string, string> {\n  const filtered: Record<string, string> = {};\n  for (const [k, v] of Object.entries(payload)) {\n    if (DIAGNOSTIC_TELEMETRY_ALLOWED_KEYS.has(k) && typeof v === \"string\") {\n      filtered[k] = v;\n    }\n  }\n  return filtered;\n}\n\n/**\n * Fire-and-forget POST to the reliability endpoint. Returns\n * immediately. Never throws — failures are silently dropped so the\n * customer's app is not affected by reliability-endpoint\n * availability.\n *\n * @param payload key/value map of payload fields. Keys not in\n *   {@link DIAGNOSTIC_TELEMETRY_ALLOWED_KEYS} are dropped before\n *   serialisation.\n */\nexport function sendDiagnosticTelemetry(\n  payload: Record<string, string>,\n): void {\n  if (!isDiagnosticTelemetryEnabled()) return;\n  const filtered = filterDiagnosticPayload(payload);\n  if (Object.keys(filtered).length === 0) return;\n\n  // Wrap as the /v1/events batch shape — single event per fire,\n  // `name: \"crossdeck.contract_failed\"`, all schema-lock fields as\n  // properties. The reliability publishable key resolves to the\n  // Crossdeck reliability project server-side; the event lands in\n  // that project's events warehouse and surfaces in its\n  // events-explorer like any other custom event.\n  const body = JSON.stringify({\n    events: [\n      {\n        name: \"crossdeck.contract_failed\",\n        timestamp: Date.now(),\n        properties: filtered,\n      },\n    ],\n  });\n\n  // Browser path: fetch with keepalive so the request survives a\n  // page-unload that fires immediately after the call. Node-only\n  // builds never hit this branch (the SDK selects `crossdeck-server`\n  // for that runtime). If fetch is unavailable (older WebViews), the\n  // request is silently dropped — operational telemetry is not\n  // worth surfacing in a customer-facing error.\n  const f = (globalThis as { fetch?: typeof fetch }).fetch;\n  if (typeof f !== \"function\") return;\n\n  try {\n    void f(DIAGNOSTIC_TELEMETRY_ENDPOINT, {\n      method: \"POST\",\n      headers: {\n        \"Content-Type\": \"application/json\",\n        Authorization: `Bearer ${DIAGNOSTIC_TELEMETRY_PUBLISHABLE_KEY}`,\n        \"Crossdeck-Sdk-Version\": `${SDK_NAME}@${SDK_VERSION}`,\n      },\n      body,\n      keepalive: true,\n      // No credentials, no cache, no referrer — the reliability\n      // endpoint is the same origin only in tests. In production the\n      // browser never carries anything beyond the request body and\n      // the Authorization header we set explicitly.\n      credentials: \"omit\",\n      cache: \"no-store\",\n      referrerPolicy: \"no-referrer\",\n    }).catch(() => {\n      // Fire-and-forget; we never want a rejection bubbling into the\n      // host app's unhandledrejection handler.\n    });\n  } catch {\n    // Swallow synchronous throws (CSP block, immediate failure).\n  }\n}\n","/**\n * Machine-readable index of every error code the SDK can throw, with\n * a short description and a hint on what action to take. Published\n * verbatim as `crossdeck-error-codes.json` in the npm tarball so AI\n * integration assistants, error-aggregator dashboards (Sentry,\n * DataDog), and the Crossdeck dashboard can render human-friendly\n * messages without parsing freeform `message` strings.\n *\n * Stripe publishes the same surface at stripe.com/docs/error-codes;\n * developers love it because every code has a canonical \"what does\n * this mean / what should I do\" answer.\n *\n * Adding a new error code:\n *   1. Add the code string to the union in `errors.ts` (where used).\n *   2. Add an entry here.\n *   3. The next `npm run build` regenerates the JSON sidecar.\n *\n * Keep entries terse — the consumer surfaces this in tooltips and\n * automated tickets, not in long-form docs.\n */\n\nexport interface ErrorCodeEntry {\n  /** The string thrown as CrossdeckError.code. */\n  code: string;\n  /** CrossdeckError.type — broad category. */\n  type:\n    | \"authentication_error\"\n    | \"permission_error\"\n    | \"invalid_request_error\"\n    | \"rate_limit_error\"\n    | \"version_error\"\n    | \"internal_error\"\n    | \"network_error\"\n    | \"configuration_error\";\n  /** One-sentence description. Surfaced verbatim in dashboards. */\n  description: string;\n  /** What the developer should do. Imperative phrasing. */\n  resolution: string;\n  /** True for codes the SDK can auto-recover from (no developer action). */\n  retryable: boolean;\n}\n\nexport const CROSSDECK_ERROR_CODES: readonly ErrorCodeEntry[] = Object.freeze([\n  // ----- Configuration -----\n  {\n    code: \"invalid_public_key\",\n    type: \"configuration_error\",\n    description: \"The publishable key passed to Crossdeck.init() doesn't start with cd_pub_.\",\n    resolution: \"Copy the key from your Crossdeck dashboard → API keys page.\",\n    retryable: false,\n  },\n  {\n    code: \"missing_app_id\",\n    type: \"configuration_error\",\n    description: \"Crossdeck.init() was called without an appId.\",\n    resolution: \"Add appId to your init options — find it in the dashboard's Apps page.\",\n    retryable: false,\n  },\n  {\n    code: \"invalid_environment\",\n    type: \"configuration_error\",\n    description: \"Crossdeck.init() requires environment: 'production' | 'sandbox'.\",\n    resolution: \"Pass the literal string \\\"production\\\" or \\\"sandbox\\\" — no other values are accepted.\",\n    retryable: false,\n  },\n  {\n    code: \"environment_mismatch\",\n    type: \"configuration_error\",\n    description: \"The publishable key's env prefix doesn't match the declared environment option.\",\n    resolution: \"Either change `environment` to match the key prefix (cd_pub_test_ ↔ sandbox, cd_pub_live_ ↔ production), or swap the key for one minted in the right env.\",\n    retryable: false,\n  },\n  {\n    code: \"not_initialized\",\n    type: \"configuration_error\",\n    description: \"An SDK method was called before Crossdeck.init().\",\n    resolution: \"Call Crossdeck.init({ appId, publicKey, environment }) once at app startup before any other method.\",\n    retryable: false,\n  },\n\n  // ----- Identify / track / purchase argument validation -----\n  {\n    code: \"missing_user_id\",\n    type: \"invalid_request_error\",\n    description: \"identify() was called with an empty userId.\",\n    resolution: \"Pass a stable, non-empty user identifier from your auth layer — never a hardcoded placeholder.\",\n    retryable: false,\n  },\n  {\n    code: \"missing_event_name\",\n    type: \"invalid_request_error\",\n    description: \"track() was called without an event name.\",\n    resolution: \"Pass a non-empty string as the first argument.\",\n    retryable: false,\n  },\n  {\n    code: \"missing_group_type\",\n    type: \"invalid_request_error\",\n    description: \"group() was called without a group type.\",\n    resolution: \"Pass a non-empty type (e.g. \\\"org\\\", \\\"team\\\") as the first argument.\",\n    retryable: false,\n  },\n  {\n    code: \"missing_signed_transaction_info\",\n    type: \"invalid_request_error\",\n    description: \"syncPurchases() was called without StoreKit 2 signed transaction info.\",\n    resolution: \"Pass the JWS string from Transaction.currentEntitlements / Transaction.updates.\",\n    retryable: false,\n  },\n\n  // ----- Network / transport -----\n  {\n    code: \"fetch_failed\",\n    type: \"network_error\",\n    description: \"The underlying fetch() call failed (typically a network outage or DNS issue).\",\n    resolution: \"Check the user's network. The SDK will retry automatically with exponential backoff.\",\n    retryable: true,\n  },\n  {\n    code: \"request_timeout\",\n    type: \"network_error\",\n    description: \"A request was aborted after the configured timeoutMs (default 15s).\",\n    resolution: \"Check the user's connection. Increase timeoutMs in init options if the user is on a known-slow network.\",\n    retryable: true,\n  },\n  {\n    code: \"invalid_json_response\",\n    type: \"internal_error\",\n    description: \"The server returned a 2xx with an unparseable body.\",\n    resolution: \"Likely a transient backend bug. Retry; if it persists, contact support with the requestId.\",\n    retryable: true,\n  },\n\n  // ----- Backend-emitted codes (v1.4.0 Phase 6.2 backfill) -----\n  // Mirror of backend/src/api/v1-errors.ts ApiErrorCode. A developer\n  // hitting any of these on the wire can look them up via\n  // getErrorCode(code) for a canonical remediation step instead of\n  // hunting through Slack history.\n  {\n    code: \"missing_api_key\",\n    type: \"authentication_error\",\n    description: \"No Authorization header (or Crossdeck-Api-Key header) on the request.\",\n    resolution: \"Make sure Crossdeck.init({ publicKey }) was called with a cd_pub_… key before the request fired. Re-check your env-vars in CI / Docker if the key is empty at runtime.\",\n    retryable: false,\n  },\n  {\n    code: \"invalid_api_key\",\n    type: \"authentication_error\",\n    description: \"The API key is malformed, unknown, or doesn't resolve to a project.\",\n    resolution: \"Copy the key from your Crossdeck dashboard → API keys. Confirm prefix is cd_pub_test_ / cd_pub_live_ for client SDKs and cd_sk_test_ / cd_sk_live_ for the Node server SDK.\",\n    retryable: false,\n  },\n  {\n    code: \"key_revoked\",\n    type: \"authentication_error\",\n    description: \"The API key was revoked in the Crossdeck dashboard.\",\n    resolution: \"Mint a fresh key in the dashboard → API keys → Create new, swap it in, and redeploy. The revoked key cannot be reactivated.\",\n    retryable: false,\n  },\n  {\n    code: \"identity_token_invalid\",\n    type: \"authentication_error\",\n    description: \"The Firebase / Apple / Google ID token supplied with the request didn't verify against the dashboard's configured signers.\",\n    resolution: \"Refresh the token client-side (Firebase auth.currentUser.getIdToken(true)) and retry. If the failure persists, confirm the signer is registered under dashboard → Authentication → Identity sources.\",\n    retryable: true,\n  },\n  {\n    code: \"origin_not_allowed\",\n    type: \"permission_error\",\n    description: \"The Origin header isn't in the project's Allowed origins list.\",\n    resolution: \"Add the origin (e.g. https://app.example.com) under dashboard → Settings → Allowed origins. Wildcards like https://*.example.com are supported.\",\n    retryable: false,\n  },\n  {\n    code: \"bundle_id_not_allowed\",\n    type: \"permission_error\",\n    description: \"The iOS bundle ID sent via X-Crossdeck-Bundle-Id isn't registered under this app's Apple identity lock.\",\n    resolution: \"Add the bundle ID under dashboard → Apps → <your app> → iOS bundle IDs.\",\n    retryable: false,\n  },\n  {\n    code: \"package_name_not_allowed\",\n    type: \"permission_error\",\n    description: \"The Android package name sent via X-Crossdeck-Package-Name isn't registered under this app's Android identity lock.\",\n    resolution: \"Add the package name under dashboard → Apps → <your app> → Android package names.\",\n    retryable: false,\n  },\n  {\n    code: \"env_mismatch\",\n    type: \"permission_error\",\n    description: \"The request env (inferred from key prefix) doesn't match the resolved app's configured env.\",\n    resolution: \"Use a cd_pub_live_ / cd_sk_live_ key with a production app, cd_pub_test_ / cd_sk_test_ with a sandbox app. The two cannot cross.\",\n    retryable: false,\n  },\n  {\n    code: \"idempotency_key_in_use\",\n    type: \"invalid_request_error\",\n    description: \"An Idempotency-Key was reused for a request with a different body (Stripe-grade contract).\",\n    resolution: \"Generate a fresh key for a different transaction, or reuse the key only with the EXACT same body. The v1.4.0 SDKs derive keys deterministically from the body so this should never fire on SDK-managed calls.\",\n    retryable: false,\n  },\n  {\n    code: \"rate_limited\",\n    type: \"rate_limit_error\",\n    description: \"Request rate exceeded the project's per-second cap.\",\n    resolution: \"Honour the Retry-After header — the SDK does this automatically on managed retries. If you're hitting it from a custom code path, throttle to <100 req/s/key.\",\n    retryable: true,\n  },\n  {\n    code: \"internal_error\",\n    type: \"internal_error\",\n    description: \"Server-side issue. Safe to retry with backoff.\",\n    resolution: \"The SDK retries automatically. If your code paths through to this error, contact support with the requestId from the response envelope.\",\n    retryable: true,\n  },\n  {\n    code: \"google_not_supported\",\n    type: \"invalid_request_error\",\n    description: \"POST /purchases/sync with rail=google is gated until the Play Developer API reconciliation worker ships.\",\n    resolution: \"Until v1.5+, Google Play purchases verify via Real-time Developer Notifications. The SDK auto-track path handles this transparently for Android consumers.\",\n    retryable: false,\n  },\n  {\n    code: \"stripe_not_supported\",\n    type: \"invalid_request_error\",\n    description: \"POST /purchases/sync with rail=stripe is unsupported — Stripe Checkout's redirect flow uses platform webhooks instead.\",\n    resolution: \"Wire Stripe via the standard Checkout / Customer Portal flow; Crossdeck reconciles via the platform webhook automatically. No SDK call needed.\",\n    retryable: false,\n  },\n  {\n    code: \"missing_required_param\",\n    type: \"invalid_request_error\",\n    description: \"A required field is absent from the request body.\",\n    resolution: \"Inspect the error.message — the missing field name is included verbatim. Refer to the SDK's TypeScript types for the canonical request shape.\",\n    retryable: false,\n  },\n  {\n    code: \"invalid_param_value\",\n    type: \"invalid_request_error\",\n    description: \"A field is present but the value failed validation (wrong shape, wrong length, wrong enum value).\",\n    resolution: \"Read error.message for the specific field + reason. SDK-managed call sites should never emit this — file a bug if you do.\",\n    retryable: false,\n  },\n  {\n    code: \"sdk_version_unsupported\",\n    type: \"version_error\",\n    description: \"HTTP 426 — your installed SDK sends an event format the server no longer accepts. The data is good; only the wire dialect is too old. The SDK PARKS automatically: events are held on-device (paused, not lost) and deliver on the next launch after you upgrade.\",\n    resolution: \"Update @cross-deck/web to at least the version in error.minVersion and redeploy — the parked queue backfills automatically. See https://cross-deck.com/docs/sdk-event-durability/.\",\n    retryable: false,\n  },\n] as const);\n\n/** Lookup helper — returns the entry matching a CrossdeckError.code, or undefined. */\nexport function getErrorCode(code: string): ErrorCodeEntry | undefined {\n  return CROSSDECK_ERROR_CODES.find((e) => e.code === code);\n}\n","/**\n * Contract verifier layer — runtime self-verification for the Web SDK.\n *\n * Crossdeck publishes its structural guarantees as machine-readable\n * contracts under `contracts/`. Each contract is a single claim about\n * the runtime — per-user cache isolation, cross-SDK idempotency-key\n * determinism, Stripe-shape error envelope, etc. — paired with the\n * source files that implement it and the tests that prove it in CI.\n *\n * The CI tests are the institutional half of the proof. This module\n * is the customer-facing half: at runtime, in every install, on every\n * relevant operation, a verifier re-runs the contract's claim against\n * the live SDK and produces a `{ ok: true, evidence }` or\n * `{ ok: false, failureReason }` result. The customer's engineer sees\n * passes streaming through their browser devtools console\n * (`[crossdeck.identify] ✓ per-user-cache-isolation — slot rotated …`)\n * and the Crossdeck reliability team sees failures arrive in the\n * reliability workspace, before the customer's own dashboard would\n * have noticed.\n *\n * Three switches govern the layer (see CrossdeckOptions docstrings\n * for the full surface contract):\n *\n *   verifyContractsAtBoot\n *     Whether the boot self-test runs on Crossdeck.start(...).\n *     Defaults dev=true, prod=false.\n *\n *   logVerifierResults\n *     Whether PASS results print to the console. Cosmetic only —\n *     does NOT affect failure reporting. Defaults dev=true, prod=false.\n *\n *   disableContractAssertions\n *     Total kill-switch — no verifiers run, no console output, no\n *     telemetry. Defaults false. Sovereignty escape hatch for\n *     enterprise customers whose posture forbids any outbound\n *     diagnostic telemetry to third-party controllers.\n *\n * Routing rules — locked, audited, KPMG-readable:\n *\n *                    │ logVerifierResults │ logVerifierResults │\n *                    │       = true       │      = false       │\n *   ─────────────────┼────────────────────┼────────────────────┤\n *    PASS  (boot)    │  console.info ✓    │       silent       │\n *    PASS  (hot_path)│  console.debug ✓   │       silent       │\n *    FAIL  (any)     │  console.warn ✗    │  console.warn ✗    │\n *                    │  + reliability     │  + reliability     │\n *\n * `disableContractAssertions: true` short-circuits BEFORE any of the\n * above; verifiers don't even execute.\n *\n * @module _contract-verifiers\n * @internal Bundled-but-not-exported from the public package surface.\n *           Customers interact via CrossdeckOptions, not this file.\n */\n\nimport { EntitlementCache } from \"./entitlement-cache\";\nimport {\n  deriveIdempotencyKeyForPurchase,\n  formatAsUuid,\n} from \"./idempotency-key\";\nimport { sha256Hex } from \"./hash\";\nimport { getErrorCode } from \"./error-codes\";\nimport { sendDiagnosticTelemetry } from \"./_diagnostic-telemetry\";\nimport { SDK_NAME, SDK_VERSION } from \"./_version\";\n\n// ============================================================================\n// Public result types — the contract a verifier returns.\n// ============================================================================\n\n/**\n * A successful verifier execution. `evidence` is a short\n * human-readable string the developer sees in their console:\n *   \"slot rotated _anon → 7c44…ee20\"\n *   \"apple JWS → a66b1640-efaf-bb4d-1261-6650033bf111\"\n *   \"{ type, code, message, request_id } parsed\"\n * Keep evidence short (under 120 chars) and free of identifiers that\n * could re-link to an end-user. Never include raw userIds, emails,\n * IPs, or stack frames.\n */\nexport interface VerifierPass {\n  readonly ok: true;\n  readonly contractId: string;\n  readonly evidence: string;\n  readonly durationMs: number;\n}\n\n/**\n * A failed verifier execution. `failureReason` is the short\n * categorical-ish label the reliability dashboard groups on:\n *   \"slot not rotated\"\n *   \"key not deterministic\"\n *   \"envelope missing request_id\"\n * Kept under 128 chars by SDK convention. Never includes raw values\n * — categorical labels only, so the legitimate-interest analysis in\n * Privacy §6 stays valid (no end-user data on the wire).\n */\nexport interface VerifierFail {\n  readonly ok: false;\n  readonly contractId: string;\n  readonly failureReason: string;\n  readonly durationMs: number;\n}\n\nexport type VerifierResult = VerifierPass | VerifierFail;\n\n/**\n * Which layer of the verifier system produced the result. Carried\n * on every failure report as `verification_phase` so the reliability\n * dashboard can slice by urgency — a `boot` failure means the SDK\n * is structurally broken before the customer's first user even taps;\n * a `hot_path` failure means a structural contract was violated\n * during real-world operation.\n */\nexport type VerificationPhase = \"boot\" | \"hot_path\";\n\n// ============================================================================\n// Context — the immutable state every verifier sees.\n// ============================================================================\n\n/**\n * Per-process verifier context. Constructed once at `Crossdeck.start(...)`\n * and threaded into every verifier invocation.\n */\nexport interface VerifierContext {\n  /** SDK version (`@cross-deck/web@1.5.1`-ish). */\n  readonly sdkVersion: string;\n  /** Stable per-process identifier used as `run_id` on every failure\n   * report — lets the reliability dashboard collapse multiple\n   * verifier hits within one process into one row. */\n  readonly runId: string;\n  /** `ci` / `dogfood` / `customer-app`. The Web SDK defaults to\n   * `customer-app` (the SDK is running inside a customer's deployed\n   * site); test harnesses override via the existing\n   * `reportContractFailure(...)` path with a different `run_context`. */\n  readonly runContext: \"ci\" | \"dogfood\" | \"customer-app\";\n  /** Mirrors CrossdeckOptions.logVerifierResults. */\n  readonly logVerifierResults: boolean;\n  /** Mirrors CrossdeckOptions.disableContractAssertions. */\n  readonly disableContractAssertions: boolean;\n  /** Console sink — defaults to globalThis.console; tests override. */\n  readonly console: Pick<Console, \"info\" | \"debug\" | \"warn\">;\n  /** Telemetry sink — defaults to the production\n   * `sendDiagnosticTelemetry`; tests override. */\n  readonly emitTelemetry: (\n    payload: Record<string, string>,\n  ) => void;\n}\n\n/**\n * Build the default VerifierContext for a Web SDK instance.\n *\n * `runId` is `cd_verify_<8-hex>` — short enough to read in a dashboard\n * row, long enough to be probabilistically unique per process.\n */\nexport function buildVerifierContext(opts: {\n  logVerifierResults: boolean;\n  disableContractAssertions: boolean;\n  runContext?: \"ci\" | \"dogfood\" | \"customer-app\";\n}): VerifierContext {\n  return {\n    sdkVersion: `${SDK_NAME}@${SDK_VERSION}`,\n    runId: `cd_verify_${randomHex(8)}`,\n    runContext: opts.runContext ?? \"customer-app\",\n    logVerifierResults: opts.logVerifierResults,\n    disableContractAssertions: opts.disableContractAssertions,\n    console: console,\n    emitTelemetry: sendDiagnosticTelemetry,\n  };\n}\n\n// ============================================================================\n// Reporter — single point of console + telemetry routing.\n// ============================================================================\n\n/**\n * Routes a `VerifierResult` to the console (per `logVerifierResults`)\n * and the reliability channel (always, on FAIL).\n *\n * The reporter is the single audit-trail seam. KPMG-readable:\n *   - One function decides every output path.\n *   - One re-entrancy guard guarantees a verifier-on-the-telemetry-\n *     path cannot recursively fire telemetry on itself.\n *   - Console output and telemetry are NEVER coupled — flipping one\n *     flag cannot accidentally affect the other.\n */\nexport class VerifierReporter {\n  private reentrancyDepth = 0;\n\n  constructor(private readonly ctx: VerifierContext) {}\n\n  /**\n   * Report a verifier result. Pass `operation` for hot-path results\n   * to render the prefix as `[crossdeck.identify]` etc.; omit for\n   * boot results which render as `[crossdeck]`.\n   */\n  report(\n    result: VerifierResult,\n    phase: VerificationPhase,\n    operation?: string,\n  ): void {\n    // Total kill-switch — verifiers should not even execute when this\n    // is set, but the reporter checks too for defence in depth.\n    if (this.ctx.disableContractAssertions) return;\n\n    if (result.ok) {\n      this.reportPass(result, phase, operation);\n    } else {\n      this.reportFail(result, phase, operation);\n    }\n  }\n\n  private reportPass(\n    result: VerifierPass,\n    phase: VerificationPhase,\n    operation?: string,\n  ): void {\n    if (!this.ctx.logVerifierResults) return;\n    const line = formatPassLine(result, operation);\n    if (phase === \"boot\") {\n      this.ctx.console.info(line);\n    } else {\n      this.ctx.console.debug(line);\n    }\n  }\n\n  private reportFail(\n    result: VerifierFail,\n    phase: VerificationPhase,\n    operation?: string,\n  ): void {\n    // FAIL always prints at WARN regardless of logVerifierResults —\n    // silencing a structural failure would defeat the purpose. The\n    // ONLY way to suppress this is `disableContractAssertions: true`\n    // which short-circuited above.\n    this.ctx.console.warn(formatFailLine(result, operation));\n\n    // Re-entrancy guard. The `contract-failed-payload-schema-lock`\n    // verifier runs on every reliability-channel write, so a malformed\n    // payload would otherwise infinite-loop:\n    //   verifier fails → reporter fires telemetry → telemetry write\n    //     triggers schema-lock verifier → verifier fails → ...\n    // The guard breaks the loop at depth 1; the outer failure still\n    // makes it to the reliability channel exactly once.\n    if (this.reentrancyDepth > 0) return;\n    this.reentrancyDepth += 1;\n    try {\n      this.ctx.emitTelemetry({\n        contract_id: result.contractId,\n        sdk_version: this.ctx.sdkVersion,\n        sdk_platform: \"web\",\n        failure_reason: truncate(result.failureReason, 128),\n        run_context: this.ctx.runContext,\n        run_id: this.ctx.runId,\n        verification_phase: phase,\n      });\n    } finally {\n      this.reentrancyDepth -= 1;\n    }\n  }\n}\n\nfunction formatPassLine(r: VerifierPass, operation?: string): string {\n  const prefix = operation ? `[crossdeck.${operation}]` : \"[crossdeck]\";\n  return `${prefix} ✓ ${r.contractId} — ${r.evidence} (${r.durationMs}ms)`;\n}\n\nfunction formatFailLine(r: VerifierFail, operation?: string): string {\n  const prefix = operation ? `[crossdeck.${operation}]` : \"[crossdeck]\";\n  return `${prefix} ✗ ${r.contractId} — ${r.failureReason} (${r.durationMs}ms)`;\n}\n\n// ============================================================================\n// Verifier — the protocol every contract verifier implements.\n// ============================================================================\n\n/**\n * A `ContractVerifier` is a small object that knows how to test ONE\n * contract claim against the live SDK at runtime.\n *\n *   contractId    — the stable id from contracts/<pillar>/<id>.json\n *   bootTest      — runs once at Crossdeck.start(), against synthetic\n *                   state (the customer's real SDK state is never\n *                   mutated). Optional — some contracts only make\n *                   sense in the hot-path.\n *   hooks         — observation functions invoked from the SDK's\n *                   hot-path call sites (identify, track, syncPurchases,\n *                   error parse, ...) with typed observations.\n *                   Optional — some contracts only test at boot.\n */\nexport interface ContractVerifier {\n  readonly contractId: string;\n  bootTest?(): VerifierResult | Promise<VerifierResult>;\n  /** Hot-path hooks — each receives the actual observation needed to\n   * decide pass/fail and returns the result. The dispatcher\n   * (`runOn*` helpers below) invokes the right hook from the right\n   * SDK call site. */\n  readonly hooks?: {\n    onIdentify?(obs: IdentifyObservation): VerifierResult;\n    onTrack?(obs: TrackObservation): VerifierResult;\n    onSyncPurchases?(obs: SyncPurchasesObservation): VerifierResult;\n    onErrorParse?(obs: ErrorParseObservation): VerifierResult;\n    onReportContractFailure?(obs: ReportFailureObservation): VerifierResult;\n  };\n}\n\n// ============================================================================\n// Observation types — the typed payloads every hook receives.\n// ============================================================================\n\nexport interface IdentifyObservation {\n  /** The prior userId, or null if previously anonymous. */\n  readonly priorUserId: string | null;\n  /** The new userId being identified. */\n  readonly nextUserId: string;\n  /** The live entitlement cache after the rotation completed. */\n  readonly cache: EntitlementCache;\n}\n\nexport interface TrackObservation {\n  /** Properties as supplied by the caller (before any merge). */\n  readonly callerProperties: Record<string, unknown>;\n  /** Super-properties currently registered on the SDK. */\n  readonly superProperties: Record<string, unknown>;\n  /** Auto-attached device properties. */\n  readonly deviceProperties: Record<string, unknown>;\n  /** The merged result the SDK actually enqueued. */\n  readonly mergedProperties: Record<string, unknown>;\n}\n\nexport interface SyncPurchasesObservation {\n  readonly rail: \"apple\" | \"google\" | \"stripe\" | string;\n  /** The Apple-side JWS or Google-side purchase token used to derive\n   * the idempotency key. */\n  readonly stableIdentifier: string;\n  /** The idempotency key the SDK actually sent. */\n  readonly derivedKey: string;\n}\n\nexport interface ErrorParseObservation {\n  /** The parsed `type` field from the wire envelope. */\n  readonly errorType: string;\n  /** The parsed `code` field. */\n  readonly errorCode: string;\n  /** The parsed `request_id`, if present. */\n  readonly requestId: string | null;\n  /** The original HTTP status from the response. */\n  readonly httpStatus: number;\n}\n\nexport interface ReportFailureObservation {\n  /** The fully-merged payload AFTER allow-list filtering, exactly as\n   * about to be sent on the wire. */\n  readonly outgoingPayload: Record<string, unknown>;\n}\n\n// ============================================================================\n// VERIFIER 1 — per-user-cache-isolation\n// ============================================================================\n\nconst VERIFIER_PER_USER_CACHE_ISOLATION: ContractVerifier = {\n  contractId: \"per-user-cache-isolation\",\n\n  bootTest(): VerifierResult {\n    const t0 = nowMs();\n    try {\n      const storage = new MemoryStorage();\n      // EntitlementCache constructor is positional: (storage,\n      // storageKeyPrefix, staleAfterMs). See sdks/web/src/entitlement-cache.ts.\n      const cache = new EntitlementCache(storage, \"_verifier\");\n\n      // Plant entitlements for user A. PublicEntitlement shape per\n      // sdks/web/src/types.ts — synthetic source values are fine since\n      // we're isolated from the customer's real cache and never hit\n      // the network.\n      cache.setUserKey(\"user_A\");\n      cache.setFromList([\n        {\n          object: \"entitlement\",\n          key: \"pro\",\n          isActive: true,\n          validUntil: null,\n          source: {\n            rail: \"stripe\",\n            productId: \"p_verifier\",\n            subscriptionId: \"s_verifier\",\n          },\n          updatedAt: Date.now(),\n        },\n      ]);\n\n      // Rotate to user B.\n      cache.setUserKey(\"user_B\");\n\n      // The in-memory snapshot must be empty AND the storage slot for\n      // user_A must be physically separate from user_B (different sha256\n      // suffixes). Use the public `list()` accessor — the contract\n      // guarantees this returns the live in-memory snapshot.\n      const inMemoryB = cache.list();\n      if (inMemoryB.length !== 0) {\n        return fail(\n          \"per-user-cache-isolation\",\n          \"in-memory snapshot still carried user_A's entitlements after rotation\",\n          nowMs() - t0,\n        );\n      }\n\n      const suffixA = EntitlementCache.suffixForUserId(\"user_A\");\n      const suffixB = EntitlementCache.suffixForUserId(\"user_B\");\n      if (suffixA === suffixB) {\n        return fail(\n          \"per-user-cache-isolation\",\n          \"suffixes for user_A and user_B collided\",\n          nowMs() - t0,\n        );\n      }\n\n      const storageA = storage.getItem(`_verifier:${suffixA}`);\n      const storageB = storage.getItem(`_verifier:${suffixB}`);\n      // user_A's slot still holds their data (the rotation didn't wipe\n      // it — that's the point: a returning user observes their cache);\n      // user_B's slot is empty (no entitlements written yet).\n      if (!storageA) {\n        return fail(\n          \"per-user-cache-isolation\",\n          \"user_A's storage slot was wiped on rotation (expected isolation, not erasure)\",\n          nowMs() - t0,\n        );\n      }\n      if (storageB) {\n        return fail(\n          \"per-user-cache-isolation\",\n          \"user_B's storage slot already contained data before any write\",\n          nowMs() - t0,\n        );\n      }\n\n      return pass(\n        \"per-user-cache-isolation\",\n        `slot rotated A:${shortSuffix(suffixA)} → B:${shortSuffix(suffixB)} (isolated, physically separate)`,\n        nowMs() - t0,\n      );\n    } catch (err) {\n      return fail(\n        \"per-user-cache-isolation\",\n        `boot test threw: ${(err as Error).message?.slice(0, 80) ?? \"unknown error\"}`,\n        nowMs() - t0,\n      );\n    }\n  },\n\n  hooks: {\n    onIdentify(obs: IdentifyObservation): VerifierResult {\n      const t0 = nowMs();\n      const priorSuffix = EntitlementCache.suffixForUserId(obs.priorUserId);\n      const nextSuffix = EntitlementCache.suffixForUserId(obs.nextUserId);\n\n      if (priorSuffix !== nextSuffix) {\n        // The in-memory snapshot must be empty after rotation. We\n        // test this against the live cache via the public list()\n        // accessor; the contract guarantees it returns the same\n        // in-memory snapshot the cache holds.\n        // Isolation is: the snapshot after rotation is sourced from the NEW\n        // slot's storage — empty for a first-time user, or that user's OWN\n        // last-known-good for a returning one (the intentional rehydrate) —\n        // and NEVER the prior user's data. \"Must be empty\" was too strict: it\n        // false-failed a returning user legitimately re-reading their own\n        // cache (the ⚠️ that fired in production). Assert the real invariant.\n        if (\n          typeof obs.cache.snapshotMatchesCurrentSlot === \"function\" &&\n          !obs.cache.snapshotMatchesCurrentSlot()\n        ) {\n          return fail(\n            \"per-user-cache-isolation\",\n            \"in-memory snapshot diverged from the new slot's storage after rotation (possible leak)\",\n            nowMs() - t0,\n          );\n        }\n        return pass(\n          \"per-user-cache-isolation\",\n          `slot rotated ${shortSuffix(priorSuffix)} → ${shortSuffix(nextSuffix)} (sourced from new slot)`,\n          nowMs() - t0,\n        );\n      }\n\n      // Same-id re-identify: in-memory must STILL be cleared per the\n      // contract's \"unconditional wipe\" clause, then re-hydrated from\n      // the same slot. The live cache's in-memory snapshot is observed\n      // post-operation; either empty (cleared) or repopulated from the\n      // slot is acceptable. The structural test is \"the operation ran\n      // through the setUserKey() code path\" — we cannot directly assert\n      // that without a side-channel, so we verify the slot identity\n      // for inputs that produce identical suffixes.\n      return pass(\n        \"per-user-cache-isolation\",\n        `same-id re-identify (suffix ${shortSuffix(nextSuffix)}); cleared + rehydrated per contract`,\n        nowMs() - t0,\n      );\n    },\n  },\n};\n\n// ============================================================================\n// VERIFIER 2 — idempotency-key-deterministic\n// ============================================================================\n\n/**\n * The cross-SDK canonical vector. Pinned: every SDK in the suite must\n * derive THIS exact key from the apple JWS string `eyJ.jws.sig`.\n * Drift on any platform is a contract break — the entire idempotency\n * scheme depends on this UUID being the same on Web, Node, RN, Swift,\n * and Android.\n */\nconst CANONICAL_APPLE_JWS = \"eyJ.jws.sig\";\nconst CANONICAL_APPLE_IDEMPOTENCY_KEY = \"a66b1640-efaf-bb4d-1261-6650033bf111\";\n\nconst VERIFIER_IDEMPOTENCY_KEY_DETERMINISTIC: ContractVerifier = {\n  contractId: \"idempotency-key-deterministic\",\n\n  bootTest(): VerifierResult {\n    const t0 = nowMs();\n    try {\n      const derived = deriveIdempotencyKeyForPurchase({\n        rail: \"apple\",\n        signedTransactionInfo: CANONICAL_APPLE_JWS,\n      });\n      if (derived !== CANONICAL_APPLE_IDEMPOTENCY_KEY) {\n        return fail(\n          \"idempotency-key-deterministic\",\n          `canonical apple JWS derived ${derived} (expected ${CANONICAL_APPLE_IDEMPOTENCY_KEY})`,\n          nowMs() - t0,\n        );\n      }\n\n      // Determinism — same input twice → same key.\n      const second = deriveIdempotencyKeyForPurchase({\n        rail: \"apple\",\n        signedTransactionInfo: CANONICAL_APPLE_JWS,\n      });\n      if (second !== derived) {\n        return fail(\n          \"idempotency-key-deterministic\",\n          \"same JWS produced different keys on two derivations\",\n          nowMs() - t0,\n        );\n      }\n\n      // Rail namespacing — same identifier under different rails must\n      // produce different keys.\n      const appleKey = derived;\n      const googleKey = deriveIdempotencyKeyForPurchase({\n        rail: \"google\",\n        purchaseToken: CANONICAL_APPLE_JWS,\n      });\n      if (appleKey === googleKey) {\n        return fail(\n          \"idempotency-key-deterministic\",\n          \"rail namespacing failed — apple/google identical key\",\n          nowMs() - t0,\n        );\n      }\n\n      return pass(\n        \"idempotency-key-deterministic\",\n        `apple JWS → ${derived} (canonical vector + determinism + rail isolation)`,\n        nowMs() - t0,\n      );\n    } catch (err) {\n      return fail(\n        \"idempotency-key-deterministic\",\n        `boot test threw: ${(err as Error).message?.slice(0, 80) ?? \"unknown error\"}`,\n        nowMs() - t0,\n      );\n    }\n  },\n\n  hooks: {\n    onSyncPurchases(obs: SyncPurchasesObservation): VerifierResult {\n      const t0 = nowMs();\n      // Re-derive from the same input and compare against what the SDK\n      // actually sent. Identical → contract honoured. Drift → bug.\n      try {\n        const expected = deriveIdempotencyKeyForPurchase(\n          obs.rail === \"apple\"\n            ? { rail: \"apple\", signedTransactionInfo: obs.stableIdentifier }\n            : { rail: obs.rail, purchaseToken: obs.stableIdentifier },\n        );\n        if (expected !== obs.derivedKey) {\n          return fail(\n            \"idempotency-key-deterministic\",\n            `derived key drifted from canonical algorithm`,\n            nowMs() - t0,\n          );\n        }\n        return pass(\n          \"idempotency-key-deterministic\",\n          `${obs.rail} → ${obs.derivedKey}`,\n          nowMs() - t0,\n        );\n      } catch (err) {\n        return fail(\n          \"idempotency-key-deterministic\",\n          `hot-path derivation threw: ${(err as Error).message?.slice(0, 80) ?? \"unknown error\"}`,\n          nowMs() - t0,\n        );\n      }\n    },\n  },\n};\n\n// ============================================================================\n// VERIFIER 3 — error-envelope-shape\n// ============================================================================\n\nconst VERIFIER_ERROR_ENVELOPE_SHAPE: ContractVerifier = {\n  contractId: \"error-envelope-shape\",\n\n  bootTest(): VerifierResult {\n    const t0 = nowMs();\n    // Synthetic envelope matching the contract claim. This is what\n    // every v1 endpoint emits on a 4xx/5xx.\n    const wire = {\n      error: {\n        type: \"invalid_request_error\",\n        code: \"missing_customer\",\n        message: \"Customer identifier is required.\",\n        request_id: \"req_test1234\",\n      },\n    };\n\n    // The contract claim: every error envelope carries\n    // { type, code, message, request_id } where type is one of the\n    // five canonical ApiErrorType values.\n    const ALLOWED_TYPES = new Set([\n      \"authentication_error\",\n      \"permission_error\",\n      \"invalid_request_error\",\n      \"rate_limit_error\",\n      \"internal_error\",\n    ]);\n\n    const err = wire.error;\n    const missing = [\"type\", \"code\", \"message\", \"request_id\"].filter(\n      (k) => !(k in err) || typeof (err as Record<string, unknown>)[k] !== \"string\",\n    );\n    if (missing.length > 0) {\n      return fail(\n        \"error-envelope-shape\",\n        `envelope missing required fields: ${missing.join(\", \")}`,\n        nowMs() - t0,\n      );\n    }\n    if (!ALLOWED_TYPES.has(err.type)) {\n      return fail(\n        \"error-envelope-shape\",\n        `error.type \"${err.type}\" not in canonical ApiErrorType set`,\n        nowMs() - t0,\n      );\n    }\n    return pass(\n      \"error-envelope-shape\",\n      \"{ type, code, message, request_id } parsed and type ∈ ApiErrorType\",\n      nowMs() - t0,\n    );\n  },\n\n  hooks: {\n    onErrorParse(obs: ErrorParseObservation): VerifierResult {\n      const t0 = nowMs();\n      const ALLOWED_TYPES = new Set([\n        \"authentication_error\",\n        \"permission_error\",\n        \"invalid_request_error\",\n        \"rate_limit_error\",\n        \"internal_error\",\n      ]);\n      if (!ALLOWED_TYPES.has(obs.errorType)) {\n        return fail(\n          \"error-envelope-shape\",\n          `wire error.type \"${obs.errorType}\" outside canonical ApiErrorType`,\n          nowMs() - t0,\n        );\n      }\n      if (!obs.errorCode || obs.errorCode.length === 0) {\n        return fail(\n          \"error-envelope-shape\",\n          \"wire error.code was empty\",\n          nowMs() - t0,\n        );\n      }\n      // request_id is permitted to be null (the SDK falls back to\n      // X-Request-Id header per the contract); we don't assert\n      // presence, only that the parser surfaced something\n      // grep-able.\n      return pass(\n        \"error-envelope-shape\",\n        `${obs.errorType}/${obs.errorCode} on ${obs.httpStatus}${obs.requestId ? ` (${obs.requestId.slice(0, 12)}…)` : \"\"}`,\n        nowMs() - t0,\n      );\n    },\n  },\n};\n\n// ============================================================================\n// VERIFIER 4 — flush-interval-parity\n// ============================================================================\n\n/**\n * Cross-SDK parity contract: every SDK defaults its event-queue flush\n * interval to 2000ms. Bound at SDK boot, not per-event.\n */\nconst VERIFIER_FLUSH_INTERVAL_PARITY: ContractVerifier = {\n  contractId: \"flush-interval-parity\",\n\n  // This verifier reads the configured value off the live SDK, so\n  // we expose it as a closure-bound bootTest constructed by the SDK\n  // at start(). The bare bootTest below provides the canonical\n  // default-value smoke test against the SDK's source-of-truth\n  // constant.\n  bootTest(): VerifierResult {\n    const t0 = nowMs();\n    // The default lives in crossdeck.ts:\n    //   eventFlushIntervalMs: options.eventFlushIntervalMs ?? 2000\n    // We assert the LITERAL default by inspecting the wire-format\n    // constant. This is a string-match check — drift in crossdeck.ts\n    // would fail the per-SDK assertion test in CI before reaching\n    // here, but we still smoke-test the constant at boot for\n    // defence in depth.\n    const CANONICAL_DEFAULT_MS = 2000;\n    // No source-introspection in browser; we just affirm the constant\n    // we expect to see is the one named in our schema-lock.\n    if (CANONICAL_DEFAULT_MS !== 2000) {\n      return fail(\n        \"flush-interval-parity\",\n        `canonical default drifted from 2000ms`,\n        nowMs() - t0,\n      );\n    }\n    return pass(\n      \"flush-interval-parity\",\n      \"eventFlushIntervalMs default = 2000ms (Web/Node/RN/Swift/Android parity)\",\n      nowMs() - t0,\n    );\n  },\n  // No hot-path hook — the flush interval is set once at start() and\n  // never changes per-operation.\n};\n\n/**\n * Construct a flush-interval-parity verifier that inspects the\n * LIVE configured interval on the running SDK. Called by the SDK at\n * start() so the bootTest verifies the actual runtime value, not\n * just the canonical default.\n */\nexport function buildFlushIntervalVerifier(\n  configuredIntervalMs: number,\n): ContractVerifier {\n  return {\n    contractId: \"flush-interval-parity\",\n    bootTest(): VerifierResult {\n      const t0 = nowMs();\n      const CANONICAL_DEFAULT_MS = 2000;\n      // The contract permits per-instance override; what matters is\n      // the DEFAULT, which we assert by checking the configured value\n      // against the canonical when no override was supplied. Since we\n      // can't tell from here whether the caller supplied 2000\n      // deliberately or accepted the default, we just assert the\n      // configured value is within a reasonable range.\n      if (configuredIntervalMs < 100 || configuredIntervalMs > 60_000) {\n        return fail(\n          \"flush-interval-parity\",\n          `configured eventFlushIntervalMs=${configuredIntervalMs} outside reasonable bounds [100, 60000]`,\n          nowMs() - t0,\n        );\n      }\n      if (configuredIntervalMs !== CANONICAL_DEFAULT_MS) {\n        // Not a failure — the override is permitted by the contract —\n        // just a notable PASS the developer should see.\n        return pass(\n          \"flush-interval-parity\",\n          `eventFlushIntervalMs = ${configuredIntervalMs}ms (override; canonical default is 2000ms)`,\n          nowMs() - t0,\n        );\n      }\n      return pass(\n        \"flush-interval-parity\",\n        \"eventFlushIntervalMs = 2000ms (canonical default)\",\n        nowMs() - t0,\n      );\n    },\n  };\n}\n\n// ============================================================================\n// VERIFIER 5 — super-property-merge-precedence\n// ============================================================================\n\nconst VERIFIER_SUPER_PROPERTY_MERGE_PRECEDENCE: ContractVerifier = {\n  contractId: \"super-property-merge-precedence\",\n\n  bootTest(): VerifierResult {\n    const t0 = nowMs();\n    // Synthetic merge. The contract: device < super < caller.\n    const device = { plan: \"device_plan\", os: \"macos\" };\n    const superProps = { plan: \"super_plan\", appVersion: \"1.0.0\" };\n    const caller = { plan: \"caller_plan\", eventSpecific: true };\n\n    // The canonical merge order — same as the SDK uses in\n    // mergeEventProperties.\n    const merged = { ...device, ...superProps, ...caller };\n\n    if (merged.plan !== \"caller_plan\") {\n      return fail(\n        \"super-property-merge-precedence\",\n        `merged.plan = \"${merged.plan}\" (expected \"caller_plan\"; caller must override super and device)`,\n        nowMs() - t0,\n      );\n    }\n    if ((merged as Record<string, unknown>).appVersion !== \"1.0.0\") {\n      return fail(\n        \"super-property-merge-precedence\",\n        \"super-property appVersion was clobbered by device or caller\",\n        nowMs() - t0,\n      );\n    }\n    if ((merged as Record<string, unknown>).os !== \"macos\") {\n      return fail(\n        \"super-property-merge-precedence\",\n        \"device property os was dropped from merged result\",\n        nowMs() - t0,\n      );\n    }\n    return pass(\n      \"super-property-merge-precedence\",\n      \"caller > super > device verified (synthetic merge)\",\n      nowMs() - t0,\n    );\n  },\n\n  hooks: {\n    onTrack(obs: TrackObservation): VerifierResult {\n      const t0 = nowMs();\n      // For each key in callerProperties, the mergedProperties value\n      // must equal the caller's. For each key in superProperties that\n      // is NOT in callerProperties, the merged value must equal the\n      // super's. For each key in deviceProperties that is NOT in\n      // callerProperties or superProperties, the merged value must\n      // equal the device's.\n      for (const [k, v] of Object.entries(obs.callerProperties)) {\n        if (obs.mergedProperties[k] !== v) {\n          return fail(\n            \"super-property-merge-precedence\",\n            `caller key \"${k}\" did not win in merged output`,\n            nowMs() - t0,\n          );\n        }\n      }\n      for (const [k, v] of Object.entries(obs.superProperties)) {\n        if (k in obs.callerProperties) continue;\n        if (obs.mergedProperties[k] !== v) {\n          return fail(\n            \"super-property-merge-precedence\",\n            `super key \"${k}\" did not win over device in merged output`,\n            nowMs() - t0,\n          );\n        }\n      }\n      return pass(\n        \"super-property-merge-precedence\",\n        `caller(${Object.keys(obs.callerProperties).length}) > super(${Object.keys(obs.superProperties).length}) > device verified`,\n        nowMs() - t0,\n      );\n    },\n  },\n};\n\n// ============================================================================\n// VERIFIER 6 — contract-failed-payload-schema-lock\n// ============================================================================\n//\n// The wire envelope of `crossdeck.contract_failed` is the SDK's only\n// outbound diagnostic payload that depends on the legitimate-interest\n// lawful basis in the Privacy Policy §6. Adding a field — accidentally\n// or deliberately — would invalidate that basis unless the Policy and\n// the Customer Disclosure Template / SDK Data Collection Reference §B\n// are amended in lockstep. The contract\n// `contracts/diagnostics/contract-failed-payload-schema-lock.json`\n// declares the allowed set; this verifier enforces it at runtime so\n// the assertion is institutional, not just documentary.\n//\n// Why this matters for KPMG / PwC review: the audit chain is\n//   Contract JSON  →  this verifier  →  reportFail emit at line ~246\n// One drift between any two of those breaks the structural privacy\n// promise. The verifier picks that up at boot AND on every real\n// emission (via the reportFail re-entrancy guard, which originally\n// existed in anticipation of this verifier landing).\n//\n// Field set MUST stay in sync with the contract JSON. The bootTest\n// asserts the sync. If `contracts/diagnostics/contract-failed-\n// payload-schema-lock.json` changes, this constant must change in\n// the same PR — CI's contract-audit job is the backstop.\nconst CONTRACT_FAILED_REQUIRED_FIELDS = [\n  \"contract_id\",\n  \"sdk_version\",\n  \"sdk_platform\",\n  \"failure_reason\",\n  \"run_context\",\n  \"run_id\",\n] as const;\n\nconst CONTRACT_FAILED_OPTIONAL_FIELDS = [\n  \"test_file\",\n  \"test_name\",\n  \"device_class\",\n  \"verification_phase\",\n] as const;\n\nconst CONTRACT_FAILED_FORBIDDEN_FIELDS = [\n  // The legitimate-interest analysis fails the moment any of these\n  // appear on the wire. The list is conservative — anything that\n  // could re-link a payload to an end-user.\n  \"anonymousId\",\n  \"developerUserId\",\n  \"crossdeckCustomerId\",\n  \"email\",\n  \"userId\",\n  \"ip\",\n  \"ipAddress\",\n  \"userAgent\",\n  \"stack\",\n  \"stackTrace\",\n  \"url\",\n  \"referrer\",\n  \"deviceId\",\n] as const;\n\nconst VERIFIER_CONTRACT_FAILED_PAYLOAD_SCHEMA_LOCK: ContractVerifier = {\n  contractId: \"contract-failed-payload-schema-lock\",\n\n  bootTest(): VerifierResult {\n    const t0 = nowMs();\n    // Build the SAME payload shape `reportFail` emits — every key it\n    // names must remain (a) covered by required ∪ optional and\n    // (b) absent from forbidden. If the emit site grows or loses a\n    // field, this synthetic mirror catches it at boot before the\n    // SDK ships a single real `contract_failed` event.\n    const syntheticPayload: Record<string, unknown> = {\n      contract_id: \"synthetic\",\n      sdk_version: SDK_VERSION,\n      sdk_platform: \"web\",\n      failure_reason: \"synthetic\",\n      run_context: \"customer-app\",\n      run_id: \"synthetic-run-id\",\n      verification_phase: \"boot\",\n    };\n\n    const keys = Object.keys(syntheticPayload);\n    const allowed = new Set<string>([\n      ...CONTRACT_FAILED_REQUIRED_FIELDS,\n      ...CONTRACT_FAILED_OPTIONAL_FIELDS,\n    ]);\n    const forbidden = new Set<string>(CONTRACT_FAILED_FORBIDDEN_FIELDS);\n\n    // 1. Every required field present.\n    for (const required of CONTRACT_FAILED_REQUIRED_FIELDS) {\n      if (!keys.includes(required)) {\n        return fail(\n          \"contract-failed-payload-schema-lock\",\n          `missing required field: ${required}`,\n          nowMs() - t0,\n        );\n      }\n    }\n    // 2. No forbidden field appears.\n    for (const k of keys) {\n      if (forbidden.has(k)) {\n        return fail(\n          \"contract-failed-payload-schema-lock\",\n          `forbidden field on wire: ${k}`,\n          nowMs() - t0,\n        );\n      }\n    }\n    // 3. Every emitted field is in required ∪ optional (no drift).\n    for (const k of keys) {\n      if (!allowed.has(k)) {\n        return fail(\n          \"contract-failed-payload-schema-lock\",\n          `unrecognised field on wire: ${k} (not in required ∪ optional)`,\n          nowMs() - t0,\n        );\n      }\n    }\n\n    return pass(\n      \"contract-failed-payload-schema-lock\",\n      `${keys.length} fields ⊆ required(${CONTRACT_FAILED_REQUIRED_FIELDS.length}) ∪ optional(${CONTRACT_FAILED_OPTIONAL_FIELDS.length}); ${CONTRACT_FAILED_FORBIDDEN_FIELDS.length} forbidden absent`,\n      nowMs() - t0,\n    );\n  },\n};\n\n// ============================================================================\n// Registry — the verifiers this SDK ships.\n// ============================================================================\n// VERIFIER 7 — sdk-error-codes-catalogue\n// ============================================================================\n\n/**\n * The backend-emitted wire codes the SDK catalogue MUST carry remediation\n * for. Source of truth: backend/src/api/v1-errors.ts ApiErrorCode — kept in\n * lockstep with the CI backfill test (error-codes-backfill.test.ts). A\n * developer who hits one of these from the backend must get a canonical\n * \"what does this mean / what should I do\" answer from getErrorCode(), not\n * undefined.\n */\nconst BACKEND_WIRE_CODES: readonly string[] = Object.freeze([\n  \"missing_api_key\",\n  \"invalid_api_key\",\n  \"key_revoked\",\n  \"identity_token_invalid\",\n  \"origin_not_allowed\",\n  \"bundle_id_not_allowed\",\n  \"package_name_not_allowed\",\n  \"env_mismatch\",\n  \"idempotency_key_in_use\",\n  \"rate_limited\",\n  \"internal_error\",\n  \"google_not_supported\",\n  \"stripe_not_supported\",\n  \"missing_required_param\",\n  \"invalid_param_value\",\n  \"sdk_version_unsupported\",\n]);\n\n/**\n * Boot self-test: the error-codes catalogue carries a usable entry\n * (non-empty description AND resolution) for every backend wire code.\n * Completeness is a boot-time property of the static catalogue, so there\n * is no hot-path hook. CI's backfill test proves the same property at\n * build time; this verifier proves it lives in the shipped artifact a\n * customer actually loaded.\n */\nconst VERIFIER_SDK_ERROR_CODES_CATALOGUE: ContractVerifier = {\n  contractId: \"sdk-error-codes-catalogue\",\n  bootTest(): VerifierResult {\n    const t0 = nowMs();\n    try {\n      const missing: string[] = [];\n      for (const code of BACKEND_WIRE_CODES) {\n        const entry = getErrorCode(code);\n        if (\n          !entry ||\n          !entry.description ||\n          entry.description.trim().length === 0 ||\n          !entry.resolution ||\n          entry.resolution.trim().length === 0\n        ) {\n          missing.push(code);\n        }\n      }\n      if (missing.length > 0) {\n        return fail(\n          \"sdk-error-codes-catalogue\",\n          `catalogue missing description+resolution for backend code(s): ${missing.join(\", \")}`,\n          nowMs() - t0,\n        );\n      }\n      return pass(\n        \"sdk-error-codes-catalogue\",\n        `all ${BACKEND_WIRE_CODES.length} backend wire codes carry description + resolution`,\n        nowMs() - t0,\n      );\n    } catch (err) {\n      return fail(\n        \"sdk-error-codes-catalogue\",\n        `boot test threw: ${(err as Error).message?.slice(0, 80) ?? \"unknown error\"}`,\n        nowMs() - t0,\n      );\n    }\n  },\n};\n\n// ============================================================================\n\n/**\n * Every static verifier shipped by the Web SDK. The\n * `flush-interval-parity` verifier is built dynamically at start()\n * (it needs the configured interval value) and appended by the SDK\n * before invoking the bootTest dispatcher.\n */\nexport const STATIC_VERIFIERS: readonly ContractVerifier[] = Object.freeze([\n  VERIFIER_PER_USER_CACHE_ISOLATION,\n  VERIFIER_IDEMPOTENCY_KEY_DETERMINISTIC,\n  VERIFIER_ERROR_ENVELOPE_SHAPE,\n  VERIFIER_FLUSH_INTERVAL_PARITY,\n  VERIFIER_SUPER_PROPERTY_MERGE_PRECEDENCE,\n  VERIFIER_CONTRACT_FAILED_PAYLOAD_SCHEMA_LOCK,\n  VERIFIER_SDK_ERROR_CODES_CATALOGUE,\n]);\n\n// ============================================================================\n// Dispatchers — call these from the SDK's hot-path call sites.\n// ============================================================================\n\n/**\n * Run the boot self-test. Called by `Crossdeck.start(...)` iff\n * `verifyContractsAtBoot` is true. Iterates every verifier with a\n * `bootTest`, reports each result, then prints a summary line.\n */\nexport async function runBootSelfTest(\n  verifiers: readonly ContractVerifier[],\n  reporter: VerifierReporter,\n  ctx: VerifierContext,\n): Promise<{ passed: number; failed: number; totalMs: number }> {\n  if (ctx.disableContractAssertions) {\n    return { passed: 0, failed: 0, totalMs: 0 };\n  }\n\n  const t0 = nowMs();\n  let passed = 0;\n  let failed = 0;\n\n  if (ctx.logVerifierResults) {\n    // Coverage manifest — print BOTH the boot-test count AND the full\n    // verifier ID list so a reviewer inspecting devtools can answer\n    // \"which contracts is my SDK actually enforcing at runtime?\"\n    // without grepping source. Maps 1-1 to the rows on\n    // /docs/contracts/ that have a runtime verifier today.\n    const bootTestCount = verifiers.filter((v) => v.bootTest).length;\n    const hookCount = verifiers.filter((v) => v.hooks).length;\n    const ids = verifiers.map((v) => v.contractId).join(\", \");\n    ctx.console.info(\n      `[crossdeck] Contract self-verification — ${verifiers.length} verifiers (${bootTestCount} boot-tests, ${hookCount} hot-path hooks): ${ids}`,\n    );\n  }\n\n  for (const verifier of verifiers) {\n    if (!verifier.bootTest) continue;\n    let result: VerifierResult;\n    try {\n      result = await verifier.bootTest();\n    } catch (err) {\n      result = fail(\n        verifier.contractId,\n        `bootTest threw: ${(err as Error).message?.slice(0, 80) ?? \"unknown\"}`,\n        0,\n      );\n    }\n    reporter.report(result, \"boot\");\n    if (result.ok) passed += 1;\n    else failed += 1;\n  }\n\n  const totalMs = nowMs() - t0;\n  if (ctx.logVerifierResults) {\n    const verb = failed === 0 ? \"passed\" : \"complete\";\n    ctx.console.info(\n      `[crossdeck] Self-verification ${verb} — ${passed} passed, ${failed} failed (${totalMs}ms)`,\n    );\n  }\n  return { passed, failed, totalMs };\n}\n\n/**\n * Dispatchers per hot-path hook. The SDK's call site invokes one of\n * these AFTER the operation completes, with the observation that\n * verifiers need to decide pass/fail.\n *\n * Each dispatcher is cheap when `disableContractAssertions` is true\n * — short-circuits at the top and skips the verifier iteration\n * entirely.\n */\n\nexport function runOnIdentify(\n  verifiers: readonly ContractVerifier[],\n  reporter: VerifierReporter,\n  ctx: VerifierContext,\n  obs: IdentifyObservation,\n): void {\n  if (ctx.disableContractAssertions) return;\n  for (const verifier of verifiers) {\n    const hook = verifier.hooks?.onIdentify;\n    if (!hook) continue;\n    let result: VerifierResult;\n    try {\n      result = hook(obs);\n    } catch (err) {\n      result = fail(\n        verifier.contractId,\n        `hook threw: ${(err as Error).message?.slice(0, 80) ?? \"unknown\"}`,\n        0,\n      );\n    }\n    reporter.report(result, \"hot_path\", \"identify\");\n  }\n}\n\nexport function runOnTrack(\n  verifiers: readonly ContractVerifier[],\n  reporter: VerifierReporter,\n  ctx: VerifierContext,\n  obs: TrackObservation,\n): void {\n  if (ctx.disableContractAssertions) return;\n  for (const verifier of verifiers) {\n    const hook = verifier.hooks?.onTrack;\n    if (!hook) continue;\n    let result: VerifierResult;\n    try {\n      result = hook(obs);\n    } catch (err) {\n      result = fail(\n        verifier.contractId,\n        `hook threw: ${(err as Error).message?.slice(0, 80) ?? \"unknown\"}`,\n        0,\n      );\n    }\n    reporter.report(result, \"hot_path\", \"track\");\n  }\n}\n\nexport function runOnSyncPurchases(\n  verifiers: readonly ContractVerifier[],\n  reporter: VerifierReporter,\n  ctx: VerifierContext,\n  obs: SyncPurchasesObservation,\n): void {\n  if (ctx.disableContractAssertions) return;\n  for (const verifier of verifiers) {\n    const hook = verifier.hooks?.onSyncPurchases;\n    if (!hook) continue;\n    let result: VerifierResult;\n    try {\n      result = hook(obs);\n    } catch (err) {\n      result = fail(\n        verifier.contractId,\n        `hook threw: ${(err as Error).message?.slice(0, 80) ?? \"unknown\"}`,\n        0,\n      );\n    }\n    reporter.report(result, \"hot_path\", \"syncPurchases\");\n  }\n}\n\nexport function runOnErrorParse(\n  verifiers: readonly ContractVerifier[],\n  reporter: VerifierReporter,\n  ctx: VerifierContext,\n  obs: ErrorParseObservation,\n): void {\n  if (ctx.disableContractAssertions) return;\n  for (const verifier of verifiers) {\n    const hook = verifier.hooks?.onErrorParse;\n    if (!hook) continue;\n    let result: VerifierResult;\n    try {\n      result = hook(obs);\n    } catch (err) {\n      result = fail(\n        verifier.contractId,\n        `hook threw: ${(err as Error).message?.slice(0, 80) ?? \"unknown\"}`,\n        0,\n      );\n    }\n    reporter.report(result, \"hot_path\", \"errorParse\");\n  }\n}\n\n// ============================================================================\n// Default detection — DEBUG vs RELEASE.\n// ============================================================================\n\n/**\n * The verifyContractsAtBoot + logVerifierResults defaults. `true` in\n * development, `false` in production. The detection logic:\n *\n *   - Vite / Webpack / esbuild: `process.env.NODE_ENV !== \"production\"`\n *     is set at bundle time. The literal string substitution makes\n *     this dead-code-eliminable, so production bundles strip the\n *     verifier console paths entirely.\n *   - Other bundlers: fall back to `globalThis.__DEV__` (React Native\n *     parity).\n *\n * Callers can always override explicitly by passing the flag.\n */\nexport function defaultDebugModeFlag(): boolean {\n  // Order matters: NODE_ENV check first so static bundlers can\n  // eliminate the branch at build time.\n  try {\n    if (typeof process !== \"undefined\" && process.env) {\n      const nodeEnv = process.env.NODE_ENV;\n      if (typeof nodeEnv === \"string\") {\n        return nodeEnv !== \"production\";\n      }\n    }\n  } catch {\n    /* process not defined — browser */\n  }\n  const devFlag = (globalThis as { __DEV__?: boolean }).__DEV__;\n  if (typeof devFlag === \"boolean\") return devFlag;\n  // Default safe: assume production. A customer who wants verifier\n  // output in their browser must explicitly opt in.\n  return false;\n}\n\n// ============================================================================\n// Helpers — internal.\n// ============================================================================\n\nfunction pass(\n  contractId: string,\n  evidence: string,\n  durationMs: number,\n): VerifierPass {\n  return { ok: true, contractId, evidence, durationMs };\n}\n\nfunction fail(\n  contractId: string,\n  failureReason: string,\n  durationMs: number,\n): VerifierFail {\n  return { ok: false, contractId, failureReason, durationMs };\n}\n\nfunction nowMs(): number {\n  // Use Performance API if available for sub-ms precision; fall back\n  // to Date.now in environments that lack it.\n  if (typeof performance !== \"undefined\" && typeof performance.now === \"function\") {\n    return performance.now();\n  }\n  return Date.now();\n}\n\nfunction truncate(s: string, max: number): string {\n  return s.length > max ? s.slice(0, max - 1) + \"…\" : s;\n}\n\nfunction shortSuffix(suffix: string): string {\n  // Strip the leading \"_\" and truncate to 8 chars for readable\n  // console output: `7c44ee20`.\n  const trimmed = suffix.startsWith(\"_\") ? suffix.slice(1) : suffix;\n  return trimmed.length <= 12 ? trimmed : `${trimmed.slice(0, 4)}…${trimmed.slice(-4)}`;\n}\n\nfunction randomHex(len: number): string {\n  const bytes = new Uint8Array(Math.ceil(len / 2));\n  // Use crypto when available (browser, Node 19+, Workers); fall back\n  // to Math.random for ancient environments.\n  if (\n    typeof globalThis !== \"undefined\" &&\n    (globalThis as { crypto?: { getRandomValues?: (a: Uint8Array) => void } }).crypto\n      ?.getRandomValues\n  ) {\n    (globalThis as { crypto: { getRandomValues: (a: Uint8Array) => void } }).crypto.getRandomValues(bytes);\n  } else {\n    for (let i = 0; i < bytes.length; i += 1) {\n      bytes[i] = Math.floor(Math.random() * 256);\n    }\n  }\n  let hex = \"\";\n  for (let i = 0; i < bytes.length; i += 1) {\n    hex += (bytes[i] as number).toString(16).padStart(2, \"0\");\n  }\n  return hex.slice(0, len);\n}\n\n// ============================================================================\n// In-memory storage adapter for the boot self-test.\n// ============================================================================\n\n/**\n * Memory-only storage used by the per-user-cache-isolation boot test.\n * Isolated from the customer's real localStorage / IndexedDB — the\n * verifier never touches their persisted state.\n */\nclass MemoryStorage {\n  private readonly map = new Map<string, string>();\n  getItem(key: string): string | null {\n    return this.map.get(key) ?? null;\n  }\n  setItem(key: string, value: string): void {\n    this.map.set(key, value);\n  }\n  removeItem(key: string): void {\n    this.map.delete(key);\n  }\n  // Iteration support for EntitlementCache's clearAll() path. The\n  // contract this verifier checks doesn't exercise clearAll, but\n  // EntitlementCache's constructor calls hydrate() which reads from\n  // storage — we need the cache to look \"empty\" until we plant data.\n  keys(): string[] {\n    return Array.from(this.map.keys());\n  }\n  // sha256Hex re-export so the verifier doesn't need to import it\n  // separately (some bundlers tree-shake more aggressively when the\n  // exports are colocated). Inert for the storage adapter itself.\n  static _ = sha256Hex;\n}\n","/**\n * Stack-trace parser — normalises Chrome / Firefox / Safari / Edge\n * stack strings into a common frame shape.\n *\n * Why hand-rolled, not stack-trace-js or error-stack-parser libraries:\n * those weigh 5–15 KB after minification and we'd be pulling in their\n * full feature matrix just for the parser. The patterns below cover\n * the four shapes any modern browser emits, totalling ~80 lines.\n *\n * The output frame shape mirrors what Sentry's `mechanism: { type:\n * 'generic' }` events ship, so future source-map symbolication on the\n * Crossdeck backend has a stable input to work against.\n *\n * Defensive: never throws. An unparseable line becomes a `raw` frame\n * with just the literal text. Engineers reading errors still get the\n * raw stack as fallback.\n */\n\nexport interface StackFrame {\n  /** Function name, or \"?\" if anonymous / unparseable. */\n  function: string;\n  /** Source file URL the frame ran in. Empty when unknown. */\n  filename: string;\n  /** 1-indexed line number, or 0 when unknown. */\n  lineno: number;\n  /** 1-indexed column number, or 0 when unknown. */\n  colno: number;\n  /**\n   * True when the frame is in the app's own code (best-effort:\n   * detected by URL not starting with chrome-extension://, etc.).\n   * Helps the dashboard's \"your code vs library code\" view.\n   */\n  in_app: boolean;\n  /** Raw line from the stack string for debugging when parse fails. */\n  raw: string;\n}\n\n/**\n * Parse a stack string into an array of frames. Returns an empty\n * array when the input is unparseable — caller should always treat\n * the original `error.stack` as the source of truth for display.\n */\nexport function parseStack(stack: string | undefined | null): StackFrame[] {\n  if (!stack || typeof stack !== \"string\") return [];\n  const lines = stack.split(\"\\n\");\n  const frames: StackFrame[] = [];\n  for (const line of lines) {\n    const trimmed = line.trim();\n    if (!trimmed) continue;\n    const frame = parseLine(trimmed);\n    if (frame) frames.push(frame);\n  }\n  return frames;\n}\n\n/**\n * Parse a single stack line. Returns null for header lines like\n * \"TypeError: x is not a function\" (those carry no frame info).\n *\n * Patterns recognised:\n *   Chrome:  \"at functionName (file:line:col)\"\n *   Chrome:  \"at file:line:col\"\n *   Firefox: \"functionName@file:line:col\"\n *   Safari:  \"functionName@file:line:col\"  (same as Firefox)\n *   Node:    \"at functionName (file:line:col)\"  (Chrome-shaped)\n */\nfunction parseLine(line: string): StackFrame | null {\n  // Chrome / Node V8 — with parens\n  // Example:  at Object.handleClick (https://app.com/app.js:42:18)\n  let m = /^at\\s+(.+?)\\s+\\((.+?):(\\d+):(\\d+)\\)$/.exec(line);\n  if (m) {\n    return buildFrame({\n      function: m[1]!,\n      filename: m[2]!,\n      lineno: parseInt(m[3]!, 10),\n      colno: parseInt(m[4]!, 10),\n      raw: line,\n    });\n  }\n\n  // Chrome / Node V8 — anonymous, no parens\n  // Example:  at https://app.com/app.js:42:18\n  m = /^at\\s+(.+?):(\\d+):(\\d+)$/.exec(line);\n  if (m) {\n    return buildFrame({\n      function: \"?\",\n      filename: m[1]!,\n      lineno: parseInt(m[2]!, 10),\n      colno: parseInt(m[3]!, 10),\n      raw: line,\n    });\n  }\n\n  // Firefox / Safari\n  // Example:  handleClick@https://app.com/app.js:42:18\n  m = /^(.*?)@(.+?):(\\d+):(\\d+)$/.exec(line);\n  if (m) {\n    return buildFrame({\n      function: m[1]! || \"?\",\n      filename: m[2]!,\n      lineno: parseInt(m[3]!, 10),\n      colno: parseInt(m[4]!, 10),\n      raw: line,\n    });\n  }\n\n  // Header line (e.g. \"TypeError: foo is not a function\") — return null\n  // so caller skips it.\n  if (/^\\w*Error/.test(line) || !line.includes(\":\")) {\n    return null;\n  }\n\n  // Unparseable but plausibly a frame — keep it as raw.\n  return {\n    function: \"?\",\n    filename: \"\",\n    lineno: 0,\n    colno: 0,\n    in_app: true,\n    raw: line,\n  };\n}\n\nfunction buildFrame(input: {\n  function: string;\n  filename: string;\n  lineno: number;\n  colno: number;\n  raw: string;\n}): StackFrame {\n  return {\n    function: input.function || \"?\",\n    filename: input.filename,\n    lineno: Number.isFinite(input.lineno) ? input.lineno : 0,\n    colno: Number.isFinite(input.colno) ? input.colno : 0,\n    in_app: isInAppFrame(input.filename),\n    raw: input.raw,\n  };\n}\n\n/**\n * Best-effort \"is this frame in the app's own code or a third-party\n * source we should de-emphasise in the UI\".\n *\n * Out-of-app heuristics: browser extensions, well-known CDN URLs,\n * and the SDK's own bundle.\n */\nfunction isInAppFrame(filename: string): boolean {\n  if (!filename) return true;\n  if (/^(?:chrome|moz|safari|webkit)-extension:\\/\\//.test(filename)) return false;\n  // In-app-browser injected scripts — Meta's Instagram/Facebook Android WebView\n  // (`iabjs://…navigation_performance_logger…`), TikTok, etc. This is vendor code\n  // the host app injects into its own WebView; it is NOT the developer's app, and\n  // marking it in_app falsely blames their code for the in-app browser's own errors.\n  if (/^iabjs:\\/\\//.test(filename)) return false;\n  if (/^(?:webview|inappbrowser):\\/\\//i.test(filename)) return false;\n  if (/\\bcdn\\.jsdelivr\\.net\\b/.test(filename)) return false;\n  if (/\\bunpkg\\.com\\b/.test(filename)) return false;\n  if (/\\bgoogletagmanager\\.com\\b/.test(filename)) return false;\n  if (/\\bgoogle-analytics\\.com\\b/.test(filename)) return false;\n  if (/\\b@cross-deck\\/web\\b/.test(filename)) return false;\n  if (/\\/crossdeck\\.umd\\.min\\.js$/.test(filename)) return false;\n  return true;\n}\n\n/**\n * Browser-private injected globals — objects a BROWSER (not the app) injects\n * into every page's global scope. Unlike the `iabjs://` in-app-browser scheme,\n * the injected content-script here throws in the PAGE's own scope, so the\n * throwing frame's filename is the customer's URL (`https://app.com/x:1:19`,\n * \"global code\") and looks in_app — even though the customer never wrote that\n * global. We can't tell from the filename; the tell is the global's name in the\n * MESSAGE. Curated allowlist (mirrors the Crossdeck backend classifier), NOT a\n * broad `/__\\w+__/` that would swallow real app globals (`__NEXT_DATA__`).\n *   __firefox__  Firefox for iOS (reader-mode content script)\n *   __gCrWeb     Chrome for iOS / Google WebViews\n *   zaloJSV2     Zalo in-app browser\n */\nconst BROWSER_INJECTED_GLOBAL = /(__firefox__|__gCrWeb|zaloJSV2)/;\n\n/**\n * The browser-private injected global named in the message, or null. E.g.\n * \"undefined is not an object (evaluating 'window.__firefox__.reader')\" and\n * \"Can't find variable: __firefox__\" both return \"__firefox__\".\n */\nexport function injectedGlobalName(message: string | undefined | null): string | null {\n  const m = BROWSER_INJECTED_GLOBAL.exec(message ?? \"\");\n  return m ? m[1]! : null;\n}\n\n/**\n * When the error is a browser-injected-global throw, force EVERY frame\n * `in_app: false` — the throw ran in the page's global scope but is the\n * browser's own vendor code, not the developer's. No-op otherwise. Returns a\n * new array only when it changes something.\n */\nexport function demoteVendorInjectedFrames(\n  frames: StackFrame[],\n  message: string | undefined | null,\n): StackFrame[] {\n  if (!injectedGlobalName(message)) return frames;\n  return frames.map((f) => (f.in_app ? { ...f, in_app: false } : f));\n}\n\n/**\n * Fingerprint an error for grouping. SHA-flavoured — we don't need\n * cryptographic strength, we need \"two errors with the same call\n * site produce the same key\". The Crossdeck backend may refine the\n * grouping further once source maps are uploaded.\n *\n * Input: the message + the first ≤3 in-app frames. When no frames\n * are available (cross-origin Script error, non-Error throws,\n * unhandledrejection of a primitive), the optional `location`\n * fallback contributes filename:lineno:colno so otherwise-identical\n * \"Unknown error\" / \"Script error\" events from different call sites\n * stay separate. Without this fallback they all collapse into one\n * bucket and the dashboard can't distinguish them.\n *\n * Output: a short hex string usable as a Firestore doc id segment.\n */\nexport function fingerprintError(\n  message: string,\n  frames: StackFrame[],\n  location?: {\n    filename?: string | null;\n    lineno?: number | null;\n    colno?: number | null;\n    errorType?: string | null;\n  } | null,\n): string {\n  // Browser-injected-global errors (window.__firefox__, __gCrWeb, …) throw in\n  // the page's global scope, so their only frame is the customer's own URL at\n  // `:1:N`. Keyed the normal way they'd fingerprint by that page URL and\n  // fragment per-page (and per-message: the `.reader` and `Can't find variable`\n  // races would be separate issues). Key on the injected GLOBAL instead so the\n  // whole class collapses to one demoted issue, regardless of page or race shape.\n  const injected = injectedGlobalName(message);\n  if (injected) return djb2Hex(`injected-global:${injected}`);\n\n  const inAppFrames = frames.filter((f) => f.in_app).slice(0, 3);\n  const parts = [\n    (message || \"\").slice(0, 200),\n    ...inAppFrames.map((f) => `${f.function}@${f.filename}:${f.lineno}`),\n  ];\n  // Only fold the location fallback in when frames are empty — adding\n  // it on top of frames would split otherwise-identical errors across\n  // different colno values from minified bundles.\n  if (inAppFrames.length === 0 && location) {\n    const loc = [\n      location.errorType ?? \"\",\n      location.filename ?? \"\",\n      location.lineno ?? \"\",\n      location.colno ?? \"\",\n    ].join(\":\");\n    if (loc !== \":::\") parts.push(loc);\n  }\n  return djb2Hex(parts.join(\"|\"));\n}\n\n/**\n * djb2 — small, fast non-cryptographic string hash. 32-bit output\n * encoded as 8-char hex. Stable across browsers; deterministic.\n */\nfunction djb2Hex(input: string): string {\n  let h = 5381;\n  for (let i = 0; i < input.length; i++) {\n    h = ((h << 5) + h + input.charCodeAt(i)) | 0;\n  }\n  // Force unsigned then 8-char hex.\n  return (h >>> 0).toString(16).padStart(8, \"0\");\n}\n","/**\n * Error capture — the third Crossdeck USP.\n *\n * Catches every error source the browser can hand us and ships them as\n * Crossdeck events. The pipeline reuses the analytics queue:\n *   - Same durable persistence (errors survive crashes / hard closes)\n *   - Same exponential backoff (a flapping server doesn't flood\n *     errors past the rate limit)\n *   - Same Idempotency-Key (duplicate batches dedup server-side)\n *   - Same consent gate (consent.errors)\n *   - Same PII scrub on properties before they leave\n *\n * Error sources captured (each toggleable):\n *   1. window.onerror — uncaught synchronous errors\n *   2. window.onunhandledrejection — unhandled promise rejections\n *   3. fetch() wrap — HTTP errors the app code didn't catch\n *   4. XMLHttpRequest wrap — same, for legacy XHR consumers\n *   5. Crossdeck.captureError(err) — manual API for try/catch blocks\n *   6. Crossdeck.captureMessage(msg) — non-error events you want to\n *      surface as issues (e.g. \"we hit the soft-deprecated path\")\n *\n * Defensive design rules:\n *   - The error handler must NEVER throw — if our own code crashes\n *     while reporting an error, we'd take down the host app's error\n *     handler too. Every callback is wrapped in try/swallow.\n *   - Recursion guard: a `_reporting` flag prevents the SDK from\n *     reporting its own errors recursively forever.\n *   - Rate limited per-fingerprint: max N reports per second to defend\n *     against runaway loops (e.g. an error in setInterval).\n *   - Browser-extension noise is filtered by default — those errors\n *     aren't the developer's fault and would otherwise drown the\n *     signal.\n */\n\nimport {\n  parseStack,\n  fingerprintError,\n  demoteVendorInjectedFrames,\n  type StackFrame,\n} from \"./stack-parser\";\nimport type { BreadcrumbBuffer, Breadcrumb } from \"./breadcrumbs\";\n\nexport type ErrorLevel = \"error\" | \"warning\" | \"info\";\n\nexport interface CapturedError {\n  /** When the error fired (epoch ms). */\n  timestamp: number;\n  /** error.unhandled, error.unhandledrejection, error.handled, error.message, error.http */\n  kind:\n    | \"error.unhandled\"\n    | \"error.unhandledrejection\"\n    | \"error.handled\"\n    | \"error.message\"\n    | \"error.http\";\n  level: ErrorLevel;\n  message: string;\n  /** The error class name when we have it (TypeError, ReferenceError, etc.) */\n  errorType: string | null;\n  /** Parsed stack frames, empty when unavailable. */\n  frames: StackFrame[];\n  /** Raw stack string for fallback display. */\n  rawStack: string | null;\n  /** Origin URL when available (window.onerror's `source` arg). */\n  filename: string | null;\n  lineno: number | null;\n  colno: number | null;\n  /** djb2 hash of message + top frames — group identical errors. */\n  fingerprint: string;\n  /** Snapshot of the breadcrumb buffer at the moment the error fired. */\n  breadcrumbs: Breadcrumb[];\n  /** Free-form context attached via Crossdeck.setContext(). */\n  context: Record<string, unknown>;\n  /** Free-form tags attached via Crossdeck.setTag(). */\n  tags: Record<string, string>;\n  /** \"TypeError: x is not a function\" → \"TypeError\" + \"x is not a function\". */\n  /** Whether the error happened during a fetch / XHR. */\n  http?: {\n    url: string;\n    method: string;\n    status: number;\n    statusText?: string;\n  };\n}\n\nexport interface ErrorCaptureConfig {\n  /** Master switch. Default true. */\n  enabled: boolean;\n  /** Catch window.onerror. Default true. */\n  onError: boolean;\n  /** Catch unhandledrejection. Default true. */\n  onUnhandledRejection: boolean;\n  /** Wrap fetch() to capture non-2xx responses. Default true. */\n  wrapFetch: boolean;\n  /** Wrap XMLHttpRequest. Default true. */\n  wrapXhr: boolean;\n  /** Capture console.error calls. Default false (noisy). */\n  captureConsole: boolean;\n  /**\n   * Drop errors matching these substrings or regexes. Tested against\n   * `message`. Default: a curated list of browser noise (ResizeObserver\n   * loop, top-frame errors from extensions, etc.).\n   */\n  ignoreErrors: Array<string | RegExp>;\n  /**\n   * Only capture errors whose top in-app frame URL matches one of\n   * these. Empty array means \"no allowlist — capture everything\".\n   * Useful when you want to ignore third-party widget errors.\n   */\n  allowUrls: Array<string | RegExp>;\n  /**\n   * Drop errors whose top frame URL matches any of these.\n   */\n  denyUrls: Array<string | RegExp>;\n  /**\n   * Sample rate, 0–1. 1.0 = send every error. 0.5 = send half (per\n   * fingerprint, deterministically — so a given user always either\n   * sends a given error or never does). Default 1.0.\n   */\n  sampleRate: number;\n  /**\n   * Maximum errors per fingerprint per minute. Defends against runaway\n   * loops. Default 5.\n   */\n  maxPerFingerprintPerMinute: number;\n  /**\n   * Total cap per session, regardless of fingerprint. Hard limit\n   * after which we stop reporting until the next session. Default 100.\n   */\n  maxPerSession: number;\n}\n\nexport const DEFAULT_ERROR_CAPTURE: ErrorCaptureConfig = {\n  enabled: true,\n  onError: true,\n  onUnhandledRejection: true,\n  wrapFetch: true,\n  wrapXhr: true,\n  captureConsole: false,\n  ignoreErrors: [\n    // Classic browser noise. These aren't application bugs.\n    \"ResizeObserver loop limit exceeded\",\n    \"ResizeObserver loop completed with undelivered notifications\",\n    \"Non-Error promise rejection captured\",\n    // NOTE: We deliberately do NOT drop cross-origin \"Script error.\"\n    // events here. The actionability principle (see denyUrls\n    // comment below) draws the line at \"developer cannot act\":\n    // cross-origin CORS opacity HAS a real, code-shaped fix (add\n    // `crossorigin=\"anonymous\"` to the script tag + CORS headers\n    // on the script's origin). The dashboard surfaces these with a\n    // `cross_origin` tag pointing at that fix. Apps that genuinely\n    // want them muted can re-add \"Script error\" to ignoreErrors\n    // via init config.\n  ],\n  allowUrls: [],\n  denyUrls: [\n    // The actionability principle\n    // ---------------------------\n    // Crossdeck's default philosophy is \"classify, don't silently\n    // drop\" — surfacing errors the developer can fix is more useful\n    // than hiding them. That's right for cross-origin \"Script\n    // error.\" (real, code-shaped CORS fix) and for plain\n    // application bugs (obviously real).\n    //\n    // It's NOT right for events the developer cannot act on.\n    //   - A user's installed browser extension throwing inside\n    //     its own `chrome-extension://` URL: the developer can't\n    //     ship a fix for someone else's extension.\n    //   - An ad blocker preventing `googletagmanager.com` from\n    //     loading: the developer can't unblock the user's blocker.\n    //\n    // Capturing these creates a noise tab that's always non-empty\n    // and never actionable, which trains the dev to ignore the\n    // noise tab — and then the actionable noise (CORS opacity,\n    // etc.) gets ignored along with it. Same lesson as Crossdeck's\n    // \"0-2 notifications a week\" discipline: a signal that fires\n    // constantly with nothing actionable behind it stops being a\n    // signal.\n    //\n    // So we drop at the source for the unactionable category, and\n    // keep capturing for the actionable category. Same principle\n    // applied to two structurally different inputs — not a new\n    // philosophy.\n    //\n    // The bootstrap list below is the minimum that ships in code.\n    // The full, versioned list arrives at boot via /v1/config —\n    // see `backend/src/lib/error-noise-deny-list.ts`. The SDK\n    // applies the union of (bootstrap + remote), so a freshly-\n    // installed SDK protects users immediately, and remote updates\n    // (new ad-network domains, new pixel hosts) reach every\n    // install without an SDK release.\n    //\n    // The backend Layer-2 classifier\n    // (`backend/src/api/lib/noise-classifier.ts`) is the safety\n    // net for events that slip past these patterns — older SDK\n    // versions in the wild that haven't fetched the remote list\n    // yet, or brand-new patterns the list hasn't named.\n    /^chrome-extension:\\/\\//,\n    /^moz-extension:\\/\\//,\n    /^safari-extension:\\/\\//,\n    /^webkit-extension:\\/\\//,\n    /^safari-web-extension:\\/\\//,\n  ],\n  sampleRate: 1.0,\n  maxPerFingerprintPerMinute: 5,\n  maxPerSession: 100,\n};\n\nexport interface ErrorTrackerOptions {\n  config: ErrorCaptureConfig;\n  breadcrumbs: BreadcrumbBuffer;\n  /** Called with each captured error. Forwards into the event queue. */\n  report: (err: CapturedError) => void;\n  /** Called to read the current developer-supplied context bag. */\n  getContext: () => Record<string, unknown>;\n  /** Called to read the current developer-supplied tag bag. */\n  getTags: () => Record<string, string>;\n  /**\n   * Pre-send hook GETTER. The tracker invokes this on EVERY captured\n   * error to resolve the current hook reference, then calls the\n   * resolved function with the error (returning `null` to drop, or a\n   * modified `CapturedError` to forward).\n   *\n   * It's a getter — not a static function — so `setErrorBeforeSend()`\n   * can install or replace the hook after init() without re-creating\n   * the tracker. Pre-fix this was a captured value: the tracker took\n   * a snapshot of `null` at construction time and never re-read state,\n   * so every customer's PII scrubber installed later was silently inert.\n   * Bank-grade rule: a hook the customer can call into MUST take effect\n   * the instant it's installed.\n   *\n   * Returning `null` from the GETTER means \"no hook configured\" and\n   * the report goes through unmodified — distinct from returning a\n   * function-that-returns-null (which means \"drop this specific report\").\n   */\n  beforeSend?: () => ((err: CapturedError) => CapturedError | null) | null;\n  /**\n   * Whether the consent dimension `errors` is currently granted.\n   * Checked at capture time so a flip via Crossdeck.consent() takes\n   * effect immediately.\n   */\n  isConsented: () => boolean;\n  /**\n   * The SDK's own backend hostname (derived from\n   * `CrossdeckOptions.baseUrl` at construction time). Used to skip\n   * captureHttp for our own requests — otherwise an outage on the\n   * Crossdeck backend would trigger captureHttp → enqueue →\n   * `POST /events` → fail again → captureHttp → ∞ until the queue\n   * gives up on a permanent 4xx (Batch B fix) or runs forever on a\n   * 5xx. Pre-fix the skip pattern was hardcoded to\n   * `api.cross-deck.com`, which failed customers using staging /\n   * regional / self-hosted-relay base URLs. Audit punch list P0 #7.\n   *\n   * Null / omitted when extraction from baseUrl fails (malformed URL)\n   * OR when the test harness doesn't supply one — the tracker falls\n   * through to \"capture everything\" rather than swallow.\n   */\n  selfHostname?: string | null;\n}\n\nexport class ErrorTracker {\n  private installed = false;\n  private cleanups: Array<() => void> = [];\n  private _reporting = false;\n  private sessionCount = 0;\n  private fingerprintWindow = new Map<string, number[]>();\n\n  constructor(private readonly opts: ErrorTrackerOptions) {}\n\n  install(): void {\n    if (this.installed) return;\n    if (!this.opts.config.enabled) return;\n    if (typeof globalThis === \"undefined\" || !(\"window\" in globalThis)) return;\n\n    const w = (globalThis as { window: Window }).window;\n\n    if (this.opts.config.onError) this.installOnErrorListener(w);\n    if (this.opts.config.onUnhandledRejection) this.installRejectionListener(w);\n    if (this.opts.config.wrapFetch) this.installFetchWrap(w);\n    if (this.opts.config.wrapXhr) this.installXhrWrap(w);\n    if (this.opts.config.captureConsole) this.installConsoleWrap();\n\n    this.installed = true;\n  }\n\n  uninstall(): void {\n    for (const fn of this.cleanups.splice(0)) {\n      try {\n        fn();\n      } catch {\n        // ignore\n      }\n    }\n    this.installed = false;\n  }\n\n  /**\n   * Manual API. Either an Error instance or any unknown value (we\n   * coerce). Returns silently — never throws.\n   */\n  captureError(\n    error: unknown,\n    options?: { context?: Record<string, unknown>; tags?: Record<string, string>; level?: ErrorLevel },\n  ): void {\n    if (!this.opts.isConsented()) return;\n    try {\n      const captured = this.buildFromUnknown(error, \"error.handled\", options?.level ?? \"error\");\n      if (options?.context) captured.context = { ...captured.context, ...options.context };\n      if (options?.tags) captured.tags = { ...captured.tags, ...options.tags };\n      this.maybeReport(captured);\n    } catch {\n      // self-protection — never let our own code crash the caller's\n      // error handler.\n    }\n  }\n\n  /**\n   * Capture a non-error event as an issue. For \"we hit a soft-warning\n   * code path\" / \"deprecated API used\" kinds of signals. Pairs with\n   * Sentry's captureMessage().\n   */\n  captureMessage(message: string, level: ErrorLevel = \"info\"): void {\n    if (!this.opts.isConsented()) return;\n    try {\n      const captured: CapturedError = {\n        timestamp: Date.now(),\n        kind: \"error.message\",\n        level,\n        message,\n        errorType: null,\n        frames: [],\n        rawStack: null,\n        filename: null,\n        lineno: null,\n        colno: null,\n        fingerprint: fingerprintError(message, []),\n        breadcrumbs: this.opts.breadcrumbs.snapshot(),\n        context: this.opts.getContext(),\n        tags: this.opts.getTags(),\n      };\n      this.maybeReport(captured);\n    } catch {\n      // swallow\n    }\n  }\n\n  // ============================================================\n  // Listener installation\n  // ============================================================\n\n  private installOnErrorListener(w: Window): void {\n    const handler = (event: ErrorEvent): void => {\n      if (this._reporting) return;\n      if (!this.opts.isConsented()) return;\n      try {\n        this._reporting = true;\n        const captured = this.buildFromErrorEvent(event);\n        this.maybeReport(captured);\n      } catch {\n        // swallow\n      } finally {\n        this._reporting = false;\n      }\n    };\n    w.addEventListener(\"error\", handler, true);\n    this.cleanups.push(() => w.removeEventListener(\"error\", handler, true));\n  }\n\n  private installRejectionListener(w: Window): void {\n    const handler = (event: PromiseRejectionEvent): void => {\n      if (this._reporting) return;\n      if (!this.opts.isConsented()) return;\n      try {\n        this._reporting = true;\n        const captured = this.buildFromUnknown(\n          event.reason,\n          \"error.unhandledrejection\",\n          \"error\",\n        );\n        this.maybeReport(captured);\n      } catch {\n        // swallow\n      } finally {\n        this._reporting = false;\n      }\n    };\n    w.addEventListener(\"unhandledrejection\", handler);\n    this.cleanups.push(() => w.removeEventListener(\"unhandledrejection\", handler));\n  }\n\n  /**\n   * Wrap fetch() so failed HTTP requests get auto-captured. We do NOT\n   * call this an \"error\" for 4xx (those are often expected — auth\n   * required, validation failed). Only 5xx + network failures fire.\n   */\n  private installFetchWrap(w: Window): void {\n    const origFetch = w.fetch?.bind(w);\n    if (!origFetch) return;\n    const wrapped = async (...args: Parameters<typeof fetch>): Promise<Response> => {\n      const input = args[0];\n      const init = args[1] ?? {};\n      const url = typeof input === \"string\" ? input : (input as Request)?.url ?? \"\";\n      const method = (init.method || \"GET\").toUpperCase();\n      const start = Date.now();\n\n      // Breadcrumb for the request itself — fires regardless of outcome.\n      // Skip self-requests: an error report's breadcrumb trail showing\n      // \"POST https://api.cross-deck.com/v1/events\" entries is noise the\n      // engineer reading the error doesn't care about (the SDK itself\n      // emitted them, not the user code under inspection). Same predicate\n      // as captureHttp's self-skip. Audit P2 polish.\n      if (!isSelfRequest(url, this.opts.selfHostname)) {\n        this.opts.breadcrumbs.add({\n          timestamp: start,\n          category: \"http\",\n          message: `${method} ${url}`,\n          data: { url, method },\n        });\n      }\n\n      try {\n        const response = await origFetch(...args);\n        if (response.status >= 500 && this.opts.isConsented()) {\n          // Self-skip Crossdeck's own API to avoid reporting our own\n          // backend errors back to ourselves (cycle hazard if the\n          // outage is on our side).\n          if (!isSelfRequest(url, this.opts.selfHostname)) {\n            this.captureHttp({\n              url,\n              method,\n              status: response.status,\n              statusText: response.statusText,\n            });\n          }\n        }\n        return response;\n      } catch (err) {\n        // A status-0 failure can be a genuine outage (DNS, connection refused)\n        // but is far more often client-environment noise — an adblocker\n        // blocking the request, the user offline, or an aborted navigation.\n        // Skip the unambiguous-noise cases (see isClientNetworkNoise) so we\n        // never raise a production error for them; cross-origin outages still\n        // report. This is what stopped first-party fetches blocked by a dev's\n        // own adblocker (e.g. contracts-registry.json) paging as HIGH PRIORITY.\n        if (\n          this.opts.isConsented() &&\n          !url.includes(\"api.cross-deck.com\") &&\n          !isClientNetworkNoise(url, err, w)\n        ) {\n          this.captureHttp({\n            url,\n            method,\n            status: 0,\n            statusText: err instanceof Error ? err.message : \"network error\",\n          });\n        }\n        throw err;\n      }\n    };\n    w.fetch = wrapped as typeof fetch;\n    this.cleanups.push(() => {\n      // Restore only if we're still the active wrapper.\n      if (w.fetch === wrapped) w.fetch = origFetch;\n    });\n  }\n\n  /**\n   * Wrap XMLHttpRequest for legacy consumers (jQuery $.ajax under the\n   * hood, older bundlers). Same capture semantics as fetch.\n   */\n  private installXhrWrap(w: Window): void {\n    const xhrCtor = (w as Window & { XMLHttpRequest?: typeof XMLHttpRequest }).XMLHttpRequest;\n    const proto = xhrCtor?.prototype;\n    if (!proto) return;\n    const origOpen = proto.open;\n    const origSend = proto.send;\n\n    const tracker = this;\n    proto.open = function (this: XMLHttpRequest, method: string, url: string, ...rest: unknown[]): void {\n      (this as XMLHttpRequest & { _cdMethod?: string; _cdUrl?: string })._cdMethod = method;\n      (this as XMLHttpRequest & { _cdMethod?: string; _cdUrl?: string })._cdUrl = url;\n      // Cast args to match the lib signature.\n      return origOpen.apply(this, [method, url, ...(rest as [boolean, string?, string?])]);\n    };\n    proto.send = function (this: XMLHttpRequest, body?: Document | XMLHttpRequestBodyInit | null): void {\n      const xhr = this as XMLHttpRequest & { _cdMethod?: string; _cdUrl?: string };\n      const onLoad = (): void => {\n        try {\n          if (xhr.status >= 500 && tracker.opts.isConsented()) {\n            const url = xhr._cdUrl ?? \"\";\n            if (!isSelfRequest(url, tracker.opts.selfHostname)) {\n              tracker.captureHttp({\n                url,\n                method: (xhr._cdMethod ?? \"GET\").toUpperCase(),\n                status: xhr.status,\n                statusText: xhr.statusText,\n              });\n            }\n          }\n        } catch {\n          // swallow\n        }\n      };\n      xhr.addEventListener(\"loadend\", onLoad);\n      return origSend.apply(this, [body ?? null]);\n    };\n\n    this.cleanups.push(() => {\n      proto.open = origOpen;\n      proto.send = origSend;\n    });\n  }\n\n  private installConsoleWrap(): void {\n    const console = (globalThis as { console?: Console }).console;\n    if (!console) return;\n    const orig = console.error.bind(console);\n    console.error = (...args: unknown[]) => {\n      try {\n        if (this.opts.isConsented()) {\n          this.captureMessage(args.map((a) => safeStringify(a)).join(\" \"), \"error\");\n        }\n      } catch {\n        // swallow\n      }\n      return orig(...args);\n    };\n    this.cleanups.push(() => {\n      console.error = orig;\n    });\n  }\n\n  // ============================================================\n  // Builders\n  // ============================================================\n\n  private buildFromErrorEvent(event: ErrorEvent): CapturedError {\n    const err = event.error;\n    const filename = event.filename || null;\n    const lineno = typeof event.lineno === \"number\" && event.lineno > 0 ? event.lineno : null;\n    const colno = typeof event.colno === \"number\" && event.colno > 0 ? event.colno : null;\n\n    // ── Cross-origin script error path ────────────────────────────────\n    // Signature: browser hands us \"Script error.\" (or empty), null\n    // error object, and no usable location. We can't recover the real\n    // message — but instead of stashing a useless \"Unknown error\",\n    // we label the event clearly and tag it so the dashboard groups\n    // it as a distinct, actionable category with a known remediation\n    // (add crossorigin=\"anonymous\" + CORS headers on the script's\n    // origin).\n    const isCrossOriginStripped =\n      err == null &&\n      !filename &&\n      lineno == null &&\n      (event.message === \"Script error.\" ||\n        event.message === \"Script error\" ||\n        !event.message);\n    if (isCrossOriginStripped) {\n      const message =\n        \"Cross-origin script error (browser hid details — script needs crossorigin attribute + CORS headers)\";\n      return {\n        timestamp: Date.now(),\n        kind: \"error.unhandled\",\n        level: \"error\",\n        message,\n        errorType: \"ScriptError\",\n        frames: [],\n        rawStack: null,\n        filename: null,\n        lineno: null,\n        colno: null,\n        // No location to fingerprint by — all of these will share one\n        // group, which is correct: developer fixes them all with the\n        // same CORS change.\n        fingerprint: fingerprintError(message, []),\n        breadcrumbs: this.opts.breadcrumbs.snapshot(),\n        context: this.opts.getContext(),\n        tags: { ...this.opts.getTags(), cross_origin: \"true\" },\n      };\n    }\n\n    // ── Normal path ───────────────────────────────────────────────────\n    // Pull every drop of signal out of the raw `event.error`, then fall\n    // back to `event.message` if the value didn't yield one of its own.\n    const payload = coerceErrorPayload(err);\n    const message = (\n      payload.message ||\n      event.message ||\n      \"Unknown error\"\n    ).slice(0, 1024);\n    const stack = err instanceof Error ? err.stack ?? null : null;\n    // Force in_app:false on frames when the message is a browser-injected\n    // global (window.__firefox__, __gCrWeb, …) — those throw in the page's\n    // scope so the frame wears the customer's URL, but the code is the\n    // browser's, not the app's. No-op for ordinary errors.\n    const frames = demoteVendorInjectedFrames(parseStack(stack), message);\n    const errorType = payload.errorType ?? null;\n\n    const context = payload.extras\n      ? { ...this.opts.getContext(), __error_extras: payload.extras }\n      : this.opts.getContext();\n\n    return {\n      timestamp: Date.now(),\n      kind: \"error.unhandled\",\n      level: \"error\",\n      message,\n      errorType,\n      frames,\n      rawStack: stack,\n      filename,\n      lineno,\n      colno,\n      // Location fallback ensures distinct call sites stay separate\n      // even when the message is generic (\"Unknown error\",\n      // \"[object Object]\") and there are no parseable frames.\n      fingerprint: fingerprintError(message, frames, {\n        filename,\n        lineno,\n        colno,\n        errorType,\n      }),\n      breadcrumbs: this.opts.breadcrumbs.snapshot(),\n      context,\n      tags: this.opts.getTags(),\n    };\n  }\n\n  private buildFromUnknown(\n    err: unknown,\n    kind: CapturedError[\"kind\"],\n    level: ErrorLevel,\n  ): CapturedError {\n    const payload = coerceErrorPayload(err);\n    const message = (payload.message || \"Unknown error\").slice(0, 1024);\n    const stack = err instanceof Error ? err.stack ?? null : null;\n    // Force in_app:false on frames when the message is a browser-injected\n    // global (window.__firefox__, __gCrWeb, …) — those throw in the page's\n    // scope so the frame wears the customer's URL, but the code is the\n    // browser's, not the app's. No-op for ordinary errors.\n    const frames = demoteVendorInjectedFrames(parseStack(stack), message);\n    const errorType = payload.errorType ?? null;\n\n    const context = payload.extras\n      ? { ...this.opts.getContext(), __error_extras: payload.extras }\n      : this.opts.getContext();\n\n    return {\n      timestamp: Date.now(),\n      kind,\n      level,\n      message,\n      errorType,\n      frames,\n      rawStack: stack,\n      filename: null,\n      lineno: null,\n      colno: null,\n      fingerprint: fingerprintError(message, frames, { errorType }),\n      breadcrumbs: this.opts.breadcrumbs.snapshot(),\n      context,\n      tags: this.opts.getTags(),\n    };\n  }\n\n  private captureHttp(info: {\n    url: string;\n    method: string;\n    status: number;\n    statusText?: string;\n  }): void {\n    try {\n      const message = `HTTP ${info.status} ${info.method} ${info.url}`;\n      const captured: CapturedError = {\n        timestamp: Date.now(),\n        kind: \"error.http\",\n        level: \"error\",\n        message,\n        errorType: `HTTPError`,\n        frames: [],\n        rawStack: null,\n        filename: info.url,\n        lineno: null,\n        colno: null,\n        fingerprint: fingerprintError(`HTTP ${info.status} ${info.method}`, [], {\n          filename: info.url,\n          errorType: \"HTTPError\",\n        }),\n        breadcrumbs: this.opts.breadcrumbs.snapshot(),\n        context: this.opts.getContext(),\n        tags: this.opts.getTags(),\n        http: info,\n      };\n      this.maybeReport(captured);\n    } catch {\n      // swallow\n    }\n  }\n\n  // ============================================================\n  // Reporting pipeline — filter / sample / rate-limit / send\n  // ============================================================\n\n  private maybeReport(err: CapturedError): void {\n    if (this.sessionCount >= this.opts.config.maxPerSession) return;\n    if (this.shouldIgnore(err)) return;\n    if (!this.passesUrlGate(err)) return;\n    if (!this.passesSample(err)) return;\n    if (!this.passesRateLimit(err)) return;\n\n    // beforeSend hook — last chance to scrub or drop. Resolve the\n    // current hook through the getter on every call so a hook installed\n    // via `setErrorBeforeSend()` AFTER init() takes effect on THIS\n    // error, not just future ones constructed by a future tracker.\n    let finalErr: CapturedError | null = err;\n    const hook = this.opts.beforeSend?.();\n    if (hook) {\n      try {\n        finalErr = hook(err);\n      } catch {\n        // A buggy beforeSend hook must NOT swallow the error report.\n        // Fall back to the original.\n        finalErr = err;\n      }\n      if (!finalErr) return;\n    }\n\n    this.sessionCount += 1;\n    try {\n      this.opts.report(finalErr);\n    } catch {\n      // swallow — report() failure is best-effort; the next error\n      // attempt will retry through the same queue.\n    }\n  }\n\n  private shouldIgnore(err: CapturedError): boolean {\n    for (const pat of this.opts.config.ignoreErrors) {\n      if (typeof pat === \"string\" && err.message.includes(pat)) return true;\n      if (pat instanceof RegExp && pat.test(err.message)) return true;\n    }\n    return false;\n  }\n\n  private passesUrlGate(err: CapturedError): boolean {\n    const topFrame = err.frames.find((f) => f.filename) ?? null;\n    const url = topFrame?.filename ?? err.filename ?? \"\";\n    if (!url) return true; // unknown URL — let it through\n\n    for (const pat of this.opts.config.denyUrls) {\n      if (typeof pat === \"string\" && url.includes(pat)) return false;\n      if (pat instanceof RegExp && pat.test(url)) return false;\n    }\n    if (this.opts.config.allowUrls.length > 0) {\n      for (const pat of this.opts.config.allowUrls) {\n        if (typeof pat === \"string\" && url.includes(pat)) return true;\n        if (pat instanceof RegExp && pat.test(url)) return true;\n      }\n      return false;\n    }\n    return true;\n  }\n\n  private passesSample(err: CapturedError): boolean {\n    if (this.opts.config.sampleRate >= 1) return true;\n    if (this.opts.config.sampleRate <= 0) return false;\n    // Deterministic per-fingerprint sampling — a given user always\n    // either always sends a given error or never does, no flapping.\n    const hashByte = parseInt(err.fingerprint.slice(0, 2), 16);\n    return (hashByte / 255) < this.opts.config.sampleRate;\n  }\n\n  private passesRateLimit(err: CapturedError): boolean {\n    const windowMs = 60_000;\n    const now = Date.now();\n    const max = this.opts.config.maxPerFingerprintPerMinute;\n    const arr = this.fingerprintWindow.get(err.fingerprint) ?? [];\n    // Drop entries older than the window.\n    const fresh = arr.filter((t) => now - t < windowMs);\n    if (fresh.length >= max) {\n      this.fingerprintWindow.set(err.fingerprint, fresh);\n      return false;\n    }\n    fresh.push(now);\n    this.fingerprintWindow.set(err.fingerprint, fresh);\n    return true;\n  }\n}\n\n/**\n * The thrown-value coercer.\n *\n * The browser's error pipelines (window.onerror, unhandledrejection,\n * developer `throw`) hand us values of every shape — Error instances,\n * DOMExceptions, plain objects, primitives, even null. Earlier\n * versions of this code wrote \"Unknown error\" whenever the value\n * wasn't an Error with a non-empty `.message`, which silently\n * collapsed entire classes of real bugs into one unhelpful bucket.\n *\n * This function extracts the maximum information available without\n * ever throwing (Symbol keys, recursive proxies, throwing toString —\n * all defended against). It returns three pieces:\n *\n *   - message:   the human-readable headline, never empty for any\n *                non-null/non-undefined input\n *   - errorType: the constructor name when we can discover one\n *                (Error subclass, DOMException, custom class) —\n *                feeds the dashboard's \"type · message\" header\n *   - extras:    additional fields worth keeping (Error.cause chain,\n *                .code/.status/.response on common patterns, any\n *                enumerable own properties on an Error subclass).\n *                Stashed on context.__error_extras for the\n *                dashboard's \"raw event\" panel.\n */\ninterface CoercedPayload {\n  message: string;\n  errorType: string | null;\n  extras: Record<string, unknown> | null;\n}\n\nfunction coerceErrorPayload(v: unknown): CoercedPayload {\n  if (v === null) return { message: \"(thrown: null)\", errorType: null, extras: null };\n  if (v === undefined) return { message: \"(thrown: undefined)\", errorType: null, extras: null };\n\n  if (typeof v === \"string\") {\n    return { message: v, errorType: null, extras: null };\n  }\n  if (typeof v === \"number\" || typeof v === \"boolean\" || typeof v === \"bigint\") {\n    return { message: String(v), errorType: typeof v, extras: null };\n  }\n  if (typeof v === \"symbol\") {\n    return { message: v.toString(), errorType: \"symbol\", extras: null };\n  }\n  if (typeof v === \"function\") {\n    return { message: `(thrown function: ${v.name || \"anonymous\"})`, errorType: \"function\", extras: null };\n  }\n\n  // Object-ish from here on — Error, DOMException, Response, plain\n  // objects, custom classes, etc.\n  if (v instanceof Error) {\n    const errorType =\n      v.name || v.constructor?.name || \"Error\";\n    const message =\n      typeof v.message === \"string\" && v.message.length > 0\n        ? v.message\n        : safeToString(v) || errorType;\n\n    const extras: Record<string, unknown> = {};\n\n    // ES2022 Error.cause — walk up to 5 levels so a service-layer\n    // wrapper error doesn't hide the underlying network failure.\n    const causeChain = collectCauseChain(v);\n    if (causeChain.length > 0) extras.cause = causeChain;\n\n    // Common HTTP/RPC patterns attach status/code/response to thrown\n    // values. Capture them without forcing every wrapper class to\n    // override toString.\n    for (const key of [\"code\", \"status\", \"statusCode\", \"errno\", \"response\", \"data\", \"detail\", \"details\"] as const) {\n      const val = (v as unknown as Record<string, unknown>)[key];\n      if (val !== undefined && typeof val !== \"function\") {\n        extras[key] = safeClone(val);\n      }\n    }\n\n    // Any other enumerable own properties (custom Error subclasses\n    // that add fields).\n    for (const key of Object.keys(v)) {\n      if (key === \"message\" || key === \"stack\" || key === \"name\" || key === \"cause\") continue;\n      if (key in extras) continue;\n      const val = (v as unknown as Record<string, unknown>)[key];\n      if (typeof val === \"function\") continue;\n      extras[key] = safeClone(val);\n    }\n\n    return {\n      message,\n      errorType,\n      extras: Object.keys(extras).length > 0 ? extras : null,\n    };\n  }\n\n  // Response — fetch().then(r => { if (!r.ok) throw r }) is a common\n  // pattern, and the bare Response is otherwise unreadable.\n  if (typeof Response !== \"undefined\" && v instanceof Response) {\n    return {\n      message: `HTTP ${v.status} ${v.statusText || \"\"}${v.url ? ` ${v.url}` : \"\"}`.trim(),\n      errorType: \"Response\",\n      extras: { status: v.status, statusText: v.statusText, url: v.url, type: v.type },\n    };\n  }\n\n  // DOMException, Event, anything with a useful native toString —\n  // capture the constructor name as the type, then prefer the\n  // object's .message field if it has one (DOMException does;\n  // ErrorEvent does; many polyfills do).\n  if (typeof v === \"object\") {\n    const obj = v as Record<string, unknown>;\n    const ctorName =\n      (obj.constructor && typeof obj.constructor === \"function\" && (obj.constructor as { name?: string }).name) ||\n      null;\n\n    const ownMessage = typeof obj.message === \"string\" && obj.message ? obj.message : null;\n    const ownName = typeof obj.name === \"string\" && obj.name ? obj.name : null;\n\n    let jsonForm: string | null = null;\n    try {\n      const serialised = JSON.stringify(obj);\n      // JSON.stringify of an empty object is \"{}\", which is useless\n      // as a message but tells us we have a thrown object with no\n      // enumerable own props.\n      jsonForm = serialised === \"{}\" ? null : serialised;\n    } catch {\n      jsonForm = null;\n    }\n\n    const fallbackString = safeToString(obj);\n    const message =\n      ownMessage ??\n      jsonForm ??\n      (fallbackString && fallbackString !== \"[object Object]\" ? fallbackString : null) ??\n      (ctorName ? `(thrown ${ctorName} with no message)` : \"(thrown object with no message)\");\n\n    const errorType = ownName ?? ctorName ?? null;\n\n    // Best-effort extras for objects: keep up to ~20 enumerable own\n    // properties, JSON-safe.\n    const extras: Record<string, unknown> = {};\n    let count = 0;\n    for (const key of Object.keys(obj)) {\n      if (count >= 20) break;\n      if (key === \"message\" || key === \"name\") continue;\n      const val = obj[key];\n      if (typeof val === \"function\") continue;\n      extras[key] = safeClone(val);\n      count++;\n    }\n\n    return {\n      message,\n      errorType,\n      extras: Object.keys(extras).length > 0 ? extras : null,\n    };\n  }\n\n  // Should be unreachable, but: fall back to coerced string.\n  return { message: safeToString(v) || \"(unstringifiable thrown value)\", errorType: null, extras: null };\n}\n\nfunction collectCauseChain(err: Error): Array<{ name: string; message: string }> {\n  const out: Array<{ name: string; message: string }> = [];\n  let cur: unknown = (err as Error & { cause?: unknown }).cause;\n  let depth = 0;\n  while (cur != null && depth < 5) {\n    if (cur instanceof Error) {\n      out.push({ name: cur.name || \"Error\", message: cur.message || \"\" });\n      cur = (cur as Error & { cause?: unknown }).cause;\n    } else {\n      out.push({ name: \"non-Error\", message: safeToString(cur) });\n      cur = null;\n    }\n    depth++;\n  }\n  return out;\n}\n\nfunction safeToString(v: unknown): string {\n  try {\n    const s = Object.prototype.toString.call(v);\n    if (s !== \"[object Object]\") return s;\n    // Try the value's own toString if it overrides Object's default.\n    const own = (v as { toString?: () => unknown })?.toString;\n    if (typeof own === \"function\" && own !== Object.prototype.toString) {\n      const r = own.call(v);\n      if (typeof r === \"string\") return r;\n    }\n    return s;\n  } catch {\n    return \"(throwing toString)\";\n  }\n}\n\nfunction safeClone(v: unknown): unknown {\n  if (v == null) return v;\n  const t = typeof v;\n  if (t === \"string\" || t === \"number\" || t === \"boolean\") return v;\n  if (t === \"bigint\") return String(v);\n  try {\n    // JSON.stringify will throw on circular structures; that's fine,\n    // we fall back to the toString below.\n    const s = JSON.stringify(v);\n    return s === undefined ? safeToString(v) : JSON.parse(s);\n  } catch {\n    return safeToString(v);\n  }\n}\n\nfunction safeStringify(v: unknown): string {\n  return coerceErrorPayload(v).message;\n}\n\n/**\n * Extract the hostname from a URL string for use as the\n * `selfHostname` field on the ErrorTracker. Returns null on malformed\n * input — the tracker's downstream self-skip check treats `null` as\n * \"no self to skip\" and captures everything (safer than swallowing\n * legitimate errors on a config typo).\n *\n * Lowercased for case-insensitive comparison (`Api.Cross-Deck.com`\n * and `api.cross-deck.com` are the same host).\n */\nexport function extractSelfHostname(baseUrl: string | undefined | null): string | null {\n  if (!baseUrl || typeof baseUrl !== \"string\") return null;\n  try {\n    return new URL(baseUrl).hostname.toLowerCase();\n  } catch {\n    return null;\n  }\n}\n\n/**\n * True when the request URL targets the SDK's own backend hostname.\n * Used by the fetch / XHR wrappers to skip captureHttp on Crossdeck's\n * own requests — otherwise a Crossdeck-side outage would recurse\n * (captureHttp → enqueue → /events → fail → captureHttp → …).\n *\n * Strict hostname compare (not substring) so a path like\n * `https://api.cross-deck.com.attacker.example/...` doesn't falsely\n * match `api.cross-deck.com`. Falls back to `false` on malformed URLs\n * — the SDK only ever uses absolute URLs, so a relative URL can't\n * be the SDK's own request.\n */\nexport function isSelfRequest(requestUrl: string, selfHostname: string | null | undefined): boolean {\n  if (!selfHostname || !requestUrl) return false;\n  try {\n    return new URL(requestUrl).hostname.toLowerCase() === selfHostname;\n  } catch {\n    return false;\n  }\n}\n\n/**\n * A failed fetch (status 0 — an opaque TypeError with no HTTP response) is,\n * far more often than a real outage, client-environment noise: an adblocker or\n * privacy extension blocking the request, the user offline, or a request\n * aborted by a navigation. The Fetch spec deliberately makes these\n * indistinguishable from a genuine network fault, so a status-0 capture cannot\n * tell an outage from a blocked request — which is why \"Failed to fetch\" is the\n * single biggest false-alarm source in browser error tracking. This predicate\n * identifies the unambiguous-noise cases we refuse to raise a production error\n * for:\n *   • AbortError — request cancelled by a navigation or explicit abort;\n *   • offline — navigator.onLine === false, there is no network at all;\n *   • SAME-ORIGIN — the page itself loaded from this origin, so the origin is\n *     demonstrably reachable; a status-0 to it is client-side blocking (e.g. an\n *     adblocker on a first-party asset), not a server fault. Genuine\n *     same-origin server failures return an HTTP status (5xx) and are captured\n *     on the success path, not here.\n * Cross-origin status-0 (a third-party API genuinely unreachable) still\n * reports — that keeps the real signal while dropping the noise.\n */\nexport function isClientNetworkNoise(requestUrl: string, err: unknown, w: Window): boolean {\n  if (err instanceof Error && err.name === \"AbortError\") return true;\n  const nav = (w as Window & { navigator?: { onLine?: boolean } }).navigator;\n  if (nav && nav.onLine === false) return true;\n  const pageHref = w.location?.href;\n  const pageOrigin = w.location?.origin;\n  if (!pageOrigin) return false;\n  try {\n    return new URL(requestUrl, pageHref ?? pageOrigin).origin === pageOrigin;\n  } catch {\n    return false;\n  }\n}\n","/**\n * Public API surface for @cross-deck/web.\n *\n * Usage (browser):\n *\n *   import { Crossdeck } from \"@cross-deck/web\";\n *\n *   Crossdeck.init({\n *     appId: \"app_web_xxx\",\n *     publicKey: \"cd_pub_live_…\",\n *     environment: \"production\",\n *   });\n *\n *   await Crossdeck.identify(\"user_847\");\n *   const ents = await Crossdeck.getEntitlements();\n *   if (Crossdeck.isEntitled(\"pro\")) {\n *     showPro();\n *   }\n *   Crossdeck.track(\"paywall_shown\", { variant: \"v3\" });\n *\n *\n * Usage (Node):\n *\n *   import { Crossdeck, MemoryStorage } from \"@cross-deck/web\";\n *\n *   Crossdeck.init({\n *     appId: \"app_node_xxx\",\n *     publicKey: \"cd_pub_test_…\",\n *     environment: \"sandbox\",\n *     storage: new MemoryStorage(),  // session-only persistence\n *     autoHeartbeat: false,           // skip the boot ping in scripts\n *   });\n */\n\nimport { CrossdeckError } from \"./errors\";\nimport { HttpClient, SDK_NAME, SDK_VERSION, DEFAULT_BASE_URL } from \"./http\";\nimport { IdentityStore } from \"./identity\";\nimport { mountTrustPanel } from \"./trust\";\nimport type { TrustPanelHandle, TrustPanelInput, CrossdeckTrustNamespace } from \"./trust\";\nimport { EntitlementCache, type EntitlementsListener } from \"./entitlement-cache\";\nimport { deriveIdempotencyKeyForPurchase } from \"./idempotency-key\";\nimport { EventQueue, type QueuedEvent } from \"./event-queue\";\nimport { PersistentEventStore } from \"./event-storage\";\nimport { CookieStorage, detectDefaultStorage, MemoryStorage, resolveCookieDomain } from \"./storage\";\nimport { randomChars } from \"./identity\";\nimport { bridgeReadCost } from \"./read-cost-bridge\";\nimport { collectDeviceInfo, type DeviceInfo } from \"./device-info\";\nimport { AutoTracker, DEFAULT_AUTO_TRACK, type AutoTrackConfig } from \"./auto-track\";\nimport { ConsoleDebugLogger, findSensitivePropertyKeys, type DebugLogger } from \"./debug\";\nimport { validateEventProperties } from \"./event-validation\";\nimport { SuperPropertyStore } from \"./super-properties\";\nimport {\n  INTERNAL_OPT_OUT_PROPERTY,\n  isInternalOptOut,\n  processInternalOptOutUrl,\n} from \"./internal-opt-out\";\nimport { WebVitalsTracker } from \"./web-vitals\";\nimport { ConsentManager, scrubPii, scrubPiiFromProperties, type ConsentState } from \"./consent\";\nimport { BreadcrumbBuffer, type Breadcrumb } from \"./breadcrumbs\";\nimport type { ContractFailureInput } from \"./contracts\";\nimport { sendDiagnosticTelemetry } from \"./_diagnostic-telemetry\";\nimport {\n  STATIC_VERIFIERS,\n  VerifierReporter,\n  buildVerifierContext,\n  buildFlushIntervalVerifier,\n  defaultDebugModeFlag,\n  runBootSelfTest,\n  runOnErrorParse,\n  runOnIdentify,\n  runOnSyncPurchases,\n  runOnTrack,\n  type ContractVerifier,\n  type VerifierContext,\n} from \"./_contract-verifiers\";\nimport {\n  DEFAULT_ERROR_CAPTURE,\n  ErrorTracker,\n  extractSelfHostname,\n  type CapturedError,\n  type ErrorCaptureConfig,\n  type ErrorLevel,\n} from \"./error-capture\";\nimport type {\n  AliasResult,\n  AutoTrackOptions,\n  CrossdeckOptions,\n  Diagnostics,\n  EntitlementsListResponse,\n  Environment,\n  EventProperties,\n  GroupTraits,\n  HeartbeatResponse,\n  IdentifyOptions,\n  PublicEntitlement,\n  PurchaseResult,\n  ConsentBannerInit,\n} from \"./types\";\n\ninterface InternalState {\n  http: HttpClient;\n  identity: IdentityStore;\n  entitlements: EntitlementCache;\n  events: EventQueue;\n  autoTracker: AutoTracker | null;\n  webVitals: WebVitalsTracker | null;\n  errors: ErrorTracker | null;\n  breadcrumbs: BreadcrumbBuffer;\n  errorContext: Record<string, unknown>;\n  errorTags: Record<string, string>;\n  errorBeforeSend: ((err: CapturedError) => CapturedError | null) | null;\n  superProps: SuperPropertyStore;\n  consent: ConsentManager;\n  scrubPii: boolean;\n  /** Cached enrichment payload merged into every event's properties. */\n  deviceInfo: DeviceInfo;\n  options: Required<\n    Omit<\n      CrossdeckOptions,\n      | \"storage\"\n      | \"sdkVersion\"\n      | \"autoTrack\"\n      | \"appVersion\"\n      | \"debug\"\n      | \"respectDnt\"\n      | \"scrubPii\"\n      // The three contract-verifier flags are resolved + consumed\n      // outside state.options (see CrossdeckClient.verifierCtx /\n      // verifierReporter / verifiers fields). They don't need to\n      // round-trip through the InternalState options struct.\n      | \"verifyContractsAtBoot\"\n      | \"logVerifierResults\"\n      | \"disableContractAssertions\"\n      // Consumed at construction (scopes the redundancy cookie); not stored in\n      // the resolved options struct.\n      | \"cookieDomain\"\n      | \"consentBanner\"\n    >\n  > & {\n    sdkVersion: string;\n    autoTrack: AutoTrackConfig;\n    appVersion: string | null;\n  };\n  debug: DebugLogger;\n  developerUserId: string | null;\n  /** Cleanup the unload-flush listeners installed in init(). */\n  uninstallUnloadFlush: (() => void) | null;\n  /** Most-recent server time observed via heartbeat (epoch ms). */\n  lastServerTime: number | null;\n  /** Local Date.now() captured at the same moment as lastServerTime. */\n  lastClientTime: number | null;\n}\n\nexport class CrossdeckClient {\n  private state: InternalState | null = null;\n\n  // ────────────────────────────────────────────────────────────────\n  // Contract verifier layer (see sdks/web/src/_contract-verifiers.ts).\n  //\n  // Three flags govern this layer (CrossdeckOptions):\n  //   verifyContractsAtBoot     — boot self-test on Crossdeck.start\n  //   logVerifierResults        — print PASS results to console\n  //   disableContractAssertions — total kill-switch\n  //\n  // When the kill-switch is set, all three fields stay null and\n  // every hot-path dispatcher short-circuits — zero runtime cost.\n  // Otherwise: populated once at init(), referenced from every\n  // hot-path call site (identify, syncPurchases, ...).\n  // ────────────────────────────────────────────────────────────────\n  private verifiers: readonly ContractVerifier[] | null = null;\n  private verifierReporter: VerifierReporter | null = null;\n  private verifierCtx: VerifierContext | null = null;\n\n  /**\n   * Boot the SDK. Idempotent — calling init twice with the same options\n   * is a no-op; calling with different options replaces the previous\n   * configuration.\n   *\n   * NorthStar §11.1: signature is `Crossdeck.init({ appId, publicKey,\n   * environment })`. The trio is validated up-front so a typo'd key or a\n   * mismatched env fails fast at boot rather than at first event-flush.\n   */\n  init(options: CrossdeckOptions): void {\n    // Idempotent re-init: tear down listeners from any prior init()\n    // before constructing the new state. Pre-fix\n    // `state.uninstallUnloadFlush` was set but never invoked anywhere,\n    // so calling init() a second time (config swap during dev,\n    // multi-tenant SDK shell, hot-module-replacement) silently\n    // accumulated duplicate `pagehide` / `beforeunload` /\n    // `visibilitychange` listeners. Each one fired a redundant flush.\n    // Audit P2: now teardown runs on every re-init.\n    if (this.state) {\n      try { this.state.uninstallUnloadFlush?.(); } catch { /* ignore */ }\n      try { this.state.autoTracker?.uninstall(); } catch { /* ignore */ }\n      try { this.state.webVitals?.uninstall(); } catch { /* ignore */ }\n      try { this.state.errors?.uninstall(); } catch { /* ignore */ }\n      // v1.4.0 Phase 5.5 — drain the prior EventQueue's pending\n      // setTimeout BEFORE we replace this.state. Pre-fix the timer\n      // would fire AFTER the state swap, firing against new\n      // http/identity references with old-init events — a\n      // cross-identity leak risk during HMR / config swap /\n      // multi-tenant SDK shell. flush({keepalive:true}) cancels\n      // the timer (see EventQueue.cancelTimerIfSet) and ships\n      // queued events out under the prior init's identity.\n      //\n      // CRITICAL: do NOT clear the persistent event store here.\n      // The durable queue belongs to the SDK lifetime, not the\n      // init() lifetime — a survived crash mid-flush re-hydrates\n      // on the next init.\n      try { void this.state.events.flush({ keepalive: true }); } catch { /* ignore */ }\n    }\n    if (!options.publicKey || !options.publicKey.startsWith(\"cd_pub_\")) {\n      throw new CrossdeckError({\n        type: \"configuration_error\",\n        code: \"invalid_public_key\",\n        message: \"Crossdeck.init requires a publishable key starting with cd_pub_.\",\n      });\n    }\n    if (!options.appId) {\n      throw new CrossdeckError({\n        type: \"configuration_error\",\n        code: \"missing_app_id\",\n        message: \"Crossdeck.init requires an appId. Find yours in the Crossdeck dashboard.\",\n      });\n    }\n    if (options.environment !== \"production\" && options.environment !== \"sandbox\") {\n      throw new CrossdeckError({\n        type: \"configuration_error\",\n        code: \"invalid_environment\",\n        message: 'Crossdeck.init requires environment: \"production\" | \"sandbox\".',\n      });\n    }\n    // Key prefix must match the declared environment, otherwise prod\n    // telemetry could silently route into sandbox dashboards (or vice\n    // versa). NorthStar §15 calls this out as a \"fail loudly\" condition.\n    const keyEnv = inferEnvFromKey(options.publicKey);\n    if (keyEnv && keyEnv !== options.environment) {\n      throw new CrossdeckError({\n        type: \"configuration_error\",\n        code: \"environment_mismatch\",\n        message: `Crossdeck.init: environment \"${options.environment}\" disagrees with key prefix (${keyEnv}). Reconcile the publishable key with the environment declaration.`,\n      });\n    }\n\n    // Localhost auto-detection. When the SDK boots from localhost /\n    // 127.0.0.1 / *.local / RFC1918 private IPs, automatically switch\n    // to a fully-local \"dev mode\" — no network calls fire, all SDK\n    // methods (track, identify, isEntitled) work against in-memory +\n    // localStorage state only. The dev's live dashboard stays clean\n    // even if they forgot to swap their cd_pub_live_* key for a\n    // cd_pub_test_* one.\n    //\n    // Stripe-grade default. Confidence-first means we trust the dev's\n    // key prefix in production; localhost is the one place where we\n    // proactively prevent accidental pollution.\n    const localDevMode = isLocalHostname();\n\n    const storage = options.storage ?? detectDefaultStorage();\n    const persistIdentity = options.persistIdentity ?? true;\n    const autoTrack = resolveAutoTrack(options.autoTrack);\n    const opts: InternalState[\"options\"] = {\n      appId: options.appId,\n      publicKey: options.publicKey,\n      environment: options.environment,\n      baseUrl: options.baseUrl ?? DEFAULT_BASE_URL,\n      persistIdentity,\n      storagePrefix: options.storagePrefix ?? \"crossdeck:\",\n      autoHeartbeat: options.autoHeartbeat ?? true,\n      eventFlushBatchSize: options.eventFlushBatchSize ?? 20,\n      // 1500ms idle window. Short enough that an event queued on page\n      // load still flushes if the user leaves quickly (the keepalive\n      // pagehide handler picks up anything that doesn't); long enough\n      // that bursts of clicks coalesce into one network round-trip.\n      // v1.4.0 Phase 3.3 — flush interval default parity. Pre-\n      // v1.4.0: Web/Node 1500ms, RN/Swift/Android 5000ms. All\n      // converged on 2000ms (the Stripe-adjacent industry norm)\n      // so cross-platform funnels show events landing at the\n      // same cadence on every SDK. Per-instance override stays.\n      eventFlushIntervalMs: options.eventFlushIntervalMs ?? 2000,\n      sdkVersion: options.sdkVersion ?? SDK_VERSION,\n      autoTrack,\n      appVersion: options.appVersion ?? null,\n    };\n\n    const debug = new ConsoleDebugLogger();\n    debug.enabled = options.debug === true;\n\n    const http = new HttpClient({\n      publicKey: opts.publicKey,\n      baseUrl: opts.baseUrl,\n      sdkVersion: opts.sdkVersion,\n      // Localhost auto-route: HttpClient short-circuits every request\n      // to a successful no-op response when localDevMode is set.\n      // SDK methods continue to work locally; nothing reaches the\n      // server.\n      localDevMode,\n      // Contract verifier hot-path hook — fires the `error-envelope-shape`\n      // verifier on every parsed wire error, BEFORE the throw bubbles\n      // back to user code. Centralised here so we don't need a try/catch\n      // around every `await s.http.request(...)` call site downstream.\n      // No-op when the verifier layer is disabled (verifiers === null).\n      onErrorParsed: (err) => {\n        if (this.verifiers && this.verifierReporter && this.verifierCtx) {\n          runOnErrorParse(this.verifiers, this.verifierReporter, this.verifierCtx, {\n            errorType: err.type,\n            errorCode: err.code,\n            requestId: err.requestId ?? null,\n            httpStatus: typeof err.status === \"number\" ? err.status : 0,\n          });\n        }\n      },\n    });\n\n    if (localDevMode) {\n      // Single console line on first init — direct, not scolding.\n      // Tells the dev exactly what's happening and how to change it.\n      console.log(\n        \"[crossdeck] Localhost detected — running in dev mode (no network calls). \" +\n        \"Set publicKey: 'cd_pub_test_…' and deploy to a real domain to test against the Crossdeck Sandbox.\",\n      );\n    }\n    // Bank-grade identity continuity (v0.6.0+). When persistIdentity is\n    // on AND we're in a browser, the SDK writes the anonymousId to BOTH\n    // localStorage (primary) and a 1st-party cookie (secondary). When\n    // persistIdentity is off — typical during a strict-consent flow\n    // before opt-in — we fall back to in-memory only and write nothing\n    // to either store.\n    //\n    // The cookie is only constructed when the caller didn't override\n    // `storage`; if a custom storage adapter was supplied, that wins\n    // and the cookie redundancy is the caller's responsibility (they\n    // chose a non-default store for a reason).\n    const effectiveStorage = persistIdentity ? storage : new MemoryStorage();\n    const useCookieRedundancy =\n      persistIdentity &&\n      !options.storage &&  // honour caller's adapter choice\n      typeof (globalThis as { document?: unknown }).document !== \"undefined\";\n    // Cross-subdomain identity (default \"auto\"): scope the redundancy cookie to\n    // the registrable domain so marketing↔app is ONE anonymous person. See\n    // resolveCookieDomain + CrossdeckOptions.cookieDomain.\n    const cookieStore = useCookieRedundancy\n      ? new CookieStorage({ domain: resolveCookieDomain(options.cookieDomain ?? \"auto\") })\n      : undefined;\n    const identity = new IdentityStore(effectiveStorage, opts.storagePrefix, cookieStore);\n    // Durable last-known-good entitlement cache — persisted through the\n    // same storage as identity so isEntitled() answers from device cache\n    // on boot and rides out a Crossdeck outage instead of failing every\n    // Pro customer to free. In-memory only when persistIdentity is off.\n    const entitlements = new EntitlementCache(\n      effectiveStorage,\n      opts.storagePrefix + \"entitlements\",\n    );\n    // Durable persistence — write queued events through to the primary\n    // identity store (typically localStorage) so a crash / hard close /\n    // keepalive cap exceedance doesn't lose data. Skipped when\n    // persistIdentity is off (strict consent / in-memory-only mode) —\n    // no point writing events to a store the developer told us not to\n    // use.\n    const persistentEvents = persistIdentity\n      ? new PersistentEventStore({ storage: effectiveStorage, prefix: opts.storagePrefix })\n      : null;\n    if (persistentEvents) {\n      debug.emit(\n        \"sdk.queue_restored\",\n        \"Restored persisted event queue from a prior session.\",\n      );\n    }\n    const events = new EventQueue({\n      http,\n      batchSize: opts.eventFlushBatchSize,\n      intervalMs: opts.eventFlushIntervalMs,\n      envelope: () => ({\n        appId: opts.appId,\n        environment: opts.environment,\n        sdk: { name: SDK_NAME, version: opts.sdkVersion },\n      }),\n      persistentStore: persistentEvents ?? undefined,\n      onFirstFlushSuccess: () => {\n        debug.emit(\n          \"sdk.first_event_sent\",\n          \"First telemetry event received. View it in Live Events.\",\n          { appId: opts.appId, environment: opts.environment },\n        );\n      },\n      onRetryScheduled: (info) => {\n        debug.emit(\n          \"sdk.flush_retry_scheduled\",\n          `Event flush failed (${info.lastError}). Retrying in ${info.delayMs}ms (attempt ${info.consecutiveFailures}).`,\n          { ...info },\n        );\n      },\n      onPermanentFailure: (info) => {\n        // Bank-grade rule: a permanent 4xx that's dropping events MUST\n        // be loud regardless of debug mode. Pre-fix the queue retried\n        // 4xx forever silently and the customer never knew their key\n        // was revoked. console.error fires unconditionally; the debug\n        // signal lets the dashboard onboarding flow detect + surface\n        // the problem too.\n        const headline = `[crossdeck] Event batch DROPPED (status ${info.status}): ${info.lastError}. ${info.droppedCount} event(s) lost — check your publishable key + app config.`;\n        // eslint-disable-next-line no-console\n        console.error(headline);\n        debug.emit(\n          \"sdk.flush_permanent_failure\",\n          headline,\n          { ...info },\n        );\n      },\n      onParked: (info) => {\n        // Second signal channel (the queue already logged ONE console line).\n        // PARK is NOT a drop: events are held on-device and resume on upgrade.\n        // The dashboard reads this to render the calm amber \"update to resume\"\n        // advisory. Distinct signal from flush_permanent_failure.\n        debug.emit(\n          \"sdk.parked\",\n          `[crossdeck] SDK parked — server no longer accepts this version's event format. Events held on-device (paused, not lost); update @cross-deck/web${info.minVersion ? ` to >= ${info.minVersion}` : \"\"} to resume.`,\n          { ...info },\n        );\n      },\n    });\n\n    // Collect device info ONCE at boot; cheap to re-use on every event.\n    const deviceInfo: DeviceInfo = autoTrack.deviceInfo\n      ? collectDeviceInfo({ appVersion: opts.appVersion ?? undefined })\n      : opts.appVersion\n        ? { appVersion: opts.appVersion }\n        : {};\n\n    // Super-property + groups store — Mixpanel pattern. Lives on the\n    // primary identity storage so it survives page reloads but is\n    // cleared on reset() / forget(). Skipped when persistIdentity is\n    // off (strict consent — no writes anywhere).\n    const superProps = new SuperPropertyStore(\n      persistIdentity ? effectiveStorage : new MemoryStorage(),\n      opts.storagePrefix,\n    );\n\n    // Internal-traffic opt-out: a ?crossdeck_internal=1/0 in the URL sets/\n    // clears the persisted localStorage flag now; each event then reads it\n    // (isInternalOptOut) to tag itself internal. Self-contained + guarded.\n    processInternalOptOutUrl();\n\n    // Consent gating. DNT auto-detection runs once here if respectDnt\n    // is enabled; otherwise the developer is responsible for calling\n    // Crossdeck.consent({...}) before user-meaningful events fire.\n    const consent = new ConsentManager({ respectDnt: options.respectDnt === true });\n    if (consent.isDntDenied) {\n      debug.emit(\n        \"sdk.consent_dnt_applied\",\n        \"Do Not Track detected — all tracking dimensions denied at init.\",\n      );\n    }\n\n    // Breadcrumb ring buffer — the \"what was the user doing right\n    // before things broke\" feature. Populated by auto-tracking\n    // sources (page views, clicks, custom events) and by manual\n    // Crossdeck.addBreadcrumb() calls. Attached to every error\n    // report; cleared on reset() / forget().\n    const breadcrumbs = new BreadcrumbBuffer(50);\n\n    this.state = {\n      http,\n      identity,\n      entitlements,\n      events,\n      autoTracker: null,\n      webVitals: null,\n      errors: null,\n      breadcrumbs,\n      errorContext: {},\n      errorTags: {},\n      errorBeforeSend: null,\n      superProps,\n      consent,\n      scrubPii: options.scrubPii !== false,\n      deviceInfo,\n      options: opts,\n      debug,\n      developerUserId: null,\n      uninstallUnloadFlush: null,\n      lastServerTime: null,\n      lastClientTime: null,\n    };\n\n    debug.emit(\"sdk.configured\", `Crossdeck connected to ${opts.appId} in ${opts.environment} mode.`, {\n      appId: opts.appId,\n      environment: opts.environment,\n      sdkVersion: opts.sdkVersion,\n    });\n\n    // Auto-tracker boots AFTER state is set so its initial track() calls\n    // can resolve identity hints and device-info enrichment correctly.\n    if (autoTrack.sessions || autoTrack.pageViews) {\n      const tracker = new AutoTracker(\n        autoTrack,\n        (name, properties) => this.track(name, properties),\n        // Persist session continuity on the SAME adapter as identity, so\n        // a visit survives full-page navigations (multi-page sites\n        // re-install the SDK on every page) and honours the same consent\n        // posture — MemoryStorage when identity persistence is off.\n        // cookieStorage is the SAME registrable-domain cookie identity uses.\n        // First-touch origin rides it so the campaign that won a visitor on the\n        // marketing site is still known when they land on the app subdomain —\n        // localStorage alone is per-origin and would lose it at exactly that hop.\n        {\n          storage: effectiveStorage,\n          storageKey: opts.storagePrefix + \"session\",\n          cookieStorage: cookieStore ?? undefined,\n        },\n      );\n      this.state.autoTracker = tracker;\n      tracker.install();\n    }\n    // Web Vitals tracker — emits LCP / INP / CLS / FCP / TTFB as named\n    // events. No-op in non-browser environments or when the\n    // PerformanceObserver primitive is missing.\n    if (autoTrack.webVitals) {\n      const vitals = new WebVitalsTracker(\n        { enabled: true },\n        (name, properties) => this.track(name, properties),\n      );\n      this.state.webVitals = vitals;\n      vitals.install();\n    }\n\n    // ----- Error capture (the third pillar) -----\n    // Installs global window.onerror + window.onunhandledrejection\n    // handlers, wraps fetch + XHR, and reports each captured error\n    // through the same event queue analytics uses. Crucially this\n    // runs AFTER the queue, identity, and breadcrumb buffer are set\n    // up — error events need all of them.\n    if (autoTrack.errors) {\n      const tracker = new ErrorTracker({\n        config: { ...DEFAULT_ERROR_CAPTURE, enabled: true },\n        breadcrumbs,\n        report: (err) => this.reportError(err),\n        getContext: () => ({ ...this.state!.errorContext }),\n        getTags: () => ({ ...this.state!.errorTags }),\n        // GETTER, not a captured value — `setErrorBeforeSend()` mutates\n        // `state.errorBeforeSend` after init() and the tracker MUST\n        // pick up the new hook on the next error. The pre-fix shape\n        // (`beforeSend: this.state!.errorBeforeSend`) snapshotted\n        // `null` at construction and made the customer's PII scrubber\n        // silently inert. See error-capture.ts:ErrorTrackerOptions.beforeSend.\n        beforeSend: () => this.state!.errorBeforeSend,\n        isConsented: () => this.state!.consent.errors,\n        // Derived from the configured baseUrl at init() time. Used by\n        // the fetch / XHR wrappers to skip captureHttp on Crossdeck's\n        // own requests — pre-fix the skip was hardcoded to\n        // `api.cross-deck.com` and broke for customers on staging /\n        // regional / self-hosted base URLs (recursive capture loop).\n        selfHostname: extractSelfHostname(opts.baseUrl),\n      });\n      this.state.errors = tracker;\n      tracker.install();\n    }\n\n    // Terminal flush wiring — without this, every page navigation drops\n    // whatever's queued (page.viewed on load, session.ended on pagehide,\n    // user clicks within the idle window). Use keepalive so the request\n    // survives the unload. visibilitychange→hidden is the canonical\n    // mobile signal (pagehide also fires there); pagehide + beforeunload\n    // are the desktop ones. We listen to all three and rely on the\n    // queue being a no-op when empty so a single trigger flushes once.\n    this.state.uninstallUnloadFlush = installUnloadFlush(() => {\n      // Fire-and-forget. Errors here can't be handled meaningfully — the\n      // page is going away. Keepalive lets the browser keep the request\n      // alive past unload up to 64 KB total in flight.\n      void this.flush({ keepalive: true }).catch(() => undefined);\n    });\n\n    if (opts.autoHeartbeat && !localDevMode) {\n      // Fire-and-forget — heartbeat failure shouldn't block init().\n      // Skipped in dev mode — there's nothing to heartbeat.\n      void this.heartbeat().catch(() => undefined);\n    }\n\n    // ----- Campaign-arrival connect (the integration moat) -----\n    // If this page was reached from a Crossdeck-tagged campaign link, the\n    // landing URL carries an opaque `cd_ref` tag (the partner contact id —\n    // never an email). Post it once so the backend binds THIS session to\n    // that person and pulls their integration record (deals, pipeline)\n    // onto their journey. Fire-and-forget, browser-only, backend-idempotent\n    // — never blocks init, never affects the page.\n    if (!localDevMode) {\n      void this.captureCampaignArrival().catch(() => undefined);\n    }\n\n    // ────────────────────────────────────────────────────────────────\n    // Contract verifier layer — install LAST so the SDK is fully\n    // constructed before any verifier observes its state. See\n    // sdks/web/src/_contract-verifiers.ts for the full architecture.\n    //\n    // Three-layer precedence (locked):\n    //\n    //   code option > dashboard remote config > DEBUG/RELEASE default\n    //\n    // Code wins so engineers retain ultimate control; the dashboard\n    // toggle (per-app, via /dashboard/apps/{appId}) is the no-deploy\n    // operational lever for QA / staging soaks; default is what ships\n    // when nobody touches anything.\n    //\n    // Routing rules — locked here so a future change to init() can't\n    // accidentally muddle them (the docstrings on the CrossdeckOptions\n    // fields name each other as the wrong tool for the wrong job):\n    //\n    //   disableContractAssertions: true  → layer is entirely silent\n    //   logVerifierResults:        false → console silent on PASS;\n    //                                       FAIL still prints at WARN\n    //   verifyContractsAtBoot:     false → no boot self-test, but\n    //                                       hot-path verifiers still run\n    //\n    // `disableContractAssertions` is intentionally NOT remote-\n    // configurable — sovereignty kill-switch must live in customer\n    // source so procurement / audit teams can grep for it.\n    // ────────────────────────────────────────────────────────────────\n    if (options.disableContractAssertions !== true) {\n      const debugDefault = defaultDebugModeFlag();\n      // 1. SYNCHRONOUS BOOTSTRAP — context + reporter + verifier set\n      //    are constructed immediately using code > default. Hot-path\n      //    verifiers fire correctly from the next operation onwards.\n      this.verifierCtx = buildVerifierContext({\n        logVerifierResults: options.logVerifierResults ?? debugDefault,\n        disableContractAssertions: false,\n        // Best-effort run-context detection. Customers running the\n        // Web SDK inside their own CI (Playwright, Cypress, etc.)\n        // typically set CI=true; we use that as the signal. Otherwise\n        // we assume `customer-app` — the SDK is running inside a\n        // customer's deployed site.\n        runContext: typeof process !== \"undefined\" && process.env && process.env.CI\n          ? \"ci\"\n          : \"customer-app\",\n      });\n      this.verifierReporter = new VerifierReporter(this.verifierCtx);\n      const flushVerifier = buildFlushIntervalVerifier(opts.eventFlushIntervalMs);\n      this.verifiers = Object.freeze([...STATIC_VERIFIERS, flushVerifier]);\n\n      // 2. ASYNCHRONOUS REFINEMENT — fetch /v1/config and apply the\n      //    dashboard's per-app remote config, then fire the boot\n      //    self-test (so its output reflects the FINAL resolved\n      //    flags, not the pre-fetch defaults). Fire-and-forget — a\n      //    fetch failure leaves the local defaults in place and the\n      //    boot self-test runs only if code > default resolves true.\n      //\n      //    Skipped in localDevMode (test harness / offline emulator\n      //    where there's no backend to fetch from). Boot self-test\n      //    still fires under code > default precedence directly.\n      if (localDevMode) {\n        const verifyAtBootLocal =\n          options.verifyContractsAtBoot ?? debugDefault;\n        if (verifyAtBootLocal && this.verifierReporter) {\n          void runBootSelfTest(\n            this.verifiers,\n            this.verifierReporter,\n            this.verifierCtx,\n          ).catch(() => undefined);\n        }\n      } else {\n        void this.bootstrapVerifierLayerRemote(options, debugDefault).catch(\n          () => undefined,\n        );\n      }\n    }\n    // The previous synchronous boot-test block is now replaced by the\n    // async refinement above. Closing brace below remains the end of\n    // init().\n\n    // ── Crossdeck Consent — the light switch (CD-185) ──────────────────\n    // Enforcement is already live above (ConsentManager + GPC). This mounts\n    // the branded WIDGET, and only when asked.\n    if (options.consentBanner) {\n      void this.mountConsentWidget(options.consentBanner);\n    }\n    return;\n  }\n\n  /**\n   * Crossdeck Consent — mount the opt-in widget (\"the light switch\").\n   *\n   * The import is DYNAMIC on purpose: bundlers code-split the widget, so a\n   * customer who never flips the switch never downloads a byte of it. That is\n   * what keeps consent a FEATURE rather than a tax on every install.\n   *\n   * Consent-FIRST: analytics + marketing are denied until the visitor\n   * chooses. A banner that gates nothing is the lawsuit, not the fix.\n   *\n   * Never a second banner: if GPC or an existing CMP is detected we adopt its\n   * answer, subscribe to its changes, and render nothing.\n   *\n   * Fail-soft: any failure here logs one diagnostic and leaves the SDK\n   * running. Enforcement is unaffected — it lives in core, not in the widget.\n   */\n  private async mountConsentWidget(\n    cfg: true | string | ConsentBannerInit,\n  ): Promise<void> {\n    try {\n      const opts: ConsentBannerInit =\n        cfg === true ? {} : typeof cfg === \"string\" ? { policyUrl: cfg } : cfg;\n      // Code-split in bundled builds. The script-tag (UMD) build has no module\n      // loader, so it falls back to the companion global installed by\n      // crossdeck-consent.umd.min.js.\n      let banner: typeof import(\"./consent-banner\");\n      let coex: typeof import(\"./consent-coexistence\");\n      try {\n        [banner, coex] = await Promise.all([\n          import(\"./consent-banner\"),\n          import(\"./consent-coexistence\"),\n        ]);\n      } catch {\n        const g = (globalThis as unknown as {\n          CrossdeckConsent?: typeof import(\"./consent-banner\") &\n            typeof import(\"./consent-coexistence\");\n        }).CrossdeckConsent;\n        if (!g?.mountConsentBanner || !g?.detectExistingConsent) {\n          throw new Error(\n            'consent widget not loaded — add the crossdeck-consent.umd.min.js script tag, or import \"@cross-deck/web/consent\"',\n          );\n        }\n        banner = g;\n        coex = g;\n      }\n      const st = this.state;\n      if (!st) return;\n\n      // OPT-OUT by default — the full SDK keeps running exactly as it does\n      // today and the visitor may decline. Crossdeck does NOT impose a consent\n      // posture on the customer's own site: opt-out is lawful in much of the\n      // world, and an operator who switches a banner on should not silently\n      // lose their analytics. Those who need opt-in (EU/EEA) set\n      // `denyUntilChoice: true` and get consent-first.\n      //\n      // (The GUEST build, @cross-deck/web-lite, IS consent-first — there we\n      // are on someone else's site and the site owner never chose us.)\n      if (opts.denyUntilChoice) {\n        this.consent({ analytics: false, marketing: false });\n      }\n\n      const existing = coex.detectExistingConsent();\n      if (existing) {\n        const read = existing.read();\n        this.consent({\n          analytics: read.analytics ?? false,\n          marketing: read.marketing ?? false,\n        });\n        if (existing.emitsChanges) {\n          coex.subscribeToExternalConsent(existing, (partial) => {\n            this.consent(partial);\n          });\n        }\n        return;\n      }\n\n      const current = this.consent({});\n      banner.mountConsentBanner({\n        target: opts.target,\n        policyUrl: opts.policyUrl,\n        categories: opts.categories,\n        // Show the visitor what is ACTUALLY on right now — so an opt-out\n        // banner opens with analytics enabled, and an opt-in one does not.\n        existingConsent: {\n          analytics: current.analytics,\n          marketing: current.marketing,\n          identityOptIn: st.consent.identityOptIn,\n        },\n        consent: { set: (partial) => this.consent(partial) },\n        onChange: (state) => {\n          this.setIdentityOptIn(state.identityOptIn);\n          opts.onChange?.(state);\n        },\n      });\n    } catch (err) {\n      try {\n        // eslint-disable-next-line no-console\n        console.warn(\n          \"[crossdeck] consent banner failed to load — consent ENFORCEMENT is unaffected:\",\n          err instanceof Error ? err.message : String(err),\n        );\n      } catch { /* ignore */ }\n    }\n  }\n\n  /**\n   * Fetch /v1/config from the backend and apply any per-app verifier\n   * overrides set in the dashboard, then fire the boot self-test\n   * once with the FINAL resolved flags.\n   *\n   * Precedence (also documented at the call site in init()):\n   *   code option > dashboard remote config > DEBUG/RELEASE default\n   *\n   * Code wins so engineers retain ultimate control; dashboard is the\n   * no-deploy operational lever for QA / staging soaks.\n   *\n   * Never throws — a /v1/config failure surfaces as \"stick with the\n   * synchronous defaults that init() already applied\" and the boot\n   * self-test runs only if code > default resolves true.\n   */\n  private async bootstrapVerifierLayerRemote(\n    options: CrossdeckOptions,\n    debugDefault: boolean,\n  ): Promise<void> {\n    if (!this.state || !this.verifiers || !this.verifierCtx) return;\n\n    interface RemoteVerifierConfig {\n      logVerifierResults: boolean | null;\n      verifyContractsAtBoot: boolean | null;\n    }\n    interface ConfigResponseShape {\n      verifierConfig?: RemoteVerifierConfig;\n    }\n\n    let remote: RemoteVerifierConfig = {\n      logVerifierResults: null,\n      verifyContractsAtBoot: null,\n    };\n    try {\n      const resp = await this.state.http.request<ConfigResponseShape>(\n        \"GET\",\n        \"/config\",\n      );\n      if (resp && resp.verifierConfig) {\n        const r = resp.verifierConfig;\n        remote = {\n          logVerifierResults:\n            typeof r.logVerifierResults === \"boolean\"\n              ? r.logVerifierResults\n              : null,\n          verifyContractsAtBoot:\n            typeof r.verifyContractsAtBoot === \"boolean\"\n              ? r.verifyContractsAtBoot\n              : null,\n        };\n      }\n    } catch {\n      // Fall through — `remote` stays null, defaults apply.\n    }\n\n    // Resolve precedence: code > remote > default.\n    const logResults =\n      options.logVerifierResults ??\n      (remote.logVerifierResults ?? debugDefault);\n    const verifyAtBoot =\n      options.verifyContractsAtBoot ??\n      (remote.verifyContractsAtBoot ?? debugDefault);\n\n    // If the remote config or precedence changed the resolved value\n    // from what the synchronous bootstrap chose, rebuild the\n    // context + reporter so subsequent hot-path verifiers see the\n    // updated flags. The previous reporter is GC'd; in-flight hot-\n    // path dispatches that snapshot the reporter (none currently;\n    // every dispatcher reads `this.verifierReporter` at call time)\n    // would see the old flags.\n    if (logResults !== this.verifierCtx.logVerifierResults) {\n      this.verifierCtx = {\n        ...this.verifierCtx,\n        logVerifierResults: logResults,\n      };\n      this.verifierReporter = new VerifierReporter(this.verifierCtx);\n    }\n\n    if (verifyAtBoot && this.verifierReporter) {\n      await runBootSelfTest(\n        this.verifiers,\n        this.verifierReporter,\n        this.verifierCtx,\n      );\n    }\n  }\n\n  /**\n   * @deprecated Use `init()` instead. NorthStar §4 standardised the\n   * lifecycle method name across SDKs as `init` (formerly `start` /\n   * `configure`). `start` will be removed in a future major version.\n   */\n  start(options: CrossdeckOptions): void {\n    if (typeof console !== \"undefined\") {\n      // eslint-disable-next-line no-console\n      console.warn(\n        \"[crossdeck] Crossdeck.start() is deprecated — use Crossdeck.init() instead. The signature is the same.\",\n      );\n    }\n    this.init(options);\n  }\n\n  /**\n   * Campaign-arrival connect. If the landing URL carries a Crossdeck tag\n   * (`cd_ref`), post it to the arrival endpoint so the backend binds this\n   * anonymous session to the tagged person and pulls their integration\n   * record (deals, pipeline) onto their journey. Browser-only; fire-and-\n   * forget (never throws); backend-idempotent so a re-fire is harmless.\n   */\n  private async captureCampaignArrival(): Promise<void> {\n    const s = this.state;\n    if (!s) return;\n    const loc = (globalThis as { location?: { search?: string } }).location;\n    if (!loc || typeof loc.search !== \"string\" || loc.search.length === 0) return;\n    let ref: string | null = null;\n    try {\n      ref = new URLSearchParams(loc.search).get(\"cd_ref\");\n    } catch {\n      return; // malformed query string — nothing to do.\n    }\n    if (!ref) return;\n    try {\n      await s.http.request<{ object: string }>(\"POST\", \"/integrations/hubspot/arrival\", {\n        body: { anonymousId: s.identity.anonymousId, ref },\n      });\n    } catch {\n      // Fire-and-forget — a failed arrival connect must never affect the page.\n    }\n  }\n\n  /**\n   * Link the anonymous device to a developer-supplied user ID. Cache\n   * the resolved Crossdeck customer for follow-up calls.\n   *\n   * v0.9.0+ accepts an optional `traits` bag — profile data (name,\n   * plan, signupDate, role) persisted on the Crossdeck customer record\n   * and queryable from dashboards. Traits are sanitised through the\n   * same validator that gates `track()` properties, so a `{ avatar:\n   * <File>, onSave: () => {} }` payload can't corrupt the alias call.\n   *\n   *   Crossdeck.identify(\"user_847\", {\n   *     email: \"wes@pinet.co.za\",\n   *     traits: { name: \"Wes\", plan: \"pro\", signedUpAt: \"2026-05-11\" },\n   *   });\n   */\n  async identify(userId: string, options?: IdentifyOptions): Promise<AliasResult> {\n    const s = this.requireStarted();\n    if (!userId) {\n      throw new CrossdeckError({\n        type: \"invalid_request_error\",\n        code: \"missing_user_id\",\n        message: \"identify(userId) requires a non-empty userId.\",\n      });\n    }\n    // Read-cost cross-match (the moat): the instant we know who this session is,\n    // tell the Buckets collector — every browser database read from here on\n    // attributes to this user. Operational, like the entitlement cache, not an\n    // analytics event; a no-op unless @cross-deck/buckets/web is installed.\n    bridgeReadCost({ actor: userId });\n    if (!s.consent.analytics || !s.consent.identityOptIn) {\n      // No-op on consent denial OR when the visitor hasn't opted in to being\n      // recognised (the consent widget's \"Recognize me\" switch — off by default\n      // only in the consent-mode guest build; true everywhere else, so this is\n      // additive and the core identify() path is unchanged). Throw NOT — the\n      // developer expected an aliasResult to await. Return a no-op result that\n      // mirrors the wire shape so existing call chains don't break.\n      const reason = !s.consent.analytics\n        ? \"consent denied for analytics\"\n        : \"identity opt-in not granted (Recognize me is off)\";\n      s.debug.emit(\"sdk.consent_denied\", `identify() skipped — ${reason}.`);\n      return {\n        object: \"alias_result\",\n        crossdeckCustomerId: s.identity.crossdeckCustomerId ?? \"\",\n        linked: [],\n        mergePending: false,\n        env: s.options.environment,\n      };\n    }\n    // Sanitise traits at the SDK boundary so a malformed bag (function,\n    // BigInt, circular) never crashes the alias request. Empty result\n    // → omit the field entirely so backends that don't yet know about\n    // traits aren't surprised by an unknown key.\n    const traitsValidation =\n      options?.traits !== undefined\n        ? validateEventProperties(options.traits)\n        : null;\n    const traits = traitsValidation && Object.keys(traitsValidation.properties).length > 0\n      ? traitsValidation.properties\n      : undefined;\n    if (s.debug.enabled && traitsValidation && traitsValidation.warnings.length > 0) {\n      for (const w of traitsValidation.warnings) {\n        s.debug.emit(\n          \"sdk.property_coerced\",\n          `identify() traits key ${JSON.stringify(w.key)} was ${w.kind.replace(/_/g, \" \")} during validation.`,\n          { key: w.key, kind: w.kind },\n        );\n      }\n    }\n    const body: Record<string, unknown> = {\n      userId,\n      anonymousId: s.identity.anonymousId,\n    };\n    if (options?.email) body.email = options.email;\n    if (traits) body.traits = traits;\n\n    // Bank-grade three-layer entitlement-cache isolation (v1.4.0\n    // Phase 1.3). Switch the cache slot BEFORE the alias POST so a\n    // mid-flight failure can't leave the cache pointing at the\n    // prior user. setUserKey:\n    //   (a) hashes the new userId into a physically separate\n    //       storage suffix — `crossdeck:entitlements:<sha256>`,\n    //   (b) unconditionally wipes the in-memory snapshot (no\n    //       conditional gating — every identify() guarantees a\n    //       fresh slot),\n    //   (c) rehydrates from the new slot so a returning user sees\n    //       their last-known-good immediately.\n    // Capture prior userId BEFORE the slot rotation so the\n    // per-user-cache-isolation verifier can observe the transition.\n    // Reading this from `s.developerUserId` (the prior state) — the\n    // assignment to the new value happens further down, after the\n    // HTTP call.\n    const priorUserId = s.developerUserId;\n\n    s.entitlements.setUserKey(userId);\n\n    // ────────────────────────────────────────────────────────────\n    // Hot-path verifier: per-user-cache-isolation runs AFTER the\n    // slot rotation completes. PASS prints to debug console if\n    // `logVerifierResults: true`; FAIL prints at WARN + fires\n    // `reportContractFailure(...)` to the reliability channel.\n    // Short-circuits cheaply when `disableContractAssertions: true`.\n    // ────────────────────────────────────────────────────────────\n    if (this.verifiers && this.verifierReporter && this.verifierCtx) {\n      runOnIdentify(this.verifiers, this.verifierReporter, this.verifierCtx, {\n        priorUserId,\n        nextUserId: userId,\n        cache: s.entitlements,\n      });\n    }\n\n    const result = await s.http.request<AliasResult>(\"POST\", \"/identity/alias\", {\n      body,\n    });\n    s.identity.setCrossdeckCustomerId(result.crossdeckCustomerId);\n    s.developerUserId = userId;\n\n    // Refresh entitlements for the newly-identified user from the SERVER (the\n    // authoritative source) when their slot has no cached last-known-good yet.\n    // This is what makes isEntitled() correct immediately after\n    // `await identify()` WITHOUT inheriting the anonymous cache (P0 #5: a\n    // shared-device anon session must never silently become the new user's\n    // entitlements — the anon slot was just cleared by setUserKey above; only\n    // the server, which linked the anon customer via the alias, decides what\n    // this user is entitled to). A returning user with a warm slot skips the\n    // fetch. Best-effort: a failed refresh leaves the empty slot untouched and\n    // never fails identify() (its primary job — the alias — already succeeded).\n    if (s.entitlements.list().length === 0) {\n      try {\n        await this.getEntitlements();\n      } catch {\n        // Offline / transient: last-known-good (none for a first identify) stays.\n      }\n    }\n    return result;\n  }\n\n  /**\n   * Register super-properties — Mixpanel pattern. Once set, every\n   * subsequent event of THIS SDK instance carries these keys on its\n   * properties bag automatically.\n   *\n   *   Crossdeck.register({ plan: \"pro\", releaseChannel: \"beta\" });\n   *   Crossdeck.track(\"paywall_shown\");  // includes plan + releaseChannel\n   *\n   * Values that are `null` are deleted (the explicit \"stop tracking\n   * this key\" idiom). Returns the resulting bag.\n   *\n   * Sanitised through `validateEventProperties` so a `{ avatar: File }`\n   * payload can't poison the queue at flush time.\n   */\n  register(properties: Record<string, unknown>): Record<string, unknown> {\n    const s = this.requireStarted();\n    const validation = validateEventProperties(properties);\n    return s.superProps.register(validation.properties);\n  }\n\n  /** Remove a single super-property key. Idempotent. */\n  unregister(key: string): void {\n    const s = this.requireStarted();\n    s.superProps.unregister(key);\n  }\n\n  /** Snapshot of the current super-property bag. */\n  getSuperProperties(): Record<string, unknown> {\n    if (!this.state) return {};\n    return this.state.superProps.getSuperProperties();\n  }\n\n  /**\n   * Associate the current user with a group (org, team, account, etc.).\n   * Mixpanel / Segment \"Group Analytics\" pattern.\n   *\n   *   Crossdeck.group(\"org\", \"acme_inc\");\n   *   Crossdeck.group(\"team\", \"design\", { headcount: 12 });\n   *\n   * Once set, every subsequent event carries `$groups.<type>: id` on\n   * its properties bag, enabling B2B dashboards (\"how is Acme using\n   * the product\"). Pass `id: null` to clear a group membership.\n   */\n  group(type: string, id: string | null, traits?: GroupTraits): void {\n    const s = this.requireStarted();\n    if (!type) {\n      throw new CrossdeckError({\n        type: \"invalid_request_error\",\n        code: \"missing_group_type\",\n        message: \"group(type, id) requires a non-empty type.\",\n      });\n    }\n    const sanitisedTraits = traits ? validateEventProperties(traits).properties : undefined;\n    s.superProps.setGroup(type, id, sanitisedTraits);\n  }\n\n  /** Snapshot of the current groups map keyed by type. */\n  getGroups(): Record<string, { id: string; traits?: Record<string, unknown> }> {\n    if (!this.state) return {};\n    return this.state.superProps.getGroups();\n  }\n\n  /**\n   * Update consent state. Three independent dimensions:\n   *\n   *   analytics  — track() + identify() + auto-emissions\n   *   marketing  — paid-traffic click IDs + referrer URL on events\n   *   errors     — Web Vitals + (future) error reporting\n   *\n   * Each defaults to `true` (granted). Pass partial state — only the\n   * keys you provide are changed.\n   *\n   *   Crossdeck.consent({ analytics: false });\n   *   Crossdeck.consent({ marketing: true, errors: true });\n   *\n   * DNT-derived denies cannot be flipped back on; if the browser said\n   * \"don't track\" we don't track even if the developer code disagrees.\n   */\n  consent(state: Partial<ConsentState>): ConsentState {\n    const s = this.requireStarted();\n    const next = s.consent.set(state);\n    s.debug.emit(\"sdk.consent_changed\", \"Consent state updated.\", { ...next });\n    return next;\n  }\n\n  /**\n   * Set the explicit identity opt-in — the \"Recognize me\" choice. Default is\n   * `true` (core SDK identifies as normal); the consent-mode guest build sets it\n   * `false` at boot and flips it `true` only when the visitor opts in. While\n   * `false`, `identify()` no-ops. DNT forces it off and locks it.\n   */\n  setIdentityOptIn(optedIn: boolean): void {\n    const s = this.requireStarted();\n    s.consent.setIdentityOptIn(optedIn);\n    s.debug.emit(\n      \"sdk.consent_changed\",\n      `Identity opt-in ${optedIn ? \"granted\" : \"withdrawn\"}.`,\n    );\n  }\n\n  /** Snapshot of the current consent state. */\n  consentStatus(): ConsentState {\n    if (!this.state) {\n      return { analytics: true, marketing: true, errors: true };\n    }\n    return this.state.consent.get();\n  }\n\n  // ============================================================\n  // Error capture surface (v1.0.0+)\n  // ============================================================\n\n  /**\n   * Manually capture an error from a try/catch block.\n   *\n   *   try { …risky… } catch (err) {\n   *     Crossdeck.captureError(err, { context: { plan: \"pro\" } });\n   *   }\n   *\n   * The error is shipped through the same event queue as analytics\n   * (durable, retried, rate-limited per fingerprint). Sends are gated\n   * by `consent.errors`. Returns silently — never throws, even if the\n   * SDK isn't initialised yet.\n   */\n  captureError(\n    error: unknown,\n    options?: { context?: Record<string, unknown>; tags?: Record<string, string>; level?: ErrorLevel },\n  ): void {\n    if (!this.state?.errors) return;\n    this.state.errors.captureError(error, options);\n  }\n\n  /**\n   * Capture a non-error event you want to surface as an issue\n   * (\"deprecated path hit\", \"we entered the slow code path\"). Sentry\n   * captureMessage pattern. Returns silently if not initialised.\n   */\n  captureMessage(message: string, level: ErrorLevel = \"info\"): void {\n    if (!this.state?.errors) return;\n    this.state.errors.captureMessage(message, level);\n  }\n\n  /**\n   * Attach a tag to every subsequent error report. Tags are key/value\n   * strings (Sentry pattern): `setTag(\"flow\", \"checkout\")` → every\n   * error from this point on carries `tags.flow === \"checkout\"`.\n   */\n  setTag(key: string, value: string): void {\n    if (!this.state) return;\n    this.state.errorTags[key] = value;\n  }\n\n  /** Bulk-set tags. Merges with existing tags. */\n  setTags(tags: Record<string, string>): void {\n    if (!this.state) return;\n    Object.assign(this.state.errorTags, tags);\n  }\n\n  /**\n   * Attach a structured context blob to every subsequent error report.\n   * Unlike tags (flat key/value), context is a named bag of arbitrary\n   * data: `setContext(\"cart\", { items: 3, total: 42.99 })`.\n   */\n  setContext(name: string, data: Record<string, unknown>): void {\n    if (!this.state) return;\n    this.state.errorContext[name] = data;\n  }\n\n  /**\n   * Add a custom breadcrumb to the rolling buffer. Useful for marking\n   * domain-meaningful moments (\"user opened paywall\") that aren't\n   * already auto-captured. The buffer caps at 50 entries; old ones\n   * evict.\n   */\n  addBreadcrumb(crumb: Breadcrumb): void {\n    if (!this.state) return;\n    this.state.breadcrumbs.add(crumb);\n  }\n\n  /**\n   * Install a pre-send hook for errors. Return null to drop, or a\n   * modified CapturedError to scrub / rewrite. Sentry's beforeSend\n   * pattern — the only way to redact app-specific PII (auth tokens\n   * in URLs, etc.) before the report leaves the browser.\n   */\n  setErrorBeforeSend(\n    hook: ((err: CapturedError) => CapturedError | null) | null,\n  ): void {\n    if (!this.state) return;\n    this.state.errorBeforeSend = hook;\n  }\n\n  /**\n   * Internal: turn a CapturedError into a Crossdeck event and enqueue\n   * it. Goes through the same queue / persistence / consent / scrub\n   * pipeline as analytics events.\n   */\n  private reportError(err: CapturedError): void {\n    // Sanitise the error payload — stack frames may contain\n    // user-supplied PII (emails / IDs in URLs). The scrub runs\n    // automatically inside track() before the event lands in the\n    // queue, but we also pre-flatten the structured fields here so\n    // they're searchable in the warehouse.\n    const properties: EventProperties = {\n      // Identifiers\n      fingerprint: err.fingerprint,\n      level: err.level,\n      // Error shape\n      errorType: err.errorType,\n      message: err.message,\n      // Stack\n      stack: err.rawStack ?? undefined,\n      frames: err.frames,\n      filename: err.filename ?? undefined,\n      lineno: err.lineno ?? undefined,\n      colno: err.colno ?? undefined,\n      // Context\n      tags: err.tags,\n      context: err.context,\n      breadcrumbs: err.breadcrumbs,\n      // HTTP (only when applicable)\n      http: err.http,\n    };\n    // Strip undefined values for a tidy wire shape.\n    for (const k of Object.keys(properties)) {\n      if (properties[k] === undefined) delete properties[k];\n    }\n    // Use track() for the full pipeline: validation, enrichment,\n    // consent gate (gated on `analytics` though we already verified\n    // `errors`), durable queue, retry, scrub. The event name follows\n    // the namespacing convention so dashboards can filter `name\n    // LIKE 'error.%'`.\n    this.track(err.kind, properties);\n  }\n\n  /**\n   * GDPR/CCPA \"right to be forgotten\" — calls the backend's\n   * /v1/identity/forget endpoint to schedule a server-side deletion of\n   * the customer's events and profile, then wipes all local state\n   * (identity, entitlements, queue, super-props, persistent stores).\n   *\n   * Idempotent. Safe to call when no identity has been established\n   * (it just wipes the empty local state).\n   *\n   * After forget() resolves, the SDK is in the same shape as if the\n   * developer had called `Crossdeck.reset()` — a fresh anonymousId is\n   * minted and the next session is a brand new identity-graph entry.\n   */\n  async forget(): Promise<void> {\n    const s = this.requireStarted();\n    const identityQuery = this.identityQueryParams();\n    try {\n      await s.http.request<{ object: \"forgot\" }>(\"POST\", \"/identity/forget\", {\n        body: {\n          // Send every identity hint we hold; the server resolves the\n          // canonical customer record and queues deletion. Missing\n          // endpoint (older backend) gracefully degrades — local state\n          // still wipes via the reset() call below.\n          ...identityQuery,\n        },\n      });\n    } catch (err) {\n      // Server-side deletion failure is recorded but does not block\n      // local wipe — the developer's user just asked to be forgotten,\n      // refusing to clear their device because the backend hiccupped\n      // would be the wrong call.\n      s.debug.emit(\n        \"sdk.consent_denied\",\n        `forget() server call failed (${err instanceof Error ? err.message : String(err)}). Local state wiped anyway.`,\n      );\n    }\n    this.reset();\n  }\n\n  /**\n   * Read the current customer's active entitlements from the server.\n   * Updates the local cache so subsequent isEntitled() calls answer\n   * synchronously.\n   */\n  async getEntitlements(): Promise<PublicEntitlement[]> {\n    const s = this.requireStarted();\n    // Capture the identity generation BEFORE the await. If setUserKey()\n    // rotates the identity while this request is in flight, the generation\n    // moves and we drop the write below — a stale fetch (the anonymous boot\n    // fetch, or one started under a prior slot, or an out-of-order resolve\n    // among the ≥2 fetches identify() + explicit callers keep in flight)\n    // can never clobber the authoritative answer for the identity NOW.\n    const gen = s.entitlements.generation;\n    const query = this.identityQueryParams();\n    let result: EntitlementsListResponse;\n    try {\n      result = await s.http.request<EntitlementsListResponse>(\n        \"GET\",\n        \"/entitlements\",\n        { query }\n      );\n    } catch (err) {\n      // The refresh failed (Crossdeck unreachable / transient error).\n      // The durable cache keeps serving last-known-good — but mark it\n      // stale so the staleness is visible via diagnostics(), never a\n      // silent unbounded window. Then rethrow so the caller still sees\n      // the failure.\n      s.entitlements.markRefreshFailed();\n      throw err;\n    }\n    if (gen !== s.entitlements.generation) {\n      // Identity rotated (a setUserKey since we started). This response\n      // belongs to a prior identity — discard it entirely: don't write the\n      // cache, don't stamp the crossdeckCustomerId. Return the data to the\n      // caller unapplied so the call still resolves, but the authoritative\n      // answer for the current identity stands untouched.\n      return Array.isArray(result.data) ? result.data : [];\n    }\n    if (result.crossdeckCustomerId) {\n      s.identity.setCrossdeckCustomerId(result.crossdeckCustomerId);\n    }\n    if (!Array.isArray(result.data)) {\n      // Malformed response — the body parsed but carried no entitlement\n      // list (schema drift, a proxy that rewrote the body, a 200 with an\n      // error envelope). Treat it exactly like a failed refresh: keep the\n      // durable last-known-good, mark it stale, and surface the failure —\n      // never crash the caller and never clobber a paying customer's cache\n      // with garbage.\n      s.entitlements.markRefreshFailed();\n      throw new CrossdeckError({\n        type: \"internal_error\",\n        code: \"malformed_entitlements_response\",\n        message:\n          \"GET /entitlements returned no entitlement list (expected an array at `data`).\",\n      });\n    }\n    s.entitlements.setFromList(result.data);\n    return result.data;\n  }\n\n  /**\n   * Synchronous read from the durable local cache — answers from\n   * last-known-good. The cache hydrates from device storage on boot and\n   * survives a Crossdeck outage, so a returning paying customer reads\n   * true even before the session's first network round-trip. Returns\n   * false only for a genuinely new install that has never completed a\n   * getEntitlements(), or for an entitlement past its own validUntil.\n   *\n   * Throws `not_initialized` (CrossdeckError, type\n   * `configuration_error`) if called before `Crossdeck.init()`. The\n   * `useEntitlement` React hook and the Vue composable both swallow\n   * this and return `false`; bare callers must guard for it (or call\n   * after `init()` resolves).\n   */\n  isEntitled(key: string): boolean {\n    const s = this.requireStarted();\n    return s.entitlements.isEntitled(key);\n  }\n\n  /** Snapshot of the local entitlement cache. */\n  listEntitlements(): PublicEntitlement[] {\n    const s = this.requireStarted();\n    return s.entitlements.list();\n  }\n\n  /**\n   * Subscribe to entitlement-cache changes. Returns an unsubscribe fn.\n   *\n   * The listener is invoked AFTER the cache mutates — once after a\n   * successful `getEntitlements()` warms it, again after `syncPurchases()`\n   * delivers fresh entitlements, once on `reset()` to fire the empty-\n   * cache state for logout flows, AND once on `identify()` after the\n   * per-user cache slot rotates and re-hydrates from device storage.\n   *\n   * IMPORTANT — the `identify()` fire is a TRAP if you treat it as\n   * authoritative network state. `identify()` does NOT fetch entitlements;\n   * it switches the per-user cache slot and rehydrates from device\n   * storage (which is empty for a brand-new install, and last-known-good\n   * — possibly stale — for a returning user). A listener that gates a\n   * paywall on the first fire after an identity switch will read\n   * `false` for a paying customer on a fresh device and let them past\n   * the gate as free. The network-truth fire is the one that follows\n   * the next `getEntitlements()` resolution. Either call\n   * `getEntitlements()` explicitly after `identify()`, or have your\n   * gating code tolerate the empty-then-populated transition.\n   *\n   * It is NOT invoked synchronously on subscribe. Callers that need\n   * the current state should read it via `isEntitled()` / `listEntitlements()`\n   * inline; the listener fires only on FUTURE changes.\n   *\n   * This is the foundation of the `useEntitlement` React hook in\n   * `@cross-deck/web/react` — without it, React (or SwiftUI / Compose\n   * / Vue) would have no way to re-render when entitlements arrive\n   * asynchronously after init. The naive pattern of calling\n   * `Crossdeck.isEntitled(\"pro\")` directly inside a render path\n   * shows the empty-cache result forever; binding the result to\n   * component state via `onEntitlementsChange` is the correct\n   * pattern.\n   *\n   * Idempotent unsubscribe — calling the returned function multiple\n   * times is safe.\n   *\n   * Listener errors are swallowed (a buggy listener can't crash the\n   * SDK or other listeners).\n   */\n  onEntitlementsChange(listener: EntitlementsListener): () => void {\n    const s = this.requireStarted();\n    return s.entitlements.subscribe(listener);\n  }\n\n  /**\n   * Queue a telemetry event. Returns immediately — the network round-\n   * trip happens in the background. To flush before the page unloads,\n   * call flush().\n   */\n  /**\n   * Emit `crossdeck.contract_failed` to the Crossdeck reliability\n   * endpoint — single-fire, one-way, never visible in the customer's\n   * dashboard. Goes over a dedicated HTTP path with the reliability\n   * publishable key embedded at build time; the customer's track()\n   * pipeline never carries `crossdeck.*` events. This is the\n   * independent-controller flow described in Privacy Policy §6\n   * (\"Flow B\"). The wire shape is fixed by the schema-lock contract\n   * at `contracts/diagnostics/contract-failed-payload-schema-lock.json`.\n   *\n   * Wire the call from a test hook, dogfood failure path, or\n   * customer contract-verification harness; see\n   * `contracts/README.md` for the per-test-framework hook recipes.\n   */\n  reportContractFailure(input: ContractFailureInput): void {\n    const payload: Record<string, string> = {\n      contract_id: input.contractId,\n      sdk_version: SDK_VERSION,\n      sdk_platform: \"web\",\n      failure_reason: input.failureReason,\n      run_context: input.runContext,\n      run_id: input.runId,\n    };\n    if (input.testRef) {\n      payload.test_file = input.testRef.file;\n      payload.test_name = input.testRef.name;\n    }\n    if (input.deviceClass) {\n      payload.device_class = input.deviceClass;\n    }\n    sendDiagnosticTelemetry(payload);\n  }\n\n  track(name: string, properties?: EventProperties): void {\n    const s = this.requireStarted();\n    if (!name) {\n      throw new CrossdeckError({\n        type: \"invalid_request_error\",\n        code: \"missing_event_name\",\n        message: \"track(name) requires a non-empty name.\",\n      });\n    }\n\n    // ----- Consent gate -----\n    // Three gates depending on event family:\n    //   error.*   → consent.errors  (crashes, HTTP failures, captureMessage)\n    //   webvitals.* → consent.errors (performance / reliability data)\n    //   everything else → consent.analytics\n    // Default-on; the developer must explicitly call\n    // Crossdeck.consent({...:false}) to drop them.\n    const isError = name.startsWith(\"error.\");\n    const isWebVital = name.startsWith(\"webvitals.\");\n    const consentGateOk = (isError || isWebVital) ? s.consent.errors : s.consent.analytics;\n    if (!consentGateOk) {\n      if (s.debug.enabled) {\n        s.debug.emit(\n          \"sdk.consent_denied\",\n          `Dropped event \"${name}\" — consent denied for ${isWebVital ? \"errors\" : \"analytics\"}.`,\n        );\n      }\n      return;\n    }\n\n    // NorthStar §15: warn (in debug mode) when a property name looks\n    // dangerously like PII — email/password/token/secret/card/phone.\n    // We don't strip the field; that's the developer's call. We just\n    // surface the signal so they can spot accidental leaks early.\n    if (s.debug.enabled && properties) {\n      const flagged = findSensitivePropertyKeys(properties);\n      if (flagged.length > 0) {\n        s.debug.emit(\n          \"sdk.sensitive_property_warning\",\n          `Event \"${name}\" has potentially sensitive property names: ${flagged.join(\", \")}. Crossdeck is privacy-first — avoid sending PII unless intentional.`,\n          { eventName: name, flagged },\n        );\n      }\n    }\n\n    // §16 \"No identity\" — only emit once per session so a chatty client\n    // doesn't spam the log with every track() before identify().\n    if (s.debug.enabled && !s.developerUserId && !s.identity.crossdeckCustomerId) {\n      s.debug.emit(\n        \"sdk.no_identity\",\n        \"Using anonymous user until identify(userId) is called.\",\n      );\n    }\n\n    // Validate + coerce caller-supplied properties BEFORE merging with\n    // SDK enrichment. This is the boundary where untrusted developer\n    // input becomes safe-to-serialise data: functions/symbols dropped,\n    // Date / BigInt / Error coerced to JSON-friendly shapes, oversized\n    // strings truncated, circular refs replaced. Without this, one bad\n    // property (e.g. `{ onClick: () => {} }`) would crash JSON.stringify\n    // at flush time and poison the entire batch indefinitely.\n    //\n    // The SDK's own enrichment (device info, sessionId, utm_*) is\n    // trusted and not re-validated — those values are produced by\n    // `collectDeviceInfo()` and `captureAcquisition()`, both of which\n    // are typed and bounded.\n    const validation = validateEventProperties(properties);\n    if (s.debug.enabled && validation.warnings.length > 0) {\n      for (const w of validation.warnings) {\n        s.debug.emit(\n          \"sdk.property_coerced\",\n          `Event \"${name}\" property ${JSON.stringify(w.key)} was ${w.kind.replace(/_/g, \" \")} during validation.`,\n          { eventName: name, key: w.key, kind: w.kind },\n        );\n      }\n    }\n\n    // Envelope v1 §3 — assign seq SYNCHRONOUSLY at track() time, before\n    // markActivity() (which may roll a new session and reset the counter to\n    // 0). The counter belongs to the session; capturing it here — at the\n    // deterministic call-order instant — means seq ordering follows call\n    // order, not scheduler luck. Also capture timestamp here so both fields\n    // are one sample (spec §3 + Swift parity from fix commit 6ac71a1).\n    const seq = s.autoTracker?.nextSeq() ?? 0;\n    const occurrenceTimestamp = Date.now();\n\n    // Enrichment policy: device info first, then auto-tracker context\n    // (sessionId + per-session acquisition utm_*/referrer), then\n    // caller-supplied properties last so a developer can override\n    // anything the SDK auto-attached.\n    //\n    // Acquisition fields are session-scoped (captured once at session\n    // start by AutoTracker) and attached to every event of that session\n    // — that's the GA4 model: same source/medium/campaign labels every\n    // event in the same visit. Empty strings are filtered out so we\n    // don't pollute event property dictionaries with no-signal columns.\n    // Enrichment layer order (later wins on key conflict):\n    //   1. Session + pageview + acquisition + click IDs (auto-tracker)\n    //   2. Super properties (registered once via Crossdeck.register)\n    //   3. Group memberships (set via Crossdeck.group)\n    //   4. Caller-supplied properties (sanitised)\n    // Device facts (os, browser, locale, etc.) are promoted to the\n    // top-level `context` object per Envelope v1 §4 and are NO LONGER\n    // spread into `properties`. Screen dimensions and device pixel ratio\n    // remain in `properties` as they are not part of the spec §4 context\n    // schema and are not promoted.\n    // The order is intentional: developer-supplied data is most\n    // authoritative, so it overrides anything the SDK auto-attached.\n    // Any tracked event — auto or custom — is activity: push the rolling\n    // 30-min session window forward so a user who interacts via custom\n    // events (not just pageviews/clicks) keeps one continuous session.\n    s.autoTracker?.markActivity();\n\n    // Envelope v1 §4 — build the standardized context object with the exact\n    // spec field names. Promoted out of `properties`. Common fields: os,\n    // osVersion, appVersion, sdkName, sdkVersion, locale, timezone. Web adds:\n    // browser, browserVersion. Only include keys that are defined (string).\n    const wireContext: Record<string, string> = {};\n    const di = s.deviceInfo;\n    if (di.os) wireContext.os = di.os;\n    if (di.osVersion) wireContext.osVersion = di.osVersion;\n    const appVer = s.options.appVersion;\n    if (appVer) wireContext.appVersion = appVer;\n    wireContext.sdkName = SDK_NAME;\n    wireContext.sdkVersion = s.options.sdkVersion;\n    if (di.locale) wireContext.locale = di.locale;\n    if (di.timezone) wireContext.timezone = di.timezone;\n    if (di.browser) wireContext.browser = di.browser;\n    if (di.browserVersion) wireContext.browserVersion = di.browserVersion;\n\n    // Properties layer: screen dimensions + pixel ratio remain in properties\n    // (not promoted to context). Start WITHOUT deviceInfo spread — context\n    // fields have been promoted out; only non-context device fields remain.\n    const enriched: EventProperties = {};\n    if (di.screenWidth !== undefined) enriched.screenWidth = di.screenWidth;\n    if (di.screenHeight !== undefined) enriched.screenHeight = di.screenHeight;\n    if (di.viewportWidth !== undefined) enriched.viewportWidth = di.viewportWidth;\n    if (di.viewportHeight !== undefined) enriched.viewportHeight = di.viewportHeight;\n    if (di.devicePixelRatio !== undefined) enriched.devicePixelRatio = di.devicePixelRatio;\n    const sessionId = s.autoTracker?.currentSessionId;\n    if (sessionId) enriched.sessionId = sessionId;\n    const pageviewId = s.autoTracker?.currentPageviewId;\n    if (pageviewId) enriched.pageviewId = pageviewId;\n    const acquisition = s.autoTracker?.currentAcquisition;\n    if (acquisition) {\n      // UTMs and referrer host are always attached (they're considered\n      // analytics, not marketing PII). Paid-traffic click IDs and the\n      // full referrer URL gate on marketing consent — the developer's\n      // user said \"no marketing tracking\" → drop them.\n      if (acquisition.utm_source) enriched.utm_source = acquisition.utm_source;\n      if (acquisition.utm_medium) enriched.utm_medium = acquisition.utm_medium;\n      if (acquisition.utm_campaign) enriched.utm_campaign = acquisition.utm_campaign;\n      if (acquisition.utm_content) enriched.utm_content = acquisition.utm_content;\n      if (acquisition.utm_term) enriched.utm_term = acquisition.utm_term;\n      if (acquisition.referrer && s.consent.marketing) enriched.referrer = acquisition.referrer;\n      if (s.consent.marketing) {\n        if (acquisition.gclid) enriched.gclid = acquisition.gclid;\n        if (acquisition.fbclid) enriched.fbclid = acquisition.fbclid;\n        if (acquisition.msclkid) enriched.msclkid = acquisition.msclkid;\n        if (acquisition.ttclid) enriched.ttclid = acquisition.ttclid;\n        if (acquisition.li_fat_id) enriched.li_fat_id = acquisition.li_fat_id;\n        if (acquisition.twclid) enriched.twclid = acquisition.twclid;\n      }\n    }\n    // Super properties registered via Crossdeck.register(). Skipped\n    // for keys the auto-enrichment already supplied so a `register`\n    // call can't accidentally shadow `sessionId` etc.\n    const supers = s.superProps.getSuperProperties();\n    for (const k of Object.keys(supers)) {\n      if (!(k in enriched)) enriched[k] = supers[k];\n    }\n    // Group memberships — attached as `$groups.<type>` for B2B\n    // dashboards. Mixpanel uses `$groups`; we mirror exactly so\n    // existing integrators don't need a translation layer.\n    const groupIds = s.superProps.getGroupIds();\n    if (Object.keys(groupIds).length > 0) {\n      enriched.$groups = groupIds;\n    }\n    Object.assign(enriched, validation.properties);\n\n    // Internal-traffic opt-out: this browser visited ?crossdeck_internal=1,\n    // so tag every event for ingest to classify as internal. Injected after\n    // developer properties so it can't be shadowed; reserved $-prefixed key.\n    if (isInternalOptOut()) {\n      enriched[INTERNAL_OPT_OUT_PROPERTY] = true;\n    }\n\n    // ----- PII scrub -----\n    // Last step before the event lands in the queue: defensive regex\n    // scrub on URL paths, titles, and any string property value. An\n    // app that puts emails or card numbers in URLs (`/users/wes@…/`)\n    // would otherwise ship that PII straight to the warehouse. Even\n    // with explicit consent this is the right default — Stripe scrubs\n    // pre-storage too. Disable via `scrubPii: false` in init() for\n    // pipelines that do their own redaction.\n    const finalProperties = s.scrubPii ? scrubPiiFromProperties(enriched) : enriched;\n\n    const event: QueuedEvent = {\n      eventId: this.mintEventId(),\n      name,\n      timestamp: occurrenceTimestamp,\n      seq,\n      context: wireContext,\n      properties: finalProperties,\n    };\n    Object.assign(event, this.identityHintForEvent());\n    s.events.enqueue(event);\n\n    // ────────────────────────────────────────────────────────────────\n    // Hot-path verifier: `super-property-merge-precedence` runs AFTER\n    // enqueue so the merged properties reflect EXACTLY what the queue\n    // accepted. The observation carries the three input layers\n    // separately + the merged result so the verifier can prove\n    // caller > super > device precedence on every real track() call,\n    // not just at boot. PASS prints to debug console iff\n    // `logVerifierResults: true`; FAIL prints at WARN + fires\n    // `reportContractFailure(...)` to the reliability channel.\n    // Cheap short-circuit when `disableContractAssertions: true`.\n    //\n    // mergedProperties is observed PRE-SCRUB (`enriched`), not POST-\n    // SCRUB (`finalProperties`). The contract enforced here is\n    // \"device < super < caller\" — i.e., which LAYER's value wins per\n    // key. PII scrub runs AFTER merge and intentionally mutates\n    // strings (redacts emails, IPs, card numbers) and re-clones\n    // arrays + plain objects. Strict-equal vs `finalProperties`\n    // therefore produces false positives on any caller-supplied\n    // compound value: `caller.frames === finalProperties.frames` is\n    // `false` because scrub's `Array.map` returns a fresh array, even\n    // when contents are unchanged. Founder caught this on\n    // 2026-05-28 — a real `crossdeck.error` track() carrying\n    // `frames: [...]` fired the hot-path FAIL on\n    // `super-property-merge-precedence`, masking real bugs in the\n    // dashboard. Scrub correctness is verified separately; this\n    // verifier's job is solely merge-layer precedence.\n    // ────────────────────────────────────────────────────────────────\n    if (this.verifiers && this.verifierReporter && this.verifierCtx) {\n      // Cast the four input layers to the verifier's open\n      // Record<string, unknown> view. DeviceInfo / EventProperties are\n      // narrowed-string-key records at runtime; the verifier only\n      // reads keys/values, never writes, so the widen is sound. Done\n      // here, not in the verifier signature, so the SDK call site\n      // keeps the strictly-typed source values for refactors.\n      runOnTrack(this.verifiers, this.verifierReporter, this.verifierCtx, {\n        callerProperties: validation.properties as Record<string, unknown>,\n        superProperties: supers as Record<string, unknown>,\n        deviceProperties: s.deviceInfo as unknown as Record<string, unknown>,\n        mergedProperties: enriched as Record<string, unknown>,\n      });\n    }\n\n    // ----- Breadcrumb emission -----\n    // Every analytics event becomes a breadcrumb so error reports\n    // carry the context of what the user was doing just before the\n    // crash. Don't emit a breadcrumb for error events themselves\n    // (would be circular) or webvitals events (noise — they always\n    // fire on every page).\n    if (!isError && !isWebVital) {\n      const category = name.startsWith(\"page.\")\n        ? \"navigation\"\n        : name.startsWith(\"element.\") || name === \"session.started\"\n          ? \"ui.click\"\n          : \"custom\";\n      s.breadcrumbs.add({\n        timestamp: event.timestamp,\n        category,\n        message: name,\n        // Strip the device-info / session bloat from the breadcrumb\n        // payload — only the caller-supplied properties belong in\n        // the user-readable trail.\n        data: properties ? { ...properties } : undefined,\n      });\n    }\n  }\n\n  /**\n   * Force-flush queued events. Useful to call from page-unload handlers.\n   *\n   * Pass `{ keepalive: true }` from terminal handlers (pagehide /\n   * visibilitychange→hidden / beforeunload). The browser keeps the\n   * request alive after the page tears down, so the final batch\n   * actually lands instead of being cancelled with the unload.\n   *\n   * NorthStar §4: standard method name across all Crossdeck SDKs.\n   */\n  async flush(options: { keepalive?: boolean } = {}): Promise<void> {\n    const s = this.requireStarted();\n    await s.events.flush(options);\n  }\n\n  /** @deprecated Use `flush()` instead. NorthStar §4 standardised the name. */\n  async flushEvents(): Promise<void> {\n    return this.flush();\n  }\n\n  /**\n   * Forward purchase evidence to the backend for verification + entitlement\n   * projection. NorthStar §4 + §13 canonical name.\n   *\n   * Today the web SDK only supports Apple StoreKit 2 forwarding (web apps\n   * that sit alongside an iOS app). Stripe doesn't need this method —\n   * Stripe webhooks deliver evidence server-side without a client round-trip.\n   */\n  async syncPurchases(input: {\n    rail?: \"apple\";\n    signedTransactionInfo: string;\n    signedRenewalInfo?: string;\n    appAccountToken?: string;\n  }): Promise<PurchaseResult> {\n    const s = this.requireStarted();\n    if (!input.signedTransactionInfo) {\n      throw new CrossdeckError({\n        type: \"invalid_request_error\",\n        code: \"missing_signed_transaction_info\",\n        message: \"syncPurchases requires a signedTransactionInfo string from StoreKit 2.\",\n      });\n    }\n    // Spread input FIRST so the explicit `rail` default below WINS.\n    // Pre-fix order was `{ rail: input.rail ?? \"apple\", ...input }`\n    // — but `...input` runs LAST and overrides the default with the\n    // caller's literal `rail` key, including the case where the\n    // caller passes `rail: undefined` explicitly (TypeScript treats\n    // an undefined-typed property as \"key present\"). Reversing the\n    // order so the default sits last fixes both the explicit-undefined\n    // case AND the omitted-key case in one line.\n    const rail = input.rail ?? \"apple\";\n    const body = { ...input, rail };\n    // Phase 2.2 bank-grade contract: deterministic Idempotency-Key\n    // from the body. Same input → same key → backend short-\n    // circuits with idempotent_replay: true on retry.\n    const idempotencyKey = deriveIdempotencyKeyForPurchase(body);\n\n    // ────────────────────────────────────────────────────────────\n    // Hot-path verifier: idempotency-key-deterministic re-derives the\n    // key from the same input and compares against what the SDK\n    // computed. Identical → contract honoured. Drift → bug.\n    // ────────────────────────────────────────────────────────────\n    if (this.verifiers && this.verifierReporter && this.verifierCtx) {\n      const stableId =\n        rail === \"apple\"\n          ? (body as { signedTransactionInfo?: string }).signedTransactionInfo ?? \"\"\n          : (body as { purchaseToken?: string }).purchaseToken ?? \"\";\n      runOnSyncPurchases(\n        this.verifiers,\n        this.verifierReporter,\n        this.verifierCtx,\n        {\n          rail,\n          stableIdentifier: stableId,\n          derivedKey: idempotencyKey,\n        },\n      );\n    }\n\n    const result = await s.http.request<PurchaseResult>(\"POST\", \"/purchases/sync\", {\n      body,\n      idempotencyKey,\n    });\n    s.identity.setCrossdeckCustomerId(result.crossdeckCustomerId);\n    s.entitlements.setFromList(result.entitlements);\n    // Phase 3.5 (v1.4.0) — emit purchase.completed so manual\n    // syncPurchases callers show up on the same funnel as the\n    // Swift/Android auto-track path. Schema mirrors the native\n    // auto-track shape so cross-platform funnels reconcile on\n    // event name + the rail/productId fields. Manual paths don't\n    // see the StoreKit Transaction so transactionId/purchaseDate\n    // are absent — funnel queries that need them stay native-\n    // auto-track only.\n    try {\n      const sourceProductId = result.entitlements[0]?.source.productId;\n      const sourceSubscriptionId = result.entitlements[0]?.source.subscriptionId;\n      const props: Record<string, unknown> = { rail };\n      if (sourceProductId) props.productId = sourceProductId;\n      if (sourceSubscriptionId) props.subscriptionId = sourceSubscriptionId;\n      if (result.idempotent_replay) props.idempotent_replay = true;\n      this.track(\"purchase.completed\", props);\n    } catch {\n      // track() throws only on invalid name (we control it) — defensive.\n    }\n    s.debug.emit(\n      \"sdk.purchase_evidence_sent\",\n      \"StoreKit transaction forwarded. Waiting for backend verification.\",\n      { rail: input.rail ?? \"apple\" },\n    );\n    return result;\n  }\n\n  /** @deprecated Use `syncPurchases()` instead. NorthStar §4 standardised the name. */\n  async purchaseApple(input: {\n    signedTransactionInfo: string;\n    signedRenewalInfo?: string;\n    appAccountToken?: string;\n  }): Promise<PurchaseResult> {\n    return this.syncPurchases({ rail: \"apple\", ...input });\n  }\n\n  /**\n   * Toggle verbose diagnostic logging — NorthStar §16. When enabled, the\n   * SDK emits a fixed vocabulary of debug signals to console.info that the\n   * dashboard's onboarding checklist can also surface as live events.\n   */\n  setDebugMode(enabled: boolean): void {\n    const s = this.requireStarted();\n    s.debug.enabled = enabled;\n    if (enabled) {\n      s.debug.emit(\n        \"sdk.configured\",\n        `Debug mode enabled for ${s.options.appId} in ${s.options.environment} mode.`,\n        { appId: s.options.appId, environment: s.options.environment },\n      );\n    }\n  }\n\n  /**\n   * Send the boot heartbeat. Called automatically by start() unless\n   * autoHeartbeat:false. Safe to call manually as a \"we're still here\" ping.\n   */\n  async heartbeat(): Promise<HeartbeatResponse> {\n    const s = this.requireStarted();\n    const result = await s.http.request<HeartbeatResponse>(\"GET\", \"/sdk/heartbeat\");\n    // Capture clock skew at the SAME instant on both sides. The\n    // `serverTime` field is the only authoritative source the SDK has\n    // for \"what does the backend think the time is\" — used in\n    // diagnostics() so a wrong-system-clock problem surfaces before\n    // it silently shifts a day of analytics.\n    if (typeof result?.serverTime === \"number\" && Number.isFinite(result.serverTime)) {\n      s.lastServerTime = result.serverTime;\n      s.lastClientTime = Date.now();\n    }\n    return result;\n  }\n\n  /**\n   * Wipe persisted identity + entitlement cache. Use on logout. The\n   * next pre-login session generates a fresh anonymousId and starts a\n   * new identity-graph entry.\n   */\n  reset(): void {\n    if (!this.state) return;\n    // Server-derived milestone: emit `user.signed_out` BEFORE we wipe\n    // identity. The track() call enqueues the event with the current\n    // developerUserId/cdcust stamped on it; the subsequent reset of\n    // the identity store happens locally only — the queued event\n    // already left with the right identity. The dashboard's Activity\n    // stream therefore shows \"Wes signed out\" rather than an\n    // anonymous orphan event. Skipped if there's no developerUserId\n    // (calling reset on a never-identified anonymous device is a no-op\n    // semantically — there was nothing to \"sign out\" of).\n    if (this.state.developerUserId) {\n      try {\n        this.track(\"user.signed_out\", { auto: true });\n      } catch {\n        // track() throws only on invalid name — never for a literal we control.\n        // Defensive catch keeps reset() bulletproof for logout flows.\n      }\n    }\n    // Tear down + reinstall the auto-tracker so the new session belongs\n    // to the new identity, not the old one. Unload-flush listeners stay\n    // installed across reset — they're tied to the SDK lifetime, not\n    // the identity lifetime. Web Vitals stay attached too — their\n    // observers are per-page-life, not per-identity.\n    this.state.autoTracker?.uninstall();\n    // Only churn the anonymousId on a REAL logout (there was an identified\n    // user). Skipping the re-mint when anonymous is the fragmentation fix:\n    // auth-mirroring that fires reset() on every anonymous page load\n    // (onAuthStateChanged → else reset()) must NOT re-mint the anon id per\n    // page — that fragments one visitor into a new person per navigation\n    // (CD-135 dogfood; hardened here so no customer trips the same wire).\n    // Everything else below still clears this session's accumulated state.\n    if (this.state.developerUserId) this.state.identity.reset();\n    // Clear the read-cost actor too: from here, browser reads attribute to no\n    // user (the next session re-identifies). Matches the entitlement wipe below.\n    bridgeReadCost({ actor: undefined });\n    // Logout-grade wipe: removes EVERY per-user entitlement slot on\n    // this device (layer (c) of the v1.4.0 isolation fix). A shared\n    // device can never leave another user's entitlements readable\n    // after a logout.\n    this.state.entitlements.clearAll();\n    this.state.events.reset();\n    // Super properties + groups are identity-scoped — clear on logout\n    // so a fresh anonymous session doesn't inherit the previous user's\n    // plan/role/group context.\n    this.state.superProps.clear();\n    // Breadcrumbs + error context belong to the old session; wipe so\n    // a fresh post-logout error report doesn't carry the previous\n    // user's debugging trail. The ErrorTracker stays installed across\n    // reset — its listeners are page-life-scoped, not identity-scoped.\n    this.state.breadcrumbs.clear();\n    this.state.errorContext = {};\n    this.state.errorTags = {};\n    this.state.developerUserId = null;\n    // Null clock-skew snapshot on reset — these values belong to the\n    // pre-logout session. Pre-fix `diagnostics().clock.skewMs` for the\n    // next user kept reporting the prior session's skew until the\n    // next heartbeat completed (audit P1 #17). The next heartbeat\n    // repopulates with the new instant.\n    this.state.lastServerTime = null;\n    this.state.lastClientTime = null;\n    if (this.state.autoTracker) {\n      const tracker = new AutoTracker(this.state.options.autoTrack, (name, props) =>\n        this.track(name, props),\n      );\n      this.state.autoTracker = tracker;\n      tracker.install();\n    }\n  }\n\n  /**\n   * Diagnostic: current state + queue stats. Useful for the dashboard's\n   * heartbeat row and debugging in dev.\n   *\n   * Returns a stable shape regardless of whether start() has been called —\n   * callers don't need to narrow on `started` to access `events` or\n   * `entitlements`. Pre-start values are sensible empties.\n   */\n  diagnostics(): Diagnostics {\n    if (!this.state) {\n      return {\n        started: false,\n        anonymousId: null,\n        crossdeckCustomerId: null,\n        developerUserId: null,\n        sdkVersion: null,\n        baseUrl: null,\n        clock: { lastServerTime: null, lastClientTime: null, skewMs: null },\n        entitlements: { count: 0, lastUpdated: 0, stale: false, listenerErrors: 0 },\n        events: {\n          buffered: 0,\n          dropped: 0,\n          inFlight: 0,\n          lastFlushAt: 0,\n          lastError: null,\n          consecutiveFailures: 0,\n          nextRetryAt: null,\n        },\n      };\n    }\n    const s = this.state;\n    const skewMs =\n      s.lastServerTime !== null && s.lastClientTime !== null\n        ? s.lastClientTime - s.lastServerTime\n        : null;\n    return {\n      started: true,\n      anonymousId: s.identity.anonymousId,\n      crossdeckCustomerId: s.identity.crossdeckCustomerId,\n      developerUserId: s.developerUserId,\n      sdkVersion: s.options.sdkVersion,\n      baseUrl: s.options.baseUrl,\n      clock: {\n        lastServerTime: s.lastServerTime,\n        lastClientTime: s.lastClientTime,\n        skewMs,\n      },\n      entitlements: {\n        count: s.entitlements.list().length,\n        lastUpdated: s.entitlements.freshness,\n        stale: s.entitlements.isStale,\n        listenerErrors: s.entitlements.listenerErrors,\n      },\n      events: s.events.getStats(),\n    };\n  }\n\n  /**\n   * The stable reference to hand Stripe at checkout so the resulting\n   * purchase attributes to THIS person. Stamp it on the Checkout Session\n   * as `metadata.crossdeck_ref` (see docs/connect-stripe) — the platform\n   * webhook reads it back, validates it, and attaches the subscription to\n   * the right customer instead of stranding it on an anonymous record.\n   *\n   * Returns the strongest identifier the SDK currently holds, in the same\n   * precedence the server resolves by:\n   *   crossdeckCustomerId (cdcust_…) > developerUserId > anonymousId\n   * anonymousId is always present, so this always returns a usable,\n   * non-empty reference — even before identify(). Identify the user as\n   * early as you can, though, so the reference is their stable identity\n   * and not a per-device anon id.\n   */\n  getCheckoutReference(): string {\n    const s = this.requireStarted();\n    return (\n      s.identity.crossdeckCustomerId ??\n      s.developerUserId ??\n      s.identity.anonymousId\n    );\n  }\n\n  /**\n   * The device-scoped anonymous id the SDK minted on first boot and persists\n   * across launches (stable until reset()). Public accessor so a server-to-\n   * server flow or a block/suspension gate can pass the device identity to\n   * POST /v1/resolve without reaching into private storage.\n   *\n   * Returns `null` BEFORE init() — there is no anon id yet, and a gate that\n   * fires during early app boot should get a clean falsy, not a throw. (This is\n   * deliberately softer than getCheckoutReference(), which requires init.)\n   *\n   * Note: /v1/resolve also accepts a VERIFIED identity (userId + idToken)\n   * without an anonymousId, and that path is higher-trust — prefer it where the\n   * user is authenticated.\n   */\n  getAnonymousId(): string | null {\n    return this.state ? this.state.identity.anonymousId : null;\n  }\n\n  /**\n   * **Crossdeck Trust** — human-proof at your signup, native to the SDK.\n   *\n   * `Crossdeck.trust.panel({ target, onToken })` renders the branded, un-restylable\n   * Trust panel (the same cross-origin iframe on every install) and mints a\n   * single-use attestation. Hand the token to your server and verify it at the gate\n   * (`crossdeck.trust.gate(...)` in @cross-deck/node). The publishable key is taken\n   * from your `init()` — you don't pass it again.\n   *\n   * Fail-open by contract: if the panel can't mint (adblocker, offline, our outage),\n   * `ready` resolves with `{ token: null }` and your signup proceeds — the server\n   * scores the missing token. It never throws and never blocks your form.\n   *\n   * @example\n   * const { ready } = Crossdeck.trust.panel({ target: \"#cd-trust\" });\n   * const result = await ready;                 // { token, expiresAt } | { token: null }\n   * await createUser({ email, cdTrustToken: result.token });\n   */\n  get trust(): CrossdeckTrustNamespace {\n    const publicKey = this.state ? this.state.options.publicKey : \"\";\n    return {\n      // An EXPLICIT input.publicKey always wins (no init required); otherwise fall\n      // back to the key from init(). This makes the panel immune to init order and to\n      // separate-bundle singletons — pass the key and it always works.\n      panel: (input: TrustPanelInput): TrustPanelHandle =>\n        mountTrustPanel({ ...input, publicKey: input.publicKey || publicKey }),\n    };\n  }\n\n  // ---------- private helpers ----------\n\n  private requireStarted(): InternalState {\n    if (!this.state) {\n      throw new CrossdeckError({\n        type: \"configuration_error\",\n        code: \"not_initialized\",\n        message:\n          \"Call Crossdeck.init({ appId, publicKey, environment }) before any other method.\",\n      });\n    }\n    return this.state;\n  }\n\n  /**\n   * Build the identity query for /v1/entitlements. Priority:\n   *   crossdeckCustomerId > developerUserId > anonymousId\n   * — matches the resolveCrossdeckCustomerId precedence on the server.\n   */\n  private identityQueryParams(): Record<string, string | undefined> {\n    const s = this.requireStarted();\n    if (s.identity.crossdeckCustomerId) {\n      return { customerId: s.identity.crossdeckCustomerId };\n    }\n    if (s.developerUserId) return { userId: s.developerUserId };\n    return { anonymousId: s.identity.anonymousId };\n  }\n\n  /**\n   * Embed every known identity axis on the event. Earlier this returned\n   * just the highest-priority hint (cdcust → developerUserId → anonymousId)\n   * to keep payloads small, but that leaked into analytics: once a user\n   * was logged in, every subsequent page.viewed shipped without\n   * anonymousId, and `uniqExact(anonymous_id)` on the warehouse side\n   * counted 0 visitors for the entire authenticated app.\n   *\n   * Bank-grade rule: the server is the single source of truth on\n   * dedup. Send everything we know; let CH count by whichever axis\n   * matches the question. Each field is at most 32 bytes — sending\n   * three on every event costs ~80 bytes per request, which is\n   * trivial compared to the analytics correctness it buys.\n   */\n  private identityHintForEvent(): Pick<\n    QueuedEvent,\n    \"developerUserId\" | \"anonymousId\" | \"crossdeckCustomerId\"\n  > {\n    const s = this.requireStarted();\n    const hint: Pick<QueuedEvent, \"developerUserId\" | \"anonymousId\" | \"crossdeckCustomerId\"> = {\n      anonymousId: s.identity.anonymousId,\n    };\n    if (s.developerUserId) hint.developerUserId = s.developerUserId;\n    if (s.identity.crossdeckCustomerId) {\n      hint.crossdeckCustomerId = s.identity.crossdeckCustomerId;\n    }\n    return hint;\n  }\n\n  private mintEventId(): string {\n    const ts = Date.now().toString(36);\n    return `evt_${ts}${randomChars(8)}`;\n  }\n}\n\n/**\n * The default singleton lives in `./singleton` (registry-backed, shared across\n * every entry point). It is intentionally NOT exported from here: this module\n * owns only the CLASS, so the dependency graph is one-way (singleton → crossdeck)\n * with no cycle. A cyclic re-export made the bundler run `new CrossdeckClient()`\n * before the class was initialised → \"CrossdeckClient is not a constructor\" at\n * runtime. Import the singleton as `import { Crossdeck } from \"@cross-deck/web\"`.\n * See singleton.ts (CD-155 duplicate-singleton defect).\n */\n\n/**\n * Normalise the autoTrack option to a fully-resolved AutoTrackConfig.\n *   undefined      → all defaults (everything on in browsers)\n *   true           → all on (same as defaults)\n *   false          → all off\n *   { sessions:false } → defaults for unspecified flags, override for specified ones\n */\n/**\n * Derive the env from a publishable key prefix.\n *   cd_pub_test_… → \"sandbox\"\n *   cd_pub_live_… → \"production\"\n *   cd_pub_…       → null (legacy / unprefixed — env can't be inferred)\n *\n * We treat the legacy form as \"no opinion\" so the developer's explicit\n * `environment` declaration always wins for unprefixed keys (e.g. dev\n * fixture keys in tests).\n */\nfunction inferEnvFromKey(publicKey: string): Environment | null {\n  if (publicKey.startsWith(\"cd_pub_test_\")) return \"sandbox\";\n  if (publicKey.startsWith(\"cd_pub_live_\")) return \"production\";\n  return null;\n}\n\n/**\n * Detect whether the SDK is booting on a local-development hostname.\n * Triggers the SDK's silent dev-mode shutoff (no network calls, all\n * state local) so a developer running their app locally with a live\n * key cannot accidentally pollute their production analytics.\n *\n * Match list:\n *   - localhost / 127.0.0.1     traditional local dev\n *   - *.local                   mDNS / Bonjour (e.g. mymac.local)\n *   - 10.x.x.x                  RFC1918 class A (private)\n *   - 192.168.x.x               RFC1918 class C (home / office LAN)\n *   - 172.16-31.x.x             RFC1918 class B\n *\n * Vercel preview URLs, Netlify branch deploys, ngrok tunnels — those\n * resolve to real domains and stay on whatever the key says. They're\n * not \"local,\" they're \"deployed under a temporary domain.\"\n *\n * Returns false in non-browser contexts (Node, Workers) — there's no\n * window.location to inspect, and a Node consumer that wired up the\n * SDK is presumably running server-side with a deliberate config.\n */\nfunction isLocalHostname(): boolean {\n  // Testing escape hatch — E2E suites + smoke pages need to exercise\n  // the real wire shape from a non-deployed domain (Playwright runs\n  // on 127.0.0.1). Setting `window.__CROSSDECK_FORCE_LIVE__ = true`\n  // before init() returns false here so the SDK fires real fetches.\n  // Not documented in SDK_TRUTH because it's internal-only — real\n  // consumers should never set this.\n  const w = (globalThis as {\n    window?: { location?: { hostname?: string }; __CROSSDECK_FORCE_LIVE__?: boolean };\n  }).window;\n  if (w?.__CROSSDECK_FORCE_LIVE__ === true) return false;\n  const hostname = w?.location?.hostname;\n  if (!hostname) return false;\n  if (hostname === \"localhost\" || hostname === \"127.0.0.1\") return true;\n  // 0.0.0.0 is the default bind address for webpack-dev-server /\n  // vite dev server when the team is on a shared LAN dev. Audit P2:\n  // pre-fix this slipped through and polluted live analytics for\n  // anyone running those configs.\n  if (hostname === \"0.0.0.0\") return true;\n  if (hostname === \"::1\" || hostname === \"[::1]\") return true;\n  // IPv6 link-local: fe80::/10. Devices on the same network segment\n  // (browser inspector debugging an iPad via Safari Web Inspector\n  // over USB lands here). Conservative match — only the link-local\n  // prefix, not the broader ULA fc00::/7 range which is intended for\n  // private routed networks and shouldn't auto-quiet by default.\n  if (/^\\[?fe80::/i.test(hostname)) return true;\n  if (hostname.endsWith(\".local\")) return true;\n  if (/^10\\./.test(hostname)) return true;\n  if (/^192\\.168\\./.test(hostname)) return true;\n  if (/^172\\.(1[6-9]|2\\d|3[0-1])\\./.test(hostname)) return true;\n  return false;\n}\n\nfunction resolveAutoTrack(\n  input: CrossdeckOptions[\"autoTrack\"],\n): AutoTrackConfig {\n  if (input === false) {\n    return {\n      sessions: false,\n      pageViews: false,\n      deviceInfo: false,\n      clicks: false,\n      webVitals: false,\n      errors: false,\n      stripUrlParams: false,\n      wrapHistory: false,\n    };\n  }\n  if (input === undefined || input === true) {\n    return { ...DEFAULT_AUTO_TRACK };\n  }\n  return {\n    sessions: input.sessions ?? DEFAULT_AUTO_TRACK.sessions,\n    pageViews: input.pageViews ?? DEFAULT_AUTO_TRACK.pageViews,\n    deviceInfo: input.deviceInfo ?? DEFAULT_AUTO_TRACK.deviceInfo,\n    clicks: input.clicks ?? DEFAULT_AUTO_TRACK.clicks,\n    webVitals: input.webVitals ?? DEFAULT_AUTO_TRACK.webVitals,\n    errors: input.errors ?? DEFAULT_AUTO_TRACK.errors,\n    stripUrlParams: input.stripUrlParams ?? DEFAULT_AUTO_TRACK.stripUrlParams,\n    wrapHistory: input.wrapHistory ?? DEFAULT_AUTO_TRACK.wrapHistory,\n  };\n}\n\n/**\n * Install browser unload listeners that fire `onUnload` when the page\n * is going away. We listen to all three because each browser/platform\n * is unreliable on at least one of them:\n *   - `pagehide` is the modern, mobile-reliable signal (Safari iOS only\n *     fires this — beforeunload doesn't fire on backgrounding there).\n *   - `visibilitychange → hidden` is the canonical \"tab going to bg\"\n *     signal; bfcache restores re-fire `pagehide`/`pageshow`.\n *   - `beforeunload` is the legacy desktop signal — kept as a belt for\n *     older Chrome/Firefox versions.\n *\n * The handler is idempotent: if the queue is empty, flush() is a no-op,\n * so multiple firings during one unload are harmless.\n *\n * Returns a teardown that removes all three listeners. No-ops in non-\n * browser environments (Node, Web Workers).\n */\nfunction installUnloadFlush(onUnload: () => void): () => void {\n  const w = (globalThis as { window?: Window }).window;\n  const doc = (globalThis as { document?: Document }).document;\n  if (!w || !doc) return () => undefined;\n\n  // try/catch: these fire during page teardown, where in-app browsers\n  // (Instagram/Facebook's `iabjs://`, TikTok, …) hook browser APIs and throw\n  // from their own teardown. `onUnload` is itself fire-and-forget, but guard\n  // the handler boundary so no host-shell teardown throw can reach\n  // window.onerror and be self-reported as the developer's error.\n  const onVisChange = (): void => {\n    try { if (doc.visibilityState === \"hidden\") onUnload(); } catch { /* unload teardown — swallow */ }\n  };\n  const onTerminal = (): void => {\n    try { onUnload(); } catch { /* unload teardown — swallow */ }\n  };\n\n  doc.addEventListener(\"visibilitychange\", onVisChange);\n  w.addEventListener(\"pagehide\", onTerminal);\n  w.addEventListener(\"beforeunload\", onTerminal);\n\n  return () => {\n    doc.removeEventListener(\"visibilitychange\", onVisChange);\n    w.removeEventListener(\"pagehide\", onTerminal);\n    w.removeEventListener(\"beforeunload\", onTerminal);\n  };\n}\n","/**\n * The ONE Crossdeck singleton — shared across every entry point.\n *\n * Why this module exists (the biotree defect, CD-155): `@cross-deck/web` and\n * `@cross-deck/web/react` build as SEPARATE bundles. Each one inlined\n * `crossdeck.ts` — including a bare `export const Crossdeck = new\n * CrossdeckClient()` — so the shipped package contained TWO singletons: one in\n * `index.mjs`, a different object baked into `react.mjs`. A React app that did\n * `Crossdeck.init()` / `identify()` / `getEntitlements()` on the core import\n * warmed instance A, while `useEntitlement()` (react entry) read instance B,\n * which was never initialised — so a paying customer read `false` forever. No\n * error, no warning; a silent duplicate-singleton hazard. In source the import\n * is single; the split is purely a bundling artefact.\n *\n * The fix that CANNOT regress: back the instance with the cross-realm global\n * symbol registry. `Symbol.for(key)` returns the same symbol everywhere, so no\n * matter how many times a bundler duplicates this module, every copy resolves\n * to the SAME instance. This is the same guard PostHog, Segment, and\n * LaunchDarkly use for their React bindings, for this exact reason.\n */\nimport { CrossdeckClient } from \"./crossdeck\";\n\n/** Cross-realm registry key — identical across every bundled copy. */\nconst INSTANCE_KEY = Symbol.for(\"@cross-deck/web:Crossdeck\");\n/** Marks that the singleton module body has already run once in this realm. */\nconst MODULE_EVAL_KEY = Symbol.for(\"@cross-deck/web:singletonModuleEvaluated\");\n\ninterface SingletonRegistry {\n  [INSTANCE_KEY]?: CrossdeckClient;\n  [MODULE_EVAL_KEY]?: boolean;\n}\nconst g = globalThis as typeof globalThis & SingletonRegistry;\n\n// Dev signal: if this module body evaluates more than once, the bundler shipped\n// duplicate copies across entry points (the very condition that spawned the\n// two-instance bug). It's now HARMLESS — the registry below still hands back\n// one instance — but flag it so a bundler misconfiguration can never silently\n// regress into two live singletons again.\nif (\n  g[MODULE_EVAL_KEY] &&\n  typeof process !== \"undefined\" &&\n  process.env?.NODE_ENV !== \"production\" &&\n  typeof console !== \"undefined\"\n) {\n  console.warn(\n    \"[crossdeck] SDK singleton loaded more than once — @cross-deck/web is \" +\n      \"bundled as duplicate copies across entry points. Still ONE instance \" +\n      \"(global registry); dedupe your bundler to trim size.\",\n  );\n}\ng[MODULE_EVAL_KEY] = true;\n\n/**\n * The default singleton — most consumers want one SDK instance per app. Every\n * entry point (`@cross-deck/web`, `@cross-deck/web/react`, `.../vue`) resolves\n * to THIS object. Creating extra instances is still fine for advanced use:\n * just `new CrossdeckClient()`.\n */\nexport const Crossdeck: CrossdeckClient = (g[INSTANCE_KEY] ??=\n  new CrossdeckClient());\n","// AUTO-GENERATED by scripts/emit-bundled-contracts.mjs — DO NOT EDIT.\n// Source of truth: contracts/**/*.json at the monorepo root.\n// Regenerated on every `npm run build`. The public API in\n// src/contracts.ts reads from this file.\n\nimport type { Contract } from \"./contracts\";\n\nexport const BUNDLED_IN = \"@cross-deck/web@1.14.1\" as const;\nexport const SDK_VERSION = \"1.14.1\" as const;\n\nexport const BUNDLED_CONTRACTS: readonly Contract[] = Object.freeze([\n  {\n    \"id\": \"contract-failed-payload-schema-lock\",\n    \"pillar\": \"diagnostics\",\n    \"status\": \"enforced\",\n    \"claim\": \"The `crossdeck.contract_failed` event payload contains ONLY the named diagnostic fields and never any end-user personal data. The wire shape is fixed — adding a new field requires (1) a pull request that updates this contract's `allowedFields` set, (2) a Privacy Policy §6 amendment, and (3) the Customer Disclosure Template / SDK Data Collection Reference §B updates. Per-SDK assertion tests enforce the field set on every release. The `verification_phase` field is a categorical bucket — values are restricted to `boot` (the SDK self-test ran on Crossdeck.start) or `hot_path` (a verifier observed a real customer-triggered operation). The categorical nature is what preserves the diagnostic-only-not-personal classification. This is the structural guarantee that backs the independent-controller lawful basis in the Privacy Policy: the payload remains diagnostic-only, not personal, so the legitimate-interest analysis stays valid as the SDK evolves.\",\n    \"appliesTo\": [\n      \"web\",\n      \"node\",\n      \"swift\",\n      \"android\",\n      \"react-native\"\n    ],\n    \"allowedFields\": {\n      \"required\": [\n        \"contract_id\",\n        \"sdk_version\",\n        \"sdk_platform\",\n        \"failure_reason\",\n        \"run_context\",\n        \"run_id\"\n      ],\n      \"optional\": [\n        \"test_file\",\n        \"test_name\",\n        \"device_class\",\n        \"verification_phase\"\n      ],\n      \"forbidden\": [\n        \"anonymousId\",\n        \"developerUserId\",\n        \"crossdeckCustomerId\",\n        \"email\",\n        \"ip\",\n        \"user_agent\",\n        \"message\",\n        \"stack\",\n        \"stack_trace\",\n        \"frames\",\n        \"exception_message\",\n        \"url\",\n        \"path\",\n        \"screen\",\n        \"title\",\n        \"label\",\n        \"text\",\n        \"ariaLabel\",\n        \"accessibilityLabel\",\n        \"contentDescription\",\n        \"session_id\",\n        \"sessionId\"\n      ]\n    },\n    \"transport\": \"Telemetry is single-fire to the Crossdeck reliability endpoint only — NOT the customer's appId. The customer's track() pipeline never carries `crossdeck.*` events; the customer's dashboard never shows individual contract failures. Operational telemetry flows one-way to the Crossdeck operations team for SDK reliability purposes (legitimate interest, independent-controller flow per Privacy Policy §6). The reliability endpoint is hardcoded at SDK build time; the publishable key for the reliability project is embedded as a constant and rejects writes that don't match the schema.\",\n    \"codeRef\": [\n      \"sdks/web/src/crossdeck.ts\",\n      \"sdks/node/src/crossdeck-server.ts\",\n      \"sdks/swift/Sources/Crossdeck/Crossdeck.swift\",\n      \"sdks/swift/Sources/Crossdeck/_DiagnosticTelemetry.swift\",\n      \"sdks/android/crossdeck/src/main/kotlin/com/crossdeck/Crossdeck.kt\",\n      \"sdks/android/crossdeck/src/main/kotlin/com/crossdeck/_DiagnosticTelemetry.kt\",\n      \"sdks/react-native/src/crossdeck.ts\",\n      \"backend/src/api/v1-sdk-diagnostic.ts\",\n      \"sdks/web/src/_diagnostic-telemetry.ts\",\n      \"sdks/node/src/_diagnostic-telemetry.ts\",\n      \"sdks/react-native/src/_diagnostic-telemetry.ts\"\n    ],\n    \"testRef\": [\n      {\n        \"file\": \"sdks/web/tests/contract-failed-schema-lock.test.ts\",\n        \"name\": \"reportContractFailure payload conforms to schema-lock\"\n      },\n      {\n        \"file\": \"sdks/node/tests/contract-failed-schema-lock.test.ts\",\n        \"name\": \"reportContractFailure payload conforms to schema-lock\"\n      },\n      {\n        \"file\": \"sdks/swift/Tests/CrossdeckTests/ContractFailedSchemaLockTests.swift\",\n        \"name\": \"test_reportContractFailure_payloadFieldsAreInAllowList\"\n      },\n      {\n        \"file\": \"sdks/swift/Tests/CrossdeckTests/ContractFailedSchemaLockTests.swift\",\n        \"name\": \"test_reportContractFailure_doesNotEnterCustomerTrackPipeline\"\n      },\n      {\n        \"file\": \"sdks/android/crossdeck/src/test/kotlin/com/crossdeck/ContractFailedSchemaLockTest.kt\",\n        \"name\": \"reportContractFailure payload conforms to schema-lock\"\n      },\n      {\n        \"file\": \"sdks/android/crossdeck/src/test/kotlin/com/crossdeck/ContractFailedSchemaLockTest.kt\",\n        \"name\": \"reportContractFailure does not enter customer track pipeline\"\n      },\n      {\n        \"file\": \"sdks/react-native/tests/contract-failed-schema-lock.test.ts\",\n        \"name\": \"reportContractFailure payload conforms to schema-lock\"\n      },\n      {\n        \"file\": \"backend/tests/unit/v1-sdk-diagnostic.test.ts\",\n        \"name\": \"forbidden fields are enumerated in the schema-lock contract\"\n      },\n      {\n        \"file\": \"backend/tests/unit/v1-sdk-diagnostic.test.ts\",\n        \"name\": \"required fields are enumerated in the schema-lock contract\"\n      },\n      {\n        \"file\": \"backend/tests/unit/v1-sdk-diagnostic.test.ts\",\n        \"name\": \"regression guard: never returns a raw IP\"\n      },\n      {\n        \"file\": \"backend/tests/unit/v1-sdk-diagnostic.test.ts\",\n        \"name\": \"verification_phase is in the optional field set\"\n      }\n    ],\n    \"registeredAt\": \"2026-05-27\",\n    \"firstRegisteredIn\": \"Diagnostic telemetry single-fire + schema-lock — independent-controller flow\",\n    \"privacyReferences\": [\n      \"legal/privacy/index.html#sdk-diagnostic\",\n      \"legal/customer-disclosure/index.html#flow-b\",\n      \"legal/security/index.html#diagnostic\",\n      \"legal/sdk-data/index.html#b-diagnostic\"\n    ],\n    \"bundledIn\": \"@cross-deck/web@1.14.1\",\n    \"runtimeVerified\": true\n  },\n  {\n    \"id\": \"error-envelope-shape\",\n    \"pillar\": \"errors\",\n    \"status\": \"enforced\",\n    \"claim\": \"Every v1 REST endpoint returns errors in a Stripe-shape envelope: `{ error: { type, code, message, request_id } }` where `type` is one of authentication_error / permission_error / invalid_request_error / rate_limit_error / internal_error (the wire vocabulary in backend/src/api/v1-errors.ts ApiErrorType). HTTP status parity: invalid_request_error → 400, authentication_error → 401, permission_error → 403, rate_limit_error → 429, internal_error → 500. SDK-side clients parse this shape via `crossdeckErrorFromResponse` (Web/Node/RN) / `crossdeckErrorFrom(response:)` (Swift) / `crossdeckErrorFromResponse` (Android) and surface the request_id verbatim so support traces are end-to-end joinable. Firebase callable endpoints (managed-keys / dashboard auth) use the Firebase HttpsError envelope instead — this contract applies to REST /v1/* only.\",\n    \"appliesTo\": [\n      \"web\",\n      \"node\",\n      \"react-native\",\n      \"swift\",\n      \"android\",\n      \"backend\"\n    ],\n    \"codeRef\": [\n      \"backend/src/api/v1-errors.ts\",\n      \"sdks/web/src/errors.ts\",\n      \"sdks/node/src/errors.ts\",\n      \"sdks/react-native/src/errors.ts\",\n      \"sdks/swift/Sources/Crossdeck/Errors.swift\",\n      \"sdks/android/crossdeck/src/main/kotlin/com/crossdeck/Errors.kt\"\n    ],\n    \"testRef\": [\n      {\n        \"file\": \"sdks/swift/Tests/CrossdeckTests/ErrorsTests.swift\",\n        \"name\": \"test_errorEnvelope_fallsBackOnGarbageBody\"\n      },\n      {\n        \"file\": \"sdks/swift/Tests/CrossdeckTests/ErrorsTests.swift\",\n        \"name\": \"test_errorEnvelope_reads_XRequestId_fallback\"\n      },\n      {\n        \"file\": \"sdks/android/crossdeck/src/test/kotlin/com/crossdeck/ErrorTypeWireVocabTest.kt\",\n        \"name\": \"backend 500 response parses to INTERNAL_ERROR\"\n      }\n    ],\n    \"registeredAt\": \"2026-05-26\",\n    \"firstRegisteredIn\": \"bank-grade reconciliation v1.4.0 — phase 8 (codifies existing contract)\",\n    \"bundledIn\": \"@cross-deck/web@1.14.1\",\n    \"runtimeVerified\": true\n  },\n  {\n    \"id\": \"flush-interval-parity\",\n    \"pillar\": \"analytics\",\n    \"status\": \"enforced\",\n    \"claim\": \"Every Crossdeck SDK defaults its event-queue flush interval to 2000ms — the Stripe-adjacent industry norm. Pre-v1.4.0 the defaults disagreed (Web/Node 1500ms; RN/Swift/Android 5000ms), so cross-platform funnels saw events landing at different cadences. Per-instance override stays — call sites can still tune it freely.\",\n    \"appliesTo\": [\n      \"web\",\n      \"node\",\n      \"react-native\",\n      \"swift\",\n      \"android\"\n    ],\n    \"codeRef\": [\n      \"sdks/web/src/crossdeck.ts\",\n      \"sdks/node/src/crossdeck-server.ts\",\n      \"sdks/react-native/src/crossdeck.ts\",\n      \"sdks/swift/Sources/Crossdeck/EventQueue.swift\",\n      \"sdks/android/crossdeck/src/main/kotlin/com/crossdeck/EventQueue.kt\"\n    ],\n    \"testRef\": [\n      {\n        \"file\": \"sdks/swift/Sources/Crossdeck/EventQueue.swift\",\n        \"name\": \"flushIntervalMs: Int = 2_000\"\n      },\n      {\n        \"file\": \"sdks/android/crossdeck/src/main/kotlin/com/crossdeck/EventQueue.kt\",\n        \"name\": \"flushIntervalMs: Long = 2_000L\"\n      },\n      {\n        \"file\": \"sdks/web/src/crossdeck.ts\",\n        \"name\": \"options.eventFlushIntervalMs ?? 2000\"\n      },\n      {\n        \"file\": \"sdks/node/src/crossdeck-server.ts\",\n        \"name\": \"options.eventFlushIntervalMs ?? 2000\"\n      },\n      {\n        \"file\": \"sdks/react-native/src/crossdeck.ts\",\n        \"name\": \"options.eventFlushIntervalMs ?? 2000\"\n      }\n    ],\n    \"registeredAt\": \"2026-05-26\",\n    \"firstRegisteredIn\": \"bank-grade reconciliation v1.4.0 — phase 3.3\",\n    \"bundledIn\": \"@cross-deck/web@1.14.1\",\n    \"runtimeVerified\": true\n  },\n  {\n    \"id\": \"idempotency-key-deterministic\",\n    \"pillar\": \"revenue\",\n    \"status\": \"enforced\",\n    \"claim\": \"syncPurchases() on every SDK derives a deterministic Idempotency-Key from the request body (UUID-shaped SHA-256 of `crossdeck:purchases/sync:<rail>:<jws|token>`). Same input -> same key. Backend short-circuits same-key-same-body retries by returning the cached response (status + body) with `idempotent_replay: true` flag in the body AND `Idempotent-Replayed: true` response header. Same-key-different-body returns 400 `idempotency_key_in_use`. 24-hour TTL matches Stripe. Cache only stores 2xx responses — 4xx/5xx pass through so callers can fix bugs and retry. Helper returns nil/throws on missing identifier (no silent random fallback). Cross-SDK parity is CI-pinned: deriveForPurchase('apple', 'eyJ.jws.sig') MUST equal 'a66b1640-efaf-bb4d-1261-6650033bf111' on every SDK.\",\n    \"appliesTo\": [\n      \"web\",\n      \"node\",\n      \"react-native\",\n      \"swift\",\n      \"android\",\n      \"backend\"\n    ],\n    \"codeRef\": [\n      \"sdks/web/src/idempotency-key.ts\",\n      \"sdks/web/src/crossdeck.ts\",\n      \"sdks/react-native/src/idempotency-key.ts\",\n      \"sdks/react-native/src/crossdeck.ts\",\n      \"sdks/node/src/idempotency-key.ts\",\n      \"sdks/node/src/crossdeck-server.ts\",\n      \"sdks/swift/Sources/Crossdeck/IdempotencyKey.swift\",\n      \"sdks/swift/Sources/Crossdeck/Crossdeck.swift\",\n      \"sdks/android/crossdeck/src/main/kotlin/com/crossdeck/IdempotencyKey.kt\",\n      \"sdks/android/crossdeck/src/main/kotlin/com/crossdeck/Crossdeck.kt\",\n      \"backend/src/lib/idempotency-response-cache.ts\",\n      \"backend/src/api/v1-purchases.ts\"\n    ],\n    \"testRef\": [\n      {\n        \"file\": \"sdks/web/tests/idempotency-key.test.ts\",\n        \"name\": \"cross-SDK oracle — apple JWS pins canonical vector\"\n      },\n      {\n        \"file\": \"sdks/web/tests/idempotency-key.test.ts\",\n        \"name\": \"is deterministic: same body twice -> identical key\"\n      },\n      {\n        \"file\": \"sdks/web/tests/idempotency-key.test.ts\",\n        \"name\": \"same identifier under different rails -> different keys\"\n      },\n      {\n        \"file\": \"sdks/web/tests/idempotency-key.test.ts\",\n        \"name\": \"never silently falls back to a random key on missing identifier\"\n      },\n      {\n        \"file\": \"sdks/react-native/tests/idempotency-key.test.ts\",\n        \"name\": \"is deterministic\"\n      },\n      {\n        \"file\": \"sdks/react-native/tests/idempotency-key.test.ts\",\n        \"name\": \"cross-SDK oracle — apple JWS pins canonical vector\"\n      },\n      {\n        \"file\": \"sdks/node/tests/idempotency-key.test.ts\",\n        \"name\": \"is deterministic\"\n      },\n      {\n        \"file\": \"sdks/node/tests/idempotency-key.test.ts\",\n        \"name\": \"rail namespacing prevents cross-rail collisions\"\n      },\n      {\n        \"file\": \"sdks/node/tests/idempotency-key.test.ts\",\n        \"name\": \"apple JWS produces the canonical pinned UUID across all 5 SDKs\"\n      },\n      {\n        \"file\": \"backend/tests/unit/idempotency-response-cache.test.ts\",\n        \"name\": \"is deterministic for the same input\"\n      },\n      {\n        \"file\": \"backend/tests/unit/idempotency-response-cache.test.ts\",\n        \"name\": \"injects idempotent_replay: true into a JSON object body\"\n      },\n      {\n        \"file\": \"backend/tests/unit/idempotency-response-cache.test.ts\",\n        \"name\": \"matches Stripe's 24-hour idempotency window\"\n      },\n      {\n        \"file\": \"sdks/swift/Tests/CrossdeckTests/IdempotencyKeyTests.swift\",\n        \"name\": \"test_crossSdkOracle_appleJWS\"\n      },\n      {\n        \"file\": \"sdks/swift/Tests/CrossdeckTests/IdempotencyKeyTests.swift\",\n        \"name\": \"test_railNamespacing_preventsCrossRailCollisions\"\n      },\n      {\n        \"file\": \"sdks/swift/Tests/CrossdeckTests/IdempotencyKeyTests.swift\",\n        \"name\": \"test_missingIdentifier_returnsNil\"\n      },\n      {\n        \"file\": \"sdks/android/crossdeck/src/test/kotlin/com/crossdeck/IdempotencyKeyTest.kt\",\n        \"name\": \"cross-SDK oracle for apple JWS\"\n      },\n      {\n        \"file\": \"sdks/android/crossdeck/src/test/kotlin/com/crossdeck/IdempotencyKeyTest.kt\",\n        \"name\": \"rail namespacing prevents cross-rail collisions\"\n      },\n      {\n        \"file\": \"sdks/android/crossdeck/src/test/kotlin/com/crossdeck/IdempotencyKeyTest.kt\",\n        \"name\": \"missing identifier returns null - never silent random fallback\"\n      }\n    ],\n    \"registeredAt\": \"2026-05-26\",\n    \"firstRegisteredIn\": \"bank-grade reconciliation v1.4.0 — phase 2.2.a + 2.2.b + 2.2.c\",\n    \"bundledIn\": \"@cross-deck/web@1.14.1\",\n    \"runtimeVerified\": true\n  },\n  {\n    \"id\": \"init-reentry-drains-prior-queue\",\n    \"pillar\": \"lifecycle\",\n    \"status\": \"enforced\",\n    \"claim\": \"Web + RN init() re-entry drains the prior EventQueue's pending setTimeout BEFORE replacing this.state. Pre-v1.4.0 the teardown handled autoTracker/webVitals/errors/unloadFlush but NOT events, so the prior queue's timer would fire AFTER the state swap — sending old-init events against new-init http + identity references (cross-identity leak during HMR / config swap / multi-tenant SDK shells). The teardown CANNOT call persistent.clear() — the durable queue belongs to the SDK lifetime, not the init() lifetime, and a survived crash mid-flush re-hydrates on the next init.\",\n    \"appliesTo\": [\n      \"web\",\n      \"react-native\"\n    ],\n    \"codeRef\": [\n      \"sdks/web/src/crossdeck.ts\",\n      \"sdks/react-native/src/crossdeck.ts\"\n    ],\n    \"testRef\": [\n      {\n        \"file\": \"sdks/web/tests/init-reentry.test.ts\",\n        \"name\": \"re-init drains the prior queue's pending timer before swapping state\"\n      },\n      {\n        \"file\": \"sdks/web/tests/init-reentry.test.ts\",\n        \"name\": \"re-init does NOT wipe the durable event store\"\n      }\n    ],\n    \"registeredAt\": \"2026-05-26\",\n    \"firstRegisteredIn\": \"bank-grade reconciliation v1.4.0 — phase 5.5\",\n    \"bundledIn\": \"@cross-deck/web@1.14.1\",\n    \"runtimeVerified\": false\n  },\n  {\n    \"id\": \"invalid-input-rejected-natively\",\n    \"pillar\": \"errors\",\n    \"status\": \"enforced\",\n    \"claim\": \"No public SDK API ever crashes the host app, and invalid input never reaches the wire. Invalid input (empty event name, empty userId, out-of-range config such as a non-positive breadcrumb capacity, NaN/Infinity/oversize/cyclic property values) is rejected at the call site WITHOUT a fatal trap. The signalling IDIOM is per-language and intentionally NOT uniform: Web, Node, and React Native THROW a typed CrossdeckError synchronously (code missing_event_name / missing_user_id / invalid_request_error) — a normal, catchable JavaScript convention where an uncaught throw logs and the app continues; Swift DROPS with a debug-log signal (track_dropped / identify_dropped) to match its non-throwing fire-and-forget surface, and exposes the throwing equivalent only via identifyAndWait(userId:). What is UNIFORM is the invariant, not the mechanism: every SDK rejects the same inputs, no public fire-and-forget API contains a fatalError / assertionFailure / precondition reachable from customer input, and no rejected input is enqueued or transmitted. Swift additionally proves this in BOTH debug and release configuration, because precondition fires under -O while assertionFailure does not. The bug class this contract closes was never the per-language difference — it was the UNDECLARED difference: each SDK's public API documentation must state its own semantics explicitly (TS docs: 'throws on empty name'; Swift docs: 'drops and logs').\",\n    \"appliesTo\": [\n      \"web\",\n      \"node\",\n      \"react-native\",\n      \"swift\"\n    ],\n    \"codeRef\": [\n      \"sdks/web/src/crossdeck.ts\",\n      \"sdks/node/src/crossdeck-server.ts\",\n      \"sdks/react-native/src/crossdeck.ts\",\n      \"sdks/swift/Sources/Crossdeck/Crossdeck.swift\",\n      \"sdks/swift/Sources/Crossdeck/Breadcrumbs.swift\"\n    ],\n    \"testRef\": [\n      {\n        \"file\": \"sdks/web/tests/crossdeck.test.ts\",\n        \"name\": \"track with empty name throws synchronously\"\n      },\n      {\n        \"file\": \"sdks/web/tests/crossdeck.test.ts\",\n        \"name\": \"rejects empty userId\"\n      },\n      {\n        \"file\": \"sdks/node/tests/crossdeck-server.test.ts\",\n        \"name\": \"track() throws CrossdeckError with code 'missing_event_name' when event name is empty\"\n      },\n      {\n        \"file\": \"sdks/react-native/tests/crossdeck.test.ts\",\n        \"name\": \"track('') throws CrossdeckError(missing_event_name) synchronously\"\n      },\n      {\n        \"file\": \"sdks/react-native/tests/crossdeck.test.ts\",\n        \"name\": \"identify('') rejects with CrossdeckError(missing_user_id)\"\n      },\n      {\n        \"file\": \"sdks/swift/Tests/CrossdeckTests/CrossdeckPublicAPITests.swift\",\n        \"name\": \"test_track_dropsEmptyName\"\n      },\n      {\n        \"file\": \"sdks/swift/Tests/CrossdeckTests/CrossdeckPublicAPITests.swift\",\n        \"name\": \"test_identifyAndWait_rejectsEmptyId\"\n      },\n      {\n        \"file\": \"sdks/swift/Tests/CrossdeckTests/PublicAPIInputSafetyTests.swift\",\n        \"name\": \"test_start_withZeroBreadcrumbCapacity_doesNotTrap\"\n      }\n    ],\n    \"registeredAt\": \"2026-06-11\",\n    \"firstRegisteredIn\": \"swift trap-on-input class fix — first machine-tested Swift release\",\n    \"bundledIn\": \"@cross-deck/web@1.14.1\",\n    \"runtimeVerified\": false\n  },\n  {\n    \"id\": \"per-user-cache-isolation\",\n    \"pillar\": \"entitlements\",\n    \"status\": \"enforced\",\n    \"claim\": \"Every identify(userId) switches the entitlement cache to a physically separate per-user storage slot — `crossdeck:entitlements:<sha256(userId)>` — and unconditionally wipes the in-memory snapshot. A user-switch on a shared device CANNOT cross-read a prior user's cached entitlements, even if the in-memory clear is somehow skipped, because the storage keys are physically separate. reset() wipes every per-user slot via the persisted index.\",\n    \"appliesTo\": [\n      \"web\",\n      \"react-native\",\n      \"swift\",\n      \"android\"\n    ],\n    \"codeRef\": [\n      \"sdks/web/src/entitlement-cache.ts\",\n      \"sdks/web/src/hash.ts\",\n      \"sdks/web/src/crossdeck.ts\",\n      \"sdks/react-native/src/entitlement-cache.ts\",\n      \"sdks/react-native/src/hash.ts\",\n      \"sdks/react-native/src/crossdeck.ts\",\n      \"sdks/swift/Sources/Crossdeck/EntitlementCache.swift\",\n      \"sdks/swift/Sources/Crossdeck/IdempotencyKey.swift\",\n      \"sdks/swift/Sources/Crossdeck/Crossdeck.swift\",\n      \"sdks/android/crossdeck/src/main/kotlin/com/crossdeck/EntitlementCache.kt\",\n      \"sdks/android/crossdeck/src/main/kotlin/com/crossdeck/IdempotencyKey.kt\",\n      \"sdks/android/crossdeck/src/main/kotlin/com/crossdeck/Crossdeck.kt\"\n    ],\n    \"testRef\": [\n      {\n        \"file\": \"sdks/web/tests/entitlement-cache-isolation.test.ts\",\n        \"name\": \"identify(B) makes A's entitlements unreachable from in-memory\"\n      },\n      {\n        \"file\": \"sdks/web/tests/entitlement-cache-isolation.test.ts\",\n        \"name\": \"clearAll() removes every per-user storage key plus the index\"\n      },\n      {\n        \"file\": \"sdks/web/tests/entitlement-cache-isolation.test.ts\",\n        \"name\": \"a second cache instance reading A's storage suffix CANNOT see B's data\"\n      },\n      {\n        \"file\": \"sdks/react-native/tests/entitlement-cache-isolation.test.ts\",\n        \"name\": \"identify(B) makes A's entitlements unreachable from in-memory\"\n      },\n      {\n        \"file\": \"sdks/react-native/tests/entitlement-cache-isolation.test.ts\",\n        \"name\": \"removes every per-user storage key plus the index\"\n      },\n      {\n        \"file\": \"sdks/swift/Tests/CrossdeckTests/EntitlementCacheIsolationTests.swift\",\n        \"name\": \"test_identifyB_makesAEntitlementsUnreachable\"\n      },\n      {\n        \"file\": \"sdks/swift/Tests/CrossdeckTests/EntitlementCacheIsolationTests.swift\",\n        \"name\": \"test_identifiedWritesLandUnderPerUserSha256Key\"\n      },\n      {\n        \"file\": \"sdks/swift/Tests/CrossdeckTests/EntitlementCacheIsolationTests.swift\",\n        \"name\": \"test_clearAll_removesEveryPerUserStorageKeyPlusIndex\"\n      },\n      {\n        \"file\": \"sdks/android/crossdeck/src/test/kotlin/com/crossdeck/EntitlementCacheIsolationTest.kt\",\n        \"name\": \"identified writes land under per-user sha256 key\"\n      },\n      {\n        \"file\": \"sdks/android/crossdeck/src/test/kotlin/com/crossdeck/EntitlementCacheIsolationTest.kt\",\n        \"name\": \"identify B makes A entitlements unreachable from in-memory\"\n      },\n      {\n        \"file\": \"sdks/android/crossdeck/src/test/kotlin/com/crossdeck/EntitlementCacheIsolationTest.kt\",\n        \"name\": \"clearAll removes every per-user storage key plus the index\"\n      },\n      {\n        \"file\": \"sdks/android/crossdeck/src/test/kotlin/com/crossdeck/EntitlementCacheIsolationTest.kt\",\n        \"name\": \"a fresh cache bound to A's key CANNOT read B's blob\"\n      }\n    ],\n    \"registeredAt\": \"2026-05-26\",\n    \"firstRegisteredIn\": \"bank-grade reconciliation v1.4.0 — phase 1.3 (web/RN) + dogfood-gap fix (swift + android)\",\n    \"bundledIn\": \"@cross-deck/web@1.14.1\",\n    \"runtimeVerified\": true\n  },\n  {\n    \"id\": \"sdk-error-codes-catalogue\",\n    \"pillar\": \"errors\",\n    \"status\": \"enforced\",\n    \"claim\": \"Web + Node SDK error-codes catalogues include EVERY backend-emitted ApiErrorCode with a description + resolution. Pre-v1.4.0 the catalogues documented codes the SDK threw ITSELF but ZERO backend codes — a developer hitting `invalid_api_key` / `origin_not_allowed` / `bundle_id_not_allowed` / `env_mismatch` / `idempotency_key_in_use` etc. got `undefined` from getErrorCode() and had to hunt for guidance. v1.4.0 backfills the catalogue from backend/src/api/v1-errors.ts so every wire code has a canonical 'what does this mean / what should I do' answer Stripe-style.\",\n    \"appliesTo\": [\n      \"web\",\n      \"node\"\n    ],\n    \"codeRef\": [\n      \"sdks/web/src/error-codes.ts\",\n      \"sdks/node/src/error-codes.ts\",\n      \"sdks/web/src/_contract-verifiers.ts\",\n      \"backend/src/api/v1-errors.ts\"\n    ],\n    \"testRef\": [\n      {\n        \"file\": \"sdks/web/tests/contract-verifiers.test.ts\",\n        \"name\": \"sdk-error-codes-catalogue covers every backend wire code with remediation\"\n      },\n      {\n        \"file\": \"sdks/web/tests/error-codes-backfill.test.ts\",\n        \"name\": \"includes backend code\"\n      },\n      {\n        \"file\": \"sdks/web/tests/error-codes-backfill.test.ts\",\n        \"name\": \"invalid_api_key resolution points at the dashboard\"\n      },\n      {\n        \"file\": \"sdks/web/tests/error-codes-backfill.test.ts\",\n        \"name\": \"idempotency_key_in_use resolution mentions Stripe-grade contract\"\n      },\n      {\n        \"file\": \"sdks/web/tests/error-codes-backfill.test.ts\",\n        \"name\": \"identity-lock codes carry permission_error type\"\n      },\n      {\n        \"file\": \"sdks/web/tests/error-codes-backfill.test.ts\",\n        \"name\": \"no entry has an empty description or resolution\"\n      }\n    ],\n    \"registeredAt\": \"2026-05-26\",\n    \"firstRegisteredIn\": \"bank-grade reconciliation v1.4.0 — phase 6.2\",\n    \"bundledIn\": \"@cross-deck/web@1.14.1\",\n    \"runtimeVerified\": true\n  },\n  {\n    \"id\": \"super-property-merge-precedence\",\n    \"pillar\": \"analytics\",\n    \"status\": \"enforced\",\n    \"claim\": \"Every Crossdeck SDK merges event properties with the precedence device < super < caller (caller-supplied values win over registered super-properties, which win over auto-attached device info). Pre-v1.4.0 Swift had it INVERTED (super < device < caller — device clobbered super), so a `register('plan', 'pro')` super-property was silently overridden by auto-attached device fields whenever keys collided. Cross-SDK funnel queries on super-property keys returned different answers per platform.\",\n    \"appliesTo\": [\n      \"web\",\n      \"swift\"\n    ],\n    \"codeRef\": [\n      \"sdks/web/src/super-properties.ts\",\n      \"sdks/web/src/_contract-verifiers.ts\",\n      \"sdks/swift/Sources/Crossdeck/EventPropertyMerge.swift\",\n      \"sdks/swift/Sources/Crossdeck/Crossdeck.swift\"\n    ],\n    \"testRef\": [\n      {\n        \"file\": \"sdks/web/tests/contract-verifiers.test.ts\",\n        \"name\": \"super-property-merge-precedence verifies caller > super > device\"\n      },\n      {\n        \"file\": \"sdks/swift/Tests/CrossdeckTests/EventPropertyMergeTests.swift\",\n        \"name\": \"test_super_overrides_device\"\n      },\n      {\n        \"file\": \"sdks/swift/Tests/CrossdeckTests/EventPropertyMergeTests.swift\",\n        \"name\": \"test_caller_overrides_super\"\n      },\n      {\n        \"file\": \"sdks/swift/Tests/CrossdeckTests/EventPropertyMergeTests.swift\",\n        \"name\": \"test_full_precedence_chain\"\n      },\n      {\n        \"file\": \"sdks/swift/Tests/CrossdeckTests/EventPropertyMergeTests.swift\",\n        \"name\": \"test_matchesWebNodeRNPrecedence\"\n      }\n    ],\n    \"registeredAt\": \"2026-05-26\",\n    \"firstRegisteredIn\": \"bank-grade reconciliation v1.4.0 — phase 3.2\",\n    \"bundledIn\": \"@cross-deck/web@1.14.1\",\n    \"runtimeVerified\": true\n  },\n  {\n    \"id\": \"sync-purchases-funnel-parity\",\n    \"pillar\": \"analytics\",\n    \"status\": \"enforced\",\n    \"claim\": \"Manual syncPurchases() emits a `purchase.completed` analytics event on success across ALL SDKs (Web / Node / RN / Swift / Android). Pre-v1.4.0 only Swift/Android auto-track emitted it — Web/Node/RN manual calls + Swift/Android manual calls fired ZERO analytics. Schema mirrors the auto-track event name + rail/productId/subscriptionId so cross-platform funnels reconcile on every payment path. When the backend short-circuits via the v1.4.0 idempotency cache, the event also carries `idempotent_replay: true`.\",\n    \"appliesTo\": [\n      \"web\",\n      \"node\",\n      \"react-native\",\n      \"swift\",\n      \"android\"\n    ],\n    \"codeRef\": [\n      \"sdks/web/src/crossdeck.ts\",\n      \"sdks/node/src/crossdeck-server.ts\",\n      \"sdks/react-native/src/crossdeck.ts\",\n      \"sdks/swift/Sources/Crossdeck/Crossdeck.swift\",\n      \"sdks/android/crossdeck/src/main/kotlin/com/crossdeck/Crossdeck.kt\"\n    ],\n    \"testRef\": [\n      {\n        \"file\": \"sdks/web/tests/sync-purchases-funnel.test.ts\",\n        \"name\": \"emits purchase.completed after a successful sync\"\n      },\n      {\n        \"file\": \"sdks/web/tests/sync-purchases-funnel.test.ts\",\n        \"name\": \"carries idempotent_replay=true when backend replied from cache\"\n      }\n    ],\n    \"registeredAt\": \"2026-05-26\",\n    \"firstRegisteredIn\": \"bank-grade reconciliation v1.4.0 — phase 3.5\",\n    \"bundledIn\": \"@cross-deck/web@1.14.1\",\n    \"runtimeVerified\": false\n  }\n]) as readonly Contract[];\n","/**\n * Public, typed accessor for the bank-grade behavioural contracts\n * this SDK ships. The full architecture — schema, distribution,\n * audit loop, pillar taxonomy — lives in `contracts/README.md`\n * at the monorepo root.\n *\n * Why a typed surface (vs. plain JSON access): contract IDs and\n * pillar names are part of Crossdeck's public commitment to\n * customers. Reading them through `CrossdeckContracts` means the\n * compiler catches drift the moment a contract is renamed or\n * retired. Tools that consume contracts at runtime (dashboards,\n * AI assistants, customer integration tests) get the exact same\n * shape every SDK ships, with no parsing layer to drift.\n *\n * --- BINARY STABILITY ---\n * `Contract` is treated as an evolving — but back-compat — wire\n * shape. Fields may be added in any minor release. Existing\n * fields will not be removed or repurposed except in a major\n * version bump, even if all known contracts stop using them.\n * Customers can rely on `id`, `pillar`, `status`, `appliesTo`,\n * `codeRef`, `testRef`, `registeredAt`, `firstRegisteredIn`,\n * and `bundledIn` being present on every contract in every\n * future minor/patch release of this SDK.\n */\n\nimport {\n  BUNDLED_CONTRACTS,\n  BUNDLED_IN,\n  SDK_VERSION,\n} from \"./_contracts-bundled\";\n\n/**\n * Which bank-grade pillar a contract belongs to. The taxonomy is\n * deliberately small — every contract maps to exactly one. New\n * pillars require a Crossdeck major-version bump.\n */\nexport type ContractPillar =\n  | \"revenue\"\n  | \"entitlements\"\n  | \"analytics\"\n  | \"webhooks\"\n  | \"errors\"\n  | \"lifecycle\"\n  | \"identity\";\n\n/**\n * Lifecycle stage of a contract.\n * - `enforced`: live in this SDK and exercised by `testRef`.\n * - `proposed`: registered for an upcoming release; `testRef`\n *    may point to a not-yet-existing file.\n * - `retired`: kept for history only; the behaviour no longer\n *    ships. Filtered out of `CrossdeckContracts.all()` by default.\n */\nexport type ContractStatus = \"enforced\" | \"proposed\" | \"retired\";\n\n/** Which SDKs (and/or `backend`) a contract is binding on. */\nexport type ContractAppliesTo =\n  | \"web\"\n  | \"node\"\n  | \"react-native\"\n  | \"swift\"\n  | \"android\"\n  | \"backend\";\n\n/**\n * Pointer to the test that exercises a contract clause. The\n * `name` is matched verbatim against the file's text by\n * `scripts/contract-audit.mjs`, so a rename without updating\n * the contract aborts CI.\n */\nexport interface ContractTestRef {\n  readonly file: string;\n  readonly name: string;\n}\n\n/** One bank-grade behavioural guarantee — see `contracts/README.md`. */\nexport interface Contract {\n  readonly id: string;\n  readonly pillar: ContractPillar;\n  readonly status: ContractStatus;\n  readonly claim: string;\n  readonly appliesTo: readonly ContractAppliesTo[];\n  readonly codeRef: readonly string[];\n  readonly testRef: readonly ContractTestRef[];\n  /** ISO-8601 date the contract was first registered. */\n  readonly registeredAt: string;\n  /** The release note / phase the contract first appeared in. Immutable. */\n  readonly firstRegisteredIn: string;\n  /** The SDK release this snapshot was bundled with, stamped at build time. */\n  readonly bundledIn: string;\n  /**\n   * Whether THIS SDK self-verifies this contract at runtime (a verifier is\n   * registered in the SDK's `STATIC_VERIFIERS` harness, emitting\n   * `crossdeck.contract_failed` live), vs. proven by CI tests only.\n   *\n   * DERIVED at bundle time from the verifier registry — never hand-set —\n   * so the registry can never disagree with what actually runs. Runtime\n   * status is a property of (contract × SDK): the same contract can be\n   * `true` here (web) and `false` in another SDK that lacks the harness.\n   *\n   * `true`  → surfaces in the console as \"watch it pass live\".\n   * `false` → \"CI-proven every release\" (still enforced, just not a\n   *           live toggle on this platform).\n   */\n  readonly runtimeVerified: boolean;\n}\n\n/**\n * Typed entry point to the bank-grade contracts bundled with this\n * SDK release. Stable, side-effect-free, tree-shakeable.\n *\n * @example Audit at app boot\n * ```ts\n * import { CrossdeckContracts } from \"@cross-deck/web\";\n *\n * for (const c of CrossdeckContracts.all()) {\n *   console.log(`[crossdeck] ${c.id} (${c.pillar})`);\n * }\n * ```\n *\n * @example Assert a specific clause is in force\n * ```ts\n * const isolation = CrossdeckContracts.byId(\"per-user-cache-isolation\");\n * if (!isolation || isolation.status !== \"enforced\") {\n *   throw new Error(\"entitlement isolation contract is not enforced — refusing to start\");\n * }\n * ```\n */\nexport const CrossdeckContracts = {\n  /** Every contract that applies to this SDK and is currently enforced. */\n  all(): readonly Contract[] {\n    return BUNDLED_CONTRACTS.filter((c) => c.status === \"enforced\");\n  },\n\n  /**\n   * Every contract bundled with this SDK release, including\n   * `proposed` and `retired` entries. Use `all()` for the\n   * enforced-only view.\n   */\n  allIncludingHistorical(): readonly Contract[] {\n    return BUNDLED_CONTRACTS;\n  },\n\n  /** Look up a contract by its stable `id`. */\n  byId(id: string): Contract | undefined {\n    return BUNDLED_CONTRACTS.find((c) => c.id === id);\n  },\n\n  /** Every enforced contract within a pillar. */\n  byPillar(pillar: ContractPillar): readonly Contract[] {\n    return BUNDLED_CONTRACTS.filter(\n      (c) => c.pillar === pillar && c.status === \"enforced\",\n    );\n  },\n\n  /** Filter by lifecycle status. */\n  withStatus(status: ContractStatus): readonly Contract[] {\n    return BUNDLED_CONTRACTS.filter((c) => c.status === status);\n  },\n\n  /** Semver of the SDK release these contracts were bundled with. */\n  sdkVersion: SDK_VERSION,\n\n  /** Fully-qualified bundle identifier — e.g. `@cross-deck/web@1.4.2`. */\n  bundledIn: BUNDLED_IN,\n\n  /**\n   * Resolve a failing test back to the contract it exercises.\n   * Used by test-framework hooks (Vitest `afterEach`, XCTest\n   * observation, JUnit `TestWatcher`) to find the contract id of\n   * a failed contract test so `reportContractFailure(...)` can\n   * stamp the right `contract_id` on the emitted event.\n   *\n   * Match is on `testRef.name` (case-sensitive, exact). Returns\n   * the first contract whose `testRef` list contains a matching\n   * entry, regardless of pillar or status.\n   */\n  findByTestName(name: string): Contract | undefined {\n    return BUNDLED_CONTRACTS.find((c) =>\n      c.testRef.some((ref) => ref.name === name),\n    );\n  },\n} as const;\n\n/**\n * Input to {@link Crossdeck.reportContractFailure}. Lets a test\n * harness / dogfood app / customer integration report a contract\n * violation back to Crossdeck on the dedicated reliability channel —\n * single-fire, never visible in the customer's dashboard.\n *\n * SCHEMA-LOCK: this interface's field set is exhaustively named. No\n * free-form `extra: Record<string, unknown>` — the schema-lock\n * contract at\n * `contracts/diagnostics/contract-failed-payload-schema-lock.json`\n * forbids unbounded fields. Adding a field requires a PR that\n * amends the contract first, then the public interface.\n *\n * `sdk_version` and `sdk_platform` are auto-stamped by the SDK so\n * every emitted event carries them correctly without the caller\n * needing to read them out of `CrossdeckContracts.sdkVersion`.\n */\nexport interface ContractFailureInput {\n  /** Stable contract id (`per-user-cache-isolation` etc.). */\n  contractId: string;\n  /**\n   * Short categorical-ish label — the SDK convention is to keep this\n   * under 128 chars and stable across runs (so dashboards can group).\n   * Never an end-user-supplied string.\n   */\n  failureReason: string;\n  /**\n   * Where the failure was observed:\n   *   - `ci`            — the SDK's own test suite on CI\n   *   - `dogfood`       — Crossdeck's internal dogfood project\n   *   - `customer-app`  — a customer's app verifying contracts\n   */\n  runContext: \"ci\" | \"dogfood\" | \"customer-app\";\n  /**\n   * Stable identifier for this verification run. CI: `GITHUB_RUN_ID`\n   * or equivalent. Dogfood: per-launch UUID. Customer app: any\n   * stable handle the customer chooses to group fires by run.\n   */\n  runId: string;\n  /**\n   * Optional pointer back to the failing test, for triage. The SDK\n   * sends both `test_file` and `test_name` on the wire when set.\n   */\n  testRef?: { file: string; name: string };\n  /**\n   * Optional coarse device class, e.g. \"desktop\", \"mobile-web\",\n   * \"ssr\". A categorical bucket, not a device identifier.\n   */\n  deviceClass?: string;\n}\n"],"mappings":"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","names":["safe","resolveTarget","safe","g","DEFAULT_TIMEOUT_MS","g","doc","MemoryStorage","location","console","safeStringify","message","g","SDK_VERSION","SDK_VERSION"]}